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
29using namespace BPrivate;
30
31
32bool
33StackingEventHandler::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
209SATStacking::SATStacking(SATWindow* window)
210	:
211	fSATWindow(window),
212	fStackingParent(NULL)
213{
214
215}
216
217
218SATStacking::~SATStacking()
219{
220
221}
222
223
224bool
225SATStacking::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
267bool
268SATStacking::JoinCandidates()
269{
270	if (!fStackingParent)
271		return false;
272
273	bool result = fStackingParent->StackWindow(fSATWindow);
274
275	_ClearSearchResult();
276	return result;
277}
278
279
280void
281SATStacking::RemovedFromArea(WindowArea* area)
282{
283	const SATWindowList& list = area->WindowList();
284	if (list.CountItems() > 0)
285		list.ItemAt(0)->DoGroupLayout();
286}
287
288
289void
290SATStacking::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
304bool
305SATStacking::_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
315void
316SATStacking::_ClearSearchResult()
317{
318	if (!fStackingParent)
319		return;
320
321	_HighlightWindows(false);
322	fStackingParent = NULL;
323}
324
325
326void
327SATStacking::_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