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