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