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	fDirtyCause(0),
85
86	fContentRegion(),
87	fEffectiveDrawingRegion(),
88
89	fVisibleContentRegionValid(false),
90	fContentRegionValid(false),
91	fEffectiveDrawingRegionValid(false),
92
93	fRegionPool(),
94
95	fWindow(window),
96	fDrawingEngine(drawingEngine),
97	fDesktop(window->Desktop()),
98
99	fCurrentUpdateSession(&fUpdateSessions[0]),
100	fPendingUpdateSession(&fUpdateSessions[1]),
101	fUpdateRequested(false),
102	fInUpdate(false),
103	fUpdatesEnabled(false),
104
105	// Windows start hidden
106	fHidden(true),
107	// Hidden is 1 or more
108	fShowLevel(1),
109	fMinimized(false),
110	fIsFocus(false),
111
112	fLook(look),
113	fFeel(feel),
114	fWorkspaces(workspaces),
115	fCurrentWorkspace(-1),
116
117	fMinWidth(1),
118	fMaxWidth(32768),
119	fMinHeight(1),
120	fMaxHeight(32768),
121
122	fWorkspacesViewCount(0)
123{
124	_InitWindowStack();
125
126	// make sure our arguments are valid
127	if (!IsValidLook(fLook))
128		fLook = B_TITLED_WINDOW_LOOK;
129	if (!IsValidFeel(fFeel))
130		fFeel = B_NORMAL_WINDOW_FEEL;
131
132	SetFlags(flags, NULL);
133
134	if (fLook != B_NO_BORDER_WINDOW_LOOK && fCurrentStack.IsSet()) {
135		// allocates a decorator
136		::Decorator* decorator = Decorator();
137		if (decorator != NULL) {
138			decorator->GetSizeLimits(&fMinWidth, &fMinHeight, &fMaxWidth,
139				&fMaxHeight);
140		}
141	}
142	if (fFeel != kOffscreenWindowFeel)
143		fWindowBehaviour.SetTo(gDecorManager.AllocateWindowBehaviour(this));
144
145	// do we need to change our size to let the decorator fit?
146	// _ResizeBy() will adapt the frame for validity before resizing
147	if (feel == kDesktopWindowFeel) {
148		// the desktop window spans over the whole screen
149		// TODO: this functionality should be moved somewhere else
150		//  (so that it is always used when the workspace is changed)
151		uint16 width, height;
152		uint32 colorSpace;
153		float frequency;
154		if (Screen() != NULL) {
155			Screen()->GetMode(width, height, colorSpace, frequency);
156// TODO: MOVE THIS AWAY!!! ResizeBy contains calls to virtual methods!
157// Also, there is no TopView()!
158			fFrame.OffsetTo(B_ORIGIN);
159//			ResizeBy(width - frame.Width(), height - frame.Height(), NULL);
160		}
161	}
162
163	STRACE(("Window %p, %s:\n", this, Name()));
164	STRACE(("\tFrame: (%.1f, %.1f, %.1f, %.1f)\n", fFrame.left, fFrame.top,
165		fFrame.right, fFrame.bottom));
166	STRACE(("\tWindow %s\n", window ? window->Title() : "NULL"));
167}
168
169
170Window::~Window()
171{
172	if (fTopView.IsSet()) {
173		fTopView->DetachedFromWindow();
174	}
175
176	DetachFromWindowStack(false);
177
178	gDecorManager.CleanupForWindow(this);
179}
180
181
182status_t
183Window::InitCheck() const
184{
185	if (GetDrawingEngine() == NULL
186		|| (fFeel != kOffscreenWindowFeel && !fWindowBehaviour.IsSet()))
187		return B_NO_MEMORY;
188	// TODO: anything else?
189	return B_OK;
190}
191
192
193void
194Window::SetClipping(BRegion* stillAvailableOnScreen)
195{
196	// this function is only called from the Desktop thread
197
198	// start from full region (as if the window was fully visible)
199	GetFullRegion(&fVisibleRegion);
200	// clip to region still available on screen
201	fVisibleRegion.IntersectWith(stillAvailableOnScreen);
202
203	fVisibleContentRegionValid = false;
204	fEffectiveDrawingRegionValid = false;
205}
206
207
208void
209Window::GetFullRegion(BRegion* region)
210{
211	// TODO: if someone needs to call this from
212	// the outside, the clipping needs to be readlocked!
213
214	// start from the decorator border, extend to use the frame
215	GetBorderRegion(region);
216	region->Include(fFrame);
217}
218
219
220void
221Window::GetBorderRegion(BRegion* region)
222{
223	// TODO: if someone needs to call this from
224	// the outside, the clipping needs to be readlocked!
225
226	::Decorator* decorator = Decorator();
227	if (decorator)
228		*region = decorator->GetFootprint();
229	else
230		region->MakeEmpty();
231}
232
233
234void
235Window::GetContentRegion(BRegion* region)
236{
237	// TODO: if someone needs to call this from
238	// the outside, the clipping needs to be readlocked!
239
240	if (!fContentRegionValid) {
241		_UpdateContentRegion();
242	}
243
244	*region = fContentRegion;
245}
246
247
248BRegion&
249Window::VisibleContentRegion()
250{
251	// TODO: if someone needs to call this from
252	// the outside, the clipping needs to be readlocked!
253
254	// regions expected to be locked
255	if (!fVisibleContentRegionValid) {
256		GetContentRegion(&fVisibleContentRegion);
257		fVisibleContentRegion.IntersectWith(&fVisibleRegion);
258	}
259	return fVisibleContentRegion;
260}
261
262
263// #pragma mark -
264
265
266void
267Window::_PropagatePosition()
268{
269	if ((fFlags & B_SAME_POSITION_IN_ALL_WORKSPACES) == 0)
270		return;
271
272	for (int32 i = 0; i < kListCount; i++) {
273		Anchor(i).position = fFrame.LeftTop();
274	}
275}
276
277
278void
279Window::MoveBy(int32 x, int32 y, bool moveStack)
280{
281	// this function is only called from the desktop thread
282
283	if (x == 0 && y == 0)
284		return;
285
286	fFrame.OffsetBy(x, y);
287	_PropagatePosition();
288
289	// take along the dirty region which is not
290	// processed yet
291	fDirtyRegion.OffsetBy(x, y);
292
293	if (fContentRegionValid)
294		fContentRegion.OffsetBy(x, y);
295
296	if (fCurrentUpdateSession->IsUsed())
297		fCurrentUpdateSession->MoveBy(x, y);
298	if (fPendingUpdateSession->IsUsed())
299		fPendingUpdateSession->MoveBy(x, y);
300
301	fEffectiveDrawingRegionValid = false;
302
303	if (fTopView.IsSet()) {
304		fTopView->MoveBy(x, y, NULL);
305		fTopView->UpdateOverlay();
306	}
307
308	::Decorator* decorator = Decorator();
309	if (moveStack && decorator)
310		decorator->MoveBy(x, y);
311
312	WindowStack* stack = GetWindowStack();
313	if (moveStack && stack) {
314		for (int32 i = 0; i < stack->CountWindows(); i++) {
315			Window* window = stack->WindowList().ItemAt(i);
316			if (window == this)
317				continue;
318			window->MoveBy(x, y, false);
319		}
320	}
321
322	// the desktop will take care of dirty regions
323
324	// dispatch a message to the client informing about the changed size
325	BMessage msg(B_WINDOW_MOVED);
326	msg.AddInt64("when", system_time());
327	msg.AddPoint("where", fFrame.LeftTop());
328	fWindow->SendMessageToClient(&msg);
329}
330
331
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(BRegion& region)
741{
742	// if this is executed in the desktop thread,
743	// it means that the window thread currently
744	// blocks to get the read lock, if it is
745	// executed from the window thread, it should
746	// have the read lock and the desktop thread
747	// is blocking to get the write lock. IAW, this
748	// is only executed in one thread.
749	if (fDirtyRegion.CountRects() == 0) {
750		// the window needs to be informed
751		// when the dirty region was empty.
752		// NOTE: when the window thread has processed
753		// the dirty region in MessageReceived(),
754		// it will make the region empty again,
755		// when it is empty here, we need to send
756		// the message to initiate the next update round.
757		// Until the message is processed in the window
758		// thread, the desktop thread can add parts to
759		// the region as it likes.
760		ServerWindow()->RequestRedraw();
761	}
762
763	fDirtyRegion.Include(&region);
764	fDirtyCause |= UPDATE_EXPOSE;
765}
766
767
768void
769Window::RedrawDirtyRegion()
770{
771	if (TopLayerStackWindow() != this) {
772		fDirtyRegion.MakeEmpty();
773		fDirtyCause = 0;
774		return;
775	}
776
777	// executed from ServerWindow with the read lock held
778	if (IsVisible()) {
779		_DrawBorder();
780
781		BRegion* dirtyContentRegion =
782			fRegionPool.GetRegion(VisibleContentRegion());
783		dirtyContentRegion->IntersectWith(&fDirtyRegion);
784
785		_TriggerContentRedraw(*dirtyContentRegion);
786
787		fRegionPool.Recycle(dirtyContentRegion);
788	}
789
790	// reset the dirty region, since
791	// we're fully clean. If the desktop
792	// thread wanted to mark something
793	// dirty in the mean time, it was
794	// blocking on the global region lock to
795	// get write access, since we're holding
796	// the read lock for the whole time.
797	fDirtyRegion.MakeEmpty();
798	fDirtyCause = 0;
799}
800
801
802void
803Window::MarkDirty(BRegion& regionOnScreen)
804{
805	// for marking any part of the desktop dirty
806	// this will get write access to the global
807	// region lock, and result in ProcessDirtyRegion()
808	// to be called for any windows affected
809	if (fDesktop)
810		fDesktop->MarkDirty(regionOnScreen);
811}
812
813
814void
815Window::MarkContentDirty(BRegion& regionOnScreen)
816{
817	// for triggering AS_REDRAW
818	// since this won't affect other windows, read locking
819	// is sufficient. If there was no dirty region before,
820	// an update message is triggered
821	if (fHidden || IsOffscreenWindow())
822		return;
823
824	regionOnScreen.IntersectWith(&VisibleContentRegion());
825	fDirtyCause |= UPDATE_REQUEST;
826	_TriggerContentRedraw(regionOnScreen);
827}
828
829
830void
831Window::MarkContentDirtyAsync(BRegion& regionOnScreen)
832{
833	// NOTE: see comments in ProcessDirtyRegion()
834	if (fHidden || IsOffscreenWindow())
835		return;
836
837	regionOnScreen.IntersectWith(&VisibleContentRegion());
838
839	if (fDirtyRegion.CountRects() == 0) {
840		ServerWindow()->RequestRedraw();
841	}
842
843	fDirtyRegion.Include(&regionOnScreen);
844	fDirtyCause |= UPDATE_REQUEST;
845}
846
847
848void
849Window::InvalidateView(View* view, BRegion& viewRegion)
850{
851	if (view && IsVisible() && view->IsVisible()) {
852		if (!fContentRegionValid)
853			_UpdateContentRegion();
854
855		view->LocalToScreenTransform().Apply(&viewRegion);
856		viewRegion.IntersectWith(&VisibleContentRegion());
857		if (viewRegion.CountRects() > 0) {
858			viewRegion.IntersectWith(
859				&view->ScreenAndUserClipping(&fContentRegion));
860
861//fDrawingEngine->FillRegion(viewRegion, rgb_color{ 0, 255, 0, 255 });
862//snooze(10000);
863			fDirtyCause |= UPDATE_REQUEST;
864			_TriggerContentRedraw(viewRegion);
865		}
866	}
867}
868
869// DisableUpdateRequests
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& dirtyContentRegion)
1771{
1772	if (!IsVisible() || dirtyContentRegion.CountRects() == 0
1773		|| (fFlags & kWindowScreenFlag) != 0)
1774		return;
1775
1776	// put this into the pending dirty region
1777	// to eventually trigger a client redraw
1778
1779	_TransferToUpdateSession(&dirtyContentRegion);
1780}
1781
1782
1783void
1784Window::_DrawBorder()
1785{
1786	// this is executed in the window thread, but only
1787	// in respond to a REDRAW message having been received, the
1788	// clipping lock is held for reading
1789	::Decorator* decorator = Decorator();
1790	if (!decorator)
1791		return;
1792
1793	// construct the region of the border that needs redrawing
1794	BRegion* dirtyBorderRegion = fRegionPool.GetRegion();
1795	if (!dirtyBorderRegion)
1796		return;
1797	GetBorderRegion(dirtyBorderRegion);
1798	// intersect with our visible region
1799	dirtyBorderRegion->IntersectWith(&fVisibleRegion);
1800	// intersect with the dirty region
1801	dirtyBorderRegion->IntersectWith(&fDirtyRegion);
1802
1803	DrawingEngine* engine = decorator->GetDrawingEngine();
1804	if (dirtyBorderRegion->CountRects() > 0 && engine->LockParallelAccess()) {
1805		engine->ConstrainClippingRegion(dirtyBorderRegion);
1806		bool copyToFrontEnabled = engine->CopyToFrontEnabled();
1807		engine->SetCopyToFrontEnabled(false);
1808
1809		decorator->Draw(dirtyBorderRegion->Frame());
1810
1811		engine->SetCopyToFrontEnabled(copyToFrontEnabled);
1812		engine->CopyToFront(*dirtyBorderRegion);
1813
1814// TODO: remove this once the DrawState stuff is handled
1815// more cleanly. The reason why this is needed is that
1816// when the decorator draws strings, a draw state is set
1817// on the Painter object, and this is were it might get
1818// out of sync with what the ServerWindow things is the
1819// current DrawState set on the Painter
1820fWindow->ResyncDrawState();
1821
1822		engine->UnlockParallelAccess();
1823	}
1824	fRegionPool.Recycle(dirtyBorderRegion);
1825}
1826
1827
1828/*!	pre: the clipping is readlocked (this function is
1829	only called from _TriggerContentRedraw()), which
1830	in turn is only called from MessageReceived() with
1831	the clipping lock held
1832*/
1833void
1834Window::_TransferToUpdateSession(BRegion* contentDirtyRegion)
1835{
1836	if (contentDirtyRegion->CountRects() <= 0)
1837		return;
1838
1839//fDrawingEngine->FillRegion(*contentDirtyRegion, sPendingColor);
1840//snooze(20000);
1841
1842	// add to pending
1843	fPendingUpdateSession->SetUsed(true);
1844//	if (!fPendingUpdateSession->IsExpose())
1845	fPendingUpdateSession->AddCause(fDirtyCause);
1846	fPendingUpdateSession->Include(contentDirtyRegion);
1847
1848	if (!fUpdateRequested) {
1849		// send this to client
1850		_SendUpdateMessage();
1851		// the pending region is now the current,
1852		// though the update does not start until
1853		// we received BEGIN_UPDATE from the client
1854	}
1855}
1856
1857
1858void
1859Window::_SendUpdateMessage()
1860{
1861	if (!fUpdatesEnabled)
1862		return;
1863
1864	BMessage message(_UPDATE_);
1865	if (ServerWindow()->SendMessageToClient(&message) != B_OK) {
1866		// If sending the message failed, we'll just keep adding to the dirty
1867		// region until sending was successful.
1868		// TODO: we might want to automatically resend this message in this case
1869		return;
1870	}
1871
1872	fUpdateRequested = true;
1873	fEffectiveDrawingRegionValid = false;
1874}
1875
1876
1877void
1878Window::BeginUpdate(BPrivate::PortLink& link)
1879{
1880	// NOTE: since we might "shift" parts of the
1881	// internal dirty regions from the desktop thread
1882	// in response to Window::ResizeBy(), which
1883	// might move arround views, the user of this function
1884	// needs to hold the global clipping lock so that the internal
1885	// dirty regions are not messed with from the Desktop thread
1886	// and ServerWindow thread at the same time.
1887
1888	if (!fUpdateRequested) {
1889		link.StartMessage(B_ERROR);
1890		link.Flush();
1891		fprintf(stderr, "Window::BeginUpdate() - no update requested!\n");
1892		return;
1893	}
1894
1895	// make the pending update session the current update session
1896	// (toggle the pointers)
1897	UpdateSession* temp = fCurrentUpdateSession;
1898	fCurrentUpdateSession = fPendingUpdateSession;
1899	fPendingUpdateSession = temp;
1900	fPendingUpdateSession->SetUsed(false);
1901	// all drawing command from the client
1902	// will have the dirty region from the update
1903	// session enforced
1904	fInUpdate = true;
1905	fEffectiveDrawingRegionValid = false;
1906
1907	// TODO: each view could be drawn individually
1908	// right before carrying out the first drawing
1909	// command from the client during an update
1910	// (View::IsBackgroundDirty() can be used
1911	// for this)
1912	if (!fContentRegionValid)
1913		_UpdateContentRegion();
1914
1915	BRegion* dirty = fRegionPool.GetRegion(
1916		fCurrentUpdateSession->DirtyRegion());
1917	if (!dirty) {
1918		link.StartMessage(B_ERROR);
1919		link.Flush();
1920		return;
1921	}
1922
1923	dirty->IntersectWith(&VisibleContentRegion());
1924
1925//if (!fCurrentUpdateSession->IsExpose()) {
1926////sCurrentColor.red = rand() % 255;
1927////sCurrentColor.green = rand() % 255;
1928////sCurrentColor.blue = rand() % 255;
1929////sPendingColor.red = rand() % 255;
1930////sPendingColor.green = rand() % 255;
1931////sPendingColor.blue = rand() % 255;
1932//fDrawingEngine->FillRegion(*dirty, sCurrentColor);
1933//snooze(10000);
1934//}
1935
1936	link.StartMessage(B_OK);
1937	// append the current window geometry to the
1938	// message, the client will need it
1939	link.Attach<BPoint>(fFrame.LeftTop());
1940	link.Attach<float>(fFrame.Width());
1941	link.Attach<float>(fFrame.Height());
1942	// find and attach all views that intersect with
1943	// the dirty region
1944	fTopView->AddTokensForViewsInRegion(link, *dirty, &fContentRegion);
1945	// mark the end of the token "list"
1946	link.Attach<int32>(B_NULL_TOKEN);
1947	link.Flush();
1948
1949	// supress back to front buffer copies in the drawing engine
1950	fDrawingEngine->SetCopyToFrontEnabled(false);
1951
1952	if (fDrawingEngine->LockParallelAccess()) {
1953		fDrawingEngine->SuspendAutoSync();
1954
1955		fTopView->Draw(GetDrawingEngine(), dirty, &fContentRegion, true);
1956
1957		fDrawingEngine->Sync();
1958		fDrawingEngine->UnlockParallelAccess();
1959	} // else the background was cleared already
1960
1961	fRegionPool.Recycle(dirty);
1962}
1963
1964
1965void
1966Window::EndUpdate()
1967{
1968	// NOTE: see comment in _BeginUpdate()
1969
1970	if (fInUpdate) {
1971		// reenable copy to front
1972		fDrawingEngine->SetCopyToFrontEnabled(true);
1973
1974		BRegion* dirty = fRegionPool.GetRegion(
1975			fCurrentUpdateSession->DirtyRegion());
1976
1977		if (dirty) {
1978			dirty->IntersectWith(&VisibleContentRegion());
1979
1980			fDrawingEngine->CopyToFront(*dirty);
1981			fRegionPool.Recycle(dirty);
1982		}
1983
1984		fCurrentUpdateSession->SetUsed(false);
1985
1986		fInUpdate = false;
1987		fEffectiveDrawingRegionValid = false;
1988	}
1989	if (fPendingUpdateSession->IsUsed()) {
1990		// send this to client
1991		_SendUpdateMessage();
1992	} else {
1993		fUpdateRequested = false;
1994	}
1995}
1996
1997
1998void
1999Window::_UpdateContentRegion()
2000{
2001	fContentRegion.Set(fFrame);
2002
2003	// resize handle
2004	::Decorator* decorator = Decorator();
2005	if (decorator)
2006		fContentRegion.Exclude(&decorator->GetFootprint());
2007
2008	fContentRegionValid = true;
2009}
2010
2011
2012void
2013Window::_ObeySizeLimits()
2014{
2015	// make sure we even have valid size limits
2016	if (fMaxWidth < fMinWidth)
2017		fMaxWidth = fMinWidth;
2018
2019	if (fMaxHeight < fMinHeight)
2020		fMaxHeight = fMinHeight;
2021
2022	// Automatically resize the window to fit these new limits
2023	// if it does not already.
2024
2025	// On R5, Windows don't automatically resize, but since
2026	// BWindow::ResizeTo() even honors the limits, I would guess
2027	// this is a bug that we don't have to adopt.
2028	// Note that most current apps will do unnecessary resizing
2029	// after having set the limits, but the overhead is neglible.
2030
2031	float minWidthDiff = fMinWidth - fFrame.Width();
2032	float minHeightDiff = fMinHeight - fFrame.Height();
2033	float maxWidthDiff = fMaxWidth - fFrame.Width();
2034	float maxHeightDiff = fMaxHeight - fFrame.Height();
2035
2036	float xDiff = 0.0;
2037	if (minWidthDiff > 0.0)	// we're currently smaller than minWidth
2038		xDiff = minWidthDiff;
2039	else if (maxWidthDiff < 0.0) // we're currently larger than maxWidth
2040		xDiff = maxWidthDiff;
2041
2042	float yDiff = 0.0;
2043	if (minHeightDiff > 0.0) // we're currently smaller than minHeight
2044		yDiff = minHeightDiff;
2045	else if (maxHeightDiff < 0.0) // we're currently larger than maxHeight
2046		yDiff = maxHeightDiff;
2047
2048	if (fDesktop)
2049		fDesktop->ResizeWindowBy(this, xDiff, yDiff);
2050	else
2051		ResizeBy((int32)xDiff, (int32)yDiff, NULL);
2052}
2053
2054
2055// #pragma mark - UpdateSession
2056
2057
2058Window::UpdateSession::UpdateSession()
2059	:
2060	fDirtyRegion(),
2061	fInUse(false),
2062	fCause(0)
2063{
2064}
2065
2066
2067Window::UpdateSession::~UpdateSession()
2068{
2069}
2070
2071
2072void
2073Window::UpdateSession::Include(BRegion* additionalDirty)
2074{
2075	fDirtyRegion.Include(additionalDirty);
2076}
2077
2078
2079void
2080Window::UpdateSession::Exclude(BRegion* dirtyInNextSession)
2081{
2082	fDirtyRegion.Exclude(dirtyInNextSession);
2083}
2084
2085
2086void
2087Window::UpdateSession::MoveBy(int32 x, int32 y)
2088{
2089	fDirtyRegion.OffsetBy(x, y);
2090}
2091
2092
2093void
2094Window::UpdateSession::SetUsed(bool used)
2095{
2096	fInUse = used;
2097	if (!fInUse) {
2098		fDirtyRegion.MakeEmpty();
2099		fCause = 0;
2100	}
2101}
2102
2103
2104void
2105Window::UpdateSession::AddCause(uint8 cause)
2106{
2107	fCause |= cause;
2108}
2109
2110
2111int32
2112Window::PositionInStack() const
2113{
2114	if (!fCurrentStack.IsSet())
2115		return -1;
2116	return fCurrentStack->WindowList().IndexOf(this);
2117}
2118
2119
2120bool
2121Window::DetachFromWindowStack(bool ownStackNeeded)
2122{
2123	// The lock must normally be held but is not held when closing the window.
2124	//ASSERT_MULTI_WRITE_LOCKED(fDesktop->WindowLocker());
2125
2126	if (!fCurrentStack.IsSet())
2127		return false;
2128	if (fCurrentStack->CountWindows() == 1)
2129		return true;
2130
2131	int32 index = PositionInStack();
2132
2133	if (fCurrentStack->RemoveWindow(this) == false)
2134		return false;
2135
2136	BRegion invalidatedRegion;
2137	::Decorator* decorator = fCurrentStack->Decorator();
2138	if (decorator != NULL) {
2139		decorator->RemoveTab(index, &invalidatedRegion);
2140		decorator->SetTopTab(fCurrentStack->LayerOrder().CountItems() - 1);
2141	}
2142
2143	Window* remainingTop = fCurrentStack->TopLayerWindow();
2144	if (remainingTop != NULL) {
2145		if (decorator != NULL)
2146			decorator->SetDrawingEngine(remainingTop->GetDrawingEngine());
2147		// propagate focus to the decorator
2148		remainingTop->SetFocus(remainingTop->IsFocus());
2149		remainingTop->SetLook(remainingTop->Look(), NULL);
2150	}
2151
2152	fCurrentStack = NULL;
2153	if (ownStackNeeded == true)
2154		_InitWindowStack();
2155	// propagate focus to the new decorator
2156	SetFocus(IsFocus());
2157
2158	if (remainingTop != NULL) {
2159		invalidatedRegion.Include(&remainingTop->VisibleRegion());
2160		fDesktop->RebuildAndRedrawAfterWindowChange(remainingTop,
2161			invalidatedRegion);
2162	}
2163	return true;
2164}
2165
2166
2167bool
2168Window::AddWindowToStack(Window* window)
2169{
2170	ASSERT_MULTI_WRITE_LOCKED(fDesktop->WindowLocker());
2171
2172	WindowStack* stack = GetWindowStack();
2173	if (stack == NULL)
2174		return false;
2175
2176	BRegion dirty;
2177	// move window to the own position
2178	BRect ownFrame = Frame();
2179	BRect frame = window->Frame();
2180	float deltaToX = round(ownFrame.left - frame.left);
2181	float deltaToY = round(ownFrame.top - frame.top);
2182	frame.OffsetBy(deltaToX, deltaToY);
2183	float deltaByX = round(ownFrame.right - frame.right);
2184	float deltaByY = round(ownFrame.bottom - frame.bottom);
2185	dirty.Include(&window->VisibleRegion());
2186	window->MoveBy(deltaToX, deltaToY, false);
2187	window->ResizeBy(deltaByX, deltaByY, &dirty, false);
2188
2189	// first collect dirt from the window to add
2190	::Decorator* otherDecorator = window->Decorator();
2191	if (otherDecorator != NULL)
2192		dirty.Include(otherDecorator->TitleBarRect());
2193	::Decorator* decorator = stack->Decorator();
2194	if (decorator != NULL)
2195		dirty.Include(decorator->TitleBarRect());
2196
2197	int32 position = PositionInStack() + 1;
2198	if (position >= stack->CountWindows())
2199		position = -1;
2200	if (stack->AddWindow(window, position) == false)
2201		return false;
2202	window->DetachFromWindowStack(false);
2203	window->fCurrentStack.SetTo(stack);
2204
2205	if (decorator != NULL) {
2206		DesktopSettings settings(fDesktop);
2207		decorator->AddTab(settings, window->Title(), window->Look(),
2208			window->Flags(), position, &dirty);
2209	}
2210
2211	window->SetLook(window->Look(), &dirty);
2212	fDesktop->RebuildAndRedrawAfterWindowChange(TopLayerStackWindow(), dirty);
2213	window->SetFocus(window->IsFocus());
2214	return true;
2215}
2216
2217
2218Window*
2219Window::StackedWindowAt(const BPoint& where)
2220{
2221	::Decorator* decorator = Decorator();
2222	if (decorator == NULL)
2223		return this;
2224
2225	int tab = decorator->TabAt(where);
2226	// if we have a decorator we also have a stack
2227	Window* window = fCurrentStack->WindowAt(tab);
2228	if (window != NULL)
2229		return window;
2230	return this;
2231}
2232
2233
2234Window*
2235Window::TopLayerStackWindow()
2236{
2237	if (!fCurrentStack.IsSet())
2238		return this;
2239	return fCurrentStack->TopLayerWindow();
2240}
2241
2242
2243WindowStack*
2244Window::GetWindowStack()
2245{
2246	if (!fCurrentStack.IsSet())
2247		return _InitWindowStack();
2248	return fCurrentStack;
2249}
2250
2251
2252bool
2253Window::MoveToTopStackLayer()
2254{
2255	::Decorator* decorator = Decorator();
2256	if (decorator == NULL)
2257		return false;
2258	decorator->SetDrawingEngine(GetDrawingEngine());
2259	SetLook(Look(), NULL);
2260	decorator->SetTopTab(PositionInStack());
2261	return fCurrentStack->MoveToTopLayer(this);
2262}
2263
2264
2265bool
2266Window::MoveToStackPosition(int32 to, bool isMoving)
2267{
2268	if (!fCurrentStack.IsSet())
2269		return false;
2270	int32 index = PositionInStack();
2271	if (fCurrentStack->Move(index, to) == false)
2272		return false;
2273
2274	BRegion dirty;
2275	::Decorator* decorator = Decorator();
2276	if (decorator && decorator->MoveTab(index, to, isMoving, &dirty) == false)
2277		return false;
2278
2279	fDesktop->RebuildAndRedrawAfterWindowChange(this, dirty);
2280	return true;
2281}
2282
2283
2284WindowStack*
2285Window::_InitWindowStack()
2286{
2287	fCurrentStack = NULL;
2288	::Decorator* decorator = NULL;
2289	if (fLook != B_NO_BORDER_WINDOW_LOOK)
2290		decorator = gDecorManager.AllocateDecorator(this);
2291
2292	WindowStack* stack = new(std::nothrow) WindowStack(decorator);
2293	if (stack == NULL)
2294		return NULL;
2295
2296	if (stack->AddWindow(this) != true) {
2297		delete stack;
2298		return NULL;
2299	}
2300	fCurrentStack.SetTo(stack, true);
2301	return stack;
2302}
2303
2304
2305WindowStack::WindowStack(::Decorator* decorator)
2306	:
2307	fDecorator(decorator)
2308{
2309
2310}
2311
2312
2313WindowStack::~WindowStack()
2314{
2315}
2316
2317
2318void
2319WindowStack::SetDecorator(::Decorator* decorator)
2320{
2321	fDecorator.SetTo(decorator);
2322}
2323
2324
2325::Decorator*
2326WindowStack::Decorator()
2327{
2328	return fDecorator.Get();
2329}
2330
2331
2332Window*
2333WindowStack::TopLayerWindow() const
2334{
2335	return fWindowLayerOrder.ItemAt(fWindowLayerOrder.CountItems() - 1);
2336}
2337
2338
2339int32
2340WindowStack::CountWindows()
2341{
2342	return fWindowList.CountItems();
2343}
2344
2345
2346Window*
2347WindowStack::WindowAt(int32 index)
2348{
2349	return fWindowList.ItemAt(index);
2350}
2351
2352
2353bool
2354WindowStack::AddWindow(Window* window, int32 position)
2355{
2356	if (position >= 0) {
2357		if (fWindowList.AddItem(window, position) == false)
2358			return false;
2359	} else if (fWindowList.AddItem(window) == false)
2360		return false;
2361
2362	if (fWindowLayerOrder.AddItem(window) == false) {
2363		fWindowList.RemoveItem(window);
2364		return false;
2365	}
2366	return true;
2367}
2368
2369
2370bool
2371WindowStack::RemoveWindow(Window* window)
2372{
2373	if (fWindowList.RemoveItem(window) == false)
2374		return false;
2375
2376	fWindowLayerOrder.RemoveItem(window);
2377	return true;
2378}
2379
2380
2381bool
2382WindowStack::MoveToTopLayer(Window* window)
2383{
2384	int32 index = fWindowLayerOrder.IndexOf(window);
2385	return fWindowLayerOrder.MoveItem(index,
2386		fWindowLayerOrder.CountItems() - 1);
2387}
2388
2389
2390bool
2391WindowStack::Move(int32 from, int32 to)
2392{
2393	return fWindowList.MoveItem(from, to);
2394}
2395