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