vfs: check userland buffers before reading them.
[haiku.git] / src / kits / interface / Shelf.cpp
blob070c0566f8dcdcd7e4820c70728e8ea87616ac93
1 /*
2 * Copyright 2001-2010, Haiku, Inc.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * Marc Flerackers (mflerackers@androme.be)
7 * Axel Dörfler, axeld@pinc-software.de
8 * Jérôme Duval
9 * René Gollent
10 * Alexandre Deckner, alex@zappotek.com
13 /*! BShelf stores replicant views that are dropped onto it */
15 #include <Shelf.h>
17 #include <pthread.h>
19 #include <AutoDeleter.h>
20 #include <AutoLock.h>
21 #include <Beep.h>
22 #include <Dragger.h>
23 #include <Entry.h>
24 #include <File.h>
25 #include <Looper.h>
26 #include <Message.h>
27 #include <MessageFilter.h>
28 #include <Messenger.h>
29 #include <Point.h>
30 #include <PropertyInfo.h>
31 #include <Rect.h>
32 #include <String.h>
33 #include <View.h>
35 #include <ViewPrivate.h>
37 #include "ZombieReplicantView.h"
39 #include <stdio.h>
40 #include <string.h>
42 #include <map>
43 #include <utility>
46 namespace {
48 typedef std::map<BString, std::pair<image_id, int32> > LoadedImageMap;
50 struct LoadedImages {
51 LoadedImageMap images;
53 LoadedImages()
55 fLock("BShelf loaded image map")
59 bool Lock()
61 return fLock.Lock();
64 void Unlock()
66 fLock.Unlock();
69 static LoadedImages* Default()
71 if (sDefaultInstance == NULL)
72 pthread_once(&sDefaultInitOnce, &_InitSingleton);
74 return sDefaultInstance;
77 private:
78 static void _InitSingleton()
80 sDefaultInstance = new LoadedImages;
83 private:
84 BLocker fLock;
86 static pthread_once_t sDefaultInitOnce;
87 static LoadedImages* sDefaultInstance;
90 pthread_once_t LoadedImages::sDefaultInitOnce = PTHREAD_ONCE_INIT;
91 LoadedImages* LoadedImages::sDefaultInstance = NULL;
93 } // unnamed namespace
96 static property_info sShelfPropertyList[] = {
98 "Replicant",
99 { B_COUNT_PROPERTIES, B_CREATE_PROPERTY },
100 { B_DIRECT_SPECIFIER },
101 NULL, 0,
105 "Replicant",
106 { B_DELETE_PROPERTY, B_GET_PROPERTY },
107 { B_INDEX_SPECIFIER, B_REVERSE_INDEX_SPECIFIER, B_NAME_SPECIFIER, B_ID_SPECIFIER },
108 NULL, 0,
112 "Replicant",
114 { B_INDEX_SPECIFIER, B_REVERSE_INDEX_SPECIFIER, B_NAME_SPECIFIER, B_ID_SPECIFIER },
115 "... of Replicant {index | name | id} of ...", 0,
118 { 0 }
121 static property_info sReplicantPropertyList[] = {
123 "ID",
124 { B_GET_PROPERTY },
125 { B_DIRECT_SPECIFIER },
126 NULL, 0, { B_INT32_TYPE }
130 "Name",
131 { B_GET_PROPERTY },
132 { B_DIRECT_SPECIFIER },
133 NULL, 0, { B_STRING_TYPE }
137 "Signature",
138 { B_GET_PROPERTY },
139 { B_DIRECT_SPECIFIER },
140 NULL, 0, { B_STRING_TYPE }
144 "Suites",
145 { B_GET_PROPERTY },
146 { B_DIRECT_SPECIFIER },
147 NULL, 0, { B_PROPERTY_INFO_TYPE }
151 "View",
152 { },
153 { B_DIRECT_SPECIFIER },
154 NULL, 0,
157 { 0 }
161 namespace BPrivate {
163 struct replicant_data {
164 replicant_data(BMessage *message, BView *view, BDragger *dragger,
165 BDragger::relation relation, unsigned long id);
166 replicant_data();
167 ~replicant_data();
169 static replicant_data* Find(BList const *list, BMessage const *msg);
170 static replicant_data* Find(BList const *list, BView const *view, bool allowZombie);
171 static replicant_data* Find(BList const *list, unsigned long id);
173 static int32 IndexOf(BList const *list, BMessage const *msg);
174 static int32 IndexOf(BList const *list, BView const *view, bool allowZombie);
175 static int32 IndexOf(BList const *list, unsigned long id);
177 status_t Archive(BMessage *msg);
179 BMessage* message;
180 BView* view;
181 BDragger* dragger;
182 BDragger::relation relation;
183 unsigned long id;
184 status_t error;
185 BView* zombie_view;
188 class ShelfContainerViewFilter : public BMessageFilter {
189 public:
190 ShelfContainerViewFilter(BShelf *shelf, BView *view);
192 filter_result Filter(BMessage *msg, BHandler **handler);
194 private:
195 filter_result _ObjectDropFilter(BMessage *msg, BHandler **handler);
197 BShelf *fShelf;
198 BView *fView;
201 class ReplicantViewFilter : public BMessageFilter {
202 public:
203 ReplicantViewFilter(BShelf *shelf, BView *view);
205 filter_result Filter(BMessage *message, BHandler **handler);
207 private:
208 BShelf *fShelf;
209 BView *fView;
212 } // namespace BPrivate
215 using BPrivate::replicant_data;
216 using BPrivate::ReplicantViewFilter;
217 using BPrivate::ShelfContainerViewFilter;
220 // #pragma mark -
223 /*! \brief Helper function for BShelf::_AddReplicant()
225 static status_t
226 send_reply(BMessage* message, status_t status, uint32 uniqueID)
228 if (message->IsSourceWaiting()) {
229 BMessage reply(B_REPLY);
230 reply.AddInt32("id", uniqueID);
231 reply.AddInt32("error", status);
232 message->SendReply(&reply);
235 return status;
239 static bool
240 find_replicant(BList &list, const char *className, const char *addOn)
242 int32 i = 0;
243 replicant_data *item;
244 while ((item = (replicant_data *)list.ItemAt(i++)) != NULL) {
245 const char *replicantClassName;
246 const char *replicantAddOn;
247 if (item->message->FindString("class", &replicantClassName) == B_OK
248 && item->message->FindString("add_on", &replicantAddOn) == B_OK
249 && !strcmp(className, replicantClassName)
250 && addOn != NULL && replicantAddOn != NULL
251 && !strcmp(addOn, replicantAddOn))
252 return true;
254 return false;
258 // #pragma mark -
261 replicant_data::replicant_data(BMessage *_message, BView *_view, BDragger *_dragger,
262 BDragger::relation _relation, unsigned long _id)
264 message(_message),
265 view(_view),
266 dragger(NULL),
267 relation(_relation),
268 id(_id),
269 error(B_OK),
270 zombie_view(NULL)
275 replicant_data::replicant_data()
277 message(NULL),
278 view(NULL),
279 dragger(NULL),
280 relation(BDragger::TARGET_UNKNOWN),
281 id(0),
282 error(B_ERROR),
283 zombie_view(NULL)
287 replicant_data::~replicant_data()
289 delete message;
292 status_t
293 replicant_data::Archive(BMessage* msg)
295 status_t result = B_OK;
296 BMessage archive;
297 if (view)
298 result = view->Archive(&archive);
299 else if (zombie_view)
300 result = zombie_view->Archive(&archive);
302 if (result != B_OK)
303 return result;
305 msg->AddInt32("uniqueid", id);
306 BPoint pos (0,0);
307 msg->AddMessage("message", &archive);
308 if (view)
309 pos = view->Frame().LeftTop();
310 else if (zombie_view)
311 pos = zombie_view->Frame().LeftTop();
312 msg->AddPoint("position", pos);
314 return result;
317 //static
318 replicant_data *
319 replicant_data::Find(BList const *list, BMessage const *msg)
321 int32 i = 0;
322 replicant_data *item;
323 while ((item = (replicant_data*)list->ItemAt(i++)) != NULL) {
324 if (item->message == msg)
325 return item;
328 return NULL;
332 //static
333 replicant_data *
334 replicant_data::Find(BList const *list, BView const *view, bool allowZombie)
336 int32 i = 0;
337 replicant_data *item;
338 while ((item = (replicant_data*)list->ItemAt(i++)) != NULL) {
339 if (item->view == view)
340 return item;
342 if (allowZombie && item->zombie_view == view)
343 return item;
346 return NULL;
350 //static
351 replicant_data *
352 replicant_data::Find(BList const *list, unsigned long id)
354 int32 i = 0;
355 replicant_data *item;
356 while ((item = (replicant_data*)list->ItemAt(i++)) != NULL) {
357 if (item->id == id)
358 return item;
361 return NULL;
365 //static
366 int32
367 replicant_data::IndexOf(BList const *list, BMessage const *msg)
369 int32 i = 0;
370 replicant_data *item;
371 while ((item = (replicant_data*)list->ItemAt(i)) != NULL) {
372 if (item->message == msg)
373 return i;
374 i++;
377 return -1;
381 //static
382 int32
383 replicant_data::IndexOf(BList const *list, BView const *view, bool allowZombie)
385 int32 i = 0;
386 replicant_data *item;
387 while ((item = (replicant_data*)list->ItemAt(i)) != NULL) {
388 if (item->view == view)
389 return i;
391 if (allowZombie && item->zombie_view == view)
392 return i;
393 i++;
396 return -1;
400 //static
401 int32
402 replicant_data::IndexOf(BList const *list, unsigned long id)
404 int32 i = 0;
405 replicant_data *item;
406 while ((item = (replicant_data*)list->ItemAt(i)) != NULL) {
407 if (item->id == id)
408 return i;
409 i++;
412 return -1;
416 // #pragma mark -
419 ShelfContainerViewFilter::ShelfContainerViewFilter(BShelf *shelf, BView *view)
420 : BMessageFilter(B_ANY_DELIVERY, B_ANY_SOURCE),
421 fShelf(shelf),
422 fView(view)
427 filter_result
428 ShelfContainerViewFilter::Filter(BMessage *msg, BHandler **handler)
430 filter_result filter = B_DISPATCH_MESSAGE;
432 if (msg->what == B_ARCHIVED_OBJECT
433 || msg->what == B_ABOUT_REQUESTED)
434 return _ObjectDropFilter(msg, handler);
436 return filter;
440 filter_result
441 ShelfContainerViewFilter::_ObjectDropFilter(BMessage *msg, BHandler **_handler)
443 BView *mouseView = NULL;
444 if (*_handler)
445 mouseView = dynamic_cast<BView*>(*_handler);
447 if (msg->WasDropped()) {
448 if (!fShelf->fAllowDragging)
449 return B_SKIP_MESSAGE;
452 BPoint point;
453 BPoint offset;
455 if (msg->WasDropped()) {
456 point = msg->DropPoint(&offset);
457 point = mouseView->ConvertFromScreen(point - offset);
460 BLooper *looper = NULL;
461 BHandler *handler = msg->ReturnAddress().Target(&looper);
463 if (Looper() == looper) {
464 BDragger *dragger = NULL;
465 if (handler)
466 dragger = dynamic_cast<BDragger*>(handler);
468 BRect rect;
469 if (dragger->fRelation == BDragger::TARGET_IS_CHILD)
470 rect = dragger->Frame();
471 else
472 rect = dragger->fTarget->Frame();
473 rect.OffsetTo(point);
474 point = rect.LeftTop() + fShelf->AdjustReplicantBy(rect, msg);
476 if (dragger->fRelation == BDragger::TARGET_IS_PARENT)
477 dragger->fTarget->MoveTo(point);
478 else if (dragger->fRelation == BDragger::TARGET_IS_CHILD)
479 dragger->MoveTo(point);
480 else {
481 //TODO: TARGET_UNKNOWN/TARGET_SIBLING
484 } else {
485 if (fShelf->_AddReplicant(msg, &point, fShelf->fGenCount++) == B_OK)
486 Looper()->DetachCurrentMessage();
489 return B_SKIP_MESSAGE;
493 // #pragma mark -
496 ReplicantViewFilter::ReplicantViewFilter(BShelf *shelf, BView *view)
497 : BMessageFilter(B_ANY_DELIVERY, B_ANY_SOURCE),
498 fShelf(shelf),
499 fView(view)
504 filter_result
505 ReplicantViewFilter::Filter(BMessage *message, BHandler **handler)
507 if (message->what == kDeleteReplicant) {
508 if (handler != NULL)
509 *handler = fShelf;
510 message->AddPointer("_target", fView);
512 return B_DISPATCH_MESSAGE;
516 // #pragma mark -
519 BShelf::BShelf(BView *view, bool allowDrags, const char *shelfType)
520 : BHandler(shelfType)
522 _InitData(NULL, NULL, view, allowDrags);
526 BShelf::BShelf(const entry_ref *ref, BView *view, bool allowDrags,
527 const char *shelfType)
528 : BHandler(shelfType)
530 _InitData(new BEntry(ref), NULL, view, allowDrags);
534 BShelf::BShelf(BDataIO *stream, BView *view, bool allowDrags,
535 const char *shelfType)
536 : BHandler(shelfType)
538 _InitData(NULL, stream, view, allowDrags);
542 BShelf::BShelf(BMessage *data)
543 : BHandler(data)
545 // TODO: Implement ?
549 BShelf::~BShelf()
551 Save();
553 // we own fStream only when fEntry is set
554 if (fEntry != NULL) {
555 delete fEntry;
556 delete fStream;
559 while (fReplicants.CountItems() > 0) {
560 replicant_data *data = (replicant_data *)fReplicants.ItemAt(0);
561 fReplicants.RemoveItem((int32)0);
562 delete data;
565 fContainerView->_SetShelf(NULL);
569 status_t
570 BShelf::Archive(BMessage *data, bool deep) const
572 return B_ERROR;
576 BArchivable *
577 BShelf::Instantiate(BMessage *data)
579 return NULL;
583 void
584 BShelf::MessageReceived(BMessage *msg)
586 if (msg->what == kDeleteReplicant) {
587 BHandler *replicant = NULL;
588 if (msg->FindPointer("_target", (void **)&replicant) == B_OK) {
589 BView *view = dynamic_cast<BView *>(replicant);
590 if (view != NULL)
591 DeleteReplicant(view);
593 return;
596 BMessage replyMsg(B_REPLY);
597 status_t err = B_BAD_SCRIPT_SYNTAX;
599 BMessage specifier;
600 int32 what;
601 const char *prop;
602 int32 index;
603 if (msg->GetCurrentSpecifier(&index, &specifier, &what, &prop) != B_OK)
604 return BHandler::MessageReceived(msg);
606 switch (msg->what) {
607 case B_DELETE_PROPERTY:
608 case B_GET_PROPERTY:
609 case B_GET_SUPPORTED_SUITES:
610 if (strcmp(prop, "Replicant") == 0) {
611 BMessage reply;
612 int32 i;
613 uint32 ID;
614 BView *replicant = NULL;
615 BMessage *repMessage = NULL;
616 err = _GetProperty(&specifier, &reply);
617 if (err == B_OK)
618 err = reply.FindInt32("index", &i);
620 if (err == B_OK && msg->what == B_DELETE_PROPERTY) { // Delete Replicant
621 err = DeleteReplicant(i);
622 break;
624 if (err == B_OK && msg->what == B_GET_SUPPORTED_SUITES) {
625 err = replyMsg.AddString("suites", "suite/vnd.Be-replicant");
626 if (err == B_OK) {
627 BPropertyInfo propInfo(sReplicantPropertyList);
628 err = replyMsg.AddFlat("messages", &propInfo);
630 break;
632 if (err == B_OK )
633 repMessage = ReplicantAt(i, &replicant, &ID, &err);
634 if (err == B_OK && replicant) {
635 msg->PopSpecifier();
636 BMessage archive;
637 err = replicant->Archive(&archive);
638 if (err == B_OK && msg->GetCurrentSpecifier(&index, &specifier, &what, &prop) != B_OK) {
639 err = replyMsg.AddMessage("result", &archive);
640 break;
642 // now handles the replicant suite
643 err = B_BAD_SCRIPT_SYNTAX;
644 if (msg->what != B_GET_PROPERTY)
645 break;
646 if (strcmp(prop, "ID") == 0) {
647 err = replyMsg.AddInt32("result", ID);
648 } else if (strcmp(prop, "Name") == 0) {
649 err = replyMsg.AddString("result", replicant->Name());
650 } else if (strcmp(prop, "Signature") == 0) {
651 const char *add_on = NULL;
652 err = repMessage->FindString("add_on", &add_on);
653 if (err == B_OK)
654 err = replyMsg.AddString("result", add_on);
655 } else if (strcmp(prop, "Suites") == 0) {
656 err = replyMsg.AddString("suites", "suite/vnd.Be-replicant");
657 if (err == B_OK) {
658 BPropertyInfo propInfo(sReplicantPropertyList);
659 err = replyMsg.AddFlat("messages", &propInfo);
663 break;
665 return BHandler::MessageReceived(msg);
667 case B_COUNT_PROPERTIES:
668 if (strcmp(prop, "Replicant") == 0) {
669 err = replyMsg.AddInt32("result", CountReplicants());
670 break;
672 return BHandler::MessageReceived(msg);
674 case B_CREATE_PROPERTY:
676 BMessage replicantMsg;
677 BPoint pos;
678 if (msg->FindMessage("data", &replicantMsg) == B_OK
679 && msg->FindPoint("location", &pos) == B_OK) {
680 err = AddReplicant(&replicantMsg, pos);
683 break;
686 if (err < B_OK) {
687 replyMsg.what = B_MESSAGE_NOT_UNDERSTOOD;
689 if (err == B_BAD_SCRIPT_SYNTAX)
690 replyMsg.AddString("message", "Didn't understand the specifier(s)");
691 else
692 replyMsg.AddString("message", strerror(err));
695 replyMsg.AddInt32("error", err);
696 msg->SendReply(&replyMsg);
700 status_t
701 BShelf::Save()
703 status_t status = B_ERROR;
704 if (fEntry != NULL) {
705 BFile *file = new BFile(fEntry, B_READ_WRITE | B_ERASE_FILE);
706 status = file->InitCheck();
707 if (status != B_OK) {
708 delete file;
709 return status;
711 delete fStream;
712 fStream = file;
715 if (fStream != NULL) {
716 BMessage message;
717 status = _Archive(&message);
718 if (status == B_OK)
719 status = message.Flatten(fStream);
722 return status;
726 void
727 BShelf::SetDirty(bool state)
729 fDirty = state;
733 bool
734 BShelf::IsDirty() const
736 return fDirty;
740 BHandler *
741 BShelf::ResolveSpecifier(BMessage *msg, int32 index, BMessage *specifier,
742 int32 form, const char *property)
744 BPropertyInfo shelfPropInfo(sShelfPropertyList);
745 BHandler *target = NULL;
746 BView *replicant = NULL;
748 switch (shelfPropInfo.FindMatch(msg, 0, specifier, form, property)) {
749 case 0:
750 target = this;
751 break;
752 case 1:
753 if (msg->PopSpecifier() != B_OK) {
754 target = this;
755 break;
757 msg->SetCurrentSpecifier(index);
758 // fall through
759 case 2: {
760 BMessage reply;
761 status_t err = _GetProperty(specifier, &reply);
762 int32 i;
763 uint32 ID;
764 if (err == B_OK)
765 err = reply.FindInt32("index", &i);
766 if (err == B_OK)
767 ReplicantAt(i, &replicant, &ID, &err);
769 if (err == B_OK && replicant != NULL) {
770 if (index == 0)
771 return this;
772 } else {
773 BMessage replyMsg(B_MESSAGE_NOT_UNDERSTOOD);
774 replyMsg.AddInt32("error", B_BAD_INDEX);
775 replyMsg.AddString("message", "Cannot find replicant at/with specified index/name.");
776 msg->SendReply(&replyMsg);
779 msg->PopSpecifier();
780 break;
783 if (!replicant) {
784 if (target)
785 return target;
786 return BHandler::ResolveSpecifier(msg, index, specifier, form,
787 property);
790 int32 repIndex;
791 status_t err = msg->GetCurrentSpecifier(&repIndex, specifier, &form, &property);
792 if (err) {
793 BMessage reply(B_MESSAGE_NOT_UNDERSTOOD);
794 reply.AddInt32("error", err);
795 msg->SendReply(&reply);
796 return NULL;
799 BPropertyInfo replicantPropInfo(sReplicantPropertyList);
800 switch (replicantPropInfo.FindMatch(msg, 0, specifier, form, property)) {
801 case 0:
802 case 1:
803 case 2:
804 case 3:
805 msg->SetCurrentSpecifier(index);
806 target = this;
807 break;
808 case 4:
809 target = replicant;
810 msg->PopSpecifier();
811 break;
812 default:
813 break;
815 if (!target) {
816 BMessage replyMsg(B_MESSAGE_NOT_UNDERSTOOD);
817 replyMsg.AddInt32("error", B_BAD_SCRIPT_SYNTAX);
818 replyMsg.AddString("message", "Didn't understand the specifier(s)");
819 msg->SendReply(&replyMsg);
821 return target;
825 status_t
826 BShelf::GetSupportedSuites(BMessage *message)
828 status_t err;
829 err = message->AddString("suites", "suite/vnd.Be-shelf");
830 if (err == B_OK) {
831 BPropertyInfo propInfo(sShelfPropertyList);
832 err = message->AddFlat("messages", &propInfo);
834 if (err == B_OK)
835 return BHandler::GetSupportedSuites(message);
836 return err;
840 status_t
841 BShelf::Perform(perform_code d, void *arg)
843 return BHandler::Perform(d, arg);
847 bool
848 BShelf::AllowsDragging() const
850 return fAllowDragging;
854 void
855 BShelf::SetAllowsDragging(bool state)
857 fAllowDragging = state;
861 bool
862 BShelf::AllowsZombies() const
864 return fAllowZombies;
868 void
869 BShelf::SetAllowsZombies(bool state)
871 fAllowZombies = state;
875 bool
876 BShelf::DisplaysZombies() const
878 return fDisplayZombies;
882 void
883 BShelf::SetDisplaysZombies(bool state)
885 fDisplayZombies = state;
889 bool
890 BShelf::IsTypeEnforced() const
892 return fTypeEnforced;
896 void
897 BShelf::SetTypeEnforced(bool state)
899 fTypeEnforced = state;
903 status_t
904 BShelf::SetSaveLocation(BDataIO *data_io)
906 fDirty = true;
908 if (fEntry != NULL) {
909 delete fEntry;
910 fEntry = NULL;
913 fStream = data_io;
915 return B_OK;
919 status_t
920 BShelf::SetSaveLocation(const entry_ref *ref)
922 fDirty = true;
924 if (fEntry)
925 delete fEntry;
926 else
927 fStream = NULL;
929 fEntry = new BEntry(ref);
931 return B_OK;
935 BDataIO *
936 BShelf::SaveLocation(entry_ref *ref) const
938 if (fStream) {
939 if (ref)
940 *ref = entry_ref();
941 return fStream;
942 } else if (fEntry && ref)
943 fEntry->GetRef(ref);
945 return NULL;
949 status_t
950 BShelf::AddReplicant(BMessage *data, BPoint location)
952 return _AddReplicant(data, &location, fGenCount++);
956 status_t
957 BShelf::DeleteReplicant(BView *replicant)
959 int32 index = replicant_data::IndexOf(&fReplicants, replicant, true);
961 replicant_data *item = (replicant_data*)fReplicants.ItemAt(index);
962 if (item == NULL)
963 return B_BAD_VALUE;
965 return _DeleteReplicant(item);
969 status_t
970 BShelf::DeleteReplicant(BMessage *data)
972 int32 index = replicant_data::IndexOf(&fReplicants, data);
974 replicant_data *item = (replicant_data*)fReplicants.ItemAt(index);
975 if (!item)
976 return B_BAD_VALUE;
978 return _DeleteReplicant(item);
982 status_t
983 BShelf::DeleteReplicant(int32 index)
985 replicant_data *item = (replicant_data*)fReplicants.ItemAt(index);
986 if (!item)
987 return B_BAD_INDEX;
989 return _DeleteReplicant(item);
993 int32
994 BShelf::CountReplicants() const
996 return fReplicants.CountItems();
1000 BMessage *
1001 BShelf::ReplicantAt(int32 index, BView **_view, uint32 *_uniqueID,
1002 status_t *_error) const
1004 replicant_data *item = (replicant_data*)fReplicants.ItemAt(index);
1005 if (item == NULL) {
1006 // no replicant found
1007 if (_view)
1008 *_view = NULL;
1009 if (_uniqueID)
1010 *_uniqueID = ~(uint32)0;
1011 if (_error)
1012 *_error = B_BAD_INDEX;
1014 return NULL;
1017 if (_view)
1018 *_view = item->view;
1019 if (_uniqueID)
1020 *_uniqueID = item->id;
1021 if (_error)
1022 *_error = item->error;
1024 return item->message;
1028 int32
1029 BShelf::IndexOf(const BView* replicantView) const
1031 return replicant_data::IndexOf(&fReplicants, replicantView, false);
1035 int32
1036 BShelf::IndexOf(const BMessage *archive) const
1038 return replicant_data::IndexOf(&fReplicants, archive);
1042 int32
1043 BShelf::IndexOf(uint32 id) const
1045 return replicant_data::IndexOf(&fReplicants, id);
1049 bool
1050 BShelf::CanAcceptReplicantMessage(BMessage*) const
1052 return true;
1056 bool
1057 BShelf::CanAcceptReplicantView(BRect, BView*, BMessage*) const
1059 return true;
1063 BPoint
1064 BShelf::AdjustReplicantBy(BRect, BMessage*) const
1066 return B_ORIGIN;
1070 void
1071 BShelf::ReplicantDeleted(int32 index, const BMessage *archive,
1072 const BView *replicant)
1077 extern "C" void
1078 _ReservedShelf1__6BShelfFv(BShelf *const, int32, const BMessage*, const BView*)
1080 // is not contained in BeOS R5's libbe, so we leave it empty
1084 void BShelf::_ReservedShelf2() {}
1085 void BShelf::_ReservedShelf3() {}
1086 void BShelf::_ReservedShelf4() {}
1087 void BShelf::_ReservedShelf5() {}
1088 void BShelf::_ReservedShelf6() {}
1089 void BShelf::_ReservedShelf7() {}
1090 void BShelf::_ReservedShelf8() {}
1093 BShelf::BShelf(const BShelf&)
1098 BShelf &
1099 BShelf::operator=(const BShelf &)
1101 return *this;
1105 status_t
1106 BShelf::_Archive(BMessage *data) const
1108 status_t status = BHandler::Archive(data);
1109 if (status != B_OK)
1110 return status;
1112 status = data->AddBool("_zom_dsp", DisplaysZombies());
1113 if (status != B_OK)
1114 return status;
1116 status = data->AddBool("_zom_alw", AllowsZombies());
1117 if (status != B_OK)
1118 return status;
1120 status = data->AddInt32("_sg_cnt", fGenCount);
1121 if (status != B_OK)
1122 return status;
1124 BMessage archive('ARCV');
1125 for (int32 i = 0; i < fReplicants.CountItems(); i++) {
1126 if (((replicant_data *)fReplicants.ItemAt(i))->Archive(&archive) == B_OK)
1127 status = data->AddMessage("replicant", &archive);
1128 if (status != B_OK)
1129 break;
1130 archive.MakeEmpty();
1133 return status;
1137 void
1138 BShelf::_InitData(BEntry *entry, BDataIO *stream, BView *view,
1139 bool allowDrags)
1141 fContainerView = view;
1142 fStream = NULL;
1143 fEntry = entry;
1144 fFilter = NULL;
1145 fGenCount = 1;
1146 fAllowDragging = allowDrags;
1147 fDirty = true;
1148 fDisplayZombies = false;
1149 fAllowZombies = true;
1150 fTypeEnforced = false;
1152 if (fEntry != NULL)
1153 fStream = new BFile(entry, B_READ_ONLY);
1154 else
1155 fStream = stream;
1157 fFilter = new ShelfContainerViewFilter(this, fContainerView);
1159 fContainerView->AddFilter(fFilter);
1160 fContainerView->_SetShelf(this);
1162 if (fStream != NULL) {
1163 BMessage archive;
1165 if (archive.Unflatten(fStream) == B_OK) {
1166 bool allowZombies;
1167 if (archive.FindBool("_zom_dsp", &allowZombies) != B_OK)
1168 allowZombies = false;
1170 SetDisplaysZombies(allowZombies);
1172 if (archive.FindBool("_zom_alw", &allowZombies) != B_OK)
1173 allowZombies = true;
1175 SetAllowsZombies(allowZombies);
1177 int32 genCount;
1178 if (!archive.FindInt32("_sg_cnt", &genCount))
1179 genCount = 1;
1181 BMessage replicant;
1182 for (int32 i = 0; archive.FindMessage("replicant", i, &replicant)
1183 == B_OK; i++) {
1184 BPoint point;
1185 BMessage *replMsg = new BMessage();
1186 ObjectDeleter<BMessage> deleter(replMsg);
1187 replicant.FindPoint("position", &point);
1188 if (replicant.FindMessage("message", replMsg) == B_OK)
1189 if (AddReplicant(replMsg, point) == B_OK) {
1190 // Detach the deleter since AddReplicant is taking
1191 // ownership on success. In R2 API this should be
1192 // changed to take always ownership on the message.
1193 deleter.Detach();
1201 status_t
1202 BShelf::_DeleteReplicant(replicant_data* item)
1204 BView *view = item->view;
1205 if (view == NULL)
1206 view = item->zombie_view;
1208 if (view != NULL)
1209 view->RemoveSelf();
1211 if (item->dragger != NULL)
1212 item->dragger->RemoveSelf();
1214 int32 index = replicant_data::IndexOf(&fReplicants, item->message);
1216 ReplicantDeleted(index, item->message, view);
1218 fReplicants.RemoveItem(item);
1220 if (item->relation == BDragger::TARGET_IS_PARENT
1221 || item->relation == BDragger::TARGET_IS_SIBLING) {
1222 delete view;
1224 if (item->relation == BDragger::TARGET_IS_CHILD
1225 || item->relation == BDragger::TARGET_IS_SIBLING) {
1226 delete item->dragger;
1229 // Update use count for image and unload if necessary
1230 const char* signature = NULL;
1231 if (item->message->FindString("add_on", &signature) == B_OK
1232 && signature != NULL) {
1233 LoadedImages* loadedImages = LoadedImages::Default();
1234 AutoLock<LoadedImages> lock(loadedImages);
1235 if (lock.IsLocked()) {
1236 LoadedImageMap::iterator it = loadedImages->images.find(
1237 BString(signature));
1239 if (it != loadedImages->images.end()) {
1240 (*it).second.second--;
1241 if ((*it).second.second <= 0) {
1242 unload_add_on((*it).second.first);
1243 loadedImages->images.erase(it);
1249 delete item;
1251 return B_OK;
1255 //! Takes over ownership of \a data on success only
1256 status_t
1257 BShelf::_AddReplicant(BMessage *data, BPoint *location, uint32 uniqueID)
1259 // Check shelf types if needed
1260 if (fTypeEnforced) {
1261 const char *shelfType = NULL;
1262 if (data->FindString("shelf_type", &shelfType) == B_OK
1263 && shelfType != NULL) {
1264 if (Name() && strcmp(shelfType, Name()) != 0) {
1265 printf("Replicant was rejected by BShelf: The BShelf's type and the Replicant's type don't match.");
1266 return send_reply(data, B_ERROR, uniqueID);
1267 } else {
1268 printf("Replicant was rejected by BShelf: Replicant indicated a <type> (%s), but the shelf does not.", shelfType);
1269 return send_reply(data, B_ERROR, uniqueID);
1271 } else {
1272 printf("Replicant was rejected by BShelf: Replicant did not have a <type>");
1273 return send_reply(data, B_ERROR, uniqueID);
1277 // Check if we can accept this message
1278 if (!CanAcceptReplicantMessage(data)) {
1279 printf("Replicant was rejected by BShelf::CanAcceptReplicantMessage()");
1280 return send_reply(data, B_ERROR, uniqueID);
1283 // Check if we can create multiple instances
1284 if (data->FindBool("be:load_each_time")) {
1285 const char *className = NULL;
1286 const char *addOn = NULL;
1288 if (data->FindString("class", &className) == B_OK
1289 && data->FindString("add_on", &addOn) == B_OK) {
1290 if (find_replicant(fReplicants, className, addOn)) {
1291 printf("Replicant was rejected. Unique replicant already exists. class=%s, signature=%s",
1292 className, addOn);
1293 return send_reply(data, B_ERROR, uniqueID);
1298 // Instantiate the object, if this fails we have a zombie
1299 image_id image = -1;
1300 BArchivable *archivable = _InstantiateObject(data, &image);
1302 BView *view = NULL;
1304 if (archivable != NULL) {
1305 view = dynamic_cast<BView*>(archivable);
1307 if (view == NULL)
1308 return send_reply(data, B_ERROR, uniqueID);
1311 BDragger* dragger = NULL;
1312 BView* replicant = NULL;
1313 BDragger::relation relation = BDragger::TARGET_UNKNOWN;
1314 _BZombieReplicantView_* zombie = NULL;
1315 if (view != NULL) {
1316 const BPoint point = location ? *location : view->Frame().LeftTop();
1317 replicant = _GetReplicant(data, view, point, dragger, relation);
1318 if (replicant == NULL)
1319 return send_reply(data, B_ERROR, uniqueID);
1320 } else if (fDisplayZombies && fAllowZombies) {
1321 zombie = _CreateZombie(data, dragger);
1322 } else if (!fAllowZombies) {
1323 // There was no view, and we're not allowed to have any zombies
1324 // in the house
1325 return send_reply(data, B_ERROR, uniqueID);
1328 // Update use count for image
1329 const char* signature = NULL;
1330 if (data->FindString("add_on", &signature) == B_OK && signature != NULL) {
1331 LoadedImages* loadedImages = LoadedImages::Default();
1332 AutoLock<LoadedImages> lock(loadedImages);
1333 if (lock.IsLocked()) {
1334 LoadedImageMap::iterator it = loadedImages->images.find(
1335 BString(signature));
1337 if (it == loadedImages->images.end())
1338 loadedImages->images.insert(LoadedImageMap::value_type(
1339 BString(signature), std::pair<image_id, int>(image, 1)));
1340 else
1341 (*it).second.second++;
1345 if (zombie == NULL) {
1346 data->RemoveName("_drop_point_");
1347 data->RemoveName("_drop_offset_");
1350 replicant_data *item = new replicant_data(data, replicant, dragger,
1351 relation, uniqueID);
1353 item->error = B_OK;
1354 item->zombie_view = zombie;
1356 fReplicants.AddItem(item);
1358 return send_reply(data, B_OK, uniqueID);
1362 BView *
1363 BShelf::_GetReplicant(BMessage *data, BView *view, const BPoint &point,
1364 BDragger *&dragger, BDragger::relation &relation)
1366 // TODO: test me -- there seems to be lots of bugs parked here!
1367 BView *replicant = NULL;
1368 _GetReplicantData(data, view, replicant, dragger, relation);
1370 if (dragger != NULL)
1371 dragger->_SetViewToDrag(replicant);
1373 BRect frame = view->Frame().OffsetToCopy(point);
1374 if (!CanAcceptReplicantView(frame, replicant, data)) {
1375 // the view has not been accepted
1376 if (relation == BDragger::TARGET_IS_PARENT
1377 || relation == BDragger::TARGET_IS_SIBLING) {
1378 delete replicant;
1380 if (relation == BDragger::TARGET_IS_CHILD
1381 || relation == BDragger::TARGET_IS_SIBLING) {
1382 delete dragger;
1384 return NULL;
1387 BPoint adjust = AdjustReplicantBy(frame, data);
1389 if (dragger != NULL)
1390 dragger->_SetShelf(this);
1392 // TODO: could be not correct for some relations
1393 view->MoveTo(point + adjust);
1395 // if it's a sibling or a child, we need to add the dragger
1396 if (relation == BDragger::TARGET_IS_SIBLING
1397 || relation == BDragger::TARGET_IS_CHILD)
1398 fContainerView->AddChild(dragger);
1400 if (relation != BDragger::TARGET_IS_CHILD)
1401 fContainerView->AddChild(replicant);
1403 replicant->AddFilter(new ReplicantViewFilter(this, replicant));
1405 return replicant;
1409 /* static */
1410 void
1411 BShelf::_GetReplicantData(BMessage *data, BView *view, BView *&replicant,
1412 BDragger *&dragger, BDragger::relation &relation)
1414 // Check if we have a dragger archived as "__widget" inside the message
1415 BMessage widget;
1416 if (data->FindMessage("__widget", &widget) == B_OK) {
1417 image_id draggerImage = B_ERROR;
1418 replicant = view;
1419 dragger = dynamic_cast<BDragger*>(_InstantiateObject(&widget, &draggerImage));
1420 // Replicant is a sibling, or unknown, if there isn't a dragger
1421 if (dragger != NULL)
1422 relation = BDragger::TARGET_IS_SIBLING;
1424 } else if ((dragger = dynamic_cast<BDragger*>(view)) != NULL) {
1425 // Replicant is child of the dragger
1426 relation = BDragger::TARGET_IS_CHILD;
1427 replicant = dragger->ChildAt(0);
1429 } else {
1430 // Replicant is parent of the dragger
1431 relation = BDragger::TARGET_IS_PARENT;
1432 replicant = view;
1433 dragger = dynamic_cast<BDragger *>(replicant->FindView("_dragger_"));
1434 // can be NULL, the replicant could not have a dragger at all
1439 _BZombieReplicantView_ *
1440 BShelf::_CreateZombie(BMessage *data, BDragger *&dragger)
1442 // TODO: the zombies must be adjusted and moved as well!
1443 BRect frame;
1444 if (data->FindRect("_frame", &frame) != B_OK)
1445 frame = BRect();
1447 _BZombieReplicantView_ *zombie = NULL;
1448 if (data->WasDropped()) {
1449 BPoint offset;
1450 BPoint dropPoint = data->DropPoint(&offset);
1452 frame.OffsetTo(fContainerView->ConvertFromScreen(dropPoint) - offset);
1454 zombie = new _BZombieReplicantView_(frame, B_ERROR);
1456 frame.OffsetTo(B_ORIGIN);
1458 dragger = new BDragger(frame, zombie);
1459 dragger->_SetShelf(this);
1460 dragger->_SetZombied(true);
1462 zombie->AddChild(dragger);
1463 zombie->SetArchive(data);
1464 zombie->AddFilter(new ReplicantViewFilter(this, zombie));
1466 fContainerView->AddChild(zombie);
1469 return zombie;
1473 status_t
1474 BShelf::_GetProperty(BMessage *msg, BMessage *reply)
1476 uint32 ID;
1477 status_t err = B_ERROR;
1478 BView *replicant = NULL;
1479 switch (msg->what) {
1480 case B_INDEX_SPECIFIER: {
1481 int32 index = -1;
1482 if (msg->FindInt32("index", &index)!=B_OK)
1483 break;
1484 ReplicantAt(index, &replicant, &ID, &err);
1485 break;
1487 case B_REVERSE_INDEX_SPECIFIER: {
1488 int32 rindex;
1489 if (msg->FindInt32("index", &rindex) != B_OK)
1490 break;
1491 ReplicantAt(CountReplicants() - rindex, &replicant, &ID, &err);
1492 break;
1494 case B_NAME_SPECIFIER: {
1495 const char *name;
1496 if (msg->FindString("name", &name) != B_OK)
1497 break;
1498 for (int32 i = 0; i < CountReplicants(); i++) {
1499 BView *view = NULL;
1500 ReplicantAt(i, &view, &ID, &err);
1501 if (err != B_OK || view == NULL)
1502 continue;
1503 if (view->Name() != NULL && strcmp(view->Name(), name) == 0) {
1504 replicant = view;
1505 break;
1507 err = B_NAME_NOT_FOUND;
1509 break;
1511 case B_ID_SPECIFIER: {
1512 uint32 id;
1513 if (msg->FindInt32("id", (int32 *)&id) != B_OK)
1514 break;
1515 for (int32 i = 0; i < CountReplicants(); i++) {
1516 BView *view = NULL;
1517 ReplicantAt(i, &view, &ID, &err);
1518 if (err != B_OK || view == NULL)
1519 continue;
1520 if (ID == id) {
1521 replicant = view;
1522 break;
1524 err = B_NAME_NOT_FOUND;
1526 break;
1528 default:
1529 break;
1532 if (replicant) {
1533 reply->AddInt32("index", IndexOf(replicant));
1534 reply->AddInt32("ID", ID);
1537 return err;
1541 /* static */
1542 BArchivable *
1543 BShelf::_InstantiateObject(BMessage *archive, image_id *image)
1545 // Stay on the safe side. The constructor called by instantiate_object
1546 // could throw an exception, which we catch here. Otherwise our calling app
1547 // could die without notice.
1548 try {
1549 return instantiate_object(archive, image);
1550 } catch (...) {
1551 return NULL;