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