1 /*
2 * Copyright 2010-2014 Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
4 *
5 * Authors:
6 * John Scipione, jscipione@gmail.com
7 * Clemens Zeidler, haiku@clemens-zeidler.de
8 */
9
10
11 #include "SATGroup.h"
12
13 #include <vector>
14
15 #include <Debug.h>
16 #include <Message.h>
17
18 #include "Desktop.h"
19
20 #include "SATWindow.h"
21 #include "StackAndTile.h"
22 #include "Window.h"
23
24
25 using namespace std;
26 using namespace LinearProgramming;
27
28
29 const float kExtentPenalty = 1;
30 const float kHighPenalty = 100;
31 const float kInequalityPenalty = 10000;
32
33
WindowArea(Crossing * leftTop,Crossing * rightTop,Crossing * leftBottom,Crossing * rightBottom)34 WindowArea::WindowArea(Crossing* leftTop, Crossing* rightTop,
35 Crossing* leftBottom, Crossing* rightBottom)
36 :
37 fGroup(NULL),
38
39 fLeftTopCrossing(leftTop),
40 fRightTopCrossing(rightTop),
41 fLeftBottomCrossing(leftBottom),
42 fRightBottomCrossing(rightBottom),
43
44 fMinWidthConstraint(NULL),
45 fMinHeightConstraint(NULL),
46 fMaxWidthConstraint(NULL),
47 fMaxHeightConstraint(NULL),
48 fWidthConstraint(NULL),
49 fHeightConstraint(NULL)
50 {
51 }
52
53
~WindowArea()54 WindowArea::~WindowArea()
55 {
56 if (fGroup)
57 fGroup->WindowAreaRemoved(this);
58
59 _CleanupCorners();
60 fGroup->fWindowAreaList.RemoveItem(this);
61
62 _UninitConstraints();
63 }
64
65
66 bool
Init(SATGroup * group)67 WindowArea::Init(SATGroup* group)
68 {
69 _UninitConstraints();
70
71 if (group == NULL || group->fWindowAreaList.AddItem(this) == false)
72 return false;
73
74 fGroup = group;
75
76 LinearSpec* linearSpec = fGroup->GetLinearSpec();
77
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);
82
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);
87
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);
95
96 if (!fMinWidthConstraint || !fMinHeightConstraint || !fWidthConstraint
97 || !fHeightConstraint || !fMaxWidthConstraint
98 || !fMaxHeightConstraint)
99 return false;
100
101 return true;
102 }
103
104
105 void
DoGroupLayout()106 WindowArea::DoGroupLayout()
107 {
108 SATWindow* parentWindow = fWindowLayerOrder.ItemAt(0);
109 if (parentWindow == NULL)
110 return;
111
112 BRect frame = parentWindow->CompleteWindowFrame();
113 // Make it also work for solver which don't support negative variables
114 frame.OffsetBy(kMakePositiveOffset, kMakePositiveOffset);
115
116 // adjust window size soft constraints
117 fWidthConstraint->SetRightSide(frame.Width());
118 fHeightConstraint->SetRightSide(frame.Height());
119
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);
125
126 // give soft constraints a high penalty
127 fWidthConstraint->SetPenaltyNeg(kHighPenalty);
128 fWidthConstraint->SetPenaltyPos(kHighPenalty);
129 fHeightConstraint->SetPenaltyNeg(kHighPenalty);
130 fHeightConstraint->SetPenaltyPos(kHighPenalty);
131
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;
139 }
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);
145 }
146 break;
147 }
148 }
149
150 // set penalties back to normal
151 fWidthConstraint->SetPenaltyNeg(kExtentPenalty);
152 fWidthConstraint->SetPenaltyPos(kExtentPenalty);
153 fHeightConstraint->SetPenaltyNeg(kExtentPenalty);
154 fHeightConstraint->SetPenaltyPos(kExtentPenalty);
155
156 linearSpec->RemoveConstraint(leftConstraint);
157 linearSpec->RemoveConstraint(topConstraint);
158 }
159
160
161 void
UpdateSizeLimits()162 WindowArea::UpdateSizeLimits()
163 {
164 _UpdateConstraintValues();
165 }
166
167
168 void
UpdateSizeConstaints(const BRect & frame)169 WindowArea::UpdateSizeConstaints(const BRect& frame)
170 {
171 // adjust window size soft constraints
172 fWidthConstraint->SetRightSide(frame.Width());
173 fHeightConstraint->SetRightSide(frame.Height());
174 }
175
176
177 bool
MoveWindowToPosition(SATWindow * window,int32 index)178 WindowArea::MoveWindowToPosition(SATWindow* window, int32 index)
179 {
180 int32 oldIndex = fWindowList.IndexOf(window);
181 ASSERT(oldIndex != index);
182 return fWindowList.MoveItem(oldIndex, index);
183 }
184
185
186 SATWindow*
TopWindow()187 WindowArea::TopWindow()
188 {
189 return fWindowLayerOrder.ItemAt(fWindowLayerOrder.CountItems() - 1);
190 }
191
192
193 void
_UpdateConstraintValues()194 WindowArea::_UpdateConstraintValues()
195 {
196 SATWindow* topWindow = TopWindow();
197 if (topWindow == NULL)
198 return;
199
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;
218 }
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;
229
230 topWindow->AddDecorator(&minWidth, &maxWidth, &minHeight, &maxHeight);
231 fMinWidthConstraint->SetRightSide(minWidth);
232 fMinHeightConstraint->SetRightSide(minHeight);
233
234 fMaxWidthConstraint->SetRightSide(maxWidth);
235 fMaxHeightConstraint->SetRightSide(maxHeight);
236
237 BRect frame = topWindow->CompleteWindowFrame();
238 fWidthConstraint->SetRightSide(frame.Width());
239 fHeightConstraint->SetRightSide(frame.Height());
240 }
241
242
243 bool
_AddWindow(SATWindow * window,SATWindow * after)244 WindowArea::_AddWindow(SATWindow* window, SATWindow* after)
245 {
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;
252
253 AcquireReference();
254
255 if (fWindowList.CountItems() <= 1)
256 _InitCorners();
257
258 fWindowLayerOrder.AddItem(window);
259
260 _UpdateConstraintValues();
261 return true;
262 }
263
264
265 bool
_RemoveWindow(SATWindow * window)266 WindowArea::_RemoveWindow(SATWindow* window)
267 {
268 if (!fWindowList.RemoveItem(window))
269 return false;
270
271 fWindowLayerOrder.RemoveItem(window);
272 _UpdateConstraintValues();
273
274 window->RemovedFromArea(this);
275 ReleaseReference();
276 return true;
277 }
278
279
280 Tab*
LeftTab()281 WindowArea::LeftTab()
282 {
283 return fLeftTopCrossing->VerticalTab();
284 }
285
286
287 Tab*
RightTab()288 WindowArea::RightTab()
289 {
290 return fRightBottomCrossing->VerticalTab();
291 }
292
293
294 Tab*
TopTab()295 WindowArea::TopTab()
296 {
297 return fLeftTopCrossing->HorizontalTab();
298 }
299
300
301 Tab*
BottomTab()302 WindowArea::BottomTab()
303 {
304 return fRightBottomCrossing->HorizontalTab();
305 }
306
307
308 BRect
Frame()309 WindowArea::Frame()
310 {
311 return BRect(fLeftTopCrossing->VerticalTab()->Position(),
312 fLeftTopCrossing->HorizontalTab()->Position(),
313 fRightBottomCrossing->VerticalTab()->Position(),
314 fRightBottomCrossing->HorizontalTab()->Position());
315 }
316
317
318 bool
PropagateToGroup(SATGroup * group)319 WindowArea::PropagateToGroup(SATGroup* group)
320 {
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);
329
330 if (!newLeftTop || !newRightTop || !newLeftBottom || !newRightBottom)
331 return false;
332
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;
338
339 fLeftTopCrossing = newLeftTop;
340 fRightTopCrossing = newRightTop;
341 fLeftBottomCrossing = newLeftBottom;
342 fRightBottomCrossing = newRightBottom;
343
344 _InitCorners();
345
346 BReference<SATGroup> oldGroup = fGroup;
347 // manage constraints
348 if (Init(group) == false)
349 return false;
350
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;
359 }
360 }
361
362 _UpdateConstraintValues();
363
364 return true;
365 }
366
367
368 bool
MoveToTopLayer(SATWindow * window)369 WindowArea::MoveToTopLayer(SATWindow* window)
370 {
371 if (!fWindowLayerOrder.RemoveItem(window))
372 return false;
373 return fWindowLayerOrder.AddItem(window);
374 }
375
376
377 void
_UninitConstraints()378 WindowArea::_UninitConstraints()
379 {
380 if (fGroup != NULL) {
381 LinearSpec* linearSpec = fGroup->GetLinearSpec();
382
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);
390 }
391 }
392
393 fMinWidthConstraint = NULL;
394 fMinHeightConstraint = NULL;
395 fMaxWidthConstraint = NULL;
396 fMaxHeightConstraint = NULL;
397 fWidthConstraint = NULL;
398 fHeightConstraint = NULL;
399 }
400
401
402 BReference<Crossing>
_CrossingByPosition(Crossing * crossing,SATGroup * group)403 WindowArea::_CrossingByPosition(Crossing* crossing, SATGroup* group)
404 {
405 BReference<Crossing> crossRef = NULL;
406
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;
413
414 Tab* oldVTab = crossing->VerticalTab();
415 crossRef = hTab->FindCrossing(oldVTab->Position());
416 if (crossRef)
417 return crossRef;
418
419 BReference<Tab> vTab = group->FindVerticalTab(oldVTab->Position());
420 if (!vTab)
421 vTab = group->_AddVerticalTab(oldVTab->Position());
422 if (!vTab)
423 return crossRef;
424
425 return hTab->AddCrossing(vTab);
426 }
427
428
429 void
_InitCorners()430 WindowArea::_InitCorners()
431 {
432 _SetToWindowCorner(fLeftTopCrossing->RightBottomCorner());
433 _SetToNeighbourCorner(fLeftTopCrossing->LeftBottomCorner());
434 _SetToNeighbourCorner(fLeftTopCrossing->RightTopCorner());
435
436 _SetToWindowCorner(fRightTopCrossing->LeftBottomCorner());
437 _SetToNeighbourCorner(fRightTopCrossing->LeftTopCorner());
438 _SetToNeighbourCorner(fRightTopCrossing->RightBottomCorner());
439
440 _SetToWindowCorner(fLeftBottomCrossing->RightTopCorner());
441 _SetToNeighbourCorner(fLeftBottomCrossing->LeftTopCorner());
442 _SetToNeighbourCorner(fLeftBottomCrossing->RightBottomCorner());
443
444 _SetToWindowCorner(fRightBottomCrossing->LeftTopCorner());
445 _SetToNeighbourCorner(fRightBottomCrossing->LeftBottomCorner());
446 _SetToNeighbourCorner(fRightBottomCrossing->RightTopCorner());
447 }
448
449
450 void
_CleanupCorners()451 WindowArea::_CleanupCorners()
452 {
453 _UnsetWindowCorner(fLeftTopCrossing->RightBottomCorner());
454 _UnsetNeighbourCorner(fLeftTopCrossing->LeftBottomCorner(),
455 fLeftBottomCrossing->LeftTopCorner());
456 _UnsetNeighbourCorner(fLeftTopCrossing->RightTopCorner(),
457 fLeftBottomCrossing->LeftTopCorner());
458
459 _UnsetWindowCorner(fRightTopCrossing->LeftBottomCorner());
460 _UnsetNeighbourCorner(fRightTopCrossing->LeftTopCorner(),
461 fLeftBottomCrossing->RightTopCorner());
462 _UnsetNeighbourCorner(fRightTopCrossing->RightBottomCorner(),
463 fLeftBottomCrossing->RightTopCorner());
464
465 _UnsetWindowCorner(fLeftBottomCrossing->RightTopCorner());
466 _UnsetNeighbourCorner(fLeftBottomCrossing->LeftTopCorner(),
467 fLeftBottomCrossing->LeftBottomCorner());
468 _UnsetNeighbourCorner(fLeftBottomCrossing->RightBottomCorner(),
469 fLeftBottomCrossing->LeftBottomCorner());
470
471 _UnsetWindowCorner(fRightBottomCrossing->LeftTopCorner());
472 _UnsetNeighbourCorner(fRightBottomCrossing->LeftBottomCorner(),
473 fRightBottomCrossing->RightBottomCorner());
474 _UnsetNeighbourCorner(fRightBottomCrossing->RightTopCorner(),
475 fRightBottomCrossing->RightBottomCorner());
476 }
477
478
479 void
_SetToWindowCorner(Corner * corner)480 WindowArea::_SetToWindowCorner(Corner* corner)
481 {
482 corner->status = Corner::kUsed;
483 corner->windowArea = this;
484 }
485
486
487 void
_SetToNeighbourCorner(Corner * neighbour)488 WindowArea::_SetToNeighbourCorner(Corner* neighbour)
489 {
490 if (neighbour->status == Corner::kNotDockable)
491 neighbour->status = Corner::kFree;
492 }
493
494
495 void
_UnsetWindowCorner(Corner * corner)496 WindowArea::_UnsetWindowCorner(Corner* corner)
497 {
498 corner->status = Corner::kFree;
499 corner->windowArea = NULL;
500 }
501
502
503 void
_UnsetNeighbourCorner(Corner * neighbour,Corner * opponent)504 WindowArea::_UnsetNeighbourCorner(Corner* neighbour, Corner* opponent)
505 {
506 if (neighbour->status == Corner::kFree && opponent->status != Corner::kUsed)
507 neighbour->status = Corner::kNotDockable;
508 }
509
510
511 void
_MoveToSAT(SATWindow * triggerWindow)512 WindowArea::_MoveToSAT(SATWindow* triggerWindow)
513 {
514 SATWindow* topWindow = TopWindow();
515 // if there is no window in the group we are done
516 if (topWindow == NULL)
517 return;
518
519 BRect frameSAT(LeftVar()->Value() - kMakePositiveOffset,
520 TopVar()->Value() - kMakePositiveOffset,
521 RightVar()->Value() - kMakePositiveOffset,
522 BottomVar()->Value() - kMakePositiveOffset);
523 topWindow->AdjustSizeLimits(frameSAT);
524
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);
531
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);
538
539 UpdateSizeConstaints(frameSAT);
540 }
541
542
Corner()543 Corner::Corner()
544 :
545 status(kNotDockable),
546 windowArea(NULL)
547 {
548
549 }
550
551
552 void
Trace() const553 Corner::Trace() const
554 {
555 switch (status) {
556 case kFree:
557 debug_printf("free corner\n");
558 break;
559
560 case kUsed:
561 {
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());
566 }
567 break;
568 }
569
570 case kNotDockable:
571 debug_printf("not dockable\n");
572 break;
573 };
574 }
575
576
Crossing(Tab * vertical,Tab * horizontal)577 Crossing::Crossing(Tab* vertical, Tab* horizontal)
578 :
579 fVerticalTab(vertical),
580 fHorizontalTab(horizontal)
581 {
582 }
583
584
~Crossing()585 Crossing::~Crossing()
586 {
587 fVerticalTab->RemoveCrossing(this);
588 fHorizontalTab->RemoveCrossing(this);
589 }
590
591
592 Corner*
GetCorner(Corner::position_t corner) const593 Crossing::GetCorner(Corner::position_t corner) const
594 {
595 return &const_cast<Corner*>(fCorners)[corner];
596 }
597
598
599 Corner*
GetOppositeCorner(Corner::position_t corner) const600 Crossing::GetOppositeCorner(Corner::position_t corner) const
601 {
602 return &const_cast<Corner*>(fCorners)[3 - corner];
603 }
604
605
606 Tab*
VerticalTab() const607 Crossing::VerticalTab() const
608 {
609 return fVerticalTab;
610 }
611
612
613 Tab*
HorizontalTab() const614 Crossing::HorizontalTab() const
615 {
616 return fHorizontalTab;
617 }
618
619
620 void
Trace() const621 Crossing::Trace() const
622 {
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();
631 }
632
633
Tab(SATGroup * group,Variable * variable,orientation_t orientation)634 Tab::Tab(SATGroup* group, Variable* variable, orientation_t orientation)
635 :
636 fGroup(group),
637 fVariable(variable),
638 fOrientation(orientation)
639 {
640
641 }
642
643
~Tab()644 Tab::~Tab()
645 {
646 if (fOrientation == kVertical)
647 fGroup->_RemoveVerticalTab(this);
648 else
649 fGroup->_RemoveHorizontalTab(this);
650 }
651
652
653 float
Position() const654 Tab::Position() const
655 {
656 return (float)fVariable->Value() - kMakePositiveOffset;
657 }
658
659
660 void
SetPosition(float position)661 Tab::SetPosition(float position)
662 {
663 fVariable->SetValue(position + kMakePositiveOffset);
664 }
665
666
667 Tab::orientation_t
Orientation() const668 Tab::Orientation() const
669 {
670 return fOrientation;
671 }
672
673
674 Constraint*
Connect(Variable * variable)675 Tab::Connect(Variable* variable)
676 {
677 return fVariable->IsEqual(variable);
678 }
679
680
681 BReference<Crossing>
AddCrossing(Tab * tab)682 Tab::AddCrossing(Tab* tab)
683 {
684 if (tab->Orientation() == fOrientation)
685 return NULL;
686
687 Tab* vTab = (fOrientation == kVertical) ? this : tab;
688 Tab* hTab = (fOrientation == kHorizontal) ? this : tab;
689
690 Crossing* crossing = new (std::nothrow)Crossing(vTab, hTab);
691 if (!crossing)
692 return NULL;
693
694 if (!fCrossingList.AddItem(crossing)) {
695 return NULL;
696 }
697 if (!tab->fCrossingList.AddItem(crossing)) {
698 fCrossingList.RemoveItem(crossing);
699 return NULL;
700 }
701
702 BReference<Crossing> crossingRef(crossing, true);
703 return crossingRef;
704 }
705
706
707 bool
RemoveCrossing(Crossing * crossing)708 Tab::RemoveCrossing(Crossing* crossing)
709 {
710 Tab* vTab = crossing->VerticalTab();
711 Tab* hTab = crossing->HorizontalTab();
712
713 if (vTab != this && hTab != this)
714 return false;
715 fCrossingList.RemoveItem(crossing);
716
717 return true;
718 }
719
720
721 int32
FindCrossingIndex(Tab * tab)722 Tab::FindCrossingIndex(Tab* tab)
723 {
724 if (fOrientation == kVertical) {
725 for (int32 i = 0; i < fCrossingList.CountItems(); i++) {
726 if (fCrossingList.ItemAt(i)->HorizontalTab() == tab)
727 return i;
728 }
729 } else {
730 for (int32 i = 0; i < fCrossingList.CountItems(); i++) {
731 if (fCrossingList.ItemAt(i)->VerticalTab() == tab)
732 return i;
733 }
734 }
735 return -1;
736 }
737
738
739 int32
FindCrossingIndex(float pos)740 Tab::FindCrossingIndex(float pos)
741 {
742 if (fOrientation == kVertical) {
743 for (int32 i = 0; i < fCrossingList.CountItems(); i++) {
744 if (fabs(fCrossingList.ItemAt(i)->HorizontalTab()->Position() - pos)
745 < 0.0001)
746 return i;
747 }
748 } else {
749 for (int32 i = 0; i < fCrossingList.CountItems(); i++) {
750 if (fabs(fCrossingList.ItemAt(i)->VerticalTab()->Position() - pos)
751 < 0.0001)
752 return i;
753 }
754 }
755 return -1;
756 }
757
758
759 Crossing*
FindCrossing(Tab * tab)760 Tab::FindCrossing(Tab* tab)
761 {
762 return fCrossingList.ItemAt(FindCrossingIndex(tab));
763 }
764
765
766 Crossing*
FindCrossing(float tabPosition)767 Tab::FindCrossing(float tabPosition)
768 {
769 return fCrossingList.ItemAt(FindCrossingIndex(tabPosition));
770 }
771
772
773 const CrossingList*
GetCrossingList() const774 Tab::GetCrossingList() const
775 {
776 return &fCrossingList;
777 }
778
779
780 int
CompareFunction(const Tab * tab1,const Tab * tab2)781 Tab::CompareFunction(const Tab* tab1, const Tab* tab2)
782 {
783 if (tab1->Position() < tab2->Position())
784 return -1;
785
786 return 1;
787 }
788
789
SATGroup()790 SATGroup::SATGroup()
791 :
792 fLinearSpec(new(std::nothrow) LinearSpec()),
793 fHorizontalTabsSorted(false),
794 fVerticalTabsSorted(false),
795 fActiveWindow(NULL)
796 {
797 }
798
799
~SATGroup()800 SATGroup::~SATGroup()
801 {
802 // Should be empty
803 if (fSATWindowList.CountItems() > 0)
804 debugger("Deleting a SATGroup which is not empty");
805 //while (fSATWindowList.CountItems() > 0)
806 // RemoveWindow(fSATWindowList.ItemAt(0));
807
808 fLinearSpec->ReleaseReference();
809 }
810
811
812 bool
AddWindow(SATWindow * window,Tab * left,Tab * top,Tab * right,Tab * bottom)813 SATGroup::AddWindow(SATWindow* window, Tab* left, Tab* top, Tab* right,
814 Tab* bottom)
815 {
816 STRACE_SAT("SATGroup::AddWindow\n");
817
818 // first check if we have to create tabs and missing corners.
819 BReference<Tab> leftRef, rightRef, topRef, bottomRef;
820 BReference<Crossing> leftTopRef, rightTopRef, leftBottomRef, rightBottomRef;
821
822 if (left != NULL && top != NULL)
823 leftTopRef = left->FindCrossing(top);
824 if (right != NULL && top != NULL)
825 rightTopRef = right->FindCrossing(top);
826 if (left != NULL && bottom != NULL)
827 leftBottomRef = left->FindCrossing(bottom);
828 if (right != NULL && bottom != NULL)
829 rightBottomRef = right->FindCrossing(bottom);
830
831 if (left == NULL) {
832 leftRef = _AddVerticalTab();
833 left = leftRef.Get();
834 }
835 if (top == NULL) {
836 topRef = _AddHorizontalTab();
837 top = topRef.Get();
838 }
839 if (right == NULL) {
840 rightRef = _AddVerticalTab();
841 right = rightRef.Get();
842 }
843 if (bottom == NULL) {
844 bottomRef = _AddHorizontalTab();
845 bottom = bottomRef.Get();
846 }
847 if (left == NULL || top == NULL || right == NULL || bottom == NULL)
848 return false;
849
850 if (leftTopRef == NULL) {
851 leftTopRef = left->AddCrossing(top);
852 if (leftTopRef == NULL)
853 return false;
854 }
855 if (!rightTopRef) {
856 rightTopRef = right->AddCrossing(top);
857 if (!rightTopRef)
858 return false;
859 }
860 if (!leftBottomRef) {
861 leftBottomRef = left->AddCrossing(bottom);
862 if (!leftBottomRef)
863 return false;
864 }
865 if (!rightBottomRef) {
866 rightBottomRef = right->AddCrossing(bottom);
867 if (!rightBottomRef)
868 return false;
869 }
870
871 WindowArea* area = new(std::nothrow) WindowArea(leftTopRef, rightTopRef,
872 leftBottomRef, rightBottomRef);
873 if (area == NULL)
874 return false;
875 // the area register itself in our area list
876 if (area->Init(this) == false) {
877 delete area;
878 return false;
879 }
880 // delete the area if AddWindow failed / release our reference on it
881 BReference<WindowArea> areaRef(area, true);
882
883 return AddWindow(window, area);
884 }
885
886
887 bool
AddWindow(SATWindow * window,WindowArea * area,SATWindow * after)888 SATGroup::AddWindow(SATWindow* window, WindowArea* area, SATWindow* after)
889 {
890 if (!area->_AddWindow(window, after))
891 return false;
892
893 if (!fSATWindowList.AddItem(window)) {
894 area->_RemoveWindow(window);
895 return false;
896 }
897
898 if (!window->AddedToGroup(this, area)) {
899 area->_RemoveWindow(window);
900 fSATWindowList.RemoveItem(window);
901 return false;
902 }
903
904 return true;
905 }
906
907
908 bool
RemoveWindow(SATWindow * window,bool stayBelowMouse)909 SATGroup::RemoveWindow(SATWindow* window, bool stayBelowMouse)
910 {
911 if (!fSATWindowList.RemoveItem(window))
912 return false;
913
914 // We need the area a little bit longer because the area could hold the
915 // last reference to the group.
916 BReference<WindowArea> area = window->GetWindowArea();
917 if (area.IsSet())
918 area->_RemoveWindow(window);
919
920 window->RemovedFromGroup(this, stayBelowMouse);
921
922 if (CountItems() >= 2)
923 WindowAt(0)->DoGroupLayout();
924
925 return true;
926 }
927
928
929 int32
CountItems()930 SATGroup::CountItems()
931 {
932 return fSATWindowList.CountItems();
933 }
934
935
936 SATWindow*
WindowAt(int32 index)937 SATGroup::WindowAt(int32 index)
938 {
939 return fSATWindowList.ItemAt(index);
940 }
941
942
943 SATWindow*
ActiveWindow() const944 SATGroup::ActiveWindow() const
945 {
946 return fActiveWindow;
947 }
948
949
950 void
SetActiveWindow(SATWindow * window)951 SATGroup::SetActiveWindow(SATWindow* window)
952 {
953 fActiveWindow = window;
954 }
955
956
957 const TabList*
HorizontalTabs()958 SATGroup::HorizontalTabs()
959 {
960 if (!fHorizontalTabsSorted) {
961 fHorizontalTabs.SortItems(Tab::CompareFunction);
962 fHorizontalTabsSorted = true;
963 }
964 return &fHorizontalTabs;
965 }
966
967
968 const TabList*
VerticalTabs()969 SATGroup::VerticalTabs()
970 {
971 if (!fVerticalTabsSorted) {
972 fVerticalTabs.SortItems(Tab::CompareFunction);
973 fVerticalTabsSorted = true;
974 }
975 return &fVerticalTabs;
976 }
977
978
979 Tab*
FindHorizontalTab(float position)980 SATGroup::FindHorizontalTab(float position)
981 {
982 return _FindTab(fHorizontalTabs, position);
983 }
984
985
986 Tab*
FindVerticalTab(float position)987 SATGroup::FindVerticalTab(float position)
988 {
989 return _FindTab(fVerticalTabs, position);
990 }
991
992
993 void
WindowAreaRemoved(WindowArea * area)994 SATGroup::WindowAreaRemoved(WindowArea* area)
995 {
996 _SplitGroupIfNecessary(area);
997 }
998
999
1000 status_t
RestoreGroup(const BMessage & archive,StackAndTile * sat)1001 SATGroup::RestoreGroup(const BMessage& archive, StackAndTile* sat)
1002 {
1003 // create new group
1004 SATGroup* group = new (std::nothrow)SATGroup;
1005 if (group == NULL)
1006 return B_NO_MEMORY;
1007 BReference<SATGroup> groupRef;
1008 groupRef.SetTo(group, true);
1009
1010 int32 nHTabs, nVTabs;
1011 status_t status;
1012 status = archive.FindInt32("htab_count", &nHTabs);
1013 if (status != B_OK)
1014 return status;
1015 status = archive.FindInt32("vtab_count", &nVTabs);
1016 if (status != B_OK)
1017 return status;
1018
1019 vector<BReference<Tab> > tempHTabs;
1020 for (int i = 0; i < nHTabs; i++) {
1021 BReference<Tab> tab = group->_AddHorizontalTab();
1022 if (!tab)
1023 return B_NO_MEMORY;
1024 tempHTabs.push_back(tab);
1025 }
1026 vector<BReference<Tab> > tempVTabs;
1027 for (int i = 0; i < nVTabs; i++) {
1028 BReference<Tab> tab = group->_AddVerticalTab();
1029 if (!tab)
1030 return B_NO_MEMORY;
1031 tempVTabs.push_back(tab);
1032 }
1033
1034 BMessage areaArchive;
1035 for (int32 i = 0; archive.FindMessage("area", i, &areaArchive) == B_OK;
1036 i++) {
1037 uint32 leftTab, rightTab, topTab, bottomTab;
1038 if (areaArchive.FindInt32("left_tab", (int32*)&leftTab) != B_OK
1039 || areaArchive.FindInt32("right_tab", (int32*)&rightTab) != B_OK
1040 || areaArchive.FindInt32("top_tab", (int32*)&topTab) != B_OK
1041 || areaArchive.FindInt32("bottom_tab", (int32*)&bottomTab) != B_OK)
1042 return B_ERROR;
1043
1044 if (leftTab >= tempVTabs.size() || rightTab >= tempVTabs.size())
1045 return B_BAD_VALUE;
1046 if (topTab >= tempHTabs.size() || bottomTab >= tempHTabs.size())
1047 return B_BAD_VALUE;
1048
1049 Tab* left = tempVTabs[leftTab];
1050 Tab* right = tempVTabs[rightTab];
1051 Tab* top = tempHTabs[topTab];
1052 Tab* bottom = tempHTabs[bottomTab];
1053
1054 // adding windows to area
1055 uint64 windowId;
1056 SATWindow* prevWindow = NULL;
1057 for (int32 i = 0; areaArchive.FindInt64("window", i,
1058 (int64*)&windowId) == B_OK; i++) {
1059 SATWindow* window = sat->FindSATWindow(windowId);
1060 if (!window)
1061 continue;
1062
1063 if (prevWindow == NULL) {
1064 if (!group->AddWindow(window, left, top, right, bottom))
1065 continue;
1066 prevWindow = window;
1067 } else {
1068 if (!prevWindow->StackWindow(window))
1069 continue;
1070 prevWindow = window;
1071 }
1072 }
1073 }
1074 return B_OK;
1075 }
1076
1077
1078 status_t
ArchiveGroup(BMessage & archive)1079 SATGroup::ArchiveGroup(BMessage& archive)
1080 {
1081 archive.AddInt32("htab_count", fHorizontalTabs.CountItems());
1082 archive.AddInt32("vtab_count", fVerticalTabs.CountItems());
1083
1084 for (int i = 0; i < fWindowAreaList.CountItems(); i++) {
1085 WindowArea* area = fWindowAreaList.ItemAt(i);
1086 int32 leftTab = fVerticalTabs.IndexOf(area->LeftTab());
1087 int32 rightTab = fVerticalTabs.IndexOf(area->RightTab());
1088 int32 topTab = fHorizontalTabs.IndexOf(area->TopTab());
1089 int32 bottomTab = fHorizontalTabs.IndexOf(area->BottomTab());
1090
1091 BMessage areaMessage;
1092 areaMessage.AddInt32("left_tab", leftTab);
1093 areaMessage.AddInt32("right_tab", rightTab);
1094 areaMessage.AddInt32("top_tab", topTab);
1095 areaMessage.AddInt32("bottom_tab", bottomTab);
1096
1097 const SATWindowList& windowList = area->WindowList();
1098 for (int a = 0; a < windowList.CountItems(); a++)
1099 areaMessage.AddInt64("window", windowList.ItemAt(a)->Id());
1100
1101 archive.AddMessage("area", &areaMessage);
1102 }
1103 return B_OK;
1104 }
1105
1106
1107 BReference<Tab>
_AddHorizontalTab(float position)1108 SATGroup::_AddHorizontalTab(float position)
1109 {
1110 if (fLinearSpec == NULL)
1111 return NULL;
1112 Variable* variable = fLinearSpec->AddVariable();
1113 if (variable == NULL)
1114 return NULL;
1115
1116 Tab* tab = new (std::nothrow)Tab(this, variable, Tab::kHorizontal);
1117 if (tab == NULL)
1118 return NULL;
1119 BReference<Tab> tabRef(tab, true);
1120
1121 if (!fHorizontalTabs.AddItem(tab))
1122 return NULL;
1123
1124 fHorizontalTabsSorted = false;
1125 tabRef->SetPosition(position);
1126 return tabRef;
1127 }
1128
1129
1130 BReference<Tab>
_AddVerticalTab(float position)1131 SATGroup::_AddVerticalTab(float position)
1132 {
1133 if (fLinearSpec == NULL)
1134 return NULL;
1135 Variable* variable = fLinearSpec->AddVariable();
1136 if (variable == NULL)
1137 return NULL;
1138
1139 Tab* tab = new (std::nothrow)Tab(this, variable, Tab::kVertical);
1140 if (tab == NULL)
1141 return NULL;
1142 BReference<Tab> tabRef(tab, true);
1143
1144 if (!fVerticalTabs.AddItem(tab))
1145 return NULL;
1146
1147 fVerticalTabsSorted = false;
1148 tabRef->SetPosition(position);
1149 return tabRef;
1150 }
1151
1152
1153 bool
_RemoveHorizontalTab(Tab * tab)1154 SATGroup::_RemoveHorizontalTab(Tab* tab)
1155 {
1156 if (!fHorizontalTabs.RemoveItem(tab))
1157 return false;
1158 fHorizontalTabsSorted = false;
1159 // don't delete the tab it is reference counted
1160 return true;
1161 }
1162
1163
1164 bool
_RemoveVerticalTab(Tab * tab)1165 SATGroup::_RemoveVerticalTab(Tab* tab)
1166 {
1167 if (!fVerticalTabs.RemoveItem(tab))
1168 return false;
1169 fVerticalTabsSorted = false;
1170 // don't delete the tab it is reference counted
1171 return true;
1172 }
1173
1174
1175 Tab*
_FindTab(const TabList & list,float position)1176 SATGroup::_FindTab(const TabList& list, float position)
1177 {
1178 for (int i = 0; i < list.CountItems(); i++)
1179 if (fabs(list.ItemAt(i)->Position() - position) < 0.00001)
1180 return list.ItemAt(i);
1181
1182 return NULL;
1183 }
1184
1185
1186 void
_SplitGroupIfNecessary(WindowArea * removedArea)1187 SATGroup::_SplitGroupIfNecessary(WindowArea* removedArea)
1188 {
1189 // if there are windows stacked in the area we don't need to split
1190 if (removedArea == NULL || removedArea->WindowList().CountItems() > 1)
1191 return;
1192
1193 WindowAreaList neighbourWindows;
1194
1195 _FillNeighbourList(neighbourWindows, removedArea);
1196
1197 bool ownGroupProcessed = false;
1198 WindowAreaList newGroup;
1199 while (_FindConnectedGroup(neighbourWindows, removedArea, newGroup)) {
1200 STRACE_SAT("Connected group found; %i window(s)\n",
1201 (int)newGroup.CountItems());
1202 if (newGroup.CountItems() == 1
1203 && newGroup.ItemAt(0)->WindowList().CountItems() == 1) {
1204 SATWindow* window = newGroup.ItemAt(0)->WindowList().ItemAt(0);
1205 RemoveWindow(window);
1206 _EnsureGroupIsOnScreen(window->GetGroup());
1207 } else if (ownGroupProcessed)
1208 _SpawnNewGroup(newGroup);
1209 else {
1210 _EnsureGroupIsOnScreen(this);
1211 ownGroupProcessed = true;
1212 }
1213
1214 newGroup.MakeEmpty();
1215 }
1216 }
1217
1218
1219 void
_FillNeighbourList(WindowAreaList & neighbourWindows,WindowArea * area)1220 SATGroup::_FillNeighbourList(WindowAreaList& neighbourWindows,
1221 WindowArea* area)
1222 {
1223 _LeftNeighbours(neighbourWindows, area);
1224 _RightNeighbours(neighbourWindows, area);
1225 _TopNeighbours(neighbourWindows, area);
1226 _BottomNeighbours(neighbourWindows, area);
1227 }
1228
1229
1230 void
_LeftNeighbours(WindowAreaList & neighbourWindows,WindowArea * parent)1231 SATGroup::_LeftNeighbours(WindowAreaList& neighbourWindows, WindowArea* parent)
1232 {
1233 float startPos = parent->LeftTopCrossing()->HorizontalTab()->Position();
1234 float endPos = parent->LeftBottomCrossing()->HorizontalTab()->Position();
1235
1236 Tab* tab = parent->LeftTopCrossing()->VerticalTab();
1237 const CrossingList* crossingList = tab->GetCrossingList();
1238 for (int i = 0; i < crossingList->CountItems(); i++) {
1239 Corner* corner = crossingList->ItemAt(i)->LeftTopCorner();
1240 if (corner->status != Corner::kUsed)
1241 continue;
1242
1243 WindowArea* area = corner->windowArea;
1244 float pos1 = area->LeftTopCrossing()->HorizontalTab()->Position();
1245 float pos2 = area->LeftBottomCrossing()->HorizontalTab()->Position();
1246
1247 if (pos1 < endPos && pos2 > startPos)
1248 neighbourWindows.AddItem(area);
1249
1250 if (pos2 > endPos)
1251 break;
1252 }
1253 }
1254
1255
1256 void
_TopNeighbours(WindowAreaList & neighbourWindows,WindowArea * parent)1257 SATGroup::_TopNeighbours(WindowAreaList& neighbourWindows, WindowArea* parent)
1258 {
1259 float startPos = parent->LeftTopCrossing()->VerticalTab()->Position();
1260 float endPos = parent->RightTopCrossing()->VerticalTab()->Position();
1261
1262 Tab* tab = parent->LeftTopCrossing()->HorizontalTab();
1263 const CrossingList* crossingList = tab->GetCrossingList();
1264 for (int i = 0; i < crossingList->CountItems(); i++) {
1265 Corner* corner = crossingList->ItemAt(i)->LeftTopCorner();
1266 if (corner->status != Corner::kUsed)
1267 continue;
1268
1269 WindowArea* area = corner->windowArea;
1270 float pos1 = area->LeftTopCrossing()->VerticalTab()->Position();
1271 float pos2 = area->RightTopCrossing()->VerticalTab()->Position();
1272
1273 if (pos1 < endPos && pos2 > startPos)
1274 neighbourWindows.AddItem(area);
1275
1276 if (pos2 > endPos)
1277 break;
1278 }
1279 }
1280
1281
1282 void
_RightNeighbours(WindowAreaList & neighbourWindows,WindowArea * parent)1283 SATGroup::_RightNeighbours(WindowAreaList& neighbourWindows, WindowArea* parent)
1284 {
1285 float startPos = parent->RightTopCrossing()->HorizontalTab()->Position();
1286 float endPos = parent->RightBottomCrossing()->HorizontalTab()->Position();
1287
1288 Tab* tab = parent->RightTopCrossing()->VerticalTab();
1289 const CrossingList* crossingList = tab->GetCrossingList();
1290 for (int i = 0; i < crossingList->CountItems(); i++) {
1291 Corner* corner = crossingList->ItemAt(i)->RightTopCorner();
1292 if (corner->status != Corner::kUsed)
1293 continue;
1294
1295 WindowArea* area = corner->windowArea;
1296 float pos1 = area->RightTopCrossing()->HorizontalTab()->Position();
1297 float pos2 = area->RightBottomCrossing()->HorizontalTab()->Position();
1298
1299 if (pos1 < endPos && pos2 > startPos)
1300 neighbourWindows.AddItem(area);
1301
1302 if (pos2 > endPos)
1303 break;
1304 }
1305 }
1306
1307
1308 void
_BottomNeighbours(WindowAreaList & neighbourWindows,WindowArea * parent)1309 SATGroup::_BottomNeighbours(WindowAreaList& neighbourWindows,
1310 WindowArea* parent)
1311 {
1312 float startPos = parent->LeftBottomCrossing()->VerticalTab()->Position();
1313 float endPos = parent->RightBottomCrossing()->VerticalTab()->Position();
1314
1315 Tab* tab = parent->LeftBottomCrossing()->HorizontalTab();
1316 const CrossingList* crossingList = tab->GetCrossingList();
1317 for (int i = 0; i < crossingList->CountItems(); i++) {
1318 Corner* corner = crossingList->ItemAt(i)->LeftBottomCorner();
1319 if (corner->status != Corner::kUsed)
1320 continue;
1321
1322 WindowArea* area = corner->windowArea;
1323 float pos1 = area->LeftBottomCrossing()->VerticalTab()->Position();
1324 float pos2 = area->RightBottomCrossing()->VerticalTab()->Position();
1325
1326 if (pos1 < endPos && pos2 > startPos)
1327 neighbourWindows.AddItem(area);
1328
1329 if (pos2 > endPos)
1330 break;
1331 }
1332 }
1333
1334
1335 bool
_FindConnectedGroup(WindowAreaList & seedList,WindowArea * removedArea,WindowAreaList & newGroup)1336 SATGroup::_FindConnectedGroup(WindowAreaList& seedList, WindowArea* removedArea,
1337 WindowAreaList& newGroup)
1338 {
1339 if (seedList.CountItems() == 0)
1340 return false;
1341
1342 WindowArea* area = seedList.RemoveItemAt(0);
1343 newGroup.AddItem(area);
1344
1345 _FollowSeed(area, removedArea, seedList, newGroup);
1346 return true;
1347 }
1348
1349
1350 void
_FollowSeed(WindowArea * area,WindowArea * veto,WindowAreaList & seedList,WindowAreaList & newGroup)1351 SATGroup::_FollowSeed(WindowArea* area, WindowArea* veto,
1352 WindowAreaList& seedList, WindowAreaList& newGroup)
1353 {
1354 WindowAreaList neighbours;
1355 _FillNeighbourList(neighbours, area);
1356 for (int i = 0; i < neighbours.CountItems(); i++) {
1357 WindowArea* currentArea = neighbours.ItemAt(i);
1358 if (currentArea != veto && !newGroup.HasItem(currentArea)) {
1359 newGroup.AddItem(currentArea);
1360 // if we get a area from the seed list it is not a seed any more
1361 seedList.RemoveItem(currentArea);
1362 } else {
1363 // don't _FollowSeed of invalid areas
1364 neighbours.RemoveItemAt(i);
1365 i--;
1366 }
1367 }
1368
1369 for (int i = 0; i < neighbours.CountItems(); i++)
1370 _FollowSeed(neighbours.ItemAt(i), veto, seedList, newGroup);
1371 }
1372
1373
1374 void
_SpawnNewGroup(const WindowAreaList & newGroup)1375 SATGroup::_SpawnNewGroup(const WindowAreaList& newGroup)
1376 {
1377 STRACE_SAT("SATGroup::_SpawnNewGroup\n");
1378 SATGroup* group = new (std::nothrow)SATGroup;
1379 if (group == NULL)
1380 return;
1381 BReference<SATGroup> groupRef;
1382 groupRef.SetTo(group, true);
1383
1384 for (int i = 0; i < newGroup.CountItems(); i++)
1385 newGroup.ItemAt(i)->PropagateToGroup(group);
1386
1387 _EnsureGroupIsOnScreen(group);
1388 }
1389
1390
1391 const float kMinOverlap = 50;
1392 const float kMoveToScreen = 75;
1393
1394
1395 void
_EnsureGroupIsOnScreen(SATGroup * group)1396 SATGroup::_EnsureGroupIsOnScreen(SATGroup* group)
1397 {
1398 STRACE_SAT("SATGroup::_EnsureGroupIsOnScreen\n");
1399 if (group == NULL || group->CountItems() < 1)
1400 return;
1401
1402 SATWindow* window = group->WindowAt(0);
1403 Desktop* desktop = window->GetWindow()->Desktop();
1404 if (desktop == NULL)
1405 return;
1406
1407 const float kBigDistance = 1E+10;
1408
1409 float minLeftDistance = kBigDistance;
1410 BRect leftRect;
1411 float minTopDistance = kBigDistance;
1412 BRect topRect;
1413 float minRightDistance = kBigDistance;
1414 BRect rightRect;
1415 float minBottomDistance = kBigDistance;
1416 BRect bottomRect;
1417
1418 BRect screen = window->GetWindow()->Screen()->Frame();
1419 BRect reducedScreen = screen;
1420 reducedScreen.InsetBy(kMinOverlap, kMinOverlap);
1421
1422 for (int i = 0; i < group->CountItems(); i++) {
1423 SATWindow* window = group->WindowAt(i);
1424 BRect frame = window->CompleteWindowFrame();
1425 if (reducedScreen.Intersects(frame))
1426 return;
1427
1428 if (frame.right < screen.left + kMinOverlap) {
1429 float dist = fabs(screen.left - frame.right);
1430 if (dist < minLeftDistance) {
1431 minLeftDistance = dist;
1432 leftRect = frame;
1433 } else if (dist == minLeftDistance)
1434 leftRect = leftRect | frame;
1435 }
1436 if (frame.top > screen.bottom - kMinOverlap) {
1437 float dist = fabs(frame.top - screen.bottom);
1438 if (dist < minBottomDistance) {
1439 minBottomDistance = dist;
1440 bottomRect = frame;
1441 } else if (dist == minBottomDistance)
1442 bottomRect = bottomRect | frame;
1443 }
1444 if (frame.left > screen.right - kMinOverlap) {
1445 float dist = fabs(frame.left - screen.right);
1446 if (dist < minRightDistance) {
1447 minRightDistance = dist;
1448 rightRect = frame;
1449 } else if (dist == minRightDistance)
1450 rightRect = rightRect | frame;
1451 }
1452 if (frame.bottom < screen.top + kMinOverlap) {
1453 float dist = fabs(frame.bottom - screen.top);
1454 if (dist < minTopDistance) {
1455 minTopDistance = dist;
1456 topRect = frame;
1457 } else if (dist == minTopDistance)
1458 topRect = topRect | frame;
1459 }
1460 }
1461
1462 BPoint offset;
1463 if (minLeftDistance < kBigDistance) {
1464 offset.x = screen.left - leftRect.right + kMoveToScreen;
1465 _CallculateYOffset(offset, leftRect, screen);
1466 } else if (minTopDistance < kBigDistance) {
1467 offset.y = screen.top - topRect.bottom + kMoveToScreen;
1468 _CallculateXOffset(offset, topRect, screen);
1469 } else if (minRightDistance < kBigDistance) {
1470 offset.x = screen.right - rightRect.left - kMoveToScreen;
1471 _CallculateYOffset(offset, rightRect, screen);
1472 } else if (minBottomDistance < kBigDistance) {
1473 offset.y = screen.bottom - bottomRect.top - kMoveToScreen;
1474 _CallculateXOffset(offset, bottomRect, screen);
1475 }
1476
1477 if (offset.x == 0. && offset.y == 0.)
1478 return;
1479 STRACE_SAT("move group back to screen: offset x: %f offset y: %f\n",
1480 offset.x, offset.y);
1481
1482 desktop->MoveWindowBy(window->GetWindow(), offset.x, offset.y);
1483 window->DoGroupLayout();
1484 }
1485
1486
1487 void
_CallculateXOffset(BPoint & offset,BRect & frame,BRect & screen)1488 SATGroup::_CallculateXOffset(BPoint& offset, BRect& frame, BRect& screen)
1489 {
1490 if (frame.right < screen.left + kMinOverlap)
1491 offset.x = screen.left - frame.right + kMoveToScreen;
1492 else if (frame.left > screen.right - kMinOverlap)
1493 offset.x = screen.right - frame.left - kMoveToScreen;
1494 }
1495
1496
1497 void
_CallculateYOffset(BPoint & offset,BRect & frame,BRect & screen)1498 SATGroup::_CallculateYOffset(BPoint& offset, BRect& frame, BRect& screen)
1499 {
1500 if (frame.top > screen.bottom - kMinOverlap)
1501 offset.y = screen.bottom - frame.top - kMoveToScreen;
1502 else if (frame.bottom < screen.top + kMinOverlap)
1503 offset.y = screen.top - frame.bottom + kMoveToScreen;
1504 }
1505