diff --git a/src/servers/app/Decorator.cpp b/src/servers/app/Decorator.cpp index e87e74ca45..943daa05f7 100644 --- a/src/servers/app/Decorator.cpp +++ b/src/servers/app/Decorator.cpp @@ -44,6 +44,10 @@ Decorator::Decorator(DesktopSettings& settings, BRect rect, window_look look, fFrame(rect), fResizeRect(), fBorderRect(), + fTabHighlighted(false), + fBordersHighlighted(false), + + fWindow(NULL), fClosePressed(false), fZoomPressed(false), @@ -580,3 +584,22 @@ void Decorator::_SetFocus() { } + + +void +Decorator::HighlightTab(bool active, BRegion* dirty) +{ +} + + +void +Decorator::HighlightBorders(bool active, BRegion* dirty) +{ +} + + +void +Decorator::SetWindow(Window* window) +{ + fWindow = window; +} diff --git a/src/servers/app/Decorator.h b/src/servers/app/Decorator.h index 1bb978e8b6..07042bf12c 100644 --- a/src/servers/app/Decorator.h +++ b/src/servers/app/Decorator.h @@ -21,6 +21,7 @@ class DesktopSettings; class DrawingEngine; class ServerFont; class BRegion; +class Window; enum click_type { @@ -44,6 +45,8 @@ enum click_type { CLICK_RESIZE_RB }; +#include "Window.h" + class Decorator { public: Decorator(DesktopSettings& settings, BRect rect, @@ -68,6 +71,14 @@ public: virtual void SetTitle(const char* string, BRegion* updateRegion = NULL); + virtual void HighlightTab(bool active, BRegion* dirty); + virtual void HighlightBorders(bool active, BRegion* dirty); + bool IsTabHighlighted() + { return fTabHighlighted; } + bool IsBordersHighlighted() + { return fBordersHighlighted; } + + window_look Look() const; uint32 Flags() const; @@ -121,6 +132,8 @@ public: rgb_color UIColor(color_which which); + void SetWindow(Window* window); + protected: int32 _TitleWidth() const { return fTitle.CountChars(); } @@ -151,6 +164,11 @@ protected: BRect fResizeRect; BRect fBorderRect; + bool fTabHighlighted; + bool fBordersHighlighted; + + Window* fWindow; + private: bool fClosePressed : 1; bool fZoomPressed : 1; diff --git a/src/servers/app/DefaultDecorator.cpp b/src/servers/app/DefaultDecorator.cpp index cc5108fd31..c2aa5378a7 100644 --- a/src/servers/app/DefaultDecorator.cpp +++ b/src/servers/app/DefaultDecorator.cpp @@ -32,13 +32,22 @@ #include "ServerBitmap.h" +// Toggle debug output //#define DEBUG_DECORATOR +//#define DEBUG_STACK_AND_TILE + #ifdef DEBUG_DECORATOR # define STRACE(x) printf x #else # define STRACE(x) ; #endif +#ifdef DEBUG_STACK_AND_TILE +# define STRACE_SAT(x) debug_printf x +#else +# define STRACE_SAT(x) ; +#endif + static inline uint8 blend_color_value(uint8 a, uint8 b, float position) @@ -69,22 +78,35 @@ DefaultDecorator::DefaultDecorator(DesktopSettings& settings, BRect rect, { _UpdateFont(settings); - // common colors to both focus and non focus state - fFrameColors[0] = (rgb_color){ 152, 152, 152, 255 }; - fFrameColors[1] = (rgb_color){ 240, 240, 240, 255 }; - fFrameColors[4] = (rgb_color){ 152, 152, 152, 255 }; - fFrameColors[5] = (rgb_color){ 108, 108, 108, 255 }; + // all colors are state based + fNonHighlightFrameColors[0] = (rgb_color){ 152, 152, 152, 255 }; + fNonHighlightFrameColors[1] = (rgb_color){ 240, 240, 240, 255 }; + fNonHighlightFrameColors[2] = (rgb_color){ 152, 152, 152, 255 }; + fNonHighlightFrameColors[3] = (rgb_color){ 108, 108, 108, 255 }; - // state based colors fFocusFrameColors[0] = (rgb_color){ 224, 224, 224, 255 }; fFocusFrameColors[1] = (rgb_color){ 208, 208, 208, 255 }; fNonFocusFrameColors[0] = (rgb_color){ 232, 232, 232, 255 }; fNonFocusFrameColors[1] = (rgb_color){ 216, 216, 216, 255 }; fNonFocusFrameColors[1] = fNonFocusFrameColors[0]; + fHighlightFrameColors[0] = (rgb_color){ 152, 0, 0, 255 }; + fHighlightFrameColors[1] = (rgb_color){ 240, 0, 0, 255 }; + fHighlightFrameColors[2] = (rgb_color){ 224, 0, 0, 255 }; + fHighlightFrameColors[3] = (rgb_color){ 208, 0, 0, 255 }; + fHighlightFrameColors[4] = (rgb_color){ 152, 0, 0, 255 }; + fHighlightFrameColors[5] = (rgb_color){ 108, 0, 0, 255 }; + + // initial colors + fFrameColors[0] = fNonHighlightFrameColors[0]; + fFrameColors[1] = fNonHighlightFrameColors[1]; + fFrameColors[4] = fNonHighlightFrameColors[2]; + fFrameColors[5] = fNonHighlightFrameColors[3]; + fFocusTabColor = settings.UIColor(B_WINDOW_TAB_COLOR); fFocusTextColor = settings.UIColor(B_WINDOW_TEXT_COLOR); fNonFocusTabColor = settings.UIColor(B_WINDOW_INACTIVE_TAB_COLOR); + fHighlightTabColor = (rgb_color){ 255, 0, 0, 255 }; fNonFocusTextColor = settings.UIColor(B_WINDOW_INACTIVE_TEXT_COLOR); fCloseBitmaps[0] = fCloseBitmaps[1] = fCloseBitmaps[2] = fCloseBitmaps[3] @@ -413,17 +435,190 @@ DefaultDecorator::SetTabLocation(float location, BRegion* updateRegion) bool DefaultDecorator::SetSettings(const BMessage& settings, BRegion* updateRegion) { + STRACE_SAT(("DefaultDecorator::SetSettings() on %s\n", fWindow->Title())); + float tabLocation; if (settings.FindFloat("tab location", &tabLocation) == B_OK) - return SetTabLocation(tabLocation, updateRegion); + SetTabLocation(tabLocation, updateRegion); - return false; + int32 windowId = 0; + if (settings.FindInt32("window id", &windowId) != B_OK) + return false; + fWindow->SetWindowId(windowId); + + STRACE_SAT(("\twindow id = %x\n", windowId)); + + // find id's of stacked windows and do the stacking + type_code typeFound; + int32 countFound; + settings.GetInfo("window id", &typeFound, &countFound); + settings.GetInfo("stacked windows", &typeFound, &countFound); + + // if stacked window ids are found, then use them to stack this window + if (typeFound == B_INT32_TYPE && countFound > 0) { + Window* windowToStackUnder = NULL; + + //This list contains all window id's that are supposed to be + //stacked with current window but aren't open + BList* persistentIdsToAdd = new BList(); + + for (int i = 0; i < countFound; i++) { + int32 id; + settings.FindInt32("stacked windows", i, &id); + + bool persist = true; + + // find a window (W) from the stack with an id from the list. + // if it is not this window itself, then stack it with this window + // UNLESS the W's stacking list doesn't contain this window, + // which implies W's been unstacked while this window was hidden + Window* window = fWindow->Desktop()->FindWindow(id); + if (window && window != fWindow && window->StackedWindowIds()) { + bool idExists = false; + for (int j = 0; !idExists && + j < window->StackedWindowIds()->CountItems(); j++) { + int32* stackedId = + static_cast(window->StackedWindowIds()->ItemAt(j)); + idExists = windowId == *stackedId; + } + if (idExists) { + if (!windowToStackUnder) { + //note this will execute only once during loop + windowToStackUnder = window; + } + } + else { + persist = false; + } + } + + if (persist) { + int32* idRef = static_cast(malloc(sizeof(int32))); + *idRef = id; + persistentIdsToAdd->AddItem(idRef); + } + } + + if (windowToStackUnder) { + fWindow->StackWindowBefore(windowToStackUnder); + windowToStackUnder->StackAndTile(); + } + else { + fWindow->InitStackedWindowIds(); + } + + for (int i = 0; i < fWindow->StackedWindowIds()->CountItems(); i++) { + int32* stackedId = + static_cast(fWindow->StackedWindowIds()->ItemAt(i)); + STRACE_SAT(("\tstackedWindowIds[%d]=%x\n", i, *stackedId)); + } + + //Add the remaining window id's to the persistent stacking list + //These are the ones that belong to currently unopened windows + for (int i = 0; i < persistentIdsToAdd->CountItems(); i++) { + int32* idRef = + static_cast(persistentIdsToAdd->ItemAt(i)); + bool idExists = false; + for (int j = 0; !idExists && + j < fWindow->StackedWindowIds()->CountItems(); j++) { + int32* stackedId = + static_cast(fWindow->StackedWindowIds()->ItemAt(j)); + idExists = *idRef == *stackedId; + } + if (!idExists) { + STRACE_SAT(("\t** window %x isn't open - but stacked\n", + *idRef)); + fWindow->StackedWindowIds()->AddItem(idRef); + } + } + } + + _SnapWindowFromSettings("snap left2left", SNAP_LEFT, SNAP_LEFT, + &settings); + _SnapWindowFromSettings("snap left2right", SNAP_LEFT, SNAP_RIGHT, + &settings); + _SnapWindowFromSettings("snap right2left", SNAP_RIGHT, SNAP_LEFT, + &settings); + _SnapWindowFromSettings("snap right2right", SNAP_RIGHT, SNAP_RIGHT, + &settings); + _SnapWindowFromSettings("snap top2top", SNAP_TOP, SNAP_TOP, &settings); + _SnapWindowFromSettings("snap top2bottom", SNAP_TOP, SNAP_BOTTOM, + &settings); + _SnapWindowFromSettings("snap bottom2top", SNAP_BOTTOM, SNAP_TOP, + &settings); + _SnapWindowFromSettings("snap bottom2bottom", SNAP_BOTTOM, SNAP_BOTTOM, + &settings); + + fWindow->StackAndTile(); + + STRACE_SAT(("Finished DefaultDecorator::SetSettings() on %s\n", + fWindow->Title())); + return true; +} + + +void +DefaultDecorator::_SnapWindowFromSettings(const char* label, + SnapOrientation thisSnapOrientation, + SnapOrientation otherSnapOrientation, + const BMessage* settings) +{ + type_code typeFound; + int32 countFound; + settings->GetInfo(label, &typeFound, &countFound); + if (typeFound == B_INT32_TYPE && countFound > 0) { + for (int i = 0; i < countFound; i++) { + int32 id; + settings->FindInt32(label, i, &id); + + Window* window = fWindow->Desktop()->FindWindow(id); + if (window == fWindow) { + continue; + } + + char* debug_suffix = "... NOT!"; + + if (window) { + //There can be cases where the other window to which this + //window's snapped doesn't contain a reference to this window + //in its snapping list. This would happen when the other window + //was de-snapped while this window was hidden. So only add the + //other window to this window's snapping list when such is not + //the case. + BList* otherList = window->GetSnappingList(otherSnapOrientation, + thisSnapOrientation, false); + if (!otherList) { + debug_suffix = "\n"; + continue; + } + for (int i = 0; i < otherList->CountItems(); i++) { + int32* snappedId = static_cast(otherList->ItemAt(i)); + if (*snappedId == fWindow->WindowId()) { + fWindow->SnapToWindow(window, thisSnapOrientation, + otherSnapOrientation); + debug_suffix = ""; + break; + } + } + } + else { //window isn't open - still retain snap id + fWindow->AddToSnappingList(id, thisSnapOrientation, + otherSnapOrientation); + debug_suffix = "... ?"; + } + + STRACE_SAT(("\t%s[%d]=%x", label, i, id)); + STRACE_SAT(("%s\n", debug_suffix)); + } + } } bool DefaultDecorator::GetSettings(BMessage* settings) const { + STRACE_SAT(("DefaultDecorator::GetSettings() on %s\n", fWindow->Title())); + if (!fTabRect.IsValid()) return false; @@ -433,13 +628,67 @@ DefaultDecorator::GetSettings(BMessage* settings) const if (settings->AddFloat("border width", fBorderWidth) != B_OK) return false; - return settings->AddFloat("tab location", (float)fTabOffset) == B_OK; + if (settings->AddFloat("tab location", (float)fTabOffset) != B_OK) + return false; + + if (settings->AddInt32("window id", fWindow->WindowId()) != B_OK) + return false; + + // store id's of stacked windows + if (!_StoreIntsInSettings("stacked windows", fWindow->StackedWindowIds(), + settings)) + return false; + + // store id's of snapped windows + if (!_StoreIntsInSettings("snap left2left", + fWindow->Left2LeftSnappingWindowIds(), settings)) + return false; + if (!_StoreIntsInSettings("snap left2right", + fWindow->Left2RightSnappingWindowIds(), settings)) + return false; + if (!_StoreIntsInSettings("snap right2right", + fWindow->Right2RightSnappingWindowIds(), settings)) + return false; + if (!_StoreIntsInSettings("snap right2left", + fWindow->Right2LeftSnappingWindowIds(), settings)) + return false; + if (!_StoreIntsInSettings("snap top2top", + fWindow->Top2TopSnappingWindowIds(), settings)) + return false; + if (!_StoreIntsInSettings("snap top2bottom", + fWindow->Top2BottomSnappingWindowIds(), settings)) + return false; + if (!_StoreIntsInSettings("snap bottom2top", + fWindow->Bottom2TopSnappingWindowIds(), settings)) + return false; + if (!_StoreIntsInSettings("snap bottom2bottom", + fWindow->Bottom2BottomSnappingWindowIds(), settings)) + return false; + + STRACE_SAT(("Finished DefaultDecorator::GetSettings() on %s\n", + fWindow->Title())); + return true; +} + + +bool +DefaultDecorator::_StoreIntsInSettings(const char* label, + BList* ids, BMessage* settings) const +{ + if (ids) { + for (int i = 0; i < ids->CountItems(); i++) { + int32* id = static_cast(ids->ItemAt(i)); + if (settings->AddInt32(label, *id) != B_OK) + return false; + STRACE_SAT(("\t%s[%d]=%x\n", label, i, *id)); + } + } + return true; } // #pragma mark - - void DefaultDecorator::Draw(BRect update) { @@ -1372,3 +1621,51 @@ DefaultDecorator::_GetBitmapForButton(int32 item, bool down, bool focus, sBitmapList = entry; return bitmap; } + + +void +DefaultDecorator::HighlightTab(bool active, BRegion* dirty) +{ + if (active) + fTabColor = fHighlightTabColor; + else if (IsFocus()) + fTabColor = fFocusTabColor; + else + fTabColor = fNonFocusTabColor; + dirty->Include(fTabRect); + fTabHighlighted = active; +} + + +void +DefaultDecorator::HighlightBorders(bool active, BRegion* dirty) +{ + if (active) { + fFrameColors[0] = fHighlightFrameColors[0]; + fFrameColors[1] = fHighlightFrameColors[1]; + fFrameColors[2] = fHighlightFrameColors[2]; + fFrameColors[3] = fHighlightFrameColors[3]; + fFrameColors[4] = fHighlightFrameColors[4]; + fFrameColors[5] = fHighlightFrameColors[5]; + } else if (IsFocus()) { + fFrameColors[0] = fNonHighlightFrameColors[0]; + fFrameColors[1] = fNonHighlightFrameColors[1]; + fFrameColors[2] = fFocusFrameColors[0]; + fFrameColors[3] = fFocusFrameColors[1]; + fFrameColors[4] = fNonHighlightFrameColors[2]; + fFrameColors[5] = fNonHighlightFrameColors[3]; + } else { + fFrameColors[0] = fNonHighlightFrameColors[0]; + fFrameColors[1] = fNonHighlightFrameColors[1]; + fFrameColors[2] = fNonFocusFrameColors[0]; + fFrameColors[3] = fNonFocusFrameColors[1]; + fFrameColors[4] = fNonHighlightFrameColors[2]; + fFrameColors[5] = fNonHighlightFrameColors[3]; + } + dirty->Include(fLeftBorder); + dirty->Include(fRightBorder); + dirty->Include(fTopBorder); + dirty->Include(fBottomBorder); + dirty->Include(fResizeRect); + fBordersHighlighted = active; +} diff --git a/src/servers/app/DefaultDecorator.h b/src/servers/app/DefaultDecorator.h index 2c53ef3a22..aa991b0381 100644 --- a/src/servers/app/DefaultDecorator.h +++ b/src/servers/app/DefaultDecorator.h @@ -10,10 +10,9 @@ #define DEFAULT_DECORATOR_H -#include - #include "Decorator.h" - +#include "Desktop.h" +#include class Desktop; class ServerBitmap; @@ -58,6 +57,9 @@ public: virtual click_type Clicked(BPoint pt, int32 buttons, int32 modifiers); + virtual void HighlightTab(bool active, BRegion* dirty); + virtual void HighlightBorders(bool active, BRegion* dirty); + protected: virtual void _DoLayout(); @@ -92,6 +94,7 @@ private: rgb_color fTabColor; rgb_color fFocusTabColor; rgb_color fNonFocusTabColor; + rgb_color fHighlightTabColor; rgb_color fTextColor; rgb_color fFocusTextColor; rgb_color fNonFocusTextColor; @@ -103,6 +106,8 @@ private: rgb_color fFrameColors[6]; rgb_color fFocusFrameColors[2]; rgb_color fNonFocusFrameColors[2]; + rgb_color fNonHighlightFrameColors[4]; + rgb_color fHighlightFrameColors[6]; bool fButtonFocus; ServerBitmap* fCloseBitmaps[4]; @@ -128,6 +133,14 @@ private: bigtime_t fLastClicked; bool fWasDoubleClick; + + //Stack & Tile specific private methods + bool _StoreIntsInSettings(const char* label, + BList* ids, BMessage* settings) const; + void _SnapWindowFromSettings(const char* label, + SnapOrientation thisSnapOrientation, + SnapOrientation otherSnapOrientation, + const BMessage* settings); }; #endif // DEFAULT_DECORATOR_H diff --git a/src/servers/app/Desktop.cpp b/src/servers/app/Desktop.cpp index 5b1809a94f..9047a4af34 100644 --- a/src/servers/app/Desktop.cpp +++ b/src/servers/app/Desktop.cpp @@ -50,6 +50,22 @@ #include "Workspace.h" #include "WorkspacesView.h" +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include + #if TEST_MODE # include "EventStream.h" #endif @@ -186,6 +202,30 @@ KeyboardFilter::Filter(BMessage* message, EventTarget** _target, fDesktop->SetWorkspaceAsync(-1); return B_SKIP_MESSAGE; } + + // switch between stacked windows + if (modifiers & B_OPTION_KEY) { + BList* stackedWindows = fDesktop->FocusWindow()->StackedWindows(); + if (key == 0x61 && stackedWindows) { + int32 oldIndex = + stackedWindows->IndexOf(fDesktop->FocusWindow()); + int32 newIndex = + (oldIndex - 1 >= 0)? + oldIndex - 1 : stackedWindows->CountItems() - 1; + fDesktop->ActivateWindow( + static_cast(stackedWindows->ItemAt(newIndex))); + return B_SKIP_MESSAGE; + } else if (key == 0x63 && stackedWindows) { + int32 oldIndex = + stackedWindows->IndexOf(fDesktop->FocusWindow()); + int32 newIndex = + (oldIndex + 1 < stackedWindows->CountItems())? + oldIndex + 1 : 0; + fDesktop->ActivateWindow( + static_cast(stackedWindows->ItemAt(newIndex))); + return B_SKIP_MESSAGE; + } + } } if (message->what == B_KEY_DOWN @@ -194,6 +234,15 @@ KeyboardFilter::Filter(BMessage* message, EventTarget** _target, || message->what == B_INPUT_METHOD_EVENT) _UpdateFocus(key, modifiers, _target); + // switch to and from stacking and snapping mode + if (message->what == B_MODIFIERS_CHANGED + && message->FindInt32("modifiers", &modifiers) == B_OK) { + // disable highlights if the stacking and snapping mode was just left + if (fDesktop->fIsStackingAndSnapping && !(modifiers & B_OPTION_KEY)) + fDesktop->FinishStackingAndSnapping(); + fDesktop->fIsStackingAndSnapping = modifiers & B_OPTION_KEY; + } + return B_DISPATCH_MESSAGE; } @@ -305,6 +354,9 @@ Desktop::Desktop(uid_t userID, const char* targetScreen) : MessageLooper("desktop"), + fIsStackingAndSnapping(false), + fStackAndTileSpec(new LinearSpec()), + fUserID(userID), fTargetScreen(strdup(targetScreen)), fSettings(NULL), @@ -348,6 +400,7 @@ Desktop::Desktop(uid_t userID, const char* targetScreen) Desktop::~Desktop() { + delete fStackAndTileSpec; delete fSettings; delete_area(fSharedReadOnlyArea); @@ -937,7 +990,7 @@ Desktop::ActivateWindow(Window* window) ShowWindow(window); } - if (window == FrontWindow()) { + if (window == FrontWindow() && !window->ForceActivate()) { // see if there is a normal B_AVOID_FRONT window still in front of us Window* avoidsFront = window->NextWindow(fCurrentWorkspace); while (avoidsFront && avoidsFront->IsNormal() @@ -961,6 +1014,55 @@ Desktop::ActivateWindow(Window* window) Window* frontmost = window->Frontmost(); + BList* stackedAndTiledWindows = new BList(); + + //Prepare to move tiled windows to the front as well + _AddWindowsByIdsToList(window->Left2LeftSnappingWindowIds(), + stackedAndTiledWindows); + _AddWindowsByIdsToList(window->Left2RightSnappingWindowIds(), + stackedAndTiledWindows); + _AddWindowsByIdsToList(window->Right2RightSnappingWindowIds(), + stackedAndTiledWindows); + _AddWindowsByIdsToList(window->Right2LeftSnappingWindowIds(), + stackedAndTiledWindows); + _AddWindowsByIdsToList(window->Top2TopSnappingWindowIds(), + stackedAndTiledWindows); + _AddWindowsByIdsToList(window->Top2BottomSnappingWindowIds(), + stackedAndTiledWindows); + _AddWindowsByIdsToList(window->Bottom2TopSnappingWindowIds(), + stackedAndTiledWindows); + _AddWindowsByIdsToList(window->Bottom2BottomSnappingWindowIds(), + stackedAndTiledWindows); + + bool forceDirty = false; + + //And then prepare to move stacked windows to the front + BList* stackedWindows = window->StackedWindows(); + if (stackedWindows) { + for (int i = 0; i < stackedWindows->CountItems(); i++) { + Window* stackedWindow = + static_cast(stackedWindows->ItemAt(i)); + if (stackedWindow != window + && !stackedAndTiledWindows->HasItem(stackedWindow)) { + stackedAndTiledWindows->AddItem(stackedWindow); + + //Basically if there are any stacked windows associated with + //this window, then designate this window as "dirty" so it is + //forced to repaint + forceDirty = true; + } + } + } + + //Do the actual moving here + for (int i = 0; i < stackedAndTiledWindows->CountItems(); i ++) { + Window* win = static_cast(stackedAndTiledWindows->ItemAt(i)); + _CurrentWindows().RemoveWindow(win); + windows.AddWindow(win); + } + + delete stackedAndTiledWindows; + _CurrentWindows().RemoveWindow(window); windows.AddWindow(window); @@ -984,13 +1086,29 @@ Desktop::ActivateWindow(Window* window) } } - _BringWindowsToFront(windows, kWorkingList, true); + _BringWindowsToFront(windows, kWorkingList, !forceDirty); if ((window->Flags() & B_AVOID_FOCUS) == 0) SetFocusWindow(window); } +bool +Desktop::_AddWindowsByIdsToList(BList* windowIdsToAdd, BList* windows) +{ + if (!windowIdsToAdd || !windows) + return false; + bool added = false; + for (int i = 0; i < windowIdsToAdd->CountItems(); i++) { + int32* id = static_cast(windowIdsToAdd->ItemAt(i)); + Window* windowToAdd = FindWindow(*id); + if (windowToAdd && !windows->HasItem(windowToAdd)) + windows->AddItem(windowToAdd); + } + return added; +} + + void Desktop::SendWindowBehind(Window* window, Window* behindOf) { @@ -3257,3 +3375,69 @@ Desktop::_SetWorkspace(int32 index) if (previousColor != fWorkspaces[fCurrentWorkspace].Color()) RedrawBackground(); } + + +WindowList& +Desktop::GetWindows() +{ + return _CurrentWindows(); +} + + +bool +Desktop::HighlightTab(Window* window, bool active) +{ + AutoWriteLocker _(fWindowLock); + + if (window->IsTabHighlighted() == active) + return false; + + BRegion dirty; + bool changed = window->HighlightTab(active, dirty); + if (changed) + _RebuildAndRedrawAfterWindowChange(window, dirty); + + return changed; +} + + +bool +Desktop::HighlightBorders(Window* window, bool active) +{ + AutoWriteLocker _(fWindowLock); + + if (window->IsBordersHighlighted() == active) + return false; + + BRegion dirty; + bool changed = window->HighlightBorders(active, dirty); + if (changed) + _RebuildAndRedrawAfterWindowChange(window, dirty); + + return changed; +} + + +void +Desktop::FinishStackingAndSnapping() +{ + for (Window* window = _CurrentWindows().LastWindow(); window != NULL; + window = window->PreviousWindow(fCurrentWorkspace)) { + HighlightTab(window, false); + HighlightBorders(window, false); + window->FinishStackingAndSnapping(); + } + fIsStackingAndSnapping = false; +} + + +Window* +Desktop::FindWindow(int32 windowId) +{ + for (Window* window = _CurrentWindows().LastWindow(); window != NULL; + window = window->PreviousWindow(fCurrentWorkspace)) { + if (window->WindowId() == windowId) + return window; + } + return NULL; +} \ No newline at end of file diff --git a/src/servers/app/Desktop.h b/src/servers/app/Desktop.h index cfd45215ba..8cf741392e 100644 --- a/src/servers/app/Desktop.h +++ b/src/servers/app/Desktop.h @@ -34,6 +34,7 @@ #include #include +#include "LinearSpec.h" #define USE_MULTI_LOCKER 1 @@ -241,6 +242,15 @@ public: void WriteWindowOrder(int32 workspace, BPrivate::LinkSender& sender); + WindowList& GetWindows(); + bool HighlightTab(Window* window, bool active); + bool HighlightBorders(Window* window, bool active); + void FinishStackingAndSnapping(); + Window* FindWindow(int32 windowId); + + bool fIsStackingAndSnapping; + LinearSpec* fStackAndTileSpec; + private: void _LaunchInputServer(); void _GetLooperName(char* name, size_t size); @@ -294,6 +304,9 @@ private: void _SetCurrentWorkspaceConfiguration(); void _SetWorkspace(int32 index); + bool _AddWindowsByIdsToList(BList* windowIdsToAdd, + BList* windows); + private: friend class DesktopSettings; friend class LockedDesktopSettings; diff --git a/src/servers/app/Jamfile b/src/servers/app/Jamfile index 00e0d60224..b86c9f99a0 100644 --- a/src/servers/app/Jamfile +++ b/src/servers/app/Jamfile @@ -1,6 +1,6 @@ SubDir HAIKU_TOP src servers app ; -UseLibraryHeaders agg ; +UseLibraryHeaders agg lp_solve linprog ; UsePrivateHeaders app graphics input interface kernel shared storage ; UseHeaders [ FDirName $(HAIKU_TOP) src servers app drawing ] ; @@ -66,6 +66,7 @@ Server app_server : libtranslation.so libbe.so libbnetapi.so libasdrawing.a libasremote.a libpainter.a libagg.a libfreetype.so libtextencoding.so libshared.a $(TARGET_LIBSTDC++) + liblpsolve55.so liblinprog.so : app_server.rdef ; diff --git a/src/servers/app/ServerWindow.cpp b/src/servers/app/ServerWindow.cpp index 53b745f2fd..74563c426a 100644 --- a/src/servers/app/ServerWindow.cpp +++ b/src/servers/app/ServerWindow.cpp @@ -895,6 +895,9 @@ ServerWindow::_DispatchMessage(int32 code, BPrivate::LinkReceiver &link) fDesktop->ResizeWindowBy(fWindow, xResizeTo - fWindow->Frame().Width(), yResizeTo - fWindow->Frame().Height()); + //Re-apply stack & tile constraints so that any other windows + //that are stacked/snapped against this window can be re-adjusted + fWindow->StackAndTile(); fLink.StartMessage(B_OK); // } fLink.Flush(); @@ -919,6 +922,9 @@ ServerWindow::_DispatchMessage(int32 code, BPrivate::LinkReceiver &link) } else { fDesktop->MoveWindowBy(fWindow, xMoveTo - fWindow->Frame().left, yMoveTo - fWindow->Frame().top); + //Re-apply stack & tile constraints so that any other windows + //that are stacked/snapped against this window can be re-adjusted + fWindow->StackAndTile(); fLink.StartMessage(B_OK); } fLink.Flush(); diff --git a/src/servers/app/Window.cpp b/src/servers/app/Window.cpp index 3d434542ba..58718bbf53 100644 --- a/src/servers/app/Window.cpp +++ b/src/servers/app/Window.cpp @@ -40,6 +40,7 @@ // Toggle debug output //#define DEBUG_WINDOW //#define DEBUG_WINDOW_CLICK +//#define DEBUG_STACK_AND_TILE #ifdef DEBUG_WINDOW # define STRACE(x) printf x @@ -53,6 +54,12 @@ # define STRACE_CLICK(x) ; #endif +#ifdef DEBUG_STACK_AND_TILE +# define STRACE_SAT(x) debug_printf x +#else +# define STRACE_SAT(x) ; +#endif + // IMPORTANT: nested LockSingleWindow()s are not supported (by MultiLocker) using std::nothrow; @@ -136,7 +143,43 @@ Window::Window(const BRect& frame, const char *name, fMinHeight(1), fMaxHeight(32768), - fWorkspacesViewCount(0) + fWorkspacesViewCount(0), + + fLeftVar(NULL), + fTopVar(NULL), + fRightVar(NULL), + fBottomVar(NULL), + fLeftConstraint(NULL), + fTopConstraint(NULL), + fMinWidthConstraint(NULL), + fMinHeightConstraint(NULL), + fWidthConstraint(NULL), + fHeightConstraint(NULL), + + fLeftAdjacentWindows(NULL), + fTopAdjacentWindows(NULL), + fRightAdjacentWindows(NULL), + fBottomAdjacentWindows(NULL), + fSnappingConstraints(NULL), + + fLeft2LeftSnappingWindowIds(NULL), + fLeft2RightSnappingWindowIds(NULL), + fRight2RightSnappingWindowIds(NULL), + fRight2LeftSnappingWindowIds(NULL), + fTop2TopSnappingWindowIds(NULL), + fTop2BottomSnappingWindowIds(NULL), + fBottom2BottomSnappingWindowIds(NULL), + fBottom2TopSnappingWindowIds(NULL), + + fWindowUnder(NULL), + fStackedWindows(NULL), + fPrevStackedWindows(NULL), + fLeftStackingConstraint(NULL), + fTopStackingConstraint(NULL), + fRightStackingConstraint(NULL), + fBottomStackingConstraint(NULL), + fId(0), + fStackedWindowIds(NULL) { // make sure our arguments are valid if (!IsValidLook(fLook)) @@ -182,6 +225,34 @@ Window::Window(const BRect& frame, const char *name, Window::~Window() { + // if the window still has stackedWindowIds but is not stacked anymore, + // clean up the stackedWindowIds (they are not used by other windows + // anymore) + if (fStackedWindowIds + && (!fStackedWindows || fStackedWindows->CountItems()==1)) { + for (int i = 0; i < fStackedWindowIds->CountItems(); i++) { + int32* id = static_cast(fStackedWindowIds->ItemAt(i)); + free(id); + } + delete fStackedWindowIds; + fStackedWindowIds = NULL; + } + + // clean up the entries in the snapping lists for each combo of + // snapping orientations + _FreeUpSnappingList(SNAP_LEFT, SNAP_LEFT, false); + _FreeUpSnappingList(SNAP_LEFT, SNAP_RIGHT, false); + _FreeUpSnappingList(SNAP_RIGHT, SNAP_LEFT, false); + _FreeUpSnappingList(SNAP_RIGHT, SNAP_RIGHT, false); + _FreeUpSnappingList(SNAP_TOP, SNAP_TOP, false); + _FreeUpSnappingList(SNAP_TOP, SNAP_BOTTOM, false); + _FreeUpSnappingList(SNAP_BOTTOM, SNAP_TOP, false); + _FreeUpSnappingList(SNAP_BOTTOM, SNAP_BOTTOM, false); + + // clean up all other stacking and snapping + _RemoveStackingAndSnapping(); + FinishStackingAndSnapping(); + if (fTopView) { fTopView->DetachedFromWindow(); delete fTopView; @@ -749,6 +820,7 @@ Window::InvalidateView(View* view, BRegion& viewRegion) } } + // DisableUpdateRequests void Window::DisableUpdateRequests() @@ -873,6 +945,16 @@ Window::MouseDown(BMessage* message, BPoint where, int32* _viewToken) fRegionPool.Recycle(visibleBorder); } + // Re-stack and re-snap the window if in stacking & snapping mode + if (fDesktop->fIsStackingAndSnapping) { + _RemoveStackingAndSnapping(); + _RemoveStackingPersistently(); + _RemoveSnappingPersistently(); + _CheckIfReadyToStack(); + _CheckIfReadyToSnap(); + } + + // based on what the Decorator returned, properly place this window. if (action == DEC_MOVETOBACK) { if (desktopSettings.MouseMode() == B_CLICK_TO_FOCUS_MOUSE) { bool covered = true; @@ -983,6 +1065,23 @@ Window::MouseUp(BMessage* message, BPoint where, int32* _viewToken) } } + // Snap or stack the window if in stack & tiling mode + if (fDesktop->fIsStackingAndSnapping) { + _RemoveStackingAndSnapping(); + _RemoveStackingPersistently(); + _RemoveSnappingPersistently(); + _CheckIfReadyToStack(); + _CheckIfReadyToSnap(); + _StackWindow(); + _SnapWindow(); + StackAndTile(); + fDesktop->FinishStackingAndSnapping(); + + //Activate this window so that others stacked/tiled along with it + //stay on top. + fForceActivate = true; + } + engine->UnlockParallelAccess(); fRegionPool.Recycle(visibleBorder); @@ -1000,10 +1099,15 @@ Window::MouseUp(BMessage* message, BPoint where, int32* _viewToken) } } + // This will be true right after stacking & tiling mode + if (fForceActivate) { + fDesktop->ActivateWindow(this); + fForceActivate = false; + } // in FFM mode, activate the window and bring it // to front in case this was a drag click but the // mouse was not moved - if (fActivateOnMouseUp) { + else if (fActivateOnMouseUp) { fActivateOnMouseUp = false; // on R5, there is a time window for this feature // ie, click and press too long, nothing will happen @@ -1111,7 +1215,20 @@ Window::MouseMoved(BMessage *message, BPoint where, int32* _viewToken, _AlterDeltaForSnap(delta, now); fDesktop->MoveWindowBy(this, delta.x, delta.y); - // constrain delta to true change in position + // Dragging in StackingAndSnapping mode means stacking and + // snapping of this window is removed and can be changed. + // Otherwise, dragging means the stack & tile constraints are + // solved and windows adjusted accordingly. + if (fDesktop->fIsStackingAndSnapping) { + _RemoveStackingAndSnapping(); + _RemoveStackingPersistently(); + _RemoveSnappingPersistently(); + _CheckIfReadyToStack(); + _CheckIfReadyToSnap(); + } else + StackAndTile(); + + // constrain delta to true change in size delta = fFrame.LeftTop() - oldLeftTop; } else delta = BPoint(0, 0); @@ -1128,6 +1245,18 @@ Window::MouseMoved(BMessage *message, BPoint where, int32* _viewToken, fDesktop->ResizeWindowBy(this, delta.x, delta.y); + // Resizing in StackingAndSnapping mode means stacking and + // snapping of this window is removed and can be changed. + // Otherwise, resizing means the stack & tile constraints are + // solved and windows adjusted accordingly. + if (fDesktop->fIsStackingAndSnapping) { + _RemoveStackingAndSnapping(); + _RemoveStackingPersistently(); + _RemoveSnappingPersistently(); + _CheckIfReadyToSnap(); + } else + StackAndTile(); + // constrain delta to true change in size delta = fFrame.RightBottom() - oldRightBottom; } else @@ -1426,6 +1555,7 @@ Window::SetDecoratorSettings(const BMessage& settings, BRegion& dirty) { bool ret = false; if (fDecorator) { + fDecorator->SetWindow(this); ret = fDecorator->SetSettings(settings, &dirty); // the border region changed if ret is true fBorderRegionValid = fBorderRegionValid && !ret; @@ -1437,8 +1567,10 @@ Window::SetDecoratorSettings(const BMessage& settings, BRegion& dirty) bool Window::GetDecoratorSettings(BMessage* settings) { - if (fDecorator) + if (fDecorator) { + fDecorator->SetWindow(this); return fDecorator->GetSettings(settings); + } return false; } @@ -2335,6 +2467,1650 @@ Window::UpdateSession::Exclude(BRegion* dirtyInNextSession) } +bool +Window::HighlightTab(bool active, BRegion& dirty) +{ + if (fDecorator) { + fDecorator->HighlightTab(active, &dirty); + fBorderRegionValid = false; + return true; + } + return false; +} + + +bool +Window::HighlightBorders(bool active, BRegion& dirty) +{ + if (fDecorator) { + fDecorator->HighlightBorders(active, &dirty); + fBorderRegionValid = false; + return true; + } + return false; +} + + +bool +Window::IsTabHighlighted() +{ + if (fDecorator) + return fDecorator->IsTabHighlighted(); + return false; +} + + +bool +Window::IsBordersHighlighted() +{ + if (fDecorator) + return fDecorator->IsBordersHighlighted(); + return false; +} + + +void +Window::_EnsureWindowWithinScreenBounds(Window* window, Window* detached) +{ + BRect screenFrame = Screen()->Frame(); + float deltaX = 0; + float deltaY = 0; + BList* windowsToRight = window->Right2LeftSnappingWindowIds(); + BList* windowsToLeft = window->Left2RightSnappingWindowIds(); + BList* windowsToTop = window->Bottom2TopSnappingWindowIds(); + BList* windowsToBottom = window->Top2BottomSnappingWindowIds(); + + if (window->Frame().left < 0 + && (!windowsToRight + || windowsToRight->IsEmpty() + || windowsToRight->CountItems() == 1 + && *static_cast(windowsToRight->ItemAt(0)) + == detached->WindowId())) { + //If window is off-screen to the left and there's no window tiled + //to its right that can still "save" it from going totally off-screen + deltaX = 6 - window->Frame().left; + } + else if (window->Frame().right > screenFrame.right + && (!windowsToLeft + || windowsToLeft->IsEmpty() + || windowsToLeft->CountItems() == 1 + && *static_cast(windowsToLeft->ItemAt(0)) + == detached->WindowId())) { + //If window is off-screen to the right and there's no window tiled + //to its left that can still "save" it from going totally off-screen + deltaX = screenFrame.right - window->Frame().right - 6; + } + if (window->Frame().top < 0 + && (!windowsToTop + || windowsToTop->IsEmpty() + || windowsToTop->CountItems() == 1 + && *static_cast(windowsToTop->ItemAt(0)) + == detached->WindowId())) { + //If window is off-screen to the top and there's no window tiled + //to its bottom that can still "save" it from going totally off-screen + deltaY = 27 - window->Frame().top; + } + else if (window->Frame().bottom > screenFrame.bottom + && (!windowsToBottom + || windowsToBottom->IsEmpty() + || windowsToBottom->CountItems() == 1 + && *static_cast(windowsToBottom->ItemAt(0)) + == detached->WindowId())) { + //If window is off-screen to the bottom and there's no window tiled + //to its top that can still "save" it from going totally off-screen + deltaY = screenFrame.bottom - window->Frame().bottom - 6; + } + if (deltaX != 0 || deltaY != 0) + fDesktop->MoveWindowBy(window, deltaX, deltaY); +} + + +void +Window::InitStackedWindowIds() +{ + if (!fStackedWindowIds) + fStackedWindowIds = new BList(); +} + + +/*! \brief Initializes all the Stack & Tile specific data for this window. + Unless this initialization is done, the methods for stacking and + snapping will not work. If the window is already initialized, the + method returns immediately. +*/ +void +Window::_InitStackingAndSnapping() +{ + STRACE_SAT(("Window::_InitStackingAndSnapping() on %s, fLeftVar=%d\n", + Title(), fLeftVar)); + + // if there are variables for the sides, then it is already initialized + if (fLeftVar) + return; + + fLeftVar = fDesktop->fStackAndTileSpec->AddVariable(); + fTopVar = fDesktop->fStackAndTileSpec->AddVariable(); + fRightVar = fDesktop->fStackAndTileSpec->AddVariable(); + fBottomVar = fDesktop->fStackAndTileSpec->AddVariable(); + BString label(Title()); + label << ".fLeftVar"; + fLeftVar->SetLabel(label.String()); + label = Title(); + label << ".fTopVar"; + fTopVar->SetLabel(label.String()); + label = Title(); + label << ".fRightVar"; + fRightVar->SetLabel(label.String()); + label = Title(); + label << ".fBottomVar"; + fBottomVar->SetLabel(label.String()); + + fLeftVar->SetRange(-DBL_MAX, DBL_MAX); + fTopVar->SetRange(-DBL_MAX, DBL_MAX); + fRightVar->SetRange(-DBL_MAX, DBL_MAX); + fBottomVar->SetRange(-DBL_MAX, DBL_MAX); + + fLeftConstraint = fDesktop->fStackAndTileSpec + ->AddConstraint(1.0, fLeftVar, + OperatorType(EQ), fFrame.left, 1, 1); + fTopConstraint = fDesktop->fStackAndTileSpec + ->AddConstraint(1.0, fTopVar, + OperatorType(EQ), fFrame.top, 1, 1); + label = Title(); + label << ".fLeftConstraint"; + fLeftConstraint->SetLabel(label.String()); + label = Title(); + label << ".fTopConstraint"; + fTopConstraint->SetLabel(label.String()); + + int32 minWidth, maxWidth, minHeight, maxHeight; + GetSizeLimits(&minWidth, &maxWidth, &minHeight, &maxHeight); + fMinWidthConstraint = fDesktop->fStackAndTileSpec + ->AddConstraint(1.0, fLeftVar, -1.0, fRightVar, + OperatorType(LE), -minWidth); + fMinHeightConstraint = fDesktop->fStackAndTileSpec + ->AddConstraint(1.0, fTopVar, -1.0, fBottomVar, + OperatorType(LE), -minHeight); + label = Title(); + label << ".fMinWidthConstraint"; + fMinWidthConstraint->SetLabel(label.String()); + label = Title(); + label << ".fMinHeightConstraint"; + fMinHeightConstraint->SetLabel(label.String()); + + // The width and height constraints have higher penalties than the + // position constraints (left, top), so a window will keep its size + // unless explicitly resized. + fWidthConstraint = fDesktop->fStackAndTileSpec + ->AddConstraint(-1.0, fLeftVar, 1.0, fRightVar, + OperatorType(EQ), fFrame.Width(), 10, 10); + fHeightConstraint = fDesktop->fStackAndTileSpec + ->AddConstraint(-1.0, fTopVar, 1.0, fBottomVar, + OperatorType(EQ), fFrame.Height(), 10, 10); + label = Title(); + label << ".fWidthConstraint"; + fWidthConstraint->SetLabel(label.String()); + label = Title(); + label << ".fHeightConstraint"; + fHeightConstraint->SetLabel(label.String()); + + fSnappingConstraints = new BList(); + + STRACE_SAT(("Finished Window::_InitStackingAndSnapping() on %s\n", + Title())); +} + + +/*! \brief Removes all the Stack & Tile specific data from this window, so that + the window is not stacked or snapped anymore. This method is called + before the Stack & Tile configuration mode, so that stacking and + snapping of a window can be set up from scratch. +*/ +void +Window::_RemoveStackingAndSnapping() +{ + STRACE_SAT(("Window::_RemoveStackingAndSnapping() on %s, fLeftVar=%d\n", + Title(), fLeftVar)); + + // if there are no variables for the sides, then it is not active + if (!fLeftVar) + return; + + delete fLeftConstraint; + delete fTopConstraint; + delete fMinWidthConstraint; + delete fMinHeightConstraint; + delete fWidthConstraint; + delete fHeightConstraint; + fLeftConstraint = NULL; + fTopConstraint = NULL; + fMinWidthConstraint = NULL; + fMinHeightConstraint = NULL; + fWidthConstraint = NULL; + fHeightConstraint = NULL; + + // remove stacking specific data + fPrevStackedWindows = fStackedWindows; + if (fStackedWindows) { + // remove constraints + delete fLeftStackingConstraint; + delete fTopStackingConstraint; + delete fRightStackingConstraint; + delete fBottomStackingConstraint; + fLeftStackingConstraint = NULL; + fTopStackingConstraint = NULL; + fRightStackingConstraint = NULL; + fBottomStackingConstraint = NULL; + + // if this is the first window in the stack, then the stacking + // constraints have to be changed to refer to the new first window + if (this == fStackedWindows->FirstItem()) { + Window* newFirstWindow = static_cast( + fStackedWindows->ItemAt(1)); + + // the first window does not need stacking constraints + delete newFirstWindow->fLeftStackingConstraint; + delete newFirstWindow->fTopStackingConstraint; + delete newFirstWindow->fRightStackingConstraint; + delete newFirstWindow->fBottomStackingConstraint; + newFirstWindow->fLeftStackingConstraint = NULL; + newFirstWindow->fTopStackingConstraint = NULL; + newFirstWindow->fRightStackingConstraint = NULL; + newFirstWindow->fBottomStackingConstraint = NULL; + + // the following windows need stacking constraints referring to + // the first window + for (int i = 2; i < fStackedWindows->CountItems(); i++) { + Window* window = static_cast( + fStackedWindows->ItemAt(i)); + window->fLeftStackingConstraint->SetLeftSide( + 1, window->fLeftVar, -1, newFirstWindow->fLeftVar); + window->fTopStackingConstraint->SetLeftSide( + 1, window->fTopVar, -1, newFirstWindow->fTopVar); + window->fRightStackingConstraint->SetLeftSide( + 1, window->fRightVar, -1, newFirstWindow->fRightVar); + window->fBottomStackingConstraint->SetLeftSide( + 1, window->fBottomVar, -1, newFirstWindow->fBottomVar); + } + } + + // remove this window from the stack + fStackedWindows->RemoveItem(this); + fStackedWindows = NULL; + } + + // remove snapping specific data + if (fSnappingConstraints) { + for (int i = 0; i < fSnappingConstraints->CountItems(); i++) + delete static_cast(fSnappingConstraints->ItemAt(i)); + fSnappingConstraints = NULL; + } + + // deleting variables also invalidates their constraints + // therefore we can delete them afterwards without harm + delete fLeftVar; + delete fTopVar; + delete fRightVar; + delete fBottomVar; + fLeftVar = NULL; + fTopVar = NULL; + fRightVar = NULL; + fBottomVar = NULL; + + //For each previously snapped window, make sure that it is within + //screen bounds, so as to prevent it from being lost off-screen + for (int i = 0; fLeft2LeftSnappingWindowIds + && i < fLeft2LeftSnappingWindowIds->CountItems(); i++) { + int32* id = static_cast(fLeft2LeftSnappingWindowIds->ItemAt(i)); + Window* win = fDesktop->FindWindow(*id); + if (win) { + _EnsureWindowWithinScreenBounds(win, this); + win->StackAndTile(); + } + } + for (int i = 0; fLeft2RightSnappingWindowIds + && i < fLeft2RightSnappingWindowIds->CountItems(); i++) { + int32* id = static_cast(fLeft2RightSnappingWindowIds->ItemAt(i)); + Window* win = fDesktop->FindWindow(*id); + if (win) { + _EnsureWindowWithinScreenBounds(win, this); + win->StackAndTile(); + } + } + for (int i = 0; fRight2LeftSnappingWindowIds + && i < fRight2LeftSnappingWindowIds->CountItems(); i++) { + int32* id = static_cast(fRight2LeftSnappingWindowIds->ItemAt(i)); + Window* win = fDesktop->FindWindow(*id); + if (win) { + _EnsureWindowWithinScreenBounds(win, this); + win->StackAndTile(); + } + } + for (int i = 0; fRight2RightSnappingWindowIds + && i < fRight2RightSnappingWindowIds->CountItems(); i++) { + int32* id = static_cast(fRight2RightSnappingWindowIds->ItemAt(i)); + Window* win = fDesktop->FindWindow(*id); + if (win) { + _EnsureWindowWithinScreenBounds(win, this); + win->StackAndTile(); + } + } + for (int i = 0; fTop2TopSnappingWindowIds + && i < fTop2TopSnappingWindowIds->CountItems(); i++) { + int32* id = static_cast(fTop2TopSnappingWindowIds->ItemAt(i)); + Window* win = fDesktop->FindWindow(*id); + if (win) { + _EnsureWindowWithinScreenBounds(win, this); + win->StackAndTile(); + } + } + for (int i = 0; fTop2BottomSnappingWindowIds + && i < fTop2BottomSnappingWindowIds->CountItems(); i++) { + int32* id = static_cast(fTop2BottomSnappingWindowIds->ItemAt(i)); + Window* win = fDesktop->FindWindow(*id); + if (win) { + _EnsureWindowWithinScreenBounds(win, this); + win->StackAndTile(); + } + } + for (int i = 0; fBottom2TopSnappingWindowIds + && i < fBottom2TopSnappingWindowIds->CountItems(); i++) { + int32* id = static_cast(fBottom2TopSnappingWindowIds->ItemAt(i)); + Window* win = fDesktop->FindWindow(*id); + if (win) { + _EnsureWindowWithinScreenBounds(win, this); + win->StackAndTile(); + } + } + for (int i = 0; fBottom2BottomSnappingWindowIds + && i < fBottom2BottomSnappingWindowIds->CountItems(); i++) { + int32* id = static_cast(fBottom2BottomSnappingWindowIds->ItemAt(i)); + Window* win = fDesktop->FindWindow(*id); + if (win) { + _EnsureWindowWithinScreenBounds(win, this); + win->StackAndTile(); + } + } + + STRACE_SAT(("Finished Window::_RemoveStackingAndSnapping() on %s\n", + Title())); +} + + +/*! \brief Looks for a suitable stacking candidate by checking which window + tabs overlap with this window's tab, and gives visual feedback by + adjusting window tab highlights. This method is called during the + Stack & Tile configuration mode. +*/ +void +Window::_CheckIfReadyToStack() +{ + // stacking only works if this window has a tab + if (!fDecorator) + return; + + Window* prevWindowUnder = fWindowUnder; + fWindowUnder = NULL; + + // search for stacking candidate + BRect thisTabRect( + fDecorator->TabRect().left, fDecorator->TabRect().top, + fDecorator->TabRect().right + 1, fDecorator->TabRect().bottom); + for (Window* window = fDesktop->GetWindows().LastWindow(); + window; + window = window->PreviousWindow(fCurrentWorkspace)) { + if (window != this + && !window->IsHidden() + && window->Decorator() + && thisTabRect.Intersects(window->Decorator()->TabRect())) { + + // remember window as the candidate for stacking + fWindowUnder = window; + break; + } + } + + // if the stacking candidate has changed, change tab highlights + if(prevWindowUnder != fWindowUnder) { + if(!prevWindowUnder) { // candidate found for the first time + fDesktop->HighlightTab(this, true); + fDesktop->HighlightTab(fWindowUnder, true); + } else if(!fWindowUnder) { // no candidate after there was one + fDesktop->HighlightTab(this, false); + fDesktop->HighlightTab(prevWindowUnder, false); + } else { // changing from one to another candidate + fDesktop->HighlightTab(prevWindowUnder, false); + fDesktop->HighlightTab(fWindowUnder, true); + } + } +} + + +/*! \brief Stacks this window to the current candidate window for stacking + and adjusts the window tabs of the stack. Afterwards the stacking + candidate is cleared. +*/ +void +Window::_StackWindow() +{ + STRACE_SAT(("Window::_StackWindow() on %s, fWindowUnder=%x\n", + Title(), fWindowUnder)); + + // stack only if there is a candidate window for stacking + if (fWindowUnder == NULL) + return; + + // align this window with the stack + fDesktop->MoveWindowBy(this, + fWindowUnder->Frame().left - Frame().left, + fWindowUnder->Frame().top - Frame().top); + fDesktop->ResizeWindowBy(this, + fWindowUnder->Frame().right - Frame().right, + fWindowUnder->Frame().bottom - Frame().bottom); + + // make sure there is a stacked window list + BList* stack = fWindowUnder->fStackedWindows; + if (!stack) { + stack = new BList(); + stack->AddItem(fWindowUnder); + fWindowUnder->fStackedWindows = stack; + } + + // make sure there is a stacked window id list + BList* stackIds = fWindowUnder->fStackedWindowIds; + if (!stackIds) { + stackIds = new BList(); + int32* id = static_cast(malloc(sizeof(int32))); + *id = fWindowUnder->WindowId(); + stackIds->AddItem(id); + fWindowUnder->fStackedWindowIds = stackIds; + } + + // add this window's id to the stacked window id list - ONLY + // if not already contained + bool idExists = false; + for (int i = 0; !idExists && i < stackIds->CountItems(); i++) { + int32* stackedId = static_cast(stackIds->ItemAt(i)); + idExists = *stackedId == WindowId(); + } + if (!idExists) { + int32* id = static_cast(malloc(sizeof(int32))); + *id = WindowId(); + stackIds->AddItem(id); + } + fStackedWindowIds = stackIds; + + // add this window to the stack before the windowUnder, + // rearrange the window tabs and initialize constraints + stack->AddItem(this, stack->IndexOf(fWindowUnder)); + fStackedWindows = stack; + _ArrangeStackedWindowTabs(); + _InitStackingAndSnapping(); + + // set up stacking constraints + Window* firstWindow = static_cast(stack->FirstItem()); + if (this != firstWindow) { + // if this is not the first window, then add stacking constraints + // referring to the first window in the stack + firstWindow->_InitStackingAndSnapping(); + fLeftStackingConstraint = fLeftVar->IsEqual(firstWindow->fLeftVar); + fTopStackingConstraint = fTopVar->IsEqual(firstWindow->fTopVar); + fRightStackingConstraint = fRightVar->IsEqual(firstWindow->fRightVar); + fBottomStackingConstraint = fBottomVar->IsEqual(firstWindow->fBottomVar); + BString label("stacking of "); + label << Title(); + fLeftStackingConstraint->SetLabel(label.String()); + fTopStackingConstraint->SetLabel(label.String()); + fRightStackingConstraint->SetLabel(label.String()); + fBottomStackingConstraint->SetLabel(label.String()); + } else { + // if this is the first window in the stack, then the stacking + // constraints of the following windows have to be changed to refer + // to this window + for (int i = 1; i < fStackedWindows->CountItems(); i++) { + Window* window = static_cast( + fStackedWindows->ItemAt(i)); + window->_InitStackingAndSnapping(); + + // either create new stacking constraints + // or just change the existing ones + if (!window->fLeftStackingConstraint) { + window->fLeftStackingConstraint = + window->fLeftVar->IsEqual(fLeftVar); + window->fTopStackingConstraint = + window->fTopVar->IsEqual(fTopVar); + window->fRightStackingConstraint = + window->fRightVar->IsEqual(fRightVar); + window->fBottomStackingConstraint = + window->fBottomVar->IsEqual(fBottomVar); + BString label("stacking of "); + label << Title(); + window->fLeftStackingConstraint->SetLabel(label.String()); + window->fTopStackingConstraint->SetLabel(label.String()); + window->fRightStackingConstraint->SetLabel(label.String()); + window->fBottomStackingConstraint->SetLabel(label.String()); + } else { + window->fLeftStackingConstraint->SetLeftSide( + 1, window->fLeftVar, -1, fLeftVar); + window->fTopStackingConstraint->SetLeftSide( + 1, window->fTopVar, -1, fTopVar); + window->fRightStackingConstraint->SetLeftSide( + 1, window->fRightVar, -1, fRightVar); + window->fBottomStackingConstraint->SetLeftSide( + 1, window->fBottomVar, -1, fBottomVar); + } + } + } + + // clear stacking candidate + fWindowUnder = NULL; + + STRACE_SAT(("Finished Window::_StackWindow() on %s\n", Title())); +} + + +/*! \brief Stacks this window onto the stack of the given window so that the + tab of this window is before the tab of the given window. + + \param window the window with the stack where this window will be added. +*/ +void +Window::StackWindowBefore(Window* window) +{ + fWindowUnder = window; + _StackWindow(); +} + + +/*! \brief Arranges the tab locations of the windows that are in the stack of + this window. +*/ +void +Window::_ArrangeStackedWindowTabs() +{ + STRACE_SAT(("Window::_ArrangeStackedWindowTabs() on %s, fStackedWindows=%x\n", + Title(), fStackedWindows)); + if (!fStackedWindows) + return; + + // adjust tab locations of stacked windows + float location = 0; + for (int i = 0; i < fStackedWindows->CountItems(); i++) { + Window* window = static_cast(fStackedWindows->ItemAt(i)); + fDesktop->SetWindowTabLocation(window, location); + STRACE_SAT(("\t%s.tabLocation = %f\n", window->Title(), location)); + location = window->TabLocation() + + window->Decorator()->TabRect().Width() + 1; + } + STRACE_SAT(("Finished Window::_ArrangeStackedWindowTabs() on %s\n", + Title())); +} + + +/*! \brief Compiles suitable snapping candidates by checking which windows + are adjacent to this window, and gives visual feedback by adjusting + window border highlights. This method is called during the Stack & Tile + configuration mode. +*/ +void +Window::_CheckIfReadyToSnap() +{ + if (!fLeftAdjacentWindows) { + fLeftAdjacentWindows = new BList(); + fTopAdjacentWindows = new BList(); + fRightAdjacentWindows = new BList(); + fBottomAdjacentWindows = new BList(); + } + + // remember previous adjacent windows for changing highlights later + BList prevLeftAdjacentWindows(*fLeftAdjacentWindows); + BList prevTopAdjacentWindows(*fTopAdjacentWindows); + BList prevRightAdjacentWindows(*fRightAdjacentWindows); + BList prevBottomAdjacentWindows(*fBottomAdjacentWindows); + + // clear lists of old adjacent windows + fLeftAdjacentWindows->MakeEmpty(); + fTopAdjacentWindows->MakeEmpty(); + fRightAdjacentWindows->MakeEmpty(); + fBottomAdjacentWindows->MakeEmpty(); + + // go through all windows and look for snapping candidates + for (Window* window2 = fDesktop->GetWindows().LastWindow(); + window2; + window2 = window2->PreviousWindow(fCurrentWorkspace)) { + if (!window2->IsHidden() + && this != window2 // don't snap window to itself + && window2 != fWindowUnder // don't snap with stacking candidate + && !(strcmp(window2->Title(), "Deskbar")==0) + && !(strcmp(window2->Title(), "Desktop")==0)) { + + // is window2 adjacent on the left of this window? + BRect thisLeft( + this->Frame().left - 12, this->Frame().top + 12, + this->Frame().left + 6, this->Frame().bottom - 12); + BRect window2Right( + window2->Frame().right - 6, window2->Frame().top + 12, + window2->Frame().right + 12, window2->Frame().bottom - 12); + if (thisLeft.Intersects(window2Right)) + fLeftAdjacentWindows->AddItem(window2); + + // is window2 adjacent on the top of this window? + BRect thisTop( + this->Frame().left + 12, this->Frame().top - 22, + this->Frame().right - 12, this->Frame().top + 6); + BRect window2Bottom( + window2->Frame().left + 12, window2->Frame().bottom - 6, + window2->Frame().right - 12, window2->Frame().bottom + 12); + if (thisTop.Intersects(window2Bottom)) + fTopAdjacentWindows->AddItem(window2); + + // is window2 adjacent on the right of this window? + BRect thisRight( + this->Frame().right - 6, this->Frame().top + 12, + this->Frame().right + 12, this->Frame().bottom - 12); + BRect window2Left( + window2->Frame().left - 12, window2->Frame().top + 12, + window2->Frame().left + 6, window2->Frame().bottom - 12); + if (thisRight.Intersects(window2Left)) + fRightAdjacentWindows->AddItem(window2); + + // is window2 adjacent on the bottom of this window? + BRect thisBottom( + this->Frame().left + 12, this->Frame().bottom - 6, + this->Frame().right - 12, this->Frame().bottom + 12); + BRect window2Top( + window2->Frame().left + 12, window2->Frame().top - 22, + window2->Frame().right - 12, window2->Frame().top + 6); + if (thisBottom.Intersects(window2Top)) + fBottomAdjacentWindows->AddItem(window2); + } + } + + // change window border highlight of this window + if (!fLeftAdjacentWindows->IsEmpty() + || !fTopAdjacentWindows->IsEmpty() + || !fRightAdjacentWindows->IsEmpty() + || !fBottomAdjacentWindows->IsEmpty()) + fDesktop->HighlightBorders(this, true); + else + fDesktop->HighlightBorders(this, false); + + // switch off border highlights of windows that are not candidates any more + for (int i = 0; i < prevLeftAdjacentWindows.CountItems(); i++) { + Window* window = static_cast( + prevLeftAdjacentWindows.ItemAt(i)); + if (!fLeftAdjacentWindows->HasItem(window)) + fDesktop->HighlightBorders(window, false); + } + for (int i = 0; i < prevTopAdjacentWindows.CountItems(); i++) { + Window* window = static_cast( + prevTopAdjacentWindows.ItemAt(i)); + if (!fTopAdjacentWindows->HasItem(window)) + fDesktop->HighlightBorders(window, false); + } + for (int i = 0; i < prevRightAdjacentWindows.CountItems(); i++) { + Window* window = static_cast( + prevRightAdjacentWindows.ItemAt(i)); + if (!fRightAdjacentWindows->HasItem(window)) + fDesktop->HighlightBorders(window, false); + } + for (int i = 0; i < prevBottomAdjacentWindows.CountItems(); i++) { + Window* window = static_cast( + prevBottomAdjacentWindows.ItemAt(i)); + if (!fBottomAdjacentWindows->HasItem(window)) + fDesktop->HighlightBorders(window, false); + } + + // switch on border highlights of candidate windows + // if they are already on, HighlightBorders will detect this + for (int i = 0; i < fLeftAdjacentWindows->CountItems(); i++) + fDesktop->HighlightBorders( + static_cast(fLeftAdjacentWindows->ItemAt(i)), true); + for (int i = 0; i < fTopAdjacentWindows->CountItems(); i++) + fDesktop->HighlightBorders( + static_cast(fTopAdjacentWindows->ItemAt(i)), true); + for (int i = 0; i < fRightAdjacentWindows->CountItems(); i++) + fDesktop->HighlightBorders( + static_cast(fRightAdjacentWindows->ItemAt(i)), true); + for (int i = 0; i < fBottomAdjacentWindows->CountItems(); i++) + fDesktop->HighlightBorders( + static_cast(fBottomAdjacentWindows->ItemAt(i)), true); +} + + +/*! \brief Snaps this window to the current candidate windows for snapping + on each side. Afterwards snapping candidates are cleared. +*/ +void Window::_SnapWindow() +{ + BRect bounds; + Window* leftmostWindow; + Window* topmostWindow; + Window* rightmostWindow; + Window* bottommostWindow; + + // snap other windows to the left border + if (!fLeftAdjacentWindows->IsEmpty()) { + bounds = _BoundingRectAndWindows(fLeftAdjacentWindows, + &leftmostWindow, &topmostWindow, + &rightmostWindow, &bottommostWindow); + + // adjust window position & initialize snapping + fDesktop->MoveWindowBy(this, bounds.right - Frame().left + 11, 0); + _InitStackingAndSnapping(); + topmostWindow->_InitStackingAndSnapping(); + bottommostWindow->_InitStackingAndSnapping(); + + // adjust top border + if (fTopAdjacentWindows->IsEmpty()) { + fDesktop->MoveWindowBy(this, 0, bounds.top - Frame().top); + Constraint* topSnapping = fTopVar->IsEqual(topmostWindow->fTopVar); + BString label("topSnapping of "); + label << Title(); + topSnapping->SetLabel(label.String()); + fSnappingConstraints->AddItem(topSnapping); + + AddToSnappingList(topmostWindow, SNAP_TOP, SNAP_TOP); + AddToSnappingList(topmostWindow, SNAP_LEFT, SNAP_RIGHT); + topmostWindow->AddToSnappingList(this, SNAP_TOP, SNAP_TOP); + topmostWindow->AddToSnappingList(this, SNAP_RIGHT, SNAP_LEFT); + + STRACE_SAT(("Adding %s[%d] to %s[%d]'s T2T & L2R list\n", + topmostWindow->Title(), topmostWindow->WindowId(), + Title(), WindowId())); + } + + // adjust bottom border + if (fBottomAdjacentWindows->IsEmpty()) { + fDesktop->ResizeWindowBy(this, 0, bounds.bottom - Frame().bottom); + Constraint* bottomSnapping = + fBottomVar->IsEqual(bottommostWindow->fBottomVar); + BString label("bottomSnapping of "); + label << Title(); + bottomSnapping->SetLabel(label.String()); + fSnappingConstraints->AddItem(bottomSnapping); + + AddToSnappingList(bottommostWindow, SNAP_BOTTOM, SNAP_BOTTOM); + AddToSnappingList(bottommostWindow, SNAP_LEFT, SNAP_RIGHT); + bottommostWindow->AddToSnappingList(this, SNAP_BOTTOM, SNAP_BOTTOM); + bottommostWindow->AddToSnappingList(this, SNAP_RIGHT, SNAP_LEFT); + + STRACE_SAT(("Adding %s[%d] to %s[%d]'s B2B & L2R list\n", + bottommostWindow->Title(), bottommostWindow->WindowId(), + Title(), WindowId())); + } + + // adjust other windows + for (int i = 0; i < fLeftAdjacentWindows->CountItems(); i++) { + Window* window = static_cast( + fLeftAdjacentWindows->ItemAt(i)); + window->_InitStackingAndSnapping(); + Constraint* leftSnapping = + fDesktop->fStackAndTileSpec->AddConstraint( + -1, window->fRightVar, 1, fLeftVar, + OperatorType(EQ), 11); + BString label("leftSnapping of "); + label << Title(); + leftSnapping->SetLabel(label.String()); + window->fSnappingConstraints->AddItem(leftSnapping); + + AddToSnappingList(window, SNAP_LEFT, SNAP_RIGHT); + window->AddToSnappingList(this, SNAP_RIGHT, SNAP_LEFT); + + STRACE_SAT(("Adding %s[%d] to %s[%d]'s L2R list\n", + window->Title(), window->WindowId(), Title(), + WindowId())); + } + } + + // snap other windows to the top border + if (!fTopAdjacentWindows->IsEmpty()) { + bounds = _BoundingRectAndWindows(fTopAdjacentWindows, + &leftmostWindow, &topmostWindow, + &rightmostWindow, &bottommostWindow); + + // adjust window position & initialize snapping + fDesktop->MoveWindowBy(this, 0, bounds.bottom - Frame().top + 32); + _InitStackingAndSnapping(); + leftmostWindow->_InitStackingAndSnapping(); + rightmostWindow->_InitStackingAndSnapping(); + + // adjust left border + if (fLeftAdjacentWindows->IsEmpty()) { + fDesktop->MoveWindowBy(this, bounds.left - Frame().left, 0); + Constraint* leftSnapping = + fLeftVar->IsEqual(leftmostWindow->fLeftVar); + BString label("leftSnapping of "); + label << Title(); + leftSnapping->SetLabel(label.String()); + fSnappingConstraints->AddItem(leftSnapping); + + AddToSnappingList(leftmostWindow, SNAP_LEFT, SNAP_LEFT); + AddToSnappingList(leftmostWindow, SNAP_TOP, SNAP_BOTTOM); + leftmostWindow->AddToSnappingList(this, SNAP_LEFT, SNAP_LEFT); + leftmostWindow->AddToSnappingList(this, SNAP_BOTTOM, SNAP_TOP); + + STRACE_SAT(("Adding %s[%d] to %s[%d]'s L2L & T2B list\n", + leftmostWindow->Title(), leftmostWindow->WindowId(), + Title(), WindowId())); + } + + // adjust right border + if (fRightAdjacentWindows->IsEmpty()) { + fDesktop->ResizeWindowBy(this, bounds.right - Frame().right, 0); + Constraint* rightSnapping = + fRightVar->IsEqual(rightmostWindow->fRightVar); + BString label("rightSnapping of "); + label << Title(); + rightSnapping->SetLabel(label.String()); + fSnappingConstraints->AddItem(rightSnapping); + + AddToSnappingList(rightmostWindow, SNAP_RIGHT, SNAP_RIGHT); + AddToSnappingList(rightmostWindow, SNAP_TOP, SNAP_BOTTOM); + rightmostWindow->AddToSnappingList(this, SNAP_RIGHT, SNAP_RIGHT); + rightmostWindow->AddToSnappingList(this, SNAP_BOTTOM, SNAP_TOP); + + STRACE_SAT(("Adding %s[%d] to %s[%d]'s R2R & T2B list\n", + rightmostWindow->Title(), rightmostWindow->WindowId(), + Title(), WindowId())); + } + + // adjust other windows + for (int i = 0; i < fTopAdjacentWindows->CountItems(); i++) { + Window* window = static_cast( + fTopAdjacentWindows->ItemAt(i)); + window->_InitStackingAndSnapping(); + Constraint* topSnapping = + fDesktop->fStackAndTileSpec->AddConstraint( + -1, window->fBottomVar, 1, fTopVar, + OperatorType(EQ), 32); + BString label("topSnapping of "); + label << Title(); + topSnapping->SetLabel(label.String()); + window->fSnappingConstraints->AddItem(topSnapping); + + AddToSnappingList(window, SNAP_TOP, SNAP_BOTTOM); + window->AddToSnappingList(this, SNAP_BOTTOM, SNAP_TOP); + + STRACE_SAT(("Adding %s[%d] to %s[%d]'s T2B list\n", + window->Title(), window->WindowId(), Title(), + WindowId())); + } + } + + // snap other windows to the right border + if (!fRightAdjacentWindows->IsEmpty()) { + bounds = _BoundingRectAndWindows(fRightAdjacentWindows, + &leftmostWindow, &topmostWindow, + &rightmostWindow, &bottommostWindow); + + // adjust window position & initialize snapping + fDesktop->MoveWindowBy(this, bounds.left - Frame().right - 11, 0); + _InitStackingAndSnapping(); + topmostWindow->_InitStackingAndSnapping(); + bottommostWindow->_InitStackingAndSnapping(); + + // adjust top border + if (fTopAdjacentWindows->IsEmpty()) { + fDesktop->MoveWindowBy(this, 0, bounds.top - Frame().top); + Constraint* topSnapping = + fTopVar->IsEqual(topmostWindow->fTopVar); + BString label("topSnapping of "); + label << Title(); + topSnapping->SetLabel(label.String()); + fSnappingConstraints->AddItem(topSnapping); + + AddToSnappingList(topmostWindow, SNAP_TOP, SNAP_TOP); + AddToSnappingList(topmostWindow, SNAP_RIGHT, SNAP_LEFT); + topmostWindow->AddToSnappingList(this, SNAP_TOP, SNAP_TOP); + topmostWindow->AddToSnappingList(this, SNAP_LEFT, SNAP_RIGHT); + + STRACE_SAT(("Adding %s[%d] to %s[%d]'s T2T & R2L list\n", + topmostWindow->Title(), topmostWindow->WindowId(), + Title(), WindowId())); + } + + // adjust bottom border + if (fBottomAdjacentWindows->IsEmpty()) { + fDesktop->ResizeWindowBy(this, 0, bounds.bottom - Frame().bottom); + Constraint* bottomSnapping = + fBottomVar->IsEqual(bottommostWindow->fBottomVar); + BString label("bottomSnapping of "); + label << Title(); + bottomSnapping->SetLabel(label.String()); + fSnappingConstraints->AddItem(bottomSnapping); + + AddToSnappingList(bottommostWindow, SNAP_BOTTOM, SNAP_BOTTOM); + AddToSnappingList(bottommostWindow, SNAP_RIGHT, SNAP_LEFT); + bottommostWindow->AddToSnappingList(this, SNAP_BOTTOM, SNAP_BOTTOM); + bottommostWindow->AddToSnappingList(this, SNAP_LEFT, SNAP_RIGHT); + + STRACE_SAT(("Adding %s[%d] to %s[%d]'s B2B & R2L list\n", + bottommostWindow->Title(), bottommostWindow->WindowId(), + Title(), WindowId())); + } + + // adjust other windows + for (int i = 0; i < fRightAdjacentWindows->CountItems(); i++) { + Window* window = static_cast( + fRightAdjacentWindows->ItemAt(i)); + window->_InitStackingAndSnapping(); + Constraint* rightSnapping = + fDesktop->fStackAndTileSpec->AddConstraint( + -1, fRightVar, 1, window->fLeftVar, + OperatorType(EQ), 11); + BString label("rightSnapping of "); + label << Title(); + rightSnapping->SetLabel(label.String()); + window->fSnappingConstraints->AddItem(rightSnapping); + + AddToSnappingList(window, SNAP_RIGHT, SNAP_LEFT); + window->AddToSnappingList(this, SNAP_LEFT, SNAP_RIGHT); + + STRACE_SAT(("Adding %s[%d] to %s[%d]'s R2L list\n", + window->Title(), window->WindowId(), Title(), + WindowId())); + } + } + + // snap other windows to the bottom border + if (!fBottomAdjacentWindows->IsEmpty()) { + bounds = _BoundingRectAndWindows(fBottomAdjacentWindows, + &leftmostWindow, &topmostWindow, + &rightmostWindow, &bottommostWindow); + + // adjust window position & initialize snapping + fDesktop->MoveWindowBy(this, 0, bounds.top - Frame().bottom - 32); + _InitStackingAndSnapping(); + leftmostWindow->_InitStackingAndSnapping(); + rightmostWindow->_InitStackingAndSnapping(); + + // adjust left border + if (fLeftAdjacentWindows->IsEmpty()) { + fDesktop->MoveWindowBy(this, bounds.left - Frame().left, 0); + Constraint* leftSnapping = + fLeftVar->IsEqual(leftmostWindow->fLeftVar); + BString label("leftSnapping of "); + label << Title(); + leftSnapping->SetLabel(label.String()); + fSnappingConstraints->AddItem(leftSnapping); + + AddToSnappingList(leftmostWindow, SNAP_LEFT, SNAP_LEFT); + AddToSnappingList(leftmostWindow, SNAP_BOTTOM, SNAP_TOP); + leftmostWindow->AddToSnappingList(this, SNAP_LEFT, SNAP_LEFT); + leftmostWindow->AddToSnappingList(this, SNAP_TOP, SNAP_BOTTOM); + + STRACE_SAT(("Adding %s[%d] to %s[%d]'s L2L & B2T list\n", + leftmostWindow->Title(), leftmostWindow->WindowId(), + Title(), WindowId())); + } + + // adjust right border + if (fRightAdjacentWindows->IsEmpty()) { + fDesktop->ResizeWindowBy(this, bounds.right - Frame().right, 0); + Constraint* rightSnapping = + fRightVar->IsEqual(rightmostWindow->fRightVar); + BString label("rightSnapping of "); + label << Title(); + rightSnapping->SetLabel(label.String()); + fSnappingConstraints->AddItem(rightSnapping); + + AddToSnappingList(rightmostWindow, SNAP_RIGHT, SNAP_RIGHT); + AddToSnappingList(rightmostWindow, SNAP_BOTTOM, SNAP_TOP); + rightmostWindow->AddToSnappingList(this, SNAP_RIGHT, SNAP_RIGHT); + rightmostWindow->AddToSnappingList(this, SNAP_TOP, SNAP_BOTTOM); + + STRACE_SAT(("Adding %s[%d] to %s[%d]'s R2R & B2T list\n", + rightmostWindow->Title(), rightmostWindow->WindowId(), + Title(), WindowId())); + } + + // adjust other windows + for (int i = 0; i < fBottomAdjacentWindows->CountItems(); i++) { + Window* window = static_cast( + fBottomAdjacentWindows->ItemAt(i)); + window->_InitStackingAndSnapping(); + Constraint* bottomSnapping = + fDesktop->fStackAndTileSpec->AddConstraint( + -1, fBottomVar, 1, window->fTopVar, + OperatorType(EQ), 32); + BString label("bottomSnapping of "); + label << Title(); + bottomSnapping->SetLabel(label.String()); + window->fSnappingConstraints->AddItem(bottomSnapping); + + AddToSnappingList(window, SNAP_BOTTOM, SNAP_TOP); + window->AddToSnappingList(this, SNAP_TOP, SNAP_BOTTOM); + + STRACE_SAT(("Adding %s[%d] to %s[%d]'s B2T list\n", + window->Title(), window->WindowId(), Title(), + WindowId())); + } + } + + // clear snapping candidates + delete fLeftAdjacentWindows; + delete fTopAdjacentWindows; + delete fRightAdjacentWindows; + delete fBottomAdjacentWindows; + fLeftAdjacentWindows = NULL; + fTopAdjacentWindows = NULL; + fRightAdjacentWindows = NULL; + fBottomAdjacentWindows = NULL; +} + + +void +Window::AddToSnappingList(Window* window, + SnapOrientation thisSnapOrientation, + SnapOrientation otherSnapOrientation) { + AddToSnappingList(window->WindowId(), thisSnapOrientation, + otherSnapOrientation); +} + + +/*! \brief Adds the given window's id to current window's snapping list, + according to given combo of snapping orientations. For example, if + thisSnapOrientation is SNAP_LEFT and otherSnapOrientation is + SNAP_RIGHT, then add the window's id to the left2right snapping list. + Discards duplicate id's + + \param thisSnapOrientation snapping orientation for current window + \param otherSnapOrientation snapping orientation for given window + */ +void +Window::AddToSnappingList(int32 windowId, + SnapOrientation thisSnapOrientation, + SnapOrientation otherSnapOrientation) { + + BList* windowIdList = GetSnappingList(thisSnapOrientation, + otherSnapOrientation, true); + //This may be the case if invalid snap orientation combo is given + //(e.g. top to left) + if (!windowIdList) + return; + + //don't add id if already contained + for (int i = 0; i < windowIdList->CountItems(); i++) { + int32* id = static_cast(windowIdList->ItemAt(i)); + if (*id == windowId) { + return; + } + } + int32* id = static_cast(malloc(sizeof(int32))); + *id = windowId; + windowIdList->AddItem(id); + + char snapStr[][7] = {"left", "right", "top", "bottom"}; + STRACE_SAT(("\tAdded %s to %s's %s 2 %s list\n", + windowId, Title(), + snapStr[thisSnapOrientation], + snapStr[otherSnapOrientation])); +} + + +/*! \brief Removes given window from the snapping list according to the + combination of given snapping orientations (e.g. left to left, + top to bottom, etc.) + + \param window the window to remove + \param thisSnapOrientation snapping orientation for this window + \param otherSnapOrientation snapping orientation for other window + */ +void +Window::RemoveFromSnappingList(int32 windowId, + SnapOrientation thisSnapOrientation, + SnapOrientation otherSnapOrientation) { + BList* windowIdList = GetSnappingList(thisSnapOrientation, + otherSnapOrientation, false); + + // This may be the case if invalid snap orientation combo is given + //(e.g. top to left) + if (!windowIdList) + return; + + for (int i = 0; i < windowIdList->CountItems(); i++) { + int32* id = static_cast(windowIdList->ItemAt(i)); + if (*id == windowId) { + windowIdList->RemoveItem(i); + free(id); + + char snapStr[][7] = {"left", "right", "top", "bottom"}; + STRACE_SAT(("\tRemoved %x from %s's %s 2 %s list\n", + windowId, Title(), + snapStr[thisSnapOrientation], + snapStr[otherSnapOrientation])); + return; + } + } +} + + +/*! \brief Returns a reference to the snapping list according to the + combination of given snapping orientations (e.g. left to left, + top to bottom, etc.). If the snapping list is not initialised, + then creates it before returning its reference. + + \param thisSnapOrientation snapping orientation for this window + \param otherSnapOrientation snapping orientation for other window + \param createIfNull whether to create the list if null + */ +BList* +Window::GetSnappingList(SnapOrientation thisSnapOrientation, + SnapOrientation otherSnapOrientation, + bool createIfNull) +{ + BList** windowIdListRef = _GetSnappingListRef(thisSnapOrientation, + otherSnapOrientation, createIfNull); + + if (windowIdListRef) + return *windowIdListRef; + return NULL; +} + + +BList** +Window::_GetSnappingListRef(SnapOrientation thisSnapOrientation, + SnapOrientation otherSnapOrientation, + bool createIfNull) +{ + BList** windowIdListRef = NULL; + if (thisSnapOrientation == SNAP_LEFT + && otherSnapOrientation == SNAP_LEFT) + windowIdListRef = &fLeft2LeftSnappingWindowIds; + if (thisSnapOrientation == SNAP_LEFT + && otherSnapOrientation == SNAP_RIGHT) + windowIdListRef = &fLeft2RightSnappingWindowIds; + if (thisSnapOrientation == SNAP_RIGHT + && otherSnapOrientation == SNAP_LEFT) + windowIdListRef = &fRight2LeftSnappingWindowIds; + if (thisSnapOrientation == SNAP_RIGHT + && otherSnapOrientation == SNAP_RIGHT) + windowIdListRef = &fRight2RightSnappingWindowIds; + if (thisSnapOrientation == SNAP_TOP + && otherSnapOrientation == SNAP_TOP) + windowIdListRef = &fTop2TopSnappingWindowIds; + if (thisSnapOrientation == SNAP_TOP + && otherSnapOrientation == SNAP_BOTTOM) + windowIdListRef = &fTop2BottomSnappingWindowIds; + if (thisSnapOrientation == SNAP_BOTTOM + && otherSnapOrientation == SNAP_TOP) + windowIdListRef = &fBottom2TopSnappingWindowIds; + if (thisSnapOrientation == SNAP_BOTTOM + && otherSnapOrientation == SNAP_BOTTOM) + windowIdListRef = &fBottom2BottomSnappingWindowIds; + + //This will be the case if invalid snap orientation combo is given + //(e.g. top to left) + if (!windowIdListRef) { + return NULL; + } + + //Create snapping list if not already initialised. + if (!(*windowIdListRef) && createIfNull) { + *windowIdListRef = new BList(); + } + + return windowIdListRef; +} + + +/*! \brief Snaps current window's side (left, right, top or bottom + according to given orientation) to given window's side (again + according to given orientation). Returns the object representing + this snapping constraint. + + TODO some of the logic is duplicative w.r.t _SnapWindow - consider + refactoring + + \param otherWindow the window to snap to + \param thisSnap snapping orientation for current window + \param otherSnap snapping orientation for given window +*/ +Constraint* +Window::SnapToWindow(Window* otherWindow, + SnapOrientation thisSnap, + SnapOrientation otherSnap) +{ + Variable* thisSnappingVar; + Variable* otherSnappingVar; + BString snappingLabel; + + int snapDistance = 0; + float moveDeltaX = 0; + float moveDeltaY = 0; + + BRect thisFrame = Frame(); + BRect otherFrame = otherWindow->Frame(); + + _InitStackingAndSnapping(); + otherWindow->_InitStackingAndSnapping(); + + //For each possible pair of snapping orientations, work out the + //necessary updates to make to the linear constraints + switch(thisSnap) { + case SNAP_LEFT: + thisSnappingVar = fLeftVar; + snappingLabel << "leftSnapping of "; + switch(otherSnap) { + case SNAP_LEFT: + moveDeltaX = otherFrame.left - thisFrame.left; + otherSnappingVar = otherWindow->fLeftVar; + break; + case SNAP_RIGHT: + snapDistance = -11; + moveDeltaX = otherFrame.right - thisFrame.left - snapDistance; + otherSnappingVar = otherWindow->fRightVar; + break; + default: + return NULL; + } + break; + case SNAP_RIGHT: + thisSnappingVar = fRightVar; + snappingLabel << "rightSnapping of "; + switch(otherSnap) { + case SNAP_LEFT: + snapDistance = 11; + moveDeltaX = otherFrame.left - thisFrame.right - snapDistance; + otherSnappingVar = otherWindow->fLeftVar; + break; + case SNAP_RIGHT: + moveDeltaX = otherFrame.right - thisFrame.right; + otherSnappingVar = otherWindow->fRightVar; + break; + default: + return NULL; + } + break; + case SNAP_TOP: + thisSnappingVar = fTopVar; + snappingLabel << "topSnapping of "; + switch(otherSnap) { + case SNAP_TOP: + moveDeltaY = otherFrame.top - thisFrame.top; + otherSnappingVar = otherWindow->fTopVar; + break; + case SNAP_BOTTOM: + snapDistance = -32; + moveDeltaY = otherFrame.bottom - thisFrame.top - snapDistance; + otherSnappingVar = otherWindow->fBottomVar; + break; + default: + return NULL; + } + break; + default: //SNAP_BOTTOM + thisSnappingVar = fBottomVar; + snappingLabel << "bottomSnapping of "; + switch(otherSnap) { + case SNAP_TOP: + snapDistance = 32; + moveDeltaY = otherFrame.top - thisFrame.bottom - snapDistance; + otherSnappingVar = otherWindow->fTopVar; + break; + case SNAP_BOTTOM: + moveDeltaY = otherFrame.bottom - thisFrame.bottom; + otherSnappingVar = otherWindow->fBottomVar; + break; + default: + return NULL; + } + break; + } + + fDesktop->MoveWindowBy(this, moveDeltaX, moveDeltaY, 0); + + Constraint* snappingConstraint; + if (snapDistance == 0) + snappingConstraint = thisSnappingVar->IsEqual(otherSnappingVar); + else + snappingConstraint = fDesktop->fStackAndTileSpec->AddConstraint( + -1, thisSnappingVar, 1, otherSnappingVar, + OperatorType(EQ), snapDistance); + + snappingLabel << Title(); + snappingConstraint->SetLabel(snappingLabel.String()); + fSnappingConstraints->AddItem(snappingConstraint); + + AddToSnappingList(otherWindow, thisSnap, otherSnap); + otherWindow->AddToSnappingList(this, otherSnap, thisSnap); + + return snappingConstraint; +} + + +/*! \brief Determines the overall bounding rectangle and the leftmost, topmost, + rightmost and bottommost window of a list of windows. The bounding + rectangle is returned. + + \param windows the list of windows for which to do the calculations. + \param leftmostWindow address where the pointer to the leftmost window + should be stored. + \param topmostWindow address where the pointer to the topmost window + should be stored. + \param rightmostWindow address where the pointer to the rightmost window + should be stored. + \param bottommostWindow address where the pointer to the bottommost window + should be stored. +*/ +BRect +Window::_BoundingRectAndWindows(BList* windows, + Window** leftmostWindow, Window** topmostWindow, + Window** rightmostWindow, Window** bottommostWindow) +{ + ASSERT(!windows->IsEmpty()); + + Window* firstWindow = static_cast(windows->FirstItem()); + BRect bounds = firstWindow->fFrame; + *leftmostWindow = firstWindow; + *topmostWindow = firstWindow; + *rightmostWindow = firstWindow; + *bottommostWindow = firstWindow; + + // relax result by processing all other windows + for (int i = 1; i < windows->CountItems(); i++) { + Window* window = static_cast(windows->ItemAt(i)); + if (window->fFrame.left < bounds.left) { + bounds.left = window->fFrame.left; + *leftmostWindow = window; + } + if (window->fFrame.top < bounds.top) { + bounds.top = window->fFrame.top; + *topmostWindow = window; + } + if (window->fFrame.right > bounds.right) { + bounds.right = window->fFrame.right; + *rightmostWindow = window; + } + if (window->fFrame.bottom > bounds.bottom) { + bounds.bottom = window->fFrame.bottom; + *bottommostWindow = window; + } + } + return bounds; +} + + +/*! \brief Cleans up after the Stack & Tile configuration mode of the window + manager was exited. The candidates are cleared, tab locations are + readjusted and unnecessary stack window lists are removed. +*/ +void +Window::FinishStackingAndSnapping() +{ + // clear stacking candidate + fWindowUnder = NULL; + + // clear snapping candidates + delete fLeftAdjacentWindows; + delete fTopAdjacentWindows; + delete fRightAdjacentWindows; + delete fBottomAdjacentWindows; + fLeftAdjacentWindows = NULL; + fTopAdjacentWindows = NULL; + fRightAdjacentWindows = NULL; + fBottomAdjacentWindows = NULL; + + // if the window was removed from a stack then rearrange tabs in the stack, + // remove the stack if it contains only one window + // and reset tab location of this window if it is not stacked again + if (fPrevStackedWindows) { + Window* firstWindow = static_cast( + fPrevStackedWindows->FirstItem()); + firstWindow->_ArrangeStackedWindowTabs(); + + // remove stacks with only one window + if (fPrevStackedWindows->CountItems() == 1) { + firstWindow->fStackedWindows = NULL; + delete fPrevStackedWindows; + } + + if (!fStackedWindows) + fDesktop->SetWindowTabLocation(this, 0); + fPrevStackedWindows = NULL; + } + + // if the window was added to a stack then rearrange tabs in that stack + _ArrangeStackedWindowTabs(); +} + + +/*! \brief Solves the Stack & Tile constraints, if the window uses them, sets + window locations and sizes accordingly, and readjusts window tabs of + the windows that are stacked with this window. +*/ +void +Window::StackAndTile() +{ + STRACE_SAT(("Window::StackAndTile() on %s, fDesktop->fStackAndTileSpec=%x\n", + Title(), fDesktop->fStackAndTileSpec)); + + // works only if stack and tile is active for this window + if (!fLeftVar) + return; + + // adjust window size soft constraints + fWidthConstraint->SetRightSide(fFrame.Width()); + fHeightConstraint->SetRightSide(fFrame.Height()); + fWidthConstraint->SetPenaltyNeg(110); + fWidthConstraint->SetPenaltyPos(110); + fHeightConstraint->SetPenaltyNeg(110); + fHeightConstraint->SetPenaltyPos(110); + + // adjust window position soft constraints + // (a bit more penalty for them so they take precedence) + fLeftConstraint->SetRightSide(fFrame.left); + fTopConstraint->SetRightSide(fFrame.top); + fLeftConstraint->SetPenaltyNeg(100); + fLeftConstraint->SetPenaltyPos(100); + fTopConstraint->SetPenaltyNeg(100); + fTopConstraint->SetPenaltyPos(100); + + // solve Stack & Tile specification + ResultType result; + for (int32 tries = 0; tries < 15; tries++) { + result = fDesktop->fStackAndTileSpec->Solve(); + if (result == OPTIMAL || result == INFEASIBLE) + break; + } + + // set window locations and sizes + for (Window* window = fDesktop->GetWindows().LastWindow(); + window; + window = window->PreviousWindow(fCurrentWorkspace)) { + + if (window->fLeftVar) { + fDesktop->MoveWindowBy(window, + window->fLeftVar->Value() - window->fFrame.left, + window->fTopVar->Value() - window->fFrame.top); + fDesktop->ResizeWindowBy(window, + window->fRightVar->Value() - window->fFrame.right, + window->fBottomVar->Value() - window->fFrame.bottom); + } + } + + // set penalties back to normal + fWidthConstraint->SetPenaltyNeg(10); + fWidthConstraint->SetPenaltyPos(10); + fHeightConstraint->SetPenaltyNeg(10); + fHeightConstraint->SetPenaltyPos(10); + fLeftConstraint->SetPenaltyNeg(1); + fLeftConstraint->SetPenaltyPos(1); + fTopConstraint->SetPenaltyNeg(1); + fTopConstraint->SetPenaltyPos(1); + + _ArrangeStackedWindowTabs(); + + #ifdef DEBUG_STACK_AND_TILE + // debug_printf crashes if given too large strings + // therefore we cut our specification string up into smaller chunks + BString* specStr = fDesktop->fStackAndTileSpec->ToBString(); + char line[71]; + int32 i = specStr->FindFirst("\n"); + while(i != B_ERROR) { + if (i < 70) { + specStr->MoveInto(line, 0, i + 1); + line[i + 1] = '\0'; + } else { + specStr->MoveInto(line, 0, 70); + line[70] = '\0'; + } + debug_printf("%s", line); + i = specStr->FindFirst("\n"); + } + while (specStr->Length() > 0) { + int length = specStr->Length(); + if (length < 70) { + specStr->MoveInto(line, 0, length); + line[length] = '\0'; + } else { + specStr->MoveInto(line, 0, 70); + line[70] = '\0'; + } + debug_printf("%s", line); + } + debug_printf("\n"); + delete specStr; + #endif + STRACE_SAT(("Finished Window::StackAndTile() on %s\n", Title())); +} + + +void +Window::_BoundWindowByWorkspace() +{ + BRect workspaceRect = fDesktop->WorkspaceFrame(Workspaces()); + + _InitStackingAndSnapping(); + fLeftVar->SetRange(workspaceRect.left, workspaceRect.right); + fTopVar->SetRange(workspaceRect.top, workspaceRect.bottom); + fRightVar->SetRange(workspaceRect.left, workspaceRect.right); + fBottomVar->SetRange(workspaceRect.top, workspaceRect.bottom); +} + + +void +Window::_UnboundWindowByWorkspace() +{ + if (!fLeftVar) + return; + + fLeftVar->SetRange(-DBL_MAX, DBL_MAX); + fTopVar->SetRange(-DBL_MAX, DBL_MAX); + fRightVar->SetRange(-DBL_MAX, DBL_MAX); + fBottomVar->SetRange(-DBL_MAX, DBL_MAX); +} + + +/*! \brief Gets the id of this window. A new id is created randomly if the + window does not have an id yet. +*/ +int32 +Window::WindowId() +{ + if(!fId) + fId = rand() + 1; + + return fId; +} + + +void +Window::SetWindowId(int32 windowId) +{ + fId = windowId; +} + + +/*! \brief Removes the id of this window from the list of stacked window ids + if there is such a list for this window. The list is made persistent + through DefaultDecorator::GetSettings(). +*/ +void +Window::_RemoveStackingPersistently() +{ + STRACE_SAT(("Window::_RemoveStackingPersistently() on %s, fStackedWindowIds=%x\n", + Title(), fStackedWindowIds)); + if (!fStackedWindowIds) + return; + + for (int i = 0; i < fStackedWindowIds->CountItems(); i++) { + int32* id = static_cast(fStackedWindowIds->ItemAt(i)); + if (*id == fId) { + fStackedWindowIds->RemoveItem(i); + free(id); + } + } + + fStackedWindowIds = NULL; + STRACE_SAT(("Finished Window::_RemoveStackingPersistently() on %s\n", + Title())); +} + + +/*! \brief Removes information about snapped windows that would otherwise be + made persistent through DefaultDecorator::GetSettings(). +*/ +void +Window::_RemoveSnappingPersistently() +{ + STRACE_SAT(("Window::_RemoveSnappingPersistently() on %s\n", Title())); + + _FreeUpSnappingList(SNAP_LEFT, SNAP_LEFT, true); + _FreeUpSnappingList(SNAP_LEFT, SNAP_RIGHT, true); + _FreeUpSnappingList(SNAP_RIGHT, SNAP_LEFT, true); + _FreeUpSnappingList(SNAP_RIGHT, SNAP_RIGHT, true); + _FreeUpSnappingList(SNAP_TOP, SNAP_TOP, true); + _FreeUpSnappingList(SNAP_TOP, SNAP_BOTTOM, true); + _FreeUpSnappingList(SNAP_BOTTOM, SNAP_TOP, true); + _FreeUpSnappingList(SNAP_BOTTOM, SNAP_BOTTOM, true); + + STRACE_SAT(("Finished Window::_RemoveSnappingPersistently() on %s\n", + Title())); +} + + +/*! \brief Deletes the snapping list that corresponds to given snapping + orientations. Also optionally deletes the current window's id from the + snapping list of other relevant windows. For exmaple, if current window + (W1) is snapped to other window (W2) via left 2 right, then removes + W1 from W2's right2left snapping list. + + \param thisSnap snapping orientation of current window + \param otherSnap snapping orientation of other window(s) + \param deleteFromOtherWindowsList whether to delete the current window's + id from the snapping list of other relevant windows +*/ +void +Window::_FreeUpSnappingList(SnapOrientation thisSnap, + SnapOrientation otherSnap, + bool deleteFromOtherWindowsList) { + BList** idListRef = _GetSnappingListRef(thisSnap, otherSnap, false); + + if (!idListRef) + return; + + BList* idList = *idListRef; + if (!idList) + return; + + for (int i = 0; i < idList->CountItems(); i++) { + int32* id = static_cast(idList->ItemAt(i)); + if (deleteFromOtherWindowsList) { + Window* otherWindow = fDesktop->FindWindow(*id); + //Remove this window from the other window's snapping list. + //Note the order of orientation is reversed. + if (otherWindow) + otherWindow->RemoveFromSnappingList(WindowId(), otherSnap, thisSnap); + } + free(id); + } + delete idList; + *idListRef = NULL; +} + + void Window::UpdateSession::MoveBy(int32 x, int32 y) { diff --git a/src/servers/app/Window.h b/src/servers/app/Window.h index de82098229..c0b1b55a43 100644 --- a/src/servers/app/Window.h +++ b/src/servers/app/Window.h @@ -23,6 +23,8 @@ #include #include +#include "LinearSpec.h" + namespace BPrivate { class PortLink; }; @@ -43,6 +45,10 @@ enum { UPDATE_EXPOSE = 0x02, }; +// Used for Stack & Tile +enum SnapOrientation { + SNAP_LEFT, SNAP_RIGHT, SNAP_TOP, SNAP_BOTTOM +}; class Window { public: @@ -244,6 +250,59 @@ public: static uint32 ValidWindowFlags(); static uint32 ValidWindowFlags(window_feel feel); + bool HighlightTab(bool active, BRegion& dirty); + bool HighlightBorders(bool active, BRegion& dirty); + bool IsTabHighlighted(); + bool IsBordersHighlighted(); + + void StackAndTile(); + void StackWindowBefore(Window* window); + void FinishStackingAndSnapping(); + BList* StackedWindows() const + { return fStackedWindows; } + int32 WindowId(); + void SetWindowId(int32 windowId); + BList* StackedWindowIds() const + { return fStackedWindowIds; } + void InitStackedWindowIds(); + + Constraint* SnapToWindow(Window* otherWindow, + SnapOrientation thisSnap, + SnapOrientation otherSnap); + + void AddToSnappingList(int32 windowId, + SnapOrientation thisSnapOrientation, + SnapOrientation otherSnapOrientation); + void AddToSnappingList(Window* window, + SnapOrientation thisSnapOrientation, + SnapOrientation otherSnapOrientation); + void RemoveFromSnappingList(int32 windowId, + SnapOrientation thisSnapOrientation, + SnapOrientation otherSnapOrientation); + BList* GetSnappingList( + SnapOrientation thisSnapOrientation, + SnapOrientation otherSnapOrientation, + bool createIfNull); + + BList* Left2LeftSnappingWindowIds() const + { return fLeft2LeftSnappingWindowIds; } + BList* Left2RightSnappingWindowIds() const + { return fLeft2RightSnappingWindowIds; } + BList* Right2RightSnappingWindowIds() const + { return fRight2RightSnappingWindowIds; } + BList* Right2LeftSnappingWindowIds() const + { return fRight2LeftSnappingWindowIds; } + BList* Top2TopSnappingWindowIds() const + { return fTop2TopSnappingWindowIds; } + BList* Top2BottomSnappingWindowIds() const + { return fTop2BottomSnappingWindowIds; } + BList* Bottom2BottomSnappingWindowIds() const + { return fBottom2BottomSnappingWindowIds; } + BList* Bottom2TopSnappingWindowIds() const + { return fBottom2TopSnappingWindowIds; } + + bool ForceActivate() { return fForceActivate; } + protected: void _ShiftPartOfRegion(BRegion* region, BRegion* regionToShift, int32 xOffset, @@ -393,6 +452,78 @@ protected: int32 fMaxHeight; int32 fWorkspacesViewCount; + + // Stack & Tile specific members + void _InitStackingAndSnapping(); + void _RemoveStackingAndSnapping(); + + void _CheckIfReadyToStack(); + void _StackWindow(); + void _ArrangeStackedWindowTabs(); + + void _CheckIfReadyToSnap(); + void _SnapWindow(); + BRect _BoundingRectAndWindows(BList* windows, + Window** leftmostWindow, + Window** topmostWindow, + Window** rightmostWindow, + Window** bottommostWindow); + + void _BoundWindowByWorkspace(); + void _UnboundWindowByWorkspace(); + + void _RemoveStackingPersistently(); + void _RemoveSnappingPersistently(); + + void _FreeUpSnappingList(SnapOrientation thisSnap, + SnapOrientation otherSnap, + bool deleteFromOtherWindowsList); + BList** _GetSnappingListRef( + SnapOrientation thisSnapOrientation, + SnapOrientation otherSnapOrientation, + bool createIfNull); + + void _EnsureWindowWithinScreenBounds(Window* window, Window* detached); + + Variable* fLeftVar; + Variable* fTopVar; + Variable* fRightVar; + Variable* fBottomVar; + Constraint* fLeftConstraint; + Constraint* fTopConstraint; + Constraint* fMinWidthConstraint; + Constraint* fMinHeightConstraint; + Constraint* fWidthConstraint; + Constraint* fHeightConstraint; + + BList* fLeftAdjacentWindows; + BList* fTopAdjacentWindows; + BList* fRightAdjacentWindows; + BList* fBottomAdjacentWindows; + BList* fSnappingConstraints; + + BList* fLeft2LeftSnappingWindowIds; + BList* fLeft2RightSnappingWindowIds; + BList* fRight2RightSnappingWindowIds; + BList* fRight2LeftSnappingWindowIds; + BList* fTop2TopSnappingWindowIds; + BList* fTop2BottomSnappingWindowIds; + BList* fBottom2BottomSnappingWindowIds; + BList* fBottom2TopSnappingWindowIds; + + //True means activate this window even if it's already at the front + bool fForceActivate; + + Window* fWindowUnder; + BList* fStackedWindows; + BList* fPrevStackedWindows; + Constraint* fLeftStackingConstraint; + Constraint* fTopStackingConstraint; + Constraint* fRightStackingConstraint; + Constraint* fBottomStackingConstraint; + + int32 fId; + BList* fStackedWindowIds; }; #endif // WINDOW_H diff --git a/src/servers/app/WorkspacesView.cpp b/src/servers/app/WorkspacesView.cpp index c8495fc1a2..100a959fcd 100644 --- a/src/servers/app/WorkspacesView.cpp +++ b/src/servers/app/WorkspacesView.cpp @@ -568,6 +568,10 @@ WorkspacesView::MouseMoved(BMessage* message, BPoint where) if (fHasMoved) { Window()->Desktop()->MoveWindowBy(fSelectedWindow, left - leftTop.x, top - leftTop.y, fSelectedWorkspace); + //TODO Re-apply stack & tile constraints so that any other windows + //that are stacked/snapped against this window can be re-adjusted. + //Must smartly call stack and tile after workspace shifting has + //finalised. How? } }