* Copyright 2001-2020, Haiku, Inc.
* Distributed under the terms of the MIT license.
*
* Authors:
* DarkWyrm, bpmagic@columbus.rr.com
* Adi Oanca, adioanca@gmail.com
* Stephan Aßmus, superstippi@gmx.de
* Axel Dörfler, axeld@pinc-software.de
* Brecht Machiels, brecht@mos6581.org
* Clemens Zeidler, haiku@clemens-zeidler.de
* Ingo Weinhold, ingo_weinhold@gmx.de
* Tri-Edge AI
* Jacob Secunda, secundja@gmail.com
*/
#include "DefaultWindowBehaviour.h"
#include <math.h>
#include <PortLink.h>
#include <WindowPrivate.h>
#include "AppServer.h"
#include "ClickTarget.h"
#include "Desktop.h"
#include "DefaultDecorator.h"
#include "DrawingEngine.h"
#include "Window.h"
#ifdef DEBUG_WINDOW_CLICK
# define STRACE_CLICK(x) printf x
#else
# define STRACE_CLICK(x) ;
#endif
static const bigtime_t kWindowActivationTimeout = 500000LL;
struct DefaultWindowBehaviour::State {
State(DefaultWindowBehaviour& behavior)
:
fBehavior(behavior),
fWindow(behavior.fWindow),
fDesktop(behavior.fDesktop)
{
}
virtual ~State()
{
}
virtual void EnterState(State* previousState)
{
}
virtual void ExitState(State* nextState)
{
}
virtual bool MouseDown(BMessage* message, BPoint where, bool& _unhandled)
{
return true;
}
virtual void MouseUp(BMessage* message, BPoint where)
{
}
virtual void MouseMoved(BMessage* message, BPoint where, bool isFake)
{
}
virtual void ModifiersChanged(BPoint where, int32 modifiers)
{
}
protected:
DefaultWindowBehaviour& fBehavior;
Window* fWindow;
Desktop* fDesktop;
};
struct DefaultWindowBehaviour::MouseTrackingState : State {
MouseTrackingState(DefaultWindowBehaviour& behavior, BPoint where,
bool windowActionOnMouseUp, bool minimizeCheckOnMouseUp,
int32 mouseButton = B_PRIMARY_MOUSE_BUTTON)
:
State(behavior),
fMouseButton(mouseButton),
fWindowActionOnMouseUp(windowActionOnMouseUp),
fMinimizeCheckOnMouseUp(minimizeCheckOnMouseUp),
fLastMousePosition(where),
fMouseMoveDistance(0),
fLastMoveTime(system_time())
{
}
virtual void MouseUp(BMessage* message, BPoint where)
{
int32 buttons = message->FindInt32("buttons");
if ((buttons & fMouseButton) != 0)
return;
if (fMinimizeCheckOnMouseUp) {
fMinimizeCheckOnMouseUp = false;
if (message->FindInt32("modifiers") == fBehavior.fLastModifiers
&& (fWindow->Flags() & B_NOT_MINIMIZABLE) == 0
&& system_time() - fLastMoveTime < kWindowActivationTimeout) {
fWindow->ServerWindow()->NotifyMinimize(true);
}
}
if (fWindowActionOnMouseUp) {
if (system_time() - fLastMoveTime < kWindowActivationTimeout)
MouseUpWindowAction();
}
fBehavior._NextState(NULL);
}
virtual void MouseMoved(BMessage* message, BPoint where, bool isFake)
{
bigtime_t now = system_time();
if (now - fLastMoveTime < 13333) {
return;
}
if (fWindowActionOnMouseUp || fMinimizeCheckOnMouseUp) {
if (now - fLastMoveTime >= kWindowActivationTimeout) {
fWindowActionOnMouseUp = false;
fMinimizeCheckOnMouseUp = false;
fLastMoveTime = now;
}
} else
fLastMoveTime = now;
BPoint delta = where - fLastMousePosition;
if (fWindowActionOnMouseUp || fMinimizeCheckOnMouseUp) {
fMouseMoveDistance += delta.x * delta.x + delta.y * delta.y;
if (fMouseMoveDistance > 16.0f) {
fWindowActionOnMouseUp = false;
fMinimizeCheckOnMouseUp = false;
} else
delta = B_ORIGIN;
}
MouseMovedAction(delta, now);
fLastMousePosition += delta;
}
virtual void MouseMovedAction(BPoint& delta, bigtime_t now)
{
}
virtual void MouseUpWindowAction()
{
fDesktop->ActivateWindow(fWindow);
}
protected:
int32 fMouseButton;
bool fWindowActionOnMouseUp : 1;
bool fMinimizeCheckOnMouseUp : 1;
BPoint fLastMousePosition;
float fMouseMoveDistance;
bigtime_t fLastMoveTime;
};
struct DefaultWindowBehaviour::DragState : MouseTrackingState {
DragState(DefaultWindowBehaviour& behavior, BPoint where,
bool activateOnMouseUp, bool minimizeCheckOnMouseUp)
:
MouseTrackingState(behavior, where, activateOnMouseUp,
minimizeCheckOnMouseUp)
{
}
virtual bool MouseDown(BMessage* message, BPoint where, bool& _unhandled)
{
int32 buttons = message->FindInt32("buttons");
if ((buttons & B_SECONDARY_MOUSE_BUTTON) != 0) {
if (fWindow == fDesktop->BackWindow())
fDesktop->ActivateWindow(fWindow);
else
fDesktop->SendWindowBehind(fWindow);
return true;
}
return MouseTrackingState::MouseDown(message, where, _unhandled);
}
virtual void MouseMovedAction(BPoint& delta, bigtime_t now)
{
if ((fWindow->Flags() & B_NOT_MOVABLE) == 0) {
BPoint oldLeftTop = fWindow->Frame().LeftTop();
fBehavior.AlterDeltaForSnap(fWindow, delta, now);
fDesktop->MoveWindowBy(fWindow, delta.x, delta.y);
delta = fWindow->Frame().LeftTop() - oldLeftTop;
} else
delta = BPoint(0, 0);
}
};
struct DefaultWindowBehaviour::ResizeState : MouseTrackingState {
BPoint fDelta;
ResizeState(DefaultWindowBehaviour& behavior, BPoint where,
bool activateOnMouseUp, bool minimizeCheckOnMouseUp)
:
MouseTrackingState(behavior, where, activateOnMouseUp, minimizeCheckOnMouseUp)
{
fDelta = BPoint(0, 0);
}
virtual void EnterState(State* prevState)
{
}
virtual void ExitState(State* nextState)
{
if ((fWindow->Flags() & B_OUTLINE_RESIZE) != 0) {
fDesktop->SetWindowOutlinesDelta(fWindow, BPoint(0, 0));
fDesktop->ResizeWindowBy(fWindow, fDelta.x, fDelta.y);
}
}
virtual void MouseMovedAction(BPoint& delta, bigtime_t now)
{
if ((fWindow->Flags() & B_NOT_RESIZABLE) == 0) {
if ((fWindow->Flags() & B_NOT_V_RESIZABLE) != 0)
delta.y = 0;
if ((fWindow->Flags() & B_NOT_H_RESIZABLE) != 0)
delta.x = 0;
BPoint oldRightBottom = fWindow->Frame().RightBottom();
if ((fWindow->Flags() & B_OUTLINE_RESIZE) != 0) {
fDelta = delta;
fDesktop->SetWindowOutlinesDelta(fWindow, delta);
} else
fDesktop->ResizeWindowBy(fWindow, delta.x, delta.y);
delta = fWindow->Frame().RightBottom() - oldRightBottom;
} else
delta = BPoint(0, 0);
}
};
struct DefaultWindowBehaviour::SlideTabState : MouseTrackingState {
SlideTabState(DefaultWindowBehaviour& behavior, BPoint where)
:
MouseTrackingState(behavior, where, false, false)
{
}
virtual
~SlideTabState()
{
fDesktop->SetWindowTabLocation(fWindow, fWindow->TabLocation(), false);
}
virtual void MouseMovedAction(BPoint& delta, bigtime_t now)
{
float location = fWindow->TabLocation();
location += delta.x;
AdjustMultiTabLocation(location, true);
if (fDesktop->SetWindowTabLocation(fWindow, location, true))
delta.y = 0;
else
delta = BPoint(0, 0);
}
void AdjustMultiTabLocation(float location, bool isShifting)
{
::Decorator* decorator = fWindow->Decorator();
if (decorator == NULL || decorator->CountTabs() <= 1)
return;
int32 windowIndex = fWindow->PositionInStack();
DefaultDecorator::Tab* movingTab = static_cast<DefaultDecorator::Tab*>(
decorator->TabAt(windowIndex));
int32 neighbourIndex = windowIndex;
if (movingTab->tabOffset > location)
neighbourIndex--;
else
neighbourIndex++;
DefaultDecorator::Tab* neighbourTab
= static_cast<DefaultDecorator::Tab*>(decorator->TabAt(
neighbourIndex));
if (neighbourTab == NULL)
return;
if (movingTab->tabOffset > location) {
if (location > neighbourTab->tabOffset
+ neighbourTab->tabRect.Width() / 2) {
return;
}
} else {
if (location + movingTab->tabRect.Width() < neighbourTab->tabOffset
+ neighbourTab->tabRect.Width() / 2) {
return;
}
}
fWindow->MoveToStackPosition(neighbourIndex, isShifting);
}
};
struct DefaultWindowBehaviour::ResizeBorderState : MouseTrackingState {
BPoint fDelta;
ResizeBorderState(DefaultWindowBehaviour& behavior, BPoint where,
Decorator::Region region)
:
MouseTrackingState(behavior, where, true, false,
B_SECONDARY_MOUSE_BUTTON),
fHorizontal(NONE),
fVertical(NONE)
{
switch (region) {
case Decorator::REGION_TAB:
break;
case Decorator::REGION_LEFT_BORDER:
fHorizontal = LEFT;
break;
case Decorator::REGION_RIGHT_BORDER:
fHorizontal = RIGHT;
break;
case Decorator::REGION_TOP_BORDER:
fVertical = TOP;
break;
case Decorator::REGION_BOTTOM_BORDER:
fVertical = BOTTOM;
break;
case Decorator::REGION_LEFT_TOP_CORNER:
fHorizontal = LEFT;
fVertical = TOP;
break;
case Decorator::REGION_LEFT_BOTTOM_CORNER:
fHorizontal = LEFT;
fVertical = BOTTOM;
break;
case Decorator::REGION_RIGHT_TOP_CORNER:
fHorizontal = RIGHT;
fVertical = TOP;
break;
case Decorator::REGION_RIGHT_BOTTOM_CORNER:
fHorizontal = RIGHT;
fVertical = BOTTOM;
break;
default:
break;
}
fDelta = B_ORIGIN;
}
ResizeBorderState(DefaultWindowBehaviour& behavior, BPoint where,
int8 horizontal, int8 vertical)
:
MouseTrackingState(behavior, where, true, false,
B_SECONDARY_MOUSE_BUTTON),
fHorizontal(horizontal),
fVertical(vertical)
{
fDelta = B_ORIGIN;
}
virtual void EnterState(State* previousState)
{
if ((fWindow->Flags() & B_NOT_RESIZABLE) != 0)
fHorizontal = fVertical = NONE;
else {
if ((fWindow->Flags() & B_NOT_H_RESIZABLE) != 0)
fHorizontal = NONE;
if ((fWindow->Flags() & B_NOT_V_RESIZABLE) != 0)
fVertical = NONE;
}
fBehavior._SetResizeCursor(fHorizontal, fVertical);
}
virtual void ExitState(State* nextState)
{
fBehavior._ResetResizeCursor();
if (fWindow->Flags() & B_OUTLINE_RESIZE) {
fDesktop->SetWindowOutlinesDelta(fWindow, B_ORIGIN);
fDesktop->ResizeWindowBy(fWindow, fDelta.x, fDelta.y);
}
}
virtual void MouseMovedAction(BPoint& delta, bigtime_t now)
{
if (fHorizontal == NONE)
delta.x = 0;
if (fVertical == NONE)
delta.y = 0;
if (delta.x == 0 && delta.y == 0)
return;
BPoint oldRightBottom = fWindow->Frame().RightBottom();
if (fWindow->Flags() & B_OUTLINE_RESIZE) {
fDelta = delta;
fDesktop->SetWindowOutlinesDelta(fWindow, BPoint(
delta.x * fHorizontal, delta.y * fVertical));
} else {
fDesktop->ResizeWindowBy(fWindow, delta.x * fHorizontal,
delta.y * fVertical);
}
delta = fWindow->Frame().RightBottom() - oldRightBottom;
delta.x *= fHorizontal;
delta.y *= fVertical;
float moveX = fHorizontal == LEFT ? delta.x : 0;
float moveY = fVertical == TOP ? delta.y : 0;
if (moveX != 0 || moveY != 0)
fDesktop->MoveWindowBy(fWindow, moveX, moveY);
}
virtual void MouseUpWindowAction()
{
fDesktop->SendWindowBehind(fWindow);
}
private:
int8 fHorizontal;
int8 fVertical;
};
struct DefaultWindowBehaviour::DecoratorButtonState : State {
DecoratorButtonState(DefaultWindowBehaviour& behavior,
int32 tab, Decorator::Region button)
:
State(behavior),
fTab(tab),
fButton(button)
{
}
virtual void EnterState(State* previousState)
{
_RedrawDecorator(NULL);
}
virtual void MouseUp(BMessage* message, BPoint where)
{
int32 buttons = message->FindInt32("buttons");
if ((buttons & B_PRIMARY_MOUSE_BUTTON) != 0)
return;
if (Decorator* decorator = fWindow->Decorator()) {
BRegion* visibleBorder = fWindow->RegionPool()->GetRegion();
fWindow->GetBorderRegion(visibleBorder);
visibleBorder->IntersectWith(&fWindow->VisibleRegion());
DrawingEngine* engine = decorator->GetDrawingEngine();
engine->LockParallelAccess();
engine->ConstrainClippingRegion(visibleBorder);
int32 tab;
switch (fButton) {
case Decorator::REGION_CLOSE_BUTTON:
decorator->SetClose(fTab, false);
if (fBehavior._RegionFor(message, tab) == fButton)
fWindow->ServerWindow()->NotifyQuitRequested();
break;
case Decorator::REGION_ZOOM_BUTTON:
decorator->SetZoom(fTab, false);
if (fBehavior._RegionFor(message, tab) == fButton)
fWindow->ServerWindow()->NotifyZoom();
break;
case Decorator::REGION_MINIMIZE_BUTTON:
decorator->SetMinimize(fTab, false);
if (fBehavior._RegionFor(message, tab) == fButton)
fWindow->ServerWindow()->NotifyMinimize(true);
break;
default:
break;
}
engine->UnlockParallelAccess();
fWindow->RegionPool()->Recycle(visibleBorder);
}
fBehavior._NextState(NULL);
}
virtual void MouseMoved(BMessage* message, BPoint where, bool isFake)
{
_RedrawDecorator(message);
}
private:
void _RedrawDecorator(const BMessage* message)
{
if (Decorator* decorator = fWindow->Decorator()) {
BRegion* visibleBorder = fWindow->RegionPool()->GetRegion();
fWindow->GetBorderRegion(visibleBorder);
visibleBorder->IntersectWith(&fWindow->VisibleRegion());
DrawingEngine* engine = decorator->GetDrawingEngine();
engine->LockParallelAccess();
engine->ConstrainClippingRegion(visibleBorder);
int32 tab;
Decorator::Region hitRegion = message != NULL
? fBehavior._RegionFor(message, tab) : fButton;
switch (fButton) {
case Decorator::REGION_CLOSE_BUTTON:
decorator->SetClose(fTab, hitRegion == fButton);
break;
case Decorator::REGION_ZOOM_BUTTON:
decorator->SetZoom(fTab, hitRegion == fButton);
break;
case Decorator::REGION_MINIMIZE_BUTTON:
decorator->SetMinimize(fTab, hitRegion == fButton);
break;
default:
break;
}
engine->UnlockParallelAccess();
fWindow->RegionPool()->Recycle(visibleBorder);
}
}
protected:
int32 fTab;
Decorator::Region fButton;
};
struct DefaultWindowBehaviour::ManageWindowState : State {
ManageWindowState(DefaultWindowBehaviour& behavior, BPoint where)
:
State(behavior),
fLastMousePosition(where),
fHorizontal(NONE),
fVertical(NONE)
{
}
virtual void EnterState(State* previousState)
{
_UpdateBorders(fLastMousePosition);
}
virtual void ExitState(State* nextState)
{
fBehavior._SetBorderHighlights(fHorizontal, fVertical, false);
}
virtual bool MouseDown(BMessage* message, BPoint where, bool& _unhandled)
{
int32 buttons = message->FindInt32("buttons");
if ((buttons & B_SECONDARY_MOUSE_BUTTON) == 0) {
_unhandled = true;
return true;
}
fBehavior._NextState(new (std::nothrow) ResizeBorderState(fBehavior,
where, fHorizontal, fVertical));
return true;
}
virtual void MouseMoved(BMessage* message, BPoint where, bool isFake)
{
if (fDesktop->WindowAt(where) == fWindow) {
fLastMousePosition = where;
_UpdateBorders(fLastMousePosition);
} else
fBehavior._NextState(NULL);
}
virtual void ModifiersChanged(BPoint where, int32 modifiers)
{
if (!fBehavior._IsWindowModifier(modifiers))
fBehavior._NextState(NULL);
}
private:
void _UpdateBorders(BPoint where)
{
if ((fWindow->Flags() & B_NOT_RESIZABLE) != 0)
return;
BRect frame(fWindow->Frame());
if (frame.Width() + 1 == 0 || frame.Height() + 1 == 0)
return;
float x = (where.x - (frame.left + frame.right) / 2)
/ (frame.Width() + 1);
float y = (where.y - (frame.top + frame.bottom) / 2)
/ (frame.Height() + 1);
int8 horizontal;
int8 vertical;
_ComputeResizeDirection(x, y, horizontal, vertical);
if ((fWindow->Flags() & B_NOT_H_RESIZABLE) != 0)
horizontal = NONE;
if ((fWindow->Flags() & B_NOT_V_RESIZABLE) != 0)
vertical = NONE;
if (horizontal != fHorizontal || vertical != fVertical) {
fBehavior._SetBorderHighlights(fHorizontal, fVertical, false);
fHorizontal = horizontal;
fVertical = vertical;
fBehavior._SetBorderHighlights(fHorizontal, fVertical, true);
}
}
private:
BPoint fLastMousePosition;
int8 fHorizontal;
int8 fVertical;
};
DefaultWindowBehaviour::DefaultWindowBehaviour(Window* window)
:
fWindow(window),
fDesktop(window->Desktop()),
fLastModifiers(0)
{
}
DefaultWindowBehaviour::~DefaultWindowBehaviour()
{
}
bool
DefaultWindowBehaviour::MouseDown(BMessage* message, BPoint where,
int32 lastHitRegion, int32& clickCount, int32& _hitRegion)
{
fLastModifiers = message->FindInt32("modifiers");
int32 buttons = message->FindInt32("buttons");
int32 numButtons;
if (get_mouse_type(&numButtons) == B_OK) {
switch (numButtons) {
case 1:
if ((fLastModifiers & B_CONTROL_KEY) != 0) {
buttons = B_SECONDARY_MOUSE_BUTTON;
message->ReplaceInt32("buttons", buttons);
}
break;
case 2:
default:
break;
}
}
if (fState.IsSet()) {
bool unhandled = false;
bool result = fState->MouseDown(message, where, unhandled);
if (!unhandled)
return result;
}
Decorator* decorator = fWindow->Decorator();
Decorator::Region hitRegion = Decorator::REGION_NONE;
int32 tab = -1;
Action action = ACTION_NONE;
bool inBorderRegion = false;
if (decorator != NULL)
inBorderRegion = decorator->GetFootprint().Contains(where);
bool windowModifier = _IsWindowModifier(fLastModifiers);
if (windowModifier || inBorderRegion) {
if (windowModifier) {
hitRegion = Decorator::REGION_LEFT_BORDER;
} else {
hitRegion = _RegionFor(message, tab);
}
uint32 flags = fWindow->Flags();
if ((buttons & B_PRIMARY_MOUSE_BUTTON) != 0) {
switch (hitRegion) {
case Decorator::REGION_TAB: {
if ((fLastModifiers & B_SHIFT_KEY) != 0
&& fWindow->Look() != kLeftTitledWindowLook) {
action = ACTION_SLIDE_TAB;
break;
}
action = ACTION_DRAG;
break;
}
case Decorator::REGION_LEFT_BORDER:
case Decorator::REGION_RIGHT_BORDER:
case Decorator::REGION_TOP_BORDER:
case Decorator::REGION_BOTTOM_BORDER:
action = ACTION_DRAG;
break;
case Decorator::REGION_CLOSE_BUTTON:
action = (flags & B_NOT_CLOSABLE) == 0
? ACTION_CLOSE : ACTION_DRAG;
break;
case Decorator::REGION_ZOOM_BUTTON:
action = (flags & B_NOT_ZOOMABLE) == 0
? ACTION_ZOOM : ACTION_DRAG;
break;
case Decorator::REGION_MINIMIZE_BUTTON:
action = (flags & B_NOT_MINIMIZABLE) == 0
? ACTION_MINIMIZE : ACTION_DRAG;
break;
case Decorator::REGION_LEFT_TOP_CORNER:
case Decorator::REGION_LEFT_BOTTOM_CORNER:
case Decorator::REGION_RIGHT_TOP_CORNER:
action = ACTION_DRAG;
break;
case Decorator::REGION_RIGHT_BOTTOM_CORNER:
action = (flags & B_NOT_RESIZABLE) == 0
? ACTION_RESIZE : ACTION_DRAG;
break;
default:
break;
}
} else if ((buttons & B_SECONDARY_MOUSE_BUTTON) != 0) {
switch (hitRegion) {
case Decorator::REGION_TAB:
case Decorator::REGION_LEFT_BORDER:
case Decorator::REGION_RIGHT_BORDER:
case Decorator::REGION_TOP_BORDER:
case Decorator::REGION_BOTTOM_BORDER:
case Decorator::REGION_CLOSE_BUTTON:
case Decorator::REGION_ZOOM_BUTTON:
case Decorator::REGION_MINIMIZE_BUTTON:
case Decorator::REGION_LEFT_TOP_CORNER:
case Decorator::REGION_LEFT_BOTTOM_CORNER:
case Decorator::REGION_RIGHT_TOP_CORNER:
case Decorator::REGION_RIGHT_BOTTOM_CORNER:
action = ACTION_RESIZE_BORDER;
break;
default:
break;
}
}
}
_hitRegion = (int32)hitRegion;
if (action == ACTION_NONE) {
return inBorderRegion;
}
if (hitRegion != lastHitRegion)
clickCount = 1;
DesktopSettings desktopSettings(fDesktop);
if (!desktopSettings.AcceptFirstClick()) {
if (!fWindow->IsFocus() && !fWindow->IsFloating()
&& action != ACTION_RESIZE_BORDER
&& action != ACTION_RESIZE && action != ACTION_SLIDE_TAB)
action = ACTION_DRAG;
}
bool activateOnMouseUp = false;
if (action != ACTION_RESIZE_BORDER) {
if (desktopSettings.MouseMode() == B_NORMAL_MOUSE) {
fDesktop->ActivateWindow(fWindow);
} else {
fDesktop->SetFocusWindow(fWindow);
activateOnMouseUp = true;
}
}
switch (action) {
case ACTION_CLOSE:
case ACTION_ZOOM:
case ACTION_MINIMIZE:
_NextState(
new (std::nothrow) DecoratorButtonState(*this, tab, hitRegion));
STRACE_CLICK(("===> ACTION_CLOSE/ZOOM/MINIMIZE\n"));
break;
case ACTION_DRAG:
_NextState(new (std::nothrow) DragState(*this, where,
activateOnMouseUp, clickCount == 2));
STRACE_CLICK(("===> ACTION_DRAG\n"));
break;
case ACTION_RESIZE:
_NextState(new (std::nothrow) ResizeState(*this, where,
activateOnMouseUp, clickCount == 2));
STRACE_CLICK(("===> ACTION_RESIZE\n"));
break;
case ACTION_SLIDE_TAB:
_NextState(new (std::nothrow) SlideTabState(*this, where));
STRACE_CLICK(("===> ACTION_SLIDE_TAB\n"));
break;
case ACTION_RESIZE_BORDER:
_NextState(new (std::nothrow) ResizeBorderState(*this, where,
hitRegion));
STRACE_CLICK(("===> ACTION_RESIZE_BORDER\n"));
break;
default:
break;
}
return true;
}
void
DefaultWindowBehaviour::MouseUp(BMessage* message, BPoint where)
{
if (fState.IsSet())
fState->MouseUp(message, where);
}
void
DefaultWindowBehaviour::MouseMoved(BMessage* message, BPoint where, bool isFake)
{
if (fState.IsSet()) {
fState->MouseMoved(message, where, isFake);
} else {
if (_IsWindowModifier(message->FindInt32("modifiers")))
_NextState(new(std::nothrow) ManageWindowState(*this, where));
}
DesktopSettings desktopSettings(fDesktop);
if (desktopSettings.FocusFollowsMouse()
&& !fWindow->IsFocus() && (fWindow->Flags() & B_AVOID_FOCUS) == 0) {
fDesktop->SetFocusWindow(isFake ? NULL : fWindow);
}
}
void
DefaultWindowBehaviour::ModifiersChanged(int32 modifiers)
{
BPoint where;
int32 buttons;
fDesktop->GetLastMouseState(&where, &buttons);
if (fState.IsSet()) {
fState->ModifiersChanged(where, modifiers);
} else {
if (_IsWindowModifier(modifiers))
_NextState(new(std::nothrow) ManageWindowState(*this, where));
}
}
bool
DefaultWindowBehaviour::AlterDeltaForSnap(Window* window, BPoint& delta,
bigtime_t now)
{
return fMagneticBorder.AlterDeltaForSnap(window, delta, now);
}
bool
DefaultWindowBehaviour::_IsWindowModifier(int32 modifiers) const
{
return (fWindow->Flags() & B_NO_SERVER_SIDE_WINDOW_MODIFIERS) == 0
&& (modifiers & (B_COMMAND_KEY | B_CONTROL_KEY | B_OPTION_KEY
| B_SHIFT_KEY)) == (B_COMMAND_KEY | B_CONTROL_KEY);
}
Decorator::Region
DefaultWindowBehaviour::_RegionFor(const BMessage* message, int32& tab) const
{
Decorator* decorator = fWindow->Decorator();
if (decorator == NULL)
return Decorator::REGION_NONE;
BPoint where;
if (message->FindPoint("where", &where) != B_OK)
return Decorator::REGION_NONE;
return decorator->RegionAt(where, tab);
}
void
DefaultWindowBehaviour::_SetBorderHighlights(int8 horizontal, int8 vertical,
bool active)
{
if (Decorator* decorator = fWindow->Decorator()) {
uint8 highlight = active
? Decorator::HIGHLIGHT_RESIZE_BORDER
: Decorator::HIGHLIGHT_NONE;
BRegion dirtyRegion;
switch (horizontal) {
case LEFT:
decorator->SetRegionHighlight(Decorator::REGION_LEFT_BORDER,
highlight, &dirtyRegion);
break;
case RIGHT:
decorator->SetRegionHighlight(
Decorator::REGION_RIGHT_BORDER, highlight,
&dirtyRegion);
break;
}
switch (vertical) {
case TOP:
decorator->SetRegionHighlight(Decorator::REGION_TOP_BORDER,
highlight, &dirtyRegion);
break;
case BOTTOM:
decorator->SetRegionHighlight(
Decorator::REGION_BOTTOM_BORDER, highlight,
&dirtyRegion);
break;
}
if (horizontal != NONE && vertical != NONE) {
if (horizontal == LEFT) {
if (vertical == TOP) {
decorator->SetRegionHighlight(
Decorator::REGION_LEFT_TOP_CORNER, highlight,
&dirtyRegion);
} else {
decorator->SetRegionHighlight(
Decorator::REGION_LEFT_BOTTOM_CORNER, highlight,
&dirtyRegion);
}
} else {
if (vertical == TOP) {
decorator->SetRegionHighlight(
Decorator::REGION_RIGHT_TOP_CORNER, highlight,
&dirtyRegion);
} else {
decorator->SetRegionHighlight(
Decorator::REGION_RIGHT_BOTTOM_CORNER, highlight,
&dirtyRegion);
}
}
}
fWindow->ProcessDirtyRegion(dirtyRegion);
}
}
ServerCursor*
DefaultWindowBehaviour::_ResizeCursorFor(int8 horizontal, int8 vertical)
{
BCursorID cursorID = B_CURSOR_ID_SYSTEM_DEFAULT;
if (horizontal == LEFT) {
if (vertical == TOP)
cursorID = B_CURSOR_ID_RESIZE_NORTH_WEST;
else if (vertical == BOTTOM)
cursorID = B_CURSOR_ID_RESIZE_SOUTH_WEST;
else
cursorID = B_CURSOR_ID_RESIZE_WEST;
} else if (horizontal == RIGHT) {
if (vertical == TOP)
cursorID = B_CURSOR_ID_RESIZE_NORTH_EAST;
else if (vertical == BOTTOM)
cursorID = B_CURSOR_ID_RESIZE_SOUTH_EAST;
else
cursorID = B_CURSOR_ID_RESIZE_EAST;
} else {
if (vertical == TOP)
cursorID = B_CURSOR_ID_RESIZE_NORTH;
else if (vertical == BOTTOM)
cursorID = B_CURSOR_ID_RESIZE_SOUTH;
}
return fDesktop->GetCursorManager().GetCursor(cursorID);
}
void
DefaultWindowBehaviour::_SetResizeCursor(int8 horizontal, int8 vertical)
{
fDesktop->SetManagementCursor(_ResizeCursorFor(horizontal, vertical));
}
void
DefaultWindowBehaviour::_ResetResizeCursor()
{
fDesktop->SetManagementCursor(NULL);
}
void
DefaultWindowBehaviour::_ComputeResizeDirection(float x, float y,
int8& _horizontal, int8& _vertical)
{
_horizontal = NONE;
_vertical = NONE;
if (x == 0 && y == 0)
return;
float angle = atan2f(y, x);
angle += M_PI / 8;
if (angle < 0)
angle += M_PI * 2;
switch (int(angle / M_PI_4)) {
case 0:
_horizontal = RIGHT;
break;
case 1:
_horizontal = RIGHT;
_vertical = BOTTOM;
break;
case 2:
_vertical = BOTTOM;
break;
case 3:
_horizontal = LEFT;
_vertical = BOTTOM;
break;
case 4:
_horizontal = LEFT;
break;
case 5:
_horizontal = LEFT;
_vertical = TOP;
break;
case 6:
_vertical = TOP;
break;
case 7:
default:
_horizontal = RIGHT;
_vertical = TOP;
break;
}
}
void
DefaultWindowBehaviour::_NextState(State* state)
{
if (fState.IsSet())
fState->ExitState(state);
ObjectDeleter<State> oldState(fState.Detach());
fState.SetTo(state);
if (fState.IsSet()) {
fState->EnterState(oldState.Get());
fDesktop->SetMouseEventWindow(fWindow);
} else if (oldState.IsSet()) {
if (fDesktop->MouseEventWindow() == fWindow)
fDesktop->SetMouseEventWindow(NULL);
}
}