Author: aldeck Date: 2011-06-03 23:12:14 +0200 (Fri, 03 Jun 2011) New Revision: 41892 Changeset: https://dev.haiku-os.org/changeset/41892 Ticket: https://dev.haiku-os.org/ticket/880 Ticket: https://dev.haiku-os.org/ticket/7241 Added: haiku/trunk/headers/private/shared/LongAndDragTrackingFilter.h haiku/trunk/src/kits/shared/LongAndDragTrackingFilter.cpp Modified: haiku/trunk/src/kits/shared/Jamfile haiku/trunk/src/kits/tracker/PoseView.cpp haiku/trunk/src/kits/tracker/PoseView.h Log: * Finally rewrote the filthy, busy-looping, mouse tracking in Tracker. The new asynchronous tracking doesn't lock the view and makes desktop replicants happy while clicking and dragging around. Fixes #880, #7241 and certainly other tickets, that i will revisit, about refresh locks on the desktop or mouse related bugs. Based on a reusable MessageFilter and the recent B_MOUSE_IDLE message. Some parts are ported from the old code and could use further simplification. There should be no intentional user fonctional change, except: - slightly bigger threshold radius, shorter duration thresholds (= 1.0 x the system-wide doubleclick setting) - not reimplemented: quickening threshold when holding shift while dragging a pose over a destination pose. Some parts are ported from the old code and could use further simplification. There is one known minor regression with autoscrolling while rect-selecting but that will be better fixed with some changes to the autoscroll code. Will address that ASAP. As for the unknown regressions, please test :) Added: haiku/trunk/headers/private/shared/LongAndDragTrackingFilter.h =================================================================== --- haiku/trunk/headers/private/shared/LongAndDragTrackingFilter.h (rev 0) +++ haiku/trunk/headers/private/shared/LongAndDragTrackingFilter.h 2011-06-03 21:12:14 UTC (rev 41892) @@ -0,0 +1,47 @@ +/* + * Copyright 2009, Alexandre Deckner, alex@xxxxxxxxxxxx + * Distributed under the terms of the MIT License. + */ +#ifndef LONG_AND_DRAG_TRACKING_FILTER_H +#define LONG_AND_DRAG_TRACKING_FILTER_H + + +#include <MessageFilter.h> +#include <Point.h> + + +class BHandler; +class BMessageRunner; + + +namespace BPrivate { + +class LongAndDragTrackingFilter : public BMessageFilter { +public: + LongAndDragTrackingFilter( + uint32 longMessageWhat, + uint32 dragMessageWhat, + float radiusThreshold = 4.0f, + bigtime_t durationThreshold = 0); + ~LongAndDragTrackingFilter(); + + filter_result Filter(BMessage* message, BHandler** target); + +private: + void _StopTracking(); + + uint32 fLongMessageWhat; + uint32 fDragMessageWhat; + BMessageRunner* fMessageRunner; + BPoint fClickPoint; + uint32 fClickButtons; + float fSquaredRadiusThreshold; + bigtime_t fDurationThreshold; +}; + +} // namespace BPrivate + +using BPrivate::LongAndDragTrackingFilter; + +#endif // LONG_AND_DRAG_TRACKING_FILTER_H + Modified: haiku/trunk/src/kits/shared/Jamfile =================================================================== --- haiku/trunk/src/kits/shared/Jamfile 2011-06-03 21:11:38 UTC (rev 41891) +++ haiku/trunk/src/kits/shared/Jamfile 2011-06-03 21:12:14 UTC (rev 41892) @@ -23,6 +23,7 @@ IconButton.cpp IconView.cpp Keymap.cpp + LongAndDragTrackingFilter.cpp NaturalCompare.cpp QueryFile.cpp RWLockManager.cpp Added: haiku/trunk/src/kits/shared/LongAndDragTrackingFilter.cpp =================================================================== --- haiku/trunk/src/kits/shared/LongAndDragTrackingFilter.cpp (rev 0) +++ haiku/trunk/src/kits/shared/LongAndDragTrackingFilter.cpp 2011-06-03 21:12:14 UTC (rev 41892) @@ -0,0 +1,136 @@ +/* + * Copyright 2011, Alexandre Deckner, alex@xxxxxxxxxxxx + * Distributed under the terms of the MIT License. + */ + +/*! + \class LongAndDragTrackingFilter + \brief A simple long mouse down and drag detection filter + * + * A simple mouse filter that detects long clicks and pointer drags. + * A long click message is sent when the mouse button is kept down + * for a duration longer than a given threshold while the pointer stays + * within the limits of a given threshold radius. + * A drag message is triggered if the mouse goes further than the + * threshold radius before the duration threshold elapsed. + * + * The messages contain the pointer position and the buttons state at + * the moment of the click. The drag message is ready to use with the + * be/haiku drag and drop API cf. comment in code. + * + * Note: for simplicity and current needs (Tracker), only the left mouse + * button is tracked. + * +*/ + + +#include <LongAndDragTrackingFilter.h> + +#include <Message.h> +#include <Messenger.h> +#include <MessageRunner.h> +#include <View.h> + +#include <new> + + +LongAndDragTrackingFilter::LongAndDragTrackingFilter(uint32 longMessageWhat, + uint32 dragMessageWhat, float radiusThreshold, + bigtime_t durationThreshold) + : + BMessageFilter(B_ANY_DELIVERY, B_ANY_SOURCE), + fLongMessageWhat(longMessageWhat), + fDragMessageWhat(dragMessageWhat), + fMessageRunner(NULL), + fClickButtons(0), + fSquaredRadiusThreshold(radiusThreshold * radiusThreshold) +{ + if (durationThreshold == 0) { + get_click_speed(&fDurationThreshold); + // use system's doubleClickSpeed as default threshold + } +} + + +LongAndDragTrackingFilter::~LongAndDragTrackingFilter() +{ + delete fMessageRunner; +} + + +void +LongAndDragTrackingFilter::_StopTracking() +{ + delete fMessageRunner; + fMessageRunner = NULL; +} + + +filter_result +LongAndDragTrackingFilter::Filter(BMessage* message, BHandler** target) +{ + if (*target == NULL) + return B_DISPATCH_MESSAGE; + + switch (message->what) { + case B_MOUSE_DOWN: + + message->FindInt32("buttons", (int32*)&fClickButtons); + + if ((fClickButtons & B_PRIMARY_MOUSE_BUTTON) + == B_PRIMARY_MOUSE_BUTTON) { + + BView* targetView = dynamic_cast<BView*>(*target); + if (targetView != NULL) + targetView->SetMouseEventMask(B_POINTER_EVENTS); + + message->FindPoint("where", &fClickPoint); + BMessage message(fLongMessageWhat); + message.AddPoint("where", fClickPoint); + message.AddInt32("buttons", fClickButtons); + + delete fMessageRunner; + fMessageRunner = new (std::nothrow) BMessageRunner( + BMessenger(*target), &message, fDurationThreshold, 1); + } + return B_DISPATCH_MESSAGE; + + case B_MOUSE_UP: + _StopTracking(); + return B_DISPATCH_MESSAGE; + + case B_MOUSE_MOVED: + { + if (fMessageRunner != NULL) { + BPoint where; + message->FindPoint("be:view_where", &where); + + BPoint delta(fClickPoint - where); + float squaredDelta = (delta.x * delta.x) + (delta.y * delta.y); + + if (squaredDelta >= fSquaredRadiusThreshold) { + BMessage dragMessage(fDragMessageWhat); + dragMessage.AddPoint("be:view_where", fClickPoint); + // name it "be:view_where" since BView::DragMessage + // positions the dragging frame/bitmap by retrieving + // the current message and reading that field + dragMessage.AddInt32("buttons", fClickButtons); + BMessenger messenger(*target); + messenger.SendMessage(&dragMessage); + + _StopTracking(); + } + } + return B_DISPATCH_MESSAGE; + } + + default: + if (message->what == fLongMessageWhat) { + _StopTracking(); + return B_DISPATCH_MESSAGE; + } + break; + } + + return B_DISPATCH_MESSAGE; +} Modified: haiku/trunk/src/kits/tracker/PoseView.cpp =================================================================== --- haiku/trunk/src/kits/tracker/PoseView.cpp 2011-06-03 21:11:38 UTC (rev 41891) +++ haiku/trunk/src/kits/tracker/PoseView.cpp 2011-06-03 21:12:14 UTC (rev 41892) @@ -58,6 +58,7 @@ #include <Query.h> #include <List.h> #include <Locale.h> +#include <LongAndDragTrackingFilter.h> #include <MenuItem.h> #include <NodeMonitor.h> #include <Path.h> @@ -113,6 +114,8 @@ const uint32 kAddNewPoses = 'Tanp'; const uint32 kAddPosesCompleted = 'Tapc'; const int32 kMaxAddPosesChunk = 50; +const uint32 kMsgMouseDragged = 'Mdrg'; +const uint32 kMsgMouseLongDown = 'Mold'; namespace BPrivate { @@ -922,6 +925,8 @@ AddFilter(new ShortcutFilter(B_ESCAPE, B_SHIFT_KEY, kCancelSelectionToClipboard, this)); // Escape + SHIFT will remove current selection from clipboard, or all poses from current folder if 0 selected + AddFilter(new LongAndDragTrackingFilter(kMsgMouseLongDown, kMsgMouseDragged)); + fLastLeftTop = LeftTop(); BFont font(be_plain_font); font.SetSpacing(B_BITMAP_SPACING); @@ -2154,6 +2159,18 @@ case kMiniIconMode: SetViewMode(message->what); break; + + case kMsgMouseDragged: + MouseDragged(message); + break; + + case kMsgMouseLongDown: + MouseLongDown(message); + break; + + case B_MOUSE_IDLE: + MouseIdle(message); + break; case B_SELECT_ALL: { @@ -6545,10 +6562,237 @@ void -BPoseView::MouseDown(BPoint where) +BPoseView::_BeginSelectionRect(const BPoint& point, bool shouldExtend) { - // TODO: add asynch mouse tracking + // set initial empty selection rectangle + fSelectionRectInfo.rect = BRect(point, point - BPoint(1, 1)); + if (!fTransparentSelection) { + SetDrawingMode(B_OP_INVERT); + StrokeRect(fSelectionRectInfo.rect, B_MIXED_COLORS); + SetDrawingMode(B_OP_OVER); + } + + fSelectionRectInfo.lastRect = fSelectionRectInfo.rect; + fSelectionRectInfo.selection = new BList; + fSelectionRectInfo.startPoint = point; + fSelectionRectInfo.lastPoint = point; + fSelectionRectInfo.isDragging = true; +} + + +static void +AddIfPoseSelected(BPose *pose, PoseList *list) +{ + if (pose->IsSelected()) + list->AddItem(pose); +} + + +void +BPoseView::_UpdateSelectionRect(const BPoint& point) +{ + if (point != fSelectionRectInfo.lastPoint) { + + fSelectionRectInfo.lastPoint = point; + + // erase last rect + if (!fTransparentSelection) { + SetDrawingMode(B_OP_INVERT); + StrokeRect(fSelectionRectInfo.rect, B_MIXED_COLORS); + SetDrawingMode(B_OP_OVER); + } + + fSelectionRectInfo.rect.top = std::min(point.y, + fSelectionRectInfo.startPoint.y); + fSelectionRectInfo.rect.left = std::min(point.x, + fSelectionRectInfo.startPoint.x); + fSelectionRectInfo.rect.bottom = std::max(point.y, + fSelectionRectInfo.startPoint.y); + fSelectionRectInfo.rect.right = std::max(point.x, + fSelectionRectInfo.startPoint.x); + + fIsDrawingSelectionRect = true; + + CheckAutoScroll(point, true, true); + + // use current selection rectangle to scan poses + if (ViewMode() == kListMode) { + SelectPosesListMode(fSelectionRectInfo.rect, + &fSelectionRectInfo.selection); + } else { + SelectPosesIconMode(fSelectionRectInfo.rect, + &fSelectionRectInfo.selection); + } + + Window()->UpdateIfNeeded(); + + // draw new rect + if (!fTransparentSelection) { + SetDrawingMode(B_OP_INVERT); + StrokeRect(fSelectionRectInfo.rect, B_MIXED_COLORS); + SetDrawingMode(B_OP_OVER); + } else { + BRegion updateRegion1; + BRegion updateRegion2; + + bool sameWidth = fSelectionRectInfo.rect.Width() + == fSelectionRectInfo.lastRect.Width(); + bool sameHeight = fSelectionRectInfo.rect.Height() + == fSelectionRectInfo.lastRect.Height(); + + updateRegion1.Include(fSelectionRectInfo.rect); + updateRegion1.Exclude(fSelectionRectInfo.lastRect.InsetByCopy( + sameWidth ? 0 : 1, sameHeight ? 0 : 1)); + updateRegion2.Include(fSelectionRectInfo.lastRect); + updateRegion2.Exclude(fSelectionRectInfo.rect.InsetByCopy( + sameWidth ? 0 : 1, sameHeight ? 0 : 1)); + updateRegion1.Include(&updateRegion2); + BRect unionRect = fSelectionRectInfo.rect + & fSelectionRectInfo.lastRect; + updateRegion1.Exclude(unionRect + & BRect(-2000, fSelectionRectInfo.startPoint.y, 2000, + fSelectionRectInfo.startPoint.y)); + updateRegion1.Exclude(unionRect + & BRect(fSelectionRectInfo.startPoint.x, -2000, + fSelectionRectInfo.startPoint.x, 2000)); + + fSelectionRectInfo.lastRect = fSelectionRectInfo.rect; + + Invalidate(&updateRegion1); + Window()->UpdateIfNeeded(); + } + Flush(); + } +} + + +void +BPoseView::_EndSelectionRect() +{ + delete fSelectionRectInfo.selection; + fSelectionRectInfo.selection = NULL; + + fSelectionRectInfo.isDragging = false; + fIsDrawingSelectionRect = false; // TODO: remove BPose dependency? + + // do final erase of selection rect + if (!fTransparentSelection) { + SetDrawingMode(B_OP_INVERT); + StrokeRect(fSelectionRectInfo.rect, B_MIXED_COLORS); + SetDrawingMode(B_OP_COPY); + } else { + Invalidate(fSelectionRectInfo.rect); + fSelectionRectInfo.rect.Set(0, 0, -1, -1); + Window()->UpdateIfNeeded(); + } + + // we now need to update the pose view's selection list by clearing it + // and then polling each pose for selection state and rebuilding list + fSelectionList->MakeEmpty(); + fMimeTypesInSelectionCache.MakeEmpty(); + + EachListItem(fPoseList, AddIfPoseSelected, fSelectionList); + + // and now make sure that the pivot point is in sync + if (fSelectionPivotPose && !fSelectionList->HasItem(fSelectionPivotPose)) + fSelectionPivotPose = NULL; + if (fRealPivotPose && !fSelectionList->HasItem(fRealPivotPose)) + fRealPivotPose = NULL; +} + + +void +BPoseView::MouseMoved(BPoint mouseLoc, uint32 moveCode, const BMessage *message) +{ + if (fSelectionRectInfo.isDragging) + _UpdateSelectionRect(mouseLoc); + + if (!fDropEnabled || !message) + return; + + BContainerWindow* window = ContainerWindow(); + if (!window) + return; + + switch (moveCode) { + case B_INSIDE_VIEW: + case B_ENTERED_VIEW: + UpdateDropTarget(mouseLoc, message, window->ContextMenu()); + if (fAutoScrollState == kAutoScrollOff) { + // turn on auto scrolling if it's not yet on + fAutoScrollState = kWaitForTransition; + window->SetPulseRate(100000); + } + break; + + case B_EXITED_VIEW: + // reset cursor in case we set it to the copy cursor + // in UpdateDropTarget + SetViewCursor(B_CURSOR_SYSTEM_DEFAULT); + fCursorCheck = false; + // TODO: autoscroll here + if (!window->ContextMenu()) { + HiliteDropTarget(false); + fDropTarget = NULL; + } + break; + } +} + + +void +BPoseView::MouseDragged(const BMessage *message) +{ + BPoint where; + if (message->FindPoint("be:view_where", &where) != B_OK) + return; + + bool extendSelection = (modifiers() & B_COMMAND_KEY) && fMultipleSelection; + + int32 index; + BPose* pose = FindPose(where, &index); + if (pose != NULL) + DragSelectedPoses(pose, where); + else + _BeginSelectionRect(where, extendSelection); +} + + +void +BPoseView::MouseLongDown(const BMessage *message) +{ + BPoint where; + if (message->FindPoint("where", &where) != B_OK) + return; + + ShowContextMenu(where); +} + + +void +BPoseView::MouseIdle(const BMessage *message) +{ + BPoint where; + uint32 buttons = 0; + GetMouse(&where, &buttons); + BContainerWindow* window = ContainerWindow(); + + if (buttons == 0 || window == NULL) + return; + + if (fDropTarget != NULL) { + window->DragStart(message); + FrameForPose(fDropTarget, true, &fStartFrame); + ShowContextMenu(where); + } else + window->Activate(); +} + + +void +BPoseView::MouseDown(BPoint where) +{ // handle disposing of drag data lazily DragStop(); BContainerWindow *window = ContainerWindow(); @@ -6572,7 +6816,6 @@ if ((buttons & B_SECONDARY_MOUSE_BUTTON) == 0) showContext = (modifs & B_CONTROL_KEY) != 0; - // if a pose was hit, delay context menu for a bit to see if user dragged if (showContext) { int32 index; BPose *pose = FindPose(where, &index); @@ -6586,24 +6829,7 @@ fSelectionList->AddItem(pose); DrawPose(pose, index, false); } - - bigtime_t clickTime = system_time(); - BPoint loc; - GetMouse(&loc, &buttons); - for (;;) { - if (fabs(loc.x - where.x) > 4 || fabs(loc.y - where.y) > 4) - // moved the mouse, cancel showing the context menu - break; - - if (!buttons || (system_time() - clickTime) > 200000) { - // let go of button or pressing for a while, show menu now - ShowContextMenu(where); - return; - } - - snooze(10000); - GetMouse(&loc, &buttons); - } + ShowContextMenu(where); } bool extendSelection = (modifs & B_COMMAND_KEY) && fMultipleSelection; @@ -6616,26 +6842,10 @@ if (pose) { AddRemoveSelectionRange(where, extendSelection, pose); - switch (WaitForMouseUpOrDrag(where)) { - case kWasDragged: - DragSelectedPoses(pose, where); - break; - - case kNotDragged: - if (!extendSelection && WasDoubleClick(pose, where)) { - // special handling for Path field double-clicks - if (!WasClickInPath(pose, index, where)) - OpenSelection(pose, &index); - - } else if (fAllowPoseEditing) - // mouse is up but no drag or double-click occurred - pose->MouseUp(BPoint(0, index * fListElemHeight), this, where, index); - - break; - - default: - // this is the CONTEXT_MENU case - break; + if (!extendSelection && WasDoubleClick(pose, where)) { + // special handling for Path field double-clicks + if (!WasClickInPath(pose, index, where)) + OpenSelection(pose, &index); } } else { // click was not in any pose @@ -6643,7 +6853,10 @@ window->Activate(); window->UpdateIfNeeded(); - DragSelectionRect(where, extendSelection); + + // only clear selection if we are not extending it + if (!extendSelection || !fSelectionRectEnabled || !fMultipleSelection) + ClearSelection(); } if (fSelectionChangedHook) @@ -6651,6 +6864,19 @@ } +void +BPoseView::MouseUp(BPoint where) +{ + if (fSelectionRectInfo.isDragging) + _EndSelectionRect(); + + int32 index; + BPose* pose = FindPose(where, &index); + if (pose != NULL && fAllowPoseEditing) + pose->MouseUp(BPoint(0, index * fListElemHeight), this, where, index); +} + + bool BPoseView::WasClickInPath(const BPose *pose, int32 index, BPoint mouseLoc) const { @@ -6971,149 +7197,8 @@ } -static void -AddIfPoseSelected(BPose *pose, PoseList *list) -{ - if (pose->IsSelected()) - list->AddItem(pose); -} - - -void -BPoseView::DragSelectionRect(BPoint startPoint, bool shouldExtend) -{ - // only clear selection if we are not extending it - if (!shouldExtend) - ClearSelection(); - - if (WaitForMouseUpOrDrag(startPoint) != kWasDragged) { - if (!shouldExtend) - ClearSelection(); - return; - } - - if (!fSelectionRectEnabled || !fMultipleSelection) { - ClearSelection(); - return; - } - - // clearing the selection could take a while so poll the mouse again - BPoint newMousePoint; - uint32 button; - GetMouse(&newMousePoint, &button); - - // draw initial empty selection rectangle - BRect lastSelectionRect; - fSelectionRect = lastSelectionRect = BRect(startPoint, startPoint - BPoint(1, 1)); - - if (!fTransparentSelection) { - SetDrawingMode(B_OP_INVERT); - StrokeRect(fSelectionRect, B_MIXED_COLORS); - SetDrawingMode(B_OP_OVER); - } - - BList *selectionList = new BList; - - BPoint oldMousePoint(startPoint); - while (button) { - GetMouse(&newMousePoint, &button, false); - - if (newMousePoint != oldMousePoint) { - oldMousePoint = newMousePoint; - BRect oldRect = fSelectionRect; - fSelectionRect.top = std::min(newMousePoint.y, startPoint.y); - fSelectionRect.left = std::min(newMousePoint.x, startPoint.x); - fSelectionRect.bottom = std::max(newMousePoint.y, startPoint.y); - fSelectionRect.right = std::max(newMousePoint.x, startPoint.x); - - // erase old rect - if (!fTransparentSelection) { - SetDrawingMode(B_OP_INVERT); - StrokeRect(oldRect, B_MIXED_COLORS); - SetDrawingMode(B_OP_OVER); - } - - fIsDrawingSelectionRect = true; - - CheckAutoScroll(newMousePoint, true, true); - - // use current selection rectangle to scan poses - if (ViewMode() == kListMode) - SelectPosesListMode(fSelectionRect, &selectionList); - else - SelectPosesIconMode(fSelectionRect, &selectionList); - - Window()->UpdateIfNeeded(); - - // draw new selected rect - if (!fTransparentSelection) { - SetDrawingMode(B_OP_INVERT); - StrokeRect(fSelectionRect, B_MIXED_COLORS); - SetDrawingMode(B_OP_OVER); - } else { - BRegion updateRegion1; - BRegion updateRegion2; - - bool sameWidth = fSelectionRect.Width() == lastSelectionRect.Width(); - bool sameHeight = fSelectionRect.Height() == lastSelectionRect.Height(); - - updateRegion1.Include(fSelectionRect); - updateRegion1.Exclude(lastSelectionRect.InsetByCopy( - sameWidth ? 0 : 1, sameHeight ? 0 : 1)); - updateRegion2.Include(lastSelectionRect); - updateRegion2.Exclude(fSelectionRect.InsetByCopy( - sameWidth ? 0 : 1, sameHeight ? 0 : 1)); - updateRegion1.Include(&updateRegion2); - BRect unionRect = fSelectionRect & lastSelectionRect; - updateRegion1.Exclude(unionRect - & BRect(-2000, startPoint.y, 2000, startPoint.y)); - updateRegion1.Exclude(unionRect - & BRect(startPoint.x, -2000, startPoint.x, 2000)); - - lastSelectionRect = fSelectionRect; - - Invalidate(&updateRegion1); - Window()->UpdateIfNeeded(); - } - - Flush(); - } - - snooze(20000); - } - - delete selectionList; - - fIsDrawingSelectionRect = false; - - // do final erase of selection rect - if (!fTransparentSelection) { - SetDrawingMode(B_OP_INVERT); - StrokeRect(fSelectionRect, B_MIXED_COLORS); - SetDrawingMode(B_OP_COPY); - } else { - Invalidate(fSelectionRect); - fSelectionRect.Set(0, 0, -1, -1); - Window()->UpdateIfNeeded(); - } - - // we now need to update the pose view's selection list by clearing it - // and then polling each pose for selection state and rebuilding list - fSelectionList->MakeEmpty(); - fMimeTypesInSelectionCache.MakeEmpty(); - - EachListItem(fPoseList, AddIfPoseSelected, fSelectionList); - - // and now make sure that the pivot point is in sync - if (fSelectionPivotPose && !fSelectionList->HasItem(fSelectionPivotPose)) - fSelectionPivotPose = NULL; - if (fRealPivotPose && !fSelectionList->HasItem(fRealPivotPose)) - fRealPivotPose = NULL; -} - // TODO: SelectPosesListMode and SelectPosesIconMode are terrible and share // most code - void BPoseView::SelectPosesListMode(BRect selectionRect, BList **oldList) { @@ -7347,41 +7432,6 @@ } -int32 -BPoseView::WaitForMouseUpOrDrag(BPoint start) -{ - bigtime_t start_time = system_time(); - bigtime_t doubleClickSpeed; - get_click_speed(&doubleClickSpeed); - - // use double the doubleClickSpeed as a treshold - doubleClickSpeed *= 2; - - // loop until mouse has been dragged at least 2 pixels - uint32 button; - BPoint loc; - GetMouse(&loc, &button, false); - - while (button) { - GetMouse(&loc, &button, false); - if (fabs(loc.x - start.x) > 2 || fabs(loc.y - start.y) > 2) - return kWasDragged; - - if ((system_time() - start_time) > doubleClickSpeed) { - ShowContextMenu(start); - return kContextMenuShown; - } - - snooze(15000); - } - - // user let up on mouse button without dragging - Window()->Activate(); - Window()->UpdateIfNeeded(); - return kNotDragged; -} - - void BPoseView::DeleteSymLinkPoseTarget(const node_ref *itemNode, BPose *pose, int32 index) @@ -8391,14 +8441,16 @@ void BPoseView::DrawAfterChildren(BRect updateRect) { - if (fTransparentSelection && fSelectionRect.IsValid()) { + if (fTransparentSelection && fSelectionRectInfo.rect.IsValid()) { SetDrawingMode(B_OP_ALPHA); SetHighColor(255, 255, 255, 128); - if (fSelectionRect.Width() == 0 || fSelectionRect.Height() == 0) - StrokeLine(fSelectionRect.LeftTop(), fSelectionRect.RightBottom()); - else { - StrokeRect(fSelectionRect); - BRect interior = fSelectionRect; + if (fSelectionRectInfo.rect.Width() == 0 + || fSelectionRectInfo.rect.Height() == 0) { + StrokeLine(fSelectionRectInfo.rect.LeftTop(), + fSelectionRectInfo.rect.RightBottom()); + } else { + StrokeRect(fSelectionRectInfo.rect); + BRect interior = fSelectionRectInfo.rect; interior.InsetBy(1, 1); if (interior.IsValid()) { SetHighColor(80, 80, 80, 90); @@ -8947,82 +8999,6 @@ } -void -BPoseView::MouseMoved(BPoint mouseLoc, uint32 moveCode, const BMessage *message) -{ - if (!fDropEnabled || !message) - return; - - BContainerWindow* window = ContainerWindow(); - if (!window) - return; - - switch (moveCode) { - case B_INSIDE_VIEW: - case B_ENTERED_VIEW: - { - UpdateDropTarget(mouseLoc, message, window->ContextMenu()); - if (fAutoScrollState == kAutoScrollOff) { - // turn on auto scrolling if it's not yet on - fAutoScrollState = kWaitForTransition; - window->SetPulseRate(100000); - } - - bigtime_t dropActionDelay; - get_click_speed(&dropActionDelay); - dropActionDelay *= 3; - - if (window->ContextMenu()) - break; - - bigtime_t clickTime = system_time(); - BPoint loc; - uint32 buttons; - GetMouse(&loc, &buttons); - for (;;) { - if (buttons == 0 - || fabs(loc.x - mouseLoc.x) > 4 || fabs(loc.y - mouseLoc.y) > 4) { - // only loop if mouse buttons are down - // moved the mouse, cancel showing the context menu - break; - } - - // handle drag and drop - bigtime_t now = system_time(); - // use shift key to get around over-loading of Control key - // for context menus and auto-dnd menu - if (((modifiers() & B_SHIFT_KEY) && (now - clickTime) > 200000) - || now - clickTime > dropActionDelay) { - // let go of button or pressing for a while, show menu now - if (fDropTarget) { - window->DragStart(message); - FrameForPose(fDropTarget, true, &fStartFrame); - ShowContextMenu(mouseLoc); - } else - window->Activate(); - break; - } - - snooze(10000); - GetMouse(&loc, &buttons); - } - break; - } - - case B_EXITED_VIEW: - // reset cursor in case we set it to the copy cursor in UpdateDropTarget - SetViewCursor(B_CURSOR_SYSTEM_DEFAULT); - fCursorCheck = false; - // TODO: autoscroll here - if (!window->ContextMenu()) { - HiliteDropTarget(false); - fDropTarget = NULL; - } - break; - } -} - - bool BPoseView::UpdateDropTarget(BPoint mouseLoc, const BMessage *dragMessage, bool trackingContextMenu) Modified: haiku/trunk/src/kits/tracker/PoseView.h =================================================================== --- haiku/trunk/src/kits/tracker/PoseView.h 2011-06-03 21:11:38 UTC (rev 41891) +++ haiku/trunk/src/kits/tracker/PoseView.h 2011-06-03 21:12:14 UTC (rev 41892) @@ -161,10 +161,14 @@ virtual void AttachedToWindow(); virtual void WindowActivated(bool); virtual void MakeFocus(bool = true); - virtual void MouseMoved(BPoint, uint32, const BMessage *); virtual void Draw(BRect update_rect); virtual void DrawAfterChildren(BRect update_rect); + virtual void MouseMoved(BPoint, uint32, const BMessage *); virtual void MouseDown(BPoint where); + virtual void MouseUp(BPoint where); + virtual void MouseDragged(const BMessage *); + virtual void MouseLongDown(const BMessage *); + virtual void MouseIdle(const BMessage *); virtual void KeyDown(const char *, int32); virtual void Pulse(); virtual void MoveBy(float, float); @@ -355,7 +359,6 @@ static bool CanHandleDragSelection(const Model *target, const BMessage *dragMessage, bool ignoreTypes); virtual void DragSelectedPoses(const BPose *clickedPose, BPoint); - virtual void DragSelectionRect(BPoint, bool extendSelection); void MoveSelectionInto(Model *destFolder, BContainerWindow *srcWindow, bool forceCopy, bool forceMove = false, bool createLink = false, bool relativeLink = false); @@ -574,13 +577,16 @@ // click handling bool WasDoubleClick(const BPose *, BPoint); bool WasClickInPath(const BPose *, int32 index, BPoint) const; - int32 WaitForMouseUpOrDrag(BPoint start); // selection void SelectPosesListMode(BRect, BList **); void SelectPosesIconMode(BRect, BList **); void AddRemoveSelectionRange(BPoint where, bool extendSelection, BPose *); + void _BeginSelectionRect(const BPoint& point, bool extendSelection); + void _UpdateSelectionRect(const BPoint& point); + void _EndSelectionRect(); + // view drawing void SynchronousUpdate(BRect, bool clip = false); @@ -685,6 +691,18 @@ const BPose *fRealPivotPose; BMessageRunner *fKeyRunner; + struct SelectionRectInfo { + SelectionRectInfo() + : isDragging(false), selection(NULL) {}; + bool isDragging; + BRect rect; + BRect lastRect; + BPoint startPoint; + BPoint lastPoint; + BList* selection; + }; + SelectionRectInfo fSelectionRectInfo; + bool fSelectionVisible : 1; bool fMultipleSelection : 1; bool fDragEnabled : 1; @@ -709,7 +727,6 @@ int32 fLastFilterStringLength; BRect fStartFrame; - BRect fSelectionRect; static float sFontHeight; static font_height sFontInfo;