xref: /haiku/src/kits/shared/LongAndDragTrackingFilter.cpp (revision 6bff91840029a45bf3336a2c54bf4898efe7a253)
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