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