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