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