vfs: check userland buffers before reading them.
[haiku.git] / src / servers / app / stackandtile / SATGroup.cpp
blob8afcd38d34735cbc2dfe5c6f6a726bcd9a6712da
1 /*
2 * Copyright 2010-2014 Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
5 * Authors:
6 * John Scipione, jscipione@gmail.com
7 * Clemens Zeidler, haiku@clemens-zeidler.de
8 */
11 #include "SATGroup.h"
13 #include <vector>
15 #include <Debug.h>
16 #include <Message.h>
18 #include "Desktop.h"
20 #include "SATWindow.h"
21 #include "StackAndTile.h"
22 #include "Window.h"
25 using namespace std;
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)
37 fGroup(NULL),
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()
56 if (fGroup)
57 fGroup->WindowAreaRemoved(this);
59 _CleanupCorners();
60 fGroup->fWindowAreaList.RemoveItem(this);
62 _UninitConstraints();
66 bool
67 WindowArea::Init(SATGroup* group)
69 _UninitConstraints();
71 if (group == NULL || group->fWindowAreaList.AddItem(this) == false)
72 return false;
74 fGroup = group;
76 LinearSpec* linearSpec = fGroup->GetLinearSpec();
78 fMinWidthConstraint = linearSpec->AddConstraint(1.0, RightVar(), -1.0,
79 LeftVar(), kGE, 0);
80 fMinHeightConstraint = linearSpec->AddConstraint(1.0, BottomVar(), -1.0,
81 TopVar(), kGE, 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,
91 kExtentPenalty);
92 fHeightConstraint = linearSpec->AddConstraint(-1.0, TopVar(), 1.0,
93 BottomVar(), kEQ, 0, kExtentPenalty,
94 kExtentPenalty);
96 if (!fMinWidthConstraint || !fMinHeightConstraint || !fWidthConstraint
97 || !fHeightConstraint || !fMaxWidthConstraint
98 || !fMaxHeightConstraint)
99 return false;
101 return true;
105 void
106 WindowArea::DoGroupLayout()
108 SATWindow* parentWindow = fWindowLayerOrder.ItemAt(0);
109 if (parentWindow == NULL)
110 return;
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(),
122 kEQ, frame.left);
123 Constraint* topConstraint = linearSpec->AddConstraint(1.0, TopVar(), kEQ,
124 frame.top);
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.
133 ResultType result;
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");
138 break;
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);
146 break;
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);
161 void
162 WindowArea::UpdateSizeLimits()
164 _UpdateConstraintValues();
168 void
169 WindowArea::UpdateSizeConstaints(const BRect& frame)
171 // adjust window size soft constraints
172 fWidthConstraint->SetRightSide(frame.Width());
173 fHeightConstraint->SetRightSide(frame.Height());
177 bool
178 WindowArea::MoveWindowToPosition(SATWindow* window, int32 index)
180 int32 oldIndex = fWindowList.IndexOf(window);
181 ASSERT(oldIndex != index);
182 return fWindowList.MoveItem(oldIndex, index);
186 SATWindow*
187 WindowArea::TopWindow()
189 return fWindowLayerOrder.ItemAt(fWindowLayerOrder.CountItems() - 1);
193 void
194 WindowArea::_UpdateConstraintValues()
196 SATWindow* topWindow = TopWindow();
197 if (topWindow == NULL)
198 return;
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
207 int32 minW, maxW;
208 int32 minH, maxH;
209 window->GetSizeLimits(&minW, &maxW, &minH, &maxH);
210 if (minWidth < minW)
211 minWidth = minW;
212 if (minHeight < minH)
213 minHeight = minH;
214 if (maxWidth < maxW)
215 maxWidth = maxW;
216 if (maxHeight < maxH)
217 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());
243 bool
244 WindowArea::_AddWindow(SATWindow* window, SATWindow* after)
246 if (after) {
247 int32 indexAfter = fWindowList.IndexOf(after);
248 if (!fWindowList.AddItem(window, indexAfter + 1))
249 return false;
250 } else if (fWindowList.AddItem(window) == false)
251 return false;
253 AcquireReference();
255 if (fWindowList.CountItems() <= 1)
256 _InitCorners();
258 fWindowLayerOrder.AddItem(window);
260 _UpdateConstraintValues();
261 return true;
265 bool
266 WindowArea::_RemoveWindow(SATWindow* window)
268 if (!fWindowList.RemoveItem(window))
269 return false;
271 fWindowLayerOrder.RemoveItem(window);
272 _UpdateConstraintValues();
274 window->RemovedFromArea(this);
275 ReleaseReference();
276 return true;
280 Tab*
281 WindowArea::LeftTab()
283 return fLeftTopCrossing->VerticalTab();
287 Tab*
288 WindowArea::RightTab()
290 return fRightBottomCrossing->VerticalTab();
294 Tab*
295 WindowArea::TopTab()
297 return fLeftTopCrossing->HorizontalTab();
301 Tab*
302 WindowArea::BottomTab()
304 return fRightBottomCrossing->HorizontalTab();
308 BRect
309 WindowArea::Frame()
311 return BRect(fLeftTopCrossing->VerticalTab()->Position(),
312 fLeftTopCrossing->HorizontalTab()->Position(),
313 fRightBottomCrossing->VerticalTab()->Position(),
314 fRightBottomCrossing->HorizontalTab()->Position());
318 bool
319 WindowArea::PropagateToGroup(SATGroup* group)
321 BReference<Crossing> newLeftTop = _CrossingByPosition(fLeftTopCrossing,
322 group);
323 BReference<Crossing> newRightTop = _CrossingByPosition(fRightTopCrossing,
324 group);
325 BReference<Crossing> newLeftBottom = _CrossingByPosition(
326 fLeftBottomCrossing, group);
327 BReference<Crossing> newRightBottom = _CrossingByPosition(
328 fRightBottomCrossing, group);
330 if (!newLeftTop || !newRightTop || !newLeftBottom || !newRightBottom)
331 return false;
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;
344 _InitCorners();
346 BReference<SATGroup> oldGroup = fGroup;
347 // manage constraints
348 if (Init(group) == false)
349 return 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)
355 return false;
356 if (group->fSATWindowList.AddItem(window) == false) {
357 _UninitConstraints();
358 return false;
362 _UpdateConstraintValues();
364 return true;
368 bool
369 WindowArea::MoveToTopLayer(SATWindow* window)
371 if (!fWindowLayerOrder.RemoveItem(window))
372 return false;
373 return fWindowLayerOrder.AddItem(window);
377 void
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;
402 BReference<Crossing>
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());
409 if (!hTab)
410 hTab = group->_AddHorizontalTab(oldHTab->Position());
411 if (!hTab)
412 return crossRef;
414 Tab* oldVTab = crossing->VerticalTab();
415 crossRef = hTab->FindCrossing(oldVTab->Position());
416 if (crossRef)
417 return crossRef;
419 BReference<Tab> vTab = group->FindVerticalTab(oldVTab->Position());
420 if (!vTab)
421 vTab = group->_AddVerticalTab(oldVTab->Position());
422 if (!vTab)
423 return crossRef;
425 return hTab->AddCrossing(vTab);
429 void
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());
450 void
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());
479 void
480 WindowArea::_SetToWindowCorner(Corner* corner)
482 corner->status = Corner::kUsed;
483 corner->windowArea = this;
487 void
488 WindowArea::_SetToNeighbourCorner(Corner* neighbour)
490 if (neighbour->status == Corner::kNotDockable)
491 neighbour->status = Corner::kFree;
495 void
496 WindowArea::_UnsetWindowCorner(Corner* corner)
498 corner->status = Corner::kFree;
499 corner->windowArea = NULL;
503 void
504 WindowArea::_UnsetNeighbourCorner(Corner* neighbour, Corner* opponent)
506 if (neighbour->status == Corner::kFree && opponent->status != Corner::kUsed)
507 neighbour->status = Corner::kNotDockable;
511 void
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)
517 return;
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,
535 workspace);
536 // Update frame to the new position
537 desktop->ResizeWindowBy(topWindow->GetWindow(), deltaByX, deltaByY);
539 UpdateSizeConstaints(frameSAT);
543 Corner::Corner()
545 status(kNotDockable),
546 windowArea(NULL)
552 void
553 Corner::Trace() const
555 switch (status) {
556 case kFree:
557 debug_printf("free corner\n");
558 break;
560 case kUsed:
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());
567 break;
570 case kNotDockable:
571 debug_printf("not dockable\n");
572 break;
577 Crossing::Crossing(Tab* vertical, Tab* horizontal)
579 fVerticalTab(vertical),
580 fHorizontalTab(horizontal)
585 Crossing::~Crossing()
587 fVerticalTab->RemoveCrossing(this);
588 fHorizontalTab->RemoveCrossing(this);
592 Corner*
593 Crossing::GetCorner(Corner::position_t corner) const
595 return &const_cast<Corner*>(fCorners)[corner];
599 Corner*
600 Crossing::GetOppositeCorner(Corner::position_t corner) const
602 return &const_cast<Corner*>(fCorners)[3 - corner];
606 Tab*
607 Crossing::VerticalTab() const
609 return fVerticalTab;
613 Tab*
614 Crossing::HorizontalTab() const
616 return fHorizontalTab;
620 void
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)
636 fGroup(group),
637 fVariable(variable),
638 fOrientation(orientation)
644 Tab::~Tab()
646 if (fOrientation == kVertical)
647 fGroup->_RemoveVerticalTab(this);
648 else
649 fGroup->_RemoveHorizontalTab(this);
651 delete fVariable;
655 float
656 Tab::Position() const
658 return (float)fVariable->Value() - kMakePositiveOffset;
662 void
663 Tab::SetPosition(float position)
665 fVariable->SetValue(position + kMakePositiveOffset);
669 Tab::orientation_t
670 Tab::Orientation() const
672 return fOrientation;
676 Constraint*
677 Tab::Connect(Variable* variable)
679 return fVariable->IsEqual(variable);
683 BReference<Crossing>
684 Tab::AddCrossing(Tab* tab)
686 if (tab->Orientation() == fOrientation)
687 return NULL;
689 Tab* vTab = (fOrientation == kVertical) ? this : tab;
690 Tab* hTab = (fOrientation == kHorizontal) ? this : tab;
692 Crossing* crossing = new (std::nothrow)Crossing(vTab, hTab);
693 if (!crossing)
694 return NULL;
696 if (!fCrossingList.AddItem(crossing)) {
697 return NULL;
699 if (!tab->fCrossingList.AddItem(crossing)) {
700 fCrossingList.RemoveItem(crossing);
701 return NULL;
704 BReference<Crossing> crossingRef(crossing, true);
705 return crossingRef;
709 bool
710 Tab::RemoveCrossing(Crossing* crossing)
712 Tab* vTab = crossing->VerticalTab();
713 Tab* hTab = crossing->HorizontalTab();
715 if (vTab != this && hTab != this)
716 return false;
717 fCrossingList.RemoveItem(crossing);
719 return true;
723 int32
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)
729 return i;
731 } else {
732 for (int32 i = 0; i < fCrossingList.CountItems(); i++) {
733 if (fCrossingList.ItemAt(i)->VerticalTab() == tab)
734 return i;
737 return -1;
741 int32
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)
747 < 0.0001)
748 return i;
750 } else {
751 for (int32 i = 0; i < fCrossingList.CountItems(); i++) {
752 if (fabs(fCrossingList.ItemAt(i)->VerticalTab()->Position() - pos)
753 < 0.0001)
754 return i;
757 return -1;
761 Crossing*
762 Tab::FindCrossing(Tab* tab)
764 return fCrossingList.ItemAt(FindCrossingIndex(tab));
768 Crossing*
769 Tab::FindCrossing(float tabPosition)
771 return fCrossingList.ItemAt(FindCrossingIndex(tabPosition));
775 const CrossingList*
776 Tab::GetCrossingList() const
778 return &fCrossingList;
783 Tab::CompareFunction(const Tab* tab1, const Tab* tab2)
785 if (tab1->Position() < tab2->Position())
786 return -1;
788 return 1;
792 SATGroup::SATGroup()
794 fLinearSpec(new(std::nothrow) LinearSpec()),
795 fHorizontalTabsSorted(false),
796 fVerticalTabsSorted(false),
797 fActiveWindow(NULL)
802 SATGroup::~SATGroup()
804 // Should be empty
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();
814 bool
815 SATGroup::AddWindow(SATWindow* window, Tab* left, Tab* top, Tab* right,
816 Tab* bottom)
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);
833 if (left == NULL) {
834 leftRef = _AddVerticalTab();
835 left = leftRef.Get();
837 if (top == NULL) {
838 topRef = _AddHorizontalTab();
839 top = topRef.Get();
841 if (right == NULL) {
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)
850 return false;
852 if (leftTopRef == NULL) {
853 leftTopRef = left->AddCrossing(top);
854 if (leftTopRef == NULL)
855 return false;
857 if (!rightTopRef) {
858 rightTopRef = right->AddCrossing(top);
859 if (!rightTopRef)
860 return false;
862 if (!leftBottomRef) {
863 leftBottomRef = left->AddCrossing(bottom);
864 if (!leftBottomRef)
865 return false;
867 if (!rightBottomRef) {
868 rightBottomRef = right->AddCrossing(bottom);
869 if (!rightBottomRef)
870 return false;
873 WindowArea* area = new(std::nothrow) WindowArea(leftTopRef, rightTopRef,
874 leftBottomRef, rightBottomRef);
875 if (area == NULL)
876 return false;
877 // the area register itself in our area list
878 if (area->Init(this) == false) {
879 delete area;
880 return false;
882 // delete the area if AddWindow failed / release our reference on it
883 BReference<WindowArea> areaRef(area, true);
885 return AddWindow(window, area);
889 bool
890 SATGroup::AddWindow(SATWindow* window, WindowArea* area, SATWindow* after)
892 if (!area->_AddWindow(window, after))
893 return false;
895 if (!fSATWindowList.AddItem(window)) {
896 area->_RemoveWindow(window);
897 return false;
900 if (!window->AddedToGroup(this, area)) {
901 area->_RemoveWindow(window);
902 fSATWindowList.RemoveItem(window);
903 return false;
906 return true;
910 bool
911 SATGroup::RemoveWindow(SATWindow* window, bool stayBelowMouse)
913 if (!fSATWindowList.RemoveItem(window))
914 return false;
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();
927 return true;
931 int32
932 SATGroup::CountItems()
934 return fSATWindowList.CountItems();
938 SATWindow*
939 SATGroup::WindowAt(int32 index)
941 return fSATWindowList.ItemAt(index);
945 SATWindow*
946 SATGroup::ActiveWindow() const
948 return fActiveWindow;
952 void
953 SATGroup::SetActiveWindow(SATWindow* window)
955 fActiveWindow = window;
959 const TabList*
960 SATGroup::HorizontalTabs()
962 if (!fHorizontalTabsSorted) {
963 fHorizontalTabs.SortItems(Tab::CompareFunction);
964 fHorizontalTabsSorted = true;
966 return &fHorizontalTabs;
970 const TabList*
971 SATGroup::VerticalTabs()
973 if (!fVerticalTabsSorted) {
974 fVerticalTabs.SortItems(Tab::CompareFunction);
975 fVerticalTabsSorted = true;
977 return &fVerticalTabs;
981 Tab*
982 SATGroup::FindHorizontalTab(float position)
984 return _FindTab(fHorizontalTabs, position);
988 Tab*
989 SATGroup::FindVerticalTab(float position)
991 return _FindTab(fVerticalTabs, position);
995 void
996 SATGroup::WindowAreaRemoved(WindowArea* area)
998 _SplitGroupIfNecessary(area);
1002 status_t
1003 SATGroup::RestoreGroup(const BMessage& archive, StackAndTile* sat)
1005 // create new group
1006 SATGroup* group = new (std::nothrow)SATGroup;
1007 if (group == NULL)
1008 return B_NO_MEMORY;
1009 BReference<SATGroup> groupRef;
1010 groupRef.SetTo(group, true);
1012 int32 nHTabs, nVTabs;
1013 status_t status;
1014 status = archive.FindInt32("htab_count", &nHTabs);
1015 if (status != B_OK)
1016 return status;
1017 status = archive.FindInt32("vtab_count", &nVTabs);
1018 if (status != B_OK)
1019 return status;
1021 vector<BReference<Tab> > tempHTabs;
1022 for (int i = 0; i < nHTabs; i++) {
1023 BReference<Tab> tab = group->_AddHorizontalTab();
1024 if (!tab)
1025 return B_NO_MEMORY;
1026 tempHTabs.push_back(tab);
1028 vector<BReference<Tab> > tempVTabs;
1029 for (int i = 0; i < nVTabs; i++) {
1030 BReference<Tab> tab = group->_AddVerticalTab();
1031 if (!tab)
1032 return B_NO_MEMORY;
1033 tempVTabs.push_back(tab);
1036 BMessage areaArchive;
1037 for (int32 i = 0; archive.FindMessage("area", i, &areaArchive) == B_OK;
1038 i++) {
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)
1044 return B_ERROR;
1046 if (leftTab >= tempVTabs.size() || rightTab >= tempVTabs.size())
1047 return B_BAD_VALUE;
1048 if (topTab >= tempHTabs.size() || bottomTab >= tempHTabs.size())
1049 return B_BAD_VALUE;
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
1057 uint64 windowId;
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);
1062 if (!window)
1063 continue;
1065 if (prevWindow == NULL) {
1066 if (!group->AddWindow(window, left, top, right, bottom))
1067 continue;
1068 prevWindow = window;
1069 } else {
1070 if (!prevWindow->StackWindow(window))
1071 continue;
1072 prevWindow = window;
1076 return B_OK;
1080 status_t
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);
1105 return B_OK;
1109 BReference<Tab>
1110 SATGroup::_AddHorizontalTab(float position)
1112 if (fLinearSpec == NULL)
1113 return NULL;
1114 Variable* variable = fLinearSpec->AddVariable();
1115 if (variable == NULL)
1116 return NULL;
1118 Tab* tab = new (std::nothrow)Tab(this, variable, Tab::kHorizontal);
1119 if (tab == NULL)
1120 return NULL;
1121 BReference<Tab> tabRef(tab, true);
1123 if (!fHorizontalTabs.AddItem(tab))
1124 return NULL;
1126 fHorizontalTabsSorted = false;
1127 tabRef->SetPosition(position);
1128 return tabRef;
1132 BReference<Tab>
1133 SATGroup::_AddVerticalTab(float position)
1135 if (fLinearSpec == NULL)
1136 return NULL;
1137 Variable* variable = fLinearSpec->AddVariable();
1138 if (variable == NULL)
1139 return NULL;
1141 Tab* tab = new (std::nothrow)Tab(this, variable, Tab::kVertical);
1142 if (tab == NULL)
1143 return NULL;
1144 BReference<Tab> tabRef(tab, true);
1146 if (!fVerticalTabs.AddItem(tab))
1147 return NULL;
1149 fVerticalTabsSorted = false;
1150 tabRef->SetPosition(position);
1151 return tabRef;
1155 bool
1156 SATGroup::_RemoveHorizontalTab(Tab* tab)
1158 if (!fHorizontalTabs.RemoveItem(tab))
1159 return false;
1160 fHorizontalTabsSorted = false;
1161 // don't delete the tab it is reference counted
1162 return true;
1166 bool
1167 SATGroup::_RemoveVerticalTab(Tab* tab)
1169 if (!fVerticalTabs.RemoveItem(tab))
1170 return false;
1171 fVerticalTabsSorted = false;
1172 // don't delete the tab it is reference counted
1173 return true;
1177 Tab*
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);
1184 return NULL;
1188 void
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)
1193 return;
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);
1211 else {
1212 _EnsureGroupIsOnScreen(this);
1213 ownGroupProcessed = true;
1216 newGroup.MakeEmpty();
1221 void
1222 SATGroup::_FillNeighbourList(WindowAreaList& neighbourWindows,
1223 WindowArea* area)
1225 _LeftNeighbours(neighbourWindows, area);
1226 _RightNeighbours(neighbourWindows, area);
1227 _TopNeighbours(neighbourWindows, area);
1228 _BottomNeighbours(neighbourWindows, area);
1232 void
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)
1243 continue;
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);
1252 if (pos2 > endPos)
1253 break;
1258 void
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)
1269 continue;
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);
1278 if (pos2 > endPos)
1279 break;
1284 void
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)
1295 continue;
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);
1304 if (pos2 > endPos)
1305 break;
1310 void
1311 SATGroup::_BottomNeighbours(WindowAreaList& neighbourWindows,
1312 WindowArea* parent)
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)
1322 continue;
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);
1331 if (pos2 > endPos)
1332 break;
1337 bool
1338 SATGroup::_FindConnectedGroup(WindowAreaList& seedList, WindowArea* removedArea,
1339 WindowAreaList& newGroup)
1341 if (seedList.CountItems() == 0)
1342 return false;
1344 WindowArea* area = seedList.RemoveItemAt(0);
1345 newGroup.AddItem(area);
1347 _FollowSeed(area, removedArea, seedList, newGroup);
1348 return true;
1352 void
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);
1364 } else {
1365 // don't _FollowSeed of invalid areas
1366 neighbours.RemoveItemAt(i);
1367 i--;
1371 for (int i = 0; i < neighbours.CountItems(); i++)
1372 _FollowSeed(neighbours.ItemAt(i), veto, seedList, newGroup);
1376 void
1377 SATGroup::_SpawnNewGroup(const WindowAreaList& newGroup)
1379 STRACE_SAT("SATGroup::_SpawnNewGroup\n");
1380 SATGroup* group = new (std::nothrow)SATGroup;
1381 if (group == NULL)
1382 return;
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;
1397 void
1398 SATGroup::_EnsureGroupIsOnScreen(SATGroup* group)
1400 STRACE_SAT("SATGroup::_EnsureGroupIsOnScreen\n");
1401 if (group == NULL || group->CountItems() < 1)
1402 return;
1404 SATWindow* window = group->WindowAt(0);
1405 Desktop* desktop = window->GetWindow()->Desktop();
1406 if (desktop == NULL)
1407 return;
1409 const float kBigDistance = 1E+10;
1411 float minLeftDistance = kBigDistance;
1412 BRect leftRect;
1413 float minTopDistance = kBigDistance;
1414 BRect topRect;
1415 float minRightDistance = kBigDistance;
1416 BRect rightRect;
1417 float minBottomDistance = kBigDistance;
1418 BRect bottomRect;
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))
1428 return;
1430 if (frame.right < screen.left + kMinOverlap) {
1431 float dist = fabs(screen.left - frame.right);
1432 if (dist < minLeftDistance) {
1433 minLeftDistance = dist;
1434 leftRect = frame;
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;
1442 bottomRect = frame;
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;
1450 rightRect = frame;
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;
1458 topRect = frame;
1459 } else if (dist == minTopDistance)
1460 topRect = topRect | frame;
1464 BPoint offset;
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.)
1480 return;
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();
1489 void
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;
1499 void
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;