1/*
2 * Copyright 2009, Alexandre Deckner, alex@zappotek.com
3 * Distributed under the terms of the MIT License.
4 */
5
6/*!
7	\class DragTrackingFilter
8	\brief A simple mouse drag detection filter
9	*
10	* A simple mouse filter that detects the start of a mouse drag over a
11	* threshold distance and sends a message with the 'what' field of your
12	* choice. Especially useful for drag and drop.
13	* Allows you to free your code of encumbering mouse tracking details.
14	*
15	* It can detect fast drags spanning outside of a small view by temporarily
16	* setting the B_POINTER_EVENTS flag on the view.
17*/
18
19#include <DragTrackingFilter.h>
20
21#include <Message.h>
22#include <Messenger.h>
23#include <View.h>
24
25static const int kSquaredDragThreshold = 9;
26
27DragTrackingFilter::DragTrackingFilter(BView* targetView, uint32 messageWhat)
28	: BMessageFilter(B_ANY_DELIVERY, B_ANY_SOURCE),
29	fTargetView(targetView),
30	fMessageWhat(messageWhat),
31	fIsTracking(false),
32	fClickButtons(0)
33{
34}
35
36
37filter_result
38DragTrackingFilter::Filter(BMessage* message, BHandler** /*_target*/)
39{
40	if (fTargetView == NULL)
41		return B_DISPATCH_MESSAGE;
42
43	switch (message->what) {
44		case B_MOUSE_DOWN:
45			message->FindPoint("where", &fClickPoint);
46			message->FindInt32("buttons", (int32*)&fClickButtons);
47			fIsTracking = true;
48
49			fTargetView->SetMouseEventMask(B_POINTER_EVENTS);
50
51			return B_DISPATCH_MESSAGE;
52
53		case B_MOUSE_UP:
54			fIsTracking = false;
55			return B_DISPATCH_MESSAGE;
56
57		case B_MOUSE_MOVED:
58		{
59			BPoint where;
60			message->FindPoint("be:view_where", &where);
61
62			// TODO: be more flexible about buttons and pass their state
63			//		 in the message
64			if (fIsTracking && (fClickButtons & B_PRIMARY_MOUSE_BUTTON)) {
65
66				BPoint delta(fClickPoint - where);
67				float squaredDelta = (delta.x * delta.x) + (delta.y * delta.y);
68
69				if (squaredDelta >= kSquaredDragThreshold) {
70					BMessage dragClickMessage(fMessageWhat);
71					dragClickMessage.AddPoint("be:view_where", fClickPoint);
72						// name it "be:view_where" since BView::DragMessage
73						// positions the dragging frame/bitmap by retrieving the
74						// current message and reading that field
75					BMessenger messenger(fTargetView);
76					messenger.SendMessage(&dragClickMessage);
77
78					fIsTracking = false;
79				}
80			}
81			return B_DISPATCH_MESSAGE;
82		}
83		default:
84			break;
85	}
86
87	return B_DISPATCH_MESSAGE;
88}
89