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 "SATWindow.h"
11
12#include <Debug.h>
13
14#include "StackAndTilePrivate.h"
15
16#include "Desktop.h"
17#include "SATGroup.h"
18#include "ServerApp.h"
19#include "Window.h"
20
21
22using namespace BPrivate;
23
24
25// #pragma mark -
26
27
28SATWindow::SATWindow(StackAndTile* sat, Window* window)
29	:
30	fWindow(window),
31	fStackAndTile(sat),
32
33	fWindowArea(NULL),
34
35	fOngoingSnapping(NULL),
36	fSATStacking(this),
37	fSATTiling(this)
38{
39	fId = _GenerateId();
40
41	fDesktop = fWindow->Desktop();
42
43	// read initial limit values
44	fWindow->GetSizeLimits(&fOriginalMinWidth, &fOriginalMaxWidth,
45		&fOriginalMinHeight, &fOriginalMaxHeight);
46	BRect frame = fWindow->Frame();
47	fOriginalWidth = frame.Width();
48	fOriginalHeight = frame.Height();
49
50	fSATSnappingBehaviourList.AddItem(&fSATStacking);
51	fSATSnappingBehaviourList.AddItem(&fSATTiling);
52}
53
54
55SATWindow::~SATWindow()
56{
57	if (fWindowArea != NULL)
58		fWindowArea->Group()->RemoveWindow(this);
59}
60
61
62SATDecorator*
63SATWindow::GetDecorator() const
64{
65	return static_cast<SATDecorator*>(fWindow->Decorator());
66}
67
68
69SATGroup*
70SATWindow::GetGroup()
71{
72	if (fWindowArea == NULL) {
73		SATGroup* group = new (std::nothrow)SATGroup;
74		if (group == NULL)
75			return group;
76		BReference<SATGroup> groupRef;
77		groupRef.SetTo(group, true);
78
79		/* AddWindow also will trigger the window to hold a reference on the new
80		group. */
81		if (group->AddWindow(this, NULL, NULL, NULL, NULL) == false)
82			return NULL;
83	}
84
85	ASSERT(fWindowArea != NULL);
86
87	 // manually set the tabs of the single window
88	if (PositionManagedBySAT() == false) {
89		BRect frame = CompleteWindowFrame();
90		fWindowArea->LeftTopCrossing()->VerticalTab()->SetPosition(frame.left);
91		fWindowArea->LeftTopCrossing()->HorizontalTab()->SetPosition(frame.top);
92		fWindowArea->RightBottomCrossing()->VerticalTab()->SetPosition(
93			frame.right);
94		fWindowArea->RightBottomCrossing()->HorizontalTab()->SetPosition(
95			frame.bottom);
96	}
97
98	return fWindowArea->Group();
99}
100
101
102bool
103SATWindow::HandleMessage(SATWindow* sender, BPrivate::LinkReceiver& link,
104	BPrivate::LinkSender& reply)
105{
106	int32 target;
107	link.Read<int32>(&target);
108	if (target == kStacking)
109		return StackingEventHandler::HandleMessage(sender, link, reply);
110
111	return false;
112}
113
114
115bool
116SATWindow::PropagateToGroup(SATGroup* group)
117{
118	if (fWindowArea == NULL)
119		return false;
120	return fWindowArea->PropagateToGroup(group);
121}
122
123
124bool
125SATWindow::AddedToGroup(SATGroup* group, WindowArea* area)
126{
127	STRACE_SAT("SATWindow::AddedToGroup group: %p window %s\n", group,
128		fWindow->Title());
129	fWindowArea = area;
130	return true;
131}
132
133
134bool
135SATWindow::RemovedFromGroup(SATGroup* group, bool stayBelowMouse)
136{
137	STRACE_SAT("SATWindow::RemovedFromGroup group: %p window %s\n", group,
138		fWindow->Title());
139
140	_RestoreOriginalSize(stayBelowMouse);
141	if (group->CountItems() == 1)
142		group->WindowAt(0)->_RestoreOriginalSize(false);
143
144	return true;
145}
146
147
148bool
149SATWindow::StackWindow(SATWindow* child)
150{
151	SATGroup* group = GetGroup();
152	WindowArea* area = GetWindowArea();
153	if (!group || !area)
154		return false;
155
156	if (group->AddWindow(child, area, this) == false)
157		return false;
158
159	DoGroupLayout();
160
161	if (fWindow->AddWindowToStack(child->GetWindow()) == false) {
162		group->RemoveWindow(child);
163		DoGroupLayout();
164		return false;
165	}
166
167	return true;
168}
169
170
171void
172SATWindow::RemovedFromArea(WindowArea* area)
173{
174	SATDecorator* decorator = GetDecorator();
175	if (decorator != NULL)
176		fOldTabLocatiom = decorator->TabRect(fWindow->PositionInStack()).left;
177
178	fWindow->DetachFromWindowStack(true);
179	for (int i = 0; i < fSATSnappingBehaviourList.CountItems(); i++)
180		fSATSnappingBehaviourList.ItemAt(i)->RemovedFromArea(area);
181
182	fWindowArea = NULL;
183}
184
185
186void
187SATWindow::WindowLookChanged(window_look look)
188{
189	for (int i = 0; i < fSATSnappingBehaviourList.CountItems(); i++)
190		fSATSnappingBehaviourList.ItemAt(i)->WindowLookChanged(look);
191}
192
193
194void
195SATWindow::FindSnappingCandidates()
196{
197	fOngoingSnapping = NULL;
198
199	if (fWindow->Feel() != B_NORMAL_WINDOW_FEEL)
200		return;
201
202	GroupIterator groupIterator(fStackAndTile, GetWindow()->Desktop());
203	for (SATGroup* group = groupIterator.NextGroup(); group;
204		group = groupIterator.NextGroup()) {
205		if (group->CountItems() == 1
206			&& group->WindowAt(0)->GetWindow()->Feel() != B_NORMAL_WINDOW_FEEL)
207			continue;
208		for (int i = 0; i < fSATSnappingBehaviourList.CountItems(); i++) {
209			if (fSATSnappingBehaviourList.ItemAt(i)->FindSnappingCandidates(
210				group)) {
211				fOngoingSnapping = fSATSnappingBehaviourList.ItemAt(i);
212				return;
213			}
214		}
215	}
216}
217
218
219bool
220SATWindow::JoinCandidates()
221{
222	if (!fOngoingSnapping)
223		return false;
224	bool status = fOngoingSnapping->JoinCandidates();
225	fOngoingSnapping = NULL;
226
227	return status;
228}
229
230
231void
232SATWindow::DoGroupLayout()
233{
234	if (!PositionManagedBySAT())
235		return;
236
237	if (fWindowArea != NULL)
238		fWindowArea->DoGroupLayout();
239}
240
241
242void
243SATWindow::AdjustSizeLimits(BRect targetFrame)
244{
245	SATDecorator* decorator = GetDecorator();
246	if (decorator == NULL)
247		return;
248
249	targetFrame.right -= 2 * (int32)decorator->BorderWidth();
250	targetFrame.bottom -= 2 * (int32)decorator->BorderWidth()
251		+ (int32)decorator->TabHeight() + 1;
252
253	int32 minWidth, maxWidth;
254	int32 minHeight, maxHeight;
255	GetSizeLimits(&minWidth, &maxWidth, &minHeight, &maxHeight);
256
257	if (maxWidth < targetFrame.Width())
258		maxWidth = targetFrame.IntegerWidth();
259	if (maxHeight < targetFrame.Height())
260		maxHeight = targetFrame.IntegerHeight();
261
262	fWindow->SetSizeLimits(minWidth, maxWidth, minHeight, maxHeight);
263}
264
265
266void
267SATWindow::GetSizeLimits(int32* minWidth, int32* maxWidth, int32* minHeight,
268	int32* maxHeight) const
269{
270	*minWidth = fOriginalMinWidth;
271	*minHeight = fOriginalMinHeight;
272	*maxWidth = fOriginalMaxWidth;
273	*maxHeight = fOriginalMaxHeight;
274
275	SATDecorator* decorator = GetDecorator();
276	if (decorator == NULL)
277		return;
278
279	int32 minDecorWidth = 1, maxDecorWidth = 1;
280	int32 minDecorHeight = 1, maxDecorHeight = 1;
281	decorator->GetSizeLimits(&minDecorWidth, &minDecorHeight,
282		&maxDecorWidth, &maxDecorHeight);
283
284	// if no size limit is set but the window is not resizeable choose the
285	// current size as limit
286	if (IsHResizeable() == false && fOriginalMinWidth <= minDecorWidth)
287		*minWidth = (int32)fOriginalWidth;
288	if (IsVResizeable() == false && fOriginalMinHeight <= minDecorHeight)
289		*minHeight = (int32)fOriginalHeight;
290
291	if (*minWidth > *maxWidth)
292		*maxWidth = *minWidth;
293	if (*minHeight > *maxHeight)
294		*maxHeight = *minHeight;
295}
296
297
298void
299SATWindow::AddDecorator(int32* minWidth, int32* maxWidth, int32* minHeight,
300	int32* maxHeight)
301{
302	SATDecorator* decorator = GetDecorator();
303	if (decorator == NULL)
304		return;
305
306	*minWidth += 2 * (int32)decorator->BorderWidth();
307	*minHeight += 2 * (int32)decorator->BorderWidth()
308		+ (int32)decorator->TabHeight() + 1;
309	*maxWidth += 2 * (int32)decorator->BorderWidth();
310	*maxHeight += 2 * (int32)decorator->BorderWidth()
311		+ (int32)decorator->TabHeight() + 1;
312}
313
314
315void
316SATWindow::AddDecorator(BRect& frame)
317{
318	SATDecorator* decorator = GetDecorator();
319	if (!decorator)
320		return;
321	frame.left -= decorator->BorderWidth();
322	frame.right += decorator->BorderWidth() + 1;
323	frame.top -= decorator->BorderWidth() + decorator->TabHeight() + 1;
324	frame.bottom += decorator->BorderWidth();
325}
326
327
328void
329SATWindow::SetOriginalSizeLimits(int32 minWidth, int32 maxWidth,
330	int32 minHeight, int32 maxHeight)
331{
332	fOriginalMinWidth = minWidth;
333	fOriginalMaxWidth = maxWidth;
334	fOriginalMinHeight = minHeight;
335	fOriginalMaxHeight = maxHeight;
336
337	if (fWindowArea != NULL)
338		fWindowArea->UpdateSizeLimits();
339}
340
341
342void
343SATWindow::Resized()
344{
345	bool hResizeable = IsHResizeable();
346	bool vResizeable = IsVResizeable();
347	if (hResizeable == false && vResizeable == false)
348		return;
349
350	BRect frame = fWindow->Frame();
351	if (hResizeable)
352		fOriginalWidth = frame.Width();
353	if (vResizeable)
354		fOriginalHeight = frame.Height();
355
356	if (fWindowArea != NULL)
357		fWindowArea->UpdateSizeConstaints(CompleteWindowFrame());
358}
359
360
361bool
362SATWindow::IsHResizeable() const
363{
364	if (fWindow->Look() == B_MODAL_WINDOW_LOOK
365		|| fWindow->Look() == B_BORDERED_WINDOW_LOOK
366		|| fWindow->Look() == B_NO_BORDER_WINDOW_LOOK
367		|| (fWindow->Flags() & B_NOT_RESIZABLE) != 0
368		|| (fWindow->Flags() & B_NOT_H_RESIZABLE) != 0)
369		return false;
370	return true;
371}
372
373
374bool
375SATWindow::IsVResizeable() const
376{
377	if (fWindow->Look() == B_MODAL_WINDOW_LOOK
378		|| fWindow->Look() == B_BORDERED_WINDOW_LOOK
379		|| fWindow->Look() == B_NO_BORDER_WINDOW_LOOK
380		|| (fWindow->Flags() & B_NOT_RESIZABLE) != 0
381		|| (fWindow->Flags() & B_NOT_V_RESIZABLE) != 0)
382		return false;
383	return true;
384}
385
386
387BRect
388SATWindow::CompleteWindowFrame()
389{
390	BRect frame = fWindow->Frame();
391	if (fDesktop
392		&& fDesktop->CurrentWorkspace() != fWindow->CurrentWorkspace()) {
393		window_anchor& anchor = fWindow->Anchor(fWindow->CurrentWorkspace());
394		if (anchor.position != kInvalidWindowPosition)
395			frame.OffsetTo(anchor.position);
396	}
397
398	AddDecorator(frame);
399	return frame;
400}
401
402
403bool
404SATWindow::PositionManagedBySAT()
405{
406	if (fWindowArea == NULL || fWindowArea->Group()->CountItems() == 1)
407		return false;
408
409	return true;
410}
411
412
413bool
414SATWindow::HighlightTab(bool active)
415{
416	SATDecorator* decorator = GetDecorator();
417	if (!decorator)
418		return false;
419
420	int32 tabIndex = fWindow->PositionInStack();
421	BRegion dirty;
422	uint8 highlight = active ?  SATDecorator::HIGHLIGHT_STACK_AND_TILE : 0;
423	decorator->SetRegionHighlight(Decorator::REGION_TAB, highlight, &dirty,
424		tabIndex);
425	decorator->SetRegionHighlight(Decorator::REGION_CLOSE_BUTTON, highlight,
426		&dirty, tabIndex);
427	decorator->SetRegionHighlight(Decorator::REGION_ZOOM_BUTTON, highlight,
428		&dirty, tabIndex);
429
430	fWindow->TopLayerStackWindow()->ProcessDirtyRegion(dirty);
431	return true;
432}
433
434
435bool
436SATWindow::HighlightBorders(Decorator::Region region, bool active)
437{
438	SATDecorator* decorator = GetDecorator();
439	if (!decorator)
440		return false;
441
442	BRegion dirty;
443	uint8 highlight = active ? SATDecorator::HIGHLIGHT_STACK_AND_TILE : 0;
444	decorator->SetRegionHighlight(region, highlight, &dirty);
445
446	fWindow->ProcessDirtyRegion(dirty);
447	return true;
448}
449
450
451uint64
452SATWindow::Id()
453{
454	return fId;
455}
456
457
458bool
459SATWindow::SetSettings(const BMessage& message)
460{
461	uint64 id;
462	if (message.FindInt64("window_id", (int64*)&id) != B_OK)
463		return false;
464	fId = id;
465	return true;
466}
467
468
469void
470SATWindow::GetSettings(BMessage& message)
471{
472	message.AddInt64("window_id", fId);
473}
474
475
476uint64
477SATWindow::_GenerateId()
478{
479	bigtime_t time = real_time_clock_usecs();
480	srand(time);
481	int16 randNumber = rand();
482	return (time & ~0xFFFF) | randNumber;
483}
484
485
486void
487SATWindow::_RestoreOriginalSize(bool stayBelowMouse)
488{
489	// restore size
490	fWindow->SetSizeLimits(fOriginalMinWidth, fOriginalMaxWidth,
491		fOriginalMinHeight, fOriginalMaxHeight);
492	BRect frame = fWindow->Frame();
493	float x = 0, y = 0;
494	if (IsHResizeable() == false)
495		x = fOriginalWidth - frame.Width();
496	if (IsVResizeable() == false)
497		y = fOriginalHeight - frame.Height();
498	fDesktop->ResizeWindowBy(fWindow, x, y);
499
500	if (!stayBelowMouse)
501		return;
502	// verify that the window stays below the mouse
503	BPoint mousePosition;
504	int32 buttons;
505	fDesktop->GetLastMouseState(&mousePosition, &buttons);
506	SATDecorator* decorator = GetDecorator();
507	if (decorator == NULL)
508		return;
509	BRect tabRect = decorator->TitleBarRect();
510	if (mousePosition.y < tabRect.bottom && mousePosition.y > tabRect.top
511		&& mousePosition.x <= frame.right + decorator->BorderWidth() +1
512		&& mousePosition.x >= frame.left + decorator->BorderWidth()) {
513		// verify mouse stays on the tab
514		float oldOffset = mousePosition.x - fOldTabLocatiom;
515		float deltaX = mousePosition.x - (tabRect.left + oldOffset);
516		fDesktop->MoveWindowBy(fWindow, deltaX, 0);
517	} else {
518		// verify mouse stays on the border
519		float deltaX = 0;
520		float deltaY = 0;
521		BRect newFrame = fWindow->Frame();
522		if (x != 0 && mousePosition.x > frame.left
523			&& mousePosition.x > newFrame.right) {
524			deltaX = mousePosition.x - newFrame.right;
525			if (mousePosition.x > frame.right)
526				deltaX -= mousePosition.x - frame.right;
527		}
528		if (y != 0 && mousePosition.y > frame.top
529			&& mousePosition.y > newFrame.bottom) {
530			deltaY = mousePosition.y - newFrame.bottom;
531			if (mousePosition.y > frame.bottom)
532				deltaY -= mousePosition.y - frame.bottom;
533		}
534			fDesktop->MoveWindowBy(fWindow, deltaX, deltaY);
535	}
536}
537