xref: /haiku/src/servers/app/stackandtile/SATGroup.cpp (revision 8e8f7748d39f8407894a5ab79f7b4f93bc4f4652)
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 	LinearSpec* linearSpec = fGroup->GetLinearSpec();
381 
382 	linearSpec->RemoveConstraint(fMinWidthConstraint, true);
383 	linearSpec->RemoveConstraint(fMinHeightConstraint, true);
384 	linearSpec->RemoveConstraint(fMaxWidthConstraint, true);
385 	linearSpec->RemoveConstraint(fMaxHeightConstraint, true);
386 	linearSpec->RemoveConstraint(fWidthConstraint, true);
387 	linearSpec->RemoveConstraint(fHeightConstraint, true);
388 
389 	fMinWidthConstraint = NULL;
390 	fMinHeightConstraint = NULL;
391 	fMaxWidthConstraint = NULL;
392 	fMaxHeightConstraint = NULL;
393 	fWidthConstraint = NULL;
394 	fHeightConstraint = NULL;
395 }
396 
397 
398 BReference<Crossing>
399 WindowArea::_CrossingByPosition(Crossing* crossing, SATGroup* group)
400 {
401 	BReference<Crossing> crossRef = NULL;
402 
403 	Tab* oldHTab = crossing->HorizontalTab();
404 	BReference<Tab> hTab = group->FindHorizontalTab(oldHTab->Position());
405 	if (!hTab)
406 		hTab = group->_AddHorizontalTab(oldHTab->Position());
407 	if (!hTab)
408 		return crossRef;
409 
410 	Tab* oldVTab = crossing->VerticalTab();
411 	crossRef = hTab->FindCrossing(oldVTab->Position());
412 	if (crossRef)
413 		return crossRef;
414 
415 	BReference<Tab> vTab = group->FindVerticalTab(oldVTab->Position());
416 	if (!vTab)
417 		vTab = group->_AddVerticalTab(oldVTab->Position());
418 	if (!vTab)
419 		return crossRef;
420 
421 	return hTab->AddCrossing(vTab);
422 }
423 
424 
425 void
426 WindowArea::_InitCorners()
427 {
428 	_SetToWindowCorner(fLeftTopCrossing->RightBottomCorner());
429 	_SetToNeighbourCorner(fLeftTopCrossing->LeftBottomCorner());
430 	_SetToNeighbourCorner(fLeftTopCrossing->RightTopCorner());
431 
432 	_SetToWindowCorner(fRightTopCrossing->LeftBottomCorner());
433 	_SetToNeighbourCorner(fRightTopCrossing->LeftTopCorner());
434 	_SetToNeighbourCorner(fRightTopCrossing->RightBottomCorner());
435 
436 	_SetToWindowCorner(fLeftBottomCrossing->RightTopCorner());
437 	_SetToNeighbourCorner(fLeftBottomCrossing->LeftTopCorner());
438 	_SetToNeighbourCorner(fLeftBottomCrossing->RightBottomCorner());
439 
440 	_SetToWindowCorner(fRightBottomCrossing->LeftTopCorner());
441 	_SetToNeighbourCorner(fRightBottomCrossing->LeftBottomCorner());
442 	_SetToNeighbourCorner(fRightBottomCrossing->RightTopCorner());
443 }
444 
445 
446 void
447 WindowArea::_CleanupCorners()
448 {
449 	_UnsetWindowCorner(fLeftTopCrossing->RightBottomCorner());
450 	_UnsetNeighbourCorner(fLeftTopCrossing->LeftBottomCorner(),
451 		fLeftBottomCrossing->LeftTopCorner());
452 	_UnsetNeighbourCorner(fLeftTopCrossing->RightTopCorner(),
453 		fLeftBottomCrossing->LeftTopCorner());
454 
455 	_UnsetWindowCorner(fRightTopCrossing->LeftBottomCorner());
456 	_UnsetNeighbourCorner(fRightTopCrossing->LeftTopCorner(),
457 		fLeftBottomCrossing->RightTopCorner());
458 	_UnsetNeighbourCorner(fRightTopCrossing->RightBottomCorner(),
459 		fLeftBottomCrossing->RightTopCorner());
460 
461 	_UnsetWindowCorner(fLeftBottomCrossing->RightTopCorner());
462 	_UnsetNeighbourCorner(fLeftBottomCrossing->LeftTopCorner(),
463 		fLeftBottomCrossing->LeftBottomCorner());
464 	_UnsetNeighbourCorner(fLeftBottomCrossing->RightBottomCorner(),
465 		fLeftBottomCrossing->LeftBottomCorner());
466 
467 	_UnsetWindowCorner(fRightBottomCrossing->LeftTopCorner());
468 	_UnsetNeighbourCorner(fRightBottomCrossing->LeftBottomCorner(),
469 		fRightBottomCrossing->RightBottomCorner());
470 	_UnsetNeighbourCorner(fRightBottomCrossing->RightTopCorner(),
471 		fRightBottomCrossing->RightBottomCorner());
472 }
473 
474 
475 void
476 WindowArea::_SetToWindowCorner(Corner* corner)
477 {
478 	corner->status = Corner::kUsed;
479 	corner->windowArea = this;
480 }
481 
482 
483 void
484 WindowArea::_SetToNeighbourCorner(Corner* neighbour)
485 {
486 	if (neighbour->status == Corner::kNotDockable)
487 		neighbour->status = Corner::kFree;
488 }
489 
490 
491 void
492 WindowArea::_UnsetWindowCorner(Corner* corner)
493 {
494 	corner->status = Corner::kFree;
495 	corner->windowArea = NULL;
496 }
497 
498 
499 void
500 WindowArea::_UnsetNeighbourCorner(Corner* neighbour, Corner* opponent)
501 {
502 	if (neighbour->status == Corner::kFree && opponent->status != Corner::kUsed)
503 		neighbour->status = Corner::kNotDockable;
504 }
505 
506 
507 void
508 WindowArea::_MoveToSAT(SATWindow* triggerWindow)
509 {
510 	SATWindow* topWindow = TopWindow();
511 	// if there is no window in the group we are done
512 	if (topWindow == NULL)
513 		return;
514 
515 	BRect frameSAT(LeftVar()->Value() - kMakePositiveOffset,
516 		TopVar()->Value() - kMakePositiveOffset,
517 		RightVar()->Value() - kMakePositiveOffset,
518 		BottomVar()->Value() - kMakePositiveOffset);
519 	topWindow->AdjustSizeLimits(frameSAT);
520 
521 	BRect frame = topWindow->CompleteWindowFrame();
522 	float deltaToX = round(frameSAT.left - frame.left);
523 	float deltaToY = round(frameSAT.top - frame.top);
524 	frame.OffsetBy(deltaToX, deltaToY);
525 	float deltaByX = round(frameSAT.right - frame.right);
526 	float deltaByY = round(frameSAT.bottom - frame.bottom);
527 
528 	int32 workspace = triggerWindow->GetWindow()->CurrentWorkspace();
529 	Desktop* desktop = triggerWindow->GetWindow()->Desktop();
530 	desktop->MoveWindowBy(topWindow->GetWindow(), deltaToX, deltaToY,
531 		workspace);
532 	// Update frame to the new position
533 	desktop->ResizeWindowBy(topWindow->GetWindow(), deltaByX, deltaByY);
534 
535 	UpdateSizeConstaints(frameSAT);
536 }
537 
538 
539 Corner::Corner()
540 	:
541 	status(kNotDockable),
542 	windowArea(NULL)
543 {
544 
545 }
546 
547 
548 void
549 Corner::Trace() const
550 {
551 	switch (status) {
552 		case kFree:
553 			debug_printf("free corner\n");
554 			break;
555 
556 		case kUsed:
557 		{
558 			debug_printf("attached windows:\n");
559 			const SATWindowList& list = windowArea->WindowList();
560 			for (int i = 0; i < list.CountItems(); i++) {
561 				debug_printf("- %s\n", list.ItemAt(i)->GetWindow()->Title());
562 			}
563 			break;
564 		}
565 
566 		case kNotDockable:
567 			debug_printf("not dockable\n");
568 			break;
569 	};
570 }
571 
572 
573 Crossing::Crossing(Tab* vertical, Tab* horizontal)
574 	:
575 	fVerticalTab(vertical),
576 	fHorizontalTab(horizontal)
577 {
578 }
579 
580 
581 Crossing::~Crossing()
582 {
583 	fVerticalTab->RemoveCrossing(this);
584 	fHorizontalTab->RemoveCrossing(this);
585 }
586 
587 
588 Corner*
589 Crossing::GetCorner(Corner::position_t corner) const
590 {
591 	return &const_cast<Corner*>(fCorners)[corner];
592 }
593 
594 
595 Corner*
596 Crossing::GetOppositeCorner(Corner::position_t corner) const
597 {
598 	return &const_cast<Corner*>(fCorners)[3 - corner];
599 }
600 
601 
602 Tab*
603 Crossing::VerticalTab() const
604 {
605 	return fVerticalTab;
606 }
607 
608 
609 Tab*
610 Crossing::HorizontalTab() const
611 {
612 	return fHorizontalTab;
613 }
614 
615 
616 void
617 Crossing::Trace() const
618 {
619 	debug_printf("left-top corner: ");
620 	fCorners[Corner::kLeftTop].Trace();
621 	debug_printf("right-top corner: ");
622 	fCorners[Corner::kRightTop].Trace();
623 	debug_printf("left-bottom corner: ");
624 	fCorners[Corner::kLeftBottom].Trace();
625 	debug_printf("right-bottom corner: ");
626 	fCorners[Corner::kRightBottom].Trace();
627 }
628 
629 
630 Tab::Tab(SATGroup* group, Variable* variable, orientation_t orientation)
631 	:
632 	fGroup(group),
633 	fVariable(variable),
634 	fOrientation(orientation)
635 {
636 
637 }
638 
639 
640 Tab::~Tab()
641 {
642 	if (fOrientation == kVertical)
643 		fGroup->_RemoveVerticalTab(this);
644 	else
645 		fGroup->_RemoveHorizontalTab(this);
646 
647 	delete fVariable;
648 }
649 
650 
651 float
652 Tab::Position() const
653 {
654 	return (float)fVariable->Value() - kMakePositiveOffset;
655 }
656 
657 
658 void
659 Tab::SetPosition(float position)
660 {
661 	fVariable->SetValue(position + kMakePositiveOffset);
662 }
663 
664 
665 Tab::orientation_t
666 Tab::Orientation() const
667 {
668 	return fOrientation;
669 }
670 
671 
672 Constraint*
673 Tab::Connect(Variable* variable)
674 {
675 	return fVariable->IsEqual(variable);
676 }
677 
678 
679 BReference<Crossing>
680 Tab::AddCrossing(Tab* tab)
681 {
682 	if (tab->Orientation() == fOrientation)
683 		return NULL;
684 
685 	Tab* vTab = (fOrientation == kVertical) ? this : tab;
686 	Tab* hTab = (fOrientation == kHorizontal) ? this : tab;
687 
688 	Crossing* crossing = new (std::nothrow)Crossing(vTab, hTab);
689 	if (!crossing)
690 		return NULL;
691 
692 	if (!fCrossingList.AddItem(crossing)) {
693 		return NULL;
694 	}
695 	if (!tab->fCrossingList.AddItem(crossing)) {
696 		fCrossingList.RemoveItem(crossing);
697 		return NULL;
698 	}
699 
700 	BReference<Crossing> crossingRef(crossing, true);
701 	return crossingRef;
702 }
703 
704 
705 bool
706 Tab::RemoveCrossing(Crossing* crossing)
707 {
708 	Tab* vTab = crossing->VerticalTab();
709 	Tab* hTab = crossing->HorizontalTab();
710 
711 	if (vTab != this && hTab != this)
712 		return false;
713 	fCrossingList.RemoveItem(crossing);
714 
715 	return true;
716 }
717 
718 
719 int32
720 Tab::FindCrossingIndex(Tab* tab)
721 {
722 	if (fOrientation == kVertical) {
723 		for (int32 i = 0; i < fCrossingList.CountItems(); i++) {
724 			if (fCrossingList.ItemAt(i)->HorizontalTab() == tab)
725 				return i;
726 		}
727 	} else {
728 		for (int32 i = 0; i < fCrossingList.CountItems(); i++) {
729 			if (fCrossingList.ItemAt(i)->VerticalTab() == tab)
730 				return i;
731 		}
732 	}
733 	return -1;
734 }
735 
736 
737 int32
738 Tab::FindCrossingIndex(float pos)
739 {
740 	if (fOrientation == kVertical) {
741 		for (int32 i = 0; i < fCrossingList.CountItems(); i++) {
742 			if (fabs(fCrossingList.ItemAt(i)->HorizontalTab()->Position() - pos)
743 				< 0.0001)
744 				return i;
745 		}
746 	} else {
747 		for (int32 i = 0; i < fCrossingList.CountItems(); i++) {
748 			if (fabs(fCrossingList.ItemAt(i)->VerticalTab()->Position() - pos)
749 				< 0.0001)
750 				return i;
751 		}
752 	}
753 	return -1;
754 }
755 
756 
757 Crossing*
758 Tab::FindCrossing(Tab* tab)
759 {
760 	return fCrossingList.ItemAt(FindCrossingIndex(tab));
761 }
762 
763 
764 Crossing*
765 Tab::FindCrossing(float tabPosition)
766 {
767 	return fCrossingList.ItemAt(FindCrossingIndex(tabPosition));
768 }
769 
770 
771 const CrossingList*
772 Tab::GetCrossingList() const
773 {
774 	return &fCrossingList;
775 }
776 
777 
778 int
779 Tab::CompareFunction(const Tab* tab1, const Tab* tab2)
780 {
781 	if (tab1->Position() < tab2->Position())
782 		return -1;
783 
784 	return 1;
785 }
786 
787 
788 SATGroup::SATGroup()
789 	:
790 	fHorizontalTabsSorted(false),
791 	fVerticalTabsSorted(false),
792 	fActiveWindow(NULL)
793 {
794 }
795 
796 
797 SATGroup::~SATGroup()
798 {
799 	// Should be empty
800 	//while (fSATWindowList.CountItems() > 0)
801 	//	RemoveWindow(fSATWindowList.ItemAt(0));
802 }
803 
804 
805 bool
806 SATGroup::AddWindow(SATWindow* window, Tab* left, Tab* top, Tab* right,
807 	Tab* bottom)
808 {
809 	STRACE_SAT("SATGroup::AddWindow\n");
810 
811 	// first check if we have to create tabs and missing corners.
812 	BReference<Tab> leftRef, rightRef, topRef, bottomRef;
813 	BReference<Crossing> leftTopRef, rightTopRef, leftBottomRef, rightBottomRef;
814 
815 	if (left && top)
816 		leftTopRef = left->FindCrossing(top);
817 	if (right && top)
818 		rightTopRef = right->FindCrossing(top);
819 	if (left && bottom)
820 		leftBottomRef = left->FindCrossing(bottom);
821 	if (right && bottom)
822 		rightBottomRef = right->FindCrossing(bottom);
823 
824 	if (!left) {
825 		leftRef = _AddVerticalTab();
826 		left = leftRef.Get();
827 	}
828 	if (!top) {
829 		topRef = _AddHorizontalTab();
830 		top = topRef.Get();
831 	}
832 	if (!right) {
833 		rightRef = _AddVerticalTab();
834 		right = rightRef.Get();
835 	}
836 	if (!bottom) {
837 		bottomRef = _AddHorizontalTab();
838 		bottom = bottomRef.Get();
839 	}
840 	if (!left || !top || !right || !bottom)
841 		return false;
842 
843 	if (!leftTopRef) {
844 		leftTopRef = left->AddCrossing(top);
845 		if (!leftTopRef)
846 			return false;
847 	}
848 	if (!rightTopRef) {
849 		rightTopRef = right->AddCrossing(top);
850 		if (!rightTopRef)
851 			return false;
852 	}
853 	if (!leftBottomRef) {
854 		leftBottomRef = left->AddCrossing(bottom);
855 		if (!leftBottomRef)
856 			return false;
857 	}
858 	if (!rightBottomRef) {
859 		rightBottomRef = right->AddCrossing(bottom);
860 		if (!rightBottomRef)
861 			return false;
862 	}
863 
864 	WindowArea* area = new(std::nothrow) WindowArea(leftTopRef, rightTopRef,
865 		leftBottomRef, rightBottomRef);
866 	if (!area)
867 		return false;
868 	// the area register itself in our area list
869 	if (area->Init(this) == false) {
870 		delete area;
871 		return false;
872 	}
873 	// delete the area if AddWindow failed / release our reference on it
874 	BReference<WindowArea> areaRef(area, true);
875 
876 	return AddWindow(window, area);
877 }
878 
879 
880 bool
881 SATGroup::AddWindow(SATWindow* window, WindowArea* area, SATWindow* after)
882 {
883 	if (!area->_AddWindow(window, after))
884 		return false;
885 
886 	if (!fSATWindowList.AddItem(window)) {
887 		area->_RemoveWindow(window);
888 		return false;
889 	}
890 
891 	if (!window->AddedToGroup(this, area)) {
892 		area->_RemoveWindow(window);
893 		fSATWindowList.RemoveItem(window);
894 		return false;
895 	}
896 
897 	return true;
898 }
899 
900 
901 bool
902 SATGroup::RemoveWindow(SATWindow* window, bool stayBelowMouse)
903 {
904 	if (!fSATWindowList.RemoveItem(window))
905 		return false;
906 
907 	// We need the area a little bit longer because the area could hold the
908 	// last reference to the group.
909 	BReference<WindowArea> area = window->GetWindowArea();
910 	if (area.Get() != NULL)
911 		area->_RemoveWindow(window);
912 
913 	window->RemovedFromGroup(this, stayBelowMouse);
914 
915 	if (CountItems() >= 2)
916 		WindowAt(0)->DoGroupLayout();
917 
918 	return true;
919 }
920 
921 
922 int32
923 SATGroup::CountItems()
924 {
925 	return fSATWindowList.CountItems();
926 };
927 
928 
929 SATWindow*
930 SATGroup::WindowAt(int32 index)
931 {
932 	return fSATWindowList.ItemAt(index);
933 }
934 
935 
936 SATWindow*
937 SATGroup::ActiveWindow() const
938 {
939 	return fActiveWindow;
940 }
941 
942 
943 void
944 SATGroup::SetActiveWindow(SATWindow* window)
945 {
946 	fActiveWindow = window;
947 }
948 
949 
950 const TabList*
951 SATGroup::HorizontalTabs()
952 {
953 	if (!fHorizontalTabsSorted) {
954 		fHorizontalTabs.SortItems(Tab::CompareFunction);
955 		fHorizontalTabsSorted = true;
956 	}
957 	return &fHorizontalTabs;
958 }
959 
960 
961 const TabList*
962 SATGroup::VerticalTabs()
963 {
964 	if (!fVerticalTabsSorted) {
965 		fVerticalTabs.SortItems(Tab::CompareFunction);
966 		fVerticalTabsSorted = true;
967 	}
968 	return &fVerticalTabs;
969 }
970 
971 
972 Tab*
973 SATGroup::FindHorizontalTab(float position)
974 {
975 	return _FindTab(fHorizontalTabs, position);
976 }
977 
978 
979 Tab*
980 SATGroup::FindVerticalTab(float position)
981 {
982 	return _FindTab(fVerticalTabs, position);
983 }
984 
985 
986 void
987 SATGroup::WindowAreaRemoved(WindowArea* area)
988 {
989 	_SplitGroupIfNecessary(area);
990 }
991 
992 
993 status_t
994 SATGroup::RestoreGroup(const BMessage& archive, StackAndTile* sat)
995 {
996 	// create new group
997 	SATGroup* group = new (std::nothrow)SATGroup;
998 	if (!group)
999 		return B_NO_MEMORY;
1000 	BReference<SATGroup> groupRef;
1001 	groupRef.SetTo(group, true);
1002 
1003 	int32 nHTabs, nVTabs;
1004 	status_t status;
1005 	status = archive.FindInt32("htab_count", &nHTabs);
1006 	if (status != B_OK)
1007 		return status;
1008 	status = archive.FindInt32("vtab_count", &nVTabs);
1009 	if (status != B_OK)
1010 		return status;
1011 
1012 	vector<BReference<Tab> > tempHTabs;
1013 	for (int i = 0; i < nHTabs; i++) {
1014 		BReference<Tab> tab = group->_AddHorizontalTab();
1015 		if (!tab)
1016 			return B_NO_MEMORY;
1017 		tempHTabs.push_back(tab);
1018 	}
1019 	vector<BReference<Tab> > tempVTabs;
1020 	for (int i = 0; i < nVTabs; i++) {
1021 		BReference<Tab> tab = group->_AddVerticalTab();
1022 		if (!tab)
1023 			return B_NO_MEMORY;
1024 		tempVTabs.push_back(tab);
1025 	}
1026 
1027 	BMessage areaArchive;
1028 	for (int32 i = 0; archive.FindMessage("area", i, &areaArchive) == B_OK;
1029 		i++) {
1030 		uint32 leftTab, rightTab, topTab, bottomTab;
1031 		if (areaArchive.FindInt32("left_tab", (int32*)&leftTab) != B_OK
1032 			|| areaArchive.FindInt32("right_tab", (int32*)&rightTab) != B_OK
1033 			|| areaArchive.FindInt32("top_tab", (int32*)&topTab) != B_OK
1034 			|| areaArchive.FindInt32("bottom_tab", (int32*)&bottomTab) != B_OK)
1035 			return B_ERROR;
1036 
1037 		if (leftTab >= tempVTabs.size() || rightTab >= tempVTabs.size())
1038 			return B_BAD_VALUE;
1039 		if (topTab >= tempHTabs.size() || bottomTab >= tempHTabs.size())
1040 			return B_BAD_VALUE;
1041 
1042 		Tab* left = tempVTabs[leftTab];
1043 		Tab* right = tempVTabs[rightTab];
1044 		Tab* top = tempHTabs[topTab];
1045 		Tab* bottom = tempHTabs[bottomTab];
1046 
1047 		// adding windows to area
1048 		uint64 windowId;
1049 		SATWindow* prevWindow = NULL;
1050 		for (int32 i = 0; areaArchive.FindInt64("window", i,
1051 			(int64*)&windowId) == B_OK; i++) {
1052 			SATWindow* window = sat->FindSATWindow(windowId);
1053 			if (!window)
1054 				continue;
1055 
1056 			if (prevWindow == NULL) {
1057 				if (!group->AddWindow(window, left, top, right, bottom))
1058 					continue;
1059 				prevWindow = window;
1060 			} else {
1061 				if (!prevWindow->StackWindow(window))
1062 					continue;
1063 				prevWindow = window;
1064 			}
1065 		}
1066 	}
1067 	return B_OK;
1068 }
1069 
1070 
1071 status_t
1072 SATGroup::ArchiveGroup(BMessage& archive)
1073 {
1074 	archive.AddInt32("htab_count", fHorizontalTabs.CountItems());
1075 	archive.AddInt32("vtab_count", fVerticalTabs.CountItems());
1076 
1077 	for (int i = 0; i < fWindowAreaList.CountItems(); i++) {
1078 		WindowArea* area = fWindowAreaList.ItemAt(i);
1079 		int32 leftTab = fVerticalTabs.IndexOf(area->LeftTab());
1080 		int32 rightTab = fVerticalTabs.IndexOf(area->RightTab());
1081 		int32 topTab = fHorizontalTabs.IndexOf(area->TopTab());
1082 		int32 bottomTab = fHorizontalTabs.IndexOf(area->BottomTab());
1083 
1084 		BMessage areaMessage;
1085 		areaMessage.AddInt32("left_tab", leftTab);
1086 		areaMessage.AddInt32("right_tab", rightTab);
1087 		areaMessage.AddInt32("top_tab", topTab);
1088 		areaMessage.AddInt32("bottom_tab", bottomTab);
1089 
1090 		const SATWindowList& windowList = area->WindowList();
1091 		for (int a = 0; a < windowList.CountItems(); a++)
1092 			areaMessage.AddInt64("window", windowList.ItemAt(a)->Id());
1093 
1094 		archive.AddMessage("area", &areaMessage);
1095 	}
1096 	return B_OK;
1097 }
1098 
1099 
1100 BReference<Tab>
1101 SATGroup::_AddHorizontalTab(float position)
1102 {
1103 	Variable* variable = fLinearSpec.AddVariable();
1104 	if (!variable)
1105 		return NULL;
1106 
1107 	Tab* tab = new (std::nothrow)Tab(this, variable, Tab::kHorizontal);
1108 	if (!tab)
1109 		return NULL;
1110 	BReference<Tab> tabRef(tab, true);
1111 
1112 	if (!fHorizontalTabs.AddItem(tab))
1113 		return NULL;
1114 
1115 	fHorizontalTabsSorted = false;
1116 	tabRef->SetPosition(position);
1117 	return tabRef;
1118 }
1119 
1120 
1121 BReference<Tab>
1122 SATGroup::_AddVerticalTab(float position)
1123 {
1124 	Variable* variable = fLinearSpec.AddVariable();
1125 	if (!variable)
1126 		return NULL;
1127 
1128 	Tab* tab = new (std::nothrow)Tab(this, variable, Tab::kVertical);
1129 	if (!tab)
1130 		return NULL;
1131 	BReference<Tab> tabRef(tab, true);
1132 
1133 	if (!fVerticalTabs.AddItem(tab))
1134 		return NULL;
1135 
1136 	fVerticalTabsSorted = false;
1137 	tabRef->SetPosition(position);
1138 	return tabRef;
1139 }
1140 
1141 
1142 bool
1143 SATGroup::_RemoveHorizontalTab(Tab* tab)
1144 {
1145 	if (!fHorizontalTabs.RemoveItem(tab))
1146 		return false;
1147 	fHorizontalTabsSorted = false;
1148 	// don't delete the tab it is reference counted
1149 	return true;
1150 }
1151 
1152 
1153 bool
1154 SATGroup::_RemoveVerticalTab(Tab* tab)
1155 {
1156 	if (!fVerticalTabs.RemoveItem(tab))
1157 		return false;
1158 	fVerticalTabsSorted = false;
1159 	// don't delete the tab it is reference counted
1160 	return true;
1161 }
1162 
1163 
1164 Tab*
1165 SATGroup::_FindTab(const TabList& list, float position)
1166 {
1167 	for (int i = 0; i < list.CountItems(); i++)
1168 		if (fabs(list.ItemAt(i)->Position() - position) < 0.00001)
1169 			return list.ItemAt(i);
1170 
1171 	return NULL;
1172 }
1173 
1174 
1175 void
1176 SATGroup::_SplitGroupIfNecessary(WindowArea* removedArea)
1177 {
1178 	// if there are windows stacked in the area we don't need to split
1179 	if (!removedArea || removedArea->WindowList().CountItems() > 1)
1180 		return;
1181 
1182 	WindowAreaList neighbourWindows;
1183 
1184 	_FillNeighbourList(neighbourWindows, removedArea);
1185 
1186 	bool ownGroupProcessed = false;
1187 	WindowAreaList newGroup;
1188 	while (_FindConnectedGroup(neighbourWindows, removedArea, newGroup)) {
1189 		STRACE_SAT("Connected group found; %i window(s)\n",
1190 			(int)newGroup.CountItems());
1191 		if (newGroup.CountItems() == 1
1192 			&& newGroup.ItemAt(0)->WindowList().CountItems() == 1) {
1193 			SATWindow* window = newGroup.ItemAt(0)->WindowList().ItemAt(0);
1194 			RemoveWindow(window);
1195 			_EnsureGroupIsOnScreen(window->GetGroup());
1196 		} else if (ownGroupProcessed)
1197 			_SpawnNewGroup(newGroup);
1198 		else {
1199 			_EnsureGroupIsOnScreen(this);
1200 			ownGroupProcessed = true;
1201 		}
1202 
1203 		newGroup.MakeEmpty();
1204 	}
1205 }
1206 
1207 
1208 void
1209 SATGroup::_FillNeighbourList(WindowAreaList& neighbourWindows,
1210 	WindowArea* area)
1211 {
1212 	_LeftNeighbours(neighbourWindows, area);
1213 	_RightNeighbours(neighbourWindows, area);
1214 	_TopNeighbours(neighbourWindows, area);
1215 	_BottomNeighbours(neighbourWindows, area);
1216 }
1217 
1218 
1219 void
1220 SATGroup::_LeftNeighbours(WindowAreaList& neighbourWindows, WindowArea* parent)
1221 {
1222 	float startPos = parent->LeftTopCrossing()->HorizontalTab()->Position();
1223 	float endPos = parent->LeftBottomCrossing()->HorizontalTab()->Position();
1224 
1225 	Tab* tab = parent->LeftTopCrossing()->VerticalTab();
1226 	const CrossingList* crossingList = tab->GetCrossingList();
1227 	for (int i = 0; i < crossingList->CountItems(); i++) {
1228 		Corner* corner = crossingList->ItemAt(i)->LeftTopCorner();
1229 		if (corner->status != Corner::kUsed)
1230 			continue;
1231 
1232 		WindowArea* area = corner->windowArea;
1233 		float pos1 = area->LeftTopCrossing()->HorizontalTab()->Position();
1234 		float pos2 = area->LeftBottomCrossing()->HorizontalTab()->Position();
1235 
1236 		if (pos1 < endPos && pos2 > startPos)
1237 			neighbourWindows.AddItem(area);
1238 
1239 		if (pos2 > endPos)
1240 			break;
1241 	}
1242 }
1243 
1244 
1245 void
1246 SATGroup::_TopNeighbours(WindowAreaList& neighbourWindows, WindowArea* parent)
1247 {
1248 	float startPos = parent->LeftTopCrossing()->VerticalTab()->Position();
1249 	float endPos = parent->RightTopCrossing()->VerticalTab()->Position();
1250 
1251 	Tab* tab = parent->LeftTopCrossing()->HorizontalTab();
1252 	const CrossingList* crossingList = tab->GetCrossingList();
1253 	for (int i = 0; i < crossingList->CountItems(); i++) {
1254 		Corner* corner = crossingList->ItemAt(i)->LeftTopCorner();
1255 		if (corner->status != Corner::kUsed)
1256 			continue;
1257 
1258 		WindowArea* area = corner->windowArea;
1259 		float pos1 = area->LeftTopCrossing()->VerticalTab()->Position();
1260 		float pos2 = area->RightTopCrossing()->VerticalTab()->Position();
1261 
1262 		if (pos1 < endPos && pos2 > startPos)
1263 			neighbourWindows.AddItem(area);
1264 
1265 		if (pos2 > endPos)
1266 			break;
1267 	}
1268 }
1269 
1270 
1271 void
1272 SATGroup::_RightNeighbours(WindowAreaList& neighbourWindows, WindowArea* parent)
1273 {
1274 	float startPos = parent->RightTopCrossing()->HorizontalTab()->Position();
1275 	float endPos = parent->RightBottomCrossing()->HorizontalTab()->Position();
1276 
1277 	Tab* tab = parent->RightTopCrossing()->VerticalTab();
1278 	const CrossingList* crossingList = tab->GetCrossingList();
1279 	for (int i = 0; i < crossingList->CountItems(); i++) {
1280 		Corner* corner = crossingList->ItemAt(i)->RightTopCorner();
1281 		if (corner->status != Corner::kUsed)
1282 			continue;
1283 
1284 		WindowArea* area = corner->windowArea;
1285 		float pos1 = area->RightTopCrossing()->HorizontalTab()->Position();
1286 		float pos2 = area->RightBottomCrossing()->HorizontalTab()->Position();
1287 
1288 		if (pos1 < endPos && pos2 > startPos)
1289 			neighbourWindows.AddItem(area);
1290 
1291 		if (pos2 > endPos)
1292 			break;
1293 	}
1294 }
1295 
1296 
1297 void
1298 SATGroup::_BottomNeighbours(WindowAreaList& neighbourWindows,
1299 	WindowArea* parent)
1300 {
1301 	float startPos = parent->LeftBottomCrossing()->VerticalTab()->Position();
1302 	float endPos = parent->RightBottomCrossing()->VerticalTab()->Position();
1303 
1304 	Tab* tab = parent->LeftBottomCrossing()->HorizontalTab();
1305 	const CrossingList* crossingList = tab->GetCrossingList();
1306 	for (int i = 0; i < crossingList->CountItems(); i++) {
1307 		Corner* corner = crossingList->ItemAt(i)->LeftBottomCorner();
1308 		if (corner->status != Corner::kUsed)
1309 			continue;
1310 
1311 		WindowArea* area = corner->windowArea;
1312 		float pos1 = area->LeftBottomCrossing()->VerticalTab()->Position();
1313 		float pos2 = area->RightBottomCrossing()->VerticalTab()->Position();
1314 
1315 		if (pos1 < endPos && pos2 > startPos)
1316 			neighbourWindows.AddItem(area);
1317 
1318 		if (pos2 > endPos)
1319 			break;
1320 	}
1321 }
1322 
1323 
1324 bool
1325 SATGroup::_FindConnectedGroup(WindowAreaList& seedList, WindowArea* removedArea,
1326 	WindowAreaList& newGroup)
1327 {
1328 	if (seedList.CountItems() == 0)
1329 		return false;
1330 
1331 	WindowArea* area = seedList.RemoveItemAt(0);
1332 	newGroup.AddItem(area);
1333 
1334 	_FollowSeed(area, removedArea, seedList, newGroup);
1335 	return true;
1336 }
1337 
1338 
1339 void
1340 SATGroup::_FollowSeed(WindowArea* area, WindowArea* veto,
1341 	WindowAreaList& seedList, WindowAreaList& newGroup)
1342 {
1343 	WindowAreaList neighbours;
1344 	_FillNeighbourList(neighbours, area);
1345 	for (int i = 0; i < neighbours.CountItems(); i++) {
1346 		WindowArea* currentArea = neighbours.ItemAt(i);
1347 		if (currentArea != veto && !newGroup.HasItem(currentArea)) {
1348 			newGroup.AddItem(currentArea);
1349 			// if we get a area from the seed list it is not a seed any more
1350 			seedList.RemoveItem(currentArea);
1351 		}
1352 		else {
1353 			// don't _FollowSeed of invalid areas
1354 			neighbours.RemoveItemAt(i);
1355 			i--;
1356 		}
1357 	}
1358 
1359 	for (int i = 0; i < neighbours.CountItems(); i++)
1360 		_FollowSeed(neighbours.ItemAt(i), veto, seedList, newGroup);
1361 }
1362 
1363 
1364 void
1365 SATGroup::_SpawnNewGroup(const WindowAreaList& newGroup)
1366 {
1367 	STRACE_SAT("SATGroup::_SpawnNewGroup\n");
1368 	SATGroup* group = new (std::nothrow)SATGroup;
1369 	if (!group)
1370 		return;
1371 	BReference<SATGroup> groupRef;
1372 	groupRef.SetTo(group, true);
1373 
1374 	for (int i = 0; i < newGroup.CountItems(); i++)
1375 		newGroup.ItemAt(i)->PropagateToGroup(group);
1376 
1377 	_EnsureGroupIsOnScreen(group);
1378 }
1379 
1380 
1381 const float kMinOverlap = 50;
1382 const float kMoveToScreen = 75;
1383 
1384 
1385 void
1386 SATGroup::_EnsureGroupIsOnScreen(SATGroup* group)
1387 {
1388 	STRACE_SAT("SATGroup::_EnsureGroupIsOnScreen\n");
1389 	if (!group)
1390 		return;
1391 
1392 	if (group->CountItems() < 1)
1393 		return;
1394 
1395 	SATWindow* window = group->WindowAt(0);
1396 	Desktop* desktop = window->GetWindow()->Desktop();
1397 	if (!desktop)
1398 		return;
1399 
1400 	const float kBigDistance = 1E+10;
1401 
1402 	float minLeftDistance = kBigDistance;
1403 	BRect leftRect;
1404 	float minTopDistance = kBigDistance;
1405 	BRect topRect;
1406 	float minRightDistance = kBigDistance;
1407 	BRect rightRect;
1408 	float minBottomDistance = kBigDistance;
1409 	BRect bottomRect;
1410 
1411 	BRect screen = window->GetWindow()->Screen()->Frame();
1412 	BRect reducedScreen = screen;
1413 	reducedScreen.InsetBy(kMinOverlap, kMinOverlap);
1414 
1415 	for (int i = 0; i < group->CountItems(); i++) {
1416 		SATWindow* window = group->WindowAt(i);
1417 		BRect frame = window->CompleteWindowFrame();
1418 		if (reducedScreen.Intersects(frame))
1419 			return;
1420 
1421 		if (frame.right < screen.left + kMinOverlap) {
1422 			float dist = fabs(screen.left - frame.right);
1423 			if (dist < minLeftDistance) {
1424 				minLeftDistance = dist;
1425 				leftRect = frame;
1426 			}
1427 			else if (dist == minLeftDistance)
1428 				leftRect = leftRect | frame;
1429 		}
1430 		if (frame.top > screen.bottom - kMinOverlap) {
1431 			float dist = fabs(frame.top - screen.bottom);
1432 			if (dist < minBottomDistance) {
1433 				minBottomDistance = dist;
1434 				bottomRect = frame;
1435 			}
1436 			else if (dist == minBottomDistance)
1437 				bottomRect = bottomRect | frame;
1438 		}
1439 		if (frame.left > screen.right - kMinOverlap) {
1440 			float dist = fabs(frame.left - screen.right);
1441 			if (dist < minRightDistance) {
1442 				minRightDistance = dist;
1443 				rightRect = frame;
1444 			}
1445 			else if (dist == minRightDistance)
1446 				rightRect = rightRect | frame;
1447 		}
1448 		if (frame.bottom < screen.top + kMinOverlap) {
1449 			float dist = fabs(frame.bottom - screen.top);
1450 			if (dist < minTopDistance) {
1451 				minTopDistance = dist;
1452 				topRect = frame;
1453 			}
1454 			else if (dist == minTopDistance)
1455 				topRect = topRect | frame;
1456 		}
1457 	}
1458 
1459 	BPoint offset;
1460 	if (minLeftDistance < kBigDistance) {
1461 		offset.x = screen.left - leftRect.right + kMoveToScreen;
1462 		_CallculateYOffset(offset, leftRect, screen);
1463 	}
1464 	else if (minTopDistance < kBigDistance) {
1465 		offset.y = screen.top - topRect.bottom + kMoveToScreen;
1466 		_CallculateXOffset(offset, topRect, screen);
1467 	}
1468 	else if (minRightDistance < kBigDistance) {
1469 		offset.x = screen.right - rightRect.left - kMoveToScreen;
1470 		_CallculateYOffset(offset, rightRect, screen);
1471 	}
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
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
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