xref: /haiku/src/servers/app/stackandtile/SATWindow.cpp (revision fc75f2df0c666dcc61be83c4facdd3132340c2fb)
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 	fWindowArea = NULL;
145 	return true;
146 }
147 
148 
149 bool
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
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 
184 
185 void
186 SATWindow::WindowLookChanged(window_look look)
187 {
188 	for (int i = 0; i < fSATSnappingBehaviourList.CountItems(); i++)
189 		fSATSnappingBehaviourList.ItemAt(i)->WindowLookChanged(look);
190 }
191 
192 
193 void
194 SATWindow::FindSnappingCandidates()
195 {
196 	fOngoingSnapping = NULL;
197 
198 	if (fWindow->Feel() != B_NORMAL_WINDOW_FEEL)
199 		return;
200 
201 	GroupIterator groupIterator(fStackAndTile, GetWindow()->Desktop());
202 	for (SATGroup* group = groupIterator.NextGroup(); group;
203 		group = groupIterator.NextGroup()) {
204 		if (group->CountItems() == 1
205 			&& group->WindowAt(0)->GetWindow()->Feel() != B_NORMAL_WINDOW_FEEL)
206 			continue;
207 		for (int i = 0; i < fSATSnappingBehaviourList.CountItems(); i++) {
208 			if (fSATSnappingBehaviourList.ItemAt(i)->FindSnappingCandidates(
209 				group)) {
210 				fOngoingSnapping = fSATSnappingBehaviourList.ItemAt(i);
211 				return;
212 			}
213 		}
214 	}
215 }
216 
217 
218 bool
219 SATWindow::JoinCandidates()
220 {
221 	if (!fOngoingSnapping)
222 		return false;
223 	bool status = fOngoingSnapping->JoinCandidates();
224 	fOngoingSnapping = NULL;
225 
226 	return status;
227 }
228 
229 
230 void
231 SATWindow::DoGroupLayout()
232 {
233 	if (!PositionManagedBySAT())
234 		return;
235 
236 	if (fWindowArea != NULL)
237 		fWindowArea->DoGroupLayout();
238 }
239 
240 
241 void
242 SATWindow::AdjustSizeLimits(BRect targetFrame)
243 {
244 	SATDecorator* decorator = GetDecorator();
245 	if (decorator == NULL)
246 		return;
247 
248 	targetFrame.right -= 2 * (int32)decorator->BorderWidth();
249 	targetFrame.bottom -= 2 * (int32)decorator->BorderWidth()
250 		+ (int32)decorator->TabHeight() + 1;
251 
252 	int32 minWidth, maxWidth;
253 	int32 minHeight, maxHeight;
254 	GetSizeLimits(&minWidth, &maxWidth, &minHeight, &maxHeight);
255 
256 	if (maxWidth < targetFrame.Width())
257 		maxWidth = targetFrame.IntegerWidth();
258 	if (maxHeight < targetFrame.Height())
259 		maxHeight = targetFrame.IntegerHeight();
260 
261 	fWindow->SetSizeLimits(minWidth, maxWidth, minHeight, maxHeight);
262 }
263 
264 
265 void
266 SATWindow::GetSizeLimits(int32* minWidth, int32* maxWidth, int32* minHeight,
267 	int32* maxHeight) const
268 {
269 	*minWidth = fOriginalMinWidth;
270 	*minHeight = fOriginalMinHeight;
271 	*maxWidth = fOriginalMaxWidth;
272 	*maxHeight = fOriginalMaxHeight;
273 
274 	SATDecorator* decorator = GetDecorator();
275 	if (decorator == NULL)
276 		return;
277 
278 	int32 minDecorWidth = 1, maxDecorWidth = 1;
279 	int32 minDecorHeight = 1, maxDecorHeight = 1;
280 	decorator->GetSizeLimits(&minDecorWidth, &minDecorHeight,
281 		&maxDecorWidth, &maxDecorHeight);
282 
283 	// if no size limit is set but the window is not resizeable choose the
284 	// current size as limit
285 	if (IsHResizeable() == false && fOriginalMinWidth <= minDecorWidth)
286 		*minWidth = (int32)fOriginalWidth;
287 	if (IsVResizeable() == false && fOriginalMinHeight <= minDecorHeight)
288 		*minHeight = (int32)fOriginalHeight;
289 
290 	if (*minWidth > *maxWidth)
291 		*maxWidth = *minWidth;
292 	if (*minHeight > *maxHeight)
293 		*maxHeight = *minHeight;
294 }
295 
296 
297 void
298 SATWindow::AddDecorator(int32* minWidth, int32* maxWidth, int32* minHeight,
299 	int32* maxHeight)
300 {
301 	SATDecorator* decorator = GetDecorator();
302 	if (decorator == NULL)
303 		return;
304 
305 	*minWidth += 2 * (int32)decorator->BorderWidth();
306 	*minHeight += 2 * (int32)decorator->BorderWidth()
307 		+ (int32)decorator->TabHeight() + 1;
308 	*maxWidth += 2 * (int32)decorator->BorderWidth();
309 	*maxHeight += 2 * (int32)decorator->BorderWidth()
310 		+ (int32)decorator->TabHeight() + 1;
311 }
312 
313 
314 void
315 SATWindow::AddDecorator(BRect& frame)
316 {
317 	SATDecorator* decorator = GetDecorator();
318 	if (!decorator)
319 		return;
320 	frame.left -= decorator->BorderWidth();
321 	frame.right += decorator->BorderWidth() + 1;
322 	frame.top -= decorator->BorderWidth() + decorator->TabHeight() + 1;
323 	frame.bottom += decorator->BorderWidth();
324 }
325 
326 
327 void
328 SATWindow::SetOriginalSizeLimits(int32 minWidth, int32 maxWidth,
329 	int32 minHeight, int32 maxHeight)
330 {
331 	fOriginalMinWidth = minWidth;
332 	fOriginalMaxWidth = maxWidth;
333 	fOriginalMinHeight = minHeight;
334 	fOriginalMaxHeight = maxHeight;
335 
336 	if (fWindowArea != NULL)
337 		fWindowArea->UpdateSizeLimits();
338 }
339 
340 
341 void
342 SATWindow::Resized()
343 {
344 	bool hResizeable = IsHResizeable();
345 	bool vResizeable = IsVResizeable();
346 	if (hResizeable == false && vResizeable == false)
347 		return;
348 
349 	BRect frame = fWindow->Frame();
350 	if (hResizeable)
351 		fOriginalWidth = frame.Width();
352 	if (vResizeable)
353 		fOriginalHeight = frame.Height();
354 
355 	if (fWindowArea != NULL)
356 		fWindowArea->UpdateSizeConstaints(CompleteWindowFrame());
357 }
358 
359 
360 bool
361 SATWindow::IsHResizeable() const
362 {
363 	if (fWindow->Look() == B_MODAL_WINDOW_LOOK
364 		|| fWindow->Look() == B_BORDERED_WINDOW_LOOK
365 		|| fWindow->Look() == B_NO_BORDER_WINDOW_LOOK
366 		|| (fWindow->Flags() & B_NOT_RESIZABLE) != 0
367 		|| (fWindow->Flags() & B_NOT_H_RESIZABLE) != 0)
368 		return false;
369 	return true;
370 }
371 
372 
373 bool
374 SATWindow::IsVResizeable() const
375 {
376 	if (fWindow->Look() == B_MODAL_WINDOW_LOOK
377 		|| fWindow->Look() == B_BORDERED_WINDOW_LOOK
378 		|| fWindow->Look() == B_NO_BORDER_WINDOW_LOOK
379 		|| (fWindow->Flags() & B_NOT_RESIZABLE) != 0
380 		|| (fWindow->Flags() & B_NOT_V_RESIZABLE) != 0)
381 		return false;
382 	return true;
383 }
384 
385 
386 BRect
387 SATWindow::CompleteWindowFrame()
388 {
389 	BRect frame = fWindow->Frame();
390 	if (fDesktop
391 		&& fDesktop->CurrentWorkspace() != fWindow->CurrentWorkspace()) {
392 		window_anchor& anchor = fWindow->Anchor(fWindow->CurrentWorkspace());
393 		if (anchor.position != kInvalidWindowPosition)
394 			frame.OffsetTo(anchor.position);
395 	}
396 
397 	AddDecorator(frame);
398 	return frame;
399 }
400 
401 
402 bool
403 SATWindow::PositionManagedBySAT()
404 {
405 	if (fWindowArea == NULL || fWindowArea->Group()->CountItems() == 1)
406 		return false;
407 
408 	return true;
409 }
410 
411 
412 bool
413 SATWindow::HighlightTab(bool active)
414 {
415 	SATDecorator* decorator = GetDecorator();
416 	if (!decorator)
417 		return false;
418 
419 	int32 tabIndex = fWindow->PositionInStack();
420 	BRegion dirty;
421 	uint8 highlight = active ?  SATDecorator::HIGHLIGHT_STACK_AND_TILE : 0;
422 	decorator->SetRegionHighlight(Decorator::REGION_TAB, highlight, &dirty,
423 		tabIndex);
424 	decorator->SetRegionHighlight(Decorator::REGION_CLOSE_BUTTON, highlight,
425 		&dirty, tabIndex);
426 	decorator->SetRegionHighlight(Decorator::REGION_ZOOM_BUTTON, highlight,
427 		&dirty, tabIndex);
428 
429 	fWindow->TopLayerStackWindow()->ProcessDirtyRegion(dirty);
430 	return true;
431 }
432 
433 
434 bool
435 SATWindow::HighlightBorders(Decorator::Region region, bool active)
436 {
437 	SATDecorator* decorator = GetDecorator();
438 	if (!decorator)
439 		return false;
440 
441 	BRegion dirty;
442 	uint8 highlight = active ? SATDecorator::HIGHLIGHT_STACK_AND_TILE : 0;
443 	decorator->SetRegionHighlight(region, highlight, &dirty);
444 
445 	fWindow->ProcessDirtyRegion(dirty);
446 	return true;
447 }
448 
449 
450 uint64
451 SATWindow::Id()
452 {
453 	return fId;
454 }
455 
456 
457 bool
458 SATWindow::SetSettings(const BMessage& message)
459 {
460 	uint64 id;
461 	if (message.FindInt64("window_id", (int64*)&id) != B_OK)
462 		return false;
463 	fId = id;
464 	return true;
465 }
466 
467 
468 void
469 SATWindow::GetSettings(BMessage& message)
470 {
471 	message.AddInt64("window_id", fId);
472 }
473 
474 
475 uint64
476 SATWindow::_GenerateId()
477 {
478 	bigtime_t time = real_time_clock_usecs();
479 	srand(time);
480 	int16 randNumber = rand();
481 	return (time & ~0xFFFF) | randNumber;
482 }
483 
484 
485 void
486 SATWindow::_RestoreOriginalSize(bool stayBelowMouse)
487 {
488 	// restore size
489 	fWindow->SetSizeLimits(fOriginalMinWidth, fOriginalMaxWidth,
490 		fOriginalMinHeight, fOriginalMaxHeight);
491 	BRect frame = fWindow->Frame();
492 	float x = 0, y = 0;
493 	if (IsHResizeable() == false)
494 		x = fOriginalWidth - frame.Width();
495 	if (IsVResizeable() == false)
496 		y = fOriginalHeight - frame.Height();
497 	fDesktop->ResizeWindowBy(fWindow, x, y);
498 
499 	if (!stayBelowMouse)
500 		return;
501 	// verify that the window stays below the mouse
502 	BPoint mousePosition;
503 	int32 buttons;
504 	fDesktop->GetLastMouseState(&mousePosition, &buttons);
505 	SATDecorator* decorator = GetDecorator();
506 	if (decorator == NULL)
507 		return;
508 	BRect tabRect = decorator->TitleBarRect();
509 	if (mousePosition.y < tabRect.bottom && mousePosition.y > tabRect.top
510 		&& mousePosition.x <= frame.right + decorator->BorderWidth() +1
511 		&& mousePosition.x >= frame.left + decorator->BorderWidth()) {
512 		// verify mouse stays on the tab
513 		float oldOffset = mousePosition.x - fOldTabLocatiom;
514 		float deltaX = mousePosition.x - (tabRect.left + oldOffset);
515 		fDesktop->MoveWindowBy(fWindow, deltaX, 0);
516 	} else {
517 		// verify mouse stays on the border
518 		float deltaX = 0;
519 		float deltaY = 0;
520 		BRect newFrame = fWindow->Frame();
521 		if (x != 0 && mousePosition.x > frame.left
522 			&& mousePosition.x > newFrame.right) {
523 			deltaX = mousePosition.x - newFrame.right;
524 			if (mousePosition.x > frame.right)
525 				deltaX -= mousePosition.x - frame.right;
526 		}
527 		if (y != 0 && mousePosition.y > frame.top
528 			&& mousePosition.y > newFrame.bottom) {
529 			deltaY = mousePosition.y - newFrame.bottom;
530 			if (mousePosition.y > frame.bottom)
531 				deltaY -= mousePosition.y - frame.bottom;
532 		}
533 			fDesktop->MoveWindowBy(fWindow, deltaX, deltaY);
534 	}
535 }
536