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