From b960e45194de0e83356b92962515bab16d860cfc Mon Sep 17 00:00:00 2001 From: Clemens Zeidler Date: Thu, 12 Aug 2010 22:37:16 +0000 Subject: [PATCH] Add the SATDecorator. This is a new implementation of Stack and Tile with a different tiling logic. For example, this should make overlapping windows in the same tiling group impossible. There are still many issues I know about and this version is far from usable but the basic features are working now. So if you like to test it at this state please only report errors like crashes or memory leaks (think there is one). git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@38071 a95241bf-73f2-0310-859d-f6bbb57e9c96 --- src/add-ons/decorators/Jamfile | 3 +- src/add-ons/decorators/SATDecorator/Jamfile | 25 + .../decorators/SATDecorator/SATDecorator.cpp | 482 +++++++ .../decorators/SATDecorator/SATDecorator.h | 72 ++ .../decorators/SATDecorator/SATGroup.cpp | 1148 +++++++++++++++++ .../decorators/SATDecorator/SATGroup.h | 267 ++++ .../decorators/SATDecorator/SATWindow.cpp | 546 ++++++++ .../decorators/SATDecorator/SATWindow.h | 134 ++ .../decorators/SATDecorator/StackAndTile.cpp | 399 ++++++ .../decorators/SATDecorator/StackAndTile.h | 137 ++ .../decorators/SATDecorator/Stacking.cpp | 225 ++++ .../decorators/SATDecorator/Stacking.h | 39 + .../decorators/SATDecorator/Tiling.cpp | 521 ++++++++ src/add-ons/decorators/SATDecorator/Tiling.h | 68 + 14 files changed, 4065 insertions(+), 1 deletion(-) create mode 100644 src/add-ons/decorators/SATDecorator/Jamfile create mode 100644 src/add-ons/decorators/SATDecorator/SATDecorator.cpp create mode 100644 src/add-ons/decorators/SATDecorator/SATDecorator.h create mode 100644 src/add-ons/decorators/SATDecorator/SATGroup.cpp create mode 100644 src/add-ons/decorators/SATDecorator/SATGroup.h create mode 100644 src/add-ons/decorators/SATDecorator/SATWindow.cpp create mode 100644 src/add-ons/decorators/SATDecorator/SATWindow.h create mode 100644 src/add-ons/decorators/SATDecorator/StackAndTile.cpp create mode 100644 src/add-ons/decorators/SATDecorator/StackAndTile.h create mode 100644 src/add-ons/decorators/SATDecorator/Stacking.cpp create mode 100644 src/add-ons/decorators/SATDecorator/Stacking.h create mode 100644 src/add-ons/decorators/SATDecorator/Tiling.cpp create mode 100644 src/add-ons/decorators/SATDecorator/Tiling.h 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