xref: /haiku/src/kits/tracker/Utilities.cpp (revision 8d2bf6953e851d431fc67de1bc970c40afa79e9f)
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 uint32
90 HashString(const char* string, uint32 seed)
91 {
92 	char ch;
93 	uint32 result = seed;
94 
95 	while((ch = *string++) != 0) {
96 		result = (result << 7) ^ (result >> 24);
97 		result ^= ch;
98 	}
99 
100 	result ^= result << 12;
101 	return result;
102 }
103 
104 
105 uint32
106 AttrHashString(const char* string, uint32 type)
107 {
108 	char c;
109 	uint32 hash = 0;
110 
111 	while((c = *string++) != 0) {
112 		hash = (hash << 7) ^ (hash >> 24);
113 		hash ^= c;
114 	}
115 
116 	hash ^= hash << 12;
117 
118 	hash &= ~0xff;
119 	hash |= type;
120 
121 	return hash;
122 }
123 
124 
125 bool
126 ValidateStream(BMallocIO* stream, uint32 key, int32 version)
127 {
128 	uint32 testKey;
129 	int32 testVersion;
130 
131 	if (stream->Read(&testKey, sizeof(uint32)) <= 0
132 		|| stream->Read(&testVersion, sizeof(int32)) <=0)
133 		return false;
134 
135 	return testKey == key && testVersion == version;
136 }
137 
138 
139 void
140 DisallowFilenameKeys(BTextView* textView)
141 {
142 	textView->DisallowChar('/');
143 }
144 
145 
146 void
147 DisallowMetaKeys(BTextView* textView)
148 {
149 	textView->DisallowChar(B_TAB);
150 	textView->DisallowChar(B_ESCAPE);
151 	textView->DisallowChar(B_INSERT);
152 	textView->DisallowChar(B_DELETE);
153 	textView->DisallowChar(B_HOME);
154 	textView->DisallowChar(B_END);
155 	textView->DisallowChar(B_PAGE_UP);
156 	textView->DisallowChar(B_PAGE_DOWN);
157 	textView->DisallowChar(B_FUNCTION_KEY);
158 }
159 
160 
161 PeriodicUpdatePoses::PeriodicUpdatePoses()
162 	:
163 	fPoseList(20, true)
164 {
165 	fLock = new Benaphore("PeriodicUpdatePoses");
166 }
167 
168 
169 PeriodicUpdatePoses::~PeriodicUpdatePoses()
170 {
171 	fLock->Lock();
172 	fPoseList.MakeEmpty();
173 	delete fLock;
174 }
175 
176 
177 void
178 PeriodicUpdatePoses::AddPose(BPose* pose, BPoseView* poseView,
179 	PeriodicUpdateCallback callback, void* cookie)
180 {
181 	periodic_pose* periodic = new periodic_pose;
182 	periodic->pose = pose;
183 	periodic->pose_view = poseView;
184 	periodic->callback = callback;
185 	periodic->cookie = cookie;
186 	fPoseList.AddItem(periodic);
187 }
188 
189 
190 bool
191 PeriodicUpdatePoses::RemovePose(BPose* pose, void** cookie)
192 {
193 	int32 count = fPoseList.CountItems();
194 	for (int32 index = 0; index < count; index++) {
195 		if (fPoseList.ItemAt(index)->pose == pose) {
196 			if (!fLock->Lock())
197 				return false;
198 
199 			periodic_pose* periodic = fPoseList.RemoveItemAt(index);
200 			if (cookie)
201 				*cookie = periodic->cookie;
202 			delete periodic;
203 			fLock->Unlock();
204 			return true;
205 		}
206 	}
207 
208 	return false;
209 }
210 
211 
212 void
213 PeriodicUpdatePoses::DoPeriodicUpdate(bool forceRedraw)
214 {
215 	if (!fLock->Lock())
216 		return;
217 
218 	int32 count = fPoseList.CountItems();
219 	for (int32 index = 0; index < count; index++) {
220 		periodic_pose* periodic = fPoseList.ItemAt(index);
221 		if (periodic->callback(periodic->pose, periodic->cookie)
222 			|| forceRedraw) {
223 			periodic->pose_view->LockLooper();
224 			periodic->pose_view->UpdateIcon(periodic->pose);
225 			periodic->pose_view->UnlockLooper();
226 		}
227 	}
228 
229 	fLock->Unlock();
230 }
231 
232 
233 PeriodicUpdatePoses gPeriodicUpdatePoses;
234 
235 
236 }	// namespace BPrivate
237 
238 
239 void
240 PoseInfo::EndianSwap(void* castToThis)
241 {
242 	PoseInfo* self = (PoseInfo*)castToThis;
243 
244 	PRINT(("swapping PoseInfo\n"));
245 
246 	STATIC_ASSERT(sizeof(ino_t) == sizeof(int64));
247 	self->fInitedDirectory = SwapInt64(self->fInitedDirectory);
248 	swap_data(B_POINT_TYPE, &self->fLocation, sizeof(BPoint), B_SWAP_ALWAYS);
249 
250 	// do a sanity check on the icon position
251 	if (self->fLocation.x < -20000 || self->fLocation.x > 20000
252 		|| self->fLocation.y < -20000 || self->fLocation.y > 20000) {
253 		// position out of range, force autoplcemement
254 		PRINT((" rejecting icon position out of range\n"));
255 		self->fInitedDirectory = -1LL;
256 		self->fLocation = BPoint(0, 0);
257 	}
258 }
259 
260 
261 void
262 PoseInfo::PrintToStream()
263 {
264 	PRINT(("%s, inode:%" B_PRIx64 ", location %f %f\n",
265 		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)
291 		* 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 void
478 FadeRGBA32Horizontal(uint32* bits, int32 width, int32 height, int32 from,
479 	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 void
512 FadeRGBA32Vertical(uint32* bits, int32 width, int32 height, int32 from,
513 	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 
544 }	// namespace BPrivate
545 
546 
547 // #pragma mark -
548 
549 
550 DraggableIcon::DraggableIcon(BRect rect, const char* name,
551 		const char* mimeType, icon_size size, const BMessage* message,
552 		BMessenger target, uint32 resizeMask, uint32 flags)
553 	:
554 	BView(rect, name, resizeMask, flags),
555 	fMessage(*message),
556 	fTarget(target)
557 {
558 	fBitmap = new BBitmap(Bounds(), kDefaultIconDepth);
559 	BMimeType mime(mimeType);
560 	status_t error = mime.GetIcon(fBitmap, size);
561 	ASSERT(mime.IsValid());
562 	if (error != B_OK) {
563 		PRINT(("failed to get icon for %s, %s\n", mimeType, strerror(error)));
564 		BMimeType mime(B_FILE_MIMETYPE);
565 		ASSERT(mime.IsInstalled());
566 		mime.GetIcon(fBitmap, size);
567 	}
568 }
569 
570 
571 DraggableIcon::~DraggableIcon()
572 {
573 	delete fBitmap;
574 }
575 
576 
577 void
578 DraggableIcon::SetTarget(BMessenger target)
579 {
580 	fTarget = target;
581 }
582 
583 
584 BRect
585 DraggableIcon::PreferredRect(BPoint offset, icon_size size)
586 {
587 	BRect result(0, 0, size - 1, size - 1);
588 	result.OffsetTo(offset);
589 	return result;
590 }
591 
592 
593 void
594 DraggableIcon::AttachedToWindow()
595 {
596 	BView* parent = Parent();
597 	if (parent != NULL) {
598 		SetViewColor(parent->ViewColor());
599 		SetLowColor(parent->LowColor());
600 	}
601 }
602 
603 
604 void
605 DraggableIcon::MouseDown(BPoint point)
606 {
607 	if (!DragStarted(&fMessage))
608 		return;
609 
610 	BRect rect(Bounds());
611 	BBitmap* dragBitmap = new BBitmap(rect, B_RGBA32, true);
612 	dragBitmap->Lock();
613 	BView* view = new BView(dragBitmap->Bounds(), "", B_FOLLOW_NONE, 0);
614 	dragBitmap->AddChild(view);
615 	view->SetOrigin(0, 0);
616 	BRect clipRect(view->Bounds());
617 	BRegion newClip;
618 	newClip.Set(clipRect);
619 	view->ConstrainClippingRegion(&newClip);
620 
621 	// Transparent draw magic
622 	view->SetHighColor(0, 0, 0, 0);
623 	view->FillRect(view->Bounds());
624 	view->SetDrawingMode(B_OP_ALPHA);
625 	view->SetHighColor(0, 0, 0, 128);
626 		// set the level of transparency by value
627 	view->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_COMPOSITE);
628 	view->DrawBitmap(fBitmap);
629 	view->Sync();
630 	dragBitmap->Unlock();
631 	DragMessage(&fMessage, dragBitmap, B_OP_ALPHA, point, fTarget.Target(0));
632 }
633 
634 
635 bool
636 DraggableIcon::DragStarted(BMessage*)
637 {
638 	return true;
639 }
640 
641 
642 void
643 DraggableIcon::Draw(BRect)
644 {
645 	SetDrawingMode(B_OP_ALPHA);
646 	SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
647 	DrawBitmap(fBitmap);
648 }
649 
650 
651 // #pragma mark -
652 
653 
654 FlickerFreeStringView::FlickerFreeStringView(BRect bounds, const char* name,
655 		const char* text, uint32 resizeFlags, uint32 flags)
656 	:
657 	BStringView(bounds, name, text, resizeFlags, flags),
658 	fBitmap(NULL),
659 	fOrigBitmap(NULL)
660 {
661 }
662 
663 
664 FlickerFreeStringView::FlickerFreeStringView(BRect bounds, const char* name,
665 		const char* text, BBitmap* inBitmap, uint32 resizeFlags, uint32 flags)
666 	:
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 			case B_ALIGN_HORIZONTAL_UNSET:
718 			case B_ALIGN_USE_FULL_WIDTH:
719 			{
720 				// If the first char has a negative left edge give it
721 				// some more room by shifting that much more to the right.
722 				font.GetEdges(Text(), 1, &eInfo);
723 				loc.x = bounds.left + (2 - eInfo.left);
724 				break;
725 			}
726 
727 			case B_ALIGN_CENTER:
728 			{
729 				float width = StringWidth(Text());
730 				float center = (bounds.right - bounds.left) / 2;
731 				loc.x = center - (width/2);
732 				break;
733 			}
734 
735 			case B_ALIGN_RIGHT:
736 			{
737 				float width = StringWidth(Text());
738 				loc.x = bounds.right - width - 2;
739 				break;
740 			}
741 		}
742 		loc.y = bounds.bottom - (1 + height.descent);
743 		offscreen->DrawString(Text(), loc);
744 	}
745 	offscreen->Sync();
746 	SetDrawingMode(B_OP_COPY);
747 	DrawBitmap(fBitmap->Bitmap());
748 	fBitmap->DoneUsing();
749 }
750 
751 
752 void
753 FlickerFreeStringView::AttachedToWindow()
754 {
755 	_inherited::AttachedToWindow();
756 	if (Parent()) {
757 		fViewColor = Parent()->ViewColor();
758 		fLowColor = Parent()->ViewColor();
759 	}
760 	SetViewColor(B_TRANSPARENT_32_BIT);
761 	SetLowColor(B_TRANSPARENT_32_BIT);
762 }
763 
764 
765 void
766 FlickerFreeStringView::SetViewColor(rgb_color color)
767 {
768 	if (fViewColor != color) {
769 		fViewColor = color;
770 		Invalidate();
771 	}
772 	_inherited::SetViewColor(B_TRANSPARENT_32_BIT);
773 }
774 
775 
776 void
777 FlickerFreeStringView::SetLowColor(rgb_color color)
778 {
779 	if (fLowColor != color) {
780 		fLowColor = color;
781 		Invalidate();
782 	}
783 	_inherited::SetLowColor(B_TRANSPARENT_32_BIT);
784 }
785 
786 
787 // #pragma mark -
788 
789 
790 TitledSeparatorItem::TitledSeparatorItem(const char* label)
791 	:
792 	BMenuItem(label, 0)
793 {
794 	_inherited::SetEnabled(false);
795 }
796 
797 
798 TitledSeparatorItem::~TitledSeparatorItem()
799 {
800 }
801 
802 
803 void
804 TitledSeparatorItem::SetEnabled(bool)
805 {
806 	// leave disabled
807 }
808 
809 
810 void
811 TitledSeparatorItem::GetContentSize(float* width, float* height)
812 {
813 	_inherited::GetContentSize(width, height);
814 }
815 
816 
817 inline rgb_color
818 ShiftMenuBackgroundColor(float by)
819 {
820 	return tint_color(ui_color(B_MENU_BACKGROUND_COLOR), by);
821 }
822 
823 
824 void
825 TitledSeparatorItem::Draw()
826 {
827 	BRect frame(Frame());
828 
829 	BMenu* parent = Menu();
830 	ASSERT(parent);
831 
832 	menu_info minfo;
833 	get_menu_info(&minfo);
834 
835 	if (minfo.separator > 0) {
836 		frame.left += 10;
837 		frame.right -= 10;
838 	} else {
839 		frame.left += 1;
840 		frame.right -= 1;
841 	}
842 
843 	float startX = frame.left;
844 	float endX = frame.right;
845 
846 	float maxStringWidth = endX - startX - (2 * kMinSeparatorStubX
847 		+ 2 * kStubToStringSlotX);
848 
849 	// ToDo:
850 	// handle case where maxStringWidth turns out negative here
851 
852 	BString truncatedLabel(Label());
853 	parent->TruncateString(&truncatedLabel, B_TRUNCATE_END, maxStringWidth);
854 
855 	maxStringWidth = parent->StringWidth(truncatedLabel.String());
856 
857 	// first calculate the length of the stub part of the
858 	// divider line, so we can use it for secondStartX
859 	float firstEndX = ((endX - startX) - maxStringWidth) / 2
860 		- 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,
905 		ContentLocation().y + finfo.ascent);
906 
907 	parent->MovePenTo(loc + BPoint(1, 1));
908 	parent->SetHighColor(ShiftMenuBackgroundColor(B_DARKEN_1_TINT));
909 	parent->DrawString(truncatedLabel.String());
910 
911 	parent->MovePenTo(loc);
912 	parent->SetHighColor(ShiftMenuBackgroundColor(B_DISABLED_LABEL_TINT));
913 	parent->DrawString(truncatedLabel.String());
914 
915 	parent->PopState();
916 }
917 
918 
919 // #pragma mark -
920 
921 
922 ShortcutFilter::ShortcutFilter(uint32 shortcutKey, uint32 shortcutModifier,
923 		uint32 shortcutWhat, BHandler* target)
924 	:
925 	BMessageFilter(B_KEY_DOWN),
926 	fShortcutKey(shortcutKey),
927 	fShortcutModifier(shortcutModifier),
928 	fShortcutWhat(shortcutWhat),
929 	fTarget(target)
930 {
931 }
932 
933 
934 filter_result
935 ShortcutFilter::Filter(BMessage* message, BHandler**)
936 {
937 	if (message->what == B_KEY_DOWN) {
938 		uint32 modifiers;
939 		uint32 rawKeyChar = 0;
940 		uint8 byte = 0;
941 		int32 key = 0;
942 
943 		if (message->FindInt32("modifiers", (int32*)&modifiers) != B_OK
944 			|| message->FindInt32("raw_char", (int32*)&rawKeyChar) != B_OK
945 			|| message->FindInt8("byte", (int8*)&byte) != B_OK
946 			|| message->FindInt32("key", &key) != B_OK)
947 			return B_DISPATCH_MESSAGE;
948 
949 		modifiers &= B_SHIFT_KEY | B_COMMAND_KEY | B_CONTROL_KEY
950 			| B_OPTION_KEY | B_MENU_KEY;
951 			// strip caps lock, etc.
952 
953 		if (modifiers == fShortcutModifier && rawKeyChar == fShortcutKey) {
954 			fTarget->Looper()->PostMessage(fShortcutWhat, fTarget);
955 			return B_SKIP_MESSAGE;
956 		}
957 	}
958 
959 	// let others deal with this
960 	return B_DISPATCH_MESSAGE;
961 }
962 
963 
964 // #pragma mark -
965 
966 
967 namespace BPrivate {
968 
969 
970 void
971 EmbedUniqueVolumeInfo(BMessage* message, const BVolume* volume)
972 {
973 	BDirectory rootDirectory;
974 	time_t created;
975 	fs_info info;
976 
977 	if (volume->GetRootDirectory(&rootDirectory) == B_OK
978 		&& rootDirectory.GetCreationTime(&created) == B_OK
979 		&& fs_stat_dev(volume->Device(), &info) == 0) {
980 		message->AddInt32("creationDate", created);
981 		message->AddInt64("capacity", volume->Capacity());
982 		message->AddString("deviceName", info.device_name);
983 		message->AddString("volumeName", info.volume_name);
984 		message->AddString("fshName", info.fsh_name);
985 	}
986 }
987 
988 
989 status_t
990 MatchArchivedVolume(BVolume* result, const BMessage* message, int32 index)
991 {
992 	time_t created;
993 	off_t capacity;
994 
995 	if (message->FindInt32("creationDate", index, &created) != B_OK
996 		|| message->FindInt64("capacity", index, &capacity) != B_OK)
997 		return B_ERROR;
998 
999 	BVolumeRoster roster;
1000 	BVolume volume;
1001 	BString deviceName, volumeName, fshName;
1002 
1003 	if (message->FindString("deviceName", &deviceName) == B_OK
1004 		&& message->FindString("volumeName", &volumeName) == B_OK
1005 		&& message->FindString("fshName", &fshName) == B_OK) {
1006 		// New style volume identifiers: We have a couple of characteristics,
1007 		// and compute a score from them. The volume with the greatest score
1008 		// (if over a certain threshold) is the one we're looking for. We
1009 		// pick the first volume, in case there is more than one with the
1010 		// same score.
1011 		dev_t foundDevice = -1;
1012 		int foundScore = -1;
1013 		roster.Rewind();
1014 		while (roster.GetNextVolume(&volume) == B_OK) {
1015 			if (volume.IsPersistent() && volume.KnowsQuery()) {
1016 				// get creation time and fs_info
1017 				BDirectory root;
1018 				volume.GetRootDirectory(&root);
1019 				time_t cmpCreated;
1020 				fs_info info;
1021 				if (root.GetCreationTime(&cmpCreated) == B_OK
1022 					&& fs_stat_dev(volume.Device(), &info) == 0) {
1023 					// compute the score
1024 					int score = 0;
1025 
1026 					// creation time
1027 					if (created == cmpCreated)
1028 						score += 5;
1029 					// capacity
1030 					if (capacity == volume.Capacity())
1031 						score += 4;
1032 					// device name
1033 					if (deviceName == info.device_name)
1034 						score += 3;
1035 					// volume name
1036 					if (volumeName == info.volume_name)
1037 						score += 2;
1038 					// fsh name
1039 					if (fshName == info.fsh_name)
1040 						score += 1;
1041 
1042 					// check score
1043 					if (score >= 9 && score > foundScore) {
1044 						foundDevice = volume.Device();
1045 						foundScore = score;
1046 					}
1047 				}
1048 			}
1049 		}
1050 		if (foundDevice >= 0)
1051 			return result->SetTo(foundDevice);
1052 	} else {
1053 		// Old style volume identifiers: We have only creation time and
1054 		// capacity. Both must match.
1055 		roster.Rewind();
1056 		while (roster.GetNextVolume(&volume) == B_OK)
1057 			if (volume.IsPersistent() && volume.KnowsQuery()) {
1058 				BDirectory root;
1059 				volume.GetRootDirectory(&root);
1060 				time_t cmpCreated;
1061 				root.GetCreationTime(&cmpCreated);
1062 				if (created == cmpCreated && capacity == volume.Capacity()) {
1063 					*result = volume;
1064 					return B_OK;
1065 				}
1066 			}
1067 	}
1068 
1069 	return B_DEV_BAD_DRIVE_NUM;
1070 }
1071 
1072 
1073 void
1074 StringFromStream(BString* string, BMallocIO* stream, bool endianSwap)
1075 {
1076 	int32 length;
1077 	stream->Read(&length, sizeof(length));
1078 	if (endianSwap)
1079 		length = SwapInt32(length);
1080 
1081 	if (length < 0 || length > 10000) {
1082 		// TODO: should fail here
1083 		PRINT(("problems instatiating a string, length probably wrong %"
1084 			B_PRId32 "\n", length));
1085 		return;
1086 	}
1087 
1088 	char* buffer = string->LockBuffer(length + 1);
1089 	stream->Read(buffer, (size_t)length + 1);
1090 	string->UnlockBuffer(length);
1091 }
1092 
1093 
1094 void
1095 StringToStream(const BString* string, BMallocIO* stream)
1096 {
1097 	int32 length = string->Length();
1098 	stream->Write(&length, sizeof(int32));
1099 	stream->Write(string->String(), (size_t)string->Length() + 1);
1100 }
1101 
1102 
1103 int32
1104 ArchiveSize(const BString* string)
1105 {
1106 	return string->Length() + 1 + (ssize_t)sizeof(int32);
1107 }
1108 
1109 
1110 int32
1111 CountRefs(const BMessage* message)
1112 {
1113 	uint32 type;
1114 	int32 count;
1115 	message->GetInfo("refs", &type, &count);
1116 
1117 	return count;
1118 }
1119 
1120 
1121 static entry_ref*
1122 EachEntryRefCommon(BMessage* message, entry_ref *(*func)(entry_ref*, void*),
1123 	void* passThru, int32 maxCount)
1124 {
1125 	uint32 type;
1126 	int32 count;
1127 	message->GetInfo("refs", &type, &count);
1128 
1129 	if (maxCount >= 0 && count > maxCount)
1130 		count = maxCount;
1131 
1132 	for (int32 index = 0; index < count; index++) {
1133 		entry_ref ref;
1134 		message->FindRef("refs", index, &ref);
1135 		entry_ref* result = (func)(&ref, passThru);
1136 		if (result)
1137 			return result;
1138 	}
1139 
1140 	return NULL;
1141 }
1142 
1143 
1144 bool
1145 ContainsEntryRef(const BMessage* message, const entry_ref* ref)
1146 {
1147 	entry_ref match;
1148 	for (int32 index = 0; (message->FindRef("refs", index, &match) == B_OK);
1149 			index++) {
1150 		if (*ref == match)
1151 			return true;
1152 	}
1153 
1154 	return false;
1155 }
1156 
1157 
1158 entry_ref*
1159 EachEntryRef(BMessage* message, entry_ref* (*func)(entry_ref*, void*),
1160 	void* passThru)
1161 {
1162 	return EachEntryRefCommon(message, func, passThru, -1);
1163 }
1164 
1165 typedef entry_ref *(*EachEntryIteratee)(entry_ref *, void *);
1166 
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 } // namespace BPrivate
1666