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