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