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