xref: /haiku/src/servers/app/stackandtile/Stacking.cpp (revision 6f80a9801fedbe7355c4360bd204ba746ec3ec2d)
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 #include "Stacking.h"
10 
11 #include <Debug.h>
12 
13 #include "StackAndTilePrivate.h"
14 
15 #include "Desktop.h"
16 #include "SATWindow.h"
17 #include "Window.h"
18 
19 
20 //#define DEBUG_STACKING
21 
22 #ifdef DEBUG_STACKING
23 #	define STRACE_STACKING(x...) debug_printf("SAT Stacking: "x)
24 #else
25 #	define STRACE_STACKING(x...) ;
26 #endif
27 
28 
29 using namespace BPrivate;
30 
31 
32 bool
33 StackingEventHandler::HandleMessage(SATWindow* sender,
34 	BPrivate::LinkReceiver& link, BPrivate::LinkSender& reply)
35 {
36 	Desktop* desktop = sender->GetDesktop();
37 	StackAndTile* stackAndTile = sender->GetStackAndTile();
38 
39 	int32 what;
40 	link.Read<int32>(&what);
41 
42 	switch (what) {
43 		case kAddWindowToStack:
44 		{
45 			port_id port;
46 			int32 token;
47 			team_id team;
48 			link.Read<port_id>(&port);
49 			link.Read<int32>(&token);
50 			link.Read<team_id>(&team);
51 			int32 position;
52 			if (link.Read<int32>(&position) != B_OK)
53 				return false;
54 
55 			WindowArea* area = sender->GetWindowArea();
56 			if (!area)
57 				return false;
58 			if (position < 0)
59 				position = area->WindowList().CountItems() - 1;
60 
61 			SATWindow* parent = area->WindowList().ItemAt(position);
62 			Window* window = desktop->WindowForClientLooperPort(port);
63 			if (!parent || !window) {
64 				reply.StartMessage(B_BAD_VALUE);
65 				reply.Flush();
66 				break;
67 			}
68 
69 			SATWindow* candidate = stackAndTile->GetSATWindow(window);
70 			if (!candidate)
71 				return false;
72 
73 			// Is that window already part of the stack?
74 			if (area->WindowList().HasItem(candidate)) {
75 				reply.StartMessage(B_MISMATCHED_VALUES);
76 				reply.Flush();
77 				break;
78 			}
79 
80 			if (!parent->StackWindow(candidate))
81 				return false;
82 
83 			reply.StartMessage(B_OK);
84 			reply.Flush();
85 			break;
86 		}
87 		case kRemoveWindowFromStack:
88 		{
89 			port_id port;
90 			int32 token;
91 			team_id team;
92 			link.Read<port_id>(&port);
93 			link.Read<int32>(&token);
94 			if (link.Read<team_id>(&team) != B_OK)
95 				return false;
96 
97 			SATGroup* group = sender->GetGroup();
98 			if (!group)
99 				return false;
100 
101 			Window* window = desktop->WindowForClientLooperPort(port);
102 			if (!window) {
103 				reply.StartMessage(B_BAD_VALUE);
104 				reply.Flush();
105 				break;
106 			}
107 			SATWindow* candidate = stackAndTile->GetSATWindow(window);
108 			if (!candidate)
109 				return false;
110 			if (!group->RemoveWindow(candidate, false))
111 				return false;
112 			break;
113 		}
114 		case kRemoveWindowFromStackAt:
115 		{
116 			int32 position;
117 			if (link.Read<int32>(&position) != B_OK)
118 				return false;
119 			SATGroup* group = sender->GetGroup();
120 			WindowArea* area = sender->GetWindowArea();
121 			if (!area || !group)
122 				return false;
123 			SATWindow* removeWindow = area->WindowList().ItemAt(position);
124 			if (!removeWindow) {
125 				reply.StartMessage(B_BAD_VALUE);
126 				reply.Flush();
127 				break;
128 			}
129 
130 			if (!group->RemoveWindow(removeWindow, false))
131 				return false;
132 
133 			ServerWindow* window = removeWindow->GetWindow()->ServerWindow();
134 			reply.StartMessage(B_OK);
135 			reply.Attach<port_id>(window->ClientLooperPort());
136 			reply.Attach<int32>(window->ClientToken());
137 			reply.Attach<team_id>(window->ClientTeam());
138 			reply.Flush();
139 			break;
140 		}
141 		case kCountWindowsOnStack:
142 		{
143 			WindowArea* area = sender->GetWindowArea();
144 			if (!area)
145 				return false;
146 			reply.StartMessage(B_OK);
147 			reply.Attach<int32>(area->WindowList().CountItems());
148 			reply.Flush();
149 			break;
150 		}
151 		case kWindowOnStackAt:
152 		{
153 			int32 position;
154 			if (link.Read<int32>(&position) != B_OK)
155 				return false;
156 			WindowArea* area = sender->GetWindowArea();
157 			if (!area)
158 				return false;
159 			SATWindow* satWindow = area->WindowList().ItemAt(position);
160 			if (!satWindow) {
161 				reply.StartMessage(B_BAD_VALUE);
162 				reply.Flush();
163 				break;
164 			}
165 
166 			ServerWindow* window = satWindow->GetWindow()->ServerWindow();
167 			reply.StartMessage(B_OK);
168 			reply.Attach<port_id>(window->ClientLooperPort());
169 			reply.Attach<int32>(window->ClientToken());
170 			reply.Attach<team_id>(window->ClientTeam());
171 			reply.Flush();
172 			break;
173 		}
174 		case kStackHasWindow:
175 		{
176 			port_id port;
177 			int32 token;
178 			team_id team;
179 			link.Read<port_id>(&port);
180 			link.Read<int32>(&token);
181 			if (link.Read<team_id>(&team) != B_OK)
182 				return false;
183 
184 			Window* window = desktop->WindowForClientLooperPort(port);
185 			if (!window) {
186 				reply.StartMessage(B_BAD_VALUE);
187 				reply.Flush();
188 				break;
189 			}
190 			SATWindow* candidate = stackAndTile->GetSATWindow(window);
191 			if (!candidate)
192 				return false;
193 
194 			WindowArea* area = sender->GetWindowArea();
195 			if (!area)
196 				return false;
197 			reply.StartMessage(B_OK);
198 			reply.Attach<bool>(area->WindowList().HasItem(candidate));
199 			reply.Flush();
200 			break;
201 		}
202 		default:
203 			return false;
204 	}
205 	return true;
206 }
207 
208 
209 SATStacking::SATStacking(SATWindow* window)
210 	:
211 	fSATWindow(window),
212 	fStackingParent(NULL)
213 {
214 
215 }
216 
217 
218 SATStacking::~SATStacking()
219 {
220 
221 }
222 
223 
224 bool
225 SATStacking::FindSnappingCandidates(SATGroup* group)
226 {
227 	_ClearSearchResult();
228 
229 	Window* window = fSATWindow->GetWindow();
230 	if (!window->Decorator())
231 		return false;
232 
233 	BPoint mousePosition;
234 	int32 buttons;
235 	fSATWindow->GetDesktop()->GetLastMouseState(&mousePosition, &buttons);
236 	if (!window->Decorator()->TitleBarRect().Contains(mousePosition))
237 		return false;
238 
239 	// use the upper edge of the candidate window to find the parent window
240 	mousePosition.y = window->Decorator()->TitleBarRect().top;
241 
242 	for (int i = 0; i < group->CountItems(); i++) {
243 		SATWindow* satWindow = group->WindowAt(i);
244 		// search for stacking parent
245 		Window* parentWindow = satWindow->GetWindow();
246 		if (parentWindow == window || parentWindow->Decorator() == NULL)
247 			continue;
248 		if (_IsStackableWindow(parentWindow) == false
249 			|| _IsStackableWindow(window) == false)
250 			continue;
251 		Decorator::Tab* tab = parentWindow->Decorator()->TabAt(
252 			parentWindow->PositionInStack());
253 		if (tab == NULL)
254 			continue;
255 		if (tab->tabRect.Contains(mousePosition)) {
256 			// remember window as the parent for stacking
257 			fStackingParent = satWindow;
258 			_HighlightWindows(true);
259 			return true;
260 		}
261 	}
262 
263 	return false;
264 }
265 
266 
267 bool
268 SATStacking::JoinCandidates()
269 {
270 	if (!fStackingParent)
271 		return false;
272 
273 	bool result = fStackingParent->StackWindow(fSATWindow);
274 
275 	_ClearSearchResult();
276 	return result;
277 }
278 
279 
280 void
281 SATStacking::RemovedFromArea(WindowArea* area)
282 {
283 	const SATWindowList& list = area->WindowList();
284 	if (list.CountItems() > 0)
285 		list.ItemAt(0)->DoGroupLayout();
286 }
287 
288 
289 void
290 SATStacking::WindowLookChanged(window_look look)
291 {
292 	Window* window = fSATWindow->GetWindow();
293 	WindowStack* stack = window->GetWindowStack();
294 	if (stack == NULL)
295 		return;
296 	SATGroup* group = fSATWindow->GetGroup();
297 	if (group == NULL)
298 		return;
299 	if (stack->CountWindows() > 1 && _IsStackableWindow(window) == false)
300 		group->RemoveWindow(fSATWindow);
301 }
302 
303 
304 bool
305 SATStacking::_IsStackableWindow(Window* window)
306 {
307 	if (window->Look() == B_DOCUMENT_WINDOW_LOOK)
308 		return true;
309 	if (window->Look() == B_TITLED_WINDOW_LOOK)
310 		return true;
311 	return false;
312 }
313 
314 
315 void
316 SATStacking::_ClearSearchResult()
317 {
318 	if (!fStackingParent)
319 		return;
320 
321 	_HighlightWindows(false);
322 	fStackingParent = NULL;
323 }
324 
325 
326 void
327 SATStacking::_HighlightWindows(bool highlight)
328 {
329 	Desktop* desktop = fSATWindow->GetWindow()->Desktop();
330 	if (!desktop)
331 		return;
332 	fStackingParent->HighlightTab(highlight);
333 	fSATWindow->HighlightTab(highlight);
334 }
335