xref: /haiku/src/kits/tracker/Utilities.cpp (revision a629567a9001547736cfe892cdf992be16868fed)
1 /*
2 Open Tracker License
3 
4 Terms and Conditions
5 
6 Copyright (c) 1991-2000, Be Incorporated. All rights reserved.
7 
8 Permission is hereby granted, free of charge, to any person obtaining a copy of
9 this software and associated documentation files (the "Software"), to deal in
10 the Software without restriction, including without limitation the rights to
11 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
12 of the Software, and to permit persons to whom the Software is furnished to do
13 so, subject to the following conditions:
14 
15 The above copyright notice and this permission notice applies to all licensees
16 and shall be included in all copies or substantial portions of the Software.
17 
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY,
20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
22 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION
23 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 
25 Except as contained in this notice, the name of Be Incorporated shall not be
26 used in advertising or otherwise to promote the sale, use or other dealings in
27 this Software without prior written authorization from Be Incorporated.
28 
29 Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks
30 of Be Incorporated in the United States and other countries. Other brand product
31 names are registered trademarks or trademarks of their respective holders.
32 All rights reserved.
33 */
34 
35 
36 #include "Attributes.h"
37 #include "MimeTypes.h"
38 #include "Model.h"
39 #include "PoseView.h"
40 #include "Utilities.h"
41 #include "ContainerWindow.h"
42 
43 #include <IconUtils.h>
44 
45 #include <Bitmap.h>
46 #include <Catalog.h>
47 #include <Debug.h>
48 #include <Directory.h>
49 #include <fs_attr.h>
50 #include <fs_info.h>
51 #include <MenuItem.h>
52 #include <OS.h>
53 #include <PopUpMenu.h>
54 #include <Region.h>
55 #include <StorageDefs.h>
56 #include <TextView.h>
57 #include <Volume.h>
58 #include <VolumeRoster.h>
59 #include <Window.h>
60 
61 #include <ctype.h>
62 #include <string.h>
63 #include <stdlib.h>
64 #include <time.h>
65 #include <stdarg.h>
66 
67 
68 #ifndef _IMPEXP_BE
69 #	define _IMPEXP_BE
70 #endif
71 extern _IMPEXP_BE const uint32	LARGE_ICON_TYPE;
72 extern _IMPEXP_BE const uint32	MINI_ICON_TYPE;
73 
74 
75 FILE* logFile = NULL;
76 
77 static const float kMinSeparatorStubX = 10;
78 static const float kStubToStringSlotX = 5;
79 
80 
81 namespace BPrivate {
82 
83 const float kExactMatchScore = INFINITY;
84 
85 
86 bool gLocalizedNamePreferred;
87 
88 
89 bool
90 SecondaryMouseButtonDown(int32 modifiers, int32 buttons)
91 {
92 	return (buttons & B_SECONDARY_MOUSE_BUTTON) != 0
93 		|| ((buttons & B_PRIMARY_MOUSE_BUTTON) != 0
94 			&& (modifiers & B_CONTROL_KEY) != 0);
95 }
96 
97 
98 uint32
99 HashString(const char* string, uint32 seed)
100 {
101 	char ch;
102 	uint32 result = seed;
103 
104 	while((ch = *string++) != 0) {
105 		result = (result << 7) ^ (result >> 24);
106 		result ^= ch;
107 	}
108 
109 	result ^= result << 12;
110 	return result;
111 }
112 
113 
114 uint32
115 AttrHashString(const char* string, uint32 type)
116 {
117 	char c;
118 	uint32 hash = 0;
119 
120 	while((c = *string++) != 0) {
121 		hash = (hash << 7) ^ (hash >> 24);
122 		hash ^= c;
123 	}
124 
125 	hash ^= hash << 12;
126 
127 	hash &= ~0xff;
128 	hash |= type;
129 
130 	return hash;
131 }
132 
133 
134 bool
135 ValidateStream(BMallocIO* stream, uint32 key, int32 version)
136 {
137 	uint32 testKey;
138 	int32 testVersion;
139 
140 	if (stream->Read(&testKey, sizeof(uint32)) <= 0
141 		|| stream->Read(&testVersion, sizeof(int32)) <=0)
142 		return false;
143 
144 	return testKey == key && testVersion == version;
145 }
146 
147 
148 void
149 DisallowFilenameKeys(BTextView* textView)
150 {
151 	textView->DisallowChar('/');
152 }
153 
154 
155 void
156 DisallowMetaKeys(BTextView* textView)
157 {
158 	textView->DisallowChar(B_TAB);
159 	textView->DisallowChar(B_ESCAPE);
160 	textView->DisallowChar(B_INSERT);
161 	textView->DisallowChar(B_DELETE);
162 	textView->DisallowChar(B_HOME);
163 	textView->DisallowChar(B_END);
164 	textView->DisallowChar(B_PAGE_UP);
165 	textView->DisallowChar(B_PAGE_DOWN);
166 	textView->DisallowChar(B_FUNCTION_KEY);
167 }
168 
169 
170 PeriodicUpdatePoses::PeriodicUpdatePoses()
171 	:
172 	fPoseList(20, true)
173 {
174 	fLock = new Benaphore("PeriodicUpdatePoses");
175 }
176 
177 
178 PeriodicUpdatePoses::~PeriodicUpdatePoses()
179 {
180 	fLock->Lock();
181 	fPoseList.MakeEmpty();
182 	delete fLock;
183 }
184 
185 
186 void
187 PeriodicUpdatePoses::AddPose(BPose* pose, BPoseView* poseView,
188 	PeriodicUpdateCallback callback, void* cookie)
189 {
190 	periodic_pose* periodic = new periodic_pose;
191 	periodic->pose = pose;
192 	periodic->pose_view = poseView;
193 	periodic->callback = callback;
194 	periodic->cookie = cookie;
195 	fPoseList.AddItem(periodic);
196 }
197 
198 
199 bool
200 PeriodicUpdatePoses::RemovePose(BPose* pose, void** cookie)
201 {
202 	int32 count = fPoseList.CountItems();
203 	for (int32 index = 0; index < count; index++) {
204 		if (fPoseList.ItemAt(index)->pose == pose) {
205 			if (!fLock->Lock())
206 				return false;
207 
208 			periodic_pose* periodic = fPoseList.RemoveItemAt(index);
209 			if (cookie)
210 				*cookie = periodic->cookie;
211 			delete periodic;
212 			fLock->Unlock();
213 			return true;
214 		}
215 	}
216 
217 	return false;
218 }
219 
220 
221 void
222 PeriodicUpdatePoses::DoPeriodicUpdate(bool forceRedraw)
223 {
224 	if (!fLock->Lock())
225 		return;
226 
227 	int32 count = fPoseList.CountItems();
228 	for (int32 index = 0; index < count; index++) {
229 		periodic_pose* periodic = fPoseList.ItemAt(index);
230 		if (periodic->callback(periodic->pose, periodic->cookie)
231 			|| forceRedraw) {
232 			periodic->pose_view->LockLooper();
233 			periodic->pose_view->UpdateIcon(periodic->pose);
234 			periodic->pose_view->UnlockLooper();
235 		}
236 	}
237 
238 	fLock->Unlock();
239 }
240 
241 
242 PeriodicUpdatePoses gPeriodicUpdatePoses;
243 
244 }	// namespace BPrivate
245 
246 
247 void
248 PoseInfo::EndianSwap(void* castToThis)
249 {
250 	PoseInfo* self = (PoseInfo*)castToThis;
251 
252 	PRINT(("swapping PoseInfo\n"));
253 
254 	STATIC_ASSERT(sizeof(ino_t) == sizeof(int64));
255 	self->fInitedDirectory = SwapInt64(self->fInitedDirectory);
256 	swap_data(B_POINT_TYPE, &self->fLocation, sizeof(BPoint), B_SWAP_ALWAYS);
257 
258 	// do a sanity check on the icon position
259 	if (self->fLocation.x < -20000 || self->fLocation.x > 20000
260 		|| self->fLocation.y < -20000 || self->fLocation.y > 20000) {
261 		// position out of range, force autoplcemement
262 		PRINT((" rejecting icon position out of range\n"));
263 		self->fInitedDirectory = -1LL;
264 		self->fLocation = BPoint(0, 0);
265 	}
266 }
267 
268 
269 void
270 PoseInfo::PrintToStream()
271 {
272 	PRINT(("%s, inode:%" B_PRIx64 ", location %f %f\n",
273 		fInvisible ? "hidden" : "visible",
274 		fInitedDirectory, fLocation.x, fLocation.y));
275 }
276 
277 
278 // #pragma mark - ExtendedPoseInfo
279 
280 
281 size_t
282 ExtendedPoseInfo::Size() const
283 {
284 	return sizeof(ExtendedPoseInfo) + fNumFrames * sizeof(FrameLocation);
285 }
286 
287 
288 size_t
289 ExtendedPoseInfo::Size(int32 count)
290 {
291 	return sizeof(ExtendedPoseInfo) + count * sizeof(FrameLocation);
292 }
293 
294 
295 size_t
296 ExtendedPoseInfo::SizeWithHeadroom() const
297 {
298 	return sizeof(ExtendedPoseInfo) + (fNumFrames + 1) * sizeof(FrameLocation);
299 }
300 
301 
302 size_t
303 ExtendedPoseInfo::SizeWithHeadroom(size_t oldSize)
304 {
305 	int32 count = (ssize_t)oldSize - (ssize_t)sizeof(ExtendedPoseInfo);
306 	if (count > 0)
307 		count /= sizeof(FrameLocation);
308 	else
309 		count = 0;
310 
311 	return Size(count + 1);
312 }
313 
314 
315 bool
316 ExtendedPoseInfo::HasLocationForFrame(BRect frame) const
317 {
318 	for (int32 index = 0; index < fNumFrames; index++) {
319 		if (fLocations[index].fFrame == frame)
320 			return true;
321 	}
322 
323 	return false;
324 }
325 
326 
327 BPoint
328 ExtendedPoseInfo::LocationForFrame(BRect frame) const
329 {
330 	for (int32 index = 0; index < fNumFrames; index++) {
331 		if (fLocations[index].fFrame == frame)
332 			return fLocations[index].fLocation;
333 	}
334 
335 	TRESPASS();
336 	return BPoint(0, 0);
337 }
338 
339 
340 bool
341 ExtendedPoseInfo::SetLocationForFrame(BPoint newLocation, BRect frame)
342 {
343 	for (int32 index = 0; index < fNumFrames; index++) {
344 		if (fLocations[index].fFrame == frame) {
345 			if (fLocations[index].fLocation == newLocation)
346 				return false;
347 
348 			fLocations[index].fLocation = newLocation;
349 			return true;
350 		}
351 	}
352 
353 	fLocations[fNumFrames].fFrame = frame;
354 	fLocations[fNumFrames].fLocation = newLocation;
355 	fLocations[fNumFrames].fWorkspaces = 0xffffffff;
356 	fNumFrames++;
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 size, const BMessage* message,
559 	BMessenger target, uint32 resizeMask, uint32 flags)
560 	:
561 	BView(rect, name, resizeMask, flags),
562 	fMessage(*message),
563 	fTarget(target)
564 {
565 	fBitmap = new BBitmap(Bounds(), kDefaultIconDepth);
566 	BMimeType mime(mimeType);
567 	status_t error = mime.GetIcon(fBitmap, size);
568 	ASSERT(mime.IsValid());
569 	if (error != B_OK) {
570 		PRINT(("failed to get icon for %s, %s\n", mimeType, strerror(error)));
571 		BMimeType mime(B_FILE_MIMETYPE);
572 		ASSERT(mime.IsInstalled());
573 		mime.GetIcon(fBitmap, size);
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 size)
593 {
594 	BRect result(0, 0, size - 1, size - 1);
595 	result.OffsetTo(offset);
596 	return result;
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 resizeFlags, uint32 flags)
663 	:
664 	BStringView(bounds, name, text, resizeFlags, flags),
665 	fBitmap(NULL),
666 	fOrigBitmap(NULL)
667 {
668 }
669 
670 
671 FlickerFreeStringView::FlickerFreeStringView(BRect bounds, const char* name,
672 	const char* text, BBitmap* inBitmap, uint32 resizeFlags, uint32 flags)
673 	:
674 	BStringView(bounds, name, text, resizeFlags, flags),
675 	fBitmap(NULL),
676 	fOrigBitmap(inBitmap)
677 {
678 }
679 
680 
681 FlickerFreeStringView::~FlickerFreeStringView()
682 {
683 	delete fBitmap;
684 }
685 
686 
687 void
688 FlickerFreeStringView::Draw(BRect)
689 {
690 	BRect bounds(Bounds());
691 	if (!fBitmap)
692 		fBitmap = new OffscreenBitmap(Bounds());
693 
694 	BView* offscreen = fBitmap->BeginUsing(bounds);
695 
696 	if (Parent() != NULL) {
697 		fViewColor = Parent()->ViewColor();
698 		fLowColor = Parent()->ViewColor();
699 	}
700 
701 	offscreen->SetViewColor(fViewColor);
702 	offscreen->SetHighColor(HighColor());
703 	offscreen->SetLowColor(fLowColor);
704 
705 	BFont font;
706     GetFont(&font);
707 	offscreen->SetFont(&font);
708 
709 	offscreen->Sync();
710 	if (fOrigBitmap)
711 		offscreen->DrawBitmap(fOrigBitmap, Frame(), bounds);
712 	else
713 		offscreen->FillRect(bounds, B_SOLID_LOW);
714 
715 	if (Text() != NULL) {
716 		BPoint loc;
717 
718 		font_height	height;
719 		GetFontHeight(&height);
720 
721 		edge_info eInfo;
722 		switch (Alignment()) {
723 			case B_ALIGN_LEFT:
724 			case B_ALIGN_HORIZONTAL_UNSET:
725 			case B_ALIGN_USE_FULL_WIDTH:
726 			{
727 				// If the first char has a negative left edge give it
728 				// some more room by shifting that much more to the right.
729 				font.GetEdges(Text(), 1, &eInfo);
730 				loc.x = bounds.left + (2 - eInfo.left);
731 				break;
732 			}
733 
734 			case B_ALIGN_CENTER:
735 			{
736 				float width = StringWidth(Text());
737 				float center = (bounds.right - bounds.left) / 2;
738 				loc.x = center - (width/2);
739 				break;
740 			}
741 
742 			case B_ALIGN_RIGHT:
743 			{
744 				float width = StringWidth(Text());
745 				loc.x = bounds.right - width - 2;
746 				break;
747 			}
748 		}
749 		loc.y = bounds.bottom - (1 + height.descent);
750 		offscreen->DrawString(Text(), loc);
751 	}
752 	offscreen->Sync();
753 	SetDrawingMode(B_OP_COPY);
754 	DrawBitmap(fBitmap->Bitmap());
755 	fBitmap->DoneUsing();
756 }
757 
758 
759 void
760 FlickerFreeStringView::AttachedToWindow()
761 {
762 	_inherited::AttachedToWindow();
763 	if (Parent() != NULL) {
764 		fViewColor = Parent()->ViewColor();
765 		fLowColor = Parent()->ViewColor();
766 	}
767 	SetViewColor(B_TRANSPARENT_32_BIT);
768 	SetLowColor(B_TRANSPARENT_32_BIT);
769 }
770 
771 
772 void
773 FlickerFreeStringView::SetViewColor(rgb_color color)
774 {
775 	if (fViewColor != color) {
776 		fViewColor = color;
777 		Invalidate();
778 	}
779 	_inherited::SetViewColor(B_TRANSPARENT_32_BIT);
780 }
781 
782 
783 void
784 FlickerFreeStringView::SetLowColor(rgb_color color)
785 {
786 	if (fLowColor != color) {
787 		fLowColor = color;
788 		Invalidate();
789 	}
790 	_inherited::SetLowColor(B_TRANSPARENT_32_BIT);
791 }
792 
793 
794 // #pragma mark - TitledSeparatorItem
795 
796 
797 TitledSeparatorItem::TitledSeparatorItem(const char* label)
798 	:
799 	BMenuItem(label, 0)
800 {
801 	_inherited::SetEnabled(false);
802 }
803 
804 
805 TitledSeparatorItem::~TitledSeparatorItem()
806 {
807 }
808 
809 
810 void
811 TitledSeparatorItem::SetEnabled(bool)
812 {
813 	// leave disabled
814 }
815 
816 
817 void
818 TitledSeparatorItem::GetContentSize(float* width, float* height)
819 {
820 	_inherited::GetContentSize(width, height);
821 }
822 
823 
824 inline rgb_color
825 ShiftMenuBackgroundColor(float by)
826 {
827 	return tint_color(ui_color(B_MENU_BACKGROUND_COLOR), by);
828 }
829 
830 
831 void
832 TitledSeparatorItem::Draw()
833 {
834 	BRect frame(Frame());
835 
836 	BMenu* parent = Menu();
837 	ASSERT(parent);
838 
839 	menu_info minfo;
840 	get_menu_info(&minfo);
841 
842 	if (minfo.separator > 0) {
843 		frame.left += 10;
844 		frame.right -= 10;
845 	} else {
846 		frame.left += 1;
847 		frame.right -= 1;
848 	}
849 
850 	float startX = frame.left;
851 	float endX = frame.right;
852 
853 	float maxStringWidth = endX - startX - (2 * kMinSeparatorStubX
854 		+ 2 * kStubToStringSlotX);
855 
856 	// ToDo:
857 	// handle case where maxStringWidth turns out negative here
858 
859 	BString truncatedLabel(Label());
860 	parent->TruncateString(&truncatedLabel, B_TRUNCATE_END, maxStringWidth);
861 
862 	maxStringWidth = parent->StringWidth(truncatedLabel.String());
863 
864 	// first calculate the length of the stub part of the
865 	// divider line, so we can use it for secondStartX
866 	float firstEndX = ((endX - startX) - maxStringWidth) / 2
867 		- kStubToStringSlotX;
868 	if (firstEndX < 0)
869 		firstEndX = 0;
870 
871 	float secondStartX = endX - firstEndX;
872 
873 	// now finish calculating firstEndX
874 	firstEndX += startX;
875 
876 	parent->PushState();
877 
878 	int32 y = (int32) (frame.top + (frame.bottom - frame.top) / 2);
879 
880 	parent->BeginLineArray(minfo.separator == 2 ? 6 : 4);
881 	parent->AddLine(BPoint(frame.left, y), BPoint(firstEndX, y),
882 		ShiftMenuBackgroundColor(B_DARKEN_1_TINT));
883 	parent->AddLine(BPoint(secondStartX, y), BPoint(frame.right, y),
884 		ShiftMenuBackgroundColor(B_DARKEN_1_TINT));
885 
886 	if (minfo.separator == 2) {
887 		y++;
888 		frame.left++;
889 		frame.right--;
890 		parent->AddLine(BPoint(frame.left,y), BPoint(firstEndX, y),
891 			ShiftMenuBackgroundColor(B_DARKEN_1_TINT));
892 		parent->AddLine(BPoint(secondStartX,y), BPoint(frame.right, y),
893 			ShiftMenuBackgroundColor(B_DARKEN_1_TINT));
894 	}
895 	y++;
896 	if (minfo.separator == 2) {
897 		frame.left++;
898 		frame.right--;
899 	}
900 	parent->AddLine(BPoint(frame.left, y), BPoint(firstEndX, y),
901 		ShiftMenuBackgroundColor(B_DARKEN_1_TINT));
902 	parent->AddLine(BPoint(secondStartX, y), BPoint(frame.right, y),
903 		ShiftMenuBackgroundColor(B_DARKEN_1_TINT));
904 
905 	parent->EndLineArray();
906 
907 	font_height	finfo;
908 	parent->GetFontHeight(&finfo);
909 
910 	parent->SetLowColor(parent->ViewColor());
911 	BPoint loc(firstEndX + kStubToStringSlotX,
912 		ContentLocation().y + finfo.ascent);
913 
914 	parent->MovePenTo(loc + BPoint(1, 1));
915 	parent->SetHighColor(ShiftMenuBackgroundColor(B_DARKEN_1_TINT));
916 	parent->DrawString(truncatedLabel.String());
917 
918 	parent->MovePenTo(loc);
919 	parent->SetHighColor(ShiftMenuBackgroundColor(B_DISABLED_LABEL_TINT));
920 	parent->DrawString(truncatedLabel.String());
921 
922 	parent->PopState();
923 }
924 
925 
926 // #pragma mark - ShortcutFilter
927 
928 
929 ShortcutFilter::ShortcutFilter(uint32 shortcutKey, uint32 shortcutModifier,
930 	uint32 shortcutWhat, BHandler* target)
931 	:
932 	BMessageFilter(B_KEY_DOWN),
933 	fShortcutKey(shortcutKey),
934 	fShortcutModifier(shortcutModifier),
935 	fShortcutWhat(shortcutWhat),
936 	fTarget(target)
937 {
938 }
939 
940 
941 filter_result
942 ShortcutFilter::Filter(BMessage* message, BHandler**)
943 {
944 	if (message->what == B_KEY_DOWN) {
945 		uint32 modifiers;
946 		uint32 rawKeyChar = 0;
947 		uint8 byte = 0;
948 		int32 key = 0;
949 
950 		if (message->FindInt32("modifiers", (int32*)&modifiers) != B_OK
951 			|| message->FindInt32("raw_char", (int32*)&rawKeyChar) != B_OK
952 			|| message->FindInt8("byte", (int8*)&byte) != B_OK
953 			|| message->FindInt32("key", &key) != B_OK)
954 			return B_DISPATCH_MESSAGE;
955 
956 		modifiers &= B_SHIFT_KEY | B_COMMAND_KEY | B_CONTROL_KEY
957 			| B_OPTION_KEY | B_MENU_KEY;
958 			// strip caps lock, etc.
959 
960 		if (modifiers == fShortcutModifier && rawKeyChar == fShortcutKey) {
961 			fTarget->Looper()->PostMessage(fShortcutWhat, fTarget);
962 			return B_SKIP_MESSAGE;
963 		}
964 	}
965 
966 	// let others deal with this
967 	return B_DISPATCH_MESSAGE;
968 }
969 
970 
971 // #pragma mark - BPrivate functions
972 
973 
974 namespace BPrivate {
975 
976 void
977 EmbedUniqueVolumeInfo(BMessage* message, const BVolume* volume)
978 {
979 	BDirectory rootDirectory;
980 	time_t created;
981 	fs_info info;
982 
983 	if (volume->GetRootDirectory(&rootDirectory) == B_OK
984 		&& rootDirectory.GetCreationTime(&created) == B_OK
985 		&& fs_stat_dev(volume->Device(), &info) == 0) {
986 		message->AddInt32("creationDate", created);
987 		message->AddInt64("capacity", volume->Capacity());
988 		message->AddString("deviceName", info.device_name);
989 		message->AddString("volumeName", info.volume_name);
990 		message->AddString("fshName", info.fsh_name);
991 	}
992 }
993 
994 
995 status_t
996 MatchArchivedVolume(BVolume* result, const BMessage* message, int32 index)
997 {
998 	time_t created;
999 	off_t capacity;
1000 
1001 	if (message->FindInt32("creationDate", index, &created) != B_OK
1002 		|| message->FindInt64("capacity", index, &capacity) != B_OK)
1003 		return B_ERROR;
1004 
1005 	BVolumeRoster roster;
1006 	BVolume volume;
1007 	BString deviceName, volumeName, fshName;
1008 
1009 	if (message->FindString("deviceName", &deviceName) == B_OK
1010 		&& message->FindString("volumeName", &volumeName) == B_OK
1011 		&& message->FindString("fshName", &fshName) == B_OK) {
1012 		// New style volume identifiers: We have a couple of characteristics,
1013 		// and compute a score from them. The volume with the greatest score
1014 		// (if over a certain threshold) is the one we're looking for. We
1015 		// pick the first volume, in case there is more than one with the
1016 		// same score.
1017 		dev_t foundDevice = -1;
1018 		int foundScore = -1;
1019 		roster.Rewind();
1020 		while (roster.GetNextVolume(&volume) == B_OK) {
1021 			if (volume.IsPersistent() && volume.KnowsQuery()) {
1022 				// get creation time and fs_info
1023 				BDirectory root;
1024 				volume.GetRootDirectory(&root);
1025 				time_t cmpCreated;
1026 				fs_info info;
1027 				if (root.GetCreationTime(&cmpCreated) == B_OK
1028 					&& fs_stat_dev(volume.Device(), &info) == 0) {
1029 					// compute the score
1030 					int score = 0;
1031 
1032 					// creation time
1033 					if (created == cmpCreated)
1034 						score += 5;
1035 					// capacity
1036 					if (capacity == volume.Capacity())
1037 						score += 4;
1038 					// device name
1039 					if (deviceName == info.device_name)
1040 						score += 3;
1041 					// volume name
1042 					if (volumeName == info.volume_name)
1043 						score += 2;
1044 					// fsh name
1045 					if (fshName == info.fsh_name)
1046 						score += 1;
1047 
1048 					// check score
1049 					if (score >= 9 && score > foundScore) {
1050 						foundDevice = volume.Device();
1051 						foundScore = score;
1052 					}
1053 				}
1054 			}
1055 		}
1056 		if (foundDevice >= 0)
1057 			return result->SetTo(foundDevice);
1058 	} else {
1059 		// Old style volume identifiers: We have only creation time and
1060 		// capacity. Both must match.
1061 		roster.Rewind();
1062 		while (roster.GetNextVolume(&volume) == B_OK)
1063 			if (volume.IsPersistent() && volume.KnowsQuery()) {
1064 				BDirectory root;
1065 				volume.GetRootDirectory(&root);
1066 				time_t cmpCreated;
1067 				root.GetCreationTime(&cmpCreated);
1068 				if (created == cmpCreated && capacity == volume.Capacity()) {
1069 					*result = volume;
1070 					return B_OK;
1071 				}
1072 			}
1073 	}
1074 
1075 	return B_DEV_BAD_DRIVE_NUM;
1076 }
1077 
1078 
1079 void
1080 StringFromStream(BString* string, BMallocIO* stream, bool endianSwap)
1081 {
1082 	int32 length;
1083 	stream->Read(&length, sizeof(length));
1084 	if (endianSwap)
1085 		length = SwapInt32(length);
1086 
1087 	if (length < 0 || length > 10000) {
1088 		// TODO: should fail here
1089 		PRINT(("problems instatiating a string, length probably wrong %"
1090 			B_PRId32 "\n", length));
1091 		return;
1092 	}
1093 
1094 	char* buffer = string->LockBuffer(length + 1);
1095 	stream->Read(buffer, (size_t)length + 1);
1096 	string->UnlockBuffer(length);
1097 }
1098 
1099 
1100 void
1101 StringToStream(const BString* string, BMallocIO* stream)
1102 {
1103 	int32 length = string->Length();
1104 	stream->Write(&length, sizeof(int32));
1105 	stream->Write(string->String(), (size_t)string->Length() + 1);
1106 }
1107 
1108 
1109 int32
1110 ArchiveSize(const BString* string)
1111 {
1112 	return string->Length() + 1 + (ssize_t)sizeof(int32);
1113 }
1114 
1115 
1116 int32
1117 CountRefs(const BMessage* message)
1118 {
1119 	uint32 type;
1120 	int32 count;
1121 	message->GetInfo("refs", &type, &count);
1122 
1123 	return count;
1124 }
1125 
1126 
1127 static entry_ref*
1128 EachEntryRefCommon(BMessage* message, entry_ref *(*func)(entry_ref*, void*),
1129 	void* passThru, int32 maxCount)
1130 {
1131 	uint32 type;
1132 	int32 count;
1133 	message->GetInfo("refs", &type, &count);
1134 
1135 	if (maxCount >= 0 && count > maxCount)
1136 		count = maxCount;
1137 
1138 	for (int32 index = 0; index < count; index++) {
1139 		entry_ref ref;
1140 		message->FindRef("refs", index, &ref);
1141 		entry_ref* result = (func)(&ref, passThru);
1142 		if (result)
1143 			return result;
1144 	}
1145 
1146 	return NULL;
1147 }
1148 
1149 
1150 bool
1151 ContainsEntryRef(const BMessage* message, const entry_ref* ref)
1152 {
1153 	entry_ref match;
1154 	for (int32 index = 0; (message->FindRef("refs", index, &match) == B_OK);
1155 			index++) {
1156 		if (*ref == match)
1157 			return true;
1158 	}
1159 
1160 	return false;
1161 }
1162 
1163 
1164 entry_ref*
1165 EachEntryRef(BMessage* message, entry_ref* (*func)(entry_ref*, void*),
1166 	void* passThru)
1167 {
1168 	return EachEntryRefCommon(message, func, passThru, -1);
1169 }
1170 
1171 typedef entry_ref *(*EachEntryIteratee)(entry_ref *, void *);
1172 
1173 
1174 const entry_ref*
1175 EachEntryRef(const BMessage* message,
1176 	const entry_ref* (*func)(const entry_ref*, void*), void* passThru)
1177 {
1178 	return EachEntryRefCommon(const_cast<BMessage*>(message),
1179 		(EachEntryIteratee)func, passThru, -1);
1180 }
1181 
1182 
1183 entry_ref*
1184 EachEntryRef(BMessage* message, entry_ref* (*func)(entry_ref*, void*),
1185 	void* passThru, int32 maxCount)
1186 {
1187 	return EachEntryRefCommon(message, func, passThru, maxCount);
1188 }
1189 
1190 
1191 const entry_ref *
1192 EachEntryRef(const BMessage* message,
1193 	const entry_ref *(*func)(const entry_ref *, void *), void* passThru,
1194 	int32 maxCount)
1195 {
1196 	return EachEntryRefCommon(const_cast<BMessage *>(message),
1197 		(EachEntryIteratee)func, passThru, maxCount);
1198 }
1199 
1200 
1201 void
1202 TruncateLeaf(BString* string)
1203 {
1204 	for (int32 index = string->Length(); index >= 0; index--) {
1205 		if ((*string)[index] == '/') {
1206 			string->Truncate(index + 1);
1207 			return;
1208 		}
1209 	}
1210 }
1211 
1212 
1213 int64
1214 StringToScalar(const char* text)
1215 {
1216 	char* end;
1217 	int64 val;
1218 
1219 	char* buffer = new char [strlen(text) + 1];
1220 	strcpy(buffer, text);
1221 
1222 	if (strstr(buffer, "k") || strstr(buffer, "K")) {
1223 		val = strtoll(buffer, &end, 10);
1224 		val *= kKBSize;
1225 	} else if (strstr(buffer, "mb") || strstr(buffer, "MB")) {
1226 		val = strtoll(buffer, &end, 10);
1227 		val *= kMBSize;
1228 	} else if (strstr(buffer, "gb") || strstr(buffer, "GB")) {
1229 		val = strtoll(buffer, &end, 10);
1230 		val *= kGBSize;
1231 	} else if (strstr(buffer, "byte") || strstr(buffer, "BYTE")) {
1232 		val = strtoll(buffer, &end, 10);
1233 		val *= kGBSize;
1234 	} else {
1235 		// no suffix, try plain byte conversion
1236 		val = strtoll(buffer, &end, 10);
1237 	}
1238 	delete[] buffer;
1239 
1240 	return val;
1241 }
1242 
1243 
1244 static BRect
1245 LineBounds(BPoint where, float length, bool vertical)
1246 {
1247 	BRect result;
1248 	result.SetLeftTop(where);
1249 	result.SetRightBottom(where + BPoint(2, 2));
1250 	if (vertical)
1251 		result.bottom = result.top + length;
1252 	else
1253 		result.right = result.left + length;
1254 
1255 	return result;
1256 }
1257 
1258 
1259 SeparatorLine::SeparatorLine(BPoint where, float length, bool vertical,
1260 	const char* name)
1261 	:
1262 	BView(LineBounds(where, length, vertical), name,
1263 		B_FOLLOW_LEFT | B_FOLLOW_TOP, B_WILL_DRAW)
1264 {
1265 	SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
1266 	SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR));
1267 }
1268 
1269 
1270 void
1271 SeparatorLine::Draw(BRect)
1272 {
1273 	BRect bounds(Bounds());
1274 	rgb_color hiliteColor = tint_color(ViewColor(), 1.5f);
1275 
1276 	bool vertical = (bounds.left > bounds.right - 3);
1277 	BeginLineArray(2);
1278 	if (vertical) {
1279 		AddLine(bounds.LeftTop(), bounds.LeftBottom(), hiliteColor);
1280 		AddLine(bounds.LeftTop() + BPoint(1, 0),
1281 			bounds.LeftBottom() + BPoint(1, 0), kWhite);
1282 	} else {
1283 		AddLine(bounds.LeftTop(), bounds.RightTop(), hiliteColor);
1284 		AddLine(bounds.LeftTop() + BPoint(0, 1),
1285 			bounds.RightTop() + BPoint(0, 1), kWhite);
1286 	}
1287 	EndLineArray();
1288 }
1289 
1290 
1291 void
1292 HexDump(const void* buf, int32 length)
1293 {
1294 	const int32 kBytesPerLine = 16;
1295 	int32 offset;
1296 	unsigned char* buffer = (unsigned char*)buf;
1297 
1298 	for (offset = 0; ; offset += kBytesPerLine, buffer += kBytesPerLine) {
1299 		int32 remain = length;
1300 		int32 index;
1301 
1302 		printf( "0x%06x: ", (int)offset);
1303 
1304 		for (index = 0; index < kBytesPerLine; index++) {
1305 			if (remain-- > 0)
1306 				printf("%02x%c", buffer[index], remain > 0 ? ',' : ' ');
1307 			else
1308 				printf("   ");
1309 		}
1310 
1311 		remain = length;
1312 		printf(" \'");
1313 		for (index = 0; index < kBytesPerLine; index++) {
1314 			if (remain-- > 0)
1315 				printf("%c", buffer[index] > ' ' ? buffer[index] : '.');
1316 			else
1317 				printf(" ");
1318 		}
1319 		printf("\'\n");
1320 
1321 		length -= kBytesPerLine;
1322 		if (length <= 0)
1323 			break;
1324 	}
1325 	fflush(stdout);
1326 }
1327 
1328 
1329 void
1330 EnableNamedMenuItem(BMenu* menu, const char* itemName, bool on)
1331 {
1332 	BMenuItem* item = menu->FindItem(itemName);
1333 	if (item)
1334 		item->SetEnabled(on);
1335 }
1336 
1337 
1338 void
1339 MarkNamedMenuItem(BMenu* menu, const char* itemName, bool on)
1340 {
1341 	BMenuItem* item = menu->FindItem(itemName);
1342 	if (item)
1343 		item->SetMarked(on);
1344 }
1345 
1346 
1347 void
1348 EnableNamedMenuItem(BMenu* menu, uint32 commandName, bool on)
1349 {
1350 	BMenuItem* item = menu->FindItem(commandName);
1351 	if (item)
1352 		item->SetEnabled(on);
1353 }
1354 
1355 
1356 void
1357 MarkNamedMenuItem(BMenu* menu, uint32 commandName, bool on)
1358 {
1359 	BMenuItem* item = menu->FindItem(commandName);
1360 	if (item)
1361 		item->SetMarked(on);
1362 }
1363 
1364 
1365 void
1366 DeleteSubmenu(BMenuItem* submenuItem)
1367 {
1368 	if (!submenuItem)
1369 		return;
1370 
1371 	BMenu* menu = submenuItem->Submenu();
1372 	if (!menu)
1373 		return;
1374 
1375 	for (;;) {
1376 		BMenuItem* item = menu->RemoveItem((int32)0);
1377 		if (!item)
1378 			return;
1379 
1380 		delete item;
1381 	}
1382 }
1383 
1384 
1385 status_t
1386 GetAppSignatureFromAttr(BFile* file, char* result)
1387 {
1388 	// This call is a performance improvement that
1389 	// avoids using the BAppFileInfo API when retrieving the
1390 	// app signature -- the call is expensive because by default
1391 	// the resource fork is scanned to read the attribute
1392 
1393 #ifdef B_APP_FILE_INFO_IS_FAST
1394 	BAppFileInfo appFileInfo(file);
1395 	return appFileInfo.GetSignature(result);
1396 #else
1397 	ssize_t readResult = file->ReadAttr(kAttrAppSignature, B_MIME_STRING_TYPE,
1398 		0, result, B_MIME_TYPE_LENGTH);
1399 
1400 	if (readResult <= 0)
1401 		return (status_t)readResult;
1402 
1403 	return B_OK;
1404 #endif	// B_APP_FILE_INFO_IS_FAST
1405 }
1406 
1407 
1408 status_t
1409 GetAppIconFromAttr(BFile* file, BBitmap* result, icon_size size)
1410 {
1411 	// This call is a performance improvement that
1412 	// avoids using the BAppFileInfo API when retrieving the
1413 	// app icons -- the call is expensive because by default
1414 	// the resource fork is scanned to read the icons
1415 
1416 //#ifdef B_APP_FILE_INFO_IS_FAST
1417 	BAppFileInfo appFileInfo(file);
1418 	return appFileInfo.GetIcon(result, size);
1419 //#else
1420 //
1421 //	const char* attrName = kAttrIcon;
1422 //	uint32 type = B_VECTOR_ICON_TYPE;
1423 //
1424 //	// try vector icon
1425 //	attr_info ainfo;
1426 //	status_t ret = file->GetAttrInfo(attrName, &ainfo);
1427 //
1428 //	if (ret == B_OK) {
1429 //		uint8 buffer[ainfo.size];
1430 //		ssize_t readResult = file->ReadAttr(attrName, type, 0, buffer,
1431 //											ainfo.size);
1432 //		if (readResult == ainfo.size) {
1433 //			if (BIconUtils::GetVectorIcon(buffer, ainfo.size, result) == B_OK)
1434 //				return B_OK;
1435 //		}
1436 //	}
1437 //
1438 //	// try again with R5 icons
1439 //	attrName = size == B_LARGE_ICON ? kAttrLargeIcon : kAttrMiniIcon;
1440 //	type = size == B_LARGE_ICON ? LARGE_ICON_TYPE : MINI_ICON_TYPE;
1441 //
1442 //	ret = file->GetAttrInfo(attrName, &ainfo);
1443 //	if (ret < B_OK)
1444 //		return ret;
1445 //
1446 //	uint8 buffer[ainfo.size];
1447 //
1448 //	ssize_t readResult = file->ReadAttr(attrName, type, 0, buffer, ainfo.size);
1449 //	if (readResult <= 0)
1450 //		return (status_t)readResult;
1451 //
1452 //	if (result->ColorSpace() != B_CMAP8) {
1453 //		ret = BIconUtils::ConvertFromCMAP8(buffer, size, size, size, result);
1454 //	} else {
1455 //		result->SetBits(buffer, result->BitsLength(), 0, B_CMAP8);
1456 //	}
1457 //
1458 //	return ret;
1459 //#endif	// B_APP_FILE_INFO_IS_FAST
1460 }
1461 
1462 
1463 status_t
1464 GetFileIconFromAttr(BNode* file, BBitmap* result, icon_size size)
1465 {
1466 	BNodeInfo fileInfo(file);
1467 	return fileInfo.GetIcon(result, size);
1468 }
1469 
1470 
1471 void
1472 PrintToStream(rgb_color color)
1473 {
1474 	printf("r:%x, g:%x, b:%x, a:%x\n",
1475 		color.red, color.green, color.blue, color.alpha);
1476 }
1477 
1478 
1479 extern BMenuItem*
1480 EachMenuItem(BMenu* menu, bool recursive, BMenuItem* (*func)(BMenuItem *))
1481 {
1482 	int32 count = menu->CountItems();
1483 	for (int32 index = 0; index < count; index++) {
1484 		BMenuItem* item = menu->ItemAt(index);
1485 		BMenuItem* result = (func)(item);
1486 		if (result)
1487 			return result;
1488 
1489 		if (recursive) {
1490 			BMenu* submenu = menu->SubmenuAt(index);
1491 			if (submenu)
1492 				return EachMenuItem(submenu, true, func);
1493 		}
1494 	}
1495 
1496 	return NULL;
1497 }
1498 
1499 
1500 extern const BMenuItem*
1501 EachMenuItem(const BMenu* menu, bool recursive,
1502 	BMenuItem* (*func)(const BMenuItem *))
1503 {
1504 	int32 count = menu->CountItems();
1505 	for (int32 index = 0; index < count; index++) {
1506 		BMenuItem* item = menu->ItemAt(index);
1507 		BMenuItem* result = (func)(item);
1508 		if (result)
1509 			return result;
1510 
1511 		if (recursive) {
1512 			BMenu* submenu = menu->SubmenuAt(index);
1513 			if (submenu)
1514 				return EachMenuItem(submenu, true, func);
1515 		}
1516 	}
1517 
1518 	return NULL;
1519 }
1520 
1521 
1522 //	#pragma mark - PositionPassingMenuItem
1523 
1524 
1525 PositionPassingMenuItem::PositionPassingMenuItem(const char* title,
1526 	BMessage* message, char shortcut, uint32 modifiers)
1527 	:
1528 	BMenuItem(title, message, shortcut, modifiers)
1529 {
1530 }
1531 
1532 
1533 PositionPassingMenuItem::PositionPassingMenuItem(BMenu* menu, BMessage* message)
1534 	:
1535 	BMenuItem(menu, message)
1536 {
1537 }
1538 
1539 
1540 status_t
1541 PositionPassingMenuItem::Invoke(BMessage* message)
1542 {
1543 	if (!Menu())
1544 		return B_ERROR;
1545 
1546 	if (!IsEnabled())
1547 		return B_ERROR;
1548 
1549 	if (!message)
1550 		message = Message();
1551 
1552 	if (!message)
1553 		return B_BAD_VALUE;
1554 
1555 	BMessage clone(*message);
1556 	clone.AddInt32("index", Menu()->IndexOf(this));
1557 	clone.AddInt64("when", system_time());
1558 	clone.AddPointer("source", this);
1559 
1560 	// embed the invoke location of the menu so that we can create
1561 	// a new folder, etc. on the spot
1562 	BMenu* menu = Menu();
1563 
1564 	for (;;) {
1565 		if (!menu->Supermenu())
1566 			break;
1567 		menu = menu->Supermenu();
1568 	}
1569 
1570 	// use the window position only, if the item was invoked from the menu
1571 	// menu->Window() points to the window the item was invoked from
1572 	if (dynamic_cast<BContainerWindow*>(menu->Window()) == NULL) {
1573 		LooperAutoLocker lock(menu);
1574 		if (lock.IsLocked()) {
1575 			BPoint invokeOrigin(menu->Window()->Frame().LeftTop());
1576 			clone.AddPoint("be:invoke_origin", invokeOrigin);
1577 		}
1578 	}
1579 
1580 	return BInvoker::Invoke(&clone);
1581 }
1582 
1583 
1584 //	#pragma mark - BPrivate functions
1585 
1586 
1587 bool
1588 BootedInSafeMode()
1589 {
1590 	const char* safeMode = getenv("SAFEMODE");
1591 	return (safeMode && strcmp(safeMode, "yes") == 0);
1592 }
1593 
1594 
1595 float
1596 ComputeTypeAheadScore(const char* text, const char* match, bool wordMode)
1597 {
1598 	// highest score: exact match
1599 	const char* found = strcasestr(text, match);
1600 	if (found != NULL) {
1601 		if (found == text)
1602 			return kExactMatchScore;
1603 
1604 		return 1.f / (found - text);
1605 	}
1606 
1607 	// there was no exact match
1608 
1609 	// second best: all characters at word beginnings
1610 	if (wordMode) {
1611 		float score = 0;
1612 		for (int32 j = 0, k = 0; match[j]; j++) {
1613 			while (text[k]
1614 				&& tolower(text[k]) != tolower(match[j])) {
1615 				k++;
1616 			}
1617 			if (text[k] == '\0') {
1618 				score = 0;
1619 				break;
1620 			}
1621 
1622 			bool wordStart = k == 0 || isspace(text[k - 1]);
1623 			if (wordStart)
1624 				score++;
1625 			if (j > 0) {
1626 				bool wordEnd = !text[k + 1] || isspace(text[k + 1]);
1627 				if (wordEnd)
1628 					score += 0.3;
1629 				if (match[j - 1] == text[k - 1])
1630 					score += 0.7;
1631 			}
1632 
1633 			score += 1.f / (k + 1);
1634 			k++;
1635 		}
1636 
1637 		return score;
1638 	}
1639 
1640 	return -1;
1641 }
1642 
1643 
1644 void
1645 _ThrowOnError(status_t error, const char* DEBUG_ONLY(file),
1646 	int32 DEBUG_ONLY(line))
1647 {
1648 	if (error != B_OK) {
1649 		PRINT(("failing %s at %s:%d\n", strerror(error), file, (int)line));
1650 		throw error;
1651 	}
1652 }
1653 
1654 
1655 void
1656 _ThrowIfNotSize(ssize_t size, const char* DEBUG_ONLY(file),
1657 	int32 DEBUG_ONLY(line))
1658 {
1659 	if (size < B_OK) {
1660 		PRINT(("failing %s at %s:%d\n", strerror(size), file, (int)line));
1661 		throw (status_t)size;
1662 	}
1663 }
1664 
1665 
1666 void
1667 _ThrowOnError(status_t error, const char* DEBUG_ONLY(debugString),
1668 	const char* DEBUG_ONLY(file), int32 DEBUG_ONLY(line))
1669 {
1670 	if (error != B_OK) {
1671 		PRINT(("failing %s, %s at %s:%d\n", debugString, strerror(error), file,
1672 			(int)line));
1673 		throw error;
1674 	}
1675 }
1676 
1677 } // namespace BPrivate
1678