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