xref: /haiku/src/servers/app/stackandtile/StackAndTile.cpp (revision ceafe5b48a249b02738ad4ad22d95e74f94c9c8f)
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 "StackAndTile.h"
12 
13 #include <Debug.h>
14 
15 #include "StackAndTilePrivate.h"
16 
17 #include "Desktop.h"
18 #include "SATWindow.h"
19 #include "Tiling.h"
20 #include "Window.h"
21 
22 
23 static const int32 kRightOptionKey	= 0x67;
24 static const int32 kTabKey			= 0x26;
25 static const int32 kPageUpKey		= 0x21;
26 static const int32 kPageDownKey		= 0x36;
27 static const int32 kLeftArrowKey	= 0x61;
28 static const int32 kUpArrowKey		= 0x57;
29 static const int32 kRightArrowKey	= 0x63;
30 static const int32 kDownArrowKey	= 0x62;
31 
32 static const int32 kModifiers = B_SHIFT_KEY | B_COMMAND_KEY
33 	| B_CONTROL_KEY | B_OPTION_KEY | B_MENU_KEY;
34 
35 
36 using namespace std;
37 
38 
39 //	#pragma mark - StackAndTile
40 
41 
StackAndTile()42 StackAndTile::StackAndTile()
43 	:
44 	fDesktop(NULL),
45 	fSATKeyPressed(false),
46 	fCurrentSATWindow(NULL)
47 {
48 
49 }
50 
51 
~StackAndTile()52 StackAndTile::~StackAndTile()
53 {
54 
55 }
56 
57 
58 int32
Identifier()59 StackAndTile::Identifier()
60 {
61 	return BPrivate::kMagicSATIdentifier;
62 }
63 
64 
65 void
ListenerRegistered(Desktop * desktop)66 StackAndTile::ListenerRegistered(Desktop* desktop)
67 {
68 	fDesktop = desktop;
69 
70 	WindowList& windows = desktop->AllWindows();
71 	for (Window *window = windows.FirstWindow(); window != NULL;
72 			window = window->NextWindow(kAllWindowList))
73 		WindowAdded(window);
74 }
75 
76 
77 void
ListenerUnregistered()78 StackAndTile::ListenerUnregistered()
79 {
80 	for (SATWindowMap::iterator it = fSATWindowMap.begin();
81 		it != fSATWindowMap.end(); it++) {
82 		SATWindow* satWindow = it->second;
83 		delete satWindow;
84 	}
85 	fSATWindowMap.clear();
86 }
87 
88 
89 bool
HandleMessage(Window * sender,BPrivate::LinkReceiver & link,BPrivate::LinkSender & reply)90 StackAndTile::HandleMessage(Window* sender, BPrivate::LinkReceiver& link,
91 	BPrivate::LinkSender& reply)
92 {
93 	if (sender == NULL)
94 		return _HandleMessage(link, reply);
95 
96 	SATWindow* satWindow = GetSATWindow(sender);
97 	if (!satWindow)
98 		return false;
99 
100 	return satWindow->HandleMessage(satWindow, link, reply);
101 }
102 
103 
104 void
WindowAdded(Window * window)105 StackAndTile::WindowAdded(Window* window)
106 {
107 	SATWindow* satWindow = new (std::nothrow)SATWindow(this, window);
108 	if (!satWindow)
109 		return;
110 
111 	ASSERT(fSATWindowMap.find(window) == fSATWindowMap.end());
112 	fSATWindowMap[window] = satWindow;
113 }
114 
115 
116 void
WindowRemoved(Window * window)117 StackAndTile::WindowRemoved(Window* window)
118 {
119 	STRACE_SAT("StackAndTile::WindowRemoved %s\n", window->Title());
120 
121 	SATWindowMap::iterator it = fSATWindowMap.find(window);
122 	if (it == fSATWindowMap.end())
123 		return;
124 
125 	SATWindow* satWindow = it->second;
126 	// delete SATWindow
127 	delete satWindow;
128 	fSATWindowMap.erase(it);
129 }
130 
131 
132 bool
KeyPressed(uint32 what,int32 key,int32 modifiers)133 StackAndTile::KeyPressed(uint32 what, int32 key, int32 modifiers)
134 {
135 	if (what == B_MODIFIERS_CHANGED
136 		|| (what == B_UNMAPPED_KEY_DOWN && key == kRightOptionKey)
137 		|| (what == B_UNMAPPED_KEY_UP && key == kRightOptionKey)) {
138 		// switch to and from stacking and snapping mode
139 		bool wasPressed = fSATKeyPressed;
140 		fSATKeyPressed = (what == B_MODIFIERS_CHANGED
141 				&& (modifiers & kModifiers) == B_OPTION_KEY)
142 			|| (what == B_UNMAPPED_KEY_DOWN && key == kRightOptionKey);
143 		if (wasPressed && !fSATKeyPressed)
144 			_StopSAT();
145 		if (!wasPressed && fSATKeyPressed)
146 			_StartSAT();
147 	}
148 
149 	if (!SATKeyPressed() || what != B_KEY_DOWN)
150 		return false;
151 
152 	SATWindow* frontWindow = GetSATWindow(fDesktop->FocusWindow());
153 	SATGroup* currentGroup = _GetSATGroup(frontWindow);
154 
155 	switch (key) {
156 		case kLeftArrowKey:
157 		case kRightArrowKey:
158 		case kTabKey:
159 		{
160 			// go to previous or next window tab in current window group
161 			if (currentGroup == NULL)
162 				return false;
163 
164 			int32 groupSize = currentGroup->CountItems();
165 			if (groupSize <= 1)
166 				return false;
167 
168 			for (int32 i = 0; i < groupSize; i++) {
169 				SATWindow* targetWindow = currentGroup->WindowAt(i);
170 				if (targetWindow == frontWindow) {
171 					if (key == kLeftArrowKey
172 						|| (key == kTabKey && (modifiers & B_SHIFT_KEY) != 0)) {
173 						// Go to previous window tab (wrap around)
174 						int32 previousIndex = i > 0 ? i - 1 : groupSize - 1;
175 						targetWindow = currentGroup->WindowAt(previousIndex);
176 					} else {
177 						// Go to next window tab (wrap around)
178 						int32 nextIndex = i < groupSize - 1 ? i + 1 : 0;
179 						targetWindow = currentGroup->WindowAt(nextIndex);
180 					}
181 
182 					_ActivateWindow(targetWindow);
183 					return true;
184 				}
185 			}
186 			break;
187 		}
188 
189 		case kUpArrowKey:
190 		case kPageUpKey:
191 		{
192 			// go to previous window group
193 			GroupIterator groups(this, fDesktop);
194 			groups.SetCurrentGroup(currentGroup);
195 			SATGroup* backmostGroup = NULL;
196 
197 			while (true) {
198 				SATGroup* group = groups.NextGroup();
199 				if (group == NULL || group == currentGroup)
200 					break;
201 				else if (group->CountItems() < 1)
202 					continue;
203 
204 				if (currentGroup == NULL) {
205 					SATWindow* activeWindow = group->ActiveWindow();
206 					if (activeWindow != NULL)
207 						_ActivateWindow(activeWindow);
208 					else
209 						_ActivateWindow(group->WindowAt(0));
210 
211 					return true;
212 				}
213 				backmostGroup = group;
214 			}
215 			if (backmostGroup != NULL && backmostGroup != currentGroup) {
216 				SATWindow* activeWindow = backmostGroup->ActiveWindow();
217 				if (activeWindow != NULL)
218 					_ActivateWindow(activeWindow);
219 				else
220 					_ActivateWindow(backmostGroup->WindowAt(0));
221 
222 				return true;
223 			}
224 
225 			break;
226 		}
227 
228 		case kDownArrowKey:
229 		case kPageDownKey:
230 		{
231 			// go to next window group
232 			GroupIterator groups(this, fDesktop);
233 			groups.SetCurrentGroup(currentGroup);
234 
235 			while (true) {
236 				SATGroup* group = groups.NextGroup();
237 				if (group == NULL || group == currentGroup)
238 					break;
239 				else if (group->CountItems() < 1)
240 					continue;
241 
242 				SATWindow* activeWindow = group->ActiveWindow();
243 				if (activeWindow != NULL)
244 					_ActivateWindow(activeWindow);
245 				else
246 					_ActivateWindow(group->WindowAt(0));
247 
248 				if (currentGroup != NULL && frontWindow != NULL) {
249 					Window* window = frontWindow->GetWindow();
250 					fDesktop->SendWindowBehind(window);
251 					WindowSentBehind(window, NULL);
252 				}
253 				return true;
254 			}
255 			break;
256 		}
257 	}
258 
259 	return false;
260 }
261 
262 
263 void
MouseDown(Window * window,BMessage * message,const BPoint & where)264 StackAndTile::MouseDown(Window* window, BMessage* message, const BPoint& where)
265 {
266 	SATWindow* satWindow = GetSATWindow(window);
267 	if (!satWindow || !satWindow->GetDecorator())
268 		return;
269 
270 	// fCurrentSATWindow is not zero if e.g. the secondary and the primary
271 	// mouse button are pressed at the same time
272 	if ((message->FindInt32("buttons") & B_PRIMARY_MOUSE_BUTTON) == 0 ||
273 		fCurrentSATWindow != NULL)
274 		return;
275 
276 	// we are only interested in single clicks
277 	if (message->FindInt32("clicks") == 2)
278 		return;
279 
280 	int32 tab;
281 	switch (satWindow->GetDecorator()->RegionAt(where, tab)) {
282 		case Decorator::REGION_TAB:
283 		case Decorator::REGION_LEFT_BORDER:
284 		case Decorator::REGION_RIGHT_BORDER:
285 		case Decorator::REGION_TOP_BORDER:
286 		case Decorator::REGION_BOTTOM_BORDER:
287 		case Decorator::REGION_LEFT_TOP_CORNER:
288 		case Decorator::REGION_LEFT_BOTTOM_CORNER:
289 		case Decorator::REGION_RIGHT_TOP_CORNER:
290 		case Decorator::REGION_RIGHT_BOTTOM_CORNER:
291 			break;
292 
293 		default:
294 			return;
295 	}
296 
297 	ASSERT(fCurrentSATWindow == NULL);
298 	fCurrentSATWindow = satWindow;
299 
300 	if (!SATKeyPressed())
301 		return;
302 
303 	_StartSAT();
304 }
305 
306 
307 void
MouseUp(Window * window,BMessage * message,const BPoint & where)308 StackAndTile::MouseUp(Window* window, BMessage* message, const BPoint& where)
309 {
310 	if (fSATKeyPressed)
311 		_StopSAT();
312 
313 	fCurrentSATWindow = NULL;
314 }
315 
316 
317 void
WindowMoved(Window * window)318 StackAndTile::WindowMoved(Window* window)
319 {
320 	SATWindow* satWindow = GetSATWindow(window);
321 	if (satWindow == NULL)
322 		return;
323 
324 	if (SATKeyPressed() && fCurrentSATWindow)
325 		satWindow->FindSnappingCandidates();
326 	else
327 		satWindow->DoGroupLayout();
328 }
329 
330 
331 void
WindowResized(Window * window)332 StackAndTile::WindowResized(Window* window)
333 {
334 	SATWindow* satWindow = GetSATWindow(window);
335 	if (satWindow == NULL)
336 		return;
337 	satWindow->Resized();
338 
339 	if (SATKeyPressed() && fCurrentSATWindow)
340 		satWindow->FindSnappingCandidates();
341 	else
342 		satWindow->DoGroupLayout();
343 }
344 
345 
346 void
WindowActivated(Window * window)347 StackAndTile::WindowActivated(Window* window)
348 {
349 	SATWindow* satWindow = GetSATWindow(window);
350 	if (satWindow == NULL)
351 		return;
352 
353 	_ActivateWindow(satWindow);
354 }
355 
356 
357 void
WindowSentBehind(Window * window,Window * behindOf)358 StackAndTile::WindowSentBehind(Window* window, Window* behindOf)
359 {
360 	SATWindow* satWindow = GetSATWindow(window);
361 	if (satWindow == NULL)
362 		return;
363 
364 	SATGroup* group = satWindow->GetGroup();
365 	if (group == NULL)
366 		return;
367 
368 	Desktop* desktop = satWindow->GetWindow()->Desktop();
369 	if (desktop == NULL)
370 		return;
371 
372 	const WindowAreaList& areaList = group->GetAreaList();
373 	for (int32 i = 0; i < areaList.CountItems(); i++) {
374 		WindowArea* area = areaList.ItemAt(i);
375 		SATWindow* topWindow = area->TopWindow();
376 		if (topWindow == NULL || topWindow == satWindow)
377 			continue;
378 		desktop->SendWindowBehind(topWindow->GetWindow(), behindOf);
379 	}
380 }
381 
382 
383 void
WindowWorkspacesChanged(Window * window,uint32 workspaces)384 StackAndTile::WindowWorkspacesChanged(Window* window, uint32 workspaces)
385 {
386 	SATWindow* satWindow = GetSATWindow(window);
387 	if (satWindow == NULL)
388 		return;
389 
390 	SATGroup* group = satWindow->GetGroup();
391 	if (group == NULL)
392 		return;
393 
394 	Desktop* desktop = satWindow->GetWindow()->Desktop();
395 	if (desktop == NULL)
396 		return;
397 
398 	const WindowAreaList& areaList = group->GetAreaList();
399 	for (int32 i = 0; i < areaList.CountItems(); i++) {
400 		WindowArea* area = areaList.ItemAt(i);
401 		if (area->WindowList().HasItem(satWindow))
402 			continue;
403 		SATWindow* topWindow = area->TopWindow();
404 		desktop->SetWindowWorkspaces(topWindow->GetWindow(), workspaces);
405 	}
406 }
407 
408 
409 void
WindowHidden(Window * window,bool fromMinimize)410 StackAndTile::WindowHidden(Window* window, bool fromMinimize)
411 {
412 	SATWindow* satWindow = GetSATWindow(window);
413 	if (satWindow == NULL)
414 		return;
415 
416 	SATGroup* group = satWindow->GetGroup();
417 	if (group == NULL)
418 		return;
419 
420 	if (fromMinimize == false && group->CountItems() > 1)
421 		group->RemoveWindow(satWindow, false);
422 }
423 
424 
425 void
WindowMinimized(Window * window,bool minimize)426 StackAndTile::WindowMinimized(Window* window, bool minimize)
427 {
428 	SATWindow* satWindow = GetSATWindow(window);
429 	if (satWindow == NULL)
430 		return;
431 
432 	SATGroup* group = satWindow->GetGroup();
433 	if (group == NULL)
434 		return;
435 
436 	Desktop* desktop = satWindow->GetWindow()->Desktop();
437 	if (desktop == NULL)
438 		return;
439 
440 	for (int i = 0; i < group->CountItems(); i++) {
441 		SATWindow* listWindow = group->WindowAt(i);
442 		if (listWindow != satWindow)
443 			listWindow->GetWindow()->ServerWindow()->NotifyMinimize(minimize);
444 	}
445 }
446 
447 
448 void
WindowTabLocationChanged(Window * window,float location,bool isShifting)449 StackAndTile::WindowTabLocationChanged(Window* window, float location,
450 	bool isShifting)
451 {
452 
453 }
454 
455 
456 void
SizeLimitsChanged(Window * window,int32 minWidth,int32 maxWidth,int32 minHeight,int32 maxHeight)457 StackAndTile::SizeLimitsChanged(Window* window, int32 minWidth, int32 maxWidth,
458 	int32 minHeight, int32 maxHeight)
459 {
460 	SATWindow* satWindow = GetSATWindow(window);
461 	if (!satWindow)
462 		return;
463 	satWindow->SetOriginalSizeLimits(minWidth, maxWidth, minHeight, maxHeight);
464 
465 	// trigger a relayout
466 	WindowMoved(window);
467 }
468 
469 
470 void
WindowLookChanged(Window * window,window_look look)471 StackAndTile::WindowLookChanged(Window* window, window_look look)
472 {
473 	SATWindow* satWindow = GetSATWindow(window);
474 	if (!satWindow)
475 		return;
476 	satWindow->WindowLookChanged(look);
477 }
478 
479 
480 void
WindowFeelChanged(Window * window,window_feel feel)481 StackAndTile::WindowFeelChanged(Window* window, window_feel feel)
482 {
483 	// check if it is still a compatible feel
484 	if (feel == B_NORMAL_WINDOW_FEEL)
485 		return;
486 	SATWindow* satWindow = GetSATWindow(window);
487 	if (satWindow == NULL)
488 		return;
489 
490 	SATGroup* group = satWindow->GetGroup();
491 	if (group == NULL)
492 		return;
493 
494 	if (group->CountItems() > 1)
495 		group->RemoveWindow(satWindow, false);
496 }
497 
498 
499 bool
SetDecoratorSettings(Window * window,const BMessage & settings)500 StackAndTile::SetDecoratorSettings(Window* window, const BMessage& settings)
501 {
502 	SATWindow* satWindow = GetSATWindow(window);
503 	if (!satWindow)
504 		return false;
505 
506 	return satWindow->SetSettings(settings);
507 }
508 
509 
510 void
GetDecoratorSettings(Window * window,BMessage & settings)511 StackAndTile::GetDecoratorSettings(Window* window, BMessage& settings)
512 {
513 	SATWindow* satWindow = GetSATWindow(window);
514 	if (!satWindow)
515 		return;
516 
517 	satWindow->GetSettings(settings);
518 }
519 
520 
521 SATWindow*
GetSATWindow(Window * window)522 StackAndTile::GetSATWindow(Window* window)
523 {
524 	if (window == NULL)
525 		return NULL;
526 
527 	SATWindowMap::const_iterator it = fSATWindowMap.find(
528 		window);
529 	if (it != fSATWindowMap.end())
530 		return it->second;
531 
532 	// TODO fix race condition with WindowAdded this method is called before
533 	// WindowAdded and a SATWindow is created twice!
534 	return NULL;
535 
536 	// If we don't know this window, memory allocation might has been failed
537 	// previously. Try to add the window now.
538 	SATWindow* satWindow = new (std::nothrow)SATWindow(this, window);
539 	if (satWindow)
540 		fSATWindowMap[window] = satWindow;
541 
542 	return satWindow;
543 }
544 
545 
546 SATWindow*
FindSATWindow(uint64 id)547 StackAndTile::FindSATWindow(uint64 id)
548 {
549 	for (SATWindowMap::const_iterator it = fSATWindowMap.begin();
550 		it != fSATWindowMap.end(); it++) {
551 		SATWindow* window = it->second;
552 		if (window->Id() == id)
553 			return window;
554 	}
555 
556 	return NULL;
557 }
558 
559 
560 //	#pragma mark - StackAndTile private methods
561 
562 
563 void
_StartSAT()564 StackAndTile::_StartSAT()
565 {
566 	STRACE_SAT("StackAndTile::_StartSAT()\n");
567 	if (!fCurrentSATWindow)
568 		return;
569 
570 	// Remove window from the group.
571 	SATGroup* group = fCurrentSATWindow->GetGroup();
572 	if (group == NULL)
573 		return;
574 
575 	group->RemoveWindow(fCurrentSATWindow, false);
576 	// Bring window to the front. (in focus follow mouse this is not
577 	// automatically the case)
578 	_ActivateWindow(fCurrentSATWindow);
579 
580 	fCurrentSATWindow->FindSnappingCandidates();
581 }
582 
583 
584 void
_StopSAT()585 StackAndTile::_StopSAT()
586 {
587 	STRACE_SAT("StackAndTile::_StopSAT()\n");
588 	if (!fCurrentSATWindow)
589 		return;
590 	if (fCurrentSATWindow->JoinCandidates())
591 		_ActivateWindow(fCurrentSATWindow);
592 }
593 
594 
595 void
_ActivateWindow(SATWindow * satWindow)596 StackAndTile::_ActivateWindow(SATWindow* satWindow)
597 {
598 	if (satWindow == NULL)
599 		return;
600 
601 	SATGroup* group = satWindow->GetGroup();
602 	if (group == NULL)
603 		return;
604 
605 	Desktop* desktop = satWindow->GetWindow()->Desktop();
606 	if (desktop == NULL)
607 		return;
608 
609 	WindowArea* area = satWindow->GetWindowArea();
610 	if (area == NULL)
611 		return;
612 
613 	area->MoveToTopLayer(satWindow);
614 
615 	// save the active window of the current group
616 	SATWindow* frontWindow = GetSATWindow(fDesktop->FocusWindow());
617 	SATGroup* currentGroup = _GetSATGroup(frontWindow);
618 	if (currentGroup != NULL && currentGroup != group && frontWindow != NULL)
619 		currentGroup->SetActiveWindow(frontWindow);
620 	else
621 		group->SetActiveWindow(satWindow);
622 
623 	const WindowAreaList& areas = group->GetAreaList();
624 	int32 areasCount = areas.CountItems();
625 	for (int32 i = 0; i < areasCount; i++) {
626 		WindowArea* currentArea = areas.ItemAt(i);
627 		if (currentArea == area)
628 			continue;
629 
630 		desktop->ActivateWindow(currentArea->TopWindow()->GetWindow());
631 	}
632 
633 	desktop->ActivateWindow(satWindow->GetWindow());
634 }
635 
636 
637 bool
_HandleMessage(BPrivate::LinkReceiver & link,BPrivate::LinkSender & reply)638 StackAndTile::_HandleMessage(BPrivate::LinkReceiver& link,
639 	BPrivate::LinkSender& reply)
640 {
641 	int32 what;
642 	link.Read<int32>(&what);
643 
644 	switch (what) {
645 		case BPrivate::kSaveAllGroups:
646 		{
647 			BMessage allGroupsArchive;
648 			GroupIterator groups(this, fDesktop);
649 			while (true) {
650 				SATGroup* group = groups.NextGroup();
651 				if (group == NULL)
652 					break;
653 				if (group->CountItems() <= 1)
654 					continue;
655 				BMessage groupArchive;
656 				if (group->ArchiveGroup(groupArchive) != B_OK)
657 					continue;
658 				allGroupsArchive.AddMessage("group", &groupArchive);
659 			}
660 			int32 size = allGroupsArchive.FlattenedSize();
661 			char buffer[size];
662 			if (allGroupsArchive.Flatten(buffer, size) == B_OK) {
663 				reply.StartMessage(B_OK);
664 				reply.Attach<int32>(size);
665 				reply.Attach(buffer, size);
666 			} else
667 				reply.StartMessage(B_ERROR);
668 			reply.Flush();
669 			break;
670 		}
671 
672 		case BPrivate::kRestoreGroup:
673 		{
674 			int32 size;
675 			if (link.Read<int32>(&size) == B_OK) {
676 				char buffer[size];
677 				BMessage group;
678 				if (link.Read(buffer, size) == B_OK
679 					&& group.Unflatten(buffer) == B_OK) {
680 					status_t status = SATGroup::RestoreGroup(group, this);
681 					reply.StartMessage(status);
682 					reply.Flush();
683 				}
684 			}
685 			break;
686 		}
687 
688 		default:
689 			return false;
690 	}
691 
692 	return true;
693 }
694 
695 
696 SATGroup*
_GetSATGroup(SATWindow * window)697 StackAndTile::_GetSATGroup(SATWindow* window)
698 {
699 	if (window == NULL)
700 		return NULL;
701 
702 	SATGroup* group = window->GetGroup();
703 	if (group == NULL)
704 		return NULL;
705 
706 	if (group->CountItems() < 1)
707 		return NULL;
708 
709 	return group;
710 }
711 
712 
713 //	#pragma mark - GroupIterator
714 
715 
GroupIterator(StackAndTile * sat,Desktop * desktop)716 GroupIterator::GroupIterator(StackAndTile* sat, Desktop* desktop)
717 	:
718 	fStackAndTile(sat),
719 	fDesktop(desktop),
720 	fCurrentGroup(NULL)
721 {
722 	RewindToFront();
723 }
724 
725 
726 void
RewindToFront()727 GroupIterator::RewindToFront()
728 {
729 	fCurrentWindow = fDesktop->CurrentWindows().LastWindow();
730 }
731 
732 
733 SATGroup*
NextGroup()734 GroupIterator::NextGroup()
735 {
736 	SATGroup* group = NULL;
737 	do {
738 		Window* window = fCurrentWindow;
739 		if (window == NULL) {
740 			group = NULL;
741 			break;
742 		}
743 		fCurrentWindow = fCurrentWindow->PreviousWindow(
744 			fCurrentWindow->CurrentWorkspace());
745 		if (window->IsHidden()
746 			|| strcmp(window->Title(), "Deskbar") == 0
747 			|| strcmp(window->Title(), "Desktop") == 0) {
748 			continue;
749 		}
750 
751 		SATWindow* satWindow = fStackAndTile->GetSATWindow(window);
752 		group = satWindow->GetGroup();
753 	} while (group == NULL || fCurrentGroup == group);
754 
755 	fCurrentGroup = group;
756 	return fCurrentGroup;
757 }
758 
759 
760 //	#pragma mark - WindowIterator
761 
762 
WindowIterator(SATGroup * group,bool reverseLayerOrder)763 WindowIterator::WindowIterator(SATGroup* group, bool reverseLayerOrder)
764 	:
765 	fGroup(group),
766 	fReverseLayerOrder(reverseLayerOrder)
767 {
768 	if (fReverseLayerOrder)
769 		_ReverseRewind();
770 	else
771 		Rewind();
772 }
773 
774 
775 void
Rewind()776 WindowIterator::Rewind()
777 {
778 	fAreaIndex = 0;
779 	fWindowIndex = 0;
780 	fCurrentArea = fGroup->GetAreaList().ItemAt(fAreaIndex);
781 }
782 
783 
784 SATWindow*
NextWindow()785 WindowIterator::NextWindow()
786 {
787 	if (fReverseLayerOrder)
788 		return _ReverseNextWindow();
789 
790 	if (fWindowIndex == fCurrentArea->LayerOrder().CountItems()) {
791 		fAreaIndex++;
792 		fWindowIndex = 0;
793 		fCurrentArea = fGroup->GetAreaList().ItemAt(fAreaIndex);
794 		if (!fCurrentArea)
795 			return NULL;
796 	}
797 	SATWindow* window = fCurrentArea->LayerOrder().ItemAt(fWindowIndex);
798 	fWindowIndex++;
799 	return window;
800 }
801 
802 
803 //	#pragma mark - WindowIterator private methods
804 
805 
806 SATWindow*
_ReverseNextWindow()807 WindowIterator::_ReverseNextWindow()
808 {
809 	if (fWindowIndex < 0) {
810 		fAreaIndex++;
811 		fCurrentArea = fGroup->GetAreaList().ItemAt(fAreaIndex);
812 		if (!fCurrentArea)
813 			return NULL;
814 		fWindowIndex = fCurrentArea->LayerOrder().CountItems() - 1;
815 	}
816 	SATWindow* window = fCurrentArea->LayerOrder().ItemAt(fWindowIndex);
817 	fWindowIndex--;
818 	return window;
819 }
820 
821 
822 void
_ReverseRewind()823 WindowIterator::_ReverseRewind()
824 {
825 	Rewind();
826 	if (fCurrentArea)
827 		fWindowIndex = fCurrentArea->LayerOrder().CountItems() - 1;
828 }
829