1 /* 2 * Copyright 2001-2010, Haiku, Inc. 3 * Distributed under the terms of the MIT License. 4 * 5 * Authors: 6 * Marc Flerackers (mflerackers@androme.be) 7 * Axel Dörfler, axeld@pinc-software.de 8 * Jérôme Duval 9 * René Gollent 10 * Alexandre Deckner, alex@zappotek.com 11 */ 12 13 /*! BShelf stores replicant views that are dropped onto it */ 14 15 #include <Shelf.h> 16 17 #include <pthread.h> 18 19 #include <AutoLock.h> 20 #include <Beep.h> 21 #include <Dragger.h> 22 #include <Entry.h> 23 #include <File.h> 24 #include <Looper.h> 25 #include <Message.h> 26 #include <MessageFilter.h> 27 #include <Messenger.h> 28 #include <Point.h> 29 #include <PropertyInfo.h> 30 #include <Rect.h> 31 #include <String.h> 32 #include <View.h> 33 34 #include <ViewPrivate.h> 35 36 #include "ZombieReplicantView.h" 37 38 #include <stdio.h> 39 #include <string.h> 40 41 #include <map> 42 #include <utility> 43 44 45 namespace { 46 47 typedef std::map<BString, std::pair<image_id, int32> > LoadedImageMap; 48 49 struct LoadedImages { 50 LoadedImageMap images; 51 52 LoadedImages() 53 : 54 fLock("BShelf loaded image map") 55 { 56 } 57 58 bool Lock() 59 { 60 return fLock.Lock(); 61 } 62 63 void Unlock() 64 { 65 fLock.Unlock(); 66 } 67 68 static LoadedImages* Default() 69 { 70 if (sDefaultInstance == NULL) 71 pthread_once(&sDefaultInitOnce, &_InitSingleton); 72 73 return sDefaultInstance; 74 } 75 76 private: 77 static void _InitSingleton() 78 { 79 sDefaultInstance = new LoadedImages; 80 } 81 82 private: 83 BLocker fLock; 84 85 static pthread_once_t sDefaultInitOnce; 86 static LoadedImages* sDefaultInstance; 87 }; 88 89 pthread_once_t LoadedImages::sDefaultInitOnce = PTHREAD_ONCE_INIT; 90 LoadedImages* LoadedImages::sDefaultInstance = NULL; 91 92 } // unnamed namespace 93 94 95 static property_info sShelfPropertyList[] = { 96 { 97 "Replicant", 98 { B_COUNT_PROPERTIES, B_CREATE_PROPERTY }, 99 { B_DIRECT_SPECIFIER }, 100 NULL, 0, 101 }, 102 103 { 104 "Replicant", 105 { B_DELETE_PROPERTY, B_GET_PROPERTY }, 106 { B_INDEX_SPECIFIER, B_REVERSE_INDEX_SPECIFIER, B_NAME_SPECIFIER, B_ID_SPECIFIER }, 107 NULL, 0, 108 }, 109 110 { 111 "Replicant", 112 {}, 113 { B_INDEX_SPECIFIER, B_REVERSE_INDEX_SPECIFIER, B_NAME_SPECIFIER, B_ID_SPECIFIER }, 114 "... of Replicant {index | name | id} of ...", 0, 115 }, 116 117 { 0, { 0 }, { 0 }, 0, 0 } 118 }; 119 120 static property_info sReplicantPropertyList[] = { 121 { 122 "ID", 123 { B_GET_PROPERTY }, 124 { B_DIRECT_SPECIFIER }, 125 NULL, 0, { B_INT32_TYPE } 126 }, 127 128 { 129 "Name", 130 { B_GET_PROPERTY }, 131 { B_DIRECT_SPECIFIER }, 132 NULL, 0, { B_STRING_TYPE } 133 }, 134 135 { 136 "Signature", 137 { B_GET_PROPERTY }, 138 { B_DIRECT_SPECIFIER }, 139 NULL, 0, { B_STRING_TYPE } 140 }, 141 142 { 143 "Suites", 144 { B_GET_PROPERTY }, 145 { B_DIRECT_SPECIFIER }, 146 NULL, 0, { B_PROPERTY_INFO_TYPE } 147 }, 148 149 { 150 "View", 151 { }, 152 { B_DIRECT_SPECIFIER }, 153 NULL, 0, 154 }, 155 156 { 0, { 0 }, { 0 }, 0, 0 } 157 }; 158 159 160 namespace BPrivate { 161 162 struct replicant_data { 163 replicant_data(BMessage *message, BView *view, BDragger *dragger, 164 BDragger::relation relation, unsigned long id); 165 replicant_data(); 166 ~replicant_data(); 167 168 static replicant_data* Find(BList const *list, BMessage const *msg); 169 static replicant_data* Find(BList const *list, BView const *view, bool allowZombie); 170 static replicant_data* Find(BList const *list, unsigned long id); 171 172 static int32 IndexOf(BList const *list, BMessage const *msg); 173 static int32 IndexOf(BList const *list, BView const *view, bool allowZombie); 174 static int32 IndexOf(BList const *list, unsigned long id); 175 176 status_t Archive(BMessage *msg); 177 178 BMessage* message; 179 BView* view; 180 BDragger* dragger; 181 BDragger::relation relation; 182 unsigned long id; 183 status_t error; 184 BView* zombie_view; 185 }; 186 187 class ShelfContainerViewFilter : public BMessageFilter { 188 public: 189 ShelfContainerViewFilter(BShelf *shelf, BView *view); 190 191 filter_result Filter(BMessage *msg, BHandler **handler); 192 193 private: 194 filter_result _ObjectDropFilter(BMessage *msg, BHandler **handler); 195 196 BShelf *fShelf; 197 BView *fView; 198 }; 199 200 class ReplicantViewFilter : public BMessageFilter { 201 public: 202 ReplicantViewFilter(BShelf *shelf, BView *view); 203 204 filter_result Filter(BMessage *message, BHandler **handler); 205 206 private: 207 BShelf *fShelf; 208 BView *fView; 209 }; 210 211 } // namespace BPrivate 212 213 214 using BPrivate::replicant_data; 215 using BPrivate::ReplicantViewFilter; 216 using BPrivate::ShelfContainerViewFilter; 217 218 219 // #pragma mark - 220 221 222 /*! \brief Helper function for BShelf::_AddReplicant() 223 */ 224 static status_t 225 send_reply(BMessage* message, status_t status, uint32 uniqueID) 226 { 227 if (message->IsSourceWaiting()) { 228 BMessage reply(B_REPLY); 229 reply.AddInt32("id", uniqueID); 230 reply.AddInt32("error", status); 231 message->SendReply(&reply); 232 } 233 234 return status; 235 } 236 237 238 static bool 239 find_replicant(BList &list, const char *className, const char *addOn) 240 { 241 int32 i = 0; 242 replicant_data *item; 243 while ((item = (replicant_data *)list.ItemAt(i++)) != NULL) { 244 const char *replicantClassName; 245 const char *replicantAddOn; 246 if (item->message->FindString("class", &replicantClassName) == B_OK 247 && item->message->FindString("add_on", &replicantAddOn) == B_OK 248 && !strcmp(className, replicantClassName) 249 && addOn != NULL && replicantAddOn != NULL 250 && !strcmp(addOn, replicantAddOn)) 251 return true; 252 } 253 return false; 254 } 255 256 257 // #pragma mark - 258 259 260 replicant_data::replicant_data(BMessage *_message, BView *_view, BDragger *_dragger, 261 BDragger::relation _relation, unsigned long _id) 262 : 263 message(_message), 264 view(_view), 265 dragger(NULL), 266 relation(_relation), 267 id(_id), 268 error(B_OK), 269 zombie_view(NULL) 270 { 271 } 272 273 274 replicant_data::replicant_data() 275 : 276 message(NULL), 277 view(NULL), 278 dragger(NULL), 279 relation(BDragger::TARGET_UNKNOWN), 280 id(0), 281 error(B_ERROR), 282 zombie_view(NULL) 283 { 284 } 285 286 replicant_data::~replicant_data() 287 { 288 delete message; 289 } 290 291 status_t 292 replicant_data::Archive(BMessage* msg) 293 { 294 status_t result = B_OK; 295 BMessage archive; 296 if (view) 297 result = view->Archive(&archive); 298 else if (zombie_view) 299 result = zombie_view->Archive(&archive); 300 301 if (result != B_OK) 302 return result; 303 304 msg->AddInt32("uniqueid", id); 305 BPoint pos (0,0); 306 msg->AddMessage("message", &archive); 307 if (view) 308 pos = view->Frame().LeftTop(); 309 else if (zombie_view) 310 pos = zombie_view->Frame().LeftTop(); 311 msg->AddPoint("position", pos); 312 313 return result; 314 } 315 316 //static 317 replicant_data * 318 replicant_data::Find(BList const *list, BMessage const *msg) 319 { 320 int32 i = 0; 321 replicant_data *item; 322 while ((item = (replicant_data*)list->ItemAt(i++)) != NULL) { 323 if (item->message == msg) 324 return item; 325 } 326 327 return NULL; 328 } 329 330 331 //static 332 replicant_data * 333 replicant_data::Find(BList const *list, BView const *view, bool allowZombie) 334 { 335 int32 i = 0; 336 replicant_data *item; 337 while ((item = (replicant_data*)list->ItemAt(i++)) != NULL) { 338 if (item->view == view) 339 return item; 340 341 if (allowZombie && item->zombie_view == view) 342 return item; 343 } 344 345 return NULL; 346 } 347 348 349 //static 350 replicant_data * 351 replicant_data::Find(BList const *list, unsigned long id) 352 { 353 int32 i = 0; 354 replicant_data *item; 355 while ((item = (replicant_data*)list->ItemAt(i++)) != NULL) { 356 if (item->id == id) 357 return item; 358 } 359 360 return NULL; 361 } 362 363 364 //static 365 int32 366 replicant_data::IndexOf(BList const *list, BMessage const *msg) 367 { 368 int32 i = 0; 369 replicant_data *item; 370 while ((item = (replicant_data*)list->ItemAt(i)) != NULL) { 371 if (item->message == msg) 372 return i; 373 i++; 374 } 375 376 return -1; 377 } 378 379 380 //static 381 int32 382 replicant_data::IndexOf(BList const *list, BView const *view, bool allowZombie) 383 { 384 int32 i = 0; 385 replicant_data *item; 386 while ((item = (replicant_data*)list->ItemAt(i)) != NULL) { 387 if (item->view == view) 388 return i; 389 390 if (allowZombie && item->zombie_view == view) 391 return i; 392 i++; 393 } 394 395 return -1; 396 } 397 398 399 //static 400 int32 401 replicant_data::IndexOf(BList const *list, unsigned long id) 402 { 403 int32 i = 0; 404 replicant_data *item; 405 while ((item = (replicant_data*)list->ItemAt(i)) != NULL) { 406 if (item->id == id) 407 return i; 408 i++; 409 } 410 411 return -1; 412 } 413 414 415 // #pragma mark - 416 417 418 ShelfContainerViewFilter::ShelfContainerViewFilter(BShelf *shelf, BView *view) 419 : BMessageFilter(B_ANY_DELIVERY, B_ANY_SOURCE), 420 fShelf(shelf), 421 fView(view) 422 { 423 } 424 425 426 filter_result 427 ShelfContainerViewFilter::Filter(BMessage *msg, BHandler **handler) 428 { 429 filter_result filter = B_DISPATCH_MESSAGE; 430 431 if (msg->what == B_ARCHIVED_OBJECT 432 || msg->what == B_ABOUT_REQUESTED) 433 return _ObjectDropFilter(msg, handler); 434 435 return filter; 436 } 437 438 439 filter_result 440 ShelfContainerViewFilter::_ObjectDropFilter(BMessage *msg, BHandler **_handler) 441 { 442 BView *mouseView = NULL; 443 if (*_handler) 444 mouseView = dynamic_cast<BView*>(*_handler); 445 446 if (msg->WasDropped()) { 447 if (!fShelf->fAllowDragging) 448 return B_SKIP_MESSAGE; 449 } 450 451 BPoint point; 452 BPoint offset; 453 454 if (msg->WasDropped()) { 455 point = msg->DropPoint(&offset); 456 point = mouseView->ConvertFromScreen(point - offset); 457 } 458 459 BLooper *looper = NULL; 460 BHandler *handler = msg->ReturnAddress().Target(&looper); 461 462 if (Looper() == looper) { 463 BDragger *dragger = NULL; 464 if (handler) 465 dragger = dynamic_cast<BDragger*>(handler); 466 467 BRect rect; 468 if (dragger->fRelation == BDragger::TARGET_IS_CHILD) 469 rect = dragger->Frame(); 470 else 471 rect = dragger->fTarget->Frame(); 472 rect.OffsetTo(point); 473 point = rect.LeftTop() + fShelf->AdjustReplicantBy(rect, msg); 474 475 if (dragger->fRelation == BDragger::TARGET_IS_PARENT) 476 dragger->fTarget->MoveTo(point); 477 else if (dragger->fRelation == BDragger::TARGET_IS_CHILD) 478 dragger->MoveTo(point); 479 else { 480 //TODO: TARGET_UNKNOWN/TARGET_SIBLING 481 } 482 483 } else { 484 if (fShelf->_AddReplicant(msg, &point, fShelf->fGenCount++) == B_OK) 485 Looper()->DetachCurrentMessage(); 486 } 487 488 return B_SKIP_MESSAGE; 489 } 490 491 492 // #pragma mark - 493 494 495 ReplicantViewFilter::ReplicantViewFilter(BShelf *shelf, BView *view) 496 : BMessageFilter(B_ANY_DELIVERY, B_ANY_SOURCE), 497 fShelf(shelf), 498 fView(view) 499 { 500 } 501 502 503 filter_result 504 ReplicantViewFilter::Filter(BMessage *message, BHandler **handler) 505 { 506 if (message->what == kDeleteReplicant) { 507 if (handler != NULL) 508 *handler = fShelf; 509 message->AddPointer("_target", fView); 510 } 511 return B_DISPATCH_MESSAGE; 512 } 513 514 515 // #pragma mark - 516 517 518 BShelf::BShelf(BView *view, bool allowDrags, const char *shelfType) 519 : BHandler(shelfType) 520 { 521 _InitData(NULL, NULL, view, allowDrags); 522 } 523 524 525 BShelf::BShelf(const entry_ref *ref, BView *view, bool allowDrags, 526 const char *shelfType) 527 : BHandler(shelfType) 528 { 529 _InitData(new BEntry(ref), NULL, view, allowDrags); 530 } 531 532 533 BShelf::BShelf(BDataIO *stream, BView *view, bool allowDrags, 534 const char *shelfType) 535 : BHandler(shelfType) 536 { 537 _InitData(NULL, stream, view, allowDrags); 538 } 539 540 541 BShelf::BShelf(BMessage *data) 542 : BHandler(data) 543 { 544 // TODO: Implement ? 545 } 546 547 548 BShelf::~BShelf() 549 { 550 Save(); 551 552 // we own fStream only when fEntry is set 553 if (fEntry) { 554 delete fEntry; 555 delete fStream; 556 } 557 558 while (fReplicants.CountItems() > 0) { 559 replicant_data *data = (replicant_data *)fReplicants.ItemAt(0); 560 fReplicants.RemoveItem(0L); 561 delete data; 562 } 563 } 564 565 566 status_t 567 BShelf::Archive(BMessage *data, bool deep) const 568 { 569 return B_ERROR; 570 } 571 572 573 BArchivable * 574 BShelf::Instantiate(BMessage *data) 575 { 576 return NULL; 577 } 578 579 580 void 581 BShelf::MessageReceived(BMessage *msg) 582 { 583 if (msg->what == kDeleteReplicant) { 584 BHandler *replicant = NULL; 585 if (msg->FindPointer("_target", (void **)&replicant) == B_OK) { 586 BView *view = dynamic_cast<BView *>(replicant); 587 if (view != NULL) 588 DeleteReplicant(view); 589 } 590 return; 591 } 592 593 BMessage replyMsg(B_REPLY); 594 status_t err = B_BAD_SCRIPT_SYNTAX; 595 596 BMessage specifier; 597 int32 what; 598 const char *prop; 599 int32 index; 600 if (msg->GetCurrentSpecifier(&index, &specifier, &what, &prop) != B_OK) 601 return BHandler::MessageReceived(msg); 602 603 switch (msg->what) { 604 case B_DELETE_PROPERTY: 605 case B_GET_PROPERTY: 606 case B_GET_SUPPORTED_SUITES: 607 if (strcmp(prop, "Replicant") == 0) { 608 BMessage reply; 609 int32 i; 610 uint32 ID; 611 BView *replicant = NULL; 612 BMessage *repMessage = NULL; 613 err = _GetProperty(&specifier, &reply); 614 if (err == B_OK) 615 err = reply.FindInt32("index", &i); 616 617 if (err == B_OK && msg->what == B_DELETE_PROPERTY) { // Delete Replicant 618 err = DeleteReplicant(i); 619 break; 620 } 621 if (err == B_OK && msg->what == B_GET_SUPPORTED_SUITES) { 622 err = replyMsg.AddString("suites", "suite/vnd.Be-replicant"); 623 if (err == B_OK) { 624 BPropertyInfo propInfo(sReplicantPropertyList); 625 err = replyMsg.AddFlat("messages", &propInfo); 626 } 627 break; 628 } 629 if (err == B_OK ) 630 repMessage = ReplicantAt(i, &replicant, &ID, &err); 631 if (err == B_OK && replicant) { 632 msg->PopSpecifier(); 633 BMessage archive; 634 err = replicant->Archive(&archive); 635 if (err == B_OK && msg->GetCurrentSpecifier(&index, &specifier, &what, &prop) != B_OK) { 636 err = replyMsg.AddMessage("result", &archive); 637 break; 638 } 639 // now handles the replicant suite 640 err = B_BAD_SCRIPT_SYNTAX; 641 if (msg->what != B_GET_PROPERTY) 642 break; 643 if (strcmp(prop, "ID") == 0) { 644 err = replyMsg.AddInt32("result", ID); 645 } else if (strcmp(prop, "Name") == 0) { 646 err = replyMsg.AddString("result", replicant->Name()); 647 } else if (strcmp(prop, "Signature") == 0) { 648 const char *add_on = NULL; 649 err = repMessage->FindString("add_on", &add_on); 650 if (err == B_OK) 651 err = replyMsg.AddString("result", add_on); 652 } else if (strcmp(prop, "Suites") == 0) { 653 err = replyMsg.AddString("suites", "suite/vnd.Be-replicant"); 654 if (err == B_OK) { 655 BPropertyInfo propInfo(sReplicantPropertyList); 656 err = replyMsg.AddFlat("messages", &propInfo); 657 } 658 } 659 } 660 break; 661 } 662 return BHandler::MessageReceived(msg); 663 664 case B_COUNT_PROPERTIES: 665 if (strcmp(prop, "Replicant") == 0) { 666 err = replyMsg.AddInt32("result", CountReplicants()); 667 break; 668 } 669 return BHandler::MessageReceived(msg); 670 671 case B_CREATE_PROPERTY: 672 { 673 BMessage replicantMsg; 674 BPoint pos; 675 if (msg->FindMessage("data", &replicantMsg) == B_OK 676 && msg->FindPoint("location", &pos) == B_OK) { 677 err = AddReplicant(&replicantMsg, pos); 678 } 679 } 680 break; 681 } 682 683 if (err < B_OK) { 684 replyMsg.what = B_MESSAGE_NOT_UNDERSTOOD; 685 686 if (err == B_BAD_SCRIPT_SYNTAX) 687 replyMsg.AddString("message", "Didn't understand the specifier(s)"); 688 else 689 replyMsg.AddString("message", strerror(err)); 690 } 691 692 replyMsg.AddInt32("error", err); 693 msg->SendReply(&replyMsg); 694 } 695 696 697 status_t 698 BShelf::Save() 699 { 700 status_t status = B_ERROR; 701 if (fEntry != NULL) { 702 BFile *file = new BFile(fEntry, B_READ_WRITE | B_ERASE_FILE); 703 status = file->InitCheck(); 704 if (status < B_OK) { 705 delete file; 706 return status; 707 } 708 fStream = file; 709 } 710 711 if (fStream != NULL) { 712 BMessage message; 713 status = _Archive(&message); 714 if (status == B_OK) 715 status = message.Flatten(fStream); 716 } 717 718 return status; 719 } 720 721 722 void 723 BShelf::SetDirty(bool state) 724 { 725 fDirty = state; 726 } 727 728 729 bool 730 BShelf::IsDirty() const 731 { 732 return fDirty; 733 } 734 735 736 BHandler * 737 BShelf::ResolveSpecifier(BMessage *msg, int32 index, BMessage *specifier, 738 int32 form, const char *property) 739 { 740 BPropertyInfo shelfPropInfo(sShelfPropertyList); 741 BHandler *target = NULL; 742 BView *replicant = NULL; 743 744 switch (shelfPropInfo.FindMatch(msg, 0, specifier, form, property)) { 745 case 0: 746 target = this; 747 break; 748 case 1: 749 if (msg->PopSpecifier() != B_OK) { 750 target = this; 751 break; 752 } 753 msg->SetCurrentSpecifier(index); 754 // fall through 755 case 2: { 756 BMessage reply; 757 status_t err = _GetProperty(specifier, &reply); 758 int32 i; 759 uint32 ID; 760 if (err == B_OK) 761 err = reply.FindInt32("index", &i); 762 if (err == B_OK) 763 ReplicantAt(i, &replicant, &ID, &err); 764 765 if (err == B_OK && replicant != NULL) { 766 if (index == 0) 767 return this; 768 } else { 769 BMessage replyMsg(B_MESSAGE_NOT_UNDERSTOOD); 770 replyMsg.AddInt32("error", B_BAD_INDEX); 771 replyMsg.AddString("message", "Cannot find replicant at/with specified index/name."); 772 msg->SendReply(&replyMsg); 773 } 774 } 775 msg->PopSpecifier(); 776 break; 777 } 778 779 if (!replicant) { 780 if (target) 781 return target; 782 return BHandler::ResolveSpecifier(msg, index, specifier, form, 783 property); 784 } 785 786 int32 repIndex; 787 status_t err = msg->GetCurrentSpecifier(&repIndex, specifier, &form, &property); 788 if (err) { 789 BMessage reply(B_MESSAGE_NOT_UNDERSTOOD); 790 reply.AddInt32("error", err); 791 msg->SendReply(&reply); 792 return NULL; 793 } 794 795 BPropertyInfo replicantPropInfo(sReplicantPropertyList); 796 switch (replicantPropInfo.FindMatch(msg, 0, specifier, form, property)) { 797 case 0: 798 case 1: 799 case 2: 800 case 3: 801 msg->SetCurrentSpecifier(index); 802 target = this; 803 break; 804 case 4: 805 target = replicant; 806 msg->PopSpecifier(); 807 break; 808 default: 809 break; 810 } 811 if (!target) { 812 BMessage replyMsg(B_MESSAGE_NOT_UNDERSTOOD); 813 replyMsg.AddInt32("error", B_BAD_SCRIPT_SYNTAX); 814 replyMsg.AddString("message", "Didn't understand the specifier(s)"); 815 msg->SendReply(&replyMsg); 816 } 817 return target; 818 } 819 820 821 status_t 822 BShelf::GetSupportedSuites(BMessage *message) 823 { 824 status_t err; 825 err = message->AddString("suites", "suite/vnd.Be-shelf"); 826 if (err == B_OK) { 827 BPropertyInfo propInfo(sShelfPropertyList); 828 err = message->AddFlat("messages", &propInfo); 829 } 830 if (err == B_OK) 831 return BHandler::GetSupportedSuites(message); 832 return err; 833 } 834 835 836 status_t 837 BShelf::Perform(perform_code d, void *arg) 838 { 839 return BHandler::Perform(d, arg); 840 } 841 842 843 bool 844 BShelf::AllowsDragging() const 845 { 846 return fAllowDragging; 847 } 848 849 850 void 851 BShelf::SetAllowsDragging(bool state) 852 { 853 fAllowDragging = state; 854 } 855 856 857 bool 858 BShelf::AllowsZombies() const 859 { 860 return fAllowZombies; 861 } 862 863 864 void 865 BShelf::SetAllowsZombies(bool state) 866 { 867 fAllowZombies = state; 868 } 869 870 871 bool 872 BShelf::DisplaysZombies() const 873 { 874 return fDisplayZombies; 875 } 876 877 878 void 879 BShelf::SetDisplaysZombies(bool state) 880 { 881 fDisplayZombies = state; 882 } 883 884 885 bool 886 BShelf::IsTypeEnforced() const 887 { 888 return fTypeEnforced; 889 } 890 891 892 void 893 BShelf::SetTypeEnforced(bool state) 894 { 895 fTypeEnforced = state; 896 } 897 898 899 status_t 900 BShelf::SetSaveLocation(BDataIO *data_io) 901 { 902 fDirty = true; 903 904 if (fEntry) { 905 delete fEntry; 906 fEntry = NULL; 907 } 908 909 fStream = data_io; 910 911 return B_OK; 912 } 913 914 915 status_t 916 BShelf::SetSaveLocation(const entry_ref *ref) 917 { 918 fDirty = true; 919 920 if (fEntry) 921 delete fEntry; 922 else 923 fStream = NULL; 924 925 fEntry = new BEntry(ref); 926 927 return B_OK; 928 } 929 930 931 BDataIO * 932 BShelf::SaveLocation(entry_ref *ref) const 933 { 934 if (fStream) { 935 if (ref) 936 *ref = entry_ref(); 937 return fStream; 938 } else if (fEntry && ref) 939 fEntry->GetRef(ref); 940 941 return NULL; 942 } 943 944 945 status_t 946 BShelf::AddReplicant(BMessage *data, BPoint location) 947 { 948 return _AddReplicant(data, &location, fGenCount++); 949 } 950 951 952 status_t 953 BShelf::DeleteReplicant(BView *replicant) 954 { 955 int32 index = replicant_data::IndexOf(&fReplicants, replicant, true); 956 957 replicant_data *item = (replicant_data*)fReplicants.ItemAt(index); 958 if (item == NULL) 959 return B_BAD_VALUE; 960 961 return _DeleteReplicant(item); 962 } 963 964 965 status_t 966 BShelf::DeleteReplicant(BMessage *data) 967 { 968 int32 index = replicant_data::IndexOf(&fReplicants, data); 969 970 replicant_data *item = (replicant_data*)fReplicants.ItemAt(index); 971 if (!item) 972 return B_BAD_VALUE; 973 974 return _DeleteReplicant(item); 975 } 976 977 978 status_t 979 BShelf::DeleteReplicant(int32 index) 980 { 981 replicant_data *item = (replicant_data*)fReplicants.ItemAt(index); 982 if (!item) 983 return B_BAD_INDEX; 984 985 return _DeleteReplicant(item); 986 } 987 988 989 int32 990 BShelf::CountReplicants() const 991 { 992 return fReplicants.CountItems(); 993 } 994 995 996 BMessage * 997 BShelf::ReplicantAt(int32 index, BView **_view, uint32 *_uniqueID, 998 status_t *_error) const 999 { 1000 replicant_data *item = (replicant_data*)fReplicants.ItemAt(index); 1001 if (item == NULL) { 1002 // no replicant found 1003 if (_view) 1004 *_view = NULL; 1005 if (_uniqueID) 1006 *_uniqueID = ~0UL; 1007 if (_error) 1008 *_error = B_BAD_INDEX; 1009 1010 return NULL; 1011 } 1012 1013 if (_view) 1014 *_view = item->view; 1015 if (_uniqueID) 1016 *_uniqueID = item->id; 1017 if (_error) 1018 *_error = item->error; 1019 1020 return item->message; 1021 } 1022 1023 1024 int32 1025 BShelf::IndexOf(const BView* replicantView) const 1026 { 1027 return replicant_data::IndexOf(&fReplicants, replicantView, false); 1028 } 1029 1030 1031 int32 1032 BShelf::IndexOf(const BMessage *archive) const 1033 { 1034 return replicant_data::IndexOf(&fReplicants, archive); 1035 } 1036 1037 1038 int32 1039 BShelf::IndexOf(uint32 id) const 1040 { 1041 return replicant_data::IndexOf(&fReplicants, id); 1042 } 1043 1044 1045 bool 1046 BShelf::CanAcceptReplicantMessage(BMessage*) const 1047 { 1048 return true; 1049 } 1050 1051 1052 bool 1053 BShelf::CanAcceptReplicantView(BRect, BView*, BMessage*) const 1054 { 1055 return true; 1056 } 1057 1058 1059 BPoint 1060 BShelf::AdjustReplicantBy(BRect, BMessage*) const 1061 { 1062 return B_ORIGIN; 1063 } 1064 1065 1066 void 1067 BShelf::ReplicantDeleted(int32 index, const BMessage *archive, 1068 const BView *replicant) 1069 { 1070 } 1071 1072 1073 extern "C" void 1074 _ReservedShelf1__6BShelfFv(BShelf *const, int32, const BMessage*, const BView*) 1075 { 1076 // is not contained in BeOS R5's libbe, so we leave it empty 1077 } 1078 1079 1080 void BShelf::_ReservedShelf2() {} 1081 void BShelf::_ReservedShelf3() {} 1082 void BShelf::_ReservedShelf4() {} 1083 void BShelf::_ReservedShelf5() {} 1084 void BShelf::_ReservedShelf6() {} 1085 void BShelf::_ReservedShelf7() {} 1086 void BShelf::_ReservedShelf8() {} 1087 1088 1089 BShelf::BShelf(const BShelf&) 1090 { 1091 } 1092 1093 1094 BShelf & 1095 BShelf::operator=(const BShelf &) 1096 { 1097 return *this; 1098 } 1099 1100 1101 status_t 1102 BShelf::_Archive(BMessage *data) const 1103 { 1104 status_t status = BHandler::Archive(data); 1105 if (status != B_OK) 1106 return status; 1107 1108 status = data->AddBool("_zom_dsp", DisplaysZombies()); 1109 if (status != B_OK) 1110 return status; 1111 1112 status = data->AddBool("_zom_alw", AllowsZombies()); 1113 if (status != B_OK) 1114 return status; 1115 1116 status = data->AddInt32("_sg_cnt", fGenCount); 1117 if (status != B_OK) 1118 return status; 1119 1120 BMessage archive('ARCV'); 1121 for (int32 i = 0; i < fReplicants.CountItems(); i++) { 1122 if (((replicant_data *)fReplicants.ItemAt(i))->Archive(&archive) == B_OK) 1123 status = data->AddMessage("replicant", &archive); 1124 if (status != B_OK) 1125 break; 1126 archive.MakeEmpty(); 1127 } 1128 1129 return status; 1130 } 1131 1132 1133 void 1134 BShelf::_InitData(BEntry *entry, BDataIO *stream, BView *view, 1135 bool allowDrags) 1136 { 1137 fContainerView = view; 1138 fStream = NULL; 1139 fEntry = entry; 1140 fFilter = NULL; 1141 fGenCount = 1; 1142 fAllowDragging = allowDrags; 1143 fDirty = true; 1144 fDisplayZombies = false; 1145 fAllowZombies = true; 1146 fTypeEnforced = false; 1147 1148 if (entry) 1149 fStream = new BFile(entry, B_READ_ONLY); 1150 else 1151 fStream = stream; 1152 1153 fFilter = new ShelfContainerViewFilter(this, fContainerView); 1154 1155 fContainerView->AddFilter(fFilter); 1156 fContainerView->_SetShelf(this); 1157 1158 if (fStream) { 1159 BMessage archive; 1160 1161 if (archive.Unflatten(fStream) == B_OK) { 1162 bool allowZombies; 1163 if (archive.FindBool("_zom_dsp", &allowZombies) != B_OK) 1164 allowZombies = false; 1165 1166 SetDisplaysZombies(allowZombies); 1167 1168 if (archive.FindBool("_zom_alw", &allowZombies) != B_OK) 1169 allowZombies = true; 1170 1171 SetAllowsZombies(allowZombies); 1172 1173 int32 genCount; 1174 if (!archive.FindInt32("_sg_cnt", &genCount)) 1175 genCount = 1; 1176 1177 BMessage replicant; 1178 BMessage *replmsg = NULL; 1179 for (int32 i = 0; archive.FindMessage("replicant", i, &replicant) == B_OK; i++) { 1180 BPoint point; 1181 replmsg = new BMessage(); 1182 replicant.FindPoint("position", &point); 1183 replicant.FindMessage("message", replmsg); 1184 AddReplicant(replmsg, point); 1185 } 1186 } 1187 } 1188 } 1189 1190 1191 status_t 1192 BShelf::_DeleteReplicant(replicant_data* item) 1193 { 1194 BView *view = item->view; 1195 if (view == NULL) 1196 view = item->zombie_view; 1197 1198 if (view) 1199 view->RemoveSelf(); 1200 1201 if (item->dragger) 1202 item->dragger->RemoveSelf(); 1203 1204 int32 index = replicant_data::IndexOf(&fReplicants, item->message); 1205 1206 ReplicantDeleted(index, item->message, view); 1207 1208 fReplicants.RemoveItem(item); 1209 1210 if (item->relation == BDragger::TARGET_IS_PARENT 1211 || item->relation == BDragger::TARGET_IS_SIBLING) { 1212 delete view; 1213 } 1214 if (item->relation == BDragger::TARGET_IS_CHILD 1215 || item->relation == BDragger::TARGET_IS_SIBLING) { 1216 delete item->dragger; 1217 } 1218 1219 // Update use count for image and unload if necessary 1220 const char* signature = NULL; 1221 if (item->message->FindString("add_on", &signature) == B_OK 1222 && signature != NULL) { 1223 LoadedImages* loadedImages = LoadedImages::Default(); 1224 AutoLock<LoadedImages> lock(loadedImages); 1225 if (lock.IsLocked()) { 1226 LoadedImageMap::iterator it = loadedImages->images.find( 1227 BString(signature)); 1228 1229 if (it != loadedImages->images.end()) { 1230 (*it).second.second--; 1231 if ((*it).second.second <= 0) { 1232 unload_add_on((*it).second.first); 1233 loadedImages->images.erase(it); 1234 } 1235 } 1236 } 1237 } 1238 1239 delete item; 1240 1241 return B_OK; 1242 } 1243 1244 1245 //! Takes over ownership of \a data on success only 1246 status_t 1247 BShelf::_AddReplicant(BMessage *data, BPoint *location, uint32 uniqueID) 1248 { 1249 // Check shelf types if needed 1250 if (fTypeEnforced) { 1251 const char *shelfType = NULL; 1252 if (data->FindString("shelf_type", &shelfType) == B_OK 1253 && shelfType != NULL) { 1254 if (Name() && strcmp(shelfType, Name()) != 0) { 1255 printf("Replicant was rejected by BShelf: The BShelf's type and the Replicant's type don't match."); 1256 return send_reply(data, B_ERROR, uniqueID); 1257 } else { 1258 printf("Replicant was rejected by BShelf: Replicant indicated a <type> (%s), but the shelf does not.", shelfType); 1259 return send_reply(data, B_ERROR, uniqueID); 1260 } 1261 } else { 1262 printf("Replicant was rejected by BShelf: Replicant did not have a <type>"); 1263 return send_reply(data, B_ERROR, uniqueID); 1264 } 1265 } 1266 1267 // Check if we can accept this message 1268 if (!CanAcceptReplicantMessage(data)) { 1269 printf("Replicant was rejected by BShelf::CanAcceptReplicantMessage()"); 1270 return send_reply(data, B_ERROR, uniqueID); 1271 } 1272 1273 // Check if we can create multiple instances 1274 if (data->FindBool("be:load_each_time")) { 1275 const char *className = NULL; 1276 const char *addOn = NULL; 1277 1278 if (data->FindString("class", &className) == B_OK 1279 && data->FindString("add_on", &addOn) == B_OK) { 1280 if (find_replicant(fReplicants, className, addOn)) { 1281 printf("Replicant was rejected. Unique replicant already exists. class=%s, signature=%s", 1282 className, addOn); 1283 return send_reply(data, B_ERROR, uniqueID); 1284 } 1285 } 1286 } 1287 1288 // Instantiate the object, if this fails we have a zombie 1289 image_id image = -1; 1290 BArchivable *archivable = _InstantiateObject(data, &image); 1291 1292 BView *view = NULL; 1293 1294 if (archivable) { 1295 view = dynamic_cast<BView*>(archivable); 1296 1297 if (!view) { 1298 return send_reply(data, B_ERROR, uniqueID); 1299 } 1300 } 1301 1302 BDragger* dragger = NULL; 1303 BView* replicant = NULL; 1304 BDragger::relation relation = BDragger::TARGET_UNKNOWN; 1305 _BZombieReplicantView_* zombie = NULL; 1306 if (view) { 1307 const BPoint point = location ? *location : view->Frame().LeftTop(); 1308 replicant = _GetReplicant(data, view, point, dragger, relation); 1309 if (replicant == NULL) 1310 return send_reply(data, B_ERROR, uniqueID); 1311 } else if (fDisplayZombies && fAllowZombies) 1312 zombie = _CreateZombie(data, dragger); 1313 1314 else if (!fAllowZombies) { 1315 // There was no view, and we're not allowed to have any zombies 1316 // in the house 1317 return send_reply(data, B_ERROR, uniqueID); 1318 } 1319 1320 // Update use count for image 1321 const char* signature = NULL; 1322 if (data->FindString("add_on", &signature) == B_OK && signature != NULL) { 1323 LoadedImages* loadedImages = LoadedImages::Default(); 1324 AutoLock<LoadedImages> lock(loadedImages); 1325 if (lock.IsLocked()) { 1326 LoadedImageMap::iterator it = loadedImages->images.find( 1327 BString(signature)); 1328 1329 if (it == loadedImages->images.end()) 1330 loadedImages->images.insert(LoadedImageMap::value_type( 1331 BString(signature), std::pair<image_id, int>(image, 1))); 1332 else 1333 (*it).second.second++; 1334 } 1335 } 1336 1337 if (!zombie) { 1338 data->RemoveName("_drop_point_"); 1339 data->RemoveName("_drop_offset_"); 1340 } 1341 1342 replicant_data *item = new replicant_data(data, replicant, dragger, 1343 relation, uniqueID); 1344 1345 item->error = B_OK; 1346 item->zombie_view = zombie; 1347 1348 fReplicants.AddItem(item); 1349 1350 return send_reply(data, B_OK, uniqueID); 1351 } 1352 1353 1354 BView * 1355 BShelf::_GetReplicant(BMessage *data, BView *view, const BPoint &point, 1356 BDragger *&dragger, BDragger::relation &relation) 1357 { 1358 // TODO: test me -- there seems to be lots of bugs parked here! 1359 BView *replicant = NULL; 1360 _GetReplicantData(data, view, replicant, dragger, relation); 1361 1362 if (dragger != NULL) 1363 dragger->_SetViewToDrag(replicant); 1364 1365 BRect frame = view->Frame().OffsetToCopy(point); 1366 if (!CanAcceptReplicantView(frame, replicant, data)) { 1367 // the view has not been accepted 1368 if (relation == BDragger::TARGET_IS_PARENT 1369 || relation == BDragger::TARGET_IS_SIBLING) { 1370 delete replicant; 1371 } 1372 if (relation == BDragger::TARGET_IS_CHILD 1373 || relation == BDragger::TARGET_IS_SIBLING) { 1374 delete dragger; 1375 } 1376 return NULL; 1377 } 1378 1379 BPoint adjust = AdjustReplicantBy(frame, data); 1380 1381 if (dragger != NULL) 1382 dragger->_SetShelf(this); 1383 1384 // TODO: could be not correct for some relations 1385 view->MoveTo(point + adjust); 1386 1387 // if it's a sibling or a child, we need to add the dragger 1388 if (relation == BDragger::TARGET_IS_SIBLING 1389 || relation == BDragger::TARGET_IS_CHILD) 1390 fContainerView->AddChild(dragger); 1391 1392 if (relation != BDragger::TARGET_IS_CHILD) 1393 fContainerView->AddChild(replicant); 1394 1395 replicant->AddFilter(new ReplicantViewFilter(this, replicant)); 1396 1397 return replicant; 1398 } 1399 1400 1401 /* static */ 1402 void 1403 BShelf::_GetReplicantData(BMessage *data, BView *view, BView *&replicant, 1404 BDragger *&dragger, BDragger::relation &relation) 1405 { 1406 // Check if we have a dragger archived as "__widget" inside the message 1407 BMessage widget; 1408 if (data->FindMessage("__widget", &widget) == B_OK) { 1409 image_id draggerImage = B_ERROR; 1410 replicant = view; 1411 dragger = dynamic_cast<BDragger*>(_InstantiateObject(&widget, &draggerImage)); 1412 // Replicant is a sibling, or unknown, if there isn't a dragger 1413 if (dragger != NULL) 1414 relation = BDragger::TARGET_IS_SIBLING; 1415 1416 } else if ((dragger = dynamic_cast<BDragger*>(view)) != NULL) { 1417 // Replicant is child of the dragger 1418 relation = BDragger::TARGET_IS_CHILD; 1419 replicant = dragger->ChildAt(0); 1420 1421 } else { 1422 // Replicant is parent of the dragger 1423 relation = BDragger::TARGET_IS_PARENT; 1424 replicant = view; 1425 dragger = dynamic_cast<BDragger *>(replicant->FindView("_dragger_")); 1426 // can be NULL, the replicant could not have a dragger at all 1427 } 1428 } 1429 1430 1431 _BZombieReplicantView_ * 1432 BShelf::_CreateZombie(BMessage *data, BDragger *&dragger) 1433 { 1434 // TODO: the zombies must be adjusted and moved as well! 1435 BRect frame; 1436 if (data->FindRect("_frame", &frame) != B_OK) 1437 frame = BRect(); 1438 1439 _BZombieReplicantView_ *zombie = NULL; 1440 if (data->WasDropped()) { 1441 BPoint offset; 1442 BPoint dropPoint = data->DropPoint(&offset); 1443 1444 frame.OffsetTo(fContainerView->ConvertFromScreen(dropPoint) - offset); 1445 1446 zombie = new _BZombieReplicantView_(frame, B_ERROR); 1447 1448 frame.OffsetTo(B_ORIGIN); 1449 1450 dragger = new BDragger(frame, zombie); 1451 dragger->_SetShelf(this); 1452 dragger->_SetZombied(true); 1453 1454 zombie->AddChild(dragger); 1455 zombie->SetArchive(data); 1456 zombie->AddFilter(new ReplicantViewFilter(this, zombie)); 1457 1458 fContainerView->AddChild(zombie); 1459 } 1460 1461 return zombie; 1462 } 1463 1464 1465 status_t 1466 BShelf::_GetProperty(BMessage *msg, BMessage *reply) 1467 { 1468 uint32 ID; 1469 status_t err = B_ERROR; 1470 BView *replicant = NULL; 1471 switch (msg->what) { 1472 case B_INDEX_SPECIFIER: { 1473 int32 index = -1; 1474 if (msg->FindInt32("index", &index)!=B_OK) 1475 break; 1476 ReplicantAt(index, &replicant, &ID, &err); 1477 break; 1478 } 1479 case B_REVERSE_INDEX_SPECIFIER: { 1480 int32 rindex; 1481 if (msg->FindInt32("index", &rindex) != B_OK) 1482 break; 1483 ReplicantAt(CountReplicants() - rindex, &replicant, &ID, &err); 1484 break; 1485 } 1486 case B_NAME_SPECIFIER: { 1487 const char *name; 1488 if (msg->FindString("name", &name) != B_OK) 1489 break; 1490 for (int32 i = 0; i < CountReplicants(); i++) { 1491 BView *view = NULL; 1492 ReplicantAt(i, &view, &ID, &err); 1493 if (err == B_OK) { 1494 if (view->Name() != NULL && 1495 strlen(view->Name()) == strlen(name) && !strcmp(view->Name(), name)) { 1496 replicant = view; 1497 break; 1498 } 1499 err = B_NAME_NOT_FOUND; 1500 } 1501 } 1502 break; 1503 } 1504 case B_ID_SPECIFIER: { 1505 uint32 id; 1506 if (msg->FindInt32("id", (int32 *)&id) != B_OK) 1507 break; 1508 for (int32 i = 0; i < CountReplicants(); i++) { 1509 BView *view = NULL; 1510 ReplicantAt(i, &view, &ID, &err); 1511 if (err == B_OK) { 1512 if (ID == id) { 1513 replicant = view; 1514 break; 1515 } 1516 err = B_NAME_NOT_FOUND; 1517 } 1518 } 1519 break; 1520 } 1521 default: 1522 break; 1523 } 1524 1525 if (replicant) { 1526 reply->AddInt32("index", IndexOf(replicant)); 1527 reply->AddInt32("ID", ID); 1528 } 1529 1530 return err; 1531 } 1532 1533 1534 /* static */ 1535 BArchivable * 1536 BShelf::_InstantiateObject(BMessage *archive, image_id *image) 1537 { 1538 // Stay on the safe side. The constructor called by instantiate_object 1539 // could throw an exception, which we catch here. Otherwise our calling app 1540 // could die without notice. 1541 try { 1542 return instantiate_object(archive, image); 1543 } catch (...) { 1544 return NULL; 1545 } 1546 } 1547 1548