btrfs: Attempt to fix GCC2 build.
[haiku.git] / src / kits / tracker / Utilities.cpp
blobfaa5299fbb5bd0d75987bcfdcffdd2ec3bb468db
1 /*
2 Open Tracker License
4 Terms and Conditions
6 Copyright (c) 1991-2000, Be Incorporated. All rights reserved.
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:
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.
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.
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.
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.
36 #include "Utilities.h"
38 #include <ctype.h>
39 #include <fs_attr.h>
40 #include <fs_info.h>
41 #include <stdarg.h>
42 #include <string.h>
43 #include <stdlib.h>
44 #include <time.h>
46 #include <BitmapStream.h>
47 #include <Catalog.h>
48 #include <Debug.h>
49 #include <Font.h>
50 #include <IconUtils.h>
51 #include <MenuItem.h>
52 #include <OS.h>
53 #include <PopUpMenu.h>
54 #include <Region.h>
55 #include <StorageDefs.h>
56 #include <TextView.h>
57 #include <Volume.h>
58 #include <VolumeRoster.h>
59 #include <Window.h>
61 #include "Attributes.h"
62 #include "ContainerWindow.h"
63 #include "MimeTypes.h"
64 #include "Model.h"
65 #include "PoseView.h"
68 #ifndef _IMPEXP_BE
69 # define _IMPEXP_BE
70 #endif
71 extern _IMPEXP_BE const uint32 LARGE_ICON_TYPE;
72 extern _IMPEXP_BE const uint32 MINI_ICON_TYPE;
75 FILE* logFile = NULL;
77 static const float kMinSeparatorStubX = 10;
78 static const float kStubToStringSlotX = 5;
81 namespace BPrivate {
83 const float kExactMatchScore = INFINITY;
86 bool gLocalizedNamePreferred;
89 bool
90 SecondaryMouseButtonDown(int32 modifiers, int32 buttons)
92 return (buttons & B_SECONDARY_MOUSE_BUTTON) != 0
93 || ((buttons & B_PRIMARY_MOUSE_BUTTON) != 0
94 && (modifiers & B_CONTROL_KEY) != 0);
98 uint32
99 HashString(const char* string, uint32 seed)
101 char ch;
102 uint32 hash = seed;
104 while((ch = *string++) != 0) {
105 hash = (hash << 7) ^ (hash >> 24);
106 hash ^= ch;
108 hash ^= hash << 12;
110 return hash;
114 uint32
115 AttrHashString(const char* string, uint32 type)
117 char c;
118 uint32 hash = 0;
120 while((c = *string++) != 0) {
121 hash = (hash << 7) ^ (hash >> 24);
122 hash ^= c;
124 hash ^= hash << 12;
126 hash &= ~0xff;
127 hash |= type;
129 return hash;
133 bool
134 ValidateStream(BMallocIO* stream, uint32 key, int32 version)
136 uint32 testKey;
137 int32 testVersion;
139 if (stream->Read(&testKey, sizeof(uint32)) <= 0
140 || stream->Read(&testVersion, sizeof(int32)) <= 0) {
141 return false;
144 return testKey == key && testVersion == version;
148 void
149 DisallowFilenameKeys(BTextView* textView)
151 textView->DisallowChar('/');
155 void
156 DisallowMetaKeys(BTextView* textView)
158 textView->DisallowChar(B_TAB);
159 textView->DisallowChar(B_ESCAPE);
160 textView->DisallowChar(B_INSERT);
161 textView->DisallowChar(B_DELETE);
162 textView->DisallowChar(B_HOME);
163 textView->DisallowChar(B_END);
164 textView->DisallowChar(B_PAGE_UP);
165 textView->DisallowChar(B_PAGE_DOWN);
166 textView->DisallowChar(B_FUNCTION_KEY);
170 PeriodicUpdatePoses::PeriodicUpdatePoses()
172 fPoseList(20, true)
174 fLock = new Benaphore("PeriodicUpdatePoses");
178 PeriodicUpdatePoses::~PeriodicUpdatePoses()
180 fLock->Lock();
181 fPoseList.MakeEmpty();
182 delete fLock;
186 void
187 PeriodicUpdatePoses::AddPose(BPose* pose, BPoseView* poseView,
188 PeriodicUpdateCallback callback, void* cookie)
190 periodic_pose* periodic = new periodic_pose;
191 periodic->pose = pose;
192 periodic->pose_view = poseView;
193 periodic->callback = callback;
194 periodic->cookie = cookie;
195 fPoseList.AddItem(periodic);
199 bool
200 PeriodicUpdatePoses::RemovePose(BPose* pose, void** cookie)
202 int32 count = fPoseList.CountItems();
203 for (int32 index = 0; index < count; index++) {
204 if (fPoseList.ItemAt(index)->pose == pose) {
205 if (!fLock->Lock())
206 return false;
208 periodic_pose* periodic = fPoseList.RemoveItemAt(index);
209 if (cookie)
210 *cookie = periodic->cookie;
211 delete periodic;
212 fLock->Unlock();
213 return true;
217 return false;
221 void
222 PeriodicUpdatePoses::DoPeriodicUpdate(bool forceRedraw)
224 if (!fLock->Lock())
225 return;
227 int32 count = fPoseList.CountItems();
228 for (int32 index = 0; index < count; index++) {
229 periodic_pose* periodic = fPoseList.ItemAt(index);
230 if ((periodic->callback(periodic->pose, periodic->cookie)
231 || forceRedraw) && periodic->pose_view->LockLooper()) {
232 periodic->pose_view->UpdateIcon(periodic->pose);
233 periodic->pose_view->UnlockLooper();
237 fLock->Unlock();
241 PeriodicUpdatePoses gPeriodicUpdatePoses;
243 } // namespace BPrivate
246 void
247 PoseInfo::EndianSwap(void* castToThis)
249 PoseInfo* self = (PoseInfo*)castToThis;
251 PRINT(("swapping PoseInfo\n"));
253 STATIC_ASSERT(sizeof(ino_t) == sizeof(int64));
254 self->fInitedDirectory = SwapInt64(self->fInitedDirectory);
255 swap_data(B_POINT_TYPE, &self->fLocation, sizeof(BPoint), B_SWAP_ALWAYS);
257 // do a sanity check on the icon position
258 if (self->fLocation.x < -20000 || self->fLocation.x > 20000
259 || self->fLocation.y < -20000 || self->fLocation.y > 20000) {
260 // position out of range, force autoplcemement
261 PRINT((" rejecting icon position out of range\n"));
262 self->fInitedDirectory = -1LL;
263 self->fLocation = BPoint(0, 0);
268 void
269 PoseInfo::PrintToStream()
271 PRINT(("%s, inode:%" B_PRIx64 ", location %f %f\n",
272 fInvisible ? "hidden" : "visible",
273 fInitedDirectory, fLocation.x, fLocation.y));
277 // #pragma mark - ExtendedPoseInfo
280 size_t
281 ExtendedPoseInfo::Size() const
283 return sizeof(ExtendedPoseInfo) + fNumFrames * sizeof(FrameLocation);
287 size_t
288 ExtendedPoseInfo::Size(int32 count)
290 return sizeof(ExtendedPoseInfo) + count * sizeof(FrameLocation);
294 size_t
295 ExtendedPoseInfo::SizeWithHeadroom() const
297 return sizeof(ExtendedPoseInfo) + (fNumFrames + 1) * sizeof(FrameLocation);
301 size_t
302 ExtendedPoseInfo::SizeWithHeadroom(size_t oldSize)
304 int32 count = (ssize_t)oldSize - (ssize_t)sizeof(ExtendedPoseInfo);
305 if (count > 0)
306 count /= sizeof(FrameLocation);
307 else
308 count = 0;
310 return Size(count + 1);
314 bool
315 ExtendedPoseInfo::HasLocationForFrame(BRect frame) const
317 for (int32 index = 0; index < fNumFrames; index++) {
318 if (fLocations[index].fFrame == frame)
319 return true;
322 return false;
326 BPoint
327 ExtendedPoseInfo::LocationForFrame(BRect frame) const
329 for (int32 index = 0; index < fNumFrames; index++) {
330 if (fLocations[index].fFrame == frame)
331 return fLocations[index].fLocation;
334 TRESPASS();
335 return BPoint(0, 0);
339 bool
340 ExtendedPoseInfo::SetLocationForFrame(BPoint newLocation, BRect frame)
342 for (int32 index = 0; index < fNumFrames; index++) {
343 if (fLocations[index].fFrame == frame) {
344 if (fLocations[index].fLocation == newLocation)
345 return false;
347 fLocations[index].fLocation = newLocation;
348 return true;
352 fLocations[fNumFrames].fFrame = frame;
353 fLocations[fNumFrames].fLocation = newLocation;
354 fLocations[fNumFrames].fWorkspaces = 0xffffffff;
355 fNumFrames++;
357 return true;
361 void
362 ExtendedPoseInfo::EndianSwap(void* castToThis)
364 ExtendedPoseInfo* self = (ExtendedPoseInfo *)castToThis;
366 PRINT(("swapping ExtendedPoseInfo\n"));
368 self->fWorkspaces = SwapUInt32(self->fWorkspaces);
369 self->fNumFrames = SwapInt32(self->fNumFrames);
371 for (int32 index = 0; index < self->fNumFrames; index++) {
372 swap_data(B_POINT_TYPE, &self->fLocations[index].fLocation,
373 sizeof(BPoint), B_SWAP_ALWAYS);
375 if (self->fLocations[index].fLocation.x < -20000
376 || self->fLocations[index].fLocation.x > 20000
377 || self->fLocations[index].fLocation.y < -20000
378 || self->fLocations[index].fLocation.y > 20000) {
379 // position out of range, force autoplcemement
380 PRINT((" rejecting icon position out of range\n"));
381 self->fLocations[index].fLocation = BPoint(0, 0);
383 swap_data(B_RECT_TYPE, &self->fLocations[index].fFrame,
384 sizeof(BRect), B_SWAP_ALWAYS);
389 void
390 ExtendedPoseInfo::PrintToStream()
395 // #pragma mark - OffscreenBitmap
398 OffscreenBitmap::OffscreenBitmap(BRect frame)
400 fBitmap(NULL)
402 NewBitmap(frame);
406 OffscreenBitmap::OffscreenBitmap()
408 fBitmap(NULL)
413 OffscreenBitmap::~OffscreenBitmap()
415 delete fBitmap;
419 void
420 OffscreenBitmap::NewBitmap(BRect bounds)
422 delete fBitmap;
423 fBitmap = new(std::nothrow) BBitmap(bounds, B_RGB32, true);
424 if (fBitmap && fBitmap->Lock()) {
425 BView* view = new BView(fBitmap->Bounds(), "", B_FOLLOW_NONE, 0);
426 fBitmap->AddChild(view);
428 BRect clipRect = view->Bounds();
429 BRegion newClip;
430 newClip.Set(clipRect);
431 view->ConstrainClippingRegion(&newClip);
433 fBitmap->Unlock();
434 } else {
435 delete fBitmap;
436 fBitmap = NULL;
441 BView*
442 OffscreenBitmap::BeginUsing(BRect frame)
444 if (!fBitmap || fBitmap->Bounds() != frame)
445 NewBitmap(frame);
447 fBitmap->Lock();
448 return View();
452 void
453 OffscreenBitmap::DoneUsing()
455 fBitmap->Unlock();
459 BBitmap*
460 OffscreenBitmap::Bitmap() const
462 ASSERT(fBitmap);
463 ASSERT(fBitmap->IsLocked());
464 return fBitmap;
468 BView*
469 OffscreenBitmap::View() const
471 ASSERT(fBitmap);
472 return fBitmap->ChildAt(0);
476 // #pragma mark - BPrivate functions
479 namespace BPrivate {
481 // Changes the alpha value of the given bitmap to create a nice
482 // horizontal fade out in the specified region.
483 // "from" is always transparent, "to" opaque.
484 void
485 FadeRGBA32Horizontal(uint32* bits, int32 width, int32 height, int32 from,
486 int32 to)
488 // check parameters
489 if (width < 0 || height < 0 || from < 0 || to < 0)
490 return;
492 float change = 1.f / (to - from);
493 if (from > to) {
494 int32 temp = from;
495 from = to;
496 to = temp;
499 for (int32 y = 0; y < height; y++) {
500 float alpha = change > 0 ? 0.0f : 1.0f;
502 for (int32 x = from; x <= to; x++) {
503 if (bits[x] & 0xff000000) {
504 uint32 a = uint32((bits[x] >> 24) * alpha);
505 bits[x] = (bits[x] & 0x00ffffff) | (a << 24);
507 alpha += change;
509 bits += width;
514 /*! Changes the alpha value of the given bitmap to create a nice
515 vertical fade out in the specified region.
516 "from" is always transparent, "to" opaque.
518 void
519 FadeRGBA32Vertical(uint32* bits, int32 width, int32 height, int32 from,
520 int32 to)
522 // check parameters
523 if (width < 0 || height < 0 || from < 0 || to < 0)
524 return;
526 if (from > to)
527 bits += width * (height - (from - to));
529 float change = 1.f / (to - from);
530 if (from > to) {
531 int32 temp = from;
532 from = to;
533 to = temp;
536 float alpha = change > 0 ? 0.0f : 1.0f;
538 for (int32 y = from; y <= to; y++) {
539 for (int32 x = 0; x < width; x++) {
540 if (bits[x] & 0xff000000) {
541 uint32 a = uint32((bits[x] >> 24) * alpha);
542 bits[x] = (bits[x] & 0x00ffffff) | (a << 24);
545 alpha += change;
546 bits += width;
551 } // namespace BPrivate
554 // #pragma mark - DraggableIcon
557 DraggableIcon::DraggableIcon(BRect rect, const char* name,
558 const char* mimeType, icon_size which, const BMessage* message,
559 BMessenger target, uint32 resizingMode, uint32 flags)
561 BView(rect, name, resizingMode, flags),
562 fMessage(*message),
563 fTarget(target)
565 fBitmap = new BBitmap(Bounds(), kDefaultIconDepth);
566 BMimeType mime(mimeType);
567 status_t result = mime.GetIcon(fBitmap, which);
568 ASSERT(mime.IsValid());
569 if (result != B_OK) {
570 PRINT(("failed to get icon for %s, %s\n", mimeType, strerror(result)));
571 BMimeType mime(B_FILE_MIMETYPE);
572 ASSERT(mime.IsInstalled());
573 mime.GetIcon(fBitmap, which);
578 DraggableIcon::~DraggableIcon()
580 delete fBitmap;
584 void
585 DraggableIcon::SetTarget(BMessenger target)
587 fTarget = target;
591 BRect
592 DraggableIcon::PreferredRect(BPoint offset, icon_size which)
594 BRect rect(0, 0, which - 1, which - 1);
595 rect.OffsetTo(offset);
596 return rect;
600 void
601 DraggableIcon::AttachedToWindow()
603 AdoptParentColors();
607 void
608 DraggableIcon::MouseDown(BPoint point)
610 if (!DragStarted(&fMessage))
611 return;
613 BRect rect(Bounds());
614 BBitmap* dragBitmap = new BBitmap(rect, B_RGBA32, true);
615 dragBitmap->Lock();
616 BView* view = new BView(dragBitmap->Bounds(), "", B_FOLLOW_NONE, 0);
617 dragBitmap->AddChild(view);
618 view->SetOrigin(0, 0);
619 BRect clipRect(view->Bounds());
620 BRegion newClip;
621 newClip.Set(clipRect);
622 view->ConstrainClippingRegion(&newClip);
624 // Transparent draw magic
625 view->SetHighColor(0, 0, 0, 0);
626 view->FillRect(view->Bounds());
627 view->SetDrawingMode(B_OP_ALPHA);
628 view->SetHighColor(0, 0, 0, 128);
629 // set the level of transparency by value
630 view->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_COMPOSITE);
631 view->DrawBitmap(fBitmap);
632 view->Sync();
633 dragBitmap->Unlock();
634 DragMessage(&fMessage, dragBitmap, B_OP_ALPHA, point, fTarget.Target(0));
638 bool
639 DraggableIcon::DragStarted(BMessage*)
641 return true;
645 void
646 DraggableIcon::Draw(BRect)
648 SetDrawingMode(B_OP_ALPHA);
649 SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY);
650 DrawBitmap(fBitmap);
654 // #pragma mark - FlickerFreeStringView
657 FlickerFreeStringView::FlickerFreeStringView(BRect bounds, const char* name,
658 const char* text, uint32 resizingMode, uint32 flags)
660 BStringView(bounds, name, text, resizingMode, flags),
661 fBitmap(NULL),
662 fViewColor(ViewColor()),
663 fLowColor(LowColor()),
664 fOriginalBitmap(NULL)
669 FlickerFreeStringView::FlickerFreeStringView(BRect bounds, const char* name,
670 const char* text, BBitmap* inBitmap, uint32 resizingMode, uint32 flags)
672 BStringView(bounds, name, text, resizingMode, flags),
673 fBitmap(NULL),
674 fViewColor(ViewColor()),
675 fLowColor(LowColor()),
676 fOriginalBitmap(inBitmap)
681 FlickerFreeStringView::~FlickerFreeStringView()
683 delete fBitmap;
687 void
688 FlickerFreeStringView::Draw(BRect)
690 BRect bounds(Bounds());
691 if (fBitmap == NULL)
692 fBitmap = new OffscreenBitmap(Bounds());
694 BView* offscreen = fBitmap->BeginUsing(bounds);
696 if (Parent() != NULL) {
697 fViewColor = Parent()->ViewColor();
698 fLowColor = Parent()->ViewColor();
701 offscreen->SetViewColor(fViewColor);
702 offscreen->SetHighColor(HighColor());
703 offscreen->SetLowColor(fLowColor);
705 BFont font;
706 GetFont(&font);
707 offscreen->SetFont(&font);
709 offscreen->Sync();
710 if (fOriginalBitmap != NULL)
711 offscreen->DrawBitmap(fOriginalBitmap, Frame(), bounds);
712 else
713 offscreen->FillRect(bounds, B_SOLID_LOW);
715 if (Text() != NULL) {
716 BPoint loc;
718 font_height height;
719 GetFontHeight(&height);
721 edge_info eInfo;
722 switch (Alignment()) {
723 case B_ALIGN_LEFT:
724 case B_ALIGN_HORIZONTAL_UNSET:
725 case B_ALIGN_USE_FULL_WIDTH:
727 // If the first char has a negative left edge give it
728 // some more room by shifting that much more to the right.
729 font.GetEdges(Text(), 1, &eInfo);
730 loc.x = bounds.left + (2 - eInfo.left);
731 break;
734 case B_ALIGN_CENTER:
736 float width = StringWidth(Text());
737 float center = (bounds.right - bounds.left) / 2;
738 loc.x = center - (width/2);
739 break;
742 case B_ALIGN_RIGHT:
744 float width = StringWidth(Text());
745 loc.x = bounds.right - width - 2;
746 break;
749 loc.y = bounds.bottom - (1 + height.descent);
750 offscreen->DrawString(Text(), loc);
752 offscreen->Sync();
753 SetDrawingMode(B_OP_COPY);
754 DrawBitmap(fBitmap->Bitmap());
755 fBitmap->DoneUsing();
759 void
760 FlickerFreeStringView::AttachedToWindow()
762 _inherited::AttachedToWindow();
763 if (Parent() != NULL) {
764 fViewColor = Parent()->ViewColor();
765 fLowColor = Parent()->ViewColor();
767 SetViewColor(B_TRANSPARENT_32_BIT);
768 SetLowColor(B_TRANSPARENT_32_BIT);
772 void
773 FlickerFreeStringView::SetViewColor(rgb_color color)
775 if (fViewColor != color) {
776 fViewColor = color;
777 Invalidate();
779 _inherited::SetViewColor(B_TRANSPARENT_32_BIT);
783 void
784 FlickerFreeStringView::SetLowColor(rgb_color color)
786 if (fLowColor != color) {
787 fLowColor = color;
788 Invalidate();
790 _inherited::SetLowColor(B_TRANSPARENT_32_BIT);
794 // #pragma mark - TitledSeparatorItem
797 TitledSeparatorItem::TitledSeparatorItem(const char* label)
799 BMenuItem(label, 0)
801 _inherited::SetEnabled(false);
805 TitledSeparatorItem::~TitledSeparatorItem()
810 void
811 TitledSeparatorItem::SetEnabled(bool)
813 // leave disabled
817 void
818 TitledSeparatorItem::GetContentSize(float* width, float* height)
820 _inherited::GetContentSize(width, height);
824 inline rgb_color
825 ShiftMenuBackgroundColor(float by)
827 return tint_color(ui_color(B_MENU_BACKGROUND_COLOR), by);
831 void
832 TitledSeparatorItem::Draw()
834 BRect frame(Frame());
836 BMenu* parent = Menu();
837 ASSERT(parent != NULL);
839 menu_info minfo;
840 get_menu_info(&minfo);
842 if (minfo.separator > 0) {
843 frame.left += 10;
844 frame.right -= 10;
845 } else {
846 frame.left += 1;
847 frame.right -= 1;
850 float startX = frame.left;
851 float endX = frame.right;
853 float maxStringWidth = endX - startX - (2 * kMinSeparatorStubX
854 + 2 * kStubToStringSlotX);
856 // ToDo:
857 // handle case where maxStringWidth turns out negative here
859 BString truncatedLabel(Label());
860 parent->TruncateString(&truncatedLabel, B_TRUNCATE_END, maxStringWidth);
862 maxStringWidth = parent->StringWidth(truncatedLabel.String());
864 // first calculate the length of the stub part of the
865 // divider line, so we can use it for secondStartX
866 float firstEndX = ((endX - startX) - maxStringWidth) / 2
867 - kStubToStringSlotX;
868 if (firstEndX < 0)
869 firstEndX = 0;
871 float secondStartX = endX - firstEndX;
873 // now finish calculating firstEndX
874 firstEndX += startX;
876 parent->PushState();
878 int32 y = (int32) (frame.top + (frame.bottom - frame.top) / 2);
880 parent->BeginLineArray(minfo.separator == 2 ? 6 : 4);
881 parent->AddLine(BPoint(frame.left, y), BPoint(firstEndX, y),
882 ShiftMenuBackgroundColor(B_DARKEN_1_TINT));
883 parent->AddLine(BPoint(secondStartX, y), BPoint(frame.right, y),
884 ShiftMenuBackgroundColor(B_DARKEN_1_TINT));
886 if (minfo.separator == 2) {
887 y++;
888 frame.left++;
889 frame.right--;
890 parent->AddLine(BPoint(frame.left,y), BPoint(firstEndX, y),
891 ShiftMenuBackgroundColor(B_DARKEN_1_TINT));
892 parent->AddLine(BPoint(secondStartX,y), BPoint(frame.right, y),
893 ShiftMenuBackgroundColor(B_DARKEN_1_TINT));
895 y++;
896 if (minfo.separator == 2) {
897 frame.left++;
898 frame.right--;
900 parent->AddLine(BPoint(frame.left, y), BPoint(firstEndX, y),
901 ShiftMenuBackgroundColor(B_DARKEN_1_TINT));
902 parent->AddLine(BPoint(secondStartX, y), BPoint(frame.right, y),
903 ShiftMenuBackgroundColor(B_DARKEN_1_TINT));
905 parent->EndLineArray();
907 font_height finfo;
908 parent->GetFontHeight(&finfo);
910 parent->SetLowColor(parent->ViewColor());
911 BPoint loc(firstEndX + kStubToStringSlotX,
912 ContentLocation().y + finfo.ascent);
914 parent->MovePenTo(loc + BPoint(1, 1));
915 parent->SetHighColor(ShiftMenuBackgroundColor(B_DARKEN_1_TINT));
916 parent->DrawString(truncatedLabel.String());
918 parent->MovePenTo(loc);
919 parent->SetHighColor(ShiftMenuBackgroundColor(B_DISABLED_LABEL_TINT));
920 parent->DrawString(truncatedLabel.String());
922 parent->PopState();
926 // #pragma mark - ShortcutFilter
929 ShortcutFilter::ShortcutFilter(uint32 shortcutKey, uint32 shortcutModifier,
930 uint32 shortcutWhat, BHandler* target)
932 BMessageFilter(B_KEY_DOWN),
933 fShortcutKey(shortcutKey),
934 fShortcutModifier(shortcutModifier),
935 fShortcutWhat(shortcutWhat),
936 fTarget(target)
941 filter_result
942 ShortcutFilter::Filter(BMessage* message, BHandler**)
944 if (message->what == B_KEY_DOWN) {
945 uint32 modifiers;
946 uint32 rawKeyChar = 0;
947 uint8 byte = 0;
948 int32 key = 0;
950 if (message->FindInt32("modifiers", (int32*)&modifiers) != B_OK
951 || message->FindInt32("raw_char", (int32*)&rawKeyChar) != B_OK
952 || message->FindInt8("byte", (int8*)&byte) != B_OK
953 || message->FindInt32("key", &key) != B_OK) {
954 return B_DISPATCH_MESSAGE;
957 modifiers &= B_SHIFT_KEY | B_COMMAND_KEY | B_CONTROL_KEY
958 | B_OPTION_KEY | B_MENU_KEY;
959 // strip caps lock, etc.
961 if (modifiers == fShortcutModifier && rawKeyChar == fShortcutKey) {
962 fTarget->Looper()->PostMessage(fShortcutWhat, fTarget);
963 return B_SKIP_MESSAGE;
967 // let others deal with this
968 return B_DISPATCH_MESSAGE;
972 // #pragma mark - BPrivate functions
975 namespace BPrivate {
977 void
978 EmbedUniqueVolumeInfo(BMessage* message, const BVolume* volume)
980 BDirectory rootDirectory;
981 time_t created;
982 fs_info info;
984 if (volume->GetRootDirectory(&rootDirectory) == B_OK
985 && rootDirectory.GetCreationTime(&created) == B_OK
986 && fs_stat_dev(volume->Device(), &info) == 0) {
987 message->AddInt64("creationDate", created);
988 message->AddInt64("capacity", volume->Capacity());
989 message->AddString("deviceName", info.device_name);
990 message->AddString("volumeName", info.volume_name);
991 message->AddString("fshName", info.fsh_name);
996 status_t
997 MatchArchivedVolume(BVolume* volume, const BMessage* message, int32 index)
999 int64 created64;
1000 off_t capacity;
1002 if (message->FindInt64("creationDate", index, &created64) != B_OK) {
1003 int32 created32;
1004 if (message->FindInt32("creationDate", index, &created32) != B_OK)
1005 return B_ERROR;
1006 created64 = created32;
1009 time_t created = created64;
1011 if (message->FindInt64("capacity", index, &capacity) != B_OK)
1012 return B_ERROR;
1014 BVolumeRoster roster;
1015 BVolume tempVolume;
1016 BString deviceName;
1017 BString volumeName;
1018 BString fshName;
1020 if (message->FindString("deviceName", &deviceName) == B_OK
1021 && message->FindString("volumeName", &volumeName) == B_OK
1022 && message->FindString("fshName", &fshName) == B_OK) {
1023 // New style volume identifiers: We have a couple of characteristics,
1024 // and compute a score from them. The volume with the greatest score
1025 // (if over a certain threshold) is the one we're looking for. We
1026 // pick the first volume, in case there is more than one with the
1027 // same score.
1028 dev_t foundDevice = -1;
1029 int foundScore = -1;
1030 roster.Rewind();
1031 while (roster.GetNextVolume(&tempVolume) == B_OK) {
1032 if (tempVolume.IsPersistent() && tempVolume.KnowsQuery()) {
1033 // get creation time and fs_info
1034 BDirectory root;
1035 tempVolume.GetRootDirectory(&root);
1036 time_t cmpCreated;
1037 fs_info info;
1038 if (root.GetCreationTime(&cmpCreated) == B_OK
1039 && fs_stat_dev(tempVolume.Device(), &info) == 0) {
1040 // compute the score
1041 int score = 0;
1043 // creation time
1044 if (created == cmpCreated)
1045 score += 5;
1047 // capacity
1048 if (capacity == tempVolume.Capacity())
1049 score += 4;
1051 // device name
1052 if (deviceName == info.device_name)
1053 score += 3;
1055 // volume name
1056 if (volumeName == info.volume_name)
1057 score += 2;
1059 // fsh name
1060 if (fshName == info.fsh_name)
1061 score += 1;
1063 // check score
1064 if (score >= 9 && score > foundScore) {
1065 foundDevice = tempVolume.Device();
1066 foundScore = score;
1071 if (foundDevice >= 0)
1072 return volume->SetTo(foundDevice);
1073 } else {
1074 // Old style volume identifiers: We have only creation time and
1075 // capacity. Both must match.
1076 roster.Rewind();
1077 while (roster.GetNextVolume(&tempVolume) == B_OK) {
1078 if (tempVolume.IsPersistent() && tempVolume.KnowsQuery()) {
1079 BDirectory root;
1080 tempVolume.GetRootDirectory(&root);
1081 time_t cmpCreated;
1082 root.GetCreationTime(&cmpCreated);
1083 if (created == cmpCreated && capacity == tempVolume.Capacity()) {
1084 *volume = tempVolume;
1085 return B_OK;
1091 return B_DEV_BAD_DRIVE_NUM;
1095 void
1096 StringFromStream(BString* string, BMallocIO* stream, bool endianSwap)
1098 int32 length;
1099 stream->Read(&length, sizeof(length));
1100 if (endianSwap)
1101 length = SwapInt32(length);
1103 if (length < 0 || length > 10000) {
1104 // TODO: should fail here
1105 PRINT(("problems instatiating a string, length probably wrong %"
1106 B_PRId32 "\n", length));
1107 return;
1110 char* buffer = string->LockBuffer(length + 1);
1111 stream->Read(buffer, (size_t)length + 1);
1112 string->UnlockBuffer(length);
1116 void
1117 StringToStream(const BString* string, BMallocIO* stream)
1119 int32 length = string->Length();
1120 stream->Write(&length, sizeof(int32));
1121 stream->Write(string->String(), (size_t)string->Length() + 1);
1125 int32
1126 ArchiveSize(const BString* string)
1128 return string->Length() + 1 + (ssize_t)sizeof(int32);
1132 int32
1133 CountRefs(const BMessage* message)
1135 uint32 type;
1136 int32 count;
1137 message->GetInfo("refs", &type, &count);
1139 return count;
1143 static entry_ref*
1144 EachEntryRefCommon(BMessage* message, entry_ref *(*func)(entry_ref*, void*),
1145 void* passThru, int32 maxCount)
1147 uint32 type;
1148 int32 count;
1149 message->GetInfo("refs", &type, &count);
1151 if (maxCount >= 0 && count > maxCount)
1152 count = maxCount;
1154 for (int32 index = 0; index < count; index++) {
1155 entry_ref ref;
1156 message->FindRef("refs", index, &ref);
1157 entry_ref* newRef = (func)(&ref, passThru);
1158 if (newRef != NULL)
1159 return newRef;
1162 return NULL;
1166 bool
1167 ContainsEntryRef(const BMessage* message, const entry_ref* ref)
1169 entry_ref match;
1170 for (int32 index = 0; (message->FindRef("refs", index, &match) == B_OK);
1171 index++) {
1172 if (*ref == match)
1173 return true;
1176 return false;
1180 entry_ref*
1181 EachEntryRef(BMessage* message, entry_ref* (*func)(entry_ref*, void*),
1182 void* passThru)
1184 return EachEntryRefCommon(message, func, passThru, -1);
1187 typedef entry_ref *(*EachEntryIteratee)(entry_ref *, void *);
1190 const entry_ref*
1191 EachEntryRef(const BMessage* message,
1192 const entry_ref* (*func)(const entry_ref*, void*), void* passThru)
1194 return EachEntryRefCommon(const_cast<BMessage*>(message),
1195 (EachEntryIteratee)func, passThru, -1);
1199 entry_ref*
1200 EachEntryRef(BMessage* message, entry_ref* (*func)(entry_ref*, void*),
1201 void* passThru, int32 maxCount)
1203 return EachEntryRefCommon(message, func, passThru, maxCount);
1207 const entry_ref *
1208 EachEntryRef(const BMessage* message,
1209 const entry_ref *(*func)(const entry_ref *, void *), void* passThru,
1210 int32 maxCount)
1212 return EachEntryRefCommon(const_cast<BMessage *>(message),
1213 (EachEntryIteratee)func, passThru, maxCount);
1217 void
1218 TruncateLeaf(BString* string)
1220 for (int32 index = string->Length(); index >= 0; index--) {
1221 if ((*string)[index] == '/') {
1222 string->Truncate(index + 1);
1223 return;
1229 int64
1230 StringToScalar(const char* text)
1232 char* end;
1233 int64 val;
1235 char* buffer = new char [strlen(text) + 1];
1236 strcpy(buffer, text);
1238 if (strstr(buffer, "k") || strstr(buffer, "K")) {
1239 val = strtoll(buffer, &end, 10);
1240 val *= kKBSize;
1241 } else if (strstr(buffer, "mb") || strstr(buffer, "MB")) {
1242 val = strtoll(buffer, &end, 10);
1243 val *= kMBSize;
1244 } else if (strstr(buffer, "gb") || strstr(buffer, "GB")) {
1245 val = strtoll(buffer, &end, 10);
1246 val *= kGBSize;
1247 } else if (strstr(buffer, "byte") || strstr(buffer, "BYTE")) {
1248 val = strtoll(buffer, &end, 10);
1249 val *= kGBSize;
1250 } else {
1251 // no suffix, try plain byte conversion
1252 val = strtoll(buffer, &end, 10);
1254 delete[] buffer;
1256 return val;
1260 static BRect
1261 LineBounds(BPoint where, float length, bool vertical)
1263 BRect rect;
1264 rect.SetLeftTop(where);
1265 rect.SetRightBottom(where + BPoint(2, 2));
1266 if (vertical)
1267 rect.bottom = rect.top + length;
1268 else
1269 rect.right = rect.left + length;
1271 return rect;
1275 SeparatorLine::SeparatorLine(BPoint where, float length, bool vertical,
1276 const char* name)
1278 BView(LineBounds(where, length, vertical), name,
1279 B_FOLLOW_LEFT | B_FOLLOW_TOP, B_WILL_DRAW)
1281 SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
1282 SetLowUIColor(B_PANEL_BACKGROUND_COLOR);
1286 void
1287 SeparatorLine::Draw(BRect)
1289 BRect bounds(Bounds());
1290 rgb_color hiliteColor = tint_color(ViewColor(), 1.5f);
1292 bool vertical = (bounds.left > bounds.right - 3);
1293 BeginLineArray(2);
1294 if (vertical) {
1295 AddLine(bounds.LeftTop(), bounds.LeftBottom(), hiliteColor);
1296 AddLine(bounds.LeftTop() + BPoint(1, 0),
1297 bounds.LeftBottom() + BPoint(1, 0), kWhite);
1298 } else {
1299 AddLine(bounds.LeftTop(), bounds.RightTop(), hiliteColor);
1300 AddLine(bounds.LeftTop() + BPoint(0, 1),
1301 bounds.RightTop() + BPoint(0, 1), kWhite);
1303 EndLineArray();
1307 void
1308 HexDump(const void* buf, int32 length)
1310 const int32 kBytesPerLine = 16;
1311 int32 offset;
1312 unsigned char* buffer = (unsigned char*)buf;
1314 for (offset = 0; ; offset += kBytesPerLine, buffer += kBytesPerLine) {
1315 int32 remain = length;
1316 int32 index;
1318 printf( "0x%06x: ", (int)offset);
1320 for (index = 0; index < kBytesPerLine; index++) {
1321 if (remain-- > 0)
1322 printf("%02x%c", buffer[index], remain > 0 ? ',' : ' ');
1323 else
1324 printf(" ");
1327 remain = length;
1328 printf(" \'");
1329 for (index = 0; index < kBytesPerLine; index++) {
1330 if (remain-- > 0)
1331 printf("%c", buffer[index] > ' ' ? buffer[index] : '.');
1332 else
1333 printf(" ");
1335 printf("\'\n");
1337 length -= kBytesPerLine;
1338 if (length <= 0)
1339 break;
1341 fflush(stdout);
1345 void
1346 EnableNamedMenuItem(BMenu* menu, const char* itemName, bool on)
1348 BMenuItem* item = menu->FindItem(itemName);
1349 if (item != NULL)
1350 item->SetEnabled(on);
1354 void
1355 MarkNamedMenuItem(BMenu* menu, const char* itemName, bool on)
1357 BMenuItem* item = menu->FindItem(itemName);
1358 if (item != NULL)
1359 item->SetMarked(on);
1363 void
1364 EnableNamedMenuItem(BMenu* menu, uint32 commandName, bool on)
1366 BMenuItem* item = menu->FindItem(commandName);
1367 if (item != NULL)
1368 item->SetEnabled(on);
1372 void
1373 MarkNamedMenuItem(BMenu* menu, uint32 commandName, bool on)
1375 BMenuItem* item = menu->FindItem(commandName);
1376 if (item != NULL)
1377 item->SetMarked(on);
1381 void
1382 DeleteSubmenu(BMenuItem* submenuItem)
1384 if (submenuItem == NULL)
1385 return;
1387 BMenu* menu = submenuItem->Submenu();
1388 if (menu == NULL)
1389 return;
1391 for (;;) {
1392 BMenuItem* item = menu->RemoveItem((int32)0);
1393 if (item == NULL)
1394 return;
1396 delete item;
1401 status_t
1402 GetAppSignatureFromAttr(BFile* file, char* attr)
1404 // This call is a performance improvement that
1405 // avoids using the BAppFileInfo API when retrieving the
1406 // app signature -- the call is expensive because by default
1407 // the resource fork is scanned to read the attribute
1409 #ifdef B_APP_FILE_INFO_IS_FAST
1410 BAppFileInfo appFileInfo(file);
1411 return appFileInfo.GetSignature(attr);
1412 #else
1413 ssize_t readResult = file->ReadAttr(kAttrAppSignature, B_MIME_STRING_TYPE,
1414 0, attr, B_MIME_TYPE_LENGTH);
1416 if (readResult <= 0)
1417 return (status_t)readResult;
1419 return B_OK;
1420 #endif // B_APP_FILE_INFO_IS_FAST
1424 status_t
1425 GetAppIconFromAttr(BFile* file, BBitmap* icon, icon_size which)
1427 // This call is a performance improvement that
1428 // avoids using the BAppFileInfo API when retrieving the
1429 // app icons -- the call is expensive because by default
1430 // the resource fork is scanned to read the icons
1432 //#ifdef B_APP_FILE_INFO_IS_FAST
1433 BAppFileInfo appFileInfo(file);
1434 return appFileInfo.GetIcon(icon, which);
1435 //#else
1437 // const char* attrName = kAttrIcon;
1438 // uint32 type = B_VECTOR_ICON_TYPE;
1440 // // try vector icon
1441 // attr_info ainfo;
1442 // status_t result = file->GetAttrInfo(attrName, &ainfo);
1444 // if (result == B_OK) {
1445 // uint8 buffer[ainfo.size];
1446 // ssize_t readResult = file->ReadAttr(attrName, type, 0, buffer,
1447 // ainfo.size);
1448 // if (readResult == ainfo.size) {
1449 // if (BIconUtils::GetVectorIcon(buffer, ainfo.size, icon) == B_OK)
1450 // return B_OK;
1451 // }
1452 // }
1454 // // try again with R5 icons
1455 // attrName = which == B_LARGE_ICON ? kAttrLargeIcon : kAttrMiniIcon;
1456 // type = which == B_LARGE_ICON ? LARGE_ICON_TYPE : MINI_ICON_TYPE;
1458 // result = file->GetAttrInfo(attrName, &ainfo);
1459 // if (result < B_OK)
1460 // return result;
1462 // uint8 buffer[ainfo.size];
1464 // ssize_t readResult = file->ReadAttr(attrName, type, 0, buffer, ainfo.size);
1465 // if (readResult <= 0)
1466 // return (status_t)readResult;
1468 // if (icon->ColorSpace() != B_CMAP8)
1469 // result = BIconUtils::ConvertFromCMAP8(buffer, which, which, which, icon);
1470 // else
1471 // icon->SetBits(buffer, icon->BitsLength(), 0, B_CMAP8);
1473 // return result;
1474 //#endif // B_APP_FILE_INFO_IS_FAST
1478 status_t
1479 GetFileIconFromAttr(BNode* node, BBitmap* icon, icon_size which)
1481 BNodeInfo fileInfo(node);
1482 return fileInfo.GetIcon(icon, which);
1486 void
1487 PrintToStream(rgb_color color)
1489 printf("r:%x, g:%x, b:%x, a:%x\n",
1490 color.red, color.green, color.blue, color.alpha);
1494 extern BMenuItem*
1495 EachMenuItem(BMenu* menu, bool recursive, BMenuItem* (*func)(BMenuItem *))
1497 int32 count = menu->CountItems();
1498 for (int32 index = 0; index < count; index++) {
1499 BMenuItem* item = menu->ItemAt(index);
1500 BMenuItem* newItem = (func)(item);
1501 if (newItem != NULL)
1502 return newItem;
1504 if (recursive) {
1505 BMenu* submenu = menu->SubmenuAt(index);
1506 if (submenu != NULL)
1507 return EachMenuItem(submenu, true, func);
1511 return NULL;
1515 extern const BMenuItem*
1516 EachMenuItem(const BMenu* menu, bool recursive,
1517 BMenuItem* (*func)(const BMenuItem *))
1519 int32 count = menu->CountItems();
1520 for (int32 index = 0; index < count; index++) {
1521 BMenuItem* item = menu->ItemAt(index);
1522 BMenuItem* newItem = (func)(item);
1523 if (newItem != NULL)
1524 return newItem;
1526 if (recursive) {
1527 BMenu* submenu = menu->SubmenuAt(index);
1528 if (submenu != NULL)
1529 return EachMenuItem(submenu, true, func);
1533 return NULL;
1537 // #pragma mark - PositionPassingMenuItem
1540 PositionPassingMenuItem::PositionPassingMenuItem(const char* title,
1541 BMessage* message, char shortcut, uint32 modifiers)
1543 BMenuItem(title, message, shortcut, modifiers)
1548 PositionPassingMenuItem::PositionPassingMenuItem(BMenu* menu, BMessage* message)
1550 BMenuItem(menu, message)
1555 PositionPassingMenuItem::PositionPassingMenuItem(BMessage* data)
1557 BMenuItem(data)
1562 BArchivable*
1563 PositionPassingMenuItem::Instantiate(BMessage* data)
1565 if (validate_instantiation(data, "PositionPassingMenuItem"))
1566 return new PositionPassingMenuItem(data);
1568 return NULL;
1572 status_t
1573 PositionPassingMenuItem::Invoke(BMessage* message)
1575 if (Menu() == NULL)
1576 return B_ERROR;
1578 if (!IsEnabled())
1579 return B_ERROR;
1581 if (message == NULL)
1582 message = Message();
1584 if (message == NULL)
1585 return B_BAD_VALUE;
1587 BMessage clone(*message);
1588 clone.AddInt32("index", Menu()->IndexOf(this));
1589 clone.AddInt64("when", system_time());
1590 clone.AddPointer("source", this);
1592 // embed the invoke location of the menu so that we can create
1593 // a new folder, etc. on the spot
1594 BMenu* menu = Menu();
1596 for (;;) {
1597 if (!menu->Supermenu())
1598 break;
1600 menu = menu->Supermenu();
1603 // use the window position only, if the item was invoked from the menu
1604 // menu->Window() points to the window the item was invoked from
1605 if (dynamic_cast<BContainerWindow*>(menu->Window()) == NULL) {
1606 LooperAutoLocker lock(menu);
1607 if (lock.IsLocked()) {
1608 BPoint invokeOrigin(menu->Window()->Frame().LeftTop());
1609 clone.AddPoint("be:invoke_origin", invokeOrigin);
1613 return BInvoker::Invoke(&clone);
1617 // #pragma mark - BPrivate functions
1620 bool
1621 BootedInSafeMode()
1623 const char* safeMode = getenv("SAFEMODE");
1624 return (safeMode && strcmp(safeMode, "yes") == 0);
1628 float
1629 ComputeTypeAheadScore(const char* text, const char* match, bool wordMode)
1631 // highest score: exact match
1632 const char* found = strcasestr(text, match);
1633 if (found != NULL) {
1634 if (found == text)
1635 return kExactMatchScore;
1637 return 1.f / (found - text);
1640 // there was no exact match
1642 // second best: all characters at word beginnings
1643 if (wordMode) {
1644 float score = 0;
1645 for (int32 j = 0, k = 0; match[j]; j++) {
1646 while (text[k]
1647 && tolower(text[k]) != tolower(match[j])) {
1648 k++;
1650 if (text[k] == '\0') {
1651 score = 0;
1652 break;
1655 bool wordStart = k == 0 || isspace(text[k - 1]);
1656 if (wordStart)
1657 score++;
1658 if (j > 0) {
1659 bool wordEnd = !text[k + 1] || isspace(text[k + 1]);
1660 if (wordEnd)
1661 score += 0.3;
1662 if (match[j - 1] == text[k - 1])
1663 score += 0.7;
1666 score += 1.f / (k + 1);
1667 k++;
1670 return score;
1673 return -1;
1677 // #pragma mark - throw on error functions.
1680 void
1681 _ThrowOnError(status_t result, const char* DEBUG_ONLY(file),
1682 int32 DEBUG_ONLY(line))
1684 if (result != B_OK) {
1685 PRINT(("%s at %s:%d\n", strerror(result), file, (int)line));
1686 throw result;
1691 void
1692 _ThrowIfNotSize(ssize_t size, const char* DEBUG_ONLY(file),
1693 int32 DEBUG_ONLY(line))
1695 if (size < B_OK) {
1696 PRINT(("%s at %s:%d\n", strerror((status_t)size), file, (int)line));
1697 throw (status_t)size;
1702 void
1703 _ThrowOnAssert(bool success, const char* DEBUG_ONLY(file),
1704 int32 DEBUG_ONLY(line))
1706 if (!success) {
1707 PRINT(("Assert failed at %s:%d\n", file, (int)line));
1708 throw B_ERROR;
1712 } // namespace BPrivate