xref: /haiku/src/servers/app/stackandtile/SATWindow.cpp (revision d3ff06683af390a4c2e83b69177e0a2eb76679bc)
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 
22 using namespace BPrivate;
23 
24 
25 // #pragma mark -
26 
27 
28 SATWindow::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 
55 SATWindow::~SATWindow()
56 {
57 	if (fWindowArea != NULL)
58 		fWindowArea->Group()->RemoveWindow(this);
59 }
60 
61 
62 SATDecorator*
63 SATWindow::GetDecorator() const
64 {
65 	return static_cast<SATDecorator*>(fWindow->Decorator());
66 }
67 
68 
69 SATGroup*
70 SATWindow::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 
102 bool
103 SATWindow::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 
115 bool
116 SATWindow::PropagateToGroup(SATGroup* group)
117 {
118 	if (fWindowArea == NULL)
119 		return false;
120 	return fWindowArea->PropagateToGroup(group);
121 }
122 
123 
124 bool
125 SATWindow::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 
134 bool
135 SATWindow::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 
148 bool
149 SATWindow::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 
171 void
172 SATWindow::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 
186 void
187 SATWindow::WindowLookChanged(window_look look)
188 {
189 	for (int i = 0; i < fSATSnappingBehaviourList.CountItems(); i++)
190 		fSATSnappingBehaviourList.ItemAt(i)->WindowLookChanged(look);
191 }
192 
193 
194 void
195 SATWindow::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 
219 bool
220 SATWindow::JoinCandidates()
221 {
222 	if (!fOngoingSnapping)
223 		return false;
224 	bool status = fOngoingSnapping->JoinCandidates();
225 	fOngoingSnapping = NULL;
226 
227 	return status;
228 }
229 
230 
231 void
232 SATWindow::DoGroupLayout()
233 {
234 	if (!PositionManagedBySAT())
235 		return;
236 
237 	if (fWindowArea != NULL)
238 		fWindowArea->DoGroupLayout();
239 }
240 
241 
242 void
243 SATWindow::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 
266 void
267 SATWindow::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 
298 void
299 SATWindow::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 
315 void
316 SATWindow::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 
328 void
329 SATWindow::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 
342 void
343 SATWindow::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 
361 bool
362 SATWindow::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 
374 bool
375 SATWindow::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 
387 BRect
388 SATWindow::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 
403 bool
404 SATWindow::PositionManagedBySAT()
405 {
406 	if (fWindowArea == NULL || fWindowArea->Group()->CountItems() == 1)
407 		return false;
408 
409 	return true;
410 }
411 
412 
413 bool
414 SATWindow::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 
435 bool
436 SATWindow::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 
451 uint64
452 SATWindow::Id()
453 {
454 	return fId;
455 }
456 
457 
458 bool
459 SATWindow::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 
469 void
470 SATWindow::GetSettings(BMessage& message)
471 {
472 	message.AddInt64("window_id", fId);
473 }
474 
475 
476 uint64
477 SATWindow::_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 
486 void
487 SATWindow::_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