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 #ifndef _UTILITIES_H 36 #define _UTILITIES_H 37 38 39 #include <ctype.h> 40 #include <stdarg.h> 41 #include <stdlib.h> 42 43 #include <ByteOrder.h> 44 #include <Bitmap.h> 45 #include <DataIO.h> 46 #include <Directory.h> 47 #include <Entry.h> 48 #include <Font.h> 49 #include <GraphicsDefs.h> 50 #include <Looper.h> 51 #include <MenuItem.h> 52 #include <MessageFilter.h> 53 #include <Mime.h> 54 #include <ObjectList.h> 55 #include <Point.h> 56 #include <Path.h> 57 #include <String.h> 58 #include <StringView.h> 59 60 61 class BMessage; 62 class BVolume; 63 class BBitmap; 64 class BTextView; 65 class BView; 66 67 namespace BPrivate { 68 69 class Benaphore; 70 class BPose; 71 class BPoseView; 72 73 // global variables 74 extern const rgb_color kBlack; 75 extern const rgb_color kWhite; 76 77 const int64 kHalfKBSize = 512; 78 const int64 kKBSize = 1024; 79 const int64 kMBSize = 1048576; 80 const int64 kGBSize = 1073741824; 81 const int64 kTBSize = kGBSize * kKBSize; 82 83 const int32 kMiniIconSeparator = 3; 84 85 #ifdef __HAIKU__ 86 const color_space kDefaultIconDepth = B_RGBA32; 87 #else 88 const color_space kDefaultIconDepth = B_CMAP8; 89 #endif 90 91 // misc typedefs, constants and structs 92 93 // Periodically updated poses (ones with a volume space bar) register 94 // themselfs in this global list. This way they can be iterated over instead 95 // of sending around update messages. 96 97 class PeriodicUpdatePoses { 98 public: 99 PeriodicUpdatePoses(); 100 ~PeriodicUpdatePoses(); 101 102 typedef bool (*PeriodicUpdateCallback)(BPose *pose, void *cookie); 103 104 void AddPose(BPose *pose, BPoseView *poseView, 105 PeriodicUpdateCallback callback, void *cookie); 106 bool RemovePose(BPose *pose, void **cookie); 107 108 void DoPeriodicUpdate(bool forceRedraw); 109 110 private: 111 struct periodic_pose { 112 BPose *pose; 113 BPoseView *pose_view; 114 PeriodicUpdateCallback callback; 115 void *cookie; 116 }; 117 118 Benaphore *fLock; 119 BObjectList<periodic_pose> fPoseList; 120 }; 121 122 extern PeriodicUpdatePoses gPeriodicUpdatePoses; 123 124 125 // PoseInfo is the structure that gets saved as attributes for every node on 126 // disk, defining the node's position and visibility 127 class PoseInfo { 128 public: 129 static void EndianSwap(void *castToThis); 130 void PrintToStream(); 131 132 bool fInvisible; 133 ino_t fInitedDirectory; 134 // for a location to be valid, fInitedDirectory has to contain the inode 135 // of the items parent directory 136 // This makes it impossible to for instance zip up files and extract 137 // them in the same location. This should probably be reworked -- Tracker 138 // could say strip the file location attributes when dropping files into 139 // a closed folder 140 BPoint fLocation; 141 }; 142 143 144 // extends PoseInfo adding workspace support; used for desktop 145 // poses only 146 class ExtendedPoseInfo { 147 public: 148 size_t Size() const; 149 static size_t Size(int32); 150 size_t SizeWithHeadroom() const; 151 static size_t SizeWithHeadroom(size_t); 152 bool HasLocationForFrame(BRect) const; 153 BPoint LocationForFrame(BRect) const; 154 bool SetLocationForFrame(BPoint, BRect); 155 156 static void EndianSwap(void *castToThis); 157 void PrintToStream(); 158 159 uint32 fWorkspaces; 160 bool fInvisible; 161 bool fShowFromBootOnly; 162 bool fReservedBool1; 163 bool fReservedBool2; 164 int32 fReservedInt1; 165 int32 fReservedInt2; 166 int32 fReservedInt3; 167 int32 fReservedInt4; 168 int32 fReservedInt5; 169 170 int32 fNumFrames; 171 struct FrameLocation { 172 BPoint fLocation; 173 BRect fFrame; 174 uint32 fWorkspaces; 175 }; 176 177 FrameLocation fLocations[0]; 178 }; 179 180 // misc functions 181 void DisallowMetaKeys(BTextView *); 182 void DisallowFilenameKeys(BTextView *); 183 184 185 // #pragma mark - Natural sorting 186 187 188 struct natural_chunk { 189 enum chunk_type { 190 NUMBER, 191 ASCII, 192 END 193 }; 194 chunk_type type; 195 char buffer[B_FILE_NAME_LENGTH]; 196 int32 length; 197 }; 198 199 200 inline int32 201 FetchNaturalChunk(natural_chunk& chunk, const char* source) 202 { 203 if (chunk.type == natural_chunk::ASCII) { 204 // string chunk 205 int32 pos = 0; 206 while (!isdigit(source[pos]) && !isspace(source[pos]) 207 && source[pos] != '\0') { 208 pos++; 209 } 210 strlcpy(chunk.buffer, source, pos + 1); 211 chunk.length = pos; 212 return pos; 213 } 214 215 // skip leading zeros and whitespace characters 216 int32 skip = 0; 217 while (source[0] == '0' || isspace(source[0])) { 218 source++; 219 skip++; 220 } 221 222 // number chunk (stop at next white space) 223 int32 pos = 0; 224 while (isdigit(source[pos]) && source[pos] != '\0') { 225 pos++; 226 } 227 strlcpy(&chunk.buffer[sizeof(chunk.buffer) - 1 - pos], source, pos + 1); 228 chunk.length = pos; 229 230 return pos + skip; 231 } 232 233 234 //! Makes sure both number strings have the same size 235 inline void 236 NormalizeNumberChunks(natural_chunk& a, natural_chunk& b) 237 { 238 if (a.length > b.length) { 239 memset(&b.buffer[sizeof(b.buffer) - 1 - a.length], ' ', 240 a.length - b.length); 241 b.length = a.length; 242 } else if (b.length > a.length) { 243 memset(&a.buffer[sizeof(a.buffer) - 1 - b.length], ' ', 244 b.length - a.length); 245 a.length = b.length; 246 } 247 } 248 249 250 //! Compares two strings naturally, as opposed to lexicographically 251 inline int 252 NaturalCompare(const char* stringA, const char* stringB) 253 { 254 natural_chunk a; 255 natural_chunk b; 256 257 uint32 indexA = 0; 258 uint32 indexB = 0; 259 260 while (true) { 261 // Determine type of next chunks in each string based on first char 262 if (stringA[indexA] == '\0') 263 a.type = natural_chunk::END; 264 else if (isdigit(stringA[indexA]) || isspace(stringA[indexA])) 265 a.type = natural_chunk::NUMBER; 266 else 267 a.type = natural_chunk::ASCII; 268 269 if (stringB[indexB] == '\0') 270 b.type = natural_chunk::END; 271 else if (isdigit(stringB[indexB]) || isspace(stringB[indexB])) 272 b.type = natural_chunk::NUMBER; 273 else 274 b.type = natural_chunk::ASCII; 275 276 // Check if we reached the end of either string 277 if (a.type == natural_chunk::END) 278 return b.type == natural_chunk::END ? 0 : -1; 279 if (b.type == natural_chunk::END) 280 return 1; 281 282 if (a.type != b.type) { 283 // Different chunk types, just compare the remaining strings 284 return strcasecmp(&stringA[indexA], &stringB[indexB]); 285 } 286 287 // Fetch the next chunks 288 indexA += FetchNaturalChunk(a, &stringA[indexA]); 289 indexB += FetchNaturalChunk(b, &stringB[indexB]); 290 291 // Compare the two chunks based on their type 292 if (a.type == natural_chunk::ASCII) { 293 // String chunks 294 int result = strcasecmp(a.buffer, b.buffer); 295 if (result != 0) 296 return result; 297 } else { 298 // Number chunks - they are compared as strings to allow an 299 // arbitrary number of digits. 300 NormalizeNumberChunks(a, b); 301 302 int result = strcmp(a.buffer - 1 + sizeof(a.buffer) - a.length, 303 b.buffer - 1 + sizeof(b.buffer) - b.length); 304 if (result != 0) 305 return result; 306 } 307 308 // The chunks were equal, proceed with the next chunk 309 } 310 311 return 0; 312 } 313 314 315 bool ValidateStream(BMallocIO *, uint32, int32 version); 316 317 318 uint32 HashString(const char *string, uint32 seed); 319 uint32 AttrHashString(const char *string, uint32 type); 320 321 322 class OffscreenBitmap { 323 // a utility class for setting up offscreen bitmaps 324 public: 325 OffscreenBitmap(BRect bounds); 326 OffscreenBitmap(); 327 ~OffscreenBitmap(); 328 329 BView *BeginUsing(BRect bounds); 330 void DoneUsing(); 331 BBitmap *Bitmap() const; 332 // blit this to your view when you are done rendering 333 BView *View() const; 334 // use this to render your image 335 336 private: 337 void NewBitmap(BRect frame); 338 BBitmap *fBitmap; 339 }; 340 341 342 // bitmap functions 343 extern void FadeRGBA32Horizontal(uint32 *bits, int32 width, int32 height, int32 from, int32 to); 344 extern void FadeRGBA32Vertical(uint32 *bits, int32 width, int32 height, int32 from, int32 to); 345 346 347 class FlickerFreeStringView : public BStringView { 348 // Adds support for offscreen bitmap drawing for string views that update often 349 // this would be better implemented as an option of BStringView 350 public: 351 FlickerFreeStringView(BRect bounds, const char *name, 352 const char *text, uint32 resizeFlags = B_FOLLOW_LEFT | B_FOLLOW_TOP, 353 uint32 flags = B_WILL_DRAW); 354 FlickerFreeStringView(BRect bounds, const char *name, 355 const char *text, BBitmap *existingOffscreen, 356 uint32 resizeFlags = B_FOLLOW_LEFT | B_FOLLOW_TOP, 357 uint32 flags = B_WILL_DRAW); 358 virtual ~FlickerFreeStringView(); 359 virtual void Draw(BRect); 360 virtual void AttachedToWindow(); 361 virtual void SetViewColor(rgb_color); 362 virtual void SetLowColor(rgb_color); 363 364 private: 365 OffscreenBitmap *fBitmap; 366 rgb_color fViewColor; 367 rgb_color fLowColor; 368 BBitmap *fOrigBitmap; 369 370 typedef BStringView _inherited; 371 }; 372 373 374 class DraggableIcon : public BView { 375 // used to determine a save location for a file 376 public: 377 DraggableIcon(BRect, const char *, const char *mimeType, icon_size, 378 const BMessage *, BMessenger, 379 uint32 resizeFlags = B_FOLLOW_LEFT | B_FOLLOW_TOP, 380 uint32 flags = B_WILL_DRAW); 381 virtual ~DraggableIcon(); 382 383 static BRect PreferredRect(BPoint offset, icon_size); 384 void SetTarget(BMessenger); 385 386 protected: 387 virtual void AttachedToWindow(); 388 virtual void MouseDown(BPoint); 389 virtual void Draw(BRect); 390 391 virtual bool DragStarted(BMessage *dragMessage); 392 393 protected: 394 BBitmap *fBitmap; 395 BMessage fMessage; 396 BMessenger fTarget; 397 }; 398 399 400 class PositionPassingMenuItem : public BMenuItem { 401 public: 402 PositionPassingMenuItem(const char *title, BMessage *, char shortcut = 0, 403 uint32 modifiers = 0); 404 405 PositionPassingMenuItem(BMenu *, BMessage *); 406 407 protected: 408 virtual status_t Invoke(BMessage * = 0); 409 // appends the invoke location for NewFolder, etc. to use 410 411 private: 412 typedef BMenuItem _inherited; 413 }; 414 415 416 class Benaphore { 417 // aka benaphore 418 public: 419 Benaphore(const char *name = "Light Lock") 420 : fSemaphore(create_sem(0, name)), 421 fCount(1) 422 { 423 } 424 425 ~Benaphore() 426 { 427 delete_sem(fSemaphore); 428 } 429 430 bool Lock() 431 { 432 if (atomic_add(&fCount, -1) <= 0) 433 return acquire_sem(fSemaphore) == B_OK; 434 435 return true; 436 } 437 438 void Unlock() 439 { 440 if (atomic_add(&fCount, 1) < 0) 441 release_sem(fSemaphore); 442 } 443 444 bool IsLocked() const 445 { 446 return fCount <= 0; 447 } 448 449 private: 450 sem_id fSemaphore; 451 int32 fCount; 452 }; 453 454 455 class SeparatorLine : public BView { 456 public: 457 SeparatorLine(BPoint , float , bool vertical, const char *name = ""); 458 virtual void Draw(BRect bounds); 459 }; 460 461 462 class TitledSeparatorItem : public BMenuItem { 463 public: 464 TitledSeparatorItem(const char *); 465 virtual ~TitledSeparatorItem(); 466 467 virtual void SetEnabled(bool state); 468 469 protected: 470 virtual void GetContentSize(float *width, float *height); 471 virtual void Draw(); 472 473 private: 474 typedef BMenuItem _inherited; 475 }; 476 477 478 class LooperAutoLocker { 479 public: 480 LooperAutoLocker(BHandler *handler) 481 : fHandler(handler), 482 fHasLock(handler->LockLooper()) 483 { 484 } 485 486 ~LooperAutoLocker() 487 { 488 if (fHasLock) 489 fHandler->UnlockLooper(); 490 } 491 492 bool operator!() const 493 { 494 return !fHasLock; 495 } 496 497 bool IsLocked() const 498 { 499 return fHasLock; 500 } 501 502 private: 503 BHandler *fHandler; 504 bool fHasLock; 505 }; 506 507 508 class MessengerAutoLocker { 509 // move this into AutoLock.h 510 public: 511 MessengerAutoLocker(BMessenger *messenger) 512 : fMessenger(messenger), 513 fHasLock(messenger->LockTarget()) 514 { } 515 516 ~MessengerAutoLocker() 517 { 518 Unlock(); 519 } 520 521 bool operator!() const 522 { 523 return !fHasLock; 524 } 525 526 bool IsLocked() const 527 { 528 return fHasLock; 529 } 530 531 void Unlock() 532 { 533 if (fHasLock) { 534 BLooper *looper; 535 fMessenger->Target(&looper); 536 if (looper) 537 looper->Unlock(); 538 fHasLock = false; 539 } 540 } 541 542 private: 543 BMessenger *fMessenger; 544 bool fHasLock; 545 }; 546 547 548 class ShortcutFilter : public BMessageFilter { 549 public: 550 ShortcutFilter(uint32 shortcutKey, uint32 shortcutModifier, 551 uint32 shortcutWhat, BHandler *target); 552 553 protected: 554 filter_result Filter(BMessage *, BHandler **); 555 556 private: 557 uint32 fShortcutKey; 558 uint32 fShortcutModifier; 559 uint32 fShortcutWhat; 560 BHandler *fTarget; 561 }; 562 563 // iterates over all the refs in a message 564 entry_ref *EachEntryRef(BMessage *, entry_ref *(*)(entry_ref *, void *), 565 void *passThru = 0); 566 const entry_ref *EachEntryRef(const BMessage *, 567 const entry_ref *(*)(const entry_ref *, void *), void *passThru = 0); 568 569 entry_ref *EachEntryRef(BMessage *, entry_ref *(*)(entry_ref *, void *), 570 void *passThru, int32 maxCount); 571 const entry_ref *EachEntryRef(const BMessage *, 572 const entry_ref *(*)(const entry_ref *, void *), void *passThru, int32 maxCount); 573 574 575 bool ContainsEntryRef(const BMessage *, const entry_ref *); 576 int32 CountRefs(const BMessage *); 577 578 BMenuItem *EachMenuItem(BMenu *menu, bool recursive, BMenuItem *(*func)(BMenuItem *)); 579 const BMenuItem *EachMenuItem(const BMenu *menu, bool recursive, 580 BMenuItem *(*func)(const BMenuItem *)); 581 582 int64 StringToScalar(const char *text); 583 // string to num, understands kB, MB, etc. 584 585 // misc calls 586 void EmbedUniqueVolumeInfo(BMessage *, const BVolume *); 587 status_t MatchArchivedVolume(BVolume *, const BMessage *, int32 index = 0); 588 void TruncateLeaf(BString *string); 589 590 void StringFromStream(BString *, BMallocIO *, bool endianSwap = false); 591 void StringToStream(const BString *, BMallocIO *); 592 int32 ArchiveSize(const BString *); 593 594 extern void EnableNamedMenuItem(BMenu *menu, const char *itemName, bool on); 595 extern void MarkNamedMenuItem(BMenu *menu, const char *itemName, bool on); 596 extern void EnableNamedMenuItem(BMenu *menu, uint32 commandName, bool on); 597 extern void MarkNamedMenuItem(BMenu *menu, uint32 commandName, bool on); 598 extern void DeleteSubmenu(BMenuItem *submenuItem); 599 600 extern bool BootedInSafeMode(); 601 602 // Now is in kits 603 #if B_BEOS_VERSION <= B_BEOS_VERSION_MAUI && !defined(__HAIKU__) 604 605 // Should be in kits 606 bool operator==(const rgb_color &, const rgb_color &); 607 bool operator!=(const rgb_color &, const rgb_color &); 608 609 #endif 610 611 inline rgb_color 612 Color(int32 r, int32 g, int32 b, int32 alpha = 255) 613 { 614 rgb_color result; 615 result.red = (uchar)r; 616 result.green = (uchar)g; 617 result.blue = (uchar)b; 618 result.alpha = (uchar)alpha; 619 620 return result; 621 } 622 623 void PrintToStream(rgb_color color); 624 625 template <class InitCheckable> 626 void 627 ThrowOnInitCheckError(InitCheckable *item) 628 { 629 if (!item) 630 throw B_ERROR; 631 status_t error = item->InitCheck(); 632 if (error != B_OK) 633 throw error; 634 } 635 636 #if DEBUG 637 #define ThrowOnError(error) _ThrowOnError(error, __FILE__, __LINE__) 638 #define ThrowIfNotSize(error) _ThrowIfNotSize(error, __FILE__, __LINE__) 639 #define ThrowOnErrorWithMessage(error, debugStr) _ThrowOnError(error, debugStr, __FILE__, __LINE__) 640 #else 641 #define ThrowOnError(x) _ThrowOnError(x, 0, 0) 642 #define ThrowIfNotSize(x) _ThrowIfNotSize(x, 0, 0) 643 #define ThrowOnErrorWithMessage(error, debugStr) _ThrowOnError(error, debugStr, __FILE__, __LINE__) 644 #endif 645 646 void _ThrowOnError(status_t, const char *, int32); 647 void _ThrowIfNotSize(ssize_t, const char *, int32); 648 void _ThrowOnError(status_t, const char *debugStr, const char *, int32); 649 650 // stub calls that work around BAppFile info inefficiency 651 status_t GetAppSignatureFromAttr(BFile *, char *); 652 status_t GetAppIconFromAttr(BFile *, BBitmap *, icon_size); 653 status_t GetFileIconFromAttr(BNode *, BBitmap *, icon_size); 654 655 656 // debugging 657 void HexDump(const void *buffer, int32 length); 658 659 #if xDEBUG 660 661 inline void 662 PrintRefToStream(const entry_ref *ref, const char *trailer = "\n") 663 { 664 if (!ref) { 665 PRINT(("NULL entry_ref%s", trailer)); 666 return; 667 } 668 BPath path; 669 BEntry entry(ref); 670 entry.GetPath(&path); 671 PRINT(("%s%s", path.Path(), trailer)); 672 } 673 674 inline void 675 PrintEntryToStream(const BEntry *entry, const char *trailer = "\n") 676 { 677 if (!entry) { 678 PRINT(("NULL entry%s", trailer)); 679 return; 680 } 681 BPath path; 682 entry->GetPath(&path); 683 PRINT(("%s%s", path.Path(), trailer)); 684 } 685 686 inline void 687 PrintDirToStream(const BDirectory *dir, const char *trailer = "\n") 688 { 689 if (!dir) { 690 PRINT(("NULL entry_ref%s", trailer)); 691 return; 692 } 693 BPath path; 694 BEntry entry; 695 dir->GetEntry(&entry); 696 entry.GetPath(&path); 697 PRINT(("%s%s", path.Path(), trailer)); 698 } 699 700 #else 701 702 inline void PrintRefToStream(const entry_ref *, const char * = 0) {} 703 inline void PrintEntryToStream(const BEntry *, const char * = 0) {} 704 inline void PrintDirToStream(const BDirectory *, const char * = 0) {} 705 706 #endif 707 708 #ifdef xDEBUG 709 710 extern FILE *logFile; 711 712 inline void PrintToLogFile(const char *fmt, ...) 713 { 714 va_list ap; 715 va_start(ap, fmt); 716 vfprintf(logFile, fmt, ap); 717 va_end(ap); 718 } 719 720 #define WRITELOG(_ARGS_) \ 721 if (logFile == 0) \ 722 logFile = fopen("/var/log/tracker.log", "a+"); \ 723 if (logFile != 0) { \ 724 thread_info info; \ 725 get_thread_info(find_thread(NULL), &info); \ 726 PrintToLogFile("[t %Ld] \"%s\" (%s:%i) ", system_time(), \ 727 info.name, __FILE__, __LINE__); \ 728 PrintToLogFile _ARGS_; \ 729 PrintToLogFile("\n"); \ 730 fflush(logFile); \ 731 } 732 733 #else 734 735 #define WRITELOG(_ARGS_) 736 737 #endif 738 739 // fancy casting macros 740 741 template <typename NewType, typename OldType> 742 inline NewType assert_cast(OldType castedPointer) { 743 ASSERT(dynamic_cast<NewType>(castedPointer) != NULL); 744 return static_cast<NewType>(castedPointer); 745 } 746 747 // B_SWAP_INT32 have broken signedness, simple cover calls to fix that 748 // should fix up in ByteOrder.h 749 750 inline int32 SwapInt32(int32 value) { return (int32)B_SWAP_INT32((uint32)value); } 751 inline uint32 SwapUInt32(uint32 value) { return B_SWAP_INT32(value); } 752 inline int64 SwapInt64(int64 value) { return (int64)B_SWAP_INT64((uint64)value); } 753 inline uint64 SwapUInt64(uint64 value) { return B_SWAP_INT64(value); } 754 755 756 extern const float kExactMatchScore; 757 float ComputeTypeAheadScore(const char *text, const char *match, 758 bool wordMode = false); 759 760 } // namespace BPrivate 761 762 #endif // _UTILITIES_H 763