2 * Copyright 2010-2014 Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
6 * John Scipione, jscipione@gmail.com
7 * Clemens Zeidler, haiku@clemens-zeidler.de
20 #include "SATWindow.h"
21 #include "StackAndTile.h"
26 using namespace LinearProgramming
;
29 const float kExtentPenalty
= 1;
30 const float kHighPenalty
= 100;
31 const float kInequalityPenalty
= 10000;
34 WindowArea::WindowArea(Crossing
* leftTop
, Crossing
* rightTop
,
35 Crossing
* leftBottom
, Crossing
* rightBottom
)
39 fLeftTopCrossing(leftTop
),
40 fRightTopCrossing(rightTop
),
41 fLeftBottomCrossing(leftBottom
),
42 fRightBottomCrossing(rightBottom
),
44 fMinWidthConstraint(NULL
),
45 fMinHeightConstraint(NULL
),
46 fMaxWidthConstraint(NULL
),
47 fMaxHeightConstraint(NULL
),
48 fWidthConstraint(NULL
),
49 fHeightConstraint(NULL
)
54 WindowArea::~WindowArea()
57 fGroup
->WindowAreaRemoved(this);
60 fGroup
->fWindowAreaList
.RemoveItem(this);
67 WindowArea::Init(SATGroup
* group
)
71 if (group
== NULL
|| group
->fWindowAreaList
.AddItem(this) == false)
76 LinearSpec
* linearSpec
= fGroup
->GetLinearSpec();
78 fMinWidthConstraint
= linearSpec
->AddConstraint(1.0, RightVar(), -1.0,
80 fMinHeightConstraint
= linearSpec
->AddConstraint(1.0, BottomVar(), -1.0,
83 fMaxWidthConstraint
= linearSpec
->AddConstraint(1.0, RightVar(), -1.0,
84 LeftVar(), kLE
, 0, kInequalityPenalty
, kInequalityPenalty
);
85 fMaxHeightConstraint
= linearSpec
->AddConstraint(1.0, BottomVar(), -1.0,
86 TopVar(), kLE
, 0, kInequalityPenalty
, kInequalityPenalty
);
88 // Width and height have soft constraints
89 fWidthConstraint
= linearSpec
->AddConstraint(1.0, RightVar(), -1.0,
90 LeftVar(), kEQ
, 0, kExtentPenalty
,
92 fHeightConstraint
= linearSpec
->AddConstraint(-1.0, TopVar(), 1.0,
93 BottomVar(), kEQ
, 0, kExtentPenalty
,
96 if (!fMinWidthConstraint
|| !fMinHeightConstraint
|| !fWidthConstraint
97 || !fHeightConstraint
|| !fMaxWidthConstraint
98 || !fMaxHeightConstraint
)
106 WindowArea::DoGroupLayout()
108 SATWindow
* parentWindow
= fWindowLayerOrder
.ItemAt(0);
109 if (parentWindow
== NULL
)
112 BRect frame
= parentWindow
->CompleteWindowFrame();
113 // Make it also work for solver which don't support negative variables
114 frame
.OffsetBy(kMakePositiveOffset
, kMakePositiveOffset
);
116 // adjust window size soft constraints
117 fWidthConstraint
->SetRightSide(frame
.Width());
118 fHeightConstraint
->SetRightSide(frame
.Height());
120 LinearSpec
* linearSpec
= fGroup
->GetLinearSpec();
121 Constraint
* leftConstraint
= linearSpec
->AddConstraint(1.0, LeftVar(),
123 Constraint
* topConstraint
= linearSpec
->AddConstraint(1.0, TopVar(), kEQ
,
126 // give soft constraints a high penalty
127 fWidthConstraint
->SetPenaltyNeg(kHighPenalty
);
128 fWidthConstraint
->SetPenaltyPos(kHighPenalty
);
129 fHeightConstraint
->SetPenaltyNeg(kHighPenalty
);
130 fHeightConstraint
->SetPenaltyPos(kHighPenalty
);
132 // After we set the new parameter solve and apply the new layout.
134 for (int32 tries
= 0; tries
< 15; tries
++) {
135 result
= fGroup
->GetLinearSpec()->Solve();
136 if (result
== kInfeasible
) {
137 debug_printf("can't solve constraints!\n");
140 if (result
== kOptimal
) {
141 const WindowAreaList
& areas
= fGroup
->GetAreaList();
142 for (int32 i
= 0; i
< areas
.CountItems(); i
++) {
143 WindowArea
* area
= areas
.ItemAt(i
);
144 area
->_MoveToSAT(parentWindow
);
150 // set penalties back to normal
151 fWidthConstraint
->SetPenaltyNeg(kExtentPenalty
);
152 fWidthConstraint
->SetPenaltyPos(kExtentPenalty
);
153 fHeightConstraint
->SetPenaltyNeg(kExtentPenalty
);
154 fHeightConstraint
->SetPenaltyPos(kExtentPenalty
);
156 linearSpec
->RemoveConstraint(leftConstraint
);
157 linearSpec
->RemoveConstraint(topConstraint
);
162 WindowArea::UpdateSizeLimits()
164 _UpdateConstraintValues();
169 WindowArea::UpdateSizeConstaints(const BRect
& frame
)
171 // adjust window size soft constraints
172 fWidthConstraint
->SetRightSide(frame
.Width());
173 fHeightConstraint
->SetRightSide(frame
.Height());
178 WindowArea::MoveWindowToPosition(SATWindow
* window
, int32 index
)
180 int32 oldIndex
= fWindowList
.IndexOf(window
);
181 ASSERT(oldIndex
!= index
);
182 return fWindowList
.MoveItem(oldIndex
, index
);
187 WindowArea::TopWindow()
189 return fWindowLayerOrder
.ItemAt(fWindowLayerOrder
.CountItems() - 1);
194 WindowArea::_UpdateConstraintValues()
196 SATWindow
* topWindow
= TopWindow();
197 if (topWindow
== NULL
)
200 int32 minWidth
, maxWidth
;
201 int32 minHeight
, maxHeight
;
202 SATWindow
* window
= fWindowList
.ItemAt(0);
203 window
->GetSizeLimits(&minWidth
, &maxWidth
, &minHeight
, &maxHeight
);
204 for (int32 i
= 1; i
< fWindowList
.CountItems(); i
++) {
205 window
= fWindowList
.ItemAt(i
);
206 // size limit constraints
209 window
->GetSizeLimits(&minW
, &maxW
, &minH
, &maxH
);
212 if (minHeight
< minH
)
216 if (maxHeight
< maxH
)
219 // the current solver don't like big values
220 const int32 kMaxSolverValue
= 5000;
221 if (minWidth
> kMaxSolverValue
)
222 minWidth
= kMaxSolverValue
;
223 if (minHeight
> kMaxSolverValue
)
224 minHeight
= kMaxSolverValue
;
225 if (maxWidth
> kMaxSolverValue
)
226 maxWidth
= kMaxSolverValue
;
227 if (maxHeight
> kMaxSolverValue
)
228 maxHeight
= kMaxSolverValue
;
230 topWindow
->AddDecorator(&minWidth
, &maxWidth
, &minHeight
, &maxHeight
);
231 fMinWidthConstraint
->SetRightSide(minWidth
);
232 fMinHeightConstraint
->SetRightSide(minHeight
);
234 fMaxWidthConstraint
->SetRightSide(maxWidth
);
235 fMaxHeightConstraint
->SetRightSide(maxHeight
);
237 BRect frame
= topWindow
->CompleteWindowFrame();
238 fWidthConstraint
->SetRightSide(frame
.Width());
239 fHeightConstraint
->SetRightSide(frame
.Height());
244 WindowArea::_AddWindow(SATWindow
* window
, SATWindow
* after
)
247 int32 indexAfter
= fWindowList
.IndexOf(after
);
248 if (!fWindowList
.AddItem(window
, indexAfter
+ 1))
250 } else if (fWindowList
.AddItem(window
) == false)
255 if (fWindowList
.CountItems() <= 1)
258 fWindowLayerOrder
.AddItem(window
);
260 _UpdateConstraintValues();
266 WindowArea::_RemoveWindow(SATWindow
* window
)
268 if (!fWindowList
.RemoveItem(window
))
271 fWindowLayerOrder
.RemoveItem(window
);
272 _UpdateConstraintValues();
274 window
->RemovedFromArea(this);
281 WindowArea::LeftTab()
283 return fLeftTopCrossing
->VerticalTab();
288 WindowArea::RightTab()
290 return fRightBottomCrossing
->VerticalTab();
297 return fLeftTopCrossing
->HorizontalTab();
302 WindowArea::BottomTab()
304 return fRightBottomCrossing
->HorizontalTab();
311 return BRect(fLeftTopCrossing
->VerticalTab()->Position(),
312 fLeftTopCrossing
->HorizontalTab()->Position(),
313 fRightBottomCrossing
->VerticalTab()->Position(),
314 fRightBottomCrossing
->HorizontalTab()->Position());
319 WindowArea::PropagateToGroup(SATGroup
* group
)
321 BReference
<Crossing
> newLeftTop
= _CrossingByPosition(fLeftTopCrossing
,
323 BReference
<Crossing
> newRightTop
= _CrossingByPosition(fRightTopCrossing
,
325 BReference
<Crossing
> newLeftBottom
= _CrossingByPosition(
326 fLeftBottomCrossing
, group
);
327 BReference
<Crossing
> newRightBottom
= _CrossingByPosition(
328 fRightBottomCrossing
, group
);
330 if (!newLeftTop
|| !newRightTop
|| !newLeftBottom
|| !newRightBottom
)
333 // hold a ref to the crossings till we cleaned up everything
334 BReference
<Crossing
> oldLeftTop
= fLeftTopCrossing
;
335 BReference
<Crossing
> oldRightTop
= fRightTopCrossing
;
336 BReference
<Crossing
> oldLeftBottom
= fLeftBottomCrossing
;
337 BReference
<Crossing
> oldRightBottom
= fRightBottomCrossing
;
339 fLeftTopCrossing
= newLeftTop
;
340 fRightTopCrossing
= newRightTop
;
341 fLeftBottomCrossing
= newLeftBottom
;
342 fRightBottomCrossing
= newRightBottom
;
346 BReference
<SATGroup
> oldGroup
= fGroup
;
347 // manage constraints
348 if (Init(group
) == false)
351 oldGroup
->fWindowAreaList
.RemoveItem(this);
352 for (int32 i
= 0; i
< fWindowList
.CountItems(); i
++) {
353 SATWindow
* window
= fWindowList
.ItemAt(i
);
354 if (oldGroup
->fSATWindowList
.RemoveItem(window
) == false)
356 if (group
->fSATWindowList
.AddItem(window
) == false) {
357 _UninitConstraints();
362 _UpdateConstraintValues();
369 WindowArea::MoveToTopLayer(SATWindow
* window
)
371 if (!fWindowLayerOrder
.RemoveItem(window
))
373 return fWindowLayerOrder
.AddItem(window
);
378 WindowArea::_UninitConstraints()
380 if (fGroup
!= NULL
) {
381 LinearSpec
* linearSpec
= fGroup
->GetLinearSpec();
383 if (linearSpec
!= NULL
) {
384 linearSpec
->RemoveConstraint(fMinWidthConstraint
, true);
385 linearSpec
->RemoveConstraint(fMinHeightConstraint
, true);
386 linearSpec
->RemoveConstraint(fMaxWidthConstraint
, true);
387 linearSpec
->RemoveConstraint(fMaxHeightConstraint
, true);
388 linearSpec
->RemoveConstraint(fWidthConstraint
, true);
389 linearSpec
->RemoveConstraint(fHeightConstraint
, true);
393 fMinWidthConstraint
= NULL
;
394 fMinHeightConstraint
= NULL
;
395 fMaxWidthConstraint
= NULL
;
396 fMaxHeightConstraint
= NULL
;
397 fWidthConstraint
= NULL
;
398 fHeightConstraint
= NULL
;
403 WindowArea::_CrossingByPosition(Crossing
* crossing
, SATGroup
* group
)
405 BReference
<Crossing
> crossRef
= NULL
;
407 Tab
* oldHTab
= crossing
->HorizontalTab();
408 BReference
<Tab
> hTab
= group
->FindHorizontalTab(oldHTab
->Position());
410 hTab
= group
->_AddHorizontalTab(oldHTab
->Position());
414 Tab
* oldVTab
= crossing
->VerticalTab();
415 crossRef
= hTab
->FindCrossing(oldVTab
->Position());
419 BReference
<Tab
> vTab
= group
->FindVerticalTab(oldVTab
->Position());
421 vTab
= group
->_AddVerticalTab(oldVTab
->Position());
425 return hTab
->AddCrossing(vTab
);
430 WindowArea::_InitCorners()
432 _SetToWindowCorner(fLeftTopCrossing
->RightBottomCorner());
433 _SetToNeighbourCorner(fLeftTopCrossing
->LeftBottomCorner());
434 _SetToNeighbourCorner(fLeftTopCrossing
->RightTopCorner());
436 _SetToWindowCorner(fRightTopCrossing
->LeftBottomCorner());
437 _SetToNeighbourCorner(fRightTopCrossing
->LeftTopCorner());
438 _SetToNeighbourCorner(fRightTopCrossing
->RightBottomCorner());
440 _SetToWindowCorner(fLeftBottomCrossing
->RightTopCorner());
441 _SetToNeighbourCorner(fLeftBottomCrossing
->LeftTopCorner());
442 _SetToNeighbourCorner(fLeftBottomCrossing
->RightBottomCorner());
444 _SetToWindowCorner(fRightBottomCrossing
->LeftTopCorner());
445 _SetToNeighbourCorner(fRightBottomCrossing
->LeftBottomCorner());
446 _SetToNeighbourCorner(fRightBottomCrossing
->RightTopCorner());
451 WindowArea::_CleanupCorners()
453 _UnsetWindowCorner(fLeftTopCrossing
->RightBottomCorner());
454 _UnsetNeighbourCorner(fLeftTopCrossing
->LeftBottomCorner(),
455 fLeftBottomCrossing
->LeftTopCorner());
456 _UnsetNeighbourCorner(fLeftTopCrossing
->RightTopCorner(),
457 fLeftBottomCrossing
->LeftTopCorner());
459 _UnsetWindowCorner(fRightTopCrossing
->LeftBottomCorner());
460 _UnsetNeighbourCorner(fRightTopCrossing
->LeftTopCorner(),
461 fLeftBottomCrossing
->RightTopCorner());
462 _UnsetNeighbourCorner(fRightTopCrossing
->RightBottomCorner(),
463 fLeftBottomCrossing
->RightTopCorner());
465 _UnsetWindowCorner(fLeftBottomCrossing
->RightTopCorner());
466 _UnsetNeighbourCorner(fLeftBottomCrossing
->LeftTopCorner(),
467 fLeftBottomCrossing
->LeftBottomCorner());
468 _UnsetNeighbourCorner(fLeftBottomCrossing
->RightBottomCorner(),
469 fLeftBottomCrossing
->LeftBottomCorner());
471 _UnsetWindowCorner(fRightBottomCrossing
->LeftTopCorner());
472 _UnsetNeighbourCorner(fRightBottomCrossing
->LeftBottomCorner(),
473 fRightBottomCrossing
->RightBottomCorner());
474 _UnsetNeighbourCorner(fRightBottomCrossing
->RightTopCorner(),
475 fRightBottomCrossing
->RightBottomCorner());
480 WindowArea::_SetToWindowCorner(Corner
* corner
)
482 corner
->status
= Corner::kUsed
;
483 corner
->windowArea
= this;
488 WindowArea::_SetToNeighbourCorner(Corner
* neighbour
)
490 if (neighbour
->status
== Corner::kNotDockable
)
491 neighbour
->status
= Corner::kFree
;
496 WindowArea::_UnsetWindowCorner(Corner
* corner
)
498 corner
->status
= Corner::kFree
;
499 corner
->windowArea
= NULL
;
504 WindowArea::_UnsetNeighbourCorner(Corner
* neighbour
, Corner
* opponent
)
506 if (neighbour
->status
== Corner::kFree
&& opponent
->status
!= Corner::kUsed
)
507 neighbour
->status
= Corner::kNotDockable
;
512 WindowArea::_MoveToSAT(SATWindow
* triggerWindow
)
514 SATWindow
* topWindow
= TopWindow();
515 // if there is no window in the group we are done
516 if (topWindow
== NULL
)
519 BRect
frameSAT(LeftVar()->Value() - kMakePositiveOffset
,
520 TopVar()->Value() - kMakePositiveOffset
,
521 RightVar()->Value() - kMakePositiveOffset
,
522 BottomVar()->Value() - kMakePositiveOffset
);
523 topWindow
->AdjustSizeLimits(frameSAT
);
525 BRect frame
= topWindow
->CompleteWindowFrame();
526 float deltaToX
= round(frameSAT
.left
- frame
.left
);
527 float deltaToY
= round(frameSAT
.top
- frame
.top
);
528 frame
.OffsetBy(deltaToX
, deltaToY
);
529 float deltaByX
= round(frameSAT
.right
- frame
.right
);
530 float deltaByY
= round(frameSAT
.bottom
- frame
.bottom
);
532 int32 workspace
= triggerWindow
->GetWindow()->CurrentWorkspace();
533 Desktop
* desktop
= triggerWindow
->GetWindow()->Desktop();
534 desktop
->MoveWindowBy(topWindow
->GetWindow(), deltaToX
, deltaToY
,
536 // Update frame to the new position
537 desktop
->ResizeWindowBy(topWindow
->GetWindow(), deltaByX
, deltaByY
);
539 UpdateSizeConstaints(frameSAT
);
545 status(kNotDockable
),
553 Corner::Trace() const
557 debug_printf("free corner\n");
562 debug_printf("attached windows:\n");
563 const SATWindowList
& list
= windowArea
->WindowList();
564 for (int i
= 0; i
< list
.CountItems(); i
++) {
565 debug_printf("- %s\n", list
.ItemAt(i
)->GetWindow()->Title());
571 debug_printf("not dockable\n");
577 Crossing::Crossing(Tab
* vertical
, Tab
* horizontal
)
579 fVerticalTab(vertical
),
580 fHorizontalTab(horizontal
)
585 Crossing::~Crossing()
587 fVerticalTab
->RemoveCrossing(this);
588 fHorizontalTab
->RemoveCrossing(this);
593 Crossing::GetCorner(Corner::position_t corner
) const
595 return &const_cast<Corner
*>(fCorners
)[corner
];
600 Crossing::GetOppositeCorner(Corner::position_t corner
) const
602 return &const_cast<Corner
*>(fCorners
)[3 - corner
];
607 Crossing::VerticalTab() const
614 Crossing::HorizontalTab() const
616 return fHorizontalTab
;
621 Crossing::Trace() const
623 debug_printf("left-top corner: ");
624 fCorners
[Corner::kLeftTop
].Trace();
625 debug_printf("right-top corner: ");
626 fCorners
[Corner::kRightTop
].Trace();
627 debug_printf("left-bottom corner: ");
628 fCorners
[Corner::kLeftBottom
].Trace();
629 debug_printf("right-bottom corner: ");
630 fCorners
[Corner::kRightBottom
].Trace();
634 Tab::Tab(SATGroup
* group
, Variable
* variable
, orientation_t orientation
)
638 fOrientation(orientation
)
646 if (fOrientation
== kVertical
)
647 fGroup
->_RemoveVerticalTab(this);
649 fGroup
->_RemoveHorizontalTab(this);
656 Tab::Position() const
658 return (float)fVariable
->Value() - kMakePositiveOffset
;
663 Tab::SetPosition(float position
)
665 fVariable
->SetValue(position
+ kMakePositiveOffset
);
670 Tab::Orientation() const
677 Tab::Connect(Variable
* variable
)
679 return fVariable
->IsEqual(variable
);
684 Tab::AddCrossing(Tab
* tab
)
686 if (tab
->Orientation() == fOrientation
)
689 Tab
* vTab
= (fOrientation
== kVertical
) ? this : tab
;
690 Tab
* hTab
= (fOrientation
== kHorizontal
) ? this : tab
;
692 Crossing
* crossing
= new (std::nothrow
)Crossing(vTab
, hTab
);
696 if (!fCrossingList
.AddItem(crossing
)) {
699 if (!tab
->fCrossingList
.AddItem(crossing
)) {
700 fCrossingList
.RemoveItem(crossing
);
704 BReference
<Crossing
> crossingRef(crossing
, true);
710 Tab::RemoveCrossing(Crossing
* crossing
)
712 Tab
* vTab
= crossing
->VerticalTab();
713 Tab
* hTab
= crossing
->HorizontalTab();
715 if (vTab
!= this && hTab
!= this)
717 fCrossingList
.RemoveItem(crossing
);
724 Tab::FindCrossingIndex(Tab
* tab
)
726 if (fOrientation
== kVertical
) {
727 for (int32 i
= 0; i
< fCrossingList
.CountItems(); i
++) {
728 if (fCrossingList
.ItemAt(i
)->HorizontalTab() == tab
)
732 for (int32 i
= 0; i
< fCrossingList
.CountItems(); i
++) {
733 if (fCrossingList
.ItemAt(i
)->VerticalTab() == tab
)
742 Tab::FindCrossingIndex(float pos
)
744 if (fOrientation
== kVertical
) {
745 for (int32 i
= 0; i
< fCrossingList
.CountItems(); i
++) {
746 if (fabs(fCrossingList
.ItemAt(i
)->HorizontalTab()->Position() - pos
)
751 for (int32 i
= 0; i
< fCrossingList
.CountItems(); i
++) {
752 if (fabs(fCrossingList
.ItemAt(i
)->VerticalTab()->Position() - pos
)
762 Tab::FindCrossing(Tab
* tab
)
764 return fCrossingList
.ItemAt(FindCrossingIndex(tab
));
769 Tab::FindCrossing(float tabPosition
)
771 return fCrossingList
.ItemAt(FindCrossingIndex(tabPosition
));
776 Tab::GetCrossingList() const
778 return &fCrossingList
;
783 Tab::CompareFunction(const Tab
* tab1
, const Tab
* tab2
)
785 if (tab1
->Position() < tab2
->Position())
794 fLinearSpec(new(std::nothrow
) LinearSpec()),
795 fHorizontalTabsSorted(false),
796 fVerticalTabsSorted(false),
802 SATGroup::~SATGroup()
805 if (fSATWindowList
.CountItems() > 0)
806 debugger("Deleting a SATGroup which is not empty");
807 //while (fSATWindowList.CountItems() > 0)
808 // RemoveWindow(fSATWindowList.ItemAt(0));
810 fLinearSpec
->ReleaseReference();
815 SATGroup::AddWindow(SATWindow
* window
, Tab
* left
, Tab
* top
, Tab
* right
,
818 STRACE_SAT("SATGroup::AddWindow\n");
820 // first check if we have to create tabs and missing corners.
821 BReference
<Tab
> leftRef
, rightRef
, topRef
, bottomRef
;
822 BReference
<Crossing
> leftTopRef
, rightTopRef
, leftBottomRef
, rightBottomRef
;
824 if (left
!= NULL
&& top
!= NULL
)
825 leftTopRef
= left
->FindCrossing(top
);
826 if (right
!= NULL
&& top
!= NULL
)
827 rightTopRef
= right
->FindCrossing(top
);
828 if (left
!= NULL
&& bottom
!= NULL
)
829 leftBottomRef
= left
->FindCrossing(bottom
);
830 if (right
!= NULL
&& bottom
!= NULL
)
831 rightBottomRef
= right
->FindCrossing(bottom
);
834 leftRef
= _AddVerticalTab();
835 left
= leftRef
.Get();
838 topRef
= _AddHorizontalTab();
842 rightRef
= _AddVerticalTab();
843 right
= rightRef
.Get();
845 if (bottom
== NULL
) {
846 bottomRef
= _AddHorizontalTab();
847 bottom
= bottomRef
.Get();
849 if (left
== NULL
|| top
== NULL
|| right
== NULL
|| bottom
== NULL
)
852 if (leftTopRef
== NULL
) {
853 leftTopRef
= left
->AddCrossing(top
);
854 if (leftTopRef
== NULL
)
858 rightTopRef
= right
->AddCrossing(top
);
862 if (!leftBottomRef
) {
863 leftBottomRef
= left
->AddCrossing(bottom
);
867 if (!rightBottomRef
) {
868 rightBottomRef
= right
->AddCrossing(bottom
);
873 WindowArea
* area
= new(std::nothrow
) WindowArea(leftTopRef
, rightTopRef
,
874 leftBottomRef
, rightBottomRef
);
877 // the area register itself in our area list
878 if (area
->Init(this) == false) {
882 // delete the area if AddWindow failed / release our reference on it
883 BReference
<WindowArea
> areaRef(area
, true);
885 return AddWindow(window
, area
);
890 SATGroup::AddWindow(SATWindow
* window
, WindowArea
* area
, SATWindow
* after
)
892 if (!area
->_AddWindow(window
, after
))
895 if (!fSATWindowList
.AddItem(window
)) {
896 area
->_RemoveWindow(window
);
900 if (!window
->AddedToGroup(this, area
)) {
901 area
->_RemoveWindow(window
);
902 fSATWindowList
.RemoveItem(window
);
911 SATGroup::RemoveWindow(SATWindow
* window
, bool stayBelowMouse
)
913 if (!fSATWindowList
.RemoveItem(window
))
916 // We need the area a little bit longer because the area could hold the
917 // last reference to the group.
918 BReference
<WindowArea
> area
= window
->GetWindowArea();
919 if (area
.Get() != NULL
)
920 area
->_RemoveWindow(window
);
922 window
->RemovedFromGroup(this, stayBelowMouse
);
924 if (CountItems() >= 2)
925 WindowAt(0)->DoGroupLayout();
932 SATGroup::CountItems()
934 return fSATWindowList
.CountItems();
939 SATGroup::WindowAt(int32 index
)
941 return fSATWindowList
.ItemAt(index
);
946 SATGroup::ActiveWindow() const
948 return fActiveWindow
;
953 SATGroup::SetActiveWindow(SATWindow
* window
)
955 fActiveWindow
= window
;
960 SATGroup::HorizontalTabs()
962 if (!fHorizontalTabsSorted
) {
963 fHorizontalTabs
.SortItems(Tab::CompareFunction
);
964 fHorizontalTabsSorted
= true;
966 return &fHorizontalTabs
;
971 SATGroup::VerticalTabs()
973 if (!fVerticalTabsSorted
) {
974 fVerticalTabs
.SortItems(Tab::CompareFunction
);
975 fVerticalTabsSorted
= true;
977 return &fVerticalTabs
;
982 SATGroup::FindHorizontalTab(float position
)
984 return _FindTab(fHorizontalTabs
, position
);
989 SATGroup::FindVerticalTab(float position
)
991 return _FindTab(fVerticalTabs
, position
);
996 SATGroup::WindowAreaRemoved(WindowArea
* area
)
998 _SplitGroupIfNecessary(area
);
1003 SATGroup::RestoreGroup(const BMessage
& archive
, StackAndTile
* sat
)
1006 SATGroup
* group
= new (std::nothrow
)SATGroup
;
1009 BReference
<SATGroup
> groupRef
;
1010 groupRef
.SetTo(group
, true);
1012 int32 nHTabs
, nVTabs
;
1014 status
= archive
.FindInt32("htab_count", &nHTabs
);
1017 status
= archive
.FindInt32("vtab_count", &nVTabs
);
1021 vector
<BReference
<Tab
> > tempHTabs
;
1022 for (int i
= 0; i
< nHTabs
; i
++) {
1023 BReference
<Tab
> tab
= group
->_AddHorizontalTab();
1026 tempHTabs
.push_back(tab
);
1028 vector
<BReference
<Tab
> > tempVTabs
;
1029 for (int i
= 0; i
< nVTabs
; i
++) {
1030 BReference
<Tab
> tab
= group
->_AddVerticalTab();
1033 tempVTabs
.push_back(tab
);
1036 BMessage areaArchive
;
1037 for (int32 i
= 0; archive
.FindMessage("area", i
, &areaArchive
) == B_OK
;
1039 uint32 leftTab
, rightTab
, topTab
, bottomTab
;
1040 if (areaArchive
.FindInt32("left_tab", (int32
*)&leftTab
) != B_OK
1041 || areaArchive
.FindInt32("right_tab", (int32
*)&rightTab
) != B_OK
1042 || areaArchive
.FindInt32("top_tab", (int32
*)&topTab
) != B_OK
1043 || areaArchive
.FindInt32("bottom_tab", (int32
*)&bottomTab
) != B_OK
)
1046 if (leftTab
>= tempVTabs
.size() || rightTab
>= tempVTabs
.size())
1048 if (topTab
>= tempHTabs
.size() || bottomTab
>= tempHTabs
.size())
1051 Tab
* left
= tempVTabs
[leftTab
];
1052 Tab
* right
= tempVTabs
[rightTab
];
1053 Tab
* top
= tempHTabs
[topTab
];
1054 Tab
* bottom
= tempHTabs
[bottomTab
];
1056 // adding windows to area
1058 SATWindow
* prevWindow
= NULL
;
1059 for (int32 i
= 0; areaArchive
.FindInt64("window", i
,
1060 (int64
*)&windowId
) == B_OK
; i
++) {
1061 SATWindow
* window
= sat
->FindSATWindow(windowId
);
1065 if (prevWindow
== NULL
) {
1066 if (!group
->AddWindow(window
, left
, top
, right
, bottom
))
1068 prevWindow
= window
;
1070 if (!prevWindow
->StackWindow(window
))
1072 prevWindow
= window
;
1081 SATGroup::ArchiveGroup(BMessage
& archive
)
1083 archive
.AddInt32("htab_count", fHorizontalTabs
.CountItems());
1084 archive
.AddInt32("vtab_count", fVerticalTabs
.CountItems());
1086 for (int i
= 0; i
< fWindowAreaList
.CountItems(); i
++) {
1087 WindowArea
* area
= fWindowAreaList
.ItemAt(i
);
1088 int32 leftTab
= fVerticalTabs
.IndexOf(area
->LeftTab());
1089 int32 rightTab
= fVerticalTabs
.IndexOf(area
->RightTab());
1090 int32 topTab
= fHorizontalTabs
.IndexOf(area
->TopTab());
1091 int32 bottomTab
= fHorizontalTabs
.IndexOf(area
->BottomTab());
1093 BMessage areaMessage
;
1094 areaMessage
.AddInt32("left_tab", leftTab
);
1095 areaMessage
.AddInt32("right_tab", rightTab
);
1096 areaMessage
.AddInt32("top_tab", topTab
);
1097 areaMessage
.AddInt32("bottom_tab", bottomTab
);
1099 const SATWindowList
& windowList
= area
->WindowList();
1100 for (int a
= 0; a
< windowList
.CountItems(); a
++)
1101 areaMessage
.AddInt64("window", windowList
.ItemAt(a
)->Id());
1103 archive
.AddMessage("area", &areaMessage
);
1110 SATGroup::_AddHorizontalTab(float position
)
1112 if (fLinearSpec
== NULL
)
1114 Variable
* variable
= fLinearSpec
->AddVariable();
1115 if (variable
== NULL
)
1118 Tab
* tab
= new (std::nothrow
)Tab(this, variable
, Tab::kHorizontal
);
1121 BReference
<Tab
> tabRef(tab
, true);
1123 if (!fHorizontalTabs
.AddItem(tab
))
1126 fHorizontalTabsSorted
= false;
1127 tabRef
->SetPosition(position
);
1133 SATGroup::_AddVerticalTab(float position
)
1135 if (fLinearSpec
== NULL
)
1137 Variable
* variable
= fLinearSpec
->AddVariable();
1138 if (variable
== NULL
)
1141 Tab
* tab
= new (std::nothrow
)Tab(this, variable
, Tab::kVertical
);
1144 BReference
<Tab
> tabRef(tab
, true);
1146 if (!fVerticalTabs
.AddItem(tab
))
1149 fVerticalTabsSorted
= false;
1150 tabRef
->SetPosition(position
);
1156 SATGroup::_RemoveHorizontalTab(Tab
* tab
)
1158 if (!fHorizontalTabs
.RemoveItem(tab
))
1160 fHorizontalTabsSorted
= false;
1161 // don't delete the tab it is reference counted
1167 SATGroup::_RemoveVerticalTab(Tab
* tab
)
1169 if (!fVerticalTabs
.RemoveItem(tab
))
1171 fVerticalTabsSorted
= false;
1172 // don't delete the tab it is reference counted
1178 SATGroup::_FindTab(const TabList
& list
, float position
)
1180 for (int i
= 0; i
< list
.CountItems(); i
++)
1181 if (fabs(list
.ItemAt(i
)->Position() - position
) < 0.00001)
1182 return list
.ItemAt(i
);
1189 SATGroup::_SplitGroupIfNecessary(WindowArea
* removedArea
)
1191 // if there are windows stacked in the area we don't need to split
1192 if (removedArea
== NULL
|| removedArea
->WindowList().CountItems() > 1)
1195 WindowAreaList neighbourWindows
;
1197 _FillNeighbourList(neighbourWindows
, removedArea
);
1199 bool ownGroupProcessed
= false;
1200 WindowAreaList newGroup
;
1201 while (_FindConnectedGroup(neighbourWindows
, removedArea
, newGroup
)) {
1202 STRACE_SAT("Connected group found; %i window(s)\n",
1203 (int)newGroup
.CountItems());
1204 if (newGroup
.CountItems() == 1
1205 && newGroup
.ItemAt(0)->WindowList().CountItems() == 1) {
1206 SATWindow
* window
= newGroup
.ItemAt(0)->WindowList().ItemAt(0);
1207 RemoveWindow(window
);
1208 _EnsureGroupIsOnScreen(window
->GetGroup());
1209 } else if (ownGroupProcessed
)
1210 _SpawnNewGroup(newGroup
);
1212 _EnsureGroupIsOnScreen(this);
1213 ownGroupProcessed
= true;
1216 newGroup
.MakeEmpty();
1222 SATGroup::_FillNeighbourList(WindowAreaList
& neighbourWindows
,
1225 _LeftNeighbours(neighbourWindows
, area
);
1226 _RightNeighbours(neighbourWindows
, area
);
1227 _TopNeighbours(neighbourWindows
, area
);
1228 _BottomNeighbours(neighbourWindows
, area
);
1233 SATGroup::_LeftNeighbours(WindowAreaList
& neighbourWindows
, WindowArea
* parent
)
1235 float startPos
= parent
->LeftTopCrossing()->HorizontalTab()->Position();
1236 float endPos
= parent
->LeftBottomCrossing()->HorizontalTab()->Position();
1238 Tab
* tab
= parent
->LeftTopCrossing()->VerticalTab();
1239 const CrossingList
* crossingList
= tab
->GetCrossingList();
1240 for (int i
= 0; i
< crossingList
->CountItems(); i
++) {
1241 Corner
* corner
= crossingList
->ItemAt(i
)->LeftTopCorner();
1242 if (corner
->status
!= Corner::kUsed
)
1245 WindowArea
* area
= corner
->windowArea
;
1246 float pos1
= area
->LeftTopCrossing()->HorizontalTab()->Position();
1247 float pos2
= area
->LeftBottomCrossing()->HorizontalTab()->Position();
1249 if (pos1
< endPos
&& pos2
> startPos
)
1250 neighbourWindows
.AddItem(area
);
1259 SATGroup::_TopNeighbours(WindowAreaList
& neighbourWindows
, WindowArea
* parent
)
1261 float startPos
= parent
->LeftTopCrossing()->VerticalTab()->Position();
1262 float endPos
= parent
->RightTopCrossing()->VerticalTab()->Position();
1264 Tab
* tab
= parent
->LeftTopCrossing()->HorizontalTab();
1265 const CrossingList
* crossingList
= tab
->GetCrossingList();
1266 for (int i
= 0; i
< crossingList
->CountItems(); i
++) {
1267 Corner
* corner
= crossingList
->ItemAt(i
)->LeftTopCorner();
1268 if (corner
->status
!= Corner::kUsed
)
1271 WindowArea
* area
= corner
->windowArea
;
1272 float pos1
= area
->LeftTopCrossing()->VerticalTab()->Position();
1273 float pos2
= area
->RightTopCrossing()->VerticalTab()->Position();
1275 if (pos1
< endPos
&& pos2
> startPos
)
1276 neighbourWindows
.AddItem(area
);
1285 SATGroup::_RightNeighbours(WindowAreaList
& neighbourWindows
, WindowArea
* parent
)
1287 float startPos
= parent
->RightTopCrossing()->HorizontalTab()->Position();
1288 float endPos
= parent
->RightBottomCrossing()->HorizontalTab()->Position();
1290 Tab
* tab
= parent
->RightTopCrossing()->VerticalTab();
1291 const CrossingList
* crossingList
= tab
->GetCrossingList();
1292 for (int i
= 0; i
< crossingList
->CountItems(); i
++) {
1293 Corner
* corner
= crossingList
->ItemAt(i
)->RightTopCorner();
1294 if (corner
->status
!= Corner::kUsed
)
1297 WindowArea
* area
= corner
->windowArea
;
1298 float pos1
= area
->RightTopCrossing()->HorizontalTab()->Position();
1299 float pos2
= area
->RightBottomCrossing()->HorizontalTab()->Position();
1301 if (pos1
< endPos
&& pos2
> startPos
)
1302 neighbourWindows
.AddItem(area
);
1311 SATGroup::_BottomNeighbours(WindowAreaList
& neighbourWindows
,
1314 float startPos
= parent
->LeftBottomCrossing()->VerticalTab()->Position();
1315 float endPos
= parent
->RightBottomCrossing()->VerticalTab()->Position();
1317 Tab
* tab
= parent
->LeftBottomCrossing()->HorizontalTab();
1318 const CrossingList
* crossingList
= tab
->GetCrossingList();
1319 for (int i
= 0; i
< crossingList
->CountItems(); i
++) {
1320 Corner
* corner
= crossingList
->ItemAt(i
)->LeftBottomCorner();
1321 if (corner
->status
!= Corner::kUsed
)
1324 WindowArea
* area
= corner
->windowArea
;
1325 float pos1
= area
->LeftBottomCrossing()->VerticalTab()->Position();
1326 float pos2
= area
->RightBottomCrossing()->VerticalTab()->Position();
1328 if (pos1
< endPos
&& pos2
> startPos
)
1329 neighbourWindows
.AddItem(area
);
1338 SATGroup::_FindConnectedGroup(WindowAreaList
& seedList
, WindowArea
* removedArea
,
1339 WindowAreaList
& newGroup
)
1341 if (seedList
.CountItems() == 0)
1344 WindowArea
* area
= seedList
.RemoveItemAt(0);
1345 newGroup
.AddItem(area
);
1347 _FollowSeed(area
, removedArea
, seedList
, newGroup
);
1353 SATGroup::_FollowSeed(WindowArea
* area
, WindowArea
* veto
,
1354 WindowAreaList
& seedList
, WindowAreaList
& newGroup
)
1356 WindowAreaList neighbours
;
1357 _FillNeighbourList(neighbours
, area
);
1358 for (int i
= 0; i
< neighbours
.CountItems(); i
++) {
1359 WindowArea
* currentArea
= neighbours
.ItemAt(i
);
1360 if (currentArea
!= veto
&& !newGroup
.HasItem(currentArea
)) {
1361 newGroup
.AddItem(currentArea
);
1362 // if we get a area from the seed list it is not a seed any more
1363 seedList
.RemoveItem(currentArea
);
1365 // don't _FollowSeed of invalid areas
1366 neighbours
.RemoveItemAt(i
);
1371 for (int i
= 0; i
< neighbours
.CountItems(); i
++)
1372 _FollowSeed(neighbours
.ItemAt(i
), veto
, seedList
, newGroup
);
1377 SATGroup::_SpawnNewGroup(const WindowAreaList
& newGroup
)
1379 STRACE_SAT("SATGroup::_SpawnNewGroup\n");
1380 SATGroup
* group
= new (std::nothrow
)SATGroup
;
1383 BReference
<SATGroup
> groupRef
;
1384 groupRef
.SetTo(group
, true);
1386 for (int i
= 0; i
< newGroup
.CountItems(); i
++)
1387 newGroup
.ItemAt(i
)->PropagateToGroup(group
);
1389 _EnsureGroupIsOnScreen(group
);
1393 const float kMinOverlap
= 50;
1394 const float kMoveToScreen
= 75;
1398 SATGroup::_EnsureGroupIsOnScreen(SATGroup
* group
)
1400 STRACE_SAT("SATGroup::_EnsureGroupIsOnScreen\n");
1401 if (group
== NULL
|| group
->CountItems() < 1)
1404 SATWindow
* window
= group
->WindowAt(0);
1405 Desktop
* desktop
= window
->GetWindow()->Desktop();
1406 if (desktop
== NULL
)
1409 const float kBigDistance
= 1E+10;
1411 float minLeftDistance
= kBigDistance
;
1413 float minTopDistance
= kBigDistance
;
1415 float minRightDistance
= kBigDistance
;
1417 float minBottomDistance
= kBigDistance
;
1420 BRect screen
= window
->GetWindow()->Screen()->Frame();
1421 BRect reducedScreen
= screen
;
1422 reducedScreen
.InsetBy(kMinOverlap
, kMinOverlap
);
1424 for (int i
= 0; i
< group
->CountItems(); i
++) {
1425 SATWindow
* window
= group
->WindowAt(i
);
1426 BRect frame
= window
->CompleteWindowFrame();
1427 if (reducedScreen
.Intersects(frame
))
1430 if (frame
.right
< screen
.left
+ kMinOverlap
) {
1431 float dist
= fabs(screen
.left
- frame
.right
);
1432 if (dist
< minLeftDistance
) {
1433 minLeftDistance
= dist
;
1435 } else if (dist
== minLeftDistance
)
1436 leftRect
= leftRect
| frame
;
1438 if (frame
.top
> screen
.bottom
- kMinOverlap
) {
1439 float dist
= fabs(frame
.top
- screen
.bottom
);
1440 if (dist
< minBottomDistance
) {
1441 minBottomDistance
= dist
;
1443 } else if (dist
== minBottomDistance
)
1444 bottomRect
= bottomRect
| frame
;
1446 if (frame
.left
> screen
.right
- kMinOverlap
) {
1447 float dist
= fabs(frame
.left
- screen
.right
);
1448 if (dist
< minRightDistance
) {
1449 minRightDistance
= dist
;
1451 } else if (dist
== minRightDistance
)
1452 rightRect
= rightRect
| frame
;
1454 if (frame
.bottom
< screen
.top
+ kMinOverlap
) {
1455 float dist
= fabs(frame
.bottom
- screen
.top
);
1456 if (dist
< minTopDistance
) {
1457 minTopDistance
= dist
;
1459 } else if (dist
== minTopDistance
)
1460 topRect
= topRect
| frame
;
1465 if (minLeftDistance
< kBigDistance
) {
1466 offset
.x
= screen
.left
- leftRect
.right
+ kMoveToScreen
;
1467 _CallculateYOffset(offset
, leftRect
, screen
);
1468 } else if (minTopDistance
< kBigDistance
) {
1469 offset
.y
= screen
.top
- topRect
.bottom
+ kMoveToScreen
;
1470 _CallculateXOffset(offset
, topRect
, screen
);
1471 } else if (minRightDistance
< kBigDistance
) {
1472 offset
.x
= screen
.right
- rightRect
.left
- kMoveToScreen
;
1473 _CallculateYOffset(offset
, rightRect
, screen
);
1474 } else if (minBottomDistance
< kBigDistance
) {
1475 offset
.y
= screen
.bottom
- bottomRect
.top
- kMoveToScreen
;
1476 _CallculateXOffset(offset
, bottomRect
, screen
);
1479 if (offset
.x
== 0. && offset
.y
== 0.)
1481 STRACE_SAT("move group back to screen: offset x: %f offset y: %f\n",
1482 offset
.x
, offset
.y
);
1484 desktop
->MoveWindowBy(window
->GetWindow(), offset
.x
, offset
.y
);
1485 window
->DoGroupLayout();
1490 SATGroup::_CallculateXOffset(BPoint
& offset
, BRect
& frame
, BRect
& screen
)
1492 if (frame
.right
< screen
.left
+ kMinOverlap
)
1493 offset
.x
= screen
.left
- frame
.right
+ kMoveToScreen
;
1494 else if (frame
.left
> screen
.right
- kMinOverlap
)
1495 offset
.x
= screen
.right
- frame
.left
- kMoveToScreen
;
1500 SATGroup::_CallculateYOffset(BPoint
& offset
, BRect
& frame
, BRect
& screen
)
1502 if (frame
.top
> screen
.bottom
- kMinOverlap
)
1503 offset
.y
= screen
.bottom
- frame
.top
- kMoveToScreen
;
1504 else if (frame
.bottom
< screen
.top
+ kMinOverlap
)
1505 offset
.y
= screen
.top
- frame
.bottom
+ kMoveToScreen
;