xref: /haiku/src/kits/tracker/Utilities.cpp (revision 239222b2369c39dc52df52b0a7cdd6cc0a91bc92)
1 /*
2 Open Tracker License
3 
4 Terms and Conditions
5 
6 Copyright (c) 1991-2000, Be Incorporated. All rights reserved.
7 
8 Permission is hereby granted, free of charge, to any person obtaining a copy of
9 this software and associated documentation files (the "Software"), to deal in
10 the Software without restriction, including without limitation the rights to
11 use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
12 of the Software, and to permit persons to whom the Software is furnished to do
13 so, subject to the following conditions:
14 
15 The above copyright notice and this permission notice applies to all licensees
16 and shall be included in all copies or substantial portions of the Software.
17 
18 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
19 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF TITLE, MERCHANTABILITY,
20 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
21 BE INCORPORATED BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN
22 AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF, OR IN CONNECTION
23 WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
24 
25 Except as contained in this notice, the name of Be Incorporated shall not be
26 used in advertising or otherwise to promote the sale, use or other dealings in
27 this Software without prior written authorization from Be Incorporated.
28 
29 Tracker(TM), Be(R), BeOS(R), and BeIA(TM) are trademarks or registered trademarks
30 of Be Incorporated in the United States and other countries. Other brand product
31 names are registered trademarks or trademarks of their respective holders.
32 All rights reserved.
33 */
34 
35 #include "Attributes.h"
36 #include "MimeTypes.h"
37 #include "Model.h"
38 #include "PoseView.h"
39 #include "Utilities.h"
40 #include "ContainerWindow.h"
41 
42 #ifdef __HAIKU__
43 #	include <IconUtils.h>
44 #endif
45 
46 #include <Bitmap.h>
47 #include <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 const rgb_color	kBlack = {0, 0, 0, 255};
86 const rgb_color	kWhite = {255, 255, 255, 255};
87 
88 
89 uint32
90 HashString(const char *string, uint32 seed)
91 {
92 	char ch;
93 	uint32 result = seed;
94 
95 	while((ch = *string++) != 0) {
96 		result = (result << 7) ^ (result >> 24);
97 		result ^= ch;
98 	}
99 
100 	result ^= result << 12;
101 	return result;
102 }
103 
104 
105 uint32
106 AttrHashString(const char *string, uint32 type)
107 {
108 	char c;
109 	uint32 hash = 0;
110 
111 	while((c = *string++) != 0) {
112 		hash = (hash << 7) ^ (hash >> 24);
113 		hash ^= c;
114 	}
115 
116 	hash ^= hash << 12;
117 
118 	hash &= ~0xff;
119 	hash |= type;
120 
121 	return hash;
122 }
123 
124 
125 bool
126 ValidateStream(BMallocIO *stream, uint32 key, int32 version)
127 {
128 	uint32 testKey;
129 	int32 testVersion;
130 
131 	if (stream->Read(&testKey, sizeof(uint32)) <= 0
132 		|| stream->Read(&testVersion, sizeof(int32)) <=0)
133 		return false;
134 
135 	return testKey == key && testVersion == version;
136 }
137 
138 
139 void
140 DisallowFilenameKeys(BTextView *textView)
141 {
142 	textView->DisallowChar('/');
143 }
144 
145 
146 void
147 DisallowMetaKeys(BTextView *textView)
148 {
149 	textView->DisallowChar(B_TAB);
150 	textView->DisallowChar(B_ESCAPE);
151 	textView->DisallowChar(B_INSERT);
152 	textView->DisallowChar(B_DELETE);
153 	textView->DisallowChar(B_HOME);
154 	textView->DisallowChar(B_END);
155 	textView->DisallowChar(B_PAGE_UP);
156 	textView->DisallowChar(B_PAGE_DOWN);
157 	textView->DisallowChar(B_FUNCTION_KEY);
158 }
159 
160 
161 PeriodicUpdatePoses::PeriodicUpdatePoses()
162 	:	fPoseList(20, true)
163 {
164 	fLock = new Benaphore("PeriodicUpdatePoses");
165 }
166 
167 
168 PeriodicUpdatePoses::~PeriodicUpdatePoses()
169 {
170 	fLock->Lock();
171 	fPoseList.MakeEmpty();
172 	delete fLock;
173 }
174 
175 
176 void
177 PeriodicUpdatePoses::AddPose(BPose *pose, BPoseView *poseView,
178 	PeriodicUpdateCallback callback, void *cookie)
179 {
180 	periodic_pose *periodic = new periodic_pose;
181 	periodic->pose = pose;
182 	periodic->pose_view = poseView;
183 	periodic->callback = callback;
184 	periodic->cookie = cookie;
185 	fPoseList.AddItem(periodic);
186 }
187 
188 
189 bool
190 PeriodicUpdatePoses::RemovePose(BPose *pose, void **cookie)
191 {
192 	int32 count = fPoseList.CountItems();
193 	for (int32 index = 0; index < count; index++) {
194 		if (fPoseList.ItemAt(index)->pose == pose) {
195 			if (!fLock->Lock())
196 				return false;
197 
198 			periodic_pose *periodic = fPoseList.RemoveItemAt(index);
199 			if (cookie)
200 				*cookie = periodic->cookie;
201 			delete periodic;
202 			fLock->Unlock();
203 			return true;
204 		}
205 	}
206 
207 	return false;
208 }
209 
210 
211 void
212 PeriodicUpdatePoses::DoPeriodicUpdate(bool forceRedraw)
213 {
214 	if (!fLock->Lock())
215 		return;
216 
217 	int32 count = fPoseList.CountItems();
218 	for (int32 index = 0; index < count; index++) {
219 		periodic_pose *periodic = fPoseList.ItemAt(index);
220 		if (periodic->callback(periodic->pose, periodic->cookie)
221 			|| forceRedraw) {
222 			periodic->pose_view->LockLooper();
223 			periodic->pose_view->UpdateIcon(periodic->pose);
224 			periodic->pose_view->UnlockLooper();
225 		}
226 	}
227 
228 	fLock->Unlock();
229 }
230 
231 
232 PeriodicUpdatePoses gPeriodicUpdatePoses;
233 
234 }	// namespace BPrivate
235 
236 
237 void
238 PoseInfo::EndianSwap(void *castToThis)
239 {
240 	PoseInfo *self = (PoseInfo *)castToThis;
241 
242 	PRINT(("swapping PoseInfo\n"));
243 
244 	STATIC_ASSERT(sizeof(ino_t) == sizeof(int64));
245 	self->fInitedDirectory = SwapInt64(self->fInitedDirectory);
246 	swap_data(B_POINT_TYPE, &self->fLocation, sizeof(BPoint), B_SWAP_ALWAYS);
247 
248 	// do a sanity check on the icon position
249 	if (self->fLocation.x < -20000 || self->fLocation.x > 20000
250 		|| self->fLocation.y < -20000 || self->fLocation.y > 20000) {
251 		// position out of range, force autoplcemement
252 		PRINT((" rejecting icon position out of range\n"));
253 		self->fInitedDirectory = -1LL;
254 		self->fLocation = BPoint(0, 0);
255 	}
256 }
257 
258 
259 void
260 PoseInfo::PrintToStream()
261 {
262 	PRINT(("%s, inode:%Lx, location %f %f\n", fInvisible ? "hidden" : "visible",
263 		fInitedDirectory, fLocation.x, fLocation.y));
264 }
265 
266 
267 // #pragma mark -
268 
269 
270 size_t
271 ExtendedPoseInfo::Size() const
272 {
273 	return sizeof(ExtendedPoseInfo) + fNumFrames * sizeof(FrameLocation);
274 }
275 
276 
277 size_t
278 ExtendedPoseInfo::Size(int32 count)
279 {
280 	return sizeof(ExtendedPoseInfo) + count * sizeof(FrameLocation);
281 }
282 
283 
284 size_t
285 ExtendedPoseInfo::SizeWithHeadroom() const
286 {
287 	return sizeof(ExtendedPoseInfo) + (fNumFrames + 1) * sizeof(FrameLocation);
288 }
289 
290 
291 size_t
292 ExtendedPoseInfo::SizeWithHeadroom(size_t oldSize)
293 {
294 	int32 count = (ssize_t)oldSize - (ssize_t)sizeof(ExtendedPoseInfo);
295 	if (count > 0)
296 		count /= sizeof(FrameLocation);
297 	else
298 		count = 0;
299 
300 	return Size(count + 1);
301 }
302 
303 
304 bool
305 ExtendedPoseInfo::HasLocationForFrame(BRect frame) const
306 {
307 	for (int32 index = 0; index < fNumFrames; index++) {
308 		if (fLocations[index].fFrame == frame)
309 			return true;
310 	}
311 
312 	return false;
313 }
314 
315 
316 BPoint
317 ExtendedPoseInfo::LocationForFrame(BRect frame) const
318 {
319 	for (int32 index = 0; index < fNumFrames; index++) {
320 		if (fLocations[index].fFrame == frame)
321 			return fLocations[index].fLocation;
322 	}
323 
324 	TRESPASS();
325 	return BPoint(0, 0);
326 }
327 
328 
329 bool
330 ExtendedPoseInfo::SetLocationForFrame(BPoint newLocation, BRect frame)
331 {
332 	for (int32 index = 0; index < fNumFrames; index++) {
333 		if (fLocations[index].fFrame == frame) {
334 			if (fLocations[index].fLocation == newLocation)
335 				return false;
336 
337 			fLocations[index].fLocation = newLocation;
338 			return true;
339 		}
340 	}
341 
342 	fLocations[fNumFrames].fFrame = frame;
343 	fLocations[fNumFrames].fLocation = newLocation;
344 	fLocations[fNumFrames].fWorkspaces = 0xffffffff;
345 	fNumFrames++;
346 	return true;
347 }
348 
349 
350 void
351 ExtendedPoseInfo::EndianSwap(void *castToThis)
352 {
353 	ExtendedPoseInfo *self = (ExtendedPoseInfo *)castToThis;
354 
355 	PRINT(("swapping ExtendedPoseInfo\n"));
356 
357 	self->fWorkspaces = SwapUInt32(self->fWorkspaces);
358 	self->fNumFrames = SwapInt32(self->fNumFrames);
359 
360 	for (int32 index = 0; index < self->fNumFrames; index++) {
361 		swap_data(B_POINT_TYPE, &self->fLocations[index].fLocation,
362 			sizeof(BPoint), B_SWAP_ALWAYS);
363 
364 		if (self->fLocations[index].fLocation.x < -20000
365 			|| self->fLocations[index].fLocation.x > 20000
366 			|| self->fLocations[index].fLocation.y < -20000
367 			|| self->fLocations[index].fLocation.y > 20000) {
368 			// position out of range, force autoplcemement
369 			PRINT((" rejecting icon position out of range\n"));
370 			self->fLocations[index].fLocation = BPoint(0, 0);
371 		}
372 		swap_data(B_RECT_TYPE, &self->fLocations[index].fFrame,
373 			sizeof(BRect), B_SWAP_ALWAYS);
374 	}
375 }
376 
377 
378 void
379 ExtendedPoseInfo::PrintToStream()
380 {
381 }
382 
383 
384 // #pragma mark -
385 
386 
387 OffscreenBitmap::OffscreenBitmap(BRect frame)
388 	:
389 	fBitmap(NULL)
390 {
391 	NewBitmap(frame);
392 }
393 
394 
395 OffscreenBitmap::OffscreenBitmap()
396 	:
397 	fBitmap(NULL)
398 {
399 }
400 
401 
402 OffscreenBitmap::~OffscreenBitmap()
403 {
404 	delete fBitmap;
405 }
406 
407 
408 void
409 OffscreenBitmap::NewBitmap(BRect bounds)
410 {
411 	delete fBitmap;
412 	fBitmap = new BBitmap(bounds, B_RGB32, true);
413 	if (fBitmap->Lock()) {
414 		BView *view = new BView(fBitmap->Bounds(), "", B_FOLLOW_NONE, 0);
415 		fBitmap->AddChild(view);
416 
417 		BRect clipRect = view->Bounds();
418 		BRegion newClip;
419 		newClip.Set(clipRect);
420 		view->ConstrainClippingRegion(&newClip);
421 
422 		fBitmap->Unlock();
423 	} else {
424 		delete fBitmap;
425 		fBitmap = NULL;
426 	}
427 }
428 
429 
430 BView *
431 OffscreenBitmap::BeginUsing(BRect frame)
432 {
433 	if (!fBitmap || fBitmap->Bounds() != frame)
434 		NewBitmap(frame);
435 
436 	fBitmap->Lock();
437 	return View();
438 }
439 
440 
441 void
442 OffscreenBitmap::DoneUsing()
443 {
444 	fBitmap->Unlock();
445 }
446 
447 
448 BBitmap *
449 OffscreenBitmap::Bitmap() const
450 {
451 	ASSERT(fBitmap);
452 	ASSERT(fBitmap->IsLocked());
453 	return fBitmap;
454 }
455 
456 
457 BView *
458 OffscreenBitmap::View() const
459 {
460 	ASSERT(fBitmap);
461 	return fBitmap->ChildAt(0);
462 }
463 
464 
465 // #pragma mark -
466 
467 
468 namespace BPrivate {
469 
470 /** Changes the alpha value of the given bitmap to create a nice
471  *	horizontal fade out in the specified region.
472  *	"from" is always transparent, "to" opaque.
473  */
474 
475 void
476 FadeRGBA32Horizontal(uint32 *bits, int32 width, int32 height, int32 from, int32 to)
477 {
478 	// check parameters
479 	if (width < 0 || height < 0 || from < 0 || to < 0)
480 		return;
481 
482 	float change = 1.f / (to - from);
483 	if (from > to) {
484 		int32 temp = from;
485 		from = to;
486 		to = temp;
487 	}
488 
489 	for (int32 y = 0; y < height; y++) {
490 		float alpha = change > 0 ? 0.0f : 1.0f;
491 
492 		for (int32 x = from; x <= to; x++) {
493 			if (bits[x] & 0xff000000) {
494 				uint32 a = uint32((bits[x] >> 24) * alpha);
495 				bits[x] = (bits[x] & 0x00ffffff) | (a << 24);
496 			}
497 			alpha += change;
498 		}
499 		bits += width;
500 	}
501 }
502 
503 
504 /** Changes the alpha value of the given bitmap to create a nice
505  *	vertical fade out in the specified region.
506  *	"from" is always transparent, "to" opaque.
507  */
508 
509 void
510 FadeRGBA32Vertical(uint32 *bits, int32 width, int32 height, int32 from, int32 to)
511 {
512 	// check parameters
513 	if (width < 0 || height < 0 || from < 0 || to < 0)
514 		return;
515 
516 	if (from > to)
517 		bits += width * (height - (from - to));
518 
519 	float change = 1.f / (to - from);
520 	if (from > to) {
521 		int32 temp = from;
522 		from = to;
523 		to = temp;
524 	}
525 
526 	float alpha = change > 0 ? 0.0f : 1.0f;
527 
528 	for (int32 y = from; y <= to; y++) {
529 		for (int32 x = 0; x < width; x++) {
530 			if (bits[x] & 0xff000000) {
531 				uint32 a = uint32((bits[x] >> 24) * alpha);
532 				bits[x] = (bits[x] & 0x00ffffff) | (a << 24);
533 			}
534 		}
535 		alpha += change;
536 		bits += width;
537 	}
538 }
539 
540 }	// namespace BPrivate
541 
542 
543 // #pragma mark -
544 
545 
546 DraggableIcon::DraggableIcon(BRect rect, const char *name, const char *mimeType,
547 		icon_size size, const BMessage *message, BMessenger target,
548 		uint32 resizeMask, uint32 flags)
549 	: BView(rect, name, resizeMask, flags),
550 	fMessage(*message),
551 	fTarget(target)
552 {
553 	fBitmap = new BBitmap(Bounds(), kDefaultIconDepth);
554 	BMimeType mime(mimeType);
555 	status_t error = mime.GetIcon(fBitmap, size);
556 	ASSERT(mime.IsValid());
557 	if (error != B_OK) {
558 		PRINT(("failed to get icon for %s, %s\n", mimeType, strerror(error)));
559 		BMimeType mime(B_FILE_MIMETYPE);
560 		ASSERT(mime.IsInstalled());
561 		mime.GetIcon(fBitmap, size);
562 	}
563 }
564 
565 
566 DraggableIcon::~DraggableIcon()
567 {
568 	delete fBitmap;
569 }
570 
571 
572 void
573 DraggableIcon::SetTarget(BMessenger target)
574 {
575 	fTarget = target;
576 }
577 
578 
579 BRect
580 DraggableIcon::PreferredRect(BPoint offset, icon_size size)
581 {
582 	BRect result(0, 0, size - 1, size - 1);
583 	result.OffsetTo(offset);
584 	return result;
585 }
586 
587 
588 void
589 DraggableIcon::AttachedToWindow()
590 {
591 	BView *parent = Parent();
592 	if (parent) {
593 		SetViewColor(parent->ViewColor());
594 		SetLowColor(parent->LowColor());
595 	}
596 }
597 
598 
599 void
600 DraggableIcon::MouseDown(BPoint point)
601 {
602 	if (!DragStarted(&fMessage))
603 		return;
604 
605 	BRect rect(Bounds());
606 	BBitmap *dragBitmap = new BBitmap(rect, B_RGBA32, true);
607 	dragBitmap->Lock();
608 	BView *view = new BView(dragBitmap->Bounds(), "", B_FOLLOW_NONE, 0);
609 	dragBitmap->AddChild(view);
610 	view->SetOrigin(0, 0);
611 	BRect clipRect(view->Bounds());
612 	BRegion newClip;
613 	newClip.Set(clipRect);
614 	view->ConstrainClippingRegion(&newClip);
615 
616 	// Transparent draw magic
617 	view->SetHighColor(0, 0, 0, 0);
618 	view->FillRect(view->Bounds());
619 	view->SetDrawingMode(B_OP_ALPHA);
620 	view->SetHighColor(0, 0, 0, 128);
621 		// set the level of transparency by value
622 	view->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_COMPOSITE);
623 	view->DrawBitmap(fBitmap);
624 	view->Sync();
625 	dragBitmap->Unlock();
626 	DragMessage(&fMessage, dragBitmap, B_OP_ALPHA, point, fTarget.Target(0));
627 }
628 
629 
630 bool
631 DraggableIcon::DragStarted(BMessage *)
632 {
633 	return true;
634 }
635 
636 
637 void
638 DraggableIcon::Draw(BRect)
639 {
640 #ifdef __HAIKU__
641 	SetDrawingMode(B_OP_ALPHA);
642 	SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
643 #else
644 	SetDrawingMode(B_OP_OVER);
645 #endif
646 	DrawBitmap(fBitmap);
647 }
648 
649 
650 // #pragma mark -
651 
652 
653 FlickerFreeStringView::FlickerFreeStringView(BRect bounds, const char *name,
654 		const char *text, uint32 resizeFlags, uint32 flags)
655 	: BStringView(bounds, name, text, resizeFlags, flags),
656 	fBitmap(NULL),
657 	fOrigBitmap(NULL)
658 {
659 }
660 
661 
662 FlickerFreeStringView::FlickerFreeStringView(BRect bounds, const char *name,
663 		const char *text, BBitmap *inBitmap, uint32 resizeFlags, uint32 flags)
664 	: BStringView(bounds, name, text, resizeFlags, flags),
665 	fBitmap(NULL),
666 	fOrigBitmap(inBitmap)
667 {
668 }
669 
670 
671 FlickerFreeStringView::~FlickerFreeStringView()
672 {
673 	delete fBitmap;
674 }
675 
676 
677 void
678 FlickerFreeStringView::Draw(BRect)
679 {
680 	BRect bounds(Bounds());
681 	if (!fBitmap)
682 		fBitmap = new OffscreenBitmap(Bounds());
683 
684 	BView *offscreen = fBitmap->BeginUsing(bounds);
685 
686 	if (Parent()) {
687 		fViewColor = Parent()->ViewColor();
688 		fLowColor = Parent()->ViewColor();
689 	}
690 
691 	offscreen->SetViewColor(fViewColor);
692 	offscreen->SetHighColor(HighColor());
693 	offscreen->SetLowColor(fLowColor);
694 
695 	BFont font;
696     GetFont(&font);
697 	offscreen->SetFont(&font);
698 
699 	offscreen->Sync();
700 	if (fOrigBitmap)
701 		offscreen->DrawBitmap(fOrigBitmap, Frame(), bounds);
702 	else
703 		offscreen->FillRect(bounds, B_SOLID_LOW);
704 
705 	if (Text()) {
706 		BPoint loc;
707 
708 		font_height	height;
709 		GetFontHeight(&height);
710 
711 		edge_info eInfo;
712 		switch (Alignment()) {
713 			case B_ALIGN_LEFT:
714 #ifdef HAIKU_TARGET_PLATFORM_HAIKU
715 			case B_ALIGN_HORIZONTAL_UNSET:
716 			case B_ALIGN_USE_FULL_WIDTH:
717 #endif
718 			{
719 				// If the first char has a negative left edge give it
720 				// some more room by shifting that much more to the right.
721 				font.GetEdges(Text(), 1, &eInfo);
722 				loc.x = bounds.left + (2 - eInfo.left);
723 				break;
724 			}
725 
726 			case B_ALIGN_CENTER:
727 			{
728 				float width = StringWidth(Text());
729 				float center = (bounds.right - bounds.left) / 2;
730 				loc.x = center - (width/2);
731 				break;
732 			}
733 
734 			case B_ALIGN_RIGHT:
735 			{
736 				float width = StringWidth(Text());
737 				loc.x = bounds.right - width - 2;
738 				break;
739 			}
740 		}
741 		loc.y = bounds.bottom - (1 + height.descent);
742 		offscreen->DrawString(Text(), loc);
743 	}
744 	offscreen->Sync();
745 	SetDrawingMode(B_OP_COPY);
746 	DrawBitmap(fBitmap->Bitmap());
747 	fBitmap->DoneUsing();
748 }
749 
750 
751 void
752 FlickerFreeStringView::AttachedToWindow()
753 {
754 	_inherited::AttachedToWindow();
755 	if (Parent()) {
756 		fViewColor = Parent()->ViewColor();
757 		fLowColor = Parent()->ViewColor();
758 	}
759 	SetViewColor(B_TRANSPARENT_32_BIT);
760 	SetLowColor(B_TRANSPARENT_32_BIT);
761 }
762 
763 
764 void
765 FlickerFreeStringView::SetViewColor(rgb_color color)
766 {
767 	if (fViewColor != color) {
768 		fViewColor = color;
769 		Invalidate();
770 	}
771 	_inherited::SetViewColor(B_TRANSPARENT_32_BIT);
772 }
773 
774 
775 void
776 FlickerFreeStringView::SetLowColor(rgb_color color)
777 {
778 	if (fLowColor != color) {
779 		fLowColor = color;
780 		Invalidate();
781 	}
782 	_inherited::SetLowColor(B_TRANSPARENT_32_BIT);
783 }
784 
785 
786 // #pragma mark -
787 
788 
789 TitledSeparatorItem::TitledSeparatorItem(const char *label)
790 	: BMenuItem(label, 0)
791 {
792 	_inherited::SetEnabled(false);
793 }
794 
795 
796 TitledSeparatorItem::~TitledSeparatorItem()
797 {
798 }
799 
800 
801 void
802 TitledSeparatorItem::SetEnabled(bool)
803 {
804 	// leave disabled
805 }
806 
807 
808 void
809 TitledSeparatorItem::GetContentSize(float *width, float *height)
810 {
811 	_inherited::GetContentSize(width, height);
812 }
813 
814 
815 inline rgb_color
816 ShiftMenuBackgroundColor(float by)
817 {
818 	return tint_color(ui_color(B_MENU_BACKGROUND_COLOR), by);
819 }
820 
821 
822 void
823 TitledSeparatorItem::Draw()
824 {
825 	BRect frame(Frame());
826 
827 	BMenu *parent = Menu();
828 	ASSERT(parent);
829 
830 	menu_info minfo;
831 	get_menu_info(&minfo);
832 
833 	if (minfo.separator > 0) {
834 		frame.left += 10;
835 		frame.right -= 10;
836 	} else {
837 		frame.left += 1;
838 		frame.right -= 1;
839 	}
840 
841 	float startX = frame.left;
842 	float endX = frame.right;
843 
844 	float maxStringWidth = endX - startX - (2 * kMinSeparatorStubX
845 		+ 2 * kStubToStringSlotX);
846 
847 	// ToDo:
848 	// handle case where maxStringWidth turns out negative here
849 
850 	BString truncatedLabel(Label());
851 	parent->TruncateString(&truncatedLabel, B_TRUNCATE_END, maxStringWidth);
852 
853 	maxStringWidth = parent->StringWidth(truncatedLabel.String());
854 
855 	// first calculate the length of the stub part of the
856 	// divider line, so we can use it for secondStartX
857 	float firstEndX = ((endX - startX) - maxStringWidth) / 2 - kStubToStringSlotX;
858 	if (firstEndX < 0)
859 		firstEndX = 0;
860 
861 	float secondStartX = endX - firstEndX;
862 
863 	// now finish calculating firstEndX
864 	firstEndX += startX;
865 
866 	parent->PushState();
867 
868 	int32 y = (int32) (frame.top + (frame.bottom - frame.top) / 2);
869 
870 	parent->BeginLineArray(minfo.separator == 2 ? 6 : 4);
871 	parent->AddLine(BPoint(frame.left, y), BPoint(firstEndX, y),
872 		ShiftMenuBackgroundColor(B_DARKEN_1_TINT));
873 	parent->AddLine(BPoint(secondStartX, y), BPoint(frame.right, y),
874 		ShiftMenuBackgroundColor(B_DARKEN_1_TINT));
875 
876 	if (minfo.separator == 2) {
877 		y++;
878 		frame.left++;
879 		frame.right--;
880 		parent->AddLine(BPoint(frame.left,y), BPoint(firstEndX, y),
881 			ShiftMenuBackgroundColor(B_DARKEN_1_TINT));
882 		parent->AddLine(BPoint(secondStartX,y), BPoint(frame.right, y),
883 			ShiftMenuBackgroundColor(B_DARKEN_1_TINT));
884 	}
885 	y++;
886 	if (minfo.separator == 2) {
887 		frame.left++;
888 		frame.right--;
889 	}
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 	parent->EndLineArray();
896 
897 	font_height	finfo;
898 	parent->GetFontHeight(&finfo);
899 
900 	parent->SetLowColor(parent->ViewColor());
901 	BPoint loc(firstEndX + kStubToStringSlotX, ContentLocation().y + finfo.ascent);
902 
903 	parent->MovePenTo(loc + BPoint(1, 1));
904 	parent->SetHighColor(ShiftMenuBackgroundColor(B_DARKEN_1_TINT));
905 	parent->DrawString(truncatedLabel.String());
906 
907 	parent->MovePenTo(loc);
908 	parent->SetHighColor(ShiftMenuBackgroundColor(B_DISABLED_LABEL_TINT));
909 	parent->DrawString(truncatedLabel.String());
910 
911 	parent->PopState();
912 }
913 
914 
915 // #pragma mark -
916 
917 
918 ShortcutFilter::ShortcutFilter(uint32 shortcutKey, uint32 shortcutModifier,
919 		uint32 shortcutWhat, BHandler *target)
920 	: BMessageFilter(B_KEY_DOWN),
921 	fShortcutKey(shortcutKey),
922 	fShortcutModifier(shortcutModifier),
923 	fShortcutWhat(shortcutWhat),
924 	fTarget(target)
925 {
926 }
927 
928 
929 filter_result
930 ShortcutFilter::Filter(BMessage *message, BHandler **)
931 {
932 	if (message->what == B_KEY_DOWN) {
933 		uint32 modifiers;
934 		uint32 rawKeyChar = 0;
935 		uint8 byte = 0;
936 		int32 key = 0;
937 
938 		if (message->FindInt32("modifiers", (int32 *)&modifiers) != B_OK
939 			|| message->FindInt32("raw_char", (int32 *)&rawKeyChar) != B_OK
940 			|| message->FindInt8("byte", (int8 *)&byte) != B_OK
941 			|| message->FindInt32("key", &key) != B_OK)
942 			return B_DISPATCH_MESSAGE;
943 
944 		modifiers &= B_SHIFT_KEY | B_COMMAND_KEY | B_CONTROL_KEY
945 			| B_OPTION_KEY | B_MENU_KEY;
946 			// strip caps lock, etc.
947 
948 		if (modifiers == fShortcutModifier && rawKeyChar == fShortcutKey) {
949 			fTarget->Looper()->PostMessage(fShortcutWhat, fTarget);
950 			return B_SKIP_MESSAGE;
951 		}
952 	}
953 
954 	// let others deal with this
955 	return B_DISPATCH_MESSAGE;
956 }
957 
958 
959 // #pragma mark -
960 
961 
962 namespace BPrivate {
963 
964 void
965 EmbedUniqueVolumeInfo(BMessage *message, const BVolume *volume)
966 {
967 	BDirectory rootDirectory;
968 	time_t created;
969 	fs_info info;
970 
971 	if (volume->GetRootDirectory(&rootDirectory) == B_OK
972 		&& rootDirectory.GetCreationTime(&created) == B_OK
973 		&& fs_stat_dev(volume->Device(), &info) == 0) {
974 		message->AddInt32("creationDate", created);
975 		message->AddInt64("capacity", volume->Capacity());
976 		message->AddString("deviceName", info.device_name);
977 		message->AddString("volumeName", info.volume_name);
978 		message->AddString("fshName", info.fsh_name);
979 	}
980 }
981 
982 
983 status_t
984 MatchArchivedVolume(BVolume *result, const BMessage *message, int32 index)
985 {
986 	time_t created;
987 	off_t capacity;
988 
989 	if (message->FindInt32("creationDate", index, &created) != B_OK
990 		|| message->FindInt64("capacity", index, &capacity) != B_OK)
991 		return B_ERROR;
992 
993 	BVolumeRoster roster;
994 	BVolume volume;
995 	BString deviceName, volumeName, fshName;
996 
997 	if (message->FindString("deviceName", &deviceName) == B_OK
998 		&& message->FindString("volumeName", &volumeName) == B_OK
999 		&& message->FindString("fshName", &fshName) == B_OK) {
1000 		// New style volume identifiers: We have a couple of characteristics,
1001 		// and compute a score from them. The volume with the greatest score
1002 		// (if over a certain threshold) is the one we're looking for. We
1003 		// pick the first volume, in case there is more than one with the
1004 		// same score.
1005 		dev_t foundDevice = -1;
1006 		int foundScore = -1;
1007 		roster.Rewind();
1008 		while (roster.GetNextVolume(&volume) == B_OK) {
1009 			if (volume.IsPersistent() && volume.KnowsQuery()) {
1010 				// get creation time and fs_info
1011 				BDirectory root;
1012 				volume.GetRootDirectory(&root);
1013 				time_t cmpCreated;
1014 				fs_info info;
1015 				if (root.GetCreationTime(&cmpCreated) == B_OK
1016 					&& fs_stat_dev(volume.Device(), &info) == 0) {
1017 					// compute the score
1018 					int score = 0;
1019 
1020 					// creation time
1021 					if (created == cmpCreated)
1022 						score += 5;
1023 					// capacity
1024 					if (capacity == volume.Capacity())
1025 						score += 4;
1026 					// device name
1027 					if (deviceName == info.device_name)
1028 						score += 3;
1029 					// volume name
1030 					if (volumeName == info.volume_name)
1031 						score += 2;
1032 					// fsh name
1033 					if (fshName == info.fsh_name)
1034 						score += 1;
1035 
1036 					// check score
1037 					if (score >= 9 && score > foundScore) {
1038 						foundDevice = volume.Device();
1039 						foundScore = score;
1040 					}
1041 				}
1042 			}
1043 		}
1044 		if (foundDevice >= 0)
1045 			return result->SetTo(foundDevice);
1046 	} else {
1047 		// Old style volume identifiers: We have only creation time and
1048 		// capacity. Both must match.
1049 		roster.Rewind();
1050 		while (roster.GetNextVolume(&volume) == B_OK)
1051 			if (volume.IsPersistent() && volume.KnowsQuery()) {
1052 				BDirectory root;
1053 				volume.GetRootDirectory(&root);
1054 				time_t cmpCreated;
1055 				root.GetCreationTime(&cmpCreated);
1056 				if (created == cmpCreated && capacity == volume.Capacity()) {
1057 					*result = volume;
1058 					return B_OK;
1059 				}
1060 			}
1061 	}
1062 
1063 	return B_DEV_BAD_DRIVE_NUM;
1064 }
1065 
1066 
1067 void
1068 StringFromStream(BString *string, BMallocIO *stream, bool endianSwap)
1069 {
1070 	int32 length;
1071 	stream->Read(&length, sizeof(length));
1072 	if (endianSwap)
1073 		length = SwapInt32(length);
1074 
1075 	if (length < 0 || length > 10000) {
1076 		// TODO: should fail here
1077 		PRINT(("problems instatiating a string, length probably wrong %d\n", length));
1078 		return;
1079 	}
1080 
1081 	char *buffer = string->LockBuffer(length + 1);
1082 	stream->Read(buffer, (size_t)length + 1);
1083 	string->UnlockBuffer(length);
1084 }
1085 
1086 
1087 void
1088 StringToStream(const BString *string, BMallocIO *stream)
1089 {
1090 	int32 length = string->Length();
1091 	stream->Write(&length, sizeof(int32));
1092 	stream->Write(string->String(), (size_t)string->Length() + 1);
1093 }
1094 
1095 
1096 int32
1097 ArchiveSize(const BString *string)
1098 {
1099 	return string->Length() + 1 + (ssize_t)sizeof(int32);
1100 }
1101 
1102 
1103 int32
1104 CountRefs(const BMessage *message)
1105 {
1106 	uint32 type;
1107 	int32 count;
1108 	message->GetInfo("refs", &type, &count);
1109 
1110 	return count;
1111 }
1112 
1113 
1114 static entry_ref *
1115 EachEntryRefCommon(BMessage *message, entry_ref *(*func)(entry_ref *, void *),
1116 	void *passThru, int32 maxCount)
1117 {
1118 	uint32 type;
1119 	int32 count;
1120 	message->GetInfo("refs", &type, &count);
1121 
1122 	if (maxCount >= 0 && count > maxCount)
1123 		count = maxCount;
1124 
1125 	for (int32 index = 0; index < count; index++) {
1126 		entry_ref ref;
1127 		message->FindRef("refs", index, &ref);
1128 		entry_ref *result = (func)(&ref, passThru);
1129 		if (result)
1130 			return result;
1131 	}
1132 
1133 	return NULL;
1134 }
1135 
1136 
1137 bool
1138 ContainsEntryRef(const BMessage *message, const entry_ref *ref)
1139 {
1140 	entry_ref match;
1141 	for (int32 index = 0; (message->FindRef("refs", index, &match) == B_OK); index++) {
1142 		if (*ref == match)
1143 			return true;
1144 	}
1145 
1146 	return false;
1147 }
1148 
1149 
1150 entry_ref *
1151 EachEntryRef(BMessage *message, entry_ref *(*func)(entry_ref *, void *),
1152 	void *passThru)
1153 {
1154 	return EachEntryRefCommon(message, func, passThru, -1);
1155 }
1156 
1157 typedef entry_ref *(*EachEntryIteratee)(entry_ref *, void *);
1158 
1159 const entry_ref *
1160 EachEntryRef(const BMessage *message, const entry_ref *(*func)(const entry_ref *, void *),
1161 	void *passThru)
1162 {
1163 	return EachEntryRefCommon(const_cast<BMessage *>(message),
1164 		(EachEntryIteratee)func, passThru, -1);
1165 }
1166 
1167 
1168 entry_ref *
1169 EachEntryRef(BMessage *message, entry_ref *(*func)(entry_ref *, void *),
1170 	void *passThru, int32 maxCount)
1171 {
1172 	return EachEntryRefCommon(message, func, passThru, maxCount);
1173 }
1174 
1175 
1176 const entry_ref *
1177 EachEntryRef(const BMessage *message, const entry_ref *(*func)(const entry_ref *, void *),
1178 	void *passThru, int32 maxCount)
1179 {
1180 	return EachEntryRefCommon(const_cast<BMessage *>(message),
1181 		(EachEntryIteratee)func, passThru, maxCount);
1182 }
1183 
1184 
1185 void
1186 TruncateLeaf(BString *string)
1187 {
1188 	for (int32 index = string->Length(); index >= 0; index--) {
1189 		if ((*string)[index] == '/') {
1190 			string->Truncate(index + 1);
1191 			return;
1192 		}
1193 	}
1194 }
1195 
1196 
1197 int64
1198 StringToScalar(const char *text)
1199 {
1200 	char *end;
1201 	int64 val;
1202 
1203 	char *buffer = new char [strlen(text) + 1];
1204 	strcpy(buffer, text);
1205 
1206 	if (strstr(buffer, "k") || strstr(buffer, "K")) {
1207 		val = strtoll(buffer, &end, 10);
1208 		val *= kKBSize;
1209 	} else if (strstr(buffer, "mb") || strstr(buffer, "MB")) {
1210 		val = strtoll(buffer, &end, 10);
1211 		val *= kMBSize;
1212 	} else if (strstr(buffer, "gb") || strstr(buffer, "GB")) {
1213 		val = strtoll(buffer, &end, 10);
1214 		val *= kGBSize;
1215 	} else if (strstr(buffer, "byte") || strstr(buffer, "BYTE")) {
1216 		val = strtoll(buffer, &end, 10);
1217 		val *= kGBSize;
1218 	} else {
1219 		// no suffix, try plain byte conversion
1220 		val = strtoll(buffer, &end, 10);
1221 	}
1222 
1223 	delete [] buffer;
1224 	return val;
1225 }
1226 
1227 #if B_BEOS_VERSION <= B_BEOS_VERSION_MAUI && !defined(__HAIKU__)
1228 
1229 bool
1230 operator==(const rgb_color &a, const rgb_color &b)
1231 {
1232 	return a.red == b.red
1233 		&& a.green == b.green
1234 		&& a.blue == b.blue
1235 		&& a.alpha == b.alpha;
1236 }
1237 
1238 
1239 bool
1240 operator!=(const rgb_color &a, const rgb_color &b)
1241 {
1242 	return !operator==(a, b);
1243 }
1244 
1245 #endif
1246 
1247 
1248 static BRect
1249 LineBounds(BPoint where, float length, bool vertical)
1250 {
1251 	BRect result;
1252 	result.SetLeftTop(where);
1253 	result.SetRightBottom(where + BPoint(2, 2));
1254 	if (vertical)
1255 		result.bottom = result.top + length;
1256 	else
1257 		result.right = result.left + length;
1258 
1259 	return result;
1260 }
1261 
1262 
1263 SeparatorLine::SeparatorLine(BPoint where, float length, bool vertical, const char *name)
1264 	: BView(LineBounds(where, length, vertical), name,
1265 		B_FOLLOW_LEFT | B_FOLLOW_TOP, B_WILL_DRAW)
1266 {
1267 	SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
1268 	SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR));
1269 }
1270 
1271 
1272 void
1273 SeparatorLine::Draw(BRect)
1274 {
1275 	BRect bounds(Bounds());
1276 	rgb_color hiliteColor = tint_color(ViewColor(), 1.5f);
1277 
1278 	bool vertical = (bounds.left > bounds.right - 3);
1279 	BeginLineArray(2);
1280 	if (vertical) {
1281 		AddLine(bounds.LeftTop(), bounds.LeftBottom(), hiliteColor);
1282 		AddLine(bounds.LeftTop() + BPoint(1, 0), bounds.LeftBottom() + BPoint(1, 0), kWhite);
1283 	} else {
1284 		AddLine(bounds.LeftTop(), bounds.RightTop(), hiliteColor);
1285 		AddLine(bounds.LeftTop() + BPoint(0, 1), 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, BMenuItem *(*func)(const BMenuItem *))
1502 {
1503 	int32 count = menu->CountItems();
1504 	for (int32 index = 0; index < count; index++) {
1505 		BMenuItem *item = menu->ItemAt(index);
1506 		BMenuItem *result = (func)(item);
1507 		if (result)
1508 			return result;
1509 
1510 		if (recursive) {
1511 			BMenu *submenu = menu->SubmenuAt(index);
1512 			if (submenu)
1513 				return EachMenuItem(submenu, true, func);
1514 		}
1515 	}
1516 
1517 	return NULL;
1518 }
1519 
1520 
1521 PositionPassingMenuItem::PositionPassingMenuItem(const char *title,
1522 		BMessage *message, char shortcut, uint32 modifiers)
1523 	: BMenuItem(title, message, shortcut, modifiers)
1524 {
1525 }
1526 
1527 
1528 PositionPassingMenuItem::PositionPassingMenuItem(BMenu *menu,
1529 		BMessage *message)
1530 	: BMenuItem(menu, message)
1531 {
1532 }
1533 
1534 
1535 status_t
1536 PositionPassingMenuItem::Invoke(BMessage *message)
1537 {
1538 	if (!Menu())
1539 		return B_ERROR;
1540 
1541 	if (!IsEnabled())
1542 		return B_ERROR;
1543 
1544 	if (!message)
1545 		message = Message();
1546 
1547 	if (!message)
1548 		return B_BAD_VALUE;
1549 
1550 	BMessage clone(*message);
1551 	clone.AddInt32("index", Menu()->IndexOf(this));
1552 	clone.AddInt64("when", system_time());
1553 	clone.AddPointer("source", this);
1554 
1555 	// embed the invoke location of the menu so that we can create
1556 	// a new folder, etc. on the spot
1557 	BMenu *menu = Menu();
1558 
1559 	for (;;) {
1560 		if (!menu->Supermenu())
1561 			break;
1562 		menu = menu->Supermenu();
1563 	}
1564 
1565 	// use the window position only, if the item was invoked from the menu
1566 	// menu->Window() points to the window the item was invoked from
1567 	if (dynamic_cast<BContainerWindow *>(menu->Window()) == NULL) {
1568 		LooperAutoLocker lock(menu);
1569 		if (lock.IsLocked()) {
1570 			BPoint invokeOrigin(menu->Window()->Frame().LeftTop());
1571 			clone.AddPoint("be:invoke_origin", invokeOrigin);
1572 		}
1573 	}
1574 
1575 	return BInvoker::Invoke(&clone);
1576 }
1577 
1578 
1579 bool
1580 BootedInSafeMode()
1581 {
1582 	const char *safeMode = getenv("SAFEMODE");
1583 	return (safeMode && strcmp(safeMode, "yes") == 0);
1584 }
1585 
1586 
1587 float
1588 ComputeTypeAheadScore(const char *text, const char *match, size_t matchLength,
1589 	bool wordMode)
1590 {
1591 	// highest score: exact match
1592 	const char* found = strcasestr(text, match);
1593 	if (found != NULL) {
1594 		if (found == text)
1595 			return kExactMatchScore;
1596 
1597 		return 1.f / (found - text);
1598 	}
1599 
1600 	// there was no exact match
1601 
1602 	// second best: all characters at word beginnings
1603 	if (wordMode) {
1604 		float score = 0;
1605 		for (int32 j = 0, k = 0; match[j]; j++) {
1606 			while (text[k]
1607 				&& tolower(text[k]) != tolower(match[j])) {
1608 				k++;
1609 			}
1610 			if (text[k] == '\0') {
1611 				score = 0;
1612 				break;
1613 			}
1614 
1615 			bool wordStart = k == 0 || isspace(text[k - 1]);
1616 			if (wordStart)
1617 				score++;
1618 			if (j > 0) {
1619 				bool wordEnd = !text[k + 1] || isspace(text[k + 1]);
1620 				if (wordEnd)
1621 					score += 0.3;
1622 				if (match[j - 1] == text[k - 1])
1623 					score += 0.7;
1624 			}
1625 
1626 			score += 1.f / (k + 1);
1627 			k++;
1628 		}
1629 
1630 		return score;
1631 	}
1632 
1633 	return -1;
1634 }
1635 
1636 
1637 void
1638 _ThrowOnError(status_t error, const char *DEBUG_ONLY(file), int32 DEBUG_ONLY(line))
1639 {
1640 	if (error != B_OK) {
1641 		PRINT(("failing %s at %s:%d\n", strerror(error), file, line));
1642 		throw error;
1643 	}
1644 }
1645 
1646 
1647 void
1648 _ThrowIfNotSize(ssize_t size, const char *DEBUG_ONLY(file), int32 DEBUG_ONLY(line))
1649 {
1650 	if (size < B_OK) {
1651 		PRINT(("failing %s at %s:%d\n", strerror(size), file, line));
1652 		throw (status_t)size;
1653 	}
1654 }
1655 
1656 
1657 void
1658 _ThrowOnError(status_t error, const char *DEBUG_ONLY(debugString),
1659 	const char *DEBUG_ONLY(file), int32 DEBUG_ONLY(line))
1660 {
1661 	if (error != B_OK) {
1662 		PRINT(("failing %s, %s at %s:%d\n", debugString, strerror(error), file, line));
1663 		throw error;
1664 	}
1665 }
1666 
1667 } // namespace BPrivate
1668