xref: /haiku/src/servers/app/stackandtile/StackAndTile.cpp (revision e7be020ce59cd8a50dcb9a782b3b15cfa769396c)
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 }
312 
313 
314 void
315 StackAndTile::WindowWorkspacesChanged(Window* window, uint32 workspaces)
316 {
317 	SATWindow* satWindow = GetSATWindow(window);
318 	if (satWindow == NULL)
319 		return;
320 	SATGroup* group = satWindow->GetGroup();
321 	if (group == NULL)
322 		return;
323 	Desktop* desktop = satWindow->GetWindow()->Desktop();
324 	if (desktop == NULL)
325 		return;
326 
327 	for (int i = 0; i < group->CountItems(); i++) {
328 		SATWindow* listWindow = group->WindowAt(i);
329 		if (listWindow != satWindow)
330 			desktop->SetWindowWorkspaces(listWindow->GetWindow(), workspaces);
331 	}
332 }
333 
334 
335 void
336 StackAndTile::WindowHidden(Window* window, bool fromMinimize)
337 {
338 	SATWindow* satWindow = GetSATWindow(window);
339 	if (satWindow == NULL)
340 		return;
341 	SATGroup* group = satWindow->GetGroup();
342 	if (group == NULL)
343 		return;
344 	if (fromMinimize == false && group->CountItems() > 1)
345 		group->RemoveWindow(satWindow, false);
346 }
347 
348 
349 void
350 StackAndTile::WindowMinimized(Window* window, bool minimize)
351 {
352 	SATWindow* satWindow = GetSATWindow(window);
353 	if (satWindow == NULL)
354 		return;
355 	SATGroup* group = satWindow->GetGroup();
356 	if (group == NULL)
357 		return;
358 	Desktop* desktop = satWindow->GetWindow()->Desktop();
359 	if (desktop == NULL)
360 		return;
361 
362 	for (int i = 0; i < group->CountItems(); i++) {
363 		SATWindow* listWindow = group->WindowAt(i);
364 		if (listWindow != satWindow)
365 			listWindow->GetWindow()->ServerWindow()->NotifyMinimize(minimize);
366 	}
367 }
368 
369 
370 void
371 StackAndTile::WindowTabLocationChanged(Window* window, float location,
372 	bool isShifting)
373 {
374 
375 }
376 
377 
378 void
379 StackAndTile::SizeLimitsChanged(Window* window, int32 minWidth, int32 maxWidth,
380 	int32 minHeight, int32 maxHeight)
381 {
382 	SATWindow* satWindow = GetSATWindow(window);
383 	if (!satWindow)
384 		return;
385 	satWindow->SetOriginalSizeLimits(minWidth, maxWidth, minHeight, maxHeight);
386 
387 	// trigger a relayout
388 	WindowMoved(window);
389 }
390 
391 
392 void
393 StackAndTile::WindowLookChanged(Window* window, window_look look)
394 {
395 	SATWindow* satWindow = GetSATWindow(window);
396 	if (!satWindow)
397 		return;
398 	satWindow->WindowLookChanged(look);
399 }
400 
401 
402 void
403 StackAndTile::WindowFeelChanged(Window* window, window_feel feel)
404 {
405 	// check if it is still a compatible feel
406 	if (feel == B_NORMAL_WINDOW_FEEL)
407 		return;
408 	SATWindow* satWindow = GetSATWindow(window);
409 	if (!satWindow)
410 		return;
411 	SATGroup* group = satWindow->GetGroup();
412 	if (!group)
413 		return;
414 	if (group->CountItems() > 1)
415 		group->RemoveWindow(satWindow, false);
416 }
417 
418 
419 bool
420 StackAndTile::SetDecoratorSettings(Window* window, const BMessage& settings)
421 {
422 	SATWindow* satWindow = GetSATWindow(window);
423 	if (!satWindow)
424 		return false;
425 
426 	return satWindow->SetSettings(settings);
427 }
428 
429 
430 void
431 StackAndTile::GetDecoratorSettings(Window* window, BMessage& settings)
432 {
433 	SATWindow* satWindow = GetSATWindow(window);
434 	if (!satWindow)
435 		return;
436 
437 	satWindow->GetSettings(settings);
438 }
439 
440 
441 SATWindow*
442 StackAndTile::GetSATWindow(Window* window)
443 {
444 	if (window == NULL)
445 		return NULL;
446 
447 	SATWindowMap::const_iterator it = fSATWindowMap.find(
448 		window);
449 	if (it != fSATWindowMap.end())
450 		return it->second;
451 
452 	// TODO fix race condition with WindowAdded this method is called before
453 	// WindowAdded and a SATWindow is created twice!
454 	return NULL;
455 
456 	// If we don't know this window, memory allocation might has been failed
457 	// previously. Try to add the window now.
458 	SATWindow* satWindow = new (std::nothrow)SATWindow(this, window);
459 	if (satWindow)
460 		fSATWindowMap[window] = satWindow;
461 
462 	return satWindow;
463 }
464 
465 
466 SATWindow*
467 StackAndTile::FindSATWindow(uint64 id)
468 {
469 	for (SATWindowMap::const_iterator it = fSATWindowMap.begin();
470 		it != fSATWindowMap.end(); it++) {
471 		SATWindow* window = it->second;
472 		if (window->Id() == id)
473 			return window;
474 	}
475 	return NULL;
476 }
477 
478 
479 void
480 StackAndTile::_StartSAT()
481 {
482 	STRACE_SAT("StackAndTile::_StartSAT()\n");
483 	if (!fCurrentSATWindow)
484 		return;
485 
486 	// Remove window from the group.
487 	SATGroup* group = fCurrentSATWindow->GetGroup();
488 	if (!group)
489 		return;
490 
491 	group->RemoveWindow(fCurrentSATWindow, false);
492 	// Bring window to the front. (in focus follow mouse this is not
493 	// automatically the case)
494 	_ActivateWindow(fCurrentSATWindow);
495 
496 	fCurrentSATWindow->FindSnappingCandidates();
497 }
498 
499 
500 void
501 StackAndTile::_StopSAT()
502 {
503 	STRACE_SAT("StackAndTile::_StopSAT()\n");
504 	if (!fCurrentSATWindow)
505 		return;
506 	if (fCurrentSATWindow->JoinCandidates())
507 		_ActivateWindow(fCurrentSATWindow);
508 }
509 
510 
511 void
512 StackAndTile::_ActivateWindow(SATWindow* satWindow)
513 {
514 	SATGroup* group = satWindow->GetGroup();
515 	if (!group)
516 		return;
517 	Desktop* desktop = satWindow->GetWindow()->Desktop();
518 	if (!desktop)
519 		return;
520 	WindowArea* area = satWindow->GetWindowArea();
521 	if (!area)
522 		return;
523 	area->MoveToTopLayer(satWindow);
524 
525 	const WindowAreaList& areas = group->GetAreaList() ;
526 	for (int32 i = 0; i < areas.CountItems(); i++) {
527 		WindowArea* currentArea = areas.ItemAt(i);
528 		if (currentArea == area)
529 			continue;
530 		desktop->ActivateWindow(currentArea->TopWindow()->GetWindow());
531 	}
532 
533 	desktop->ActivateWindow(satWindow->GetWindow());
534 }
535 
536 
537 bool
538 StackAndTile::_HandleMessage(BPrivate::LinkReceiver& link,
539 	BPrivate::LinkSender& reply)
540 {
541 	int32 what;
542 	link.Read<int32>(&what);
543 
544 	switch (what) {
545 		case BPrivate::kSaveAllGroups:
546 		{
547 			BMessage allGroupsArchive;
548 			GroupIterator groups(this, fDesktop);
549 			while (true) {
550 				SATGroup* group = groups.NextGroup();
551 				if (group == NULL)
552 					break;
553 				if (group->CountItems() <= 1)
554 					continue;
555 				BMessage groupArchive;
556 				if (group->ArchiveGroup(groupArchive) != B_OK)
557 					continue;
558 				allGroupsArchive.AddMessage("group", &groupArchive);
559 			}
560 			int32 size = allGroupsArchive.FlattenedSize();
561 			char buffer[size];
562 			if (allGroupsArchive.Flatten(buffer, size) == B_OK) {
563 				reply.StartMessage(B_OK);
564 				reply.Attach<int32>(size);
565 				reply.Attach(buffer, size);
566 			} else
567 				reply.StartMessage(B_ERROR);
568 			reply.Flush();
569 			break;
570 		}
571 
572 		case BPrivate::kRestoreGroup:
573 		{
574 			int32 size;
575 			if (link.Read<int32>(&size) == B_OK) {
576 				char buffer[size];
577 				BMessage group;
578 				if (link.Read(buffer, size) == B_OK
579 					&& group.Unflatten(buffer) == B_OK) {
580 					status_t status = SATGroup::RestoreGroup(group, this);
581 					reply.StartMessage(status);
582 					reply.Flush();
583 				}
584 			}
585 			break;
586 		}
587 
588 		default:
589 			return false;
590 	}
591 
592 	return true;
593 }
594 
595 
596 GroupIterator::GroupIterator(StackAndTile* sat, Desktop* desktop)
597 	:
598 	fStackAndTile(sat),
599 	fDesktop(desktop),
600 	fCurrentGroup(NULL)
601 {
602 	RewindToFront();
603 }
604 
605 
606 void
607 GroupIterator::RewindToFront()
608 {
609 	fCurrentWindow = fDesktop->CurrentWindows().LastWindow();
610 }
611 
612 
613 SATGroup*
614 GroupIterator::NextGroup()
615 {
616 	SATGroup* group = NULL;
617 	do {
618 		Window* window = fCurrentWindow;
619 		if (window == NULL) {
620 			group = NULL;
621 			break;
622 		}
623 		fCurrentWindow = fCurrentWindow->PreviousWindow(
624 				fCurrentWindow->CurrentWorkspace());
625 		if (window->IsHidden()
626 			|| strcmp(window->Title(), "Deskbar") == 0
627 			|| strcmp(window->Title(), "Desktop") == 0)
628 			continue;
629 
630 		SATWindow* satWindow = fStackAndTile->GetSATWindow(window);
631 		group = satWindow->GetGroup();
632 	} while (group == NULL || fCurrentGroup == group);
633 
634 	fCurrentGroup = group;
635 	return fCurrentGroup;
636 }
637 
638 
639 WindowIterator::WindowIterator(SATGroup* group, bool reverseLayerOrder)
640 	:
641 	fGroup(group),
642 	fReverseLayerOrder(reverseLayerOrder)
643 {
644 	if (fReverseLayerOrder)
645 		_ReverseRewind();
646 	else
647 		Rewind();
648 }
649 
650 
651 void
652 WindowIterator::Rewind()
653 {
654 	fAreaIndex = 0;
655 	fWindowIndex = 0;
656 	fCurrentArea = fGroup->GetAreaList().ItemAt(fAreaIndex);
657 }
658 
659 
660 SATWindow*
661 WindowIterator::NextWindow()
662 {
663 	if (fReverseLayerOrder)
664 		return _ReverseNextWindow();
665 
666 	if (fWindowIndex == fCurrentArea->LayerOrder().CountItems()) {
667 		fAreaIndex++;
668 		fWindowIndex = 0;
669 		fCurrentArea = fGroup->GetAreaList().ItemAt(fAreaIndex);
670 		if (!fCurrentArea)
671 			return NULL;
672 	}
673 	SATWindow* window = fCurrentArea->LayerOrder().ItemAt(fWindowIndex);
674 	fWindowIndex++;
675 	return window;
676 }
677 
678 
679 SATWindow*
680 WindowIterator::_ReverseNextWindow()
681 {
682 	if (fWindowIndex < 0) {
683 		fAreaIndex++;
684 		fCurrentArea = fGroup->GetAreaList().ItemAt(fAreaIndex);
685 		if (!fCurrentArea)
686 			return NULL;
687 		fWindowIndex = fCurrentArea->LayerOrder().CountItems() - 1;
688 	}
689 	SATWindow* window = fCurrentArea->LayerOrder().ItemAt(fWindowIndex);
690 	fWindowIndex--;
691 	return window;
692 }
693 
694 
695 void
696 WindowIterator::_ReverseRewind()
697 {
698 	Rewind();
699 	if (fCurrentArea)
700 		fWindowIndex = fCurrentArea->LayerOrder().CountItems() - 1;
701 }
702 
703 
704 SATSnappingBehaviour::~SATSnappingBehaviour()
705 {
706 
707 }
708