1596bb689SAlexandre Deckner /*
2596bb689SAlexandre Deckner * Copyright 2011, Alexandre Deckner, alex@zappotek.com
3596bb689SAlexandre Deckner * Distributed under the terms of the MIT License.
4596bb689SAlexandre Deckner */
5596bb689SAlexandre Deckner
6596bb689SAlexandre Deckner /*!
7596bb689SAlexandre Deckner \class LongAndDragTrackingFilter
8596bb689SAlexandre Deckner \brief A simple long mouse down and drag detection filter
9596bb689SAlexandre Deckner *
10596bb689SAlexandre Deckner * A simple mouse filter that detects long clicks and pointer drags.
11596bb689SAlexandre Deckner * A long click message is sent when the mouse button is kept down
12596bb689SAlexandre Deckner * for a duration longer than a given threshold while the pointer stays
13596bb689SAlexandre Deckner * within the limits of a given threshold radius.
14596bb689SAlexandre Deckner * A drag message is triggered if the mouse goes further than the
15596bb689SAlexandre Deckner * threshold radius before the duration threshold elapsed.
16596bb689SAlexandre Deckner *
17596bb689SAlexandre Deckner * The messages contain the pointer position and the buttons state at
18596bb689SAlexandre Deckner * the moment of the click. The drag message is ready to use with the
19596bb689SAlexandre Deckner * be/haiku drag and drop API cf. comment in code.
20596bb689SAlexandre Deckner *
21f2068166SAlexandre Deckner * Current limitation: A long mouse down or a drag can be detected for
22f2068166SAlexandre Deckner * any mouse button, but any released button cancels the tracking.
23596bb689SAlexandre Deckner *
24596bb689SAlexandre Deckner */
25596bb689SAlexandre Deckner
26596bb689SAlexandre Deckner
27596bb689SAlexandre Deckner #include <LongAndDragTrackingFilter.h>
28596bb689SAlexandre Deckner
29596bb689SAlexandre Deckner #include <Message.h>
30596bb689SAlexandre Deckner #include <Messenger.h>
31596bb689SAlexandre Deckner #include <MessageRunner.h>
32596bb689SAlexandre Deckner #include <View.h>
33596bb689SAlexandre Deckner
34596bb689SAlexandre Deckner #include <new>
35596bb689SAlexandre Deckner
36596bb689SAlexandre Deckner
LongAndDragTrackingFilter(uint32 longMessageWhat,uint32 dragMessageWhat,float radiusThreshold,bigtime_t durationThreshold)37596bb689SAlexandre Deckner LongAndDragTrackingFilter::LongAndDragTrackingFilter(uint32 longMessageWhat,
38596bb689SAlexandre Deckner uint32 dragMessageWhat, float radiusThreshold,
39596bb689SAlexandre Deckner bigtime_t durationThreshold)
40596bb689SAlexandre Deckner :
41596bb689SAlexandre Deckner BMessageFilter(B_ANY_DELIVERY, B_ANY_SOURCE),
42596bb689SAlexandre Deckner fLongMessageWhat(longMessageWhat),
43596bb689SAlexandre Deckner fDragMessageWhat(dragMessageWhat),
44596bb689SAlexandre Deckner fMessageRunner(NULL),
45596bb689SAlexandre Deckner fClickButtons(0),
4624b1f931SPhilippe Saint-Pierre fSquaredRadiusThreshold(radiusThreshold * radiusThreshold),
4724b1f931SPhilippe Saint-Pierre fDurationThreshold(durationThreshold)
48596bb689SAlexandre Deckner {
49596bb689SAlexandre Deckner if (durationThreshold == 0) {
50596bb689SAlexandre Deckner get_click_speed(&fDurationThreshold);
51596bb689SAlexandre Deckner // use system's doubleClickSpeed as default threshold
52596bb689SAlexandre Deckner }
53596bb689SAlexandre Deckner }
54596bb689SAlexandre Deckner
55596bb689SAlexandre Deckner
~LongAndDragTrackingFilter()56596bb689SAlexandre Deckner LongAndDragTrackingFilter::~LongAndDragTrackingFilter()
57596bb689SAlexandre Deckner {
58596bb689SAlexandre Deckner delete fMessageRunner;
59596bb689SAlexandre Deckner }
60596bb689SAlexandre Deckner
61596bb689SAlexandre Deckner
62596bb689SAlexandre Deckner void
_StopTracking()63596bb689SAlexandre Deckner LongAndDragTrackingFilter::_StopTracking()
64596bb689SAlexandre Deckner {
65596bb689SAlexandre Deckner delete fMessageRunner;
66596bb689SAlexandre Deckner fMessageRunner = NULL;
67596bb689SAlexandre Deckner }
68596bb689SAlexandre Deckner
69596bb689SAlexandre Deckner
70596bb689SAlexandre Deckner filter_result
Filter(BMessage * message,BHandler ** target)71596bb689SAlexandre Deckner LongAndDragTrackingFilter::Filter(BMessage* message, BHandler** target)
72596bb689SAlexandre Deckner {
73596bb689SAlexandre Deckner if (*target == NULL)
74596bb689SAlexandre Deckner return B_DISPATCH_MESSAGE;
75596bb689SAlexandre Deckner
76596bb689SAlexandre Deckner switch (message->what) {
77*6bff9184SX512 case B_MOUSE_DOWN: {
78*6bff9184SX512 int32 clicks = 0;
79596bb689SAlexandre Deckner
80596bb689SAlexandre Deckner message->FindInt32("buttons", (int32*)&fClickButtons);
81*6bff9184SX512 message->FindInt32("clicks", (int32*)&clicks);
82596bb689SAlexandre Deckner
83*6bff9184SX512 if (fClickButtons != 0 && clicks == 1) {
84596bb689SAlexandre Deckner
85596bb689SAlexandre Deckner BView* targetView = dynamic_cast<BView*>(*target);
86596bb689SAlexandre Deckner if (targetView != NULL)
87596bb689SAlexandre Deckner targetView->SetMouseEventMask(B_POINTER_EVENTS);
88596bb689SAlexandre Deckner
89596bb689SAlexandre Deckner message->FindPoint("where", &fClickPoint);
90596bb689SAlexandre Deckner BMessage message(fLongMessageWhat);
91596bb689SAlexandre Deckner message.AddPoint("where", fClickPoint);
92596bb689SAlexandre Deckner message.AddInt32("buttons", fClickButtons);
93596bb689SAlexandre Deckner
94596bb689SAlexandre Deckner delete fMessageRunner;
95596bb689SAlexandre Deckner fMessageRunner = new (std::nothrow) BMessageRunner(
96596bb689SAlexandre Deckner BMessenger(*target), &message, fDurationThreshold, 1);
97596bb689SAlexandre Deckner }
98596bb689SAlexandre Deckner return B_DISPATCH_MESSAGE;
99*6bff9184SX512 }
100596bb689SAlexandre Deckner
101596bb689SAlexandre Deckner case B_MOUSE_UP:
102596bb689SAlexandre Deckner _StopTracking();
103f2068166SAlexandre Deckner message->AddInt32("last_buttons", (int32)fClickButtons);
104*6bff9184SX512 message->FindInt32("buttons", (int32*)&fClickButtons);
105596bb689SAlexandre Deckner return B_DISPATCH_MESSAGE;
106596bb689SAlexandre Deckner
107596bb689SAlexandre Deckner case B_MOUSE_MOVED:
108596bb689SAlexandre Deckner {
109596bb689SAlexandre Deckner if (fMessageRunner != NULL) {
110596bb689SAlexandre Deckner BPoint where;
111596bb689SAlexandre Deckner message->FindPoint("be:view_where", &where);
112596bb689SAlexandre Deckner
113596bb689SAlexandre Deckner BPoint delta(fClickPoint - where);
114596bb689SAlexandre Deckner float squaredDelta = (delta.x * delta.x) + (delta.y * delta.y);
115596bb689SAlexandre Deckner
116596bb689SAlexandre Deckner if (squaredDelta >= fSquaredRadiusThreshold) {
117596bb689SAlexandre Deckner BMessage dragMessage(fDragMessageWhat);
118596bb689SAlexandre Deckner dragMessage.AddPoint("be:view_where", fClickPoint);
119596bb689SAlexandre Deckner // name it "be:view_where" since BView::DragMessage
120596bb689SAlexandre Deckner // positions the dragging frame/bitmap by retrieving
121596bb689SAlexandre Deckner // the current message and reading that field
122f2068166SAlexandre Deckner dragMessage.AddInt32("buttons", (int32)fClickButtons);
123596bb689SAlexandre Deckner BMessenger messenger(*target);
124596bb689SAlexandre Deckner messenger.SendMessage(&dragMessage);
125596bb689SAlexandre Deckner
126596bb689SAlexandre Deckner _StopTracking();
127596bb689SAlexandre Deckner }
128596bb689SAlexandre Deckner }
129596bb689SAlexandre Deckner return B_DISPATCH_MESSAGE;
130596bb689SAlexandre Deckner }
131596bb689SAlexandre Deckner
132596bb689SAlexandre Deckner default:
133596bb689SAlexandre Deckner if (message->what == fLongMessageWhat) {
134596bb689SAlexandre Deckner _StopTracking();
135596bb689SAlexandre Deckner return B_DISPATCH_MESSAGE;
136596bb689SAlexandre Deckner }
137596bb689SAlexandre Deckner break;
138596bb689SAlexandre Deckner }
139596bb689SAlexandre Deckner
140596bb689SAlexandre Deckner return B_DISPATCH_MESSAGE;
141596bb689SAlexandre Deckner }
142