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 BBitmap(bounds, B_RGB32, true); 413 if (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 %d\n", length)); 1078 return; 1079 } 1080 1081 char *buffer = string->LockBuffer(length + 1); 1082 stream->Read(buffer, (size_t)length + 1); 1083 string->UnlockBuffer(length); 1084 } 1085 1086 1087 void 1088 StringToStream(const BString *string, BMallocIO *stream) 1089 { 1090 int32 length = string->Length(); 1091 stream->Write(&length, sizeof(int32)); 1092 stream->Write(string->String(), (size_t)string->Length() + 1); 1093 } 1094 1095 1096 int32 1097 ArchiveSize(const BString *string) 1098 { 1099 return string->Length() + 1 + (ssize_t)sizeof(int32); 1100 } 1101 1102 1103 int32 1104 CountRefs(const BMessage *message) 1105 { 1106 uint32 type; 1107 int32 count; 1108 message->GetInfo("refs", &type, &count); 1109 1110 return count; 1111 } 1112 1113 1114 static entry_ref * 1115 EachEntryRefCommon(BMessage *message, entry_ref *(*func)(entry_ref *, void *), 1116 void *passThru, int32 maxCount) 1117 { 1118 uint32 type; 1119 int32 count; 1120 message->GetInfo("refs", &type, &count); 1121 1122 if (maxCount >= 0 && count > maxCount) 1123 count = maxCount; 1124 1125 for (int32 index = 0; index < count; index++) { 1126 entry_ref ref; 1127 message->FindRef("refs", index, &ref); 1128 entry_ref *result = (func)(&ref, passThru); 1129 if (result) 1130 return result; 1131 } 1132 1133 return NULL; 1134 } 1135 1136 1137 bool 1138 ContainsEntryRef(const BMessage *message, const entry_ref *ref) 1139 { 1140 entry_ref match; 1141 for (int32 index = 0; (message->FindRef("refs", index, &match) == B_OK); index++) { 1142 if (*ref == match) 1143 return true; 1144 } 1145 1146 return false; 1147 } 1148 1149 1150 entry_ref * 1151 EachEntryRef(BMessage *message, entry_ref *(*func)(entry_ref *, void *), 1152 void *passThru) 1153 { 1154 return EachEntryRefCommon(message, func, passThru, -1); 1155 } 1156 1157 typedef entry_ref *(*EachEntryIteratee)(entry_ref *, void *); 1158 1159 const entry_ref * 1160 EachEntryRef(const BMessage *message, const entry_ref *(*func)(const entry_ref *, void *), 1161 void *passThru) 1162 { 1163 return EachEntryRefCommon(const_cast<BMessage *>(message), 1164 (EachEntryIteratee)func, passThru, -1); 1165 } 1166 1167 1168 entry_ref * 1169 EachEntryRef(BMessage *message, entry_ref *(*func)(entry_ref *, void *), 1170 void *passThru, int32 maxCount) 1171 { 1172 return EachEntryRefCommon(message, func, passThru, maxCount); 1173 } 1174 1175 1176 const entry_ref * 1177 EachEntryRef(const BMessage *message, const entry_ref *(*func)(const entry_ref *, void *), 1178 void *passThru, int32 maxCount) 1179 { 1180 return EachEntryRefCommon(const_cast<BMessage *>(message), 1181 (EachEntryIteratee)func, passThru, maxCount); 1182 } 1183 1184 1185 void 1186 TruncateLeaf(BString *string) 1187 { 1188 for (int32 index = string->Length(); index >= 0; index--) { 1189 if ((*string)[index] == '/') { 1190 string->Truncate(index + 1); 1191 return; 1192 } 1193 } 1194 } 1195 1196 1197 int64 1198 StringToScalar(const char *text) 1199 { 1200 char *end; 1201 int64 val; 1202 1203 char *buffer = new char [strlen(text) + 1]; 1204 strcpy(buffer, text); 1205 1206 if (strstr(buffer, "k") || strstr(buffer, "K")) { 1207 val = strtoll(buffer, &end, 10); 1208 val *= kKBSize; 1209 } else if (strstr(buffer, "mb") || strstr(buffer, "MB")) { 1210 val = strtoll(buffer, &end, 10); 1211 val *= kMBSize; 1212 } else if (strstr(buffer, "gb") || strstr(buffer, "GB")) { 1213 val = strtoll(buffer, &end, 10); 1214 val *= kGBSize; 1215 } else if (strstr(buffer, "byte") || strstr(buffer, "BYTE")) { 1216 val = strtoll(buffer, &end, 10); 1217 val *= kGBSize; 1218 } else { 1219 // no suffix, try plain byte conversion 1220 val = strtoll(buffer, &end, 10); 1221 } 1222 1223 delete [] buffer; 1224 return val; 1225 } 1226 1227 #if B_BEOS_VERSION <= B_BEOS_VERSION_MAUI && !defined(__HAIKU__) 1228 1229 bool 1230 operator==(const rgb_color &a, const rgb_color &b) 1231 { 1232 return a.red == b.red 1233 && a.green == b.green 1234 && a.blue == b.blue 1235 && a.alpha == b.alpha; 1236 } 1237 1238 1239 bool 1240 operator!=(const rgb_color &a, const rgb_color &b) 1241 { 1242 return !operator==(a, b); 1243 } 1244 1245 #endif 1246 1247 1248 static BRect 1249 LineBounds(BPoint where, float length, bool vertical) 1250 { 1251 BRect result; 1252 result.SetLeftTop(where); 1253 result.SetRightBottom(where + BPoint(2, 2)); 1254 if (vertical) 1255 result.bottom = result.top + length; 1256 else 1257 result.right = result.left + length; 1258 1259 return result; 1260 } 1261 1262 1263 SeparatorLine::SeparatorLine(BPoint where, float length, bool vertical, const char *name) 1264 : BView(LineBounds(where, length, vertical), name, 1265 B_FOLLOW_LEFT | B_FOLLOW_TOP, B_WILL_DRAW) 1266 { 1267 SetViewColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 1268 SetLowColor(ui_color(B_PANEL_BACKGROUND_COLOR)); 1269 } 1270 1271 1272 void 1273 SeparatorLine::Draw(BRect) 1274 { 1275 BRect bounds(Bounds()); 1276 rgb_color hiliteColor = tint_color(ViewColor(), 1.5f); 1277 1278 bool vertical = (bounds.left > bounds.right - 3); 1279 BeginLineArray(2); 1280 if (vertical) { 1281 AddLine(bounds.LeftTop(), bounds.LeftBottom(), hiliteColor); 1282 AddLine(bounds.LeftTop() + BPoint(1, 0), bounds.LeftBottom() + BPoint(1, 0), kWhite); 1283 } else { 1284 AddLine(bounds.LeftTop(), bounds.RightTop(), hiliteColor); 1285 AddLine(bounds.LeftTop() + BPoint(0, 1), bounds.RightTop() + BPoint(0, 1), kWhite); 1286 } 1287 EndLineArray(); 1288 } 1289 1290 1291 void 1292 HexDump(const void *buf, int32 length) 1293 { 1294 const int32 kBytesPerLine = 16; 1295 int32 offset; 1296 unsigned char *buffer = (unsigned char *)buf; 1297 1298 for (offset = 0; ; offset += kBytesPerLine, buffer += kBytesPerLine) { 1299 int32 remain = length; 1300 int32 index; 1301 1302 printf( "0x%06x: ", (int)offset); 1303 1304 for (index = 0; index < kBytesPerLine; index++) { 1305 if (remain-- > 0) 1306 printf("%02x%c", buffer[index], remain > 0 ? ',' : ' '); 1307 else 1308 printf(" "); 1309 } 1310 1311 remain = length; 1312 printf(" \'"); 1313 for (index = 0; index < kBytesPerLine; index++) { 1314 if (remain-- > 0) 1315 printf("%c", buffer[index] > ' ' ? buffer[index] : '.'); 1316 else 1317 printf(" "); 1318 } 1319 printf("\'\n"); 1320 1321 length -= kBytesPerLine; 1322 if (length <= 0) 1323 break; 1324 } 1325 fflush(stdout); 1326 } 1327 1328 1329 void 1330 EnableNamedMenuItem(BMenu *menu, const char *itemName, bool on) 1331 { 1332 BMenuItem *item = menu->FindItem(itemName); 1333 if (item) 1334 item->SetEnabled(on); 1335 } 1336 1337 1338 void 1339 MarkNamedMenuItem(BMenu *menu, const char *itemName, bool on) 1340 { 1341 BMenuItem *item = menu->FindItem(itemName); 1342 if (item) 1343 item->SetMarked(on); 1344 } 1345 1346 1347 void 1348 EnableNamedMenuItem(BMenu *menu, uint32 commandName, bool on) 1349 { 1350 BMenuItem *item = menu->FindItem(commandName); 1351 if (item) 1352 item->SetEnabled(on); 1353 } 1354 1355 1356 void 1357 MarkNamedMenuItem(BMenu *menu, uint32 commandName, bool on) 1358 { 1359 BMenuItem *item = menu->FindItem(commandName); 1360 if (item) 1361 item->SetMarked(on); 1362 } 1363 1364 1365 void 1366 DeleteSubmenu(BMenuItem *submenuItem) 1367 { 1368 if (!submenuItem) 1369 return; 1370 1371 BMenu *menu = submenuItem->Submenu(); 1372 if (!menu) 1373 return; 1374 1375 for (;;) { 1376 BMenuItem *item = menu->RemoveItem((int32)0); 1377 if (!item) 1378 return; 1379 1380 delete item; 1381 } 1382 } 1383 1384 1385 status_t 1386 GetAppSignatureFromAttr(BFile *file, char *result) 1387 { 1388 // This call is a performance improvement that 1389 // avoids using the BAppFileInfo API when retrieving the 1390 // app signature -- the call is expensive because by default 1391 // the resource fork is scanned to read the attribute 1392 1393 #ifdef B_APP_FILE_INFO_IS_FAST 1394 BAppFileInfo appFileInfo(file); 1395 return appFileInfo.GetSignature(result); 1396 #else 1397 ssize_t readResult = file->ReadAttr(kAttrAppSignature, B_MIME_STRING_TYPE, 1398 0, result, B_MIME_TYPE_LENGTH); 1399 1400 if (readResult <= 0) 1401 return (status_t)readResult; 1402 1403 return B_OK; 1404 #endif // B_APP_FILE_INFO_IS_FAST 1405 } 1406 1407 1408 status_t 1409 GetAppIconFromAttr(BFile *file, BBitmap *result, icon_size size) 1410 { 1411 // This call is a performance improvement that 1412 // avoids using the BAppFileInfo API when retrieving the 1413 // app icons -- the call is expensive because by default 1414 // the resource fork is scanned to read the icons 1415 1416 //#ifdef B_APP_FILE_INFO_IS_FAST 1417 BAppFileInfo appFileInfo(file); 1418 return appFileInfo.GetIcon(result, size); 1419 //#else 1420 // 1421 // const char *attrName = kAttrIcon; 1422 // uint32 type = B_VECTOR_ICON_TYPE; 1423 // 1424 // // try vector icon 1425 // attr_info ainfo; 1426 // status_t ret = file->GetAttrInfo(attrName, &ainfo); 1427 // 1428 // if (ret == B_OK) { 1429 // uint8 buffer[ainfo.size]; 1430 // ssize_t readResult = file->ReadAttr(attrName, type, 0, buffer, 1431 // ainfo.size); 1432 // if (readResult == ainfo.size) { 1433 // if (BIconUtils::GetVectorIcon(buffer, ainfo.size, result) == B_OK) 1434 // return B_OK; 1435 // } 1436 // } 1437 // 1438 // // try again with R5 icons 1439 // attrName = size == B_LARGE_ICON ? kAttrLargeIcon : kAttrMiniIcon; 1440 // type = size == B_LARGE_ICON ? LARGE_ICON_TYPE : MINI_ICON_TYPE; 1441 // 1442 // ret = file->GetAttrInfo(attrName, &ainfo); 1443 // if (ret < B_OK) 1444 // return ret; 1445 // 1446 // uint8 buffer[ainfo.size]; 1447 // 1448 // ssize_t readResult = file->ReadAttr(attrName, type, 0, buffer, ainfo.size); 1449 // if (readResult <= 0) 1450 // return (status_t)readResult; 1451 // 1452 // if (result->ColorSpace() != B_CMAP8) { 1453 // ret = BIconUtils::ConvertFromCMAP8(buffer, size, size, size, result); 1454 // } else { 1455 // result->SetBits(buffer, result->BitsLength(), 0, B_CMAP8); 1456 // } 1457 // 1458 // return ret; 1459 //#endif // B_APP_FILE_INFO_IS_FAST 1460 } 1461 1462 1463 status_t 1464 GetFileIconFromAttr(BNode *file, BBitmap *result, icon_size size) 1465 { 1466 BNodeInfo fileInfo(file); 1467 return fileInfo.GetIcon(result, size); 1468 } 1469 1470 1471 void 1472 PrintToStream(rgb_color color) 1473 { 1474 printf("r:%x, g:%x, b:%x, a:%x\n", 1475 color.red, color.green, color.blue, color.alpha); 1476 } 1477 1478 1479 extern BMenuItem * 1480 EachMenuItem(BMenu *menu, bool recursive, BMenuItem *(*func)(BMenuItem *)) 1481 { 1482 int32 count = menu->CountItems(); 1483 for (int32 index = 0; index < count; index++) { 1484 BMenuItem *item = menu->ItemAt(index); 1485 BMenuItem *result = (func)(item); 1486 if (result) 1487 return result; 1488 1489 if (recursive) { 1490 BMenu *submenu = menu->SubmenuAt(index); 1491 if (submenu) 1492 return EachMenuItem(submenu, true, func); 1493 } 1494 } 1495 1496 return NULL; 1497 } 1498 1499 1500 extern const BMenuItem * 1501 EachMenuItem(const BMenu *menu, bool recursive, BMenuItem *(*func)(const BMenuItem *)) 1502 { 1503 int32 count = menu->CountItems(); 1504 for (int32 index = 0; index < count; index++) { 1505 BMenuItem *item = menu->ItemAt(index); 1506 BMenuItem *result = (func)(item); 1507 if (result) 1508 return result; 1509 1510 if (recursive) { 1511 BMenu *submenu = menu->SubmenuAt(index); 1512 if (submenu) 1513 return EachMenuItem(submenu, true, func); 1514 } 1515 } 1516 1517 return NULL; 1518 } 1519 1520 1521 PositionPassingMenuItem::PositionPassingMenuItem(const char *title, 1522 BMessage *message, char shortcut, uint32 modifiers) 1523 : BMenuItem(title, message, shortcut, modifiers) 1524 { 1525 } 1526 1527 1528 PositionPassingMenuItem::PositionPassingMenuItem(BMenu *menu, 1529 BMessage *message) 1530 : BMenuItem(menu, message) 1531 { 1532 } 1533 1534 1535 status_t 1536 PositionPassingMenuItem::Invoke(BMessage *message) 1537 { 1538 if (!Menu()) 1539 return B_ERROR; 1540 1541 if (!IsEnabled()) 1542 return B_ERROR; 1543 1544 if (!message) 1545 message = Message(); 1546 1547 if (!message) 1548 return B_BAD_VALUE; 1549 1550 BMessage clone(*message); 1551 clone.AddInt32("index", Menu()->IndexOf(this)); 1552 clone.AddInt64("when", system_time()); 1553 clone.AddPointer("source", this); 1554 1555 // embed the invoke location of the menu so that we can create 1556 // a new folder, etc. on the spot 1557 BMenu *menu = Menu(); 1558 1559 for (;;) { 1560 if (!menu->Supermenu()) 1561 break; 1562 menu = menu->Supermenu(); 1563 } 1564 1565 // use the window position only, if the item was invoked from the menu 1566 // menu->Window() points to the window the item was invoked from 1567 if (dynamic_cast<BContainerWindow *>(menu->Window()) == NULL) { 1568 LooperAutoLocker lock(menu); 1569 if (lock.IsLocked()) { 1570 BPoint invokeOrigin(menu->Window()->Frame().LeftTop()); 1571 clone.AddPoint("be:invoke_origin", invokeOrigin); 1572 } 1573 } 1574 1575 return BInvoker::Invoke(&clone); 1576 } 1577 1578 1579 bool 1580 BootedInSafeMode() 1581 { 1582 const char *safeMode = getenv("SAFEMODE"); 1583 return (safeMode && strcmp(safeMode, "yes") == 0); 1584 } 1585 1586 1587 float 1588 ComputeTypeAheadScore(const char *text, const char *match, size_t matchLength, 1589 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, 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, 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, line)); 1663 throw error; 1664 } 1665 } 1666 1667 } // namespace BPrivate 1668