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