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