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 != NULL) { 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((int32)0); 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 delete fStream; 709 fStream = file; 710 } 711 712 if (fStream != NULL) { 713 BMessage message; 714 status = _Archive(&message); 715 if (status == B_OK) 716 status = message.Flatten(fStream); 717 } 718 719 return status; 720 } 721 722 723 void 724 BShelf::SetDirty(bool state) 725 { 726 fDirty = state; 727 } 728 729 730 bool 731 BShelf::IsDirty() const 732 { 733 return fDirty; 734 } 735 736 737 BHandler * 738 BShelf::ResolveSpecifier(BMessage *msg, int32 index, BMessage *specifier, 739 int32 form, const char *property) 740 { 741 BPropertyInfo shelfPropInfo(sShelfPropertyList); 742 BHandler *target = NULL; 743 BView *replicant = NULL; 744 745 switch (shelfPropInfo.FindMatch(msg, 0, specifier, form, property)) { 746 case 0: 747 target = this; 748 break; 749 case 1: 750 if (msg->PopSpecifier() != B_OK) { 751 target = this; 752 break; 753 } 754 msg->SetCurrentSpecifier(index); 755 // fall through 756 case 2: { 757 BMessage reply; 758 status_t err = _GetProperty(specifier, &reply); 759 int32 i; 760 uint32 ID; 761 if (err == B_OK) 762 err = reply.FindInt32("index", &i); 763 if (err == B_OK) 764 ReplicantAt(i, &replicant, &ID, &err); 765 766 if (err == B_OK && replicant != NULL) { 767 if (index == 0) 768 return this; 769 } else { 770 BMessage replyMsg(B_MESSAGE_NOT_UNDERSTOOD); 771 replyMsg.AddInt32("error", B_BAD_INDEX); 772 replyMsg.AddString("message", "Cannot find replicant at/with specified index/name."); 773 msg->SendReply(&replyMsg); 774 } 775 } 776 msg->PopSpecifier(); 777 break; 778 } 779 780 if (!replicant) { 781 if (target) 782 return target; 783 return BHandler::ResolveSpecifier(msg, index, specifier, form, 784 property); 785 } 786 787 int32 repIndex; 788 status_t err = msg->GetCurrentSpecifier(&repIndex, specifier, &form, &property); 789 if (err) { 790 BMessage reply(B_MESSAGE_NOT_UNDERSTOOD); 791 reply.AddInt32("error", err); 792 msg->SendReply(&reply); 793 return NULL; 794 } 795 796 BPropertyInfo replicantPropInfo(sReplicantPropertyList); 797 switch (replicantPropInfo.FindMatch(msg, 0, specifier, form, property)) { 798 case 0: 799 case 1: 800 case 2: 801 case 3: 802 msg->SetCurrentSpecifier(index); 803 target = this; 804 break; 805 case 4: 806 target = replicant; 807 msg->PopSpecifier(); 808 break; 809 default: 810 break; 811 } 812 if (!target) { 813 BMessage replyMsg(B_MESSAGE_NOT_UNDERSTOOD); 814 replyMsg.AddInt32("error", B_BAD_SCRIPT_SYNTAX); 815 replyMsg.AddString("message", "Didn't understand the specifier(s)"); 816 msg->SendReply(&replyMsg); 817 } 818 return target; 819 } 820 821 822 status_t 823 BShelf::GetSupportedSuites(BMessage *message) 824 { 825 status_t err; 826 err = message->AddString("suites", "suite/vnd.Be-shelf"); 827 if (err == B_OK) { 828 BPropertyInfo propInfo(sShelfPropertyList); 829 err = message->AddFlat("messages", &propInfo); 830 } 831 if (err == B_OK) 832 return BHandler::GetSupportedSuites(message); 833 return err; 834 } 835 836 837 status_t 838 BShelf::Perform(perform_code d, void *arg) 839 { 840 return BHandler::Perform(d, arg); 841 } 842 843 844 bool 845 BShelf::AllowsDragging() const 846 { 847 return fAllowDragging; 848 } 849 850 851 void 852 BShelf::SetAllowsDragging(bool state) 853 { 854 fAllowDragging = state; 855 } 856 857 858 bool 859 BShelf::AllowsZombies() const 860 { 861 return fAllowZombies; 862 } 863 864 865 void 866 BShelf::SetAllowsZombies(bool state) 867 { 868 fAllowZombies = state; 869 } 870 871 872 bool 873 BShelf::DisplaysZombies() const 874 { 875 return fDisplayZombies; 876 } 877 878 879 void 880 BShelf::SetDisplaysZombies(bool state) 881 { 882 fDisplayZombies = state; 883 } 884 885 886 bool 887 BShelf::IsTypeEnforced() const 888 { 889 return fTypeEnforced; 890 } 891 892 893 void 894 BShelf::SetTypeEnforced(bool state) 895 { 896 fTypeEnforced = state; 897 } 898 899 900 status_t 901 BShelf::SetSaveLocation(BDataIO *data_io) 902 { 903 fDirty = true; 904 905 if (fEntry != NULL) { 906 delete fEntry; 907 fEntry = NULL; 908 } 909 910 fStream = data_io; 911 912 return B_OK; 913 } 914 915 916 status_t 917 BShelf::SetSaveLocation(const entry_ref *ref) 918 { 919 fDirty = true; 920 921 if (fEntry) 922 delete fEntry; 923 else 924 fStream = NULL; 925 926 fEntry = new BEntry(ref); 927 928 return B_OK; 929 } 930 931 932 BDataIO * 933 BShelf::SaveLocation(entry_ref *ref) const 934 { 935 if (fStream) { 936 if (ref) 937 *ref = entry_ref(); 938 return fStream; 939 } else if (fEntry && ref) 940 fEntry->GetRef(ref); 941 942 return NULL; 943 } 944 945 946 status_t 947 BShelf::AddReplicant(BMessage *data, BPoint location) 948 { 949 return _AddReplicant(data, &location, fGenCount++); 950 } 951 952 953 status_t 954 BShelf::DeleteReplicant(BView *replicant) 955 { 956 int32 index = replicant_data::IndexOf(&fReplicants, replicant, true); 957 958 replicant_data *item = (replicant_data*)fReplicants.ItemAt(index); 959 if (item == NULL) 960 return B_BAD_VALUE; 961 962 return _DeleteReplicant(item); 963 } 964 965 966 status_t 967 BShelf::DeleteReplicant(BMessage *data) 968 { 969 int32 index = replicant_data::IndexOf(&fReplicants, data); 970 971 replicant_data *item = (replicant_data*)fReplicants.ItemAt(index); 972 if (!item) 973 return B_BAD_VALUE; 974 975 return _DeleteReplicant(item); 976 } 977 978 979 status_t 980 BShelf::DeleteReplicant(int32 index) 981 { 982 replicant_data *item = (replicant_data*)fReplicants.ItemAt(index); 983 if (!item) 984 return B_BAD_INDEX; 985 986 return _DeleteReplicant(item); 987 } 988 989 990 int32 991 BShelf::CountReplicants() const 992 { 993 return fReplicants.CountItems(); 994 } 995 996 997 BMessage * 998 BShelf::ReplicantAt(int32 index, BView **_view, uint32 *_uniqueID, 999 status_t *_error) const 1000 { 1001 replicant_data *item = (replicant_data*)fReplicants.ItemAt(index); 1002 if (item == NULL) { 1003 // no replicant found 1004 if (_view) 1005 *_view = NULL; 1006 if (_uniqueID) 1007 *_uniqueID = ~(uint32)0; 1008 if (_error) 1009 *_error = B_BAD_INDEX; 1010 1011 return NULL; 1012 } 1013 1014 if (_view) 1015 *_view = item->view; 1016 if (_uniqueID) 1017 *_uniqueID = item->id; 1018 if (_error) 1019 *_error = item->error; 1020 1021 return item->message; 1022 } 1023 1024 1025 int32 1026 BShelf::IndexOf(const BView* replicantView) const 1027 { 1028 return replicant_data::IndexOf(&fReplicants, replicantView, false); 1029 } 1030 1031 1032 int32 1033 BShelf::IndexOf(const BMessage *archive) const 1034 { 1035 return replicant_data::IndexOf(&fReplicants, archive); 1036 } 1037 1038 1039 int32 1040 BShelf::IndexOf(uint32 id) const 1041 { 1042 return replicant_data::IndexOf(&fReplicants, id); 1043 } 1044 1045 1046 bool 1047 BShelf::CanAcceptReplicantMessage(BMessage*) const 1048 { 1049 return true; 1050 } 1051 1052 1053 bool 1054 BShelf::CanAcceptReplicantView(BRect, BView*, BMessage*) const 1055 { 1056 return true; 1057 } 1058 1059 1060 BPoint 1061 BShelf::AdjustReplicantBy(BRect, BMessage*) const 1062 { 1063 return B_ORIGIN; 1064 } 1065 1066 1067 void 1068 BShelf::ReplicantDeleted(int32 index, const BMessage *archive, 1069 const BView *replicant) 1070 { 1071 } 1072 1073 1074 extern "C" void 1075 _ReservedShelf1__6BShelfFv(BShelf *const, int32, const BMessage*, const BView*) 1076 { 1077 // is not contained in BeOS R5's libbe, so we leave it empty 1078 } 1079 1080 1081 void BShelf::_ReservedShelf2() {} 1082 void BShelf::_ReservedShelf3() {} 1083 void BShelf::_ReservedShelf4() {} 1084 void BShelf::_ReservedShelf5() {} 1085 void BShelf::_ReservedShelf6() {} 1086 void BShelf::_ReservedShelf7() {} 1087 void BShelf::_ReservedShelf8() {} 1088 1089 1090 BShelf::BShelf(const BShelf&) 1091 { 1092 } 1093 1094 1095 BShelf & 1096 BShelf::operator=(const BShelf &) 1097 { 1098 return *this; 1099 } 1100 1101 1102 status_t 1103 BShelf::_Archive(BMessage *data) const 1104 { 1105 status_t status = BHandler::Archive(data); 1106 if (status != B_OK) 1107 return status; 1108 1109 status = data->AddBool("_zom_dsp", DisplaysZombies()); 1110 if (status != B_OK) 1111 return status; 1112 1113 status = data->AddBool("_zom_alw", AllowsZombies()); 1114 if (status != B_OK) 1115 return status; 1116 1117 status = data->AddInt32("_sg_cnt", fGenCount); 1118 if (status != B_OK) 1119 return status; 1120 1121 BMessage archive('ARCV'); 1122 for (int32 i = 0; i < fReplicants.CountItems(); i++) { 1123 if (((replicant_data *)fReplicants.ItemAt(i))->Archive(&archive) == B_OK) 1124 status = data->AddMessage("replicant", &archive); 1125 if (status != B_OK) 1126 break; 1127 archive.MakeEmpty(); 1128 } 1129 1130 return status; 1131 } 1132 1133 1134 void 1135 BShelf::_InitData(BEntry *entry, BDataIO *stream, BView *view, 1136 bool allowDrags) 1137 { 1138 fContainerView = view; 1139 fStream = NULL; 1140 fEntry = entry; 1141 fFilter = NULL; 1142 fGenCount = 1; 1143 fAllowDragging = allowDrags; 1144 fDirty = true; 1145 fDisplayZombies = false; 1146 fAllowZombies = true; 1147 fTypeEnforced = false; 1148 1149 if (fEntry != NULL) 1150 fStream = new BFile(entry, B_READ_ONLY); 1151 else 1152 fStream = stream; 1153 1154 fFilter = new ShelfContainerViewFilter(this, fContainerView); 1155 1156 fContainerView->AddFilter(fFilter); 1157 fContainerView->_SetShelf(this); 1158 1159 if (fStream != NULL) { 1160 BMessage archive; 1161 1162 if (archive.Unflatten(fStream) == B_OK) { 1163 bool allowZombies; 1164 if (archive.FindBool("_zom_dsp", &allowZombies) != B_OK) 1165 allowZombies = false; 1166 1167 SetDisplaysZombies(allowZombies); 1168 1169 if (archive.FindBool("_zom_alw", &allowZombies) != B_OK) 1170 allowZombies = true; 1171 1172 SetAllowsZombies(allowZombies); 1173 1174 int32 genCount; 1175 if (!archive.FindInt32("_sg_cnt", &genCount)) 1176 genCount = 1; 1177 1178 BMessage replicant; 1179 BMessage *replmsg = NULL; 1180 for (int32 i = 0; archive.FindMessage("replicant", i, &replicant) == B_OK; i++) { 1181 BPoint point; 1182 replmsg = new BMessage(); 1183 replicant.FindPoint("position", &point); 1184 replicant.FindMessage("message", replmsg); 1185 AddReplicant(replmsg, point); 1186 } 1187 } 1188 } 1189 } 1190 1191 1192 status_t 1193 BShelf::_DeleteReplicant(replicant_data* item) 1194 { 1195 BView *view = item->view; 1196 if (view == NULL) 1197 view = item->zombie_view; 1198 1199 if (view != NULL) 1200 view->RemoveSelf(); 1201 1202 if (item->dragger != NULL) 1203 item->dragger->RemoveSelf(); 1204 1205 int32 index = replicant_data::IndexOf(&fReplicants, item->message); 1206 1207 ReplicantDeleted(index, item->message, view); 1208 1209 fReplicants.RemoveItem(item); 1210 1211 if (item->relation == BDragger::TARGET_IS_PARENT 1212 || item->relation == BDragger::TARGET_IS_SIBLING) { 1213 delete view; 1214 } 1215 if (item->relation == BDragger::TARGET_IS_CHILD 1216 || item->relation == BDragger::TARGET_IS_SIBLING) { 1217 delete item->dragger; 1218 } 1219 1220 // Update use count for image and unload if necessary 1221 const char* signature = NULL; 1222 if (item->message->FindString("add_on", &signature) == B_OK 1223 && signature != NULL) { 1224 LoadedImages* loadedImages = LoadedImages::Default(); 1225 AutoLock<LoadedImages> lock(loadedImages); 1226 if (lock.IsLocked()) { 1227 LoadedImageMap::iterator it = loadedImages->images.find( 1228 BString(signature)); 1229 1230 if (it != loadedImages->images.end()) { 1231 (*it).second.second--; 1232 if ((*it).second.second <= 0) { 1233 unload_add_on((*it).second.first); 1234 loadedImages->images.erase(it); 1235 } 1236 } 1237 } 1238 } 1239 1240 delete item; 1241 1242 return B_OK; 1243 } 1244 1245 1246 //! Takes over ownership of \a data on success only 1247 status_t 1248 BShelf::_AddReplicant(BMessage *data, BPoint *location, uint32 uniqueID) 1249 { 1250 // Check shelf types if needed 1251 if (fTypeEnforced) { 1252 const char *shelfType = NULL; 1253 if (data->FindString("shelf_type", &shelfType) == B_OK 1254 && shelfType != NULL) { 1255 if (Name() && strcmp(shelfType, Name()) != 0) { 1256 printf("Replicant was rejected by BShelf: The BShelf's type and the Replicant's type don't match."); 1257 return send_reply(data, B_ERROR, uniqueID); 1258 } else { 1259 printf("Replicant was rejected by BShelf: Replicant indicated a <type> (%s), but the shelf does not.", shelfType); 1260 return send_reply(data, B_ERROR, uniqueID); 1261 } 1262 } else { 1263 printf("Replicant was rejected by BShelf: Replicant did not have a <type>"); 1264 return send_reply(data, B_ERROR, uniqueID); 1265 } 1266 } 1267 1268 // Check if we can accept this message 1269 if (!CanAcceptReplicantMessage(data)) { 1270 printf("Replicant was rejected by BShelf::CanAcceptReplicantMessage()"); 1271 return send_reply(data, B_ERROR, uniqueID); 1272 } 1273 1274 // Check if we can create multiple instances 1275 if (data->FindBool("be:load_each_time")) { 1276 const char *className = NULL; 1277 const char *addOn = NULL; 1278 1279 if (data->FindString("class", &className) == B_OK 1280 && data->FindString("add_on", &addOn) == B_OK) { 1281 if (find_replicant(fReplicants, className, addOn)) { 1282 printf("Replicant was rejected. Unique replicant already exists. class=%s, signature=%s", 1283 className, addOn); 1284 return send_reply(data, B_ERROR, uniqueID); 1285 } 1286 } 1287 } 1288 1289 // Instantiate the object, if this fails we have a zombie 1290 image_id image = -1; 1291 BArchivable *archivable = _InstantiateObject(data, &image); 1292 1293 BView *view = NULL; 1294 1295 if (archivable != NULL) { 1296 view = dynamic_cast<BView*>(archivable); 1297 1298 if (view == NULL) 1299 return send_reply(data, B_ERROR, uniqueID); 1300 } 1301 1302 BDragger* dragger = NULL; 1303 BView* replicant = NULL; 1304 BDragger::relation relation = BDragger::TARGET_UNKNOWN; 1305 _BZombieReplicantView_* zombie = NULL; 1306 if (view != NULL) { 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 } else if (!fAllowZombies) { 1314 // There was no view, and we're not allowed to have any zombies 1315 // in the house 1316 return send_reply(data, B_ERROR, uniqueID); 1317 } 1318 1319 // Update use count for image 1320 const char* signature = NULL; 1321 if (data->FindString("add_on", &signature) == B_OK && signature != NULL) { 1322 LoadedImages* loadedImages = LoadedImages::Default(); 1323 AutoLock<LoadedImages> lock(loadedImages); 1324 if (lock.IsLocked()) { 1325 LoadedImageMap::iterator it = loadedImages->images.find( 1326 BString(signature)); 1327 1328 if (it == loadedImages->images.end()) 1329 loadedImages->images.insert(LoadedImageMap::value_type( 1330 BString(signature), std::pair<image_id, int>(image, 1))); 1331 else 1332 (*it).second.second++; 1333 } 1334 } 1335 1336 if (zombie == NULL) { 1337 data->RemoveName("_drop_point_"); 1338 data->RemoveName("_drop_offset_"); 1339 } 1340 1341 replicant_data *item = new replicant_data(data, replicant, dragger, 1342 relation, uniqueID); 1343 1344 item->error = B_OK; 1345 item->zombie_view = zombie; 1346 1347 fReplicants.AddItem(item); 1348 1349 return send_reply(data, B_OK, uniqueID); 1350 } 1351 1352 1353 BView * 1354 BShelf::_GetReplicant(BMessage *data, BView *view, const BPoint &point, 1355 BDragger *&dragger, BDragger::relation &relation) 1356 { 1357 // TODO: test me -- there seems to be lots of bugs parked here! 1358 BView *replicant = NULL; 1359 _GetReplicantData(data, view, replicant, dragger, relation); 1360 1361 if (dragger != NULL) 1362 dragger->_SetViewToDrag(replicant); 1363 1364 BRect frame = view->Frame().OffsetToCopy(point); 1365 if (!CanAcceptReplicantView(frame, replicant, data)) { 1366 // the view has not been accepted 1367 if (relation == BDragger::TARGET_IS_PARENT 1368 || relation == BDragger::TARGET_IS_SIBLING) { 1369 delete replicant; 1370 } 1371 if (relation == BDragger::TARGET_IS_CHILD 1372 || relation == BDragger::TARGET_IS_SIBLING) { 1373 delete dragger; 1374 } 1375 return NULL; 1376 } 1377 1378 BPoint adjust = AdjustReplicantBy(frame, data); 1379 1380 if (dragger != NULL) 1381 dragger->_SetShelf(this); 1382 1383 // TODO: could be not correct for some relations 1384 view->MoveTo(point + adjust); 1385 1386 // if it's a sibling or a child, we need to add the dragger 1387 if (relation == BDragger::TARGET_IS_SIBLING 1388 || relation == BDragger::TARGET_IS_CHILD) 1389 fContainerView->AddChild(dragger); 1390 1391 if (relation != BDragger::TARGET_IS_CHILD) 1392 fContainerView->AddChild(replicant); 1393 1394 replicant->AddFilter(new ReplicantViewFilter(this, replicant)); 1395 1396 return replicant; 1397 } 1398 1399 1400 /* static */ 1401 void 1402 BShelf::_GetReplicantData(BMessage *data, BView *view, BView *&replicant, 1403 BDragger *&dragger, BDragger::relation &relation) 1404 { 1405 // Check if we have a dragger archived as "__widget" inside the message 1406 BMessage widget; 1407 if (data->FindMessage("__widget", &widget) == B_OK) { 1408 image_id draggerImage = B_ERROR; 1409 replicant = view; 1410 dragger = dynamic_cast<BDragger*>(_InstantiateObject(&widget, &draggerImage)); 1411 // Replicant is a sibling, or unknown, if there isn't a dragger 1412 if (dragger != NULL) 1413 relation = BDragger::TARGET_IS_SIBLING; 1414 1415 } else if ((dragger = dynamic_cast<BDragger*>(view)) != NULL) { 1416 // Replicant is child of the dragger 1417 relation = BDragger::TARGET_IS_CHILD; 1418 replicant = dragger->ChildAt(0); 1419 1420 } else { 1421 // Replicant is parent of the dragger 1422 relation = BDragger::TARGET_IS_PARENT; 1423 replicant = view; 1424 dragger = dynamic_cast<BDragger *>(replicant->FindView("_dragger_")); 1425 // can be NULL, the replicant could not have a dragger at all 1426 } 1427 } 1428 1429 1430 _BZombieReplicantView_ * 1431 BShelf::_CreateZombie(BMessage *data, BDragger *&dragger) 1432 { 1433 // TODO: the zombies must be adjusted and moved as well! 1434 BRect frame; 1435 if (data->FindRect("_frame", &frame) != B_OK) 1436 frame = BRect(); 1437 1438 _BZombieReplicantView_ *zombie = NULL; 1439 if (data->WasDropped()) { 1440 BPoint offset; 1441 BPoint dropPoint = data->DropPoint(&offset); 1442 1443 frame.OffsetTo(fContainerView->ConvertFromScreen(dropPoint) - offset); 1444 1445 zombie = new _BZombieReplicantView_(frame, B_ERROR); 1446 1447 frame.OffsetTo(B_ORIGIN); 1448 1449 dragger = new BDragger(frame, zombie); 1450 dragger->_SetShelf(this); 1451 dragger->_SetZombied(true); 1452 1453 zombie->AddChild(dragger); 1454 zombie->SetArchive(data); 1455 zombie->AddFilter(new ReplicantViewFilter(this, zombie)); 1456 1457 fContainerView->AddChild(zombie); 1458 } 1459 1460 return zombie; 1461 } 1462 1463 1464 status_t 1465 BShelf::_GetProperty(BMessage *msg, BMessage *reply) 1466 { 1467 uint32 ID; 1468 status_t err = B_ERROR; 1469 BView *replicant = NULL; 1470 switch (msg->what) { 1471 case B_INDEX_SPECIFIER: { 1472 int32 index = -1; 1473 if (msg->FindInt32("index", &index)!=B_OK) 1474 break; 1475 ReplicantAt(index, &replicant, &ID, &err); 1476 break; 1477 } 1478 case B_REVERSE_INDEX_SPECIFIER: { 1479 int32 rindex; 1480 if (msg->FindInt32("index", &rindex) != B_OK) 1481 break; 1482 ReplicantAt(CountReplicants() - rindex, &replicant, &ID, &err); 1483 break; 1484 } 1485 case B_NAME_SPECIFIER: { 1486 const char *name; 1487 if (msg->FindString("name", &name) != B_OK) 1488 break; 1489 for (int32 i = 0; i < CountReplicants(); i++) { 1490 BView *view = NULL; 1491 ReplicantAt(i, &view, &ID, &err); 1492 if (err == B_OK) { 1493 if (view->Name() != NULL && 1494 strlen(view->Name()) == strlen(name) && !strcmp(view->Name(), name)) { 1495 replicant = view; 1496 break; 1497 } 1498 err = B_NAME_NOT_FOUND; 1499 } 1500 } 1501 break; 1502 } 1503 case B_ID_SPECIFIER: { 1504 uint32 id; 1505 if (msg->FindInt32("id", (int32 *)&id) != B_OK) 1506 break; 1507 for (int32 i = 0; i < CountReplicants(); i++) { 1508 BView *view = NULL; 1509 ReplicantAt(i, &view, &ID, &err); 1510 if (err == B_OK) { 1511 if (ID == id) { 1512 replicant = view; 1513 break; 1514 } 1515 err = B_NAME_NOT_FOUND; 1516 } 1517 } 1518 break; 1519 } 1520 default: 1521 break; 1522 } 1523 1524 if (replicant) { 1525 reply->AddInt32("index", IndexOf(replicant)); 1526 reply->AddInt32("ID", ID); 1527 } 1528 1529 return err; 1530 } 1531 1532 1533 /* static */ 1534 BArchivable * 1535 BShelf::_InstantiateObject(BMessage *archive, image_id *image) 1536 { 1537 // Stay on the safe side. The constructor called by instantiate_object 1538 // could throw an exception, which we catch here. Otherwise our calling app 1539 // could die without notice. 1540 try { 1541 return instantiate_object(archive, image); 1542 } catch (...) { 1543 return NULL; 1544 } 1545 } 1546 1547