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