xref: /haiku/src/kits/tracker/Utilities.cpp (revision 1c09002cbee8e797a0f8bbfc5678dfadd39ee1a7)
1 /*
2 Open Tracker License
3 
4 Terms and Conditions
5 
6 Copyright (c) 1991-2000, Be Incorporated. All rights reserved.
7 
8 Permission is hereby granted, free of charge, to any person obtaining a copy of
9 this software and associated documentation files (the "Software"), to deal in
10 the Software without restriction, including without limitation the rights to
11 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
12 of the Software, and to permit persons to whom the Software is furnished to do
13 so, subject to the following conditions:
14 
15 The above copyright notice and this permission notice applies to all licensees
16 and shall be included in all copies or substantial portions of the Software.
17 
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY,
20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
22 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION
23 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 
25 Except as contained in this notice, the name of Be Incorporated shall not be
26 used in advertising or otherwise to promote the sale, use or other dealings in
27 this Software without prior written authorization from Be Incorporated.
28 
29 Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks
30 of Be Incorporated in the United States and other countries. Other brand product
31 names are registered trademarks or trademarks of their respective holders.
32 All rights reserved.
33 */
34 
35 #include "Attributes.h"
36 #include "MimeTypes.h"
37 #include "Model.h"
38 #include "PoseView.h"
39 #include "Utilities.h"
40 #include "ContainerWindow.h"
41 
42 #ifdef __HAIKU__
43 #	include <IconUtils.h>
44 #endif
45 
46 #include <Bitmap.h>
47 #include <Catalog.h>
48 #include <Debug.h>
49 #include <Directory.h>
50 #include <fs_attr.h>
51 #include <fs_info.h>
52 #include <MenuItem.h>
53 #include <OS.h>
54 #include <PopUpMenu.h>
55 #include <Region.h>
56 #include <StorageDefs.h>
57 #include <TextView.h>
58 #include <Volume.h>
59 #include <VolumeRoster.h>
60 #include <Window.h>
61 
62 #include <ctype.h>
63 #include <string.h>
64 #include <stdlib.h>
65 #include <time.h>
66 #include <stdarg.h>
67 
68 
69 #ifndef _IMPEXP_BE
70 #	define _IMPEXP_BE
71 #endif
72 extern _IMPEXP_BE const uint32	LARGE_ICON_TYPE;
73 extern _IMPEXP_BE const uint32	MINI_ICON_TYPE;
74 
75 
76 FILE *logFile = NULL;
77 
78 static const float kMinSeparatorStubX = 10;
79 static const float kStubToStringSlotX = 5;
80 
81 
82 namespace BPrivate {
83 
84 const float kExactMatchScore = INFINITY;
85 
86 const rgb_color	kBlack = {0, 0, 0, 255};
87 const rgb_color	kWhite = {255, 255, 255, 255};
88 
89 
90 bool gLocalizedNamePreferred;
91 
92 
93 uint32
94 HashString(const char *string, uint32 seed)
95 {
96 	char ch;
97 	uint32 result = seed;
98 
99 	while((ch = *string++) != 0) {
100 		result = (result << 7) ^ (result >> 24);
101 		result ^= ch;
102 	}
103 
104 	result ^= result << 12;
105 	return result;
106 }
107 
108 
109 uint32
110 AttrHashString(const char *string, uint32 type)
111 {
112 	char c;
113 	uint32 hash = 0;
114 
115 	while((c = *string++) != 0) {
116 		hash = (hash << 7) ^ (hash >> 24);
117 		hash ^= c;
118 	}
119 
120 	hash ^= hash << 12;
121 
122 	hash &= ~0xff;
123 	hash |= type;
124 
125 	return hash;
126 }
127 
128 
129 bool
130 ValidateStream(BMallocIO *stream, uint32 key, int32 version)
131 {
132 	uint32 testKey;
133 	int32 testVersion;
134 
135 	if (stream->Read(&testKey, sizeof(uint32)) <= 0
136 		|| stream->Read(&testVersion, sizeof(int32)) <=0)
137 		return false;
138 
139 	return testKey == key && testVersion == version;
140 }
141 
142 
143 void
144 DisallowFilenameKeys(BTextView *textView)
145 {
146 	textView->DisallowChar('/');
147 }
148 
149 
150 void
151 DisallowMetaKeys(BTextView *textView)
152 {
153 	textView->DisallowChar(B_TAB);
154 	textView->DisallowChar(B_ESCAPE);
155 	textView->DisallowChar(B_INSERT);
156 	textView->DisallowChar(B_DELETE);
157 	textView->DisallowChar(B_HOME);
158 	textView->DisallowChar(B_END);
159 	textView->DisallowChar(B_PAGE_UP);
160 	textView->DisallowChar(B_PAGE_DOWN);
161 	textView->DisallowChar(B_FUNCTION_KEY);
162 }
163 
164 
165 PeriodicUpdatePoses::PeriodicUpdatePoses()
166 	:	fPoseList(20, true)
167 {
168 	fLock = new Benaphore("PeriodicUpdatePoses");
169 }
170 
171 
172 PeriodicUpdatePoses::~PeriodicUpdatePoses()
173 {
174 	fLock->Lock();
175 	fPoseList.MakeEmpty();
176 	delete fLock;
177 }
178 
179 
180 void
181 PeriodicUpdatePoses::AddPose(BPose *pose, BPoseView *poseView,
182 	PeriodicUpdateCallback callback, void *cookie)
183 {
184 	periodic_pose *periodic = new periodic_pose;
185 	periodic->pose = pose;
186 	periodic->pose_view = poseView;
187 	periodic->callback = callback;
188 	periodic->cookie = cookie;
189 	fPoseList.AddItem(periodic);
190 }
191 
192 
193 bool
194 PeriodicUpdatePoses::RemovePose(BPose *pose, void **cookie)
195 {
196 	int32 count = fPoseList.CountItems();
197 	for (int32 index = 0; index < count; index++) {
198 		if (fPoseList.ItemAt(index)->pose == pose) {
199 			if (!fLock->Lock())
200 				return false;
201 
202 			periodic_pose *periodic = fPoseList.RemoveItemAt(index);
203 			if (cookie)
204 				*cookie = periodic->cookie;
205 			delete periodic;
206 			fLock->Unlock();
207 			return true;
208 		}
209 	}
210 
211 	return false;
212 }
213 
214 
215 void
216 PeriodicUpdatePoses::DoPeriodicUpdate(bool forceRedraw)
217 {
218 	if (!fLock->Lock())
219 		return;
220 
221 	int32 count = fPoseList.CountItems();
222 	for (int32 index = 0; index < count; index++) {
223 		periodic_pose *periodic = fPoseList.ItemAt(index);
224 		if (periodic->callback(periodic->pose, periodic->cookie)
225 			|| forceRedraw) {
226 			periodic->pose_view->LockLooper();
227 			periodic->pose_view->UpdateIcon(periodic->pose);
228 			periodic->pose_view->UnlockLooper();
229 		}
230 	}
231 
232 	fLock->Unlock();
233 }
234 
235 
236 PeriodicUpdatePoses gPeriodicUpdatePoses;
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 
479 void
480 FadeRGBA32Horizontal(uint32 *bits, int32 width, int32 height, int32 from, 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 
513 void
514 FadeRGBA32Vertical(uint32 *bits, int32 width, int32 height, int32 from, 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 }	// namespace BPrivate
545 
546 
547 // #pragma mark -
548 
549 
550 DraggableIcon::DraggableIcon(BRect rect, const char *name, const char *mimeType,
551 		icon_size size, const BMessage *message, BMessenger target,
552 		uint32 resizeMask, uint32 flags)
553 	: BView(rect, name, resizeMask, flags),
554 	fMessage(*message),
555 	fTarget(target)
556 {
557 	fBitmap = new BBitmap(Bounds(), kDefaultIconDepth);
558 	BMimeType mime(mimeType);
559 	status_t error = mime.GetIcon(fBitmap, size);
560 	ASSERT(mime.IsValid());
561 	if (error != B_OK) {
562 		PRINT(("failed to get icon for %s, %s\n", mimeType, strerror(error)));
563 		BMimeType mime(B_FILE_MIMETYPE);
564 		ASSERT(mime.IsInstalled());
565 		mime.GetIcon(fBitmap, size);
566 	}
567 }
568 
569 
570 DraggableIcon::~DraggableIcon()
571 {
572 	delete fBitmap;
573 }
574 
575 
576 void
577 DraggableIcon::SetTarget(BMessenger target)
578 {
579 	fTarget = target;
580 }
581 
582 
583 BRect
584 DraggableIcon::PreferredRect(BPoint offset, icon_size size)
585 {
586 	BRect result(0, 0, size - 1, size - 1);
587 	result.OffsetTo(offset);
588 	return result;
589 }
590 
591 
592 void
593 DraggableIcon::AttachedToWindow()
594 {
595 	BView *parent = Parent();
596 	if (parent) {
597 		SetViewColor(parent->ViewColor());
598 		SetLowColor(parent->LowColor());
599 	}
600 }
601 
602 
603 void
604 DraggableIcon::MouseDown(BPoint point)
605 {
606 	if (!DragStarted(&fMessage))
607 		return;
608 
609 	BRect rect(Bounds());
610 	BBitmap *dragBitmap = new BBitmap(rect, B_RGBA32, true);
611 	dragBitmap->Lock();
612 	BView *view = new BView(dragBitmap->Bounds(), "", B_FOLLOW_NONE, 0);
613 	dragBitmap->AddChild(view);
614 	view->SetOrigin(0, 0);
615 	BRect clipRect(view->Bounds());
616 	BRegion newClip;
617 	newClip.Set(clipRect);
618 	view->ConstrainClippingRegion(&newClip);
619 
620 	// Transparent draw magic
621 	view->SetHighColor(0, 0, 0, 0);
622 	view->FillRect(view->Bounds());
623 	view->SetDrawingMode(B_OP_ALPHA);
624 	view->SetHighColor(0, 0, 0, 128);
625 		// set the level of transparency by value
626 	view->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_COMPOSITE);
627 	view->DrawBitmap(fBitmap);
628 	view->Sync();
629 	dragBitmap->Unlock();
630 	DragMessage(&fMessage, dragBitmap, B_OP_ALPHA, point, fTarget.Target(0));
631 }
632 
633 
634 bool
635 DraggableIcon::DragStarted(BMessage *)
636 {
637 	return true;
638 }
639 
640 
641 void
642 DraggableIcon::Draw(BRect)
643 {
644 #ifdef __HAIKU__
645 	SetDrawingMode(B_OP_ALPHA);
646 	SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
647 #else
648 	SetDrawingMode(B_OP_OVER);
649 #endif
650 	DrawBitmap(fBitmap);
651 }
652 
653 
654 // #pragma mark -
655 
656 
657 FlickerFreeStringView::FlickerFreeStringView(BRect bounds, const char *name,
658 		const char *text, uint32 resizeFlags, uint32 flags)
659 	: BStringView(bounds, name, text, resizeFlags, flags),
660 	fBitmap(NULL),
661 	fOrigBitmap(NULL)
662 {
663 }
664 
665 
666 FlickerFreeStringView::FlickerFreeStringView(BRect bounds, const char *name,
667 		const char *text, BBitmap *inBitmap, uint32 resizeFlags, uint32 flags)
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 #ifdef HAIKU_TARGET_PLATFORM_HAIKU
719 			case B_ALIGN_HORIZONTAL_UNSET:
720 			case B_ALIGN_USE_FULL_WIDTH:
721 #endif
722 			{
723 				// If the first char has a negative left edge give it
724 				// some more room by shifting that much more to the right.
725 				font.GetEdges(Text(), 1, &eInfo);
726 				loc.x = bounds.left + (2 - eInfo.left);
727 				break;
728 			}
729 
730 			case B_ALIGN_CENTER:
731 			{
732 				float width = StringWidth(Text());
733 				float center = (bounds.right - bounds.left) / 2;
734 				loc.x = center - (width/2);
735 				break;
736 			}
737 
738 			case B_ALIGN_RIGHT:
739 			{
740 				float width = StringWidth(Text());
741 				loc.x = bounds.right - width - 2;
742 				break;
743 			}
744 		}
745 		loc.y = bounds.bottom - (1 + height.descent);
746 		offscreen->DrawString(Text(), loc);
747 	}
748 	offscreen->Sync();
749 	SetDrawingMode(B_OP_COPY);
750 	DrawBitmap(fBitmap->Bitmap());
751 	fBitmap->DoneUsing();
752 }
753 
754 
755 void
756 FlickerFreeStringView::AttachedToWindow()
757 {
758 	_inherited::AttachedToWindow();
759 	if (Parent()) {
760 		fViewColor = Parent()->ViewColor();
761 		fLowColor = Parent()->ViewColor();
762 	}
763 	SetViewColor(B_TRANSPARENT_32_BIT);
764 	SetLowColor(B_TRANSPARENT_32_BIT);
765 }
766 
767 
768 void
769 FlickerFreeStringView::SetViewColor(rgb_color color)
770 {
771 	if (fViewColor != color) {
772 		fViewColor = color;
773 		Invalidate();
774 	}
775 	_inherited::SetViewColor(B_TRANSPARENT_32_BIT);
776 }
777 
778 
779 void
780 FlickerFreeStringView::SetLowColor(rgb_color color)
781 {
782 	if (fLowColor != color) {
783 		fLowColor = color;
784 		Invalidate();
785 	}
786 	_inherited::SetLowColor(B_TRANSPARENT_32_BIT);
787 }
788 
789 
790 // #pragma mark -
791 
792 
793 TitledSeparatorItem::TitledSeparatorItem(const char *label)
794 	: BMenuItem(label, 0)
795 {
796 	_inherited::SetEnabled(false);
797 }
798 
799 
800 TitledSeparatorItem::~TitledSeparatorItem()
801 {
802 }
803 
804 
805 void
806 TitledSeparatorItem::SetEnabled(bool)
807 {
808 	// leave disabled
809 }
810 
811 
812 void
813 TitledSeparatorItem::GetContentSize(float *width, float *height)
814 {
815 	_inherited::GetContentSize(width, height);
816 }
817 
818 
819 inline rgb_color
820 ShiftMenuBackgroundColor(float by)
821 {
822 	return tint_color(ui_color(B_MENU_BACKGROUND_COLOR), by);
823 }
824 
825 
826 void
827 TitledSeparatorItem::Draw()
828 {
829 	BRect frame(Frame());
830 
831 	BMenu *parent = Menu();
832 	ASSERT(parent);
833 
834 	menu_info minfo;
835 	get_menu_info(&minfo);
836 
837 	if (minfo.separator > 0) {
838 		frame.left += 10;
839 		frame.right -= 10;
840 	} else {
841 		frame.left += 1;
842 		frame.right -= 1;
843 	}
844 
845 	float startX = frame.left;
846 	float endX = frame.right;
847 
848 	float maxStringWidth = endX - startX - (2 * kMinSeparatorStubX
849 		+ 2 * kStubToStringSlotX);
850 
851 	// ToDo:
852 	// handle case where maxStringWidth turns out negative here
853 
854 	BString truncatedLabel(Label());
855 	parent->TruncateString(&truncatedLabel, B_TRUNCATE_END, maxStringWidth);
856 
857 	maxStringWidth = parent->StringWidth(truncatedLabel.String());
858 
859 	// first calculate the length of the stub part of the
860 	// divider line, so we can use it for secondStartX
861 	float firstEndX = ((endX - startX) - maxStringWidth) / 2 - 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, 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 	: BMessageFilter(B_KEY_DOWN),
925 	fShortcutKey(shortcutKey),
926 	fShortcutModifier(shortcutModifier),
927 	fShortcutWhat(shortcutWhat),
928 	fTarget(target)
929 {
930 }
931 
932 
933 filter_result
934 ShortcutFilter::Filter(BMessage *message, BHandler **)
935 {
936 	if (message->what == B_KEY_DOWN) {
937 		uint32 modifiers;
938 		uint32 rawKeyChar = 0;
939 		uint8 byte = 0;
940 		int32 key = 0;
941 
942 		if (message->FindInt32("modifiers", (int32 *)&modifiers) != B_OK
943 			|| message->FindInt32("raw_char", (int32 *)&rawKeyChar) != B_OK
944 			|| message->FindInt8("byte", (int8 *)&byte) != B_OK
945 			|| message->FindInt32("key", &key) != B_OK)
946 			return B_DISPATCH_MESSAGE;
947 
948 		modifiers &= B_SHIFT_KEY | B_COMMAND_KEY | B_CONTROL_KEY
949 			| B_OPTION_KEY | B_MENU_KEY;
950 			// strip caps lock, etc.
951 
952 		if (modifiers == fShortcutModifier && rawKeyChar == fShortcutKey) {
953 			fTarget->Looper()->PostMessage(fShortcutWhat, fTarget);
954 			return B_SKIP_MESSAGE;
955 		}
956 	}
957 
958 	// let others deal with this
959 	return B_DISPATCH_MESSAGE;
960 }
961 
962 
963 // #pragma mark -
964 
965 
966 namespace BPrivate {
967 
968 void
969 EmbedUniqueVolumeInfo(BMessage *message, const BVolume *volume)
970 {
971 	BDirectory rootDirectory;
972 	time_t created;
973 	fs_info info;
974 
975 	if (volume->GetRootDirectory(&rootDirectory) == B_OK
976 		&& rootDirectory.GetCreationTime(&created) == B_OK
977 		&& fs_stat_dev(volume->Device(), &info) == 0) {
978 		message->AddInt32("creationDate", created);
979 		message->AddInt64("capacity", volume->Capacity());
980 		message->AddString("deviceName", info.device_name);
981 		message->AddString("volumeName", info.volume_name);
982 		message->AddString("fshName", info.fsh_name);
983 	}
984 }
985 
986 
987 status_t
988 MatchArchivedVolume(BVolume *result, const BMessage *message, int32 index)
989 {
990 	time_t created;
991 	off_t capacity;
992 
993 	if (message->FindInt32("creationDate", index, &created) != B_OK
994 		|| message->FindInt64("capacity", index, &capacity) != B_OK)
995 		return B_ERROR;
996 
997 	BVolumeRoster roster;
998 	BVolume volume;
999 	BString deviceName, volumeName, fshName;
1000 
1001 	if (message->FindString("deviceName", &deviceName) == B_OK
1002 		&& message->FindString("volumeName", &volumeName) == B_OK
1003 		&& message->FindString("fshName", &fshName) == B_OK) {
1004 		// New style volume identifiers: We have a couple of characteristics,
1005 		// and compute a score from them. The volume with the greatest score
1006 		// (if over a certain threshold) is the one we're looking for. We
1007 		// pick the first volume, in case there is more than one with the
1008 		// same score.
1009 		dev_t foundDevice = -1;
1010 		int foundScore = -1;
1011 		roster.Rewind();
1012 		while (roster.GetNextVolume(&volume) == B_OK) {
1013 			if (volume.IsPersistent() && volume.KnowsQuery()) {
1014 				// get creation time and fs_info
1015 				BDirectory root;
1016 				volume.GetRootDirectory(&root);
1017 				time_t cmpCreated;
1018 				fs_info info;
1019 				if (root.GetCreationTime(&cmpCreated) == B_OK
1020 					&& fs_stat_dev(volume.Device(), &info) == 0) {
1021 					// compute the score
1022 					int score = 0;
1023 
1024 					// creation time
1025 					if (created == cmpCreated)
1026 						score += 5;
1027 					// capacity
1028 					if (capacity == volume.Capacity())
1029 						score += 4;
1030 					// device name
1031 					if (deviceName == info.device_name)
1032 						score += 3;
1033 					// volume name
1034 					if (volumeName == info.volume_name)
1035 						score += 2;
1036 					// fsh name
1037 					if (fshName == info.fsh_name)
1038 						score += 1;
1039 
1040 					// check score
1041 					if (score >= 9 && score > foundScore) {
1042 						foundDevice = volume.Device();
1043 						foundScore = score;
1044 					}
1045 				}
1046 			}
1047 		}
1048 		if (foundDevice >= 0)
1049 			return result->SetTo(foundDevice);
1050 	} else {
1051 		// Old style volume identifiers: We have only creation time and
1052 		// capacity. Both must match.
1053 		roster.Rewind();
1054 		while (roster.GetNextVolume(&volume) == B_OK)
1055 			if (volume.IsPersistent() && volume.KnowsQuery()) {
1056 				BDirectory root;
1057 				volume.GetRootDirectory(&root);
1058 				time_t cmpCreated;
1059 				root.GetCreationTime(&cmpCreated);
1060 				if (created == cmpCreated && capacity == volume.Capacity()) {
1061 					*result = volume;
1062 					return B_OK;
1063 				}
1064 			}
1065 	}
1066 
1067 	return B_DEV_BAD_DRIVE_NUM;
1068 }
1069 
1070 
1071 void
1072 StringFromStream(BString *string, BMallocIO *stream, bool endianSwap)
1073 {
1074 	int32 length;
1075 	stream->Read(&length, sizeof(length));
1076 	if (endianSwap)
1077 		length = SwapInt32(length);
1078 
1079 	if (length < 0 || length > 10000) {
1080 		// TODO: should fail here
1081 		PRINT(("problems instatiating a string, length probably wrong %"
1082 			B_PRId32 "\n", length));
1083 		return;
1084 	}
1085 
1086 	char *buffer = string->LockBuffer(length + 1);
1087 	stream->Read(buffer, (size_t)length + 1);
1088 	string->UnlockBuffer(length);
1089 }
1090 
1091 
1092 void
1093 StringToStream(const BString *string, BMallocIO *stream)
1094 {
1095 	int32 length = string->Length();
1096 	stream->Write(&length, sizeof(int32));
1097 	stream->Write(string->String(), (size_t)string->Length() + 1);
1098 }
1099 
1100 
1101 int32
1102 ArchiveSize(const BString *string)
1103 {
1104 	return string->Length() + 1 + (ssize_t)sizeof(int32);
1105 }
1106 
1107 
1108 int32
1109 CountRefs(const BMessage *message)
1110 {
1111 	uint32 type;
1112 	int32 count;
1113 	message->GetInfo("refs", &type, &count);
1114 
1115 	return count;
1116 }
1117 
1118 
1119 static entry_ref *
1120 EachEntryRefCommon(BMessage *message, entry_ref *(*func)(entry_ref *, void *),
1121 	void *passThru, int32 maxCount)
1122 {
1123 	uint32 type;
1124 	int32 count;
1125 	message->GetInfo("refs", &type, &count);
1126 
1127 	if (maxCount >= 0 && count > maxCount)
1128 		count = maxCount;
1129 
1130 	for (int32 index = 0; index < count; index++) {
1131 		entry_ref ref;
1132 		message->FindRef("refs", index, &ref);
1133 		entry_ref *result = (func)(&ref, passThru);
1134 		if (result)
1135 			return result;
1136 	}
1137 
1138 	return NULL;
1139 }
1140 
1141 
1142 bool
1143 ContainsEntryRef(const BMessage *message, const entry_ref *ref)
1144 {
1145 	entry_ref match;
1146 	for (int32 index = 0; (message->FindRef("refs", index, &match) == B_OK); index++) {
1147 		if (*ref == match)
1148 			return true;
1149 	}
1150 
1151 	return false;
1152 }
1153 
1154 
1155 entry_ref *
1156 EachEntryRef(BMessage *message, entry_ref *(*func)(entry_ref *, void *),
1157 	void *passThru)
1158 {
1159 	return EachEntryRefCommon(message, func, passThru, -1);
1160 }
1161 
1162 typedef entry_ref *(*EachEntryIteratee)(entry_ref *, void *);
1163 
1164 const entry_ref *
1165 EachEntryRef(const BMessage *message, const entry_ref *(*func)(const entry_ref *, void *),
1166 	void *passThru)
1167 {
1168 	return EachEntryRefCommon(const_cast<BMessage *>(message),
1169 		(EachEntryIteratee)func, passThru, -1);
1170 }
1171 
1172 
1173 entry_ref *
1174 EachEntryRef(BMessage *message, entry_ref *(*func)(entry_ref *, void *),
1175 	void *passThru, int32 maxCount)
1176 {
1177 	return EachEntryRefCommon(message, func, passThru, maxCount);
1178 }
1179 
1180 
1181 const entry_ref *
1182 EachEntryRef(const BMessage *message, const entry_ref *(*func)(const entry_ref *, void *),
1183 	void *passThru, int32 maxCount)
1184 {
1185 	return EachEntryRefCommon(const_cast<BMessage *>(message),
1186 		(EachEntryIteratee)func, passThru, maxCount);
1187 }
1188 
1189 
1190 void
1191 TruncateLeaf(BString *string)
1192 {
1193 	for (int32 index = string->Length(); index >= 0; index--) {
1194 		if ((*string)[index] == '/') {
1195 			string->Truncate(index + 1);
1196 			return;
1197 		}
1198 	}
1199 }
1200 
1201 
1202 int64
1203 StringToScalar(const char *text)
1204 {
1205 	char *end;
1206 	int64 val;
1207 
1208 	char *buffer = new char [strlen(text) + 1];
1209 	strcpy(buffer, text);
1210 
1211 	if (strstr(buffer, "k") || strstr(buffer, "K")) {
1212 		val = strtoll(buffer, &end, 10);
1213 		val *= kKBSize;
1214 	} else if (strstr(buffer, "mb") || strstr(buffer, "MB")) {
1215 		val = strtoll(buffer, &end, 10);
1216 		val *= kMBSize;
1217 	} else if (strstr(buffer, "gb") || strstr(buffer, "GB")) {
1218 		val = strtoll(buffer, &end, 10);
1219 		val *= kGBSize;
1220 	} else if (strstr(buffer, "byte") || strstr(buffer, "BYTE")) {
1221 		val = strtoll(buffer, &end, 10);
1222 		val *= kGBSize;
1223 	} else {
1224 		// no suffix, try plain byte conversion
1225 		val = strtoll(buffer, &end, 10);
1226 	}
1227 
1228 	delete [] buffer;
1229 	return val;
1230 }
1231 
1232 #if B_BEOS_VERSION <= B_BEOS_VERSION_MAUI && !defined(__HAIKU__)
1233 
1234 bool
1235 operator==(const rgb_color &a, const rgb_color &b)
1236 {
1237 	return a.red == b.red
1238 		&& a.green == b.green
1239 		&& a.blue == b.blue
1240 		&& a.alpha == b.alpha;
1241 }
1242 
1243 
1244 bool
1245 operator!=(const rgb_color &a, const rgb_color &b)
1246 {
1247 	return !operator==(a, b);
1248 }
1249 
1250 #endif
1251 
1252 
1253 static BRect
1254 LineBounds(BPoint where, float length, bool vertical)
1255 {
1256 	BRect result;
1257 	result.SetLeftTop(where);
1258 	result.SetRightBottom(where + BPoint(2, 2));
1259 	if (vertical)
1260 		result.bottom = result.top + length;
1261 	else
1262 		result.right = result.left + length;
1263 
1264 	return result;
1265 }
1266 
1267 
1268 SeparatorLine::SeparatorLine(BPoint where, float length, bool vertical, const char *name)
1269 	: BView(LineBounds(where, length, vertical), name,
1270 		B_FOLLOW_LEFT | B_FOLLOW_TOP, B_WILL_DRAW)
1271 {
1272 	SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
1273 	SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR));
1274 }
1275 
1276 
1277 void
1278 SeparatorLine::Draw(BRect)
1279 {
1280 	BRect bounds(Bounds());
1281 	rgb_color hiliteColor = tint_color(ViewColor(), 1.5f);
1282 
1283 	bool vertical = (bounds.left > bounds.right - 3);
1284 	BeginLineArray(2);
1285 	if (vertical) {
1286 		AddLine(bounds.LeftTop(), bounds.LeftBottom(), hiliteColor);
1287 		AddLine(bounds.LeftTop() + BPoint(1, 0), bounds.LeftBottom() + BPoint(1, 0), kWhite);
1288 	} else {
1289 		AddLine(bounds.LeftTop(), bounds.RightTop(), hiliteColor);
1290 		AddLine(bounds.LeftTop() + BPoint(0, 1), bounds.RightTop() + BPoint(0, 1), kWhite);
1291 	}
1292 	EndLineArray();
1293 }
1294 
1295 
1296 void
1297 HexDump(const void *buf, int32 length)
1298 {
1299 	const int32 kBytesPerLine = 16;
1300 	int32 offset;
1301 	unsigned char *buffer = (unsigned char *)buf;
1302 
1303 	for (offset = 0; ; offset += kBytesPerLine, buffer += kBytesPerLine) {
1304 		int32 remain = length;
1305 		int32 index;
1306 
1307 		printf( "0x%06x: ", (int)offset);
1308 
1309 		for (index = 0; index < kBytesPerLine; index++) {
1310 			if (remain-- > 0)
1311 				printf("%02x%c", buffer[index], remain > 0 ? ',' : ' ');
1312 			else
1313 				printf("   ");
1314 		}
1315 
1316 		remain = length;
1317 		printf(" \'");
1318 		for (index = 0; index < kBytesPerLine; index++) {
1319 			if (remain-- > 0)
1320 				printf("%c", buffer[index] > ' ' ? buffer[index] : '.');
1321 			else
1322 				printf(" ");
1323 		}
1324 		printf("\'\n");
1325 
1326 		length -= kBytesPerLine;
1327 		if (length <= 0)
1328 			break;
1329 	}
1330 	fflush(stdout);
1331 }
1332 
1333 
1334 void
1335 EnableNamedMenuItem(BMenu *menu, const char *itemName, bool on)
1336 {
1337 	BMenuItem *item = menu->FindItem(itemName);
1338 	if (item)
1339 		item->SetEnabled(on);
1340 }
1341 
1342 
1343 void
1344 MarkNamedMenuItem(BMenu *menu, const char *itemName, bool on)
1345 {
1346 	BMenuItem *item = menu->FindItem(itemName);
1347 	if (item)
1348 		item->SetMarked(on);
1349 }
1350 
1351 
1352 void
1353 EnableNamedMenuItem(BMenu *menu, uint32 commandName, bool on)
1354 {
1355 	BMenuItem *item = menu->FindItem(commandName);
1356 	if (item)
1357 		item->SetEnabled(on);
1358 }
1359 
1360 
1361 void
1362 MarkNamedMenuItem(BMenu *menu, uint32 commandName, bool on)
1363 {
1364 	BMenuItem *item = menu->FindItem(commandName);
1365 	if (item)
1366 		item->SetMarked(on);
1367 }
1368 
1369 
1370 void
1371 DeleteSubmenu(BMenuItem *submenuItem)
1372 {
1373 	if (!submenuItem)
1374 		return;
1375 
1376 	BMenu *menu = submenuItem->Submenu();
1377 	if (!menu)
1378 		return;
1379 
1380 	for (;;) {
1381 		BMenuItem *item = menu->RemoveItem((int32)0);
1382 		if (!item)
1383 			return;
1384 
1385 		delete item;
1386 	}
1387 }
1388 
1389 
1390 status_t
1391 GetAppSignatureFromAttr(BFile *file, char *result)
1392 {
1393 	// This call is a performance improvement that
1394 	// avoids using the BAppFileInfo API when retrieving the
1395 	// app signature -- the call is expensive because by default
1396 	// the resource fork is scanned to read the attribute
1397 
1398 #ifdef B_APP_FILE_INFO_IS_FAST
1399 	BAppFileInfo appFileInfo(file);
1400 	return appFileInfo.GetSignature(result);
1401 #else
1402 	ssize_t readResult = file->ReadAttr(kAttrAppSignature, B_MIME_STRING_TYPE,
1403 		0, result, B_MIME_TYPE_LENGTH);
1404 
1405 	if (readResult <= 0)
1406 		return (status_t)readResult;
1407 
1408 	return B_OK;
1409 #endif	// B_APP_FILE_INFO_IS_FAST
1410 }
1411 
1412 
1413 status_t
1414 GetAppIconFromAttr(BFile *file, BBitmap *result, icon_size size)
1415 {
1416 	// This call is a performance improvement that
1417 	// avoids using the BAppFileInfo API when retrieving the
1418 	// app icons -- the call is expensive because by default
1419 	// the resource fork is scanned to read the icons
1420 
1421 //#ifdef B_APP_FILE_INFO_IS_FAST
1422 	BAppFileInfo appFileInfo(file);
1423 	return appFileInfo.GetIcon(result, size);
1424 //#else
1425 //
1426 //	const char *attrName = kAttrIcon;
1427 //	uint32 type = B_VECTOR_ICON_TYPE;
1428 //
1429 //	// try vector icon
1430 //	attr_info ainfo;
1431 //	status_t ret = file->GetAttrInfo(attrName, &ainfo);
1432 //
1433 //	if (ret == B_OK) {
1434 //		uint8 buffer[ainfo.size];
1435 //		ssize_t readResult = file->ReadAttr(attrName, type, 0, buffer,
1436 //											ainfo.size);
1437 //		if (readResult == ainfo.size) {
1438 //			if (BIconUtils::GetVectorIcon(buffer, ainfo.size, result) == B_OK)
1439 //				return B_OK;
1440 //		}
1441 //	}
1442 //
1443 //	// try again with R5 icons
1444 //	attrName = size == B_LARGE_ICON ? kAttrLargeIcon : kAttrMiniIcon;
1445 //	type = size == B_LARGE_ICON ? LARGE_ICON_TYPE : MINI_ICON_TYPE;
1446 //
1447 //	ret = file->GetAttrInfo(attrName, &ainfo);
1448 //	if (ret < B_OK)
1449 //		return ret;
1450 //
1451 //	uint8 buffer[ainfo.size];
1452 //
1453 //	ssize_t readResult = file->ReadAttr(attrName, type, 0, buffer, ainfo.size);
1454 //	if (readResult <= 0)
1455 //		return (status_t)readResult;
1456 //
1457 //	if (result->ColorSpace() != B_CMAP8) {
1458 //		ret = BIconUtils::ConvertFromCMAP8(buffer, size, size, size, result);
1459 //	} else {
1460 //		result->SetBits(buffer, result->BitsLength(), 0, B_CMAP8);
1461 //	}
1462 //
1463 //	return ret;
1464 //#endif	// B_APP_FILE_INFO_IS_FAST
1465 }
1466 
1467 
1468 status_t
1469 GetFileIconFromAttr(BNode *file, BBitmap *result, icon_size size)
1470 {
1471 	BNodeInfo fileInfo(file);
1472 	return fileInfo.GetIcon(result, size);
1473 }
1474 
1475 
1476 void
1477 PrintToStream(rgb_color color)
1478 {
1479 	printf("r:%x, g:%x, b:%x, a:%x\n",
1480 		color.red, color.green, color.blue, color.alpha);
1481 }
1482 
1483 
1484 extern BMenuItem *
1485 EachMenuItem(BMenu *menu, bool recursive, BMenuItem *(*func)(BMenuItem *))
1486 {
1487 	int32 count = menu->CountItems();
1488 	for (int32 index = 0; index < count; index++) {
1489 		BMenuItem *item = menu->ItemAt(index);
1490 		BMenuItem *result = (func)(item);
1491 		if (result)
1492 			return result;
1493 
1494 		if (recursive) {
1495 			BMenu *submenu = menu->SubmenuAt(index);
1496 			if (submenu)
1497 				return EachMenuItem(submenu, true, func);
1498 		}
1499 	}
1500 
1501 	return NULL;
1502 }
1503 
1504 
1505 extern const BMenuItem *
1506 EachMenuItem(const BMenu *menu, bool recursive, BMenuItem *(*func)(const BMenuItem *))
1507 {
1508 	int32 count = menu->CountItems();
1509 	for (int32 index = 0; index < count; index++) {
1510 		BMenuItem *item = menu->ItemAt(index);
1511 		BMenuItem *result = (func)(item);
1512 		if (result)
1513 			return result;
1514 
1515 		if (recursive) {
1516 			BMenu *submenu = menu->SubmenuAt(index);
1517 			if (submenu)
1518 				return EachMenuItem(submenu, true, func);
1519 		}
1520 	}
1521 
1522 	return NULL;
1523 }
1524 
1525 
1526 PositionPassingMenuItem::PositionPassingMenuItem(const char *title,
1527 		BMessage *message, char shortcut, uint32 modifiers)
1528 	: BMenuItem(title, message, shortcut, modifiers)
1529 {
1530 }
1531 
1532 
1533 PositionPassingMenuItem::PositionPassingMenuItem(BMenu *menu,
1534 		BMessage *message)
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), int32 DEBUG_ONLY(line))
1643 {
1644 	if (error != B_OK) {
1645 		PRINT(("failing %s at %s:%d\n", strerror(error), file, (int)line));
1646 		throw error;
1647 	}
1648 }
1649 
1650 
1651 void
1652 _ThrowIfNotSize(ssize_t size, const char *DEBUG_ONLY(file), int32 DEBUG_ONLY(line))
1653 {
1654 	if (size < B_OK) {
1655 		PRINT(("failing %s at %s:%d\n", strerror(size), file, (int)line));
1656 		throw (status_t)size;
1657 	}
1658 }
1659 
1660 
1661 void
1662 _ThrowOnError(status_t error, const char *DEBUG_ONLY(debugString),
1663 	const char *DEBUG_ONLY(file), int32 DEBUG_ONLY(line))
1664 {
1665 	if (error != B_OK) {
1666 		PRINT(("failing %s, %s at %s:%d\n", debugString, strerror(error), file,
1667 			(int)line));
1668 		throw error;
1669 	}
1670 }
1671 
1672 } // namespace BPrivate
1673