xref: /haiku/src/servers/app/Window.cpp (revision 899e0ef82b5624ace2ccfa5f5a58c8ebee54aaef)
1 /*
2  * Copyright 2001-2020, Haiku, Inc.
3  * Distributed under the terms of the MIT license.
4  *
5  * Authors:
6  *		DarkWyrm, bpmagic@columbus.rr.com
7  *		Adi Oanca, adioanca@gmail.com
8  *		Stephan Aßmus, superstippi@gmx.de
9  *		Axel Dörfler, axeld@pinc-software.de
10  *		Brecht Machiels, brecht@mos6581.org
11  *		Clemens Zeidler, haiku@clemens-zeidler.de
12  *		Tri-Edge AI
13  *		Jacob Secunda, secundja@gmail.com
14  */
15 
16 
17 #include "Window.h"
18 
19 #include <new>
20 #include <stdio.h>
21 
22 #include <Debug.h>
23 
24 #include <DirectWindow.h>
25 #include <PortLink.h>
26 #include <View.h>
27 #include <ViewPrivate.h>
28 #include <WindowPrivate.h>
29 
30 #include "ClickTarget.h"
31 #include "Decorator.h"
32 #include "DecorManager.h"
33 #include "Desktop.h"
34 #include "DrawingEngine.h"
35 #include "HWInterface.h"
36 #include "MessagePrivate.h"
37 #include "PortLink.h"
38 #include "ServerApp.h"
39 #include "ServerWindow.h"
40 #include "WindowBehaviour.h"
41 #include "Workspace.h"
42 #include "WorkspacesView.h"
43 
44 
45 // Toggle debug output
46 //#define DEBUG_WINDOW
47 
48 #ifdef DEBUG_WINDOW
49 #	define STRACE(x) printf x
50 #else
51 #	define STRACE(x) ;
52 #endif
53 
54 // IMPORTANT: nested LockSingleWindow()s are not supported (by MultiLocker)
55 
56 using std::nothrow;
57 
58 // if the background clearing is delayed until
59 // the client draws the view, we have less flickering
60 // when contents have to be redrawn because of resizing
61 // a window or because the client invalidates parts.
62 // when redrawing something that has been exposed from underneath
63 // other windows, the other window will be seen longer at
64 // its previous position though if the exposed parts are not
65 // cleared right away. maybe there ought to be a flag in
66 // the update session, which tells us the cause of the update
67 
68 
69 //static rgb_color sPendingColor = (rgb_color){ 255, 255, 0, 255 };
70 //static rgb_color sCurrentColor = (rgb_color){ 255, 0, 255, 255 };
71 
72 
73 Window::Window(const BRect& frame, const char *name,
74 		window_look look, window_feel feel, uint32 flags, uint32 workspaces,
75 		::ServerWindow* window, DrawingEngine* drawingEngine)
76 	:
77 	fTitle(name),
78 	fFrame(frame),
79 	fScreen(NULL),
80 
81 	fVisibleRegion(),
82 	fVisibleContentRegion(),
83 	fDirtyRegion(),
84 	fDirtyCause(0),
85 
86 	fContentRegion(),
87 	fEffectiveDrawingRegion(),
88 
89 	fVisibleContentRegionValid(false),
90 	fContentRegionValid(false),
91 	fEffectiveDrawingRegionValid(false),
92 
93 	fRegionPool(),
94 
95 	fWindow(window),
96 	fDrawingEngine(drawingEngine),
97 	fDesktop(window->Desktop()),
98 
99 	fCurrentUpdateSession(&fUpdateSessions[0]),
100 	fPendingUpdateSession(&fUpdateSessions[1]),
101 	fUpdateRequested(false),
102 	fInUpdate(false),
103 	fUpdatesEnabled(false),
104 
105 	// Windows start hidden
106 	fHidden(true),
107 	// Hidden is 1 or more
108 	fShowLevel(1),
109 	fMinimized(false),
110 	fIsFocus(false),
111 
112 	fLook(look),
113 	fFeel(feel),
114 	fWorkspaces(workspaces),
115 	fCurrentWorkspace(-1),
116 
117 	fMinWidth(1),
118 	fMaxWidth(32768),
119 	fMinHeight(1),
120 	fMaxHeight(32768),
121 
122 	fWorkspacesViewCount(0)
123 {
124 	_InitWindowStack();
125 
126 	// make sure our arguments are valid
127 	if (!IsValidLook(fLook))
128 		fLook = B_TITLED_WINDOW_LOOK;
129 	if (!IsValidFeel(fFeel))
130 		fFeel = B_NORMAL_WINDOW_FEEL;
131 
132 	SetFlags(flags, NULL);
133 
134 	if (fLook != B_NO_BORDER_WINDOW_LOOK && fCurrentStack.IsSet()) {
135 		// allocates a decorator
136 		::Decorator* decorator = Decorator();
137 		if (decorator != NULL) {
138 			decorator->GetSizeLimits(&fMinWidth, &fMinHeight, &fMaxWidth,
139 				&fMaxHeight);
140 		}
141 	}
142 	if (fFeel != kOffscreenWindowFeel)
143 		fWindowBehaviour.SetTo(gDecorManager.AllocateWindowBehaviour(this));
144 
145 	// do we need to change our size to let the decorator fit?
146 	// _ResizeBy() will adapt the frame for validity before resizing
147 	if (feel == kDesktopWindowFeel) {
148 		// the desktop window spans over the whole screen
149 		// TODO: this functionality should be moved somewhere else
150 		//  (so that it is always used when the workspace is changed)
151 		uint16 width, height;
152 		uint32 colorSpace;
153 		float frequency;
154 		if (Screen() != NULL) {
155 			Screen()->GetMode(width, height, colorSpace, frequency);
156 // TODO: MOVE THIS AWAY!!! ResizeBy contains calls to virtual methods!
157 // Also, there is no TopView()!
158 			fFrame.OffsetTo(B_ORIGIN);
159 //			ResizeBy(width - frame.Width(), height - frame.Height(), NULL);
160 		}
161 	}
162 
163 	STRACE(("Window %p, %s:\n", this, Name()));
164 	STRACE(("\tFrame: (%.1f, %.1f, %.1f, %.1f)\n", fFrame.left, fFrame.top,
165 		fFrame.right, fFrame.bottom));
166 	STRACE(("\tWindow %s\n", window ? window->Title() : "NULL"));
167 }
168 
169 
170 Window::~Window()
171 {
172 	if (fTopView.IsSet()) {
173 		fTopView->DetachedFromWindow();
174 	}
175 
176 	DetachFromWindowStack(false);
177 
178 	gDecorManager.CleanupForWindow(this);
179 }
180 
181 
182 status_t
183 Window::InitCheck() const
184 {
185 	if (GetDrawingEngine() == NULL
186 		|| (fFeel != kOffscreenWindowFeel && !fWindowBehaviour.IsSet()))
187 		return B_NO_MEMORY;
188 	// TODO: anything else?
189 	return B_OK;
190 }
191 
192 
193 void
194 Window::SetClipping(BRegion* stillAvailableOnScreen)
195 {
196 	// this function is only called from the Desktop thread
197 
198 	// start from full region (as if the window was fully visible)
199 	GetFullRegion(&fVisibleRegion);
200 	// clip to region still available on screen
201 	fVisibleRegion.IntersectWith(stillAvailableOnScreen);
202 
203 	fVisibleContentRegionValid = false;
204 	fEffectiveDrawingRegionValid = false;
205 }
206 
207 
208 void
209 Window::GetFullRegion(BRegion* region)
210 {
211 	// TODO: if someone needs to call this from
212 	// the outside, the clipping needs to be readlocked!
213 
214 	// start from the decorator border, extend to use the frame
215 	GetBorderRegion(region);
216 	region->Include(fFrame);
217 }
218 
219 
220 void
221 Window::GetBorderRegion(BRegion* region)
222 {
223 	// TODO: if someone needs to call this from
224 	// the outside, the clipping needs to be readlocked!
225 
226 	::Decorator* decorator = Decorator();
227 	if (decorator)
228 		*region = decorator->GetFootprint();
229 	else
230 		region->MakeEmpty();
231 }
232 
233 
234 void
235 Window::GetContentRegion(BRegion* region)
236 {
237 	// TODO: if someone needs to call this from
238 	// the outside, the clipping needs to be readlocked!
239 
240 	if (!fContentRegionValid) {
241 		_UpdateContentRegion();
242 	}
243 
244 	*region = fContentRegion;
245 }
246 
247 
248 BRegion&
249 Window::VisibleContentRegion()
250 {
251 	// TODO: if someone needs to call this from
252 	// the outside, the clipping needs to be readlocked!
253 
254 	// regions expected to be locked
255 	if (!fVisibleContentRegionValid) {
256 		GetContentRegion(&fVisibleContentRegion);
257 		fVisibleContentRegion.IntersectWith(&fVisibleRegion);
258 	}
259 	return fVisibleContentRegion;
260 }
261 
262 
263 // #pragma mark -
264 
265 
266 void
267 Window::_PropagatePosition()
268 {
269 	if ((fFlags & B_SAME_POSITION_IN_ALL_WORKSPACES) == 0)
270 		return;
271 
272 	for (int32 i = 0; i < kListCount; i++) {
273 		Anchor(i).position = fFrame.LeftTop();
274 	}
275 }
276 
277 
278 void
279 Window::MoveBy(int32 x, int32 y, bool moveStack)
280 {
281 	// this function is only called from the desktop thread
282 
283 	if (x == 0 && y == 0)
284 		return;
285 
286 	fFrame.OffsetBy(x, y);
287 	_PropagatePosition();
288 
289 	// take along the dirty region which is not
290 	// processed yet
291 	fDirtyRegion.OffsetBy(x, y);
292 
293 	if (fContentRegionValid)
294 		fContentRegion.OffsetBy(x, y);
295 
296 	if (fCurrentUpdateSession->IsUsed())
297 		fCurrentUpdateSession->MoveBy(x, y);
298 	if (fPendingUpdateSession->IsUsed())
299 		fPendingUpdateSession->MoveBy(x, y);
300 
301 	fEffectiveDrawingRegionValid = false;
302 
303 	if (fTopView.IsSet()) {
304 		fTopView->MoveBy(x, y, NULL);
305 		fTopView->UpdateOverlay();
306 	}
307 
308 	::Decorator* decorator = Decorator();
309 	if (moveStack && decorator)
310 		decorator->MoveBy(x, y);
311 
312 	WindowStack* stack = GetWindowStack();
313 	if (moveStack && stack) {
314 		for (int32 i = 0; i < stack->CountWindows(); i++) {
315 			Window* window = stack->WindowList().ItemAt(i);
316 			if (window == this)
317 				continue;
318 			window->MoveBy(x, y, false);
319 		}
320 	}
321 
322 	// the desktop will take care of dirty regions
323 
324 	// dispatch a message to the client informing about the changed size
325 	BMessage msg(B_WINDOW_MOVED);
326 	msg.AddInt64("when", system_time());
327 	msg.AddPoint("where", fFrame.LeftTop());
328 	fWindow->SendMessageToClient(&msg);
329 }
330 
331 
332 void
333 Window::ResizeBy(int32 x, int32 y, BRegion* dirtyRegion, bool resizeStack)
334 {
335 	// this function is only called from the desktop thread
336 
337 	int32 wantWidth = fFrame.IntegerWidth() + x;
338 	int32 wantHeight = fFrame.IntegerHeight() + y;
339 
340 	// enforce size limits
341 	WindowStack* stack = GetWindowStack();
342 	if (resizeStack && stack) {
343 		for (int32 i = 0; i < stack->CountWindows(); i++) {
344 			Window* window = stack->WindowList().ItemAt(i);
345 
346 			if (wantWidth < window->fMinWidth)
347 				wantWidth = window->fMinWidth;
348 			if (wantWidth > window->fMaxWidth)
349 				wantWidth = window->fMaxWidth;
350 
351 			if (wantHeight < window->fMinHeight)
352 				wantHeight = window->fMinHeight;
353 			if (wantHeight > window->fMaxHeight)
354 				wantHeight = window->fMaxHeight;
355 		}
356 	}
357 
358 	x = wantWidth - fFrame.IntegerWidth();
359 	y = wantHeight - fFrame.IntegerHeight();
360 
361 	if (x == 0 && y == 0)
362 		return;
363 
364 	fFrame.right += x;
365 	fFrame.bottom += y;
366 
367 	fContentRegionValid = false;
368 	fEffectiveDrawingRegionValid = false;
369 
370 	if (fTopView.IsSet()) {
371 		fTopView->ResizeBy(x, y, dirtyRegion);
372 		fTopView->UpdateOverlay();
373 	}
374 
375 	::Decorator* decorator = Decorator();
376 	if (decorator && resizeStack)
377 		decorator->ResizeBy(x, y, dirtyRegion);
378 
379 	if (resizeStack && stack) {
380 		for (int32 i = 0; i < stack->CountWindows(); i++) {
381 			Window* window = stack->WindowList().ItemAt(i);
382 			if (window == this)
383 				continue;
384 			window->ResizeBy(x, y, dirtyRegion, false);
385 		}
386 	}
387 
388 	// send a message to the client informing about the changed size
389 	BRect frame(Frame());
390 	BMessage msg(B_WINDOW_RESIZED);
391 	msg.AddInt64("when", system_time());
392 	msg.AddInt32("width", frame.IntegerWidth());
393 	msg.AddInt32("height", frame.IntegerHeight());
394 	fWindow->SendMessageToClient(&msg);
395 }
396 
397 
398 void
399 Window::SetOutlinesDelta(BPoint delta, BRegion* dirtyRegion)
400 {
401 	float wantWidth = fFrame.IntegerWidth() + delta.x;
402 	float wantHeight = fFrame.IntegerHeight() + delta.y;
403 
404 	// enforce size limits
405 	WindowStack* stack = GetWindowStack();
406 	if (stack != NULL) {
407 		for (int32 i = 0; i < stack->CountWindows(); i++) {
408 			Window* window = stack->WindowList().ItemAt(i);
409 
410 			if (wantWidth < window->fMinWidth)
411 				wantWidth = window->fMinWidth;
412 			if (wantWidth > window->fMaxWidth)
413 				wantWidth = window->fMaxWidth;
414 
415 			if (wantHeight < window->fMinHeight)
416 				wantHeight = window->fMinHeight;
417 			if (wantHeight > window->fMaxHeight)
418 				wantHeight = window->fMaxHeight;
419 		}
420 
421 		delta.x = wantWidth - fFrame.IntegerWidth();
422 		delta.y = wantHeight - fFrame.IntegerHeight();
423 	}
424 
425 	::Decorator* decorator = Decorator();
426 
427 	if (decorator != NULL)
428 		decorator->SetOutlinesDelta(delta, dirtyRegion);
429 
430 	_UpdateContentRegion();
431 }
432 
433 
434 void
435 Window::ScrollViewBy(View* view, int32 dx, int32 dy)
436 {
437 	// this is executed in ServerWindow with the Readlock
438 	// held
439 
440 	if (!view || view == fTopView.Get() || (dx == 0 && dy == 0))
441 		return;
442 
443 	BRegion* dirty = fRegionPool.GetRegion();
444 	if (!dirty)
445 		return;
446 
447 	view->ScrollBy(dx, dy, dirty);
448 
449 //fDrawingEngine->FillRegion(*dirty, (rgb_color){ 255, 0, 255, 255 });
450 //snooze(20000);
451 
452 	if (!IsOffscreenWindow() && IsVisible() && view->IsVisible()) {
453 		dirty->IntersectWith(&VisibleContentRegion());
454 		_TriggerContentRedraw(*dirty);
455 	}
456 
457 	fRegionPool.Recycle(dirty);
458 }
459 
460 
461 //! Takes care of invalidating parts that could not be copied
462 void
463 Window::CopyContents(BRegion* region, int32 xOffset, int32 yOffset)
464 {
465 	// executed in ServerWindow thread with the read lock held
466 	if (!IsVisible())
467 		return;
468 
469 	BRegion* newDirty = fRegionPool.GetRegion(*region);
470 
471 	// clip the region to the visible contents at the
472 	// source and destination location (note that VisibleContentRegion()
473 	// is used once to make sure it is valid, then fVisibleContentRegion
474 	// is used directly)
475 	region->IntersectWith(&VisibleContentRegion());
476 	if (region->CountRects() > 0) {
477 		// Constrain to content region at destination
478 		region->OffsetBy(xOffset, yOffset);
479 		region->IntersectWith(&fVisibleContentRegion);
480 		if (region->CountRects() > 0) {
481 			// if the region still contains any rects
482 			// offset to source location again
483 			region->OffsetBy(-xOffset, -yOffset);
484 
485 			BRegion* allDirtyRegions = fRegionPool.GetRegion(fDirtyRegion);
486 			if (allDirtyRegions != NULL) {
487 				if (fPendingUpdateSession->IsUsed()) {
488 					allDirtyRegions->Include(
489 						&fPendingUpdateSession->DirtyRegion());
490 				}
491 				if (fCurrentUpdateSession->IsUsed()) {
492 					allDirtyRegions->Include(
493 						&fCurrentUpdateSession->DirtyRegion());
494 				}
495 				// Get just the part of the dirty regions which is semantically
496 				// copied along
497 				allDirtyRegions->IntersectWith(region);
498 			}
499 
500 			BRegion* copyRegion = fRegionPool.GetRegion(*region);
501 			if (copyRegion != NULL) {
502 				// never copy what's already dirty
503 				if (allDirtyRegions != NULL)
504 					copyRegion->Exclude(allDirtyRegions);
505 
506 				if (fDrawingEngine->LockParallelAccess()) {
507 					fDrawingEngine->CopyRegion(copyRegion, xOffset, yOffset);
508 					fDrawingEngine->UnlockParallelAccess();
509 
510 					// Prevent those parts from being added to the dirty region...
511 					newDirty->Exclude(copyRegion);
512 
513 					// The parts that could be copied are not dirty (at the
514 					// target location!)
515 					copyRegion->OffsetBy(xOffset, yOffset);
516 					// ... and even exclude them from the pending dirty region!
517 					if (fPendingUpdateSession->IsUsed())
518 						fPendingUpdateSession->DirtyRegion().Exclude(copyRegion);
519 				}
520 
521 				fRegionPool.Recycle(copyRegion);
522 			} else {
523 				// Fallback, should never be here.
524 				if (fDrawingEngine->LockParallelAccess()) {
525 					fDrawingEngine->CopyRegion(region, xOffset, yOffset);
526 					fDrawingEngine->UnlockParallelAccess();
527 				}
528 			}
529 
530 			if (allDirtyRegions != NULL)
531 				fRegionPool.Recycle(allDirtyRegions);
532 		}
533 	}
534 	// what is left visible from the original region
535 	// at the destination after the region which could be
536 	// copied has been excluded, is considered dirty
537 	// NOTE: it may look like dirty regions are not moved
538 	// if no region could be copied, but that's alright,
539 	// since these parts will now be in newDirty anyways
540 	// (with the right offset)
541 	newDirty->OffsetBy(xOffset, yOffset);
542 	newDirty->IntersectWith(&fVisibleContentRegion);
543 	if (newDirty->CountRects() > 0)
544 		ProcessDirtyRegion(*newDirty);
545 
546 	fRegionPool.Recycle(newDirty);
547 }
548 
549 
550 // #pragma mark -
551 
552 
553 void
554 Window::SetTopView(View* topView)
555 {
556 	if (fTopView.IsSet()) {
557 		fTopView->DetachedFromWindow();
558 	}
559 
560 	fTopView.SetTo(topView);
561 
562 	if (fTopView.IsSet()) {
563 		// the top view is special, it has a coordinate system
564 		// as if it was attached directly to the desktop, therefor,
565 		// the coordinate conversion through the view tree works
566 		// as expected, since the top view has no "parent" but has
567 		// fFrame as if it had
568 
569 		// make sure the location of the top view on screen matches ours
570 		fTopView->MoveBy((int32)(fFrame.left - fTopView->Frame().left),
571 			(int32)(fFrame.top - fTopView->Frame().top), NULL);
572 
573 		// make sure the size of the top view matches ours
574 		fTopView->ResizeBy((int32)(fFrame.Width() - fTopView->Frame().Width()),
575 			(int32)(fFrame.Height() - fTopView->Frame().Height()), NULL);
576 
577 		fTopView->AttachedToWindow(this);
578 	}
579 }
580 
581 
582 View*
583 Window::ViewAt(const BPoint& where)
584 {
585 	return fTopView->ViewAt(where);
586 }
587 
588 
589 window_anchor&
590 Window::Anchor(int32 index)
591 {
592 	return fAnchor[index];
593 }
594 
595 
596 Window*
597 Window::NextWindow(int32 index) const
598 {
599 	return fAnchor[index].next;
600 }
601 
602 
603 Window*
604 Window::PreviousWindow(int32 index) const
605 {
606 	return fAnchor[index].previous;
607 }
608 
609 
610 ::Decorator*
611 Window::Decorator() const
612 {
613 	if (!fCurrentStack.IsSet())
614 		return NULL;
615 	return fCurrentStack->Decorator();
616 }
617 
618 
619 bool
620 Window::ReloadDecor()
621 {
622 	::Decorator* decorator = NULL;
623 	WindowBehaviour* windowBehaviour = NULL;
624 	WindowStack* stack = GetWindowStack();
625 	if (stack == NULL)
626 		return false;
627 
628 	// only reload the window at the first position
629 	if (stack->WindowAt(0) != this)
630 		return true;
631 
632 	if (fLook != B_NO_BORDER_WINDOW_LOOK) {
633 		// we need a new decorator
634 		decorator = gDecorManager.AllocateDecorator(this);
635 		if (decorator == NULL)
636 			return false;
637 
638 		// add all tabs to the decorator
639 		for (int32 i = 1; i < stack->CountWindows(); i++) {
640 			Window* window = stack->WindowAt(i);
641 			BRegion dirty;
642 			DesktopSettings settings(fDesktop);
643 			if (decorator->AddTab(settings, window->Title(), window->Look(),
644 				window->Flags(), -1, &dirty) == NULL) {
645 				delete decorator;
646 				return false;
647 			}
648 		}
649 	} else
650 		return true;
651 
652 	windowBehaviour = gDecorManager.AllocateWindowBehaviour(this);
653 	if (windowBehaviour == NULL) {
654 		delete decorator;
655 		return false;
656 	}
657 
658 	stack->SetDecorator(decorator);
659 
660 	fWindowBehaviour.SetTo(windowBehaviour);
661 
662 	// set the correct focus and top layer tab
663 	for (int32 i = 0; i < stack->CountWindows(); i++) {
664 		Window* window = stack->WindowAt(i);
665 		if (window->IsFocus())
666 			decorator->SetFocus(i, true);
667 		if (window == stack->TopLayerWindow())
668 			decorator->SetTopTab(i);
669 	}
670 
671 	return true;
672 }
673 
674 
675 void
676 Window::SetScreen(const ::Screen* screen)
677 {
678 	// TODO this assert fails in Desktop::ShowWindow
679 	//ASSERT_MULTI_WRITE_LOCKED(fDesktop->ScreenLocker());
680 	fScreen = screen;
681 }
682 
683 
684 const ::Screen*
685 Window::Screen() const
686 {
687 	// TODO this assert also fails
688 	//ASSERT_MULTI_READ_LOCKED(fDesktop->ScreenLocker());
689 	return fScreen;
690 }
691 
692 
693 // #pragma mark -
694 
695 
696 void
697 Window::GetEffectiveDrawingRegion(View* view, BRegion& region)
698 {
699 	if (!fEffectiveDrawingRegionValid) {
700 		fEffectiveDrawingRegion = VisibleContentRegion();
701 		if (fUpdateRequested && !fInUpdate) {
702 			// We requested an update, but the client has not started it yet,
703 			// so it is only allowed to draw outside the pending update sessions
704 			// region
705 			fEffectiveDrawingRegion.Exclude(
706 				&fPendingUpdateSession->DirtyRegion());
707 		} else if (fInUpdate) {
708 			// enforce the dirty region of the update session
709 			fEffectiveDrawingRegion.IntersectWith(
710 				&fCurrentUpdateSession->DirtyRegion());
711 		} else {
712 			// not in update, the view can draw everywhere
713 //printf("Window(%s)::GetEffectiveDrawingRegion(for %s) - outside update\n", Title(), view->Name());
714 		}
715 
716 		fEffectiveDrawingRegionValid = true;
717 	}
718 
719 	// TODO: this is a region that needs to be cached later in the server
720 	// when the current view in ServerWindow is set, and we are currently
721 	// in an update (fInUpdate), than we can set this region and remember
722 	// it for the comming drawing commands until the current view changes
723 	// again or fEffectiveDrawingRegionValid is suddenly false.
724 	region = fEffectiveDrawingRegion;
725 	if (!fContentRegionValid)
726 		_UpdateContentRegion();
727 
728 	region.IntersectWith(&view->ScreenAndUserClipping(&fContentRegion));
729 }
730 
731 
732 bool
733 Window::DrawingRegionChanged(View* view) const
734 {
735 	return !fEffectiveDrawingRegionValid || !view->IsScreenClippingValid();
736 }
737 
738 
739 void
740 Window::ProcessDirtyRegion(BRegion& region)
741 {
742 	// if this is executed in the desktop thread,
743 	// it means that the window thread currently
744 	// blocks to get the read lock, if it is
745 	// executed from the window thread, it should
746 	// have the read lock and the desktop thread
747 	// is blocking to get the write lock. IAW, this
748 	// is only executed in one thread.
749 	if (fDirtyRegion.CountRects() == 0) {
750 		// the window needs to be informed
751 		// when the dirty region was empty.
752 		// NOTE: when the window thread has processed
753 		// the dirty region in MessageReceived(),
754 		// it will make the region empty again,
755 		// when it is empty here, we need to send
756 		// the message to initiate the next update round.
757 		// Until the message is processed in the window
758 		// thread, the desktop thread can add parts to
759 		// the region as it likes.
760 		ServerWindow()->RequestRedraw();
761 	}
762 
763 	fDirtyRegion.Include(&region);
764 	fDirtyCause |= UPDATE_EXPOSE;
765 }
766 
767 
768 void
769 Window::RedrawDirtyRegion()
770 {
771 	if (TopLayerStackWindow() != this) {
772 		fDirtyRegion.MakeEmpty();
773 		fDirtyCause = 0;
774 		return;
775 	}
776 
777 	// executed from ServerWindow with the read lock held
778 	if (IsVisible()) {
779 		_DrawBorder();
780 
781 		BRegion* dirtyContentRegion =
782 			fRegionPool.GetRegion(VisibleContentRegion());
783 		dirtyContentRegion->IntersectWith(&fDirtyRegion);
784 
785 		_TriggerContentRedraw(*dirtyContentRegion);
786 
787 		fRegionPool.Recycle(dirtyContentRegion);
788 	}
789 
790 	// reset the dirty region, since
791 	// we're fully clean. If the desktop
792 	// thread wanted to mark something
793 	// dirty in the mean time, it was
794 	// blocking on the global region lock to
795 	// get write access, since we're holding
796 	// the read lock for the whole time.
797 	fDirtyRegion.MakeEmpty();
798 	fDirtyCause = 0;
799 }
800 
801 
802 void
803 Window::MarkDirty(BRegion& regionOnScreen)
804 {
805 	// for marking any part of the desktop dirty
806 	// this will get write access to the global
807 	// region lock, and result in ProcessDirtyRegion()
808 	// to be called for any windows affected
809 	if (fDesktop)
810 		fDesktop->MarkDirty(regionOnScreen);
811 }
812 
813 
814 void
815 Window::MarkContentDirty(BRegion& regionOnScreen)
816 {
817 	// for triggering AS_REDRAW
818 	// since this won't affect other windows, read locking
819 	// is sufficient. If there was no dirty region before,
820 	// an update message is triggered
821 	if (fHidden || IsOffscreenWindow())
822 		return;
823 
824 	regionOnScreen.IntersectWith(&VisibleContentRegion());
825 	fDirtyCause |= UPDATE_REQUEST;
826 	_TriggerContentRedraw(regionOnScreen);
827 }
828 
829 
830 void
831 Window::MarkContentDirtyAsync(BRegion& regionOnScreen)
832 {
833 	// NOTE: see comments in ProcessDirtyRegion()
834 	if (fHidden || IsOffscreenWindow())
835 		return;
836 
837 	regionOnScreen.IntersectWith(&VisibleContentRegion());
838 
839 	if (fDirtyRegion.CountRects() == 0) {
840 		ServerWindow()->RequestRedraw();
841 	}
842 
843 	fDirtyRegion.Include(&regionOnScreen);
844 	fDirtyCause |= UPDATE_REQUEST;
845 }
846 
847 
848 void
849 Window::InvalidateView(View* view, BRegion& viewRegion)
850 {
851 	if (view && IsVisible() && view->IsVisible()) {
852 		if (!fContentRegionValid)
853 			_UpdateContentRegion();
854 
855 		view->LocalToScreenTransform().Apply(&viewRegion);
856 		viewRegion.IntersectWith(&VisibleContentRegion());
857 		if (viewRegion.CountRects() > 0) {
858 			viewRegion.IntersectWith(
859 				&view->ScreenAndUserClipping(&fContentRegion));
860 
861 //fDrawingEngine->FillRegion(viewRegion, rgb_color{ 0, 255, 0, 255 });
862 //snooze(10000);
863 			fDirtyCause |= UPDATE_REQUEST;
864 			_TriggerContentRedraw(viewRegion);
865 		}
866 	}
867 }
868 
869 // DisableUpdateRequests
870 void
871 Window::DisableUpdateRequests()
872 {
873 	fUpdatesEnabled = false;
874 }
875 
876 
877 // EnableUpdateRequests
878 void
879 Window::EnableUpdateRequests()
880 {
881 	fUpdatesEnabled = true;
882 	if (!fUpdateRequested && fPendingUpdateSession->IsUsed())
883 		_SendUpdateMessage();
884 }
885 
886 // #pragma mark -
887 
888 
889 /*!	\brief Handles a mouse-down message for the window.
890 
891 	\param message The message.
892 	\param where The point where the mouse click happened.
893 	\param lastClickTarget The target of the previous click.
894 	\param clickCount The number of subsequent, no longer than double-click
895 		interval separated clicks that have happened so far. This number doesn't
896 		necessarily match the value in the message. It has already been
897 		pre-processed in order to avoid erroneous multi-clicks (e.g. when a
898 		different button has been used or a different window was targeted). This
899 		is an in-out variable. The method can reset the value to 1, if it
900 		doesn't want this event handled as a multi-click. Returning a different
901 		click target will also make the caller reset the click count.
902 	\param _clickTarget Set by the method to a value identifying the clicked
903 		element. If not explicitly set, an invalid click target is assumed.
904 */
905 void
906 Window::MouseDown(BMessage* message, BPoint where,
907 	const ClickTarget& lastClickTarget, int32& clickCount,
908 	ClickTarget& _clickTarget)
909 {
910 	// If the previous click hit our decorator, get the hit region.
911 	int32 windowToken = fWindow->ServerToken();
912 	int32 lastHitRegion = 0;
913 	if (lastClickTarget.GetType() == ClickTarget::TYPE_WINDOW_DECORATOR
914 		&& lastClickTarget.WindowToken() == windowToken) {
915 		lastHitRegion = lastClickTarget.WindowElement();
916 	}
917 
918 	// Let the window behavior process the mouse event.
919 	int32 hitRegion = 0;
920 	bool eventEaten = fWindowBehaviour->MouseDown(message, where, lastHitRegion,
921 		clickCount, hitRegion);
922 
923 	if (eventEaten) {
924 		// click on the decorator (or equivalent)
925 		_clickTarget = ClickTarget(ClickTarget::TYPE_WINDOW_DECORATOR,
926 			windowToken, (int32)hitRegion);
927 	} else {
928 		// click was inside the window contents
929 		int32 viewToken = B_NULL_TOKEN;
930 		if (View* view = ViewAt(where)) {
931 			if (HasModal())
932 				return;
933 
934 			// clicking a simple View
935 			if (!IsFocus()) {
936 				bool acceptFirstClick
937 					= (Flags() & B_WILL_ACCEPT_FIRST_CLICK) != 0;
938 
939 				// Activate or focus the window in case it doesn't accept first
940 				// click, depending on the mouse mode
941 				if (!acceptFirstClick) {
942 					bool avoidFocus = (Flags() & B_AVOID_FOCUS) != 0;
943 					DesktopSettings desktopSettings(fDesktop);
944 					if (desktopSettings.MouseMode() == B_NORMAL_MOUSE)
945 						fDesktop->ActivateWindow(this);
946 					else if (!avoidFocus)
947 						fDesktop->SetFocusWindow(this);
948 
949 					// Eat the click if we don't accept first click
950 					// (B_AVOID_FOCUS never gets the focus, so they always accept
951 					// the first click)
952 					// TODO: the latter is unlike BeOS - if we really wanted to
953 					// imitate this behaviour, we would need to check if we're
954 					// the front window instead of the focus window
955 					if (!desktopSettings.AcceptFirstClick() && !avoidFocus)
956 						return;
957 				}
958 			}
959 
960 			// fill out view token for the view under the mouse
961 			viewToken = view->Token();
962 			view->MouseDown(message, where);
963 		}
964 
965 		_clickTarget = ClickTarget(ClickTarget::TYPE_WINDOW_CONTENTS,
966 			windowToken, viewToken);
967 	}
968 }
969 
970 
971 void
972 Window::MouseUp(BMessage* message, BPoint where, int32* _viewToken)
973 {
974 	fWindowBehaviour->MouseUp(message, where);
975 
976 	if (View* view = ViewAt(where)) {
977 		if (HasModal())
978 			return;
979 
980 		*_viewToken = view->Token();
981 		view->MouseUp(message, where);
982 	}
983 }
984 
985 
986 void
987 Window::MouseMoved(BMessage *message, BPoint where, int32* _viewToken,
988 	bool isLatestMouseMoved, bool isFake)
989 {
990 	View* view = ViewAt(where);
991 	if (view != NULL)
992 		*_viewToken = view->Token();
993 
994 	// ignore pointer history
995 	if (!isLatestMouseMoved)
996 		return;
997 
998 	fWindowBehaviour->MouseMoved(message, where, isFake);
999 
1000 	// mouse cursor
1001 
1002 	if (view != NULL) {
1003 		view->MouseMoved(message, where);
1004 
1005 		// TODO: there is more for real cursor support, ie. if a window is closed,
1006 		//		new app cursor shouldn't override view cursor, ...
1007 		ServerWindow()->App()->SetCurrentCursor(view->Cursor());
1008 	}
1009 }
1010 
1011 
1012 void
1013 Window::ModifiersChanged(int32 modifiers)
1014 {
1015 	fWindowBehaviour->ModifiersChanged(modifiers);
1016 }
1017 
1018 
1019 // #pragma mark -
1020 
1021 
1022 void
1023 Window::WorkspaceActivated(int32 index, bool active)
1024 {
1025 	BMessage activatedMsg(B_WORKSPACE_ACTIVATED);
1026 	activatedMsg.AddInt64("when", system_time());
1027 	activatedMsg.AddInt32("workspace", index);
1028 	activatedMsg.AddBool("active", active);
1029 
1030 	ServerWindow()->SendMessageToClient(&activatedMsg);
1031 }
1032 
1033 
1034 void
1035 Window::WorkspacesChanged(uint32 oldWorkspaces, uint32 newWorkspaces)
1036 {
1037 	fWorkspaces = newWorkspaces;
1038 
1039 	BMessage changedMsg(B_WORKSPACES_CHANGED);
1040 	changedMsg.AddInt64("when", system_time());
1041 	changedMsg.AddInt32("old", oldWorkspaces);
1042 	changedMsg.AddInt32("new", newWorkspaces);
1043 
1044 	ServerWindow()->SendMessageToClient(&changedMsg);
1045 }
1046 
1047 
1048 void
1049 Window::Activated(bool active)
1050 {
1051 	BMessage msg(B_WINDOW_ACTIVATED);
1052 	msg.AddBool("active", active);
1053 	ServerWindow()->SendMessageToClient(&msg);
1054 }
1055 
1056 
1057 //# pragma mark -
1058 
1059 
1060 void
1061 Window::SetTitle(const char* name, BRegion& dirty)
1062 {
1063 	// rebuild the clipping for the title area
1064 	// and redraw it.
1065 
1066 	fTitle = name;
1067 
1068 	::Decorator* decorator = Decorator();
1069 	if (decorator) {
1070 		int32 index = PositionInStack();
1071 		decorator->SetTitle(index, name, &dirty);
1072 	}
1073 }
1074 
1075 
1076 void
1077 Window::SetFocus(bool focus)
1078 {
1079 	::Decorator* decorator = Decorator();
1080 
1081 	// executed from Desktop thread
1082 	// it holds the clipping write lock,
1083 	// so the window thread cannot be
1084 	// accessing fIsFocus
1085 
1086 	BRegion* dirty = NULL;
1087 	if (decorator)
1088 		dirty = fRegionPool.GetRegion(decorator->GetFootprint());
1089 	if (dirty) {
1090 		dirty->IntersectWith(&fVisibleRegion);
1091 		fDesktop->MarkDirty(*dirty);
1092 		fRegionPool.Recycle(dirty);
1093 	}
1094 
1095 	fIsFocus = focus;
1096 	if (decorator) {
1097 		int32 index = PositionInStack();
1098 		decorator->SetFocus(index, focus);
1099 	}
1100 
1101 	Activated(focus);
1102 }
1103 
1104 
1105 void
1106 Window::SetHidden(bool hidden)
1107 {
1108 	// the desktop takes care of dirty regions
1109 	if (fHidden != hidden) {
1110 		fHidden = hidden;
1111 
1112 		fTopView->SetHidden(hidden);
1113 
1114 		// TODO: anything else?
1115 	}
1116 }
1117 
1118 
1119 void
1120 Window::SetShowLevel(int32 showLevel)
1121 {
1122 	if (showLevel == fShowLevel)
1123 		return;
1124 
1125 	fShowLevel = showLevel;
1126 }
1127 
1128 
1129 void
1130 Window::SetMinimized(bool minimized)
1131 {
1132 	if (minimized == fMinimized)
1133 		return;
1134 
1135 	fMinimized = minimized;
1136 }
1137 
1138 
1139 bool
1140 Window::IsVisible() const
1141 {
1142 	if (IsOffscreenWindow())
1143 		return true;
1144 
1145 	if (IsHidden())
1146 		return false;
1147 
1148 /*
1149 	if (fVisibleRegion.CountRects() == 0)
1150 		return false;
1151 */
1152 	return fCurrentWorkspace >= 0 && fCurrentWorkspace < kWorkingList;
1153 }
1154 
1155 
1156 bool
1157 Window::IsDragging() const
1158 {
1159 	if (!fWindowBehaviour.IsSet())
1160 		return false;
1161 	return fWindowBehaviour->IsDragging();
1162 }
1163 
1164 
1165 bool
1166 Window::IsResizing() const
1167 {
1168 	if (!fWindowBehaviour.IsSet())
1169 		return false;
1170 	return fWindowBehaviour->IsResizing();
1171 }
1172 
1173 
1174 void
1175 Window::SetSizeLimits(int32 minWidth, int32 maxWidth, int32 minHeight,
1176 	int32 maxHeight)
1177 {
1178 	if (minWidth < 0)
1179 		minWidth = 0;
1180 
1181 	if (minHeight < 0)
1182 		minHeight = 0;
1183 
1184 	fMinWidth = minWidth;
1185 	fMaxWidth = maxWidth;
1186 	fMinHeight = minHeight;
1187 	fMaxHeight = maxHeight;
1188 
1189 	// give the Decorator a say in this too
1190 	::Decorator* decorator = Decorator();
1191 	if (decorator) {
1192 		decorator->GetSizeLimits(&fMinWidth, &fMinHeight, &fMaxWidth,
1193 			&fMaxHeight);
1194 	}
1195 
1196 	_ObeySizeLimits();
1197 }
1198 
1199 
1200 void
1201 Window::GetSizeLimits(int32* minWidth, int32* maxWidth,
1202 	int32* minHeight, int32* maxHeight) const
1203 {
1204 	*minWidth = fMinWidth;
1205 	*maxWidth = fMaxWidth;
1206 	*minHeight = fMinHeight;
1207 	*maxHeight = fMaxHeight;
1208 }
1209 
1210 
1211 bool
1212 Window::SetTabLocation(float location, bool isShifting, BRegion& dirty)
1213 {
1214 	::Decorator* decorator = Decorator();
1215 	if (decorator) {
1216 		int32 index = PositionInStack();
1217 		return decorator->SetTabLocation(index, location, isShifting, &dirty);
1218 	}
1219 
1220 	return false;
1221 }
1222 
1223 
1224 float
1225 Window::TabLocation() const
1226 {
1227 	::Decorator* decorator = Decorator();
1228 	if (decorator) {
1229 		int32 index = PositionInStack();
1230 		return decorator->TabLocation(index);
1231 	}
1232 	return 0.0;
1233 }
1234 
1235 
1236 bool
1237 Window::SetDecoratorSettings(const BMessage& settings, BRegion& dirty)
1238 {
1239 	if (settings.what == 'prVu') {
1240 		// 'prVu' == preview a decorator!
1241 		BString path;
1242 		if (settings.FindString("preview", &path) == B_OK)
1243 			return gDecorManager.PreviewDecorator(path, this) == B_OK;
1244 		return false;
1245 	}
1246 
1247 	::Decorator* decorator = Decorator();
1248 	if (decorator)
1249 		return decorator->SetSettings(settings, &dirty);
1250 
1251 	return false;
1252 }
1253 
1254 
1255 bool
1256 Window::GetDecoratorSettings(BMessage* settings)
1257 {
1258 	if (fDesktop)
1259 		fDesktop->GetDecoratorSettings(this, *settings);
1260 
1261 	::Decorator* decorator = Decorator();
1262 	if (decorator)
1263 		return decorator->GetSettings(settings);
1264 
1265 	return false;
1266 }
1267 
1268 
1269 void
1270 Window::FontsChanged(BRegion* updateRegion)
1271 {
1272 	::Decorator* decorator = Decorator();
1273 	if (decorator != NULL) {
1274 		DesktopSettings settings(fDesktop);
1275 		decorator->FontsChanged(settings, updateRegion);
1276 	}
1277 }
1278 
1279 
1280 void
1281 Window::ColorsChanged(BRegion* updateRegion)
1282 {
1283 	::Decorator* decorator = Decorator();
1284 	if (decorator != NULL) {
1285 		DesktopSettings settings(fDesktop);
1286 		decorator->ColorsChanged(settings, updateRegion);
1287 	}
1288 }
1289 
1290 
1291 void
1292 Window::SetLook(window_look look, BRegion* updateRegion)
1293 {
1294 	fLook = look;
1295 
1296 	fContentRegionValid = false;
1297 		// mabye a resize handle was added...
1298 	fEffectiveDrawingRegionValid = false;
1299 		// ...and therefor the drawing region is
1300 		// likely not valid anymore either
1301 
1302 	if (!fCurrentStack.IsSet())
1303 		return;
1304 
1305 	int32 stackPosition = PositionInStack();
1306 
1307 	::Decorator* decorator = Decorator();
1308 	if (decorator == NULL && look != B_NO_BORDER_WINDOW_LOOK) {
1309 		// we need a new decorator
1310 		decorator = gDecorManager.AllocateDecorator(this);
1311 		fCurrentStack->SetDecorator(decorator);
1312 		if (IsFocus())
1313 			decorator->SetFocus(stackPosition, true);
1314 	}
1315 
1316 	if (decorator != NULL) {
1317 		DesktopSettings settings(fDesktop);
1318 		decorator->SetLook(stackPosition, settings, look, updateRegion);
1319 
1320 		// we might need to resize the window!
1321 		decorator->GetSizeLimits(&fMinWidth, &fMinHeight, &fMaxWidth,
1322 			&fMaxHeight);
1323 		_ObeySizeLimits();
1324 	}
1325 
1326 	if (look == B_NO_BORDER_WINDOW_LOOK) {
1327 		// we don't need a decorator for this window
1328 		fCurrentStack->SetDecorator(NULL);
1329 	}
1330 }
1331 
1332 
1333 void
1334 Window::SetFeel(window_feel feel)
1335 {
1336 	// if the subset list is no longer needed, clear it
1337 	if ((fFeel == B_MODAL_SUBSET_WINDOW_FEEL
1338 			|| fFeel == B_FLOATING_SUBSET_WINDOW_FEEL)
1339 		&& feel != B_MODAL_SUBSET_WINDOW_FEEL
1340 		&& feel != B_FLOATING_SUBSET_WINDOW_FEEL)
1341 		fSubsets.MakeEmpty();
1342 
1343 	fFeel = feel;
1344 
1345 	// having modal windows with B_AVOID_FRONT or B_AVOID_FOCUS doesn't
1346 	// make that much sense, so we filter those flags out on demand
1347 	fFlags = fOriginalFlags;
1348 	fFlags &= ValidWindowFlags(fFeel);
1349 
1350 	if (!IsNormal()) {
1351 		fFlags |= B_SAME_POSITION_IN_ALL_WORKSPACES;
1352 		_PropagatePosition();
1353 	}
1354 }
1355 
1356 
1357 void
1358 Window::SetFlags(uint32 flags, BRegion* updateRegion)
1359 {
1360 	fOriginalFlags = flags;
1361 	fFlags = flags & ValidWindowFlags(fFeel);
1362 	if (!IsNormal())
1363 		fFlags |= B_SAME_POSITION_IN_ALL_WORKSPACES;
1364 
1365 	if ((fFlags & B_SAME_POSITION_IN_ALL_WORKSPACES) != 0)
1366 		_PropagatePosition();
1367 
1368 	::Decorator* decorator = Decorator();
1369 	if (decorator == NULL)
1370 		return;
1371 
1372 	int32 stackPosition = PositionInStack();
1373 	decorator->SetFlags(stackPosition, flags, updateRegion);
1374 
1375 	// we might need to resize the window!
1376 	decorator->GetSizeLimits(&fMinWidth, &fMinHeight, &fMaxWidth, &fMaxHeight);
1377 	_ObeySizeLimits();
1378 
1379 // TODO: not sure if we want to do this
1380 #if 0
1381 	if ((fOriginalFlags & kWindowScreenFlag) != (flags & kWindowScreenFlag)) {
1382 		// TODO: disabling needs to be nestable (or we might lose the previous
1383 		// update state)
1384 		if ((flags & kWindowScreenFlag) != 0)
1385 			DisableUpdateRequests();
1386 		else
1387 			EnableUpdateRequests();
1388 	}
1389 #endif
1390 }
1391 
1392 
1393 /*!	Returns whether or not a window is in the workspace list with the
1394 	specified \a index.
1395 */
1396 bool
1397 Window::InWorkspace(int32 index) const
1398 {
1399 	return (fWorkspaces & (1UL << index)) != 0;
1400 }
1401 
1402 
1403 bool
1404 Window::SupportsFront()
1405 {
1406 	if (fFeel == kDesktopWindowFeel
1407 		|| fFeel == kMenuWindowFeel
1408 		|| (fFlags & B_AVOID_FRONT) != 0)
1409 		return false;
1410 
1411 	return true;
1412 }
1413 
1414 
1415 bool
1416 Window::IsModal() const
1417 {
1418 	return IsModalFeel(fFeel);
1419 }
1420 
1421 
1422 bool
1423 Window::IsFloating() const
1424 {
1425 	return IsFloatingFeel(fFeel);
1426 }
1427 
1428 
1429 bool
1430 Window::IsNormal() const
1431 {
1432 	return !IsFloatingFeel(fFeel) && !IsModalFeel(fFeel);
1433 }
1434 
1435 
1436 bool
1437 Window::HasModal() const
1438 {
1439 	for (Window* window = NextWindow(fCurrentWorkspace); window != NULL;
1440 			window = window->NextWindow(fCurrentWorkspace)) {
1441 		if (window->IsHidden() || !window->IsModal())
1442 			continue;
1443 
1444 		if (window->HasInSubset(this))
1445 			return true;
1446 	}
1447 
1448 	return false;
1449 }
1450 
1451 
1452 /*!	\brief Returns the windows that's in behind of the backmost position
1453 		this window can get.
1454 	Returns NULL is this window can be the backmost window.
1455 
1456 	\param workspace the workspace on which this check should be made. If
1457 		the value is -1, the window's current workspace will be used.
1458 */
1459 Window*
1460 Window::Backmost(Window* window, int32 workspace)
1461 {
1462 	if (workspace == -1)
1463 		workspace = fCurrentWorkspace;
1464 
1465 	ASSERT(workspace != -1);
1466 	if (workspace == -1)
1467 		return NULL;
1468 
1469 	// Desktop windows are always backmost
1470 	if (fFeel == kDesktopWindowFeel)
1471 		return NULL;
1472 
1473 	if (window == NULL)
1474 		window = PreviousWindow(workspace);
1475 
1476 	for (; window != NULL; window = window->PreviousWindow(workspace)) {
1477 		if (window->IsHidden() || window == this)
1478 			continue;
1479 
1480 		if (HasInSubset(window))
1481 			return window;
1482 	}
1483 
1484 	return NULL;
1485 }
1486 
1487 
1488 /*!	\brief Returns the window that's in front of the frontmost position
1489 		this window can get.
1490 	Returns NULL if this window can be the frontmost window.
1491 
1492 	\param workspace the workspace on which this check should be made. If
1493 		the value is -1, the window's current workspace will be used.
1494 */
1495 Window*
1496 Window::Frontmost(Window* first, int32 workspace)
1497 {
1498 	if (workspace == -1)
1499 		workspace = fCurrentWorkspace;
1500 
1501 	ASSERT(workspace != -1);
1502 	if (workspace == -1)
1503 		return NULL;
1504 
1505 	if (fFeel == kDesktopWindowFeel)
1506 		return first ? first : NextWindow(workspace);
1507 
1508 	if (first == NULL)
1509 		first = NextWindow(workspace);
1510 
1511 	for (Window* window = first; window != NULL;
1512 			window = window->NextWindow(workspace)) {
1513 		if (window->IsHidden() || window == this)
1514 			continue;
1515 
1516 		if (window->HasInSubset(this))
1517 			return window;
1518 	}
1519 
1520 	return NULL;
1521 }
1522 
1523 
1524 bool
1525 Window::AddToSubset(Window* window)
1526 {
1527 	return fSubsets.AddItem(window);
1528 }
1529 
1530 
1531 void
1532 Window::RemoveFromSubset(Window* window)
1533 {
1534 	fSubsets.RemoveItem(window);
1535 }
1536 
1537 
1538 /*!	Returns whether or not a window is in the subset of this window.
1539 	If a window is in the subset of this window, it means it should always
1540 	appear behind this window.
1541 */
1542 bool
1543 Window::HasInSubset(const Window* window) const
1544 {
1545 	if (window == NULL || fFeel == window->Feel()
1546 		|| fFeel == B_NORMAL_WINDOW_FEEL)
1547 		return false;
1548 
1549 	// Menus are a special case: they will always be on-top of every window
1550 	// of their application
1551 	if (fFeel == kMenuWindowFeel)
1552 		return window->ServerWindow()->App() == ServerWindow()->App();
1553 	if (window->Feel() == kMenuWindowFeel)
1554 		return false;
1555 
1556 	// we have a few special feels that have a fixed order
1557 
1558 	const int32 kFeels[] = {kPasswordWindowFeel, kWindowScreenFeel,
1559 		B_MODAL_ALL_WINDOW_FEEL, B_FLOATING_ALL_WINDOW_FEEL};
1560 
1561 	for (uint32 order = 0;
1562 			order < sizeof(kFeels) / sizeof(kFeels[0]); order++) {
1563 		if (fFeel == kFeels[order])
1564 			return true;
1565 		if (window->Feel() == kFeels[order])
1566 			return false;
1567 	}
1568 
1569 	if ((fFeel == B_FLOATING_APP_WINDOW_FEEL
1570 			&& window->Feel() != B_MODAL_APP_WINDOW_FEEL)
1571 		|| fFeel == B_MODAL_APP_WINDOW_FEEL)
1572 		return window->ServerWindow()->App() == ServerWindow()->App();
1573 
1574 	return fSubsets.HasItem(window);
1575 }
1576 
1577 
1578 /*!	\brief Collects all workspaces views in this window and puts it into \a list
1579 */
1580 void
1581 Window::FindWorkspacesViews(BObjectList<WorkspacesView>& list) const
1582 {
1583 	int32 count = fWorkspacesViewCount;
1584 	fTopView->FindViews(kWorkspacesViewFlag, (BObjectList<View>&)list, count);
1585 }
1586 
1587 
1588 /*!	\brief Returns on which workspaces the window should be visible.
1589 
1590 	A modal or floating window may be visible on a workspace if one
1591 	of its subset windows is visible there. Floating windows also need
1592 	to have a subset as front window to be visible.
1593 */
1594 uint32
1595 Window::SubsetWorkspaces() const
1596 {
1597 	if (fFeel == B_MODAL_ALL_WINDOW_FEEL
1598 		|| fFeel == B_FLOATING_ALL_WINDOW_FEEL)
1599 		return B_ALL_WORKSPACES;
1600 
1601 	if (fFeel == B_FLOATING_APP_WINDOW_FEEL) {
1602 		Window* front = fDesktop->FrontWindow();
1603 		if (front != NULL && front->IsNormal()
1604 			&& front->ServerWindow()->App() == ServerWindow()->App())
1605 			return ServerWindow()->App()->Workspaces();
1606 
1607 		return 0;
1608 	}
1609 
1610 	if (fFeel == B_MODAL_APP_WINDOW_FEEL) {
1611 		uint32 workspaces = ServerWindow()->App()->Workspaces();
1612 		if (workspaces == 0) {
1613 			// The application doesn't seem to have any other windows
1614 			// open or visible - but we'd like to see modal windows
1615 			// anyway, at least when they are first opened.
1616 			return 1UL << fDesktop->CurrentWorkspace();
1617 		}
1618 		return workspaces;
1619 	}
1620 
1621 	if (fFeel == B_MODAL_SUBSET_WINDOW_FEEL
1622 		|| fFeel == B_FLOATING_SUBSET_WINDOW_FEEL) {
1623 		uint32 workspaces = 0;
1624 		bool hasNormalFront = false;
1625 		for (int32 i = 0; i < fSubsets.CountItems(); i++) {
1626 			Window* window = fSubsets.ItemAt(i);
1627 
1628 			if (!window->IsHidden())
1629 				workspaces |= window->Workspaces();
1630 			if (window == fDesktop->FrontWindow() && window->IsNormal())
1631 				hasNormalFront = true;
1632 		}
1633 
1634 		if (fFeel == B_FLOATING_SUBSET_WINDOW_FEEL && !hasNormalFront)
1635 			return 0;
1636 
1637 		return workspaces;
1638 	}
1639 
1640 	return 0;
1641 }
1642 
1643 
1644 /*!	Returns whether or not a window is in the subset workspace list with the
1645 	specified \a index.
1646 	See SubsetWorkspaces().
1647 */
1648 bool
1649 Window::InSubsetWorkspace(int32 index) const
1650 {
1651 	return (SubsetWorkspaces() & (1UL << index)) != 0;
1652 }
1653 
1654 
1655 // #pragma mark - static
1656 
1657 
1658 /*static*/ bool
1659 Window::IsValidLook(window_look look)
1660 {
1661 	return look == B_TITLED_WINDOW_LOOK
1662 		|| look == B_DOCUMENT_WINDOW_LOOK
1663 		|| look == B_MODAL_WINDOW_LOOK
1664 		|| look == B_FLOATING_WINDOW_LOOK
1665 		|| look == B_BORDERED_WINDOW_LOOK
1666 		|| look == B_NO_BORDER_WINDOW_LOOK
1667 		|| look == kDesktopWindowLook
1668 		|| look == kLeftTitledWindowLook;
1669 }
1670 
1671 
1672 /*static*/ bool
1673 Window::IsValidFeel(window_feel feel)
1674 {
1675 	return feel == B_NORMAL_WINDOW_FEEL
1676 		|| feel == B_MODAL_SUBSET_WINDOW_FEEL
1677 		|| feel == B_MODAL_APP_WINDOW_FEEL
1678 		|| feel == B_MODAL_ALL_WINDOW_FEEL
1679 		|| feel == B_FLOATING_SUBSET_WINDOW_FEEL
1680 		|| feel == B_FLOATING_APP_WINDOW_FEEL
1681 		|| feel == B_FLOATING_ALL_WINDOW_FEEL
1682 		|| feel == kDesktopWindowFeel
1683 		|| feel == kMenuWindowFeel
1684 		|| feel == kWindowScreenFeel
1685 		|| feel == kPasswordWindowFeel
1686 		|| feel == kOffscreenWindowFeel;
1687 }
1688 
1689 
1690 /*static*/ bool
1691 Window::IsModalFeel(window_feel feel)
1692 {
1693 	return feel == B_MODAL_SUBSET_WINDOW_FEEL
1694 		|| feel == B_MODAL_APP_WINDOW_FEEL
1695 		|| feel == B_MODAL_ALL_WINDOW_FEEL;
1696 }
1697 
1698 
1699 /*static*/ bool
1700 Window::IsFloatingFeel(window_feel feel)
1701 {
1702 	return feel == B_FLOATING_SUBSET_WINDOW_FEEL
1703 		|| feel == B_FLOATING_APP_WINDOW_FEEL
1704 		|| feel == B_FLOATING_ALL_WINDOW_FEEL;
1705 }
1706 
1707 
1708 /*static*/ uint32
1709 Window::ValidWindowFlags()
1710 {
1711 	return B_NOT_MOVABLE
1712 		| B_NOT_CLOSABLE
1713 		| B_NOT_ZOOMABLE
1714 		| B_NOT_MINIMIZABLE
1715 		| B_NOT_RESIZABLE
1716 		| B_NOT_H_RESIZABLE
1717 		| B_NOT_V_RESIZABLE
1718 		| B_AVOID_FRONT
1719 		| B_AVOID_FOCUS
1720 		| B_WILL_ACCEPT_FIRST_CLICK
1721 		| B_OUTLINE_RESIZE
1722 		| B_NO_WORKSPACE_ACTIVATION
1723 		| B_NOT_ANCHORED_ON_ACTIVATE
1724 		| B_ASYNCHRONOUS_CONTROLS
1725 		| B_QUIT_ON_WINDOW_CLOSE
1726 		| B_SAME_POSITION_IN_ALL_WORKSPACES
1727 		| B_AUTO_UPDATE_SIZE_LIMITS
1728 		| B_CLOSE_ON_ESCAPE
1729 		| B_NO_SERVER_SIDE_WINDOW_MODIFIERS
1730 		| kWindowScreenFlag
1731 		| kAcceptKeyboardFocusFlag;
1732 }
1733 
1734 
1735 /*static*/ uint32
1736 Window::ValidWindowFlags(window_feel feel)
1737 {
1738 	uint32 flags = ValidWindowFlags();
1739 	if (IsModalFeel(feel))
1740 		return flags & ~(B_AVOID_FOCUS | B_AVOID_FRONT);
1741 
1742 	return flags;
1743 }
1744 
1745 
1746 // #pragma mark - private
1747 
1748 
1749 void
1750 Window::_ShiftPartOfRegion(BRegion* region, BRegion* regionToShift,
1751 	int32 xOffset, int32 yOffset)
1752 {
1753 	BRegion* common = fRegionPool.GetRegion(*regionToShift);
1754 	if (!common)
1755 		return;
1756 	// see if there is a common part at all
1757 	common->IntersectWith(region);
1758 	if (common->CountRects() > 0) {
1759 		// cut the common part from the region,
1760 		// offset that to destination and include again
1761 		region->Exclude(common);
1762 		common->OffsetBy(xOffset, yOffset);
1763 		region->Include(common);
1764 	}
1765 	fRegionPool.Recycle(common);
1766 }
1767 
1768 
1769 void
1770 Window::_TriggerContentRedraw(BRegion& dirtyContentRegion)
1771 {
1772 	if (!IsVisible() || dirtyContentRegion.CountRects() == 0
1773 		|| (fFlags & kWindowScreenFlag) != 0)
1774 		return;
1775 
1776 	// put this into the pending dirty region
1777 	// to eventually trigger a client redraw
1778 
1779 	_TransferToUpdateSession(&dirtyContentRegion);
1780 }
1781 
1782 
1783 void
1784 Window::_DrawBorder()
1785 {
1786 	// this is executed in the window thread, but only
1787 	// in respond to a REDRAW message having been received, the
1788 	// clipping lock is held for reading
1789 	::Decorator* decorator = Decorator();
1790 	if (!decorator)
1791 		return;
1792 
1793 	// construct the region of the border that needs redrawing
1794 	BRegion* dirtyBorderRegion = fRegionPool.GetRegion();
1795 	if (!dirtyBorderRegion)
1796 		return;
1797 	GetBorderRegion(dirtyBorderRegion);
1798 	// intersect with our visible region
1799 	dirtyBorderRegion->IntersectWith(&fVisibleRegion);
1800 	// intersect with the dirty region
1801 	dirtyBorderRegion->IntersectWith(&fDirtyRegion);
1802 
1803 	DrawingEngine* engine = decorator->GetDrawingEngine();
1804 	if (dirtyBorderRegion->CountRects() > 0 && engine->LockParallelAccess()) {
1805 		engine->ConstrainClippingRegion(dirtyBorderRegion);
1806 		bool copyToFrontEnabled = engine->CopyToFrontEnabled();
1807 		engine->SetCopyToFrontEnabled(false);
1808 
1809 		decorator->Draw(dirtyBorderRegion->Frame());
1810 
1811 		engine->SetCopyToFrontEnabled(copyToFrontEnabled);
1812 		engine->CopyToFront(*dirtyBorderRegion);
1813 
1814 // TODO: remove this once the DrawState stuff is handled
1815 // more cleanly. The reason why this is needed is that
1816 // when the decorator draws strings, a draw state is set
1817 // on the Painter object, and this is were it might get
1818 // out of sync with what the ServerWindow things is the
1819 // current DrawState set on the Painter
1820 fWindow->ResyncDrawState();
1821 
1822 		engine->UnlockParallelAccess();
1823 	}
1824 	fRegionPool.Recycle(dirtyBorderRegion);
1825 }
1826 
1827 
1828 /*!	pre: the clipping is readlocked (this function is
1829 	only called from _TriggerContentRedraw()), which
1830 	in turn is only called from MessageReceived() with
1831 	the clipping lock held
1832 */
1833 void
1834 Window::_TransferToUpdateSession(BRegion* contentDirtyRegion)
1835 {
1836 	if (contentDirtyRegion->CountRects() <= 0)
1837 		return;
1838 
1839 //fDrawingEngine->FillRegion(*contentDirtyRegion, sPendingColor);
1840 //snooze(20000);
1841 
1842 	// add to pending
1843 	fPendingUpdateSession->SetUsed(true);
1844 //	if (!fPendingUpdateSession->IsExpose())
1845 	fPendingUpdateSession->AddCause(fDirtyCause);
1846 	fPendingUpdateSession->Include(contentDirtyRegion);
1847 
1848 	if (!fUpdateRequested) {
1849 		// send this to client
1850 		_SendUpdateMessage();
1851 		// the pending region is now the current,
1852 		// though the update does not start until
1853 		// we received BEGIN_UPDATE from the client
1854 	}
1855 }
1856 
1857 
1858 void
1859 Window::_SendUpdateMessage()
1860 {
1861 	if (!fUpdatesEnabled)
1862 		return;
1863 
1864 	BMessage message(_UPDATE_);
1865 	if (ServerWindow()->SendMessageToClient(&message) != B_OK) {
1866 		// If sending the message failed, we'll just keep adding to the dirty
1867 		// region until sending was successful.
1868 		// TODO: we might want to automatically resend this message in this case
1869 		return;
1870 	}
1871 
1872 	fUpdateRequested = true;
1873 	fEffectiveDrawingRegionValid = false;
1874 }
1875 
1876 
1877 void
1878 Window::BeginUpdate(BPrivate::PortLink& link)
1879 {
1880 	// NOTE: since we might "shift" parts of the
1881 	// internal dirty regions from the desktop thread
1882 	// in response to Window::ResizeBy(), which
1883 	// might move arround views, the user of this function
1884 	// needs to hold the global clipping lock so that the internal
1885 	// dirty regions are not messed with from the Desktop thread
1886 	// and ServerWindow thread at the same time.
1887 
1888 	if (!fUpdateRequested) {
1889 		link.StartMessage(B_ERROR);
1890 		link.Flush();
1891 		fprintf(stderr, "Window::BeginUpdate() - no update requested!\n");
1892 		return;
1893 	}
1894 
1895 	// make the pending update session the current update session
1896 	// (toggle the pointers)
1897 	UpdateSession* temp = fCurrentUpdateSession;
1898 	fCurrentUpdateSession = fPendingUpdateSession;
1899 	fPendingUpdateSession = temp;
1900 	fPendingUpdateSession->SetUsed(false);
1901 	// all drawing command from the client
1902 	// will have the dirty region from the update
1903 	// session enforced
1904 	fInUpdate = true;
1905 	fEffectiveDrawingRegionValid = false;
1906 
1907 	// TODO: each view could be drawn individually
1908 	// right before carrying out the first drawing
1909 	// command from the client during an update
1910 	// (View::IsBackgroundDirty() can be used
1911 	// for this)
1912 	if (!fContentRegionValid)
1913 		_UpdateContentRegion();
1914 
1915 	BRegion* dirty = fRegionPool.GetRegion(
1916 		fCurrentUpdateSession->DirtyRegion());
1917 	if (!dirty) {
1918 		link.StartMessage(B_ERROR);
1919 		link.Flush();
1920 		return;
1921 	}
1922 
1923 	dirty->IntersectWith(&VisibleContentRegion());
1924 
1925 //if (!fCurrentUpdateSession->IsExpose()) {
1926 ////sCurrentColor.red = rand() % 255;
1927 ////sCurrentColor.green = rand() % 255;
1928 ////sCurrentColor.blue = rand() % 255;
1929 ////sPendingColor.red = rand() % 255;
1930 ////sPendingColor.green = rand() % 255;
1931 ////sPendingColor.blue = rand() % 255;
1932 //fDrawingEngine->FillRegion(*dirty, sCurrentColor);
1933 //snooze(10000);
1934 //}
1935 
1936 	link.StartMessage(B_OK);
1937 	// append the current window geometry to the
1938 	// message, the client will need it
1939 	link.Attach<BPoint>(fFrame.LeftTop());
1940 	link.Attach<float>(fFrame.Width());
1941 	link.Attach<float>(fFrame.Height());
1942 	// find and attach all views that intersect with
1943 	// the dirty region
1944 	fTopView->AddTokensForViewsInRegion(link, *dirty, &fContentRegion);
1945 	// mark the end of the token "list"
1946 	link.Attach<int32>(B_NULL_TOKEN);
1947 	link.Flush();
1948 
1949 	// supress back to front buffer copies in the drawing engine
1950 	fDrawingEngine->SetCopyToFrontEnabled(false);
1951 
1952 	if (fDrawingEngine->LockParallelAccess()) {
1953 		fDrawingEngine->SuspendAutoSync();
1954 
1955 		fTopView->Draw(GetDrawingEngine(), dirty, &fContentRegion, true);
1956 
1957 		fDrawingEngine->Sync();
1958 		fDrawingEngine->UnlockParallelAccess();
1959 	} // else the background was cleared already
1960 
1961 	fRegionPool.Recycle(dirty);
1962 }
1963 
1964 
1965 void
1966 Window::EndUpdate()
1967 {
1968 	// NOTE: see comment in _BeginUpdate()
1969 
1970 	if (fInUpdate) {
1971 		// reenable copy to front
1972 		fDrawingEngine->SetCopyToFrontEnabled(true);
1973 
1974 		BRegion* dirty = fRegionPool.GetRegion(
1975 			fCurrentUpdateSession->DirtyRegion());
1976 
1977 		if (dirty) {
1978 			dirty->IntersectWith(&VisibleContentRegion());
1979 
1980 			fDrawingEngine->CopyToFront(*dirty);
1981 			fRegionPool.Recycle(dirty);
1982 		}
1983 
1984 		fCurrentUpdateSession->SetUsed(false);
1985 
1986 		fInUpdate = false;
1987 		fEffectiveDrawingRegionValid = false;
1988 	}
1989 	if (fPendingUpdateSession->IsUsed()) {
1990 		// send this to client
1991 		_SendUpdateMessage();
1992 	} else {
1993 		fUpdateRequested = false;
1994 	}
1995 }
1996 
1997 
1998 void
1999 Window::_UpdateContentRegion()
2000 {
2001 	fContentRegion.Set(fFrame);
2002 
2003 	// resize handle
2004 	::Decorator* decorator = Decorator();
2005 	if (decorator)
2006 		fContentRegion.Exclude(&decorator->GetFootprint());
2007 
2008 	fContentRegionValid = true;
2009 }
2010 
2011 
2012 void
2013 Window::_ObeySizeLimits()
2014 {
2015 	// make sure we even have valid size limits
2016 	if (fMaxWidth < fMinWidth)
2017 		fMaxWidth = fMinWidth;
2018 
2019 	if (fMaxHeight < fMinHeight)
2020 		fMaxHeight = fMinHeight;
2021 
2022 	// Automatically resize the window to fit these new limits
2023 	// if it does not already.
2024 
2025 	// On R5, Windows don't automatically resize, but since
2026 	// BWindow::ResizeTo() even honors the limits, I would guess
2027 	// this is a bug that we don't have to adopt.
2028 	// Note that most current apps will do unnecessary resizing
2029 	// after having set the limits, but the overhead is neglible.
2030 
2031 	float minWidthDiff = fMinWidth - fFrame.Width();
2032 	float minHeightDiff = fMinHeight - fFrame.Height();
2033 	float maxWidthDiff = fMaxWidth - fFrame.Width();
2034 	float maxHeightDiff = fMaxHeight - fFrame.Height();
2035 
2036 	float xDiff = 0.0;
2037 	if (minWidthDiff > 0.0)	// we're currently smaller than minWidth
2038 		xDiff = minWidthDiff;
2039 	else if (maxWidthDiff < 0.0) // we're currently larger than maxWidth
2040 		xDiff = maxWidthDiff;
2041 
2042 	float yDiff = 0.0;
2043 	if (minHeightDiff > 0.0) // we're currently smaller than minHeight
2044 		yDiff = minHeightDiff;
2045 	else if (maxHeightDiff < 0.0) // we're currently larger than maxHeight
2046 		yDiff = maxHeightDiff;
2047 
2048 	if (fDesktop)
2049 		fDesktop->ResizeWindowBy(this, xDiff, yDiff);
2050 	else
2051 		ResizeBy((int32)xDiff, (int32)yDiff, NULL);
2052 }
2053 
2054 
2055 // #pragma mark - UpdateSession
2056 
2057 
2058 Window::UpdateSession::UpdateSession()
2059 	:
2060 	fDirtyRegion(),
2061 	fInUse(false),
2062 	fCause(0)
2063 {
2064 }
2065 
2066 
2067 Window::UpdateSession::~UpdateSession()
2068 {
2069 }
2070 
2071 
2072 void
2073 Window::UpdateSession::Include(BRegion* additionalDirty)
2074 {
2075 	fDirtyRegion.Include(additionalDirty);
2076 }
2077 
2078 
2079 void
2080 Window::UpdateSession::Exclude(BRegion* dirtyInNextSession)
2081 {
2082 	fDirtyRegion.Exclude(dirtyInNextSession);
2083 }
2084 
2085 
2086 void
2087 Window::UpdateSession::MoveBy(int32 x, int32 y)
2088 {
2089 	fDirtyRegion.OffsetBy(x, y);
2090 }
2091 
2092 
2093 void
2094 Window::UpdateSession::SetUsed(bool used)
2095 {
2096 	fInUse = used;
2097 	if (!fInUse) {
2098 		fDirtyRegion.MakeEmpty();
2099 		fCause = 0;
2100 	}
2101 }
2102 
2103 
2104 void
2105 Window::UpdateSession::AddCause(uint8 cause)
2106 {
2107 	fCause |= cause;
2108 }
2109 
2110 
2111 int32
2112 Window::PositionInStack() const
2113 {
2114 	if (!fCurrentStack.IsSet())
2115 		return -1;
2116 	return fCurrentStack->WindowList().IndexOf(this);
2117 }
2118 
2119 
2120 bool
2121 Window::DetachFromWindowStack(bool ownStackNeeded)
2122 {
2123 	// The lock must normally be held but is not held when closing the window.
2124 	//ASSERT_MULTI_WRITE_LOCKED(fDesktop->WindowLocker());
2125 
2126 	if (!fCurrentStack.IsSet())
2127 		return false;
2128 	if (fCurrentStack->CountWindows() == 1)
2129 		return true;
2130 
2131 	int32 index = PositionInStack();
2132 
2133 	if (fCurrentStack->RemoveWindow(this) == false)
2134 		return false;
2135 
2136 	BRegion invalidatedRegion;
2137 	::Decorator* decorator = fCurrentStack->Decorator();
2138 	if (decorator != NULL) {
2139 		decorator->RemoveTab(index, &invalidatedRegion);
2140 		decorator->SetTopTab(fCurrentStack->LayerOrder().CountItems() - 1);
2141 	}
2142 
2143 	Window* remainingTop = fCurrentStack->TopLayerWindow();
2144 	if (remainingTop != NULL) {
2145 		if (decorator != NULL)
2146 			decorator->SetDrawingEngine(remainingTop->GetDrawingEngine());
2147 		// propagate focus to the decorator
2148 		remainingTop->SetFocus(remainingTop->IsFocus());
2149 		remainingTop->SetLook(remainingTop->Look(), NULL);
2150 	}
2151 
2152 	fCurrentStack = NULL;
2153 	if (ownStackNeeded == true)
2154 		_InitWindowStack();
2155 	// propagate focus to the new decorator
2156 	SetFocus(IsFocus());
2157 
2158 	if (remainingTop != NULL) {
2159 		invalidatedRegion.Include(&remainingTop->VisibleRegion());
2160 		fDesktop->RebuildAndRedrawAfterWindowChange(remainingTop,
2161 			invalidatedRegion);
2162 	}
2163 	return true;
2164 }
2165 
2166 
2167 bool
2168 Window::AddWindowToStack(Window* window)
2169 {
2170 	ASSERT_MULTI_WRITE_LOCKED(fDesktop->WindowLocker());
2171 
2172 	WindowStack* stack = GetWindowStack();
2173 	if (stack == NULL)
2174 		return false;
2175 
2176 	BRegion dirty;
2177 	// move window to the own position
2178 	BRect ownFrame = Frame();
2179 	BRect frame = window->Frame();
2180 	float deltaToX = round(ownFrame.left - frame.left);
2181 	float deltaToY = round(ownFrame.top - frame.top);
2182 	frame.OffsetBy(deltaToX, deltaToY);
2183 	float deltaByX = round(ownFrame.right - frame.right);
2184 	float deltaByY = round(ownFrame.bottom - frame.bottom);
2185 	dirty.Include(&window->VisibleRegion());
2186 	window->MoveBy(deltaToX, deltaToY, false);
2187 	window->ResizeBy(deltaByX, deltaByY, &dirty, false);
2188 
2189 	// first collect dirt from the window to add
2190 	::Decorator* otherDecorator = window->Decorator();
2191 	if (otherDecorator != NULL)
2192 		dirty.Include(otherDecorator->TitleBarRect());
2193 	::Decorator* decorator = stack->Decorator();
2194 	if (decorator != NULL)
2195 		dirty.Include(decorator->TitleBarRect());
2196 
2197 	int32 position = PositionInStack() + 1;
2198 	if (position >= stack->CountWindows())
2199 		position = -1;
2200 	if (stack->AddWindow(window, position) == false)
2201 		return false;
2202 	window->DetachFromWindowStack(false);
2203 	window->fCurrentStack.SetTo(stack);
2204 
2205 	if (decorator != NULL) {
2206 		DesktopSettings settings(fDesktop);
2207 		decorator->AddTab(settings, window->Title(), window->Look(),
2208 			window->Flags(), position, &dirty);
2209 	}
2210 
2211 	window->SetLook(window->Look(), &dirty);
2212 	fDesktop->RebuildAndRedrawAfterWindowChange(TopLayerStackWindow(), dirty);
2213 	window->SetFocus(window->IsFocus());
2214 	return true;
2215 }
2216 
2217 
2218 Window*
2219 Window::StackedWindowAt(const BPoint& where)
2220 {
2221 	::Decorator* decorator = Decorator();
2222 	if (decorator == NULL)
2223 		return this;
2224 
2225 	int tab = decorator->TabAt(where);
2226 	// if we have a decorator we also have a stack
2227 	Window* window = fCurrentStack->WindowAt(tab);
2228 	if (window != NULL)
2229 		return window;
2230 	return this;
2231 }
2232 
2233 
2234 Window*
2235 Window::TopLayerStackWindow()
2236 {
2237 	if (!fCurrentStack.IsSet())
2238 		return this;
2239 	return fCurrentStack->TopLayerWindow();
2240 }
2241 
2242 
2243 WindowStack*
2244 Window::GetWindowStack()
2245 {
2246 	if (!fCurrentStack.IsSet())
2247 		return _InitWindowStack();
2248 	return fCurrentStack;
2249 }
2250 
2251 
2252 bool
2253 Window::MoveToTopStackLayer()
2254 {
2255 	::Decorator* decorator = Decorator();
2256 	if (decorator == NULL)
2257 		return false;
2258 	decorator->SetDrawingEngine(GetDrawingEngine());
2259 	SetLook(Look(), NULL);
2260 	decorator->SetTopTab(PositionInStack());
2261 	return fCurrentStack->MoveToTopLayer(this);
2262 }
2263 
2264 
2265 bool
2266 Window::MoveToStackPosition(int32 to, bool isMoving)
2267 {
2268 	if (!fCurrentStack.IsSet())
2269 		return false;
2270 	int32 index = PositionInStack();
2271 	if (fCurrentStack->Move(index, to) == false)
2272 		return false;
2273 
2274 	BRegion dirty;
2275 	::Decorator* decorator = Decorator();
2276 	if (decorator && decorator->MoveTab(index, to, isMoving, &dirty) == false)
2277 		return false;
2278 
2279 	fDesktop->RebuildAndRedrawAfterWindowChange(this, dirty);
2280 	return true;
2281 }
2282 
2283 
2284 WindowStack*
2285 Window::_InitWindowStack()
2286 {
2287 	fCurrentStack = NULL;
2288 	::Decorator* decorator = NULL;
2289 	if (fLook != B_NO_BORDER_WINDOW_LOOK)
2290 		decorator = gDecorManager.AllocateDecorator(this);
2291 
2292 	WindowStack* stack = new(std::nothrow) WindowStack(decorator);
2293 	if (stack == NULL)
2294 		return NULL;
2295 
2296 	if (stack->AddWindow(this) != true) {
2297 		delete stack;
2298 		return NULL;
2299 	}
2300 	fCurrentStack.SetTo(stack, true);
2301 	return stack;
2302 }
2303 
2304 
2305 WindowStack::WindowStack(::Decorator* decorator)
2306 	:
2307 	fDecorator(decorator)
2308 {
2309 
2310 }
2311 
2312 
2313 WindowStack::~WindowStack()
2314 {
2315 }
2316 
2317 
2318 void
2319 WindowStack::SetDecorator(::Decorator* decorator)
2320 {
2321 	fDecorator.SetTo(decorator);
2322 }
2323 
2324 
2325 ::Decorator*
2326 WindowStack::Decorator()
2327 {
2328 	return fDecorator.Get();
2329 }
2330 
2331 
2332 Window*
2333 WindowStack::TopLayerWindow() const
2334 {
2335 	return fWindowLayerOrder.ItemAt(fWindowLayerOrder.CountItems() - 1);
2336 }
2337 
2338 
2339 int32
2340 WindowStack::CountWindows()
2341 {
2342 	return fWindowList.CountItems();
2343 }
2344 
2345 
2346 Window*
2347 WindowStack::WindowAt(int32 index)
2348 {
2349 	return fWindowList.ItemAt(index);
2350 }
2351 
2352 
2353 bool
2354 WindowStack::AddWindow(Window* window, int32 position)
2355 {
2356 	if (position >= 0) {
2357 		if (fWindowList.AddItem(window, position) == false)
2358 			return false;
2359 	} else if (fWindowList.AddItem(window) == false)
2360 		return false;
2361 
2362 	if (fWindowLayerOrder.AddItem(window) == false) {
2363 		fWindowList.RemoveItem(window);
2364 		return false;
2365 	}
2366 	return true;
2367 }
2368 
2369 
2370 bool
2371 WindowStack::RemoveWindow(Window* window)
2372 {
2373 	if (fWindowList.RemoveItem(window) == false)
2374 		return false;
2375 
2376 	fWindowLayerOrder.RemoveItem(window);
2377 	return true;
2378 }
2379 
2380 
2381 bool
2382 WindowStack::MoveToTopLayer(Window* window)
2383 {
2384 	int32 index = fWindowLayerOrder.IndexOf(window);
2385 	return fWindowLayerOrder.MoveItem(index,
2386 		fWindowLayerOrder.CountItems() - 1);
2387 }
2388 
2389 
2390 bool
2391 WindowStack::Move(int32 from, int32 to)
2392 {
2393 	return fWindowList.MoveItem(from, to);
2394 }
2395