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
37LongAndDragTrackingFilter::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
56LongAndDragTrackingFilter::~LongAndDragTrackingFilter()
57{
58	delete fMessageRunner;
59}
60
61
62void
63LongAndDragTrackingFilter::_StopTracking()
64{
65	delete fMessageRunner;
66	fMessageRunner = NULL;
67}
68
69
70filter_result
71LongAndDragTrackingFilter::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