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