1 /* 2 * Copyright 2002-2010 Haiku, Inc. All rights reserved. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Ingo Weinhold, bonefish@users.sf.net 7 * Axel Dörfler, axeld@pinc-software.de 8 */ 9 10 11 #include <NodeInfo.h> 12 13 #include <new> 14 #include <string.h> 15 16 #include <MimeTypes.h> 17 #include <Bitmap.h> 18 #include <Entry.h> 19 #include <IconUtils.h> 20 #include <Node.h> 21 #include <Path.h> 22 #include <Rect.h> 23 24 #include <fs_attr.h> 25 #include <fs_info.h> 26 27 28 using namespace std; 29 30 31 // attribute names 32 #define NI_BEOS "BEOS" 33 static const char* kNITypeAttribute = NI_BEOS ":TYPE"; 34 static const char* kNIPreferredAppAttribute = NI_BEOS ":PREF_APP"; 35 static const char* kNIAppHintAttribute = NI_BEOS ":PPATH"; 36 static const char* kNIMiniIconAttribute = NI_BEOS ":M:STD_ICON"; 37 static const char* kNILargeIconAttribute = NI_BEOS ":L:STD_ICON"; 38 static const char* kNIIconAttribute = NI_BEOS ":ICON"; 39 40 41 // #pragma mark - BNodeInfo 42 43 44 BNodeInfo::BNodeInfo() 45 : 46 fNode(NULL), 47 fCStatus(B_NO_INIT) 48 { 49 } 50 51 52 BNodeInfo::BNodeInfo(BNode* node) 53 : 54 fNode(NULL), 55 fCStatus(B_NO_INIT) 56 { 57 fCStatus = SetTo(node); 58 } 59 60 61 BNodeInfo::~BNodeInfo() 62 { 63 } 64 65 66 // Initializes the BNodeInfo to the supplied node. 67 status_t 68 BNodeInfo::SetTo(BNode* node) 69 { 70 fNode = NULL; 71 // check parameter 72 fCStatus = (node && node->InitCheck() == B_OK ? B_OK : B_BAD_VALUE); 73 if (fCStatus == B_OK) 74 fNode = node; 75 76 return fCStatus; 77 } 78 79 80 // Returns whether the object has been properly initialized. 81 status_t 82 BNodeInfo::InitCheck() const 83 { 84 return fCStatus; 85 } 86 87 88 // Writes the MIME type of the node into type. 89 status_t 90 BNodeInfo::GetType(char* type) const 91 { 92 // check parameter and initialization 93 status_t result = (type ? B_OK : B_BAD_VALUE); 94 if (result == B_OK && InitCheck() != B_OK) 95 result = B_NO_INIT; 96 // get the attribute info and check type and length of the attr contents 97 attr_info attrInfo; 98 if (result == B_OK) 99 result = fNode->GetAttrInfo(kNITypeAttribute, &attrInfo); 100 if (result == B_OK && attrInfo.type != B_MIME_STRING_TYPE) 101 result = B_BAD_TYPE; 102 if (result == B_OK && attrInfo.size > B_MIME_TYPE_LENGTH) 103 result = B_BAD_DATA; 104 105 // read the data 106 if (result == B_OK) { 107 ssize_t read = fNode->ReadAttr(kNITypeAttribute, attrInfo.type, 0, 108 type, attrInfo.size); 109 if (read < 0) 110 result = read; 111 else if (read != attrInfo.size) 112 result = B_ERROR; 113 114 if (result == B_OK) { 115 // attribute strings doesn't have to be null terminated 116 type[min_c(attrInfo.size, B_MIME_TYPE_LENGTH - 1)] = '\0'; 117 } 118 } 119 120 return result; 121 } 122 123 // Sets the MIME type of the node. If type is NULL the BEOS:TYPE attribute is 124 // removed instead. 125 status_t 126 BNodeInfo::SetType(const char* type) 127 { 128 // check parameter and initialization 129 status_t result = B_OK; 130 if (result == B_OK && type && strlen(type) >= B_MIME_TYPE_LENGTH) 131 result = B_BAD_VALUE; 132 if (result == B_OK && InitCheck() != B_OK) 133 result = B_NO_INIT; 134 135 // write/remove the attribute 136 if (result == B_OK) { 137 if (type) { 138 size_t toWrite = strlen(type) + 1; 139 ssize_t written = fNode->WriteAttr(kNITypeAttribute, 140 B_MIME_STRING_TYPE, 0, type, 141 toWrite); 142 if (written < 0) 143 result = written; 144 else if (written != (ssize_t)toWrite) 145 result = B_ERROR; 146 } else 147 result = fNode->RemoveAttr(kNITypeAttribute); 148 } 149 return result; 150 } 151 152 153 // Gets the icon of the node. 154 status_t 155 BNodeInfo::GetIcon(BBitmap* icon, icon_size which) const 156 { 157 const char* iconAttribute = kNIIconAttribute; 158 const char* miniIconAttribute = kNIMiniIconAttribute; 159 const char* largeIconAttribute = kNILargeIconAttribute; 160 161 return BIconUtils::GetIcon(fNode, iconAttribute, miniIconAttribute, 162 largeIconAttribute, which, icon); 163 #if 0 164 status_t result = B_OK; 165 // set some icon size related variables 166 const char* attribute = NULL; 167 BRect bounds; 168 uint32 attrType = 0; 169 size_t attrSize = 0; 170 switch (k) { 171 case B_MINI_ICON: 172 attribute = kNIMiniIconAttribute; 173 bounds.Set(0, 0, 15, 15); 174 attrType = B_MINI_ICON_TYPE; 175 attrSize = 16 * 16; 176 break; 177 case B_LARGE_ICON: 178 attribute = kNILargeIconAttribute; 179 bounds.Set(0, 0, 31, 31); 180 attrType = B_LARGE_ICON_TYPE; 181 attrSize = 32 * 32; 182 break; 183 default: 184 result = B_BAD_VALUE; 185 break; 186 } 187 188 // check parameter and initialization 189 if (result == B_OK 190 && (icon == NULL || icon->InitCheck() != B_OK 191 || icon->Bounds() != bounds)) { 192 result = B_BAD_VALUE; 193 } 194 if (result == B_OK && InitCheck() != B_OK) 195 result = B_NO_INIT; 196 197 // get the attribute info and check type and size of the attr contents 198 attr_info attrInfo; 199 if (result == B_OK) 200 result = fNode->GetAttrInfo(attribute, &attrInfo); 201 202 if (result == B_OK && attrInfo.type != attrType) 203 result = B_BAD_TYPE; 204 205 if (result == B_OK && attrInfo.size != attrSize) 206 result = B_BAD_DATA; 207 208 // read the attribute 209 if (result == B_OK) { 210 bool otherColorSpace = (icon->ColorSpace() != B_CMAP8); 211 char *buffer = NULL; 212 ssize_t read; 213 if (otherColorSpace) { 214 // other color space than stored in attribute 215 buffer = new(nothrow) char[attrSize]; 216 if (!buffer) 217 result = B_NO_MEMORY; 218 if (result == B_OK) { 219 read = fNode->ReadAttr(attribute, attrType, 0, buffer, 220 attrSize); 221 } 222 } else { 223 read = fNode->ReadAttr(attribute, attrType, 0, icon->Bits(), 224 attrSize); 225 } 226 if (result == B_OK) { 227 if (read < 0) 228 result = read; 229 else if (read != attrInfo.size) 230 result = B_ERROR; 231 } 232 if (otherColorSpace) { 233 // other color space than stored in attribute 234 if (result == B_OK) { 235 result = icon->ImportBits(buffer, attrSize, B_ANY_BYTES_PER_ROW, 236 0, B_CMAP8); 237 } 238 delete[] buffer; 239 } 240 } 241 242 return result; 243 #endif 244 } 245 246 247 // Sets the icon of the node. If icon is NULL, the attribute is removed 248 // instead. 249 status_t 250 BNodeInfo::SetIcon(const BBitmap* icon, icon_size which) 251 { 252 status_t result = B_OK; 253 254 // set some icon size related variables 255 const char* attribute = NULL; 256 BRect bounds; 257 uint32 attrType = 0; 258 size_t attrSize = 0; 259 260 switch (which) { 261 case B_MINI_ICON: 262 attribute = kNIMiniIconAttribute; 263 bounds.Set(0, 0, 15, 15); 264 attrType = B_MINI_ICON_TYPE; 265 attrSize = 16 * 16; 266 break; 267 268 case B_LARGE_ICON: 269 attribute = kNILargeIconAttribute; 270 bounds.Set(0, 0, 31, 31); 271 attrType = B_LARGE_ICON_TYPE; 272 attrSize = 32 * 32; 273 break; 274 275 default: 276 result = B_BAD_VALUE; 277 break; 278 } 279 280 // check parameter and initialization 281 if (result == B_OK && icon != NULL 282 && (icon->InitCheck() != B_OK || icon->Bounds() != bounds)) { 283 result = B_BAD_VALUE; 284 } 285 if (result == B_OK && InitCheck() != B_OK) 286 result = B_NO_INIT; 287 288 // write/remove the attribute 289 if (result == B_OK) { 290 if (icon != NULL) { 291 bool otherColorSpace = (icon->ColorSpace() != B_CMAP8); 292 ssize_t written = 0; 293 if (otherColorSpace) { 294 BBitmap bitmap(bounds, B_BITMAP_NO_SERVER_LINK, B_CMAP8); 295 result = bitmap.InitCheck(); 296 if (result == B_OK) 297 result = bitmap.ImportBits(icon); 298 299 if (result == B_OK) { 300 written = fNode->WriteAttr(attribute, attrType, 0, 301 bitmap.Bits(), attrSize); 302 } 303 } else { 304 written = fNode->WriteAttr(attribute, attrType, 0, 305 icon->Bits(), attrSize); 306 } 307 if (result == B_OK) { 308 if (written < 0) 309 result = written; 310 else if (written != (ssize_t)attrSize) 311 result = B_ERROR; 312 } 313 } else { 314 // no icon given => remove 315 result = fNode->RemoveAttr(attribute); 316 } 317 } 318 319 return result; 320 } 321 322 323 // Gets the icon of the node. 324 status_t 325 BNodeInfo::GetIcon(uint8** data, size_t* size, type_code* type) const 326 { 327 // check params 328 if (data == NULL || size == NULL || type == NULL) 329 return B_BAD_VALUE; 330 331 // check initialization 332 if (InitCheck() != B_OK) 333 return B_NO_INIT; 334 335 // get the attribute info and check type and size of the attr contents 336 attr_info attrInfo; 337 status_t ret = fNode->GetAttrInfo(kNIIconAttribute, &attrInfo); 338 if (ret < B_OK) 339 return ret; 340 341 // chicken out on unrealisticly large attributes 342 if (attrInfo.size > 128 * 1024) 343 return B_ERROR; 344 345 // fill the params 346 *type = attrInfo.type; 347 *size = attrInfo.size; 348 *data = new (nothrow) uint8[*size]; 349 350 if (!*data) 351 return B_NO_MEMORY; 352 353 // featch the data 354 ssize_t read = fNode->ReadAttr(kNIIconAttribute, *type, 0, *data, *size); 355 if (read != attrInfo.size) { 356 delete[] *data; 357 *data = NULL; 358 return B_ERROR; 359 } 360 361 return B_OK; 362 } 363 364 365 // Sets the node icon of the node. If data is NULL of size is 0, the 366 // "BEOS:ICON" attribute is removed instead. 367 status_t 368 BNodeInfo::SetIcon(const uint8* data, size_t size) 369 { 370 // check initialization 371 if (InitCheck() != B_OK) 372 return B_NO_INIT; 373 374 status_t result = B_OK; 375 376 // write/remove the attribute 377 if (data && size > 0) { 378 ssize_t written = fNode->WriteAttr(kNIIconAttribute, 379 B_VECTOR_ICON_TYPE, 0, data, size); 380 if (written < 0) 381 result = (status_t)written; 382 else if (written != (ssize_t)size) 383 result = B_ERROR; 384 } else { 385 // no icon given => remove 386 result = fNode->RemoveAttr(kNIIconAttribute); 387 } 388 389 return result; 390 } 391 392 393 // Gets the preferred application of the node. 394 status_t 395 BNodeInfo::GetPreferredApp(char* signature, app_verb verb) const 396 { 397 // check parameter and initialization 398 status_t result = (signature && verb == B_OPEN ? B_OK : B_BAD_VALUE); 399 if (result == B_OK && InitCheck() != B_OK) 400 result = B_NO_INIT; 401 402 // get the attribute info and check type and length of the attr contents 403 attr_info attrInfo; 404 if (result == B_OK) 405 result = fNode->GetAttrInfo(kNIPreferredAppAttribute, &attrInfo); 406 407 if (result == B_OK && attrInfo.type != B_MIME_STRING_TYPE) 408 result = B_BAD_TYPE; 409 410 if (result == B_OK && attrInfo.size > B_MIME_TYPE_LENGTH) 411 result = B_BAD_DATA; 412 413 // read the data 414 if (result == B_OK) { 415 ssize_t read = fNode->ReadAttr(kNIPreferredAppAttribute, attrInfo.type, 416 0, signature, attrInfo.size); 417 if (read < 0) 418 result = read; 419 else if (read != attrInfo.size) 420 result = B_ERROR; 421 422 if (result == B_OK) { 423 // attribute strings doesn't have to be null terminated 424 signature[min_c(attrInfo.size, B_MIME_TYPE_LENGTH - 1)] = '\0'; 425 } 426 } 427 428 return result; 429 } 430 431 432 // Sets the preferred application of the node. If signature is NULL, the 433 // "BEOS:PREF_APP" attribute is removed instead. 434 status_t 435 BNodeInfo::SetPreferredApp(const char* signature, app_verb verb) 436 { 437 // check parameters and initialization 438 status_t result = (verb == B_OPEN ? B_OK : B_BAD_VALUE); 439 if (result == B_OK && signature && strlen(signature) >= B_MIME_TYPE_LENGTH) 440 result = B_BAD_VALUE; 441 442 if (result == B_OK && InitCheck() != B_OK) 443 result = B_NO_INIT; 444 445 // write/remove the attribute 446 if (result == B_OK) { 447 if (signature) { 448 size_t toWrite = strlen(signature) + 1; 449 ssize_t written = fNode->WriteAttr(kNIPreferredAppAttribute, 450 B_MIME_STRING_TYPE, 0, signature, toWrite); 451 if (written < 0) 452 result = written; 453 else if (written != (ssize_t)toWrite) 454 result = B_ERROR; 455 } else 456 result = fNode->RemoveAttr(kNIPreferredAppAttribute); 457 } 458 459 return result; 460 } 461 462 463 // Fills out ref with a pointer to a hint about what application will open 464 // this node. 465 status_t 466 BNodeInfo::GetAppHint(entry_ref* ref) const 467 { 468 // check parameter and initialization 469 status_t result = (ref ? B_OK : B_BAD_VALUE); 470 if (result == B_OK && InitCheck() != B_OK) 471 result = B_NO_INIT; 472 473 // get the attribute info and check type and length of the attr contents 474 attr_info attrInfo; 475 if (result == B_OK) 476 result = fNode->GetAttrInfo(kNIAppHintAttribute, &attrInfo); 477 478 // NOTE: The attribute type should be B_STRING_TYPE, but R5 uses 479 // B_MIME_STRING_TYPE. 480 if (result == B_OK && attrInfo.type != B_MIME_STRING_TYPE) 481 result = B_BAD_TYPE; 482 483 if (result == B_OK && attrInfo.size > B_PATH_NAME_LENGTH) 484 result = B_BAD_DATA; 485 486 // read the data 487 if (result == B_OK) { 488 char path[B_PATH_NAME_LENGTH]; 489 ssize_t read = fNode->ReadAttr(kNIAppHintAttribute, attrInfo.type, 0, 490 path, attrInfo.size); 491 if (read < 0) 492 result = read; 493 else if (read != attrInfo.size) 494 result = B_ERROR; 495 496 // get the entry_ref for the path 497 if (result == B_OK) { 498 // attribute strings doesn't have to be null terminated 499 path[min_c(attrInfo.size, B_PATH_NAME_LENGTH - 1)] = '\0'; 500 result = get_ref_for_path(path, ref); 501 } 502 } 503 504 return result; 505 } 506 507 508 // Sets the app hint of the node. If ref is NULL, the "BEOS:PPATH" attribute 509 // is removed instead. 510 status_t 511 BNodeInfo::SetAppHint(const entry_ref* ref) 512 { 513 // check parameter and initialization 514 if (InitCheck() != B_OK) 515 return B_NO_INIT; 516 517 status_t result = B_OK; 518 if (ref != NULL) { 519 // write/remove the attribute 520 BPath path; 521 result = path.SetTo(ref); 522 if (result == B_OK) { 523 size_t toWrite = strlen(path.Path()) + 1; 524 ssize_t written = fNode->WriteAttr(kNIAppHintAttribute, 525 B_MIME_STRING_TYPE, 0, path.Path(), toWrite); 526 if (written < 0) 527 result = written; 528 else if (written != (ssize_t)toWrite) 529 result = B_ERROR; 530 } 531 } else 532 result = fNode->RemoveAttr(kNIAppHintAttribute); 533 534 return result; 535 } 536 537 538 // Gets the icon displayed by Tracker for the icon. 539 status_t 540 BNodeInfo::GetTrackerIcon(BBitmap* icon, icon_size which) const 541 { 542 if (icon == NULL) 543 return B_BAD_VALUE; 544 545 // set some icon size related variables 546 BRect bounds; 547 switch (which) { 548 case B_MINI_ICON: 549 bounds.Set(0, 0, 15, 15); 550 break; 551 552 case B_LARGE_ICON: 553 bounds.Set(0, 0, 31, 31); 554 break; 555 556 default: 557 // result = B_BAD_VALUE; 558 // NOTE: added to be less strict and support scaled icons 559 bounds = icon->Bounds(); 560 break; 561 } 562 563 // check parameters and initialization 564 if (icon->InitCheck() != B_OK || icon->Bounds() != bounds) 565 return B_BAD_VALUE; 566 567 if (InitCheck() != B_OK) 568 return B_NO_INIT; 569 570 // Ask GetIcon() first. 571 if (GetIcon(icon, which) == B_OK) 572 return B_OK; 573 574 // If not successful, see if the node has a type available at all. 575 // If no type is available, use one of the standard types. 576 status_t result = B_OK; 577 char mimeString[B_MIME_TYPE_LENGTH]; 578 if (GetType(mimeString) != B_OK) { 579 // Get the icon from a mime type... 580 BMimeType type; 581 582 struct stat stat; 583 result = fNode->GetStat(&stat); 584 if (result == B_OK) { 585 // no type available -- get the icon for the appropriate type 586 // (file/dir/etc.) 587 if (S_ISREG(stat.st_mode)) { 588 // is it an application (executable) or just a regular file? 589 if ((stat.st_mode & S_IXUSR) != 0) 590 type.SetTo(B_APP_MIME_TYPE); 591 else 592 type.SetTo(B_FILE_MIME_TYPE); 593 } else if (S_ISDIR(stat.st_mode)) { 594 // it's either a volume or just a standard directory 595 fs_info info; 596 if (fs_stat_dev(stat.st_dev, &info) == 0 597 && stat.st_ino == info.root) { 598 type.SetTo(B_VOLUME_MIME_TYPE); 599 } else 600 type.SetTo(B_DIRECTORY_MIME_TYPE); 601 } else if (S_ISLNK(stat.st_mode)) 602 type.SetTo(B_SYMLINK_MIME_TYPE); 603 } else { 604 // GetStat() failed. Return the icon for 605 // "application/octet-stream" from the MIME database. 606 type.SetTo(B_FILE_MIME_TYPE); 607 } 608 609 return type.GetIcon(icon, which); 610 } else { 611 // We know the mimetype of the node. 612 bool success = false; 613 614 // Get the preferred application and ask the MIME database, if that 615 // application has a special icon for the node's file type. 616 char signature[B_MIME_TYPE_LENGTH]; 617 if (GetPreferredApp(signature) == B_OK) { 618 BMimeType type(signature); 619 success = type.GetIconForType(mimeString, icon, which) == B_OK; 620 } 621 622 // ToDo: Confirm Tracker asks preferred app icons before asking 623 // mime icons. 624 625 BMimeType nodeType(mimeString); 626 627 // Ask the MIME database for the preferred application for the node's 628 // file type and whether this application has a special icon for the 629 // type. 630 if (!success && nodeType.GetPreferredApp(signature) == B_OK) { 631 BMimeType type(signature); 632 success = type.GetIconForType(mimeString, icon, which) == B_OK; 633 } 634 635 // Ask the MIME database whether there is an icon for the node's file 636 // type. 637 if (!success) 638 success = nodeType.GetIcon(icon, which) == B_OK; 639 640 // Get the super type if still no success. 641 BMimeType superType; 642 if (!success && nodeType.GetSupertype(&superType) == B_OK) { 643 // Ask the MIME database for the preferred application for the 644 // node's super type and whether this application has a special 645 // icon for the type. 646 if (superType.GetPreferredApp(signature) == B_OK) { 647 BMimeType type(signature); 648 success = type.GetIconForType(superType.Type(), icon, 649 which) == B_OK; 650 } 651 // Get the icon of the super type itself. 652 if (!success) 653 success = superType.GetIcon(icon, which) == B_OK; 654 } 655 656 if (success) 657 return B_OK; 658 } 659 660 return B_ERROR; 661 } 662 663 664 // Gets the icon displayed by Tracker for the node referred to by ref. 665 status_t 666 BNodeInfo::GetTrackerIcon(const entry_ref* ref, BBitmap* icon, icon_size which) 667 { 668 // check ref param 669 status_t result = (ref ? B_OK : B_BAD_VALUE); 670 671 // init a BNode 672 BNode node; 673 if (result == B_OK) 674 result = node.SetTo(ref); 675 676 // init a BNodeInfo 677 BNodeInfo nodeInfo; 678 if (result == B_OK) 679 result = nodeInfo.SetTo(&node); 680 681 // let the non-static GetTrackerIcon() do the dirty work 682 if (result == B_OK) 683 result = nodeInfo.GetTrackerIcon(icon, which); 684 685 return result; 686 } 687 688 689 // NOTE: The following method is provided for binary compatibility purposes 690 // (for example the program "Guido" depends on this.) 691 extern "C" 692 status_t 693 GetTrackerIcon__9BNodeInfoP9entry_refP7BBitmap9icon_size( 694 BNodeInfo* nodeInfo, entry_ref* ref, 695 BBitmap* bitmap, icon_size iconSize) 696 { 697 // NOTE: nodeInfo is ignored - maybe that's wrong! 698 return BNodeInfo::GetTrackerIcon(ref, bitmap, iconSize); 699 } 700 701 702 void BNodeInfo::_ReservedNodeInfo1() {} 703 void BNodeInfo::_ReservedNodeInfo2() {} 704 void BNodeInfo::_ReservedNodeInfo3() {} 705 706 707 // Assignment operator is declared private to prevent it from being created 708 // automatically by the compiler. 709 BNodeInfo& 710 BNodeInfo::operator=(const BNodeInfo &nodeInfo) 711 { 712 return *this; 713 } 714 715 716 // Copy constructor is declared private to prevent it from being created 717 // automatically by the compiler. 718 BNodeInfo::BNodeInfo(const BNodeInfo &) 719 { 720 } 721 722 723 namespace BPrivate { 724 725 // Private method used by Tracker. This should be moved to the Tracker 726 // source. 727 extern bool 728 CheckNodeIconHintPrivate(const BNode* node, bool checkMiniIconOnly) 729 { 730 attr_info info; 731 if (node->GetAttrInfo(kNIMiniIconAttribute, &info) != B_OK && checkMiniIconOnly) 732 return false; 733 734 if (node->GetAttrInfo(kNILargeIconAttribute, &info) != B_OK) 735 return false; 736 737 return true; 738 } 739 740 } // namespace BPrivate 741