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