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