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