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