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