* 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 :)


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@41892 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Alexandre Deckner
2011-06-03 21:12:14 +00:00
parent 233d1dc479
commit 596bb6891a
5 changed files with 481 additions and 304 deletions
@@ -0,0 +1,47 @@
/*
* Copyright 2009, Alexandre Deckner, [email protected]
* 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
+1
View File
@@ -23,6 +23,7 @@ StaticLibrary libshared.a :
IconButton.cpp IconButton.cpp
IconView.cpp IconView.cpp
Keymap.cpp Keymap.cpp
LongAndDragTrackingFilter.cpp
NaturalCompare.cpp NaturalCompare.cpp
QueryFile.cpp QueryFile.cpp
RWLockManager.cpp RWLockManager.cpp
@@ -0,0 +1,136 @@
/*
* Copyright 2011, Alexandre Deckner, [email protected]
* 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;
}
+276 -300
View File
@@ -58,6 +58,7 @@ All rights reserved.
#include <Query.h> #include <Query.h>
#include <List.h> #include <List.h>
#include <Locale.h> #include <Locale.h>
#include <LongAndDragTrackingFilter.h>
#include <MenuItem.h> #include <MenuItem.h>
#include <NodeMonitor.h> #include <NodeMonitor.h>
#include <Path.h> #include <Path.h>
@@ -113,6 +114,8 @@ const float kCountViewWidth = 76;
const uint32 kAddNewPoses = 'Tanp'; const uint32 kAddNewPoses = 'Tanp';
const uint32 kAddPosesCompleted = 'Tapc'; const uint32 kAddPosesCompleted = 'Tapc';
const int32 kMaxAddPosesChunk = 50; const int32 kMaxAddPosesChunk = 50;
const uint32 kMsgMouseDragged = 'Mdrg';
const uint32 kMsgMouseLongDown = 'Mold';
namespace BPrivate { namespace BPrivate {
@@ -922,6 +925,8 @@ BPoseView::AttachedToWindow()
AddFilter(new ShortcutFilter(B_ESCAPE, B_SHIFT_KEY, kCancelSelectionToClipboard, this)); 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 // Escape + SHIFT will remove current selection from clipboard, or all poses from current folder if 0 selected
AddFilter(new LongAndDragTrackingFilter(kMsgMouseLongDown, kMsgMouseDragged));
fLastLeftTop = LeftTop(); fLastLeftTop = LeftTop();
BFont font(be_plain_font); BFont font(be_plain_font);
font.SetSpacing(B_BITMAP_SPACING); font.SetSpacing(B_BITMAP_SPACING);
@@ -2154,6 +2159,18 @@ BPoseView::MessageReceived(BMessage *message)
case kMiniIconMode: case kMiniIconMode:
SetViewMode(message->what); SetViewMode(message->what);
break; break;
case kMsgMouseDragged:
MouseDragged(message);
break;
case kMsgMouseLongDown:
MouseLongDown(message);
break;
case B_MOUSE_IDLE:
MouseIdle(message);
break;
case B_SELECT_ALL: case B_SELECT_ALL:
{ {
@@ -6544,11 +6561,238 @@ BPoseView::ShowContextMenu(BPoint where)
} }
void
BPoseView::_BeginSelectionRect(const BPoint& point, bool shouldExtend)
{
// 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 void
BPoseView::MouseDown(BPoint where) BPoseView::MouseDown(BPoint where)
{ {
// TODO: add asynch mouse tracking
// handle disposing of drag data lazily // handle disposing of drag data lazily
DragStop(); DragStop();
BContainerWindow *window = ContainerWindow(); BContainerWindow *window = ContainerWindow();
@@ -6572,7 +6816,6 @@ BPoseView::MouseDown(BPoint where)
if ((buttons & B_SECONDARY_MOUSE_BUTTON) == 0) if ((buttons & B_SECONDARY_MOUSE_BUTTON) == 0)
showContext = (modifs & B_CONTROL_KEY) != 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) { if (showContext) {
int32 index; int32 index;
BPose *pose = FindPose(where, &index); BPose *pose = FindPose(where, &index);
@@ -6586,24 +6829,7 @@ BPoseView::MouseDown(BPoint where)
fSelectionList->AddItem(pose); fSelectionList->AddItem(pose);
DrawPose(pose, index, false); DrawPose(pose, index, false);
} }
ShowContextMenu(where);
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);
}
} }
bool extendSelection = (modifs & B_COMMAND_KEY) && fMultipleSelection; bool extendSelection = (modifs & B_COMMAND_KEY) && fMultipleSelection;
@@ -6616,26 +6842,10 @@ BPoseView::MouseDown(BPoint where)
if (pose) { if (pose) {
AddRemoveSelectionRange(where, extendSelection, pose); AddRemoveSelectionRange(where, extendSelection, pose);
switch (WaitForMouseUpOrDrag(where)) { if (!extendSelection && WasDoubleClick(pose, where)) {
case kWasDragged: // special handling for Path field double-clicks
DragSelectedPoses(pose, where); if (!WasClickInPath(pose, index, where))
break; OpenSelection(pose, &index);
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;
} }
} else { } else {
// click was not in any pose // click was not in any pose
@@ -6643,7 +6853,10 @@ BPoseView::MouseDown(BPoint where)
window->Activate(); window->Activate();
window->UpdateIfNeeded(); window->UpdateIfNeeded();
DragSelectionRect(where, extendSelection);
// only clear selection if we are not extending it
if (!extendSelection || !fSelectionRectEnabled || !fMultipleSelection)
ClearSelection();
} }
if (fSelectionChangedHook) if (fSelectionChangedHook)
@@ -6651,6 +6864,19 @@ BPoseView::MouseDown(BPoint where)
} }
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 bool
BPoseView::WasClickInPath(const BPose *pose, int32 index, BPoint mouseLoc) const BPoseView::WasClickInPath(const BPose *pose, int32 index, BPoint mouseLoc) const
{ {
@@ -6971,149 +7197,8 @@ BPoseView::GetDragRect(int32 clickedPoseIndex)
} }
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 // TODO: SelectPosesListMode and SelectPosesIconMode are terrible and share
// most code // most code
void void
BPoseView::SelectPosesListMode(BRect selectionRect, BList **oldList) BPoseView::SelectPosesListMode(BRect selectionRect, BList **oldList)
{ {
@@ -7347,41 +7432,6 @@ BPoseView::AddRemoveSelectionRange(BPoint where, bool extendSelection, BPose *po
} }
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 void
BPoseView::DeleteSymLinkPoseTarget(const node_ref *itemNode, BPose *pose, BPoseView::DeleteSymLinkPoseTarget(const node_ref *itemNode, BPose *pose,
int32 index) int32 index)
@@ -8391,14 +8441,16 @@ BPoseView::Draw(BRect updateRect)
void void
BPoseView::DrawAfterChildren(BRect updateRect) BPoseView::DrawAfterChildren(BRect updateRect)
{ {
if (fTransparentSelection && fSelectionRect.IsValid()) { if (fTransparentSelection && fSelectionRectInfo.rect.IsValid()) {
SetDrawingMode(B_OP_ALPHA); SetDrawingMode(B_OP_ALPHA);
SetHighColor(255, 255, 255, 128); SetHighColor(255, 255, 255, 128);
if (fSelectionRect.Width() == 0 || fSelectionRect.Height() == 0) if (fSelectionRectInfo.rect.Width() == 0
StrokeLine(fSelectionRect.LeftTop(), fSelectionRect.RightBottom()); || fSelectionRectInfo.rect.Height() == 0) {
else { StrokeLine(fSelectionRectInfo.rect.LeftTop(),
StrokeRect(fSelectionRect); fSelectionRectInfo.rect.RightBottom());
BRect interior = fSelectionRect; } else {
StrokeRect(fSelectionRectInfo.rect);
BRect interior = fSelectionRectInfo.rect;
interior.InsetBy(1, 1); interior.InsetBy(1, 1);
if (interior.IsValid()) { if (interior.IsValid()) {
SetHighColor(80, 80, 80, 90); SetHighColor(80, 80, 80, 90);
@@ -8947,82 +8999,6 @@ BPoseView::MoveColumnTo(BColumn *src, BColumn *dest)
} }
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 bool
BPoseView::UpdateDropTarget(BPoint mouseLoc, const BMessage *dragMessage, BPoseView::UpdateDropTarget(BPoint mouseLoc, const BMessage *dragMessage,
bool trackingContextMenu) bool trackingContextMenu)
+21 -4
View File
@@ -161,10 +161,14 @@ class BPoseView : public BView {
virtual void AttachedToWindow(); virtual void AttachedToWindow();
virtual void WindowActivated(bool); virtual void WindowActivated(bool);
virtual void MakeFocus(bool = true); virtual void MakeFocus(bool = true);
virtual void MouseMoved(BPoint, uint32, const BMessage *);
virtual void Draw(BRect update_rect); virtual void Draw(BRect update_rect);
virtual void DrawAfterChildren(BRect update_rect); virtual void DrawAfterChildren(BRect update_rect);
virtual void MouseMoved(BPoint, uint32, const BMessage *);
virtual void MouseDown(BPoint where); 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 KeyDown(const char *, int32);
virtual void Pulse(); virtual void Pulse();
virtual void MoveBy(float, float); virtual void MoveBy(float, float);
@@ -355,7 +359,6 @@ class BPoseView : public BView {
static bool CanHandleDragSelection(const Model *target, static bool CanHandleDragSelection(const Model *target,
const BMessage *dragMessage, bool ignoreTypes); const BMessage *dragMessage, bool ignoreTypes);
virtual void DragSelectedPoses(const BPose *clickedPose, BPoint); virtual void DragSelectedPoses(const BPose *clickedPose, BPoint);
virtual void DragSelectionRect(BPoint, bool extendSelection);
void MoveSelectionInto(Model *destFolder, BContainerWindow *srcWindow, void MoveSelectionInto(Model *destFolder, BContainerWindow *srcWindow,
bool forceCopy, bool forceMove = false, bool createLink = false, bool relativeLink = false); bool forceCopy, bool forceMove = false, bool createLink = false, bool relativeLink = false);
@@ -574,13 +577,16 @@ class BPoseView : public BView {
// click handling // click handling
bool WasDoubleClick(const BPose *, BPoint); bool WasDoubleClick(const BPose *, BPoint);
bool WasClickInPath(const BPose *, int32 index, BPoint) const; bool WasClickInPath(const BPose *, int32 index, BPoint) const;
int32 WaitForMouseUpOrDrag(BPoint start);
// selection // selection
void SelectPosesListMode(BRect, BList **); void SelectPosesListMode(BRect, BList **);
void SelectPosesIconMode(BRect, BList **); void SelectPosesIconMode(BRect, BList **);
void AddRemoveSelectionRange(BPoint where, bool extendSelection, BPose *); void AddRemoveSelectionRange(BPoint where, bool extendSelection, BPose *);
void _BeginSelectionRect(const BPoint& point, bool extendSelection);
void _UpdateSelectionRect(const BPoint& point);
void _EndSelectionRect();
// view drawing // view drawing
void SynchronousUpdate(BRect, bool clip = false); void SynchronousUpdate(BRect, bool clip = false);
@@ -685,6 +691,18 @@ class BPoseView : public BView {
const BPose *fRealPivotPose; const BPose *fRealPivotPose;
BMessageRunner *fKeyRunner; 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 fSelectionVisible : 1;
bool fMultipleSelection : 1; bool fMultipleSelection : 1;
bool fDragEnabled : 1; bool fDragEnabled : 1;
@@ -709,7 +727,6 @@ class BPoseView : public BView {
int32 fLastFilterStringLength; int32 fLastFilterStringLength;
BRect fStartFrame; BRect fStartFrame;
BRect fSelectionRect;
static float sFontHeight; static float sFontHeight;
static font_height sFontInfo; static font_height sFontInfo;