xref: /haiku/src/kits/tracker/Utilities.cpp (revision 80d75f15dfa48ebea421c6b2c19a5296cc63d7eb)
1 /*
2 Open Tracker License
3 
4 Terms and Conditions
5 
6 Copyright (c) 1991-2000, Be Incorporated. All rights reserved.
7 
8 Permission is hereby granted, free of charge, to any person obtaining a copy of
9 this software and associated documentation files (the "Software"), to deal in
10 the Software without restriction, including without limitation the rights to
11 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
12 of the Software, and to permit persons to whom the Software is furnished to do
13 so, subject to the following conditions:
14 
15 The above copyright notice and this permission notice applies to all licensees
16 and shall be included in all copies or substantial portions of the Software.
17 
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY,
20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
22 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION
23 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 
25 Except as contained in this notice, the name of Be Incorporated shall not be
26 used in advertising or otherwise to promote the sale, use or other dealings in
27 this Software without prior written authorization from Be Incorporated.
28 
29 Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks
30 of Be Incorporated in the United States and other countries. Other brand product
31 names are registered trademarks or trademarks of their respective holders.
32 All rights reserved.
33 */
34 
35 #include "Attributes.h"
36 #include "MimeTypes.h"
37 #include "Model.h"
38 #include "PoseView.h"
39 #include "Utilities.h"
40 #include "ContainerWindow.h"
41 
42 #ifdef __HAIKU__
43 #	include <IconUtils.h>
44 #endif
45 
46 #include <Bitmap.h>
47 #include <Debug.h>
48 #include <Directory.h>
49 #include <fs_attr.h>
50 #include <fs_info.h>
51 #include <MenuItem.h>
52 #include <OS.h>
53 #include <PopUpMenu.h>
54 #include <Region.h>
55 #include <StorageDefs.h>
56 #include <TextView.h>
57 #include <Volume.h>
58 #include <VolumeRoster.h>
59 #include <Window.h>
60 
61 #include <ctype.h>
62 #include <malloc.h>
63 #include <string.h>
64 #include <stdlib.h>
65 #include <time.h>
66 #include <stdarg.h>
67 
68 
69 #if B_BEOS_VERSION_DANO
70 #	define _IMPEXP_BE
71 #endif
72 extern _IMPEXP_BE const uint32	LARGE_ICON_TYPE;
73 extern _IMPEXP_BE const uint32	MINI_ICON_TYPE;
74 #if B_BEOS_VERSION_DANO
75 #	undef _IMPEXP_BE
76 #endif
77 
78 
79 FILE *logFile = NULL;
80 
81 static const float kMinSeparatorStubX = 10;
82 static const float kStubToStringSlotX = 5;
83 
84 
85 namespace BPrivate {
86 
87 const rgb_color	kBlack = {0, 0, 0, 255};
88 const rgb_color	kWhite = {255, 255, 255, 255};
89 
90 
91 uint32
92 HashString(const char *string, uint32 seed)
93 {
94 	char ch;
95 	uint32 result = seed;
96 
97 	while((ch = *string++) != 0) {
98 		result = (result << 7) ^ (result >> 24);
99 		result ^= ch;
100 	}
101 
102 	result ^= result << 12;
103 	return result;
104 }
105 
106 
107 uint32
108 AttrHashString(const char *string, uint32 type)
109 {
110 	char c;
111 	uint32 hash = 0;
112 
113 	while((c = *string++) != 0) {
114 		hash = (hash << 7) ^ (hash >> 24);
115 		hash ^= c;
116 	}
117 
118 	hash ^= hash << 12;
119 
120 	hash &= ~0xff;
121 	hash |= type;
122 
123 	return hash;
124 }
125 
126 
127 bool
128 ValidateStream(BMallocIO *stream, uint32 key, int32 version)
129 {
130 	uint32 test_key;
131 	int32 test_version;
132 
133 	if (stream->Read(&test_key, sizeof(uint32)) <= 0
134 		|| stream->Read(&test_version, sizeof(int32)) <=0)
135 		return false;
136 
137 	return test_key == key && test_version == version;
138 }
139 
140 
141 void
142 DisallowFilenameKeys(BTextView *textView)
143 {
144 	textView->DisallowChar(':');
145 	textView->DisallowChar('/');
146 }
147 
148 
149 void
150 DisallowMetaKeys(BTextView *textView)
151 {
152 	textView->DisallowChar(B_TAB);
153 	textView->DisallowChar(B_ESCAPE);
154 	textView->DisallowChar(B_INSERT);
155 	textView->DisallowChar(B_DELETE);
156 	textView->DisallowChar(B_HOME);
157 	textView->DisallowChar(B_END);
158 	textView->DisallowChar(B_PAGE_UP);
159 	textView->DisallowChar(B_PAGE_DOWN);
160 	textView->DisallowChar(B_FUNCTION_KEY);
161 }
162 
163 
164 PeriodicUpdatePoses::PeriodicUpdatePoses()
165 	:	fPoseList(20, true)
166 {
167 	fLock = new Benaphore("PeriodicUpdatePoses");
168 }
169 
170 
171 PeriodicUpdatePoses::~PeriodicUpdatePoses()
172 {
173 	fLock->Lock();
174 	fPoseList.MakeEmpty();
175 	delete fLock;
176 }
177 
178 
179 void
180 PeriodicUpdatePoses::AddPose(BPose *pose, BPoseView *poseView,
181 	PeriodicUpdateCallback callback, void *cookie)
182 {
183 	periodic_pose *periodic = new periodic_pose;
184 	periodic->pose = pose;
185 	periodic->pose_view = poseView;
186 	periodic->callback = callback;
187 	periodic->cookie = cookie;
188 	fPoseList.AddItem(periodic);
189 }
190 
191 
192 bool
193 PeriodicUpdatePoses::RemovePose(BPose *pose, void **cookie)
194 {
195 	int32 count = fPoseList.CountItems();
196 	for (int32 index = 0; index < count; index++) {
197 		if (fPoseList.ItemAt(index)->pose == pose) {
198 			if (!fLock->Lock())
199 				return false;
200 
201 			periodic_pose *periodic = fPoseList.RemoveItemAt(index);
202 			if (cookie)
203 				*cookie = periodic->cookie;
204 			delete periodic;
205 			fLock->Unlock();
206 			return true;
207 		}
208 	}
209 
210 	return false;
211 }
212 
213 
214 void
215 PeriodicUpdatePoses::DoPeriodicUpdate(bool forceRedraw)
216 {
217 	if (!fLock->Lock())
218 		return;
219 
220 	int32 count = fPoseList.CountItems();
221 	for (int32 index = 0; index < count; index++) {
222 		periodic_pose *periodic = fPoseList.ItemAt(index);
223 		if (periodic->callback(periodic->pose, periodic->cookie)
224 			|| forceRedraw) {
225 			periodic->pose_view->LockLooper();
226 			periodic->pose_view->UpdateIcon(periodic->pose);
227 			periodic->pose_view->UnlockLooper();
228 		}
229 	}
230 
231 	fLock->Unlock();
232 }
233 
234 
235 PeriodicUpdatePoses gPeriodicUpdatePoses;
236 
237 }	// namespace BPrivate
238 
239 
240 void
241 PoseInfo::EndianSwap(void *castToThis)
242 {
243 	PoseInfo *self = (PoseInfo *)castToThis;
244 
245 	PRINT(("swapping PoseInfo\n"));
246 
247 	STATIC_ASSERT(sizeof(ino_t) == sizeof(int64));
248 	self->fInitedDirectory = SwapInt64(self->fInitedDirectory);
249 	swap_data(B_POINT_TYPE, &self->fLocation, sizeof(BPoint), B_SWAP_ALWAYS);
250 
251 	// do a sanity check on the icon position
252 	if (self->fLocation.x < -20000 || self->fLocation.x > 20000
253 		|| self->fLocation.y < -20000 || self->fLocation.y > 20000) {
254 		// position out of range, force autoplcemement
255 		PRINT((" rejecting icon position out of range\n"));
256 		self->fInitedDirectory = -1LL;
257 		self->fLocation = BPoint(0, 0);
258 	}
259 }
260 
261 
262 void
263 PoseInfo::PrintToStream()
264 {
265 	PRINT(("%s, inode:%Lx, location %f %f\n", fInvisible ? "hidden" : "visible",
266 		fInitedDirectory, fLocation.x, fLocation.y));
267 }
268 
269 
270 // #pragma mark -
271 
272 
273 size_t
274 ExtendedPoseInfo::Size() const
275 {
276 	return sizeof(ExtendedPoseInfo) + fNumFrames * sizeof(FrameLocation);
277 }
278 
279 
280 size_t
281 ExtendedPoseInfo::Size(int32 count)
282 {
283 	return sizeof(ExtendedPoseInfo) + count * sizeof(FrameLocation);
284 }
285 
286 
287 size_t
288 ExtendedPoseInfo::SizeWithHeadroom() const
289 {
290 	return sizeof(ExtendedPoseInfo) + (fNumFrames + 1) * sizeof(FrameLocation);
291 }
292 
293 
294 size_t
295 ExtendedPoseInfo::SizeWithHeadroom(size_t oldSize)
296 {
297 	int32 count = (ssize_t)oldSize - (ssize_t)sizeof(ExtendedPoseInfo);
298 	if (count > 0)
299 		count /= sizeof(FrameLocation);
300 	else
301 		count = 0;
302 
303 	return Size(count + 1);
304 }
305 
306 
307 bool
308 ExtendedPoseInfo::HasLocationForFrame(BRect frame) const
309 {
310 	for (int32 index = 0; index < fNumFrames; index++) {
311 		if (fLocations[index].fFrame == frame)
312 			return true;
313 	}
314 
315 	return false;
316 }
317 
318 
319 BPoint
320 ExtendedPoseInfo::LocationForFrame(BRect frame) const
321 {
322 	for (int32 index = 0; index < fNumFrames; index++) {
323 		if (fLocations[index].fFrame == frame)
324 			return fLocations[index].fLocation;
325 	}
326 
327 	TRESPASS();
328 	return BPoint(0, 0);
329 }
330 
331 
332 bool
333 ExtendedPoseInfo::SetLocationForFrame(BPoint newLocation, BRect frame)
334 {
335 	for (int32 index = 0; index < fNumFrames; index++) {
336 		if (fLocations[index].fFrame == frame) {
337 			if (fLocations[index].fLocation == newLocation)
338 				return false;
339 
340 			fLocations[index].fLocation = newLocation;
341 			return true;
342 		}
343 	}
344 
345 	fLocations[fNumFrames].fFrame = frame;
346 	fLocations[fNumFrames].fLocation = newLocation;
347 	fLocations[fNumFrames].fWorkspaces = 0xffffffff;
348 	fNumFrames++;
349 	return true;
350 }
351 
352 
353 void
354 ExtendedPoseInfo::EndianSwap(void *castToThis)
355 {
356 	ExtendedPoseInfo *self = (ExtendedPoseInfo *)castToThis;
357 
358 	PRINT(("swapping ExtendedPoseInfo\n"));
359 
360 	self->fWorkspaces = SwapUInt32(self->fWorkspaces);
361 	self->fNumFrames = SwapInt32(self->fNumFrames);
362 
363 	for (int32 index = 0; index < self->fNumFrames; index++) {
364 		swap_data(B_POINT_TYPE, &self->fLocations[index].fLocation,
365 			sizeof(BPoint), B_SWAP_ALWAYS);
366 
367 		if (self->fLocations[index].fLocation.x < -20000
368 			|| self->fLocations[index].fLocation.x > 20000
369 			|| self->fLocations[index].fLocation.y < -20000
370 			|| self->fLocations[index].fLocation.y > 20000) {
371 			// position out of range, force autoplcemement
372 			PRINT((" rejecting icon position out of range\n"));
373 			self->fLocations[index].fLocation = BPoint(0, 0);
374 		}
375 		swap_data(B_RECT_TYPE, &self->fLocations[index].fFrame,
376 			sizeof(BRect), B_SWAP_ALWAYS);
377 	}
378 }
379 
380 
381 void
382 ExtendedPoseInfo::PrintToStream()
383 {
384 }
385 
386 
387 // #pragma mark -
388 
389 
390 OffscreenBitmap::OffscreenBitmap(BRect frame)
391 	:
392 	fBitmap(NULL)
393 {
394 	NewBitmap(frame);
395 }
396 
397 
398 OffscreenBitmap::OffscreenBitmap()
399 	:
400 	fBitmap(NULL)
401 {
402 }
403 
404 
405 OffscreenBitmap::~OffscreenBitmap()
406 {
407 	delete fBitmap;
408 }
409 
410 
411 void
412 OffscreenBitmap::NewBitmap(BRect bounds)
413 {
414 	delete fBitmap;
415 	fBitmap = new BBitmap(bounds, B_RGB32, true);
416 	if (fBitmap->Lock()) {
417 		BView *view = new BView(fBitmap->Bounds(), "", B_FOLLOW_NONE, 0);
418 		fBitmap->AddChild(view);
419 
420 		BRect clipRect = view->Bounds();
421 		BRegion newClip;
422 		newClip.Set(clipRect);
423 		view->ConstrainClippingRegion(&newClip);
424 
425 		fBitmap->Unlock();
426 	} else {
427 		delete fBitmap;
428 		fBitmap = NULL;
429 	}
430 }
431 
432 
433 BView *
434 OffscreenBitmap::BeginUsing(BRect frame)
435 {
436 	if (!fBitmap || fBitmap->Bounds() != frame)
437 		NewBitmap(frame);
438 
439 	fBitmap->Lock();
440 	return View();
441 }
442 
443 
444 void
445 OffscreenBitmap::DoneUsing()
446 {
447 	fBitmap->Unlock();
448 }
449 
450 
451 BBitmap *
452 OffscreenBitmap::Bitmap() const
453 {
454 	ASSERT(fBitmap);
455 	ASSERT(fBitmap->IsLocked());
456 	return fBitmap;
457 }
458 
459 
460 BView *
461 OffscreenBitmap::View() const
462 {
463 	ASSERT(fBitmap);
464 	return fBitmap->ChildAt(0);
465 }
466 
467 
468 // #pragma mark -
469 
470 
471 namespace BPrivate {
472 
473 /** Changes the alpha value of the given bitmap to create a nice
474  *	horizontal fade out in the specified region.
475  *	"from" is always transparent, "to" opaque.
476  */
477 
478 void
479 FadeRGBA32Horizontal(uint32 *bits, int32 width, int32 height, int32 from, int32 to)
480 {
481 	// check parameters
482 	if (width < 0 || height < 0 || from < 0 || to < 0)
483 		return;
484 
485 	float change = 1.f / (to - from);
486 	if (from > to) {
487 		int32 temp = from;
488 		from = to;
489 		to = temp;
490 	}
491 
492 	for (int32 y = 0; y < height; y++) {
493 		float alpha = change > 0 ? 0.0f : 1.0f;
494 
495 		for (int32 x = from; x <= to; x++) {
496 			if (bits[x] & 0xff000000) {
497 				uint32 a = uint32((bits[x] >> 24) * alpha);
498 				bits[x] = (bits[x] & 0x00ffffff) | (a << 24);
499 			}
500 			alpha += change;
501 		}
502 		bits += width;
503 	}
504 }
505 
506 
507 /** Changes the alpha value of the given bitmap to create a nice
508  *	vertical fade out in the specified region.
509  *	"from" is always transparent, "to" opaque.
510  */
511 
512 void
513 FadeRGBA32Vertical(uint32 *bits, int32 width, int32 height, int32 from, int32 to)
514 {
515 	// check parameters
516 	if (width < 0 || height < 0 || from < 0 || to < 0)
517 		return;
518 
519 	if (from > to)
520 		bits += width * (height - (from - to));
521 
522 	float change = 1.f / (to - from);
523 	if (from > to) {
524 		int32 temp = from;
525 		from = to;
526 		to = temp;
527 	}
528 
529 	float alpha = change > 0 ? 0.0f : 1.0f;
530 
531 	for (int32 y = from; y <= to; y++) {
532 		for (int32 x = 0; x < width; x++) {
533 			if (bits[x] & 0xff000000) {
534 				uint32 a = uint32((bits[x] >> 24) * alpha);
535 				bits[x] = (bits[x] & 0x00ffffff) | (a << 24);
536 			}
537 		}
538 		alpha += change;
539 		bits += width;
540 	}
541 }
542 
543 }	// namespace BPrivate
544 
545 
546 // #pragma mark -
547 
548 
549 DraggableIcon::DraggableIcon(BRect rect, const char *name, const char *mimeType,
550 		icon_size size, const BMessage *message, BMessenger target,
551 		uint32 resizeMask, uint32 flags)
552 	: BView(rect, name, resizeMask, flags),
553 	fMessage(*message),
554 	fTarget(target)
555 {
556 	fBitmap = new BBitmap(Bounds(), kDefaultIconDepth);
557 	BMimeType mime(mimeType);
558 	status_t error = mime.GetIcon(fBitmap, size);
559 	ASSERT(mime.IsValid());
560 	if (error != B_OK) {
561 		PRINT(("failed to get icon for %s, %s\n", mimeType, strerror(error)));
562 		BMimeType mime(B_FILE_MIMETYPE);
563 		ASSERT(mime.IsInstalled());
564 		mime.GetIcon(fBitmap, size);
565 	}
566 }
567 
568 
569 DraggableIcon::~DraggableIcon()
570 {
571 	delete fBitmap;
572 }
573 
574 
575 void
576 DraggableIcon::SetTarget(BMessenger target)
577 {
578 	fTarget = target;
579 }
580 
581 
582 BRect
583 DraggableIcon::PreferredRect(BPoint offset, icon_size size)
584 {
585 	BRect result(0, 0, size - 1, size - 1);
586 	result.OffsetTo(offset);
587 	return result;
588 }
589 
590 
591 void
592 DraggableIcon::AttachedToWindow()
593 {
594 	BView *parent = Parent();
595 	if (parent) {
596 		SetViewColor(parent->ViewColor());
597 		SetLowColor(parent->LowColor());
598 	}
599 }
600 
601 
602 void
603 DraggableIcon::MouseDown(BPoint point)
604 {
605 	if (!DragStarted(&fMessage))
606 		return;
607 
608 	BRect rect(Bounds());
609 	BBitmap *dragBitmap = new BBitmap(rect, B_RGBA32, true);
610 	dragBitmap->Lock();
611 	BView *view = new BView(dragBitmap->Bounds(), "", B_FOLLOW_NONE, 0);
612 	dragBitmap->AddChild(view);
613 	view->SetOrigin(0, 0);
614 	BRect clipRect(view->Bounds());
615 	BRegion newClip;
616 	newClip.Set(clipRect);
617 	view->ConstrainClippingRegion(&newClip);
618 
619 	// Transparent draw magic
620 	view->SetHighColor(0, 0, 0, 0);
621 	view->FillRect(view->Bounds());
622 	view->SetDrawingMode(B_OP_ALPHA);
623 	view->SetHighColor(0, 0, 0, 128);
624 		// set the level of transparency by value
625 	view->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_COMPOSITE);
626 	view->DrawBitmap(fBitmap);
627 	view->Sync();
628 	dragBitmap->Unlock();
629 	DragMessage(&fMessage, dragBitmap, B_OP_ALPHA, point, fTarget.Target(0));
630 }
631 
632 
633 bool
634 DraggableIcon::DragStarted(BMessage *)
635 {
636 	return true;
637 }
638 
639 
640 void
641 DraggableIcon::Draw(BRect)
642 {
643 #ifdef __HAIKU__
644 	SetDrawingMode(B_OP_ALPHA);
645 	SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
646 #else
647 	SetDrawingMode(B_OP_OVER);
648 #endif
649 	DrawBitmap(fBitmap);
650 }
651 
652 
653 // #pragma mark -
654 
655 
656 FlickerFreeStringView::FlickerFreeStringView(BRect bounds, const char *name,
657 		const char *text, uint32 resizeFlags, uint32 flags)
658 	: BStringView(bounds, name, text, resizeFlags, flags),
659 	fBitmap(NULL),
660 	fOrigBitmap(NULL)
661 {
662 }
663 
664 
665 FlickerFreeStringView::FlickerFreeStringView(BRect bounds, const char *name,
666 		const char *text, BBitmap *inBitmap, uint32 resizeFlags, uint32 flags)
667 	: BStringView(bounds, name, text, resizeFlags, flags),
668 	fBitmap(NULL),
669 	fOrigBitmap(inBitmap)
670 {
671 }
672 
673 
674 FlickerFreeStringView::~FlickerFreeStringView()
675 {
676 	delete fBitmap;
677 }
678 
679 
680 void
681 FlickerFreeStringView::Draw(BRect)
682 {
683 	BRect bounds(Bounds());
684 	if (!fBitmap)
685 		fBitmap = new OffscreenBitmap(Bounds());
686 
687 	BView *offscreen = fBitmap->BeginUsing(bounds);
688 
689 	if (Parent()) {
690 		fViewColor = Parent()->ViewColor();
691 		fLowColor = Parent()->ViewColor();
692 	}
693 
694 	offscreen->SetViewColor(fViewColor);
695 	offscreen->SetHighColor(HighColor());
696 	offscreen->SetLowColor(fLowColor);
697 
698 	BFont font;
699     GetFont(&font);
700 	offscreen->SetFont(&font);
701 
702 	offscreen->Sync();
703 	if (fOrigBitmap)
704 		offscreen->DrawBitmap(fOrigBitmap, Frame(), bounds);
705 	else
706 		offscreen->FillRect(bounds, B_SOLID_LOW);
707 
708 	if (Text()) {
709 		BPoint loc;
710 
711 		font_height	height;
712 		GetFontHeight(&height);
713 
714 		edge_info eInfo;
715 		switch (Alignment()) {
716 			case B_ALIGN_LEFT:
717 #ifdef HAIKU_TARGET_PLATFORM_HAIKU
718 			case B_ALIGN_HORIZONTAL_UNSET:
719 			case B_ALIGN_USE_FULL_WIDTH:
720 #endif
721 			{
722 				// If the first char has a negative left edge give it
723 				// some more room by shifting that much more to the right.
724 				font.GetEdges(Text(), 1, &eInfo);
725 				loc.x = bounds.left + (2 - eInfo.left);
726 				break;
727 			}
728 
729 			case B_ALIGN_CENTER:
730 			{
731 				float width = StringWidth(Text());
732 				float center = (bounds.right - bounds.left) / 2;
733 				loc.x = center - (width/2);
734 				break;
735 			}
736 
737 			case B_ALIGN_RIGHT:
738 			{
739 				float width = StringWidth(Text());
740 				loc.x = bounds.right - width - 2;
741 				break;
742 			}
743 		}
744 		loc.y = bounds.bottom - (1 + height.descent);
745 		offscreen->DrawString(Text(), loc);
746 	}
747 	offscreen->Sync();
748 	SetDrawingMode(B_OP_COPY);
749 	DrawBitmap(fBitmap->Bitmap());
750 	fBitmap->DoneUsing();
751 }
752 
753 
754 void
755 FlickerFreeStringView::AttachedToWindow()
756 {
757 	_inherited::AttachedToWindow();
758 	if (Parent()) {
759 		fViewColor = Parent()->ViewColor();
760 		fLowColor = Parent()->ViewColor();
761 	}
762 	SetViewColor(B_TRANSPARENT_32_BIT);
763 	SetLowColor(B_TRANSPARENT_32_BIT);
764 }
765 
766 
767 void
768 FlickerFreeStringView::SetViewColor(rgb_color color)
769 {
770 	if (fViewColor != color) {
771 		fViewColor = color;
772 		Invalidate();
773 	}
774 	_inherited::SetViewColor(B_TRANSPARENT_32_BIT);
775 }
776 
777 
778 void
779 FlickerFreeStringView::SetLowColor(rgb_color color)
780 {
781 	if (fLowColor != color) {
782 		fLowColor = color;
783 		Invalidate();
784 	}
785 	_inherited::SetLowColor(B_TRANSPARENT_32_BIT);
786 }
787 
788 
789 // #pragma mark -
790 
791 
792 TitledSeparatorItem::TitledSeparatorItem(const char *label)
793 	: BMenuItem(label, 0)
794 {
795 	_inherited::SetEnabled(false);
796 }
797 
798 
799 TitledSeparatorItem::~TitledSeparatorItem()
800 {
801 }
802 
803 
804 void
805 TitledSeparatorItem::SetEnabled(bool)
806 {
807 	// leave disabled
808 }
809 
810 
811 void
812 TitledSeparatorItem::GetContentSize(float *width, float *height)
813 {
814 	_inherited::GetContentSize(width, height);
815 }
816 
817 
818 inline rgb_color
819 ShiftMenuBackgroundColor(float by)
820 {
821 	return tint_color(ui_color(B_MENU_BACKGROUND_COLOR), by);
822 }
823 
824 
825 void
826 TitledSeparatorItem::Draw()
827 {
828 	BRect frame(Frame());
829 
830 	BMenu *parent = Menu();
831 	ASSERT(parent);
832 
833 	menu_info minfo;
834 	get_menu_info(&minfo);
835 
836 	if (minfo.separator > 0) {
837 		frame.left += 10;
838 		frame.right -= 10;
839 	} else {
840 		frame.left += 1;
841 		frame.right -= 1;
842 	}
843 
844 	float startX = frame.left;
845 	float endX = frame.right;
846 
847 	float maxStringWidth = endX - startX - (2 * kMinSeparatorStubX
848 		+ 2 * kStubToStringSlotX);
849 
850 	// ToDo:
851 	// handle case where maxStringWidth turns out negative here
852 
853 	BString truncatedLabel(Label());
854 	parent->TruncateString(&truncatedLabel, B_TRUNCATE_END, maxStringWidth);
855 
856 	maxStringWidth = parent->StringWidth(truncatedLabel.String());
857 
858 	// first calculate the length of the stub part of the
859 	// divider line, so we can use it for secondStartX
860 	float firstEndX = ((endX - startX) - maxStringWidth) / 2 - kStubToStringSlotX;
861 	if (firstEndX < 0)
862 		firstEndX = 0;
863 
864 	float secondStartX = endX - firstEndX;
865 
866 	// now finish calculating firstEndX
867 	firstEndX += startX;
868 
869 	parent->PushState();
870 
871 	int32 y = (int32) (frame.top + (frame.bottom - frame.top) / 2);
872 
873 	parent->BeginLineArray(minfo.separator == 2 ? 6 : 4);
874 	parent->AddLine(BPoint(frame.left, y), BPoint(firstEndX, y),
875 		ShiftMenuBackgroundColor(B_DARKEN_1_TINT));
876 	parent->AddLine(BPoint(secondStartX, y), BPoint(frame.right, y),
877 		ShiftMenuBackgroundColor(B_DARKEN_1_TINT));
878 
879 	if (minfo.separator == 2) {
880 		y++;
881 		frame.left++;
882 		frame.right--;
883 		parent->AddLine(BPoint(frame.left,y), BPoint(firstEndX, y),
884 			ShiftMenuBackgroundColor(B_DARKEN_1_TINT));
885 		parent->AddLine(BPoint(secondStartX,y), BPoint(frame.right, y),
886 			ShiftMenuBackgroundColor(B_DARKEN_1_TINT));
887 	}
888 	y++;
889 	if (minfo.separator == 2) {
890 		frame.left++;
891 		frame.right--;
892 	}
893 	parent->AddLine(BPoint(frame.left, y), BPoint(firstEndX, y),
894 		ShiftMenuBackgroundColor(B_DARKEN_1_TINT));
895 	parent->AddLine(BPoint(secondStartX, y), BPoint(frame.right, y),
896 		ShiftMenuBackgroundColor(B_DARKEN_1_TINT));
897 
898 	parent->EndLineArray();
899 
900 	font_height	finfo;
901 	parent->GetFontHeight(&finfo);
902 
903 	parent->SetLowColor(parent->ViewColor());
904 	BPoint loc(firstEndX + kStubToStringSlotX, ContentLocation().y + finfo.ascent);
905 
906 	parent->MovePenTo(loc + BPoint(1, 1));
907 	parent->SetHighColor(ShiftMenuBackgroundColor(B_DARKEN_1_TINT));
908 	parent->DrawString(truncatedLabel.String());
909 
910 	parent->MovePenTo(loc);
911 	parent->SetHighColor(ShiftMenuBackgroundColor(B_DISABLED_LABEL_TINT));
912 	parent->DrawString(truncatedLabel.String());
913 
914 	parent->PopState();
915 }
916 
917 
918 // #pragma mark -
919 
920 
921 ShortcutFilter::ShortcutFilter(uint32 shortcutKey, uint32 shortcutModifier,
922 		uint32 shortcutWhat, BHandler *target)
923 	: BMessageFilter(B_KEY_DOWN),
924 	fShortcutKey(shortcutKey),
925 	fShortcutModifier(shortcutModifier),
926 	fShortcutWhat(shortcutWhat),
927 	fTarget(target)
928 {
929 }
930 
931 
932 filter_result
933 ShortcutFilter::Filter(BMessage *message, BHandler **)
934 {
935 	if (message->what == B_KEY_DOWN) {
936 		uint32 modifiers;
937 		uint32 rawKeyChar = 0;
938 		uint8 byte = 0;
939 		int32 key = 0;
940 
941 		if (message->FindInt32("modifiers", (int32 *)&modifiers) != B_OK
942 			|| message->FindInt32("raw_char", (int32 *)&rawKeyChar) != B_OK
943 			|| message->FindInt8("byte", (int8 *)&byte) != B_OK
944 			|| message->FindInt32("key", &key) != B_OK)
945 			return B_DISPATCH_MESSAGE;
946 
947 		modifiers &= B_SHIFT_KEY | B_COMMAND_KEY | B_CONTROL_KEY
948 			| B_OPTION_KEY | B_MENU_KEY;
949 			// strip caps lock, etc.
950 
951 		if (modifiers == fShortcutModifier && rawKeyChar == fShortcutKey) {
952 			fTarget->Looper()->PostMessage(fShortcutWhat, fTarget);
953 			return B_SKIP_MESSAGE;
954 		}
955 	}
956 
957 	// let others deal with this
958 	return B_DISPATCH_MESSAGE;
959 }
960 
961 
962 // #pragma mark -
963 
964 
965 namespace BPrivate {
966 
967 void
968 EmbedUniqueVolumeInfo(BMessage *message, const BVolume *volume)
969 {
970 	BDirectory rootDirectory;
971 	time_t created;
972 	fs_info info;
973 
974 	if (volume->GetRootDirectory(&rootDirectory) == B_OK
975 		&& rootDirectory.GetCreationTime(&created) == B_OK
976 		&& fs_stat_dev(volume->Device(), &info) == 0) {
977 		message->AddInt32("creationDate", created);
978 		message->AddInt64("capacity", volume->Capacity());
979 		message->AddString("deviceName", info.device_name);
980 		message->AddString("volumeName", info.volume_name);
981 		message->AddString("fshName", info.fsh_name);
982 	}
983 }
984 
985 
986 status_t
987 MatchArchivedVolume(BVolume *result, const BMessage *message, int32 index)
988 {
989 	time_t created;
990 	off_t capacity;
991 
992 	if (message->FindInt32("creationDate", index, &created) != B_OK
993 		|| message->FindInt64("capacity", index, &capacity) != B_OK)
994 		return B_ERROR;
995 
996 	BVolumeRoster roster;
997 	BVolume volume;
998 	BString deviceName, volumeName, fshName;
999 
1000 	if (message->FindString("deviceName", &deviceName) == B_OK
1001 		&& message->FindString("volumeName", &volumeName) == B_OK
1002 		&& message->FindString("fshName", &fshName) == B_OK) {
1003 		// New style volume identifiers: We have a couple of characteristics,
1004 		// and compute a score from them. The volume with the greatest score
1005 		// (if over a certain threshold) is the one we're looking for. We
1006 		// pick the first volume, in case there is more than one with the
1007 		// same score.
1008 		dev_t foundDevice = -1;
1009 		int foundScore = -1;
1010 		roster.Rewind();
1011 		while (roster.GetNextVolume(&volume) == B_OK) {
1012 			if (volume.IsPersistent() && volume.KnowsQuery()) {
1013 				// get creation time and fs_info
1014 				BDirectory root;
1015 				volume.GetRootDirectory(&root);
1016 				time_t cmpCreated;
1017 				fs_info info;
1018 				if (root.GetCreationTime(&cmpCreated) == B_OK
1019 					&& fs_stat_dev(volume.Device(), &info) == 0) {
1020 					// compute the score
1021 					int score = 0;
1022 
1023 					// creation time
1024 					if (created == cmpCreated)
1025 						score += 5;
1026 					// capacity
1027 					if (capacity == volume.Capacity())
1028 						score += 4;
1029 					// device name
1030 					if (deviceName == info.device_name)
1031 						score += 3;
1032 					// volume name
1033 					if (volumeName == info.volume_name)
1034 						score += 2;
1035 					// fsh name
1036 					if (fshName == info.fsh_name)
1037 						score += 1;
1038 
1039 					// check score
1040 					if (score >= 9 && score > foundScore) {
1041 						foundDevice = volume.Device();
1042 						foundScore = score;
1043 					}
1044 				}
1045 			}
1046 		}
1047 		if (foundDevice >= 0)
1048 			return result->SetTo(foundDevice);
1049 	} else {
1050 		// Old style volume identifiers: We have only creation time and
1051 		// capacity. Both must match.
1052 		roster.Rewind();
1053 		while (roster.GetNextVolume(&volume) == B_OK)
1054 			if (volume.IsPersistent() && volume.KnowsQuery()) {
1055 				BDirectory root;
1056 				volume.GetRootDirectory(&root);
1057 				time_t cmpCreated;
1058 				root.GetCreationTime(&cmpCreated);
1059 				if (created == cmpCreated && capacity == volume.Capacity()) {
1060 					*result = volume;
1061 					return B_OK;
1062 				}
1063 			}
1064 	}
1065 
1066 	return B_DEV_BAD_DRIVE_NUM;
1067 }
1068 
1069 
1070 void
1071 StringFromStream(BString *string, BMallocIO *stream, bool endianSwap)
1072 {
1073 	int32 length;
1074 	stream->Read(&length, sizeof(length));
1075 	if (endianSwap)
1076 		length = SwapInt32(length);
1077 
1078 	if (length <= 0 || length > 10000) {
1079 		// ToDo:
1080 		// should fail here
1081 		PRINT(("problems instatiating a string, length probably wrong %d\n", length));
1082 		return;
1083 	}
1084 
1085 	char *buffer = string->LockBuffer(length);
1086 	stream->Read(buffer, (size_t)length + 1);
1087 	string->UnlockBuffer(length);
1088 }
1089 
1090 
1091 void
1092 StringToStream(const BString *string, BMallocIO *stream)
1093 {
1094 	int32 length = string->Length();
1095 	stream->Write(&length, sizeof(int32));
1096 	stream->Write(string->String(), (size_t)string->Length() + 1);
1097 }
1098 
1099 
1100 int32
1101 ArchiveSize(const BString *string)
1102 {
1103 	return string->Length() + 1 + (ssize_t)sizeof(int32);
1104 }
1105 
1106 
1107 int32
1108 CountRefs(const BMessage *message)
1109 {
1110 	uint32 type;
1111 	int32 count;
1112 	message->GetInfo("refs", &type, &count);
1113 
1114 	return count;
1115 }
1116 
1117 
1118 static entry_ref *
1119 EachEntryRefCommon(BMessage *message, entry_ref *(*func)(entry_ref *, void *),
1120 	void *passThru, int32 maxCount)
1121 {
1122 	uint32 type;
1123 	int32 count;
1124 	message->GetInfo("refs", &type, &count);
1125 
1126 	if (maxCount >= 0 && count > maxCount)
1127 		count = maxCount;
1128 
1129 	for (int32 index = 0; index < count; index++) {
1130 		entry_ref ref;
1131 		message->FindRef("refs", index, &ref);
1132 		entry_ref *result = (func)(&ref, passThru);
1133 		if (result)
1134 			return result;
1135 	}
1136 
1137 	return NULL;
1138 }
1139 
1140 
1141 bool
1142 ContainsEntryRef(const BMessage *message, const entry_ref *ref)
1143 {
1144 	entry_ref match;
1145 	for (int32 index = 0; (message->FindRef("refs", index, &match) == B_OK); index++) {
1146 		if (*ref == match)
1147 			return true;
1148 	}
1149 
1150 	return false;
1151 }
1152 
1153 
1154 entry_ref *
1155 EachEntryRef(BMessage *message, entry_ref *(*func)(entry_ref *, void *),
1156 	void *passThru)
1157 {
1158 	return EachEntryRefCommon(message, func, passThru, -1);
1159 }
1160 
1161 typedef entry_ref *(*EachEntryIteratee)(entry_ref *, void *);
1162 
1163 const entry_ref *
1164 EachEntryRef(const BMessage *message, const entry_ref *(*func)(const entry_ref *, void *),
1165 	void *passThru)
1166 {
1167 	return EachEntryRefCommon(const_cast<BMessage *>(message),
1168 		(EachEntryIteratee)func, passThru, -1);
1169 }
1170 
1171 
1172 entry_ref *
1173 EachEntryRef(BMessage *message, entry_ref *(*func)(entry_ref *, void *),
1174 	void *passThru, int32 maxCount)
1175 {
1176 	return EachEntryRefCommon(message, func, passThru, maxCount);
1177 }
1178 
1179 
1180 const entry_ref *
1181 EachEntryRef(const BMessage *message, const entry_ref *(*func)(const entry_ref *, void *),
1182 	void *passThru, int32 maxCount)
1183 {
1184 	return EachEntryRefCommon(const_cast<BMessage *>(message),
1185 		(EachEntryIteratee)func, passThru, maxCount);
1186 }
1187 
1188 
1189 void
1190 TruncateLeaf(BString *string)
1191 {
1192 	for (int32 index = string->Length(); index >= 0; index--) {
1193 		if ((*string)[index] == '/') {
1194 			string->Truncate(index + 1);
1195 			return;
1196 		}
1197 	}
1198 }
1199 
1200 
1201 int64
1202 StringToScalar(const char *text)
1203 {
1204 	char *end;
1205 	int64 val;
1206 
1207 	char *buffer = new char [strlen(text) + 1];
1208 	strcpy(buffer, text);
1209 
1210 	if (strstr(buffer, "k") || strstr(buffer, "K")) {
1211 		val = strtoll(buffer, &end, 10);
1212 		val *= kKBSize;
1213 	} else if (strstr(buffer, "mb") || strstr(buffer, "MB")) {
1214 		val = strtoll(buffer, &end, 10);
1215 		val *= kMBSize;
1216 	} else if (strstr(buffer, "gb") || strstr(buffer, "GB")) {
1217 		val = strtoll(buffer, &end, 10);
1218 		val *= kGBSize;
1219 	} else if (strstr(buffer, "byte") || strstr(buffer, "BYTE")) {
1220 		val = strtoll(buffer, &end, 10);
1221 		val *= kGBSize;
1222 	} else {
1223 		// no suffix, try plain byte conversion
1224 		val = strtoll(buffer, &end, 10);
1225 	}
1226 
1227 	delete [] buffer;
1228 	return val;
1229 }
1230 
1231 #if B_BEOS_VERSION <= B_BEOS_VERSION_MAUI && !defined(__HAIKU__)
1232 
1233 bool
1234 operator==(const rgb_color &a, const rgb_color &b)
1235 {
1236 	return a.red == b.red
1237 		&& a.green == b.green
1238 		&& a.blue == b.blue
1239 		&& a.alpha == b.alpha;
1240 }
1241 
1242 
1243 bool
1244 operator!=(const rgb_color &a, const rgb_color &b)
1245 {
1246 	return !operator==(a, b);
1247 }
1248 
1249 #endif
1250 
1251 
1252 static BRect
1253 LineBounds(BPoint where, float length, bool vertical)
1254 {
1255 	BRect result;
1256 	result.SetLeftTop(where);
1257 	result.SetRightBottom(where + BPoint(2, 2));
1258 	if (vertical)
1259 		result.bottom = result.top + length;
1260 	else
1261 		result.right = result.left + length;
1262 
1263 	return result;
1264 }
1265 
1266 
1267 SeparatorLine::SeparatorLine(BPoint where, float length, bool vertical, const char *name)
1268 	: BView(LineBounds(where, length, vertical), name,
1269 		B_FOLLOW_LEFT | B_FOLLOW_TOP, B_WILL_DRAW)
1270 {
1271 	SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
1272 	SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR));
1273 }
1274 
1275 
1276 void
1277 SeparatorLine::Draw(BRect)
1278 {
1279 	BRect bounds(Bounds());
1280 	rgb_color hiliteColor = tint_color(ViewColor(), 1.5f);
1281 
1282 	bool vertical = (bounds.left > bounds.right - 3);
1283 	BeginLineArray(2);
1284 	if (vertical) {
1285 		AddLine(bounds.LeftTop(), bounds.LeftBottom(), hiliteColor);
1286 		AddLine(bounds.LeftTop() + BPoint(1, 0), bounds.LeftBottom() + BPoint(1, 0), kWhite);
1287 	} else {
1288 		AddLine(bounds.LeftTop(), bounds.RightTop(), hiliteColor);
1289 		AddLine(bounds.LeftTop() + BPoint(0, 1), bounds.RightTop() + BPoint(0, 1), kWhite);
1290 	}
1291 	EndLineArray();
1292 }
1293 
1294 
1295 void
1296 HexDump(const void *buf, int32 length)
1297 {
1298 	const int32 kBytesPerLine = 16;
1299 	int32 offset;
1300 	unsigned char *buffer = (unsigned char *)buf;
1301 
1302 	for (offset = 0; ; offset += kBytesPerLine, buffer += kBytesPerLine) {
1303 		int32 remain = length;
1304 		int32 index;
1305 
1306 		printf( "0x%06x: ", (int)offset);
1307 
1308 		for (index = 0; index < kBytesPerLine; index++) {
1309 			if (remain-- > 0)
1310 				printf("%02x%c", buffer[index], remain > 0 ? ',' : ' ');
1311 			else
1312 				printf("   ");
1313 		}
1314 
1315 		remain = length;
1316 		printf(" \'");
1317 		for (index = 0; index < kBytesPerLine; index++) {
1318 			if (remain-- > 0)
1319 				printf("%c", buffer[index] > ' ' ? buffer[index] : '.');
1320 			else
1321 				printf(" ");
1322 		}
1323 		printf("\'\n");
1324 
1325 		length -= kBytesPerLine;
1326 		if (length <= 0)
1327 			break;
1328 	}
1329 	fflush(stdout);
1330 }
1331 
1332 
1333 void
1334 EnableNamedMenuItem(BMenu *menu, const char *itemName, bool on)
1335 {
1336 	BMenuItem *item = menu->FindItem(itemName);
1337 	if (item)
1338 		item->SetEnabled(on);
1339 }
1340 
1341 
1342 void
1343 MarkNamedMenuItem(BMenu *menu, const char *itemName, bool on)
1344 {
1345 	BMenuItem *item = menu->FindItem(itemName);
1346 	if (item)
1347 		item->SetMarked(on);
1348 }
1349 
1350 
1351 void
1352 EnableNamedMenuItem(BMenu *menu, uint32 commandName, bool on)
1353 {
1354 	BMenuItem *item = menu->FindItem(commandName);
1355 	if (item)
1356 		item->SetEnabled(on);
1357 }
1358 
1359 
1360 void
1361 MarkNamedMenuItem(BMenu *menu, uint32 commandName, bool on)
1362 {
1363 	BMenuItem *item = menu->FindItem(commandName);
1364 	if (item)
1365 		item->SetMarked(on);
1366 }
1367 
1368 
1369 void
1370 DeleteSubmenu(BMenuItem *submenuItem)
1371 {
1372 	if (!submenuItem)
1373 		return;
1374 
1375 	BMenu *menu = submenuItem->Submenu();
1376 	if (!menu)
1377 		return;
1378 
1379 	for (;;) {
1380 		BMenuItem *item = menu->RemoveItem((int32)0);
1381 		if (!item)
1382 			return;
1383 
1384 		delete item;
1385 	}
1386 }
1387 
1388 
1389 status_t
1390 GetAppSignatureFromAttr(BFile *file, char *result)
1391 {
1392 	// This call is a performance improvement that
1393 	// avoids using the BAppFileInfo API when retrieving the
1394 	// app signature -- the call is expensive because by default
1395 	// the resource fork is scanned to read the attribute
1396 
1397 #ifdef B_APP_FILE_INFO_IS_FAST
1398 	BAppFileInfo appFileInfo(file);
1399 	return appFileInfo.GetSignature(result);
1400 #else
1401 	ssize_t readResult = file->ReadAttr(kAttrAppSignature, B_MIME_STRING_TYPE,
1402 		0, result, B_MIME_TYPE_LENGTH);
1403 
1404 	if (readResult <= 0)
1405 		return (status_t)readResult;
1406 
1407 	return B_OK;
1408 #endif	// B_APP_FILE_INFO_IS_FAST
1409 }
1410 
1411 
1412 status_t
1413 GetAppIconFromAttr(BFile *file, BBitmap *result, icon_size size)
1414 {
1415 	// This call is a performance improvement that
1416 	// avoids using the BAppFileInfo API when retrieving the
1417 	// app icons -- the call is expensive because by default
1418 	// the resource fork is scanned to read the icons
1419 
1420 //#ifdef B_APP_FILE_INFO_IS_FAST
1421 	BAppFileInfo appFileInfo(file);
1422 	return appFileInfo.GetIcon(result, size);
1423 //#else
1424 //
1425 //	const char *attrName = kAttrIcon;
1426 //	uint32 type = B_VECTOR_ICON_TYPE;
1427 //
1428 //	// try vector icon
1429 //	attr_info ainfo;
1430 //	status_t ret = file->GetAttrInfo(attrName, &ainfo);
1431 //
1432 //	if (ret == B_OK) {
1433 //		uint8 buffer[ainfo.size];
1434 //		ssize_t readResult = file->ReadAttr(attrName, type, 0, buffer,
1435 //											ainfo.size);
1436 //		if (readResult == ainfo.size) {
1437 //			if (BIconUtils::GetVectorIcon(buffer, ainfo.size, result) == B_OK)
1438 //				return B_OK;
1439 //		}
1440 //	}
1441 //
1442 //	// try again with R5 icons
1443 //	attrName = size == B_LARGE_ICON ? kAttrLargeIcon : kAttrMiniIcon;
1444 //	type = size == B_LARGE_ICON ? LARGE_ICON_TYPE : MINI_ICON_TYPE;
1445 //
1446 //	ret = file->GetAttrInfo(attrName, &ainfo);
1447 //	if (ret < B_OK)
1448 //		return ret;
1449 //
1450 //	uint8 buffer[ainfo.size];
1451 //
1452 //	ssize_t readResult = file->ReadAttr(attrName, type, 0, buffer, ainfo.size);
1453 //	if (readResult <= 0)
1454 //		return (status_t)readResult;
1455 //
1456 //	if (result->ColorSpace() != B_CMAP8) {
1457 //		ret = BIconUtils::ConvertFromCMAP8(buffer, size, size, size, result);
1458 //	} else {
1459 //		result->SetBits(buffer, result->BitsLength(), 0, B_CMAP8);
1460 //	}
1461 //
1462 //	return ret;
1463 //#endif	// B_APP_FILE_INFO_IS_FAST
1464 }
1465 
1466 
1467 status_t
1468 GetFileIconFromAttr(BNode *file, BBitmap *result, icon_size size)
1469 {
1470 	BNodeInfo fileInfo(file);
1471 	return fileInfo.GetIcon(result, size);
1472 }
1473 
1474 
1475 void
1476 PrintToStream(rgb_color color)
1477 {
1478 	printf("r:%x, g:%x, b:%x, a:%x\n",
1479 		color.red, color.green, color.blue, color.alpha);
1480 }
1481 
1482 
1483 extern BMenuItem *
1484 EachMenuItem(BMenu *menu, bool recursive, BMenuItem *(*func)(BMenuItem *))
1485 {
1486 	int32 count = menu->CountItems();
1487 	for (int32 index = 0; index < count; index++) {
1488 		BMenuItem *item = menu->ItemAt(index);
1489 		BMenuItem *result = (func)(item);
1490 		if (result)
1491 			return result;
1492 
1493 		if (recursive) {
1494 			BMenu *submenu = menu->SubmenuAt(index);
1495 			if (submenu)
1496 				return EachMenuItem(submenu, true, func);
1497 		}
1498 	}
1499 
1500 	return NULL;
1501 }
1502 
1503 
1504 extern const BMenuItem *
1505 EachMenuItem(const BMenu *menu, bool recursive, BMenuItem *(*func)(const BMenuItem *))
1506 {
1507 	int32 count = menu->CountItems();
1508 	for (int32 index = 0; index < count; index++) {
1509 		BMenuItem *item = menu->ItemAt(index);
1510 		BMenuItem *result = (func)(item);
1511 		if (result)
1512 			return result;
1513 
1514 		if (recursive) {
1515 			BMenu *submenu = menu->SubmenuAt(index);
1516 			if (submenu)
1517 				return EachMenuItem(submenu, true, func);
1518 		}
1519 	}
1520 
1521 	return NULL;
1522 }
1523 
1524 
1525 PositionPassingMenuItem::PositionPassingMenuItem(const char *title,
1526 		BMessage *message, char shortcut, uint32 modifiers)
1527 	: BMenuItem(title, message, shortcut, modifiers)
1528 {
1529 }
1530 
1531 
1532 PositionPassingMenuItem::PositionPassingMenuItem(BMenu *menu,
1533 		BMessage *message)
1534 	: BMenuItem(menu, message)
1535 {
1536 }
1537 
1538 
1539 status_t
1540 PositionPassingMenuItem::Invoke(BMessage *message)
1541 {
1542 	if (!Menu())
1543 		return B_ERROR;
1544 
1545 	if (!IsEnabled())
1546 		return B_ERROR;
1547 
1548 	if (!message)
1549 		message = Message();
1550 
1551 	if (!message)
1552 		return B_BAD_VALUE;
1553 
1554 	BMessage clone(*message);
1555 	clone.AddInt32("index", Menu()->IndexOf(this));
1556 	clone.AddInt64("when", system_time());
1557 	clone.AddPointer("source", this);
1558 
1559 	// embed the invoke location of the menu so that we can create
1560 	// a new folder, etc. on the spot
1561 	BMenu *menu = Menu();
1562 
1563 	for (;;) {
1564 		if (!menu->Supermenu())
1565 			break;
1566 		menu = menu->Supermenu();
1567 	}
1568 
1569 	// use the window position only, if the item was invoked from the menu
1570 	// menu->Window() points to the window the item was invoked from
1571 	if (dynamic_cast<BContainerWindow *>(menu->Window()) == NULL) {
1572 		LooperAutoLocker lock(menu);
1573 		if (lock.IsLocked()) {
1574 			BPoint invokeOrigin(menu->Window()->Frame().LeftTop());
1575 			clone.AddPoint("be:invoke_origin", invokeOrigin);
1576 		}
1577 	}
1578 
1579 	return BInvoker::Invoke(&clone);
1580 }
1581 
1582 
1583 bool
1584 BootedInSafeMode()
1585 {
1586 	const char *safeMode = getenv("SAFEMODE");
1587 	return (safeMode && strcmp(safeMode, "yes") == 0);
1588 }
1589 
1590 
1591 void
1592 _ThrowOnError(status_t error, const char *DEBUG_ONLY(file), int32 DEBUG_ONLY(line))
1593 {
1594 	if (error != B_OK) {
1595 		PRINT(("failing %s at %s:%d\n", strerror(error), file, line));
1596 		throw error;
1597 	}
1598 }
1599 
1600 
1601 void
1602 _ThrowIfNotSize(ssize_t size, const char *DEBUG_ONLY(file), int32 DEBUG_ONLY(line))
1603 {
1604 	if (size < B_OK) {
1605 		PRINT(("failing %s at %s:%d\n", strerror(size), file, line));
1606 		throw (status_t)size;
1607 	}
1608 }
1609 
1610 
1611 void
1612 _ThrowOnError(status_t error, const char *DEBUG_ONLY(debugString),
1613 	const char *DEBUG_ONLY(file), int32 DEBUG_ONLY(line))
1614 {
1615 	if (error != B_OK) {
1616 		PRINT(("failing %s, %s at %s:%d\n", debugString, strerror(error), file, line));
1617 		throw error;
1618 	}
1619 }
1620 
1621 } // namespace BPrivate
1622