xref: /haiku/src/servers/app/Window.cpp (revision 7a74a5df454197933bc6e80a542102362ee98703)
1 /*
2  * Copyright 2001-2011, Haiku, Inc.
3  * Distributed under the terms of the MIT license.
4  *
5  * Authors:
6  *		DarkWyrm <bpmagic@columbus.rr.com>
7  *		Adi Oanca <adioanca@gmail.com>
8  *		Stephan Aßmus <superstippi@gmx.de>
9  *		Axel Dörfler <axeld@pinc-software.de>
10  *		Brecht Machiels <brecht@mos6581.org>
11  *		Clemens Zeidler <haiku@clemens-zeidler.de>
12  */
13 
14 
15 #include "Window.h"
16 
17 #include <new>
18 #include <stdio.h>
19 
20 #include <Debug.h>
21 
22 #include <DirectWindow.h>
23 #include <PortLink.h>
24 #include <View.h>
25 #include <ViewPrivate.h>
26 #include <WindowPrivate.h>
27 
28 #include "ClickTarget.h"
29 #include "Decorator.h"
30 #include "DecorManager.h"
31 #include "Desktop.h"
32 #include "DrawingEngine.h"
33 #include "HWInterface.h"
34 #include "MessagePrivate.h"
35 #include "PortLink.h"
36 #include "ServerApp.h"
37 #include "ServerWindow.h"
38 #include "WindowBehaviour.h"
39 #include "Workspace.h"
40 #include "WorkspacesView.h"
41 
42 
43 // Toggle debug output
44 //#define DEBUG_WINDOW
45 
46 #ifdef DEBUG_WINDOW
47 #	define STRACE(x) printf x
48 #else
49 #	define STRACE(x) ;
50 #endif
51 
52 // IMPORTANT: nested LockSingleWindow()s are not supported (by MultiLocker)
53 
54 using std::nothrow;
55 
56 // if the background clearing is delayed until
57 // the client draws the view, we have less flickering
58 // when contents have to be redrawn because of resizing
59 // a window or because the client invalidates parts.
60 // when redrawing something that has been exposed from underneath
61 // other windows, the other window will be seen longer at
62 // its previous position though if the exposed parts are not
63 // cleared right away. maybe there ought to be a flag in
64 // the update session, which tells us the cause of the update
65 
66 
67 //static rgb_color sPendingColor = (rgb_color){ 255, 255, 0, 255 };
68 //static rgb_color sCurrentColor = (rgb_color){ 255, 0, 255, 255 };
69 
70 
71 Window::Window(const BRect& frame, const char *name,
72 		window_look look, window_feel feel, uint32 flags, uint32 workspaces,
73 		::ServerWindow* window, DrawingEngine* drawingEngine)
74 	:
75 	fTitle(name),
76 	fFrame(frame),
77 	fScreen(NULL),
78 
79 	fVisibleRegion(),
80 	fVisibleContentRegion(),
81 	fDirtyRegion(),
82 	fDirtyCause(0),
83 
84 	fContentRegion(),
85 	fEffectiveDrawingRegion(),
86 
87 	fVisibleContentRegionValid(false),
88 	fContentRegionValid(false),
89 	fEffectiveDrawingRegionValid(false),
90 
91 	fRegionPool(),
92 
93 	fWindowBehaviour(NULL),
94 	fTopView(NULL),
95 	fWindow(window),
96 	fDrawingEngine(drawingEngine),
97 	fDesktop(window->Desktop()),
98 
99 	fCurrentUpdateSession(&fUpdateSessions[0]),
100 	fPendingUpdateSession(&fUpdateSessions[1]),
101 	fUpdateRequested(false),
102 	fInUpdate(false),
103 	fUpdatesEnabled(true),
104 
105 	// Windows start hidden
106 	fHidden(true),
107 	// Hidden is 1 or more
108 	fShowLevel(1),
109 	fMinimized(false),
110 	fIsFocus(false),
111 
112 	fLook(look),
113 	fFeel(feel),
114 	fWorkspaces(workspaces),
115 	fCurrentWorkspace(-1),
116 
117 	fMinWidth(1),
118 	fMaxWidth(32768),
119 	fMinHeight(1),
120 	fMaxHeight(32768),
121 
122 	fWorkspacesViewCount(0)
123 {
124 	_InitWindowStack();
125 
126 	// make sure our arguments are valid
127 	if (!IsValidLook(fLook))
128 		fLook = B_TITLED_WINDOW_LOOK;
129 	if (!IsValidFeel(fFeel))
130 		fFeel = B_NORMAL_WINDOW_FEEL;
131 
132 	SetFlags(flags, NULL);
133 
134 	if (fLook != B_NO_BORDER_WINDOW_LOOK && fCurrentStack.Get() != NULL) {
135 		// allocates a decorator
136 		::Decorator* decorator = Decorator();
137 		if (decorator != NULL) {
138 			decorator->GetSizeLimits(&fMinWidth, &fMinHeight, &fMaxWidth,
139 				&fMaxHeight);
140 		}
141 	}
142 	if (fFeel != kOffscreenWindowFeel)
143 		fWindowBehaviour = gDecorManager.AllocateWindowBehaviour(this);
144 
145 	// do we need to change our size to let the decorator fit?
146 	// _ResizeBy() will adapt the frame for validity before resizing
147 	if (feel == kDesktopWindowFeel) {
148 		// the desktop window spans over the whole screen
149 		// TODO: this functionality should be moved somewhere else
150 		//  (so that it is always used when the workspace is changed)
151 		uint16 width, height;
152 		uint32 colorSpace;
153 		float frequency;
154 		if (Screen() != NULL) {
155 			Screen()->GetMode(width, height, colorSpace, frequency);
156 // TODO: MOVE THIS AWAY!!! ResizeBy contains calls to virtual methods!
157 // Also, there is no TopView()!
158 			fFrame.OffsetTo(B_ORIGIN);
159 //			ResizeBy(width - frame.Width(), height - frame.Height(), NULL);
160 		}
161 	}
162 
163 	STRACE(("Window %p, %s:\n", this, Name()));
164 	STRACE(("\tFrame: (%.1f, %.1f, %.1f, %.1f)\n", fFrame.left, fFrame.top,
165 		fFrame.right, fFrame.bottom));
166 	STRACE(("\tWindow %s\n", window ? window->Title() : "NULL"));
167 }
168 
169 
170 Window::~Window()
171 {
172 	if (fTopView) {
173 		fTopView->DetachedFromWindow();
174 		delete fTopView;
175 	}
176 
177 	DetachFromWindowStack(false);
178 
179 	delete fWindowBehaviour;
180 	delete fDrawingEngine;
181 
182 	gDecorManager.CleanupForWindow(this);
183 }
184 
185 
186 status_t
187 Window::InitCheck() const
188 {
189 	if (fDrawingEngine == NULL
190 		|| (fFeel != kOffscreenWindowFeel && fWindowBehaviour == NULL))
191 		return B_NO_MEMORY;
192 	// TODO: anything else?
193 	return B_OK;
194 }
195 
196 
197 void
198 Window::SetClipping(BRegion* stillAvailableOnScreen)
199 {
200 	// this function is only called from the Desktop thread
201 
202 	// start from full region (as if the window was fully visible)
203 	GetFullRegion(&fVisibleRegion);
204 	// clip to region still available on screen
205 	fVisibleRegion.IntersectWith(stillAvailableOnScreen);
206 
207 	fVisibleContentRegionValid = false;
208 	fEffectiveDrawingRegionValid = false;
209 }
210 
211 
212 void
213 Window::GetFullRegion(BRegion* region)
214 {
215 	// TODO: if someone needs to call this from
216 	// the outside, the clipping needs to be readlocked!
217 
218 	// start from the decorator border, extend to use the frame
219 	GetBorderRegion(region);
220 	region->Include(fFrame);
221 }
222 
223 
224 void
225 Window::GetBorderRegion(BRegion* region)
226 {
227 	// TODO: if someone needs to call this from
228 	// the outside, the clipping needs to be readlocked!
229 
230 	::Decorator* decorator = Decorator();
231 	if (decorator)
232 		*region = decorator->GetFootprint();
233 	else
234 		region->MakeEmpty();
235 }
236 
237 
238 void
239 Window::GetContentRegion(BRegion* region)
240 {
241 	// TODO: if someone needs to call this from
242 	// the outside, the clipping needs to be readlocked!
243 
244 	if (!fContentRegionValid) {
245 		_UpdateContentRegion();
246 	}
247 
248 	*region = fContentRegion;
249 }
250 
251 
252 BRegion&
253 Window::VisibleContentRegion()
254 {
255 	// TODO: if someone needs to call this from
256 	// the outside, the clipping needs to be readlocked!
257 
258 	// regions expected to be locked
259 	if (!fVisibleContentRegionValid) {
260 		GetContentRegion(&fVisibleContentRegion);
261 		fVisibleContentRegion.IntersectWith(&fVisibleRegion);
262 	}
263 	return fVisibleContentRegion;
264 }
265 
266 
267 // #pragma mark -
268 
269 
270 void
271 Window::_PropagatePosition()
272 {
273 	if ((fFlags & B_SAME_POSITION_IN_ALL_WORKSPACES) == 0)
274 		return;
275 
276 	for (int32 i = 0; i < kListCount; i++) {
277 		Anchor(i).position = fFrame.LeftTop();
278 	}
279 }
280 
281 
282 void
283 Window::MoveBy(int32 x, int32 y, bool moveStack)
284 {
285 	// this function is only called from the desktop thread
286 
287 	if (x == 0 && y == 0)
288 		return;
289 
290 	fFrame.OffsetBy(x, y);
291 	_PropagatePosition();
292 
293 	// take along the dirty region which is not
294 	// processed yet
295 	fDirtyRegion.OffsetBy(x, y);
296 
297 	if (fContentRegionValid)
298 		fContentRegion.OffsetBy(x, y);
299 
300 	if (fCurrentUpdateSession->IsUsed())
301 		fCurrentUpdateSession->MoveBy(x, y);
302 	if (fPendingUpdateSession->IsUsed())
303 		fPendingUpdateSession->MoveBy(x, y);
304 
305 	fEffectiveDrawingRegionValid = false;
306 
307 	if (fTopView != NULL) {
308 		fTopView->MoveBy(x, y, NULL);
309 		fTopView->UpdateOverlay();
310 	}
311 
312 	::Decorator* decorator = Decorator();
313 	if (moveStack && decorator)
314 		decorator->MoveBy(x, y);
315 
316 	WindowStack* stack = GetWindowStack();
317 	if (moveStack && stack) {
318 		for (int32 i = 0; i < stack->CountWindows(); i++) {
319 			Window* window = stack->WindowList().ItemAt(i);
320 			if (window == this)
321 				continue;
322 			window->MoveBy(x, y, false);
323 		}
324 	}
325 
326 	// the desktop will take care of dirty regions
327 
328 	// dispatch a message to the client informing about the changed size
329 	BMessage msg(B_WINDOW_MOVED);
330 	msg.AddInt64("when", system_time());
331 	msg.AddPoint("where", fFrame.LeftTop());
332 	fWindow->SendMessageToClient(&msg);
333 }
334 
335 
336 void
337 Window::ResizeBy(int32 x, int32 y, BRegion* dirtyRegion, bool resizeStack)
338 {
339 	// this function is only called from the desktop thread
340 
341 	int32 wantWidth = fFrame.IntegerWidth() + x;
342 	int32 wantHeight = fFrame.IntegerHeight() + y;
343 
344 	// enforce size limits
345 	WindowStack* stack = GetWindowStack();
346 	if (resizeStack && stack) {
347 		for (int32 i = 0; i < stack->CountWindows(); i++) {
348 			Window* window = stack->WindowList().ItemAt(i);
349 
350 			if (wantWidth < window->fMinWidth)
351 				wantWidth = window->fMinWidth;
352 			if (wantWidth > window->fMaxWidth)
353 				wantWidth = window->fMaxWidth;
354 
355 			if (wantHeight < window->fMinHeight)
356 				wantHeight = window->fMinHeight;
357 			if (wantHeight > window->fMaxHeight)
358 				wantHeight = window->fMaxHeight;
359 		}
360 	}
361 
362 	x = wantWidth - fFrame.IntegerWidth();
363 	y = wantHeight - fFrame.IntegerHeight();
364 
365 	if (x == 0 && y == 0)
366 		return;
367 
368 	fFrame.right += x;
369 	fFrame.bottom += y;
370 
371 	fContentRegionValid = false;
372 	fEffectiveDrawingRegionValid = false;
373 
374 	if (fTopView != NULL) {
375 		fTopView->ResizeBy(x, y, dirtyRegion);
376 		fTopView->UpdateOverlay();
377 	}
378 
379 	::Decorator* decorator = Decorator();
380 	if (decorator && resizeStack)
381 		decorator->ResizeBy(x, y, dirtyRegion);
382 
383 	if (resizeStack && stack) {
384 		for (int32 i = 0; i < stack->CountWindows(); i++) {
385 			Window* window = stack->WindowList().ItemAt(i);
386 			if (window == this)
387 				continue;
388 			window->ResizeBy(x, y, dirtyRegion, false);
389 		}
390 	}
391 
392 	// send a message to the client informing about the changed size
393 	BRect frame(Frame());
394 	BMessage msg(B_WINDOW_RESIZED);
395 	msg.AddInt64("when", system_time());
396 	msg.AddInt32("width", frame.IntegerWidth());
397 	msg.AddInt32("height", frame.IntegerHeight());
398 	fWindow->SendMessageToClient(&msg);
399 }
400 
401 
402 void
403 Window::ScrollViewBy(View* view, int32 dx, int32 dy)
404 {
405 	// this is executed in ServerWindow with the Readlock
406 	// held
407 
408 	if (!view || view == fTopView || (dx == 0 && dy == 0))
409 		return;
410 
411 	BRegion* dirty = fRegionPool.GetRegion();
412 	if (!dirty)
413 		return;
414 
415 	view->ScrollBy(dx, dy, dirty);
416 
417 //fDrawingEngine->FillRegion(*dirty, (rgb_color){ 255, 0, 255, 255 });
418 //snooze(20000);
419 
420 	if (!IsOffscreenWindow() && IsVisible() && view->IsVisible()) {
421 		dirty->IntersectWith(&VisibleContentRegion());
422 		_TriggerContentRedraw(*dirty);
423 	}
424 
425 	fRegionPool.Recycle(dirty);
426 }
427 
428 
429 //! Takes care of invalidating parts that could not be copied
430 void
431 Window::CopyContents(BRegion* region, int32 xOffset, int32 yOffset)
432 {
433 	// executed in ServerWindow thread with the read lock held
434 	if (!IsVisible())
435 		return;
436 
437 	BRegion* newDirty = fRegionPool.GetRegion(*region);
438 
439 	// clip the region to the visible contents at the
440 	// source and destination location (note that VisibleContentRegion()
441 	// is used once to make sure it is valid, then fVisibleContentRegion
442 	// is used directly)
443 	region->IntersectWith(&VisibleContentRegion());
444 	if (region->CountRects() > 0) {
445 		// Constrain to content region at destination
446 		region->OffsetBy(xOffset, yOffset);
447 		region->IntersectWith(&fVisibleContentRegion);
448 		if (region->CountRects() > 0) {
449 			// if the region still contains any rects
450 			// offset to source location again
451 			region->OffsetBy(-xOffset, -yOffset);
452 
453 			BRegion* allDirtyRegions = fRegionPool.GetRegion(fDirtyRegion);
454 			if (allDirtyRegions != NULL) {
455 				if (fPendingUpdateSession->IsUsed()) {
456 					allDirtyRegions->Include(
457 						&fPendingUpdateSession->DirtyRegion());
458 				}
459 				if (fCurrentUpdateSession->IsUsed()) {
460 					allDirtyRegions->Include(
461 						&fCurrentUpdateSession->DirtyRegion());
462 				}
463 				// Get just the part of the dirty regions which is semantically
464 				// copied along
465 				allDirtyRegions->IntersectWith(region);
466 			}
467 
468 			BRegion* copyRegion = fRegionPool.GetRegion(*region);
469 			if (copyRegion != NULL) {
470 				// never copy what's already dirty
471 				if (allDirtyRegions != NULL)
472 					copyRegion->Exclude(allDirtyRegions);
473 
474 				if (fDrawingEngine->LockParallelAccess()) {
475 					fDrawingEngine->CopyRegion(copyRegion, xOffset, yOffset);
476 					fDrawingEngine->UnlockParallelAccess();
477 
478 					// Prevent those parts from being added to the dirty region...
479 					newDirty->Exclude(copyRegion);
480 
481 					// The parts that could be copied are not dirty (at the
482 					// target location!)
483 					copyRegion->OffsetBy(xOffset, yOffset);
484 					// ... and even exclude them from the pending dirty region!
485 					if (fPendingUpdateSession->IsUsed())
486 						fPendingUpdateSession->DirtyRegion().Exclude(copyRegion);
487 				}
488 
489 				fRegionPool.Recycle(copyRegion);
490 			} else {
491 				// Fallback, should never be here.
492 				if (fDrawingEngine->LockParallelAccess()) {
493 					fDrawingEngine->CopyRegion(region, xOffset, yOffset);
494 					fDrawingEngine->UnlockParallelAccess();
495 				}
496 			}
497 
498 			if (allDirtyRegions != NULL)
499 				fRegionPool.Recycle(allDirtyRegions);
500 		}
501 	}
502 	// what is left visible from the original region
503 	// at the destination after the region which could be
504 	// copied has been excluded, is considered dirty
505 	// NOTE: it may look like dirty regions are not moved
506 	// if no region could be copied, but that's alright,
507 	// since these parts will now be in newDirty anyways
508 	// (with the right offset)
509 	newDirty->OffsetBy(xOffset, yOffset);
510 	newDirty->IntersectWith(&fVisibleContentRegion);
511 	if (newDirty->CountRects() > 0)
512 		ProcessDirtyRegion(*newDirty);
513 
514 	fRegionPool.Recycle(newDirty);
515 }
516 
517 
518 // #pragma mark -
519 
520 
521 void
522 Window::SetTopView(View* topView)
523 {
524 	fTopView = topView;
525 
526 	if (fTopView) {
527 		// the top view is special, it has a coordinate system
528 		// as if it was attached directly to the desktop, therefor,
529 		// the coordinate conversion through the view tree works
530 		// as expected, since the top view has no "parent" but has
531 		// fFrame as if it had
532 
533 		// make sure the location of the top view on screen matches ours
534 		fTopView->MoveBy((int32)(fFrame.left - fTopView->Frame().left),
535 			(int32)(fFrame.top - fTopView->Frame().top), NULL);
536 
537 		// make sure the size of the top view matches ours
538 		fTopView->ResizeBy((int32)(fFrame.Width() - fTopView->Frame().Width()),
539 			(int32)(fFrame.Height() - fTopView->Frame().Height()), NULL);
540 
541 		fTopView->AttachedToWindow(this);
542 	}
543 }
544 
545 
546 View*
547 Window::ViewAt(const BPoint& where)
548 {
549 	return fTopView->ViewAt(where);
550 }
551 
552 
553 window_anchor&
554 Window::Anchor(int32 index)
555 {
556 	return fAnchor[index];
557 }
558 
559 
560 Window*
561 Window::NextWindow(int32 index) const
562 {
563 	return fAnchor[index].next;
564 }
565 
566 
567 Window*
568 Window::PreviousWindow(int32 index) const
569 {
570 	return fAnchor[index].previous;
571 }
572 
573 
574 ::Decorator*
575 Window::Decorator() const
576 {
577 	if (fCurrentStack.Get() == NULL)
578 		return NULL;
579 	return fCurrentStack->Decorator();
580 }
581 
582 
583 bool
584 Window::ReloadDecor()
585 {
586 	::Decorator* decorator = NULL;
587 	WindowBehaviour* windowBehaviour = NULL;
588 	WindowStack* stack = GetWindowStack();
589 	if (stack == NULL)
590 		return false;
591 
592 	// only reload the window at the first position
593 	if (stack->WindowAt(0) != this)
594 		return true;
595 
596 	if (fLook != B_NO_BORDER_WINDOW_LOOK) {
597 		// we need a new decorator
598 		decorator = gDecorManager.AllocateDecorator(this);
599 		if (decorator == NULL)
600 			return false;
601 
602 		// add all tabs to the decorator
603 		for (int32 i = 1; i < stack->CountWindows(); i++) {
604 			Window* window = stack->WindowAt(i);
605 			BRegion dirty;
606 			DesktopSettings settings(fDesktop);
607 			if (decorator->AddTab(settings, window->Title(), window->Look(),
608 				window->Flags(), -1, &dirty) == NULL) {
609 				delete decorator;
610 				return false;
611 			}
612 		}
613 	} else
614 		return true;
615 
616 	windowBehaviour = gDecorManager.AllocateWindowBehaviour(this);
617 	if (windowBehaviour == NULL) {
618 		delete decorator;
619 		return false;
620 	}
621 
622 	stack->SetDecorator(decorator);
623 
624 	delete fWindowBehaviour;
625 	fWindowBehaviour = windowBehaviour;
626 
627 	// set the correct focus and top layer tab
628 	for (int32 i = 0; i < stack->CountWindows(); i++) {
629 		Window* window = stack->WindowAt(i);
630 		if (window->IsFocus())
631 			decorator->SetFocus(i, true);
632 		if (window == stack->TopLayerWindow())
633 			decorator->SetTopTab(i);
634 	}
635 
636 	return true;
637 }
638 
639 
640 void
641 Window::SetScreen(const ::Screen* screen)
642 {
643 	// TODO this assert fails in Desktop::ShowWindow
644 	//ASSERT_MULTI_WRITE_LOCKED(fDesktop->ScreenLocker());
645 	fScreen = screen;
646 }
647 
648 
649 const ::Screen*
650 Window::Screen() const
651 {
652 	// TODO this assert also fails
653 	//ASSERT_MULTI_READ_LOCKED(fDesktop->ScreenLocker());
654 	return fScreen;
655 }
656 
657 
658 // #pragma mark -
659 
660 
661 void
662 Window::GetEffectiveDrawingRegion(View* view, BRegion& region)
663 {
664 	if (!fEffectiveDrawingRegionValid) {
665 		fEffectiveDrawingRegion = VisibleContentRegion();
666 		if (fUpdateRequested && !fInUpdate) {
667 			// We requested an update, but the client has not started it yet,
668 			// so it is only allowed to draw outside the pending update sessions
669 			// region
670 			fEffectiveDrawingRegion.Exclude(
671 				&fPendingUpdateSession->DirtyRegion());
672 		} else if (fInUpdate) {
673 			// enforce the dirty region of the update session
674 			fEffectiveDrawingRegion.IntersectWith(
675 				&fCurrentUpdateSession->DirtyRegion());
676 		} else {
677 			// not in update, the view can draw everywhere
678 //printf("Window(%s)::GetEffectiveDrawingRegion(for %s) - outside update\n", Title(), view->Name());
679 		}
680 
681 		fEffectiveDrawingRegionValid = true;
682 	}
683 
684 	// TODO: this is a region that needs to be cached later in the server
685 	// when the current view in ServerWindow is set, and we are currently
686 	// in an update (fInUpdate), than we can set this region and remember
687 	// it for the comming drawing commands until the current view changes
688 	// again or fEffectiveDrawingRegionValid is suddenly false.
689 	region = fEffectiveDrawingRegion;
690 	if (!fContentRegionValid)
691 		_UpdateContentRegion();
692 
693 	region.IntersectWith(&view->ScreenAndUserClipping(&fContentRegion));
694 }
695 
696 
697 bool
698 Window::DrawingRegionChanged(View* view) const
699 {
700 	return !fEffectiveDrawingRegionValid || !view->IsScreenClippingValid();
701 }
702 
703 
704 void
705 Window::ProcessDirtyRegion(BRegion& region)
706 {
707 	// if this is executed in the desktop thread,
708 	// it means that the window thread currently
709 	// blocks to get the read lock, if it is
710 	// executed from the window thread, it should
711 	// have the read lock and the desktop thread
712 	// is blocking to get the write lock. IAW, this
713 	// is only executed in one thread.
714 	if (fDirtyRegion.CountRects() == 0) {
715 		// the window needs to be informed
716 		// when the dirty region was empty.
717 		// NOTE: when the window thread has processed
718 		// the dirty region in MessageReceived(),
719 		// it will make the region empty again,
720 		// when it is empty here, we need to send
721 		// the message to initiate the next update round.
722 		// Until the message is processed in the window
723 		// thread, the desktop thread can add parts to
724 		// the region as it likes.
725 		ServerWindow()->RequestRedraw();
726 	}
727 
728 	fDirtyRegion.Include(&region);
729 	fDirtyCause |= UPDATE_EXPOSE;
730 }
731 
732 
733 void
734 Window::RedrawDirtyRegion()
735 {
736 	if (TopLayerStackWindow() != this) {
737 		fDirtyRegion.MakeEmpty();
738 		fDirtyCause = 0;
739 		return;
740 	}
741 
742 	// executed from ServerWindow with the read lock held
743 	if (IsVisible()) {
744 		_DrawBorder();
745 
746 		BRegion* dirtyContentRegion =
747 			fRegionPool.GetRegion(VisibleContentRegion());
748 		dirtyContentRegion->IntersectWith(&fDirtyRegion);
749 
750 		_TriggerContentRedraw(*dirtyContentRegion);
751 
752 		fRegionPool.Recycle(dirtyContentRegion);
753 	}
754 
755 	// reset the dirty region, since
756 	// we're fully clean. If the desktop
757 	// thread wanted to mark something
758 	// dirty in the mean time, it was
759 	// blocking on the global region lock to
760 	// get write access, since we're holding
761 	// the read lock for the whole time.
762 	fDirtyRegion.MakeEmpty();
763 	fDirtyCause = 0;
764 }
765 
766 
767 void
768 Window::MarkDirty(BRegion& regionOnScreen)
769 {
770 	// for marking any part of the desktop dirty
771 	// this will get write access to the global
772 	// region lock, and result in ProcessDirtyRegion()
773 	// to be called for any windows affected
774 	if (fDesktop)
775 		fDesktop->MarkDirty(regionOnScreen);
776 }
777 
778 
779 void
780 Window::MarkContentDirty(BRegion& regionOnScreen)
781 {
782 	// for triggering AS_REDRAW
783 	// since this won't affect other windows, read locking
784 	// is sufficient. If there was no dirty region before,
785 	// an update message is triggered
786 	if (fHidden || IsOffscreenWindow())
787 		return;
788 
789 	regionOnScreen.IntersectWith(&VisibleContentRegion());
790 	fDirtyCause |= UPDATE_REQUEST;
791 	_TriggerContentRedraw(regionOnScreen);
792 }
793 
794 
795 void
796 Window::MarkContentDirtyAsync(BRegion& regionOnScreen)
797 {
798 	// NOTE: see comments in ProcessDirtyRegion()
799 	if (fHidden || IsOffscreenWindow())
800 		return;
801 
802 	regionOnScreen.IntersectWith(&VisibleContentRegion());
803 
804 	if (fDirtyRegion.CountRects() == 0) {
805 		ServerWindow()->RequestRedraw();
806 	}
807 
808 	fDirtyRegion.Include(&regionOnScreen);
809 	fDirtyCause |= UPDATE_REQUEST;
810 }
811 
812 
813 void
814 Window::InvalidateView(View* view, BRegion& viewRegion)
815 {
816 	if (view && IsVisible() && view->IsVisible()) {
817 		if (!fContentRegionValid)
818 			_UpdateContentRegion();
819 
820 		view->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
835 void
836 Window::DisableUpdateRequests()
837 {
838 	fUpdatesEnabled = false;
839 }
840 
841 
842 // EnableUpdateRequests
843 void
844 Window::EnableUpdateRequests()
845 {
846 	fUpdatesEnabled = true;
847 	if (!fUpdateRequested && fPendingUpdateSession->IsUsed())
848 		_SendUpdateMessage();
849 }
850 
851 // #pragma mark -
852 
853 
854 /*!	\brief Handles a mouse-down message for the window.
855 
856 	\param message The message.
857 	\param where The point where the mouse click happened.
858 	\param lastClickTarget The target of the previous click.
859 	\param clickCount The number of subsequent, no longer than double-click
860 		interval separated clicks that have happened so far. This number doesn't
861 		necessarily match the value in the message. It has already been
862 		pre-processed in order to avoid erroneous multi-clicks (e.g. when a
863 		different button has been used or a different window was targeted). This
864 		is an in-out variable. The method can reset the value to 1, if it
865 		doesn't want this event handled as a multi-click. Returning a different
866 		click target will also make the caller reset the click count.
867 	\param _clickTarget Set by the method to a value identifying the clicked
868 		element. If not explicitly set, an invalid click target is assumed.
869 */
870 void
871 Window::MouseDown(BMessage* message, BPoint where,
872 	const ClickTarget& lastClickTarget, int32& clickCount,
873 	ClickTarget& _clickTarget)
874 {
875 	// If the previous click hit our decorator, get the hit region.
876 	int32 windowToken = fWindow->ServerToken();
877 	int32 lastHitRegion = 0;
878 	if (lastClickTarget.GetType() == ClickTarget::TYPE_WINDOW_DECORATOR
879 		&& lastClickTarget.WindowToken() == windowToken) {
880 		lastHitRegion = lastClickTarget.WindowElement();
881 	}
882 
883 	// Let the window behavior process the mouse event.
884 	int32 hitRegion = 0;
885 	bool eventEaten = fWindowBehaviour->MouseDown(message, where, lastHitRegion,
886 		clickCount, hitRegion);
887 
888 	if (eventEaten) {
889 		// click on the decorator (or equivalent)
890 		_clickTarget = ClickTarget(ClickTarget::TYPE_WINDOW_DECORATOR,
891 			windowToken, (int32)hitRegion);
892 	} else {
893 		// click was inside the window contents
894 		int32 viewToken = B_NULL_TOKEN;
895 		if (View* view = ViewAt(where)) {
896 			if (HasModal())
897 				return;
898 
899 			// clicking a simple View
900 			if (!IsFocus()) {
901 				bool acceptFirstClick
902 					= (Flags() & B_WILL_ACCEPT_FIRST_CLICK) != 0;
903 				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 
936 void
937 Window::MouseUp(BMessage* message, BPoint where, int32* _viewToken)
938 {
939 	fWindowBehaviour->MouseUp(message, where);
940 
941 	if (View* view = ViewAt(where)) {
942 		if (HasModal())
943 			return;
944 
945 		*_viewToken = view->Token();
946 		view->MouseUp(message, where);
947 	}
948 }
949 
950 
951 void
952 Window::MouseMoved(BMessage *message, BPoint where, int32* _viewToken,
953 	bool isLatestMouseMoved, bool isFake)
954 {
955 	View* view = ViewAt(where);
956 	if (view != NULL)
957 		*_viewToken = view->Token();
958 
959 	// ignore pointer history
960 	if (!isLatestMouseMoved)
961 		return;
962 
963 	fWindowBehaviour->MouseMoved(message, where, isFake);
964 
965 	// mouse cursor
966 
967 	if (view != NULL) {
968 		view->MouseMoved(message, where);
969 
970 		// TODO: there is more for real cursor support, ie. if a window is closed,
971 		//		new app cursor shouldn't override view cursor, ...
972 		ServerWindow()->App()->SetCurrentCursor(view->Cursor());
973 	}
974 }
975 
976 
977 void
978 Window::ModifiersChanged(int32 modifiers)
979 {
980 	fWindowBehaviour->ModifiersChanged(modifiers);
981 }
982 
983 
984 // #pragma mark -
985 
986 
987 void
988 Window::WorkspaceActivated(int32 index, bool active)
989 {
990 	BMessage activatedMsg(B_WORKSPACE_ACTIVATED);
991 	activatedMsg.AddInt64("when", system_time());
992 	activatedMsg.AddInt32("workspace", index);
993 	activatedMsg.AddBool("active", active);
994 
995 	ServerWindow()->SendMessageToClient(&activatedMsg);
996 }
997 
998 
999 void
1000 Window::WorkspacesChanged(uint32 oldWorkspaces, uint32 newWorkspaces)
1001 {
1002 	fWorkspaces = newWorkspaces;
1003 
1004 	BMessage changedMsg(B_WORKSPACES_CHANGED);
1005 	changedMsg.AddInt64("when", system_time());
1006 	changedMsg.AddInt32("old", oldWorkspaces);
1007 	changedMsg.AddInt32("new", newWorkspaces);
1008 
1009 	ServerWindow()->SendMessageToClient(&changedMsg);
1010 }
1011 
1012 
1013 void
1014 Window::Activated(bool active)
1015 {
1016 	BMessage msg(B_WINDOW_ACTIVATED);
1017 	msg.AddBool("active", active);
1018 	ServerWindow()->SendMessageToClient(&msg);
1019 }
1020 
1021 
1022 //# pragma mark -
1023 
1024 
1025 void
1026 Window::SetTitle(const char* name, BRegion& dirty)
1027 {
1028 	// rebuild the clipping for the title area
1029 	// and redraw it.
1030 
1031 	fTitle = name;
1032 
1033 	::Decorator* decorator = Decorator();
1034 	if (decorator) {
1035 		int32 index = PositionInStack();
1036 		decorator->SetTitle(index, name, &dirty);
1037 	}
1038 }
1039 
1040 
1041 void
1042 Window::SetFocus(bool focus)
1043 {
1044 	::Decorator* decorator = Decorator();
1045 
1046 	// executed from Desktop thread
1047 	// it holds the clipping write lock,
1048 	// so the window thread cannot be
1049 	// accessing fIsFocus
1050 
1051 	BRegion* dirty = NULL;
1052 	if (decorator)
1053 		dirty = fRegionPool.GetRegion(decorator->GetFootprint());
1054 	if (dirty) {
1055 		dirty->IntersectWith(&fVisibleRegion);
1056 		fDesktop->MarkDirty(*dirty);
1057 		fRegionPool.Recycle(dirty);
1058 	}
1059 
1060 	fIsFocus = focus;
1061 	if (decorator) {
1062 		int32 index = PositionInStack();
1063 		decorator->SetFocus(index, focus);
1064 	}
1065 
1066 	Activated(focus);
1067 }
1068 
1069 
1070 void
1071 Window::SetHidden(bool hidden)
1072 {
1073 	// the desktop takes care of dirty regions
1074 	if (fHidden != hidden) {
1075 		fHidden = hidden;
1076 
1077 		fTopView->SetHidden(hidden);
1078 
1079 		// TODO: anything else?
1080 	}
1081 }
1082 
1083 
1084 void
1085 Window::SetShowLevel(int32 showLevel)
1086 {
1087 	if (showLevel == fShowLevel)
1088 		return;
1089 
1090 	fShowLevel = showLevel;
1091 }
1092 
1093 
1094 void
1095 Window::SetMinimized(bool minimized)
1096 {
1097 	if (minimized == fMinimized)
1098 		return;
1099 
1100 	fMinimized = minimized;
1101 }
1102 
1103 
1104 bool
1105 Window::IsVisible() const
1106 {
1107 	if (IsOffscreenWindow())
1108 		return true;
1109 
1110 	if (IsHidden())
1111 		return false;
1112 
1113 /*
1114 	if (fVisibleRegion.CountRects() == 0)
1115 		return false;
1116 */
1117 	return fCurrentWorkspace >= 0 && fCurrentWorkspace < kWorkingList;
1118 }
1119 
1120 
1121 bool
1122 Window::IsDragging() const
1123 {
1124 	if (!fWindowBehaviour)
1125 		return false;
1126 	return fWindowBehaviour->IsDragging();
1127 }
1128 
1129 
1130 bool
1131 Window::IsResizing() const
1132 {
1133 	if (!fWindowBehaviour)
1134 		return false;
1135 	return fWindowBehaviour->IsResizing();
1136 }
1137 
1138 
1139 void
1140 Window::SetSizeLimits(int32 minWidth, int32 maxWidth, int32 minHeight,
1141 	int32 maxHeight)
1142 {
1143 	if (minWidth < 0)
1144 		minWidth = 0;
1145 
1146 	if (minHeight < 0)
1147 		minHeight = 0;
1148 
1149 	fMinWidth = minWidth;
1150 	fMaxWidth = maxWidth;
1151 	fMinHeight = minHeight;
1152 	fMaxHeight = maxHeight;
1153 
1154 	// give the Decorator a say in this too
1155 	::Decorator* decorator = Decorator();
1156 	if (decorator) {
1157 		decorator->GetSizeLimits(&fMinWidth, &fMinHeight, &fMaxWidth,
1158 			&fMaxHeight);
1159 	}
1160 
1161 	_ObeySizeLimits();
1162 }
1163 
1164 
1165 void
1166 Window::GetSizeLimits(int32* minWidth, int32* maxWidth,
1167 	int32* minHeight, int32* maxHeight) const
1168 {
1169 	*minWidth = fMinWidth;
1170 	*maxWidth = fMaxWidth;
1171 	*minHeight = fMinHeight;
1172 	*maxHeight = fMaxHeight;
1173 }
1174 
1175 
1176 bool
1177 Window::SetTabLocation(float location, bool isShifting, BRegion& dirty)
1178 {
1179 	::Decorator* decorator = Decorator();
1180 	if (decorator) {
1181 		int32 index = PositionInStack();
1182 		return decorator->SetTabLocation(index, location, isShifting, &dirty);
1183 	}
1184 
1185 	return false;
1186 }
1187 
1188 
1189 float
1190 Window::TabLocation() const
1191 {
1192 	::Decorator* decorator = Decorator();
1193 	if (decorator) {
1194 		int32 index = PositionInStack();
1195 		return decorator->TabLocation(index);
1196 	}
1197 	return 0.0;
1198 }
1199 
1200 
1201 bool
1202 Window::SetDecoratorSettings(const BMessage& settings, BRegion& dirty)
1203 {
1204 	if (settings.what == 'prVu') {
1205 		// 'prVu' == preview a decorator!
1206 		BString path;
1207 		if (settings.FindString("preview", &path) == B_OK)
1208 			return gDecorManager.PreviewDecorator(path, this) == B_OK;
1209 		return false;
1210 	}
1211 
1212 	::Decorator* decorator = Decorator();
1213 	if (decorator)
1214 		return decorator->SetSettings(settings, &dirty);
1215 
1216 	return false;
1217 }
1218 
1219 
1220 bool
1221 Window::GetDecoratorSettings(BMessage* settings)
1222 {
1223 	if (fDesktop)
1224 		fDesktop->GetDecoratorSettings(this, *settings);
1225 
1226 	::Decorator* decorator = Decorator();
1227 	if (decorator)
1228 		return decorator->GetSettings(settings);
1229 
1230 	return false;
1231 }
1232 
1233 
1234 void
1235 Window::FontsChanged(BRegion* updateRegion)
1236 {
1237 	::Decorator* decorator = Decorator();
1238 	if (decorator != NULL) {
1239 		DesktopSettings settings(fDesktop);
1240 		decorator->FontsChanged(settings, updateRegion);
1241 	}
1242 }
1243 
1244 
1245 void
1246 Window::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 
1287 void
1288 Window::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 
1311 void
1312 Window::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 */
1350 bool
1351 Window::InWorkspace(int32 index) const
1352 {
1353 	return (fWorkspaces & (1UL << index)) != 0;
1354 }
1355 
1356 
1357 bool
1358 Window::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 
1369 bool
1370 Window::IsModal() const
1371 {
1372 	return IsModalFeel(fFeel);
1373 }
1374 
1375 
1376 bool
1377 Window::IsFloating() const
1378 {
1379 	return IsFloatingFeel(fFeel);
1380 }
1381 
1382 
1383 bool
1384 Window::IsNormal() const
1385 {
1386 	return !IsFloatingFeel(fFeel) && !IsModalFeel(fFeel);
1387 }
1388 
1389 
1390 bool
1391 Window::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 */
1413 Window*
1414 Window::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 */
1449 Window*
1450 Window::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 
1478 bool
1479 Window::AddToSubset(Window* window)
1480 {
1481 	return fSubsets.AddItem(window);
1482 }
1483 
1484 
1485 void
1486 Window::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 */
1496 bool
1497 Window::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 */
1534 void
1535 Window::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 */
1548 uint32
1549 Window::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 */
1602 bool
1603 Window::InSubsetWorkspace(int32 index) const
1604 {
1605 	return (SubsetWorkspaces() & (1UL << index)) != 0;
1606 }
1607 
1608 
1609 // #pragma mark - static
1610 
1611 
1612 /*static*/ bool
1613 Window::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
1627 Window::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
1645 Window::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
1654 Window::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
1663 Window::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
1690 Window::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 
1703 void
1704 Window::_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 
1723 void
1724 Window::_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 
1773 void
1774 Window::_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
1810 fWindow->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 */
1823 void
1824 Window::_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 
1848 void
1849 Window::_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 
1867 void
1868 Window::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 
1956 void
1957 Window::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 
1989 void
1990 Window::_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 
2003 void
2004 Window::_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 
2049 Window::UpdateSession::UpdateSession()
2050 	:
2051 	fDirtyRegion(),
2052 	fInUse(false),
2053 	fCause(0)
2054 {
2055 }
2056 
2057 
2058 Window::UpdateSession::~UpdateSession()
2059 {
2060 }
2061 
2062 
2063 void
2064 Window::UpdateSession::Include(BRegion* additionalDirty)
2065 {
2066 	fDirtyRegion.Include(additionalDirty);
2067 }
2068 
2069 
2070 void
2071 Window::UpdateSession::Exclude(BRegion* dirtyInNextSession)
2072 {
2073 	fDirtyRegion.Exclude(dirtyInNextSession);
2074 }
2075 
2076 
2077 void
2078 Window::UpdateSession::MoveBy(int32 x, int32 y)
2079 {
2080 	fDirtyRegion.OffsetBy(x, y);
2081 }
2082 
2083 
2084 void
2085 Window::UpdateSession::SetUsed(bool used)
2086 {
2087 	fInUse = used;
2088 	if (!fInUse) {
2089 		fDirtyRegion.MakeEmpty();
2090 		fCause = 0;
2091 	}
2092 }
2093 
2094 
2095 void
2096 Window::UpdateSession::AddCause(uint8 cause)
2097 {
2098 	fCause |= cause;
2099 }
2100 
2101 
2102 int32
2103 Window::PositionInStack() const
2104 {
2105 	if (fCurrentStack.Get() == NULL)
2106 		return -1;
2107 	return fCurrentStack->WindowList().IndexOf(this);
2108 }
2109 
2110 
2111 bool
2112 Window::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 
2156 bool
2157 Window::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 
2207 Window*
2208 Window::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 
2223 Window*
2224 Window::TopLayerStackWindow()
2225 {
2226 	if (fCurrentStack.Get() == NULL)
2227 		return this;
2228 	return fCurrentStack->TopLayerWindow();
2229 }
2230 
2231 
2232 WindowStack*
2233 Window::GetWindowStack()
2234 {
2235 	if (fCurrentStack.Get() == NULL)
2236 		return _InitWindowStack();
2237 	return fCurrentStack;
2238 }
2239 
2240 
2241 bool
2242 Window::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 
2254 bool
2255 Window::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 
2273 WindowStack*
2274 Window::_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 
2294 WindowStack::WindowStack(::Decorator* decorator)
2295 	:
2296 	fDecorator(decorator)
2297 {
2298 
2299 }
2300 
2301 
2302 WindowStack::~WindowStack()
2303 {
2304 	delete fDecorator;
2305 }
2306 
2307 
2308 void
2309 WindowStack::SetDecorator(::Decorator* decorator)
2310 {
2311 	delete fDecorator;
2312 	fDecorator = decorator;
2313 }
2314 
2315 
2316 ::Decorator*
2317 WindowStack::Decorator()
2318 {
2319 	return fDecorator;
2320 }
2321 
2322 
2323 Window*
2324 WindowStack::TopLayerWindow() const
2325 {
2326 	return fWindowLayerOrder.ItemAt(fWindowLayerOrder.CountItems() - 1);
2327 }
2328 
2329 
2330 int32
2331 WindowStack::CountWindows()
2332 {
2333 	return fWindowList.CountItems();
2334 }
2335 
2336 
2337 Window*
2338 WindowStack::WindowAt(int32 index)
2339 {
2340 	return fWindowList.ItemAt(index);
2341 }
2342 
2343 
2344 bool
2345 WindowStack::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 
2361 bool
2362 WindowStack::RemoveWindow(Window* window)
2363 {
2364 	if (fWindowList.RemoveItem(window) == false)
2365 		return false;
2366 
2367 	fWindowLayerOrder.RemoveItem(window);
2368 	return true;
2369 }
2370 
2371 
2372 bool
2373 WindowStack::MoveToTopLayer(Window* window)
2374 {
2375 	int32 index = fWindowLayerOrder.IndexOf(window);
2376 	return fWindowLayerOrder.MoveItem(index,
2377 		fWindowLayerOrder.CountItems() - 1);
2378 }
2379 
2380 
2381 bool
2382 WindowStack::Move(int32 from, int32 to)
2383 {
2384 	return fWindowList.MoveItem(from, to);
2385 }
2386