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