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