xref: /haiku/src/kits/tracker/Utilities.cpp (revision 2cad94c1c30b6223ad8c08710b26e071d32e9979)
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 	AdoptParentColors();
604 }
605 
606 
607 void
608 DraggableIcon::MouseDown(BPoint point)
609 {
610 	if (!DragStarted(&fMessage))
611 		return;
612 
613 	BRect rect(Bounds());
614 	BBitmap* dragBitmap = new BBitmap(rect, B_RGBA32, true);
615 	dragBitmap->Lock();
616 	BView* view = new BView(dragBitmap->Bounds(), "", B_FOLLOW_NONE, 0);
617 	dragBitmap->AddChild(view);
618 	view->SetOrigin(0, 0);
619 	BRect clipRect(view->Bounds());
620 	BRegion newClip;
621 	newClip.Set(clipRect);
622 	view->ConstrainClippingRegion(&newClip);
623 
624 	// Transparent draw magic
625 	view->SetHighColor(0, 0, 0, 0);
626 	view->FillRect(view->Bounds());
627 	view->SetDrawingMode(B_OP_ALPHA);
628 	view->SetHighColor(0, 0, 0, 128);
629 		// set the level of transparency by value
630 	view->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_COMPOSITE);
631 	view->DrawBitmap(fBitmap);
632 	view->Sync();
633 	dragBitmap->Unlock();
634 	DragMessage(&fMessage, dragBitmap, B_OP_ALPHA, point, fTarget.Target(0));
635 }
636 
637 
638 bool
639 DraggableIcon::DragStarted(BMessage*)
640 {
641 	return true;
642 }
643 
644 
645 void
646 DraggableIcon::Draw(BRect)
647 {
648 	SetDrawingMode(B_OP_ALPHA);
649 	SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
650 	DrawBitmap(fBitmap);
651 }
652 
653 
654 // #pragma mark - FlickerFreeStringView
655 
656 
657 FlickerFreeStringView::FlickerFreeStringView(BRect bounds, const char* name,
658 	const char* text, uint32 resizingMode, uint32 flags)
659 	:
660 	BStringView(bounds, name, text, resizingMode, flags),
661 	fBitmap(NULL),
662 	fViewColor(ViewColor()),
663 	fLowColor(LowColor()),
664 	fOriginalBitmap(NULL)
665 {
666 }
667 
668 
669 FlickerFreeStringView::FlickerFreeStringView(BRect bounds, const char* name,
670 	const char* text, BBitmap* inBitmap, uint32 resizingMode, uint32 flags)
671 	:
672 	BStringView(bounds, name, text, resizingMode, flags),
673 	fBitmap(NULL),
674 	fViewColor(ViewColor()),
675 	fLowColor(LowColor()),
676 	fOriginalBitmap(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 == NULL)
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 (fOriginalBitmap != NULL)
711 		offscreen->DrawBitmap(fOriginalBitmap, 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 != NULL);
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 
957 		modifiers &= B_SHIFT_KEY | B_COMMAND_KEY | B_CONTROL_KEY
958 			| B_OPTION_KEY | B_MENU_KEY;
959 			// strip caps lock, etc.
960 
961 		if (modifiers == fShortcutModifier && rawKeyChar == fShortcutKey) {
962 			fTarget->Looper()->PostMessage(fShortcutWhat, fTarget);
963 			return B_SKIP_MESSAGE;
964 		}
965 	}
966 
967 	// let others deal with this
968 	return B_DISPATCH_MESSAGE;
969 }
970 
971 
972 // #pragma mark - BPrivate functions
973 
974 
975 namespace BPrivate {
976 
977 void
978 EmbedUniqueVolumeInfo(BMessage* message, const BVolume* volume)
979 {
980 	BDirectory rootDirectory;
981 	time_t created;
982 	fs_info info;
983 
984 	if (volume->GetRootDirectory(&rootDirectory) == B_OK
985 		&& rootDirectory.GetCreationTime(&created) == B_OK
986 		&& fs_stat_dev(volume->Device(), &info) == 0) {
987 		message->AddInt32("creationDate", created);
988 		message->AddInt64("capacity", volume->Capacity());
989 		message->AddString("deviceName", info.device_name);
990 		message->AddString("volumeName", info.volume_name);
991 		message->AddString("fshName", info.fsh_name);
992 	}
993 }
994 
995 
996 status_t
997 MatchArchivedVolume(BVolume* volume, const BMessage* message, int32 index)
998 {
999 	time_t created;
1000 	off_t capacity;
1001 
1002 	if (message->FindInt32("creationDate", index, &created) != B_OK
1003 		|| message->FindInt64("capacity", index, &capacity) != B_OK) {
1004 		return B_ERROR;
1005 	}
1006 
1007 	BVolumeRoster roster;
1008 	BVolume tempVolume;
1009 	BString deviceName;
1010 	BString volumeName;
1011 	BString fshName;
1012 
1013 	if (message->FindString("deviceName", &deviceName) == B_OK
1014 		&& message->FindString("volumeName", &volumeName) == B_OK
1015 		&& message->FindString("fshName", &fshName) == B_OK) {
1016 		// New style volume identifiers: We have a couple of characteristics,
1017 		// and compute a score from them. The volume with the greatest score
1018 		// (if over a certain threshold) is the one we're looking for. We
1019 		// pick the first volume, in case there is more than one with the
1020 		// same score.
1021 		dev_t foundDevice = -1;
1022 		int foundScore = -1;
1023 		roster.Rewind();
1024 		while (roster.GetNextVolume(&tempVolume) == B_OK) {
1025 			if (tempVolume.IsPersistent() && tempVolume.KnowsQuery()) {
1026 				// get creation time and fs_info
1027 				BDirectory root;
1028 				tempVolume.GetRootDirectory(&root);
1029 				time_t cmpCreated;
1030 				fs_info info;
1031 				if (root.GetCreationTime(&cmpCreated) == B_OK
1032 					&& fs_stat_dev(tempVolume.Device(), &info) == 0) {
1033 					// compute the score
1034 					int score = 0;
1035 
1036 					// creation time
1037 					if (created == cmpCreated)
1038 						score += 5;
1039 
1040 					// capacity
1041 					if (capacity == tempVolume.Capacity())
1042 						score += 4;
1043 
1044 					// device name
1045 					if (deviceName == info.device_name)
1046 						score += 3;
1047 
1048 					// volume name
1049 					if (volumeName == info.volume_name)
1050 						score += 2;
1051 
1052 					// fsh name
1053 					if (fshName == info.fsh_name)
1054 						score += 1;
1055 
1056 					// check score
1057 					if (score >= 9 && score > foundScore) {
1058 						foundDevice = tempVolume.Device();
1059 						foundScore = score;
1060 					}
1061 				}
1062 			}
1063 		}
1064 		if (foundDevice >= 0)
1065 			return volume->SetTo(foundDevice);
1066 	} else {
1067 		// Old style volume identifiers: We have only creation time and
1068 		// capacity. Both must match.
1069 		roster.Rewind();
1070 		while (roster.GetNextVolume(&tempVolume) == B_OK) {
1071 			if (tempVolume.IsPersistent() && tempVolume.KnowsQuery()) {
1072 				BDirectory root;
1073 				tempVolume.GetRootDirectory(&root);
1074 				time_t cmpCreated;
1075 				root.GetCreationTime(&cmpCreated);
1076 				if (created == cmpCreated && capacity == tempVolume.Capacity()) {
1077 					*volume = tempVolume;
1078 					return B_OK;
1079 				}
1080 			}
1081 		}
1082 	}
1083 
1084 	return B_DEV_BAD_DRIVE_NUM;
1085 }
1086 
1087 
1088 void
1089 StringFromStream(BString* string, BMallocIO* stream, bool endianSwap)
1090 {
1091 	int32 length;
1092 	stream->Read(&length, sizeof(length));
1093 	if (endianSwap)
1094 		length = SwapInt32(length);
1095 
1096 	if (length < 0 || length > 10000) {
1097 		// TODO: should fail here
1098 		PRINT(("problems instatiating a string, length probably wrong %"
1099 			B_PRId32 "\n", length));
1100 		return;
1101 	}
1102 
1103 	char* buffer = string->LockBuffer(length + 1);
1104 	stream->Read(buffer, (size_t)length + 1);
1105 	string->UnlockBuffer(length);
1106 }
1107 
1108 
1109 void
1110 StringToStream(const BString* string, BMallocIO* stream)
1111 {
1112 	int32 length = string->Length();
1113 	stream->Write(&length, sizeof(int32));
1114 	stream->Write(string->String(), (size_t)string->Length() + 1);
1115 }
1116 
1117 
1118 int32
1119 ArchiveSize(const BString* string)
1120 {
1121 	return string->Length() + 1 + (ssize_t)sizeof(int32);
1122 }
1123 
1124 
1125 int32
1126 CountRefs(const BMessage* message)
1127 {
1128 	uint32 type;
1129 	int32 count;
1130 	message->GetInfo("refs", &type, &count);
1131 
1132 	return count;
1133 }
1134 
1135 
1136 static entry_ref*
1137 EachEntryRefCommon(BMessage* message, entry_ref *(*func)(entry_ref*, void*),
1138 	void* passThru, int32 maxCount)
1139 {
1140 	uint32 type;
1141 	int32 count;
1142 	message->GetInfo("refs", &type, &count);
1143 
1144 	if (maxCount >= 0 && count > maxCount)
1145 		count = maxCount;
1146 
1147 	for (int32 index = 0; index < count; index++) {
1148 		entry_ref ref;
1149 		message->FindRef("refs", index, &ref);
1150 		entry_ref* newRef = (func)(&ref, passThru);
1151 		if (newRef != NULL)
1152 			return newRef;
1153 	}
1154 
1155 	return NULL;
1156 }
1157 
1158 
1159 bool
1160 ContainsEntryRef(const BMessage* message, const entry_ref* ref)
1161 {
1162 	entry_ref match;
1163 	for (int32 index = 0; (message->FindRef("refs", index, &match) == B_OK);
1164 			index++) {
1165 		if (*ref == match)
1166 			return true;
1167 	}
1168 
1169 	return false;
1170 }
1171 
1172 
1173 entry_ref*
1174 EachEntryRef(BMessage* message, entry_ref* (*func)(entry_ref*, void*),
1175 	void* passThru)
1176 {
1177 	return EachEntryRefCommon(message, func, passThru, -1);
1178 }
1179 
1180 typedef entry_ref *(*EachEntryIteratee)(entry_ref *, void *);
1181 
1182 
1183 const entry_ref*
1184 EachEntryRef(const BMessage* message,
1185 	const entry_ref* (*func)(const entry_ref*, void*), void* passThru)
1186 {
1187 	return EachEntryRefCommon(const_cast<BMessage*>(message),
1188 		(EachEntryIteratee)func, passThru, -1);
1189 }
1190 
1191 
1192 entry_ref*
1193 EachEntryRef(BMessage* message, entry_ref* (*func)(entry_ref*, void*),
1194 	void* passThru, int32 maxCount)
1195 {
1196 	return EachEntryRefCommon(message, func, passThru, maxCount);
1197 }
1198 
1199 
1200 const entry_ref *
1201 EachEntryRef(const BMessage* message,
1202 	const entry_ref *(*func)(const entry_ref *, void *), void* passThru,
1203 	int32 maxCount)
1204 {
1205 	return EachEntryRefCommon(const_cast<BMessage *>(message),
1206 		(EachEntryIteratee)func, passThru, maxCount);
1207 }
1208 
1209 
1210 void
1211 TruncateLeaf(BString* string)
1212 {
1213 	for (int32 index = string->Length(); index >= 0; index--) {
1214 		if ((*string)[index] == '/') {
1215 			string->Truncate(index + 1);
1216 			return;
1217 		}
1218 	}
1219 }
1220 
1221 
1222 int64
1223 StringToScalar(const char* text)
1224 {
1225 	char* end;
1226 	int64 val;
1227 
1228 	char* buffer = new char [strlen(text) + 1];
1229 	strcpy(buffer, text);
1230 
1231 	if (strstr(buffer, "k") || strstr(buffer, "K")) {
1232 		val = strtoll(buffer, &end, 10);
1233 		val *= kKBSize;
1234 	} else if (strstr(buffer, "mb") || strstr(buffer, "MB")) {
1235 		val = strtoll(buffer, &end, 10);
1236 		val *= kMBSize;
1237 	} else if (strstr(buffer, "gb") || strstr(buffer, "GB")) {
1238 		val = strtoll(buffer, &end, 10);
1239 		val *= kGBSize;
1240 	} else if (strstr(buffer, "byte") || strstr(buffer, "BYTE")) {
1241 		val = strtoll(buffer, &end, 10);
1242 		val *= kGBSize;
1243 	} else {
1244 		// no suffix, try plain byte conversion
1245 		val = strtoll(buffer, &end, 10);
1246 	}
1247 	delete[] buffer;
1248 
1249 	return val;
1250 }
1251 
1252 
1253 static BRect
1254 LineBounds(BPoint where, float length, bool vertical)
1255 {
1256 	BRect rect;
1257 	rect.SetLeftTop(where);
1258 	rect.SetRightBottom(where + BPoint(2, 2));
1259 	if (vertical)
1260 		rect.bottom = rect.top + length;
1261 	else
1262 		rect.right = rect.left + length;
1263 
1264 	return rect;
1265 }
1266 
1267 
1268 SeparatorLine::SeparatorLine(BPoint where, float length, bool vertical,
1269 	const char* name)
1270 	:
1271 	BView(LineBounds(where, length, vertical), name,
1272 		B_FOLLOW_LEFT | B_FOLLOW_TOP, B_WILL_DRAW)
1273 {
1274 	SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
1275 	SetLowUIColor(B_PANEL_BACKGROUND_COLOR);
1276 }
1277 
1278 
1279 void
1280 SeparatorLine::Draw(BRect)
1281 {
1282 	BRect bounds(Bounds());
1283 	rgb_color hiliteColor = tint_color(ViewColor(), 1.5f);
1284 
1285 	bool vertical = (bounds.left > bounds.right - 3);
1286 	BeginLineArray(2);
1287 	if (vertical) {
1288 		AddLine(bounds.LeftTop(), bounds.LeftBottom(), hiliteColor);
1289 		AddLine(bounds.LeftTop() + BPoint(1, 0),
1290 			bounds.LeftBottom() + BPoint(1, 0), kWhite);
1291 	} else {
1292 		AddLine(bounds.LeftTop(), bounds.RightTop(), hiliteColor);
1293 		AddLine(bounds.LeftTop() + BPoint(0, 1),
1294 			bounds.RightTop() + BPoint(0, 1), kWhite);
1295 	}
1296 	EndLineArray();
1297 }
1298 
1299 
1300 void
1301 HexDump(const void* buf, int32 length)
1302 {
1303 	const int32 kBytesPerLine = 16;
1304 	int32 offset;
1305 	unsigned char* buffer = (unsigned char*)buf;
1306 
1307 	for (offset = 0; ; offset += kBytesPerLine, buffer += kBytesPerLine) {
1308 		int32 remain = length;
1309 		int32 index;
1310 
1311 		printf( "0x%06x: ", (int)offset);
1312 
1313 		for (index = 0; index < kBytesPerLine; index++) {
1314 			if (remain-- > 0)
1315 				printf("%02x%c", buffer[index], remain > 0 ? ',' : ' ');
1316 			else
1317 				printf("   ");
1318 		}
1319 
1320 		remain = length;
1321 		printf(" \'");
1322 		for (index = 0; index < kBytesPerLine; index++) {
1323 			if (remain-- > 0)
1324 				printf("%c", buffer[index] > ' ' ? buffer[index] : '.');
1325 			else
1326 				printf(" ");
1327 		}
1328 		printf("\'\n");
1329 
1330 		length -= kBytesPerLine;
1331 		if (length <= 0)
1332 			break;
1333 	}
1334 	fflush(stdout);
1335 }
1336 
1337 
1338 void
1339 EnableNamedMenuItem(BMenu* menu, const char* itemName, bool on)
1340 {
1341 	BMenuItem* item = menu->FindItem(itemName);
1342 	if (item != NULL)
1343 		item->SetEnabled(on);
1344 }
1345 
1346 
1347 void
1348 MarkNamedMenuItem(BMenu* menu, const char* itemName, bool on)
1349 {
1350 	BMenuItem* item = menu->FindItem(itemName);
1351 	if (item != NULL)
1352 		item->SetMarked(on);
1353 }
1354 
1355 
1356 void
1357 EnableNamedMenuItem(BMenu* menu, uint32 commandName, bool on)
1358 {
1359 	BMenuItem* item = menu->FindItem(commandName);
1360 	if (item != NULL)
1361 		item->SetEnabled(on);
1362 }
1363 
1364 
1365 void
1366 MarkNamedMenuItem(BMenu* menu, uint32 commandName, bool on)
1367 {
1368 	BMenuItem* item = menu->FindItem(commandName);
1369 	if (item != NULL)
1370 		item->SetMarked(on);
1371 }
1372 
1373 
1374 void
1375 DeleteSubmenu(BMenuItem* submenuItem)
1376 {
1377 	if (submenuItem == NULL)
1378 		return;
1379 
1380 	BMenu* menu = submenuItem->Submenu();
1381 	if (menu == NULL)
1382 		return;
1383 
1384 	for (;;) {
1385 		BMenuItem* item = menu->RemoveItem((int32)0);
1386 		if (item == NULL)
1387 			return;
1388 
1389 		delete item;
1390 	}
1391 }
1392 
1393 
1394 status_t
1395 GetAppSignatureFromAttr(BFile* file, char* attr)
1396 {
1397 	// This call is a performance improvement that
1398 	// avoids using the BAppFileInfo API when retrieving the
1399 	// app signature -- the call is expensive because by default
1400 	// the resource fork is scanned to read the attribute
1401 
1402 #ifdef B_APP_FILE_INFO_IS_FAST
1403 	BAppFileInfo appFileInfo(file);
1404 	return appFileInfo.GetSignature(attr);
1405 #else
1406 	ssize_t readResult = file->ReadAttr(kAttrAppSignature, B_MIME_STRING_TYPE,
1407 		0, attr, B_MIME_TYPE_LENGTH);
1408 
1409 	if (readResult <= 0)
1410 		return (status_t)readResult;
1411 
1412 	return B_OK;
1413 #endif // B_APP_FILE_INFO_IS_FAST
1414 }
1415 
1416 
1417 status_t
1418 GetAppIconFromAttr(BFile* file, BBitmap* icon, icon_size which)
1419 {
1420 	// This call is a performance improvement that
1421 	// avoids using the BAppFileInfo API when retrieving the
1422 	// app icons -- the call is expensive because by default
1423 	// the resource fork is scanned to read the icons
1424 
1425 //#ifdef B_APP_FILE_INFO_IS_FAST
1426 	BAppFileInfo appFileInfo(file);
1427 	return appFileInfo.GetIcon(icon, which);
1428 //#else
1429 //
1430 //	const char* attrName = kAttrIcon;
1431 //	uint32 type = B_VECTOR_ICON_TYPE;
1432 //
1433 //	// try vector icon
1434 //	attr_info ainfo;
1435 //	status_t result = file->GetAttrInfo(attrName, &ainfo);
1436 //
1437 //	if (result == B_OK) {
1438 //		uint8 buffer[ainfo.size];
1439 //		ssize_t readResult = file->ReadAttr(attrName, type, 0, buffer,
1440 //			ainfo.size);
1441 //		if (readResult == ainfo.size) {
1442 //			if (BIconUtils::GetVectorIcon(buffer, ainfo.size, icon) == B_OK)
1443 //				return B_OK;
1444 //		}
1445 //	}
1446 //
1447 //	// try again with R5 icons
1448 //	attrName = which == B_LARGE_ICON ? kAttrLargeIcon : kAttrMiniIcon;
1449 //	type = which == B_LARGE_ICON ? LARGE_ICON_TYPE : MINI_ICON_TYPE;
1450 //
1451 //	result = file->GetAttrInfo(attrName, &ainfo);
1452 //	if (result < B_OK)
1453 //		return result;
1454 //
1455 //	uint8 buffer[ainfo.size];
1456 //
1457 //	ssize_t readResult = file->ReadAttr(attrName, type, 0, buffer, ainfo.size);
1458 //	if (readResult <= 0)
1459 //		return (status_t)readResult;
1460 //
1461 //	if (icon->ColorSpace() != B_CMAP8)
1462 //		result = BIconUtils::ConvertFromCMAP8(buffer, which, which, which, icon);
1463 //	else
1464 //		icon->SetBits(buffer, icon->BitsLength(), 0, B_CMAP8);
1465 //
1466 //	return result;
1467 //#endif	// B_APP_FILE_INFO_IS_FAST
1468 }
1469 
1470 
1471 status_t
1472 GetFileIconFromAttr(BNode* node, BBitmap* icon, icon_size which)
1473 {
1474 	BNodeInfo fileInfo(node);
1475 	return fileInfo.GetIcon(icon, which);
1476 }
1477 
1478 
1479 void
1480 PrintToStream(rgb_color color)
1481 {
1482 	printf("r:%x, g:%x, b:%x, a:%x\n",
1483 		color.red, color.green, color.blue, color.alpha);
1484 }
1485 
1486 
1487 extern BMenuItem*
1488 EachMenuItem(BMenu* menu, bool recursive, BMenuItem* (*func)(BMenuItem *))
1489 {
1490 	int32 count = menu->CountItems();
1491 	for (int32 index = 0; index < count; index++) {
1492 		BMenuItem* item = menu->ItemAt(index);
1493 		BMenuItem* newItem = (func)(item);
1494 		if (newItem != NULL)
1495 			return newItem;
1496 
1497 		if (recursive) {
1498 			BMenu* submenu = menu->SubmenuAt(index);
1499 			if (submenu != NULL)
1500 				return EachMenuItem(submenu, true, func);
1501 		}
1502 	}
1503 
1504 	return NULL;
1505 }
1506 
1507 
1508 extern const BMenuItem*
1509 EachMenuItem(const BMenu* menu, bool recursive,
1510 	BMenuItem* (*func)(const BMenuItem *))
1511 {
1512 	int32 count = menu->CountItems();
1513 	for (int32 index = 0; index < count; index++) {
1514 		BMenuItem* item = menu->ItemAt(index);
1515 		BMenuItem* newItem = (func)(item);
1516 		if (newItem != NULL)
1517 			return newItem;
1518 
1519 		if (recursive) {
1520 			BMenu* submenu = menu->SubmenuAt(index);
1521 			if (submenu != NULL)
1522 				return EachMenuItem(submenu, true, func);
1523 		}
1524 	}
1525 
1526 	return NULL;
1527 }
1528 
1529 
1530 //	#pragma mark - PositionPassingMenuItem
1531 
1532 
1533 PositionPassingMenuItem::PositionPassingMenuItem(const char* title,
1534 	BMessage* message, char shortcut, uint32 modifiers)
1535 	:
1536 	BMenuItem(title, message, shortcut, modifiers)
1537 {
1538 }
1539 
1540 
1541 PositionPassingMenuItem::PositionPassingMenuItem(BMenu* menu, BMessage* message)
1542 	:
1543 	BMenuItem(menu, message)
1544 {
1545 }
1546 
1547 
1548 PositionPassingMenuItem::PositionPassingMenuItem(BMessage* data)
1549 	:
1550 	BMenuItem(data)
1551 {
1552 }
1553 
1554 
1555 BArchivable*
1556 PositionPassingMenuItem::Instantiate(BMessage* data)
1557 {
1558 	if (validate_instantiation(data, "PositionPassingMenuItem"))
1559 		return new PositionPassingMenuItem(data);
1560 
1561 	return NULL;
1562 }
1563 
1564 
1565 status_t
1566 PositionPassingMenuItem::Invoke(BMessage* message)
1567 {
1568 	if (Menu() == NULL)
1569 		return B_ERROR;
1570 
1571 	if (!IsEnabled())
1572 		return B_ERROR;
1573 
1574 	if (message == NULL)
1575 		message = Message();
1576 
1577 	if (message == NULL)
1578 		return B_BAD_VALUE;
1579 
1580 	BMessage clone(*message);
1581 	clone.AddInt32("index", Menu()->IndexOf(this));
1582 	clone.AddInt64("when", system_time());
1583 	clone.AddPointer("source", this);
1584 
1585 	// embed the invoke location of the menu so that we can create
1586 	// a new folder, etc. on the spot
1587 	BMenu* menu = Menu();
1588 
1589 	for (;;) {
1590 		if (!menu->Supermenu())
1591 			break;
1592 
1593 		menu = menu->Supermenu();
1594 	}
1595 
1596 	// use the window position only, if the item was invoked from the menu
1597 	// menu->Window() points to the window the item was invoked from
1598 	if (dynamic_cast<BContainerWindow*>(menu->Window()) == NULL) {
1599 		LooperAutoLocker lock(menu);
1600 		if (lock.IsLocked()) {
1601 			BPoint invokeOrigin(menu->Window()->Frame().LeftTop());
1602 			clone.AddPoint("be:invoke_origin", invokeOrigin);
1603 		}
1604 	}
1605 
1606 	return BInvoker::Invoke(&clone);
1607 }
1608 
1609 
1610 //	#pragma mark - BPrivate functions
1611 
1612 
1613 bool
1614 BootedInSafeMode()
1615 {
1616 	const char* safeMode = getenv("SAFEMODE");
1617 	return (safeMode && strcmp(safeMode, "yes") == 0);
1618 }
1619 
1620 
1621 float
1622 ComputeTypeAheadScore(const char* text, const char* match, bool wordMode)
1623 {
1624 	// highest score: exact match
1625 	const char* found = strcasestr(text, match);
1626 	if (found != NULL) {
1627 		if (found == text)
1628 			return kExactMatchScore;
1629 
1630 		return 1.f / (found - text);
1631 	}
1632 
1633 	// there was no exact match
1634 
1635 	// second best: all characters at word beginnings
1636 	if (wordMode) {
1637 		float score = 0;
1638 		for (int32 j = 0, k = 0; match[j]; j++) {
1639 			while (text[k]
1640 				&& tolower(text[k]) != tolower(match[j])) {
1641 				k++;
1642 			}
1643 			if (text[k] == '\0') {
1644 				score = 0;
1645 				break;
1646 			}
1647 
1648 			bool wordStart = k == 0 || isspace(text[k - 1]);
1649 			if (wordStart)
1650 				score++;
1651 			if (j > 0) {
1652 				bool wordEnd = !text[k + 1] || isspace(text[k + 1]);
1653 				if (wordEnd)
1654 					score += 0.3;
1655 				if (match[j - 1] == text[k - 1])
1656 					score += 0.7;
1657 			}
1658 
1659 			score += 1.f / (k + 1);
1660 			k++;
1661 		}
1662 
1663 		return score;
1664 	}
1665 
1666 	return -1;
1667 }
1668 
1669 
1670 //	#pragma mark - throw on error functions.
1671 
1672 
1673 void
1674 _ThrowOnError(status_t result, const char* DEBUG_ONLY(file),
1675 	int32 DEBUG_ONLY(line))
1676 {
1677 	if (result != B_OK) {
1678 		PRINT(("%s at %s:%d\n", strerror(result), file, (int)line));
1679 		throw result;
1680 	}
1681 }
1682 
1683 
1684 void
1685 _ThrowIfNotSize(ssize_t size, const char* DEBUG_ONLY(file),
1686 	int32 DEBUG_ONLY(line))
1687 {
1688 	if (size < B_OK) {
1689 		PRINT(("%s at %s:%d\n", strerror((status_t)size), file, (int)line));
1690 		throw (status_t)size;
1691 	}
1692 }
1693 
1694 
1695 void
1696 _ThrowOnAssert(bool success, const char* DEBUG_ONLY(file),
1697 	int32 DEBUG_ONLY(line))
1698 {
1699 	if (!success) {
1700 		PRINT(("Assert failed at %s:%d\n", file, (int)line));
1701 		throw B_ERROR;
1702 	}
1703 }
1704 
1705 } // namespace BPrivate
1706