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