xref: /haiku/src/servers/app/stackandtile/SATGroup.cpp (revision df3ac004ba00d875be84ec7853864b739a2292bf)
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 
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 
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
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
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
162 WindowArea::UpdateSizeLimits()
163 {
164 	_UpdateConstraintValues();
165 }
166 
167 
168 void
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
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*
187 WindowArea::TopWindow()
188 {
189 	return fWindowLayerOrder.ItemAt(fWindowLayerOrder.CountItems() - 1);
190 }
191 
192 
193 void
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
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
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*
281 WindowArea::LeftTab()
282 {
283 	return fLeftTopCrossing->VerticalTab();
284 }
285 
286 
287 Tab*
288 WindowArea::RightTab()
289 {
290 	return fRightBottomCrossing->VerticalTab();
291 }
292 
293 
294 Tab*
295 WindowArea::TopTab()
296 {
297 	return fLeftTopCrossing->HorizontalTab();
298 }
299 
300 
301 Tab*
302 WindowArea::BottomTab()
303 {
304 	return fRightBottomCrossing->HorizontalTab();
305 }
306 
307 
308 BRect
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
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
369 WindowArea::MoveToTopLayer(SATWindow* window)
370 {
371 	if (!fWindowLayerOrder.RemoveItem(window))
372 		return false;
373 	return fWindowLayerOrder.AddItem(window);
374 }
375 
376 
377 void
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>
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
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
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
480 WindowArea::_SetToWindowCorner(Corner* corner)
481 {
482 	corner->status = Corner::kUsed;
483 	corner->windowArea = this;
484 }
485 
486 
487 void
488 WindowArea::_SetToNeighbourCorner(Corner* neighbour)
489 {
490 	if (neighbour->status == Corner::kNotDockable)
491 		neighbour->status = Corner::kFree;
492 }
493 
494 
495 void
496 WindowArea::_UnsetWindowCorner(Corner* corner)
497 {
498 	corner->status = Corner::kFree;
499 	corner->windowArea = NULL;
500 }
501 
502 
503 void
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
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 
543 Corner::Corner()
544 	:
545 	status(kNotDockable),
546 	windowArea(NULL)
547 {
548 
549 }
550 
551 
552 void
553 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 
577 Crossing::Crossing(Tab* vertical, Tab* horizontal)
578 	:
579 	fVerticalTab(vertical),
580 	fHorizontalTab(horizontal)
581 {
582 }
583 
584 
585 Crossing::~Crossing()
586 {
587 	fVerticalTab->RemoveCrossing(this);
588 	fHorizontalTab->RemoveCrossing(this);
589 }
590 
591 
592 Corner*
593 Crossing::GetCorner(Corner::position_t corner) const
594 {
595 	return &const_cast<Corner*>(fCorners)[corner];
596 }
597 
598 
599 Corner*
600 Crossing::GetOppositeCorner(Corner::position_t corner) const
601 {
602 	return &const_cast<Corner*>(fCorners)[3 - corner];
603 }
604 
605 
606 Tab*
607 Crossing::VerticalTab() const
608 {
609 	return fVerticalTab;
610 }
611 
612 
613 Tab*
614 Crossing::HorizontalTab() const
615 {
616 	return fHorizontalTab;
617 }
618 
619 
620 void
621 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 
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 
644 Tab::~Tab()
645 {
646 	if (fOrientation == kVertical)
647 		fGroup->_RemoveVerticalTab(this);
648 	else
649 		fGroup->_RemoveHorizontalTab(this);
650 
651 	delete fVariable;
652 }
653 
654 
655 float
656 Tab::Position() const
657 {
658 	return (float)fVariable->Value() - kMakePositiveOffset;
659 }
660 
661 
662 void
663 Tab::SetPosition(float position)
664 {
665 	fVariable->SetValue(position + kMakePositiveOffset);
666 }
667 
668 
669 Tab::orientation_t
670 Tab::Orientation() const
671 {
672 	return fOrientation;
673 }
674 
675 
676 Constraint*
677 Tab::Connect(Variable* variable)
678 {
679 	return fVariable->IsEqual(variable);
680 }
681 
682 
683 BReference<Crossing>
684 Tab::AddCrossing(Tab* tab)
685 {
686 	if (tab->Orientation() == fOrientation)
687 		return NULL;
688 
689 	Tab* vTab = (fOrientation == kVertical) ? this : tab;
690 	Tab* hTab = (fOrientation == kHorizontal) ? this : tab;
691 
692 	Crossing* crossing = new (std::nothrow)Crossing(vTab, hTab);
693 	if (!crossing)
694 		return NULL;
695 
696 	if (!fCrossingList.AddItem(crossing)) {
697 		return NULL;
698 	}
699 	if (!tab->fCrossingList.AddItem(crossing)) {
700 		fCrossingList.RemoveItem(crossing);
701 		return NULL;
702 	}
703 
704 	BReference<Crossing> crossingRef(crossing, true);
705 	return crossingRef;
706 }
707 
708 
709 bool
710 Tab::RemoveCrossing(Crossing* crossing)
711 {
712 	Tab* vTab = crossing->VerticalTab();
713 	Tab* hTab = crossing->HorizontalTab();
714 
715 	if (vTab != this && hTab != this)
716 		return false;
717 	fCrossingList.RemoveItem(crossing);
718 
719 	return true;
720 }
721 
722 
723 int32
724 Tab::FindCrossingIndex(Tab* tab)
725 {
726 	if (fOrientation == kVertical) {
727 		for (int32 i = 0; i < fCrossingList.CountItems(); i++) {
728 			if (fCrossingList.ItemAt(i)->HorizontalTab() == tab)
729 				return i;
730 		}
731 	} else {
732 		for (int32 i = 0; i < fCrossingList.CountItems(); i++) {
733 			if (fCrossingList.ItemAt(i)->VerticalTab() == tab)
734 				return i;
735 		}
736 	}
737 	return -1;
738 }
739 
740 
741 int32
742 Tab::FindCrossingIndex(float pos)
743 {
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;
749 		}
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;
755 		}
756 	}
757 	return -1;
758 }
759 
760 
761 Crossing*
762 Tab::FindCrossing(Tab* tab)
763 {
764 	return fCrossingList.ItemAt(FindCrossingIndex(tab));
765 }
766 
767 
768 Crossing*
769 Tab::FindCrossing(float tabPosition)
770 {
771 	return fCrossingList.ItemAt(FindCrossingIndex(tabPosition));
772 }
773 
774 
775 const CrossingList*
776 Tab::GetCrossingList() const
777 {
778 	return &fCrossingList;
779 }
780 
781 
782 int
783 Tab::CompareFunction(const Tab* tab1, const Tab* tab2)
784 {
785 	if (tab1->Position() < tab2->Position())
786 		return -1;
787 
788 	return 1;
789 }
790 
791 
792 SATGroup::SATGroup()
793 	:
794 	fLinearSpec(new(std::nothrow) LinearSpec()),
795 	fHorizontalTabsSorted(false),
796 	fVerticalTabsSorted(false),
797 	fActiveWindow(NULL)
798 {
799 }
800 
801 
802 SATGroup::~SATGroup()
803 {
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));
809 
810 	fLinearSpec->ReleaseReference();
811 }
812 
813 
814 bool
815 SATGroup::AddWindow(SATWindow* window, Tab* left, Tab* top, Tab* right,
816 	Tab* bottom)
817 {
818 	STRACE_SAT("SATGroup::AddWindow\n");
819 
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;
823 
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);
832 
833 	if (left == NULL) {
834 		leftRef = _AddVerticalTab();
835 		left = leftRef.Get();
836 	}
837 	if (top == NULL) {
838 		topRef = _AddHorizontalTab();
839 		top = topRef.Get();
840 	}
841 	if (right == NULL) {
842 		rightRef = _AddVerticalTab();
843 		right = rightRef.Get();
844 	}
845 	if (bottom == NULL) {
846 		bottomRef = _AddHorizontalTab();
847 		bottom = bottomRef.Get();
848 	}
849 	if (left == NULL || top == NULL || right == NULL || bottom == NULL)
850 		return false;
851 
852 	if (leftTopRef == NULL) {
853 		leftTopRef = left->AddCrossing(top);
854 		if (leftTopRef == NULL)
855 			return false;
856 	}
857 	if (!rightTopRef) {
858 		rightTopRef = right->AddCrossing(top);
859 		if (!rightTopRef)
860 			return false;
861 	}
862 	if (!leftBottomRef) {
863 		leftBottomRef = left->AddCrossing(bottom);
864 		if (!leftBottomRef)
865 			return false;
866 	}
867 	if (!rightBottomRef) {
868 		rightBottomRef = right->AddCrossing(bottom);
869 		if (!rightBottomRef)
870 			return false;
871 	}
872 
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;
881 	}
882 	// delete the area if AddWindow failed / release our reference on it
883 	BReference<WindowArea> areaRef(area, true);
884 
885 	return AddWindow(window, area);
886 }
887 
888 
889 bool
890 SATGroup::AddWindow(SATWindow* window, WindowArea* area, SATWindow* after)
891 {
892 	if (!area->_AddWindow(window, after))
893 		return false;
894 
895 	if (!fSATWindowList.AddItem(window)) {
896 		area->_RemoveWindow(window);
897 		return false;
898 	}
899 
900 	if (!window->AddedToGroup(this, area)) {
901 		area->_RemoveWindow(window);
902 		fSATWindowList.RemoveItem(window);
903 		return false;
904 	}
905 
906 	return true;
907 }
908 
909 
910 bool
911 SATGroup::RemoveWindow(SATWindow* window, bool stayBelowMouse)
912 {
913 	if (!fSATWindowList.RemoveItem(window))
914 		return false;
915 
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);
921 
922 	window->RemovedFromGroup(this, stayBelowMouse);
923 
924 	if (CountItems() >= 2)
925 		WindowAt(0)->DoGroupLayout();
926 
927 	return true;
928 }
929 
930 
931 int32
932 SATGroup::CountItems()
933 {
934 	return fSATWindowList.CountItems();
935 }
936 
937 
938 SATWindow*
939 SATGroup::WindowAt(int32 index)
940 {
941 	return fSATWindowList.ItemAt(index);
942 }
943 
944 
945 SATWindow*
946 SATGroup::ActiveWindow() const
947 {
948 	return fActiveWindow;
949 }
950 
951 
952 void
953 SATGroup::SetActiveWindow(SATWindow* window)
954 {
955 	fActiveWindow = window;
956 }
957 
958 
959 const TabList*
960 SATGroup::HorizontalTabs()
961 {
962 	if (!fHorizontalTabsSorted) {
963 		fHorizontalTabs.SortItems(Tab::CompareFunction);
964 		fHorizontalTabsSorted = true;
965 	}
966 	return &fHorizontalTabs;
967 }
968 
969 
970 const TabList*
971 SATGroup::VerticalTabs()
972 {
973 	if (!fVerticalTabsSorted) {
974 		fVerticalTabs.SortItems(Tab::CompareFunction);
975 		fVerticalTabsSorted = true;
976 	}
977 	return &fVerticalTabs;
978 }
979 
980 
981 Tab*
982 SATGroup::FindHorizontalTab(float position)
983 {
984 	return _FindTab(fHorizontalTabs, position);
985 }
986 
987 
988 Tab*
989 SATGroup::FindVerticalTab(float position)
990 {
991 	return _FindTab(fVerticalTabs, position);
992 }
993 
994 
995 void
996 SATGroup::WindowAreaRemoved(WindowArea* area)
997 {
998 	_SplitGroupIfNecessary(area);
999 }
1000 
1001 
1002 status_t
1003 SATGroup::RestoreGroup(const BMessage& archive, StackAndTile* sat)
1004 {
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);
1011 
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;
1020 
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);
1027 	}
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);
1034 	}
1035 
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;
1045 
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;
1050 
1051 		Tab* left = tempVTabs[leftTab];
1052 		Tab* right = tempVTabs[rightTab];
1053 		Tab* top = tempHTabs[topTab];
1054 		Tab* bottom = tempHTabs[bottomTab];
1055 
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;
1064 
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;
1073 			}
1074 		}
1075 	}
1076 	return B_OK;
1077 }
1078 
1079 
1080 status_t
1081 SATGroup::ArchiveGroup(BMessage& archive)
1082 {
1083 	archive.AddInt32("htab_count", fHorizontalTabs.CountItems());
1084 	archive.AddInt32("vtab_count", fVerticalTabs.CountItems());
1085 
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());
1092 
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);
1098 
1099 		const SATWindowList& windowList = area->WindowList();
1100 		for (int a = 0; a < windowList.CountItems(); a++)
1101 			areaMessage.AddInt64("window", windowList.ItemAt(a)->Id());
1102 
1103 		archive.AddMessage("area", &areaMessage);
1104 	}
1105 	return B_OK;
1106 }
1107 
1108 
1109 BReference<Tab>
1110 SATGroup::_AddHorizontalTab(float position)
1111 {
1112 	if (fLinearSpec == NULL)
1113 		return NULL;
1114 	Variable* variable = fLinearSpec->AddVariable();
1115 	if (variable == NULL)
1116 		return NULL;
1117 
1118 	Tab* tab = new (std::nothrow)Tab(this, variable, Tab::kHorizontal);
1119 	if (tab == NULL)
1120 		return NULL;
1121 	BReference<Tab> tabRef(tab, true);
1122 
1123 	if (!fHorizontalTabs.AddItem(tab))
1124 		return NULL;
1125 
1126 	fHorizontalTabsSorted = false;
1127 	tabRef->SetPosition(position);
1128 	return tabRef;
1129 }
1130 
1131 
1132 BReference<Tab>
1133 SATGroup::_AddVerticalTab(float position)
1134 {
1135 	if (fLinearSpec == NULL)
1136 		return NULL;
1137 	Variable* variable = fLinearSpec->AddVariable();
1138 	if (variable == NULL)
1139 		return NULL;
1140 
1141 	Tab* tab = new (std::nothrow)Tab(this, variable, Tab::kVertical);
1142 	if (tab == NULL)
1143 		return NULL;
1144 	BReference<Tab> tabRef(tab, true);
1145 
1146 	if (!fVerticalTabs.AddItem(tab))
1147 		return NULL;
1148 
1149 	fVerticalTabsSorted = false;
1150 	tabRef->SetPosition(position);
1151 	return tabRef;
1152 }
1153 
1154 
1155 bool
1156 SATGroup::_RemoveHorizontalTab(Tab* tab)
1157 {
1158 	if (!fHorizontalTabs.RemoveItem(tab))
1159 		return false;
1160 	fHorizontalTabsSorted = false;
1161 	// don't delete the tab it is reference counted
1162 	return true;
1163 }
1164 
1165 
1166 bool
1167 SATGroup::_RemoveVerticalTab(Tab* tab)
1168 {
1169 	if (!fVerticalTabs.RemoveItem(tab))
1170 		return false;
1171 	fVerticalTabsSorted = false;
1172 	// don't delete the tab it is reference counted
1173 	return true;
1174 }
1175 
1176 
1177 Tab*
1178 SATGroup::_FindTab(const TabList& list, float position)
1179 {
1180 	for (int i = 0; i < list.CountItems(); i++)
1181 		if (fabs(list.ItemAt(i)->Position() - position) < 0.00001)
1182 			return list.ItemAt(i);
1183 
1184 	return NULL;
1185 }
1186 
1187 
1188 void
1189 SATGroup::_SplitGroupIfNecessary(WindowArea* removedArea)
1190 {
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;
1194 
1195 	WindowAreaList neighbourWindows;
1196 
1197 	_FillNeighbourList(neighbourWindows, removedArea);
1198 
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;
1214 		}
1215 
1216 		newGroup.MakeEmpty();
1217 	}
1218 }
1219 
1220 
1221 void
1222 SATGroup::_FillNeighbourList(WindowAreaList& neighbourWindows,
1223 	WindowArea* area)
1224 {
1225 	_LeftNeighbours(neighbourWindows, area);
1226 	_RightNeighbours(neighbourWindows, area);
1227 	_TopNeighbours(neighbourWindows, area);
1228 	_BottomNeighbours(neighbourWindows, area);
1229 }
1230 
1231 
1232 void
1233 SATGroup::_LeftNeighbours(WindowAreaList& neighbourWindows, WindowArea* parent)
1234 {
1235 	float startPos = parent->LeftTopCrossing()->HorizontalTab()->Position();
1236 	float endPos = parent->LeftBottomCrossing()->HorizontalTab()->Position();
1237 
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;
1244 
1245 		WindowArea* area = corner->windowArea;
1246 		float pos1 = area->LeftTopCrossing()->HorizontalTab()->Position();
1247 		float pos2 = area->LeftBottomCrossing()->HorizontalTab()->Position();
1248 
1249 		if (pos1 < endPos && pos2 > startPos)
1250 			neighbourWindows.AddItem(area);
1251 
1252 		if (pos2 > endPos)
1253 			break;
1254 	}
1255 }
1256 
1257 
1258 void
1259 SATGroup::_TopNeighbours(WindowAreaList& neighbourWindows, WindowArea* parent)
1260 {
1261 	float startPos = parent->LeftTopCrossing()->VerticalTab()->Position();
1262 	float endPos = parent->RightTopCrossing()->VerticalTab()->Position();
1263 
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;
1270 
1271 		WindowArea* area = corner->windowArea;
1272 		float pos1 = area->LeftTopCrossing()->VerticalTab()->Position();
1273 		float pos2 = area->RightTopCrossing()->VerticalTab()->Position();
1274 
1275 		if (pos1 < endPos && pos2 > startPos)
1276 			neighbourWindows.AddItem(area);
1277 
1278 		if (pos2 > endPos)
1279 			break;
1280 	}
1281 }
1282 
1283 
1284 void
1285 SATGroup::_RightNeighbours(WindowAreaList& neighbourWindows, WindowArea* parent)
1286 {
1287 	float startPos = parent->RightTopCrossing()->HorizontalTab()->Position();
1288 	float endPos = parent->RightBottomCrossing()->HorizontalTab()->Position();
1289 
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;
1296 
1297 		WindowArea* area = corner->windowArea;
1298 		float pos1 = area->RightTopCrossing()->HorizontalTab()->Position();
1299 		float pos2 = area->RightBottomCrossing()->HorizontalTab()->Position();
1300 
1301 		if (pos1 < endPos && pos2 > startPos)
1302 			neighbourWindows.AddItem(area);
1303 
1304 		if (pos2 > endPos)
1305 			break;
1306 	}
1307 }
1308 
1309 
1310 void
1311 SATGroup::_BottomNeighbours(WindowAreaList& neighbourWindows,
1312 	WindowArea* parent)
1313 {
1314 	float startPos = parent->LeftBottomCrossing()->VerticalTab()->Position();
1315 	float endPos = parent->RightBottomCrossing()->VerticalTab()->Position();
1316 
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;
1323 
1324 		WindowArea* area = corner->windowArea;
1325 		float pos1 = area->LeftBottomCrossing()->VerticalTab()->Position();
1326 		float pos2 = area->RightBottomCrossing()->VerticalTab()->Position();
1327 
1328 		if (pos1 < endPos && pos2 > startPos)
1329 			neighbourWindows.AddItem(area);
1330 
1331 		if (pos2 > endPos)
1332 			break;
1333 	}
1334 }
1335 
1336 
1337 bool
1338 SATGroup::_FindConnectedGroup(WindowAreaList& seedList, WindowArea* removedArea,
1339 	WindowAreaList& newGroup)
1340 {
1341 	if (seedList.CountItems() == 0)
1342 		return false;
1343 
1344 	WindowArea* area = seedList.RemoveItemAt(0);
1345 	newGroup.AddItem(area);
1346 
1347 	_FollowSeed(area, removedArea, seedList, newGroup);
1348 	return true;
1349 }
1350 
1351 
1352 void
1353 SATGroup::_FollowSeed(WindowArea* area, WindowArea* veto,
1354 	WindowAreaList& seedList, WindowAreaList& newGroup)
1355 {
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--;
1368 		}
1369 	}
1370 
1371 	for (int i = 0; i < neighbours.CountItems(); i++)
1372 		_FollowSeed(neighbours.ItemAt(i), veto, seedList, newGroup);
1373 }
1374 
1375 
1376 void
1377 SATGroup::_SpawnNewGroup(const WindowAreaList& newGroup)
1378 {
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);
1385 
1386 	for (int i = 0; i < newGroup.CountItems(); i++)
1387 		newGroup.ItemAt(i)->PropagateToGroup(group);
1388 
1389 	_EnsureGroupIsOnScreen(group);
1390 }
1391 
1392 
1393 const float kMinOverlap = 50;
1394 const float kMoveToScreen = 75;
1395 
1396 
1397 void
1398 SATGroup::_EnsureGroupIsOnScreen(SATGroup* group)
1399 {
1400 	STRACE_SAT("SATGroup::_EnsureGroupIsOnScreen\n");
1401 	if (group == NULL || group->CountItems() < 1)
1402 		return;
1403 
1404 	SATWindow* window = group->WindowAt(0);
1405 	Desktop* desktop = window->GetWindow()->Desktop();
1406 	if (desktop == NULL)
1407 		return;
1408 
1409 	const float kBigDistance = 1E+10;
1410 
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;
1419 
1420 	BRect screen = window->GetWindow()->Screen()->Frame();
1421 	BRect reducedScreen = screen;
1422 	reducedScreen.InsetBy(kMinOverlap, kMinOverlap);
1423 
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;
1429 
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;
1437 		}
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;
1445 		}
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;
1453 		}
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;
1461 		}
1462 	}
1463 
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);
1477 	}
1478 
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);
1483 
1484 	desktop->MoveWindowBy(window->GetWindow(), offset.x, offset.y);
1485 	window->DoGroupLayout();
1486 }
1487 
1488 
1489 void
1490 SATGroup::_CallculateXOffset(BPoint& offset, BRect& frame, BRect& screen)
1491 {
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;
1496 }
1497 
1498 
1499 void
1500 SATGroup::_CallculateYOffset(BPoint& offset, BRect& frame, BRect& screen)
1501 {
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;
1506 }
1507