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