xref: /haiku/src/kits/tracker/Utilities.cpp (revision ba0223da5d79c5cd27496ee0e5712921cebb7642)
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 	// Adjust for the extra space needed by the separator bars at the left and right
831 	if (width)
832 		*width += (kMinSeparatorStubX + kStubToStringSlotX) * 2;
833 }
834 
835 
836 inline rgb_color
837 ShiftMenuBackgroundColor(float by)
838 {
839 	return tint_color(ui_color(B_MENU_BACKGROUND_COLOR), by);
840 }
841 
842 
843 void
844 TitledSeparatorItem::Draw()
845 {
846 	BRect frame(Frame());
847 
848 	BMenu* parent = Menu();
849 	ASSERT(parent != NULL);
850 
851 	menu_info minfo;
852 	get_menu_info(&minfo);
853 
854 	if (minfo.separator > 0) {
855 		frame.left += 10;
856 		frame.right -= 10;
857 	} else {
858 		frame.left += 1;
859 		frame.right -= 1;
860 	}
861 
862 	float startX = frame.left;
863 	float endX = frame.right;
864 
865 	float maxStringWidth = endX - startX - (2 * kMinSeparatorStubX
866 		+ 2 * kStubToStringSlotX);
867 
868 	// ToDo:
869 	// handle case where maxStringWidth turns out negative here
870 
871 	BString truncatedLabel(Label());
872 	parent->TruncateString(&truncatedLabel, B_TRUNCATE_END, maxStringWidth);
873 
874 	maxStringWidth = parent->StringWidth(truncatedLabel.String());
875 
876 	// first calculate the length of the stub part of the
877 	// divider line, so we can use it for secondStartX
878 	float firstEndX = ((endX - startX) - maxStringWidth) / 2
879 		- kStubToStringSlotX;
880 	if (firstEndX < 0)
881 		firstEndX = 0;
882 
883 	float secondStartX = endX - firstEndX;
884 
885 	// now finish calculating firstEndX
886 	firstEndX += startX;
887 
888 	parent->PushState();
889 
890 	int32 y = (int32) (frame.top + (frame.bottom - frame.top) / 2);
891 
892 	parent->BeginLineArray(minfo.separator == 2 ? 6 : 4);
893 	parent->AddLine(BPoint(frame.left, y), BPoint(firstEndX, y),
894 		ShiftMenuBackgroundColor(B_DARKEN_1_TINT));
895 	parent->AddLine(BPoint(secondStartX, y), BPoint(frame.right, y),
896 		ShiftMenuBackgroundColor(B_DARKEN_1_TINT));
897 
898 	if (minfo.separator == 2) {
899 		y++;
900 		frame.left++;
901 		frame.right--;
902 		parent->AddLine(BPoint(frame.left,y), BPoint(firstEndX, y),
903 			ShiftMenuBackgroundColor(B_DARKEN_1_TINT));
904 		parent->AddLine(BPoint(secondStartX,y), BPoint(frame.right, y),
905 			ShiftMenuBackgroundColor(B_DARKEN_1_TINT));
906 	}
907 	y++;
908 	if (minfo.separator == 2) {
909 		frame.left++;
910 		frame.right--;
911 	}
912 	parent->AddLine(BPoint(frame.left, y), BPoint(firstEndX, y),
913 		ShiftMenuBackgroundColor(B_DARKEN_1_TINT));
914 	parent->AddLine(BPoint(secondStartX, y), BPoint(frame.right, y),
915 		ShiftMenuBackgroundColor(B_DARKEN_1_TINT));
916 
917 	parent->EndLineArray();
918 
919 	font_height finfo;
920 	parent->GetFontHeight(&finfo);
921 
922 	parent->SetLowColor(parent->ViewColor());
923 	BPoint loc(firstEndX + kStubToStringSlotX,
924 		ContentLocation().y + finfo.ascent);
925 
926 	parent->MovePenTo(loc + BPoint(1, 1));
927 	parent->SetHighColor(ShiftMenuBackgroundColor(B_DARKEN_1_TINT));
928 	parent->DrawString(truncatedLabel.String());
929 
930 	parent->MovePenTo(loc);
931 	parent->SetHighColor(ShiftMenuBackgroundColor(B_DISABLED_LABEL_TINT));
932 	parent->DrawString(truncatedLabel.String());
933 
934 	parent->PopState();
935 }
936 
937 
938 // #pragma mark - ShortcutFilter
939 
940 
941 ShortcutFilter::ShortcutFilter(uint32 shortcutKey, uint32 shortcutModifier,
942 	uint32 shortcutWhat, BHandler* target)
943 	:
944 	BMessageFilter(B_KEY_DOWN),
945 	fShortcutKey(shortcutKey),
946 	fShortcutModifier(shortcutModifier),
947 	fShortcutWhat(shortcutWhat),
948 	fTarget(target)
949 {
950 }
951 
952 
953 filter_result
954 ShortcutFilter::Filter(BMessage* message, BHandler**)
955 {
956 	if (message->what == B_KEY_DOWN) {
957 		uint32 modifiers;
958 		uint32 rawKeyChar = 0;
959 		uint8 byte = 0;
960 		int32 key = 0;
961 
962 		if (message->FindInt32("modifiers", (int32*)&modifiers) != B_OK
963 			|| message->FindInt32("raw_char", (int32*)&rawKeyChar) != B_OK
964 			|| message->FindInt8("byte", (int8*)&byte) != B_OK
965 			|| message->FindInt32("key", &key) != B_OK) {
966 			return B_DISPATCH_MESSAGE;
967 		}
968 
969 		modifiers &= B_SHIFT_KEY | B_COMMAND_KEY | B_CONTROL_KEY
970 			| B_OPTION_KEY | B_MENU_KEY;
971 			// strip caps lock, etc.
972 
973 		if (modifiers == fShortcutModifier && rawKeyChar == fShortcutKey) {
974 			fTarget->Looper()->PostMessage(fShortcutWhat, fTarget);
975 			return B_SKIP_MESSAGE;
976 		}
977 	}
978 
979 	// let others deal with this
980 	return B_DISPATCH_MESSAGE;
981 }
982 
983 
984 // #pragma mark - BPrivate functions
985 
986 
987 namespace BPrivate {
988 
989 void
990 EmbedUniqueVolumeInfo(BMessage* message, const BVolume* volume)
991 {
992 	BDirectory rootDirectory;
993 	time_t created;
994 	fs_info info;
995 
996 	if (volume->GetRootDirectory(&rootDirectory) == B_OK
997 		&& rootDirectory.GetCreationTime(&created) == B_OK
998 		&& fs_stat_dev(volume->Device(), &info) == 0) {
999 		message->AddInt64("creationDate", created);
1000 		message->AddInt64("capacity", volume->Capacity());
1001 		message->AddString("deviceName", info.device_name);
1002 		message->AddString("volumeName", info.volume_name);
1003 		message->AddString("fshName", info.fsh_name);
1004 	}
1005 }
1006 
1007 
1008 status_t
1009 MatchArchivedVolume(BVolume* volume, const BMessage* message, int32 index)
1010 {
1011 	int64 created64;
1012 	off_t capacity;
1013 
1014 	if (message->FindInt64("creationDate", index, &created64) != B_OK) {
1015 		int32 created32;
1016 		if (message->FindInt32("creationDate", index, &created32) != B_OK)
1017 			return B_ERROR;
1018 		created64 = created32;
1019 	}
1020 
1021 	time_t created = created64;
1022 
1023 	if (message->FindInt64("capacity", index, &capacity) != B_OK)
1024 		return B_ERROR;
1025 
1026 	BVolumeRoster roster;
1027 	BVolume tempVolume;
1028 	BString deviceName;
1029 	BString volumeName;
1030 	BString fshName;
1031 
1032 	if (message->FindString("deviceName", &deviceName) == B_OK
1033 		&& message->FindString("volumeName", &volumeName) == B_OK
1034 		&& message->FindString("fshName", &fshName) == B_OK) {
1035 		// New style volume identifiers: We have a couple of characteristics,
1036 		// and compute a score from them. The volume with the greatest score
1037 		// (if over a certain threshold) is the one we're looking for. We
1038 		// pick the first volume, in case there is more than one with the
1039 		// same score.
1040 		dev_t foundDevice = -1;
1041 		int foundScore = -1;
1042 		roster.Rewind();
1043 		while (roster.GetNextVolume(&tempVolume) == B_OK) {
1044 			if (tempVolume.IsPersistent() && tempVolume.KnowsQuery()) {
1045 				// get creation time and fs_info
1046 				BDirectory root;
1047 				tempVolume.GetRootDirectory(&root);
1048 				time_t cmpCreated;
1049 				fs_info info;
1050 				if (root.GetCreationTime(&cmpCreated) == B_OK
1051 					&& fs_stat_dev(tempVolume.Device(), &info) == 0) {
1052 					// compute the score
1053 					int score = 0;
1054 
1055 					// creation time
1056 					if (created == cmpCreated)
1057 						score += 5;
1058 
1059 					// capacity
1060 					if (capacity == tempVolume.Capacity())
1061 						score += 4;
1062 
1063 					// device name
1064 					if (deviceName == info.device_name)
1065 						score += 3;
1066 
1067 					// volume name
1068 					if (volumeName == info.volume_name)
1069 						score += 2;
1070 
1071 					// fsh name
1072 					if (fshName == info.fsh_name)
1073 						score += 1;
1074 
1075 					// check score
1076 					if (score >= 9 && score > foundScore) {
1077 						foundDevice = tempVolume.Device();
1078 						foundScore = score;
1079 					}
1080 				}
1081 			}
1082 		}
1083 		if (foundDevice >= 0)
1084 			return volume->SetTo(foundDevice);
1085 	} else {
1086 		// Old style volume identifiers: We have only creation time and
1087 		// capacity. Both must match.
1088 		roster.Rewind();
1089 		while (roster.GetNextVolume(&tempVolume) == B_OK) {
1090 			if (tempVolume.IsPersistent() && tempVolume.KnowsQuery()) {
1091 				BDirectory root;
1092 				tempVolume.GetRootDirectory(&root);
1093 				time_t cmpCreated;
1094 				root.GetCreationTime(&cmpCreated);
1095 				if (created == cmpCreated && capacity == tempVolume.Capacity()) {
1096 					*volume = tempVolume;
1097 					return B_OK;
1098 				}
1099 			}
1100 		}
1101 	}
1102 
1103 	return B_DEV_BAD_DRIVE_NUM;
1104 }
1105 
1106 
1107 void
1108 StringFromStream(BString* string, BMallocIO* stream, bool endianSwap)
1109 {
1110 	int32 length;
1111 	stream->Read(&length, sizeof(length));
1112 	if (endianSwap)
1113 		length = SwapInt32(length);
1114 
1115 	if (length < 0 || length > 10000) {
1116 		// TODO: should fail here
1117 		PRINT(("problems instatiating a string, length probably wrong %"
1118 			B_PRId32 "\n", length));
1119 		return;
1120 	}
1121 
1122 	char* buffer = string->LockBuffer(length + 1);
1123 	stream->Read(buffer, (size_t)length + 1);
1124 	string->UnlockBuffer(length);
1125 }
1126 
1127 
1128 void
1129 StringToStream(const BString* string, BMallocIO* stream)
1130 {
1131 	int32 length = string->Length();
1132 	stream->Write(&length, sizeof(int32));
1133 	stream->Write(string->String(), (size_t)string->Length() + 1);
1134 }
1135 
1136 
1137 int32
1138 ArchiveSize(const BString* string)
1139 {
1140 	return string->Length() + 1 + (ssize_t)sizeof(int32);
1141 }
1142 
1143 
1144 int32
1145 CountRefs(const BMessage* message)
1146 {
1147 	uint32 type;
1148 	int32 count;
1149 	message->GetInfo("refs", &type, &count);
1150 
1151 	return count;
1152 }
1153 
1154 
1155 static entry_ref*
1156 EachEntryRefCommon(BMessage* message, entry_ref *(*func)(entry_ref*, void*),
1157 	void* passThru, int32 maxCount)
1158 {
1159 	uint32 type;
1160 	int32 count;
1161 	message->GetInfo("refs", &type, &count);
1162 
1163 	if (maxCount >= 0 && count > maxCount)
1164 		count = maxCount;
1165 
1166 	for (int32 index = 0; index < count; index++) {
1167 		entry_ref ref;
1168 		message->FindRef("refs", index, &ref);
1169 		entry_ref* newRef = (func)(&ref, passThru);
1170 		if (newRef != NULL)
1171 			return newRef;
1172 	}
1173 
1174 	return NULL;
1175 }
1176 
1177 
1178 bool
1179 ContainsEntryRef(const BMessage* message, const entry_ref* ref)
1180 {
1181 	entry_ref match;
1182 	for (int32 index = 0; (message->FindRef("refs", index, &match) == B_OK);
1183 			index++) {
1184 		if (*ref == match)
1185 			return true;
1186 	}
1187 
1188 	return false;
1189 }
1190 
1191 
1192 entry_ref*
1193 EachEntryRef(BMessage* message, entry_ref* (*func)(entry_ref*, void*),
1194 	void* passThru)
1195 {
1196 	return EachEntryRefCommon(message, func, passThru, -1);
1197 }
1198 
1199 typedef entry_ref *(*EachEntryIteratee)(entry_ref *, void *);
1200 
1201 
1202 const entry_ref*
1203 EachEntryRef(const BMessage* message,
1204 	const entry_ref* (*func)(const entry_ref*, void*), void* passThru)
1205 {
1206 	return EachEntryRefCommon(const_cast<BMessage*>(message),
1207 		(EachEntryIteratee)func, passThru, -1);
1208 }
1209 
1210 
1211 entry_ref*
1212 EachEntryRef(BMessage* message, entry_ref* (*func)(entry_ref*, void*),
1213 	void* passThru, int32 maxCount)
1214 {
1215 	return EachEntryRefCommon(message, func, passThru, maxCount);
1216 }
1217 
1218 
1219 const entry_ref *
1220 EachEntryRef(const BMessage* message,
1221 	const entry_ref *(*func)(const entry_ref *, void *), void* passThru,
1222 	int32 maxCount)
1223 {
1224 	return EachEntryRefCommon(const_cast<BMessage *>(message),
1225 		(EachEntryIteratee)func, passThru, maxCount);
1226 }
1227 
1228 
1229 void
1230 TruncateLeaf(BString* string)
1231 {
1232 	for (int32 index = string->Length(); index >= 0; index--) {
1233 		if ((*string)[index] == '/') {
1234 			string->Truncate(index + 1);
1235 			return;
1236 		}
1237 	}
1238 }
1239 
1240 
1241 int64
1242 StringToScalar(const char* text)
1243 {
1244 	char* end;
1245 	int64 val;
1246 
1247 	char* buffer = new char [strlen(text) + 1];
1248 	strcpy(buffer, text);
1249 
1250 	if (strstr(buffer, "k") || strstr(buffer, "K")) {
1251 		val = strtoll(buffer, &end, 10);
1252 		val *= kKBSize;
1253 	} else if (strstr(buffer, "mb") || strstr(buffer, "MB")) {
1254 		val = strtoll(buffer, &end, 10);
1255 		val *= kMBSize;
1256 	} else if (strstr(buffer, "gb") || strstr(buffer, "GB")) {
1257 		val = strtoll(buffer, &end, 10);
1258 		val *= kGBSize;
1259 	} else if (strstr(buffer, "byte") || strstr(buffer, "BYTE")) {
1260 		val = strtoll(buffer, &end, 10);
1261 		val *= kGBSize;
1262 	} else {
1263 		// no suffix, try plain byte conversion
1264 		val = strtoll(buffer, &end, 10);
1265 	}
1266 	delete[] buffer;
1267 
1268 	return val;
1269 }
1270 
1271 
1272 int32
1273 ListIconSize()
1274 {
1275 	static int32 sIconSize = be_control_look->ComposeIconSize(B_MINI_ICON)
1276 		.IntegerWidth() + 1;
1277 	return sIconSize;
1278 }
1279 
1280 
1281 static BRect
1282 LineBounds(BPoint where, float length, bool vertical)
1283 {
1284 	BRect rect;
1285 	rect.SetLeftTop(where);
1286 	rect.SetRightBottom(where + BPoint(2, 2));
1287 	if (vertical)
1288 		rect.bottom = rect.top + length;
1289 	else
1290 		rect.right = rect.left + length;
1291 
1292 	return rect;
1293 }
1294 
1295 
1296 SeparatorLine::SeparatorLine(BPoint where, float length, bool vertical,
1297 	const char* name)
1298 	:
1299 	BView(LineBounds(where, length, vertical), name,
1300 		B_FOLLOW_LEFT | B_FOLLOW_TOP, B_WILL_DRAW)
1301 {
1302 	SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
1303 	SetLowUIColor(B_PANEL_BACKGROUND_COLOR);
1304 }
1305 
1306 
1307 void
1308 SeparatorLine::Draw(BRect)
1309 {
1310 	BRect bounds(Bounds());
1311 	rgb_color hiliteColor = tint_color(ViewColor(), 1.5f);
1312 
1313 	bool vertical = (bounds.left > bounds.right - 3);
1314 	BeginLineArray(2);
1315 	if (vertical) {
1316 		AddLine(bounds.LeftTop(), bounds.LeftBottom(), hiliteColor);
1317 		AddLine(bounds.LeftTop() + BPoint(1, 0),
1318 			bounds.LeftBottom() + BPoint(1, 0), kWhite);
1319 	} else {
1320 		AddLine(bounds.LeftTop(), bounds.RightTop(), hiliteColor);
1321 		AddLine(bounds.LeftTop() + BPoint(0, 1),
1322 			bounds.RightTop() + BPoint(0, 1), kWhite);
1323 	}
1324 	EndLineArray();
1325 }
1326 
1327 
1328 void
1329 HexDump(const void* buf, int32 length)
1330 {
1331 	const int32 kBytesPerLine = 16;
1332 	int32 offset;
1333 	unsigned char* buffer = (unsigned char*)buf;
1334 
1335 	for (offset = 0; ; offset += kBytesPerLine, buffer += kBytesPerLine) {
1336 		int32 remain = length;
1337 		int32 index;
1338 
1339 		printf( "0x%06x: ", (int)offset);
1340 
1341 		for (index = 0; index < kBytesPerLine; index++) {
1342 			if (remain-- > 0)
1343 				printf("%02x%c", buffer[index], remain > 0 ? ',' : ' ');
1344 			else
1345 				printf("   ");
1346 		}
1347 
1348 		remain = length;
1349 		printf(" \'");
1350 		for (index = 0; index < kBytesPerLine; index++) {
1351 			if (remain-- > 0)
1352 				printf("%c", buffer[index] > ' ' ? buffer[index] : '.');
1353 			else
1354 				printf(" ");
1355 		}
1356 		printf("\'\n");
1357 
1358 		length -= kBytesPerLine;
1359 		if (length <= 0)
1360 			break;
1361 	}
1362 	fflush(stdout);
1363 }
1364 
1365 
1366 int
1367 CompareLabels(const BMenuItem* item1, const BMenuItem* item2)
1368 {
1369 	return strcasecmp(item1->Label(), item2->Label());
1370 }
1371 
1372 
1373 void
1374 EnableNamedMenuItem(BMenu* menu, const char* itemName, bool on)
1375 {
1376 	BMenuItem* item = menu->FindItem(itemName);
1377 	if (item != NULL)
1378 		item->SetEnabled(on);
1379 }
1380 
1381 
1382 void
1383 MarkNamedMenuItem(BMenu* menu, const char* itemName, bool on)
1384 {
1385 	BMenuItem* item = menu->FindItem(itemName);
1386 	if (item != NULL)
1387 		item->SetMarked(on);
1388 }
1389 
1390 
1391 void
1392 EnableNamedMenuItem(BMenu* menu, uint32 commandName, bool on)
1393 {
1394 	BMenuItem* item = menu->FindItem(commandName);
1395 	if (item != NULL)
1396 		item->SetEnabled(on);
1397 }
1398 
1399 
1400 void
1401 MarkNamedMenuItem(BMenu* menu, uint32 commandName, bool on)
1402 {
1403 	BMenuItem* item = menu->FindItem(commandName);
1404 	if (item != NULL)
1405 		item->SetMarked(on);
1406 }
1407 
1408 
1409 void
1410 DeleteSubmenu(BMenuItem* submenuItem)
1411 {
1412 	if (submenuItem == NULL)
1413 		return;
1414 
1415 	BMenu* menu = submenuItem->Submenu();
1416 	if (menu == NULL)
1417 		return;
1418 
1419 	for (;;) {
1420 		BMenuItem* item = menu->RemoveItem((int32)0);
1421 		if (item == NULL)
1422 			return;
1423 
1424 		delete item;
1425 	}
1426 }
1427 
1428 
1429 status_t
1430 GetAppSignatureFromAttr(BFile* file, char* attr)
1431 {
1432 	// This call is a performance improvement that
1433 	// avoids using the BAppFileInfo API when retrieving the
1434 	// app signature -- the call is expensive because by default
1435 	// the resource fork is scanned to read the attribute
1436 
1437 #ifdef B_APP_FILE_INFO_IS_FAST
1438 	BAppFileInfo appFileInfo(file);
1439 	return appFileInfo.GetSignature(attr);
1440 #else
1441 	ssize_t readResult = file->ReadAttr(kAttrAppSignature, B_MIME_STRING_TYPE,
1442 		0, attr, B_MIME_TYPE_LENGTH);
1443 
1444 	if (readResult <= 0)
1445 		return (status_t)readResult;
1446 
1447 	return B_OK;
1448 #endif // B_APP_FILE_INFO_IS_FAST
1449 }
1450 
1451 
1452 status_t
1453 GetAppIconFromAttr(BFile* file, BBitmap* icon, icon_size which)
1454 {
1455 	// This call is a performance improvement that
1456 	// avoids using the BAppFileInfo API when retrieving the
1457 	// app icons -- the call is expensive because by default
1458 	// the resource fork is scanned to read the icons
1459 
1460 //#ifdef B_APP_FILE_INFO_IS_FAST
1461 	BAppFileInfo appFileInfo(file);
1462 	return appFileInfo.GetIcon(icon, which);
1463 //#else
1464 //
1465 //	const char* attrName = kAttrIcon;
1466 //	uint32 type = B_VECTOR_ICON_TYPE;
1467 //
1468 //	// try vector icon
1469 //	attr_info ainfo;
1470 //	status_t result = file->GetAttrInfo(attrName, &ainfo);
1471 //
1472 //	if (result == B_OK) {
1473 //		uint8 buffer[ainfo.size];
1474 //		ssize_t readResult = file->ReadAttr(attrName, type, 0, buffer,
1475 //			ainfo.size);
1476 //		if (readResult == ainfo.size) {
1477 //			if (BIconUtils::GetVectorIcon(buffer, ainfo.size, icon) == B_OK)
1478 //				return B_OK;
1479 //		}
1480 //	}
1481 //
1482 //	// try again with R5 icons
1483 //	attrName = which == B_LARGE_ICON ? kAttrLargeIcon : kAttrMiniIcon;
1484 //	type = which == B_LARGE_ICON ? LARGE_ICON_TYPE : MINI_ICON_TYPE;
1485 //
1486 //	result = file->GetAttrInfo(attrName, &ainfo);
1487 //	if (result < B_OK)
1488 //		return result;
1489 //
1490 //	uint8 buffer[ainfo.size];
1491 //
1492 //	ssize_t readResult = file->ReadAttr(attrName, type, 0, buffer, ainfo.size);
1493 //	if (readResult <= 0)
1494 //		return (status_t)readResult;
1495 //
1496 //	if (icon->ColorSpace() != B_CMAP8)
1497 //		result = BIconUtils::ConvertFromCMAP8(buffer, which, which, which, icon);
1498 //	else
1499 //		icon->SetBits(buffer, icon->BitsLength(), 0, B_CMAP8);
1500 //
1501 //	return result;
1502 //#endif	// B_APP_FILE_INFO_IS_FAST
1503 }
1504 
1505 
1506 status_t
1507 GetFileIconFromAttr(BNode* node, BBitmap* icon, icon_size which)
1508 {
1509 	// get icon from the node info
1510 	BNodeInfo nodeInfo(node);
1511 	return nodeInfo.GetIcon(icon, which);
1512 }
1513 
1514 
1515 //	#pragma mark - PrintToStream
1516 
1517 
1518 void
1519 PrintToStream(rgb_color color)
1520 {
1521 	printf("r:%x, g:%x, b:%x, a:%x\n",
1522 		color.red, color.green, color.blue, color.alpha);
1523 }
1524 
1525 
1526 //	#pragma mark - EachMenuItem
1527 
1528 
1529 extern BMenuItem*
1530 EachMenuItem(BMenu* menu, bool recursive, BMenuItem* (*func)(BMenuItem *))
1531 {
1532 	int32 count = menu->CountItems();
1533 	for (int32 index = 0; index < count; index++) {
1534 		BMenuItem* item = menu->ItemAt(index);
1535 		BMenuItem* newItem = (func)(item);
1536 		if (newItem != NULL)
1537 			return newItem;
1538 
1539 		if (recursive) {
1540 			BMenu* submenu = menu->SubmenuAt(index);
1541 			if (submenu != NULL)
1542 				return EachMenuItem(submenu, true, func);
1543 		}
1544 	}
1545 
1546 	return NULL;
1547 }
1548 
1549 
1550 extern const BMenuItem*
1551 EachMenuItem(const BMenu* menu, bool recursive,
1552 	BMenuItem* (*func)(const BMenuItem *))
1553 {
1554 	int32 count = menu->CountItems();
1555 	for (int32 index = 0; index < count; index++) {
1556 		BMenuItem* item = menu->ItemAt(index);
1557 		BMenuItem* newItem = (func)(item);
1558 		if (newItem != NULL)
1559 			return newItem;
1560 
1561 		if (recursive) {
1562 			BMenu* submenu = menu->SubmenuAt(index);
1563 			if (submenu != NULL)
1564 				return EachMenuItem(submenu, true, func);
1565 		}
1566 	}
1567 
1568 	return NULL;
1569 }
1570 
1571 
1572 //	#pragma mark - PositionPassingMenuItem
1573 
1574 
1575 PositionPassingMenuItem::PositionPassingMenuItem(const char* title,
1576 	BMessage* message, char shortcut, uint32 modifiers)
1577 	:
1578 	BMenuItem(title, message, shortcut, modifiers)
1579 {
1580 }
1581 
1582 
1583 PositionPassingMenuItem::PositionPassingMenuItem(BMenu* menu, BMessage* message)
1584 	:
1585 	BMenuItem(menu, message)
1586 {
1587 }
1588 
1589 
1590 PositionPassingMenuItem::PositionPassingMenuItem(BMessage* data)
1591 	:
1592 	BMenuItem(data)
1593 {
1594 }
1595 
1596 
1597 BArchivable*
1598 PositionPassingMenuItem::Instantiate(BMessage* data)
1599 {
1600 	if (validate_instantiation(data, "PositionPassingMenuItem"))
1601 		return new PositionPassingMenuItem(data);
1602 
1603 	return NULL;
1604 }
1605 
1606 
1607 status_t
1608 PositionPassingMenuItem::Invoke(BMessage* message)
1609 {
1610 	if (Menu() == NULL)
1611 		return B_ERROR;
1612 
1613 	if (!IsEnabled())
1614 		return B_ERROR;
1615 
1616 	if (message == NULL)
1617 		message = Message();
1618 
1619 	if (message == NULL)
1620 		return B_BAD_VALUE;
1621 
1622 	BMessage clone(*message);
1623 	clone.AddInt32("index", Menu()->IndexOf(this));
1624 	clone.AddInt64("when", system_time());
1625 	clone.AddPointer("source", this);
1626 
1627 	// embed the invoke location of the menu so that we can create
1628 	// a new folder, etc. on the spot
1629 	BMenu* menu = Menu();
1630 
1631 	for (;;) {
1632 		if (!menu->Supermenu())
1633 			break;
1634 
1635 		menu = menu->Supermenu();
1636 	}
1637 
1638 	// use the window position only, if the item was invoked from the menu
1639 	// menu->Window() points to the window the item was invoked from
1640 	if (dynamic_cast<BContainerWindow*>(menu->Window()) == NULL) {
1641 		AutoLocker<BLooper> lock(menu->Looper());
1642 		if (lock.IsLocked()) {
1643 			BPoint invokeOrigin(menu->Window()->Frame().LeftTop());
1644 			clone.AddPoint("be:invoke_origin", invokeOrigin);
1645 		}
1646 	}
1647 
1648 	return BInvoker::Invoke(&clone);
1649 }
1650 
1651 
1652 //	#pragma mark - BPrivate functions
1653 
1654 
1655 bool
1656 BootedInSafeMode()
1657 {
1658 	const char* safeMode = getenv("SAFEMODE");
1659 	return (safeMode && strcmp(safeMode, "yes") == 0);
1660 }
1661 
1662 
1663 float
1664 ComputeTypeAheadScore(const char* text, const char* match, bool wordMode)
1665 {
1666 	// highest score: exact match
1667 	const char* found = strcasestr(text, match);
1668 	if (found != NULL) {
1669 		if (found == text)
1670 			return kExactMatchScore;
1671 
1672 		return 1.f / (found - text);
1673 	}
1674 
1675 	// there was no exact match
1676 
1677 	// second best: all characters at word beginnings
1678 	if (wordMode) {
1679 		float score = 0;
1680 		for (int32 j = 0, k = 0; match[j]; j++) {
1681 			while (text[k]
1682 				&& tolower(text[k]) != tolower(match[j])) {
1683 				k++;
1684 			}
1685 			if (text[k] == '\0') {
1686 				score = 0;
1687 				break;
1688 			}
1689 
1690 			bool wordStart = k == 0 || isspace(text[k - 1]);
1691 			if (wordStart)
1692 				score++;
1693 			if (j > 0) {
1694 				bool wordEnd = !text[k + 1] || isspace(text[k + 1]);
1695 				if (wordEnd)
1696 					score += 0.3;
1697 				if (match[j - 1] == text[k - 1])
1698 					score += 0.7;
1699 			}
1700 
1701 			score += 1.f / (k + 1);
1702 			k++;
1703 		}
1704 
1705 		return score;
1706 	}
1707 
1708 	return -1;
1709 }
1710 
1711 
1712 //	#pragma mark - throw on error functions.
1713 
1714 
1715 void
1716 _ThrowOnError(status_t result, const char* DEBUG_ONLY(file),
1717 	int32 DEBUG_ONLY(line))
1718 {
1719 	if (result != B_OK) {
1720 		PRINT(("%s at %s:%d\n", strerror(result), file, (int)line));
1721 		throw result;
1722 	}
1723 }
1724 
1725 
1726 void
1727 _ThrowIfNotSize(ssize_t size, const char* DEBUG_ONLY(file),
1728 	int32 DEBUG_ONLY(line))
1729 {
1730 	if (size < B_OK) {
1731 		PRINT(("%s at %s:%d\n", strerror((status_t)size), file, (int)line));
1732 		throw (status_t)size;
1733 	}
1734 }
1735 
1736 
1737 void
1738 _ThrowOnAssert(bool success, const char* DEBUG_ONLY(file),
1739 	int32 DEBUG_ONLY(line))
1740 {
1741 	if (!success) {
1742 		PRINT(("Assert failed at %s:%d\n", file, (int)line));
1743 		throw B_ERROR;
1744 	}
1745 }
1746 
1747 } // namespace BPrivate
1748