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