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