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