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 79 message->FindInt32("buttons", (int32*)&fClickButtons); 80 81 if (fClickButtons != 0) { 82 83 BView* targetView = dynamic_cast<BView*>(*target); 84 if (targetView != NULL) 85 targetView->SetMouseEventMask(B_POINTER_EVENTS); 86 87 message->FindPoint("where", &fClickPoint); 88 BMessage message(fLongMessageWhat); 89 message.AddPoint("where", fClickPoint); 90 message.AddInt32("buttons", fClickButtons); 91 92 delete fMessageRunner; 93 fMessageRunner = new (std::nothrow) BMessageRunner( 94 BMessenger(*target), &message, fDurationThreshold, 1); 95 } 96 return B_DISPATCH_MESSAGE; 97 98 case B_MOUSE_UP: 99 _StopTracking(); 100 message->AddInt32("last_buttons", (int32)fClickButtons); 101 return B_DISPATCH_MESSAGE; 102 103 case B_MOUSE_MOVED: 104 { 105 if (fMessageRunner != NULL) { 106 BPoint where; 107 message->FindPoint("be:view_where", &where); 108 109 BPoint delta(fClickPoint - where); 110 float squaredDelta = (delta.x * delta.x) + (delta.y * delta.y); 111 112 if (squaredDelta >= fSquaredRadiusThreshold) { 113 BMessage dragMessage(fDragMessageWhat); 114 dragMessage.AddPoint("be:view_where", fClickPoint); 115 // name it "be:view_where" since BView::DragMessage 116 // positions the dragging frame/bitmap by retrieving 117 // the current message and reading that field 118 dragMessage.AddInt32("buttons", (int32)fClickButtons); 119 BMessenger messenger(*target); 120 messenger.SendMessage(&dragMessage); 121 122 _StopTracking(); 123 } 124 } 125 return B_DISPATCH_MESSAGE; 126 } 127 128 default: 129 if (message->what == fLongMessageWhat) { 130 _StopTracking(); 131 return B_DISPATCH_MESSAGE; 132 } 133 break; 134 } 135 136 return B_DISPATCH_MESSAGE; 137 } 138