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