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