diff --git a/src/add-ons/decorators/Jamfile b/src/add-ons/decorators/Jamfile index bc63c2cafe..44a74fc734 100644 --- a/src/add-ons/decorators/Jamfile +++ b/src/add-ons/decorators/Jamfile @@ -2,4 +2,5 @@ SubDir HAIKU_TOP src add-ons decorators ; SubInclude HAIKU_TOP src add-ons decorators BeDecorator ; SubInclude HAIKU_TOP src add-ons decorators MacDecorator ; -SubInclude HAIKU_TOP src add-ons decorators WinDecorator ; \ No newline at end of file +SubInclude HAIKU_TOP src add-ons decorators WinDecorator ; +SubInclude HAIKU_TOP src add-ons decorators SATDecorator ; \ No newline at end of file diff --git a/src/add-ons/decorators/SATDecorator/Jamfile b/src/add-ons/decorators/SATDecorator/Jamfile new file mode 100644 index 0000000000..23ee6811ef --- /dev/null +++ b/src/add-ons/decorators/SATDecorator/Jamfile @@ -0,0 +1,25 @@ +SubDir HAIKU_TOP src add-ons decorators SATDecorator ; + +UseLibraryHeaders agg lp_solve linprog ; +UsePrivateHeaders app graphics interface shared kernel ; + +UseHeaders [ FDirName $(HAIKU_TOP) src servers app ] ; +UseHeaders [ FDirName $(HAIKU_TOP) src servers app drawing ] ; +UseHeaders [ FDirName $(HAIKU_TOP) src servers app drawing Painter ] ; +UseFreeTypeHeaders ; + +Addon SATDecorator : + SATDecorator.cpp + SATGroup.cpp + SATWindow.cpp + StackAndTile.cpp + Stacking.cpp + Tiling.cpp + + # libraries + : + libbe.so + app_server + $(TARGET_LIBSTDC++) + liblpsolve55.so liblinprog.so +; diff --git a/src/add-ons/decorators/SATDecorator/SATDecorator.cpp b/src/add-ons/decorators/SATDecorator/SATDecorator.cpp new file mode 100644 index 0000000000..a14bad3ee3 --- /dev/null +++ b/src/add-ons/decorators/SATDecorator/SATDecorator.cpp @@ -0,0 +1,482 @@ +/* + * Copyright 2010, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Clemens Zeidler + */ + +#include "SATDecorator.h" + +#include + +#include +#include + +#include "DrawingEngine.h" + + +#define DEBUG_SATDECORATOR +#ifdef DEBUG_SATDECORATOR +# define STRACE(x) debug_printf x +#else +# define STRACE(x) ; +#endif + + +static const float kResizeKnobSize = 18.0; + + +SATDecorAddOn::SATDecorAddOn(image_id id, const char* name) + : + DecorAddOn(id, name) +{ + fDesktopListeners.AddItem(&fStackAndTile); +} + + +status_t +SATDecorAddOn::InitCheck() const +{ + if (fDesktopListeners.CountItems() != 1) + return B_ERROR; + + return B_OK; +} + + +float +SATDecorAddOn::Version() +{ + return 0.1; +} + + +Decorator* +SATDecorAddOn::_AllocateDecorator(DesktopSettings& settings, BRect rect, + window_look look, uint32 flags) +{ + return new (std::nothrow)SATDecorator(settings, rect, look, flags); +} + + +SATDecorator::SATDecorator(DesktopSettings& settings, BRect frame, + window_look look, uint32 flags) + : + DefaultDecorator(settings, frame, look, flags), + + fTabHighlighted(false), + fBordersHighlighted(false), + + fStackedMode(false), + fStackedDrawZoom(false), + fStackedTabLength(0) +{ + // 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 }; + + + 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]; + + fHighlightTabColor = (rgb_color){ 255, 0, 0, 255 }; +} + + +float +SATDecorator::GetZoomOffsetToRight() +{ + float offset; + float size; + float inset; + _GetButtonSizeAndOffset(fTabRect, &offset, &size, &inset); + + return 2 * offset + size; +} + + +void +SATDecorator::HighlightTab(bool active, BRegion* dirty) +{ + if (active) + fTabColor = fHighlightTabColor; + else if (IsFocus()) + fTabColor = fFocusTabColor; + else + fTabColor = fNonFocusTabColor; + dirty->Include(fTabRect); + fTabHighlighted = active; +} + + +void +SATDecorator::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; +} + + +void +SATDecorator::SetStackedMode(bool stacked, BRegion* dirty) +{ + fStackedMode = stacked; + + dirty->Include(fTabRect); + _DoLayout(); + _InvalidateFootprint(); + dirty->Include(fTabRect); +} + + +void +SATDecorator::SetStackedTabLength(float length, bool drawZoom, BRegion* dirty) +{ + fStackedTabLength = length; + fStackedDrawZoom = drawZoom; + + dirty->Include(fTabRect); + _DoLayout(); + _InvalidateFootprint(); + dirty->Include(fTabRect); +} + + +void +SATDecorator::_DoLayout() +{ + STRACE(("DefaultDecorator: Do Layout\n")); + // Here we determine the size of every rectangle that we use + // internally when we are given the size of the client rectangle. + + bool hasTab = false; + + switch (Look()) { + case B_MODAL_WINDOW_LOOK: + fBorderWidth = 5; + break; + + case B_TITLED_WINDOW_LOOK: + case B_DOCUMENT_WINDOW_LOOK: + hasTab = true; + fBorderWidth = 5; + break; + case B_FLOATING_WINDOW_LOOK: + case kLeftTitledWindowLook: + hasTab = true; + fBorderWidth = 3; + break; + + case B_BORDERED_WINDOW_LOOK: + fBorderWidth = 1; + break; + + default: + fBorderWidth = 0; + } + + // calculate our tab rect + if (hasTab) { + // distance from one item of the tab bar to another. + // In this case the text and close/zoom rects + if (fStackedMode && false) + fTextOffset = 5; + else + fTextOffset = (fLook == B_FLOATING_WINDOW_LOOK + || fLook == kLeftTitledWindowLook) ? 10 : 18; + + font_height fontHeight; + fDrawState.Font().GetHeight(fontHeight); + + if (fLook != kLeftTitledWindowLook) { + fTabRect.Set(fFrame.left - fBorderWidth, + fFrame.top - fBorderWidth + - ceilf(fontHeight.ascent + fontHeight.descent + 7.0), + ((fFrame.right - fFrame.left) < 35.0 ? + fFrame.left + 35.0 : fFrame.right) + fBorderWidth, + fFrame.top - fBorderWidth); + } else { + fTabRect.Set(fFrame.left - fBorderWidth + - ceilf(fontHeight.ascent + fontHeight.descent + 5.0), + fFrame.top - fBorderWidth, fFrame.left - fBorderWidth, + fFrame.bottom + fBorderWidth); + } + + // format tab rect for a floating window - make the rect smaller + if (fLook == B_FLOATING_WINDOW_LOOK) { + fTabRect.InsetBy(0, 2); + fTabRect.OffsetBy(0, 2); + } + + if (fStackedMode) + fTabRect.right = fTabRect.left + fStackedTabLength; + + float offset; + float size; + float inset; + _GetButtonSizeAndOffset(fTabRect, &offset, &size, &inset); + + // fMinTabSize contains just the room for the buttons + fMinTabSize = inset * 2 + fTextOffset; + if ((fFlags & B_NOT_CLOSABLE) == 0) + fMinTabSize += offset + size; + if ((fFlags & B_NOT_ZOOMABLE) == 0) + fMinTabSize += offset + size; + + // fMaxTabSize contains fMinWidth + the width required for the title + fMaxTabSize = fDrawingEngine + ? ceilf(fDrawingEngine->StringWidth(Title(), strlen(Title()), + fDrawState.Font())) : 0.0; + if (fMaxTabSize > 0.0) + fMaxTabSize += fTextOffset; + fMaxTabSize += fMinTabSize; + + float tabSize = (fLook != kLeftTitledWindowLook + ? fFrame.Width() : fFrame.Height()) + fBorderWidth * 2; + + if (fStackedMode) { + tabSize = fStackedTabLength; + fMaxTabSize = tabSize; + } + else { + if (tabSize < fMinTabSize) + tabSize = fMinTabSize; + if (tabSize > fMaxTabSize) + tabSize = fMaxTabSize; + } + // layout buttons and truncate text + if (fLook != kLeftTitledWindowLook) + fTabRect.right = fTabRect.left + tabSize; + else + fTabRect.bottom = fTabRect.top + tabSize; + } else { + // no tab + fMinTabSize = 0.0; + fMaxTabSize = 0.0; + fTabRect.Set(0.0, 0.0, -1.0, -1.0); + fCloseRect.Set(0.0, 0.0, -1.0, -1.0); + fZoomRect.Set(0.0, 0.0, -1.0, -1.0); + } + + // calculate left/top/right/bottom borders + if (fBorderWidth > 0) { + // NOTE: no overlapping, the left and right border rects + // don't include the corners! + fLeftBorder.Set(fFrame.left - fBorderWidth, fFrame.top, + fFrame.left - 1, fFrame.bottom); + + fRightBorder.Set(fFrame.right + 1, fFrame.top , + fFrame.right + fBorderWidth, fFrame.bottom); + + fTopBorder.Set(fFrame.left - fBorderWidth, fFrame.top - fBorderWidth, + fFrame.right + fBorderWidth, fFrame.top - 1); + + fBottomBorder.Set(fFrame.left - fBorderWidth, fFrame.bottom + 1, + fFrame.right + fBorderWidth, fFrame.bottom + fBorderWidth); + } else { + // no border + fLeftBorder.Set(0.0, 0.0, -1.0, -1.0); + fRightBorder.Set(0.0, 0.0, -1.0, -1.0); + fTopBorder.Set(0.0, 0.0, -1.0, -1.0); + fBottomBorder.Set(0.0, 0.0, -1.0, -1.0); + } + + // calculate resize rect + if (fBorderWidth > 1) { + fResizeRect.Set(fBottomBorder.right - kResizeKnobSize, + fBottomBorder.bottom - kResizeKnobSize, fBottomBorder.right, + fBottomBorder.bottom); + } else { + // no border or one pixel border (menus and such) + fResizeRect.Set(0, 0, -1, -1); + } + + if (hasTab) { + // make sure fTabOffset is within limits and apply it to + // the fTabRect + if (fTabOffset < 0) + fTabOffset = 0; + if (fTabLocation != 0.0 + && fTabOffset > (fRightBorder.right - fLeftBorder.left + - fTabRect.Width())) + fTabOffset = uint32(fRightBorder.right - fLeftBorder.left + - fTabRect.Width()); + fTabRect.OffsetBy(fTabOffset, 0); + + // finally, layout the buttons and text within the tab rect + _LayoutTabItems(fTabRect); + } +} + + +void +SATDecorator::_LayoutTabItems(const BRect& tabRect) +{ + float offset; + float size; + float inset; + _GetButtonSizeAndOffset(tabRect, &offset, &size, &inset); + + // calulate close rect based on the tab rectangle + if (fLook != kLeftTitledWindowLook) { + fCloseRect.Set(tabRect.left + offset, tabRect.top + offset, + tabRect.left + offset + size, tabRect.top + offset + size); + + fZoomRect.Set(tabRect.right - offset - size, tabRect.top + offset, + tabRect.right - offset, tabRect.top + offset + size); + + // hidden buttons have no width + if ((Flags() & B_NOT_CLOSABLE) != 0) + fCloseRect.right = fCloseRect.left - offset; + if ((Flags() & B_NOT_ZOOMABLE) != 0) + fZoomRect.left = fZoomRect.right + offset; + } else { + fCloseRect.Set(tabRect.left + offset, tabRect.top + offset, + tabRect.left + offset + size, tabRect.top + offset + size); + + fZoomRect.Set(tabRect.left + offset, tabRect.bottom - offset - size, + tabRect.left + size + offset, tabRect.bottom - offset); + + // hidden buttons have no height + if ((Flags() & B_NOT_CLOSABLE) != 0) + fCloseRect.bottom = fCloseRect.top - offset; + if ((Flags() & B_NOT_ZOOMABLE) != 0) + fZoomRect.top = fZoomRect.bottom + offset; + } + + // calculate room for title + // TODO: the +2 is there because the title often appeared + // truncated for no apparent reason - OTOH the title does + // also not appear perfectly in the middle + if (fLook != kLeftTitledWindowLook) + size = (fZoomRect.left - fCloseRect.right) - fTextOffset * 2 + inset; + else + size = (fZoomRect.top - fCloseRect.bottom) - fTextOffset * 2 + inset; + + if (fStackedMode && !fStackedDrawZoom) { + fZoomRect.Set(0, 0, 0, 0); + size = (fTabRect.right - fCloseRect.right) - fTextOffset * 2 + inset; + } + fTruncatedTitle = Title(); + fDrawState.Font().TruncateString(&fTruncatedTitle, B_TRUNCATE_MIDDLE, size); + fTruncatedTitleLength = fTruncatedTitle.Length(); +} + + +void +SATDecorator::_DrawTab(BRect invalid) +{ + STRACE(("_DrawTab(%.1f,%.1f,%.1f,%.1f)\n", + invalid.left, invalid.top, invalid.right, invalid.bottom)); + // If a window has a tab, this will draw it and any buttons which are + // in it. + if (!fTabRect.IsValid() || !invalid.Intersects(fTabRect)) + return; + + // outer frame + fDrawingEngine->StrokeLine(fTabRect.LeftTop(), fTabRect.LeftBottom(), + fFrameColors[0]); + fDrawingEngine->StrokeLine(fTabRect.LeftTop(), fTabRect.RightTop(), + fFrameColors[0]); + if (fLook != kLeftTitledWindowLook) { + fDrawingEngine->StrokeLine(fTabRect.RightTop(), fTabRect.RightBottom(), + fFrameColors[5]); + } else { + fDrawingEngine->StrokeLine(fTabRect.LeftBottom(), + fTabRect.RightBottom(), fFrameColors[5]); + } + + // bevel + fDrawingEngine->StrokeLine(BPoint(fTabRect.left + 1, fTabRect.top + 1), + BPoint(fTabRect.left + 1, + fTabRect.bottom - (fLook == kLeftTitledWindowLook ? 1 : 0)), + fTabColorBevel); + fDrawingEngine->StrokeLine(BPoint(fTabRect.left + 1, fTabRect.top + 1), + BPoint(fTabRect.right - (fLook == kLeftTitledWindowLook ? 0 : 1), + fTabRect.top + 1), + fTabColorBevel); + + if (fLook != kLeftTitledWindowLook) { + fDrawingEngine->StrokeLine(BPoint(fTabRect.right - 1, fTabRect.top + 2), + BPoint(fTabRect.right - 1, fTabRect.bottom), fTabColorShadow); + } else { + fDrawingEngine->StrokeLine( + BPoint(fTabRect.left + 2, fTabRect.bottom - 1), + BPoint(fTabRect.right, fTabRect.bottom - 1), fTabColorShadow); + } + + // fill + BGradientLinear gradient; + gradient.SetStart(fTabRect.LeftTop()); + gradient.AddColor(fTabColorLight, 0); + gradient.AddColor(fTabColor, 255); + + if (fLook != kLeftTitledWindowLook) { + gradient.SetEnd(fTabRect.LeftBottom()); + fDrawingEngine->FillRect(BRect(fTabRect.left + 2, fTabRect.top + 2, + fTabRect.right - 2, fTabRect.bottom), gradient); + } else { + gradient.SetEnd(fTabRect.RightTop()); + fDrawingEngine->FillRect(BRect(fTabRect.left + 2, fTabRect.top + 2, + fTabRect.right, fTabRect.bottom - 2), gradient); + } + + _DrawTitle(fTabRect); + + // Draw the buttons if we're supposed to + if (!(fFlags & B_NOT_CLOSABLE) && invalid.Intersects(fCloseRect)) + _DrawClose(fCloseRect); + + if (fStackedMode) { + if (fStackedDrawZoom && invalid.Intersects(fZoomRect)) + _DrawZoom(fZoomRect); + } + else if (!(fFlags & B_NOT_ZOOMABLE) && invalid.Intersects(fZoomRect)) + _DrawZoom(fZoomRect); +} + + +extern "C" DecorAddOn* (instantiate_decor_addon)(image_id id, const char* name) +{ + return new (std::nothrow)SATDecorAddOn(id, name); +} + diff --git a/src/add-ons/decorators/SATDecorator/SATDecorator.h b/src/add-ons/decorators/SATDecorator/SATDecorator.h new file mode 100644 index 0000000000..47b749e9a8 --- /dev/null +++ b/src/add-ons/decorators/SATDecorator/SATDecorator.h @@ -0,0 +1,72 @@ +/* + * Copyright 2010, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Clemens Zeidler + */ +#ifndef SAT_DECORATOR_H +#define SAT_DECORATOR_H + + +#include "DecorManager.h" +#include "DefaultDecorator.h" +#include "StackAndTile.h" + + +class SATDecorAddOn : public DecorAddOn { +public: + SATDecorAddOn(image_id id, const char* name); + + virtual status_t InitCheck() const; + float Version(); + +protected: + virtual Decorator* _AllocateDecorator(DesktopSettings& settings, + BRect rect, window_look look, uint32 flags); + + StackAndTile fStackAndTile; +}; + + +class SATDecorator : public DefaultDecorator { +public: + SATDecorator(DesktopSettings& settings, + BRect frame, window_look look, uint32 flags); + + float GetZoomOffsetToRight(); + + void HighlightTab(bool active, BRegion* dirty); + void HighlightBorders(bool active, BRegion* dirty); + bool IsTabHighlighted() { return fTabHighlighted; } + bool IsBordersHighlighted() { return fBordersHighlighted; } + + /*! Indicates that window is stacked */ + void SetStackedMode(bool stacked, BRegion* dirty); + bool StackedMode() { return fStackedMode; }; + + /*! Set the tab length if the decorator is in stacked mode and if the + tab is the last one in the tab bar. */ + void SetStackedTabLength(float length, bool drawZoom, + BRegion* dirty); + float StackedTabLength() { return fStackedTabLength; } + +protected: + void _DoLayout(); + void _DrawTab(BRect r); + void _LayoutTabItems(const BRect& tabRect); + +private: + bool fTabHighlighted; + bool fBordersHighlighted; + rgb_color fHighlightTabColor; + rgb_color fNonHighlightFrameColors[4]; + rgb_color fHighlightFrameColors[6]; + + bool fStackedMode; + bool fStackedDrawZoom; + float fStackedTabLength; + bool fStackedTabShifting; +}; + +#endif diff --git a/src/add-ons/decorators/SATDecorator/SATGroup.cpp b/src/add-ons/decorators/SATDecorator/SATGroup.cpp new file mode 100644 index 0000000000..9ed460d982 --- /dev/null +++ b/src/add-ons/decorators/SATDecorator/SATGroup.cpp @@ -0,0 +1,1148 @@ +/* + * Copyright 2010, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Clemens Zeidler + */ + +#include "SATGroup.h" + +#include + +#include "SATWindow.h" +#include "StackAndTile.h" +#include "Window.h" + + +WindowArea::WindowArea(Crossing* leftTop, Crossing* rightTop, + Crossing* leftBottom, Crossing* rightBottom) + : + fLeftTopCrossing(leftTop), + fRightTopCrossing(rightTop), + fLeftBottomCrossing(leftBottom), + fRightBottomCrossing(rightBottom) +{ + +} + + +WindowArea::~WindowArea() +{ + _CleanupCorners(); +} + + +bool +WindowArea::MoveWindowToPosition(SATWindow* window, int32 index) +{ + int32 oldIndex = fWindowList.IndexOf(window); + if (oldIndex < 0) + return false; + ASSERT(oldIndex != index); + if (oldIndex < index) + index++; + else + oldIndex++; + if (!fWindowList.AddItem(window, index)) + return false; + fWindowList.RemoveItemAt(oldIndex); + return true; +} + + +bool +WindowArea::_AddWindow(SATWindow* window, SATWindow* after) +{ + if (after) { + int32 indexAfter = fWindowList.IndexOf(after); + if (!fWindowList.AddItem(window, indexAfter + 1)) + return false; + } + else if (!fWindowList.AddItem(window)) + return false; + + AcquireReference(); + + if (fWindowList.CountItems() <= 1) + _InitCorners(); + + return true; +} + + +bool +WindowArea::_RemoveWindow(SATWindow* window) +{ + if (!fWindowList.RemoveItem(window)) + return false; + + window->RemovedFromArea(this); + ReleaseReference(); + return true; +} + + +Tab* +WindowArea::LeftTab() +{ + return fLeftTopCrossing->VerticalTab(); +} + + +Tab* +WindowArea::RightTab() +{ + return fRightBottomCrossing->VerticalTab(); +} + + +Tab* +WindowArea::TopTab() +{ + return fLeftTopCrossing->HorizontalTab(); +} + + +Tab* +WindowArea::BottomTab() +{ + return fRightBottomCrossing->HorizontalTab(); +} + + +BRect +WindowArea::Frame() +{ + return BRect(fLeftTopCrossing->VerticalTab()->Position(), + fLeftTopCrossing->HorizontalTab()->Position(), + fRightBottomCrossing->VerticalTab()->Position(), + fRightBottomCrossing->HorizontalTab()->Position()); +} + + +bool +WindowArea::PropagateToGroup(SATGroup* group) +{ + BReference newLeftTop = _CrossingByPosition(fLeftTopCrossing, + group); + BReference newRightTop = _CrossingByPosition(fRightTopCrossing, + group); + BReference newLeftBottom = _CrossingByPosition( + fLeftBottomCrossing, group); + BReference newRightBottom = _CrossingByPosition( + fRightBottomCrossing, group); + + if (!newLeftTop || !newRightTop || !newLeftBottom || !newRightBottom) + return false; + + fLeftTopCrossing = newLeftTop; + fRightTopCrossing = newRightTop; + fLeftBottomCrossing = newLeftBottom; + fRightBottomCrossing = newRightBottom; + + for (int i = 0; i < fWindowList.CountItems(); i++) + fWindowList.ItemAt(i)->PropagateToGroup(group, this); + + return true; +} + + +BReference +WindowArea::_CrossingByPosition(Crossing* crossing, SATGroup* group) +{ + BReference crossRef = NULL; + + Tab* oldHTab = crossing->HorizontalTab(); + BReference hTab = group->FindHorizontalTab(oldHTab->Position()); + if (!hTab) + hTab = group->_AddHorizontalTab(oldHTab->Position()); + if (!hTab) + return crossRef; + + Tab* oldVTab = crossing->VerticalTab(); + crossRef = hTab->FindCrossing(oldHTab->Position()); + if (crossRef) + return crossRef; + + BReference vTab = group->FindVerticalTab(oldVTab->Position()); + if (!vTab) + vTab = group->_AddVerticalTab(oldVTab->Position()); + if (!vTab) + return crossRef; + + return hTab->AddCrossing(vTab); +} + + +void +WindowArea::_InitCorners() +{ + _SetToWindowCorner(fLeftTopCrossing->RightBottomCorner()); + _SetToNeighbourCorner(fLeftTopCrossing->LeftBottomCorner()); + _SetToNeighbourCorner(fLeftTopCrossing->RightTopCorner()); + + _SetToWindowCorner(fRightTopCrossing->LeftBottomCorner()); + _SetToNeighbourCorner(fRightTopCrossing->LeftTopCorner()); + _SetToNeighbourCorner(fRightTopCrossing->RightBottomCorner()); + + _SetToWindowCorner(fLeftBottomCrossing->RightTopCorner()); + _SetToNeighbourCorner(fLeftBottomCrossing->LeftTopCorner()); + _SetToNeighbourCorner(fLeftBottomCrossing->RightBottomCorner()); + + _SetToWindowCorner(fRightBottomCrossing->LeftTopCorner()); + _SetToNeighbourCorner(fRightBottomCrossing->LeftBottomCorner()); + _SetToNeighbourCorner(fRightBottomCrossing->RightTopCorner()); +} + + +void +WindowArea::_CleanupCorners() +{ + _UnsetWindowCorner(fLeftTopCrossing->RightBottomCorner()); + _UnsetNeighbourCorner(fLeftTopCrossing->LeftBottomCorner(), + fLeftBottomCrossing->LeftTopCorner()); + _UnsetNeighbourCorner(fLeftTopCrossing->RightTopCorner(), + fLeftBottomCrossing->LeftTopCorner()); + + _UnsetWindowCorner(fRightTopCrossing->LeftBottomCorner()); + _UnsetNeighbourCorner(fRightTopCrossing->LeftTopCorner(), + fLeftBottomCrossing->RightTopCorner()); + _UnsetNeighbourCorner(fRightTopCrossing->RightBottomCorner(), + fLeftBottomCrossing->RightTopCorner()); + + _UnsetWindowCorner(fLeftBottomCrossing->RightTopCorner()); + _UnsetNeighbourCorner(fLeftBottomCrossing->LeftTopCorner(), + fLeftBottomCrossing->LeftBottomCorner()); + _UnsetNeighbourCorner(fLeftBottomCrossing->RightBottomCorner(), + fLeftBottomCrossing->LeftBottomCorner()); + + _UnsetWindowCorner(fRightBottomCrossing->LeftTopCorner()); + _UnsetNeighbourCorner(fRightBottomCrossing->LeftBottomCorner(), + fRightBottomCrossing->RightBottomCorner()); + _UnsetNeighbourCorner(fRightBottomCrossing->RightTopCorner(), + fRightBottomCrossing->RightBottomCorner()); +} + + +void +WindowArea::_SetToWindowCorner(Corner* corner) +{ + corner->status = Corner::kUsed; + corner->windowArea = this; +} + + +void +WindowArea::_SetToNeighbourCorner(Corner* neighbour) +{ + if (neighbour->status == Corner::kNotDockable) + neighbour->status = Corner::kFree; +} + + +void +WindowArea::_UnsetWindowCorner(Corner* corner) +{ + corner->status = Corner::kFree; + corner->windowArea = NULL; +} + + +void +WindowArea::_UnsetNeighbourCorner(Corner* neighbour, Corner* opponent) +{ + if (neighbour->status == Corner::kFree && opponent->status != Corner::kUsed) + neighbour->status = Corner::kNotDockable; +} + + +Corner::Corner() + : + status(kNotDockable), + windowArea(NULL) +{ + +} + + +void +Corner::Trace() const +{ + switch (status) { + case kFree: + STRACE_SAT("free corner\n"); + break; + + case kUsed: + { + STRACE_SAT("attached windows:\n"); + const SATWindowList& list = windowArea->WindowList(); + for (int i = 0; i < list.CountItems(); i++) { + STRACE_SAT("- %s\n", list.ItemAt(i)->GetWindow()->Title()); + } + break; + } + + case kNotDockable: + STRACE_SAT("not dockable\n"); + break; + }; +} + + +Crossing::Crossing(Tab* vertical, Tab* horizontal) + : + fVerticalTab(vertical), + fHorizontalTab(horizontal) +{ + fVerticalTab->AcquireReference(); + fHorizontalTab->AcquireReference(); +} + + +Crossing::~Crossing() +{ + fVerticalTab->RemoveCrossing(this); + fHorizontalTab->RemoveCrossing(this); + + fVerticalTab->ReleaseReference(); + fHorizontalTab->ReleaseReference(); +} + + +Corner* +Crossing::GetCorner(Corner::position_t corner) const +{ + return _GetCorner(corner); +} + + +Tab* +Crossing::VerticalTab() const +{ + return fVerticalTab; +} + + +Tab* +Crossing::HorizontalTab() const +{ + return fHorizontalTab; +} + + +void +Crossing::Trace() const +{ + STRACE_SAT("left-top corner: "); + fLeftTop.Trace(); + STRACE_SAT("right-top corner: "); + fRightTop.Trace(); + STRACE_SAT("left-bottom corner: "); + fLeftBottom.Trace(); + STRACE_SAT("right-bottom corner: "); + fRightBottom.Trace(); +} + + +Corner* +Crossing::_GetCorner(Corner::position_t corner) const +{ + switch (corner) { + case Corner::kLeftTop: + return const_cast(&fLeftTop); + case Corner::kRightTop: + return const_cast(&fRightTop); + case Corner::kLeftBottom: + return const_cast(&fLeftBottom); + case Corner::kRightBottom: + return const_cast(&fRightBottom); + }; + return NULL; +} + + +Corner* +Crossing::GetOppositeCorner(Corner::position_t corner) const +{ + switch (corner) { + case Corner::kLeftTop: + return const_cast(&fRightBottom); + case Corner::kRightTop: + return const_cast(&fLeftBottom); + case Corner::kLeftBottom: + return const_cast(&fRightTop); + case Corner::kRightBottom: + return const_cast(&fLeftTop); + }; + return NULL; +} + + +Tab::Tab(SATGroup* group, Variable* variable, orientation_t orientation) + : + fGroup(group), + fVariable(variable), + fOrientation(orientation) +{ + +} + + +Tab::~Tab() +{ + if (fOrientation == kVertical) + fGroup->_RemoveVerticalTab(this); + else + fGroup->_RemoveHorizontalTab(this); + + delete fVariable; +} + + +float +Tab::Position() const +{ + return (float)fVariable->Value(); +} + + +void +Tab::SetPosition(float position) +{ + fVariable->SetValue(position); +} + + +Tab::orientation_t +Tab::Orientation() const +{ + return fOrientation; +} + + +Constraint* +Tab::Connect(Variable* variable) +{ + return fVariable->IsEqual(variable); +} + + +BReference +Tab::AddCrossing(Tab* tab) +{ + if (tab->Orientation() == fOrientation) + return NULL; + + Tab* vTab = (fOrientation == kVertical) ? this : tab; + Tab* hTab = (fOrientation == kHorizontal) ? this : tab; + + Crossing* crossing = new (std::nothrow)Crossing(vTab, hTab); + if (!crossing) + return NULL; + + if (!fCrossingList.AddItem(crossing)) { + return NULL; + } + if (!tab->fCrossingList.AddItem(crossing)) { + fCrossingList.RemoveItem(crossing); + return NULL; + } + + BReference crossingRef(crossing, true); + return crossingRef; +} + + +bool +Tab::RemoveCrossing(Crossing* crossing) +{ + Tab* vTab = crossing->VerticalTab(); + Tab* hTab = crossing->HorizontalTab(); + + if (vTab != this && hTab != this) + return false; + fCrossingList.RemoveItem(crossing); + + return true; +} + + +int32 +Tab::FindCrossingIndex(Tab* tab) +{ + if (fOrientation == kVertical) { + for (int32 i = 0; i < fCrossingList.CountItems(); i++) + if (fCrossingList.ItemAt(i)->HorizontalTab() == tab) + return i; + } else { + for (int32 i = 0; i < fCrossingList.CountItems(); i++) + if (fCrossingList.ItemAt(i)->VerticalTab() == tab) + return i; + } + return -1; +} + + +int32 +Tab::FindCrossingIndex(float pos) +{ + if (fOrientation == kVertical) { + for (int32 i = 0; i < fCrossingList.CountItems(); i++) + if (fCrossingList.ItemAt(i)->HorizontalTab()->Position() == pos) + return i; + } else { + for (int32 i = 0; i < fCrossingList.CountItems(); i++) + if (fCrossingList.ItemAt(i)->VerticalTab()->Position() == pos) + return i; + } + return -1; +} + + +Crossing* +Tab::FindCrossing(Tab* tab) +{ + return fCrossingList.ItemAt(FindCrossingIndex(tab)); +} + + +Crossing* +Tab::FindCrossing(float tabPosition) +{ + return fCrossingList.ItemAt(FindCrossingIndex(tabPosition)); +} + + +const CrossingList* +Tab::GetCrossingList() const +{ + return &fCrossingList; +} + + +int +Tab::CompareFunction(const Tab* tab1, const Tab* tab2) +{ + if (tab1->Position() < tab2->Position()) + return -1; + + return 1; +} + + +SATGroup::SATGroup() + : + fHorizontalTabsSorted(false), + fVerticalTabsSorted(false) +{ + +} + + +SATGroup::~SATGroup() +{ + ASSERT(fSATWindowList.CountItems() == 0); + // Should be empty + //while (fSATWindowList.CountItems() > 0) + // RemoveWindow(fSATWindowList.ItemAt(0)); +} + + +bool +SATGroup::AddWindow(SATWindow* window, Tab* left, Tab* top, Tab* right, + Tab* bottom) +{ + STRACE_SAT("SATGroup::AddWindow\n"); + + // first check if we have to create tabs and missing corners. + BReference leftRef, rightRef, topRef, bottomRef; + BReference leftTopRef, rightTopRef, leftBottomRef, rightBottomRef; + + if (left && top) + leftTopRef = left->FindCrossing(top); + if (right && top) + rightTopRef = right->FindCrossing(top); + if (left && bottom) + leftBottomRef = left->FindCrossing(bottom); + if (right && bottom) + rightBottomRef = right->FindCrossing(bottom); + + if (!left) { + leftRef = _AddVerticalTab(); + left = leftRef.Get(); + } + if (!top) { + topRef = _AddHorizontalTab(); + top = topRef.Get(); + } + if (!right) { + rightRef = _AddVerticalTab(); + right = rightRef.Get(); + } + if (!bottom) { + bottomRef = _AddHorizontalTab(); + bottom = bottomRef.Get(); + } + if (!left || !top || !right || !bottom) + return false; + + if (!leftTopRef) { + leftTopRef = left->AddCrossing(top); + if (!leftTopRef) + return false; + } + if (!rightTopRef) { + rightTopRef = right->AddCrossing(top); + if (!rightTopRef) + return false; + } + if (!leftBottomRef) { + leftBottomRef = left->AddCrossing(bottom); + if (!leftBottomRef) + return false; + } + if (!rightBottomRef) { + rightBottomRef = right->AddCrossing(bottom); + if (!rightBottomRef) + return false; + } + + WindowArea* area = new(std::nothrow) WindowArea(leftTopRef, rightTopRef, + leftBottomRef, rightBottomRef); + if (!area) + return false; + // delete the area if AddWindow failed / release our reference on it + BReference areaRef(area, true); + + return AddWindow(window, area); +} + + +bool +SATGroup::AddWindow(SATWindow* window, WindowArea* area, SATWindow* after) +{ + if (!area->_AddWindow(window, after)) + return false; + + if (!fSATWindowList.AddItem(window)) { + area->_RemoveWindow(window); + return false; + } + + if (!window->AddedToGroup(this, area)) { + area->_RemoveWindow(window); + fSATWindowList.RemoveItem(window); + return false; + } + + return true; +} + + +bool +SATGroup::RemoveWindow(SATWindow* window) +{ + if (!fSATWindowList.RemoveItem(window)) + return false; + + _SplitGroupIfNecessary(window); + + if (window->GetWindowArea()) + window->GetWindowArea()->_RemoveWindow(window); + + for (int i = 0; i < CountItems(); i++) + WindowAt(i)->DoGroupLayout(); + + window->RemovedFromGroup(this); + // Do nothing after removing the window from the group because this + // could have released the last reference and destroyed ourself. + return true; +} + + +int32 +SATGroup::CountItems() +{ + return fSATWindowList.CountItems(); +}; + + +SATWindow* +SATGroup::WindowAt(int32 index) +{ + return fSATWindowList.ItemAt(index); +} + + +const TabList* +SATGroup::HorizontalTabs() +{ + if (!fHorizontalTabsSorted) { + fHorizontalTabs.SortItems(Tab::CompareFunction); + fHorizontalTabsSorted = true; + } + return &fHorizontalTabs; +} + + +const TabList* +SATGroup::VerticalTabs() +{ + if (!fVerticalTabsSorted) { + fVerticalTabs.SortItems(Tab::CompareFunction); + fVerticalTabsSorted = true; + } + return &fVerticalTabs; +} + + +Tab* +SATGroup::FindHorizontalTab(float position) +{ + return _FindTab(fHorizontalTabs, position); +} + + +Tab* +SATGroup::FindVerticalTab(float position) +{ + return _FindTab(fVerticalTabs, position); +} + + +void +SATGroup::SolveSATAndAdjustWindows(SATWindow* triggerWindow) +{ + // solve Stack & Tile specification + ResultType result; + for (int32 tries = 0; tries < 15; tries++) { + result = fLinearSpec.Solve(); + if (result == OPTIMAL || result == INFEASIBLE) + break; + } + + // set window locations and sizes + for (int i = 0; i < fSATWindowList.CountItems(); i++) { + SATWindow* windowSAT = fSATWindowList.ItemAt(i); + if (windowSAT == triggerWindow) + continue; + windowSAT->MoveWindowToSAT( + triggerWindow->GetWindow()->CurrentWorkspace()); + } +} + + +BReference +SATGroup::_AddHorizontalTab(float position) +{ + Variable* variable = fLinearSpec.AddVariable(); + if (!variable) + return NULL; + + Tab* tab = new (std::nothrow)Tab(this, variable, Tab::kHorizontal); + if (!tab) + return NULL; + BReference tabRef(tab, true); + + if (!fHorizontalTabs.AddItem(tab)) + return NULL; + + fHorizontalTabsSorted = false; + tabRef->SetPosition(position); + return tabRef; +} + + +BReference +SATGroup::_AddVerticalTab(float position) +{ + Variable* variable = fLinearSpec.AddVariable(); + if (!variable) + return NULL; + + Tab* tab = new (std::nothrow)Tab(this, variable, Tab::kVertical); + if (!tab) + return NULL; + BReference tabRef(tab, true); + + if (!fVerticalTabs.AddItem(tab)) + return NULL; + + fVerticalTabsSorted = false; + tabRef->SetPosition(position); + return tabRef; +} + + +bool +SATGroup::_RemoveHorizontalTab(Tab* tab) +{ + if (!fHorizontalTabs.RemoveItem(tab)) + return false; + fHorizontalTabsSorted = false; + // don't delete the tab it is reference counted + return true; +} + + +bool +SATGroup::_RemoveVerticalTab(Tab* tab) +{ + if (!fVerticalTabs.RemoveItem(tab)) + return false; + fVerticalTabsSorted = false; + // don't delete the tab it is reference counted + return true; +} + + +Tab* +SATGroup::_FindTab(const TabList& list, float position) +{ + for (int i = 0; i < list.CountItems(); i++) + if (list.ItemAt(i)->Position() == position) + return list.ItemAt(i); + + return NULL; +} + + +void +SATGroup::_SplitGroupIfNecessary(SATWindow* removedWindow) +{ + WindowAreaList neighbourWindows; + + _FillNeighbourList(neighbourWindows, removedWindow->GetWindowArea()); + + bool ownGroupProcessed = false; + WindowAreaList newGroup; + while (_FindConnectedGroup(neighbourWindows, removedWindow, newGroup)) { + STRACE_SAT("Connected group found; %i windows:\n", + (int)newGroup.CountItems()); + for (int i = 0; i < newGroup.CountItems(); i++) { + STRACE_SAT("\t%s\n", newGroup.ItemAt(i)->WindowList().ItemAt(0) + ->GetWindow()->Title()); + } + + if (newGroup.CountItems() == 1 + && newGroup.ItemAt(0)->WindowList().CountItems() == 1) { + SATWindow* window = newGroup.ItemAt(0)->WindowList().ItemAt(0); + RemoveWindow(window); + _EnsureGroupIsOnScreen(window->GetGroup()); + } + else if (ownGroupProcessed) + _SpawnNewGroup(newGroup); + else { + _EnsureGroupIsOnScreen(this); + ownGroupProcessed = true; + } + + newGroup.MakeEmpty(); + } +} + + +void +SATGroup::_FillNeighbourList(WindowAreaList& neighbourWindows, + WindowArea* area) +{ + if (!area) + return; + _LeftNeighbours(neighbourWindows, area); + _RightNeighbours(neighbourWindows, area); + _TopNeighbours(neighbourWindows, area); + _BottomNeighbours(neighbourWindows, area); +} + + +void +SATGroup::_LeftNeighbours(WindowAreaList& neighbourWindows, WindowArea* parent) +{ + float startPos = parent->LeftTopCrossing()->HorizontalTab()->Position(); + float endPos = parent->LeftBottomCrossing()->HorizontalTab()->Position(); + + Tab* tab = parent->LeftTopCrossing()->VerticalTab(); + const CrossingList* crossingList = tab->GetCrossingList(); + for (int i = 0; i < crossingList->CountItems(); i++) { + Corner* corner = crossingList->ItemAt(i)->LeftTopCorner(); + if (corner->status != Corner::kUsed) + continue; + + WindowArea* area = corner->windowArea; + float pos1 = area->LeftTopCrossing()->HorizontalTab()->Position(); + float pos2 = area->LeftBottomCrossing()->HorizontalTab()->Position(); + + if (pos1 < endPos && pos2 > startPos) + neighbourWindows.AddItem(area); + + if (pos2 > endPos) + break; + } +} + + +void +SATGroup::_TopNeighbours(WindowAreaList& neighbourWindows, WindowArea* parent) +{ + float startPos = parent->LeftTopCrossing()->VerticalTab()->Position(); + float endPos = parent->RightTopCrossing()->VerticalTab()->Position(); + + Tab* tab = parent->LeftTopCrossing()->HorizontalTab(); + const CrossingList* crossingList = tab->GetCrossingList(); + for (int i = 0; i < crossingList->CountItems(); i++) { + Corner* corner = crossingList->ItemAt(i)->LeftTopCorner(); + if (corner->status != Corner::kUsed) + continue; + + WindowArea* area = corner->windowArea; + float pos1 = area->LeftTopCrossing()->VerticalTab()->Position(); + float pos2 = area->RightTopCrossing()->VerticalTab()->Position(); + + if (pos1 < endPos && pos2 > startPos) + neighbourWindows.AddItem(area); + + if (pos2 > endPos) + break; + } +} + + +void +SATGroup::_RightNeighbours(WindowAreaList& neighbourWindows, WindowArea* parent) +{ + float startPos = parent->RightTopCrossing()->HorizontalTab()->Position(); + float endPos = parent->RightBottomCrossing()->HorizontalTab()->Position(); + + Tab* tab = parent->RightTopCrossing()->VerticalTab(); + const CrossingList* crossingList = tab->GetCrossingList(); + for (int i = 0; i < crossingList->CountItems(); i++) { + Corner* corner = crossingList->ItemAt(i)->RightTopCorner(); + if (corner->status != Corner::kUsed) + continue; + + WindowArea* area = corner->windowArea; + float pos1 = area->RightTopCrossing()->HorizontalTab()->Position(); + float pos2 = area->RightBottomCrossing()->HorizontalTab()->Position(); + + if (pos1 < endPos && pos2 > startPos) + neighbourWindows.AddItem(area); + + if (pos2 > endPos) + break; + } +} + + +void +SATGroup::_BottomNeighbours(WindowAreaList& neighbourWindows, + WindowArea* parent) +{ + float startPos = parent->LeftBottomCrossing()->VerticalTab()->Position(); + float endPos = parent->RightBottomCrossing()->VerticalTab()->Position(); + + Tab* tab = parent->LeftBottomCrossing()->HorizontalTab(); + const CrossingList* crossingList = tab->GetCrossingList(); + for (int i = 0; i < crossingList->CountItems(); i++) { + Corner* corner = crossingList->ItemAt(i)->LeftBottomCorner(); + if (corner->status != Corner::kUsed) + continue; + + WindowArea* area = corner->windowArea; + float pos1 = area->LeftBottomCrossing()->VerticalTab()->Position(); + float pos2 = area->RightBottomCrossing()->VerticalTab()->Position(); + + if (pos1 < endPos && pos2 > startPos) + neighbourWindows.AddItem(area); + + if (pos2 > endPos) + break; + } +} + + +bool +SATGroup::_FindConnectedGroup(WindowAreaList& seedList, + SATWindow* removedWindow, WindowAreaList& newGroup) +{ + if (seedList.CountItems() == 0) + return false; + WindowArea* vetoArea = removedWindow->GetWindowArea(); + if (!vetoArea) + return false; + + WindowArea* area = seedList.RemoveItemAt(0); + newGroup.AddItem(area); + + _FollowSeed(area, vetoArea, seedList, newGroup); + return true; +} + + +void +SATGroup::_FollowSeed(WindowArea* area, WindowArea* veto, + WindowAreaList& seedList, WindowAreaList& newGroup) +{ + WindowAreaList neighbours; + _FillNeighbourList(neighbours, area); + for (int i = 0; i < neighbours.CountItems(); i++) { + WindowArea* currentArea = neighbours.ItemAt(i); + if (currentArea != veto && !newGroup.HasItem(currentArea)) { + newGroup.AddItem(currentArea); + // if we get a area from the seed list it is not a seed any more + seedList.RemoveItem(currentArea); + } + else { + // don't _FollowSeed of invalid areas + neighbours.RemoveItemAt(i); + i--; + } + } + + for (int i = 0; i < neighbours.CountItems(); i++) + _FollowSeed(neighbours.ItemAt(i), veto, seedList, newGroup); +} + + +void +SATGroup::_SpawnNewGroup(const WindowAreaList& newGroup) +{ + STRACE_SAT("SATGroup::_SpawnNewGroup\n"); + SATGroup* group = new (std::nothrow)SATGroup; + if (!group) + return; + BReference groupRef; + groupRef.SetTo(group, true); + + for (int i = 0; i < newGroup.CountItems(); i++) + newGroup.ItemAt(i)->PropagateToGroup(group); + + _EnsureGroupIsOnScreen(group); +} + + +const float kMinOverlap = 50; +const float kMoveToScreen = 75; + + +void +SATGroup::_EnsureGroupIsOnScreen(SATGroup* group) +{ + STRACE_SAT("SATGroup::_EnsureGroupIsOnScreen\n"); + if (!group) + return; + + if (group->CountItems() < 1) + return; + + SATWindow* window = group->WindowAt(0); + Desktop* desktop = window->GetWindow()->Desktop(); + if (!desktop) + return; + + const float kBigDistance = 1E+10; + + float minLeftDistance = kBigDistance; + BRect leftRect; + float minTopDistance = kBigDistance; + BRect topRect; + float minRightDistance = kBigDistance; + BRect rightRect; + float minBottomDistance = kBigDistance; + BRect bottomRect; + + BRect screen = window->GetWindow()->Screen()->Frame(); + BRect reducedScreen = screen; + reducedScreen.InsetBy(kMinOverlap, kMinOverlap); + + for (int i = 0; i < group->CountItems(); i++) { + SATWindow* window = group->WindowAt(i); + BRect frame = window->CompleteWindowFrame(); + if (reducedScreen.Intersects(frame)) + return; + + if (frame.right < screen.left + kMinOverlap) { + float dist = fabs(screen.left - frame.right); + if (dist < minLeftDistance) { + minLeftDistance = dist; + leftRect = frame; + } + else if (dist == minLeftDistance) + leftRect = leftRect | frame; + } + if (frame.top > screen.bottom - kMinOverlap) { + float dist = fabs(frame.top - screen.bottom); + if (dist < minBottomDistance) { + minBottomDistance = dist; + bottomRect = frame; + } + else if (dist == minBottomDistance) + bottomRect = bottomRect | frame; + } + if (frame.left > screen.right - kMinOverlap) { + float dist = fabs(frame.left - screen.right); + if (dist < minRightDistance) { + minRightDistance = dist; + rightRect = frame; + } + else if (dist == minRightDistance) + rightRect = rightRect | frame; + } + if (frame.bottom < screen.top + kMinOverlap) { + float dist = fabs(frame.bottom - screen.top); + if (dist < minTopDistance) { + minTopDistance = dist; + topRect = frame; + } + else if (dist == minTopDistance) + topRect = topRect | frame; + } + } + + BPoint offset; + if (minLeftDistance < kBigDistance) { + offset.x = screen.left - leftRect.right + kMoveToScreen; + _CallculateYOffset(offset, leftRect, screen); + } + else if (minTopDistance < kBigDistance) { + offset.y = screen.top - topRect.bottom + kMoveToScreen; + _CallculateXOffset(offset, topRect, screen); + } + else if (minRightDistance < kBigDistance) { + offset.x = screen.right - rightRect.left - kMoveToScreen; + _CallculateYOffset(offset, rightRect, screen); + } + else if (minBottomDistance < kBigDistance) { + offset.y = screen.bottom - bottomRect.top - kMoveToScreen; + _CallculateXOffset(offset, bottomRect, screen); + } + + if (offset.x == 0. && offset.y == 0.) + return; + STRACE_SAT("move group back to screen: offset x: %f offset y: %f\n", + offset.x, offset.y); + + desktop->MoveWindowBy(window->GetWindow(), offset.x, offset.y); + window->DoGroupLayout(); +} + + +void +SATGroup::_CallculateXOffset(BPoint& offset, BRect& frame, BRect& screen) +{ + if (frame.right < screen.left + kMinOverlap) + offset.x = screen.left - frame.right + kMoveToScreen; + else if (frame.left > screen.right - kMinOverlap) + offset.x = screen.right - frame.left - kMoveToScreen; +} + + +void +SATGroup::_CallculateYOffset(BPoint& offset, BRect& frame, BRect& screen) +{ + if (frame.top > screen.bottom - kMinOverlap) + offset.y = screen.bottom - frame.top - kMoveToScreen; + else if (frame.bottom < screen.top + kMinOverlap) + offset.y = screen.top - frame.bottom + kMoveToScreen; +} diff --git a/src/add-ons/decorators/SATDecorator/SATGroup.h b/src/add-ons/decorators/SATDecorator/SATGroup.h new file mode 100644 index 0000000000..c2c4d2876c --- /dev/null +++ b/src/add-ons/decorators/SATDecorator/SATGroup.h @@ -0,0 +1,267 @@ +/* + * Copyright 2010, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Clemens Zeidler + */ +#ifndef SAT_GROUP_H +#define SAT_GROUP_H + +#include + +#include "ObjectList.h" +#include "Referenceable.h" + +#include "LinearSpec.h" + + +class SATWindow; +class Tab; +class WindowArea; + +typedef BObjectList SATWindowList; + + +class Corner { +public: + enum info_t + { + kFree, + kUsed, + kNotDockable + }; + + enum position_t + { + kLeftTop, + kRightTop, + kLeftBottom, + kRightBottom + }; + + Corner(); + void Trace() const; + + info_t status; + WindowArea* windowArea; +}; + + +class Crossing : public BReferenceable { +public: + Crossing(Tab* vertical, Tab* horizontal); + ~Crossing(); + + Corner* GetCorner(Corner::position_t corner) const; + Corner* GetOppositeCorner(Corner::position_t corner) const; + + Corner* LeftTopCorner() { return &fLeftTop; } + Corner* RightTopCorner() { return &fRightTop; } + Corner* LeftBottomCorner() { return &fLeftBottom; } + Corner* RightBottomCorner() { return &fRightBottom; } + + Tab* VerticalTab() const; + Tab* HorizontalTab() const; + + void Trace() const; +private: + Corner* _GetCorner(Corner::position_t corner) const; + + Corner fLeftTop; + Corner fRightTop; + Corner fLeftBottom; + Corner fRightBottom; + + Tab* fVerticalTab; + Tab* fHorizontalTab; +}; + + +typedef BObjectList ConstraintList; +class SATGroup; + +typedef BObjectList CrossingList; + + +class Tab : public BReferenceable { +public: + enum orientation_t + { + kVertical, + kHorizontal + }; + + Tab(SATGroup* group, Variable* variable, + orientation_t orientation); + ~Tab(); + + float Position() const; + void SetPosition(float position); + orientation_t Orientation() const; + + //! Caller takes ownership of the constraint. + Constraint* Connect(Variable* variable); + + BReference AddCrossing(Tab* tab); + bool RemoveCrossing(Crossing* crossing); + int32 FindCrossingIndex(Tab* tab); + int32 FindCrossingIndex(float tabPosition); + Crossing* FindCrossing(Tab* tab); + Crossing* FindCrossing(float tabPosition); + + const CrossingList* GetCrossingList() const; + + static int CompareFunction(const Tab* tab1, const Tab* tab2); + +private: + SATGroup* fGroup; + Variable* fVariable; + orientation_t fOrientation; + + CrossingList fCrossingList; +}; + + +class WindowArea : public BReferenceable { +public: + WindowArea(Crossing* leftTop, + Crossing* rightTop, Crossing* leftBottom, + Crossing* rightBottom); + ~WindowArea(); + + const SATWindowList& WindowList() { return fWindowList; } + bool MoveWindowToPosition(SATWindow* window, + int32 index); + + Crossing* LeftTopCrossing() + { return fLeftTopCrossing.Get(); } + Crossing* RightTopCrossing() + { return fRightTopCrossing.Get(); } + Crossing* LeftBottomCrossing() + { return fLeftBottomCrossing.Get(); } + Crossing* RightBottomCrossing() + { return fRightBottomCrossing.Get(); } + + Tab* LeftTab(); + Tab* RightTab(); + Tab* TopTab(); + Tab* BottomTab(); + + BRect Frame(); + + bool PropagateToGroup(SATGroup* group); + +private: + friend class SATGroup; + /*! SATGroup adds new windows to the area. */ + bool _AddWindow(SATWindow* window, + SATWindow* after = NULL); + /*! After the last window has been removed the WindowArea delete himself + and clean up all crossings. */ + bool _RemoveWindow(SATWindow* window); + + inline void _InitCorners(); + inline void _CleanupCorners(); + inline void _SetToWindowCorner(Corner* corner); + inline void _SetToNeighbourCorner(Corner* neighbour); + inline void _UnsetWindowCorner(Corner* corner); + //! opponent is the other neighbour of the neighbour + inline void _UnsetNeighbourCorner(Corner* neighbour, + Corner* opponent); + + // Find crossing by tab position in group and if not exist create it. + BReference _CrossingByPosition(Crossing* crossing, + SATGroup* group); + + SATWindowList fWindowList; + + BReference fLeftTopCrossing; + BReference fRightTopCrossing; + BReference fLeftBottomCrossing; + BReference fRightBottomCrossing; +}; + + +typedef BObjectList WindowAreaList; +typedef BObjectList TabList; + + +class SATGroup : public BReferenceable { +public: + friend class Tab; + friend class WindowArea; + friend class SATWindow; + + SATGroup(); + ~SATGroup(); + + LinearSpec* GetLinearSpec() { return &fLinearSpec; } + + void SolveSATAndAdjustWindows(SATWindow* triggerWindow); + + /*! Create a new WindowArea from the crossing and add the window. */ + bool AddWindow(SATWindow* window, Tab* left, Tab* top, + Tab* right, Tab* bottom); + /*! Add a window to an existing window area. */ + bool AddWindow(SATWindow* window, WindowArea* area, + SATWindow* after = NULL); + bool RemoveWindow(SATWindow* window); + int32 CountItems(); + SATWindow* WindowAt(int32 index); + + /*! \return a sorted tab list. */ + const TabList* HorizontalTabs(); + const TabList* VerticalTabs(); + + Tab* FindHorizontalTab(float position); + Tab* FindVerticalTab(float position); + +protected: + SATWindowList fSATWindowList; + LinearSpec fLinearSpec; + +private: + BReference _AddHorizontalTab(float position = 0); + BReference _AddVerticalTab(float position = 0); + + bool _RemoveHorizontalTab(Tab* tab); + bool _RemoveVerticalTab(Tab* tab); + + Tab* _FindTab(const TabList& list, float position); + + void _SplitGroupIfNecessary(SATWindow* removedWindow); + void _FillNeighbourList(WindowAreaList& neighbourWindows, + WindowArea* area); + void _LeftNeighbours(WindowAreaList& neighbourWindows, + WindowArea* window); + void _TopNeighbours(WindowAreaList& neighbourWindows, + WindowArea* window); + void _RightNeighbours(WindowAreaList& neighbourWindows, + WindowArea* window); + void _BottomNeighbours(WindowAreaList& neighbourWindows, + WindowArea* window); + bool _FindConnectedGroup(WindowAreaList& seedList, + SATWindow* removedWindow, + WindowAreaList& newGroup); + void _FollowSeed(WindowArea* area, WindowArea* veto, + WindowAreaList& seedList, + WindowAreaList& newGroup); + void _SpawnNewGroup(const WindowAreaList& newGroup); + + void _EnsureGroupIsOnScreen(SATGroup* group); + inline void _CallculateXOffset(BPoint& offset, BRect& frame, + BRect& screen); + inline void _CallculateYOffset(BPoint& offset, BRect& frame, + BRect& screen); + + TabList fHorizontalTabs; + bool fHorizontalTabsSorted; + TabList fVerticalTabs; + bool fVerticalTabsSorted; +}; + + +typedef BObjectList SATGroupList; + +#endif diff --git a/src/add-ons/decorators/SATDecorator/SATWindow.cpp b/src/add-ons/decorators/SATDecorator/SATWindow.cpp new file mode 100644 index 0000000000..cd93c4b56e --- /dev/null +++ b/src/add-ons/decorators/SATDecorator/SATWindow.cpp @@ -0,0 +1,546 @@ +/* + * Copyright 2010, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Clemens Zeidler + */ + +#include "SATWindow.h" + +#include + +#include "Window.h" + + +SATWindow::SATWindow(StackAndTile* sat, Window* window) + : + fWindow(window), + fStackAndTile(sat), + + fOwnGroupCookie(this), + fForeignGroupCookie(this), + + fOngoingSnapping(NULL), + fSATStacking(this), + fSATTiling(this), + fShutdown(false) +{ + fDecorator = dynamic_cast(fWindow->Decorator()); + fDesktop = fWindow->Desktop(); + + fGroupCookie = &fOwnGroupCookie; + _InitGroup(); + + fSATSnappingBehaviourList.AddItem(&fSATStacking); + fSATSnappingBehaviourList.AddItem(&fSATTiling); +} + + +SATWindow::~SATWindow() +{ + fShutdown = true; + + if (fForeignGroupCookie.GetGroup()) + fForeignGroupCookie.GetGroup()->RemoveWindow(this); + if (fOwnGroupCookie.GetGroup()) + fOwnGroupCookie.GetGroup()->RemoveWindow(this); +} + + +SATGroup* +SATWindow::GetGroup() +{ + if (!fGroupCookie->GetGroup()) + _InitGroup(); + + // manually set the tabs of the single window + WindowArea* windowArea = fGroupCookie->GetWindowArea(); + if (!PositionManagedBySAT() && windowArea) { + BRect frame = CompleteWindowFrame(); + windowArea->LeftTopCrossing()->VerticalTab()->SetPosition(frame.left); + windowArea->LeftTopCrossing()->HorizontalTab()->SetPosition(frame.top); + windowArea->RightBottomCrossing()->VerticalTab()->SetPosition( + frame.right); + windowArea->RightBottomCrossing()->HorizontalTab()->SetPosition( + frame.bottom); + } + + return fGroupCookie->GetGroup(); +} + + +bool +SATWindow::PropagateToGroup(SATGroup* group, WindowArea* area) +{ + return fGroupCookie->PropagateToGroup(group, area); +} + + +void +SATWindow::MoveWindowToSAT(int32 workspace) +{ + fGroupCookie->MoveWindow(workspace); +} + + +bool +SATWindow::AddedToGroup(SATGroup* group, WindowArea* area) +{ + STRACE_SAT("SATWindow::AddedToGroup group: %p window %s\n", group, + fWindow->Title()); + if (fGroupCookie == &fForeignGroupCookie) + return false; + if (fOwnGroupCookie.GetGroup()) + fGroupCookie = &fForeignGroupCookie; + + if (!fGroupCookie->Init(group, area)) { + fGroupCookie = &fOwnGroupCookie; + return false; + } + + return true; +} + + +bool +SATWindow::RemovedFromGroup(SATGroup* group) +{ + STRACE_SAT("SATWindow::RemovedFromGroup group: %p window %s\n", group, + fWindow->Title()); + + if (fShutdown) { + fGroupCookie->Uninit(); + return true; + } + + ASSERT(fGroupCookie->GetGroup() == group); + fGroupCookie->Uninit(); + if (fGroupCookie == &fOwnGroupCookie) + _InitGroup(); + else + fGroupCookie = &fOwnGroupCookie; + + return true; +} + + +void +SATWindow::RemovedFromArea(WindowArea* area) +{ + for (int i = 0; i < fSATSnappingBehaviourList.CountItems(); i++) + fSATSnappingBehaviourList.ItemAt(i)->RemovedFromArea(area); +} + + +void +SATWindow::FindSnappingCandidates() +{ + fOngoingSnapping = NULL; + GroupIterator groupIterator(fStackAndTile, GetWindow()->Desktop()); + for (SATGroup* group = groupIterator.NextGroup(); group; + group = groupIterator.NextGroup()) { + for (int i = 0; i < fSATSnappingBehaviourList.CountItems(); i++) { + if (fSATSnappingBehaviourList.ItemAt(i)->FindSnappingCandidates( + group)) { + fOngoingSnapping = fSATSnappingBehaviourList.ItemAt(i); + return; + } + } + } +} + + +bool +SATWindow::JoinCandidates() +{ + if (!fOngoingSnapping) + return false; + bool status = fOngoingSnapping->JoinCandidates(); + fOngoingSnapping = NULL; + return status; +} + + +void +SATWindow::DoGroupLayout() +{ + if (!PositionManagedBySAT()) + return; + + fGroupCookie->DoGroupLayout(this); + + for (int i = 0; i < fSATSnappingBehaviourList.CountItems(); i++) + fSATSnappingBehaviourList.ItemAt(i)->DoGroupLayout(); +} + + +BRect +SATWindow::CompleteWindowFrame() +{ + BRect frame = fWindow->Frame(); + if (fDesktop + && fDesktop->CurrentWorkspace() != fWindow->CurrentWorkspace()) { + window_anchor& anchor = fWindow->Anchor(fWindow->CurrentWorkspace()); + if (anchor.position != kInvalidWindowPosition) + frame.OffsetTo(anchor.position); + } + + // TODO get this values from the decorator + frame.left -= 5.; + frame.right += 6.; + frame.top -= 27; + frame.bottom += 5; + + return frame; +} + + +bool +SATWindow::PositionManagedBySAT() +{ + if (fGroupCookie->GetGroup() && fGroupCookie->GetGroup()->CountItems() == 1) + return false; + + return true; +} + + +bool +SATWindow::HighlightTab(bool active) +{ + if (!fDecorator) + return false; + + if (IsTabHighlighted() == active) + return false; + + BRegion dirty; + fDecorator->HighlightTab(active, &dirty); + fWindow->ProcessDirtyRegion(dirty); + + return true; +} + + +bool +SATWindow::HighlightBorders(bool active) +{ + if (!fDecorator) + return false; + + if (IsBordersHighlighted() == active) + return false; + + BRegion dirty; + fDecorator->HighlightBorders(active, &dirty); + fWindow->ProcessDirtyRegion(dirty); + return true; +} + + +bool +SATWindow::IsTabHighlighted() +{ + if (fDecorator) + return fDecorator->IsTabHighlighted(); + return false; +} + + +bool +SATWindow::IsBordersHighlighted() +{ + if (fDecorator) + return fDecorator->IsBordersHighlighted(); + return false; +} + + +bool +SATWindow::SetStackedMode(bool stacked) +{ + if (!fDecorator) + return false; + BRegion dirty; + fDecorator->SetStackedMode(stacked, &dirty); + fDesktop->RebuildAndRedrawAfterWindowChange(fWindow, dirty); + return true; +} + + +bool +SATWindow::SetStackedTabLength(float length, bool drawZoom) +{ + if (!fDecorator) + return false; + BRegion dirty; + fDecorator->SetStackedTabLength(length, drawZoom, &dirty); + fDesktop->RebuildAndRedrawAfterWindowChange(fWindow, dirty); + return true; +} + + +bool +SATWindow::SetStackedTabMoving(bool moving) +{ + if (!fDecorator) + return false; + + if (!moving) + DoGroupLayout(); + + return true; +} + + +void +SATWindow::TabLocationMoved(float location, bool shifting) +{ + for (int i = 0; i < fSATSnappingBehaviourList.CountItems(); i++) + fSATSnappingBehaviourList.ItemAt(i)->TabLocationMoved(location, + shifting); +} + + +void +SATWindow::_InitGroup() +{ + ASSERT(fGroupCookie == &fOwnGroupCookie); + ASSERT(fOwnGroupCookie.GetGroup() == NULL); + STRACE_SAT("SATWindow::_InitGroup %s\n", fWindow->Title()); + SATGroup* group = new (std::nothrow)SATGroup; + if (!group) + return; + BReference groupRef; + groupRef.SetTo(group, true); + + /* AddWindow also will trigger the window to hold a reference on the new + group. */ + if (!groupRef->AddWindow(this, NULL, NULL, NULL, NULL)) + STRACE_SAT("SATWindow::_InitGroup(): adding window to group failed\n"); +} + + +SATWindow::GroupCookie::GroupCookie(SATWindow* satWindow) + : + fSATWindow(satWindow), + + windowArea(NULL), + + leftBorder(NULL), + topBorder(NULL), + rightBorder(NULL), + bottomBorder(NULL), + + leftBorderConstraint(NULL), + topBorderConstraint(NULL), + rightBorderConstraint(NULL), + bottomBorderConstraint(NULL), + + leftConstraint(NULL), + topConstraint(NULL), + minWidthConstraint(NULL), + minHeightConstraint(NULL), + widthConstraint(NULL), + heightConstraint(NULL) +{ + +} + + +SATWindow::GroupCookie::~GroupCookie() +{ + Uninit(); +} + + +void +SATWindow::GroupCookie::DoGroupLayout(SATWindow* triggerWindow) +{ + if (!fSATGroup.Get()) + return; + + BRect frame = triggerWindow->CompleteWindowFrame(); + + // adjust window size soft constraints + widthConstraint->SetRightSide(frame.Width()); + heightConstraint->SetRightSide(frame.Height()); + + // adjust window position soft constraints + // (a bit more penalty for them so they take precedence) + leftConstraint->SetRightSide(frame.left); + topConstraint->SetRightSide(frame.top); + + + widthConstraint->SetPenaltyNeg(110); + widthConstraint->SetPenaltyPos(110); + heightConstraint->SetPenaltyNeg(110); + heightConstraint->SetPenaltyPos(110); + + leftConstraint->SetPenaltyNeg(100); + leftConstraint->SetPenaltyPos(100); + topConstraint->SetPenaltyNeg(100); + topConstraint->SetPenaltyPos(100); + + // After we set the new parameter solve and apply the new layout. + fSATGroup->SolveSATAndAdjustWindows(triggerWindow); + + // set penalties back to normal + widthConstraint->SetPenaltyNeg(10); + widthConstraint->SetPenaltyPos(10); + heightConstraint->SetPenaltyNeg(10); + heightConstraint->SetPenaltyPos(10); + + leftConstraint->SetPenaltyNeg(1); + leftConstraint->SetPenaltyPos(1); + topConstraint->SetPenaltyNeg(1); + topConstraint->SetPenaltyPos(1); +} + + +void +SATWindow::GroupCookie::MoveWindow(int32 workspace) +{ + Window* window = fSATWindow->GetWindow(); + Desktop* desktop = window->Desktop(); + + BRect frame = fSATWindow->CompleteWindowFrame(); + desktop->MoveWindowBy(window, leftBorder->Value() - frame.left, + topBorder->Value() - frame.top, workspace); + + // Update frame to the new position + frame.OffsetBy(leftBorder->Value() - frame.left, + topBorder->Value() - frame.top); + desktop->ResizeWindowBy(window, rightBorder->Value() - frame.right, + bottomBorder->Value() - frame.bottom); +} + + +bool +SATWindow::GroupCookie::Init(SATGroup* group, WindowArea* area) +{ + ASSERT(fSATGroup.Get() == NULL); + + Window* window = fSATWindow->GetWindow(); + fSATGroup.SetTo(group); + windowArea = area; + + LinearSpec* linearSpec = group->GetLinearSpec(); + // create variables + leftBorder = linearSpec->AddVariable(); + topBorder = linearSpec->AddVariable(); + rightBorder = linearSpec->AddVariable(); + bottomBorder = linearSpec->AddVariable(); + + leftBorder->SetRange(-DBL_MAX, DBL_MAX); + topBorder->SetRange(-DBL_MAX, DBL_MAX); + rightBorder->SetRange(-DBL_MAX, DBL_MAX); + bottomBorder->SetRange(-DBL_MAX, DBL_MAX); + + if (!leftBorder || !topBorder || !rightBorder || !bottomBorder) { + // clean up + Uninit(); + return false; + } + + // create constraints + BRect frame = fSATWindow->CompleteWindowFrame(); + leftConstraint = linearSpec->AddConstraint(1.0, leftBorder, + OperatorType(EQ), frame.left, 1, 1); + topConstraint = linearSpec->AddConstraint(1.0, topBorder, + OperatorType(EQ), frame.top, 1, 1); + + int32 minWidth, maxWidth, minHeight, maxHeight; + window->GetSizeLimits(&minWidth, &maxWidth, &minHeight, &maxHeight); + minWidthConstraint = linearSpec->AddConstraint(1.0, leftBorder, -1.0, + rightBorder, OperatorType(LE), -minWidth); + minHeightConstraint = linearSpec->AddConstraint(1.0, topBorder, -1.0, + bottomBorder, OperatorType(LE), -minHeight); + + // The width and height constraints have higher penalties than the + // position constraints (left, top), so a window will keep its size + // unless explicitly resized. + widthConstraint = linearSpec->AddConstraint(-1.0, leftBorder, 1.0, + rightBorder, OperatorType(EQ), frame.Width(), 10, 10); + heightConstraint = linearSpec->AddConstraint(-1.0, topBorder, 1.0, + bottomBorder, OperatorType(EQ), frame.Height(), 10, 10); + + if (!leftConstraint || !topConstraint || !minWidthConstraint + || !minHeightConstraint || !widthConstraint || !heightConstraint) { + // clean up + Uninit(); + return false; + } + + leftBorderConstraint = area->LeftTab()->Connect(leftBorder); + topBorderConstraint = area->TopTab()->Connect(topBorder); + rightBorderConstraint = area->RightTab()->Connect(rightBorder); + bottomBorderConstraint = area->BottomTab()->Connect(bottomBorder); + + if (!leftBorderConstraint || !topBorderConstraint + || !rightBorderConstraint || !bottomBorderConstraint) { + Uninit(); + return false; + } + + return true; +} + + +void +SATWindow::GroupCookie::Uninit() +{ + delete leftBorder; + delete topBorder; + delete rightBorder; + delete bottomBorder; + leftBorder = NULL; + topBorder = NULL; + rightBorder = NULL; + bottomBorder = NULL; + + delete leftBorderConstraint; + delete topBorderConstraint; + delete rightBorderConstraint; + delete bottomBorderConstraint; + leftBorderConstraint = NULL; + topBorderConstraint = NULL; + rightBorderConstraint = NULL; + bottomBorderConstraint = NULL; + + delete leftConstraint; + delete topConstraint; + delete minWidthConstraint; + delete minHeightConstraint; + delete widthConstraint; + delete heightConstraint; + leftConstraint = NULL; + topConstraint = NULL; + minWidthConstraint = NULL; + minHeightConstraint = NULL; + widthConstraint = NULL; + heightConstraint = NULL; + + fSATGroup.Unset(); + windowArea = NULL; +} + + +bool +SATWindow::GroupCookie::PropagateToGroup(SATGroup* group, WindowArea* area) +{ + if (!fSATGroup->fSATWindowList.RemoveItem(fSATWindow)) + return false; + Uninit(); + + if (!Init(group, area)) + return false; + + if (!group->fSATWindowList.AddItem(fSATWindow)) { + Uninit(); + return false; + } + + return true; +} diff --git a/src/add-ons/decorators/SATDecorator/SATWindow.h b/src/add-ons/decorators/SATDecorator/SATWindow.h new file mode 100644 index 0000000000..059ab77eba --- /dev/null +++ b/src/add-ons/decorators/SATDecorator/SATWindow.h @@ -0,0 +1,134 @@ +/* + * Copyright 2010, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Clemens Zeidler + */ +#ifndef SAT_WINDOW_H +#define SAT_WINDOW_H + + +#include +#include "SATDecorator.h" +#include "SATGroup.h" +#include "Stacking.h" +#include "Tiling.h" + + +class Desktop; +class StackAndTile; +class Window; + + +class SATWindow { +public: + SATWindow(StackAndTile* sat, Window* window); + ~SATWindow(); + + Window* GetWindow() { return fWindow; } + SATDecorator* GetDecorator() { return fDecorator; } + //! Can be NULL if memory allocation failed! + SATGroup* GetGroup(); + WindowArea* GetWindowArea() { + return fGroupCookie->GetWindowArea(); } + bool PropagateToGroup(SATGroup* group, WindowArea* area); + + //! Move the window to the tab's position. + void MoveWindowToSAT(int32 workspace); + + // hook function called from SATGroup + bool AddedToGroup(SATGroup* group, WindowArea* area); + bool RemovedFromGroup(SATGroup* group); + void RemovedFromArea(WindowArea* area); + + void FindSnappingCandidates(); + bool JoinCandidates(); + void DoGroupLayout(); + + //! \return the complete window frame including the Decorator + BRect CompleteWindowFrame(); + + //! \return true if window is in a group with a least another window + bool PositionManagedBySAT(); + + bool HighlightTab(bool active); + bool HighlightBorders(bool active); + bool IsTabHighlighted(); + bool IsBordersHighlighted(); + + bool SetStackedMode(bool stacked = true); + bool SetStackedTabLength(float length, bool drawZoom); + bool SetStackedTabMoving(bool moving = true); + void TabLocationMoved(float location, bool shifting); + +private: + void _InitGroup(); + + Window* fWindow; + SATDecorator* fDecorator; + StackAndTile* fStackAndTile; + Desktop* fDesktop; + + class GroupCookie + { + public: + GroupCookie(SATWindow* satWindow); + ~GroupCookie(); + + bool Init(SATGroup* group, WindowArea* area); + void Uninit(); + + void DoGroupLayout(SATWindow* triggerWindow); + void MoveWindow(int32 workspace); + + SATGroup* GetGroup() { return fSATGroup.Get(); } + + WindowArea* GetWindowArea() { return windowArea; } + + bool PropagateToGroup(SATGroup* group, + WindowArea* area); + + private: + SATWindow* fSATWindow; + + BReference fSATGroup; + + WindowArea* windowArea; + + Variable* leftBorder; + Variable* topBorder; + Variable* rightBorder; + Variable* bottomBorder; + + Constraint* leftBorderConstraint; + Constraint* topBorderConstraint; + Constraint* rightBorderConstraint; + Constraint* bottomBorderConstraint; + + Constraint* leftConstraint; + Constraint* topConstraint; + Constraint* minWidthConstraint; + Constraint* minHeightConstraint; + Constraint* widthConstraint; + Constraint* heightConstraint; + }; + + //! Current group. + GroupCookie* fGroupCookie; + /*! If the window is added to another group the own group is cached + here. */ + GroupCookie fOwnGroupCookie; + GroupCookie fForeignGroupCookie; + + SATSnappingBehaviour* fOngoingSnapping; + SATStacking fSATStacking; + SATTiling fSATTiling; + + SATSnappingBehaviourList fSATSnappingBehaviourList; + + bool fShutdown; +}; + + +#endif diff --git a/src/add-ons/decorators/SATDecorator/StackAndTile.cpp b/src/add-ons/decorators/SATDecorator/StackAndTile.cpp new file mode 100644 index 0000000000..e9f767e372 --- /dev/null +++ b/src/add-ons/decorators/SATDecorator/StackAndTile.cpp @@ -0,0 +1,399 @@ +/* + * Copyright 2010, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Clemens Zeidler + */ + +#include "StackAndTile.h" + +#include + +#include "Desktop.h" +#include "SATWindow.h" +#include "Tiling.h" +#include "Window.h" + + +using namespace std; + + +StackAndTile::StackAndTile() + : + fSATKeyPressed(false), + fCurrentSATWindow(NULL), + fTabIsShifting(false) +{ + +} + + +StackAndTile::~StackAndTile() +{ + +} + + +void +StackAndTile::ListenerRegistered(Desktop* desktop) +{ + WindowList& windows = desktop->AllWindows(); + for (Window *window = windows.FirstWindow(); window != NULL; + window = window->NextWindow(kAllWindowList)) + AddWindow(window); +} + + +void +StackAndTile::ListenerUnregistered() +{ + for (SATWindowMap::iterator it = fSATWindowMap.begin(); + it != fSATWindowMap.end(); it++) { + SATWindow* satWindow = it->second; + delete satWindow; + } + fSATWindowMap.clear(); +} + + +void +StackAndTile::AddWindow(Window* window) +{ + SATWindow* satWindow = new (std::nothrow)SATWindow(this, window); + if (!satWindow) + return; + + ASSERT(fSATWindowMap.find(window) == fSATWindowMap.end()); + fSATWindowMap[window] = satWindow; +} + + +void +StackAndTile::RemoveWindow(Window* window) +{ + STRACE_SAT("StackAndTile::WindowRemoved %s\n", window->Title()); + + SATWindowMap::iterator it = fSATWindowMap.find(window); + if (it == fSATWindowMap.end()) + return; + + SATWindow* satWindow = it->second; + // delete SATWindow + delete satWindow; + fSATWindowMap.erase(it); +} + + +void +StackAndTile::KeyEvent(uint32 what, int32 key, int32 modifiers) +{ + // switch to and from stacking and snapping mode + if (what == B_MODIFIERS_CHANGED) { + bool wasPressed = fSATKeyPressed; + fSATKeyPressed = modifiers & B_OPTION_KEY; + if (wasPressed && !fSATKeyPressed) + _StopSAT(); + if (!wasPressed && fSATKeyPressed) + _StartSAT(); + } + + return; +} + + +void +StackAndTile::MouseDown(Window* window, BMessage* message, const BPoint& where) +{ + SATWindow* satWindow = GetSATWindow(window); + if (!satWindow) + return; + ASSERT(fCurrentSATWindow == NULL); + fCurrentSATWindow = satWindow; + + if (!SATKeyPressed()) + return; + + _StartSAT(); +} + + +void +StackAndTile::MouseUp(Window* window, BMessage* message, const BPoint& where) +{ + if (fTabIsShifting) { + SATWindow* satWindow = GetSATWindow(window); + if (satWindow) { + fTabIsShifting = false; + satWindow->TabLocationMoved(satWindow->GetWindow()->TabLocation(), + fTabIsShifting); + } + } + + if (fSATKeyPressed) + _StopSAT(); + + fCurrentSATWindow = NULL; +} + + +void +StackAndTile::MoveWindow(Window* window) +{ + SATWindow* satWindow = GetSATWindow(window); + if (!satWindow) + return; + + if (SATKeyPressed()) + satWindow->FindSnappingCandidates(); + else + satWindow->DoGroupLayout(); +} + + +void +StackAndTile::ResizeWindow(Window* window) +{ + MoveWindow(window); +} + + +void +StackAndTile::ActivateWindow(Window* window) +{ + SATWindow* satWindow = GetSATWindow(window); + if (!satWindow) + return; + _ActivateWindow(satWindow); + +} + + +void +StackAndTile::SendWindowBehind(Window* window, Window* behindOf) +{ + SATWindow* satWindow = GetSATWindow(window); + if (!satWindow) + return; + SATGroup* group = satWindow->GetGroup(); + if (!group) + return; + Desktop* desktop = satWindow->GetWindow()->Desktop(); + if (!desktop) + return; + + for (int i = 0; i < group->CountItems(); i++) { + SATWindow* listWindow = group->WindowAt(i); + if (listWindow != satWindow) + desktop->SendWindowBehind(listWindow->GetWindow(), behindOf); + } +} + + +void +StackAndTile::SetWindowWorkspaces(Window* window, uint32 workspaces) +{ + SATWindow* satWindow = GetSATWindow(window); + if (!satWindow) + return; + SATGroup* group = satWindow->GetGroup(); + if (!group) + return; + Desktop* desktop = satWindow->GetWindow()->Desktop(); + if (!desktop) + return; + + for (int i = 0; i < group->CountItems(); i++) { + SATWindow* listWindow = group->WindowAt(i); + if (listWindow != satWindow) + desktop->SetWindowWorkspaces(listWindow->GetWindow(), workspaces); + } +} + + +void +StackAndTile::ShowWindow(Window* window) +{ + +} + + +void +StackAndTile::HideWindow(Window* window) +{ + +} + + +void +StackAndTile::MinimizeWindow(Window* window, bool minimize) +{ + SATWindow* satWindow = GetSATWindow(window); + if (!satWindow) + return; + SATGroup* group = satWindow->GetGroup(); + if (!group) + return; + Desktop* desktop = satWindow->GetWindow()->Desktop(); + if (!desktop) + return; + + for (int i = 0; i < group->CountItems(); i++) { + SATWindow* listWindow = group->WindowAt(i); + if (listWindow != satWindow) + desktop->MinimizeWindow(listWindow->GetWindow(), minimize); + } +} + + +void +StackAndTile::SetWindowTabLocation(Window* window, float location) +{ + SATWindow* satWindow = GetSATWindow(window); + if (!satWindow) + return; + + fTabIsShifting = true; + satWindow->TabLocationMoved(location, fTabIsShifting); +} + + +bool +StackAndTile::SetDecoratorSettings(Window* window, const BMessage& settings) +{ + SATWindow* satWindow = GetSATWindow(window); + if (!satWindow) + return false; + + //return satWindow->SetSettings(settings); + return false; +} + + +void +StackAndTile::GetDecoratorSettings(Window* window, BMessage& settings) +{ + SATWindow* satWindow = GetSATWindow(window); + if (!satWindow) + return; + + //satWindow->GetSettings(&settings); +} + + +SATWindow* +StackAndTile::GetSATWindow(Window* window) +{ + SATWindowMap::const_iterator it = fSATWindowMap.find( + window); + if (it != fSATWindowMap.end()) + return it->second; + + // for now don't create window on the fly + return NULL; + // If we don't know this window, memory allocation might has been failed + // previously. Try to add window now +/* SATWindow* satWindow = new (std::nothrow)SATWindow( + window, this); + if (satWindow) + fSATWindowMap[window] = satWindow; + + return satWindow;*/ +} + + +void +StackAndTile::_StartSAT() +{ + STRACE_SAT("StackAndTile::_StartSAT()\n"); + if (!fCurrentSATWindow) + return; + + // Remove window from the group. + SATGroup* group = fCurrentSATWindow->GetGroup(); + if (!group) + return; + + group->RemoveWindow(fCurrentSATWindow); + + fCurrentSATWindow->FindSnappingCandidates(); +} + + +void +StackAndTile::_StopSAT() +{ + STRACE_SAT("StackAndTile::_StopSAT()\n"); + if (!fCurrentSATWindow) + return; + if (fCurrentSATWindow->JoinCandidates()) + _ActivateWindow(fCurrentSATWindow); +} + + +void +StackAndTile::_ActivateWindow(SATWindow* satWindow) +{ + SATGroup* group = satWindow->GetGroup(); + if (!group) + return; + Desktop* desktop = satWindow->GetWindow()->Desktop(); + if (!desktop) + return; + + for (int i = 0; i < group->CountItems(); i++) { + SATWindow* listWindow = group->WindowAt(i); + if (listWindow != satWindow) + desktop->ActivateWindow(listWindow->GetWindow()); + } + desktop->ActivateWindow(satWindow->GetWindow()); +} + + +GroupIterator::GroupIterator(StackAndTile* sat, Desktop* desktop) + : + fStackAndTile(sat), + fDesktop(desktop), + fCurrentGroup(NULL) +{ + RewindToFront(); +} + + +void +GroupIterator::RewindToFront() +{ + fCurrentWindow = fDesktop->CurrentWindows().LastWindow(); +} + + +SATGroup* +GroupIterator::NextGroup() +{ + SATGroup* group = NULL; + do { + Window* window = fCurrentWindow; + if (window == NULL) { + group = NULL; + break; + } + fCurrentWindow = fCurrentWindow->PreviousWindow( + fCurrentWindow->CurrentWorkspace()); + if (window->IsHidden() + || strcmp(window->Title(), "Deskbar") == 0 + || strcmp(window->Title(), "Desktop") == 0) + continue; + + SATWindow* satWindow = fStackAndTile->GetSATWindow(window); + group = satWindow->GetGroup(); + } while (group == NULL || fCurrentGroup == group); + + fCurrentGroup = group; + return fCurrentGroup; +} + + +SATSnappingBehaviour::~SATSnappingBehaviour() +{ + +} diff --git a/src/add-ons/decorators/SATDecorator/StackAndTile.h b/src/add-ons/decorators/SATDecorator/StackAndTile.h new file mode 100644 index 0000000000..70151b4761 --- /dev/null +++ b/src/add-ons/decorators/SATDecorator/StackAndTile.h @@ -0,0 +1,137 @@ +/* + * Copyright 2010, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Clemens Zeidler + */ +#ifndef STACK_AND_TILE_H +#define STACK_AND_TILE_H + +#include + +#include +#include + +#include "Desktop.h" +#include "ObjectList.h" +#include "SATGroup.h" +#include "WindowList.h" + + +#define DEBUG_STACK_AND_TILE + +#ifdef DEBUG_STACK_AND_TILE +# define STRACE_SAT(x...) debug_printf("SAT: "x) +#else +# define STRACE_SAT(x...) ; +#endif + + +class SATWindow; +class Window; + + +typedef std::map SATWindowMap; + + +class StackAndTile : public DesktopListener { +public: + StackAndTile(); + virtual ~StackAndTile(); + + // DesktopListener hooks + virtual void ListenerRegistered(Desktop* desktop); + virtual void ListenerUnregistered(); + + virtual void AddWindow(Window* window); + virtual void RemoveWindow(Window* window); + + virtual void KeyEvent(uint32 what, int32 key, + int32 modifiers); + virtual void MouseEvent(BMessage* message) {} + virtual void MouseDown(Window* window, BMessage* message, + const BPoint& where); + virtual void MouseUp(Window* window, BMessage* message, + const BPoint& where); + virtual void MouseMoved(Window* window, BMessage* message, + const BPoint& where) {} + + virtual void MoveWindow(Window* window); + virtual void ResizeWindow(Window* window); + virtual void ActivateWindow(Window* window); + virtual void SendWindowBehind(Window* window, + Window* behindOf); + virtual void SetWindowWorkspaces(Window* window, + uint32 workspaces); + virtual void ShowWindow(Window* window); + virtual void HideWindow(Window* window); + virtual void MinimizeWindow(Window* window, bool minimize); + + virtual void SetWindowTabLocation(Window* window, + float location); + + virtual bool SetDecoratorSettings(Window* window, + const BMessage& settings); + virtual void GetDecoratorSettings(Window* window, + BMessage& settings); + + bool SATKeyPressed() + { return fSATKeyPressed; } + + SATWindow* GetSATWindow(Window* window); + +protected: + void _StartSAT(); + void _StopSAT(); + void _ActivateWindow(SATWindow* window); + + bool fSATKeyPressed; + + SATWindowMap fSATWindowMap; + SATWindowList fGrouplessWindows; + + SATWindow* fCurrentSATWindow; + + bool fTabIsShifting; +}; + + +class GroupIterator { +public: + GroupIterator(StackAndTile* sat, Desktop* desktop); + + void RewindToFront(); + SATGroup* NextGroup(); + +private: + StackAndTile* fStackAndTile; + Desktop* fDesktop; + Window* fCurrentWindow; + SATGroup* fCurrentGroup; +}; + + +class SATSnappingBehaviour { +public: + virtual ~SATSnappingBehaviour(); + + /*! Find all window candidates which possibly can join the group. Found + candidates are marked here visual. */ + virtual bool FindSnappingCandidates(SATGroup* group) = 0; + /*! Join all candidates found in FindSnappingCandidates to the group. + Previously visually mark should be removed here. \return true if + integration has been succeed. */ + virtual bool JoinCandidates() = 0; + /*! Update the window tab values, solve the layout and move all windows in + the group accordantly. */ + virtual void DoGroupLayout() = 0; + virtual void RemovedFromArea(WindowArea* area) {} + virtual void TabLocationMoved(float location, bool shifting) {} +}; + + +typedef BObjectList SATSnappingBehaviourList; + + +#endif diff --git a/src/add-ons/decorators/SATDecorator/Stacking.cpp b/src/add-ons/decorators/SATDecorator/Stacking.cpp new file mode 100644 index 0000000000..f6c6770937 --- /dev/null +++ b/src/add-ons/decorators/SATDecorator/Stacking.cpp @@ -0,0 +1,225 @@ +/* + * Copyright 2010, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Clemens Zeidler + */ + +#include "Stacking.h" + +#include + +#include "SATWindow.h" +#include "Window.h" + + +#define DEBUG_STACKING + +#ifdef DEBUG_STACKING +# define STRACE_STACKING(x...) debug_printf("SAT Stacking: "x) +#else +# define STRACE_STACKING(x...) ; +#endif + + +SATStacking::SATStacking(SATWindow* window) + : + fSATWindow(window), + fStackingCandidate(NULL) +{ + +} + + +SATStacking::~SATStacking() +{ + +} + + +bool +SATStacking::FindSnappingCandidates(SATGroup* group) +{ + _ClearSearchResult(); + + Window* window = fSATWindow->GetWindow(); + BPoint leftTop = window->Decorator()->TabRect().LeftTop(); + + for (int i = 0; i < group->CountItems(); i++) { + SATWindow* satWindow = group->WindowAt(i); + // search for stacking candidate + Window* win = satWindow->GetWindow(); + if (win != window && win->Decorator() + && win->Decorator()->TabRect().Contains(leftTop)) { + // remember window as the candidate for stacking + fStackingCandidate = satWindow; + _HighlightWindows(true); + return true; + } + } + + return false; +} + + +bool +SATStacking::JoinCandidates() +{ + if (!fStackingCandidate) + return false; + SATGroup* group = fStackingCandidate->GetGroup(); + WindowArea* area = fStackingCandidate->GetWindowArea(); + if (!group || !area) { + _ClearSearchResult(); + return false; + } + + bool status = group->AddWindow(fSATWindow, area, fStackingCandidate); + + if (status) { + area->WindowList().ItemAt(0)->SetStackedMode(true); + // for the case we are the first added window + fSATWindow->SetStackedMode(true); + } + + fStackingCandidate->DoGroupLayout(); + _ClearSearchResult(); + return status; +} + + +void +SATStacking::DoGroupLayout() +{ + _AdjustWindowTabs(); +} + + +void +SATStacking::RemovedFromArea(WindowArea* area) +{ + const SATWindowList& list = area->WindowList(); + if (list.CountItems() == 1) + list.ItemAt(0)->SetStackedMode(false); + else if (list.CountItems() > 0) + list.ItemAt(0)->DoGroupLayout(); + + fSATWindow->SetStackedMode(false); +} + + +void +SATStacking::TabLocationMoved(float location, bool shifting) +{ + if (!shifting) { + _AdjustWindowTabs(); + return; + } + + SATDecorator* decorator = fSATWindow->GetDecorator(); + Desktop* desktop = fSATWindow->GetWindow()->Desktop(); + WindowArea* area = fSATWindow->GetWindowArea(); + if (!desktop || !area || ! decorator) + return; + + const SATWindowList& stackedWindows = area->WindowList(); + ASSERT(stackedWindows.CountItems() > 0); + int32 windowIndex = stackedWindows.IndexOf(fSATWindow); + ASSERT(windowIndex >= 0); + float tabLength = stackedWindows.ItemAt(0)->GetDecorator() + ->StackedTabLength(); + float tabLengthZoom = tabLength + decorator->GetZoomOffsetToRight(); + + float oldTabPosition = windowIndex * (tabLength + 1); + if (fabs(oldTabPosition - location) < tabLength / 2) + return; + + int32 neighbourIndex = windowIndex; + if (oldTabPosition > location) + neighbourIndex--; + else + neighbourIndex++; + + SATWindow* neighbour = stackedWindows.ItemAt(neighbourIndex); + if (!neighbour) + return; + + float newNeighbourPosition = windowIndex * (tabLength + 1); + area->MoveWindowToPosition(fSATWindow, neighbourIndex); + desktop->SetWindowTabLocation(neighbour->GetWindow(), newNeighbourPosition); + + // update zoom buttons + if (windowIndex == stackedWindows.CountItems() - 1) { + fSATWindow->SetStackedTabLength(tabLength, false); + neighbour->SetStackedTabLength(tabLengthZoom, true); + } + else if (neighbourIndex == stackedWindows.CountItems() - 1) { + neighbour->SetStackedTabLength(tabLength, false); + fSATWindow->SetStackedTabLength(tabLengthZoom, true); + } +} + + +void +SATStacking::_ClearSearchResult() +{ + if (!fStackingCandidate) + return; + + _HighlightWindows(false); + fStackingCandidate = NULL; +} + + +void +SATStacking::_HighlightWindows(bool highlight) +{ + Desktop* desktop = fSATWindow->GetWindow()->Desktop(); + if (!desktop) + return; + fStackingCandidate->HighlightTab(highlight); + fSATWindow->HighlightTab(highlight); +} + + +bool +SATStacking::_AdjustWindowTabs() +{ + SATDecorator* decorator = fSATWindow->GetDecorator(); + Desktop* desktop = fSATWindow->GetWindow()->Desktop(); + WindowArea* area = fSATWindow->GetWindowArea(); + if (!desktop || !area || ! decorator) + return false; + + if (!decorator->StackedMode()) + return false; + + BRect frame = fSATWindow->CompleteWindowFrame(); + + const float kMaxTabWidth = 120.; + + const SATWindowList& stackedWindows = area->WindowList(); + + float zoomOffset = decorator->GetZoomOffsetToRight(); + float tabBarLength = frame.Width() - zoomOffset; + + ASSERT(tabBarLength > 0); + float tabLength = tabBarLength / stackedWindows.CountItems(); + if (tabLength > kMaxTabWidth) + tabLength = kMaxTabWidth; + + float location = 0; + for (int i = 0; i < stackedWindows.CountItems(); i++) { + SATWindow* window = stackedWindows.ItemAt(i); + if (i == stackedWindows.CountItems() - 1) + window->SetStackedTabLength(tabLength - 1 + zoomOffset, true); + // last tab + else + window->SetStackedTabLength(tabLength - 1, false); + + desktop->SetWindowTabLocation(window->GetWindow(), location); + location += tabLength; + } + return true; +} diff --git a/src/add-ons/decorators/SATDecorator/Stacking.h b/src/add-ons/decorators/SATDecorator/Stacking.h new file mode 100644 index 0000000000..9b94e20d15 --- /dev/null +++ b/src/add-ons/decorators/SATDecorator/Stacking.h @@ -0,0 +1,39 @@ +/* + * Copyright 2010, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Clemens Zeidler + */ +#ifndef STACKING_H +#define STACKING_H + +#include "ObjectList.h" +#include "StackAndTile.h" + + +class SATWindow; + + +class SATStacking : public SATSnappingBehaviour { +public: + SATStacking(SATWindow* window); + ~SATStacking(); + + bool FindSnappingCandidates(SATGroup* group); + bool JoinCandidates(); + void DoGroupLayout(); + void RemovedFromArea(WindowArea* area); + void TabLocationMoved(float location, bool shifting); + +private: + void _ClearSearchResult(); + void _HighlightWindows(bool highlight = true); + bool _AdjustWindowTabs(); + + SATWindow* fSATWindow; + + SATWindow* fStackingCandidate; +}; + +#endif diff --git a/src/add-ons/decorators/SATDecorator/Tiling.cpp b/src/add-ons/decorators/SATDecorator/Tiling.cpp new file mode 100644 index 0000000000..07a4c177b9 --- /dev/null +++ b/src/add-ons/decorators/SATDecorator/Tiling.cpp @@ -0,0 +1,521 @@ +/* + * Copyright 2010, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Clemens Zeidler + */ + +#include "Tiling.h" + + +#include "SATWindow.h" +#include "StackAndTile.h" +#include "Window.h" + + +using namespace std; + + +//#define DEBUG_TILEING + +#ifdef DEBUG_TILEING +# define STRACE_TILING(x...) debug_printf("SAT Tiling: "x) +#else +# define STRACE_TILING(x...) ; +#endif + + +SATTiling::SATTiling(SATWindow* window) + : + fSATWindow(window), + fFreeAreaGroup(NULL) +{ + _ResetSearchResults(); +} + + +SATTiling::~SATTiling() +{ + +} + + +bool +SATTiling::FindSnappingCandidates(SATGroup* group) +{ + _ResetSearchResults(); + + if (fSATWindow->GetGroup() == group) + return false; + + if (_FindFreeAreaInGroup(group)) { + fFreeAreaGroup = group; + _HighlightWindows(fFreeAreaGroup, true); + return true; + } + + return false; +} + + +bool +SATTiling::JoinCandidates() +{ + if (!fFreeAreaGroup) + return false; + + if (!fFreeAreaGroup->AddWindow(fSATWindow, fFreeAreaLeft, fFreeAreaTop, + fFreeAreaRight, fFreeAreaBottom)) { + _ResetSearchResults(); + return false; + } + + fFreeAreaGroup->WindowAt(0)->DoGroupLayout(); + + _ResetSearchResults(); + return true; +} + + +void +SATTiling::DoGroupLayout() +{ + +} + + +bool +SATTiling::_FindFreeAreaInGroup(SATGroup* group) +{ + if (_FindFreeAreaInGroup(group, Corner::kLeftTop)) + return true; + if (_FindFreeAreaInGroup(group, Corner::kRightTop)) + return true; + if (_FindFreeAreaInGroup(group, Corner::kLeftBottom)) + return true; + if (_FindFreeAreaInGroup(group, Corner::kRightBottom)) + return true; + + return false; +} + + +bool +SATTiling::_FindFreeAreaInGroup(SATGroup* group, Corner::position_t cor) +{ + BRect windowFrame = fSATWindow->CompleteWindowFrame(); + + const TabList* verticalTabs = group->VerticalTabs(); + for (int i = 0; i < verticalTabs->CountItems(); i++) { + Tab* tab = verticalTabs->ItemAt(i); + const CrossingList* crossingList = tab->GetCrossingList(); + for (int c = 0; c < crossingList->CountItems(); c++) { + Crossing* crossing = crossingList->ItemAt(c); + if (_InteresstingCrossing(crossing, cor, windowFrame)) { + if (_FindFreeArea(group, crossing, cor, windowFrame)) { + STRACE_TILING("SATTiling: free area found; corner %i\n", + cor); + return true; + } + } + } + } + + return false; +} + + +const float kNoMatch = 999.f; +const float kMaxMatchingDistance = 12.f; + + +bool +SATTiling::_InteresstingCrossing(Crossing* crossing, + Corner::position_t cor, BRect& windowFrame) +{ + const Corner* corner = crossing->GetOppositeCorner(cor); + if (corner->status != Corner::kFree) + return false; + + float hTabPosition = crossing->HorizontalTab()->Position(); + float vTabPosition = crossing->VerticalTab()->Position(); + float hBorder = 0., vBorder = 0.; + float vDistance = -1., hDistance = -1.; + bool windowAtH = false, windowAtV = false; + switch (cor) { + case Corner::kLeftTop: + if (crossing->RightBottomCorner()->status == Corner::kUsed) + return false; + vBorder = windowFrame.left; + hBorder = windowFrame.top; + if (crossing->LeftBottomCorner()->status == Corner::kUsed) + windowAtV = true; + if (crossing->RightTopCorner()->status == Corner::kUsed) + windowAtH = true; + vDistance = vTabPosition - vBorder; + hDistance = hTabPosition - hBorder; + break; + case Corner::kRightTop: + if (crossing->LeftBottomCorner()->status == Corner::kUsed) + return false; + vBorder = windowFrame.right; + hBorder = windowFrame.top; + if (crossing->RightBottomCorner()->status == Corner::kUsed) + windowAtV = true; + if (crossing->LeftTopCorner()->status == Corner::kUsed) + windowAtH = true; + vDistance = vBorder - vTabPosition; + hDistance = hTabPosition - hBorder; + break; + case Corner::kLeftBottom: + if (crossing->RightTopCorner()->status == Corner::kUsed) + return false; + vBorder = windowFrame.left; + hBorder = windowFrame.bottom; + if (crossing->LeftTopCorner()->status == Corner::kUsed) + windowAtV = true; + if (crossing->RightBottomCorner()->status == Corner::kUsed) + windowAtH = true; + vDistance = vTabPosition - vBorder; + hDistance = hBorder - hTabPosition; + break; + case Corner::kRightBottom: + if (crossing->LeftTopCorner()->status == Corner::kUsed) + return false; + vBorder = windowFrame.right; + hBorder = windowFrame.bottom; + if (crossing->RightTopCorner()->status == Corner::kUsed) + windowAtV = true; + if (crossing->LeftBottomCorner()->status == Corner::kUsed) + windowAtH = true; + vDistance = vBorder - vTabPosition; + hDistance = hBorder - hTabPosition; + break; + }; + + bool hValid = false; + if (windowAtH && fabs(hDistance) < kMaxMatchingDistance + && vDistance < kMaxMatchingDistance) + hValid = true; + bool vValid = false; + if (windowAtV && fabs(vDistance) < kMaxMatchingDistance + && hDistance < kMaxMatchingDistance) + vValid = true; + if (!hValid && !vValid) + return false; + + return true; +}; + + +const float kBigAreaError = 1E+17; + + +bool +SATTiling::_FindFreeArea(SATGroup* group, const Crossing* crossing, + Corner::position_t corner, BRect& windowFrame) +{ + fFreeAreaLeft = fFreeAreaRight = fFreeAreaTop = fFreeAreaBottom = NULL; + + const TabList* hTabs = group->HorizontalTabs(); + const TabList* vTabs = group->VerticalTabs(); + int32 hIndex = hTabs->IndexOf(crossing->HorizontalTab()); + if (hIndex < 0) + return false; + int32 vIndex = vTabs->IndexOf(crossing->VerticalTab()); + if (vIndex < 0) + return false; + + Tab** endHTab = NULL, **endVTab = NULL; + int8 vSearchDirection = 1, hSearchDirection = 1; + switch (corner) { + case Corner::kLeftTop: + fFreeAreaLeft = crossing->VerticalTab(); + fFreeAreaTop = crossing->HorizontalTab(); + endHTab = &fFreeAreaBottom; + endVTab = &fFreeAreaRight; + vSearchDirection = 1; + hSearchDirection = 1; + break; + case Corner::kRightTop: + fFreeAreaRight = crossing->VerticalTab(); + fFreeAreaTop = crossing->HorizontalTab(); + endHTab = &fFreeAreaBottom; + endVTab = &fFreeAreaLeft; + vSearchDirection = -1; + hSearchDirection = 1; + break; + case Corner::kLeftBottom: + fFreeAreaLeft = crossing->VerticalTab(); + fFreeAreaBottom = crossing->HorizontalTab(); + endHTab = &fFreeAreaTop; + endVTab = &fFreeAreaRight; + vSearchDirection = 1; + hSearchDirection = -1; + break; + case Corner::kRightBottom: + fFreeAreaRight = crossing->VerticalTab(); + fFreeAreaBottom = crossing->HorizontalTab(); + endHTab = &fFreeAreaTop; + endVTab = &fFreeAreaLeft; + vSearchDirection = -1; + hSearchDirection = -1; + break; + }; + + Tab* bestLeftTab = NULL, *bestRightTab = NULL, *bestTopTab = NULL, + *bestBottomTab = NULL; + float bestError = kBigAreaError; + float error; + bool stop = false; + bool found = false; + int32 v = vIndex; + do { + v += vSearchDirection; + *endVTab = vTabs->ItemAt(v); + int32 h = hIndex; + do { + h += hSearchDirection; + *endHTab = hTabs->ItemAt(h); + if (!_CheckArea(group, corner, windowFrame, error)) { + if (h == hIndex + hSearchDirection) + stop = true; + break; + } + found = true; + if (error < bestError) { + bestError = error; + bestLeftTab = fFreeAreaLeft; + bestRightTab = fFreeAreaRight; + bestTopTab = fFreeAreaTop; + bestBottomTab = fFreeAreaBottom; + } + } while (*endHTab); + if (stop) + break; + } while (*endVTab); + if (!found) + return false; + + fFreeAreaLeft = bestLeftTab; + fFreeAreaRight = bestRightTab; + fFreeAreaTop = bestTopTab; + fFreeAreaBottom = bestBottomTab; + + return true; +} + + +bool +SATTiling::_HasOverlapp(SATGroup* group) +{ + BRect areaRect = _FreeAreaSize(); + areaRect.InsetBy(1., 1.); + + const TabList* hTabs = group->HorizontalTabs(); + for (int32 h = 0; h < hTabs->CountItems(); h++) { + Tab* hTab = hTabs->ItemAt(h); + if (hTab->Position() >= areaRect.bottom) + return false; + const CrossingList* crossings = hTab->GetCrossingList(); + for (int32 i = 0; i < crossings->CountItems(); i++) { + Crossing* leftTopCrossing = crossings->ItemAt(i); + Tab* vTab = leftTopCrossing->VerticalTab(); + if (vTab->Position() > areaRect.right) + continue; + Corner* corner = leftTopCrossing->RightBottomCorner(); + if (corner->status != Corner::kUsed) + continue; + BRect rect = corner->windowArea->Frame(); + if (areaRect.Intersects(rect)) + return true; + } + } + return false; +} + + +bool +SATTiling::_CheckArea(SATGroup* group, Corner::position_t corner, + BRect& windowFrame, float& error) +{ + error = kBigAreaError; + if (!_CheckMinFreeAreaSize()) + return false; + // check if corner is in the free area + if (!_IsCornerInFreeArea(corner, windowFrame)) + return false; + + error = _FreeAreaError(windowFrame); + if (!_HasOverlapp(group)) + return true; + return false; +} + + +bool +SATTiling::_CheckMinFreeAreaSize() +{ + // check if the area has a minimum size + if (fFreeAreaLeft && fFreeAreaRight + && fFreeAreaRight->Position() - fFreeAreaLeft->Position() + < 2 * kMaxMatchingDistance) + return false; + if (fFreeAreaBottom && fFreeAreaTop + && fFreeAreaBottom->Position() - fFreeAreaTop->Position() + < 2 * kMaxMatchingDistance) + return false; + return true; +} + + +float +SATTiling::_FreeAreaError(BRect& windowFrame) +{ + const float kEndTabError = 9999999; + float error = 0.; + if (fFreeAreaLeft && fFreeAreaRight) + error += pow(fFreeAreaRight->Position() - fFreeAreaLeft->Position() + - windowFrame.Width(), 2); + else + error += kEndTabError; + if (fFreeAreaBottom && fFreeAreaTop) + error += pow(fFreeAreaBottom->Position() - fFreeAreaTop->Position() + - windowFrame.Height(), 2); + else + error += kEndTabError; + return error; +} + + +bool +SATTiling::_IsCornerInFreeArea(Corner::position_t corner, BRect& frame) +{ + BRect freeArea = _FreeAreaSize(); + + switch (corner) { + case Corner::kLeftTop: + if (freeArea.bottom - kMaxMatchingDistance > frame.top + && freeArea.right - kMaxMatchingDistance > frame.left) + return true; + break; + case Corner::kRightTop: + if (freeArea.bottom - kMaxMatchingDistance > frame.top + && freeArea.left + kMaxMatchingDistance < frame.right) + return true; + break; + case Corner::kLeftBottom: + if (freeArea.top + kMaxMatchingDistance < frame.bottom + && freeArea.right - kMaxMatchingDistance > frame.left) + return true; + break; + case Corner::kRightBottom: + if (freeArea.top + kMaxMatchingDistance < frame.bottom + && freeArea.left + kMaxMatchingDistance < frame.right) + return true; + break; + } + + return false; +} + + +BRect +SATTiling::_FreeAreaSize() +{ + // not to big to be be able to add/sub small float values + const float kBigValue = 9999999.; + float left = fFreeAreaLeft ? fFreeAreaLeft->Position() : -kBigValue; + float right = fFreeAreaRight ? fFreeAreaRight->Position() : kBigValue; + float top = fFreeAreaTop ? fFreeAreaTop->Position() : -kBigValue; + float bottom = fFreeAreaBottom ? fFreeAreaBottom->Position() : kBigValue; + return BRect(left, top, right, bottom); +} + + +void +SATTiling::_HighlightWindows(SATGroup* group, bool highlight) +{ + BRect windowFrame = fSATWindow->CompleteWindowFrame(); + + const TabList* hTabs = group->HorizontalTabs(); + const TabList* vTabs = group->VerticalTabs(); + // height light windows at all four sites + _SearchHighlightWindow(fFreeAreaLeft, fFreeAreaTop, fFreeAreaBottom, hTabs, + Corner::kLeftBottom, highlight); + + _SearchHighlightWindow(fFreeAreaTop, fFreeAreaLeft, fFreeAreaRight, vTabs, + Corner::kRightTop, highlight); + + _SearchHighlightWindow(fFreeAreaRight, fFreeAreaTop, fFreeAreaBottom, hTabs, + Corner::kRightBottom, highlight); + + _SearchHighlightWindow(fFreeAreaBottom, fFreeAreaLeft, fFreeAreaRight, + vTabs, Corner::kRightBottom, highlight); + +} + + +void +SATTiling::_SearchHighlightWindow(Tab* tab, Tab* firstOrthTab, + Tab* secondOrthTab, const TabList* orthTabs, Corner::position_t areaCorner, + bool highlight) +{ + if (!tab) + return; + + int8 searchDir = 1; + Tab* startOrthTab = NULL; + Tab* endOrthTab = NULL; + if (firstOrthTab) { + searchDir = 1; + startOrthTab = firstOrthTab; + endOrthTab = secondOrthTab; + } + else if (secondOrthTab) { + searchDir = -1; + startOrthTab = secondOrthTab; + endOrthTab = firstOrthTab; + } + else + return; + + int32 index = orthTabs->IndexOf(startOrthTab); + if (index < 0) + return; + + for (; index < orthTabs->CountItems() && index >= 0; index += searchDir) { + Tab* orthTab = orthTabs->ItemAt(index); + Crossing* crossing = tab->FindCrossing(orthTab); + if (!crossing) + continue; + if (orthTab == endOrthTab) + break; + Corner* corner = crossing->GetCorner(areaCorner); + if (corner->windowArea) + _HighlightWindows(corner->windowArea, highlight); + } +} + + +void +SATTiling::_HighlightWindows(WindowArea* area, bool highlight) +{ + const SATWindowList& list = area->WindowList(); + for (int i = 0; i < list.CountItems(); i++) + list.ItemAt(i)->HighlightBorders(highlight); + + fSATWindow->HighlightBorders(highlight); +} + + +void +SATTiling::_ResetSearchResults() +{ + if (!fFreeAreaGroup) + return; + + _HighlightWindows(fFreeAreaGroup, false); + fFreeAreaGroup = NULL; +} diff --git a/src/add-ons/decorators/SATDecorator/Tiling.h b/src/add-ons/decorators/SATDecorator/Tiling.h new file mode 100644 index 0000000000..e70a2d8f8f --- /dev/null +++ b/src/add-ons/decorators/SATDecorator/Tiling.h @@ -0,0 +1,68 @@ +/* + * Copyright 2010, Haiku. + * Distributed under the terms of the MIT License. + * + * Authors: + * Clemens Zeidler + */ +#ifndef TILING_H +#define TILING_H + +#include "ObjectList.h" +#include "StackAndTile.h" + + +class SATWindow; + + +class SATTiling : public SATSnappingBehaviour { +public: + SATTiling(SATWindow* window); + ~SATTiling(); + + bool FindSnappingCandidates(SATGroup* group); + bool JoinCandidates(); + void DoGroupLayout(); + +private: + bool _FindFreeAreaInGroup(SATGroup* group); + bool _FindFreeAreaInGroup(SATGroup* group, + Corner::position_t corner); + + bool _InteresstingCrossing(Crossing* crossing, + Corner::position_t corner, BRect& windowFrame); + bool _FindFreeArea(SATGroup* group, + const Crossing* crossing, + Corner::position_t areaCorner, + BRect& windowFrame); + bool _HasOverlapp(SATGroup* group); + bool _CheckArea(SATGroup* group, + Corner::position_t corner, BRect& windowFrame, + float& error); + bool _CheckMinFreeAreaSize(); + float _FreeAreaError(BRect& windowFrame); + bool _IsCornerInFreeArea(Corner::position_t corner, + BRect& windowFrame); + + BRect _FreeAreaSize(); + + void _HighlightWindows(SATGroup* group, + bool highlight = true); + void _SearchHighlightWindow(Tab* tab, Tab* firstOrthTab, + Tab* secondOrthTab, const TabList* orthTabs, + Corner::position_t areaCorner, bool highlight); + void _HighlightWindows(WindowArea* area, + bool highlight); + + void _ResetSearchResults(); + + SATWindow* fSATWindow; + + SATGroup* fFreeAreaGroup; + Tab* fFreeAreaLeft; + Tab* fFreeAreaRight; + Tab* fFreeAreaTop; + Tab* fFreeAreaBottom; +}; + +#endif