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