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