Author: czeidler Date: 2010-05-27 01:07:37 +0200 (Thu, 27 May 2010) New Revision: 36943 Changeset: http://dev.haiku-os.org/changeset/36943/haiku Added: haiku/branches/features/stack-and-tile/src/servers/app/Snapping.cpp Modified: haiku/branches/features/stack-and-tile/src/servers/app/Jamfile haiku/branches/features/stack-and-tile/src/servers/app/StackAndTile.cpp haiku/branches/features/stack-and-tile/src/servers/app/StackAndTile.h Log: Move snapping related stuff into a Snapping sub class. Modified: haiku/branches/features/stack-and-tile/src/servers/app/Jamfile =================================================================== --- haiku/branches/features/stack-and-tile/src/servers/app/Jamfile 2010-05-26 17:24:39 UTC (rev 36942) +++ haiku/branches/features/stack-and-tile/src/servers/app/Jamfile 2010-05-26 23:07:37 UTC (rev 36943) @@ -53,6 +53,7 @@ ServerFont.cpp ServerPicture.cpp ServerWindow.cpp + Snapping.cpp StackAndTile.cpp Stacking.cpp SystemPalette.cpp Added: haiku/branches/features/stack-and-tile/src/servers/app/Snapping.cpp =================================================================== --- haiku/branches/features/stack-and-tile/src/servers/app/Snapping.cpp (rev 0) +++ haiku/branches/features/stack-and-tile/src/servers/app/Snapping.cpp 2010-05-26 23:07:37 UTC (rev 36943) @@ -0,0 +1,1331 @@ +#include "StackAndTile.h" + +#include "Window.h" + +#include <Debug.h> + + +// #define DEBUG_SNAPPING + + +#ifdef DEBUG_SNAPPING +# define STRACE_SAT(x) debug_printf x +#else +# define STRACE_SAT(x) ; +#endif + +#ifdef DEBUG_SNAPPING +char kSnapOrientationString[][7] = {"left", "right", "top", "bottom"}; +#endif + + +SATWindowSnapping::SATWindowSnapping(Window* window, ::StackAndTile* sat) + : + SATWindow(window, sat), + + 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) +{ + +} + + +SATWindowSnapping::~SATWindowSnapping() +{ + // 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); +} + + +void +SATWindowSnapping::MouseDown() +{ + SATWindow::MouseDown(); + + // Re-stack and re-snap the window if in stacking & snapping mode + if (fStackAndTile->IsStackingAndSnapping()) { + _RemoveSnappingPersistently(); + _CheckIfReadyToSnap(); + } +} + + +void +SATWindowSnapping::MouseUp() +{ + SATWindow::MouseUp(); + + // Snap or stack the window if in stack & tiling mode + if (fStackAndTile->IsStackingAndSnapping()) { + _RemoveSnappingPersistently(); + _CheckIfReadyToSnap(); + _SnapWindow(); + } +} + + +void +SATWindowSnapping::MoveWindow() +{ + SATWindow::MoveWindow(); + + // 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 (fStackAndTile->IsStackingAndSnapping()) { + _RemoveSnappingPersistently(); + _CheckIfReadyToSnap(); + } +} + + +void +SATWindowSnapping::ResizeWindow() +{ + SATWindow::ResizeWindow(); + + // 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 (fStackAndTile->IsStackingAndSnapping()) { + _RemoveSnappingPersistently(); + _CheckIfReadyToSnap(); + } +} + + +void +SATWindowSnapping::ActivateWindow(Window* window, WindowList& bringToTopList) +{ + BObjectList<Window> stackedAndTiledWindows; + + //Prepare to move tiled windows to the front as well + _AddWindowsByIdsToList(Left2LeftSnappingWindowIds(), + stackedAndTiledWindows); + _AddWindowsByIdsToList(Left2RightSnappingWindowIds(), + stackedAndTiledWindows); + _AddWindowsByIdsToList(Right2RightSnappingWindowIds(), + stackedAndTiledWindows); + _AddWindowsByIdsToList(Right2LeftSnappingWindowIds(), + stackedAndTiledWindows); + _AddWindowsByIdsToList(Top2TopSnappingWindowIds(), + stackedAndTiledWindows); + _AddWindowsByIdsToList(Top2BottomSnappingWindowIds(), + stackedAndTiledWindows); + _AddWindowsByIdsToList(Bottom2TopSnappingWindowIds(), + stackedAndTiledWindows); + _AddWindowsByIdsToList(Bottom2BottomSnappingWindowIds(), + stackedAndTiledWindows); + + // Do the actual moving here + for (int i = 0; i < stackedAndTiledWindows.CountItems(); i ++) { + Window* win = stackedAndTiledWindows.ItemAt(i); + fWindow->Desktop()->CurrentWindows().RemoveWindow(win); + bringToTopList.AddWindow(win); + } + + SATWindow::ActivateWindow(window, bringToTopList); +} + + +bool +SATWindowSnapping::SetSettings(const BMessage& settings) +{ + STRACE_SAT(("SATWindowSnapping::SetSettings() on %s\n", + fWindow->Title())); + + _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); + + STRACE_SAT(("Finished SATWindowSnapping::SetSettings() on %s\n", + fWindow->Title())); + + return SATWindow::SetSettings(settings); +} + + +bool +SATWindowSnapping::GetSettings(BMessage* settings) const +{ + // store id's of snapped windows + if (!StoreIntsInSettings("snap left2left", Left2LeftSnappingWindowIds(), + settings)) + return false; + if (!StoreIntsInSettings("snap left2right", Left2RightSnappingWindowIds(), + settings)) + return false; + if (!StoreIntsInSettings("snap right2right", + Right2RightSnappingWindowIds(), settings)) + return false; + if (!StoreIntsInSettings("snap right2left", Right2LeftSnappingWindowIds(), + settings)) + return false; + if (!StoreIntsInSettings("snap top2top", Top2TopSnappingWindowIds(), + settings)) + return false; + if (!StoreIntsInSettings("snap top2bottom", Top2BottomSnappingWindowIds(), + settings)) + return false; + if (!StoreIntsInSettings("snap bottom2top", Bottom2TopSnappingWindowIds(), + settings)) + return false; + if (!StoreIntsInSettings("snap bottom2bottom", + Bottom2BottomSnappingWindowIds(), settings)) + return false; + + return SATWindow::GetSettings(settings); +} + + +void +SATWindowSnapping::InitStackingAndSnapping() +{ + SATWindow::InitStackingAndSnapping(); + + _InitSnapping(); +} + + +void +SATWindowSnapping::FinishStackingAndSnapping() +{ + // clear snapping candidates + delete fLeftAdjacentWindows; + delete fTopAdjacentWindows; + delete fRightAdjacentWindows; + delete fBottomAdjacentWindows; + fLeftAdjacentWindows = NULL; + fTopAdjacentWindows = NULL; + fRightAdjacentWindows = NULL; + fBottomAdjacentWindows = NULL; + + SATWindow::FinishStackingAndSnapping(); +} + + +void +SATWindowSnapping::_RemoveSAT() +{ + SATWindow::_RemoveSAT(); + + _RemoveSnapping(); +} + + +void +SATWindowSnapping::_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); + + SATWindowSnapping* window = dynamic_cast<SATWindowSnapping*>( + FindWindow(id)); + if (window == this) { + 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<int32*>(otherList->ItemAt(i)); + if (*snappedId == WindowId()) { + SnapToWindow(window, thisSnapOrientation, + otherSnapOrientation); + debug_suffix = ""; + break; + } + } + } + else { // window isn't open - still retain snap id + AddToSnappingList(id, thisSnapOrientation, + otherSnapOrientation); + debug_suffix = "... ?"; + } + + STRACE_SAT(("\t%s[%d]=%x", label, i, id)); + STRACE_SAT(("%s\n", debug_suffix)); + } + } +} + + +void +SATWindowSnapping::_EnsureWindowWithinScreenBounds(SATWindowSnapping* window, + SATWindow* detached) +{ + BRect screenFrame = fWindow->Screen()->Frame(); + float deltaX = 0; + float deltaY = 0; + + BRect windowFrame = window->GetWindow()->Frame(); + + BList* windowsToRight = window->Right2LeftSnappingWindowIds(); + BList* windowsToLeft = window->Left2RightSnappingWindowIds(); + BList* windowsToTop = window->Bottom2TopSnappingWindowIds(); + BList* windowsToBottom = window->Top2BottomSnappingWindowIds(); + + if (windowFrame.left < 0 && (!windowsToRight || windowsToRight->IsEmpty() + || windowsToRight->CountItems() == 1) + && *static_cast<int32*>(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 - windowFrame.left; + } + else if (windowFrame.right > screenFrame.right && (!windowsToLeft + || windowsToLeft->IsEmpty() || windowsToLeft->CountItems() == 1) + && *static_cast<int32*>(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 - windowFrame.right - 6; + } + if (windowFrame.top < 0 && (!windowsToTop || windowsToTop->IsEmpty() + || windowsToTop->CountItems() == 1) + && *static_cast<int32*>(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 - windowFrame.top; + } + else if (windowFrame.bottom > screenFrame.bottom && (!windowsToBottom + || windowsToBottom->IsEmpty() || windowsToBottom->CountItems() == 1) + && *static_cast<int32*>(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 - windowFrame.bottom - 6; + } + if (deltaX != 0 || deltaY != 0) + fWindow->Desktop()->MoveWindowBy(window->GetWindow(), deltaX, deltaY); +} + + +void +SATWindowSnapping::_InitSnapping() +{ + STRACE_SAT(("SATWindowSnapping::_InitSnapping() on %s, fLeftVar=%d\n", + fWindow->Title(), fLeftVar)); + + fSnappingConstraints = new BList(); + + STRACE_SAT(("Finished SATWindowSnapping::_InitSnapping() on %s\n", + fWindow->Title())); +} + + +void +SATWindowSnapping::_RemoveSnapping() +{ + STRACE_SAT(("SATWindowSnapping::_RemoveSnapping() on %s, fLeftVar=%d\n", + fWindow->Title(), fLeftVar)); + + // if there are no variables for the sides, then it is not active + if (!fSnappingConstraints) + return; + + for (int i = 0; i < fSnappingConstraints->CountItems(); i++) + delete static_cast<Constraint*>(fSnappingConstraints->ItemAt(i)); + fSnappingConstraints = NULL; + + // For each previously snapped window, make sure that it is within + // screen bounds, so as to prevent it from being lost off-screen + _EnsureAndSAT(fLeft2LeftSnappingWindowIds); + _EnsureAndSAT(fLeft2RightSnappingWindowIds); + _EnsureAndSAT(fRight2LeftSnappingWindowIds); + _EnsureAndSAT(fRight2RightSnappingWindowIds); + _EnsureAndSAT(fTop2TopSnappingWindowIds); + _EnsureAndSAT(fTop2BottomSnappingWindowIds); + _EnsureAndSAT(fBottom2TopSnappingWindowIds); + _EnsureAndSAT(fBottom2BottomSnappingWindowIds); + + STRACE_SAT(("Finished SATWindowSnapping::_RemoveStackingAndSnapping() on %s\n", + fWindow->Title())); +} + + +void +SATWindowSnapping::_EnsureAndSAT(BList* idList) +{ + if (!idList) + return; + + for (int i = 0; i < idList->CountItems(); i++) { + int32* id = static_cast<int32*>(idList->ItemAt(i)); + SATWindowSnapping* winSAT = dynamic_cast<SATWindowSnapping*>( + FindWindow(*id)); + if (winSAT) { + _EnsureWindowWithinScreenBounds(winSAT, this); + winSAT->StackAndTile(); + } + } +} + + +/*! \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 +SATWindowSnapping::_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* window = fWindow->Desktop()->CurrentWindows().LastWindow(); + window; window = window->PreviousWindow(fWindow->CurrentWorkspace())) { + Window* windowUnder = fStacking.GetWindowUnder() != NULL + ? fStacking.GetWindowUnder()->GetWindow() : NULL; + if (!window->IsHidden() + && fWindow != window // don't snap window to itself + && window != windowUnder + // don't snap with stacking candidate + && !(strcmp(window->Title(), "Deskbar") == 0) + && !(strcmp(window->Title(), "Desktop") == 0)) { + + // is window adjacent on the left of this window? + BRect thisLeft(fWindow->Frame().left - 12, + fWindow->Frame().top + 12, fWindow->Frame().left + 6, + fWindow->Frame().bottom - 12); + BRect windowRight(window->Frame().right - 6, + window->Frame().top + 12, window->Frame().right + 12, + window->Frame().bottom - 12); + if (thisLeft.Intersects(windowRight)) + fLeftAdjacentWindows->AddItem(window); + + // is window adjacent on the top of this window? + BRect thisTop(fWindow->Frame().left + 12, fWindow->Frame().top - 22, + fWindow->Frame().right - 12, fWindow->Frame().top + 6); + BRect windowBottom(window->Frame().left + 12, + window->Frame().bottom - 6, window->Frame().right - 12, + window->Frame().bottom + 12); + if (thisTop.Intersects(windowBottom)) + fTopAdjacentWindows->AddItem(window); + + // is window adjacent on the right of this window? + BRect thisRight(fWindow->Frame().right - 6, + fWindow->Frame().top + 12, fWindow->Frame().right + 12, + fWindow->Frame().bottom - 12); + BRect windowLeft(window->Frame().left - 12, + window->Frame().top + 12, window->Frame().left + 6, + window->Frame().bottom - 12); + if (thisRight.Intersects(windowLeft)) + fRightAdjacentWindows->AddItem(window); + + // is window adjacent on the bottom of this window? + BRect thisBottom(fWindow->Frame().left + 12, + fWindow->Frame().bottom - 6, fWindow->Frame().right - 12, + fWindow->Frame().bottom + 12); + BRect windowTop(window->Frame().left + 12, + window->Frame().top - 22, window->Frame().right - 12, + window->Frame().top + 6); + if (thisBottom.Intersects(windowTop)) + fBottomAdjacentWindows->AddItem(window); + } + } + + // change window border highlight of this window + if (!fLeftAdjacentWindows->IsEmpty() + || !fTopAdjacentWindows->IsEmpty() + || !fRightAdjacentWindows->IsEmpty() + || !fBottomAdjacentWindows->IsEmpty()) + fWindow->Desktop()->HighlightBorders(fWindow, true); + else + fWindow->Desktop()->HighlightBorders(fWindow, 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<Window*>( + prevLeftAdjacentWindows.ItemAt(i)); + if (!fLeftAdjacentWindows->HasItem(window)) + fWindow->Desktop()->HighlightBorders(window, false); + } + for (int i = 0; i < prevTopAdjacentWindows.CountItems(); i++) { + Window* window = static_cast<Window*>( + prevTopAdjacentWindows.ItemAt(i)); + if (!fTopAdjacentWindows->HasItem(window)) + fWindow->Desktop()->HighlightBorders(window, false); + } + for (int i = 0; i < prevRightAdjacentWindows.CountItems(); i++) { + Window* window = static_cast<Window*>( + prevRightAdjacentWindows.ItemAt(i)); + if (!fRightAdjacentWindows->HasItem(window)) + fWindow->Desktop()->HighlightBorders(window, false); + } + for (int i = 0; i < prevBottomAdjacentWindows.CountItems(); i++) { + Window* window = static_cast<Window*>( + prevBottomAdjacentWindows.ItemAt(i)); + if (!fBottomAdjacentWindows->HasItem(window)) + fWindow->Desktop()->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++) + fWindow->Desktop()->HighlightBorders( + static_cast<Window*>(fLeftAdjacentWindows->ItemAt(i)), true); + for (int i = 0; i < fTopAdjacentWindows->CountItems(); i++) + fWindow->Desktop()->HighlightBorders( + static_cast<Window*>(fTopAdjacentWindows->ItemAt(i)), true); + for (int i = 0; i < fRightAdjacentWindows->CountItems(); i++) + fWindow->Desktop()->HighlightBorders( + static_cast<Window*>(fRightAdjacentWindows->ItemAt(i)), true); + for (int i = 0; i < fBottomAdjacentWindows->CountItems(); i++) + fWindow->Desktop()->HighlightBorders( + static_cast<Window*>(fBottomAdjacentWindows->ItemAt(i)), true); +} + + +/*! \brief Snaps this window to the current candidate windows for snapping + on each side. Afterwards snapping candidates are cleared. +*/ +void +SATWindowSnapping::_SnapWindow() +{ + BRect bounds; + SATWindowSnapping* leftmostWindow; + SATWindowSnapping* topmostWindow; + SATWindowSnapping* rightmostWindow; + SATWindowSnapping* bottommostWindow; + + // snap other windows to the left border + if (!fLeftAdjacentWindows->IsEmpty()) { + bounds = _BoundingRectAndWindows(fLeftAdjacentWindows, + &leftmostWindow, &topmostWindow, + &rightmostWindow, &bottommostWindow); + + // adjust window position & initialize snapping + fWindow->Desktop()->MoveWindowBy(fWindow, + bounds.right - fWindow->Frame().left + 11, 0); + InitStackingAndSnapping(); + topmostWindow->InitStackingAndSnapping(); + bottommostWindow->InitStackingAndSnapping(); + + // adjust top border + if (fTopAdjacentWindows->IsEmpty()) { + fWindow->Desktop()->MoveWindowBy(fWindow, 0, + bounds.top - fWindow->Frame().top); + Constraint* topSnapping = fTopVar->IsEqual( + topmostWindow->fTopVar); + BString label("topSnapping of "); + label << fWindow->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(), + fWindow->Title(), WindowId())); + } + + // adjust bottom border + if (fBottomAdjacentWindows->IsEmpty()) { + fWindow->Desktop()->ResizeWindowBy(fWindow, 0, + bounds.bottom - fWindow->Frame().bottom); + Constraint* bottomSnapping = fBottomVar->IsEqual( + bottommostWindow->fBottomVar); + BString label("bottomSnapping of "); + label << fWindow->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(), + fWindow->Title(), WindowId())); + } + + // adjust other windows + for (int i = 0; i < fLeftAdjacentWindows->CountItems(); i++) { + SATWindowSnapping* window = dynamic_cast<SATWindowSnapping*>( + fStackAndTile->GetSATWindow(static_cast<Window*>( + fLeftAdjacentWindows->ItemAt(i)))); + ASSERT(window); + window->InitStackingAndSnapping(); + Constraint* leftSnapping = + fStackAndTile->GetLinearSpec()->AddConstraint(-1, + window->RightVariable(), 1, fLeftVar, OperatorType(EQ), 11); + BString label("leftSnapping of "); + label << fWindow->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(), fWindow->Title(), WindowId())); + } + } + + // snap other windows to the top border + if (!fTopAdjacentWindows->IsEmpty()) { + bounds = _BoundingRectAndWindows(fTopAdjacentWindows, + &leftmostWindow, &topmostWindow, + &rightmostWindow, &bottommostWindow); + + // adjust window position & initialize snapping + fWindow->Desktop()->MoveWindowBy(fWindow, 0, + bounds.bottom - fWindow->Frame().top + 32); + InitStackingAndSnapping(); + leftmostWindow->InitStackingAndSnapping(); + rightmostWindow->InitStackingAndSnapping(); + + // adjust left border + if (fLeftAdjacentWindows->IsEmpty()) { + fWindow->Desktop()->MoveWindowBy( + fWindow, bounds.left - fWindow->Frame().left, 0); + Constraint* leftSnapping = fLeftVar->IsEqual( + leftmostWindow->fLeftVar); + BString label("leftSnapping of "); + label << fWindow->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(), + fWindow->Title(), WindowId())); + } + + // adjust right border + if (fRightAdjacentWindows->IsEmpty()) { + fWindow->Desktop()->ResizeWindowBy(fWindow, + bounds.right - fWindow->Frame().right, 0); + Constraint* rightSnapping = fRightVar->IsEqual( + rightmostWindow->fRightVar); + BString label("rightSnapping of "); + label << fWindow->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(), + fWindow->Title(), WindowId())); + } + + // adjust other windows + for (int i = 0; i < fTopAdjacentWindows->CountItems(); i++) { + SATWindowSnapping* window = dynamic_cast<SATWindowSnapping*>( + fStackAndTile->GetSATWindow(static_cast<Window*>( + fTopAdjacentWindows->ItemAt(i)))); + ASSERT(window); + window->InitStackingAndSnapping(); + Constraint* topSnapping = + fStackAndTile->GetLinearSpec()->AddConstraint(-1, + window->fBottomVar, 1, fTopVar, OperatorType(EQ), 32); + BString label("topSnapping of "); + label << fWindow->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(), fWindow->Title(), WindowId())); + } + } + + // snap other windows to the right border + if (!fRightAdjacentWindows->IsEmpty()) { + bounds = _BoundingRectAndWindows(fRightAdjacentWindows, + &leftmostWindow, &topmostWindow, &rightmostWindow, + &bottommostWindow); + + // adjust window position & initialize snapping + fWindow->Desktop()->MoveWindowBy(fWindow, + bounds.left - fWindow->Frame().right - 11, 0); + InitStackingAndSnapping(); + topmostWindow->InitStackingAndSnapping(); + bottommostWindow->InitStackingAndSnapping(); + + // adjust top border + if (fTopAdjacentWindows->IsEmpty()) { + fWindow->Desktop()->MoveWindowBy(fWindow, 0, + bounds.top - fWindow->Frame().top); + Constraint* topSnapping = fTopVar->IsEqual(topmostWindow->fTopVar); + BString label("topSnapping of "); + label << fWindow->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(), + fWindow->Title(), WindowId())); + } + + // adjust bottom border + if (fBottomAdjacentWindows->IsEmpty()) { + fWindow->Desktop()->ResizeWindowBy(fWindow, 0, + bounds.bottom - fWindow->Frame().bottom); + Constraint* bottomSnapping = fBottomVar->IsEqual( + bottommostWindow->fBottomVar); + BString label("bottomSnapping of "); + label << fWindow->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(), + fWindow->Title(), WindowId())); + } + + // adjust other windows + for (int i = 0; i < fRightAdjacentWindows->CountItems(); i++) { + SATWindowSnapping* window = dynamic_cast<SATWindowSnapping*>( + fStackAndTile->GetSATWindow(static_cast<Window*>( + fRightAdjacentWindows->ItemAt(i)))); + ASSERT(window); + window->InitStackingAndSnapping(); + Constraint* rightSnapping = + fStackAndTile->GetLinearSpec()->AddConstraint(-1, fRightVar, 1, + window->fLeftVar, OperatorType(EQ), 11); + BString label("rightSnapping of "); + label << fWindow->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(), + WindowId(), window->Title(), WindowId())); + } + } + + // snap other windows to the bottom border + if (!fBottomAdjacentWindows->IsEmpty()) { + bounds = _BoundingRectAndWindows(fBottomAdjacentWindows, + &leftmostWindow, &topmostWindow, + &rightmostWindow, &bottommostWindow); + + // adjust window position & initialize snapping + fWindow->Desktop()->MoveWindowBy(fWindow, 0, + bounds.top - fWindow->Frame().bottom - 32); + InitStackingAndSnapping(); + leftmostWindow->InitStackingAndSnapping(); + rightmostWindow->InitStackingAndSnapping(); + + // adjust left border + if (fLeftAdjacentWindows->IsEmpty()) { + fWindow->Desktop()->MoveWindowBy(fWindow, + bounds.left - fWindow->Frame().left, 0); + Constraint* leftSnapping = fLeftVar->IsEqual( + leftmostWindow->fLeftVar); + BString label("leftSnapping of "); + label << fWindow->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(), + fWindow->Title(), WindowId())); + } + + // adjust right border + if (fRightAdjacentWindows->IsEmpty()) { + fWindow->Desktop()->ResizeWindowBy(fWindow, + bounds.right - fWindow->Frame().right, 0); + Constraint* rightSnapping = fRightVar->IsEqual( + rightmostWindow->fRightVar); + BString label("rightSnapping of "); + label << fWindow->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(), + fWindow->Title(), WindowId())); + } + + // adjust other windows + for (int i = 0; i < fBottomAdjacentWindows->CountItems(); i++) { + SATWindowSnapping* window = dynamic_cast<SATWindowSnapping*>( + fStackAndTile->GetSATWindow(static_cast<Window*>( + fBottomAdjacentWindows->ItemAt(i)))); + ASSERT(window); + window->InitStackingAndSnapping(); + Constraint* bottomSnapping = + fStackAndTile->GetLinearSpec()->AddConstraint(-1, fBottomVar, 1, + window->fTopVar, OperatorType(EQ), 32); + BString label("bottomSnapping of "); + label << fWindow->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(), fWindow->Title(), WindowId())); + } + } + + // clear snapping candidates + delete fLeftAdjacentWindows; + delete fTopAdjacentWindows; + delete fRightAdjacentWindows; + delete fBottomAdjacentWindows; + fLeftAdjacentWindows = NULL; + fTopAdjacentWindows = NULL; + fRightAdjacentWindows = NULL; + fBottomAdjacentWindows = NULL; + + STRACE_SAT(("Finished: SATWindowSnapping::_SnapWindow()\n")); +} + + +void +SATWindowSnapping::AddToSnappingList(SATWindow* 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 +SATWindowSnapping::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<int32*>(windowIdList->ItemAt(i)); + if (*id == windowId) { + return; + } + } + int32* id = static_cast<int32*>(malloc(sizeof(int32))); + *id = windowId; + windowIdList->AddItem(id); + + STRACE_SAT(("\tAdded %i to %s's %s 2 %s list\n", + windowId, fWindow->Title(), kSnapOrientationString[thisSnapOrientation], + kSnapOrientationString[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 +SATWindowSnapping::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<int32*>(windowIdList->ItemAt(i)); + if (*id == windowId) { + windowIdList->RemoveItem(i); + free(id); + + STRACE_SAT(("\tRemoved %x from %s's %s 2 %s list\n", + windowId, fWindow->Title(), + kSnapOrientationString[thisSnapOrientation], + kSnapOrientationString[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* [... truncated: 1931 lines follow ...]