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