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