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