xref: /haiku/src/kits/tracker/Utilities.cpp (revision 9e25244c5e9051f6cd333820d6332397361abd6c)
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 <Debug.h>
49 #include <Font.h>
50 #include <IconUtils.h>
51 #include <MenuItem.h>
52 #include <Mime.h>
53 #include <Node.h>
54 #include <NodeInfo.h>
55 #include <OS.h>
56 #include <PopUpMenu.h>
57 #include <Region.h>
58 #include <StorageDefs.h>
59 #include <TextView.h>
60 #include <TranslatorFormats.h>
61 #include <TranslatorRoster.h>
62 #include <TranslationUtils.h>
63 #include <TypeConstants.h>
64 #include <View.h>
65 #include <Volume.h>
66 #include <VolumeRoster.h>
67 #include <Window.h>
68 
69 #include "Attributes.h"
70 #include "Commands.h"
71 #include "ContainerWindow.h"
72 #include "FSUtils.h"
73 #include "MimeTypes.h"
74 #include "Model.h"
75 #include "PoseView.h"
76 
77 
78 #ifdef B_XXL_ICON
79 #	undef B_XXL_ICON
80 #endif
81 #define B_XXL_ICON 128
82 
83 #ifndef _IMPEXP_BE
84 #	define _IMPEXP_BE
85 #endif
86 extern _IMPEXP_BE const uint32	LARGE_ICON_TYPE;
87 extern _IMPEXP_BE const uint32	MINI_ICON_TYPE;
88 
89 
90 FILE* logFile = NULL;
91 
92 static const float kMinSeparatorStubX = 10;
93 static const float kStubToStringSlotX = 5;
94 
95 
96 namespace BPrivate {
97 
98 const float kExactMatchScore = INFINITY;
99 
100 
101 bool gLocalizedNamePreferred;
102 
103 
104 bool
105 SecondaryMouseButtonDown(int32 modifiers, int32 buttons)
106 {
107 	return (buttons & B_SECONDARY_MOUSE_BUTTON) != 0
108 		|| ((buttons & B_PRIMARY_MOUSE_BUTTON) != 0
109 			&& (modifiers & B_CONTROL_KEY) != 0);
110 }
111 
112 
113 uint32
114 HashString(const char* string, uint32 seed)
115 {
116 	char ch;
117 	uint32 hash = seed;
118 
119 	while((ch = *string++) != 0) {
120 		hash = (hash << 7) ^ (hash >> 24);
121 		hash ^= ch;
122 	}
123 	hash ^= hash << 12;
124 
125 	return hash;
126 }
127 
128 
129 uint32
130 AttrHashString(const char* string, uint32 type)
131 {
132 	char c;
133 	uint32 hash = 0;
134 
135 	while((c = *string++) != 0) {
136 		hash = (hash << 7) ^ (hash >> 24);
137 		hash ^= c;
138 	}
139 	hash ^= hash << 12;
140 
141 	hash &= ~0xff;
142 	hash |= type;
143 
144 	return hash;
145 }
146 
147 
148 bool
149 ValidateStream(BMallocIO* stream, uint32 key, int32 version)
150 {
151 	uint32 testKey;
152 	int32 testVersion;
153 
154 	if (stream->Read(&testKey, sizeof(uint32)) <= 0
155 		|| stream->Read(&testVersion, sizeof(int32)) <= 0) {
156 		return false;
157 	}
158 
159 	return testKey == key && testVersion == version;
160 }
161 
162 
163 void
164 DisallowFilenameKeys(BTextView* textView)
165 {
166 	textView->DisallowChar('/');
167 }
168 
169 
170 void
171 DisallowMetaKeys(BTextView* textView)
172 {
173 	textView->DisallowChar(B_TAB);
174 	textView->DisallowChar(B_ESCAPE);
175 	textView->DisallowChar(B_INSERT);
176 	textView->DisallowChar(B_DELETE);
177 	textView->DisallowChar(B_HOME);
178 	textView->DisallowChar(B_END);
179 	textView->DisallowChar(B_PAGE_UP);
180 	textView->DisallowChar(B_PAGE_DOWN);
181 	textView->DisallowChar(B_FUNCTION_KEY);
182 }
183 
184 
185 PeriodicUpdatePoses::PeriodicUpdatePoses()
186 	:
187 	fPoseList(20, true)
188 {
189 	fLock = new Benaphore("PeriodicUpdatePoses");
190 }
191 
192 
193 PeriodicUpdatePoses::~PeriodicUpdatePoses()
194 {
195 	fLock->Lock();
196 	fPoseList.MakeEmpty();
197 	delete fLock;
198 }
199 
200 
201 void
202 PeriodicUpdatePoses::AddPose(BPose* pose, BPoseView* poseView,
203 	PeriodicUpdateCallback callback, void* cookie)
204 {
205 	periodic_pose* periodic = new periodic_pose;
206 	periodic->pose = pose;
207 	periodic->pose_view = poseView;
208 	periodic->callback = callback;
209 	periodic->cookie = cookie;
210 	fPoseList.AddItem(periodic);
211 }
212 
213 
214 bool
215 PeriodicUpdatePoses::RemovePose(BPose* pose, void** cookie)
216 {
217 	int32 count = fPoseList.CountItems();
218 	for (int32 index = 0; index < count; index++) {
219 		if (fPoseList.ItemAt(index)->pose == pose) {
220 			if (!fLock->Lock())
221 				return false;
222 
223 			periodic_pose* periodic = fPoseList.RemoveItemAt(index);
224 			if (cookie)
225 				*cookie = periodic->cookie;
226 			delete periodic;
227 			fLock->Unlock();
228 			return true;
229 		}
230 	}
231 
232 	return false;
233 }
234 
235 
236 void
237 PeriodicUpdatePoses::DoPeriodicUpdate(bool forceRedraw)
238 {
239 	if (!fLock->Lock())
240 		return;
241 
242 	int32 count = fPoseList.CountItems();
243 	for (int32 index = 0; index < count; index++) {
244 		periodic_pose* periodic = fPoseList.ItemAt(index);
245 		if ((periodic->callback(periodic->pose, periodic->cookie)
246 			|| forceRedraw) && periodic->pose_view->LockLooper()) {
247 			periodic->pose_view->UpdateIcon(periodic->pose);
248 			periodic->pose_view->UnlockLooper();
249 		}
250 	}
251 
252 	fLock->Unlock();
253 }
254 
255 
256 PeriodicUpdatePoses gPeriodicUpdatePoses;
257 
258 }	// namespace BPrivate
259 
260 
261 void
262 PoseInfo::EndianSwap(void* castToThis)
263 {
264 	PoseInfo* self = (PoseInfo*)castToThis;
265 
266 	PRINT(("swapping PoseInfo\n"));
267 
268 	STATIC_ASSERT(sizeof(ino_t) == sizeof(int64));
269 	self->fInitedDirectory = SwapInt64(self->fInitedDirectory);
270 	swap_data(B_POINT_TYPE, &self->fLocation, sizeof(BPoint), B_SWAP_ALWAYS);
271 
272 	// do a sanity check on the icon position
273 	if (self->fLocation.x < -20000 || self->fLocation.x > 20000
274 		|| self->fLocation.y < -20000 || self->fLocation.y > 20000) {
275 		// position out of range, force autoplcemement
276 		PRINT((" rejecting icon position out of range\n"));
277 		self->fInitedDirectory = -1LL;
278 		self->fLocation = BPoint(0, 0);
279 	}
280 }
281 
282 
283 void
284 PoseInfo::PrintToStream()
285 {
286 	PRINT(("%s, inode:%" B_PRIx64 ", location %f %f\n",
287 		fInvisible ? "hidden" : "visible",
288 		fInitedDirectory, fLocation.x, fLocation.y));
289 }
290 
291 
292 // #pragma mark - ExtendedPoseInfo
293 
294 
295 size_t
296 ExtendedPoseInfo::Size() const
297 {
298 	return sizeof(ExtendedPoseInfo) + fNumFrames * sizeof(FrameLocation);
299 }
300 
301 
302 size_t
303 ExtendedPoseInfo::Size(int32 count)
304 {
305 	return sizeof(ExtendedPoseInfo) + count * sizeof(FrameLocation);
306 }
307 
308 
309 size_t
310 ExtendedPoseInfo::SizeWithHeadroom() const
311 {
312 	return sizeof(ExtendedPoseInfo) + (fNumFrames + 1) * sizeof(FrameLocation);
313 }
314 
315 
316 size_t
317 ExtendedPoseInfo::SizeWithHeadroom(size_t oldSize)
318 {
319 	int32 count = (ssize_t)oldSize - (ssize_t)sizeof(ExtendedPoseInfo);
320 	if (count > 0)
321 		count /= sizeof(FrameLocation);
322 	else
323 		count = 0;
324 
325 	return Size(count + 1);
326 }
327 
328 
329 bool
330 ExtendedPoseInfo::HasLocationForFrame(BRect frame) const
331 {
332 	for (int32 index = 0; index < fNumFrames; index++) {
333 		if (fLocations[index].fFrame == frame)
334 			return true;
335 	}
336 
337 	return false;
338 }
339 
340 
341 BPoint
342 ExtendedPoseInfo::LocationForFrame(BRect frame) const
343 {
344 	for (int32 index = 0; index < fNumFrames; index++) {
345 		if (fLocations[index].fFrame == frame)
346 			return fLocations[index].fLocation;
347 	}
348 
349 	TRESPASS();
350 	return BPoint(0, 0);
351 }
352 
353 
354 bool
355 ExtendedPoseInfo::SetLocationForFrame(BPoint newLocation, BRect frame)
356 {
357 	for (int32 index = 0; index < fNumFrames; index++) {
358 		if (fLocations[index].fFrame == frame) {
359 			if (fLocations[index].fLocation == newLocation)
360 				return false;
361 
362 			fLocations[index].fLocation = newLocation;
363 			return true;
364 		}
365 	}
366 
367 	fLocations[fNumFrames].fFrame = frame;
368 	fLocations[fNumFrames].fLocation = newLocation;
369 	fLocations[fNumFrames].fWorkspaces = 0xffffffff;
370 	fNumFrames++;
371 
372 	return true;
373 }
374 
375 
376 void
377 ExtendedPoseInfo::EndianSwap(void* castToThis)
378 {
379 	ExtendedPoseInfo* self = (ExtendedPoseInfo *)castToThis;
380 
381 	PRINT(("swapping ExtendedPoseInfo\n"));
382 
383 	self->fWorkspaces = SwapUInt32(self->fWorkspaces);
384 	self->fNumFrames = SwapInt32(self->fNumFrames);
385 
386 	for (int32 index = 0; index < self->fNumFrames; index++) {
387 		swap_data(B_POINT_TYPE, &self->fLocations[index].fLocation,
388 			sizeof(BPoint), B_SWAP_ALWAYS);
389 
390 		if (self->fLocations[index].fLocation.x < -20000
391 			|| self->fLocations[index].fLocation.x > 20000
392 			|| self->fLocations[index].fLocation.y < -20000
393 			|| self->fLocations[index].fLocation.y > 20000) {
394 			// position out of range, force autoplcemement
395 			PRINT((" rejecting icon position out of range\n"));
396 			self->fLocations[index].fLocation = BPoint(0, 0);
397 		}
398 		swap_data(B_RECT_TYPE, &self->fLocations[index].fFrame,
399 			sizeof(BRect), B_SWAP_ALWAYS);
400 	}
401 }
402 
403 
404 void
405 ExtendedPoseInfo::PrintToStream()
406 {
407 }
408 
409 
410 // #pragma mark - OffscreenBitmap
411 
412 
413 OffscreenBitmap::OffscreenBitmap(BRect frame)
414 	:
415 	fBitmap(NULL)
416 {
417 	NewBitmap(frame);
418 }
419 
420 
421 OffscreenBitmap::OffscreenBitmap()
422 	:
423 	fBitmap(NULL)
424 {
425 }
426 
427 
428 OffscreenBitmap::~OffscreenBitmap()
429 {
430 	delete fBitmap;
431 }
432 
433 
434 void
435 OffscreenBitmap::NewBitmap(BRect bounds)
436 {
437 	delete fBitmap;
438 	fBitmap = new(std::nothrow) BBitmap(bounds, B_RGB32, true);
439 	if (fBitmap != NULL && fBitmap->Lock()) {
440 		BView* view = new BView(fBitmap->Bounds(), "", B_FOLLOW_NONE, 0);
441 		fBitmap->AddChild(view);
442 
443 		BRect clipRect = view->Bounds();
444 		BRegion newClip;
445 		newClip.Set(clipRect);
446 		view->ConstrainClippingRegion(&newClip);
447 
448 		fBitmap->Unlock();
449 	} else {
450 		delete fBitmap;
451 		fBitmap = NULL;
452 	}
453 }
454 
455 
456 BView*
457 OffscreenBitmap::BeginUsing(BRect frame)
458 {
459 	if (!fBitmap || fBitmap->Bounds() != frame)
460 		NewBitmap(frame);
461 
462 	fBitmap->Lock();
463 	return View();
464 }
465 
466 
467 void
468 OffscreenBitmap::DoneUsing()
469 {
470 	fBitmap->Unlock();
471 }
472 
473 
474 BBitmap*
475 OffscreenBitmap::Bitmap() const
476 {
477 	ASSERT(fBitmap);
478 	ASSERT(fBitmap->IsLocked());
479 	return fBitmap;
480 }
481 
482 
483 BView*
484 OffscreenBitmap::View() const
485 {
486 	ASSERT(fBitmap);
487 	return fBitmap->ChildAt(0);
488 }
489 
490 
491 // #pragma mark - BPrivate functions
492 
493 
494 namespace BPrivate {
495 
496 // Changes the alpha value of the given bitmap to create a nice
497 // horizontal fade out in the specified region.
498 // "from" is always transparent, "to" opaque.
499 void
500 FadeRGBA32Horizontal(uint32* bits, int32 width, int32 height, int32 from,
501 	int32 to)
502 {
503 	// check parameters
504 	if (width < 0 || height < 0 || from < 0 || to < 0)
505 		return;
506 
507 	float change = 1.f / (to - from);
508 	if (from > to) {
509 		int32 temp = from;
510 		from = to;
511 		to = temp;
512 	}
513 
514 	for (int32 y = 0; y < height; y++) {
515 		float alpha = change > 0 ? 0.0f : 1.0f;
516 
517 		for (int32 x = from; x <= to; x++) {
518 			if (bits[x] & 0xff000000) {
519 				uint32 a = uint32((bits[x] >> 24) * alpha);
520 				bits[x] = (bits[x] & 0x00ffffff) | (a << 24);
521 			}
522 			alpha += change;
523 		}
524 		bits += width;
525 	}
526 }
527 
528 
529 /*!	Changes the alpha value of the given bitmap to create a nice
530 	vertical fade out in the specified region.
531 	"from" is always transparent, "to" opaque.
532 */
533 void
534 FadeRGBA32Vertical(uint32* bits, int32 width, int32 height, int32 from,
535 	int32 to)
536 {
537 	// check parameters
538 	if (width < 0 || height < 0 || from < 0 || to < 0)
539 		return;
540 
541 	if (from > to)
542 		bits += width * (height - (from - to));
543 
544 	float change = 1.f / (to - from);
545 	if (from > to) {
546 		int32 temp = from;
547 		from = to;
548 		to = temp;
549 	}
550 
551 	float alpha = change > 0 ? 0.0f : 1.0f;
552 
553 	for (int32 y = from; y <= to; y++) {
554 		for (int32 x = 0; x < width; x++) {
555 			if (bits[x] & 0xff000000) {
556 				uint32 a = uint32((bits[x] >> 24) * alpha);
557 				bits[x] = (bits[x] & 0x00ffffff) | (a << 24);
558 			}
559 		}
560 		alpha += change;
561 		bits += width;
562 	}
563 }
564 
565 
566 }	// namespace BPrivate
567 
568 
569 // #pragma mark - DraggableIcon
570 
571 
572 DraggableIcon::DraggableIcon(BRect rect, const char* name,
573 	const char* type, icon_size which, const BMessage* message,
574 	BMessenger target, uint32 resizingMode, uint32 flags)
575 	:
576 	BView(rect, name, resizingMode, flags),
577 	fMessage(*message),
578 	fTarget(target)
579 {
580 	fBitmap = new BBitmap(Bounds(), kDefaultIconDepth);
581 	BMimeType mime(type);
582 	status_t result = mime.GetIcon(fBitmap, which);
583 	ASSERT(mime.IsValid());
584 	if (result != B_OK) {
585 		PRINT(("failed to get icon for %s, %s\n", type, strerror(result)));
586 		BMimeType mime(B_FILE_MIMETYPE);
587 		ASSERT(mime.IsInstalled());
588 		mime.GetIcon(fBitmap, which);
589 	}
590 }
591 
592 
593 DraggableIcon::~DraggableIcon()
594 {
595 	delete fBitmap;
596 }
597 
598 
599 void
600 DraggableIcon::SetTarget(BMessenger target)
601 {
602 	fTarget = target;
603 }
604 
605 
606 BRect
607 DraggableIcon::PreferredRect(BPoint offset, icon_size which)
608 {
609 	BRect rect(0, 0, which - 1, which - 1);
610 	rect.OffsetTo(offset);
611 	return rect;
612 }
613 
614 
615 void
616 DraggableIcon::AttachedToWindow()
617 {
618 	AdoptParentColors();
619 }
620 
621 
622 void
623 DraggableIcon::MouseDown(BPoint point)
624 {
625 	if (!DragStarted(&fMessage))
626 		return;
627 
628 	BRect rect(Bounds());
629 	BBitmap* dragBitmap = new BBitmap(rect, B_RGBA32, true);
630 	dragBitmap->Lock();
631 	BView* view = new BView(dragBitmap->Bounds(), "", B_FOLLOW_NONE, 0);
632 	dragBitmap->AddChild(view);
633 	view->SetOrigin(0, 0);
634 	BRect clipRect(view->Bounds());
635 	BRegion newClip;
636 	newClip.Set(clipRect);
637 	view->ConstrainClippingRegion(&newClip);
638 
639 	// Transparent draw magic
640 	view->SetHighColor(0, 0, 0, 0);
641 	view->FillRect(view->Bounds());
642 	view->SetDrawingMode(B_OP_ALPHA);
643 	view->SetHighColor(0, 0, 0, 128);
644 		// set the level of transparency by value
645 	view->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_COMPOSITE);
646 	view->DrawBitmap(fBitmap);
647 	view->Sync();
648 	dragBitmap->Unlock();
649 	DragMessage(&fMessage, dragBitmap, B_OP_ALPHA, point, fTarget.Target(0));
650 }
651 
652 
653 bool
654 DraggableIcon::DragStarted(BMessage*)
655 {
656 	return true;
657 }
658 
659 
660 void
661 DraggableIcon::Draw(BRect)
662 {
663 	SetDrawingMode(B_OP_ALPHA);
664 	SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
665 	DrawBitmap(fBitmap);
666 }
667 
668 
669 // #pragma mark - FlickerFreeStringView
670 
671 
672 FlickerFreeStringView::FlickerFreeStringView(BRect bounds, const char* name,
673 	const char* text, uint32 resizingMode, uint32 flags)
674 	:
675 	BStringView(bounds, name, text, resizingMode, flags),
676 	fBitmap(NULL),
677 	fViewColor(ViewColor()),
678 	fLowColor(LowColor()),
679 	fOriginalBitmap(NULL)
680 {
681 }
682 
683 
684 FlickerFreeStringView::FlickerFreeStringView(BRect bounds, const char* name,
685 	const char* text, BBitmap* inBitmap, uint32 resizingMode, uint32 flags)
686 	:
687 	BStringView(bounds, name, text, resizingMode, flags),
688 	fBitmap(NULL),
689 	fViewColor(ViewColor()),
690 	fLowColor(LowColor()),
691 	fOriginalBitmap(inBitmap)
692 {
693 }
694 
695 
696 FlickerFreeStringView::~FlickerFreeStringView()
697 {
698 	delete fBitmap;
699 }
700 
701 
702 void
703 FlickerFreeStringView::Draw(BRect)
704 {
705 	BRect bounds(Bounds());
706 	if (fBitmap == NULL)
707 		fBitmap = new OffscreenBitmap(Bounds());
708 
709 	BView* offscreen = fBitmap->BeginUsing(bounds);
710 
711 	if (Parent() != NULL) {
712 		fViewColor = Parent()->ViewColor();
713 		fLowColor = Parent()->ViewColor();
714 	}
715 
716 	offscreen->SetViewColor(fViewColor);
717 	offscreen->SetHighColor(HighColor());
718 	offscreen->SetLowColor(fLowColor);
719 
720 	BFont font;
721 	GetFont(&font);
722 	offscreen->SetFont(&font);
723 
724 	offscreen->Sync();
725 	if (fOriginalBitmap != NULL)
726 		offscreen->DrawBitmap(fOriginalBitmap, Frame(), bounds);
727 	else
728 		offscreen->FillRect(bounds, B_SOLID_LOW);
729 
730 	if (Text() != NULL) {
731 		BPoint loc;
732 
733 		font_height	height;
734 		GetFontHeight(&height);
735 
736 		edge_info eInfo;
737 		switch (Alignment()) {
738 			case B_ALIGN_LEFT:
739 			case B_ALIGN_HORIZONTAL_UNSET:
740 			case B_ALIGN_USE_FULL_WIDTH:
741 			{
742 				// If the first char has a negative left edge give it
743 				// some more room by shifting that much more to the right.
744 				font.GetEdges(Text(), 1, &eInfo);
745 				loc.x = bounds.left + (2 - eInfo.left);
746 				break;
747 			}
748 
749 			case B_ALIGN_CENTER:
750 			{
751 				float width = StringWidth(Text());
752 				float center = (bounds.right - bounds.left) / 2;
753 				loc.x = center - (width/2);
754 				break;
755 			}
756 
757 			case B_ALIGN_RIGHT:
758 			{
759 				float width = StringWidth(Text());
760 				loc.x = bounds.right - width - 2;
761 				break;
762 			}
763 		}
764 		loc.y = bounds.bottom - (1 + height.descent);
765 		offscreen->DrawString(Text(), loc);
766 	}
767 	offscreen->Sync();
768 	SetDrawingMode(B_OP_COPY);
769 	DrawBitmap(fBitmap->Bitmap());
770 	fBitmap->DoneUsing();
771 }
772 
773 
774 void
775 FlickerFreeStringView::AttachedToWindow()
776 {
777 	_inherited::AttachedToWindow();
778 	if (Parent() != NULL) {
779 		fViewColor = Parent()->ViewColor();
780 		fLowColor = Parent()->ViewColor();
781 	}
782 	SetViewColor(B_TRANSPARENT_32_BIT);
783 	SetLowColor(B_TRANSPARENT_32_BIT);
784 }
785 
786 
787 void
788 FlickerFreeStringView::SetViewColor(rgb_color color)
789 {
790 	if (fViewColor != color) {
791 		fViewColor = color;
792 		Invalidate();
793 	}
794 	_inherited::SetViewColor(B_TRANSPARENT_32_BIT);
795 }
796 
797 
798 void
799 FlickerFreeStringView::SetLowColor(rgb_color color)
800 {
801 	if (fLowColor != color) {
802 		fLowColor = color;
803 		Invalidate();
804 	}
805 	_inherited::SetLowColor(B_TRANSPARENT_32_BIT);
806 }
807 
808 
809 // #pragma mark - TitledSeparatorItem
810 
811 
812 TitledSeparatorItem::TitledSeparatorItem(const char* label)
813 	:
814 	BMenuItem(label, 0)
815 {
816 	_inherited::SetEnabled(false);
817 }
818 
819 
820 TitledSeparatorItem::~TitledSeparatorItem()
821 {
822 }
823 
824 
825 void
826 TitledSeparatorItem::SetEnabled(bool)
827 {
828 	// leave disabled
829 }
830 
831 
832 void
833 TitledSeparatorItem::GetContentSize(float* width, float* height)
834 {
835 	_inherited::GetContentSize(width, height);
836 }
837 
838 
839 inline rgb_color
840 ShiftMenuBackgroundColor(float by)
841 {
842 	return tint_color(ui_color(B_MENU_BACKGROUND_COLOR), by);
843 }
844 
845 
846 void
847 TitledSeparatorItem::Draw()
848 {
849 	BRect frame(Frame());
850 
851 	BMenu* parent = Menu();
852 	ASSERT(parent != NULL);
853 
854 	menu_info minfo;
855 	get_menu_info(&minfo);
856 
857 	if (minfo.separator > 0) {
858 		frame.left += 10;
859 		frame.right -= 10;
860 	} else {
861 		frame.left += 1;
862 		frame.right -= 1;
863 	}
864 
865 	float startX = frame.left;
866 	float endX = frame.right;
867 
868 	float maxStringWidth = endX - startX - (2 * kMinSeparatorStubX
869 		+ 2 * kStubToStringSlotX);
870 
871 	// ToDo:
872 	// handle case where maxStringWidth turns out negative here
873 
874 	BString truncatedLabel(Label());
875 	parent->TruncateString(&truncatedLabel, B_TRUNCATE_END, maxStringWidth);
876 
877 	maxStringWidth = parent->StringWidth(truncatedLabel.String());
878 
879 	// first calculate the length of the stub part of the
880 	// divider line, so we can use it for secondStartX
881 	float firstEndX = ((endX - startX) - maxStringWidth) / 2
882 		- kStubToStringSlotX;
883 	if (firstEndX < 0)
884 		firstEndX = 0;
885 
886 	float secondStartX = endX - firstEndX;
887 
888 	// now finish calculating firstEndX
889 	firstEndX += startX;
890 
891 	parent->PushState();
892 
893 	int32 y = (int32) (frame.top + (frame.bottom - frame.top) / 2);
894 
895 	parent->BeginLineArray(minfo.separator == 2 ? 6 : 4);
896 	parent->AddLine(BPoint(frame.left, y), BPoint(firstEndX, y),
897 		ShiftMenuBackgroundColor(B_DARKEN_1_TINT));
898 	parent->AddLine(BPoint(secondStartX, y), BPoint(frame.right, y),
899 		ShiftMenuBackgroundColor(B_DARKEN_1_TINT));
900 
901 	if (minfo.separator == 2) {
902 		y++;
903 		frame.left++;
904 		frame.right--;
905 		parent->AddLine(BPoint(frame.left,y), BPoint(firstEndX, y),
906 			ShiftMenuBackgroundColor(B_DARKEN_1_TINT));
907 		parent->AddLine(BPoint(secondStartX,y), BPoint(frame.right, y),
908 			ShiftMenuBackgroundColor(B_DARKEN_1_TINT));
909 	}
910 	y++;
911 	if (minfo.separator == 2) {
912 		frame.left++;
913 		frame.right--;
914 	}
915 	parent->AddLine(BPoint(frame.left, y), BPoint(firstEndX, y),
916 		ShiftMenuBackgroundColor(B_DARKEN_1_TINT));
917 	parent->AddLine(BPoint(secondStartX, y), BPoint(frame.right, y),
918 		ShiftMenuBackgroundColor(B_DARKEN_1_TINT));
919 
920 	parent->EndLineArray();
921 
922 	font_height finfo;
923 	parent->GetFontHeight(&finfo);
924 
925 	parent->SetLowColor(parent->ViewColor());
926 	BPoint loc(firstEndX + kStubToStringSlotX,
927 		ContentLocation().y + finfo.ascent);
928 
929 	parent->MovePenTo(loc + BPoint(1, 1));
930 	parent->SetHighColor(ShiftMenuBackgroundColor(B_DARKEN_1_TINT));
931 	parent->DrawString(truncatedLabel.String());
932 
933 	parent->MovePenTo(loc);
934 	parent->SetHighColor(ShiftMenuBackgroundColor(B_DISABLED_LABEL_TINT));
935 	parent->DrawString(truncatedLabel.String());
936 
937 	parent->PopState();
938 }
939 
940 
941 // #pragma mark - ShortcutFilter
942 
943 
944 ShortcutFilter::ShortcutFilter(uint32 shortcutKey, uint32 shortcutModifier,
945 	uint32 shortcutWhat, BHandler* target)
946 	:
947 	BMessageFilter(B_KEY_DOWN),
948 	fShortcutKey(shortcutKey),
949 	fShortcutModifier(shortcutModifier),
950 	fShortcutWhat(shortcutWhat),
951 	fTarget(target)
952 {
953 }
954 
955 
956 filter_result
957 ShortcutFilter::Filter(BMessage* message, BHandler**)
958 {
959 	if (message->what == B_KEY_DOWN) {
960 		uint32 modifiers;
961 		uint32 rawKeyChar = 0;
962 		uint8 byte = 0;
963 		int32 key = 0;
964 
965 		if (message->FindInt32("modifiers", (int32*)&modifiers) != B_OK
966 			|| message->FindInt32("raw_char", (int32*)&rawKeyChar) != B_OK
967 			|| message->FindInt8("byte", (int8*)&byte) != B_OK
968 			|| message->FindInt32("key", &key) != B_OK) {
969 			return B_DISPATCH_MESSAGE;
970 		}
971 
972 		modifiers &= B_SHIFT_KEY | B_COMMAND_KEY | B_CONTROL_KEY
973 			| B_OPTION_KEY | B_MENU_KEY;
974 			// strip caps lock, etc.
975 
976 		if (modifiers == fShortcutModifier && rawKeyChar == fShortcutKey) {
977 			fTarget->Looper()->PostMessage(fShortcutWhat, fTarget);
978 			return B_SKIP_MESSAGE;
979 		}
980 	}
981 
982 	// let others deal with this
983 	return B_DISPATCH_MESSAGE;
984 }
985 
986 
987 // #pragma mark - BPrivate functions
988 
989 
990 namespace BPrivate {
991 
992 void
993 EmbedUniqueVolumeInfo(BMessage* message, const BVolume* volume)
994 {
995 	BDirectory rootDirectory;
996 	time_t created;
997 	fs_info info;
998 
999 	if (volume->GetRootDirectory(&rootDirectory) == B_OK
1000 		&& rootDirectory.GetCreationTime(&created) == B_OK
1001 		&& fs_stat_dev(volume->Device(), &info) == 0) {
1002 		message->AddInt64("creationDate", created);
1003 		message->AddInt64("capacity", volume->Capacity());
1004 		message->AddString("deviceName", info.device_name);
1005 		message->AddString("volumeName", info.volume_name);
1006 		message->AddString("fshName", info.fsh_name);
1007 	}
1008 }
1009 
1010 
1011 status_t
1012 MatchArchivedVolume(BVolume* volume, const BMessage* message, int32 index)
1013 {
1014 	int64 created64;
1015 	off_t capacity;
1016 
1017 	if (message->FindInt64("creationDate", index, &created64) != B_OK) {
1018 		int32 created32;
1019 		if (message->FindInt32("creationDate", index, &created32) != B_OK)
1020 			return B_ERROR;
1021 		created64 = created32;
1022 	}
1023 
1024 	time_t created = created64;
1025 
1026 	if (message->FindInt64("capacity", index, &capacity) != B_OK)
1027 		return B_ERROR;
1028 
1029 	BVolumeRoster roster;
1030 	BVolume tempVolume;
1031 	BString deviceName;
1032 	BString volumeName;
1033 	BString fshName;
1034 
1035 	if (message->FindString("deviceName", &deviceName) == B_OK
1036 		&& message->FindString("volumeName", &volumeName) == B_OK
1037 		&& message->FindString("fshName", &fshName) == B_OK) {
1038 		// New style volume identifiers: We have a couple of characteristics,
1039 		// and compute a score from them. The volume with the greatest score
1040 		// (if over a certain threshold) is the one we're looking for. We
1041 		// pick the first volume, in case there is more than one with the
1042 		// same score.
1043 		dev_t foundDevice = -1;
1044 		int foundScore = -1;
1045 		roster.Rewind();
1046 		while (roster.GetNextVolume(&tempVolume) == B_OK) {
1047 			if (tempVolume.IsPersistent() && tempVolume.KnowsQuery()) {
1048 				// get creation time and fs_info
1049 				BDirectory root;
1050 				tempVolume.GetRootDirectory(&root);
1051 				time_t cmpCreated;
1052 				fs_info info;
1053 				if (root.GetCreationTime(&cmpCreated) == B_OK
1054 					&& fs_stat_dev(tempVolume.Device(), &info) == 0) {
1055 					// compute the score
1056 					int score = 0;
1057 
1058 					// creation time
1059 					if (created == cmpCreated)
1060 						score += 5;
1061 
1062 					// capacity
1063 					if (capacity == tempVolume.Capacity())
1064 						score += 4;
1065 
1066 					// device name
1067 					if (deviceName == info.device_name)
1068 						score += 3;
1069 
1070 					// volume name
1071 					if (volumeName == info.volume_name)
1072 						score += 2;
1073 
1074 					// fsh name
1075 					if (fshName == info.fsh_name)
1076 						score += 1;
1077 
1078 					// check score
1079 					if (score >= 9 && score > foundScore) {
1080 						foundDevice = tempVolume.Device();
1081 						foundScore = score;
1082 					}
1083 				}
1084 			}
1085 		}
1086 		if (foundDevice >= 0)
1087 			return volume->SetTo(foundDevice);
1088 	} else {
1089 		// Old style volume identifiers: We have only creation time and
1090 		// capacity. Both must match.
1091 		roster.Rewind();
1092 		while (roster.GetNextVolume(&tempVolume) == B_OK) {
1093 			if (tempVolume.IsPersistent() && tempVolume.KnowsQuery()) {
1094 				BDirectory root;
1095 				tempVolume.GetRootDirectory(&root);
1096 				time_t cmpCreated;
1097 				root.GetCreationTime(&cmpCreated);
1098 				if (created == cmpCreated && capacity == tempVolume.Capacity()) {
1099 					*volume = tempVolume;
1100 					return B_OK;
1101 				}
1102 			}
1103 		}
1104 	}
1105 
1106 	return B_DEV_BAD_DRIVE_NUM;
1107 }
1108 
1109 
1110 void
1111 StringFromStream(BString* string, BMallocIO* stream, bool endianSwap)
1112 {
1113 	int32 length;
1114 	stream->Read(&length, sizeof(length));
1115 	if (endianSwap)
1116 		length = SwapInt32(length);
1117 
1118 	if (length < 0 || length > 10000) {
1119 		// TODO: should fail here
1120 		PRINT(("problems instatiating a string, length probably wrong %"
1121 			B_PRId32 "\n", length));
1122 		return;
1123 	}
1124 
1125 	char* buffer = string->LockBuffer(length + 1);
1126 	stream->Read(buffer, (size_t)length + 1);
1127 	string->UnlockBuffer(length);
1128 }
1129 
1130 
1131 void
1132 StringToStream(const BString* string, BMallocIO* stream)
1133 {
1134 	int32 length = string->Length();
1135 	stream->Write(&length, sizeof(int32));
1136 	stream->Write(string->String(), (size_t)string->Length() + 1);
1137 }
1138 
1139 
1140 int32
1141 ArchiveSize(const BString* string)
1142 {
1143 	return string->Length() + 1 + (ssize_t)sizeof(int32);
1144 }
1145 
1146 
1147 int32
1148 CountRefs(const BMessage* message)
1149 {
1150 	uint32 type;
1151 	int32 count;
1152 	message->GetInfo("refs", &type, &count);
1153 
1154 	return count;
1155 }
1156 
1157 
1158 static entry_ref*
1159 EachEntryRefCommon(BMessage* message, entry_ref *(*func)(entry_ref*, void*),
1160 	void* passThru, int32 maxCount)
1161 {
1162 	uint32 type;
1163 	int32 count;
1164 	message->GetInfo("refs", &type, &count);
1165 
1166 	if (maxCount >= 0 && count > maxCount)
1167 		count = maxCount;
1168 
1169 	for (int32 index = 0; index < count; index++) {
1170 		entry_ref ref;
1171 		message->FindRef("refs", index, &ref);
1172 		entry_ref* newRef = (func)(&ref, passThru);
1173 		if (newRef != NULL)
1174 			return newRef;
1175 	}
1176 
1177 	return NULL;
1178 }
1179 
1180 
1181 bool
1182 ContainsEntryRef(const BMessage* message, const entry_ref* ref)
1183 {
1184 	entry_ref match;
1185 	for (int32 index = 0; (message->FindRef("refs", index, &match) == B_OK);
1186 			index++) {
1187 		if (*ref == match)
1188 			return true;
1189 	}
1190 
1191 	return false;
1192 }
1193 
1194 
1195 entry_ref*
1196 EachEntryRef(BMessage* message, entry_ref* (*func)(entry_ref*, void*),
1197 	void* passThru)
1198 {
1199 	return EachEntryRefCommon(message, func, passThru, -1);
1200 }
1201 
1202 typedef entry_ref *(*EachEntryIteratee)(entry_ref *, void *);
1203 
1204 
1205 const entry_ref*
1206 EachEntryRef(const BMessage* message,
1207 	const entry_ref* (*func)(const entry_ref*, void*), void* passThru)
1208 {
1209 	return EachEntryRefCommon(const_cast<BMessage*>(message),
1210 		(EachEntryIteratee)func, passThru, -1);
1211 }
1212 
1213 
1214 entry_ref*
1215 EachEntryRef(BMessage* message, entry_ref* (*func)(entry_ref*, void*),
1216 	void* passThru, int32 maxCount)
1217 {
1218 	return EachEntryRefCommon(message, func, passThru, maxCount);
1219 }
1220 
1221 
1222 const entry_ref *
1223 EachEntryRef(const BMessage* message,
1224 	const entry_ref *(*func)(const entry_ref *, void *), void* passThru,
1225 	int32 maxCount)
1226 {
1227 	return EachEntryRefCommon(const_cast<BMessage *>(message),
1228 		(EachEntryIteratee)func, passThru, maxCount);
1229 }
1230 
1231 
1232 void
1233 TruncateLeaf(BString* string)
1234 {
1235 	for (int32 index = string->Length(); index >= 0; index--) {
1236 		if ((*string)[index] == '/') {
1237 			string->Truncate(index + 1);
1238 			return;
1239 		}
1240 	}
1241 }
1242 
1243 
1244 int64
1245 StringToScalar(const char* text)
1246 {
1247 	char* end;
1248 	int64 val;
1249 
1250 	char* buffer = new char [strlen(text) + 1];
1251 	strcpy(buffer, text);
1252 
1253 	if (strstr(buffer, "k") || strstr(buffer, "K")) {
1254 		val = strtoll(buffer, &end, 10);
1255 		val *= kKBSize;
1256 	} else if (strstr(buffer, "mb") || strstr(buffer, "MB")) {
1257 		val = strtoll(buffer, &end, 10);
1258 		val *= kMBSize;
1259 	} else if (strstr(buffer, "gb") || strstr(buffer, "GB")) {
1260 		val = strtoll(buffer, &end, 10);
1261 		val *= kGBSize;
1262 	} else if (strstr(buffer, "byte") || strstr(buffer, "BYTE")) {
1263 		val = strtoll(buffer, &end, 10);
1264 		val *= kGBSize;
1265 	} else {
1266 		// no suffix, try plain byte conversion
1267 		val = strtoll(buffer, &end, 10);
1268 	}
1269 	delete[] buffer;
1270 
1271 	return val;
1272 }
1273 
1274 
1275 int32
1276 ListIconSize()
1277 {
1278 	static int32 sIconSize = std::max((int32)B_MINI_ICON,
1279 		(int32)ceilf(B_MINI_ICON * be_plain_font->Size() / 12));
1280 	return sIconSize;
1281 }
1282 
1283 
1284 static BRect
1285 LineBounds(BPoint where, float length, bool vertical)
1286 {
1287 	BRect rect;
1288 	rect.SetLeftTop(where);
1289 	rect.SetRightBottom(where + BPoint(2, 2));
1290 	if (vertical)
1291 		rect.bottom = rect.top + length;
1292 	else
1293 		rect.right = rect.left + length;
1294 
1295 	return rect;
1296 }
1297 
1298 
1299 SeparatorLine::SeparatorLine(BPoint where, float length, bool vertical,
1300 	const char* name)
1301 	:
1302 	BView(LineBounds(where, length, vertical), name,
1303 		B_FOLLOW_LEFT | B_FOLLOW_TOP, B_WILL_DRAW)
1304 {
1305 	SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
1306 	SetLowUIColor(B_PANEL_BACKGROUND_COLOR);
1307 }
1308 
1309 
1310 void
1311 SeparatorLine::Draw(BRect)
1312 {
1313 	BRect bounds(Bounds());
1314 	rgb_color hiliteColor = tint_color(ViewColor(), 1.5f);
1315 
1316 	bool vertical = (bounds.left > bounds.right - 3);
1317 	BeginLineArray(2);
1318 	if (vertical) {
1319 		AddLine(bounds.LeftTop(), bounds.LeftBottom(), hiliteColor);
1320 		AddLine(bounds.LeftTop() + BPoint(1, 0),
1321 			bounds.LeftBottom() + BPoint(1, 0), kWhite);
1322 	} else {
1323 		AddLine(bounds.LeftTop(), bounds.RightTop(), hiliteColor);
1324 		AddLine(bounds.LeftTop() + BPoint(0, 1),
1325 			bounds.RightTop() + BPoint(0, 1), kWhite);
1326 	}
1327 	EndLineArray();
1328 }
1329 
1330 
1331 void
1332 HexDump(const void* buf, int32 length)
1333 {
1334 	const int32 kBytesPerLine = 16;
1335 	int32 offset;
1336 	unsigned char* buffer = (unsigned char*)buf;
1337 
1338 	for (offset = 0; ; offset += kBytesPerLine, buffer += kBytesPerLine) {
1339 		int32 remain = length;
1340 		int32 index;
1341 
1342 		printf( "0x%06x: ", (int)offset);
1343 
1344 		for (index = 0; index < kBytesPerLine; index++) {
1345 			if (remain-- > 0)
1346 				printf("%02x%c", buffer[index], remain > 0 ? ',' : ' ');
1347 			else
1348 				printf("   ");
1349 		}
1350 
1351 		remain = length;
1352 		printf(" \'");
1353 		for (index = 0; index < kBytesPerLine; index++) {
1354 			if (remain-- > 0)
1355 				printf("%c", buffer[index] > ' ' ? buffer[index] : '.');
1356 			else
1357 				printf(" ");
1358 		}
1359 		printf("\'\n");
1360 
1361 		length -= kBytesPerLine;
1362 		if (length <= 0)
1363 			break;
1364 	}
1365 	fflush(stdout);
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(Model* model, BBitmap* icon, icon_size which)
1504 {
1505 	// bad input value
1506 	if (model == NULL || icon == NULL)
1507 		return B_BAD_VALUE;
1508 
1509 	// unitialized model
1510 	status_t result = model->InitCheck();
1511 	if (result != B_OK)
1512 		return result;
1513 
1514 	// unitialized icon
1515 	result = icon->InitCheck();
1516 	if (result != B_OK)
1517 		return result;
1518 
1519 	// node not open
1520 	BNode* node = model->Node();
1521 	if (node == NULL)
1522 		return B_BAD_VALUE;
1523 
1524 	// look for a thumbnail in an attribute
1525 	time_t modtime;
1526 	bigtime_t created;
1527 	if (node->GetModificationTime(&modtime) == B_OK
1528 		&& node->ReadAttr(kAttrThumbnailCreationTime, B_TIME_TYPE, 0,
1529 			&created, sizeof(bigtime_t)) == sizeof(bigtime_t)) {
1530 		if (created > (bigtime_t)modtime) {
1531 			// file has not changed, try to return an existing thumbnail
1532 			attr_info attrInfo;
1533 			if (node->GetAttrInfo(kAttrThumbnail, &attrInfo) == B_OK) {
1534 				uint8 webpData[attrInfo.size];
1535 				if (node->ReadAttr(kAttrThumbnail, attrInfo.type, 0,
1536 						webpData, attrInfo.size) == attrInfo.size) {
1537 					BMemoryIO stream((const void*)webpData, attrInfo.size);
1538 					BBitmap thumb(BTranslationUtils::GetBitmap(&stream));
1539 
1540 					// convert thumb to icon size
1541 					if (which == B_XXL_ICON) {
1542 						// import icon data from attribute without resizing
1543 						result = icon->ImportBits(&thumb);
1544 					} else {
1545 						// down-scale thumb to icon size
1546 						// TODO don't make a copy, allow icon to accept views
1547 						BBitmap tmp = BBitmap(icon->Bounds(),
1548 							icon->ColorSpace(), true);
1549 						BView view(tmp.Bounds(), "", B_FOLLOW_NONE,
1550 							B_WILL_DRAW);
1551 						tmp.AddChild(&view);
1552 						if (view.LockLooper()) {
1553 							// fill with transparent
1554 							view.SetLowColor(B_TRANSPARENT_COLOR);
1555 							view.FillRect(view.Bounds(), B_SOLID_LOW);
1556 							// draw bitmap
1557 							view.SetDrawingMode(B_OP_ALPHA);
1558 							view.SetBlendingMode(B_PIXEL_ALPHA,
1559 								B_ALPHA_COMPOSITE);
1560 							view.DrawBitmap(&thumb, thumb.Bounds(),
1561 								tmp.Bounds(), B_FILTER_BITMAP_BILINEAR);
1562 							view.Sync();
1563 							view.UnlockLooper();
1564 						}
1565 						tmp.RemoveChild(&view);
1566 
1567 						// copy tmp bitmap into icon
1568 						result = icon->ImportBits(&tmp);
1569 					}
1570 					// we found a thumbnail
1571 					if (result == B_OK)
1572 						return result;
1573 				}
1574 			}
1575 			// else we did not find a thumbnail
1576 		} else {
1577 			// file changed, remove all thumb attrs
1578 			char attrName[B_ATTR_NAME_LENGTH];
1579 			while (node->GetNextAttrName(attrName) == B_OK) {
1580 				if (BString(attrName).StartsWith(kAttrThumbnail))
1581 					node->RemoveAttr(attrName);
1582 			}
1583 		}
1584 	}
1585 
1586 	if (ShouldGenerateThumbnail(model->MimeType())) {
1587 		// try to fetch a new thumbnail icon
1588 		result = GetThumbnailIcon(model, icon, which);
1589 		if (result == B_OK) {
1590 			// icon ready
1591 			return B_OK;
1592 		} else if (result == B_BUSY) {
1593 			// working on icon, come back later
1594 			return B_BUSY;
1595 		}
1596 	}
1597 
1598 	// get icon from the node info
1599 	BNodeInfo nodeInfo(node);
1600 	return nodeInfo.GetIcon(icon, which);
1601 }
1602 
1603 
1604 //	#pragma mark - image thumbnails
1605 
1606 
1607 struct ThumbGenParams {
1608 	ThumbGenParams(Model* _model, BFile* _file, icon_size _which,
1609 		color_space _colorSpace, port_id _port);
1610 	virtual	~ThumbGenParams();
1611 
1612 	status_t InitCheck() { return fInitStatus; };
1613 
1614 	Model* model;
1615 	BFile* file;
1616 	icon_size which;
1617 	color_space colorSpace;
1618 	port_id port;
1619 
1620 private:
1621 	status_t fInitStatus;
1622 };
1623 
1624 
1625 ThumbGenParams::ThumbGenParams(Model* _model, BFile* _file, icon_size _which,
1626 	color_space _colorSpace, port_id _port)
1627 {
1628 	model = new(std::nothrow) Model(*_model);
1629 	file = new(std::nothrow) BFile(*_file);
1630 	which = _which;
1631 	colorSpace = _colorSpace;
1632 	port = _port;
1633 
1634 	fInitStatus = (model == NULL || file == NULL ? B_NO_MEMORY : B_OK);
1635 }
1636 
1637 
1638 ThumbGenParams::~ThumbGenParams()
1639 {
1640 	delete file;
1641 	delete model;
1642 }
1643 
1644 
1645 status_t get_thumbnail(void* castToParams);
1646 static const int32 kMsgIconData = 'ICON';
1647 
1648 
1649 BRect
1650 ThumbBounds(BBitmap* icon, float aspectRatio)
1651 {
1652 	BRect thumbBounds;
1653 
1654 	if (aspectRatio > 1) {
1655 		// wide
1656 		thumbBounds = BRect(0, 0, icon->Bounds().IntegerWidth() - 1,
1657 			floorf((icon->Bounds().IntegerHeight() - 1) / aspectRatio));
1658 		thumbBounds.OffsetBySelf(0, floorf((icon->Bounds().IntegerHeight()
1659 			- thumbBounds.IntegerHeight()) / 2.0f));
1660 	} else if (aspectRatio < 1) {
1661 		// tall
1662 		thumbBounds = BRect(0, 0, floorf((icon->Bounds().IntegerWidth() - 1)
1663 			* aspectRatio), icon->Bounds().IntegerHeight() - 1);
1664 		thumbBounds.OffsetBySelf(floorf((icon->Bounds().IntegerWidth()
1665 			- thumbBounds.IntegerWidth()) / 2.0f), 0);
1666 	} else {
1667 		// square
1668 		thumbBounds = icon->Bounds();
1669 	}
1670 
1671 	return thumbBounds;
1672 }
1673 
1674 
1675 status_t
1676 GetThumbnailIcon(Model* model, BBitmap* icon, icon_size which)
1677 {
1678 	status_t result = B_ERROR;
1679 
1680 	// create a name for the node icon generator thread (32 chars max)
1681 	icon_size w = (icon_size)B_XXL_ICON;
1682 	dev_t d = model->NodeRef()->device;
1683 	ino_t n = model->NodeRef()->node;
1684 	BString genThreadName = BString("_thumbgen_w")
1685 		<< w << "_d" << d << "_n" << n << "_";
1686 
1687 	bool volumeReadOnly = true;
1688 	BVolume volume(model->NodeRef()->device);
1689 	if (volume.InitCheck() == B_OK)
1690 		volumeReadOnly = volume.IsReadOnly() || !volume.KnowsAttr();
1691 
1692 	port_id port = B_NAME_NOT_FOUND;
1693 	if (volumeReadOnly) {
1694 		// look for a port with some icon data
1695 		port = find_port(genThreadName.String());
1696 			// give the port the same name as the generator thread
1697 		if (port != B_NAME_NOT_FOUND && port_count(port) > 0) {
1698 			// a generator thread has written some data to the port, fetch it
1699 			uint8 iconData[icon->BitsLength()];
1700 			int32 msgCode;
1701 			int32 bytesRead = read_port(port, &msgCode, iconData,
1702 				icon->BitsLength());
1703 			if (bytesRead == icon->BitsLength() && msgCode == kMsgIconData
1704 				&& iconData != NULL) {
1705 				// fill icon data into the passed in icon
1706 				result = icon->ImportBits(iconData, icon->BitsLength(),
1707 					icon->BytesPerRow(), 0, icon->ColorSpace());
1708 			}
1709 
1710 			if (result == B_OK) {
1711 				// make a new port next time
1712 				delete_port(port);
1713 				port = B_NAME_NOT_FOUND;
1714 			}
1715 		}
1716 	}
1717 
1718 	// we found an icon from a generator thread
1719 	if (result == B_OK)
1720 		return B_OK;
1721 
1722 	// look for an existing generator thread before spawning a new one
1723 	if (find_thread(genThreadName.String()) == B_NAME_NOT_FOUND) {
1724 		// no generater thread found, spawn one
1725 		BFile* file = dynamic_cast<BFile*>(model->Node());
1726 		if (file == NULL)
1727 			result = B_NOT_SUPPORTED; // node must be a file
1728 		else {
1729 			// create a new port if one doesn't already exist
1730 			if (volumeReadOnly && port == B_NAME_NOT_FOUND)
1731 				port = create_port(1, genThreadName.String());
1732 
1733 			ThumbGenParams* params = new ThumbGenParams(model, file, which,
1734 				icon->ColorSpace(), port);
1735 			if (params->InitCheck() == B_OK) {
1736 				// generator thread will delete params, it makes copies
1737 				resume_thread(spawn_thread(get_thumbnail,
1738 					genThreadName.String(), B_LOW_PRIORITY, params));
1739 				result = B_BUSY; // try again later
1740 			} else
1741 				delete params;
1742 		}
1743 	}
1744 
1745 	return result;
1746 }
1747 
1748 
1749 bool
1750 ShouldGenerateThumbnail(const char* type)
1751 {
1752 	// check generate thumbnail setting,
1753 	// mime type must be an image (for now)
1754 	return TrackerSettings().GenerateImageThumbnails()
1755 		&& type != NULL && BString(type).IStartsWith("image");
1756 }
1757 
1758 
1759 //	#pragma mark - thumbnail generator thread
1760 
1761 
1762 status_t
1763 get_thumbnail(void* castToParams)
1764 {
1765 	ThumbGenParams* params = (ThumbGenParams*)castToParams;
1766 	Model* model = params->model;
1767 	BFile* file = params->file;
1768 	icon_size which = params->which;
1769 	color_space colorSpace = params->colorSpace;
1770 	port_id port = params->port;
1771 
1772 	// get the mime type from the model
1773 	const char* type = model->MimeType();
1774 
1775 	// check if attributes can be written to
1776 	bool volumeReadOnly = true;
1777 	BVolume volume(model->NodeRef()->device);
1778 	if (volume.InitCheck() == B_OK)
1779 		volumeReadOnly = volume.IsReadOnly() || !volume.KnowsAttr();
1780 
1781 	// see if we have a thumbnail attribute
1782 	attr_info attrInfo;
1783 	status_t result = file->GetAttrInfo(kAttrThumbnail, &attrInfo);
1784 	if (result != B_OK) {
1785 		// create a new thumbnail
1786 
1787 		// check to see if we have a translator that works
1788 		BBitmapStream imageStream;
1789 		BBitmap* image;
1790 		if (BTranslatorRoster::Default()->Translate(file, NULL, NULL,
1791 				&imageStream, B_TRANSLATOR_BITMAP, 0, type) == B_OK
1792 			&& imageStream.DetachBitmap(&image) == B_OK) {
1793 			// we have translated the image file into a BBitmap
1794 
1795 			// check if we can write attrs
1796 			if (!volumeReadOnly) {
1797 				// write image width to an attribute
1798 				int32 width = image->Bounds().IntegerWidth();
1799 				file->WriteAttr("Media:Width", B_INT32_TYPE, 0, &width,
1800 					sizeof(int32));
1801 
1802 				// write image height to an attribute
1803 				int32 height = image->Bounds().IntegerHeight();
1804 				file->WriteAttr("Media:Height", B_INT32_TYPE, 0, &height,
1805 					sizeof(int32));
1806 
1807 				// convert image into a 128x128 WebP image and stash it
1808 				BBitmap thumb = BBitmap(BRect(0, 0, B_XXL_ICON - 1,
1809 					B_XXL_ICON - 1), colorSpace, true);
1810 				BView view(thumb.Bounds(), "", B_FOLLOW_NONE,
1811 					B_WILL_DRAW);
1812 				thumb.AddChild(&view);
1813 				if (view.LockLooper()) {
1814 					// fill with transparent
1815 					view.SetLowColor(B_TRANSPARENT_COLOR);
1816 					view.FillRect(view.Bounds(), B_SOLID_LOW);
1817 					// draw bitmap
1818 					view.SetDrawingMode(B_OP_ALPHA);
1819 					view.SetBlendingMode(B_PIXEL_ALPHA,
1820 						B_ALPHA_COMPOSITE);
1821 					view.DrawBitmap(image, image->Bounds(),
1822 						ThumbBounds(&thumb, image->Bounds().Width()
1823 							/ image->Bounds().Height()),
1824 						B_FILTER_BITMAP_BILINEAR);
1825 					view.Sync();
1826 					view.UnlockLooper();
1827 				}
1828 				thumb.RemoveChild(&view);
1829 
1830 				BBitmap* thumbPointer = &thumb;
1831 				BBitmapStream thumbStream(thumbPointer);
1832 				BMallocIO stream;
1833 				if (BTranslatorRoster::Default()->Translate(&thumbStream,
1834 						NULL, NULL, &stream, B_WEBP_FORMAT) == B_OK
1835 					&& thumbStream.DetachBitmap(&thumbPointer) == B_OK) {
1836 					// write WebP image data into an attribute
1837 					file->WriteAttr(kAttrThumbnail, B_RAW_TYPE, 0,
1838 						stream.Buffer(), stream.BufferLength());
1839 
1840 					// write thumbnail creation time into an attribute
1841 					bigtime_t created = system_time();
1842 					file->WriteAttr(kAttrThumbnailCreationTime, B_TIME_TYPE,
1843 						0, &created, sizeof(bigtime_t));
1844 
1845 					// we wrote thumbnail to an attribute
1846 					result = B_OK;
1847 				}
1848 			} else if (port != B_NAME_NOT_FOUND) {
1849 				// create a thumb at the requested icon size
1850 				BBitmap thumb = BBitmap(BRect(0, 0, which - 1, which - 1),
1851 					colorSpace, true);
1852 				// copy image into a view bitmap, scaled and centered
1853 				BView view(thumb.Bounds(), "", B_FOLLOW_NONE, B_WILL_DRAW);
1854 				thumb.AddChild(&view);
1855 				if (view.LockLooper()) {
1856 					// fill with transparent
1857 					view.SetLowColor(B_TRANSPARENT_COLOR);
1858 					view.FillRect(view.Bounds(), B_SOLID_LOW);
1859 					// draw bitmap
1860 					view.SetDrawingMode(B_OP_ALPHA);
1861 					view.SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_COMPOSITE);
1862 					view.DrawBitmap(image, image->Bounds(),
1863 						ThumbBounds(&thumb, image->Bounds().Width()
1864 							/ image->Bounds().Height()),
1865 						B_FILTER_BITMAP_BILINEAR);
1866 					view.Sync();
1867 					view.UnlockLooper();
1868 				}
1869 				thumb.RemoveChild(&view);
1870 
1871 				// send icon back to the calling thread through the port
1872 				result = write_port(port, kMsgIconData, (void*)thumb.Bits(),
1873 					thumb.BitsLength());
1874 			}
1875 		}
1876 
1877 		delete image;
1878 	}
1879 
1880 	if (result == B_OK) {
1881 		// trigger an icon refresh
1882 		if (!volumeReadOnly)
1883 			model->Mimeset(true); // only works on read-write volumes
1884 		else {
1885 			// send Tracker a message to tell it to update the thumbnail
1886 			BMessage message(kUpdateThumbnail);
1887 			if (message.AddInt32("device", model->NodeRef()->device) == B_OK
1888 				&& message.AddUInt64("node", model->NodeRef()->node) == B_OK) {
1889 				be_app->PostMessage(&message);
1890 			}
1891 		}
1892 	}
1893 
1894 	delete params;
1895 
1896 	return result;
1897 }
1898 
1899 
1900 //	#pragma mark - PrintToStream
1901 
1902 
1903 void
1904 PrintToStream(rgb_color color)
1905 {
1906 	printf("r:%x, g:%x, b:%x, a:%x\n",
1907 		color.red, color.green, color.blue, color.alpha);
1908 }
1909 
1910 
1911 //	#pragma mark - EachMenuItem
1912 
1913 
1914 extern BMenuItem*
1915 EachMenuItem(BMenu* menu, bool recursive, BMenuItem* (*func)(BMenuItem *))
1916 {
1917 	int32 count = menu->CountItems();
1918 	for (int32 index = 0; index < count; index++) {
1919 		BMenuItem* item = menu->ItemAt(index);
1920 		BMenuItem* newItem = (func)(item);
1921 		if (newItem != NULL)
1922 			return newItem;
1923 
1924 		if (recursive) {
1925 			BMenu* submenu = menu->SubmenuAt(index);
1926 			if (submenu != NULL)
1927 				return EachMenuItem(submenu, true, func);
1928 		}
1929 	}
1930 
1931 	return NULL;
1932 }
1933 
1934 
1935 extern const BMenuItem*
1936 EachMenuItem(const BMenu* menu, bool recursive,
1937 	BMenuItem* (*func)(const BMenuItem *))
1938 {
1939 	int32 count = menu->CountItems();
1940 	for (int32 index = 0; index < count; index++) {
1941 		BMenuItem* item = menu->ItemAt(index);
1942 		BMenuItem* newItem = (func)(item);
1943 		if (newItem != NULL)
1944 			return newItem;
1945 
1946 		if (recursive) {
1947 			BMenu* submenu = menu->SubmenuAt(index);
1948 			if (submenu != NULL)
1949 				return EachMenuItem(submenu, true, func);
1950 		}
1951 	}
1952 
1953 	return NULL;
1954 }
1955 
1956 
1957 //	#pragma mark - PositionPassingMenuItem
1958 
1959 
1960 PositionPassingMenuItem::PositionPassingMenuItem(const char* title,
1961 	BMessage* message, char shortcut, uint32 modifiers)
1962 	:
1963 	BMenuItem(title, message, shortcut, modifiers)
1964 {
1965 }
1966 
1967 
1968 PositionPassingMenuItem::PositionPassingMenuItem(BMenu* menu, BMessage* message)
1969 	:
1970 	BMenuItem(menu, message)
1971 {
1972 }
1973 
1974 
1975 PositionPassingMenuItem::PositionPassingMenuItem(BMessage* data)
1976 	:
1977 	BMenuItem(data)
1978 {
1979 }
1980 
1981 
1982 BArchivable*
1983 PositionPassingMenuItem::Instantiate(BMessage* data)
1984 {
1985 	if (validate_instantiation(data, "PositionPassingMenuItem"))
1986 		return new PositionPassingMenuItem(data);
1987 
1988 	return NULL;
1989 }
1990 
1991 
1992 status_t
1993 PositionPassingMenuItem::Invoke(BMessage* message)
1994 {
1995 	if (Menu() == NULL)
1996 		return B_ERROR;
1997 
1998 	if (!IsEnabled())
1999 		return B_ERROR;
2000 
2001 	if (message == NULL)
2002 		message = Message();
2003 
2004 	if (message == NULL)
2005 		return B_BAD_VALUE;
2006 
2007 	BMessage clone(*message);
2008 	clone.AddInt32("index", Menu()->IndexOf(this));
2009 	clone.AddInt64("when", system_time());
2010 	clone.AddPointer("source", this);
2011 
2012 	// embed the invoke location of the menu so that we can create
2013 	// a new folder, etc. on the spot
2014 	BMenu* menu = Menu();
2015 
2016 	for (;;) {
2017 		if (!menu->Supermenu())
2018 			break;
2019 
2020 		menu = menu->Supermenu();
2021 	}
2022 
2023 	// use the window position only, if the item was invoked from the menu
2024 	// menu->Window() points to the window the item was invoked from
2025 	if (dynamic_cast<BContainerWindow*>(menu->Window()) == NULL) {
2026 		LooperAutoLocker lock(menu);
2027 		if (lock.IsLocked()) {
2028 			BPoint invokeOrigin(menu->Window()->Frame().LeftTop());
2029 			clone.AddPoint("be:invoke_origin", invokeOrigin);
2030 		}
2031 	}
2032 
2033 	return BInvoker::Invoke(&clone);
2034 }
2035 
2036 
2037 //	#pragma mark - BPrivate functions
2038 
2039 
2040 bool
2041 BootedInSafeMode()
2042 {
2043 	const char* safeMode = getenv("SAFEMODE");
2044 	return (safeMode && strcmp(safeMode, "yes") == 0);
2045 }
2046 
2047 
2048 float
2049 ComputeTypeAheadScore(const char* text, const char* match, bool wordMode)
2050 {
2051 	// highest score: exact match
2052 	const char* found = strcasestr(text, match);
2053 	if (found != NULL) {
2054 		if (found == text)
2055 			return kExactMatchScore;
2056 
2057 		return 1.f / (found - text);
2058 	}
2059 
2060 	// there was no exact match
2061 
2062 	// second best: all characters at word beginnings
2063 	if (wordMode) {
2064 		float score = 0;
2065 		for (int32 j = 0, k = 0; match[j]; j++) {
2066 			while (text[k]
2067 				&& tolower(text[k]) != tolower(match[j])) {
2068 				k++;
2069 			}
2070 			if (text[k] == '\0') {
2071 				score = 0;
2072 				break;
2073 			}
2074 
2075 			bool wordStart = k == 0 || isspace(text[k - 1]);
2076 			if (wordStart)
2077 				score++;
2078 			if (j > 0) {
2079 				bool wordEnd = !text[k + 1] || isspace(text[k + 1]);
2080 				if (wordEnd)
2081 					score += 0.3;
2082 				if (match[j - 1] == text[k - 1])
2083 					score += 0.7;
2084 			}
2085 
2086 			score += 1.f / (k + 1);
2087 			k++;
2088 		}
2089 
2090 		return score;
2091 	}
2092 
2093 	return -1;
2094 }
2095 
2096 
2097 //	#pragma mark - throw on error functions.
2098 
2099 
2100 void
2101 _ThrowOnError(status_t result, const char* DEBUG_ONLY(file),
2102 	int32 DEBUG_ONLY(line))
2103 {
2104 	if (result != B_OK) {
2105 		PRINT(("%s at %s:%d\n", strerror(result), file, (int)line));
2106 		throw result;
2107 	}
2108 }
2109 
2110 
2111 void
2112 _ThrowIfNotSize(ssize_t size, const char* DEBUG_ONLY(file),
2113 	int32 DEBUG_ONLY(line))
2114 {
2115 	if (size < B_OK) {
2116 		PRINT(("%s at %s:%d\n", strerror((status_t)size), file, (int)line));
2117 		throw (status_t)size;
2118 	}
2119 }
2120 
2121 
2122 void
2123 _ThrowOnAssert(bool success, const char* DEBUG_ONLY(file),
2124 	int32 DEBUG_ONLY(line))
2125 {
2126 	if (!success) {
2127 		PRINT(("Assert failed at %s:%d\n", file, (int)line));
2128 		throw B_ERROR;
2129 	}
2130 }
2131 
2132 } // namespace BPrivate
2133