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 namespace BPrivate { 103 104 struct replicant_data { 105 replicant_data(BMessage *message, BView *view, BDragger *dragger, 106 BDragger::relation relation, unsigned long id, image_id image); 107 replicant_data(); 108 ~replicant_data(); 109 110 static replicant_data* Find(BList const *list, BMessage const *msg); 111 static replicant_data* Find(BList const *list, BView const *view, bool allowZombie); 112 static replicant_data* Find(BList const *list, unsigned long id); 113 114 static int32 IndexOf(BList const *list, BMessage const *msg); 115 static int32 IndexOf(BList const *list, BView const *view, bool allowZombie); 116 static int32 IndexOf(BList const *list, unsigned long id); 117 118 status_t Archive(BMessage *msg); 119 120 BMessage* message; 121 BView* view; 122 BDragger* dragger; 123 BDragger::relation relation; 124 unsigned long id; 125 image_id image; 126 status_t error; 127 BView* zombie_view; 128 }; 129 130 class ShelfContainerViewFilter : public BMessageFilter { 131 public: 132 ShelfContainerViewFilter(BShelf *shelf, BView *view); 133 134 filter_result Filter(BMessage *msg, BHandler **handler); 135 136 private: 137 filter_result _ObjectDropFilter(BMessage *msg, BHandler **handler); 138 139 BShelf *fShelf; 140 BView *fView; 141 }; 142 143 class ReplicantViewFilter : public BMessageFilter { 144 public: 145 ReplicantViewFilter(BShelf *shelf, BView *view); 146 147 filter_result Filter(BMessage *message, BHandler **handler); 148 149 private: 150 BShelf *fShelf; 151 BView *fView; 152 }; 153 154 } // namespace BPrivate 155 156 157 using BPrivate::replicant_data; 158 using BPrivate::ReplicantViewFilter; 159 using BPrivate::ShelfContainerViewFilter; 160 161 162 // #pragma mark - 163 164 165 /*! \brief Helper function for BShelf::_AddReplicant() 166 */ 167 static status_t 168 send_reply(BMessage* message, status_t status, uint32 uniqueID) 169 { 170 if (message->IsSourceWaiting()) { 171 BMessage reply(B_REPLY); 172 reply.AddInt32("id", uniqueID); 173 reply.AddInt32("error", status); 174 message->SendReply(&reply); 175 } 176 177 return status; 178 } 179 180 181 static bool 182 find_replicant(BList &list, const char *className, const char *addOn) 183 { 184 int32 i = 0; 185 replicant_data *item; 186 while ((item = (replicant_data *)list.ItemAt(i++)) != NULL) { 187 const char *replicantClassName; 188 const char *replicantAddOn; 189 if (item->message->FindString("class", &replicantClassName) == B_OK 190 && item->message->FindString("add_on", &replicantAddOn) == B_OK 191 && !strcmp(className, replicantClassName) 192 && addOn != NULL && replicantAddOn != NULL 193 && !strcmp(addOn, replicantAddOn)) 194 return true; 195 } 196 return false; 197 } 198 199 200 // #pragma mark - 201 202 203 replicant_data::replicant_data(BMessage *_message, BView *_view, BDragger *_dragger, 204 BDragger::relation _relation, unsigned long _id, image_id _image) 205 : 206 message(_message), 207 view(_view), 208 dragger(NULL), 209 relation(_relation), 210 id(_id), 211 image(_image), 212 error(B_OK), 213 zombie_view(NULL) 214 { 215 } 216 217 218 replicant_data::replicant_data() 219 : 220 message(NULL), 221 view(NULL), 222 dragger(NULL), 223 relation(BDragger::TARGET_UNKNOWN), 224 id(0), 225 image(-1), 226 error(B_ERROR), 227 zombie_view(NULL) 228 { 229 } 230 231 replicant_data::~replicant_data() 232 { 233 delete message; 234 // we can't delete the view or image here since 235 // 1 - the view may already have been deleted and thus our pointer is invalid 236 // and 2 - we don't know what order they will be deleted in, so we can't unload the image yet 237 // since the destructor code for the view might still be needed if it has not been destroyed yet. 238 // this does mean we technically leak add-on images on exit, but I don't see a way around that, 239 // and in theory they should be unloaded by team cleanup on exit anyways. 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 delete fEntry; 502 delete fStream; 503 504 while (fReplicants.CountItems() > 0) { 505 replicant_data *data = (replicant_data *)fReplicants.ItemAt(0); 506 fReplicants.RemoveItem(0L); 507 delete data; 508 } 509 } 510 511 512 status_t 513 BShelf::Archive(BMessage *data, bool deep) const 514 { 515 return B_ERROR; 516 } 517 518 519 BArchivable * 520 BShelf::Instantiate(BMessage *data) 521 { 522 return NULL; 523 } 524 525 526 void 527 BShelf::MessageReceived(BMessage *msg) 528 { 529 if (msg->what == kDeleteReplicant) { 530 BHandler *replicant = NULL; 531 if (msg->FindPointer("_target", (void **)&replicant) == B_OK) { 532 BView *view = dynamic_cast<BView *>(replicant); 533 if (view != NULL) 534 DeleteReplicant(view); 535 } 536 return; 537 } 538 539 BMessage replyMsg(B_REPLY); 540 status_t err = B_BAD_SCRIPT_SYNTAX; 541 542 BMessage specifier; 543 int32 what; 544 const char *prop; 545 int32 index; 546 if (msg->GetCurrentSpecifier(&index, &specifier, &what, &prop) != B_OK) 547 return BHandler::MessageReceived(msg); 548 549 switch (msg->what) { 550 case B_DELETE_PROPERTY: 551 case B_GET_PROPERTY: 552 case B_GET_SUPPORTED_SUITES: 553 if (strcmp(prop, "Replicant") == 0) { 554 BMessage reply; 555 int32 i; 556 uint32 ID; 557 BView *replicant = NULL; 558 BMessage *repMessage = NULL; 559 err = _GetProperty(&specifier, &reply); 560 if (err == B_OK) 561 err = reply.FindInt32("index", &i); 562 563 if (err == B_OK && msg->what == B_DELETE_PROPERTY) { // Delete Replicant 564 err = DeleteReplicant(i); 565 break; 566 } 567 if (err == B_OK && msg->what == B_GET_SUPPORTED_SUITES) { 568 err = replyMsg.AddString("suites", "suite/vnd.Be-replicant"); 569 if (err == B_OK) { 570 BPropertyInfo propInfo(sReplicantPropertyList); 571 err = replyMsg.AddFlat("messages", &propInfo); 572 } 573 break; 574 } 575 if (err == B_OK ) 576 repMessage = ReplicantAt(i, &replicant, &ID, &err); 577 if (err == B_OK && replicant) { 578 msg->PopSpecifier(); 579 BMessage archive; 580 err = replicant->Archive(&archive); 581 if (err == B_OK && msg->GetCurrentSpecifier(&index, &specifier, &what, &prop) != B_OK) { 582 err = replyMsg.AddMessage("result", &archive); 583 break; 584 } 585 // now handles the replicant suite 586 err = B_BAD_SCRIPT_SYNTAX; 587 if (msg->what != B_GET_PROPERTY) 588 break; 589 if (strcmp(prop, "ID") == 0) { 590 err = replyMsg.AddInt32("result", ID); 591 } else if (strcmp(prop, "Name") == 0) { 592 err = replyMsg.AddString("result", replicant->Name()); 593 } else if (strcmp(prop, "Signature") == 0) { 594 const char *add_on = NULL; 595 err = repMessage->FindString("add_on", &add_on); 596 if (err == B_OK) 597 err = replyMsg.AddString("result", add_on); 598 } else if (strcmp(prop, "Suites") == 0) { 599 err = replyMsg.AddString("suites", "suite/vnd.Be-replicant"); 600 if (err == B_OK) { 601 BPropertyInfo propInfo(sReplicantPropertyList); 602 err = replyMsg.AddFlat("messages", &propInfo); 603 } 604 } 605 } 606 break; 607 } 608 return BHandler::MessageReceived(msg); 609 610 case B_COUNT_PROPERTIES: 611 if (strcmp(prop, "Replicant") == 0) { 612 err = replyMsg.AddInt32("result", CountReplicants()); 613 break; 614 } 615 return BHandler::MessageReceived(msg); 616 617 case B_CREATE_PROPERTY: 618 { 619 BMessage replicantMsg; 620 BPoint pos; 621 if (msg->FindMessage("data", &replicantMsg) == B_OK 622 && msg->FindPoint("location", &pos) == B_OK) { 623 err = AddReplicant(&replicantMsg, pos); 624 } 625 } 626 break; 627 } 628 629 if (err < B_OK) { 630 replyMsg.what = B_MESSAGE_NOT_UNDERSTOOD; 631 632 if (err == B_BAD_SCRIPT_SYNTAX) 633 replyMsg.AddString("message", "Didn't understand the specifier(s)"); 634 else 635 replyMsg.AddString("message", strerror(err)); 636 } 637 638 replyMsg.AddInt32("error", err); 639 msg->SendReply(&replyMsg); 640 } 641 642 643 status_t 644 BShelf::Save() 645 { 646 status_t status = B_ERROR; 647 if (fEntry != NULL) { 648 BFile *file = new BFile(fEntry, B_READ_WRITE | B_ERASE_FILE); 649 status = file->InitCheck(); 650 if (status < B_OK) { 651 delete file; 652 return status; 653 } 654 fStream = file; 655 } 656 657 if (fStream != NULL) { 658 BMessage message; 659 status = _Archive(&message); 660 if (status == B_OK) 661 status = message.Flatten(fStream); 662 } 663 664 return status; 665 } 666 667 668 void 669 BShelf::SetDirty(bool state) 670 { 671 fDirty = state; 672 } 673 674 675 bool 676 BShelf::IsDirty() const 677 { 678 return fDirty; 679 } 680 681 682 BHandler * 683 BShelf::ResolveSpecifier(BMessage *msg, int32 index, BMessage *specifier, 684 int32 form, const char *property) 685 { 686 BPropertyInfo shelfPropInfo(sShelfPropertyList); 687 BHandler *target = NULL; 688 BView *replicant = NULL; 689 690 switch (shelfPropInfo.FindMatch(msg, 0, specifier, form, property)) { 691 case 0: 692 target = this; 693 break; 694 case 1: 695 if (msg->PopSpecifier() != B_OK) { 696 target = this; 697 break; 698 } 699 msg->SetCurrentSpecifier(index); 700 // fall through 701 case 2: { 702 BMessage reply; 703 status_t err = _GetProperty(specifier, &reply); 704 int32 i; 705 uint32 ID; 706 if (err == B_OK) 707 err = reply.FindInt32("index", &i); 708 if (err == B_OK) 709 ReplicantAt(i, &replicant, &ID, &err); 710 711 if (err == B_OK && replicant != NULL) { 712 if (index == 0) 713 return this; 714 } else { 715 BMessage replyMsg(B_MESSAGE_NOT_UNDERSTOOD); 716 replyMsg.AddInt32("error", B_BAD_INDEX); 717 replyMsg.AddString("message", "Cannot find replicant at/with specified index/name."); 718 msg->SendReply(&replyMsg); 719 } 720 } 721 msg->PopSpecifier(); 722 break; 723 } 724 725 if (!replicant) { 726 if (target) 727 return target; 728 return BHandler::ResolveSpecifier(msg, index, specifier, form, 729 property); 730 } 731 732 int32 repIndex; 733 status_t err = msg->GetCurrentSpecifier(&repIndex, specifier, &form, &property); 734 if (err) { 735 BMessage reply(B_MESSAGE_NOT_UNDERSTOOD); 736 reply.AddInt32("error", err); 737 msg->SendReply(&reply); 738 return NULL; 739 } 740 741 BPropertyInfo replicantPropInfo(sReplicantPropertyList); 742 switch (replicantPropInfo.FindMatch(msg, 0, specifier, form, property)) { 743 case 0: 744 case 1: 745 case 2: 746 case 3: 747 msg->SetCurrentSpecifier(index); 748 target = this; 749 break; 750 case 4: 751 target = replicant; 752 msg->PopSpecifier(); 753 break; 754 default: 755 break; 756 } 757 if (!target) { 758 BMessage replyMsg(B_MESSAGE_NOT_UNDERSTOOD); 759 replyMsg.AddInt32("error", B_BAD_SCRIPT_SYNTAX); 760 replyMsg.AddString("message", "Didn't understand the specifier(s)"); 761 msg->SendReply(&replyMsg); 762 } 763 return target; 764 } 765 766 767 status_t 768 BShelf::GetSupportedSuites(BMessage *message) 769 { 770 status_t err; 771 err = message->AddString("suites", "suite/vnd.Be-shelf"); 772 if (err == B_OK) { 773 BPropertyInfo propInfo(sShelfPropertyList); 774 err = message->AddFlat("messages", &propInfo); 775 } 776 if (err == B_OK) 777 return BHandler::GetSupportedSuites(message); 778 return err; 779 } 780 781 782 status_t 783 BShelf::Perform(perform_code d, void *arg) 784 { 785 return BHandler::Perform(d, arg); 786 } 787 788 789 bool 790 BShelf::AllowsDragging() const 791 { 792 return fAllowDragging; 793 } 794 795 796 void 797 BShelf::SetAllowsDragging(bool state) 798 { 799 fAllowDragging = state; 800 } 801 802 803 bool 804 BShelf::AllowsZombies() const 805 { 806 return fAllowZombies; 807 } 808 809 810 void 811 BShelf::SetAllowsZombies(bool state) 812 { 813 fAllowZombies = state; 814 } 815 816 817 bool 818 BShelf::DisplaysZombies() const 819 { 820 return fDisplayZombies; 821 } 822 823 824 void 825 BShelf::SetDisplaysZombies(bool state) 826 { 827 fDisplayZombies = state; 828 } 829 830 831 bool 832 BShelf::IsTypeEnforced() const 833 { 834 return fTypeEnforced; 835 } 836 837 838 void 839 BShelf::SetTypeEnforced(bool state) 840 { 841 fTypeEnforced = state; 842 } 843 844 845 status_t 846 BShelf::SetSaveLocation(BDataIO *data_io) 847 { 848 fDirty = true; 849 850 if (fEntry) { 851 delete fEntry; 852 fEntry = NULL; 853 } 854 855 fStream = data_io; 856 857 return B_OK; 858 } 859 860 861 status_t 862 BShelf::SetSaveLocation(const entry_ref *ref) 863 { 864 fDirty = true; 865 866 if (fEntry) 867 delete fEntry; 868 else 869 fStream = NULL; 870 871 fEntry = new BEntry(ref); 872 873 return B_OK; 874 } 875 876 877 BDataIO * 878 BShelf::SaveLocation(entry_ref *ref) const 879 { 880 entry_ref entry; 881 882 if (fStream && ref) { 883 *ref = entry; 884 return fStream; 885 } 886 if (fEntry) { 887 fEntry->GetRef(&entry); 888 889 if (ref) 890 *ref = entry; 891 892 return NULL; 893 } 894 895 *ref = entry; 896 return NULL; 897 } 898 899 900 status_t 901 BShelf::AddReplicant(BMessage *data, BPoint location) 902 { 903 return _AddReplicant(data, &location, fGenCount++); 904 } 905 906 907 status_t 908 BShelf::DeleteReplicant(BView *replicant) 909 { 910 int32 index = replicant_data::IndexOf(&fReplicants, replicant, true); 911 912 replicant_data *item = (replicant_data*)fReplicants.ItemAt(index); 913 if (item == NULL) 914 return B_BAD_VALUE; 915 916 return _DeleteReplicant(item); 917 } 918 919 920 status_t 921 BShelf::DeleteReplicant(BMessage *data) 922 { 923 int32 index = replicant_data::IndexOf(&fReplicants, data); 924 925 replicant_data *item = (replicant_data*)fReplicants.ItemAt(index); 926 if (!item) 927 return B_BAD_VALUE; 928 929 return _DeleteReplicant(item); 930 } 931 932 933 status_t 934 BShelf::DeleteReplicant(int32 index) 935 { 936 replicant_data *item = (replicant_data*)fReplicants.ItemAt(index); 937 if (!item) 938 return B_BAD_INDEX; 939 940 return _DeleteReplicant(item); 941 } 942 943 944 int32 945 BShelf::CountReplicants() const 946 { 947 return fReplicants.CountItems(); 948 } 949 950 951 BMessage * 952 BShelf::ReplicantAt(int32 index, BView **_view, uint32 *_uniqueID, 953 status_t *_error) const 954 { 955 replicant_data *item = (replicant_data*)fReplicants.ItemAt(index); 956 if (item == NULL) { 957 // no replicant found 958 if (_view) 959 *_view = NULL; 960 if (_uniqueID) 961 *_uniqueID = ~0UL; 962 if (_error) 963 *_error = B_BAD_INDEX; 964 965 return NULL; 966 } 967 968 if (_view) 969 *_view = item->view; 970 if (_uniqueID) 971 *_uniqueID = item->id; 972 if (_error) 973 *_error = item->error; 974 975 return item->message; 976 } 977 978 979 int32 980 BShelf::IndexOf(const BView* replicantView) const 981 { 982 return replicant_data::IndexOf(&fReplicants, replicantView, false); 983 } 984 985 986 int32 987 BShelf::IndexOf(const BMessage *archive) const 988 { 989 return replicant_data::IndexOf(&fReplicants, archive); 990 } 991 992 993 int32 994 BShelf::IndexOf(uint32 id) const 995 { 996 return replicant_data::IndexOf(&fReplicants, id); 997 } 998 999 1000 bool 1001 BShelf::CanAcceptReplicantMessage(BMessage*) const 1002 { 1003 return true; 1004 } 1005 1006 1007 bool 1008 BShelf::CanAcceptReplicantView(BRect, BView*, BMessage*) const 1009 { 1010 return true; 1011 } 1012 1013 1014 BPoint 1015 BShelf::AdjustReplicantBy(BRect, BMessage*) const 1016 { 1017 return B_ORIGIN; 1018 } 1019 1020 1021 void 1022 BShelf::ReplicantDeleted(int32 index, const BMessage *archive, 1023 const BView *replicant) 1024 { 1025 } 1026 1027 1028 extern "C" void 1029 _ReservedShelf1__6BShelfFv(BShelf *const, int32, const BMessage*, const BView*) 1030 { 1031 // is not contained in BeOS R5's libbe, so we leave it empty 1032 } 1033 1034 1035 void BShelf::_ReservedShelf2() {} 1036 void BShelf::_ReservedShelf3() {} 1037 void BShelf::_ReservedShelf4() {} 1038 void BShelf::_ReservedShelf5() {} 1039 void BShelf::_ReservedShelf6() {} 1040 void BShelf::_ReservedShelf7() {} 1041 void BShelf::_ReservedShelf8() {} 1042 1043 1044 BShelf::BShelf(const BShelf&) 1045 { 1046 } 1047 1048 1049 BShelf & 1050 BShelf::operator=(const BShelf &) 1051 { 1052 return *this; 1053 } 1054 1055 1056 status_t 1057 BShelf::_Archive(BMessage *data) const 1058 { 1059 BHandler::Archive(data); 1060 1061 data->AddBool("_zom_dsp", DisplaysZombies()); 1062 data->AddBool("_zom_alw", AllowsZombies()); 1063 data->AddInt32("_sg_cnt", fGenCount); 1064 1065 BMessage archive('ARCV'); 1066 1067 for (int32 i = 0; i < fReplicants.CountItems(); i++) { 1068 if (((replicant_data *)fReplicants.ItemAt(i))->Archive(&archive) == B_OK) 1069 data->AddMessage("replicant", &archive); 1070 archive.MakeEmpty(); 1071 } 1072 1073 return B_OK; 1074 } 1075 1076 1077 void 1078 BShelf::_InitData(BEntry *entry, BDataIO *stream, BView *view, 1079 bool allowDrags) 1080 { 1081 fContainerView = view; 1082 fStream = NULL; 1083 fEntry = entry; 1084 fFilter = NULL; 1085 fGenCount = 1; 1086 fAllowDragging = allowDrags; 1087 fDirty = true; 1088 fDisplayZombies = false; 1089 fAllowZombies = true; 1090 fTypeEnforced = false; 1091 1092 if (entry) 1093 fStream = new BFile(entry, B_READ_ONLY); 1094 else 1095 fStream = stream; 1096 1097 fFilter = new ShelfContainerViewFilter(this, fContainerView); 1098 1099 fContainerView->AddFilter(fFilter); 1100 fContainerView->_SetShelf(this); 1101 1102 if (fStream) { 1103 BMessage archive; 1104 1105 if (archive.Unflatten(fStream) == B_OK) { 1106 bool allowZombies; 1107 if (archive.FindBool("_zom_dsp", &allowZombies) != B_OK) 1108 allowZombies = false; 1109 1110 SetDisplaysZombies(allowZombies); 1111 1112 if (archive.FindBool("_zom_alw", &allowZombies) != B_OK) 1113 allowZombies = true; 1114 1115 SetAllowsZombies(allowZombies); 1116 1117 int32 genCount; 1118 if (!archive.FindInt32("_sg_cnt", &genCount)) 1119 genCount = 1; 1120 1121 BMessage replicant; 1122 BMessage *replmsg = NULL; 1123 for (int32 i = 0; archive.FindMessage("replicant", i, &replicant) == B_OK; i++) { 1124 BPoint point; 1125 replmsg = new BMessage(); 1126 replicant.FindPoint("position", &point); 1127 replicant.FindMessage("message", replmsg); 1128 AddReplicant(replmsg, point); 1129 } 1130 } 1131 } 1132 } 1133 1134 1135 status_t 1136 BShelf::_DeleteReplicant(replicant_data* item) 1137 { 1138 bool loadedImage = item->message->FindBool("_loaded_image_"); 1139 1140 BView *view = item->view; 1141 if (view == NULL) 1142 view = item->zombie_view; 1143 1144 if (view) 1145 view->RemoveSelf(); 1146 1147 if (item->dragger) 1148 item->dragger->RemoveSelf(); 1149 1150 int32 index = replicant_data::IndexOf(&fReplicants, item->message); 1151 1152 ReplicantDeleted(index, item->message, view); 1153 1154 fReplicants.RemoveItem(item); 1155 1156 if (item->relation == BDragger::TARGET_IS_PARENT 1157 || item->relation == BDragger::TARGET_IS_SIBLING) { 1158 delete view; 1159 } 1160 if (item->relation == BDragger::TARGET_IS_CHILD 1161 || item->relation == BDragger::TARGET_IS_SIBLING) { 1162 delete item->dragger; 1163 } 1164 1165 image_id image = item->image; 1166 1167 delete item; 1168 1169 if (loadedImage && image >= 0) 1170 unload_add_on(image); 1171 1172 1173 return B_OK; 1174 } 1175 1176 1177 //! Takes over ownership of \a data on success only 1178 status_t 1179 BShelf::_AddReplicant(BMessage *data, BPoint *location, uint32 uniqueID) 1180 { 1181 // Check shelf types if needed 1182 if (fTypeEnforced) { 1183 const char *shelfType = NULL; 1184 if (data->FindString("shelf_type", &shelfType) == B_OK 1185 && shelfType != NULL) { 1186 if (Name() && strcmp(shelfType, Name()) != 0) { 1187 printf("Replicant was rejected by BShelf: The BShelf's type and the Replicant's type don't match."); 1188 return send_reply(data, B_ERROR, uniqueID); 1189 } else { 1190 printf("Replicant was rejected by BShelf: Replicant indicated a <type> (%s), but the shelf does not.", shelfType); 1191 return send_reply(data, B_ERROR, uniqueID); 1192 } 1193 } else { 1194 printf("Replicant was rejected by BShelf: Replicant did not have a <type>"); 1195 return send_reply(data, B_ERROR, uniqueID); 1196 } 1197 } 1198 1199 // Check if we can accept this message 1200 if (!CanAcceptReplicantMessage(data)) { 1201 printf("Replicant was rejected by BShelf::CanAcceptReplicantMessage()"); 1202 return send_reply(data, B_ERROR, uniqueID); 1203 } 1204 1205 // Check if we can create multiple instances 1206 if (data->FindBool("be:load_each_time")) { 1207 const char *className = NULL; 1208 const char *addOn = NULL; 1209 1210 if (data->FindString("class", &className) == B_OK 1211 && data->FindString("add_on", &addOn) == B_OK) { 1212 if (find_replicant(fReplicants, className, addOn)) { 1213 printf("Replicant was rejected. Unique replicant already exists. class=%s, signature=%s", 1214 className, addOn); 1215 return send_reply(data, B_ERROR, uniqueID); 1216 } 1217 } 1218 } 1219 1220 // Instantiate the object, if this fails we have a zombie 1221 image_id image; 1222 BArchivable *archivable = _InstantiateObject(data, &image); 1223 BView *view = dynamic_cast<BView*>(archivable); 1224 if (archivable != NULL && view == NULL) { 1225 printf("Replicant was rejected: it's not a view!"); 1226 return send_reply(data, B_ERROR, uniqueID); 1227 } 1228 1229 BDragger* dragger = NULL; 1230 BView* replicant = NULL; 1231 BDragger::relation relation = BDragger::TARGET_UNKNOWN; 1232 _BZombieReplicantView_* zombie = NULL; 1233 if (view != NULL) { 1234 const BPoint point = location ? *location : view->Frame().LeftTop(); 1235 replicant = _GetReplicant(data, view, point, dragger, relation); 1236 if (replicant == NULL) 1237 return send_reply(data, B_ERROR, uniqueID); 1238 } else if (fDisplayZombies && fAllowZombies) 1239 zombie = _CreateZombie(data, dragger); 1240 1241 else if (!fAllowZombies) { 1242 // There was no view, and we're not allowed to have any zombies 1243 // in the house 1244 return send_reply(data, B_ERROR, uniqueID); 1245 } 1246 1247 data->RemoveName("_drop_point_"); 1248 data->RemoveName("_drop_offset_"); 1249 1250 if (image >= 0) 1251 data->AddBool("_loaded_image_", true); 1252 1253 replicant_data *item = new replicant_data(data, replicant, dragger, 1254 relation, uniqueID, image); 1255 1256 item->error = B_OK; 1257 item->zombie_view = zombie; 1258 1259 fReplicants.AddItem(item); 1260 1261 return send_reply(data, B_OK, uniqueID); 1262 } 1263 1264 1265 BView * 1266 BShelf::_GetReplicant(BMessage *data, BView *view, const BPoint &point, 1267 BDragger *&dragger, BDragger::relation &relation) 1268 { 1269 // TODO: test me -- there seems to be lots of bugs parked here! 1270 BView *replicant = NULL; 1271 _GetReplicantData(data, view, replicant, dragger, relation); 1272 1273 if (dragger != NULL) 1274 dragger->_SetViewToDrag(replicant); 1275 1276 BRect frame = view->Frame().OffsetToCopy(point); 1277 if (!CanAcceptReplicantView(frame, replicant, data)) { 1278 // the view has not been accepted 1279 if (relation == BDragger::TARGET_IS_PARENT 1280 || relation == BDragger::TARGET_IS_SIBLING) { 1281 delete replicant; 1282 } 1283 if (relation == BDragger::TARGET_IS_CHILD 1284 || relation == BDragger::TARGET_IS_SIBLING) { 1285 delete dragger; 1286 } 1287 return NULL; 1288 } 1289 1290 BPoint adjust = AdjustReplicantBy(frame, data); 1291 1292 if (dragger != NULL) 1293 dragger->_SetShelf(this); 1294 1295 // TODO: could be not correct for some relations 1296 view->MoveTo(point + adjust); 1297 1298 // if it's a sibling or a child, we need to add the dragger 1299 if (relation == BDragger::TARGET_IS_SIBLING 1300 || relation == BDragger::TARGET_IS_CHILD) 1301 fContainerView->AddChild(dragger); 1302 1303 if (relation != BDragger::TARGET_IS_CHILD) 1304 fContainerView->AddChild(replicant); 1305 1306 replicant->AddFilter(new ReplicantViewFilter(this, replicant)); 1307 1308 return replicant; 1309 } 1310 1311 1312 /* static */ 1313 void 1314 BShelf::_GetReplicantData(BMessage *data, BView *view, BView *&replicant, 1315 BDragger *&dragger, BDragger::relation &relation) 1316 { 1317 // Check if we have a dragger archived as "__widget" inside the message 1318 BMessage widget; 1319 if (data->FindMessage("__widget", &widget) == B_OK) { 1320 image_id draggerImage = B_ERROR; 1321 replicant = view; 1322 dragger = dynamic_cast<BDragger*>(_InstantiateObject(&widget, &draggerImage)); 1323 // Replicant is a sibling, or unknown, if there isn't a dragger 1324 if (dragger != NULL) 1325 relation = BDragger::TARGET_IS_SIBLING; 1326 1327 } else if ((dragger = dynamic_cast<BDragger*>(view)) != NULL) { 1328 // Replicant is child of the dragger 1329 relation = BDragger::TARGET_IS_CHILD; 1330 replicant = dragger->ChildAt(0); 1331 1332 } else { 1333 // Replicant is parent of the dragger 1334 relation = BDragger::TARGET_IS_PARENT; 1335 replicant = view; 1336 dragger = dynamic_cast<BDragger *>(replicant->FindView("_dragger_")); 1337 // can be NULL, the replicant could not have a dragger at all 1338 } 1339 } 1340 1341 1342 _BZombieReplicantView_ * 1343 BShelf::_CreateZombie(BMessage *data, BDragger *&dragger) 1344 { 1345 // TODO: the zombies must be adjusted and moved as well! 1346 BRect frame; 1347 if (data->FindRect("_frame", &frame) != B_OK) 1348 frame = BRect(); 1349 1350 _BZombieReplicantView_ *zombie = NULL; 1351 if (data->WasDropped()) { 1352 BPoint offset; 1353 BPoint dropPoint = data->DropPoint(&offset); 1354 1355 frame.OffsetTo(fContainerView->ConvertFromScreen(dropPoint) - offset); 1356 1357 zombie = new _BZombieReplicantView_(frame, B_ERROR); 1358 1359 frame.OffsetTo(B_ORIGIN); 1360 1361 dragger = new BDragger(frame, zombie); 1362 dragger->_SetShelf(this); 1363 dragger->_SetZombied(true); 1364 1365 zombie->AddChild(dragger); 1366 zombie->SetArchive(data); 1367 zombie->AddFilter(new ReplicantViewFilter(this, zombie)); 1368 1369 fContainerView->AddChild(zombie); 1370 } 1371 1372 return zombie; 1373 } 1374 1375 1376 status_t 1377 BShelf::_GetProperty(BMessage *msg, BMessage *reply) 1378 { 1379 uint32 ID; 1380 status_t err = B_ERROR; 1381 BView *replicant = NULL; 1382 switch (msg->what) { 1383 case B_INDEX_SPECIFIER: { 1384 int32 index = -1; 1385 if (msg->FindInt32("index", &index)!=B_OK) 1386 break; 1387 ReplicantAt(index, &replicant, &ID, &err); 1388 break; 1389 } 1390 case B_REVERSE_INDEX_SPECIFIER: { 1391 int32 rindex; 1392 if (msg->FindInt32("index", &rindex) != B_OK) 1393 break; 1394 ReplicantAt(CountReplicants() - rindex, &replicant, &ID, &err); 1395 break; 1396 } 1397 case B_NAME_SPECIFIER: { 1398 const char *name; 1399 if (msg->FindString("name", &name) != B_OK) 1400 break; 1401 for (int32 i=0; i<CountReplicants(); i++) { 1402 BView *view = NULL; 1403 ReplicantAt(i, &view, &ID, &err); 1404 if (err == B_OK) { 1405 if (view->Name() != NULL && 1406 strlen(view->Name()) == strlen(name) && !strcmp(view->Name(), name)) { 1407 replicant = view; 1408 break; 1409 } 1410 err = B_NAME_NOT_FOUND; 1411 } 1412 } 1413 break; 1414 } 1415 case B_ID_SPECIFIER: { 1416 uint32 id; 1417 if (msg->FindInt32("id", (int32 *)&id) != B_OK) 1418 break; 1419 for (int32 i=0; i<CountReplicants(); i++) { 1420 BView *view = NULL; 1421 ReplicantAt(i, &view, &ID, &err); 1422 if (err == B_OK) { 1423 if (ID == id) { 1424 replicant = view; 1425 break; 1426 } 1427 err = B_NAME_NOT_FOUND; 1428 } 1429 } 1430 break; 1431 } 1432 default: 1433 break; 1434 } 1435 1436 if (replicant) { 1437 reply->AddInt32("index", IndexOf(replicant)); 1438 reply->AddInt32("ID", ID); 1439 } 1440 1441 return err; 1442 } 1443 1444 1445 /* static */ 1446 BArchivable * 1447 BShelf::_InstantiateObject(BMessage *archive, image_id *image) 1448 { 1449 // Stay on the safe side. The constructor called by instantiate_object 1450 // could throw an exception, which we catch here. Otherwise our calling app 1451 // could die without notice. 1452 try { 1453 return instantiate_object(archive, image); 1454 } catch (...) { 1455 return NULL; 1456 } 1457 } 1458 1459