xref: /haiku/src/kits/tracker/Utilities.cpp (revision 17889a8c70dbb3d59c1412f6431968753c767bab)
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 
552 }	// namespace BPrivate
553 
554 
555 // #pragma mark - DraggableIcon
556 
557 
558 DraggableIcon::DraggableIcon(BRect rect, const char* name,
559 	const char* type, icon_size which, const BMessage* message,
560 	BMessenger target, uint32 resizingMode, uint32 flags)
561 	:
562 	BView(rect, name, resizingMode, flags),
563 	fMessage(*message),
564 	fTarget(target)
565 {
566 	fBitmap = new BBitmap(Bounds(), kDefaultIconDepth);
567 	BMimeType mime(type);
568 	status_t result = mime.GetIcon(fBitmap, which);
569 	ASSERT(mime.IsValid());
570 	if (result != B_OK) {
571 		PRINT(("failed to get icon for %s, %s\n", type, strerror(result)));
572 		BMimeType mime(B_FILE_MIMETYPE);
573 		ASSERT(mime.IsInstalled());
574 		mime.GetIcon(fBitmap, which);
575 	}
576 }
577 
578 
579 DraggableIcon::~DraggableIcon()
580 {
581 	delete fBitmap;
582 }
583 
584 
585 void
586 DraggableIcon::SetTarget(BMessenger target)
587 {
588 	fTarget = target;
589 }
590 
591 
592 BRect
593 DraggableIcon::PreferredRect(BPoint offset, icon_size which)
594 {
595 	BRect rect(0, 0, which - 1, which - 1);
596 	rect.OffsetTo(offset);
597 	return rect;
598 }
599 
600 
601 void
602 DraggableIcon::AttachedToWindow()
603 {
604 	AdoptParentColors();
605 }
606 
607 
608 void
609 DraggableIcon::MouseDown(BPoint point)
610 {
611 	if (!DragStarted(&fMessage))
612 		return;
613 
614 	BRect rect(Bounds());
615 	BBitmap* dragBitmap = new BBitmap(rect, B_RGBA32, true);
616 	dragBitmap->Lock();
617 	BView* view = new BView(dragBitmap->Bounds(), "", B_FOLLOW_NONE, 0);
618 	dragBitmap->AddChild(view);
619 	view->SetOrigin(0, 0);
620 	BRect clipRect(view->Bounds());
621 	BRegion newClip;
622 	newClip.Set(clipRect);
623 	view->ConstrainClippingRegion(&newClip);
624 
625 	// Transparent draw magic
626 	view->SetHighColor(0, 0, 0, 0);
627 	view->FillRect(view->Bounds());
628 	view->SetDrawingMode(B_OP_ALPHA);
629 	view->SetHighColor(0, 0, 0, 128);
630 		// set the level of transparency by value
631 	view->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_COMPOSITE);
632 	view->DrawBitmap(fBitmap);
633 	view->Sync();
634 	dragBitmap->Unlock();
635 	DragMessage(&fMessage, dragBitmap, B_OP_ALPHA, point, fTarget.Target(0));
636 }
637 
638 
639 bool
640 DraggableIcon::DragStarted(BMessage*)
641 {
642 	return true;
643 }
644 
645 
646 void
647 DraggableIcon::Draw(BRect)
648 {
649 	SetDrawingMode(B_OP_ALPHA);
650 	SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
651 	DrawBitmap(fBitmap);
652 }
653 
654 
655 // #pragma mark - FlickerFreeStringView
656 
657 
658 FlickerFreeStringView::FlickerFreeStringView(BRect bounds, const char* name,
659 	const char* text, uint32 resizingMode, uint32 flags)
660 	:
661 	BStringView(bounds, name, text, resizingMode, flags),
662 	fBitmap(NULL),
663 	fViewColor(ViewColor()),
664 	fLowColor(LowColor()),
665 	fOriginalBitmap(NULL)
666 {
667 }
668 
669 
670 FlickerFreeStringView::FlickerFreeStringView(BRect bounds, const char* name,
671 	const char* text, BBitmap* inBitmap, uint32 resizingMode, uint32 flags)
672 	:
673 	BStringView(bounds, name, text, resizingMode, flags),
674 	fBitmap(NULL),
675 	fViewColor(ViewColor()),
676 	fLowColor(LowColor()),
677 	fOriginalBitmap(inBitmap)
678 {
679 }
680 
681 
682 FlickerFreeStringView::~FlickerFreeStringView()
683 {
684 	delete fBitmap;
685 }
686 
687 
688 void
689 FlickerFreeStringView::Draw(BRect)
690 {
691 	BRect bounds(Bounds());
692 	if (fBitmap == NULL)
693 		fBitmap = new OffscreenBitmap(Bounds());
694 
695 	BView* offscreen = fBitmap->BeginUsing(bounds);
696 
697 	if (Parent() != NULL) {
698 		fViewColor = Parent()->ViewColor();
699 		fLowColor = Parent()->ViewColor();
700 	}
701 
702 	offscreen->SetViewColor(fViewColor);
703 	offscreen->SetHighColor(HighColor());
704 	offscreen->SetLowColor(fLowColor);
705 
706 	BFont font;
707 	GetFont(&font);
708 	offscreen->SetFont(&font);
709 
710 	offscreen->Sync();
711 	if (fOriginalBitmap != NULL)
712 		offscreen->DrawBitmap(fOriginalBitmap, Frame(), bounds);
713 	else
714 		offscreen->FillRect(bounds, B_SOLID_LOW);
715 
716 	if (Text() != NULL) {
717 		BPoint loc;
718 
719 		font_height	height;
720 		GetFontHeight(&height);
721 
722 		edge_info eInfo;
723 		switch (Alignment()) {
724 			case B_ALIGN_LEFT:
725 			case B_ALIGN_HORIZONTAL_UNSET:
726 			case B_ALIGN_USE_FULL_WIDTH:
727 			{
728 				// If the first char has a negative left edge give it
729 				// some more room by shifting that much more to the right.
730 				font.GetEdges(Text(), 1, &eInfo);
731 				loc.x = bounds.left + (2 - eInfo.left);
732 				break;
733 			}
734 
735 			case B_ALIGN_CENTER:
736 			{
737 				float width = StringWidth(Text());
738 				float center = (bounds.right - bounds.left) / 2;
739 				loc.x = center - (width/2);
740 				break;
741 			}
742 
743 			case B_ALIGN_RIGHT:
744 			{
745 				float width = StringWidth(Text());
746 				loc.x = bounds.right - width - 2;
747 				break;
748 			}
749 		}
750 		loc.y = bounds.bottom - (1 + height.descent);
751 		offscreen->DrawString(Text(), loc);
752 	}
753 	offscreen->Sync();
754 	SetDrawingMode(B_OP_COPY);
755 	DrawBitmap(fBitmap->Bitmap());
756 	fBitmap->DoneUsing();
757 }
758 
759 
760 void
761 FlickerFreeStringView::AttachedToWindow()
762 {
763 	_inherited::AttachedToWindow();
764 	if (Parent() != NULL) {
765 		fViewColor = Parent()->ViewColor();
766 		fLowColor = Parent()->ViewColor();
767 	}
768 	SetViewColor(B_TRANSPARENT_32_BIT);
769 	SetLowColor(B_TRANSPARENT_32_BIT);
770 }
771 
772 
773 void
774 FlickerFreeStringView::SetViewColor(rgb_color color)
775 {
776 	if (fViewColor != color) {
777 		fViewColor = color;
778 		Invalidate();
779 	}
780 	_inherited::SetViewColor(B_TRANSPARENT_32_BIT);
781 }
782 
783 
784 void
785 FlickerFreeStringView::SetLowColor(rgb_color color)
786 {
787 	if (fLowColor != color) {
788 		fLowColor = color;
789 		Invalidate();
790 	}
791 	_inherited::SetLowColor(B_TRANSPARENT_32_BIT);
792 }
793 
794 
795 // #pragma mark - TitledSeparatorItem
796 
797 
798 TitledSeparatorItem::TitledSeparatorItem(const char* label)
799 	:
800 	BMenuItem(label, 0)
801 {
802 	_inherited::SetEnabled(false);
803 }
804 
805 
806 TitledSeparatorItem::~TitledSeparatorItem()
807 {
808 }
809 
810 
811 void
812 TitledSeparatorItem::SetEnabled(bool)
813 {
814 	// leave disabled
815 }
816 
817 
818 void
819 TitledSeparatorItem::GetContentSize(float* width, float* height)
820 {
821 	_inherited::GetContentSize(width, height);
822 }
823 
824 
825 inline rgb_color
826 ShiftMenuBackgroundColor(float by)
827 {
828 	return tint_color(ui_color(B_MENU_BACKGROUND_COLOR), by);
829 }
830 
831 
832 void
833 TitledSeparatorItem::Draw()
834 {
835 	BRect frame(Frame());
836 
837 	BMenu* parent = Menu();
838 	ASSERT(parent != NULL);
839 
840 	menu_info minfo;
841 	get_menu_info(&minfo);
842 
843 	if (minfo.separator > 0) {
844 		frame.left += 10;
845 		frame.right -= 10;
846 	} else {
847 		frame.left += 1;
848 		frame.right -= 1;
849 	}
850 
851 	float startX = frame.left;
852 	float endX = frame.right;
853 
854 	float maxStringWidth = endX - startX - (2 * kMinSeparatorStubX
855 		+ 2 * kStubToStringSlotX);
856 
857 	// ToDo:
858 	// handle case where maxStringWidth turns out negative here
859 
860 	BString truncatedLabel(Label());
861 	parent->TruncateString(&truncatedLabel, B_TRUNCATE_END, maxStringWidth);
862 
863 	maxStringWidth = parent->StringWidth(truncatedLabel.String());
864 
865 	// first calculate the length of the stub part of the
866 	// divider line, so we can use it for secondStartX
867 	float firstEndX = ((endX - startX) - maxStringWidth) / 2
868 		- kStubToStringSlotX;
869 	if (firstEndX < 0)
870 		firstEndX = 0;
871 
872 	float secondStartX = endX - firstEndX;
873 
874 	// now finish calculating firstEndX
875 	firstEndX += startX;
876 
877 	parent->PushState();
878 
879 	int32 y = (int32) (frame.top + (frame.bottom - frame.top) / 2);
880 
881 	parent->BeginLineArray(minfo.separator == 2 ? 6 : 4);
882 	parent->AddLine(BPoint(frame.left, y), BPoint(firstEndX, y),
883 		ShiftMenuBackgroundColor(B_DARKEN_1_TINT));
884 	parent->AddLine(BPoint(secondStartX, y), BPoint(frame.right, y),
885 		ShiftMenuBackgroundColor(B_DARKEN_1_TINT));
886 
887 	if (minfo.separator == 2) {
888 		y++;
889 		frame.left++;
890 		frame.right--;
891 		parent->AddLine(BPoint(frame.left,y), BPoint(firstEndX, y),
892 			ShiftMenuBackgroundColor(B_DARKEN_1_TINT));
893 		parent->AddLine(BPoint(secondStartX,y), BPoint(frame.right, y),
894 			ShiftMenuBackgroundColor(B_DARKEN_1_TINT));
895 	}
896 	y++;
897 	if (minfo.separator == 2) {
898 		frame.left++;
899 		frame.right--;
900 	}
901 	parent->AddLine(BPoint(frame.left, y), BPoint(firstEndX, y),
902 		ShiftMenuBackgroundColor(B_DARKEN_1_TINT));
903 	parent->AddLine(BPoint(secondStartX, y), BPoint(frame.right, y),
904 		ShiftMenuBackgroundColor(B_DARKEN_1_TINT));
905 
906 	parent->EndLineArray();
907 
908 	font_height finfo;
909 	parent->GetFontHeight(&finfo);
910 
911 	parent->SetLowColor(parent->ViewColor());
912 	BPoint loc(firstEndX + kStubToStringSlotX,
913 		ContentLocation().y + finfo.ascent);
914 
915 	parent->MovePenTo(loc + BPoint(1, 1));
916 	parent->SetHighColor(ShiftMenuBackgroundColor(B_DARKEN_1_TINT));
917 	parent->DrawString(truncatedLabel.String());
918 
919 	parent->MovePenTo(loc);
920 	parent->SetHighColor(ShiftMenuBackgroundColor(B_DISABLED_LABEL_TINT));
921 	parent->DrawString(truncatedLabel.String());
922 
923 	parent->PopState();
924 }
925 
926 
927 // #pragma mark - ShortcutFilter
928 
929 
930 ShortcutFilter::ShortcutFilter(uint32 shortcutKey, uint32 shortcutModifier,
931 	uint32 shortcutWhat, BHandler* target)
932 	:
933 	BMessageFilter(B_KEY_DOWN),
934 	fShortcutKey(shortcutKey),
935 	fShortcutModifier(shortcutModifier),
936 	fShortcutWhat(shortcutWhat),
937 	fTarget(target)
938 {
939 }
940 
941 
942 filter_result
943 ShortcutFilter::Filter(BMessage* message, BHandler**)
944 {
945 	if (message->what == B_KEY_DOWN) {
946 		uint32 modifiers;
947 		uint32 rawKeyChar = 0;
948 		uint8 byte = 0;
949 		int32 key = 0;
950 
951 		if (message->FindInt32("modifiers", (int32*)&modifiers) != B_OK
952 			|| message->FindInt32("raw_char", (int32*)&rawKeyChar) != B_OK
953 			|| message->FindInt8("byte", (int8*)&byte) != B_OK
954 			|| message->FindInt32("key", &key) != B_OK) {
955 			return B_DISPATCH_MESSAGE;
956 		}
957 
958 		modifiers &= B_SHIFT_KEY | B_COMMAND_KEY | B_CONTROL_KEY
959 			| B_OPTION_KEY | B_MENU_KEY;
960 			// strip caps lock, etc.
961 
962 		if (modifiers == fShortcutModifier && rawKeyChar == fShortcutKey) {
963 			fTarget->Looper()->PostMessage(fShortcutWhat, fTarget);
964 			return B_SKIP_MESSAGE;
965 		}
966 	}
967 
968 	// let others deal with this
969 	return B_DISPATCH_MESSAGE;
970 }
971 
972 
973 // #pragma mark - BPrivate functions
974 
975 
976 namespace BPrivate {
977 
978 void
979 EmbedUniqueVolumeInfo(BMessage* message, const BVolume* volume)
980 {
981 	BDirectory rootDirectory;
982 	time_t created;
983 	fs_info info;
984 
985 	if (volume->GetRootDirectory(&rootDirectory) == B_OK
986 		&& rootDirectory.GetCreationTime(&created) == B_OK
987 		&& fs_stat_dev(volume->Device(), &info) == 0) {
988 		message->AddInt64("creationDate", created);
989 		message->AddInt64("capacity", volume->Capacity());
990 		message->AddString("deviceName", info.device_name);
991 		message->AddString("volumeName", info.volume_name);
992 		message->AddString("fshName", info.fsh_name);
993 	}
994 }
995 
996 
997 status_t
998 MatchArchivedVolume(BVolume* volume, const BMessage* message, int32 index)
999 {
1000 	int64 created64;
1001 	off_t capacity;
1002 
1003 	if (message->FindInt64("creationDate", index, &created64) != B_OK) {
1004 		int32 created32;
1005 		if (message->FindInt32("creationDate", index, &created32) != B_OK)
1006 			return B_ERROR;
1007 		created64 = created32;
1008 	}
1009 
1010 	time_t created = created64;
1011 
1012 	if (message->FindInt64("capacity", index, &capacity) != B_OK)
1013 		return B_ERROR;
1014 
1015 	BVolumeRoster roster;
1016 	BVolume tempVolume;
1017 	BString deviceName;
1018 	BString volumeName;
1019 	BString fshName;
1020 
1021 	if (message->FindString("deviceName", &deviceName) == B_OK
1022 		&& message->FindString("volumeName", &volumeName) == B_OK
1023 		&& message->FindString("fshName", &fshName) == B_OK) {
1024 		// New style volume identifiers: We have a couple of characteristics,
1025 		// and compute a score from them. The volume with the greatest score
1026 		// (if over a certain threshold) is the one we're looking for. We
1027 		// pick the first volume, in case there is more than one with the
1028 		// same score.
1029 		dev_t foundDevice = -1;
1030 		int foundScore = -1;
1031 		roster.Rewind();
1032 		while (roster.GetNextVolume(&tempVolume) == B_OK) {
1033 			if (tempVolume.IsPersistent() && tempVolume.KnowsQuery()) {
1034 				// get creation time and fs_info
1035 				BDirectory root;
1036 				tempVolume.GetRootDirectory(&root);
1037 				time_t cmpCreated;
1038 				fs_info info;
1039 				if (root.GetCreationTime(&cmpCreated) == B_OK
1040 					&& fs_stat_dev(tempVolume.Device(), &info) == 0) {
1041 					// compute the score
1042 					int score = 0;
1043 
1044 					// creation time
1045 					if (created == cmpCreated)
1046 						score += 5;
1047 
1048 					// capacity
1049 					if (capacity == tempVolume.Capacity())
1050 						score += 4;
1051 
1052 					// device name
1053 					if (deviceName == info.device_name)
1054 						score += 3;
1055 
1056 					// volume name
1057 					if (volumeName == info.volume_name)
1058 						score += 2;
1059 
1060 					// fsh name
1061 					if (fshName == info.fsh_name)
1062 						score += 1;
1063 
1064 					// check score
1065 					if (score >= 9 && score > foundScore) {
1066 						foundDevice = tempVolume.Device();
1067 						foundScore = score;
1068 					}
1069 				}
1070 			}
1071 		}
1072 		if (foundDevice >= 0)
1073 			return volume->SetTo(foundDevice);
1074 	} else {
1075 		// Old style volume identifiers: We have only creation time and
1076 		// capacity. Both must match.
1077 		roster.Rewind();
1078 		while (roster.GetNextVolume(&tempVolume) == B_OK) {
1079 			if (tempVolume.IsPersistent() && tempVolume.KnowsQuery()) {
1080 				BDirectory root;
1081 				tempVolume.GetRootDirectory(&root);
1082 				time_t cmpCreated;
1083 				root.GetCreationTime(&cmpCreated);
1084 				if (created == cmpCreated && capacity == tempVolume.Capacity()) {
1085 					*volume = tempVolume;
1086 					return B_OK;
1087 				}
1088 			}
1089 		}
1090 	}
1091 
1092 	return B_DEV_BAD_DRIVE_NUM;
1093 }
1094 
1095 
1096 void
1097 StringFromStream(BString* string, BMallocIO* stream, bool endianSwap)
1098 {
1099 	int32 length;
1100 	stream->Read(&length, sizeof(length));
1101 	if (endianSwap)
1102 		length = SwapInt32(length);
1103 
1104 	if (length < 0 || length > 10000) {
1105 		// TODO: should fail here
1106 		PRINT(("problems instatiating a string, length probably wrong %"
1107 			B_PRId32 "\n", length));
1108 		return;
1109 	}
1110 
1111 	char* buffer = string->LockBuffer(length + 1);
1112 	stream->Read(buffer, (size_t)length + 1);
1113 	string->UnlockBuffer(length);
1114 }
1115 
1116 
1117 void
1118 StringToStream(const BString* string, BMallocIO* stream)
1119 {
1120 	int32 length = string->Length();
1121 	stream->Write(&length, sizeof(int32));
1122 	stream->Write(string->String(), (size_t)string->Length() + 1);
1123 }
1124 
1125 
1126 int32
1127 ArchiveSize(const BString* string)
1128 {
1129 	return string->Length() + 1 + (ssize_t)sizeof(int32);
1130 }
1131 
1132 
1133 int32
1134 CountRefs(const BMessage* message)
1135 {
1136 	uint32 type;
1137 	int32 count;
1138 	message->GetInfo("refs", &type, &count);
1139 
1140 	return count;
1141 }
1142 
1143 
1144 static entry_ref*
1145 EachEntryRefCommon(BMessage* message, entry_ref *(*func)(entry_ref*, void*),
1146 	void* passThru, int32 maxCount)
1147 {
1148 	uint32 type;
1149 	int32 count;
1150 	message->GetInfo("refs", &type, &count);
1151 
1152 	if (maxCount >= 0 && count > maxCount)
1153 		count = maxCount;
1154 
1155 	for (int32 index = 0; index < count; index++) {
1156 		entry_ref ref;
1157 		message->FindRef("refs", index, &ref);
1158 		entry_ref* newRef = (func)(&ref, passThru);
1159 		if (newRef != NULL)
1160 			return newRef;
1161 	}
1162 
1163 	return NULL;
1164 }
1165 
1166 
1167 bool
1168 ContainsEntryRef(const BMessage* message, const entry_ref* ref)
1169 {
1170 	entry_ref match;
1171 	for (int32 index = 0; (message->FindRef("refs", index, &match) == B_OK);
1172 			index++) {
1173 		if (*ref == match)
1174 			return true;
1175 	}
1176 
1177 	return false;
1178 }
1179 
1180 
1181 entry_ref*
1182 EachEntryRef(BMessage* message, entry_ref* (*func)(entry_ref*, void*),
1183 	void* passThru)
1184 {
1185 	return EachEntryRefCommon(message, func, passThru, -1);
1186 }
1187 
1188 typedef entry_ref *(*EachEntryIteratee)(entry_ref *, void *);
1189 
1190 
1191 const entry_ref*
1192 EachEntryRef(const BMessage* message,
1193 	const entry_ref* (*func)(const entry_ref*, void*), void* passThru)
1194 {
1195 	return EachEntryRefCommon(const_cast<BMessage*>(message),
1196 		(EachEntryIteratee)func, passThru, -1);
1197 }
1198 
1199 
1200 entry_ref*
1201 EachEntryRef(BMessage* message, entry_ref* (*func)(entry_ref*, void*),
1202 	void* passThru, int32 maxCount)
1203 {
1204 	return EachEntryRefCommon(message, func, passThru, maxCount);
1205 }
1206 
1207 
1208 const entry_ref *
1209 EachEntryRef(const BMessage* message,
1210 	const entry_ref *(*func)(const entry_ref *, void *), void* passThru,
1211 	int32 maxCount)
1212 {
1213 	return EachEntryRefCommon(const_cast<BMessage *>(message),
1214 		(EachEntryIteratee)func, passThru, maxCount);
1215 }
1216 
1217 
1218 void
1219 TruncateLeaf(BString* string)
1220 {
1221 	for (int32 index = string->Length(); index >= 0; index--) {
1222 		if ((*string)[index] == '/') {
1223 			string->Truncate(index + 1);
1224 			return;
1225 		}
1226 	}
1227 }
1228 
1229 
1230 int64
1231 StringToScalar(const char* text)
1232 {
1233 	char* end;
1234 	int64 val;
1235 
1236 	char* buffer = new char [strlen(text) + 1];
1237 	strcpy(buffer, text);
1238 
1239 	if (strstr(buffer, "k") || strstr(buffer, "K")) {
1240 		val = strtoll(buffer, &end, 10);
1241 		val *= kKBSize;
1242 	} else if (strstr(buffer, "mb") || strstr(buffer, "MB")) {
1243 		val = strtoll(buffer, &end, 10);
1244 		val *= kMBSize;
1245 	} else if (strstr(buffer, "gb") || strstr(buffer, "GB")) {
1246 		val = strtoll(buffer, &end, 10);
1247 		val *= kGBSize;
1248 	} else if (strstr(buffer, "byte") || strstr(buffer, "BYTE")) {
1249 		val = strtoll(buffer, &end, 10);
1250 		val *= kGBSize;
1251 	} else {
1252 		// no suffix, try plain byte conversion
1253 		val = strtoll(buffer, &end, 10);
1254 	}
1255 	delete[] buffer;
1256 
1257 	return val;
1258 }
1259 
1260 
1261 int32
1262 ListIconSize()
1263 {
1264 	static int32 sIconSize = be_control_look->ComposeIconSize(B_MINI_ICON)
1265 		.IntegerWidth() + 1;
1266 	return sIconSize;
1267 }
1268 
1269 
1270 static BRect
1271 LineBounds(BPoint where, float length, bool vertical)
1272 {
1273 	BRect rect;
1274 	rect.SetLeftTop(where);
1275 	rect.SetRightBottom(where + BPoint(2, 2));
1276 	if (vertical)
1277 		rect.bottom = rect.top + length;
1278 	else
1279 		rect.right = rect.left + length;
1280 
1281 	return rect;
1282 }
1283 
1284 
1285 SeparatorLine::SeparatorLine(BPoint where, float length, bool vertical,
1286 	const char* name)
1287 	:
1288 	BView(LineBounds(where, length, vertical), name,
1289 		B_FOLLOW_LEFT | B_FOLLOW_TOP, B_WILL_DRAW)
1290 {
1291 	SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
1292 	SetLowUIColor(B_PANEL_BACKGROUND_COLOR);
1293 }
1294 
1295 
1296 void
1297 SeparatorLine::Draw(BRect)
1298 {
1299 	BRect bounds(Bounds());
1300 	rgb_color hiliteColor = tint_color(ViewColor(), 1.5f);
1301 
1302 	bool vertical = (bounds.left > bounds.right - 3);
1303 	BeginLineArray(2);
1304 	if (vertical) {
1305 		AddLine(bounds.LeftTop(), bounds.LeftBottom(), hiliteColor);
1306 		AddLine(bounds.LeftTop() + BPoint(1, 0),
1307 			bounds.LeftBottom() + BPoint(1, 0), kWhite);
1308 	} else {
1309 		AddLine(bounds.LeftTop(), bounds.RightTop(), hiliteColor);
1310 		AddLine(bounds.LeftTop() + BPoint(0, 1),
1311 			bounds.RightTop() + BPoint(0, 1), kWhite);
1312 	}
1313 	EndLineArray();
1314 }
1315 
1316 
1317 void
1318 HexDump(const void* buf, int32 length)
1319 {
1320 	const int32 kBytesPerLine = 16;
1321 	int32 offset;
1322 	unsigned char* buffer = (unsigned char*)buf;
1323 
1324 	for (offset = 0; ; offset += kBytesPerLine, buffer += kBytesPerLine) {
1325 		int32 remain = length;
1326 		int32 index;
1327 
1328 		printf( "0x%06x: ", (int)offset);
1329 
1330 		for (index = 0; index < kBytesPerLine; index++) {
1331 			if (remain-- > 0)
1332 				printf("%02x%c", buffer[index], remain > 0 ? ',' : ' ');
1333 			else
1334 				printf("   ");
1335 		}
1336 
1337 		remain = length;
1338 		printf(" \'");
1339 		for (index = 0; index < kBytesPerLine; index++) {
1340 			if (remain-- > 0)
1341 				printf("%c", buffer[index] > ' ' ? buffer[index] : '.');
1342 			else
1343 				printf(" ");
1344 		}
1345 		printf("\'\n");
1346 
1347 		length -= kBytesPerLine;
1348 		if (length <= 0)
1349 			break;
1350 	}
1351 	fflush(stdout);
1352 }
1353 
1354 
1355 void
1356 EnableNamedMenuItem(BMenu* menu, const char* itemName, bool on)
1357 {
1358 	BMenuItem* item = menu->FindItem(itemName);
1359 	if (item != NULL)
1360 		item->SetEnabled(on);
1361 }
1362 
1363 
1364 void
1365 MarkNamedMenuItem(BMenu* menu, const char* itemName, bool on)
1366 {
1367 	BMenuItem* item = menu->FindItem(itemName);
1368 	if (item != NULL)
1369 		item->SetMarked(on);
1370 }
1371 
1372 
1373 void
1374 EnableNamedMenuItem(BMenu* menu, uint32 commandName, bool on)
1375 {
1376 	BMenuItem* item = menu->FindItem(commandName);
1377 	if (item != NULL)
1378 		item->SetEnabled(on);
1379 }
1380 
1381 
1382 void
1383 MarkNamedMenuItem(BMenu* menu, uint32 commandName, bool on)
1384 {
1385 	BMenuItem* item = menu->FindItem(commandName);
1386 	if (item != NULL)
1387 		item->SetMarked(on);
1388 }
1389 
1390 
1391 void
1392 DeleteSubmenu(BMenuItem* submenuItem)
1393 {
1394 	if (submenuItem == NULL)
1395 		return;
1396 
1397 	BMenu* menu = submenuItem->Submenu();
1398 	if (menu == NULL)
1399 		return;
1400 
1401 	for (;;) {
1402 		BMenuItem* item = menu->RemoveItem((int32)0);
1403 		if (item == NULL)
1404 			return;
1405 
1406 		delete item;
1407 	}
1408 }
1409 
1410 
1411 status_t
1412 GetAppSignatureFromAttr(BFile* file, char* attr)
1413 {
1414 	// This call is a performance improvement that
1415 	// avoids using the BAppFileInfo API when retrieving the
1416 	// app signature -- the call is expensive because by default
1417 	// the resource fork is scanned to read the attribute
1418 
1419 #ifdef B_APP_FILE_INFO_IS_FAST
1420 	BAppFileInfo appFileInfo(file);
1421 	return appFileInfo.GetSignature(attr);
1422 #else
1423 	ssize_t readResult = file->ReadAttr(kAttrAppSignature, B_MIME_STRING_TYPE,
1424 		0, attr, B_MIME_TYPE_LENGTH);
1425 
1426 	if (readResult <= 0)
1427 		return (status_t)readResult;
1428 
1429 	return B_OK;
1430 #endif // B_APP_FILE_INFO_IS_FAST
1431 }
1432 
1433 
1434 status_t
1435 GetAppIconFromAttr(BFile* file, BBitmap* icon, icon_size which)
1436 {
1437 	// This call is a performance improvement that
1438 	// avoids using the BAppFileInfo API when retrieving the
1439 	// app icons -- the call is expensive because by default
1440 	// the resource fork is scanned to read the icons
1441 
1442 //#ifdef B_APP_FILE_INFO_IS_FAST
1443 	BAppFileInfo appFileInfo(file);
1444 	return appFileInfo.GetIcon(icon, which);
1445 //#else
1446 //
1447 //	const char* attrName = kAttrIcon;
1448 //	uint32 type = B_VECTOR_ICON_TYPE;
1449 //
1450 //	// try vector icon
1451 //	attr_info ainfo;
1452 //	status_t result = file->GetAttrInfo(attrName, &ainfo);
1453 //
1454 //	if (result == B_OK) {
1455 //		uint8 buffer[ainfo.size];
1456 //		ssize_t readResult = file->ReadAttr(attrName, type, 0, buffer,
1457 //			ainfo.size);
1458 //		if (readResult == ainfo.size) {
1459 //			if (BIconUtils::GetVectorIcon(buffer, ainfo.size, icon) == B_OK)
1460 //				return B_OK;
1461 //		}
1462 //	}
1463 //
1464 //	// try again with R5 icons
1465 //	attrName = which == B_LARGE_ICON ? kAttrLargeIcon : kAttrMiniIcon;
1466 //	type = which == B_LARGE_ICON ? LARGE_ICON_TYPE : MINI_ICON_TYPE;
1467 //
1468 //	result = file->GetAttrInfo(attrName, &ainfo);
1469 //	if (result < B_OK)
1470 //		return result;
1471 //
1472 //	uint8 buffer[ainfo.size];
1473 //
1474 //	ssize_t readResult = file->ReadAttr(attrName, type, 0, buffer, ainfo.size);
1475 //	if (readResult <= 0)
1476 //		return (status_t)readResult;
1477 //
1478 //	if (icon->ColorSpace() != B_CMAP8)
1479 //		result = BIconUtils::ConvertFromCMAP8(buffer, which, which, which, icon);
1480 //	else
1481 //		icon->SetBits(buffer, icon->BitsLength(), 0, B_CMAP8);
1482 //
1483 //	return result;
1484 //#endif	// B_APP_FILE_INFO_IS_FAST
1485 }
1486 
1487 
1488 status_t
1489 GetFileIconFromAttr(BNode* node, BBitmap* icon, icon_size which)
1490 {
1491 	// get icon from the node info
1492 	BNodeInfo nodeInfo(node);
1493 	return nodeInfo.GetIcon(icon, which);
1494 }
1495 
1496 
1497 //	#pragma mark - PrintToStream
1498 
1499 
1500 void
1501 PrintToStream(rgb_color color)
1502 {
1503 	printf("r:%x, g:%x, b:%x, a:%x\n",
1504 		color.red, color.green, color.blue, color.alpha);
1505 }
1506 
1507 
1508 //	#pragma mark - EachMenuItem
1509 
1510 
1511 extern BMenuItem*
1512 EachMenuItem(BMenu* menu, bool recursive, BMenuItem* (*func)(BMenuItem *))
1513 {
1514 	int32 count = menu->CountItems();
1515 	for (int32 index = 0; index < count; index++) {
1516 		BMenuItem* item = menu->ItemAt(index);
1517 		BMenuItem* newItem = (func)(item);
1518 		if (newItem != NULL)
1519 			return newItem;
1520 
1521 		if (recursive) {
1522 			BMenu* submenu = menu->SubmenuAt(index);
1523 			if (submenu != NULL)
1524 				return EachMenuItem(submenu, true, func);
1525 		}
1526 	}
1527 
1528 	return NULL;
1529 }
1530 
1531 
1532 extern const BMenuItem*
1533 EachMenuItem(const BMenu* menu, bool recursive,
1534 	BMenuItem* (*func)(const BMenuItem *))
1535 {
1536 	int32 count = menu->CountItems();
1537 	for (int32 index = 0; index < count; index++) {
1538 		BMenuItem* item = menu->ItemAt(index);
1539 		BMenuItem* newItem = (func)(item);
1540 		if (newItem != NULL)
1541 			return newItem;
1542 
1543 		if (recursive) {
1544 			BMenu* submenu = menu->SubmenuAt(index);
1545 			if (submenu != NULL)
1546 				return EachMenuItem(submenu, true, func);
1547 		}
1548 	}
1549 
1550 	return NULL;
1551 }
1552 
1553 
1554 //	#pragma mark - PositionPassingMenuItem
1555 
1556 
1557 PositionPassingMenuItem::PositionPassingMenuItem(const char* title,
1558 	BMessage* message, char shortcut, uint32 modifiers)
1559 	:
1560 	BMenuItem(title, message, shortcut, modifiers)
1561 {
1562 }
1563 
1564 
1565 PositionPassingMenuItem::PositionPassingMenuItem(BMenu* menu, BMessage* message)
1566 	:
1567 	BMenuItem(menu, message)
1568 {
1569 }
1570 
1571 
1572 PositionPassingMenuItem::PositionPassingMenuItem(BMessage* data)
1573 	:
1574 	BMenuItem(data)
1575 {
1576 }
1577 
1578 
1579 BArchivable*
1580 PositionPassingMenuItem::Instantiate(BMessage* data)
1581 {
1582 	if (validate_instantiation(data, "PositionPassingMenuItem"))
1583 		return new PositionPassingMenuItem(data);
1584 
1585 	return NULL;
1586 }
1587 
1588 
1589 status_t
1590 PositionPassingMenuItem::Invoke(BMessage* message)
1591 {
1592 	if (Menu() == NULL)
1593 		return B_ERROR;
1594 
1595 	if (!IsEnabled())
1596 		return B_ERROR;
1597 
1598 	if (message == NULL)
1599 		message = Message();
1600 
1601 	if (message == NULL)
1602 		return B_BAD_VALUE;
1603 
1604 	BMessage clone(*message);
1605 	clone.AddInt32("index", Menu()->IndexOf(this));
1606 	clone.AddInt64("when", system_time());
1607 	clone.AddPointer("source", this);
1608 
1609 	// embed the invoke location of the menu so that we can create
1610 	// a new folder, etc. on the spot
1611 	BMenu* menu = Menu();
1612 
1613 	for (;;) {
1614 		if (!menu->Supermenu())
1615 			break;
1616 
1617 		menu = menu->Supermenu();
1618 	}
1619 
1620 	// use the window position only, if the item was invoked from the menu
1621 	// menu->Window() points to the window the item was invoked from
1622 	if (dynamic_cast<BContainerWindow*>(menu->Window()) == NULL) {
1623 		AutoLocker<BLooper> lock(menu->Looper());
1624 		if (lock.IsLocked()) {
1625 			BPoint invokeOrigin(menu->Window()->Frame().LeftTop());
1626 			clone.AddPoint("be:invoke_origin", invokeOrigin);
1627 		}
1628 	}
1629 
1630 	return BInvoker::Invoke(&clone);
1631 }
1632 
1633 
1634 //	#pragma mark - BPrivate functions
1635 
1636 
1637 bool
1638 BootedInSafeMode()
1639 {
1640 	const char* safeMode = getenv("SAFEMODE");
1641 	return (safeMode && strcmp(safeMode, "yes") == 0);
1642 }
1643 
1644 
1645 float
1646 ComputeTypeAheadScore(const char* text, const char* match, bool wordMode)
1647 {
1648 	// highest score: exact match
1649 	const char* found = strcasestr(text, match);
1650 	if (found != NULL) {
1651 		if (found == text)
1652 			return kExactMatchScore;
1653 
1654 		return 1.f / (found - text);
1655 	}
1656 
1657 	// there was no exact match
1658 
1659 	// second best: all characters at word beginnings
1660 	if (wordMode) {
1661 		float score = 0;
1662 		for (int32 j = 0, k = 0; match[j]; j++) {
1663 			while (text[k]
1664 				&& tolower(text[k]) != tolower(match[j])) {
1665 				k++;
1666 			}
1667 			if (text[k] == '\0') {
1668 				score = 0;
1669 				break;
1670 			}
1671 
1672 			bool wordStart = k == 0 || isspace(text[k - 1]);
1673 			if (wordStart)
1674 				score++;
1675 			if (j > 0) {
1676 				bool wordEnd = !text[k + 1] || isspace(text[k + 1]);
1677 				if (wordEnd)
1678 					score += 0.3;
1679 				if (match[j - 1] == text[k - 1])
1680 					score += 0.7;
1681 			}
1682 
1683 			score += 1.f / (k + 1);
1684 			k++;
1685 		}
1686 
1687 		return score;
1688 	}
1689 
1690 	return -1;
1691 }
1692 
1693 
1694 //	#pragma mark - throw on error functions.
1695 
1696 
1697 void
1698 _ThrowOnError(status_t result, const char* DEBUG_ONLY(file),
1699 	int32 DEBUG_ONLY(line))
1700 {
1701 	if (result != B_OK) {
1702 		PRINT(("%s at %s:%d\n", strerror(result), file, (int)line));
1703 		throw result;
1704 	}
1705 }
1706 
1707 
1708 void
1709 _ThrowIfNotSize(ssize_t size, const char* DEBUG_ONLY(file),
1710 	int32 DEBUG_ONLY(line))
1711 {
1712 	if (size < B_OK) {
1713 		PRINT(("%s at %s:%d\n", strerror((status_t)size), file, (int)line));
1714 		throw (status_t)size;
1715 	}
1716 }
1717 
1718 
1719 void
1720 _ThrowOnAssert(bool success, const char* DEBUG_ONLY(file),
1721 	int32 DEBUG_ONLY(line))
1722 {
1723 	if (!success) {
1724 		PRINT(("Assert failed at %s:%d\n", file, (int)line));
1725 		throw B_ERROR;
1726 	}
1727 }
1728 
1729 } // namespace BPrivate
1730