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 return base.IsLight() ? B_DARKEN_1_TINT : 0.85; 95 } 96 97 98 bool 99 SecondaryMouseButtonDown(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 107 uint32 108 HashString(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 123 uint32 124 AttrHashString(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 142 bool 143 ValidateStream(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 157 void 158 DisallowFilenameKeys(BTextView* textView) 159 { 160 textView->DisallowChar('/'); 161 } 162 163 164 void 165 DisallowMetaKeys(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 179 PeriodicUpdatePoses::PeriodicUpdatePoses() 180 : 181 fPoseList(20, true) 182 { 183 fLock = new Benaphore("PeriodicUpdatePoses"); 184 } 185 186 187 PeriodicUpdatePoses::~PeriodicUpdatePoses() 188 { 189 fLock->Lock(); 190 fPoseList.MakeEmpty(); 191 delete fLock; 192 } 193 194 195 void 196 PeriodicUpdatePoses::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 208 bool 209 PeriodicUpdatePoses::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 230 void 231 PeriodicUpdatePoses::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 250 PeriodicUpdatePoses gPeriodicUpdatePoses; 251 252 } // namespace BPrivate 253 254 255 void 256 PoseInfo::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 277 void 278 PoseInfo::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 289 size_t 290 ExtendedPoseInfo::Size() const 291 { 292 return sizeof(ExtendedPoseInfo) + fNumFrames * sizeof(FrameLocation); 293 } 294 295 296 size_t 297 ExtendedPoseInfo::Size(int32 count) 298 { 299 return sizeof(ExtendedPoseInfo) + count * sizeof(FrameLocation); 300 } 301 302 303 size_t 304 ExtendedPoseInfo::SizeWithHeadroom() const 305 { 306 return sizeof(ExtendedPoseInfo) + (fNumFrames + 1) * sizeof(FrameLocation); 307 } 308 309 310 size_t 311 ExtendedPoseInfo::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 323 bool 324 ExtendedPoseInfo::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 335 BPoint 336 ExtendedPoseInfo::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 348 bool 349 ExtendedPoseInfo::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 370 void 371 ExtendedPoseInfo::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 398 void 399 ExtendedPoseInfo::PrintToStream() 400 { 401 } 402 403 404 // #pragma mark - OffscreenBitmap 405 406 407 OffscreenBitmap::OffscreenBitmap(BRect frame) 408 : 409 fBitmap(NULL) 410 { 411 NewBitmap(frame); 412 } 413 414 415 OffscreenBitmap::OffscreenBitmap() 416 : 417 fBitmap(NULL) 418 { 419 } 420 421 422 OffscreenBitmap::~OffscreenBitmap() 423 { 424 delete fBitmap; 425 } 426 427 428 void 429 OffscreenBitmap::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 450 BView* 451 OffscreenBitmap::BeginUsing(BRect frame) 452 { 453 if (!fBitmap || fBitmap->Bounds() != frame) 454 NewBitmap(frame); 455 456 fBitmap->Lock(); 457 return View(); 458 } 459 460 461 void 462 OffscreenBitmap::DoneUsing() 463 { 464 fBitmap->Unlock(); 465 } 466 467 468 BBitmap* 469 OffscreenBitmap::Bitmap() const 470 { 471 ASSERT(fBitmap); 472 ASSERT(fBitmap->IsLocked()); 473 return fBitmap; 474 } 475 476 477 BView* 478 OffscreenBitmap::View() const 479 { 480 ASSERT(fBitmap); 481 return fBitmap->ChildAt(0); 482 } 483 484 485 // #pragma mark - BPrivate functions 486 487 488 namespace 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. 493 void 494 FadeRGBA32Horizontal(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 */ 527 void 528 FadeRGBA32Vertical(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 565 DraggableIcon::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 586 DraggableIcon::~DraggableIcon() 587 { 588 delete fBitmap; 589 } 590 591 592 void 593 DraggableIcon::SetTarget(BMessenger target) 594 { 595 fTarget = target; 596 } 597 598 599 BRect 600 DraggableIcon::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 608 void 609 DraggableIcon::AttachedToWindow() 610 { 611 AdoptParentColors(); 612 } 613 614 615 void 616 DraggableIcon::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 646 bool 647 DraggableIcon::DragStarted(BMessage*) 648 { 649 return true; 650 } 651 652 653 void 654 DraggableIcon::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 665 FlickerFreeStringView::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 677 FlickerFreeStringView::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 689 FlickerFreeStringView::~FlickerFreeStringView() 690 { 691 delete fBitmap; 692 } 693 694 695 void 696 FlickerFreeStringView::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 767 void 768 FlickerFreeStringView::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 780 void 781 FlickerFreeStringView::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 791 void 792 FlickerFreeStringView::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 805 TitledSeparatorItem::TitledSeparatorItem(const char* label) 806 : 807 BMenuItem(label, 0) 808 { 809 _inherited::SetEnabled(false); 810 } 811 812 813 TitledSeparatorItem::~TitledSeparatorItem() 814 { 815 } 816 817 818 void 819 TitledSeparatorItem::SetEnabled(bool) 820 { 821 // leave disabled 822 } 823 824 825 void 826 TitledSeparatorItem::GetContentSize(float* width, float* height) 827 { 828 _inherited::GetContentSize(width, height); 829 830 // Adjust for the extra space needed by the separator bars at the left and right 831 if (width) 832 *width += (kMinSeparatorStubX + kStubToStringSlotX) * 2; 833 } 834 835 836 inline rgb_color 837 ShiftMenuBackgroundColor(float by) 838 { 839 return tint_color(ui_color(B_MENU_BACKGROUND_COLOR), by); 840 } 841 842 843 void 844 TitledSeparatorItem::Draw() 845 { 846 BRect frame(Frame()); 847 848 BMenu* parent = Menu(); 849 ASSERT(parent != NULL); 850 851 menu_info minfo; 852 get_menu_info(&minfo); 853 854 if (minfo.separator > 0) { 855 frame.left += 10; 856 frame.right -= 10; 857 } else { 858 frame.left += 1; 859 frame.right -= 1; 860 } 861 862 float startX = frame.left; 863 float endX = frame.right; 864 865 float maxStringWidth = endX - startX - (2 * kMinSeparatorStubX 866 + 2 * kStubToStringSlotX); 867 868 // ToDo: 869 // handle case where maxStringWidth turns out negative here 870 871 BString truncatedLabel(Label()); 872 parent->TruncateString(&truncatedLabel, B_TRUNCATE_END, maxStringWidth); 873 874 maxStringWidth = parent->StringWidth(truncatedLabel.String()); 875 876 // first calculate the length of the stub part of the 877 // divider line, so we can use it for secondStartX 878 float firstEndX = ((endX - startX) - maxStringWidth) / 2 879 - kStubToStringSlotX; 880 if (firstEndX < 0) 881 firstEndX = 0; 882 883 float secondStartX = endX - firstEndX; 884 885 // now finish calculating firstEndX 886 firstEndX += startX; 887 888 parent->PushState(); 889 890 int32 y = (int32) (frame.top + (frame.bottom - frame.top) / 2); 891 892 parent->BeginLineArray(minfo.separator == 2 ? 6 : 4); 893 parent->AddLine(BPoint(frame.left, y), BPoint(firstEndX, y), 894 ShiftMenuBackgroundColor(B_DARKEN_1_TINT)); 895 parent->AddLine(BPoint(secondStartX, y), BPoint(frame.right, y), 896 ShiftMenuBackgroundColor(B_DARKEN_1_TINT)); 897 898 if (minfo.separator == 2) { 899 y++; 900 frame.left++; 901 frame.right--; 902 parent->AddLine(BPoint(frame.left,y), BPoint(firstEndX, y), 903 ShiftMenuBackgroundColor(B_DARKEN_1_TINT)); 904 parent->AddLine(BPoint(secondStartX,y), BPoint(frame.right, y), 905 ShiftMenuBackgroundColor(B_DARKEN_1_TINT)); 906 } 907 y++; 908 if (minfo.separator == 2) { 909 frame.left++; 910 frame.right--; 911 } 912 parent->AddLine(BPoint(frame.left, y), BPoint(firstEndX, y), 913 ShiftMenuBackgroundColor(B_DARKEN_1_TINT)); 914 parent->AddLine(BPoint(secondStartX, y), BPoint(frame.right, y), 915 ShiftMenuBackgroundColor(B_DARKEN_1_TINT)); 916 917 parent->EndLineArray(); 918 919 font_height finfo; 920 parent->GetFontHeight(&finfo); 921 922 parent->SetLowColor(parent->ViewColor()); 923 BPoint loc(firstEndX + kStubToStringSlotX, 924 ContentLocation().y + finfo.ascent); 925 926 parent->MovePenTo(loc + BPoint(1, 1)); 927 parent->SetHighColor(ShiftMenuBackgroundColor(B_DARKEN_1_TINT)); 928 parent->DrawString(truncatedLabel.String()); 929 930 parent->MovePenTo(loc); 931 parent->SetHighColor(ShiftMenuBackgroundColor(B_DISABLED_LABEL_TINT)); 932 parent->DrawString(truncatedLabel.String()); 933 934 parent->PopState(); 935 } 936 937 938 // #pragma mark - ShortcutFilter 939 940 941 ShortcutFilter::ShortcutFilter(uint32 shortcutKey, uint32 shortcutModifier, 942 uint32 shortcutWhat, BHandler* target) 943 : 944 BMessageFilter(B_KEY_DOWN), 945 fShortcutKey(shortcutKey), 946 fShortcutModifier(shortcutModifier), 947 fShortcutWhat(shortcutWhat), 948 fTarget(target) 949 { 950 } 951 952 953 filter_result 954 ShortcutFilter::Filter(BMessage* message, BHandler**) 955 { 956 if (message->what == B_KEY_DOWN) { 957 uint32 modifiers; 958 uint32 rawKeyChar = 0; 959 uint8 byte = 0; 960 int32 key = 0; 961 962 if (message->FindInt32("modifiers", (int32*)&modifiers) != B_OK 963 || message->FindInt32("raw_char", (int32*)&rawKeyChar) != B_OK 964 || message->FindInt8("byte", (int8*)&byte) != B_OK 965 || message->FindInt32("key", &key) != B_OK) { 966 return B_DISPATCH_MESSAGE; 967 } 968 969 modifiers &= B_SHIFT_KEY | B_COMMAND_KEY | B_CONTROL_KEY 970 | B_OPTION_KEY | B_MENU_KEY; 971 // strip caps lock, etc. 972 973 if (modifiers == fShortcutModifier && rawKeyChar == fShortcutKey) { 974 fTarget->Looper()->PostMessage(fShortcutWhat, fTarget); 975 return B_SKIP_MESSAGE; 976 } 977 } 978 979 // let others deal with this 980 return B_DISPATCH_MESSAGE; 981 } 982 983 984 // #pragma mark - BPrivate functions 985 986 987 namespace BPrivate { 988 989 void 990 EmbedUniqueVolumeInfo(BMessage* message, const BVolume* volume) 991 { 992 BDirectory rootDirectory; 993 time_t created; 994 fs_info info; 995 996 if (volume->GetRootDirectory(&rootDirectory) == B_OK 997 && rootDirectory.GetCreationTime(&created) == B_OK 998 && fs_stat_dev(volume->Device(), &info) == 0) { 999 message->AddInt64("creationDate", created); 1000 message->AddInt64("capacity", volume->Capacity()); 1001 message->AddString("deviceName", info.device_name); 1002 message->AddString("volumeName", info.volume_name); 1003 message->AddString("fshName", info.fsh_name); 1004 } 1005 } 1006 1007 1008 status_t 1009 MatchArchivedVolume(BVolume* volume, const BMessage* message, int32 index) 1010 { 1011 int64 created64; 1012 off_t capacity; 1013 1014 if (message->FindInt64("creationDate", index, &created64) != B_OK) { 1015 int32 created32; 1016 if (message->FindInt32("creationDate", index, &created32) != B_OK) 1017 return B_ERROR; 1018 created64 = created32; 1019 } 1020 1021 time_t created = created64; 1022 1023 if (message->FindInt64("capacity", index, &capacity) != B_OK) 1024 return B_ERROR; 1025 1026 BVolumeRoster roster; 1027 BVolume tempVolume; 1028 BString deviceName; 1029 BString volumeName; 1030 BString fshName; 1031 1032 if (message->FindString("deviceName", &deviceName) == B_OK 1033 && message->FindString("volumeName", &volumeName) == B_OK 1034 && message->FindString("fshName", &fshName) == B_OK) { 1035 // New style volume identifiers: We have a couple of characteristics, 1036 // and compute a score from them. The volume with the greatest score 1037 // (if over a certain threshold) is the one we're looking for. We 1038 // pick the first volume, in case there is more than one with the 1039 // same score. 1040 dev_t foundDevice = -1; 1041 int foundScore = -1; 1042 roster.Rewind(); 1043 while (roster.GetNextVolume(&tempVolume) == B_OK) { 1044 if (tempVolume.IsPersistent() && tempVolume.KnowsQuery()) { 1045 // get creation time and fs_info 1046 BDirectory root; 1047 tempVolume.GetRootDirectory(&root); 1048 time_t cmpCreated; 1049 fs_info info; 1050 if (root.GetCreationTime(&cmpCreated) == B_OK 1051 && fs_stat_dev(tempVolume.Device(), &info) == 0) { 1052 // compute the score 1053 int score = 0; 1054 1055 // creation time 1056 if (created == cmpCreated) 1057 score += 5; 1058 1059 // capacity 1060 if (capacity == tempVolume.Capacity()) 1061 score += 4; 1062 1063 // device name 1064 if (deviceName == info.device_name) 1065 score += 3; 1066 1067 // volume name 1068 if (volumeName == info.volume_name) 1069 score += 2; 1070 1071 // fsh name 1072 if (fshName == info.fsh_name) 1073 score += 1; 1074 1075 // check score 1076 if (score >= 9 && score > foundScore) { 1077 foundDevice = tempVolume.Device(); 1078 foundScore = score; 1079 } 1080 } 1081 } 1082 } 1083 if (foundDevice >= 0) 1084 return volume->SetTo(foundDevice); 1085 } else { 1086 // Old style volume identifiers: We have only creation time and 1087 // capacity. Both must match. 1088 roster.Rewind(); 1089 while (roster.GetNextVolume(&tempVolume) == B_OK) { 1090 if (tempVolume.IsPersistent() && tempVolume.KnowsQuery()) { 1091 BDirectory root; 1092 tempVolume.GetRootDirectory(&root); 1093 time_t cmpCreated; 1094 root.GetCreationTime(&cmpCreated); 1095 if (created == cmpCreated && capacity == tempVolume.Capacity()) { 1096 *volume = tempVolume; 1097 return B_OK; 1098 } 1099 } 1100 } 1101 } 1102 1103 return B_DEV_BAD_DRIVE_NUM; 1104 } 1105 1106 1107 void 1108 StringFromStream(BString* string, BMallocIO* stream, bool endianSwap) 1109 { 1110 int32 length; 1111 stream->Read(&length, sizeof(length)); 1112 if (endianSwap) 1113 length = SwapInt32(length); 1114 1115 if (length < 0 || length > 10000) { 1116 // TODO: should fail here 1117 PRINT(("problems instatiating a string, length probably wrong %" 1118 B_PRId32 "\n", length)); 1119 return; 1120 } 1121 1122 char* buffer = string->LockBuffer(length + 1); 1123 stream->Read(buffer, (size_t)length + 1); 1124 string->UnlockBuffer(length); 1125 } 1126 1127 1128 void 1129 StringToStream(const BString* string, BMallocIO* stream) 1130 { 1131 int32 length = string->Length(); 1132 stream->Write(&length, sizeof(int32)); 1133 stream->Write(string->String(), (size_t)string->Length() + 1); 1134 } 1135 1136 1137 int32 1138 ArchiveSize(const BString* string) 1139 { 1140 return string->Length() + 1 + (ssize_t)sizeof(int32); 1141 } 1142 1143 1144 int32 1145 CountRefs(const BMessage* message) 1146 { 1147 uint32 type; 1148 int32 count; 1149 message->GetInfo("refs", &type, &count); 1150 1151 return count; 1152 } 1153 1154 1155 static entry_ref* 1156 EachEntryRefCommon(BMessage* message, entry_ref *(*func)(entry_ref*, void*), 1157 void* passThru, int32 maxCount) 1158 { 1159 uint32 type; 1160 int32 count; 1161 message->GetInfo("refs", &type, &count); 1162 1163 if (maxCount >= 0 && count > maxCount) 1164 count = maxCount; 1165 1166 for (int32 index = 0; index < count; index++) { 1167 entry_ref ref; 1168 message->FindRef("refs", index, &ref); 1169 entry_ref* newRef = (func)(&ref, passThru); 1170 if (newRef != NULL) 1171 return newRef; 1172 } 1173 1174 return NULL; 1175 } 1176 1177 1178 bool 1179 ContainsEntryRef(const BMessage* message, const entry_ref* ref) 1180 { 1181 entry_ref match; 1182 for (int32 index = 0; (message->FindRef("refs", index, &match) == B_OK); 1183 index++) { 1184 if (*ref == match) 1185 return true; 1186 } 1187 1188 return false; 1189 } 1190 1191 1192 entry_ref* 1193 EachEntryRef(BMessage* message, entry_ref* (*func)(entry_ref*, void*), 1194 void* passThru) 1195 { 1196 return EachEntryRefCommon(message, func, passThru, -1); 1197 } 1198 1199 typedef entry_ref *(*EachEntryIteratee)(entry_ref *, void *); 1200 1201 1202 const entry_ref* 1203 EachEntryRef(const BMessage* message, 1204 const entry_ref* (*func)(const entry_ref*, void*), void* passThru) 1205 { 1206 return EachEntryRefCommon(const_cast<BMessage*>(message), 1207 (EachEntryIteratee)func, passThru, -1); 1208 } 1209 1210 1211 entry_ref* 1212 EachEntryRef(BMessage* message, entry_ref* (*func)(entry_ref*, void*), 1213 void* passThru, int32 maxCount) 1214 { 1215 return EachEntryRefCommon(message, func, passThru, maxCount); 1216 } 1217 1218 1219 const entry_ref * 1220 EachEntryRef(const BMessage* message, 1221 const entry_ref *(*func)(const entry_ref *, void *), void* passThru, 1222 int32 maxCount) 1223 { 1224 return EachEntryRefCommon(const_cast<BMessage *>(message), 1225 (EachEntryIteratee)func, passThru, maxCount); 1226 } 1227 1228 1229 void 1230 TruncateLeaf(BString* string) 1231 { 1232 for (int32 index = string->Length(); index >= 0; index--) { 1233 if ((*string)[index] == '/') { 1234 string->Truncate(index + 1); 1235 return; 1236 } 1237 } 1238 } 1239 1240 1241 int64 1242 StringToScalar(const char* text) 1243 { 1244 char* end; 1245 int64 val; 1246 1247 char* buffer = new char [strlen(text) + 1]; 1248 strcpy(buffer, text); 1249 1250 if (strstr(buffer, "k") || strstr(buffer, "K")) { 1251 val = strtoll(buffer, &end, 10); 1252 val *= kKBSize; 1253 } else if (strstr(buffer, "mb") || strstr(buffer, "MB")) { 1254 val = strtoll(buffer, &end, 10); 1255 val *= kMBSize; 1256 } else if (strstr(buffer, "gb") || strstr(buffer, "GB")) { 1257 val = strtoll(buffer, &end, 10); 1258 val *= kGBSize; 1259 } else if (strstr(buffer, "byte") || strstr(buffer, "BYTE")) { 1260 val = strtoll(buffer, &end, 10); 1261 val *= kGBSize; 1262 } else { 1263 // no suffix, try plain byte conversion 1264 val = strtoll(buffer, &end, 10); 1265 } 1266 delete[] buffer; 1267 1268 return val; 1269 } 1270 1271 1272 int32 1273 ListIconSize() 1274 { 1275 static int32 sIconSize = be_control_look->ComposeIconSize(B_MINI_ICON) 1276 .IntegerWidth() + 1; 1277 return sIconSize; 1278 } 1279 1280 1281 static BRect 1282 LineBounds(BPoint where, float length, bool vertical) 1283 { 1284 BRect rect; 1285 rect.SetLeftTop(where); 1286 rect.SetRightBottom(where + BPoint(2, 2)); 1287 if (vertical) 1288 rect.bottom = rect.top + length; 1289 else 1290 rect.right = rect.left + length; 1291 1292 return rect; 1293 } 1294 1295 1296 SeparatorLine::SeparatorLine(BPoint where, float length, bool vertical, 1297 const char* name) 1298 : 1299 BView(LineBounds(where, length, vertical), name, 1300 B_FOLLOW_LEFT | B_FOLLOW_TOP, B_WILL_DRAW) 1301 { 1302 SetViewUIColor(B_PANEL_BACKGROUND_COLOR); 1303 SetLowUIColor(B_PANEL_BACKGROUND_COLOR); 1304 } 1305 1306 1307 void 1308 SeparatorLine::Draw(BRect) 1309 { 1310 BRect bounds(Bounds()); 1311 rgb_color hiliteColor = tint_color(ViewColor(), 1.5f); 1312 1313 bool vertical = (bounds.left > bounds.right - 3); 1314 BeginLineArray(2); 1315 if (vertical) { 1316 AddLine(bounds.LeftTop(), bounds.LeftBottom(), hiliteColor); 1317 AddLine(bounds.LeftTop() + BPoint(1, 0), 1318 bounds.LeftBottom() + BPoint(1, 0), kWhite); 1319 } else { 1320 AddLine(bounds.LeftTop(), bounds.RightTop(), hiliteColor); 1321 AddLine(bounds.LeftTop() + BPoint(0, 1), 1322 bounds.RightTop() + BPoint(0, 1), kWhite); 1323 } 1324 EndLineArray(); 1325 } 1326 1327 1328 void 1329 HexDump(const void* buf, int32 length) 1330 { 1331 const int32 kBytesPerLine = 16; 1332 int32 offset; 1333 unsigned char* buffer = (unsigned char*)buf; 1334 1335 for (offset = 0; ; offset += kBytesPerLine, buffer += kBytesPerLine) { 1336 int32 remain = length; 1337 int32 index; 1338 1339 printf( "0x%06x: ", (int)offset); 1340 1341 for (index = 0; index < kBytesPerLine; index++) { 1342 if (remain-- > 0) 1343 printf("%02x%c", buffer[index], remain > 0 ? ',' : ' '); 1344 else 1345 printf(" "); 1346 } 1347 1348 remain = length; 1349 printf(" \'"); 1350 for (index = 0; index < kBytesPerLine; index++) { 1351 if (remain-- > 0) 1352 printf("%c", buffer[index] > ' ' ? buffer[index] : '.'); 1353 else 1354 printf(" "); 1355 } 1356 printf("\'\n"); 1357 1358 length -= kBytesPerLine; 1359 if (length <= 0) 1360 break; 1361 } 1362 fflush(stdout); 1363 } 1364 1365 1366 int 1367 CompareLabels(const BMenuItem* item1, const BMenuItem* item2) 1368 { 1369 return strcasecmp(item1->Label(), item2->Label()); 1370 } 1371 1372 1373 void 1374 EnableNamedMenuItem(BMenu* menu, const char* itemName, bool on) 1375 { 1376 BMenuItem* item = menu->FindItem(itemName); 1377 if (item != NULL) 1378 item->SetEnabled(on); 1379 } 1380 1381 1382 void 1383 MarkNamedMenuItem(BMenu* menu, const char* itemName, bool on) 1384 { 1385 BMenuItem* item = menu->FindItem(itemName); 1386 if (item != NULL) 1387 item->SetMarked(on); 1388 } 1389 1390 1391 void 1392 EnableNamedMenuItem(BMenu* menu, uint32 commandName, bool on) 1393 { 1394 BMenuItem* item = menu->FindItem(commandName); 1395 if (item != NULL) 1396 item->SetEnabled(on); 1397 } 1398 1399 1400 void 1401 MarkNamedMenuItem(BMenu* menu, uint32 commandName, bool on) 1402 { 1403 BMenuItem* item = menu->FindItem(commandName); 1404 if (item != NULL) 1405 item->SetMarked(on); 1406 } 1407 1408 1409 void 1410 DeleteSubmenu(BMenuItem* submenuItem) 1411 { 1412 if (submenuItem == NULL) 1413 return; 1414 1415 BMenu* menu = submenuItem->Submenu(); 1416 if (menu == NULL) 1417 return; 1418 1419 for (;;) { 1420 BMenuItem* item = menu->RemoveItem((int32)0); 1421 if (item == NULL) 1422 return; 1423 1424 delete item; 1425 } 1426 } 1427 1428 1429 status_t 1430 GetAppSignatureFromAttr(BFile* file, char* attr) 1431 { 1432 // This call is a performance improvement that 1433 // avoids using the BAppFileInfo API when retrieving the 1434 // app signature -- the call is expensive because by default 1435 // the resource fork is scanned to read the attribute 1436 1437 #ifdef B_APP_FILE_INFO_IS_FAST 1438 BAppFileInfo appFileInfo(file); 1439 return appFileInfo.GetSignature(attr); 1440 #else 1441 ssize_t readResult = file->ReadAttr(kAttrAppSignature, B_MIME_STRING_TYPE, 1442 0, attr, B_MIME_TYPE_LENGTH); 1443 1444 if (readResult <= 0) 1445 return (status_t)readResult; 1446 1447 return B_OK; 1448 #endif // B_APP_FILE_INFO_IS_FAST 1449 } 1450 1451 1452 status_t 1453 GetAppIconFromAttr(BFile* file, BBitmap* icon, icon_size which) 1454 { 1455 // This call is a performance improvement that 1456 // avoids using the BAppFileInfo API when retrieving the 1457 // app icons -- the call is expensive because by default 1458 // the resource fork is scanned to read the icons 1459 1460 //#ifdef B_APP_FILE_INFO_IS_FAST 1461 BAppFileInfo appFileInfo(file); 1462 return appFileInfo.GetIcon(icon, which); 1463 //#else 1464 // 1465 // const char* attrName = kAttrIcon; 1466 // uint32 type = B_VECTOR_ICON_TYPE; 1467 // 1468 // // try vector icon 1469 // attr_info ainfo; 1470 // status_t result = file->GetAttrInfo(attrName, &ainfo); 1471 // 1472 // if (result == B_OK) { 1473 // uint8 buffer[ainfo.size]; 1474 // ssize_t readResult = file->ReadAttr(attrName, type, 0, buffer, 1475 // ainfo.size); 1476 // if (readResult == ainfo.size) { 1477 // if (BIconUtils::GetVectorIcon(buffer, ainfo.size, icon) == B_OK) 1478 // return B_OK; 1479 // } 1480 // } 1481 // 1482 // // try again with R5 icons 1483 // attrName = which == B_LARGE_ICON ? kAttrLargeIcon : kAttrMiniIcon; 1484 // type = which == B_LARGE_ICON ? LARGE_ICON_TYPE : MINI_ICON_TYPE; 1485 // 1486 // result = file->GetAttrInfo(attrName, &ainfo); 1487 // if (result < B_OK) 1488 // return result; 1489 // 1490 // uint8 buffer[ainfo.size]; 1491 // 1492 // ssize_t readResult = file->ReadAttr(attrName, type, 0, buffer, ainfo.size); 1493 // if (readResult <= 0) 1494 // return (status_t)readResult; 1495 // 1496 // if (icon->ColorSpace() != B_CMAP8) 1497 // result = BIconUtils::ConvertFromCMAP8(buffer, which, which, which, icon); 1498 // else 1499 // icon->SetBits(buffer, icon->BitsLength(), 0, B_CMAP8); 1500 // 1501 // return result; 1502 //#endif // B_APP_FILE_INFO_IS_FAST 1503 } 1504 1505 1506 status_t 1507 GetFileIconFromAttr(BNode* node, BBitmap* icon, icon_size which) 1508 { 1509 // get icon from the node info 1510 BNodeInfo nodeInfo(node); 1511 return nodeInfo.GetIcon(icon, which); 1512 } 1513 1514 1515 // #pragma mark - PrintToStream 1516 1517 1518 void 1519 PrintToStream(rgb_color color) 1520 { 1521 printf("r:%x, g:%x, b:%x, a:%x\n", 1522 color.red, color.green, color.blue, color.alpha); 1523 } 1524 1525 1526 // #pragma mark - EachMenuItem 1527 1528 1529 extern BMenuItem* 1530 EachMenuItem(BMenu* menu, bool recursive, BMenuItem* (*func)(BMenuItem *)) 1531 { 1532 int32 count = menu->CountItems(); 1533 for (int32 index = 0; index < count; index++) { 1534 BMenuItem* item = menu->ItemAt(index); 1535 BMenuItem* newItem = (func)(item); 1536 if (newItem != NULL) 1537 return newItem; 1538 1539 if (recursive) { 1540 BMenu* submenu = menu->SubmenuAt(index); 1541 if (submenu != NULL) 1542 return EachMenuItem(submenu, true, func); 1543 } 1544 } 1545 1546 return NULL; 1547 } 1548 1549 1550 extern const BMenuItem* 1551 EachMenuItem(const BMenu* menu, bool recursive, 1552 BMenuItem* (*func)(const BMenuItem *)) 1553 { 1554 int32 count = menu->CountItems(); 1555 for (int32 index = 0; index < count; index++) { 1556 BMenuItem* item = menu->ItemAt(index); 1557 BMenuItem* newItem = (func)(item); 1558 if (newItem != NULL) 1559 return newItem; 1560 1561 if (recursive) { 1562 BMenu* submenu = menu->SubmenuAt(index); 1563 if (submenu != NULL) 1564 return EachMenuItem(submenu, true, func); 1565 } 1566 } 1567 1568 return NULL; 1569 } 1570 1571 1572 // #pragma mark - PositionPassingMenuItem 1573 1574 1575 PositionPassingMenuItem::PositionPassingMenuItem(const char* title, 1576 BMessage* message, char shortcut, uint32 modifiers) 1577 : 1578 BMenuItem(title, message, shortcut, modifiers) 1579 { 1580 } 1581 1582 1583 PositionPassingMenuItem::PositionPassingMenuItem(BMenu* menu, BMessage* message) 1584 : 1585 BMenuItem(menu, message) 1586 { 1587 } 1588 1589 1590 PositionPassingMenuItem::PositionPassingMenuItem(BMessage* data) 1591 : 1592 BMenuItem(data) 1593 { 1594 } 1595 1596 1597 BArchivable* 1598 PositionPassingMenuItem::Instantiate(BMessage* data) 1599 { 1600 if (validate_instantiation(data, "PositionPassingMenuItem")) 1601 return new PositionPassingMenuItem(data); 1602 1603 return NULL; 1604 } 1605 1606 1607 status_t 1608 PositionPassingMenuItem::Invoke(BMessage* message) 1609 { 1610 if (Menu() == NULL) 1611 return B_ERROR; 1612 1613 if (!IsEnabled()) 1614 return B_ERROR; 1615 1616 if (message == NULL) 1617 message = Message(); 1618 1619 if (message == NULL) 1620 return B_BAD_VALUE; 1621 1622 BMessage clone(*message); 1623 clone.AddInt32("index", Menu()->IndexOf(this)); 1624 clone.AddInt64("when", system_time()); 1625 clone.AddPointer("source", this); 1626 1627 // embed the invoke location of the menu so that we can create 1628 // a new folder, etc. on the spot 1629 BMenu* menu = Menu(); 1630 1631 for (;;) { 1632 if (!menu->Supermenu()) 1633 break; 1634 1635 menu = menu->Supermenu(); 1636 } 1637 1638 // use the window position only, if the item was invoked from the menu 1639 // menu->Window() points to the window the item was invoked from 1640 if (dynamic_cast<BContainerWindow*>(menu->Window()) == NULL) { 1641 AutoLocker<BLooper> lock(menu->Looper()); 1642 if (lock.IsLocked()) { 1643 BPoint invokeOrigin(menu->Window()->Frame().LeftTop()); 1644 clone.AddPoint("be:invoke_origin", invokeOrigin); 1645 } 1646 } 1647 1648 return BInvoker::Invoke(&clone); 1649 } 1650 1651 1652 // #pragma mark - BPrivate functions 1653 1654 1655 bool 1656 BootedInSafeMode() 1657 { 1658 const char* safeMode = getenv("SAFEMODE"); 1659 return (safeMode && strcmp(safeMode, "yes") == 0); 1660 } 1661 1662 1663 float 1664 ComputeTypeAheadScore(const char* text, const char* match, bool wordMode) 1665 { 1666 // highest score: exact match 1667 const char* found = strcasestr(text, match); 1668 if (found != NULL) { 1669 if (found == text) 1670 return kExactMatchScore; 1671 1672 return 1.f / (found - text); 1673 } 1674 1675 // there was no exact match 1676 1677 // second best: all characters at word beginnings 1678 if (wordMode) { 1679 float score = 0; 1680 for (int32 j = 0, k = 0; match[j]; j++) { 1681 while (text[k] 1682 && tolower(text[k]) != tolower(match[j])) { 1683 k++; 1684 } 1685 if (text[k] == '\0') { 1686 score = 0; 1687 break; 1688 } 1689 1690 bool wordStart = k == 0 || isspace(text[k - 1]); 1691 if (wordStart) 1692 score++; 1693 if (j > 0) { 1694 bool wordEnd = !text[k + 1] || isspace(text[k + 1]); 1695 if (wordEnd) 1696 score += 0.3; 1697 if (match[j - 1] == text[k - 1]) 1698 score += 0.7; 1699 } 1700 1701 score += 1.f / (k + 1); 1702 k++; 1703 } 1704 1705 return score; 1706 } 1707 1708 return -1; 1709 } 1710 1711 1712 // #pragma mark - throw on error functions. 1713 1714 1715 void 1716 _ThrowOnError(status_t result, const char* DEBUG_ONLY(file), 1717 int32 DEBUG_ONLY(line)) 1718 { 1719 if (result != B_OK) { 1720 PRINT(("%s at %s:%d\n", strerror(result), file, (int)line)); 1721 throw result; 1722 } 1723 } 1724 1725 1726 void 1727 _ThrowIfNotSize(ssize_t size, const char* DEBUG_ONLY(file), 1728 int32 DEBUG_ONLY(line)) 1729 { 1730 if (size < B_OK) { 1731 PRINT(("%s at %s:%d\n", strerror((status_t)size), file, (int)line)); 1732 throw (status_t)size; 1733 } 1734 } 1735 1736 1737 void 1738 _ThrowOnAssert(bool success, const char* DEBUG_ONLY(file), 1739 int32 DEBUG_ONLY(line)) 1740 { 1741 if (!success) { 1742 PRINT(("Assert failed at %s:%d\n", file, (int)line)); 1743 throw B_ERROR; 1744 } 1745 } 1746 1747 } // namespace BPrivate 1748