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 #ifdef __HAIKU__ 569 SetDrawingMode(B_OP_ALPHA); 570 SetBlendingMode(B_PIXEL_ALPHA, B_ALPHA_OVERLAY); 571 #else 572 SetDrawingMode(B_OP_OVER); 573 #endif 574 DrawBitmap(fBitmap); 575 } 576 577 578 // #pragma mark - 579 580 581 FlickerFreeStringView::FlickerFreeStringView(BRect bounds, const char *name, 582 const char *text, uint32 resizeFlags, uint32 flags) 583 : BStringView(bounds, name, text, resizeFlags, flags), 584 fBitmap(NULL), 585 fOrigBitmap(NULL) 586 { 587 } 588 589 590 FlickerFreeStringView::FlickerFreeStringView(BRect bounds, const char *name, 591 const char *text, BBitmap *inBitmap, uint32 resizeFlags, uint32 flags) 592 : BStringView(bounds, name, text, resizeFlags, flags), 593 fBitmap(NULL), 594 fOrigBitmap(inBitmap) 595 { 596 } 597 598 599 FlickerFreeStringView::~FlickerFreeStringView() 600 { 601 delete fBitmap; 602 } 603 604 605 void 606 FlickerFreeStringView::Draw(BRect) 607 { 608 BRect bounds(Bounds()); 609 if (!fBitmap) 610 fBitmap = new OffscreenBitmap(Bounds()); 611 612 BView *offscreen = fBitmap->BeginUsing(bounds); 613 614 if (Parent()) { 615 fViewColor = Parent()->ViewColor(); 616 fLowColor = Parent()->ViewColor(); 617 } 618 619 offscreen->SetViewColor(fViewColor); 620 offscreen->SetHighColor(HighColor()); 621 offscreen->SetLowColor(fLowColor); 622 623 BFont font; 624 GetFont(&font); 625 offscreen->SetFont(&font); 626 627 offscreen->Sync(); 628 if (fOrigBitmap) 629 offscreen->DrawBitmap(fOrigBitmap, Frame(), bounds); 630 else 631 offscreen->FillRect(bounds, B_SOLID_LOW); 632 633 if (Text()) { 634 BPoint loc; 635 636 font_height height; 637 GetFontHeight(&height); 638 639 edge_info eInfo; 640 switch (Alignment()) { 641 case B_ALIGN_LEFT: 642 #ifdef HAIKU_TARGET_PLATFORM_HAIKU 643 case B_ALIGN_HORIZONTAL_UNSET: 644 case B_ALIGN_USE_FULL_WIDTH: 645 #endif 646 { 647 // If the first char has a negative left edge give it 648 // some more room by shifting that much more to the right. 649 font.GetEdges(Text(), 1, &eInfo); 650 loc.x = bounds.left + (2 - eInfo.left); 651 break; 652 } 653 654 case B_ALIGN_CENTER: 655 { 656 float width = StringWidth(Text()); 657 float center = (bounds.right - bounds.left) / 2; 658 loc.x = center - (width/2); 659 break; 660 } 661 662 case B_ALIGN_RIGHT: 663 { 664 float width = StringWidth(Text()); 665 loc.x = bounds.right - width - 2; 666 break; 667 } 668 } 669 loc.y = bounds.bottom - (1 + height.descent); 670 offscreen->DrawString(Text(), loc); 671 } 672 offscreen->Sync(); 673 SetDrawingMode(B_OP_COPY); 674 DrawBitmap(fBitmap->Bitmap()); 675 fBitmap->DoneUsing(); 676 } 677 678 679 void 680 FlickerFreeStringView::AttachedToWindow() 681 { 682 _inherited::AttachedToWindow(); 683 if (Parent()) { 684 fViewColor = Parent()->ViewColor(); 685 fLowColor = Parent()->ViewColor(); 686 } 687 SetViewColor(B_TRANSPARENT_32_BIT); 688 SetLowColor(B_TRANSPARENT_32_BIT); 689 } 690 691 692 void 693 FlickerFreeStringView::SetViewColor(rgb_color color) 694 { 695 if (fViewColor != color) { 696 fViewColor = color; 697 Invalidate(); 698 } 699 _inherited::SetViewColor(B_TRANSPARENT_32_BIT); 700 } 701 702 703 void 704 FlickerFreeStringView::SetLowColor(rgb_color color) 705 { 706 if (fLowColor != color) { 707 fLowColor = color; 708 Invalidate(); 709 } 710 _inherited::SetLowColor(B_TRANSPARENT_32_BIT); 711 } 712 713 714 // #pragma mark - 715 716 717 TitledSeparatorItem::TitledSeparatorItem(const char *label) 718 : BMenuItem(label, 0) 719 { 720 _inherited::SetEnabled(false); 721 } 722 723 724 TitledSeparatorItem::~TitledSeparatorItem() 725 { 726 } 727 728 729 void 730 TitledSeparatorItem::SetEnabled(bool) 731 { 732 // leave disabled 733 } 734 735 736 void 737 TitledSeparatorItem::GetContentSize(float *width, float *height) 738 { 739 _inherited::GetContentSize(width, height); 740 } 741 742 743 inline rgb_color 744 ShiftMenuBackgroundColor(float by) 745 { 746 return tint_color(ui_color(B_MENU_BACKGROUND_COLOR), by); 747 } 748 749 750 void 751 TitledSeparatorItem::Draw() 752 { 753 BRect frame(Frame()); 754 755 BMenu *parent = Menu(); 756 ASSERT(parent); 757 758 menu_info minfo; 759 get_menu_info(&minfo); 760 761 if (minfo.separator > 0) { 762 frame.left += 10; 763 frame.right -= 10; 764 } else { 765 frame.left += 1; 766 frame.right -= 1; 767 } 768 769 float startX = frame.left; 770 float endX = frame.right; 771 772 float maxStringWidth = endX - startX - (2 * kMinSeparatorStubX 773 + 2 * kStubToStringSlotX); 774 775 // ToDo: 776 // handle case where maxStringWidth turns out negative here 777 778 BString truncatedLabel(Label()); 779 parent->TruncateString(&truncatedLabel, B_TRUNCATE_END, maxStringWidth); 780 781 maxStringWidth = parent->StringWidth(truncatedLabel.String()); 782 783 // first calculate the length of the stub part of the 784 // divider line, so we can use it for secondStartX 785 float firstEndX = ((endX - startX) - maxStringWidth) / 2 - kStubToStringSlotX; 786 if (firstEndX < 0) 787 firstEndX = 0; 788 789 float secondStartX = endX - firstEndX; 790 791 // now finish calculating firstEndX 792 firstEndX += startX; 793 794 parent->PushState(); 795 796 int32 y = (int32) (frame.top + (frame.bottom - frame.top) / 2); 797 798 parent->BeginLineArray(minfo.separator == 2 ? 6 : 4); 799 parent->AddLine(BPoint(frame.left, y), BPoint(firstEndX, y), 800 ShiftMenuBackgroundColor(B_DARKEN_1_TINT)); 801 parent->AddLine(BPoint(secondStartX, y), BPoint(frame.right, y), 802 ShiftMenuBackgroundColor(B_DARKEN_1_TINT)); 803 804 if (minfo.separator == 2) { 805 y++; 806 frame.left++; 807 frame.right--; 808 parent->AddLine(BPoint(frame.left,y), BPoint(firstEndX, y), 809 ShiftMenuBackgroundColor(B_DARKEN_1_TINT)); 810 parent->AddLine(BPoint(secondStartX,y), BPoint(frame.right, y), 811 ShiftMenuBackgroundColor(B_DARKEN_1_TINT)); 812 } 813 y++; 814 if (minfo.separator == 2) { 815 frame.left++; 816 frame.right--; 817 } 818 parent->AddLine(BPoint(frame.left, y), BPoint(firstEndX, y), 819 ShiftMenuBackgroundColor(B_DARKEN_1_TINT)); 820 parent->AddLine(BPoint(secondStartX, y), BPoint(frame.right, y), 821 ShiftMenuBackgroundColor(B_DARKEN_1_TINT)); 822 823 parent->EndLineArray(); 824 825 font_height finfo; 826 parent->GetFontHeight(&finfo); 827 828 parent->SetLowColor(parent->ViewColor()); 829 BPoint loc(firstEndX + kStubToStringSlotX, ContentLocation().y + finfo.ascent); 830 831 parent->MovePenTo(loc + BPoint(1, 1)); 832 parent->SetHighColor(ShiftMenuBackgroundColor(B_DARKEN_1_TINT)); 833 parent->DrawString(truncatedLabel.String()); 834 835 parent->MovePenTo(loc); 836 parent->SetHighColor(ShiftMenuBackgroundColor(B_DISABLED_LABEL_TINT)); 837 parent->DrawString(truncatedLabel.String()); 838 839 parent->PopState(); 840 } 841 842 843 // #pragma mark - 844 845 846 ShortcutFilter::ShortcutFilter(uint32 shortcutKey, uint32 shortcutModifier, 847 uint32 shortcutWhat, BHandler *target) 848 : BMessageFilter(B_KEY_DOWN), 849 fShortcutKey(shortcutKey), 850 fShortcutModifier(shortcutModifier), 851 fShortcutWhat(shortcutWhat), 852 fTarget(target) 853 { 854 } 855 856 857 filter_result 858 ShortcutFilter::Filter(BMessage *message, BHandler **) 859 { 860 if (message->what == B_KEY_DOWN) { 861 uint32 modifiers; 862 uint32 rawKeyChar = 0; 863 uint8 byte = 0; 864 int32 key = 0; 865 866 if (message->FindInt32("modifiers", (int32 *)&modifiers) != B_OK 867 || message->FindInt32("raw_char", (int32 *)&rawKeyChar) != B_OK 868 || message->FindInt8("byte", (int8 *)&byte) != B_OK 869 || message->FindInt32("key", &key) != B_OK) 870 return B_DISPATCH_MESSAGE; 871 872 modifiers &= B_SHIFT_KEY | B_COMMAND_KEY | B_CONTROL_KEY 873 | B_OPTION_KEY | B_MENU_KEY; 874 // strip caps lock, etc. 875 876 if (modifiers == fShortcutModifier && rawKeyChar == fShortcutKey) { 877 fTarget->Looper()->PostMessage(fShortcutWhat, fTarget); 878 return B_SKIP_MESSAGE; 879 } 880 } 881 882 // let others deal with this 883 return B_DISPATCH_MESSAGE; 884 } 885 886 887 // #pragma mark - 888 889 890 namespace BPrivate { 891 892 void 893 EmbedUniqueVolumeInfo(BMessage *message, const BVolume *volume) 894 { 895 BDirectory rootDirectory; 896 time_t created; 897 fs_info info; 898 899 if (volume->GetRootDirectory(&rootDirectory) == B_OK 900 && rootDirectory.GetCreationTime(&created) == B_OK 901 && fs_stat_dev(volume->Device(), &info) == 0) { 902 message->AddInt32("creationDate", created); 903 message->AddInt64("capacity", volume->Capacity()); 904 message->AddString("deviceName", info.device_name); 905 message->AddString("volumeName", info.volume_name); 906 message->AddString("fshName", info.fsh_name); 907 } 908 } 909 910 911 status_t 912 MatchArchivedVolume(BVolume *result, const BMessage *message, int32 index) 913 { 914 time_t created; 915 off_t capacity; 916 917 if (message->FindInt32("creationDate", index, &created) != B_OK 918 || message->FindInt64("capacity", index, &capacity) != B_OK) 919 return B_ERROR; 920 921 BVolumeRoster roster; 922 BVolume volume; 923 BString deviceName, volumeName, fshName; 924 925 if (message->FindString("deviceName", &deviceName) == B_OK 926 && message->FindString("volumeName", &volumeName) == B_OK 927 && message->FindString("fshName", &fshName) == B_OK) { 928 // New style volume identifiers: We have a couple of characteristics, 929 // and compute a score from them. The volume with the greatest score 930 // (if over a certain threshold) is the one we're looking for. We 931 // pick the first volume, in case there is more than one with the 932 // same score. 933 dev_t foundDevice = -1; 934 int foundScore = -1; 935 roster.Rewind(); 936 while (roster.GetNextVolume(&volume) == B_OK) { 937 if (volume.IsPersistent() && volume.KnowsQuery()) { 938 // get creation time and fs_info 939 BDirectory root; 940 volume.GetRootDirectory(&root); 941 time_t cmpCreated; 942 fs_info info; 943 if (root.GetCreationTime(&cmpCreated) == B_OK 944 && fs_stat_dev(volume.Device(), &info) == 0) { 945 // compute the score 946 int score = 0; 947 948 // creation time 949 if (created == cmpCreated) 950 score += 5; 951 // capacity 952 if (capacity == volume.Capacity()) 953 score += 4; 954 // device name 955 if (deviceName == info.device_name) 956 score += 3; 957 // volume name 958 if (volumeName == info.volume_name) 959 score += 2; 960 // fsh name 961 if (fshName == info.fsh_name) 962 score += 1; 963 964 // check score 965 if (score >= 9 && score > foundScore) { 966 foundDevice = volume.Device(); 967 foundScore = score; 968 } 969 } 970 } 971 } 972 if (foundDevice >= 0) 973 return result->SetTo(foundDevice); 974 } else { 975 // Old style volume identifiers: We have only creation time and 976 // capacity. Both must match. 977 roster.Rewind(); 978 while (roster.GetNextVolume(&volume) == B_OK) 979 if (volume.IsPersistent() && volume.KnowsQuery()) { 980 BDirectory root; 981 volume.GetRootDirectory(&root); 982 time_t cmpCreated; 983 root.GetCreationTime(&cmpCreated); 984 if (created == cmpCreated && capacity == volume.Capacity()) { 985 *result = volume; 986 return B_OK; 987 } 988 } 989 } 990 991 return B_DEV_BAD_DRIVE_NUM; 992 } 993 994 995 void 996 StringFromStream(BString *string, BMallocIO *stream, bool endianSwap) 997 { 998 int32 length; 999 stream->Read(&length, sizeof(length)); 1000 if (endianSwap) 1001 length = SwapInt32(length); 1002 1003 if (length <= 0 || length > 10000) { 1004 // ToDo: 1005 // should fail here 1006 PRINT(("problems instatiating a string, length probably wrong %d\n", length)); 1007 return; 1008 } 1009 1010 char *buffer = string->LockBuffer(length); 1011 stream->Read(buffer, (size_t)length + 1); 1012 string->UnlockBuffer(length); 1013 } 1014 1015 1016 void 1017 StringToStream(const BString *string, BMallocIO *stream) 1018 { 1019 int32 length = string->Length(); 1020 stream->Write(&length, sizeof(int32)); 1021 stream->Write(string->String(), (size_t)string->Length() + 1); 1022 } 1023 1024 1025 int32 1026 ArchiveSize(const BString *string) 1027 { 1028 return string->Length() + 1 + (ssize_t)sizeof(int32); 1029 } 1030 1031 1032 int32 1033 CountRefs(const BMessage *message) 1034 { 1035 uint32 type; 1036 int32 count; 1037 message->GetInfo("refs", &type, &count); 1038 1039 return count; 1040 } 1041 1042 1043 static entry_ref * 1044 EachEntryRefCommon(BMessage *message, entry_ref *(*func)(entry_ref *, void *), 1045 void *passThru, int32 maxCount) 1046 { 1047 uint32 type; 1048 int32 count; 1049 message->GetInfo("refs", &type, &count); 1050 1051 if (maxCount >= 0 && count > maxCount) 1052 count = maxCount; 1053 1054 for (int32 index = 0; index < count; index++) { 1055 entry_ref ref; 1056 message->FindRef("refs", index, &ref); 1057 entry_ref *result = (func)(&ref, passThru); 1058 if (result) 1059 return result; 1060 } 1061 1062 return NULL; 1063 } 1064 1065 1066 bool 1067 ContainsEntryRef(const BMessage *message, const entry_ref *ref) 1068 { 1069 entry_ref match; 1070 for (int32 index = 0; (message->FindRef("refs", index, &match) == B_OK); index++) { 1071 if (*ref == match) 1072 return true; 1073 } 1074 1075 return false; 1076 } 1077 1078 1079 entry_ref * 1080 EachEntryRef(BMessage *message, entry_ref *(*func)(entry_ref *, void *), 1081 void *passThru) 1082 { 1083 return EachEntryRefCommon(message, func, passThru, -1); 1084 } 1085 1086 typedef entry_ref *(*EachEntryIteratee)(entry_ref *, void *); 1087 1088 const entry_ref * 1089 EachEntryRef(const BMessage *message, const entry_ref *(*func)(const entry_ref *, void *), 1090 void *passThru) 1091 { 1092 return EachEntryRefCommon(const_cast<BMessage *>(message), 1093 (EachEntryIteratee)func, passThru, -1); 1094 } 1095 1096 1097 entry_ref * 1098 EachEntryRef(BMessage *message, entry_ref *(*func)(entry_ref *, void *), 1099 void *passThru, int32 maxCount) 1100 { 1101 return EachEntryRefCommon(message, func, passThru, maxCount); 1102 } 1103 1104 1105 const entry_ref * 1106 EachEntryRef(const BMessage *message, const entry_ref *(*func)(const entry_ref *, void *), 1107 void *passThru, int32 maxCount) 1108 { 1109 return EachEntryRefCommon(const_cast<BMessage *>(message), 1110 (EachEntryIteratee)func, passThru, maxCount); 1111 } 1112 1113 1114 void 1115 TruncateLeaf(BString *string) 1116 { 1117 for (int32 index = string->Length(); index >= 0; index--) { 1118 if ((*string)[index] == '/') { 1119 string->Truncate(index + 1); 1120 return; 1121 } 1122 } 1123 } 1124 1125 1126 int64 1127 StringToScalar(const char *text) 1128 { 1129 char *end; 1130 int64 val; 1131 1132 char *buffer = new char [strlen(text) + 1]; 1133 strcpy(buffer, text); 1134 1135 if (strstr(buffer, "k") || strstr(buffer, "K")) { 1136 val = strtoll(buffer, &end, 10); 1137 val *= kKBSize; 1138 } else if (strstr(buffer, "mb") || strstr(buffer, "MB")) { 1139 val = strtoll(buffer, &end, 10); 1140 val *= kMBSize; 1141 } else if (strstr(buffer, "gb") || strstr(buffer, "GB")) { 1142 val = strtoll(buffer, &end, 10); 1143 val *= kGBSize; 1144 } else if (strstr(buffer, "byte") || strstr(buffer, "BYTE")) { 1145 val = strtoll(buffer, &end, 10); 1146 val *= kGBSize; 1147 } else { 1148 // no suffix, try plain byte conversion 1149 val = strtoll(buffer, &end, 10); 1150 } 1151 1152 delete [] buffer; 1153 return val; 1154 } 1155 1156 #if B_BEOS_VERSION <= B_BEOS_VERSION_MAUI && !defined(__HAIKU__) 1157 1158 bool 1159 operator==(const rgb_color &a, const rgb_color &b) 1160 { 1161 return a.red == b.red 1162 && a.green == b.green 1163 && a.blue == b.blue 1164 && a.alpha == b.alpha; 1165 } 1166 1167 1168 bool 1169 operator!=(const rgb_color &a, const rgb_color &b) 1170 { 1171 return !operator==(a, b); 1172 } 1173 1174 #endif 1175 1176 1177 static BRect 1178 LineBounds(BPoint where, float length, bool vertical) 1179 { 1180 BRect result; 1181 result.SetLeftTop(where); 1182 result.SetRightBottom(where + BPoint(2, 2)); 1183 if (vertical) 1184 result.bottom = result.top + length; 1185 else 1186 result.right = result.left + length; 1187 1188 return result; 1189 } 1190 1191 1192 SeparatorLine::SeparatorLine(BPoint where, float length, bool vertical, const char *name) 1193 : BView(LineBounds(where, length, vertical), name, 1194 B_FOLLOW_LEFT | B_FOLLOW_TOP, B_WILL_DRAW) 1195 { 1196 SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 1197 SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 1198 } 1199 1200 1201 void 1202 SeparatorLine::Draw(BRect) 1203 { 1204 BRect bounds(Bounds()); 1205 rgb_color hiliteColor = tint_color(ViewColor(), 1.5f); 1206 1207 bool vertical = (bounds.left > bounds.right - 3); 1208 BeginLineArray(2); 1209 if (vertical) { 1210 AddLine(bounds.LeftTop(), bounds.LeftBottom(), hiliteColor); 1211 AddLine(bounds.LeftTop() + BPoint(1, 0), bounds.LeftBottom() + BPoint(1, 0), kWhite); 1212 } else { 1213 AddLine(bounds.LeftTop(), bounds.RightTop(), hiliteColor); 1214 AddLine(bounds.LeftTop() + BPoint(0, 1), bounds.RightTop() + BPoint(0, 1), kWhite); 1215 } 1216 EndLineArray(); 1217 } 1218 1219 1220 void 1221 HexDump(const void *buf, int32 length) 1222 { 1223 const int32 kBytesPerLine = 16; 1224 int32 offset; 1225 unsigned char *buffer = (unsigned char *)buf; 1226 1227 for (offset = 0; ; offset += kBytesPerLine, buffer += kBytesPerLine) { 1228 int32 remain = length; 1229 int32 index; 1230 1231 printf( "0x%06x: ", (int)offset); 1232 1233 for (index = 0; index < kBytesPerLine; index++) { 1234 if (remain-- > 0) 1235 printf("%02x%c", buffer[index], remain > 0 ? ',' : ' '); 1236 else 1237 printf(" "); 1238 } 1239 1240 remain = length; 1241 printf(" \'"); 1242 for (index = 0; index < kBytesPerLine; index++) { 1243 if (remain-- > 0) 1244 printf("%c", buffer[index] > ' ' ? buffer[index] : '.'); 1245 else 1246 printf(" "); 1247 } 1248 printf("\'\n"); 1249 1250 length -= kBytesPerLine; 1251 if (length <= 0) 1252 break; 1253 } 1254 fflush(stdout); 1255 } 1256 1257 1258 void 1259 EnableNamedMenuItem(BMenu *menu, const char *itemName, bool on) 1260 { 1261 BMenuItem *item = menu->FindItem(itemName); 1262 if (item) 1263 item->SetEnabled(on); 1264 } 1265 1266 1267 void 1268 MarkNamedMenuItem(BMenu *menu, const char *itemName, bool on) 1269 { 1270 BMenuItem *item = menu->FindItem(itemName); 1271 if (item) 1272 item->SetMarked(on); 1273 } 1274 1275 1276 void 1277 EnableNamedMenuItem(BMenu *menu, uint32 commandName, bool on) 1278 { 1279 BMenuItem *item = menu->FindItem(commandName); 1280 if (item) 1281 item->SetEnabled(on); 1282 } 1283 1284 1285 void 1286 MarkNamedMenuItem(BMenu *menu, uint32 commandName, bool on) 1287 { 1288 BMenuItem *item = menu->FindItem(commandName); 1289 if (item) 1290 item->SetMarked(on); 1291 } 1292 1293 1294 void 1295 DeleteSubmenu(BMenuItem *submenuItem) 1296 { 1297 if (!submenuItem) 1298 return; 1299 1300 BMenu *menu = submenuItem->Submenu(); 1301 if (!menu) 1302 return; 1303 1304 for (;;) { 1305 BMenuItem *item = menu->RemoveItem((int32)0); 1306 if (!item) 1307 return; 1308 1309 delete item; 1310 } 1311 } 1312 1313 1314 status_t 1315 GetAppSignatureFromAttr(BFile *file, char *result) 1316 { 1317 // This call is a performance improvement that 1318 // avoids using the BAppFileInfo API when retrieving the 1319 // app signature -- the call is expensive because by default 1320 // the resource fork is scanned to read the attribute 1321 1322 #ifdef B_APP_FILE_INFO_IS_FAST 1323 BAppFileInfo appFileInfo(file); 1324 return appFileInfo.GetSignature(result); 1325 #else 1326 ssize_t readResult = file->ReadAttr(kAttrAppSignature, B_MIME_STRING_TYPE, 1327 0, result, B_MIME_TYPE_LENGTH); 1328 1329 if (readResult <= 0) 1330 return (status_t)readResult; 1331 1332 return B_OK; 1333 #endif // B_APP_FILE_INFO_IS_FAST 1334 } 1335 1336 1337 status_t 1338 GetAppIconFromAttr(BFile *file, BBitmap *result, icon_size size) 1339 { 1340 // This call is a performance improvement that 1341 // avoids using the BAppFileInfo API when retrieving the 1342 // app icons -- the call is expensive because by default 1343 // the resource fork is scanned to read the icons 1344 1345 //#ifdef B_APP_FILE_INFO_IS_FAST 1346 BAppFileInfo appFileInfo(file); 1347 return appFileInfo.GetIcon(result, size); 1348 //#else 1349 // 1350 // const char *attrName = kAttrIcon; 1351 // uint32 type = B_VECTOR_ICON_TYPE; 1352 // 1353 // // try vector icon 1354 // attr_info ainfo; 1355 // status_t ret = file->GetAttrInfo(attrName, &ainfo); 1356 // 1357 // if (ret == B_OK) { 1358 // uint8 buffer[ainfo.size]; 1359 // ssize_t readResult = file->ReadAttr(attrName, type, 0, buffer, 1360 // ainfo.size); 1361 // if (readResult == ainfo.size) { 1362 // if (BIconUtils::GetVectorIcon(buffer, ainfo.size, result) == B_OK) 1363 // return B_OK; 1364 // } 1365 // } 1366 // 1367 // // try again with R5 icons 1368 // attrName = size == B_LARGE_ICON ? kAttrLargeIcon : kAttrMiniIcon; 1369 // type = size == B_LARGE_ICON ? LARGE_ICON_TYPE : MINI_ICON_TYPE; 1370 // 1371 // ret = file->GetAttrInfo(attrName, &ainfo); 1372 // if (ret < B_OK) 1373 // return ret; 1374 // 1375 // uint8 buffer[ainfo.size]; 1376 // 1377 // ssize_t readResult = file->ReadAttr(attrName, type, 0, buffer, ainfo.size); 1378 // if (readResult <= 0) 1379 // return (status_t)readResult; 1380 // 1381 // if (result->ColorSpace() != B_CMAP8) { 1382 // ret = BIconUtils::ConvertFromCMAP8(buffer, size, size, size, result); 1383 // } else { 1384 // result->SetBits(buffer, result->BitsLength(), 0, B_CMAP8); 1385 // } 1386 // 1387 // return ret; 1388 //#endif // B_APP_FILE_INFO_IS_FAST 1389 } 1390 1391 1392 status_t 1393 GetFileIconFromAttr(BNode *file, BBitmap *result, icon_size size) 1394 { 1395 BNodeInfo fileInfo(file); 1396 return fileInfo.GetIcon(result, size); 1397 } 1398 1399 1400 void 1401 PrintToStream(rgb_color color) 1402 { 1403 printf("r:%x, g:%x, b:%x, a:%x\n", 1404 color.red, color.green, color.blue, color.alpha); 1405 } 1406 1407 1408 extern BMenuItem * 1409 EachMenuItem(BMenu *menu, bool recursive, BMenuItem *(*func)(BMenuItem *)) 1410 { 1411 int32 count = menu->CountItems(); 1412 for (int32 index = 0; index < count; index++) { 1413 BMenuItem *item = menu->ItemAt(index); 1414 BMenuItem *result = (func)(item); 1415 if (result) 1416 return result; 1417 1418 if (recursive) { 1419 BMenu *submenu = menu->SubmenuAt(index); 1420 if (submenu) 1421 return EachMenuItem(submenu, true, func); 1422 } 1423 } 1424 1425 return NULL; 1426 } 1427 1428 1429 extern const BMenuItem * 1430 EachMenuItem(const BMenu *menu, bool recursive, BMenuItem *(*func)(const BMenuItem *)) 1431 { 1432 int32 count = menu->CountItems(); 1433 for (int32 index = 0; index < count; index++) { 1434 BMenuItem *item = menu->ItemAt(index); 1435 BMenuItem *result = (func)(item); 1436 if (result) 1437 return result; 1438 1439 if (recursive) { 1440 BMenu *submenu = menu->SubmenuAt(index); 1441 if (submenu) 1442 return EachMenuItem(submenu, true, func); 1443 } 1444 } 1445 1446 return NULL; 1447 } 1448 1449 1450 PositionPassingMenuItem::PositionPassingMenuItem(const char *title, 1451 BMessage *message, char shortcut, uint32 modifiers) 1452 : BMenuItem(title, message, shortcut, modifiers) 1453 { 1454 } 1455 1456 1457 PositionPassingMenuItem::PositionPassingMenuItem(BMenu *menu, 1458 BMessage *message) 1459 : BMenuItem(menu, message) 1460 { 1461 } 1462 1463 1464 status_t 1465 PositionPassingMenuItem::Invoke(BMessage *message) 1466 { 1467 if (!Menu()) 1468 return B_ERROR; 1469 1470 if (!IsEnabled()) 1471 return B_ERROR; 1472 1473 if (!message) 1474 message = Message(); 1475 1476 if (!message) 1477 return B_BAD_VALUE; 1478 1479 BMessage clone(*message); 1480 clone.AddInt32("index", Menu()->IndexOf(this)); 1481 clone.AddInt64("when", system_time()); 1482 clone.AddPointer("source", this); 1483 1484 // embed the invoke location of the menu so that we can create 1485 // a new folder, etc. on the spot 1486 BMenu *menu = Menu(); 1487 1488 for (;;) { 1489 if (!menu->Supermenu()) 1490 break; 1491 menu = menu->Supermenu(); 1492 } 1493 1494 // use the window position only, if the item was invoked from the menu 1495 // menu->Window() points to the window the item was invoked from 1496 if (dynamic_cast<BContainerWindow *>(menu->Window()) == NULL) { 1497 LooperAutoLocker lock(menu); 1498 if (lock.IsLocked()) { 1499 BPoint invokeOrigin(menu->Window()->Frame().LeftTop()); 1500 clone.AddPoint("be:invoke_origin", invokeOrigin); 1501 } 1502 } 1503 1504 return BInvoker::Invoke(&clone); 1505 } 1506 1507 1508 bool 1509 BootedInSafeMode() 1510 { 1511 const char *safeMode = getenv("SAFEMODE"); 1512 return (safeMode && strcmp(safeMode, "yes") == 0); 1513 } 1514 1515 1516 void 1517 _ThrowOnError(status_t error, const char *DEBUG_ONLY(file), int32 DEBUG_ONLY(line)) 1518 { 1519 if (error != B_OK) { 1520 PRINT(("failing %s at %s:%d\n", strerror(error), file, line)); 1521 throw error; 1522 } 1523 } 1524 1525 1526 void 1527 _ThrowIfNotSize(ssize_t size, const char *DEBUG_ONLY(file), int32 DEBUG_ONLY(line)) 1528 { 1529 if (size < B_OK) { 1530 PRINT(("failing %s at %s:%d\n", strerror(size), file, line)); 1531 throw (status_t)size; 1532 } 1533 } 1534 1535 1536 void 1537 _ThrowOnError(status_t error, const char *DEBUG_ONLY(debugString), 1538 const char *DEBUG_ONLY(file), int32 DEBUG_ONLY(line)) 1539 { 1540 if (error != B_OK) { 1541 PRINT(("failing %s, %s at %s:%d\n", debugString, strerror(error), file, line)); 1542 throw error; 1543 } 1544 } 1545 1546 } // namespace BPrivate 1547