2 * Copyright 2001-2012, Haiku.
3 * Distributed under the terms of the MIT License.
6 * Marc Flerackers (mflerackers@androme.be)
7 * Rene Gollent (rene@gollent.com)
8 * Alexandre Deckner (alex@zappotek.com)
12 //! BDragger represents a replicant "handle".
25 #include <PopUpMenu.h>
27 #include <SystemCatalog.h>
30 #include <AutoLocker.h>
32 #include <AppServerLink.h>
33 #include <DragTrackingFilter.h>
34 #include <binary_compatibility/Interface.h>
35 #include <ServerProtocol.h>
36 #include <ViewPrivate.h>
38 #include "ZombieReplicantView.h"
40 using BPrivate::gSystemCatalog
;
42 #undef B_TRANSLATION_CONTEXT
43 #define B_TRANSLATION_CONTEXT "Dragger"
46 #define B_TRANSLATE(str) \
47 gSystemCatalog.GetString(B_TRANSLATE_MARK(str), "Dragger")
50 static const uint32 kMsgDragStarted
= 'Drgs';
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
66 struct DraggerManager
{
68 bool visibleInitialized
;
74 visibleInitialized(false),
75 fLock("BDragger static")
89 static DraggerManager
* Default()
91 if (sDefaultInstance
== NULL
)
92 pthread_once(&sDefaultInitOnce
, &_InitSingleton
);
94 return sDefaultInstance
;
98 static void _InitSingleton()
100 sDefaultInstance
= new DraggerManager
;
106 static pthread_once_t sDefaultInitOnce
;
107 static DraggerManager
* sDefaultInstance
;
110 pthread_once_t
DraggerManager::sDefaultInitOnce
= PTHREAD_ONCE_INIT
;
111 DraggerManager
* DraggerManager::sDefaultInstance
= NULL
;
113 } // unnamed namespace
116 BDragger::BDragger(BRect frame
, BView
* target
, uint32 resizingMode
,
119 BView(frame
, "_dragger_", resizingMode
, flags
),
121 fRelation(TARGET_UNKNOWN
),
126 fPopUpIsCustom(false),
133 BDragger::BDragger(BView
* target
, uint32 flags
)
135 BView("_dragger_", flags
),
137 fRelation(TARGET_UNKNOWN
),
142 fPopUpIsCustom(false),
149 BDragger::BDragger(BMessage
* data
)
153 fRelation(TARGET_UNKNOWN
),
158 fPopUpIsCustom(false),
161 data
->FindInt32("_rel", (int32
*)&fRelation
);
166 if (data
->FindMessage("_popup", &popupMsg
) == B_OK
) {
167 BArchivable
* archivable
= instantiate_object(&popupMsg
);
170 fPopUp
= dynamic_cast<BPopUpMenu
*>(archivable
);
171 fPopUpIsCustom
= true;
177 BDragger::~BDragger()
185 BDragger::Instantiate(BMessage
* data
)
187 if (validate_instantiation(data
, "BDragger"))
188 return new BDragger(data
);
194 BDragger::Archive(BMessage
* data
, bool deep
) const
196 status_t ret
= BView::Archive(data
, deep
);
202 if (fPopUp
!= NULL
&& fPopUpIsCustom
) {
203 bool windowLocked
= fPopUp
->Window()->Lock();
205 ret
= fPopUp
->Archive(&popupMsg
, deep
);
208 fPopUp
->Window()->Unlock();
209 // TODO: Investigate, in some (rare) occasions the menu window
210 // has already been unlocked
214 ret
= data
->AddMessage("_popup", &popupMsg
);
218 ret
= data
->AddInt32("_rel", fRelation
);
224 BDragger::AttachedToWindow()
227 SetLowColor(kZombieColor
);
228 SetViewColor(kZombieColor
);
230 SetLowColor(B_TRANSPARENT_COLOR
);
231 SetViewColor(B_TRANSPARENT_COLOR
);
234 _DetermineRelationship();
237 AddFilter(new DragTrackingFilter(this, kMsgDragStarted
));
242 BDragger::DetachedFromWindow()
249 BDragger::Draw(BRect update
)
251 BRect
bounds(Bounds());
253 if (AreDraggersDrawn() && (fShelf
== NULL
|| fShelf
->AllowsDragging())) {
254 if (Parent() != NULL
&& (Parent()->Flags() & B_DRAW_ON_CHILDREN
) == 0) {
255 uint32 flags
= Parent()->Flags();
256 Parent()->SetFlags(flags
| B_DRAW_ON_CHILDREN
);
257 SetHighColor(Parent()->ViewColor());
259 Parent()->Draw(Frame() & ConvertToParent(update
));
261 Parent()->SetFlags(flags
);
264 BPoint where
= bounds
.RightBottom() - BPoint(fBitmap
->Bounds().Width(),
265 fBitmap
->Bounds().Height());
266 SetDrawingMode(B_OP_OVER
);
267 DrawBitmap(fBitmap
, where
);
268 SetDrawingMode(B_OP_COPY
);
271 // TODO: should draw it differently ?
273 } else if (IsVisibilityChanging()) {
274 if (Parent() != NULL
) {
275 if ((Parent()->Flags() & B_DRAW_ON_CHILDREN
) == 0) {
276 uint32 flags
= Parent()->Flags();
277 Parent()->SetFlags(flags
| B_DRAW_ON_CHILDREN
);
278 Parent()->Invalidate(Frame() & ConvertToParent(update
));
279 Parent()->SetFlags(flags
);
282 SetHighColor(255, 255, 255);
290 BDragger::MouseDown(BPoint where
)
292 if (fTarget
== NULL
|| !AreDraggersDrawn())
296 Window()->CurrentMessage()->FindInt32("buttons", (int32
*)&buttons
);
298 if (fShelf
!= NULL
&& (buttons
& B_SECONDARY_MOUSE_BUTTON
) != 0)
299 _ShowPopUp(fTarget
, where
);
304 BDragger::MouseUp(BPoint point
)
306 BView::MouseUp(point
);
311 BDragger::MouseMoved(BPoint point
, uint32 code
, const BMessage
* msg
)
313 BView::MouseMoved(point
, code
, msg
);
318 BDragger::MessageReceived(BMessage
* msg
)
323 Window()->PostMessage(kDeleteReplicant
, fTarget
, NULL
);
325 BAlert
* alert
= new BAlert(B_TRANSLATE("Warning"),
326 B_TRANSLATE("Can't delete this replicant from its original "
327 "application. Life goes on."),
328 B_TRANSLATE("OK"), NULL
, NULL
, B_WIDTH_FROM_WIDEST
,
330 alert
->SetFlags(alert
->Flags() | B_CLOSE_ON_ESCAPE
);
335 case _SHOW_DRAG_HANDLES_
:
336 // This code is used whenever the "are draggers drawn" option is
338 if (fRelation
== TARGET_IS_CHILD
) {
344 if ((fShelf
!= NULL
&& fShelf
->AllowsDragging()
345 && AreDraggersDrawn())
346 || AreDraggersDrawn()) {
353 case kMsgDragStarted
:
354 if (fTarget
!= NULL
) {
355 BMessage
archive(B_ARCHIVED_OBJECT
);
357 if (fRelation
== TARGET_IS_PARENT
)
358 fTarget
->Archive(&archive
);
359 else if (fRelation
== TARGET_IS_CHILD
)
361 else if (fTarget
->Archive(&archive
)) {
362 BMessage
archivedSelf(B_ARCHIVED_OBJECT
);
364 if (Archive(&archivedSelf
))
365 archive
.AddMessage("__widget", &archivedSelf
);
368 archive
.AddInt32("be:actions", B_TRASH_TARGET
);
371 BBitmap
* bitmap
= DragBitmap(&offset
, &mode
);
373 DragMessage(&archive
, bitmap
, mode
, offset
, this);
375 DragMessage(&archive
, ConvertFromScreen(
376 fTarget
->ConvertToScreen(fTarget
->Bounds())), this);
382 BView::MessageReceived(msg
);
389 BDragger::FrameMoved(BPoint newPosition
)
391 BView::FrameMoved(newPosition
);
396 BDragger::FrameResized(float newWidth
, float newHeight
)
398 BView::FrameResized(newWidth
, newHeight
);
403 BDragger::ShowAllDraggers()
405 BPrivate::AppServerLink link
;
406 link
.StartMessage(AS_SET_SHOW_ALL_DRAGGERS
);
407 link
.Attach
<bool>(true);
409 status_t status
= link
.Flush();
410 if (status
== B_OK
) {
411 DraggerManager
* manager
= DraggerManager::Default();
412 AutoLocker
<DraggerManager
> locker(manager
);
413 manager
->visible
= true;
414 manager
->visibleInitialized
= true;
422 BDragger::HideAllDraggers()
424 BPrivate::AppServerLink link
;
425 link
.StartMessage(AS_SET_SHOW_ALL_DRAGGERS
);
426 link
.Attach
<bool>(false);
428 status_t status
= link
.Flush();
429 if (status
== B_OK
) {
430 DraggerManager
* manager
= DraggerManager::Default();
431 AutoLocker
<DraggerManager
> locker(manager
);
432 manager
->visible
= false;
433 manager
->visibleInitialized
= true;
441 BDragger::AreDraggersDrawn()
443 DraggerManager
* manager
= DraggerManager::Default();
444 AutoLocker
<DraggerManager
> locker(manager
);
446 if (!manager
->visibleInitialized
) {
447 BPrivate::AppServerLink link
;
448 link
.StartMessage(AS_GET_SHOW_ALL_DRAGGERS
);
451 if (link
.FlushWithReply(status
) == B_OK
&& status
== B_OK
) {
452 link
.Read
<bool>(&manager
->visible
);
453 manager
->visibleInitialized
= true;
458 return manager
->visible
;
463 BDragger::ResolveSpecifier(BMessage
* message
, int32 index
, BMessage
* specifier
,
464 int32 form
, const char* property
)
466 return BView::ResolveSpecifier(message
, index
, specifier
, form
, property
);
471 BDragger::GetSupportedSuites(BMessage
* data
)
473 return BView::GetSupportedSuites(data
);
478 BDragger::Perform(perform_code code
, void* _data
)
481 case PERFORM_CODE_MIN_SIZE
:
482 ((perform_data_min_size
*)_data
)->return_value
483 = BDragger::MinSize();
485 case PERFORM_CODE_MAX_SIZE
:
486 ((perform_data_max_size
*)_data
)->return_value
487 = BDragger::MaxSize();
489 case PERFORM_CODE_PREFERRED_SIZE
:
490 ((perform_data_preferred_size
*)_data
)->return_value
491 = BDragger::PreferredSize();
493 case PERFORM_CODE_LAYOUT_ALIGNMENT
:
494 ((perform_data_layout_alignment
*)_data
)->return_value
495 = BDragger::LayoutAlignment();
497 case PERFORM_CODE_HAS_HEIGHT_FOR_WIDTH
:
498 ((perform_data_has_height_for_width
*)_data
)->return_value
499 = BDragger::HasHeightForWidth();
501 case PERFORM_CODE_GET_HEIGHT_FOR_WIDTH
:
503 perform_data_get_height_for_width
* data
504 = (perform_data_get_height_for_width
*)_data
;
505 BDragger::GetHeightForWidth(data
->width
, &data
->min
, &data
->max
,
509 case PERFORM_CODE_SET_LAYOUT
:
511 perform_data_set_layout
* data
= (perform_data_set_layout
*)_data
;
512 BDragger::SetLayout(data
->layout
);
515 case PERFORM_CODE_LAYOUT_INVALIDATED
:
517 perform_data_layout_invalidated
* data
518 = (perform_data_layout_invalidated
*)_data
;
519 BDragger::LayoutInvalidated(data
->descendants
);
522 case PERFORM_CODE_DO_LAYOUT
:
524 BDragger::DoLayout();
529 return BView::Perform(code
, _data
);
534 BDragger::ResizeToPreferred()
536 BView::ResizeToPreferred();
541 BDragger::GetPreferredSize(float* _width
, float* _height
)
543 BView::GetPreferredSize(_width
, _height
);
548 BDragger::MakeFocus(bool state
)
550 BView::MakeFocus(state
);
555 BDragger::AllAttached()
557 BView::AllAttached();
562 BDragger::AllDetached()
564 BView::AllDetached();
569 BDragger::SetPopUp(BPopUpMenu
* menu
)
571 if (menu
!= NULL
&& menu
!= fPopUp
) {
574 fPopUpIsCustom
= true;
582 BDragger::PopUp() const
584 if (fPopUp
== NULL
&& fTarget
)
585 const_cast<BDragger
*>(this)->_BuildDefaultPopUp();
592 BDragger::InShelf() const
594 return fShelf
!= NULL
;
599 BDragger::Target() const
606 BDragger::DragBitmap(BPoint
* offset
, drawing_mode
* mode
)
613 BDragger::IsVisibilityChanging() const
619 void BDragger::_ReservedDragger2() {}
620 void BDragger::_ReservedDragger3() {}
621 void BDragger::_ReservedDragger4() {}
625 BDragger::operator=(const BDragger
&)
632 BDragger::_UpdateShowAllDraggers(bool visible
)
634 DraggerManager
* manager
= DraggerManager::Default();
635 AutoLocker
<DraggerManager
> locker(manager
);
637 manager
->visibleInitialized
= true;
638 manager
->visible
= visible
;
640 for (int32 i
= manager
->list
.CountItems(); i
-- > 0;) {
641 BDragger
* dragger
= (BDragger
*)manager
->list
.ItemAt(i
);
642 BMessenger
target(dragger
);
643 target
.SendMessage(_SHOW_DRAG_HANDLES_
);
649 BDragger::_InitData()
651 fBitmap
= new BBitmap(BRect(0.0f
, 0.0f
, 7.0f
, 7.0f
), B_CMAP8
, false, false);
652 fBitmap
->SetBits(kHandBitmap
, fBitmap
->BitsLength(), 0, B_CMAP8
);
657 BDragger::_AddToList()
659 DraggerManager
* manager
= DraggerManager::Default();
660 AutoLocker
<DraggerManager
> locker(manager
);
661 manager
->list
.AddItem(this);
663 bool allowsDragging
= true;
665 allowsDragging
= fShelf
->AllowsDragging();
667 if (!AreDraggersDrawn() || !allowsDragging
) {
668 // The dragger is not shown - but we can't hide us in case we're the
669 // parent of the actual target view (because then you couldn't see
671 if (fRelation
!= TARGET_IS_CHILD
&& !IsHidden())
678 BDragger::_RemoveFromList()
680 DraggerManager
* manager
= DraggerManager::Default();
681 AutoLocker
<DraggerManager
> locker(manager
);
682 manager
->list
.RemoveItem(this);
687 BDragger::_DetermineRelationship()
689 if (fTarget
!= NULL
) {
690 if (fTarget
== Parent())
691 fRelation
= TARGET_IS_PARENT
;
692 else if (fTarget
== ChildAt(0))
693 fRelation
= TARGET_IS_CHILD
;
695 fRelation
= TARGET_IS_SIBLING
;
697 if (fRelation
== TARGET_IS_PARENT
)
699 else if (fRelation
== TARGET_IS_CHILD
)
700 fTarget
= ChildAt(0);
705 if (fRelation
== TARGET_IS_PARENT
) {
706 BRect
bounds(Frame());
707 BRect
parentBounds(Parent()->Bounds());
708 if (!parentBounds
.Contains(bounds
)) {
709 MoveTo(parentBounds
.right
- bounds
.Width(),
710 parentBounds
.bottom
- bounds
.Height());
719 BDragger::_SetViewToDrag(BView
* target
)
721 if (target
->Window() != Window())
726 if (Window() != NULL
)
727 _DetermineRelationship();
734 BDragger::_SetShelf(BShelf
* shelf
)
741 BDragger::_SetZombied(bool state
)
746 SetLowColor(kZombieColor
);
747 SetViewColor(kZombieColor
);
753 BDragger::_BuildDefaultPopUp()
755 fPopUp
= new BPopUpMenu("Shelf", false, false, B_ITEMS_IN_COLUMN
);
758 BMessage
* msg
= new BMessage(B_ABOUT_REQUESTED
);
760 const char* name
= fTarget
->Name();
762 msg
->AddString("target", name
);
764 BString
about(B_TRANSLATE("About %app" B_UTF8_ELLIPSIS
));
765 about
.ReplaceFirst("%app", name
);
767 fPopUp
->AddItem(new BMenuItem(about
.String(), msg
));
768 fPopUp
->AddSeparatorItem();
769 fPopUp
->AddItem(new BMenuItem(B_TRANSLATE("Remove replicant"),
770 new BMessage(kDeleteReplicant
)));
775 BDragger::_ShowPopUp(BView
* target
, BPoint where
)
777 BPoint point
= ConvertToScreen(where
);
779 if (fPopUp
== NULL
&& fTarget
!= NULL
)
780 _BuildDefaultPopUp();
782 fPopUp
->SetTargetForItems(fTarget
);
784 float menuWidth
, menuHeight
;
785 fPopUp
->GetPreferredSize(&menuWidth
, &menuHeight
);
786 BRect
rect(0, 0, menuWidth
, menuHeight
);
787 rect
.InsetBy(-0.5, -0.5);
788 rect
.OffsetTo(point
);
790 fPopUp
->Go(point
, true, false, rect
, true);
797 _ReservedDragger1__8BDragger(BDragger
* dragger
, BPoint
* offset
,
800 return dragger
->BDragger::DragBitmap(offset
, mode
);