xref: /haiku/src/kits/tracker/Utilities.cpp (revision 89d652d5e0defd9d095c778709cef82f5f10c357)
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(std::nothrow) BBitmap(bounds, B_RGB32, true);
413 	if (fBitmap && 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 %"
1078 			B_PRId32 "\n", length));
1079 		return;
1080 	}
1081 
1082 	char *buffer = string->LockBuffer(length + 1);
1083 	stream->Read(buffer, (size_t)length + 1);
1084 	string->UnlockBuffer(length);
1085 }
1086 
1087 
1088 void
1089 StringToStream(const BString *string, BMallocIO *stream)
1090 {
1091 	int32 length = string->Length();
1092 	stream->Write(&length, sizeof(int32));
1093 	stream->Write(string->String(), (size_t)string->Length() + 1);
1094 }
1095 
1096 
1097 int32
1098 ArchiveSize(const BString *string)
1099 {
1100 	return string->Length() + 1 + (ssize_t)sizeof(int32);
1101 }
1102 
1103 
1104 int32
1105 CountRefs(const BMessage *message)
1106 {
1107 	uint32 type;
1108 	int32 count;
1109 	message->GetInfo("refs", &type, &count);
1110 
1111 	return count;
1112 }
1113 
1114 
1115 static entry_ref *
1116 EachEntryRefCommon(BMessage *message, entry_ref *(*func)(entry_ref *, void *),
1117 	void *passThru, int32 maxCount)
1118 {
1119 	uint32 type;
1120 	int32 count;
1121 	message->GetInfo("refs", &type, &count);
1122 
1123 	if (maxCount >= 0 && count > maxCount)
1124 		count = maxCount;
1125 
1126 	for (int32 index = 0; index < count; index++) {
1127 		entry_ref ref;
1128 		message->FindRef("refs", index, &ref);
1129 		entry_ref *result = (func)(&ref, passThru);
1130 		if (result)
1131 			return result;
1132 	}
1133 
1134 	return NULL;
1135 }
1136 
1137 
1138 bool
1139 ContainsEntryRef(const BMessage *message, const entry_ref *ref)
1140 {
1141 	entry_ref match;
1142 	for (int32 index = 0; (message->FindRef("refs", index, &match) == B_OK); index++) {
1143 		if (*ref == match)
1144 			return true;
1145 	}
1146 
1147 	return false;
1148 }
1149 
1150 
1151 entry_ref *
1152 EachEntryRef(BMessage *message, entry_ref *(*func)(entry_ref *, void *),
1153 	void *passThru)
1154 {
1155 	return EachEntryRefCommon(message, func, passThru, -1);
1156 }
1157 
1158 typedef entry_ref *(*EachEntryIteratee)(entry_ref *, void *);
1159 
1160 const entry_ref *
1161 EachEntryRef(const BMessage *message, const entry_ref *(*func)(const entry_ref *, void *),
1162 	void *passThru)
1163 {
1164 	return EachEntryRefCommon(const_cast<BMessage *>(message),
1165 		(EachEntryIteratee)func, passThru, -1);
1166 }
1167 
1168 
1169 entry_ref *
1170 EachEntryRef(BMessage *message, entry_ref *(*func)(entry_ref *, void *),
1171 	void *passThru, int32 maxCount)
1172 {
1173 	return EachEntryRefCommon(message, func, passThru, maxCount);
1174 }
1175 
1176 
1177 const entry_ref *
1178 EachEntryRef(const BMessage *message, const entry_ref *(*func)(const entry_ref *, void *),
1179 	void *passThru, int32 maxCount)
1180 {
1181 	return EachEntryRefCommon(const_cast<BMessage *>(message),
1182 		(EachEntryIteratee)func, passThru, maxCount);
1183 }
1184 
1185 
1186 void
1187 TruncateLeaf(BString *string)
1188 {
1189 	for (int32 index = string->Length(); index >= 0; index--) {
1190 		if ((*string)[index] == '/') {
1191 			string->Truncate(index + 1);
1192 			return;
1193 		}
1194 	}
1195 }
1196 
1197 
1198 int64
1199 StringToScalar(const char *text)
1200 {
1201 	char *end;
1202 	int64 val;
1203 
1204 	char *buffer = new char [strlen(text) + 1];
1205 	strcpy(buffer, text);
1206 
1207 	if (strstr(buffer, "k") || strstr(buffer, "K")) {
1208 		val = strtoll(buffer, &end, 10);
1209 		val *= kKBSize;
1210 	} else if (strstr(buffer, "mb") || strstr(buffer, "MB")) {
1211 		val = strtoll(buffer, &end, 10);
1212 		val *= kMBSize;
1213 	} else if (strstr(buffer, "gb") || strstr(buffer, "GB")) {
1214 		val = strtoll(buffer, &end, 10);
1215 		val *= kGBSize;
1216 	} else if (strstr(buffer, "byte") || strstr(buffer, "BYTE")) {
1217 		val = strtoll(buffer, &end, 10);
1218 		val *= kGBSize;
1219 	} else {
1220 		// no suffix, try plain byte conversion
1221 		val = strtoll(buffer, &end, 10);
1222 	}
1223 
1224 	delete [] buffer;
1225 	return val;
1226 }
1227 
1228 #if B_BEOS_VERSION <= B_BEOS_VERSION_MAUI && !defined(__HAIKU__)
1229 
1230 bool
1231 operator==(const rgb_color &a, const rgb_color &b)
1232 {
1233 	return a.red == b.red
1234 		&& a.green == b.green
1235 		&& a.blue == b.blue
1236 		&& a.alpha == b.alpha;
1237 }
1238 
1239 
1240 bool
1241 operator!=(const rgb_color &a, const rgb_color &b)
1242 {
1243 	return !operator==(a, b);
1244 }
1245 
1246 #endif
1247 
1248 
1249 static BRect
1250 LineBounds(BPoint where, float length, bool vertical)
1251 {
1252 	BRect result;
1253 	result.SetLeftTop(where);
1254 	result.SetRightBottom(where + BPoint(2, 2));
1255 	if (vertical)
1256 		result.bottom = result.top + length;
1257 	else
1258 		result.right = result.left + length;
1259 
1260 	return result;
1261 }
1262 
1263 
1264 SeparatorLine::SeparatorLine(BPoint where, float length, bool vertical, const char *name)
1265 	: BView(LineBounds(where, length, vertical), name,
1266 		B_FOLLOW_LEFT | B_FOLLOW_TOP, B_WILL_DRAW)
1267 {
1268 	SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR));
1269 	SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR));
1270 }
1271 
1272 
1273 void
1274 SeparatorLine::Draw(BRect)
1275 {
1276 	BRect bounds(Bounds());
1277 	rgb_color hiliteColor = tint_color(ViewColor(), 1.5f);
1278 
1279 	bool vertical = (bounds.left > bounds.right - 3);
1280 	BeginLineArray(2);
1281 	if (vertical) {
1282 		AddLine(bounds.LeftTop(), bounds.LeftBottom(), hiliteColor);
1283 		AddLine(bounds.LeftTop() + BPoint(1, 0), bounds.LeftBottom() + BPoint(1, 0), kWhite);
1284 	} else {
1285 		AddLine(bounds.LeftTop(), bounds.RightTop(), hiliteColor);
1286 		AddLine(bounds.LeftTop() + BPoint(0, 1), bounds.RightTop() + BPoint(0, 1), kWhite);
1287 	}
1288 	EndLineArray();
1289 }
1290 
1291 
1292 void
1293 HexDump(const void *buf, int32 length)
1294 {
1295 	const int32 kBytesPerLine = 16;
1296 	int32 offset;
1297 	unsigned char *buffer = (unsigned char *)buf;
1298 
1299 	for (offset = 0; ; offset += kBytesPerLine, buffer += kBytesPerLine) {
1300 		int32 remain = length;
1301 		int32 index;
1302 
1303 		printf( "0x%06x: ", (int)offset);
1304 
1305 		for (index = 0; index < kBytesPerLine; index++) {
1306 			if (remain-- > 0)
1307 				printf("%02x%c", buffer[index], remain > 0 ? ',' : ' ');
1308 			else
1309 				printf("   ");
1310 		}
1311 
1312 		remain = length;
1313 		printf(" \'");
1314 		for (index = 0; index < kBytesPerLine; index++) {
1315 			if (remain-- > 0)
1316 				printf("%c", buffer[index] > ' ' ? buffer[index] : '.');
1317 			else
1318 				printf(" ");
1319 		}
1320 		printf("\'\n");
1321 
1322 		length -= kBytesPerLine;
1323 		if (length <= 0)
1324 			break;
1325 	}
1326 	fflush(stdout);
1327 }
1328 
1329 
1330 void
1331 EnableNamedMenuItem(BMenu *menu, const char *itemName, bool on)
1332 {
1333 	BMenuItem *item = menu->FindItem(itemName);
1334 	if (item)
1335 		item->SetEnabled(on);
1336 }
1337 
1338 
1339 void
1340 MarkNamedMenuItem(BMenu *menu, const char *itemName, bool on)
1341 {
1342 	BMenuItem *item = menu->FindItem(itemName);
1343 	if (item)
1344 		item->SetMarked(on);
1345 }
1346 
1347 
1348 void
1349 EnableNamedMenuItem(BMenu *menu, uint32 commandName, bool on)
1350 {
1351 	BMenuItem *item = menu->FindItem(commandName);
1352 	if (item)
1353 		item->SetEnabled(on);
1354 }
1355 
1356 
1357 void
1358 MarkNamedMenuItem(BMenu *menu, uint32 commandName, bool on)
1359 {
1360 	BMenuItem *item = menu->FindItem(commandName);
1361 	if (item)
1362 		item->SetMarked(on);
1363 }
1364 
1365 
1366 void
1367 DeleteSubmenu(BMenuItem *submenuItem)
1368 {
1369 	if (!submenuItem)
1370 		return;
1371 
1372 	BMenu *menu = submenuItem->Submenu();
1373 	if (!menu)
1374 		return;
1375 
1376 	for (;;) {
1377 		BMenuItem *item = menu->RemoveItem((int32)0);
1378 		if (!item)
1379 			return;
1380 
1381 		delete item;
1382 	}
1383 }
1384 
1385 
1386 status_t
1387 GetAppSignatureFromAttr(BFile *file, char *result)
1388 {
1389 	// This call is a performance improvement that
1390 	// avoids using the BAppFileInfo API when retrieving the
1391 	// app signature -- the call is expensive because by default
1392 	// the resource fork is scanned to read the attribute
1393 
1394 #ifdef B_APP_FILE_INFO_IS_FAST
1395 	BAppFileInfo appFileInfo(file);
1396 	return appFileInfo.GetSignature(result);
1397 #else
1398 	ssize_t readResult = file->ReadAttr(kAttrAppSignature, B_MIME_STRING_TYPE,
1399 		0, result, B_MIME_TYPE_LENGTH);
1400 
1401 	if (readResult <= 0)
1402 		return (status_t)readResult;
1403 
1404 	return B_OK;
1405 #endif	// B_APP_FILE_INFO_IS_FAST
1406 }
1407 
1408 
1409 status_t
1410 GetAppIconFromAttr(BFile *file, BBitmap *result, icon_size size)
1411 {
1412 	// This call is a performance improvement that
1413 	// avoids using the BAppFileInfo API when retrieving the
1414 	// app icons -- the call is expensive because by default
1415 	// the resource fork is scanned to read the icons
1416 
1417 //#ifdef B_APP_FILE_INFO_IS_FAST
1418 	BAppFileInfo appFileInfo(file);
1419 	return appFileInfo.GetIcon(result, size);
1420 //#else
1421 //
1422 //	const char *attrName = kAttrIcon;
1423 //	uint32 type = B_VECTOR_ICON_TYPE;
1424 //
1425 //	// try vector icon
1426 //	attr_info ainfo;
1427 //	status_t ret = file->GetAttrInfo(attrName, &ainfo);
1428 //
1429 //	if (ret == B_OK) {
1430 //		uint8 buffer[ainfo.size];
1431 //		ssize_t readResult = file->ReadAttr(attrName, type, 0, buffer,
1432 //											ainfo.size);
1433 //		if (readResult == ainfo.size) {
1434 //			if (BIconUtils::GetVectorIcon(buffer, ainfo.size, result) == B_OK)
1435 //				return B_OK;
1436 //		}
1437 //	}
1438 //
1439 //	// try again with R5 icons
1440 //	attrName = size == B_LARGE_ICON ? kAttrLargeIcon : kAttrMiniIcon;
1441 //	type = size == B_LARGE_ICON ? LARGE_ICON_TYPE : MINI_ICON_TYPE;
1442 //
1443 //	ret = file->GetAttrInfo(attrName, &ainfo);
1444 //	if (ret < B_OK)
1445 //		return ret;
1446 //
1447 //	uint8 buffer[ainfo.size];
1448 //
1449 //	ssize_t readResult = file->ReadAttr(attrName, type, 0, buffer, ainfo.size);
1450 //	if (readResult <= 0)
1451 //		return (status_t)readResult;
1452 //
1453 //	if (result->ColorSpace() != B_CMAP8) {
1454 //		ret = BIconUtils::ConvertFromCMAP8(buffer, size, size, size, result);
1455 //	} else {
1456 //		result->SetBits(buffer, result->BitsLength(), 0, B_CMAP8);
1457 //	}
1458 //
1459 //	return ret;
1460 //#endif	// B_APP_FILE_INFO_IS_FAST
1461 }
1462 
1463 
1464 status_t
1465 GetFileIconFromAttr(BNode *file, BBitmap *result, icon_size size)
1466 {
1467 	BNodeInfo fileInfo(file);
1468 	return fileInfo.GetIcon(result, size);
1469 }
1470 
1471 
1472 void
1473 PrintToStream(rgb_color color)
1474 {
1475 	printf("r:%x, g:%x, b:%x, a:%x\n",
1476 		color.red, color.green, color.blue, color.alpha);
1477 }
1478 
1479 
1480 extern BMenuItem *
1481 EachMenuItem(BMenu *menu, bool recursive, BMenuItem *(*func)(BMenuItem *))
1482 {
1483 	int32 count = menu->CountItems();
1484 	for (int32 index = 0; index < count; index++) {
1485 		BMenuItem *item = menu->ItemAt(index);
1486 		BMenuItem *result = (func)(item);
1487 		if (result)
1488 			return result;
1489 
1490 		if (recursive) {
1491 			BMenu *submenu = menu->SubmenuAt(index);
1492 			if (submenu)
1493 				return EachMenuItem(submenu, true, func);
1494 		}
1495 	}
1496 
1497 	return NULL;
1498 }
1499 
1500 
1501 extern const BMenuItem *
1502 EachMenuItem(const BMenu *menu, bool recursive, BMenuItem *(*func)(const BMenuItem *))
1503 {
1504 	int32 count = menu->CountItems();
1505 	for (int32 index = 0; index < count; index++) {
1506 		BMenuItem *item = menu->ItemAt(index);
1507 		BMenuItem *result = (func)(item);
1508 		if (result)
1509 			return result;
1510 
1511 		if (recursive) {
1512 			BMenu *submenu = menu->SubmenuAt(index);
1513 			if (submenu)
1514 				return EachMenuItem(submenu, true, func);
1515 		}
1516 	}
1517 
1518 	return NULL;
1519 }
1520 
1521 
1522 PositionPassingMenuItem::PositionPassingMenuItem(const char *title,
1523 		BMessage *message, char shortcut, uint32 modifiers)
1524 	: BMenuItem(title, message, shortcut, modifiers)
1525 {
1526 }
1527 
1528 
1529 PositionPassingMenuItem::PositionPassingMenuItem(BMenu *menu,
1530 		BMessage *message)
1531 	: BMenuItem(menu, message)
1532 {
1533 }
1534 
1535 
1536 status_t
1537 PositionPassingMenuItem::Invoke(BMessage *message)
1538 {
1539 	if (!Menu())
1540 		return B_ERROR;
1541 
1542 	if (!IsEnabled())
1543 		return B_ERROR;
1544 
1545 	if (!message)
1546 		message = Message();
1547 
1548 	if (!message)
1549 		return B_BAD_VALUE;
1550 
1551 	BMessage clone(*message);
1552 	clone.AddInt32("index", Menu()->IndexOf(this));
1553 	clone.AddInt64("when", system_time());
1554 	clone.AddPointer("source", this);
1555 
1556 	// embed the invoke location of the menu so that we can create
1557 	// a new folder, etc. on the spot
1558 	BMenu *menu = Menu();
1559 
1560 	for (;;) {
1561 		if (!menu->Supermenu())
1562 			break;
1563 		menu = menu->Supermenu();
1564 	}
1565 
1566 	// use the window position only, if the item was invoked from the menu
1567 	// menu->Window() points to the window the item was invoked from
1568 	if (dynamic_cast<BContainerWindow *>(menu->Window()) == NULL) {
1569 		LooperAutoLocker lock(menu);
1570 		if (lock.IsLocked()) {
1571 			BPoint invokeOrigin(menu->Window()->Frame().LeftTop());
1572 			clone.AddPoint("be:invoke_origin", invokeOrigin);
1573 		}
1574 	}
1575 
1576 	return BInvoker::Invoke(&clone);
1577 }
1578 
1579 
1580 bool
1581 BootedInSafeMode()
1582 {
1583 	const char *safeMode = getenv("SAFEMODE");
1584 	return (safeMode && strcmp(safeMode, "yes") == 0);
1585 }
1586 
1587 
1588 float
1589 ComputeTypeAheadScore(const char *text, const char *match, 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, (int)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, (int)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,
1663 			(int)line));
1664 		throw error;
1665 	}
1666 }
1667 
1668 } // namespace BPrivate
1669