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