1 /*
2 * Copyright 2011, Alexandre Deckner, alex@zappotek.com
3 * Distributed under the terms of the MIT License.
4 */
5
6 /*!
7 \class LongAndDragTrackingFilter
8 \brief A simple long mouse down and drag detection filter
9 *
10 * A simple mouse filter that detects long clicks and pointer drags.
11 * A long click message is sent when the mouse button is kept down
12 * for a duration longer than a given threshold while the pointer stays
13 * within the limits of a given threshold radius.
14 * A drag message is triggered if the mouse goes further than the
15 * threshold radius before the duration threshold elapsed.
16 *
17 * The messages contain the pointer position and the buttons state at
18 * the moment of the click. The drag message is ready to use with the
19 * be/haiku drag and drop API cf. comment in code.
20 *
21 * Current limitation: A long mouse down or a drag can be detected for
22 * any mouse button, but any released button cancels the tracking.
23 *
24 */
25
26
27 #include <LongAndDragTrackingFilter.h>
28
29 #include <Message.h>
30 #include <Messenger.h>
31 #include <MessageRunner.h>
32 #include <View.h>
33
34 #include <new>
35
36
LongAndDragTrackingFilter(uint32 longMessageWhat,uint32 dragMessageWhat,float radiusThreshold,bigtime_t durationThreshold)37 LongAndDragTrackingFilter::LongAndDragTrackingFilter(uint32 longMessageWhat,
38 uint32 dragMessageWhat, float radiusThreshold,
39 bigtime_t durationThreshold)
40 :
41 BMessageFilter(B_ANY_DELIVERY, B_ANY_SOURCE),
42 fLongMessageWhat(longMessageWhat),
43 fDragMessageWhat(dragMessageWhat),
44 fMessageRunner(NULL),
45 fClickButtons(0),
46 fSquaredRadiusThreshold(radiusThreshold * radiusThreshold),
47 fDurationThreshold(durationThreshold)
48 {
49 if (durationThreshold == 0) {
50 get_click_speed(&fDurationThreshold);
51 // use system's doubleClickSpeed as default threshold
52 }
53 }
54
55
~LongAndDragTrackingFilter()56 LongAndDragTrackingFilter::~LongAndDragTrackingFilter()
57 {
58 delete fMessageRunner;
59 }
60
61
62 void
_StopTracking()63 LongAndDragTrackingFilter::_StopTracking()
64 {
65 delete fMessageRunner;
66 fMessageRunner = NULL;
67 }
68
69
70 filter_result
Filter(BMessage * message,BHandler ** target)71 LongAndDragTrackingFilter::Filter(BMessage* message, BHandler** target)
72 {
73 if (*target == NULL)
74 return B_DISPATCH_MESSAGE;
75
76 switch (message->what) {
77 case B_MOUSE_DOWN: {
78 int32 clicks = 0;
79
80 message->FindInt32("buttons", (int32*)&fClickButtons);
81 message->FindInt32("clicks", (int32*)&clicks);
82
83 if (fClickButtons != 0 && clicks == 1) {
84
85 BView* targetView = dynamic_cast<BView*>(*target);
86 if (targetView != NULL)
87 targetView->SetMouseEventMask(B_POINTER_EVENTS);
88
89 message->FindPoint("where", &fClickPoint);
90 BMessage message(fLongMessageWhat);
91 message.AddPoint("where", fClickPoint);
92 message.AddInt32("buttons", fClickButtons);
93
94 delete fMessageRunner;
95 fMessageRunner = new (std::nothrow) BMessageRunner(
96 BMessenger(*target), &message, fDurationThreshold, 1);
97 }
98 return B_DISPATCH_MESSAGE;
99 }
100
101 case B_MOUSE_UP:
102 _StopTracking();
103 message->AddInt32("last_buttons", (int32)fClickButtons);
104 message->FindInt32("buttons", (int32*)&fClickButtons);
105 return B_DISPATCH_MESSAGE;
106
107 case B_MOUSE_MOVED:
108 {
109 if (fMessageRunner != NULL) {
110 BPoint where;
111 message->FindPoint("be:view_where", &where);
112
113 BPoint delta(fClickPoint - where);
114 float squaredDelta = (delta.x * delta.x) + (delta.y * delta.y);
115
116 if (squaredDelta >= fSquaredRadiusThreshold) {
117 BMessage dragMessage(fDragMessageWhat);
118 dragMessage.AddPoint("be:view_where", fClickPoint);
119 // name it "be:view_where" since BView::DragMessage
120 // positions the dragging frame/bitmap by retrieving
121 // the current message and reading that field
122 dragMessage.AddInt32("buttons", (int32)fClickButtons);
123 BMessenger messenger(*target);
124 messenger.SendMessage(&dragMessage);
125
126 _StopTracking();
127 }
128 }
129 return B_DISPATCH_MESSAGE;
130 }
131
132 default:
133 if (message->what == fLongMessageWhat) {
134 _StopTracking();
135 return B_DISPATCH_MESSAGE;
136 }
137 break;
138 }
139
140 return B_DISPATCH_MESSAGE;
141 }
142