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 <Mime.h> 53 #include <Node.h> 54 #include <NodeInfo.h> 55 #include <OS.h> 56 #include <PopUpMenu.h> 57 #include <Region.h> 58 #include <StorageDefs.h> 59 #include <TextView.h> 60 #include <TranslatorFormats.h> 61 #include <TranslatorRoster.h> 62 #include <TranslationUtils.h> 63 #include <TypeConstants.h> 64 #include <View.h> 65 #include <Volume.h> 66 #include <VolumeRoster.h> 67 #include <Window.h> 68 69 #include "Attributes.h" 70 #include "Commands.h" 71 #include "ContainerWindow.h" 72 #include "FSUtils.h" 73 #include "MimeTypes.h" 74 #include "Model.h" 75 #include "PoseView.h" 76 77 78 #ifdef B_XXL_ICON 79 # undef B_XXL_ICON 80 #endif 81 #define B_XXL_ICON 128 82 83 #ifndef _IMPEXP_BE 84 # define _IMPEXP_BE 85 #endif 86 extern _IMPEXP_BE const uint32 LARGE_ICON_TYPE; 87 extern _IMPEXP_BE const uint32 MINI_ICON_TYPE; 88 89 90 FILE* logFile = NULL; 91 92 static const float kMinSeparatorStubX = 10; 93 static const float kStubToStringSlotX = 5; 94 95 96 namespace BPrivate { 97 98 const float kExactMatchScore = INFINITY; 99 100 101 bool gLocalizedNamePreferred; 102 103 104 bool 105 SecondaryMouseButtonDown(int32 modifiers, int32 buttons) 106 { 107 return (buttons & B_SECONDARY_MOUSE_BUTTON) != 0 108 || ((buttons & B_PRIMARY_MOUSE_BUTTON) != 0 109 && (modifiers & B_CONTROL_KEY) != 0); 110 } 111 112 113 uint32 114 HashString(const char* string, uint32 seed) 115 { 116 char ch; 117 uint32 hash = seed; 118 119 while((ch = *string++) != 0) { 120 hash = (hash << 7) ^ (hash >> 24); 121 hash ^= ch; 122 } 123 hash ^= hash << 12; 124 125 return hash; 126 } 127 128 129 uint32 130 AttrHashString(const char* string, uint32 type) 131 { 132 char c; 133 uint32 hash = 0; 134 135 while((c = *string++) != 0) { 136 hash = (hash << 7) ^ (hash >> 24); 137 hash ^= c; 138 } 139 hash ^= hash << 12; 140 141 hash &= ~0xff; 142 hash |= type; 143 144 return hash; 145 } 146 147 148 bool 149 ValidateStream(BMallocIO* stream, uint32 key, int32 version) 150 { 151 uint32 testKey; 152 int32 testVersion; 153 154 if (stream->Read(&testKey, sizeof(uint32)) <= 0 155 || stream->Read(&testVersion, sizeof(int32)) <= 0) { 156 return false; 157 } 158 159 return testKey == key && testVersion == version; 160 } 161 162 163 void 164 DisallowFilenameKeys(BTextView* textView) 165 { 166 textView->DisallowChar('/'); 167 } 168 169 170 void 171 DisallowMetaKeys(BTextView* textView) 172 { 173 textView->DisallowChar(B_TAB); 174 textView->DisallowChar(B_ESCAPE); 175 textView->DisallowChar(B_INSERT); 176 textView->DisallowChar(B_DELETE); 177 textView->DisallowChar(B_HOME); 178 textView->DisallowChar(B_END); 179 textView->DisallowChar(B_PAGE_UP); 180 textView->DisallowChar(B_PAGE_DOWN); 181 textView->DisallowChar(B_FUNCTION_KEY); 182 } 183 184 185 PeriodicUpdatePoses::PeriodicUpdatePoses() 186 : 187 fPoseList(20, true) 188 { 189 fLock = new Benaphore("PeriodicUpdatePoses"); 190 } 191 192 193 PeriodicUpdatePoses::~PeriodicUpdatePoses() 194 { 195 fLock->Lock(); 196 fPoseList.MakeEmpty(); 197 delete fLock; 198 } 199 200 201 void 202 PeriodicUpdatePoses::AddPose(BPose* pose, BPoseView* poseView, 203 PeriodicUpdateCallback callback, void* cookie) 204 { 205 periodic_pose* periodic = new periodic_pose; 206 periodic->pose = pose; 207 periodic->pose_view = poseView; 208 periodic->callback = callback; 209 periodic->cookie = cookie; 210 fPoseList.AddItem(periodic); 211 } 212 213 214 bool 215 PeriodicUpdatePoses::RemovePose(BPose* pose, void** cookie) 216 { 217 int32 count = fPoseList.CountItems(); 218 for (int32 index = 0; index < count; index++) { 219 if (fPoseList.ItemAt(index)->pose == pose) { 220 if (!fLock->Lock()) 221 return false; 222 223 periodic_pose* periodic = fPoseList.RemoveItemAt(index); 224 if (cookie) 225 *cookie = periodic->cookie; 226 delete periodic; 227 fLock->Unlock(); 228 return true; 229 } 230 } 231 232 return false; 233 } 234 235 236 void 237 PeriodicUpdatePoses::DoPeriodicUpdate(bool forceRedraw) 238 { 239 if (!fLock->Lock()) 240 return; 241 242 int32 count = fPoseList.CountItems(); 243 for (int32 index = 0; index < count; index++) { 244 periodic_pose* periodic = fPoseList.ItemAt(index); 245 if ((periodic->callback(periodic->pose, periodic->cookie) 246 || forceRedraw) && periodic->pose_view->LockLooper()) { 247 periodic->pose_view->UpdateIcon(periodic->pose); 248 periodic->pose_view->UnlockLooper(); 249 } 250 } 251 252 fLock->Unlock(); 253 } 254 255 256 PeriodicUpdatePoses gPeriodicUpdatePoses; 257 258 } // namespace BPrivate 259 260 261 void 262 PoseInfo::EndianSwap(void* castToThis) 263 { 264 PoseInfo* self = (PoseInfo*)castToThis; 265 266 PRINT(("swapping PoseInfo\n")); 267 268 STATIC_ASSERT(sizeof(ino_t) == sizeof(int64)); 269 self->fInitedDirectory = SwapInt64(self->fInitedDirectory); 270 swap_data(B_POINT_TYPE, &self->fLocation, sizeof(BPoint), B_SWAP_ALWAYS); 271 272 // do a sanity check on the icon position 273 if (self->fLocation.x < -20000 || self->fLocation.x > 20000 274 || self->fLocation.y < -20000 || self->fLocation.y > 20000) { 275 // position out of range, force autoplcemement 276 PRINT((" rejecting icon position out of range\n")); 277 self->fInitedDirectory = -1LL; 278 self->fLocation = BPoint(0, 0); 279 } 280 } 281 282 283 void 284 PoseInfo::PrintToStream() 285 { 286 PRINT(("%s, inode:%" B_PRIx64 ", location %f %f\n", 287 fInvisible ? "hidden" : "visible", 288 fInitedDirectory, fLocation.x, fLocation.y)); 289 } 290 291 292 // #pragma mark - ExtendedPoseInfo 293 294 295 size_t 296 ExtendedPoseInfo::Size() const 297 { 298 return sizeof(ExtendedPoseInfo) + fNumFrames * sizeof(FrameLocation); 299 } 300 301 302 size_t 303 ExtendedPoseInfo::Size(int32 count) 304 { 305 return sizeof(ExtendedPoseInfo) + count * sizeof(FrameLocation); 306 } 307 308 309 size_t 310 ExtendedPoseInfo::SizeWithHeadroom() const 311 { 312 return sizeof(ExtendedPoseInfo) + (fNumFrames + 1) * sizeof(FrameLocation); 313 } 314 315 316 size_t 317 ExtendedPoseInfo::SizeWithHeadroom(size_t oldSize) 318 { 319 int32 count = (ssize_t)oldSize - (ssize_t)sizeof(ExtendedPoseInfo); 320 if (count > 0) 321 count /= sizeof(FrameLocation); 322 else 323 count = 0; 324 325 return Size(count + 1); 326 } 327 328 329 bool 330 ExtendedPoseInfo::HasLocationForFrame(BRect frame) const 331 { 332 for (int32 index = 0; index < fNumFrames; index++) { 333 if (fLocations[index].fFrame == frame) 334 return true; 335 } 336 337 return false; 338 } 339 340 341 BPoint 342 ExtendedPoseInfo::LocationForFrame(BRect frame) const 343 { 344 for (int32 index = 0; index < fNumFrames; index++) { 345 if (fLocations[index].fFrame == frame) 346 return fLocations[index].fLocation; 347 } 348 349 TRESPASS(); 350 return BPoint(0, 0); 351 } 352 353 354 bool 355 ExtendedPoseInfo::SetLocationForFrame(BPoint newLocation, BRect frame) 356 { 357 for (int32 index = 0; index < fNumFrames; index++) { 358 if (fLocations[index].fFrame == frame) { 359 if (fLocations[index].fLocation == newLocation) 360 return false; 361 362 fLocations[index].fLocation = newLocation; 363 return true; 364 } 365 } 366 367 fLocations[fNumFrames].fFrame = frame; 368 fLocations[fNumFrames].fLocation = newLocation; 369 fLocations[fNumFrames].fWorkspaces = 0xffffffff; 370 fNumFrames++; 371 372 return true; 373 } 374 375 376 void 377 ExtendedPoseInfo::EndianSwap(void* castToThis) 378 { 379 ExtendedPoseInfo* self = (ExtendedPoseInfo *)castToThis; 380 381 PRINT(("swapping ExtendedPoseInfo\n")); 382 383 self->fWorkspaces = SwapUInt32(self->fWorkspaces); 384 self->fNumFrames = SwapInt32(self->fNumFrames); 385 386 for (int32 index = 0; index < self->fNumFrames; index++) { 387 swap_data(B_POINT_TYPE, &self->fLocations[index].fLocation, 388 sizeof(BPoint), B_SWAP_ALWAYS); 389 390 if (self->fLocations[index].fLocation.x < -20000 391 || self->fLocations[index].fLocation.x > 20000 392 || self->fLocations[index].fLocation.y < -20000 393 || self->fLocations[index].fLocation.y > 20000) { 394 // position out of range, force autoplcemement 395 PRINT((" rejecting icon position out of range\n")); 396 self->fLocations[index].fLocation = BPoint(0, 0); 397 } 398 swap_data(B_RECT_TYPE, &self->fLocations[index].fFrame, 399 sizeof(BRect), B_SWAP_ALWAYS); 400 } 401 } 402 403 404 void 405 ExtendedPoseInfo::PrintToStream() 406 { 407 } 408 409 410 // #pragma mark - OffscreenBitmap 411 412 413 OffscreenBitmap::OffscreenBitmap(BRect frame) 414 : 415 fBitmap(NULL) 416 { 417 NewBitmap(frame); 418 } 419 420 421 OffscreenBitmap::OffscreenBitmap() 422 : 423 fBitmap(NULL) 424 { 425 } 426 427 428 OffscreenBitmap::~OffscreenBitmap() 429 { 430 delete fBitmap; 431 } 432 433 434 void 435 OffscreenBitmap::NewBitmap(BRect bounds) 436 { 437 delete fBitmap; 438 fBitmap = new(std::nothrow) BBitmap(bounds, B_RGB32, true); 439 if (fBitmap != NULL && fBitmap->Lock()) { 440 BView* view = new BView(fBitmap->Bounds(), "", B_FOLLOW_NONE, 0); 441 fBitmap->AddChild(view); 442 443 BRect clipRect = view->Bounds(); 444 BRegion newClip; 445 newClip.Set(clipRect); 446 view->ConstrainClippingRegion(&newClip); 447 448 fBitmap->Unlock(); 449 } else { 450 delete fBitmap; 451 fBitmap = NULL; 452 } 453 } 454 455 456 BView* 457 OffscreenBitmap::BeginUsing(BRect frame) 458 { 459 if (!fBitmap || fBitmap->Bounds() != frame) 460 NewBitmap(frame); 461 462 fBitmap->Lock(); 463 return View(); 464 } 465 466 467 void 468 OffscreenBitmap::DoneUsing() 469 { 470 fBitmap->Unlock(); 471 } 472 473 474 BBitmap* 475 OffscreenBitmap::Bitmap() const 476 { 477 ASSERT(fBitmap); 478 ASSERT(fBitmap->IsLocked()); 479 return fBitmap; 480 } 481 482 483 BView* 484 OffscreenBitmap::View() const 485 { 486 ASSERT(fBitmap); 487 return fBitmap->ChildAt(0); 488 } 489 490 491 // #pragma mark - BPrivate functions 492 493 494 namespace BPrivate { 495 496 // Changes the alpha value of the given bitmap to create a nice 497 // horizontal fade out in the specified region. 498 // "from" is always transparent, "to" opaque. 499 void 500 FadeRGBA32Horizontal(uint32* bits, int32 width, int32 height, int32 from, 501 int32 to) 502 { 503 // check parameters 504 if (width < 0 || height < 0 || from < 0 || to < 0) 505 return; 506 507 float change = 1.f / (to - from); 508 if (from > to) { 509 int32 temp = from; 510 from = to; 511 to = temp; 512 } 513 514 for (int32 y = 0; y < height; y++) { 515 float alpha = change > 0 ? 0.0f : 1.0f; 516 517 for (int32 x = from; x <= to; x++) { 518 if (bits[x] & 0xff000000) { 519 uint32 a = uint32((bits[x] >> 24) * alpha); 520 bits[x] = (bits[x] & 0x00ffffff) | (a << 24); 521 } 522 alpha += change; 523 } 524 bits += width; 525 } 526 } 527 528 529 /*! Changes the alpha value of the given bitmap to create a nice 530 vertical fade out in the specified region. 531 "from" is always transparent, "to" opaque. 532 */ 533 void 534 FadeRGBA32Vertical(uint32* bits, int32 width, int32 height, int32 from, 535 int32 to) 536 { 537 // check parameters 538 if (width < 0 || height < 0 || from < 0 || to < 0) 539 return; 540 541 if (from > to) 542 bits += width * (height - (from - to)); 543 544 float change = 1.f / (to - from); 545 if (from > to) { 546 int32 temp = from; 547 from = to; 548 to = temp; 549 } 550 551 float alpha = change > 0 ? 0.0f : 1.0f; 552 553 for (int32 y = from; y <= to; y++) { 554 for (int32 x = 0; x < width; x++) { 555 if (bits[x] & 0xff000000) { 556 uint32 a = uint32((bits[x] >> 24) * alpha); 557 bits[x] = (bits[x] & 0x00ffffff) | (a << 24); 558 } 559 } 560 alpha += change; 561 bits += width; 562 } 563 } 564 565 566 } // namespace BPrivate 567 568 569 // #pragma mark - DraggableIcon 570 571 572 DraggableIcon::DraggableIcon(BRect rect, const char* name, 573 const char* type, icon_size which, const BMessage* message, 574 BMessenger target, uint32 resizingMode, uint32 flags) 575 : 576 BView(rect, name, resizingMode, flags), 577 fMessage(*message), 578 fTarget(target) 579 { 580 fBitmap = new BBitmap(Bounds(), kDefaultIconDepth); 581 BMimeType mime(type); 582 status_t result = mime.GetIcon(fBitmap, which); 583 ASSERT(mime.IsValid()); 584 if (result != B_OK) { 585 PRINT(("failed to get icon for %s, %s\n", type, strerror(result))); 586 BMimeType mime(B_FILE_MIMETYPE); 587 ASSERT(mime.IsInstalled()); 588 mime.GetIcon(fBitmap, which); 589 } 590 } 591 592 593 DraggableIcon::~DraggableIcon() 594 { 595 delete fBitmap; 596 } 597 598 599 void 600 DraggableIcon::SetTarget(BMessenger target) 601 { 602 fTarget = target; 603 } 604 605 606 BRect 607 DraggableIcon::PreferredRect(BPoint offset, icon_size which) 608 { 609 BRect rect(0, 0, which - 1, which - 1); 610 rect.OffsetTo(offset); 611 return rect; 612 } 613 614 615 void 616 DraggableIcon::AttachedToWindow() 617 { 618 AdoptParentColors(); 619 } 620 621 622 void 623 DraggableIcon::MouseDown(BPoint point) 624 { 625 if (!DragStarted(&fMessage)) 626 return; 627 628 BRect rect(Bounds()); 629 BBitmap* dragBitmap = new BBitmap(rect, B_RGBA32, true); 630 dragBitmap->Lock(); 631 BView* view = new BView(dragBitmap->Bounds(), "", B_FOLLOW_NONE, 0); 632 dragBitmap->AddChild(view); 633 view->SetOrigin(0, 0); 634 BRect clipRect(view->Bounds()); 635 BRegion newClip; 636 newClip.Set(clipRect); 637 view->ConstrainClippingRegion(&newClip); 638 639 // Transparent draw magic 640 view->SetHighColor(0, 0, 0, 0); 641 view->FillRect(view->Bounds()); 642 view->SetDrawingMode(B_OP_ALPHA); 643 view->SetHighColor(0, 0, 0, 128); 644 // set the level of transparency by value 645 view->SetBlendingMode(B_CONSTANT_ALPHA, B_ALPHA_COMPOSITE); 646 view->DrawBitmap(fBitmap); 647 view->Sync(); 648 dragBitmap->Unlock(); 649 DragMessage(&fMessage, dragBitmap, B_OP_ALPHA, point, fTarget.Target(0)); 650 } 651 652 653 bool 654 DraggableIcon::DragStarted(BMessage*) 655 { 656 return true; 657 } 658 659 660 void 661 DraggableIcon::Draw(BRect) 662 { 663 SetDrawingMode(B_OP_ALPHA); 664 SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY); 665 DrawBitmap(fBitmap); 666 } 667 668 669 // #pragma mark - FlickerFreeStringView 670 671 672 FlickerFreeStringView::FlickerFreeStringView(BRect bounds, const char* name, 673 const char* text, uint32 resizingMode, uint32 flags) 674 : 675 BStringView(bounds, name, text, resizingMode, flags), 676 fBitmap(NULL), 677 fViewColor(ViewColor()), 678 fLowColor(LowColor()), 679 fOriginalBitmap(NULL) 680 { 681 } 682 683 684 FlickerFreeStringView::FlickerFreeStringView(BRect bounds, const char* name, 685 const char* text, BBitmap* inBitmap, uint32 resizingMode, uint32 flags) 686 : 687 BStringView(bounds, name, text, resizingMode, flags), 688 fBitmap(NULL), 689 fViewColor(ViewColor()), 690 fLowColor(LowColor()), 691 fOriginalBitmap(inBitmap) 692 { 693 } 694 695 696 FlickerFreeStringView::~FlickerFreeStringView() 697 { 698 delete fBitmap; 699 } 700 701 702 void 703 FlickerFreeStringView::Draw(BRect) 704 { 705 BRect bounds(Bounds()); 706 if (fBitmap == NULL) 707 fBitmap = new OffscreenBitmap(Bounds()); 708 709 BView* offscreen = fBitmap->BeginUsing(bounds); 710 711 if (Parent() != NULL) { 712 fViewColor = Parent()->ViewColor(); 713 fLowColor = Parent()->ViewColor(); 714 } 715 716 offscreen->SetViewColor(fViewColor); 717 offscreen->SetHighColor(HighColor()); 718 offscreen->SetLowColor(fLowColor); 719 720 BFont font; 721 GetFont(&font); 722 offscreen->SetFont(&font); 723 724 offscreen->Sync(); 725 if (fOriginalBitmap != NULL) 726 offscreen->DrawBitmap(fOriginalBitmap, Frame(), bounds); 727 else 728 offscreen->FillRect(bounds, B_SOLID_LOW); 729 730 if (Text() != NULL) { 731 BPoint loc; 732 733 font_height height; 734 GetFontHeight(&height); 735 736 edge_info eInfo; 737 switch (Alignment()) { 738 case B_ALIGN_LEFT: 739 case B_ALIGN_HORIZONTAL_UNSET: 740 case B_ALIGN_USE_FULL_WIDTH: 741 { 742 // If the first char has a negative left edge give it 743 // some more room by shifting that much more to the right. 744 font.GetEdges(Text(), 1, &eInfo); 745 loc.x = bounds.left + (2 - eInfo.left); 746 break; 747 } 748 749 case B_ALIGN_CENTER: 750 { 751 float width = StringWidth(Text()); 752 float center = (bounds.right - bounds.left) / 2; 753 loc.x = center - (width/2); 754 break; 755 } 756 757 case B_ALIGN_RIGHT: 758 { 759 float width = StringWidth(Text()); 760 loc.x = bounds.right - width - 2; 761 break; 762 } 763 } 764 loc.y = bounds.bottom - (1 + height.descent); 765 offscreen->DrawString(Text(), loc); 766 } 767 offscreen->Sync(); 768 SetDrawingMode(B_OP_COPY); 769 DrawBitmap(fBitmap->Bitmap()); 770 fBitmap->DoneUsing(); 771 } 772 773 774 void 775 FlickerFreeStringView::AttachedToWindow() 776 { 777 _inherited::AttachedToWindow(); 778 if (Parent() != NULL) { 779 fViewColor = Parent()->ViewColor(); 780 fLowColor = Parent()->ViewColor(); 781 } 782 SetViewColor(B_TRANSPARENT_32_BIT); 783 SetLowColor(B_TRANSPARENT_32_BIT); 784 } 785 786 787 void 788 FlickerFreeStringView::SetViewColor(rgb_color color) 789 { 790 if (fViewColor != color) { 791 fViewColor = color; 792 Invalidate(); 793 } 794 _inherited::SetViewColor(B_TRANSPARENT_32_BIT); 795 } 796 797 798 void 799 FlickerFreeStringView::SetLowColor(rgb_color color) 800 { 801 if (fLowColor != color) { 802 fLowColor = color; 803 Invalidate(); 804 } 805 _inherited::SetLowColor(B_TRANSPARENT_32_BIT); 806 } 807 808 809 // #pragma mark - TitledSeparatorItem 810 811 812 TitledSeparatorItem::TitledSeparatorItem(const char* label) 813 : 814 BMenuItem(label, 0) 815 { 816 _inherited::SetEnabled(false); 817 } 818 819 820 TitledSeparatorItem::~TitledSeparatorItem() 821 { 822 } 823 824 825 void 826 TitledSeparatorItem::SetEnabled(bool) 827 { 828 // leave disabled 829 } 830 831 832 void 833 TitledSeparatorItem::GetContentSize(float* width, float* height) 834 { 835 _inherited::GetContentSize(width, height); 836 } 837 838 839 inline rgb_color 840 ShiftMenuBackgroundColor(float by) 841 { 842 return tint_color(ui_color(B_MENU_BACKGROUND_COLOR), by); 843 } 844 845 846 void 847 TitledSeparatorItem::Draw() 848 { 849 BRect frame(Frame()); 850 851 BMenu* parent = Menu(); 852 ASSERT(parent != NULL); 853 854 menu_info minfo; 855 get_menu_info(&minfo); 856 857 if (minfo.separator > 0) { 858 frame.left += 10; 859 frame.right -= 10; 860 } else { 861 frame.left += 1; 862 frame.right -= 1; 863 } 864 865 float startX = frame.left; 866 float endX = frame.right; 867 868 float maxStringWidth = endX - startX - (2 * kMinSeparatorStubX 869 + 2 * kStubToStringSlotX); 870 871 // ToDo: 872 // handle case where maxStringWidth turns out negative here 873 874 BString truncatedLabel(Label()); 875 parent->TruncateString(&truncatedLabel, B_TRUNCATE_END, maxStringWidth); 876 877 maxStringWidth = parent->StringWidth(truncatedLabel.String()); 878 879 // first calculate the length of the stub part of the 880 // divider line, so we can use it for secondStartX 881 float firstEndX = ((endX - startX) - maxStringWidth) / 2 882 - kStubToStringSlotX; 883 if (firstEndX < 0) 884 firstEndX = 0; 885 886 float secondStartX = endX - firstEndX; 887 888 // now finish calculating firstEndX 889 firstEndX += startX; 890 891 parent->PushState(); 892 893 int32 y = (int32) (frame.top + (frame.bottom - frame.top) / 2); 894 895 parent->BeginLineArray(minfo.separator == 2 ? 6 : 4); 896 parent->AddLine(BPoint(frame.left, y), BPoint(firstEndX, y), 897 ShiftMenuBackgroundColor(B_DARKEN_1_TINT)); 898 parent->AddLine(BPoint(secondStartX, y), BPoint(frame.right, y), 899 ShiftMenuBackgroundColor(B_DARKEN_1_TINT)); 900 901 if (minfo.separator == 2) { 902 y++; 903 frame.left++; 904 frame.right--; 905 parent->AddLine(BPoint(frame.left,y), BPoint(firstEndX, y), 906 ShiftMenuBackgroundColor(B_DARKEN_1_TINT)); 907 parent->AddLine(BPoint(secondStartX,y), BPoint(frame.right, y), 908 ShiftMenuBackgroundColor(B_DARKEN_1_TINT)); 909 } 910 y++; 911 if (minfo.separator == 2) { 912 frame.left++; 913 frame.right--; 914 } 915 parent->AddLine(BPoint(frame.left, y), BPoint(firstEndX, y), 916 ShiftMenuBackgroundColor(B_DARKEN_1_TINT)); 917 parent->AddLine(BPoint(secondStartX, y), BPoint(frame.right, y), 918 ShiftMenuBackgroundColor(B_DARKEN_1_TINT)); 919 920 parent->EndLineArray(); 921 922 font_height finfo; 923 parent->GetFontHeight(&finfo); 924 925 parent->SetLowColor(parent->ViewColor()); 926 BPoint loc(firstEndX + kStubToStringSlotX, 927 ContentLocation().y + finfo.ascent); 928 929 parent->MovePenTo(loc + BPoint(1, 1)); 930 parent->SetHighColor(ShiftMenuBackgroundColor(B_DARKEN_1_TINT)); 931 parent->DrawString(truncatedLabel.String()); 932 933 parent->MovePenTo(loc); 934 parent->SetHighColor(ShiftMenuBackgroundColor(B_DISABLED_LABEL_TINT)); 935 parent->DrawString(truncatedLabel.String()); 936 937 parent->PopState(); 938 } 939 940 941 // #pragma mark - ShortcutFilter 942 943 944 ShortcutFilter::ShortcutFilter(uint32 shortcutKey, uint32 shortcutModifier, 945 uint32 shortcutWhat, BHandler* target) 946 : 947 BMessageFilter(B_KEY_DOWN), 948 fShortcutKey(shortcutKey), 949 fShortcutModifier(shortcutModifier), 950 fShortcutWhat(shortcutWhat), 951 fTarget(target) 952 { 953 } 954 955 956 filter_result 957 ShortcutFilter::Filter(BMessage* message, BHandler**) 958 { 959 if (message->what == B_KEY_DOWN) { 960 uint32 modifiers; 961 uint32 rawKeyChar = 0; 962 uint8 byte = 0; 963 int32 key = 0; 964 965 if (message->FindInt32("modifiers", (int32*)&modifiers) != B_OK 966 || message->FindInt32("raw_char", (int32*)&rawKeyChar) != B_OK 967 || message->FindInt8("byte", (int8*)&byte) != B_OK 968 || message->FindInt32("key", &key) != B_OK) { 969 return B_DISPATCH_MESSAGE; 970 } 971 972 modifiers &= B_SHIFT_KEY | B_COMMAND_KEY | B_CONTROL_KEY 973 | B_OPTION_KEY | B_MENU_KEY; 974 // strip caps lock, etc. 975 976 if (modifiers == fShortcutModifier && rawKeyChar == fShortcutKey) { 977 fTarget->Looper()->PostMessage(fShortcutWhat, fTarget); 978 return B_SKIP_MESSAGE; 979 } 980 } 981 982 // let others deal with this 983 return B_DISPATCH_MESSAGE; 984 } 985 986 987 // #pragma mark - BPrivate functions 988 989 990 namespace BPrivate { 991 992 void 993 EmbedUniqueVolumeInfo(BMessage* message, const BVolume* volume) 994 { 995 BDirectory rootDirectory; 996 time_t created; 997 fs_info info; 998 999 if (volume->GetRootDirectory(&rootDirectory) == B_OK 1000 && rootDirectory.GetCreationTime(&created) == B_OK 1001 && fs_stat_dev(volume->Device(), &info) == 0) { 1002 message->AddInt64("creationDate", created); 1003 message->AddInt64("capacity", volume->Capacity()); 1004 message->AddString("deviceName", info.device_name); 1005 message->AddString("volumeName", info.volume_name); 1006 message->AddString("fshName", info.fsh_name); 1007 } 1008 } 1009 1010 1011 status_t 1012 MatchArchivedVolume(BVolume* volume, const BMessage* message, int32 index) 1013 { 1014 int64 created64; 1015 off_t capacity; 1016 1017 if (message->FindInt64("creationDate", index, &created64) != B_OK) { 1018 int32 created32; 1019 if (message->FindInt32("creationDate", index, &created32) != B_OK) 1020 return B_ERROR; 1021 created64 = created32; 1022 } 1023 1024 time_t created = created64; 1025 1026 if (message->FindInt64("capacity", index, &capacity) != B_OK) 1027 return B_ERROR; 1028 1029 BVolumeRoster roster; 1030 BVolume tempVolume; 1031 BString deviceName; 1032 BString volumeName; 1033 BString fshName; 1034 1035 if (message->FindString("deviceName", &deviceName) == B_OK 1036 && message->FindString("volumeName", &volumeName) == B_OK 1037 && message->FindString("fshName", &fshName) == B_OK) { 1038 // New style volume identifiers: We have a couple of characteristics, 1039 // and compute a score from them. The volume with the greatest score 1040 // (if over a certain threshold) is the one we're looking for. We 1041 // pick the first volume, in case there is more than one with the 1042 // same score. 1043 dev_t foundDevice = -1; 1044 int foundScore = -1; 1045 roster.Rewind(); 1046 while (roster.GetNextVolume(&tempVolume) == B_OK) { 1047 if (tempVolume.IsPersistent() && tempVolume.KnowsQuery()) { 1048 // get creation time and fs_info 1049 BDirectory root; 1050 tempVolume.GetRootDirectory(&root); 1051 time_t cmpCreated; 1052 fs_info info; 1053 if (root.GetCreationTime(&cmpCreated) == B_OK 1054 && fs_stat_dev(tempVolume.Device(), &info) == 0) { 1055 // compute the score 1056 int score = 0; 1057 1058 // creation time 1059 if (created == cmpCreated) 1060 score += 5; 1061 1062 // capacity 1063 if (capacity == tempVolume.Capacity()) 1064 score += 4; 1065 1066 // device name 1067 if (deviceName == info.device_name) 1068 score += 3; 1069 1070 // volume name 1071 if (volumeName == info.volume_name) 1072 score += 2; 1073 1074 // fsh name 1075 if (fshName == info.fsh_name) 1076 score += 1; 1077 1078 // check score 1079 if (score >= 9 && score > foundScore) { 1080 foundDevice = tempVolume.Device(); 1081 foundScore = score; 1082 } 1083 } 1084 } 1085 } 1086 if (foundDevice >= 0) 1087 return volume->SetTo(foundDevice); 1088 } else { 1089 // Old style volume identifiers: We have only creation time and 1090 // capacity. Both must match. 1091 roster.Rewind(); 1092 while (roster.GetNextVolume(&tempVolume) == B_OK) { 1093 if (tempVolume.IsPersistent() && tempVolume.KnowsQuery()) { 1094 BDirectory root; 1095 tempVolume.GetRootDirectory(&root); 1096 time_t cmpCreated; 1097 root.GetCreationTime(&cmpCreated); 1098 if (created == cmpCreated && capacity == tempVolume.Capacity()) { 1099 *volume = tempVolume; 1100 return B_OK; 1101 } 1102 } 1103 } 1104 } 1105 1106 return B_DEV_BAD_DRIVE_NUM; 1107 } 1108 1109 1110 void 1111 StringFromStream(BString* string, BMallocIO* stream, bool endianSwap) 1112 { 1113 int32 length; 1114 stream->Read(&length, sizeof(length)); 1115 if (endianSwap) 1116 length = SwapInt32(length); 1117 1118 if (length < 0 || length > 10000) { 1119 // TODO: should fail here 1120 PRINT(("problems instatiating a string, length probably wrong %" 1121 B_PRId32 "\n", length)); 1122 return; 1123 } 1124 1125 char* buffer = string->LockBuffer(length + 1); 1126 stream->Read(buffer, (size_t)length + 1); 1127 string->UnlockBuffer(length); 1128 } 1129 1130 1131 void 1132 StringToStream(const BString* string, BMallocIO* stream) 1133 { 1134 int32 length = string->Length(); 1135 stream->Write(&length, sizeof(int32)); 1136 stream->Write(string->String(), (size_t)string->Length() + 1); 1137 } 1138 1139 1140 int32 1141 ArchiveSize(const BString* string) 1142 { 1143 return string->Length() + 1 + (ssize_t)sizeof(int32); 1144 } 1145 1146 1147 int32 1148 CountRefs(const BMessage* message) 1149 { 1150 uint32 type; 1151 int32 count; 1152 message->GetInfo("refs", &type, &count); 1153 1154 return count; 1155 } 1156 1157 1158 static entry_ref* 1159 EachEntryRefCommon(BMessage* message, entry_ref *(*func)(entry_ref*, void*), 1160 void* passThru, int32 maxCount) 1161 { 1162 uint32 type; 1163 int32 count; 1164 message->GetInfo("refs", &type, &count); 1165 1166 if (maxCount >= 0 && count > maxCount) 1167 count = maxCount; 1168 1169 for (int32 index = 0; index < count; index++) { 1170 entry_ref ref; 1171 message->FindRef("refs", index, &ref); 1172 entry_ref* newRef = (func)(&ref, passThru); 1173 if (newRef != NULL) 1174 return newRef; 1175 } 1176 1177 return NULL; 1178 } 1179 1180 1181 bool 1182 ContainsEntryRef(const BMessage* message, const entry_ref* ref) 1183 { 1184 entry_ref match; 1185 for (int32 index = 0; (message->FindRef("refs", index, &match) == B_OK); 1186 index++) { 1187 if (*ref == match) 1188 return true; 1189 } 1190 1191 return false; 1192 } 1193 1194 1195 entry_ref* 1196 EachEntryRef(BMessage* message, entry_ref* (*func)(entry_ref*, void*), 1197 void* passThru) 1198 { 1199 return EachEntryRefCommon(message, func, passThru, -1); 1200 } 1201 1202 typedef entry_ref *(*EachEntryIteratee)(entry_ref *, void *); 1203 1204 1205 const entry_ref* 1206 EachEntryRef(const BMessage* message, 1207 const entry_ref* (*func)(const entry_ref*, void*), void* passThru) 1208 { 1209 return EachEntryRefCommon(const_cast<BMessage*>(message), 1210 (EachEntryIteratee)func, passThru, -1); 1211 } 1212 1213 1214 entry_ref* 1215 EachEntryRef(BMessage* message, entry_ref* (*func)(entry_ref*, void*), 1216 void* passThru, int32 maxCount) 1217 { 1218 return EachEntryRefCommon(message, func, passThru, maxCount); 1219 } 1220 1221 1222 const entry_ref * 1223 EachEntryRef(const BMessage* message, 1224 const entry_ref *(*func)(const entry_ref *, void *), void* passThru, 1225 int32 maxCount) 1226 { 1227 return EachEntryRefCommon(const_cast<BMessage *>(message), 1228 (EachEntryIteratee)func, passThru, maxCount); 1229 } 1230 1231 1232 void 1233 TruncateLeaf(BString* string) 1234 { 1235 for (int32 index = string->Length(); index >= 0; index--) { 1236 if ((*string)[index] == '/') { 1237 string->Truncate(index + 1); 1238 return; 1239 } 1240 } 1241 } 1242 1243 1244 int64 1245 StringToScalar(const char* text) 1246 { 1247 char* end; 1248 int64 val; 1249 1250 char* buffer = new char [strlen(text) + 1]; 1251 strcpy(buffer, text); 1252 1253 if (strstr(buffer, "k") || strstr(buffer, "K")) { 1254 val = strtoll(buffer, &end, 10); 1255 val *= kKBSize; 1256 } else if (strstr(buffer, "mb") || strstr(buffer, "MB")) { 1257 val = strtoll(buffer, &end, 10); 1258 val *= kMBSize; 1259 } else if (strstr(buffer, "gb") || strstr(buffer, "GB")) { 1260 val = strtoll(buffer, &end, 10); 1261 val *= kGBSize; 1262 } else if (strstr(buffer, "byte") || strstr(buffer, "BYTE")) { 1263 val = strtoll(buffer, &end, 10); 1264 val *= kGBSize; 1265 } else { 1266 // no suffix, try plain byte conversion 1267 val = strtoll(buffer, &end, 10); 1268 } 1269 delete[] buffer; 1270 1271 return val; 1272 } 1273 1274 1275 int32 1276 ListIconSize() 1277 { 1278 static int32 sIconSize = std::max((int32)B_MINI_ICON, 1279 (int32)ceilf(B_MINI_ICON * be_plain_font->Size() / 12)); 1280 return sIconSize; 1281 } 1282 1283 1284 static BRect 1285 LineBounds(BPoint where, float length, bool vertical) 1286 { 1287 BRect rect; 1288 rect.SetLeftTop(where); 1289 rect.SetRightBottom(where + BPoint(2, 2)); 1290 if (vertical) 1291 rect.bottom = rect.top + length; 1292 else 1293 rect.right = rect.left + length; 1294 1295 return rect; 1296 } 1297 1298 1299 SeparatorLine::SeparatorLine(BPoint where, float length, bool vertical, 1300 const char* name) 1301 : 1302 BView(LineBounds(where, length, vertical), name, 1303 B_FOLLOW_LEFT | B_FOLLOW_TOP, B_WILL_DRAW) 1304 { 1305 SetViewUIColor(B_PANEL_BACKGROUND_COLOR); 1306 SetLowUIColor(B_PANEL_BACKGROUND_COLOR); 1307 } 1308 1309 1310 void 1311 SeparatorLine::Draw(BRect) 1312 { 1313 BRect bounds(Bounds()); 1314 rgb_color hiliteColor = tint_color(ViewColor(), 1.5f); 1315 1316 bool vertical = (bounds.left > bounds.right - 3); 1317 BeginLineArray(2); 1318 if (vertical) { 1319 AddLine(bounds.LeftTop(), bounds.LeftBottom(), hiliteColor); 1320 AddLine(bounds.LeftTop() + BPoint(1, 0), 1321 bounds.LeftBottom() + BPoint(1, 0), kWhite); 1322 } else { 1323 AddLine(bounds.LeftTop(), bounds.RightTop(), hiliteColor); 1324 AddLine(bounds.LeftTop() + BPoint(0, 1), 1325 bounds.RightTop() + BPoint(0, 1), kWhite); 1326 } 1327 EndLineArray(); 1328 } 1329 1330 1331 void 1332 HexDump(const void* buf, int32 length) 1333 { 1334 const int32 kBytesPerLine = 16; 1335 int32 offset; 1336 unsigned char* buffer = (unsigned char*)buf; 1337 1338 for (offset = 0; ; offset += kBytesPerLine, buffer += kBytesPerLine) { 1339 int32 remain = length; 1340 int32 index; 1341 1342 printf( "0x%06x: ", (int)offset); 1343 1344 for (index = 0; index < kBytesPerLine; index++) { 1345 if (remain-- > 0) 1346 printf("%02x%c", buffer[index], remain > 0 ? ',' : ' '); 1347 else 1348 printf(" "); 1349 } 1350 1351 remain = length; 1352 printf(" \'"); 1353 for (index = 0; index < kBytesPerLine; index++) { 1354 if (remain-- > 0) 1355 printf("%c", buffer[index] > ' ' ? buffer[index] : '.'); 1356 else 1357 printf(" "); 1358 } 1359 printf("\'\n"); 1360 1361 length -= kBytesPerLine; 1362 if (length <= 0) 1363 break; 1364 } 1365 fflush(stdout); 1366 } 1367 1368 1369 void 1370 EnableNamedMenuItem(BMenu* menu, const char* itemName, bool on) 1371 { 1372 BMenuItem* item = menu->FindItem(itemName); 1373 if (item != NULL) 1374 item->SetEnabled(on); 1375 } 1376 1377 1378 void 1379 MarkNamedMenuItem(BMenu* menu, const char* itemName, bool on) 1380 { 1381 BMenuItem* item = menu->FindItem(itemName); 1382 if (item != NULL) 1383 item->SetMarked(on); 1384 } 1385 1386 1387 void 1388 EnableNamedMenuItem(BMenu* menu, uint32 commandName, bool on) 1389 { 1390 BMenuItem* item = menu->FindItem(commandName); 1391 if (item != NULL) 1392 item->SetEnabled(on); 1393 } 1394 1395 1396 void 1397 MarkNamedMenuItem(BMenu* menu, uint32 commandName, bool on) 1398 { 1399 BMenuItem* item = menu->FindItem(commandName); 1400 if (item != NULL) 1401 item->SetMarked(on); 1402 } 1403 1404 1405 void 1406 DeleteSubmenu(BMenuItem* submenuItem) 1407 { 1408 if (submenuItem == NULL) 1409 return; 1410 1411 BMenu* menu = submenuItem->Submenu(); 1412 if (menu == NULL) 1413 return; 1414 1415 for (;;) { 1416 BMenuItem* item = menu->RemoveItem((int32)0); 1417 if (item == NULL) 1418 return; 1419 1420 delete item; 1421 } 1422 } 1423 1424 1425 status_t 1426 GetAppSignatureFromAttr(BFile* file, char* attr) 1427 { 1428 // This call is a performance improvement that 1429 // avoids using the BAppFileInfo API when retrieving the 1430 // app signature -- the call is expensive because by default 1431 // the resource fork is scanned to read the attribute 1432 1433 #ifdef B_APP_FILE_INFO_IS_FAST 1434 BAppFileInfo appFileInfo(file); 1435 return appFileInfo.GetSignature(attr); 1436 #else 1437 ssize_t readResult = file->ReadAttr(kAttrAppSignature, B_MIME_STRING_TYPE, 1438 0, attr, B_MIME_TYPE_LENGTH); 1439 1440 if (readResult <= 0) 1441 return (status_t)readResult; 1442 1443 return B_OK; 1444 #endif // B_APP_FILE_INFO_IS_FAST 1445 } 1446 1447 1448 status_t 1449 GetAppIconFromAttr(BFile* file, BBitmap* icon, icon_size which) 1450 { 1451 // This call is a performance improvement that 1452 // avoids using the BAppFileInfo API when retrieving the 1453 // app icons -- the call is expensive because by default 1454 // the resource fork is scanned to read the icons 1455 1456 //#ifdef B_APP_FILE_INFO_IS_FAST 1457 BAppFileInfo appFileInfo(file); 1458 return appFileInfo.GetIcon(icon, which); 1459 //#else 1460 // 1461 // const char* attrName = kAttrIcon; 1462 // uint32 type = B_VECTOR_ICON_TYPE; 1463 // 1464 // // try vector icon 1465 // attr_info ainfo; 1466 // status_t result = file->GetAttrInfo(attrName, &ainfo); 1467 // 1468 // if (result == B_OK) { 1469 // uint8 buffer[ainfo.size]; 1470 // ssize_t readResult = file->ReadAttr(attrName, type, 0, buffer, 1471 // ainfo.size); 1472 // if (readResult == ainfo.size) { 1473 // if (BIconUtils::GetVectorIcon(buffer, ainfo.size, icon) == B_OK) 1474 // return B_OK; 1475 // } 1476 // } 1477 // 1478 // // try again with R5 icons 1479 // attrName = which == B_LARGE_ICON ? kAttrLargeIcon : kAttrMiniIcon; 1480 // type = which == B_LARGE_ICON ? LARGE_ICON_TYPE : MINI_ICON_TYPE; 1481 // 1482 // result = file->GetAttrInfo(attrName, &ainfo); 1483 // if (result < B_OK) 1484 // return result; 1485 // 1486 // uint8 buffer[ainfo.size]; 1487 // 1488 // ssize_t readResult = file->ReadAttr(attrName, type, 0, buffer, ainfo.size); 1489 // if (readResult <= 0) 1490 // return (status_t)readResult; 1491 // 1492 // if (icon->ColorSpace() != B_CMAP8) 1493 // result = BIconUtils::ConvertFromCMAP8(buffer, which, which, which, icon); 1494 // else 1495 // icon->SetBits(buffer, icon->BitsLength(), 0, B_CMAP8); 1496 // 1497 // return result; 1498 //#endif // B_APP_FILE_INFO_IS_FAST 1499 } 1500 1501 1502 status_t 1503 GetFileIconFromAttr(Model* model, BBitmap* icon, icon_size which) 1504 { 1505 // bad input value 1506 if (model == NULL || icon == NULL) 1507 return B_BAD_VALUE; 1508 1509 // unitialized model 1510 status_t result = model->InitCheck(); 1511 if (result != B_OK) 1512 return result; 1513 1514 // unitialized icon 1515 result = icon->InitCheck(); 1516 if (result != B_OK) 1517 return result; 1518 1519 // node not open 1520 BNode* node = model->Node(); 1521 if (node == NULL) 1522 return B_BAD_VALUE; 1523 1524 // look for a thumbnail in an attribute 1525 time_t modtime; 1526 bigtime_t created; 1527 if (node->GetModificationTime(&modtime) == B_OK 1528 && node->ReadAttr(kAttrThumbnailCreationTime, B_TIME_TYPE, 0, 1529 &created, sizeof(bigtime_t)) == sizeof(bigtime_t)) { 1530 if (created > (bigtime_t)modtime) { 1531 // file has not changed, try to return an existing thumbnail 1532 attr_info attrInfo; 1533 if (node->GetAttrInfo(kAttrThumbnail, &attrInfo) == B_OK) { 1534 uint8 webpData[attrInfo.size]; 1535 if (node->ReadAttr(kAttrThumbnail, attrInfo.type, 0, 1536 webpData, attrInfo.size) == attrInfo.size) { 1537 BMemoryIO stream((const void*)webpData, attrInfo.size); 1538 BBitmap thumb(BTranslationUtils::GetBitmap(&stream)); 1539 1540 // convert thumb to icon size 1541 if (which == B_XXL_ICON) { 1542 // import icon data from attribute without resizing 1543 result = icon->ImportBits(&thumb); 1544 } else { 1545 // down-scale thumb to icon size 1546 // TODO don't make a copy, allow icon to accept views 1547 BBitmap tmp = BBitmap(icon->Bounds(), 1548 icon->ColorSpace(), true); 1549 BView view(tmp.Bounds(), "", B_FOLLOW_NONE, 1550 B_WILL_DRAW); 1551 tmp.AddChild(&view); 1552 if (view.LockLooper()) { 1553 // fill with transparent 1554 view.SetLowColor(B_TRANSPARENT_COLOR); 1555 view.FillRect(view.Bounds(), B_SOLID_LOW); 1556 // draw bitmap 1557 view.SetDrawingMode(B_OP_ALPHA); 1558 view.SetBlendingMode(B_PIXEL_ALPHA, 1559 B_ALPHA_COMPOSITE); 1560 view.DrawBitmap(&thumb, thumb.Bounds(), 1561 tmp.Bounds(), B_FILTER_BITMAP_BILINEAR); 1562 view.Sync(); 1563 view.UnlockLooper(); 1564 } 1565 tmp.RemoveChild(&view); 1566 1567 // copy tmp bitmap into icon 1568 result = icon->ImportBits(&tmp); 1569 } 1570 // we found a thumbnail 1571 if (result == B_OK) 1572 return result; 1573 } 1574 } 1575 // else we did not find a thumbnail 1576 } else { 1577 // file changed, remove all thumb attrs 1578 char attrName[B_ATTR_NAME_LENGTH]; 1579 while (node->GetNextAttrName(attrName) == B_OK) { 1580 if (BString(attrName).StartsWith(kAttrThumbnail)) 1581 node->RemoveAttr(attrName); 1582 } 1583 } 1584 } 1585 1586 // check generate thumbnail setting, 1587 // mime type must be an image type 1588 if (TrackerSettings().GenerateImageThumbnails() 1589 && BString(model->MimeType()).IStartsWith("image")) { 1590 // try to fetch a new thumbnail icon 1591 result = GetThumbnailIcon(model, icon, which); 1592 if (result == B_OK) { 1593 // icon ready 1594 return B_OK; 1595 } else if (result == B_BUSY) { 1596 // working on icon, come back later 1597 return B_BUSY; 1598 } 1599 } 1600 1601 // get icon from the node info 1602 BNodeInfo nodeInfo(node); 1603 return nodeInfo.GetIcon(icon, which); 1604 } 1605 1606 1607 // #pragma mark - image thumbnails 1608 1609 1610 struct ThumbGenParams { 1611 ThumbGenParams(Model* _model, BFile* _file, icon_size _which, 1612 color_space _colorSpace, port_id _port); 1613 virtual ~ThumbGenParams(); 1614 1615 status_t InitCheck() { return fInitStatus; }; 1616 1617 Model* model; 1618 BFile* file; 1619 icon_size which; 1620 color_space colorSpace; 1621 port_id port; 1622 1623 private: 1624 status_t fInitStatus; 1625 }; 1626 1627 1628 ThumbGenParams::ThumbGenParams(Model* _model, BFile* _file, icon_size _which, 1629 color_space _colorSpace, port_id _port) 1630 { 1631 model = new(std::nothrow) Model(*_model); 1632 file = new(std::nothrow) BFile(*_file); 1633 which = _which; 1634 colorSpace = _colorSpace; 1635 port = _port; 1636 1637 fInitStatus = (model == NULL || file == NULL ? B_NO_MEMORY : B_OK); 1638 } 1639 1640 1641 ThumbGenParams::~ThumbGenParams() 1642 { 1643 delete file; 1644 delete model; 1645 } 1646 1647 1648 status_t get_thumbnail(void* castToParams); 1649 static const int32 kMsgIconData = 'ICON'; 1650 1651 1652 BRect 1653 ThumbBounds(BBitmap* icon, float aspectRatio) 1654 { 1655 BRect thumbBounds; 1656 1657 if (aspectRatio > 1) { 1658 // wide 1659 thumbBounds = BRect(0, 0, icon->Bounds().IntegerWidth() - 1, 1660 floorf((icon->Bounds().IntegerHeight() - 1) / aspectRatio)); 1661 thumbBounds.OffsetBySelf(0, floorf((icon->Bounds().IntegerHeight() 1662 - thumbBounds.IntegerHeight()) / 2.0f)); 1663 } else if (aspectRatio < 1) { 1664 // tall 1665 thumbBounds = BRect(0, 0, floorf((icon->Bounds().IntegerWidth() - 1) 1666 * aspectRatio), icon->Bounds().IntegerHeight() - 1); 1667 thumbBounds.OffsetBySelf(floorf((icon->Bounds().IntegerWidth() 1668 - thumbBounds.IntegerWidth()) / 2.0f), 0); 1669 } else { 1670 // square 1671 thumbBounds = icon->Bounds(); 1672 } 1673 1674 return thumbBounds; 1675 } 1676 1677 1678 status_t 1679 GetThumbnailIcon(Model* model, BBitmap* icon, icon_size which) 1680 { 1681 status_t result = B_ERROR; 1682 1683 // create a name for the node icon generator thread (32 chars max) 1684 icon_size w = (icon_size)B_XXL_ICON; 1685 dev_t d = model->NodeRef()->device; 1686 ino_t n = model->NodeRef()->node; 1687 BString genThreadName = BString("_thumbgen_w") 1688 << w << "_d" << d << "_n" << n << "_"; 1689 1690 bool volumeReadOnly = true; 1691 BVolume volume(model->NodeRef()->device); 1692 if (volume.InitCheck() == B_OK) 1693 volumeReadOnly = volume.IsReadOnly() || !volume.KnowsAttr(); 1694 1695 port_id port = B_NAME_NOT_FOUND; 1696 if (volumeReadOnly) { 1697 // look for a port with some icon data 1698 port = find_port(genThreadName.String()); 1699 // give the port the same name as the generator thread 1700 if (port != B_NAME_NOT_FOUND && port_count(port) > 0) { 1701 // a generator thread has written some data to the port, fetch it 1702 uint8 iconData[icon->BitsLength()]; 1703 int32 msgCode; 1704 int32 bytesRead = read_port(port, &msgCode, iconData, 1705 icon->BitsLength()); 1706 if (bytesRead == icon->BitsLength() && msgCode == kMsgIconData 1707 && iconData != NULL) { 1708 // fill icon data into the passed in icon 1709 result = icon->ImportBits(iconData, icon->BitsLength(), 1710 icon->BytesPerRow(), 0, icon->ColorSpace()); 1711 } 1712 1713 if (result == B_OK) { 1714 // make a new port next time 1715 delete_port(port); 1716 port = B_NAME_NOT_FOUND; 1717 } 1718 } 1719 } 1720 1721 // we found an icon from a generator thread 1722 if (result == B_OK) 1723 return B_OK; 1724 1725 // look for an existing generator thread before spawning a new one 1726 if (find_thread(genThreadName.String()) == B_NAME_NOT_FOUND) { 1727 // no generater thread found, spawn one 1728 BFile* file = dynamic_cast<BFile*>(model->Node()); 1729 if (file == NULL) 1730 result = B_NOT_SUPPORTED; // node must be a file 1731 else { 1732 // create a new port if one doesn't already exist 1733 if (volumeReadOnly && port == B_NAME_NOT_FOUND) 1734 port = create_port(1, genThreadName.String()); 1735 1736 ThumbGenParams* params = new ThumbGenParams(model, file, which, 1737 icon->ColorSpace(), port); 1738 if (params->InitCheck() == B_OK) { 1739 // generator thread will delete params, it makes copies 1740 resume_thread(spawn_thread(get_thumbnail, 1741 genThreadName.String(), B_LOW_PRIORITY, params)); 1742 result = B_BUSY; // try again later 1743 } else 1744 delete params; 1745 } 1746 } 1747 1748 return result; 1749 } 1750 1751 1752 // #pragma mark - thumbnail generator thread 1753 1754 1755 status_t 1756 get_thumbnail(void* castToParams) 1757 { 1758 ThumbGenParams* params = (ThumbGenParams*)castToParams; 1759 Model* model = params->model; 1760 BFile* file = params->file; 1761 icon_size which = params->which; 1762 color_space colorSpace = params->colorSpace; 1763 port_id port = params->port; 1764 1765 // get the mime type from the model 1766 const char* type = model->MimeType(); 1767 1768 // check if attributes can be written to 1769 bool volumeReadOnly = true; 1770 BVolume volume(model->NodeRef()->device); 1771 if (volume.InitCheck() == B_OK) 1772 volumeReadOnly = volume.IsReadOnly() || !volume.KnowsAttr(); 1773 1774 // see if we have a thumbnail attribute 1775 attr_info attrInfo; 1776 status_t result = file->GetAttrInfo(kAttrThumbnail, &attrInfo); 1777 if (result != B_OK) { 1778 // create a new thumbnail 1779 1780 // check to see if we have a translator that works 1781 BBitmapStream imageStream; 1782 BBitmap* image; 1783 if (BTranslatorRoster::Default()->Translate(file, NULL, NULL, 1784 &imageStream, B_TRANSLATOR_BITMAP, 0, type) == B_OK 1785 && imageStream.DetachBitmap(&image) == B_OK) { 1786 // we have translated the image file into a BBitmap 1787 1788 // check if we can write attrs 1789 if (!volumeReadOnly) { 1790 // write image width to an attribute 1791 int32 width = image->Bounds().IntegerWidth(); 1792 file->WriteAttr("Media:Width", B_INT32_TYPE, 0, &width, 1793 sizeof(int32)); 1794 1795 // write image height to an attribute 1796 int32 height = image->Bounds().IntegerHeight(); 1797 file->WriteAttr("Media:Height", B_INT32_TYPE, 0, &height, 1798 sizeof(int32)); 1799 1800 // convert image into a 128x128 WebP image and stash it 1801 BBitmap thumb = BBitmap(BRect(0, 0, B_XXL_ICON - 1, 1802 B_XXL_ICON - 1), colorSpace, true); 1803 BView view(thumb.Bounds(), "", B_FOLLOW_NONE, 1804 B_WILL_DRAW); 1805 thumb.AddChild(&view); 1806 if (view.LockLooper()) { 1807 // fill with transparent 1808 view.SetLowColor(B_TRANSPARENT_COLOR); 1809 view.FillRect(view.Bounds(), B_SOLID_LOW); 1810 // draw bitmap 1811 view.SetDrawingMode(B_OP_ALPHA); 1812 view.SetBlendingMode(B_PIXEL_ALPHA, 1813 B_ALPHA_COMPOSITE); 1814 view.DrawBitmap(image, image->Bounds(), 1815 ThumbBounds(&thumb, image->Bounds().Width() 1816 / image->Bounds().Height()), 1817 B_FILTER_BITMAP_BILINEAR); 1818 view.Sync(); 1819 view.UnlockLooper(); 1820 } 1821 thumb.RemoveChild(&view); 1822 1823 BBitmap* thumbPointer = &thumb; 1824 BBitmapStream thumbStream(thumbPointer); 1825 BMallocIO stream; 1826 if (BTranslatorRoster::Default()->Translate(&thumbStream, 1827 NULL, NULL, &stream, B_WEBP_FORMAT) == B_OK 1828 && thumbStream.DetachBitmap(&thumbPointer) == B_OK) { 1829 // write WebP image data into an attribute 1830 file->WriteAttr(kAttrThumbnail, B_RAW_TYPE, 0, 1831 stream.Buffer(), stream.BufferLength()); 1832 1833 // write thumbnail creation time into an attribute 1834 bigtime_t created = system_time(); 1835 file->WriteAttr(kAttrThumbnailCreationTime, B_TIME_TYPE, 1836 0, &created, sizeof(bigtime_t)); 1837 1838 // we wrote thumbnail to an attribute 1839 result = B_OK; 1840 } 1841 } else if (port != B_NAME_NOT_FOUND) { 1842 // create a thumb at the requested icon size 1843 BBitmap thumb = BBitmap(BRect(0, 0, which - 1, which - 1), 1844 colorSpace, true); 1845 // copy image into a view bitmap, scaled and centered 1846 BView view(thumb.Bounds(), "", B_FOLLOW_NONE, B_WILL_DRAW); 1847 thumb.AddChild(&view); 1848 if (view.LockLooper()) { 1849 // fill with transparent 1850 view.SetLowColor(B_TRANSPARENT_COLOR); 1851 view.FillRect(view.Bounds(), B_SOLID_LOW); 1852 // draw bitmap 1853 view.SetDrawingMode(B_OP_ALPHA); 1854 view.SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_COMPOSITE); 1855 view.DrawBitmap(image, image->Bounds(), 1856 ThumbBounds(&thumb, image->Bounds().Width() 1857 / image->Bounds().Height()), 1858 B_FILTER_BITMAP_BILINEAR); 1859 view.Sync(); 1860 view.UnlockLooper(); 1861 } 1862 thumb.RemoveChild(&view); 1863 1864 // send icon back to the calling thread through the port 1865 result = write_port(port, kMsgIconData, (void*)thumb.Bits(), 1866 thumb.BitsLength()); 1867 } 1868 } 1869 1870 delete image; 1871 } 1872 1873 if (result == B_OK) { 1874 // trigger an icon refresh 1875 if (!volumeReadOnly) 1876 model->Mimeset(true); // only works on read-write volumes 1877 else { 1878 // send Tracker a message to tell it to update the thumbnail 1879 BMessage message(kUpdateThumbnail); 1880 if (message.AddInt32("device", model->NodeRef()->device) == B_OK 1881 && message.AddUInt64("node", model->NodeRef()->node) == B_OK) { 1882 be_app->PostMessage(&message); 1883 } 1884 } 1885 } 1886 1887 delete params; 1888 1889 return result; 1890 } 1891 1892 1893 // #pragma mark - PrintToStream 1894 1895 1896 void 1897 PrintToStream(rgb_color color) 1898 { 1899 printf("r:%x, g:%x, b:%x, a:%x\n", 1900 color.red, color.green, color.blue, color.alpha); 1901 } 1902 1903 1904 // #pragma mark - EachMenuItem 1905 1906 1907 extern BMenuItem* 1908 EachMenuItem(BMenu* menu, bool recursive, BMenuItem* (*func)(BMenuItem *)) 1909 { 1910 int32 count = menu->CountItems(); 1911 for (int32 index = 0; index < count; index++) { 1912 BMenuItem* item = menu->ItemAt(index); 1913 BMenuItem* newItem = (func)(item); 1914 if (newItem != NULL) 1915 return newItem; 1916 1917 if (recursive) { 1918 BMenu* submenu = menu->SubmenuAt(index); 1919 if (submenu != NULL) 1920 return EachMenuItem(submenu, true, func); 1921 } 1922 } 1923 1924 return NULL; 1925 } 1926 1927 1928 extern const BMenuItem* 1929 EachMenuItem(const BMenu* menu, bool recursive, 1930 BMenuItem* (*func)(const BMenuItem *)) 1931 { 1932 int32 count = menu->CountItems(); 1933 for (int32 index = 0; index < count; index++) { 1934 BMenuItem* item = menu->ItemAt(index); 1935 BMenuItem* newItem = (func)(item); 1936 if (newItem != NULL) 1937 return newItem; 1938 1939 if (recursive) { 1940 BMenu* submenu = menu->SubmenuAt(index); 1941 if (submenu != NULL) 1942 return EachMenuItem(submenu, true, func); 1943 } 1944 } 1945 1946 return NULL; 1947 } 1948 1949 1950 // #pragma mark - PositionPassingMenuItem 1951 1952 1953 PositionPassingMenuItem::PositionPassingMenuItem(const char* title, 1954 BMessage* message, char shortcut, uint32 modifiers) 1955 : 1956 BMenuItem(title, message, shortcut, modifiers) 1957 { 1958 } 1959 1960 1961 PositionPassingMenuItem::PositionPassingMenuItem(BMenu* menu, BMessage* message) 1962 : 1963 BMenuItem(menu, message) 1964 { 1965 } 1966 1967 1968 PositionPassingMenuItem::PositionPassingMenuItem(BMessage* data) 1969 : 1970 BMenuItem(data) 1971 { 1972 } 1973 1974 1975 BArchivable* 1976 PositionPassingMenuItem::Instantiate(BMessage* data) 1977 { 1978 if (validate_instantiation(data, "PositionPassingMenuItem")) 1979 return new PositionPassingMenuItem(data); 1980 1981 return NULL; 1982 } 1983 1984 1985 status_t 1986 PositionPassingMenuItem::Invoke(BMessage* message) 1987 { 1988 if (Menu() == NULL) 1989 return B_ERROR; 1990 1991 if (!IsEnabled()) 1992 return B_ERROR; 1993 1994 if (message == NULL) 1995 message = Message(); 1996 1997 if (message == NULL) 1998 return B_BAD_VALUE; 1999 2000 BMessage clone(*message); 2001 clone.AddInt32("index", Menu()->IndexOf(this)); 2002 clone.AddInt64("when", system_time()); 2003 clone.AddPointer("source", this); 2004 2005 // embed the invoke location of the menu so that we can create 2006 // a new folder, etc. on the spot 2007 BMenu* menu = Menu(); 2008 2009 for (;;) { 2010 if (!menu->Supermenu()) 2011 break; 2012 2013 menu = menu->Supermenu(); 2014 } 2015 2016 // use the window position only, if the item was invoked from the menu 2017 // menu->Window() points to the window the item was invoked from 2018 if (dynamic_cast<BContainerWindow*>(menu->Window()) == NULL) { 2019 LooperAutoLocker lock(menu); 2020 if (lock.IsLocked()) { 2021 BPoint invokeOrigin(menu->Window()->Frame().LeftTop()); 2022 clone.AddPoint("be:invoke_origin", invokeOrigin); 2023 } 2024 } 2025 2026 return BInvoker::Invoke(&clone); 2027 } 2028 2029 2030 // #pragma mark - BPrivate functions 2031 2032 2033 bool 2034 BootedInSafeMode() 2035 { 2036 const char* safeMode = getenv("SAFEMODE"); 2037 return (safeMode && strcmp(safeMode, "yes") == 0); 2038 } 2039 2040 2041 float 2042 ComputeTypeAheadScore(const char* text, const char* match, bool wordMode) 2043 { 2044 // highest score: exact match 2045 const char* found = strcasestr(text, match); 2046 if (found != NULL) { 2047 if (found == text) 2048 return kExactMatchScore; 2049 2050 return 1.f / (found - text); 2051 } 2052 2053 // there was no exact match 2054 2055 // second best: all characters at word beginnings 2056 if (wordMode) { 2057 float score = 0; 2058 for (int32 j = 0, k = 0; match[j]; j++) { 2059 while (text[k] 2060 && tolower(text[k]) != tolower(match[j])) { 2061 k++; 2062 } 2063 if (text[k] == '\0') { 2064 score = 0; 2065 break; 2066 } 2067 2068 bool wordStart = k == 0 || isspace(text[k - 1]); 2069 if (wordStart) 2070 score++; 2071 if (j > 0) { 2072 bool wordEnd = !text[k + 1] || isspace(text[k + 1]); 2073 if (wordEnd) 2074 score += 0.3; 2075 if (match[j - 1] == text[k - 1]) 2076 score += 0.7; 2077 } 2078 2079 score += 1.f / (k + 1); 2080 k++; 2081 } 2082 2083 return score; 2084 } 2085 2086 return -1; 2087 } 2088 2089 2090 // #pragma mark - throw on error functions. 2091 2092 2093 void 2094 _ThrowOnError(status_t result, const char* DEBUG_ONLY(file), 2095 int32 DEBUG_ONLY(line)) 2096 { 2097 if (result != B_OK) { 2098 PRINT(("%s at %s:%d\n", strerror(result), file, (int)line)); 2099 throw result; 2100 } 2101 } 2102 2103 2104 void 2105 _ThrowIfNotSize(ssize_t size, const char* DEBUG_ONLY(file), 2106 int32 DEBUG_ONLY(line)) 2107 { 2108 if (size < B_OK) { 2109 PRINT(("%s at %s:%d\n", strerror((status_t)size), file, (int)line)); 2110 throw (status_t)size; 2111 } 2112 } 2113 2114 2115 void 2116 _ThrowOnAssert(bool success, const char* DEBUG_ONLY(file), 2117 int32 DEBUG_ONLY(line)) 2118 { 2119 if (!success) { 2120 PRINT(("Assert failed at %s:%d\n", file, (int)line)); 2121 throw B_ERROR; 2122 } 2123 } 2124 2125 } // namespace BPrivate 2126