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