xref: /haiku/src/kits/tracker/Utilities.cpp (revision 6f80a9801fedbe7355c4360bd204ba746ec3ec2d)
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 <Debug.h>
49 #include <Font.h>
50 #include <IconUtils.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 "Attributes.h"
62 #include "ContainerWindow.h"
63 #include "MimeTypes.h"
64 #include "Model.h"
65 #include "PoseView.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 
86 bool gLocalizedNamePreferred;
87 
88 
89 bool
90 SecondaryMouseButtonDown(int32 modifiers, int32 buttons)
91 {
92 	return (buttons & B_SECONDARY_MOUSE_BUTTON) != 0
93 		|| ((buttons & B_PRIMARY_MOUSE_BUTTON) != 0
94 			&& (modifiers & B_CONTROL_KEY) != 0);
95 }
96 
97 
98 uint32
99 HashString(const char* string, uint32 seed)
100 {
101 	char ch;
102 	uint32 hash = seed;
103 
104 	while((ch = *string++) != 0) {
105 		hash = (hash << 7) ^ (hash >> 24);
106 		hash ^= ch;
107 	}
108 	hash ^= hash << 12;
109 
110 	return hash;
111 }
112 
113 
114 uint32
115 AttrHashString(const char* string, uint32 type)
116 {
117 	char c;
118 	uint32 hash = 0;
119 
120 	while((c = *string++) != 0) {
121 		hash = (hash << 7) ^ (hash >> 24);
122 		hash ^= c;
123 	}
124 	hash ^= hash << 12;
125 
126 	hash &= ~0xff;
127 	hash |= type;
128 
129 	return hash;
130 }
131 
132 
133 bool
134 ValidateStream(BMallocIO* stream, uint32 key, int32 version)
135 {
136 	uint32 testKey;
137 	int32 testVersion;
138 
139 	if (stream->Read(&testKey, sizeof(uint32)) <= 0
140 		|| stream->Read(&testVersion, sizeof(int32)) <= 0) {
141 		return false;
142 	}
143 
144 	return testKey == key && testVersion == version;
145 }
146 
147 
148 void
149 DisallowFilenameKeys(BTextView* textView)
150 {
151 	textView->DisallowChar('/');
152 }
153 
154 
155 void
156 DisallowMetaKeys(BTextView* textView)
157 {
158 	textView->DisallowChar(B_TAB);
159 	textView->DisallowChar(B_ESCAPE);
160 	textView->DisallowChar(B_INSERT);
161 	textView->DisallowChar(B_DELETE);
162 	textView->DisallowChar(B_HOME);
163 	textView->DisallowChar(B_END);
164 	textView->DisallowChar(B_PAGE_UP);
165 	textView->DisallowChar(B_PAGE_DOWN);
166 	textView->DisallowChar(B_FUNCTION_KEY);
167 }
168 
169 
170 PeriodicUpdatePoses::PeriodicUpdatePoses()
171 	:
172 	fPoseList(20, true)
173 {
174 	fLock = new Benaphore("PeriodicUpdatePoses");
175 }
176 
177 
178 PeriodicUpdatePoses::~PeriodicUpdatePoses()
179 {
180 	fLock->Lock();
181 	fPoseList.MakeEmpty();
182 	delete fLock;
183 }
184 
185 
186 void
187 PeriodicUpdatePoses::AddPose(BPose* pose, BPoseView* poseView,
188 	PeriodicUpdateCallback callback, void* cookie)
189 {
190 	periodic_pose* periodic = new periodic_pose;
191 	periodic->pose = pose;
192 	periodic->pose_view = poseView;
193 	periodic->callback = callback;
194 	periodic->cookie = cookie;
195 	fPoseList.AddItem(periodic);
196 }
197 
198 
199 bool
200 PeriodicUpdatePoses::RemovePose(BPose* pose, void** cookie)
201 {
202 	int32 count = fPoseList.CountItems();
203 	for (int32 index = 0; index < count; index++) {
204 		if (fPoseList.ItemAt(index)->pose == pose) {
205 			if (!fLock->Lock())
206 				return false;
207 
208 			periodic_pose* periodic = fPoseList.RemoveItemAt(index);
209 			if (cookie)
210 				*cookie = periodic->cookie;
211 			delete periodic;
212 			fLock->Unlock();
213 			return true;
214 		}
215 	}
216 
217 	return false;
218 }
219 
220 
221 void
222 PeriodicUpdatePoses::DoPeriodicUpdate(bool forceRedraw)
223 {
224 	if (!fLock->Lock())
225 		return;
226 
227 	int32 count = fPoseList.CountItems();
228 	for (int32 index = 0; index < count; index++) {
229 		periodic_pose* periodic = fPoseList.ItemAt(index);
230 		if ((periodic->callback(periodic->pose, periodic->cookie)
231 			|| forceRedraw) && periodic->pose_view->LockLooper()) {
232 			periodic->pose_view->UpdateIcon(periodic->pose);
233 			periodic->pose_view->UnlockLooper();
234 		}
235 	}
236 
237 	fLock->Unlock();
238 }
239 
240 
241 PeriodicUpdatePoses gPeriodicUpdatePoses;
242 
243 }	// namespace BPrivate
244 
245 
246 void
247 PoseInfo::EndianSwap(void* castToThis)
248 {
249 	PoseInfo* self = (PoseInfo*)castToThis;
250 
251 	PRINT(("swapping PoseInfo\n"));
252 
253 	STATIC_ASSERT(sizeof(ino_t) == sizeof(int64));
254 	self->fInitedDirectory = SwapInt64(self->fInitedDirectory);
255 	swap_data(B_POINT_TYPE, &self->fLocation, sizeof(BPoint), B_SWAP_ALWAYS);
256 
257 	// do a sanity check on the icon position
258 	if (self->fLocation.x < -20000 || self->fLocation.x > 20000
259 		|| self->fLocation.y < -20000 || self->fLocation.y > 20000) {
260 		// position out of range, force autoplcemement
261 		PRINT((" rejecting icon position out of range\n"));
262 		self->fInitedDirectory = -1LL;
263 		self->fLocation = BPoint(0, 0);
264 	}
265 }
266 
267 
268 void
269 PoseInfo::PrintToStream()
270 {
271 	PRINT(("%s, inode:%" B_PRIx64 ", location %f %f\n",
272 		fInvisible ? "hidden" : "visible",
273 		fInitedDirectory, fLocation.x, fLocation.y));
274 }
275 
276 
277 // #pragma mark - ExtendedPoseInfo
278 
279 
280 size_t
281 ExtendedPoseInfo::Size() const
282 {
283 	return sizeof(ExtendedPoseInfo) + fNumFrames * sizeof(FrameLocation);
284 }
285 
286 
287 size_t
288 ExtendedPoseInfo::Size(int32 count)
289 {
290 	return sizeof(ExtendedPoseInfo) + count * sizeof(FrameLocation);
291 }
292 
293 
294 size_t
295 ExtendedPoseInfo::SizeWithHeadroom() const
296 {
297 	return sizeof(ExtendedPoseInfo) + (fNumFrames + 1) * sizeof(FrameLocation);
298 }
299 
300 
301 size_t
302 ExtendedPoseInfo::SizeWithHeadroom(size_t oldSize)
303 {
304 	int32 count = (ssize_t)oldSize - (ssize_t)sizeof(ExtendedPoseInfo);
305 	if (count > 0)
306 		count /= sizeof(FrameLocation);
307 	else
308 		count = 0;
309 
310 	return Size(count + 1);
311 }
312 
313 
314 bool
315 ExtendedPoseInfo::HasLocationForFrame(BRect frame) const
316 {
317 	for (int32 index = 0; index < fNumFrames; index++) {
318 		if (fLocations[index].fFrame == frame)
319 			return true;
320 	}
321 
322 	return false;
323 }
324 
325 
326 BPoint
327 ExtendedPoseInfo::LocationForFrame(BRect frame) const
328 {
329 	for (int32 index = 0; index < fNumFrames; index++) {
330 		if (fLocations[index].fFrame == frame)
331 			return fLocations[index].fLocation;
332 	}
333 
334 	TRESPASS();
335 	return BPoint(0, 0);
336 }
337 
338 
339 bool
340 ExtendedPoseInfo::SetLocationForFrame(BPoint newLocation, BRect frame)
341 {
342 	for (int32 index = 0; index < fNumFrames; index++) {
343 		if (fLocations[index].fFrame == frame) {
344 			if (fLocations[index].fLocation == newLocation)
345 				return false;
346 
347 			fLocations[index].fLocation = newLocation;
348 			return true;
349 		}
350 	}
351 
352 	fLocations[fNumFrames].fFrame = frame;
353 	fLocations[fNumFrames].fLocation = newLocation;
354 	fLocations[fNumFrames].fWorkspaces = 0xffffffff;
355 	fNumFrames++;
356 
357 	return true;
358 }
359 
360 
361 void
362 ExtendedPoseInfo::EndianSwap(void* castToThis)
363 {
364 	ExtendedPoseInfo* self = (ExtendedPoseInfo *)castToThis;
365 
366 	PRINT(("swapping ExtendedPoseInfo\n"));
367 
368 	self->fWorkspaces = SwapUInt32(self->fWorkspaces);
369 	self->fNumFrames = SwapInt32(self->fNumFrames);
370 
371 	for (int32 index = 0; index < self->fNumFrames; index++) {
372 		swap_data(B_POINT_TYPE, &self->fLocations[index].fLocation,
373 			sizeof(BPoint), B_SWAP_ALWAYS);
374 
375 		if (self->fLocations[index].fLocation.x < -20000
376 			|| self->fLocations[index].fLocation.x > 20000
377 			|| self->fLocations[index].fLocation.y < -20000
378 			|| self->fLocations[index].fLocation.y > 20000) {
379 			// position out of range, force autoplcemement
380 			PRINT((" rejecting icon position out of range\n"));
381 			self->fLocations[index].fLocation = BPoint(0, 0);
382 		}
383 		swap_data(B_RECT_TYPE, &self->fLocations[index].fFrame,
384 			sizeof(BRect), B_SWAP_ALWAYS);
385 	}
386 }
387 
388 
389 void
390 ExtendedPoseInfo::PrintToStream()
391 {
392 }
393 
394 
395 // #pragma mark - OffscreenBitmap
396 
397 
398 OffscreenBitmap::OffscreenBitmap(BRect frame)
399 	:
400 	fBitmap(NULL)
401 {
402 	NewBitmap(frame);
403 }
404 
405 
406 OffscreenBitmap::OffscreenBitmap()
407 	:
408 	fBitmap(NULL)
409 {
410 }
411 
412 
413 OffscreenBitmap::~OffscreenBitmap()
414 {
415 	delete fBitmap;
416 }
417 
418 
419 void
420 OffscreenBitmap::NewBitmap(BRect bounds)
421 {
422 	delete fBitmap;
423 	fBitmap = new(std::nothrow) BBitmap(bounds, B_RGB32, true);
424 	if (fBitmap != NULL && fBitmap->Lock()) {
425 		BView* view = new BView(fBitmap->Bounds(), "", B_FOLLOW_NONE, 0);
426 		fBitmap->AddChild(view);
427 
428 		BRect clipRect = view->Bounds();
429 		BRegion newClip;
430 		newClip.Set(clipRect);
431 		view->ConstrainClippingRegion(&newClip);
432 
433 		fBitmap->Unlock();
434 	} else {
435 		delete fBitmap;
436 		fBitmap = NULL;
437 	}
438 }
439 
440 
441 BView*
442 OffscreenBitmap::BeginUsing(BRect frame)
443 {
444 	if (!fBitmap || fBitmap->Bounds() != frame)
445 		NewBitmap(frame);
446 
447 	fBitmap->Lock();
448 	return View();
449 }
450 
451 
452 void
453 OffscreenBitmap::DoneUsing()
454 {
455 	fBitmap->Unlock();
456 }
457 
458 
459 BBitmap*
460 OffscreenBitmap::Bitmap() const
461 {
462 	ASSERT(fBitmap);
463 	ASSERT(fBitmap->IsLocked());
464 	return fBitmap;
465 }
466 
467 
468 BView*
469 OffscreenBitmap::View() const
470 {
471 	ASSERT(fBitmap);
472 	return fBitmap->ChildAt(0);
473 }
474 
475 
476 // #pragma mark - BPrivate functions
477 
478 
479 namespace BPrivate {
480 
481 // Changes the alpha value of the given bitmap to create a nice
482 // horizontal fade out in the specified region.
483 // "from" is always transparent, "to" opaque.
484 void
485 FadeRGBA32Horizontal(uint32* bits, int32 width, int32 height, int32 from,
486 	int32 to)
487 {
488 	// check parameters
489 	if (width < 0 || height < 0 || from < 0 || to < 0)
490 		return;
491 
492 	float change = 1.f / (to - from);
493 	if (from > to) {
494 		int32 temp = from;
495 		from = to;
496 		to = temp;
497 	}
498 
499 	for (int32 y = 0; y < height; y++) {
500 		float alpha = change > 0 ? 0.0f : 1.0f;
501 
502 		for (int32 x = from; x <= to; x++) {
503 			if (bits[x] & 0xff000000) {
504 				uint32 a = uint32((bits[x] >> 24) * alpha);
505 				bits[x] = (bits[x] & 0x00ffffff) | (a << 24);
506 			}
507 			alpha += change;
508 		}
509 		bits += width;
510 	}
511 }
512 
513 
514 /*!	Changes the alpha value of the given bitmap to create a nice
515 	vertical fade out in the specified region.
516 	"from" is always transparent, "to" opaque.
517 */
518 void
519 FadeRGBA32Vertical(uint32* bits, int32 width, int32 height, int32 from,
520 	int32 to)
521 {
522 	// check parameters
523 	if (width < 0 || height < 0 || from < 0 || to < 0)
524 		return;
525 
526 	if (from > to)
527 		bits += width * (height - (from - to));
528 
529 	float change = 1.f / (to - from);
530 	if (from > to) {
531 		int32 temp = from;
532 		from = to;
533 		to = temp;
534 	}
535 
536 	float alpha = change > 0 ? 0.0f : 1.0f;
537 
538 	for (int32 y = from; y <= to; y++) {
539 		for (int32 x = 0; x < width; x++) {
540 			if (bits[x] & 0xff000000) {
541 				uint32 a = uint32((bits[x] >> 24) * alpha);
542 				bits[x] = (bits[x] & 0x00ffffff) | (a << 24);
543 			}
544 		}
545 		alpha += change;
546 		bits += width;
547 	}
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 = std::max((int32)B_MINI_ICON,
1264 		(int32)ceilf(B_MINI_ICON * be_plain_font->Size() / 12));
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