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