* Copyright 2001-2020, Haiku, Inc.
* Distributed under the terms of the MIT license.
*
* Authors:
* DarkWyrm, bpmagic@columbus.rr.com
* Adi Oanca, adioanca@gmail.com
* Stephan Aßmus, superstippi@gmx.de
* Axel Dörfler, axeld@pinc-software.de
* Brecht Machiels, brecht@mos6581.org
* Clemens Zeidler, haiku@clemens-zeidler.de
* Tri-Edge AI
* Jacob Secunda, secundja@gmail.com
*/
#include "Window.h"
#include <new>
#include <stdio.h>
#include <Debug.h>
#include <DirectWindow.h>
#include <PortLink.h>
#include <View.h>
#include <ViewPrivate.h>
#include <WindowPrivate.h>
#include "ClickTarget.h"
#include "Decorator.h"
#include "DecorManager.h"
#include "Desktop.h"
#include "DrawingEngine.h"
#include "HWInterface.h"
#include "MessagePrivate.h"
#include "PortLink.h"
#include "ServerApp.h"
#include "ServerWindow.h"
#include "WindowBehaviour.h"
#include "Workspace.h"
#include "WorkspacesView.h"
#ifdef DEBUG_WINDOW
# define STRACE(x) printf x
#else
# define STRACE(x) ;
#endif
using std::nothrow;
Window::Window(const BRect& frame, const char *name,
window_look look, window_feel feel, uint32 flags, uint32 workspaces,
::ServerWindow* window, DrawingEngine* drawingEngine)
:
fTitle(name),
fFrame(frame),
fScreen(NULL),
fVisibleRegion(),
fVisibleContentRegion(),
fDirtyRegion(),
fContentRegion(),
fEffectiveDrawingRegion(),
fVisibleContentRegionValid(false),
fContentRegionValid(false),
fEffectiveDrawingRegionValid(false),
fRegionPool(),
fWindow(window),
fDrawingEngine(drawingEngine),
fDesktop(window->Desktop()),
fCurrentUpdateSession(&fUpdateSessions[0]),
fPendingUpdateSession(&fUpdateSessions[1]),
fUpdateRequested(false),
fInUpdate(false),
fUpdatesEnabled(false),
fHidden(true),
fShowLevel(1),
fMinimized(false),
fIsFocus(false),
fLook(look),
fFeel(feel),
fWorkspaces(workspaces),
fCurrentWorkspace(-1),
fPriorWorkspace(-1),
fMinWidth(1),
fMaxWidth(32768),
fMinHeight(1),
fMaxHeight(32768),
fWorkspacesViewCount(0)
{
_InitWindowStack();
if (!IsValidLook(fLook))
fLook = B_TITLED_WINDOW_LOOK;
if (!IsValidFeel(fFeel))
fFeel = B_NORMAL_WINDOW_FEEL;
SetFlags(flags, NULL);
if (fLook != B_NO_BORDER_WINDOW_LOOK && fCurrentStack.IsSet()) {
::Decorator* decorator = Decorator();
if (decorator != NULL) {
decorator->GetSizeLimits(&fMinWidth, &fMinHeight, &fMaxWidth,
&fMaxHeight);
}
}
if (fFeel != kOffscreenWindowFeel)
fWindowBehaviour.SetTo(gDecorManager.AllocateWindowBehaviour(this));
if (feel == kDesktopWindowFeel) {
uint16 width, height;
uint32 colorSpace;
float frequency;
if (Screen() != NULL) {
Screen()->GetMode(width, height, colorSpace, frequency);
fFrame.OffsetTo(B_ORIGIN);
}
}
STRACE(("Window %p, %s:\n", this, Name()));
STRACE(("\tFrame: (%.1f, %.1f, %.1f, %.1f)\n", fFrame.left, fFrame.top,
fFrame.right, fFrame.bottom));
STRACE(("\tWindow %s\n", window ? window->Title() : "NULL"));
}
Window::~Window()
{
if (fTopView.IsSet()) {
fTopView->DetachedFromWindow();
}
DetachFromWindowStack(false);
gDecorManager.CleanupForWindow(this);
}
status_t
Window::InitCheck() const
{
if (GetDrawingEngine() == NULL
|| (fFeel != kOffscreenWindowFeel && !fWindowBehaviour.IsSet()))
return B_NO_MEMORY;
return B_OK;
}
void
Window::SetClipping(BRegion* stillAvailableOnScreen)
{
GetFullRegion(&fVisibleRegion);
fVisibleRegion.IntersectWith(stillAvailableOnScreen);
fVisibleContentRegionValid = false;
fEffectiveDrawingRegionValid = false;
}
void
Window::GetFullRegion(BRegion* region)
{
GetBorderRegion(region);
region->Include(fFrame);
}
void
Window::GetBorderRegion(BRegion* region)
{
::Decorator* decorator = Decorator();
if (decorator)
*region = decorator->GetFootprint();
else
region->MakeEmpty();
}
void
Window::GetContentRegion(BRegion* region)
{
if (!fContentRegionValid) {
_UpdateContentRegion();
}
*region = fContentRegion;
}
BRegion&
Window::VisibleContentRegion()
{
if (!fVisibleContentRegionValid) {
GetContentRegion(&fVisibleContentRegion);
fVisibleContentRegion.IntersectWith(&fVisibleRegion);
}
return fVisibleContentRegion;
}
void
Window::_PropagatePosition()
{
if ((fFlags & B_SAME_POSITION_IN_ALL_WORKSPACES) == 0)
return;
for (int32 i = 0; i < kListCount; i++) {
Anchor(i).position = fFrame.LeftTop();
}
}
void
Window::MoveBy(int32 x, int32 y, bool moveStack)
{
if (x == 0 && y == 0)
return;
fFrame.OffsetBy(x, y);
_PropagatePosition();
fDirtyRegion.OffsetBy(x, y);
fExposeRegion.OffsetBy(x, y);
if (fContentRegionValid)
fContentRegion.OffsetBy(x, y);
if (fCurrentUpdateSession->IsUsed())
fCurrentUpdateSession->MoveBy(x, y);
if (fPendingUpdateSession->IsUsed())
fPendingUpdateSession->MoveBy(x, y);
fEffectiveDrawingRegionValid = false;
if (fTopView.IsSet()) {
fTopView->MoveBy(x, y, NULL);
fTopView->UpdateOverlay();
}
::Decorator* decorator = Decorator();
if (moveStack && decorator)
decorator->MoveBy(x, y);
WindowStack* stack = GetWindowStack();
if (moveStack && stack) {
for (int32 i = 0; i < stack->CountWindows(); i++) {
Window* window = stack->WindowList().ItemAt(i);
if (window == this)
continue;
window->MoveBy(x, y, false);
}
}
BMessage msg(B_WINDOW_MOVED);
msg.AddInt64("when", system_time());
msg.AddPoint("where", fFrame.LeftTop());
fWindow->SendMessageToClient(&msg);
}
void
Window::ResizeBy(int32 x, int32 y, BRegion* dirtyRegion, bool resizeStack)
{
int32 wantWidth = fFrame.IntegerWidth() + x;
int32 wantHeight = fFrame.IntegerHeight() + y;
WindowStack* stack = GetWindowStack();
if (resizeStack && stack) {
for (int32 i = 0; i < stack->CountWindows(); i++) {
Window* window = stack->WindowList().ItemAt(i);
if (wantWidth < window->fMinWidth)
wantWidth = window->fMinWidth;
if (wantWidth > window->fMaxWidth)
wantWidth = window->fMaxWidth;
if (wantHeight < window->fMinHeight)
wantHeight = window->fMinHeight;
if (wantHeight > window->fMaxHeight)
wantHeight = window->fMaxHeight;
}
}
x = wantWidth - fFrame.IntegerWidth();
y = wantHeight - fFrame.IntegerHeight();
if (x == 0 && y == 0)
return;
fFrame.right += x;
fFrame.bottom += y;
fContentRegionValid = false;
fEffectiveDrawingRegionValid = false;
if (fTopView.IsSet()) {
fTopView->ResizeBy(x, y, dirtyRegion);
fTopView->UpdateOverlay();
}
::Decorator* decorator = Decorator();
if (decorator && resizeStack)
decorator->ResizeBy(x, y, dirtyRegion);
if (resizeStack && stack) {
for (int32 i = 0; i < stack->CountWindows(); i++) {
Window* window = stack->WindowList().ItemAt(i);
if (window == this)
continue;
window->ResizeBy(x, y, dirtyRegion, false);
}
}
BRect frame(Frame());
BMessage msg(B_WINDOW_RESIZED);
msg.AddInt64("when", system_time());
msg.AddInt32("width", frame.IntegerWidth());
msg.AddInt32("height", frame.IntegerHeight());
fWindow->SendMessageToClient(&msg);
}
void
Window::SetOutlinesDelta(BPoint delta, BRegion* dirtyRegion)
{
float wantWidth = fFrame.IntegerWidth() + delta.x;
float wantHeight = fFrame.IntegerHeight() + delta.y;
WindowStack* stack = GetWindowStack();
if (stack != NULL) {
for (int32 i = 0; i < stack->CountWindows(); i++) {
Window* window = stack->WindowList().ItemAt(i);
if (wantWidth < window->fMinWidth)
wantWidth = window->fMinWidth;
if (wantWidth > window->fMaxWidth)
wantWidth = window->fMaxWidth;
if (wantHeight < window->fMinHeight)
wantHeight = window->fMinHeight;
if (wantHeight > window->fMaxHeight)
wantHeight = window->fMaxHeight;
}
delta.x = wantWidth - fFrame.IntegerWidth();
delta.y = wantHeight - fFrame.IntegerHeight();
}
::Decorator* decorator = Decorator();
if (decorator != NULL)
decorator->SetOutlinesDelta(delta, dirtyRegion);
_UpdateContentRegion();
}
void
Window::ScrollViewBy(View* view, int32 dx, int32 dy)
{
if (!view || view == fTopView.Get() || (dx == 0 && dy == 0))
return;
BRegion* dirty = fRegionPool.GetRegion();
if (!dirty)
return;
view->ScrollBy(dx, dy, dirty);
if (!IsOffscreenWindow() && IsVisible() && view->IsVisible()) {
dirty->IntersectWith(&VisibleContentRegion());
_TriggerContentRedraw(*dirty);
}
fRegionPool.Recycle(dirty);
}
void
Window::CopyContents(BRegion* region, int32 xOffset, int32 yOffset)
{
if (!IsVisible())
return;
BRegion* newDirty = fRegionPool.GetRegion(*region);
region->IntersectWith(&VisibleContentRegion());
if (region->CountRects() > 0) {
region->OffsetBy(xOffset, yOffset);
region->IntersectWith(&fVisibleContentRegion);
if (region->CountRects() > 0) {
region->OffsetBy(-xOffset, -yOffset);
BRegion* allDirtyRegions = fRegionPool.GetRegion(fDirtyRegion);
if (allDirtyRegions != NULL) {
if (fPendingUpdateSession->IsUsed()) {
allDirtyRegions->Include(
&fPendingUpdateSession->DirtyRegion());
}
if (fCurrentUpdateSession->IsUsed()) {
allDirtyRegions->Include(
&fCurrentUpdateSession->DirtyRegion());
}
allDirtyRegions->IntersectWith(region);
}
BRegion* copyRegion = fRegionPool.GetRegion(*region);
if (copyRegion != NULL) {
if (allDirtyRegions != NULL)
copyRegion->Exclude(allDirtyRegions);
if (fDrawingEngine->LockParallelAccess()) {
fDrawingEngine->CopyRegion(copyRegion, xOffset, yOffset);
fDrawingEngine->UnlockParallelAccess();
newDirty->Exclude(copyRegion);
copyRegion->OffsetBy(xOffset, yOffset);
if (fPendingUpdateSession->IsUsed())
fPendingUpdateSession->DirtyRegion().Exclude(copyRegion);
}
fRegionPool.Recycle(copyRegion);
} else {
if (fDrawingEngine->LockParallelAccess()) {
fDrawingEngine->CopyRegion(region, xOffset, yOffset);
fDrawingEngine->UnlockParallelAccess();
}
}
if (allDirtyRegions != NULL)
fRegionPool.Recycle(allDirtyRegions);
}
}
newDirty->OffsetBy(xOffset, yOffset);
newDirty->IntersectWith(&fVisibleContentRegion);
if (newDirty->CountRects() > 0)
ProcessDirtyRegion(*newDirty);
fRegionPool.Recycle(newDirty);
}
void
Window::SetTopView(View* topView)
{
if (fTopView.IsSet()) {
fTopView->DetachedFromWindow();
}
fTopView.SetTo(topView);
if (fTopView.IsSet()) {
fTopView->MoveBy((int32)(fFrame.left - fTopView->Frame().left),
(int32)(fFrame.top - fTopView->Frame().top), NULL);
fTopView->ResizeBy((int32)(fFrame.Width() - fTopView->Frame().Width()),
(int32)(fFrame.Height() - fTopView->Frame().Height()), NULL);
fTopView->AttachedToWindow(this);
}
}
View*
Window::ViewAt(const BPoint& where)
{
return fTopView->ViewAt(where);
}
window_anchor&
Window::Anchor(int32 index)
{
return fAnchor[index];
}
Window*
Window::NextWindow(int32 index) const
{
return fAnchor[index].next;
}
Window*
Window::PreviousWindow(int32 index) const
{
return fAnchor[index].previous;
}
::Decorator*
Window::Decorator() const
{
if (!fCurrentStack.IsSet())
return NULL;
return fCurrentStack->Decorator();
}
bool
Window::ReloadDecor()
{
::Decorator* decorator = NULL;
WindowBehaviour* windowBehaviour = NULL;
WindowStack* stack = GetWindowStack();
if (stack == NULL)
return false;
if (stack->WindowAt(0) != this)
return true;
if (fLook != B_NO_BORDER_WINDOW_LOOK) {
decorator = gDecorManager.AllocateDecorator(this);
if (decorator == NULL)
return false;
for (int32 i = 1; i < stack->CountWindows(); i++) {
Window* window = stack->WindowAt(i);
BRegion dirty;
DesktopSettings settings(fDesktop);
if (decorator->AddTab(settings, window->Title(), window->Look(),
window->Flags(), -1, &dirty) == NULL) {
delete decorator;
return false;
}
}
} else
return true;
windowBehaviour = gDecorManager.AllocateWindowBehaviour(this);
if (windowBehaviour == NULL) {
delete decorator;
return false;
}
stack->SetDecorator(decorator);
fWindowBehaviour.SetTo(windowBehaviour);
for (int32 i = 0; i < stack->CountWindows(); i++) {
Window* window = stack->WindowAt(i);
if (window->IsFocus())
decorator->SetFocus(i, true);
if (window == stack->TopLayerWindow())
decorator->SetTopTab(i);
}
return true;
}
void
Window::SetScreen(const ::Screen* screen)
{
fScreen = screen;
}
const ::Screen*
Window::Screen() const
{
return fScreen;
}
void
Window::GetEffectiveDrawingRegion(View* view, BRegion& region)
{
if (!fEffectiveDrawingRegionValid) {
fEffectiveDrawingRegion = VisibleContentRegion();
if (fUpdateRequested && !fInUpdate) {
fEffectiveDrawingRegion.Exclude(
&fPendingUpdateSession->DirtyRegion());
} else if (fInUpdate) {
fEffectiveDrawingRegion.IntersectWith(
&fCurrentUpdateSession->DirtyRegion());
} else {
}
fEffectiveDrawingRegionValid = true;
}
region = fEffectiveDrawingRegion;
if (!fContentRegionValid)
_UpdateContentRegion();
region.IntersectWith(&view->ScreenAndUserClipping(&fContentRegion));
}
bool
Window::DrawingRegionChanged(View* view) const
{
return !fEffectiveDrawingRegionValid || !view->IsScreenClippingValid();
}
void
Window::ProcessDirtyRegion(const BRegion& dirtyRegion, const BRegion& exposeRegion)
{
if (fDirtyRegion.CountRects() == 0) {
ServerWindow()->RequestRedraw();
}
fDirtyRegion.Include(&dirtyRegion);
fExposeRegion.Include(&exposeRegion);
}
void
Window::RedrawDirtyRegion()
{
if (TopLayerStackWindow() != this) {
fDirtyRegion.MakeEmpty();
fExposeRegion.MakeEmpty();
return;
}
if (IsVisible()) {
_DrawBorder();
BRegion* dirtyContentRegion = fRegionPool.GetRegion(VisibleContentRegion());
BRegion* exposeContentRegion = fRegionPool.GetRegion(VisibleContentRegion());
dirtyContentRegion->IntersectWith(&fDirtyRegion);
exposeContentRegion->IntersectWith(&fExposeRegion);
_TriggerContentRedraw(*dirtyContentRegion, *exposeContentRegion);
fRegionPool.Recycle(dirtyContentRegion);
fRegionPool.Recycle(exposeContentRegion);
}
fDirtyRegion.MakeEmpty();
fExposeRegion.MakeEmpty();
}
void
Window::MarkDirty(BRegion& regionOnScreen)
{
if (fDesktop)
fDesktop->MarkDirty(regionOnScreen);
}
void
Window::MarkContentDirty(BRegion& dirtyRegion, BRegion& exposeRegion)
{
if (fHidden || IsOffscreenWindow())
return;
dirtyRegion.IntersectWith(&VisibleContentRegion());
exposeRegion.IntersectWith(&VisibleContentRegion());
_TriggerContentRedraw(dirtyRegion, exposeRegion);
}
void
Window::MarkContentDirtyAsync(BRegion& dirtyRegion)
{
if (fHidden || IsOffscreenWindow())
return;
dirtyRegion.IntersectWith(&VisibleContentRegion());
if (fDirtyRegion.CountRects() == 0) {
ServerWindow()->RequestRedraw();
}
fDirtyRegion.Include(&dirtyRegion);
}
void
Window::InvalidateView(View* view, BRegion& viewRegion)
{
if (view && IsVisible() && view->IsVisible()) {
if (!fContentRegionValid)
_UpdateContentRegion();
view->LocalToScreenTransform().Apply(&viewRegion);
viewRegion.IntersectWith(&VisibleContentRegion());
if (viewRegion.CountRects() > 0) {
viewRegion.IntersectWith(
&view->ScreenAndUserClipping(&fContentRegion));
_TriggerContentRedraw(viewRegion);
}
}
}
void
Window::DisableUpdateRequests()
{
fUpdatesEnabled = false;
}
void
Window::EnableUpdateRequests()
{
fUpdatesEnabled = true;
if (!fUpdateRequested && fPendingUpdateSession->IsUsed())
_SendUpdateMessage();
}
\param message The message.
\param where The point where the mouse click happened.
\param lastClickTarget The target of the previous click.
\param clickCount The number of subsequent, no longer than double-click
interval separated clicks that have happened so far. This number doesn't
necessarily match the value in the message. It has already been
pre-processed in order to avoid erroneous multi-clicks (e.g. when a
different button has been used or a different window was targeted). This
is an in-out variable. The method can reset the value to 1, if it
doesn't want this event handled as a multi-click. Returning a different
click target will also make the caller reset the click count.
\param _clickTarget Set by the method to a value identifying the clicked
element. If not explicitly set, an invalid click target is assumed.
*/
void
Window::MouseDown(BMessage* message, BPoint where,
const ClickTarget& lastClickTarget, int32& clickCount,
ClickTarget& _clickTarget)
{
int32 windowToken = fWindow->ServerToken();
int32 lastHitRegion = 0;
if (lastClickTarget.GetType() == ClickTarget::TYPE_WINDOW_DECORATOR
&& lastClickTarget.WindowToken() == windowToken) {
lastHitRegion = lastClickTarget.WindowElement();
}
int32 hitRegion = 0;
bool eventEaten = fWindowBehaviour->MouseDown(message, where, lastHitRegion,
clickCount, hitRegion);
if (eventEaten) {
_clickTarget = ClickTarget(ClickTarget::TYPE_WINDOW_DECORATOR,
windowToken, (int32)hitRegion);
} else {
int32 viewToken = B_NULL_TOKEN;
if (View* view = ViewAt(where)) {
if (HasModal())
return;
if (!IsFocus()) {
bool acceptFirstClick
= (Flags() & B_WILL_ACCEPT_FIRST_CLICK) != 0;
if (!acceptFirstClick) {
bool avoidFocus = (Flags() & B_AVOID_FOCUS) != 0;
DesktopSettings desktopSettings(fDesktop);
if (desktopSettings.MouseMode() == B_NORMAL_MOUSE)
fDesktop->ActivateWindow(this);
else if (!avoidFocus)
fDesktop->SetFocusWindow(this);
if (!desktopSettings.AcceptFirstClick() && !avoidFocus)
return;
}
}
viewToken = view->Token();
view->MouseDown(message, where);
}
_clickTarget = ClickTarget(ClickTarget::TYPE_WINDOW_CONTENTS,
windowToken, viewToken);
}
}
void
Window::MouseUp(BMessage* message, BPoint where, int32* _viewToken)
{
fWindowBehaviour->MouseUp(message, where);
if (View* view = ViewAt(where)) {
if (HasModal())
return;
*_viewToken = view->Token();
view->MouseUp(message, where);
}
}
void
Window::MouseMoved(BMessage *message, BPoint where, int32* _viewToken,
bool isLatestMouseMoved, bool isFake)
{
View* view = ViewAt(where);
if (view != NULL)
*_viewToken = view->Token();
if (!isLatestMouseMoved)
return;
fWindowBehaviour->MouseMoved(message, where, isFake);
if (view != NULL) {
view->MouseMoved(message, where);
ServerWindow()->App()->SetCurrentCursor(view->Cursor());
}
}
void
Window::ModifiersChanged(int32 modifiers)
{
fWindowBehaviour->ModifiersChanged(modifiers);
}
void
Window::WorkspaceActivated(int32 index, bool active)
{
BMessage activatedMsg(B_WORKSPACE_ACTIVATED);
activatedMsg.AddInt64("when", system_time());
activatedMsg.AddInt32("workspace", index);
activatedMsg.AddBool("active", active);
ServerWindow()->SendMessageToClient(&activatedMsg);
}
void
Window::WorkspacesChanged(uint32 oldWorkspaces, uint32 newWorkspaces)
{
fWorkspaces = newWorkspaces;
BMessage changedMsg(B_WORKSPACES_CHANGED);
changedMsg.AddInt64("when", system_time());
changedMsg.AddInt32("old", oldWorkspaces);
changedMsg.AddInt32("new", newWorkspaces);
ServerWindow()->SendMessageToClient(&changedMsg);
}
void
Window::Activated(bool active)
{
BMessage msg(B_WINDOW_ACTIVATED);
msg.AddBool("active", active);
ServerWindow()->SendMessageToClient(&msg);
}
void
Window::SetTitle(const char* name, BRegion& dirty)
{
fTitle = name;
::Decorator* decorator = Decorator();
if (decorator) {
int32 index = PositionInStack();
decorator->SetTitle(index, name, &dirty);
}
}
void
Window::SetFocus(bool focus)
{
::Decorator* decorator = Decorator();
BRegion* dirty = NULL;
if (decorator)
dirty = fRegionPool.GetRegion(decorator->GetFootprint());
if (dirty) {
dirty->IntersectWith(&fVisibleRegion);
fDesktop->MarkDirty(*dirty);
fRegionPool.Recycle(dirty);
}
fIsFocus = focus;
if (decorator) {
int32 index = PositionInStack();
decorator->SetFocus(index, focus);
}
Activated(focus);
}
void
Window::SetHidden(bool hidden)
{
if (fHidden != hidden) {
fHidden = hidden;
fTopView->SetHidden(hidden);
}
}
void
Window::SetShowLevel(int32 showLevel)
{
if (showLevel == fShowLevel)
return;
fShowLevel = showLevel;
}
void
Window::SetMinimized(bool minimized)
{
if (minimized == fMinimized)
return;
fMinimized = minimized;
}
bool
Window::IsVisible() const
{
if (IsOffscreenWindow())
return true;
if (IsHidden())
return false;
if (fVisibleRegion.CountRects() == 0)
return false;
*/
return fCurrentWorkspace >= 0 && fCurrentWorkspace < kWorkingList;
}
bool
Window::IsDragging() const
{
if (!fWindowBehaviour.IsSet())
return false;
return fWindowBehaviour->IsDragging();
}
bool
Window::IsResizing() const
{
if (!fWindowBehaviour.IsSet())
return false;
return fWindowBehaviour->IsResizing();
}
void
Window::SetSizeLimits(int32 minWidth, int32 maxWidth, int32 minHeight,
int32 maxHeight)
{
if (minWidth < 0)
minWidth = 0;
if (minHeight < 0)
minHeight = 0;
fMinWidth = minWidth;
fMaxWidth = maxWidth;
fMinHeight = minHeight;
fMaxHeight = maxHeight;
::Decorator* decorator = Decorator();
if (decorator) {
decorator->GetSizeLimits(&fMinWidth, &fMinHeight, &fMaxWidth,
&fMaxHeight);
}
_ObeySizeLimits();
}
void
Window::GetSizeLimits(int32* minWidth, int32* maxWidth,
int32* minHeight, int32* maxHeight) const
{
*minWidth = fMinWidth;
*maxWidth = fMaxWidth;
*minHeight = fMinHeight;
*maxHeight = fMaxHeight;
}
bool
Window::SetTabLocation(float location, bool isShifting, BRegion& dirty)
{
::Decorator* decorator = Decorator();
if (decorator) {
int32 index = PositionInStack();
return decorator->SetTabLocation(index, location, isShifting, &dirty);
}
return false;
}
float
Window::TabLocation() const
{
::Decorator* decorator = Decorator();
if (decorator) {
int32 index = PositionInStack();
return decorator->TabLocation(index);
}
return 0.0;
}
bool
Window::SetDecoratorSettings(const BMessage& settings, BRegion& dirty)
{
if (settings.what == 'prVu') {
BString path;
if (settings.FindString("preview", &path) == B_OK)
return gDecorManager.PreviewDecorator(path, this) == B_OK;
return false;
}
::Decorator* decorator = Decorator();
if (decorator)
return decorator->SetSettings(settings, &dirty);
return false;
}
bool
Window::GetDecoratorSettings(BMessage* settings)
{
if (fDesktop)
fDesktop->GetDecoratorSettings(this, *settings);
::Decorator* decorator = Decorator();
if (decorator)
return decorator->GetSettings(settings);
return false;
}
void
Window::FontsChanged(BRegion* updateRegion)
{
::Decorator* decorator = Decorator();
if (decorator != NULL) {
DesktopSettings settings(fDesktop);
decorator->FontsChanged(settings, updateRegion);
}
}
void
Window::ColorsChanged(BRegion* updateRegion)
{
::Decorator* decorator = Decorator();
if (decorator != NULL) {
DesktopSettings settings(fDesktop);
decorator->ColorsChanged(settings, updateRegion);
}
}
void
Window::SetLook(window_look look, BRegion* updateRegion)
{
fLook = look;
fContentRegionValid = false;
fEffectiveDrawingRegionValid = false;
if (!fCurrentStack.IsSet())
return;
int32 stackPosition = PositionInStack();
::Decorator* decorator = Decorator();
if (decorator == NULL && look != B_NO_BORDER_WINDOW_LOOK) {
decorator = gDecorManager.AllocateDecorator(this);
fCurrentStack->SetDecorator(decorator);
if (IsFocus())
decorator->SetFocus(stackPosition, true);
}
if (decorator != NULL) {
DesktopSettings settings(fDesktop);
decorator->SetLook(stackPosition, settings, look, updateRegion);
decorator->GetSizeLimits(&fMinWidth, &fMinHeight, &fMaxWidth,
&fMaxHeight);
_ObeySizeLimits();
}
if (look == B_NO_BORDER_WINDOW_LOOK) {
fCurrentStack->SetDecorator(NULL);
}
}
void
Window::SetFeel(window_feel feel)
{
if ((fFeel == B_MODAL_SUBSET_WINDOW_FEEL
|| fFeel == B_FLOATING_SUBSET_WINDOW_FEEL)
&& feel != B_MODAL_SUBSET_WINDOW_FEEL
&& feel != B_FLOATING_SUBSET_WINDOW_FEEL)
fSubsets.MakeEmpty();
fFeel = feel;
fFlags = fOriginalFlags;
fFlags &= ValidWindowFlags(fFeel);
if (!IsNormal()) {
fFlags |= B_SAME_POSITION_IN_ALL_WORKSPACES;
_PropagatePosition();
}
}
void
Window::SetFlags(uint32 flags, BRegion* updateRegion)
{
fOriginalFlags = flags;
fFlags = flags & ValidWindowFlags(fFeel);
if (!IsNormal())
fFlags |= B_SAME_POSITION_IN_ALL_WORKSPACES;
if ((fFlags & B_SAME_POSITION_IN_ALL_WORKSPACES) != 0)
_PropagatePosition();
::Decorator* decorator = Decorator();
if (decorator == NULL)
return;
int32 stackPosition = PositionInStack();
decorator->SetFlags(stackPosition, flags, updateRegion);
decorator->GetSizeLimits(&fMinWidth, &fMinHeight, &fMaxWidth, &fMaxHeight);
_ObeySizeLimits();
#if 0
if ((fOriginalFlags & kWindowScreenFlag) != (flags & kWindowScreenFlag)) {
if ((flags & kWindowScreenFlag) != 0)
DisableUpdateRequests();
else
EnableUpdateRequests();
}
#endif
}
specified \a index.
*/
bool
Window::InWorkspace(int32 index) const
{
return (fWorkspaces & (1UL << index)) != 0;
}
bool
Window::SupportsFront()
{
if (fFeel == kDesktopWindowFeel
|| fFeel == kMenuWindowFeel
|| (fFlags & B_AVOID_FRONT) != 0)
return false;
return true;
}
bool
Window::IsModal() const
{
return IsModalFeel(fFeel);
}
bool
Window::IsFloating() const
{
return IsFloatingFeel(fFeel);
}
bool
Window::IsNormal() const
{
return !IsFloatingFeel(fFeel) && !IsModalFeel(fFeel);
}
bool
Window::HasModal() const
{
for (Window* window = NextWindow(fCurrentWorkspace); window != NULL;
window = window->NextWindow(fCurrentWorkspace)) {
if (window->IsHidden() || !window->IsModal())
continue;
if (window->HasInSubset(this))
return true;
}
return false;
}
this window can get.
Returns NULL is this window can be the backmost window.
\param workspace the workspace on which this check should be made. If
the value is -1, the window's current workspace will be used.
*/
Window*
Window::Backmost(Window* window, int32 workspace)
{
if (workspace == -1)
workspace = fCurrentWorkspace;
ASSERT(workspace != -1);
if (workspace == -1)
return NULL;
if (fFeel == kDesktopWindowFeel)
return NULL;
if (window == NULL)
window = PreviousWindow(workspace);
for (; window != NULL; window = window->PreviousWindow(workspace)) {
if (window->IsHidden() || window == this)
continue;
if (HasInSubset(window))
return window;
}
return NULL;
}
this window can get.
Returns NULL if this window can be the frontmost window.
\param workspace the workspace on which this check should be made. If
the value is -1, the window's current workspace will be used.
*/
Window*
Window::Frontmost(Window* first, int32 workspace)
{
if (workspace == -1)
workspace = fCurrentWorkspace;
ASSERT(workspace != -1);
if (workspace == -1)
return NULL;
if (fFeel == kDesktopWindowFeel)
return first ? first : NextWindow(workspace);
if (first == NULL)
first = NextWindow(workspace);
for (Window* window = first; window != NULL;
window = window->NextWindow(workspace)) {
if (window->IsHidden() || window == this)
continue;
if (window->HasInSubset(this))
return window;
}
return NULL;
}
bool
Window::AddToSubset(Window* window)
{
return fSubsets.AddItem(window);
}
void
Window::RemoveFromSubset(Window* window)
{
fSubsets.RemoveItem(window);
}
If a window is in the subset of this window, it means it should always
appear behind this window.
*/
bool
Window::HasInSubset(const Window* window) const
{
if (window == NULL || fFeel == window->Feel()
|| fFeel == B_NORMAL_WINDOW_FEEL)
return false;
if (fFeel == kMenuWindowFeel)
return window->ServerWindow()->App() == ServerWindow()->App();
if (window->Feel() == kMenuWindowFeel)
return false;
const int32 kFeels[] = {kPasswordWindowFeel, kWindowScreenFeel,
B_MODAL_ALL_WINDOW_FEEL, B_FLOATING_ALL_WINDOW_FEEL};
for (uint32 order = 0;
order < sizeof(kFeels) / sizeof(kFeels[0]); order++) {
if (fFeel == kFeels[order])
return true;
if (window->Feel() == kFeels[order])
return false;
}
if ((fFeel == B_FLOATING_APP_WINDOW_FEEL
&& window->Feel() != B_MODAL_APP_WINDOW_FEEL)
|| fFeel == B_MODAL_APP_WINDOW_FEEL)
return window->ServerWindow()->App() == ServerWindow()->App();
return fSubsets.HasItem(window);
}
*/
void
Window::FindWorkspacesViews(BObjectList<WorkspacesView>& list) const
{
int32 count = fWorkspacesViewCount;
fTopView->FindViews(kWorkspacesViewFlag, (BObjectList<View>&)list, count);
}
A modal or floating window may be visible on a workspace if one
of its subset windows is visible there. Floating windows also need
to have a subset as front window to be visible.
*/
uint32
Window::SubsetWorkspaces() const
{
if (fFeel == B_MODAL_ALL_WINDOW_FEEL
|| fFeel == B_FLOATING_ALL_WINDOW_FEEL)
return B_ALL_WORKSPACES;
if (fFeel == B_FLOATING_APP_WINDOW_FEEL) {
Window* front = fDesktop->FrontWindow();
if (front != NULL && front->IsNormal()
&& front->ServerWindow()->App() == ServerWindow()->App())
return ServerWindow()->App()->Workspaces();
return 0;
}
if (fFeel == B_MODAL_APP_WINDOW_FEEL) {
uint32 workspaces = ServerWindow()->App()->Workspaces();
if (workspaces == 0) {
return 1UL << fDesktop->CurrentWorkspace();
}
return workspaces;
}
if (fFeel == B_MODAL_SUBSET_WINDOW_FEEL
|| fFeel == B_FLOATING_SUBSET_WINDOW_FEEL) {
uint32 workspaces = 0;
bool hasNormalFront = false;
for (int32 i = 0; i < fSubsets.CountItems(); i++) {
Window* window = fSubsets.ItemAt(i);
if (!window->IsHidden())
workspaces |= window->Workspaces();
if (window == fDesktop->FrontWindow() && window->IsNormal())
hasNormalFront = true;
}
if (fFeel == B_FLOATING_SUBSET_WINDOW_FEEL && !hasNormalFront)
return 0;
return workspaces;
}
return 0;
}
specified \a index.
See SubsetWorkspaces().
*/
bool
Window::InSubsetWorkspace(int32 index) const
{
return (SubsetWorkspaces() & (1UL << index)) != 0;
}
bool
Window::IsValidLook(window_look look)
{
return look == B_TITLED_WINDOW_LOOK
|| look == B_DOCUMENT_WINDOW_LOOK
|| look == B_MODAL_WINDOW_LOOK
|| look == B_FLOATING_WINDOW_LOOK
|| look == B_BORDERED_WINDOW_LOOK
|| look == B_NO_BORDER_WINDOW_LOOK
|| look == kDesktopWindowLook
|| look == kLeftTitledWindowLook;
}
bool
Window::IsValidFeel(window_feel feel)
{
return feel == B_NORMAL_WINDOW_FEEL
|| feel == B_MODAL_SUBSET_WINDOW_FEEL
|| feel == B_MODAL_APP_WINDOW_FEEL
|| feel == B_MODAL_ALL_WINDOW_FEEL
|| feel == B_FLOATING_SUBSET_WINDOW_FEEL
|| feel == B_FLOATING_APP_WINDOW_FEEL
|| feel == B_FLOATING_ALL_WINDOW_FEEL
|| feel == kDesktopWindowFeel
|| feel == kMenuWindowFeel
|| feel == kWindowScreenFeel
|| feel == kPasswordWindowFeel
|| feel == kOffscreenWindowFeel;
}
bool
Window::IsModalFeel(window_feel feel)
{
return feel == B_MODAL_SUBSET_WINDOW_FEEL
|| feel == B_MODAL_APP_WINDOW_FEEL
|| feel == B_MODAL_ALL_WINDOW_FEEL;
}
bool
Window::IsFloatingFeel(window_feel feel)
{
return feel == B_FLOATING_SUBSET_WINDOW_FEEL
|| feel == B_FLOATING_APP_WINDOW_FEEL
|| feel == B_FLOATING_ALL_WINDOW_FEEL;
}
uint32
Window::ValidWindowFlags()
{
return B_NOT_MOVABLE
| B_NOT_CLOSABLE
| B_NOT_ZOOMABLE
| B_NOT_MINIMIZABLE
| B_NOT_RESIZABLE
| B_NOT_H_RESIZABLE
| B_NOT_V_RESIZABLE
| B_AVOID_FRONT
| B_AVOID_FOCUS
| B_WILL_ACCEPT_FIRST_CLICK
| B_OUTLINE_RESIZE
| B_NO_WORKSPACE_ACTIVATION
| B_NOT_ANCHORED_ON_ACTIVATE
| B_ASYNCHRONOUS_CONTROLS
| B_QUIT_ON_WINDOW_CLOSE
| B_SAME_POSITION_IN_ALL_WORKSPACES
| B_AUTO_UPDATE_SIZE_LIMITS
| B_CLOSE_ON_ESCAPE
| B_NO_SERVER_SIDE_WINDOW_MODIFIERS
| kWindowScreenFlag
| kAcceptKeyboardFocusFlag;
}
uint32
Window::ValidWindowFlags(window_feel feel)
{
uint32 flags = ValidWindowFlags();
if (IsModalFeel(feel))
return flags & ~(B_AVOID_FOCUS | B_AVOID_FRONT);
return flags;
}
void
Window::_ShiftPartOfRegion(BRegion* region, BRegion* regionToShift,
int32 xOffset, int32 yOffset)
{
BRegion* common = fRegionPool.GetRegion(*regionToShift);
if (!common)
return;
common->IntersectWith(region);
if (common->CountRects() > 0) {
region->Exclude(common);
common->OffsetBy(xOffset, yOffset);
region->Include(common);
}
fRegionPool.Recycle(common);
}
void
Window::_TriggerContentRedraw(BRegion& dirty, const BRegion& expose)
{
if (!IsVisible() || dirty.CountRects() == 0 || (fFlags & kWindowScreenFlag) != 0)
return;
_TransferToUpdateSession(&dirty);
if (expose.CountRects() > 0) {
if (fDrawingEngine->LockParallelAccess()) {
bool copyToFrontEnabled = fDrawingEngine->CopyToFrontEnabled();
fDrawingEngine->SetCopyToFrontEnabled(true);
fTopView->Draw(fDrawingEngine.Get(), &expose, &fContentRegion, true);
fDrawingEngine->SetCopyToFrontEnabled(copyToFrontEnabled);
fDrawingEngine->UnlockParallelAccess();
}
}
}
void
Window::_DrawBorder()
{
::Decorator* decorator = Decorator();
if (!decorator)
return;
BRegion* dirtyBorderRegion = fRegionPool.GetRegion();
if (!dirtyBorderRegion)
return;
GetBorderRegion(dirtyBorderRegion);
dirtyBorderRegion->IntersectWith(&fVisibleRegion);
dirtyBorderRegion->IntersectWith(&fDirtyRegion);
DrawingEngine* engine = decorator->GetDrawingEngine();
if (dirtyBorderRegion->CountRects() > 0 && engine->LockParallelAccess()) {
engine->ConstrainClippingRegion(dirtyBorderRegion);
bool copyToFrontEnabled = engine->CopyToFrontEnabled();
engine->SetCopyToFrontEnabled(false);
decorator->Draw(dirtyBorderRegion->Frame());
engine->SetCopyToFrontEnabled(copyToFrontEnabled);
engine->CopyToFront(*dirtyBorderRegion);
fWindow->ResyncDrawState();
engine->UnlockParallelAccess();
}
fRegionPool.Recycle(dirtyBorderRegion);
}
only called from _TriggerContentRedraw()), which
in turn is only called from MessageReceived() with
the clipping lock held
*/
void
Window::_TransferToUpdateSession(BRegion* contentDirtyRegion)
{
if (contentDirtyRegion->CountRects() <= 0)
return;
fPendingUpdateSession->SetUsed(true);
fPendingUpdateSession->Include(contentDirtyRegion);
if (!fUpdateRequested) {
_SendUpdateMessage();
}
}
void
Window::_SendUpdateMessage()
{
if (!fUpdatesEnabled)
return;
BMessage message(_UPDATE_);
if (ServerWindow()->SendMessageToClient(&message) != B_OK) {
return;
}
fUpdateRequested = true;
fEffectiveDrawingRegionValid = false;
}
void
Window::BeginUpdate(BPrivate::PortLink& link)
{
if (!fUpdateRequested) {
link.StartMessage(B_ERROR);
link.Flush();
fprintf(stderr, "Window::BeginUpdate() - no update requested!\n");
return;
}
UpdateSession* temp = fCurrentUpdateSession;
fCurrentUpdateSession = fPendingUpdateSession;
fPendingUpdateSession = temp;
fPendingUpdateSession->SetUsed(false);
fInUpdate = true;
fEffectiveDrawingRegionValid = false;
if (!fContentRegionValid)
_UpdateContentRegion();
BRegion* dirty = fRegionPool.GetRegion(
fCurrentUpdateSession->DirtyRegion());
if (!dirty) {
link.StartMessage(B_ERROR);
link.Flush();
return;
}
dirty->IntersectWith(&VisibleContentRegion());
link.StartMessage(B_OK);
link.Attach<BPoint>(fFrame.LeftTop());
link.Attach<float>(fFrame.Width());
link.Attach<float>(fFrame.Height());
fTopView->AddTokensForViewsInRegion(link, *dirty, &fContentRegion);
link.Attach<int32>(B_NULL_TOKEN);
link.Flush();
fDrawingEngine->SetCopyToFrontEnabled(false);
if (fDrawingEngine->LockParallelAccess()) {
fTopView->Draw(GetDrawingEngine(), dirty, &fContentRegion, true);
fDrawingEngine->UnlockParallelAccess();
}
fRegionPool.Recycle(dirty);
}
void
Window::EndUpdate()
{
if (fInUpdate) {
fDrawingEngine->SetCopyToFrontEnabled(true);
BRegion* dirty = fRegionPool.GetRegion(
fCurrentUpdateSession->DirtyRegion());
if (dirty) {
dirty->IntersectWith(&VisibleContentRegion());
fDrawingEngine->CopyToFront(*dirty);
fRegionPool.Recycle(dirty);
}
fCurrentUpdateSession->SetUsed(false);
fInUpdate = false;
fEffectiveDrawingRegionValid = false;
}
if (fPendingUpdateSession->IsUsed()) {
_SendUpdateMessage();
} else {
fUpdateRequested = false;
}
}
void
Window::_UpdateContentRegion()
{
fContentRegion.Set(fFrame);
::Decorator* decorator = Decorator();
if (decorator)
fContentRegion.Exclude(&decorator->GetFootprint());
fContentRegionValid = true;
}
void
Window::_ObeySizeLimits()
{
if (fMaxWidth < fMinWidth)
fMaxWidth = fMinWidth;
if (fMaxHeight < fMinHeight)
fMaxHeight = fMinHeight;
float minWidthDiff = fMinWidth - fFrame.Width();
float minHeightDiff = fMinHeight - fFrame.Height();
float maxWidthDiff = fMaxWidth - fFrame.Width();
float maxHeightDiff = fMaxHeight - fFrame.Height();
float xDiff = 0.0;
if (minWidthDiff > 0.0)
xDiff = minWidthDiff;
else if (maxWidthDiff < 0.0)
xDiff = maxWidthDiff;
float yDiff = 0.0;
if (minHeightDiff > 0.0)
yDiff = minHeightDiff;
else if (maxHeightDiff < 0.0)
yDiff = maxHeightDiff;
if (fDesktop)
fDesktop->ResizeWindowBy(this, xDiff, yDiff);
else
ResizeBy((int32)xDiff, (int32)yDiff, NULL);
}
Window::UpdateSession::UpdateSession()
:
fDirtyRegion(),
fInUse(false)
{
}
void
Window::UpdateSession::Include(BRegion* additionalDirty)
{
fDirtyRegion.Include(additionalDirty);
}
void
Window::UpdateSession::Exclude(BRegion* dirtyInNextSession)
{
fDirtyRegion.Exclude(dirtyInNextSession);
}
void
Window::UpdateSession::MoveBy(int32 x, int32 y)
{
fDirtyRegion.OffsetBy(x, y);
}
void
Window::UpdateSession::SetUsed(bool used)
{
fInUse = used;
if (!fInUse)
fDirtyRegion.MakeEmpty();
}
int32
Window::PositionInStack() const
{
if (!fCurrentStack.IsSet())
return -1;
return fCurrentStack->WindowList().IndexOf(this);
}
bool
Window::DetachFromWindowStack(bool ownStackNeeded)
{
if (!fCurrentStack.IsSet())
return false;
if (fCurrentStack->CountWindows() == 1)
return true;
int32 index = PositionInStack();
if (fCurrentStack->RemoveWindow(this) == false)
return false;
BRegion invalidatedRegion;
::Decorator* decorator = fCurrentStack->Decorator();
if (decorator != NULL) {
decorator->RemoveTab(index, &invalidatedRegion);
decorator->SetTopTab(fCurrentStack->LayerOrder().CountItems() - 1);
}
Window* remainingTop = fCurrentStack->TopLayerWindow();
if (remainingTop != NULL) {
if (decorator != NULL)
decorator->SetDrawingEngine(remainingTop->GetDrawingEngine());
remainingTop->SetFocus(remainingTop->IsFocus());
remainingTop->SetLook(remainingTop->Look(), NULL);
}
fCurrentStack = NULL;
if (ownStackNeeded == true)
_InitWindowStack();
SetFocus(IsFocus());
if (remainingTop != NULL) {
invalidatedRegion.Include(&remainingTop->VisibleRegion());
fDesktop->RebuildAndRedrawAfterWindowChange(remainingTop,
invalidatedRegion);
}
return true;
}
bool
Window::AddWindowToStack(Window* window)
{
ASSERT_MULTI_WRITE_LOCKED(fDesktop->WindowLocker());
WindowStack* stack = GetWindowStack();
if (stack == NULL)
return false;
BRegion dirty;
BRect ownFrame = Frame();
BRect frame = window->Frame();
float deltaToX = round(ownFrame.left - frame.left);
float deltaToY = round(ownFrame.top - frame.top);
frame.OffsetBy(deltaToX, deltaToY);
float deltaByX = round(ownFrame.right - frame.right);
float deltaByY = round(ownFrame.bottom - frame.bottom);
dirty.Include(&window->VisibleRegion());
window->MoveBy(deltaToX, deltaToY, false);
window->ResizeBy(deltaByX, deltaByY, &dirty, false);
::Decorator* otherDecorator = window->Decorator();
if (otherDecorator != NULL)
dirty.Include(otherDecorator->TitleBarRect());
::Decorator* decorator = stack->Decorator();
if (decorator != NULL)
dirty.Include(decorator->TitleBarRect());
int32 position = PositionInStack() + 1;
if (position >= stack->CountWindows())
position = -1;
if (stack->AddWindow(window, position) == false)
return false;
window->DetachFromWindowStack(false);
window->fCurrentStack.SetTo(stack);
if (decorator != NULL) {
DesktopSettings settings(fDesktop);
decorator->AddTab(settings, window->Title(), window->Look(),
window->Flags(), position, &dirty);
}
window->SetLook(window->Look(), &dirty);
fDesktop->RebuildAndRedrawAfterWindowChange(TopLayerStackWindow(), dirty);
window->SetFocus(window->IsFocus());
return true;
}
Window*
Window::StackedWindowAt(const BPoint& where)
{
::Decorator* decorator = Decorator();
if (decorator == NULL)
return this;
int tab = decorator->TabAt(where);
Window* window = fCurrentStack->WindowAt(tab);
if (window != NULL)
return window;
return this;
}
Window*
Window::TopLayerStackWindow()
{
if (!fCurrentStack.IsSet())
return this;
return fCurrentStack->TopLayerWindow();
}
WindowStack*
Window::GetWindowStack()
{
if (!fCurrentStack.IsSet())
return _InitWindowStack();
return fCurrentStack;
}
bool
Window::MoveToTopStackLayer()
{
::Decorator* decorator = Decorator();
if (decorator == NULL)
return false;
decorator->SetDrawingEngine(GetDrawingEngine());
SetLook(Look(), NULL);
decorator->SetTopTab(PositionInStack());
return fCurrentStack->MoveToTopLayer(this);
}
bool
Window::MoveToStackPosition(int32 to, bool isMoving)
{
if (!fCurrentStack.IsSet())
return false;
int32 index = PositionInStack();
if (fCurrentStack->Move(index, to) == false)
return false;
BRegion dirty;
::Decorator* decorator = Decorator();
if (decorator && decorator->MoveTab(index, to, isMoving, &dirty) == false)
return false;
fDesktop->RebuildAndRedrawAfterWindowChange(this, dirty);
return true;
}
WindowStack*
Window::_InitWindowStack()
{
fCurrentStack = NULL;
::Decorator* decorator = NULL;
if (fLook != B_NO_BORDER_WINDOW_LOOK)
decorator = gDecorManager.AllocateDecorator(this);
WindowStack* stack = new(std::nothrow) WindowStack(decorator);
if (stack == NULL)
return NULL;
if (stack->AddWindow(this) != true) {
delete stack;
return NULL;
}
fCurrentStack.SetTo(stack, true);
return stack;
}
WindowStack::WindowStack(::Decorator* decorator)
:
fDecorator(decorator)
{
}
WindowStack::~WindowStack()
{
}
void
WindowStack::SetDecorator(::Decorator* decorator)
{
fDecorator.SetTo(decorator);
}
::Decorator*
WindowStack::Decorator()
{
return fDecorator.Get();
}
Window*
WindowStack::TopLayerWindow() const
{
return fWindowLayerOrder.ItemAt(fWindowLayerOrder.CountItems() - 1);
}
int32
WindowStack::CountWindows()
{
return fWindowList.CountItems();
}
Window*
WindowStack::WindowAt(int32 index)
{
return fWindowList.ItemAt(index);
}
bool
WindowStack::AddWindow(Window* window, int32 position)
{
if (position >= 0) {
if (fWindowList.AddItem(window, position) == false)
return false;
} else if (fWindowList.AddItem(window) == false)
return false;
if (fWindowLayerOrder.AddItem(window) == false) {
fWindowList.RemoveItem(window);
return false;
}
return true;
}
bool
WindowStack::RemoveWindow(Window* window)
{
if (fWindowList.RemoveItem(window) == false)
return false;
fWindowLayerOrder.RemoveItem(window);
return true;
}
bool
WindowStack::MoveToTopLayer(Window* window)
{
int32 index = fWindowLayerOrder.IndexOf(window);
return fWindowLayerOrder.MoveItem(index,
fWindowLayerOrder.CountItems() - 1);
}
bool
WindowStack::Move(int32 from, int32 to)
{
return fWindowList.MoveItem(from, to);
}