2 * Copyright 2010-2014 Haiku, Inc. All rights reserved.
3 * Distributed under the terms of the MIT License.
6 * John Scipione, jscipione@gmail.com
7 * Clemens Zeidler, haiku@clemens-zeidler.de
11 #include "StackAndTile.h"
15 #include "StackAndTilePrivate.h"
18 #include "SATWindow.h"
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;
32 static const int32 kModifiers
= B_SHIFT_KEY
| B_COMMAND_KEY
33 | B_CONTROL_KEY
| B_OPTION_KEY
| B_MENU_KEY
;
39 // #pragma mark - StackAndTile
42 StackAndTile::StackAndTile()
45 fSATKeyPressed(false),
46 fCurrentSATWindow(NULL
)
52 StackAndTile::~StackAndTile()
59 StackAndTile::Identifier()
61 return BPrivate::kMagicSATIdentifier
;
66 StackAndTile::ListenerRegistered(Desktop
* desktop
)
70 WindowList
& windows
= desktop
->AllWindows();
71 for (Window
*window
= windows
.FirstWindow(); window
!= NULL
;
72 window
= window
->NextWindow(kAllWindowList
))
78 StackAndTile::ListenerUnregistered()
80 for (SATWindowMap::iterator it
= fSATWindowMap
.begin();
81 it
!= fSATWindowMap
.end(); it
++) {
82 SATWindow
* satWindow
= it
->second
;
85 fSATWindowMap
.clear();
90 StackAndTile::HandleMessage(Window
* sender
, BPrivate::LinkReceiver
& link
,
91 BPrivate::LinkSender
& reply
)
94 return _HandleMessage(link
, reply
);
96 SATWindow
* satWindow
= GetSATWindow(sender
);
100 return satWindow
->HandleMessage(satWindow
, link
, reply
);
105 StackAndTile::WindowAdded(Window
* window
)
107 SATWindow
* satWindow
= new (std::nothrow
)SATWindow(this, window
);
111 ASSERT(fSATWindowMap
.find(window
) == fSATWindowMap
.end());
112 fSATWindowMap
[window
] = satWindow
;
117 StackAndTile::WindowRemoved(Window
* window
)
119 STRACE_SAT("StackAndTile::WindowRemoved %s\n", window
->Title());
121 SATWindowMap::iterator it
= fSATWindowMap
.find(window
);
122 if (it
== fSATWindowMap
.end())
125 SATWindow
* satWindow
= it
->second
;
128 fSATWindowMap
.erase(it
);
133 StackAndTile::KeyPressed(uint32 what
, int32 key
, int32 modifiers
)
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
)
145 if (!wasPressed
&& fSATKeyPressed
)
149 if (!SATKeyPressed() || what
!= B_KEY_DOWN
)
152 SATWindow
* frontWindow
= GetSATWindow(fDesktop
->FocusWindow());
153 SATGroup
* currentGroup
= _GetSATGroup(frontWindow
);
160 // go to previous or next window tab in current window group
161 if (currentGroup
== NULL
)
164 int32 groupSize
= currentGroup
->CountItems();
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
);
177 // Go to next window tab (wrap around)
178 int32 nextIndex
= i
< groupSize
- 1 ? i
+ 1 : 0;
179 targetWindow
= currentGroup
->WindowAt(nextIndex
);
182 _ActivateWindow(targetWindow
);
192 // go to previous window group
193 GroupIterator
groups(this, fDesktop
);
194 groups
.SetCurrentGroup(currentGroup
);
195 SATGroup
* backmostGroup
= NULL
;
198 SATGroup
* group
= groups
.NextGroup();
199 if (group
== NULL
|| group
== currentGroup
)
201 else if (group
->CountItems() < 1)
204 if (currentGroup
== NULL
) {
205 SATWindow
* activeWindow
= group
->ActiveWindow();
206 if (activeWindow
!= NULL
)
207 _ActivateWindow(activeWindow
);
209 _ActivateWindow(group
->WindowAt(0));
213 backmostGroup
= group
;
215 if (backmostGroup
!= NULL
&& backmostGroup
!= currentGroup
) {
216 SATWindow
* activeWindow
= backmostGroup
->ActiveWindow();
217 if (activeWindow
!= NULL
)
218 _ActivateWindow(activeWindow
);
220 _ActivateWindow(backmostGroup
->WindowAt(0));
231 // go to next window group
232 GroupIterator
groups(this, fDesktop
);
233 groups
.SetCurrentGroup(currentGroup
);
236 SATGroup
* group
= groups
.NextGroup();
237 if (group
== NULL
|| group
== currentGroup
)
239 else if (group
->CountItems() < 1)
242 SATWindow
* activeWindow
= group
->ActiveWindow();
243 if (activeWindow
!= NULL
)
244 _ActivateWindow(activeWindow
);
246 _ActivateWindow(group
->WindowAt(0));
248 if (currentGroup
!= NULL
&& frontWindow
!= NULL
) {
249 Window
* window
= frontWindow
->GetWindow();
250 fDesktop
->SendWindowBehind(window
);
251 WindowSentBehind(window
, NULL
);
264 StackAndTile::MouseDown(Window
* window
, BMessage
* message
, const BPoint
& where
)
266 SATWindow
* satWindow
= GetSATWindow(window
);
267 if (!satWindow
|| !satWindow
->GetDecorator())
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
)
276 // we are only interested in single clicks
277 if (message
->FindInt32("clicks") == 2)
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
:
297 ASSERT(fCurrentSATWindow
== NULL
);
298 fCurrentSATWindow
= satWindow
;
300 if (!SATKeyPressed())
308 StackAndTile::MouseUp(Window
* window
, BMessage
* message
, const BPoint
& where
)
313 fCurrentSATWindow
= NULL
;
318 StackAndTile::WindowMoved(Window
* window
)
320 SATWindow
* satWindow
= GetSATWindow(window
);
321 if (satWindow
== NULL
)
324 if (SATKeyPressed() && fCurrentSATWindow
)
325 satWindow
->FindSnappingCandidates();
327 satWindow
->DoGroupLayout();
332 StackAndTile::WindowResized(Window
* window
)
334 SATWindow
* satWindow
= GetSATWindow(window
);
335 if (satWindow
== NULL
)
337 satWindow
->Resized();
339 if (SATKeyPressed() && fCurrentSATWindow
)
340 satWindow
->FindSnappingCandidates();
342 satWindow
->DoGroupLayout();
347 StackAndTile::WindowActivated(Window
* window
)
349 SATWindow
* satWindow
= GetSATWindow(window
);
350 if (satWindow
== NULL
)
353 _ActivateWindow(satWindow
);
358 StackAndTile::WindowSentBehind(Window
* window
, Window
* behindOf
)
360 SATWindow
* satWindow
= GetSATWindow(window
);
361 if (satWindow
== NULL
)
364 SATGroup
* group
= satWindow
->GetGroup();
368 Desktop
* desktop
= satWindow
->GetWindow()->Desktop();
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
)
378 desktop
->SendWindowBehind(topWindow
->GetWindow(), behindOf
);
384 StackAndTile::WindowWorkspacesChanged(Window
* window
, uint32 workspaces
)
386 SATWindow
* satWindow
= GetSATWindow(window
);
387 if (satWindow
== NULL
)
390 SATGroup
* group
= satWindow
->GetGroup();
394 Desktop
* desktop
= satWindow
->GetWindow()->Desktop();
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
))
403 SATWindow
* topWindow
= area
->TopWindow();
404 desktop
->SetWindowWorkspaces(topWindow
->GetWindow(), workspaces
);
410 StackAndTile::WindowHidden(Window
* window
, bool fromMinimize
)
412 SATWindow
* satWindow
= GetSATWindow(window
);
413 if (satWindow
== NULL
)
416 SATGroup
* group
= satWindow
->GetGroup();
420 if (fromMinimize
== false && group
->CountItems() > 1)
421 group
->RemoveWindow(satWindow
, false);
426 StackAndTile::WindowMinimized(Window
* window
, bool minimize
)
428 SATWindow
* satWindow
= GetSATWindow(window
);
429 if (satWindow
== NULL
)
432 SATGroup
* group
= satWindow
->GetGroup();
436 Desktop
* desktop
= satWindow
->GetWindow()->Desktop();
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
);
449 StackAndTile::WindowTabLocationChanged(Window
* window
, float location
,
457 StackAndTile::SizeLimitsChanged(Window
* window
, int32 minWidth
, int32 maxWidth
,
458 int32 minHeight
, int32 maxHeight
)
460 SATWindow
* satWindow
= GetSATWindow(window
);
463 satWindow
->SetOriginalSizeLimits(minWidth
, maxWidth
, minHeight
, maxHeight
);
465 // trigger a relayout
471 StackAndTile::WindowLookChanged(Window
* window
, window_look look
)
473 SATWindow
* satWindow
= GetSATWindow(window
);
476 satWindow
->WindowLookChanged(look
);
481 StackAndTile::WindowFeelChanged(Window
* window
, window_feel feel
)
483 // check if it is still a compatible feel
484 if (feel
== B_NORMAL_WINDOW_FEEL
)
486 SATWindow
* satWindow
= GetSATWindow(window
);
487 if (satWindow
== NULL
)
490 SATGroup
* group
= satWindow
->GetGroup();
494 if (group
->CountItems() > 1)
495 group
->RemoveWindow(satWindow
, false);
500 StackAndTile::SetDecoratorSettings(Window
* window
, const BMessage
& settings
)
502 SATWindow
* satWindow
= GetSATWindow(window
);
506 return satWindow
->SetSettings(settings
);
511 StackAndTile::GetDecoratorSettings(Window
* window
, BMessage
& settings
)
513 SATWindow
* satWindow
= GetSATWindow(window
);
517 satWindow
->GetSettings(settings
);
522 StackAndTile::GetSATWindow(Window
* window
)
527 SATWindowMap::const_iterator it
= fSATWindowMap
.find(
529 if (it
!= fSATWindowMap
.end())
532 // TODO fix race condition with WindowAdded this method is called before
533 // WindowAdded and a SATWindow is created twice!
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
);
540 fSATWindowMap
[window
] = satWindow
;
547 StackAndTile::FindSATWindow(uint64 id
)
549 for (SATWindowMap::const_iterator it
= fSATWindowMap
.begin();
550 it
!= fSATWindowMap
.end(); it
++) {
551 SATWindow
* window
= it
->second
;
552 if (window
->Id() == id
)
560 // #pragma mark - StackAndTile private methods
564 StackAndTile::_StartSAT()
566 STRACE_SAT("StackAndTile::_StartSAT()\n");
567 if (!fCurrentSATWindow
)
570 // Remove window from the group.
571 SATGroup
* group
= fCurrentSATWindow
->GetGroup();
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
);
580 fCurrentSATWindow
->FindSnappingCandidates();
585 StackAndTile::_StopSAT()
587 STRACE_SAT("StackAndTile::_StopSAT()\n");
588 if (!fCurrentSATWindow
)
590 if (fCurrentSATWindow
->JoinCandidates())
591 _ActivateWindow(fCurrentSATWindow
);
596 StackAndTile::_ActivateWindow(SATWindow
* satWindow
)
598 if (satWindow
== NULL
)
601 SATGroup
* group
= satWindow
->GetGroup();
605 Desktop
* desktop
= satWindow
->GetWindow()->Desktop();
609 WindowArea
* area
= satWindow
->GetWindowArea();
613 area
->MoveToTopLayer(satWindow
);
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
);
621 group
->SetActiveWindow(satWindow
);
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
)
630 desktop
->ActivateWindow(currentArea
->TopWindow()->GetWindow());
633 desktop
->ActivateWindow(satWindow
->GetWindow());
638 StackAndTile::_HandleMessage(BPrivate::LinkReceiver
& link
,
639 BPrivate::LinkSender
& reply
)
642 link
.Read
<int32
>(&what
);
645 case BPrivate::kSaveAllGroups
:
647 BMessage allGroupsArchive
;
648 GroupIterator
groups(this, fDesktop
);
650 SATGroup
* group
= groups
.NextGroup();
653 if (group
->CountItems() <= 1)
655 BMessage groupArchive
;
656 if (group
->ArchiveGroup(groupArchive
) != B_OK
)
658 allGroupsArchive
.AddMessage("group", &groupArchive
);
660 int32 size
= allGroupsArchive
.FlattenedSize();
662 if (allGroupsArchive
.Flatten(buffer
, size
) == B_OK
) {
663 reply
.StartMessage(B_OK
);
664 reply
.Attach
<int32
>(size
);
665 reply
.Attach(buffer
, size
);
667 reply
.StartMessage(B_ERROR
);
672 case BPrivate::kRestoreGroup
:
675 if (link
.Read
<int32
>(&size
) == B_OK
) {
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
);
697 StackAndTile::_GetSATGroup(SATWindow
* window
)
702 SATGroup
* group
= window
->GetGroup();
706 if (group
->CountItems() < 1)
713 // #pragma mark - GroupIterator
716 GroupIterator::GroupIterator(StackAndTile
* sat
, Desktop
* desktop
)
727 GroupIterator::RewindToFront()
729 fCurrentWindow
= fDesktop
->CurrentWindows().LastWindow();
734 GroupIterator::NextGroup()
736 SATGroup
* group
= NULL
;
738 Window
* window
= fCurrentWindow
;
739 if (window
== NULL
) {
743 fCurrentWindow
= fCurrentWindow
->PreviousWindow(
744 fCurrentWindow
->CurrentWorkspace());
745 if (window
->IsHidden()
746 || strcmp(window
->Title(), "Deskbar") == 0
747 || strcmp(window
->Title(), "Desktop") == 0) {
751 SATWindow
* satWindow
= fStackAndTile
->GetSATWindow(window
);
752 group
= satWindow
->GetGroup();
753 } while (group
== NULL
|| fCurrentGroup
== group
);
755 fCurrentGroup
= group
;
756 return fCurrentGroup
;
760 // #pragma mark - WindowIterator
763 WindowIterator::WindowIterator(SATGroup
* group
, bool reverseLayerOrder
)
766 fReverseLayerOrder(reverseLayerOrder
)
768 if (fReverseLayerOrder
)
776 WindowIterator::Rewind()
780 fCurrentArea
= fGroup
->GetAreaList().ItemAt(fAreaIndex
);
785 WindowIterator::NextWindow()
787 if (fReverseLayerOrder
)
788 return _ReverseNextWindow();
790 if (fWindowIndex
== fCurrentArea
->LayerOrder().CountItems()) {
793 fCurrentArea
= fGroup
->GetAreaList().ItemAt(fAreaIndex
);
797 SATWindow
* window
= fCurrentArea
->LayerOrder().ItemAt(fWindowIndex
);
803 // #pragma mark - WindowIterator private methods
807 WindowIterator::_ReverseNextWindow()
809 if (fWindowIndex
< 0) {
811 fCurrentArea
= fGroup
->GetAreaList().ItemAt(fAreaIndex
);
814 fWindowIndex
= fCurrentArea
->LayerOrder().CountItems() - 1;
816 SATWindow
* window
= fCurrentArea
->LayerOrder().ItemAt(fWindowIndex
);
823 WindowIterator::_ReverseRewind()
827 fWindowIndex
= fCurrentArea
->LayerOrder().CountItems() - 1;