xref: /haiku/src/servers/app/stackandtile/SATWindow.cpp (revision fb6ab24a07c7f4af3c5fec0f9abd083e6f8c098f)
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 
23 using namespace BPrivate;
24 
25 
26 // #pragma mark -
27 
28 
SATWindow(StackAndTile * sat,Window * window)29 SATWindow::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 
~SATWindow()56 SATWindow::~SATWindow()
57 {
58 	if (fWindowArea != NULL)
59 		fWindowArea->Group()->RemoveWindow(this);
60 }
61 
62 
63 SATDecorator*
GetDecorator() const64 SATWindow::GetDecorator() const
65 {
66 	return static_cast<SATDecorator*>(fWindow->Decorator());
67 }
68 
69 
70 SATGroup*
GetGroup()71 SATWindow::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 
103 bool
HandleMessage(SATWindow * sender,BPrivate::LinkReceiver & link,BPrivate::LinkSender & reply)104 SATWindow::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 
116 bool
PropagateToGroup(SATGroup * group)117 SATWindow::PropagateToGroup(SATGroup* group)
118 {
119 	if (fWindowArea == NULL)
120 		return false;
121 	return fWindowArea->PropagateToGroup(group);
122 }
123 
124 
125 bool
AddedToGroup(SATGroup * group,WindowArea * area)126 SATWindow::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 
135 bool
RemovedFromGroup(SATGroup * group,bool stayBelowMouse)136 SATWindow::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 
149 bool
StackWindow(SATWindow * child)150 SATWindow::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 
172 void
RemovedFromArea(WindowArea * area)173 SATWindow::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 
187 void
WindowLookChanged(window_look look)188 SATWindow::WindowLookChanged(window_look look)
189 {
190 	for (int i = 0; i < fSATSnappingBehaviourList.CountItems(); i++)
191 		fSATSnappingBehaviourList.ItemAt(i)->WindowLookChanged(look);
192 }
193 
194 
195 void
FindSnappingCandidates()196 SATWindow::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 
220 bool
JoinCandidates()221 SATWindow::JoinCandidates()
222 {
223 	if (!fOngoingSnapping)
224 		return false;
225 	bool status = fOngoingSnapping->JoinCandidates();
226 	fOngoingSnapping = NULL;
227 
228 	return status;
229 }
230 
231 
232 void
DoGroupLayout()233 SATWindow::DoGroupLayout()
234 {
235 	if (!PositionManagedBySAT())
236 		return;
237 
238 	if (fWindowArea != NULL)
239 		fWindowArea->DoGroupLayout();
240 }
241 
242 
243 void
AdjustSizeLimits(BRect targetFrame)244 SATWindow::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 
267 void
GetSizeLimits(int32 * minWidth,int32 * maxWidth,int32 * minHeight,int32 * maxHeight) const268 SATWindow::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 
299 void
AddDecorator(int32 * minWidth,int32 * maxWidth,int32 * minHeight,int32 * maxHeight)300 SATWindow::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 
316 void
AddDecorator(BRect & frame)317 SATWindow::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 
329 void
SetOriginalSizeLimits(int32 minWidth,int32 maxWidth,int32 minHeight,int32 maxHeight)330 SATWindow::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 
343 void
Resized()344 SATWindow::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 
362 bool
IsHResizeable() const363 SATWindow::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 
375 bool
IsVResizeable() const376 SATWindow::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 
388 BRect
CompleteWindowFrame()389 SATWindow::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 
404 bool
PositionManagedBySAT()405 SATWindow::PositionManagedBySAT()
406 {
407 	if (fWindowArea == NULL || fWindowArea->Group()->CountItems() == 1)
408 		return false;
409 
410 	return true;
411 }
412 
413 
414 bool
HighlightTab(bool active)415 SATWindow::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 
436 bool
HighlightBorders(Decorator::Region region,bool active)437 SATWindow::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 
452 uint64
Id()453 SATWindow::Id()
454 {
455 	return fId;
456 }
457 
458 
459 bool
SetSettings(const BMessage & message)460 SATWindow::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 
470 void
GetSettings(BMessage & message)471 SATWindow::GetSettings(BMessage& message)
472 {
473 	message.AddInt64("window_id", fId);
474 }
475 
476 
477 uint64
_GenerateId()478 SATWindow::_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 
487 void
_RestoreOriginalSize(bool stayBelowMouse)488 SATWindow::_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