xref: /haiku/src/kits/shared/LongAndDragTrackingFilter.cpp (revision 644fa5a93845dc4a1bc155f1fd0f94ebdf0b47bc)
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 {
48 	if (durationThreshold == 0) {
49 		get_click_speed(&fDurationThreshold);
50 			// use system's doubleClickSpeed as default threshold
51 	}
52 }
53 
54 
55 LongAndDragTrackingFilter::~LongAndDragTrackingFilter()
56 {
57 	delete fMessageRunner;
58 }
59 
60 
61 void
62 LongAndDragTrackingFilter::_StopTracking()
63 {
64 	delete fMessageRunner;
65 	fMessageRunner = NULL;
66 }
67 
68 
69 filter_result
70 LongAndDragTrackingFilter::Filter(BMessage* message, BHandler** target)
71 {
72 	if (*target == NULL)
73 		return B_DISPATCH_MESSAGE;
74 
75 	switch (message->what) {
76 		case B_MOUSE_DOWN:
77 
78 			message->FindInt32("buttons", (int32*)&fClickButtons);
79 
80 			if (fClickButtons != 0) {
81 
82 				BView* targetView = dynamic_cast<BView*>(*target);
83 				if (targetView != NULL)
84 					targetView->SetMouseEventMask(B_POINTER_EVENTS);
85 
86 				message->FindPoint("where", &fClickPoint);
87 				BMessage message(fLongMessageWhat);
88 				message.AddPoint("where", fClickPoint);
89 				message.AddInt32("buttons", fClickButtons);
90 
91 				delete fMessageRunner;
92 				fMessageRunner = new (std::nothrow) BMessageRunner(
93 					BMessenger(*target), &message, fDurationThreshold, 1);
94 			}
95 			return B_DISPATCH_MESSAGE;
96 
97 		case B_MOUSE_UP:
98 			_StopTracking();
99 			message->AddInt32("last_buttons", (int32)fClickButtons);
100 			return B_DISPATCH_MESSAGE;
101 
102 		case B_MOUSE_MOVED:
103 		{
104 			if (fMessageRunner != NULL) {
105 				BPoint where;
106 				message->FindPoint("be:view_where", &where);
107 
108 				BPoint delta(fClickPoint - where);
109 				float squaredDelta = (delta.x * delta.x) + (delta.y * delta.y);
110 
111 				if (squaredDelta >= fSquaredRadiusThreshold) {
112 					BMessage dragMessage(fDragMessageWhat);
113 					dragMessage.AddPoint("be:view_where", fClickPoint);
114 						// name it "be:view_where" since BView::DragMessage
115 						// positions the dragging frame/bitmap by retrieving
116 						// the current message and reading that field
117 					dragMessage.AddInt32("buttons", (int32)fClickButtons);
118 					BMessenger messenger(*target);
119 					messenger.SendMessage(&dragMessage);
120 
121 					_StopTracking();
122 				}
123 			}
124 			return B_DISPATCH_MESSAGE;
125 		}
126 
127 		default:
128 			if (message->what == fLongMessageWhat) {
129 				_StopTracking();
130 				return B_DISPATCH_MESSAGE;
131 			}
132 			break;
133 	}
134 
135 	return B_DISPATCH_MESSAGE;
136 }
137