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