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