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 if (ShouldGenerateThumbnail(model->MimeType())) { 1587 // try to fetch a new thumbnail icon 1588 result = GetThumbnailIcon(model, icon, which); 1589 if (result == B_OK) { 1590 // icon ready 1591 return B_OK; 1592 } else if (result == B_BUSY) { 1593 // working on icon, come back later 1594 return B_BUSY; 1595 } 1596 } 1597 1598 // get icon from the node info 1599 BNodeInfo nodeInfo(node); 1600 return nodeInfo.GetIcon(icon, which); 1601 } 1602 1603 1604 // #pragma mark - image thumbnails 1605 1606 1607 struct ThumbGenParams { 1608 ThumbGenParams(Model* _model, BFile* _file, icon_size _which, 1609 color_space _colorSpace, port_id _port); 1610 virtual ~ThumbGenParams(); 1611 1612 status_t InitCheck() { return fInitStatus; }; 1613 1614 Model* model; 1615 BFile* file; 1616 icon_size which; 1617 color_space colorSpace; 1618 port_id port; 1619 1620 private: 1621 status_t fInitStatus; 1622 }; 1623 1624 1625 ThumbGenParams::ThumbGenParams(Model* _model, BFile* _file, icon_size _which, 1626 color_space _colorSpace, port_id _port) 1627 { 1628 model = new(std::nothrow) Model(*_model); 1629 file = new(std::nothrow) BFile(*_file); 1630 which = _which; 1631 colorSpace = _colorSpace; 1632 port = _port; 1633 1634 fInitStatus = (model == NULL || file == NULL ? B_NO_MEMORY : B_OK); 1635 } 1636 1637 1638 ThumbGenParams::~ThumbGenParams() 1639 { 1640 delete file; 1641 delete model; 1642 } 1643 1644 1645 status_t get_thumbnail(void* castToParams); 1646 static const int32 kMsgIconData = 'ICON'; 1647 1648 1649 BRect 1650 ThumbBounds(BBitmap* icon, float aspectRatio) 1651 { 1652 BRect thumbBounds; 1653 1654 if (aspectRatio > 1) { 1655 // wide 1656 thumbBounds = BRect(0, 0, icon->Bounds().IntegerWidth() - 1, 1657 floorf((icon->Bounds().IntegerHeight() - 1) / aspectRatio)); 1658 thumbBounds.OffsetBySelf(0, floorf((icon->Bounds().IntegerHeight() 1659 - thumbBounds.IntegerHeight()) / 2.0f)); 1660 } else if (aspectRatio < 1) { 1661 // tall 1662 thumbBounds = BRect(0, 0, floorf((icon->Bounds().IntegerWidth() - 1) 1663 * aspectRatio), icon->Bounds().IntegerHeight() - 1); 1664 thumbBounds.OffsetBySelf(floorf((icon->Bounds().IntegerWidth() 1665 - thumbBounds.IntegerWidth()) / 2.0f), 0); 1666 } else { 1667 // square 1668 thumbBounds = icon->Bounds(); 1669 } 1670 1671 return thumbBounds; 1672 } 1673 1674 1675 status_t 1676 GetThumbnailIcon(Model* model, BBitmap* icon, icon_size which) 1677 { 1678 status_t result = B_ERROR; 1679 1680 // create a name for the node icon generator thread (32 chars max) 1681 icon_size w = (icon_size)B_XXL_ICON; 1682 dev_t d = model->NodeRef()->device; 1683 ino_t n = model->NodeRef()->node; 1684 BString genThreadName = BString("_thumbgen_w") 1685 << w << "_d" << d << "_n" << n << "_"; 1686 1687 bool volumeReadOnly = true; 1688 BVolume volume(model->NodeRef()->device); 1689 if (volume.InitCheck() == B_OK) 1690 volumeReadOnly = volume.IsReadOnly() || !volume.KnowsAttr(); 1691 1692 port_id port = B_NAME_NOT_FOUND; 1693 if (volumeReadOnly) { 1694 // look for a port with some icon data 1695 port = find_port(genThreadName.String()); 1696 // give the port the same name as the generator thread 1697 if (port != B_NAME_NOT_FOUND && port_count(port) > 0) { 1698 // a generator thread has written some data to the port, fetch it 1699 uint8 iconData[icon->BitsLength()]; 1700 int32 msgCode; 1701 int32 bytesRead = read_port(port, &msgCode, iconData, 1702 icon->BitsLength()); 1703 if (bytesRead == icon->BitsLength() && msgCode == kMsgIconData 1704 && iconData != NULL) { 1705 // fill icon data into the passed in icon 1706 result = icon->ImportBits(iconData, icon->BitsLength(), 1707 icon->BytesPerRow(), 0, icon->ColorSpace()); 1708 } 1709 1710 if (result == B_OK) { 1711 // make a new port next time 1712 delete_port(port); 1713 port = B_NAME_NOT_FOUND; 1714 } 1715 } 1716 } 1717 1718 // we found an icon from a generator thread 1719 if (result == B_OK) 1720 return B_OK; 1721 1722 // look for an existing generator thread before spawning a new one 1723 if (find_thread(genThreadName.String()) == B_NAME_NOT_FOUND) { 1724 // no generater thread found, spawn one 1725 BFile* file = dynamic_cast<BFile*>(model->Node()); 1726 if (file == NULL) 1727 result = B_NOT_SUPPORTED; // node must be a file 1728 else { 1729 // create a new port if one doesn't already exist 1730 if (volumeReadOnly && port == B_NAME_NOT_FOUND) 1731 port = create_port(1, genThreadName.String()); 1732 1733 ThumbGenParams* params = new ThumbGenParams(model, file, which, 1734 icon->ColorSpace(), port); 1735 if (params->InitCheck() == B_OK) { 1736 // generator thread will delete params, it makes copies 1737 resume_thread(spawn_thread(get_thumbnail, 1738 genThreadName.String(), B_LOW_PRIORITY, params)); 1739 result = B_BUSY; // try again later 1740 } else 1741 delete params; 1742 } 1743 } 1744 1745 return result; 1746 } 1747 1748 1749 bool 1750 ShouldGenerateThumbnail(const char* type) 1751 { 1752 // check generate thumbnail setting, 1753 // mime type must be an image (for now) 1754 return TrackerSettings().GenerateImageThumbnails() 1755 && type != NULL && BString(type).IStartsWith("image"); 1756 } 1757 1758 1759 // #pragma mark - thumbnail generator thread 1760 1761 1762 status_t 1763 get_thumbnail(void* castToParams) 1764 { 1765 ThumbGenParams* params = (ThumbGenParams*)castToParams; 1766 Model* model = params->model; 1767 BFile* file = params->file; 1768 icon_size which = params->which; 1769 color_space colorSpace = params->colorSpace; 1770 port_id port = params->port; 1771 1772 // get the mime type from the model 1773 const char* type = model->MimeType(); 1774 1775 // check if attributes can be written to 1776 bool volumeReadOnly = true; 1777 BVolume volume(model->NodeRef()->device); 1778 if (volume.InitCheck() == B_OK) 1779 volumeReadOnly = volume.IsReadOnly() || !volume.KnowsAttr(); 1780 1781 // see if we have a thumbnail attribute 1782 attr_info attrInfo; 1783 status_t result = file->GetAttrInfo(kAttrThumbnail, &attrInfo); 1784 if (result != B_OK) { 1785 // create a new thumbnail 1786 1787 // check to see if we have a translator that works 1788 BBitmapStream imageStream; 1789 BBitmap* image; 1790 if (BTranslatorRoster::Default()->Translate(file, NULL, NULL, 1791 &imageStream, B_TRANSLATOR_BITMAP, 0, type) == B_OK 1792 && imageStream.DetachBitmap(&image) == B_OK) { 1793 // we have translated the image file into a BBitmap 1794 1795 // check if we can write attrs 1796 if (!volumeReadOnly) { 1797 // write image width to an attribute 1798 int32 width = image->Bounds().IntegerWidth(); 1799 file->WriteAttr("Media:Width", B_INT32_TYPE, 0, &width, 1800 sizeof(int32)); 1801 1802 // write image height to an attribute 1803 int32 height = image->Bounds().IntegerHeight(); 1804 file->WriteAttr("Media:Height", B_INT32_TYPE, 0, &height, 1805 sizeof(int32)); 1806 1807 // convert image into a 128x128 WebP image and stash it 1808 BBitmap thumb = BBitmap(BRect(0, 0, B_XXL_ICON - 1, 1809 B_XXL_ICON - 1), colorSpace, true); 1810 BView view(thumb.Bounds(), "", B_FOLLOW_NONE, 1811 B_WILL_DRAW); 1812 thumb.AddChild(&view); 1813 if (view.LockLooper()) { 1814 // fill with transparent 1815 view.SetLowColor(B_TRANSPARENT_COLOR); 1816 view.FillRect(view.Bounds(), B_SOLID_LOW); 1817 // draw bitmap 1818 view.SetDrawingMode(B_OP_ALPHA); 1819 view.SetBlendingMode(B_PIXEL_ALPHA, 1820 B_ALPHA_COMPOSITE); 1821 view.DrawBitmap(image, image->Bounds(), 1822 ThumbBounds(&thumb, image->Bounds().Width() 1823 / image->Bounds().Height()), 1824 B_FILTER_BITMAP_BILINEAR); 1825 view.Sync(); 1826 view.UnlockLooper(); 1827 } 1828 thumb.RemoveChild(&view); 1829 1830 BBitmap* thumbPointer = &thumb; 1831 BBitmapStream thumbStream(thumbPointer); 1832 BMallocIO stream; 1833 if (BTranslatorRoster::Default()->Translate(&thumbStream, 1834 NULL, NULL, &stream, B_WEBP_FORMAT) == B_OK 1835 && thumbStream.DetachBitmap(&thumbPointer) == B_OK) { 1836 // write WebP image data into an attribute 1837 file->WriteAttr(kAttrThumbnail, B_RAW_TYPE, 0, 1838 stream.Buffer(), stream.BufferLength()); 1839 1840 // write thumbnail creation time into an attribute 1841 bigtime_t created = system_time(); 1842 file->WriteAttr(kAttrThumbnailCreationTime, B_TIME_TYPE, 1843 0, &created, sizeof(bigtime_t)); 1844 1845 // we wrote thumbnail to an attribute 1846 result = B_OK; 1847 } 1848 } else if (port != B_NAME_NOT_FOUND) { 1849 // create a thumb at the requested icon size 1850 BBitmap thumb = BBitmap(BRect(0, 0, which - 1, which - 1), 1851 colorSpace, true); 1852 // copy image into a view bitmap, scaled and centered 1853 BView view(thumb.Bounds(), "", B_FOLLOW_NONE, B_WILL_DRAW); 1854 thumb.AddChild(&view); 1855 if (view.LockLooper()) { 1856 // fill with transparent 1857 view.SetLowColor(B_TRANSPARENT_COLOR); 1858 view.FillRect(view.Bounds(), B_SOLID_LOW); 1859 // draw bitmap 1860 view.SetDrawingMode(B_OP_ALPHA); 1861 view.SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_COMPOSITE); 1862 view.DrawBitmap(image, image->Bounds(), 1863 ThumbBounds(&thumb, image->Bounds().Width() 1864 / image->Bounds().Height()), 1865 B_FILTER_BITMAP_BILINEAR); 1866 view.Sync(); 1867 view.UnlockLooper(); 1868 } 1869 thumb.RemoveChild(&view); 1870 1871 // send icon back to the calling thread through the port 1872 result = write_port(port, kMsgIconData, (void*)thumb.Bits(), 1873 thumb.BitsLength()); 1874 } 1875 } 1876 1877 delete image; 1878 } 1879 1880 if (result == B_OK) { 1881 // trigger an icon refresh 1882 if (!volumeReadOnly) 1883 model->Mimeset(true); // only works on read-write volumes 1884 else { 1885 // send Tracker a message to tell it to update the thumbnail 1886 BMessage message(kUpdateThumbnail); 1887 if (message.AddInt32("device", model->NodeRef()->device) == B_OK 1888 && message.AddUInt64("node", model->NodeRef()->node) == B_OK) { 1889 be_app->PostMessage(&message); 1890 } 1891 } 1892 } 1893 1894 delete params; 1895 1896 return result; 1897 } 1898 1899 1900 // #pragma mark - PrintToStream 1901 1902 1903 void 1904 PrintToStream(rgb_color color) 1905 { 1906 printf("r:%x, g:%x, b:%x, a:%x\n", 1907 color.red, color.green, color.blue, color.alpha); 1908 } 1909 1910 1911 // #pragma mark - EachMenuItem 1912 1913 1914 extern BMenuItem* 1915 EachMenuItem(BMenu* menu, bool recursive, BMenuItem* (*func)(BMenuItem *)) 1916 { 1917 int32 count = menu->CountItems(); 1918 for (int32 index = 0; index < count; index++) { 1919 BMenuItem* item = menu->ItemAt(index); 1920 BMenuItem* newItem = (func)(item); 1921 if (newItem != NULL) 1922 return newItem; 1923 1924 if (recursive) { 1925 BMenu* submenu = menu->SubmenuAt(index); 1926 if (submenu != NULL) 1927 return EachMenuItem(submenu, true, func); 1928 } 1929 } 1930 1931 return NULL; 1932 } 1933 1934 1935 extern const BMenuItem* 1936 EachMenuItem(const BMenu* menu, bool recursive, 1937 BMenuItem* (*func)(const BMenuItem *)) 1938 { 1939 int32 count = menu->CountItems(); 1940 for (int32 index = 0; index < count; index++) { 1941 BMenuItem* item = menu->ItemAt(index); 1942 BMenuItem* newItem = (func)(item); 1943 if (newItem != NULL) 1944 return newItem; 1945 1946 if (recursive) { 1947 BMenu* submenu = menu->SubmenuAt(index); 1948 if (submenu != NULL) 1949 return EachMenuItem(submenu, true, func); 1950 } 1951 } 1952 1953 return NULL; 1954 } 1955 1956 1957 // #pragma mark - PositionPassingMenuItem 1958 1959 1960 PositionPassingMenuItem::PositionPassingMenuItem(const char* title, 1961 BMessage* message, char shortcut, uint32 modifiers) 1962 : 1963 BMenuItem(title, message, shortcut, modifiers) 1964 { 1965 } 1966 1967 1968 PositionPassingMenuItem::PositionPassingMenuItem(BMenu* menu, BMessage* message) 1969 : 1970 BMenuItem(menu, message) 1971 { 1972 } 1973 1974 1975 PositionPassingMenuItem::PositionPassingMenuItem(BMessage* data) 1976 : 1977 BMenuItem(data) 1978 { 1979 } 1980 1981 1982 BArchivable* 1983 PositionPassingMenuItem::Instantiate(BMessage* data) 1984 { 1985 if (validate_instantiation(data, "PositionPassingMenuItem")) 1986 return new PositionPassingMenuItem(data); 1987 1988 return NULL; 1989 } 1990 1991 1992 status_t 1993 PositionPassingMenuItem::Invoke(BMessage* message) 1994 { 1995 if (Menu() == NULL) 1996 return B_ERROR; 1997 1998 if (!IsEnabled()) 1999 return B_ERROR; 2000 2001 if (message == NULL) 2002 message = Message(); 2003 2004 if (message == NULL) 2005 return B_BAD_VALUE; 2006 2007 BMessage clone(*message); 2008 clone.AddInt32("index", Menu()->IndexOf(this)); 2009 clone.AddInt64("when", system_time()); 2010 clone.AddPointer("source", this); 2011 2012 // embed the invoke location of the menu so that we can create 2013 // a new folder, etc. on the spot 2014 BMenu* menu = Menu(); 2015 2016 for (;;) { 2017 if (!menu->Supermenu()) 2018 break; 2019 2020 menu = menu->Supermenu(); 2021 } 2022 2023 // use the window position only, if the item was invoked from the menu 2024 // menu->Window() points to the window the item was invoked from 2025 if (dynamic_cast<BContainerWindow*>(menu->Window()) == NULL) { 2026 LooperAutoLocker lock(menu); 2027 if (lock.IsLocked()) { 2028 BPoint invokeOrigin(menu->Window()->Frame().LeftTop()); 2029 clone.AddPoint("be:invoke_origin", invokeOrigin); 2030 } 2031 } 2032 2033 return BInvoker::Invoke(&clone); 2034 } 2035 2036 2037 // #pragma mark - BPrivate functions 2038 2039 2040 bool 2041 BootedInSafeMode() 2042 { 2043 const char* safeMode = getenv("SAFEMODE"); 2044 return (safeMode && strcmp(safeMode, "yes") == 0); 2045 } 2046 2047 2048 float 2049 ComputeTypeAheadScore(const char* text, const char* match, bool wordMode) 2050 { 2051 // highest score: exact match 2052 const char* found = strcasestr(text, match); 2053 if (found != NULL) { 2054 if (found == text) 2055 return kExactMatchScore; 2056 2057 return 1.f / (found - text); 2058 } 2059 2060 // there was no exact match 2061 2062 // second best: all characters at word beginnings 2063 if (wordMode) { 2064 float score = 0; 2065 for (int32 j = 0, k = 0; match[j]; j++) { 2066 while (text[k] 2067 && tolower(text[k]) != tolower(match[j])) { 2068 k++; 2069 } 2070 if (text[k] == '\0') { 2071 score = 0; 2072 break; 2073 } 2074 2075 bool wordStart = k == 0 || isspace(text[k - 1]); 2076 if (wordStart) 2077 score++; 2078 if (j > 0) { 2079 bool wordEnd = !text[k + 1] || isspace(text[k + 1]); 2080 if (wordEnd) 2081 score += 0.3; 2082 if (match[j - 1] == text[k - 1]) 2083 score += 0.7; 2084 } 2085 2086 score += 1.f / (k + 1); 2087 k++; 2088 } 2089 2090 return score; 2091 } 2092 2093 return -1; 2094 } 2095 2096 2097 // #pragma mark - throw on error functions. 2098 2099 2100 void 2101 _ThrowOnError(status_t result, const char* DEBUG_ONLY(file), 2102 int32 DEBUG_ONLY(line)) 2103 { 2104 if (result != B_OK) { 2105 PRINT(("%s at %s:%d\n", strerror(result), file, (int)line)); 2106 throw result; 2107 } 2108 } 2109 2110 2111 void 2112 _ThrowIfNotSize(ssize_t size, const char* DEBUG_ONLY(file), 2113 int32 DEBUG_ONLY(line)) 2114 { 2115 if (size < B_OK) { 2116 PRINT(("%s at %s:%d\n", strerror((status_t)size), file, (int)line)); 2117 throw (status_t)size; 2118 } 2119 } 2120 2121 2122 void 2123 _ThrowOnAssert(bool success, const char* DEBUG_ONLY(file), 2124 int32 DEBUG_ONLY(line)) 2125 { 2126 if (!success) { 2127 PRINT(("Assert failed at %s:%d\n", file, (int)line)); 2128 throw B_ERROR; 2129 } 2130 } 2131 2132 } // namespace BPrivate 2133