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