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