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