xref: /haiku/src/kits/shared/LongAndDragTrackingFilter.cpp (revision c237c4ce593ee823d9867fd997e51e4c447f5623)
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 
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 
56 LongAndDragTrackingFilter::~LongAndDragTrackingFilter()
57 {
58 	delete fMessageRunner;
59 }
60 
61 
62 void
63 LongAndDragTrackingFilter::_StopTracking()
64 {
65 	delete fMessageRunner;
66 	fMessageRunner = NULL;
67 }
68 
69 
70 filter_result
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