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