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 else 468 return B_SKIP_MESSAGE; 469 470 BRect rect; 471 if (dragger->fRelation == BDragger::TARGET_IS_CHILD) 472 rect = dragger->Frame(); 473 else 474 rect = dragger->fTarget->Frame(); 475 rect.OffsetTo(point); 476 point = rect.LeftTop() + fShelf->AdjustReplicantBy(rect, msg); 477 478 if (dragger->fRelation == BDragger::TARGET_IS_PARENT) 479 dragger->fTarget->MoveTo(point); 480 else if (dragger->fRelation == BDragger::TARGET_IS_CHILD) 481 dragger->MoveTo(point); 482 else { 483 //TODO: TARGET_UNKNOWN/TARGET_SIBLING 484 } 485 486 } else { 487 if (fShelf->_AddReplicant(msg, &point, fShelf->fGenCount++) == B_OK) 488 Looper()->DetachCurrentMessage(); 489 } 490 491 return B_SKIP_MESSAGE; 492 } 493 494 495 // #pragma mark - 496 497 498 ReplicantViewFilter::ReplicantViewFilter(BShelf *shelf, BView *view) 499 : BMessageFilter(B_ANY_DELIVERY, B_ANY_SOURCE), 500 fShelf(shelf), 501 fView(view) 502 { 503 } 504 505 506 filter_result 507 ReplicantViewFilter::Filter(BMessage *message, BHandler **handler) 508 { 509 if (message->what == kDeleteReplicant) { 510 if (handler != NULL) 511 *handler = fShelf; 512 message->AddPointer("_target", fView); 513 } 514 return B_DISPATCH_MESSAGE; 515 } 516 517 518 // #pragma mark - 519 520 521 BShelf::BShelf(BView *view, bool allowDrags, const char *shelfType) 522 : BHandler(shelfType) 523 { 524 _InitData(NULL, NULL, view, allowDrags); 525 } 526 527 528 BShelf::BShelf(const entry_ref *ref, BView *view, bool allowDrags, 529 const char *shelfType) 530 : BHandler(shelfType) 531 { 532 _InitData(new BEntry(ref), NULL, view, allowDrags); 533 } 534 535 536 BShelf::BShelf(BDataIO *stream, BView *view, bool allowDrags, 537 const char *shelfType) 538 : BHandler(shelfType) 539 { 540 _InitData(NULL, stream, view, allowDrags); 541 } 542 543 544 BShelf::BShelf(BMessage *data) 545 : BHandler(data) 546 { 547 // TODO: Implement ? 548 } 549 550 551 BShelf::~BShelf() 552 { 553 Save(); 554 555 // we own fStream only when fEntry is set 556 if (fEntry != NULL) { 557 delete fEntry; 558 delete fStream; 559 } 560 561 while (fReplicants.CountItems() > 0) { 562 replicant_data *data = (replicant_data *)fReplicants.ItemAt(0); 563 fReplicants.RemoveItem((int32)0); 564 delete data; 565 } 566 567 fContainerView->_SetShelf(NULL); 568 } 569 570 571 status_t 572 BShelf::Archive(BMessage *data, bool deep) const 573 { 574 return B_ERROR; 575 } 576 577 578 BArchivable * 579 BShelf::Instantiate(BMessage *data) 580 { 581 return NULL; 582 } 583 584 585 void 586 BShelf::MessageReceived(BMessage *msg) 587 { 588 if (msg->what == kDeleteReplicant) { 589 BHandler *replicant = NULL; 590 if (msg->FindPointer("_target", (void **)&replicant) == B_OK) { 591 BView *view = dynamic_cast<BView *>(replicant); 592 if (view != NULL) 593 DeleteReplicant(view); 594 } 595 return; 596 } 597 598 BMessage replyMsg(B_REPLY); 599 status_t err = B_BAD_SCRIPT_SYNTAX; 600 601 BMessage specifier; 602 int32 what; 603 const char *prop; 604 int32 index; 605 if (msg->GetCurrentSpecifier(&index, &specifier, &what, &prop) != B_OK) 606 return BHandler::MessageReceived(msg); 607 608 switch (msg->what) { 609 case B_DELETE_PROPERTY: 610 case B_GET_PROPERTY: 611 case B_GET_SUPPORTED_SUITES: 612 if (strcmp(prop, "Replicant") == 0) { 613 BMessage reply; 614 int32 i; 615 uint32 ID; 616 BView *replicant = NULL; 617 BMessage *repMessage = NULL; 618 err = _GetProperty(&specifier, &reply); 619 if (err == B_OK) 620 err = reply.FindInt32("index", &i); 621 622 if (err == B_OK && msg->what == B_DELETE_PROPERTY) { // Delete Replicant 623 err = DeleteReplicant(i); 624 break; 625 } 626 if (err == B_OK && msg->what == B_GET_SUPPORTED_SUITES) { 627 err = replyMsg.AddString("suites", "suite/vnd.Be-replicant"); 628 if (err == B_OK) { 629 BPropertyInfo propInfo(sReplicantPropertyList); 630 err = replyMsg.AddFlat("messages", &propInfo); 631 } 632 break; 633 } 634 if (err == B_OK ) 635 repMessage = ReplicantAt(i, &replicant, &ID, &err); 636 if (err == B_OK && replicant) { 637 msg->PopSpecifier(); 638 BMessage archive; 639 err = replicant->Archive(&archive); 640 if (err == B_OK && msg->GetCurrentSpecifier(&index, &specifier, &what, &prop) != B_OK) { 641 err = replyMsg.AddMessage("result", &archive); 642 break; 643 } 644 // now handles the replicant suite 645 err = B_BAD_SCRIPT_SYNTAX; 646 if (msg->what != B_GET_PROPERTY) 647 break; 648 if (strcmp(prop, "ID") == 0) { 649 err = replyMsg.AddInt32("result", ID); 650 } else if (strcmp(prop, "Name") == 0) { 651 err = replyMsg.AddString("result", replicant->Name()); 652 } else if (strcmp(prop, "Signature") == 0) { 653 const char *add_on = NULL; 654 err = repMessage->FindString("add_on", &add_on); 655 if (err == B_OK) 656 err = replyMsg.AddString("result", add_on); 657 } else if (strcmp(prop, "Suites") == 0) { 658 err = replyMsg.AddString("suites", "suite/vnd.Be-replicant"); 659 if (err == B_OK) { 660 BPropertyInfo propInfo(sReplicantPropertyList); 661 err = replyMsg.AddFlat("messages", &propInfo); 662 } 663 } 664 } 665 break; 666 } 667 return BHandler::MessageReceived(msg); 668 669 case B_COUNT_PROPERTIES: 670 if (strcmp(prop, "Replicant") == 0) { 671 err = replyMsg.AddInt32("result", CountReplicants()); 672 break; 673 } 674 return BHandler::MessageReceived(msg); 675 676 case B_CREATE_PROPERTY: 677 { 678 BMessage replicantMsg; 679 BPoint pos; 680 if (msg->FindMessage("data", &replicantMsg) == B_OK 681 && msg->FindPoint("location", &pos) == B_OK) { 682 err = AddReplicant(&replicantMsg, pos); 683 } 684 } 685 break; 686 } 687 688 if (err < B_OK) { 689 replyMsg.what = B_MESSAGE_NOT_UNDERSTOOD; 690 691 if (err == B_BAD_SCRIPT_SYNTAX) 692 replyMsg.AddString("message", "Didn't understand the specifier(s)"); 693 else 694 replyMsg.AddString("message", strerror(err)); 695 } 696 697 replyMsg.AddInt32("error", err); 698 msg->SendReply(&replyMsg); 699 } 700 701 702 status_t 703 BShelf::Save() 704 { 705 status_t status = B_ERROR; 706 if (fEntry != NULL) { 707 BFile *file = new BFile(fEntry, B_READ_WRITE | B_ERASE_FILE); 708 status = file->InitCheck(); 709 if (status != B_OK) { 710 delete file; 711 return status; 712 } 713 delete fStream; 714 fStream = file; 715 } 716 717 if (fStream != NULL) { 718 BMessage message; 719 status = _Archive(&message); 720 if (status == B_OK) 721 status = message.Flatten(fStream); 722 } 723 724 return status; 725 } 726 727 728 void 729 BShelf::SetDirty(bool state) 730 { 731 fDirty = state; 732 } 733 734 735 bool 736 BShelf::IsDirty() const 737 { 738 return fDirty; 739 } 740 741 742 BHandler * 743 BShelf::ResolveSpecifier(BMessage *msg, int32 index, BMessage *specifier, 744 int32 form, const char *property) 745 { 746 BPropertyInfo shelfPropInfo(sShelfPropertyList); 747 BHandler *target = NULL; 748 BView *replicant = NULL; 749 750 switch (shelfPropInfo.FindMatch(msg, 0, specifier, form, property)) { 751 case 0: 752 target = this; 753 break; 754 case 1: 755 if (msg->PopSpecifier() != B_OK) { 756 target = this; 757 break; 758 } 759 msg->SetCurrentSpecifier(index); 760 // fall through 761 case 2: { 762 BMessage reply; 763 status_t err = _GetProperty(specifier, &reply); 764 int32 i; 765 uint32 ID; 766 if (err == B_OK) 767 err = reply.FindInt32("index", &i); 768 if (err == B_OK) 769 ReplicantAt(i, &replicant, &ID, &err); 770 771 if (err == B_OK && replicant != NULL) { 772 if (index == 0) 773 return this; 774 } else { 775 BMessage replyMsg(B_MESSAGE_NOT_UNDERSTOOD); 776 replyMsg.AddInt32("error", B_BAD_INDEX); 777 replyMsg.AddString("message", "Cannot find replicant at/with specified index/name."); 778 msg->SendReply(&replyMsg); 779 } 780 } 781 msg->PopSpecifier(); 782 break; 783 } 784 785 if (!replicant) { 786 if (target) 787 return target; 788 return BHandler::ResolveSpecifier(msg, index, specifier, form, 789 property); 790 } 791 792 int32 repIndex; 793 status_t err = msg->GetCurrentSpecifier(&repIndex, specifier, &form, &property); 794 if (err) { 795 BMessage reply(B_MESSAGE_NOT_UNDERSTOOD); 796 reply.AddInt32("error", err); 797 msg->SendReply(&reply); 798 return NULL; 799 } 800 801 BPropertyInfo replicantPropInfo(sReplicantPropertyList); 802 switch (replicantPropInfo.FindMatch(msg, 0, specifier, form, property)) { 803 case 0: 804 case 1: 805 case 2: 806 case 3: 807 msg->SetCurrentSpecifier(index); 808 target = this; 809 break; 810 case 4: 811 target = replicant; 812 msg->PopSpecifier(); 813 break; 814 default: 815 break; 816 } 817 if (!target) { 818 BMessage replyMsg(B_MESSAGE_NOT_UNDERSTOOD); 819 replyMsg.AddInt32("error", B_BAD_SCRIPT_SYNTAX); 820 replyMsg.AddString("message", "Didn't understand the specifier(s)"); 821 msg->SendReply(&replyMsg); 822 } 823 return target; 824 } 825 826 827 status_t 828 BShelf::GetSupportedSuites(BMessage *message) 829 { 830 status_t err; 831 err = message->AddString("suites", "suite/vnd.Be-shelf"); 832 if (err == B_OK) { 833 BPropertyInfo propInfo(sShelfPropertyList); 834 err = message->AddFlat("messages", &propInfo); 835 } 836 if (err == B_OK) 837 return BHandler::GetSupportedSuites(message); 838 return err; 839 } 840 841 842 status_t 843 BShelf::Perform(perform_code d, void *arg) 844 { 845 return BHandler::Perform(d, arg); 846 } 847 848 849 bool 850 BShelf::AllowsDragging() const 851 { 852 return fAllowDragging; 853 } 854 855 856 void 857 BShelf::SetAllowsDragging(bool state) 858 { 859 fAllowDragging = state; 860 } 861 862 863 bool 864 BShelf::AllowsZombies() const 865 { 866 return fAllowZombies; 867 } 868 869 870 void 871 BShelf::SetAllowsZombies(bool state) 872 { 873 fAllowZombies = state; 874 } 875 876 877 bool 878 BShelf::DisplaysZombies() const 879 { 880 return fDisplayZombies; 881 } 882 883 884 void 885 BShelf::SetDisplaysZombies(bool state) 886 { 887 fDisplayZombies = state; 888 } 889 890 891 bool 892 BShelf::IsTypeEnforced() const 893 { 894 return fTypeEnforced; 895 } 896 897 898 void 899 BShelf::SetTypeEnforced(bool state) 900 { 901 fTypeEnforced = state; 902 } 903 904 905 status_t 906 BShelf::SetSaveLocation(BDataIO *data_io) 907 { 908 fDirty = true; 909 910 if (fEntry != NULL) { 911 delete fEntry; 912 fEntry = NULL; 913 } 914 915 fStream = data_io; 916 917 return B_OK; 918 } 919 920 921 status_t 922 BShelf::SetSaveLocation(const entry_ref *ref) 923 { 924 fDirty = true; 925 926 if (fEntry) 927 delete fEntry; 928 else 929 fStream = NULL; 930 931 fEntry = new BEntry(ref); 932 933 return B_OK; 934 } 935 936 937 BDataIO * 938 BShelf::SaveLocation(entry_ref *ref) const 939 { 940 if (fStream) { 941 if (ref) 942 *ref = entry_ref(); 943 return fStream; 944 } else if (fEntry && ref) 945 fEntry->GetRef(ref); 946 947 return NULL; 948 } 949 950 951 status_t 952 BShelf::AddReplicant(BMessage *data, BPoint location) 953 { 954 return _AddReplicant(data, &location, fGenCount++); 955 } 956 957 958 status_t 959 BShelf::DeleteReplicant(BView *replicant) 960 { 961 int32 index = replicant_data::IndexOf(&fReplicants, replicant, true); 962 963 replicant_data *item = (replicant_data*)fReplicants.ItemAt(index); 964 if (item == NULL) 965 return B_BAD_VALUE; 966 967 return _DeleteReplicant(item); 968 } 969 970 971 status_t 972 BShelf::DeleteReplicant(BMessage *data) 973 { 974 int32 index = replicant_data::IndexOf(&fReplicants, data); 975 976 replicant_data *item = (replicant_data*)fReplicants.ItemAt(index); 977 if (!item) 978 return B_BAD_VALUE; 979 980 return _DeleteReplicant(item); 981 } 982 983 984 status_t 985 BShelf::DeleteReplicant(int32 index) 986 { 987 replicant_data *item = (replicant_data*)fReplicants.ItemAt(index); 988 if (!item) 989 return B_BAD_INDEX; 990 991 return _DeleteReplicant(item); 992 } 993 994 995 int32 996 BShelf::CountReplicants() const 997 { 998 return fReplicants.CountItems(); 999 } 1000 1001 1002 BMessage * 1003 BShelf::ReplicantAt(int32 index, BView **_view, uint32 *_uniqueID, 1004 status_t *_error) const 1005 { 1006 replicant_data *item = (replicant_data*)fReplicants.ItemAt(index); 1007 if (item == NULL) { 1008 // no replicant found 1009 if (_view) 1010 *_view = NULL; 1011 if (_uniqueID) 1012 *_uniqueID = ~(uint32)0; 1013 if (_error) 1014 *_error = B_BAD_INDEX; 1015 1016 return NULL; 1017 } 1018 1019 if (_view) 1020 *_view = item->view; 1021 if (_uniqueID) 1022 *_uniqueID = item->id; 1023 if (_error) 1024 *_error = item->error; 1025 1026 return item->message; 1027 } 1028 1029 1030 int32 1031 BShelf::IndexOf(const BView* replicantView) const 1032 { 1033 return replicant_data::IndexOf(&fReplicants, replicantView, false); 1034 } 1035 1036 1037 int32 1038 BShelf::IndexOf(const BMessage *archive) const 1039 { 1040 return replicant_data::IndexOf(&fReplicants, archive); 1041 } 1042 1043 1044 int32 1045 BShelf::IndexOf(uint32 id) const 1046 { 1047 return replicant_data::IndexOf(&fReplicants, id); 1048 } 1049 1050 1051 bool 1052 BShelf::CanAcceptReplicantMessage(BMessage*) const 1053 { 1054 return true; 1055 } 1056 1057 1058 bool 1059 BShelf::CanAcceptReplicantView(BRect, BView*, BMessage*) const 1060 { 1061 return true; 1062 } 1063 1064 1065 BPoint 1066 BShelf::AdjustReplicantBy(BRect, BMessage*) const 1067 { 1068 return B_ORIGIN; 1069 } 1070 1071 1072 void 1073 BShelf::ReplicantDeleted(int32 index, const BMessage *archive, 1074 const BView *replicant) 1075 { 1076 } 1077 1078 1079 extern "C" void 1080 _ReservedShelf1__6BShelfFv(BShelf *const, int32, const BMessage*, const BView*) 1081 { 1082 // is not contained in BeOS R5's libbe, so we leave it empty 1083 } 1084 1085 1086 void BShelf::_ReservedShelf2() {} 1087 void BShelf::_ReservedShelf3() {} 1088 void BShelf::_ReservedShelf4() {} 1089 void BShelf::_ReservedShelf5() {} 1090 void BShelf::_ReservedShelf6() {} 1091 void BShelf::_ReservedShelf7() {} 1092 void BShelf::_ReservedShelf8() {} 1093 1094 1095 BShelf::BShelf(const BShelf&) 1096 { 1097 } 1098 1099 1100 BShelf & 1101 BShelf::operator=(const BShelf &) 1102 { 1103 return *this; 1104 } 1105 1106 1107 status_t 1108 BShelf::_Archive(BMessage *data) const 1109 { 1110 status_t status = BHandler::Archive(data); 1111 if (status != B_OK) 1112 return status; 1113 1114 status = data->AddBool("_zom_dsp", DisplaysZombies()); 1115 if (status != B_OK) 1116 return status; 1117 1118 status = data->AddBool("_zom_alw", AllowsZombies()); 1119 if (status != B_OK) 1120 return status; 1121 1122 status = data->AddInt32("_sg_cnt", fGenCount); 1123 if (status != B_OK) 1124 return status; 1125 1126 BMessage archive('ARCV'); 1127 for (int32 i = 0; i < fReplicants.CountItems(); i++) { 1128 if (((replicant_data *)fReplicants.ItemAt(i))->Archive(&archive) == B_OK) 1129 status = data->AddMessage("replicant", &archive); 1130 if (status != B_OK) 1131 break; 1132 archive.MakeEmpty(); 1133 } 1134 1135 return status; 1136 } 1137 1138 1139 void 1140 BShelf::_InitData(BEntry *entry, BDataIO *stream, BView *view, 1141 bool allowDrags) 1142 { 1143 fContainerView = view; 1144 fStream = NULL; 1145 fEntry = entry; 1146 fFilter = NULL; 1147 fGenCount = 1; 1148 fAllowDragging = allowDrags; 1149 fDirty = true; 1150 fDisplayZombies = false; 1151 fAllowZombies = true; 1152 fTypeEnforced = false; 1153 1154 if (fEntry != NULL) 1155 fStream = new BFile(entry, B_READ_ONLY); 1156 else 1157 fStream = stream; 1158 1159 fFilter = new ShelfContainerViewFilter(this, fContainerView); 1160 1161 fContainerView->AddFilter(fFilter); 1162 fContainerView->_SetShelf(this); 1163 1164 if (fStream != NULL) { 1165 BMessage archive; 1166 1167 if (archive.Unflatten(fStream) == B_OK) { 1168 bool allowZombies; 1169 if (archive.FindBool("_zom_dsp", &allowZombies) != B_OK) 1170 allowZombies = false; 1171 1172 SetDisplaysZombies(allowZombies); 1173 1174 if (archive.FindBool("_zom_alw", &allowZombies) != B_OK) 1175 allowZombies = true; 1176 1177 SetAllowsZombies(allowZombies); 1178 1179 int32 genCount; 1180 if (!archive.FindInt32("_sg_cnt", &genCount)) 1181 genCount = 1; 1182 1183 BMessage replicant; 1184 for (int32 i = 0; archive.FindMessage("replicant", i, &replicant) 1185 == B_OK; i++) { 1186 BPoint point; 1187 BMessage *replMsg = new BMessage(); 1188 ObjectDeleter<BMessage> deleter(replMsg); 1189 replicant.FindPoint("position", &point); 1190 if (replicant.FindMessage("message", replMsg) == B_OK) 1191 if (AddReplicant(replMsg, point) == B_OK) { 1192 // Detach the deleter since AddReplicant is taking 1193 // ownership on success. In R2 API this should be 1194 // changed to take always ownership on the message. 1195 deleter.Detach(); 1196 } 1197 } 1198 } 1199 } 1200 } 1201 1202 1203 status_t 1204 BShelf::_DeleteReplicant(replicant_data* item) 1205 { 1206 BView *view = item->view; 1207 if (view == NULL) 1208 view = item->zombie_view; 1209 1210 if (view != NULL) 1211 view->RemoveSelf(); 1212 1213 if (item->dragger != NULL) 1214 item->dragger->RemoveSelf(); 1215 1216 int32 index = replicant_data::IndexOf(&fReplicants, item->message); 1217 1218 ReplicantDeleted(index, item->message, view); 1219 1220 fReplicants.RemoveItem(item); 1221 1222 if (item->relation == BDragger::TARGET_IS_PARENT 1223 || item->relation == BDragger::TARGET_IS_SIBLING) { 1224 delete view; 1225 } 1226 if (item->relation == BDragger::TARGET_IS_CHILD 1227 || item->relation == BDragger::TARGET_IS_SIBLING) { 1228 delete item->dragger; 1229 } 1230 1231 // Update use count for image and unload if necessary 1232 const char* signature = NULL; 1233 if (item->message->FindString("add_on", &signature) == B_OK 1234 && signature != NULL) { 1235 LoadedImages* loadedImages = LoadedImages::Default(); 1236 AutoLock<LoadedImages> lock(loadedImages); 1237 if (lock.IsLocked()) { 1238 LoadedImageMap::iterator it = loadedImages->images.find( 1239 BString(signature)); 1240 1241 if (it != loadedImages->images.end()) { 1242 (*it).second.second--; 1243 if ((*it).second.second <= 0) { 1244 unload_add_on((*it).second.first); 1245 loadedImages->images.erase(it); 1246 } 1247 } 1248 } 1249 } 1250 1251 delete item; 1252 1253 return B_OK; 1254 } 1255 1256 1257 //! Takes over ownership of \a data on success only 1258 status_t 1259 BShelf::_AddReplicant(BMessage *data, BPoint *location, uint32 uniqueID) 1260 { 1261 // Check shelf types if needed 1262 if (fTypeEnforced) { 1263 const char *shelfType = NULL; 1264 if (data->FindString("shelf_type", &shelfType) == B_OK 1265 && shelfType != NULL) { 1266 if (Name() && strcmp(shelfType, Name()) != 0) { 1267 printf("Replicant was rejected by BShelf: The BShelf's type and the Replicant's type don't match."); 1268 return send_reply(data, B_ERROR, uniqueID); 1269 } else { 1270 printf("Replicant was rejected by BShelf: Replicant indicated a <type> (%s), but the shelf does not.", shelfType); 1271 return send_reply(data, B_ERROR, uniqueID); 1272 } 1273 } else { 1274 printf("Replicant was rejected by BShelf: Replicant did not have a <type>"); 1275 return send_reply(data, B_ERROR, uniqueID); 1276 } 1277 } 1278 1279 // Check if we can accept this message 1280 if (!CanAcceptReplicantMessage(data)) { 1281 printf("Replicant was rejected by BShelf::CanAcceptReplicantMessage()"); 1282 return send_reply(data, B_ERROR, uniqueID); 1283 } 1284 1285 // Check if we can create multiple instances 1286 if (data->FindBool("be:load_each_time")) { 1287 const char *className = NULL; 1288 const char *addOn = NULL; 1289 1290 if (data->FindString("class", &className) == B_OK 1291 && data->FindString("add_on", &addOn) == B_OK) { 1292 if (find_replicant(fReplicants, className, addOn)) { 1293 printf("Replicant was rejected. Unique replicant already exists. class=%s, signature=%s", 1294 className, addOn); 1295 return send_reply(data, B_ERROR, uniqueID); 1296 } 1297 } 1298 } 1299 1300 // Instantiate the object, if this fails we have a zombie 1301 image_id image = -1; 1302 BArchivable *archivable = _InstantiateObject(data, &image); 1303 1304 BView *view = NULL; 1305 1306 if (archivable != NULL) { 1307 view = dynamic_cast<BView*>(archivable); 1308 1309 if (view == NULL) 1310 return send_reply(data, B_ERROR, uniqueID); 1311 } 1312 1313 BDragger* dragger = NULL; 1314 BView* replicant = NULL; 1315 BDragger::relation relation = BDragger::TARGET_UNKNOWN; 1316 _BZombieReplicantView_* zombie = NULL; 1317 if (view != NULL) { 1318 const BPoint point = location ? *location : view->Frame().LeftTop(); 1319 replicant = _GetReplicant(data, view, point, dragger, relation); 1320 if (replicant == NULL) 1321 return send_reply(data, B_ERROR, uniqueID); 1322 } else if (fDisplayZombies && fAllowZombies) { 1323 zombie = _CreateZombie(data, dragger); 1324 } else if (!fAllowZombies) { 1325 // There was no view, and we're not allowed to have any zombies 1326 // in the house 1327 return send_reply(data, B_ERROR, uniqueID); 1328 } 1329 1330 // Update use count for image 1331 const char* signature = NULL; 1332 if (data->FindString("add_on", &signature) == B_OK && signature != NULL) { 1333 LoadedImages* loadedImages = LoadedImages::Default(); 1334 AutoLock<LoadedImages> lock(loadedImages); 1335 if (lock.IsLocked()) { 1336 LoadedImageMap::iterator it = loadedImages->images.find( 1337 BString(signature)); 1338 1339 if (it == loadedImages->images.end()) 1340 loadedImages->images.insert(LoadedImageMap::value_type( 1341 BString(signature), std::pair<image_id, int>(image, 1))); 1342 else 1343 (*it).second.second++; 1344 } 1345 } 1346 1347 if (zombie == NULL) { 1348 data->RemoveName("_drop_point_"); 1349 data->RemoveName("_drop_offset_"); 1350 } 1351 1352 replicant_data *item = new replicant_data(data, replicant, dragger, 1353 relation, uniqueID); 1354 1355 item->error = B_OK; 1356 item->zombie_view = zombie; 1357 1358 fReplicants.AddItem(item); 1359 1360 return send_reply(data, B_OK, uniqueID); 1361 } 1362 1363 1364 BView * 1365 BShelf::_GetReplicant(BMessage *data, BView *view, const BPoint &point, 1366 BDragger *&dragger, BDragger::relation &relation) 1367 { 1368 // TODO: test me -- there seems to be lots of bugs parked here! 1369 BView *replicant = NULL; 1370 _GetReplicantData(data, view, replicant, dragger, relation); 1371 1372 if (dragger != NULL) 1373 dragger->_SetViewToDrag(replicant); 1374 1375 BRect frame = view->Frame().OffsetToCopy(point); 1376 if (!CanAcceptReplicantView(frame, replicant, data)) { 1377 // the view has not been accepted 1378 if (relation == BDragger::TARGET_IS_PARENT 1379 || relation == BDragger::TARGET_IS_SIBLING) { 1380 delete replicant; 1381 } 1382 if (relation == BDragger::TARGET_IS_CHILD 1383 || relation == BDragger::TARGET_IS_SIBLING) { 1384 delete dragger; 1385 } 1386 return NULL; 1387 } 1388 1389 BPoint adjust = AdjustReplicantBy(frame, data); 1390 1391 if (dragger != NULL) 1392 dragger->_SetShelf(this); 1393 1394 // TODO: could be not correct for some relations 1395 view->MoveTo(point + adjust); 1396 1397 // if it's a sibling or a child, we need to add the dragger 1398 if (relation == BDragger::TARGET_IS_SIBLING 1399 || relation == BDragger::TARGET_IS_CHILD) 1400 fContainerView->AddChild(dragger); 1401 1402 if (relation != BDragger::TARGET_IS_CHILD) 1403 fContainerView->AddChild(replicant); 1404 1405 replicant->AddFilter(new ReplicantViewFilter(this, replicant)); 1406 1407 return replicant; 1408 } 1409 1410 1411 /* static */ 1412 void 1413 BShelf::_GetReplicantData(BMessage *data, BView *view, BView *&replicant, 1414 BDragger *&dragger, BDragger::relation &relation) 1415 { 1416 // Check if we have a dragger archived as "__widget" inside the message 1417 BMessage widget; 1418 if (data->FindMessage("__widget", &widget) == B_OK) { 1419 image_id draggerImage = B_ERROR; 1420 replicant = view; 1421 dragger = dynamic_cast<BDragger*>(_InstantiateObject(&widget, &draggerImage)); 1422 // Replicant is a sibling, or unknown, if there isn't a dragger 1423 if (dragger != NULL) 1424 relation = BDragger::TARGET_IS_SIBLING; 1425 1426 } else if ((dragger = dynamic_cast<BDragger*>(view)) != NULL) { 1427 // Replicant is child of the dragger 1428 relation = BDragger::TARGET_IS_CHILD; 1429 replicant = dragger->ChildAt(0); 1430 1431 } else { 1432 // Replicant is parent of the dragger 1433 relation = BDragger::TARGET_IS_PARENT; 1434 replicant = view; 1435 dragger = dynamic_cast<BDragger *>(replicant->FindView("_dragger_")); 1436 // can be NULL, the replicant could not have a dragger at all 1437 } 1438 } 1439 1440 1441 _BZombieReplicantView_ * 1442 BShelf::_CreateZombie(BMessage *data, BDragger *&dragger) 1443 { 1444 // TODO: the zombies must be adjusted and moved as well! 1445 BRect frame; 1446 if (data->FindRect("_frame", &frame) != B_OK) 1447 frame = BRect(); 1448 1449 _BZombieReplicantView_ *zombie = NULL; 1450 if (data->WasDropped()) { 1451 BPoint offset; 1452 BPoint dropPoint = data->DropPoint(&offset); 1453 1454 frame.OffsetTo(fContainerView->ConvertFromScreen(dropPoint) - offset); 1455 1456 zombie = new _BZombieReplicantView_(frame, B_ERROR); 1457 1458 frame.OffsetTo(B_ORIGIN); 1459 1460 dragger = new BDragger(frame, zombie); 1461 dragger->_SetShelf(this); 1462 dragger->_SetZombied(true); 1463 1464 zombie->AddChild(dragger); 1465 zombie->SetArchive(data); 1466 zombie->AddFilter(new ReplicantViewFilter(this, zombie)); 1467 1468 fContainerView->AddChild(zombie); 1469 } 1470 1471 return zombie; 1472 } 1473 1474 1475 status_t 1476 BShelf::_GetProperty(BMessage *msg, BMessage *reply) 1477 { 1478 uint32 ID; 1479 status_t err = B_ERROR; 1480 BView *replicant = NULL; 1481 switch (msg->what) { 1482 case B_INDEX_SPECIFIER: { 1483 int32 index = -1; 1484 if (msg->FindInt32("index", &index)!=B_OK) 1485 break; 1486 ReplicantAt(index, &replicant, &ID, &err); 1487 break; 1488 } 1489 case B_REVERSE_INDEX_SPECIFIER: { 1490 int32 rindex; 1491 if (msg->FindInt32("index", &rindex) != B_OK) 1492 break; 1493 ReplicantAt(CountReplicants() - rindex, &replicant, &ID, &err); 1494 break; 1495 } 1496 case B_NAME_SPECIFIER: { 1497 const char *name; 1498 if (msg->FindString("name", &name) != B_OK) 1499 break; 1500 for (int32 i = 0; i < CountReplicants(); i++) { 1501 BView *view = NULL; 1502 ReplicantAt(i, &view, &ID, &err); 1503 if (err != B_OK || view == NULL) 1504 continue; 1505 if (view->Name() != NULL && strcmp(view->Name(), name) == 0) { 1506 replicant = view; 1507 break; 1508 } 1509 err = B_NAME_NOT_FOUND; 1510 } 1511 break; 1512 } 1513 case B_ID_SPECIFIER: { 1514 uint32 id; 1515 if (msg->FindInt32("id", (int32 *)&id) != B_OK) 1516 break; 1517 for (int32 i = 0; i < CountReplicants(); i++) { 1518 BView *view = NULL; 1519 ReplicantAt(i, &view, &ID, &err); 1520 if (err != B_OK || view == NULL) 1521 continue; 1522 if (ID == id) { 1523 replicant = view; 1524 break; 1525 } 1526 err = B_NAME_NOT_FOUND; 1527 } 1528 break; 1529 } 1530 default: 1531 break; 1532 } 1533 1534 if (replicant) { 1535 reply->AddInt32("index", IndexOf(replicant)); 1536 reply->AddInt32("ID", ID); 1537 } 1538 1539 return err; 1540 } 1541 1542 1543 /* static */ 1544 BArchivable * 1545 BShelf::_InstantiateObject(BMessage *archive, image_id *image) 1546 { 1547 // Stay on the safe side. The constructor called by instantiate_object 1548 // could throw an exception, which we catch here. Otherwise our calling app 1549 // could die without notice. 1550 try { 1551 return instantiate_object(archive, image); 1552 } catch (...) { 1553 return NULL; 1554 } 1555 } 1556 1557