xref: /haiku/src/kits/tracker/Utilities.cpp (revision 1978089f7cec856677e46204e992c7273d70b9af)
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 int
1364 CompareLabels(const BMenuItem* item1, const BMenuItem* item2)
1365 {
1366 	return strcasecmp(item1->Label(), item2->Label());
1367 }
1368 
1369 
1370 void
1371 EnableNamedMenuItem(BMenu* menu, const char* itemName, bool on)
1372 {
1373 	BMenuItem* item = menu->FindItem(itemName);
1374 	if (item != NULL)
1375 		item->SetEnabled(on);
1376 }
1377 
1378 
1379 void
1380 MarkNamedMenuItem(BMenu* menu, const char* itemName, bool on)
1381 {
1382 	BMenuItem* item = menu->FindItem(itemName);
1383 	if (item != NULL)
1384 		item->SetMarked(on);
1385 }
1386 
1387 
1388 void
1389 EnableNamedMenuItem(BMenu* menu, uint32 commandName, bool on)
1390 {
1391 	BMenuItem* item = menu->FindItem(commandName);
1392 	if (item != NULL)
1393 		item->SetEnabled(on);
1394 }
1395 
1396 
1397 void
1398 MarkNamedMenuItem(BMenu* menu, uint32 commandName, bool on)
1399 {
1400 	BMenuItem* item = menu->FindItem(commandName);
1401 	if (item != NULL)
1402 		item->SetMarked(on);
1403 }
1404 
1405 
1406 void
1407 DeleteSubmenu(BMenuItem* submenuItem)
1408 {
1409 	if (submenuItem == NULL)
1410 		return;
1411 
1412 	BMenu* menu = submenuItem->Submenu();
1413 	if (menu == NULL)
1414 		return;
1415 
1416 	for (;;) {
1417 		BMenuItem* item = menu->RemoveItem((int32)0);
1418 		if (item == NULL)
1419 			return;
1420 
1421 		delete item;
1422 	}
1423 }
1424 
1425 
1426 status_t
1427 GetAppSignatureFromAttr(BFile* file, char* attr)
1428 {
1429 	// This call is a performance improvement that
1430 	// avoids using the BAppFileInfo API when retrieving the
1431 	// app signature -- the call is expensive because by default
1432 	// the resource fork is scanned to read the attribute
1433 
1434 #ifdef B_APP_FILE_INFO_IS_FAST
1435 	BAppFileInfo appFileInfo(file);
1436 	return appFileInfo.GetSignature(attr);
1437 #else
1438 	ssize_t readResult = file->ReadAttr(kAttrAppSignature, B_MIME_STRING_TYPE,
1439 		0, attr, B_MIME_TYPE_LENGTH);
1440 
1441 	if (readResult <= 0)
1442 		return (status_t)readResult;
1443 
1444 	return B_OK;
1445 #endif // B_APP_FILE_INFO_IS_FAST
1446 }
1447 
1448 
1449 status_t
1450 GetAppIconFromAttr(BFile* file, BBitmap* icon, icon_size which)
1451 {
1452 	// This call is a performance improvement that
1453 	// avoids using the BAppFileInfo API when retrieving the
1454 	// app icons -- the call is expensive because by default
1455 	// the resource fork is scanned to read the icons
1456 
1457 //#ifdef B_APP_FILE_INFO_IS_FAST
1458 	BAppFileInfo appFileInfo(file);
1459 	return appFileInfo.GetIcon(icon, which);
1460 //#else
1461 //
1462 //	const char* attrName = kAttrIcon;
1463 //	uint32 type = B_VECTOR_ICON_TYPE;
1464 //
1465 //	// try vector icon
1466 //	attr_info ainfo;
1467 //	status_t result = file->GetAttrInfo(attrName, &ainfo);
1468 //
1469 //	if (result == B_OK) {
1470 //		uint8 buffer[ainfo.size];
1471 //		ssize_t readResult = file->ReadAttr(attrName, type, 0, buffer,
1472 //			ainfo.size);
1473 //		if (readResult == ainfo.size) {
1474 //			if (BIconUtils::GetVectorIcon(buffer, ainfo.size, icon) == B_OK)
1475 //				return B_OK;
1476 //		}
1477 //	}
1478 //
1479 //	// try again with R5 icons
1480 //	attrName = which == B_LARGE_ICON ? kAttrLargeIcon : kAttrMiniIcon;
1481 //	type = which == B_LARGE_ICON ? LARGE_ICON_TYPE : MINI_ICON_TYPE;
1482 //
1483 //	result = file->GetAttrInfo(attrName, &ainfo);
1484 //	if (result < B_OK)
1485 //		return result;
1486 //
1487 //	uint8 buffer[ainfo.size];
1488 //
1489 //	ssize_t readResult = file->ReadAttr(attrName, type, 0, buffer, ainfo.size);
1490 //	if (readResult <= 0)
1491 //		return (status_t)readResult;
1492 //
1493 //	if (icon->ColorSpace() != B_CMAP8)
1494 //		result = BIconUtils::ConvertFromCMAP8(buffer, which, which, which, icon);
1495 //	else
1496 //		icon->SetBits(buffer, icon->BitsLength(), 0, B_CMAP8);
1497 //
1498 //	return result;
1499 //#endif	// B_APP_FILE_INFO_IS_FAST
1500 }
1501 
1502 
1503 status_t
1504 GetFileIconFromAttr(BNode* node, BBitmap* icon, icon_size which)
1505 {
1506 	// get icon from the node info
1507 	BNodeInfo nodeInfo(node);
1508 	return nodeInfo.GetIcon(icon, which);
1509 }
1510 
1511 
1512 //	#pragma mark - PrintToStream
1513 
1514 
1515 void
1516 PrintToStream(rgb_color color)
1517 {
1518 	printf("r:%x, g:%x, b:%x, a:%x\n",
1519 		color.red, color.green, color.blue, color.alpha);
1520 }
1521 
1522 
1523 //	#pragma mark - EachMenuItem
1524 
1525 
1526 extern BMenuItem*
1527 EachMenuItem(BMenu* menu, bool recursive, BMenuItem* (*func)(BMenuItem *))
1528 {
1529 	int32 count = menu->CountItems();
1530 	for (int32 index = 0; index < count; index++) {
1531 		BMenuItem* item = menu->ItemAt(index);
1532 		BMenuItem* newItem = (func)(item);
1533 		if (newItem != NULL)
1534 			return newItem;
1535 
1536 		if (recursive) {
1537 			BMenu* submenu = menu->SubmenuAt(index);
1538 			if (submenu != NULL)
1539 				return EachMenuItem(submenu, true, func);
1540 		}
1541 	}
1542 
1543 	return NULL;
1544 }
1545 
1546 
1547 extern const BMenuItem*
1548 EachMenuItem(const BMenu* menu, bool recursive,
1549 	BMenuItem* (*func)(const BMenuItem *))
1550 {
1551 	int32 count = menu->CountItems();
1552 	for (int32 index = 0; index < count; index++) {
1553 		BMenuItem* item = menu->ItemAt(index);
1554 		BMenuItem* newItem = (func)(item);
1555 		if (newItem != NULL)
1556 			return newItem;
1557 
1558 		if (recursive) {
1559 			BMenu* submenu = menu->SubmenuAt(index);
1560 			if (submenu != NULL)
1561 				return EachMenuItem(submenu, true, func);
1562 		}
1563 	}
1564 
1565 	return NULL;
1566 }
1567 
1568 
1569 //	#pragma mark - PositionPassingMenuItem
1570 
1571 
1572 PositionPassingMenuItem::PositionPassingMenuItem(const char* title,
1573 	BMessage* message, char shortcut, uint32 modifiers)
1574 	:
1575 	BMenuItem(title, message, shortcut, modifiers)
1576 {
1577 }
1578 
1579 
1580 PositionPassingMenuItem::PositionPassingMenuItem(BMenu* menu, BMessage* message)
1581 	:
1582 	BMenuItem(menu, message)
1583 {
1584 }
1585 
1586 
1587 PositionPassingMenuItem::PositionPassingMenuItem(BMessage* data)
1588 	:
1589 	BMenuItem(data)
1590 {
1591 }
1592 
1593 
1594 BArchivable*
1595 PositionPassingMenuItem::Instantiate(BMessage* data)
1596 {
1597 	if (validate_instantiation(data, "PositionPassingMenuItem"))
1598 		return new PositionPassingMenuItem(data);
1599 
1600 	return NULL;
1601 }
1602 
1603 
1604 status_t
1605 PositionPassingMenuItem::Invoke(BMessage* message)
1606 {
1607 	if (Menu() == NULL)
1608 		return B_ERROR;
1609 
1610 	if (!IsEnabled())
1611 		return B_ERROR;
1612 
1613 	if (message == NULL)
1614 		message = Message();
1615 
1616 	if (message == NULL)
1617 		return B_BAD_VALUE;
1618 
1619 	BMessage clone(*message);
1620 	clone.AddInt32("index", Menu()->IndexOf(this));
1621 	clone.AddInt64("when", system_time());
1622 	clone.AddPointer("source", this);
1623 
1624 	// embed the invoke location of the menu so that we can create
1625 	// a new folder, etc. on the spot
1626 	BMenu* menu = Menu();
1627 
1628 	for (;;) {
1629 		if (!menu->Supermenu())
1630 			break;
1631 
1632 		menu = menu->Supermenu();
1633 	}
1634 
1635 	// use the window position only, if the item was invoked from the menu
1636 	// menu->Window() points to the window the item was invoked from
1637 	if (dynamic_cast<BContainerWindow*>(menu->Window()) == NULL) {
1638 		AutoLocker<BLooper> lock(menu->Looper());
1639 		if (lock.IsLocked()) {
1640 			BPoint invokeOrigin(menu->Window()->Frame().LeftTop());
1641 			clone.AddPoint("be:invoke_origin", invokeOrigin);
1642 		}
1643 	}
1644 
1645 	return BInvoker::Invoke(&clone);
1646 }
1647 
1648 
1649 //	#pragma mark - BPrivate functions
1650 
1651 
1652 bool
1653 BootedInSafeMode()
1654 {
1655 	const char* safeMode = getenv("SAFEMODE");
1656 	return (safeMode && strcmp(safeMode, "yes") == 0);
1657 }
1658 
1659 
1660 float
1661 ComputeTypeAheadScore(const char* text, const char* match, bool wordMode)
1662 {
1663 	// highest score: exact match
1664 	const char* found = strcasestr(text, match);
1665 	if (found != NULL) {
1666 		if (found == text)
1667 			return kExactMatchScore;
1668 
1669 		return 1.f / (found - text);
1670 	}
1671 
1672 	// there was no exact match
1673 
1674 	// second best: all characters at word beginnings
1675 	if (wordMode) {
1676 		float score = 0;
1677 		for (int32 j = 0, k = 0; match[j]; j++) {
1678 			while (text[k]
1679 				&& tolower(text[k]) != tolower(match[j])) {
1680 				k++;
1681 			}
1682 			if (text[k] == '\0') {
1683 				score = 0;
1684 				break;
1685 			}
1686 
1687 			bool wordStart = k == 0 || isspace(text[k - 1]);
1688 			if (wordStart)
1689 				score++;
1690 			if (j > 0) {
1691 				bool wordEnd = !text[k + 1] || isspace(text[k + 1]);
1692 				if (wordEnd)
1693 					score += 0.3;
1694 				if (match[j - 1] == text[k - 1])
1695 					score += 0.7;
1696 			}
1697 
1698 			score += 1.f / (k + 1);
1699 			k++;
1700 		}
1701 
1702 		return score;
1703 	}
1704 
1705 	return -1;
1706 }
1707 
1708 
1709 //	#pragma mark - throw on error functions.
1710 
1711 
1712 void
1713 _ThrowOnError(status_t result, const char* DEBUG_ONLY(file),
1714 	int32 DEBUG_ONLY(line))
1715 {
1716 	if (result != B_OK) {
1717 		PRINT(("%s at %s:%d\n", strerror(result), file, (int)line));
1718 		throw result;
1719 	}
1720 }
1721 
1722 
1723 void
1724 _ThrowIfNotSize(ssize_t size, const char* DEBUG_ONLY(file),
1725 	int32 DEBUG_ONLY(line))
1726 {
1727 	if (size < B_OK) {
1728 		PRINT(("%s at %s:%d\n", strerror((status_t)size), file, (int)line));
1729 		throw (status_t)size;
1730 	}
1731 }
1732 
1733 
1734 void
1735 _ThrowOnAssert(bool success, const char* DEBUG_ONLY(file),
1736 	int32 DEBUG_ONLY(line))
1737 {
1738 	if (!success) {
1739 		PRINT(("Assert failed at %s:%d\n", file, (int)line));
1740 		throw B_ERROR;
1741 	}
1742 }
1743 
1744 } // namespace BPrivate
1745