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