1 /*
2 * Copyright 2001-2012, Haiku.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 * Marc Flerackers (mflerackers@androme.be)
7 * Rene Gollent (rene@gollent.com)
8 * Alexandre Deckner (alex@zappotek.com)
9 */
10
11
12 //! BDragger represents a replicant "handle".
13
14
15 #include <pthread.h>
16 #include <stdio.h>
17 #include <stdlib.h>
18
19 #include <Alert.h>
20 #include <Beep.h>
21 #include <Bitmap.h>
22 #include <Dragger.h>
23 #include <MenuItem.h>
24 #include <Message.h>
25 #include <PopUpMenu.h>
26 #include <Shelf.h>
27 #include <SystemCatalog.h>
28 #include <Window.h>
29
30 #include <AutoLocker.h>
31
32 #include <AppServerLink.h>
33 #include <DragTrackingFilter.h>
34 #include <binary_compatibility/Interface.h>
35 #include <ServerProtocol.h>
36 #include <ViewPrivate.h>
37
38 #include "ZombieReplicantView.h"
39
40 using BPrivate::gSystemCatalog;
41
42 #undef B_TRANSLATION_CONTEXT
43 #define B_TRANSLATION_CONTEXT "Dragger"
44
45 #undef B_TRANSLATE
46 #define B_TRANSLATE(str) \
47 gSystemCatalog.GetString(B_TRANSLATE_MARK(str), "Dragger")
48
49
50 static const uint32 kMsgDragStarted = 'Drgs';
51
52 static const unsigned char kHandBitmap[] = {
53 255, 255, 0, 0, 0, 255, 255, 255,
54 255, 255, 0, 131, 131, 0, 255, 255,
55 0, 0, 0, 0, 131, 131, 0, 0,
56 0, 131, 0, 0, 131, 131, 0, 0,
57 0, 131, 131, 131, 131, 131, 0, 0,
58 255, 0, 131, 131, 131, 131, 0, 0,
59 255, 255, 0, 0, 0, 0, 0, 0,
60 255, 255, 255, 255, 255, 255, 0, 0
61 };
62
63
64 namespace {
65
66 struct DraggerManager {
67 bool visible;
68 bool visibleInitialized;
69 BList list;
70
DraggerManager__anonc9b609ce0111::DraggerManager71 DraggerManager()
72 :
73 visible(false),
74 visibleInitialized(false),
75 fLock("BDragger static")
76 {
77 }
78
Lock__anonc9b609ce0111::DraggerManager79 bool Lock()
80 {
81 return fLock.Lock();
82 }
83
Unlock__anonc9b609ce0111::DraggerManager84 void Unlock()
85 {
86 fLock.Unlock();
87 }
88
Default__anonc9b609ce0111::DraggerManager89 static DraggerManager* Default()
90 {
91 if (sDefaultInstance == NULL)
92 pthread_once(&sDefaultInitOnce, &_InitSingleton);
93
94 return sDefaultInstance;
95 }
96
97 private:
_InitSingleton__anonc9b609ce0111::DraggerManager98 static void _InitSingleton()
99 {
100 sDefaultInstance = new DraggerManager;
101 }
102
103 private:
104 BLocker fLock;
105
106 static pthread_once_t sDefaultInitOnce;
107 static DraggerManager* sDefaultInstance;
108 };
109
110 pthread_once_t DraggerManager::sDefaultInitOnce = PTHREAD_ONCE_INIT;
111 DraggerManager* DraggerManager::sDefaultInstance = NULL;
112
113 } // unnamed namespace
114
115
BDragger(BRect frame,BView * target,uint32 resizingMode,uint32 flags)116 BDragger::BDragger(BRect frame, BView* target, uint32 resizingMode,
117 uint32 flags)
118 :
119 BView(frame, "_dragger_", resizingMode, flags),
120 fTarget(target),
121 fRelation(TARGET_UNKNOWN),
122 fShelf(NULL),
123 fTransition(false),
124 fIsZombie(false),
125 fErrCount(0),
126 fPopUpIsCustom(false),
127 fPopUp(NULL)
128 {
129 _InitData();
130 }
131
132
BDragger(BView * target,uint32 flags)133 BDragger::BDragger(BView* target, uint32 flags)
134 :
135 BView("_dragger_", flags),
136 fTarget(target),
137 fRelation(TARGET_UNKNOWN),
138 fShelf(NULL),
139 fTransition(false),
140 fIsZombie(false),
141 fErrCount(0),
142 fPopUpIsCustom(false),
143 fPopUp(NULL)
144 {
145 _InitData();
146 }
147
148
BDragger(BMessage * data)149 BDragger::BDragger(BMessage* data)
150 :
151 BView(data),
152 fTarget(NULL),
153 fRelation(TARGET_UNKNOWN),
154 fShelf(NULL),
155 fTransition(false),
156 fIsZombie(false),
157 fErrCount(0),
158 fPopUpIsCustom(false),
159 fPopUp(NULL)
160 {
161 data->FindInt32("_rel", (int32*)&fRelation);
162
163 _InitData();
164
165 BMessage popupMsg;
166 if (data->FindMessage("_popup", &popupMsg) == B_OK) {
167 BArchivable* archivable = instantiate_object(&popupMsg);
168
169 if (archivable) {
170 fPopUp = dynamic_cast<BPopUpMenu*>(archivable);
171 fPopUpIsCustom = true;
172 }
173 }
174 }
175
176
~BDragger()177 BDragger::~BDragger()
178 {
179 delete fPopUp;
180 delete fBitmap;
181 }
182
183
184 BArchivable *
Instantiate(BMessage * data)185 BDragger::Instantiate(BMessage* data)
186 {
187 if (validate_instantiation(data, "BDragger"))
188 return new BDragger(data);
189 return NULL;
190 }
191
192
193 status_t
Archive(BMessage * data,bool deep) const194 BDragger::Archive(BMessage* data, bool deep) const
195 {
196 status_t ret = BView::Archive(data, deep);
197 if (ret != B_OK)
198 return ret;
199
200 BMessage popupMsg;
201
202 if (fPopUp != NULL && fPopUpIsCustom) {
203 bool windowLocked = fPopUp->Window()->Lock();
204
205 ret = fPopUp->Archive(&popupMsg, deep);
206
207 if (windowLocked) {
208 fPopUp->Window()->Unlock();
209 // TODO: Investigate, in some (rare) occasions the menu window
210 // has already been unlocked
211 }
212
213 if (ret == B_OK)
214 ret = data->AddMessage("_popup", &popupMsg);
215 }
216
217 if (ret == B_OK)
218 ret = data->AddInt32("_rel", fRelation);
219 return ret;
220 }
221
222
223 void
AttachedToWindow()224 BDragger::AttachedToWindow()
225 {
226 if (fIsZombie) {
227 SetLowColor(kZombieColor);
228 SetViewColor(kZombieColor);
229 } else {
230 SetFlags(Flags() | B_TRANSPARENT_BACKGROUND);
231 SetLowColor(B_TRANSPARENT_COLOR);
232 SetViewColor(B_TRANSPARENT_COLOR);
233 }
234
235 _DetermineRelationship();
236 _AddToList();
237
238 AddFilter(new DragTrackingFilter(this, kMsgDragStarted));
239 }
240
241
242 void
DetachedFromWindow()243 BDragger::DetachedFromWindow()
244 {
245 _RemoveFromList();
246 }
247
248
249 void
Draw(BRect update)250 BDragger::Draw(BRect update)
251 {
252 BRect bounds(Bounds());
253
254 if (AreDraggersDrawn() && (fShelf == NULL || fShelf->AllowsDragging())) {
255 BPoint where = bounds.RightBottom() - BPoint(fBitmap->Bounds().Width(),
256 fBitmap->Bounds().Height());
257 SetDrawingMode(B_OP_OVER);
258 DrawBitmap(fBitmap, where);
259 SetDrawingMode(B_OP_COPY);
260
261 if (fIsZombie) {
262 // TODO: should draw it differently ?
263 }
264 }
265 }
266
267
268 void
MouseDown(BPoint where)269 BDragger::MouseDown(BPoint where)
270 {
271 if (fTarget == NULL || !AreDraggersDrawn())
272 return;
273
274 uint32 buttons;
275 Window()->CurrentMessage()->FindInt32("buttons", (int32*)&buttons);
276
277 if (fShelf != NULL && (buttons & B_SECONDARY_MOUSE_BUTTON) != 0)
278 _ShowPopUp(fTarget, where);
279 }
280
281
282 void
MouseUp(BPoint point)283 BDragger::MouseUp(BPoint point)
284 {
285 BView::MouseUp(point);
286 }
287
288
289 void
MouseMoved(BPoint point,uint32 code,const BMessage * msg)290 BDragger::MouseMoved(BPoint point, uint32 code, const BMessage* msg)
291 {
292 BView::MouseMoved(point, code, msg);
293 }
294
295
296 void
MessageReceived(BMessage * msg)297 BDragger::MessageReceived(BMessage* msg)
298 {
299 switch (msg->what) {
300 case B_TRASH_TARGET:
301 if (fShelf != NULL)
302 Window()->PostMessage(kDeleteReplicant, fTarget, NULL);
303 else {
304 BAlert* alert = new BAlert(B_TRANSLATE("Warning"),
305 B_TRANSLATE("Can't delete this replicant from its original "
306 "application. Life goes on."),
307 B_TRANSLATE("OK"), NULL, NULL, B_WIDTH_FROM_WIDEST,
308 B_WARNING_ALERT);
309 alert->SetFlags(alert->Flags() | B_CLOSE_ON_ESCAPE);
310 alert->Go(NULL);
311 }
312 break;
313
314 case _SHOW_DRAG_HANDLES_:
315 // This code is used whenever the "are draggers drawn" option is
316 // changed.
317 if (fRelation == TARGET_IS_CHILD) {
318 Invalidate(Bounds());
319 } else {
320 if ((fShelf != NULL && fShelf->AllowsDragging()
321 && AreDraggersDrawn())
322 || AreDraggersDrawn()) {
323 Show();
324 } else
325 Hide();
326 }
327 break;
328
329 case kMsgDragStarted:
330 if (fTarget != NULL) {
331 BMessage archive(B_ARCHIVED_OBJECT);
332
333 if (fRelation == TARGET_IS_PARENT)
334 fTarget->Archive(&archive);
335 else if (fRelation == TARGET_IS_CHILD)
336 Archive(&archive);
337 else if (fTarget->Archive(&archive)) {
338 BMessage archivedSelf(B_ARCHIVED_OBJECT);
339
340 if (Archive(&archivedSelf))
341 archive.AddMessage("__widget", &archivedSelf);
342 }
343
344 archive.AddInt32("be:actions", B_TRASH_TARGET);
345 BPoint offset;
346 drawing_mode mode;
347 BBitmap* bitmap = DragBitmap(&offset, &mode);
348 if (bitmap != NULL)
349 DragMessage(&archive, bitmap, mode, offset, this);
350 else {
351 DragMessage(&archive, ConvertFromScreen(
352 fTarget->ConvertToScreen(fTarget->Bounds())), this);
353 }
354 }
355 break;
356
357 default:
358 BView::MessageReceived(msg);
359 break;
360 }
361 }
362
363
364 void
FrameMoved(BPoint newPosition)365 BDragger::FrameMoved(BPoint newPosition)
366 {
367 BView::FrameMoved(newPosition);
368 }
369
370
371 void
FrameResized(float newWidth,float newHeight)372 BDragger::FrameResized(float newWidth, float newHeight)
373 {
374 BView::FrameResized(newWidth, newHeight);
375 }
376
377
378 status_t
ShowAllDraggers()379 BDragger::ShowAllDraggers()
380 {
381 BPrivate::AppServerLink link;
382 link.StartMessage(AS_SET_SHOW_ALL_DRAGGERS);
383 link.Attach<bool>(true);
384
385 status_t status = link.Flush();
386 if (status == B_OK) {
387 DraggerManager* manager = DraggerManager::Default();
388 AutoLocker<DraggerManager> locker(manager);
389 manager->visible = true;
390 manager->visibleInitialized = true;
391 }
392
393 return status;
394 }
395
396
397 status_t
HideAllDraggers()398 BDragger::HideAllDraggers()
399 {
400 BPrivate::AppServerLink link;
401 link.StartMessage(AS_SET_SHOW_ALL_DRAGGERS);
402 link.Attach<bool>(false);
403
404 status_t status = link.Flush();
405 if (status == B_OK) {
406 DraggerManager* manager = DraggerManager::Default();
407 AutoLocker<DraggerManager> locker(manager);
408 manager->visible = false;
409 manager->visibleInitialized = true;
410 }
411
412 return status;
413 }
414
415
416 bool
AreDraggersDrawn()417 BDragger::AreDraggersDrawn()
418 {
419 DraggerManager* manager = DraggerManager::Default();
420 AutoLocker<DraggerManager> locker(manager);
421
422 if (!manager->visibleInitialized) {
423 BPrivate::AppServerLink link;
424 link.StartMessage(AS_GET_SHOW_ALL_DRAGGERS);
425
426 status_t status;
427 if (link.FlushWithReply(status) == B_OK && status == B_OK) {
428 link.Read<bool>(&manager->visible);
429 manager->visibleInitialized = true;
430 } else
431 return false;
432 }
433
434 return manager->visible;
435 }
436
437
438 BHandler*
ResolveSpecifier(BMessage * message,int32 index,BMessage * specifier,int32 form,const char * property)439 BDragger::ResolveSpecifier(BMessage* message, int32 index, BMessage* specifier,
440 int32 form, const char* property)
441 {
442 return BView::ResolveSpecifier(message, index, specifier, form, property);
443 }
444
445
446 status_t
GetSupportedSuites(BMessage * data)447 BDragger::GetSupportedSuites(BMessage* data)
448 {
449 return BView::GetSupportedSuites(data);
450 }
451
452
453 status_t
Perform(perform_code code,void * _data)454 BDragger::Perform(perform_code code, void* _data)
455 {
456 switch (code) {
457 case PERFORM_CODE_MIN_SIZE:
458 ((perform_data_min_size*)_data)->return_value
459 = BDragger::MinSize();
460 return B_OK;
461 case PERFORM_CODE_MAX_SIZE:
462 ((perform_data_max_size*)_data)->return_value
463 = BDragger::MaxSize();
464 return B_OK;
465 case PERFORM_CODE_PREFERRED_SIZE:
466 ((perform_data_preferred_size*)_data)->return_value
467 = BDragger::PreferredSize();
468 return B_OK;
469 case PERFORM_CODE_LAYOUT_ALIGNMENT:
470 ((perform_data_layout_alignment*)_data)->return_value
471 = BDragger::LayoutAlignment();
472 return B_OK;
473 case PERFORM_CODE_HAS_HEIGHT_FOR_WIDTH:
474 ((perform_data_has_height_for_width*)_data)->return_value
475 = BDragger::HasHeightForWidth();
476 return B_OK;
477 case PERFORM_CODE_GET_HEIGHT_FOR_WIDTH:
478 {
479 perform_data_get_height_for_width* data
480 = (perform_data_get_height_for_width*)_data;
481 BDragger::GetHeightForWidth(data->width, &data->min, &data->max,
482 &data->preferred);
483 return B_OK;
484 }
485 case PERFORM_CODE_SET_LAYOUT:
486 {
487 perform_data_set_layout* data = (perform_data_set_layout*)_data;
488 BDragger::SetLayout(data->layout);
489 return B_OK;
490 }
491 case PERFORM_CODE_LAYOUT_INVALIDATED:
492 {
493 perform_data_layout_invalidated* data
494 = (perform_data_layout_invalidated*)_data;
495 BDragger::LayoutInvalidated(data->descendants);
496 return B_OK;
497 }
498 case PERFORM_CODE_DO_LAYOUT:
499 {
500 BDragger::DoLayout();
501 return B_OK;
502 }
503 }
504
505 return BView::Perform(code, _data);
506 }
507
508
509 void
ResizeToPreferred()510 BDragger::ResizeToPreferred()
511 {
512 BView::ResizeToPreferred();
513 }
514
515
516 void
GetPreferredSize(float * _width,float * _height)517 BDragger::GetPreferredSize(float* _width, float* _height)
518 {
519 BView::GetPreferredSize(_width, _height);
520 }
521
522
523 void
MakeFocus(bool state)524 BDragger::MakeFocus(bool state)
525 {
526 BView::MakeFocus(state);
527 }
528
529
530 void
AllAttached()531 BDragger::AllAttached()
532 {
533 BView::AllAttached();
534 }
535
536
537 void
AllDetached()538 BDragger::AllDetached()
539 {
540 BView::AllDetached();
541 }
542
543
544 status_t
SetPopUp(BPopUpMenu * menu)545 BDragger::SetPopUp(BPopUpMenu* menu)
546 {
547 if (menu != NULL && menu != fPopUp) {
548 delete fPopUp;
549 fPopUp = menu;
550 fPopUpIsCustom = true;
551 return B_OK;
552 }
553 return B_ERROR;
554 }
555
556
557 BPopUpMenu*
PopUp() const558 BDragger::PopUp() const
559 {
560 if (fPopUp == NULL && fTarget)
561 const_cast<BDragger*>(this)->_BuildDefaultPopUp();
562
563 return fPopUp;
564 }
565
566
567 bool
InShelf() const568 BDragger::InShelf() const
569 {
570 return fShelf != NULL;
571 }
572
573
574 BView*
Target() const575 BDragger::Target() const
576 {
577 return fTarget;
578 }
579
580
581 BBitmap*
DragBitmap(BPoint * offset,drawing_mode * mode)582 BDragger::DragBitmap(BPoint* offset, drawing_mode* mode)
583 {
584 return NULL;
585 }
586
587
588 bool
IsVisibilityChanging() const589 BDragger::IsVisibilityChanging() const
590 {
591 return fTransition;
592 }
593
594
_ReservedDragger2()595 void BDragger::_ReservedDragger2() {}
_ReservedDragger3()596 void BDragger::_ReservedDragger3() {}
_ReservedDragger4()597 void BDragger::_ReservedDragger4() {}
598
599
600 BDragger&
operator =(const BDragger &)601 BDragger::operator=(const BDragger&)
602 {
603 return *this;
604 }
605
606
607 /*static*/ void
_UpdateShowAllDraggers(bool visible)608 BDragger::_UpdateShowAllDraggers(bool visible)
609 {
610 DraggerManager* manager = DraggerManager::Default();
611 AutoLocker<DraggerManager> locker(manager);
612
613 manager->visibleInitialized = true;
614 manager->visible = visible;
615
616 for (int32 i = manager->list.CountItems(); i-- > 0;) {
617 BDragger* dragger = (BDragger*)manager->list.ItemAt(i);
618 BMessenger target(dragger);
619 target.SendMessage(_SHOW_DRAG_HANDLES_);
620 }
621 }
622
623
624 void
_InitData()625 BDragger::_InitData()
626 {
627 fBitmap = new BBitmap(BRect(0.0f, 0.0f, 7.0f, 7.0f), B_CMAP8, false, false);
628 fBitmap->SetBits(kHandBitmap, fBitmap->BitsLength(), 0, B_CMAP8);
629 }
630
631
632 void
_AddToList()633 BDragger::_AddToList()
634 {
635 DraggerManager* manager = DraggerManager::Default();
636 AutoLocker<DraggerManager> locker(manager);
637 manager->list.AddItem(this);
638
639 bool allowsDragging = true;
640 if (fShelf)
641 allowsDragging = fShelf->AllowsDragging();
642
643 if (!AreDraggersDrawn() || !allowsDragging) {
644 // The dragger is not shown - but we can't hide us in case we're the
645 // parent of the actual target view (because then you couldn't see
646 // it anymore).
647 if (fRelation != TARGET_IS_CHILD && !IsHidden(this))
648 Hide();
649 }
650 }
651
652
653 void
_RemoveFromList()654 BDragger::_RemoveFromList()
655 {
656 DraggerManager* manager = DraggerManager::Default();
657 AutoLocker<DraggerManager> locker(manager);
658 manager->list.RemoveItem(this);
659 }
660
661
662 status_t
_DetermineRelationship()663 BDragger::_DetermineRelationship()
664 {
665 if (fTarget != NULL) {
666 if (fTarget == Parent())
667 fRelation = TARGET_IS_PARENT;
668 else if (fTarget == ChildAt(0))
669 fRelation = TARGET_IS_CHILD;
670 else
671 fRelation = TARGET_IS_SIBLING;
672 } else {
673 if (fRelation == TARGET_IS_PARENT)
674 fTarget = Parent();
675 else if (fRelation == TARGET_IS_CHILD)
676 fTarget = ChildAt(0);
677 else
678 return B_ERROR;
679 }
680
681 if (fRelation == TARGET_IS_PARENT) {
682 BRect bounds(Frame());
683 BRect parentBounds(Parent()->Bounds());
684 if (!parentBounds.Contains(bounds)) {
685 MoveTo(parentBounds.right - bounds.Width(),
686 parentBounds.bottom - bounds.Height());
687 }
688 }
689
690 return B_OK;
691 }
692
693
694 status_t
_SetViewToDrag(BView * target)695 BDragger::_SetViewToDrag(BView* target)
696 {
697 if (target->Window() != Window())
698 return B_ERROR;
699
700 fTarget = target;
701
702 if (Window() != NULL)
703 _DetermineRelationship();
704
705 return B_OK;
706 }
707
708
709 void
_SetShelf(BShelf * shelf)710 BDragger::_SetShelf(BShelf* shelf)
711 {
712 fShelf = shelf;
713 }
714
715
716 void
_SetZombied(bool state)717 BDragger::_SetZombied(bool state)
718 {
719 fIsZombie = state;
720
721 if (state) {
722 SetLowColor(kZombieColor);
723 SetViewColor(kZombieColor);
724 }
725 }
726
727
728 void
_BuildDefaultPopUp()729 BDragger::_BuildDefaultPopUp()
730 {
731 fPopUp = new BPopUpMenu("Shelf", false, false, B_ITEMS_IN_COLUMN);
732
733 // About
734 BMessage* msg = new BMessage(B_ABOUT_REQUESTED);
735
736 const char* name = fTarget->Name();
737 if (name != NULL)
738 msg->AddString("target", name);
739
740 BString about(B_TRANSLATE("About %app" B_UTF8_ELLIPSIS));
741 about.ReplaceFirst("%app", name);
742
743 fPopUp->AddItem(new BMenuItem(about.String(), msg));
744 fPopUp->AddSeparatorItem();
745 fPopUp->AddItem(new BMenuItem(B_TRANSLATE("Remove replicant"),
746 new BMessage(kDeleteReplicant)));
747 }
748
749
750 void
_ShowPopUp(BView * target,BPoint where)751 BDragger::_ShowPopUp(BView* target, BPoint where)
752 {
753 BPoint point = ConvertToScreen(where);
754
755 if (fPopUp == NULL && fTarget != NULL)
756 _BuildDefaultPopUp();
757
758 fPopUp->SetTargetForItems(fTarget);
759
760 float menuWidth, menuHeight;
761 fPopUp->GetPreferredSize(&menuWidth, &menuHeight);
762 BRect rect(0, 0, menuWidth, menuHeight);
763 rect.InsetBy(-0.5, -0.5);
764 rect.OffsetTo(point);
765
766 fPopUp->Go(point, true, false, rect, true);
767 }
768
769
770 #if __GNUC__ < 3
771
772 extern "C" BBitmap*
_ReservedDragger1__8BDragger(BDragger * dragger,BPoint * offset,drawing_mode * mode)773 _ReservedDragger1__8BDragger(BDragger* dragger, BPoint* offset,
774 drawing_mode* mode)
775 {
776 return dragger->BDragger::DragBitmap(offset, mode);
777 }
778
779 #endif
780