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