root/src/kits/interface/View.cpp
/*
 * Copyright 2001-2019 Haiku, Inc. All rights reserved.
 * Distributed under the terms of the MIT License.
 *
 * Authors:
 *              Stephan Aßmus, superstippi@gmx.de
 *              Axel Dörfler, axeld@pinc-software.de
 *              Adrian Oanca, adioanca@cotty.iren.ro
 *              Ingo Weinhold. ingo_weinhold@gmx.de
 *              Julian Harnath, julian.harnath@rwth-aachen.de
 *              Joseph Groover, looncraz@looncraz.net
 */


#include <View.h>

#include <algorithm>
#include <new>

#include <math.h>
#include <stdio.h>

#include <Application.h>
#include <Bitmap.h>
#include <Button.h>
#include <Cursor.h>
#include <File.h>
#include <GradientLinear.h>
#include <GradientRadial.h>
#include <GradientRadialFocus.h>
#include <GradientDiamond.h>
#include <GradientConic.h>
#include <InterfaceDefs.h>
#include <Layout.h>
#include <LayoutContext.h>
#include <LayoutUtils.h>
#include <MenuBar.h>
#include <Message.h>
#include <MessageQueue.h>
#include <ObjectList.h>
#include <Picture.h>
#include <Point.h>
#include <Polygon.h>
#include <PropertyInfo.h>
#include <Region.h>
#include <ScrollBar.h>
#include <Shape.h>
#include <Shelf.h>
#include <String.h>
#include <Window.h>

#include <AppMisc.h>
#include <AppServerLink.h>
#include <binary_compatibility/Interface.h>
#include <binary_compatibility/Support.h>
#include <MessagePrivate.h>
#include <MessageUtils.h>
#include <PortLink.h>
#include <ServerProtocol.h>
#include <ServerProtocolStructs.h>
#include <ShapePrivate.h>
#include <ToolTip.h>
#include <ToolTipManager.h>
#include <TokenSpace.h>
#include <ViewPrivate.h>

using std::nothrow;

//#define DEBUG_BVIEW
#ifdef DEBUG_BVIEW
#       include <stdio.h>
#       define STRACE(x) printf x
#       define BVTRACE _PrintToStream()
#else
#       define STRACE(x) ;
#       define BVTRACE ;
#endif


static property_info sViewPropInfo[] = {
        { "Frame", { B_GET_PROPERTY, B_SET_PROPERTY },
                { B_DIRECT_SPECIFIER, 0 }, "The view's frame rectangle.", 0,
                { B_RECT_TYPE }
        },
        { "Hidden", { B_GET_PROPERTY, B_SET_PROPERTY },
                { B_DIRECT_SPECIFIER, 0 }, "Whether or not the view is hidden.",
                0, { B_BOOL_TYPE }
        },
        { "Shelf", { 0 },
                { B_DIRECT_SPECIFIER, 0 }, "Directs the scripting message to the "
                        "shelf.", 0
        },
        { "View", { B_COUNT_PROPERTIES, 0 },
                { B_DIRECT_SPECIFIER, 0 }, "Returns the number of child views.", 0,
                { B_INT32_TYPE }
        },
        { "View", { 0 },
                { B_INDEX_SPECIFIER, B_REVERSE_INDEX_SPECIFIER, B_NAME_SPECIFIER, 0 },
                "Directs the scripting message to the specified view.", 0
        },

        { 0 }
};


//      #pragma mark -


static inline uint32
get_uint32_color(rgb_color color)
{
        return B_BENDIAN_TO_HOST_INT32(*(uint32*)&color);
                // rgb_color is always in rgba format, no matter what endian;
                // we always return the int32 value in host endian.
}


static inline rgb_color
get_rgb_color(uint32 value)
{
        value = B_HOST_TO_BENDIAN_INT32(value);
        return *(rgb_color*)&value;
}


//      #pragma mark -


namespace BPrivate {

ViewState::ViewState()
{
        pen_location.Set(0, 0);
        pen_size = 1.0;

        // NOTE: the clipping_region is empty
        // on construction but it is not used yet,
        // we avoid having to keep track of it via
        // this flag
        clipping_region_used = false;

        high_color = (rgb_color){ 0, 0, 0, 255 };
        low_color = (rgb_color){ 255, 255, 255, 255 };
        view_color = low_color;
        which_view_color = B_NO_COLOR;
        which_view_color_tint = B_NO_TINT;

        which_high_color = B_NO_COLOR;
        which_high_color_tint = B_NO_TINT;

        which_low_color = B_NO_COLOR;
        which_low_color_tint = B_NO_TINT;

        pattern = B_SOLID_HIGH;
        drawing_mode = B_OP_COPY;

        origin.Set(0, 0);

        line_join = B_MITER_JOIN;
        line_cap = B_BUTT_CAP;
        miter_limit = B_DEFAULT_MITER_LIMIT;
        fill_rule = B_NONZERO;

        alpha_source_mode = B_PIXEL_ALPHA;
        alpha_function_mode = B_ALPHA_OVERLAY;

        scale = 1.0;

        font = *be_plain_font;
        font_flags = font.Flags();
        font_aliasing = false;

        parent_composite_transform.Reset();
        parent_composite_scale = 1.0f;
        parent_composite_origin.Set(0, 0);

        // We only keep the B_VIEW_CLIP_REGION_BIT flag invalidated,
        // because we should get the clipping region from app_server.
        // The other flags do not need to be included because the data they
        // represent is already in sync with app_server - app_server uses the
        // same init (default) values.
        valid_flags = ~B_VIEW_CLIP_REGION_BIT;

        archiving_flags = B_VIEW_FRAME_BIT | B_VIEW_RESIZE_BIT;
}


void
ViewState::UpdateServerFontState(BPrivate::PortLink &link)
{
        link.StartMessage(AS_VIEW_SET_FONT_STATE);
        link.Attach<uint16>(font_flags);
                // always present

        if (font_flags & B_FONT_FAMILY_AND_STYLE)
                link.Attach<uint32>(font.FamilyAndStyle());

        if (font_flags & B_FONT_SIZE)
                link.Attach<float>(font.Size());

        if (font_flags & B_FONT_SHEAR)
                link.Attach<float>(font.Shear());

        if (font_flags & B_FONT_ROTATION)
                link.Attach<float>(font.Rotation());

        if (font_flags & B_FONT_FALSE_BOLD_WIDTH)
                link.Attach<float>(font.FalseBoldWidth());

        if (font_flags & B_FONT_SPACING)
                link.Attach<uint8>(font.Spacing());

        if (font_flags & B_FONT_ENCODING)
                link.Attach<uint8>(font.Encoding());

        if (font_flags & B_FONT_FACE)
                link.Attach<uint16>(font.Face());

        if (font_flags & B_FONT_FLAGS)
                link.Attach<uint32>(font.Flags());
}


void
ViewState::UpdateServerState(BPrivate::PortLink &link)
{
        UpdateServerFontState(link);

        link.StartMessage(AS_VIEW_SET_STATE);

        ViewSetStateInfo info;
        info.penLocation = pen_location;
        info.penSize = pen_size;
        info.highColor = high_color;
        info.lowColor = low_color;
        info.whichHighColor = which_high_color;
        info.whichLowColor = which_low_color;
        info.whichHighColorTint = which_high_color_tint;
        info.whichLowColorTint = which_low_color_tint;
        info.pattern = pattern;
        info.drawingMode = drawing_mode;
        info.origin = origin;
        info.scale = scale;
        info.lineJoin = line_join;
        info.lineCap = line_cap;
        info.miterLimit = miter_limit;
        info.fillRule = fill_rule;
        info.alphaSourceMode = alpha_source_mode;
        info.alphaFunctionMode = alpha_function_mode;
        info.fontAntialiasing = font_aliasing;
        link.Attach<ViewSetStateInfo>(info);

        // BAffineTransform is transmitted as a double array
        double _transform[6];
        if (transform.Flatten(_transform, sizeof(_transform)) != B_OK)
                return;
        link.Attach<double[6]>(_transform);

        // we send the 'local' clipping region... if we have one...
        link.Attach<bool>(clipping_region_used);
        if (clipping_region_used)
                link.AttachRegion(clipping_region);

        // Although we might have a 'local' clipping region, when we call
        // BView::GetClippingRegion() we ask for the 'global' one and it
        // is kept on server, so we must invalidate B_VIEW_CLIP_REGION_BIT flag

        valid_flags = ~B_VIEW_CLIP_REGION_BIT;
}


void
ViewState::UpdateFrom(BPrivate::PortLink &link)
{
        link.StartMessage(AS_VIEW_GET_STATE);

        int32 code;
        if (link.FlushWithReply(code) != B_OK
                || code != B_OK)
                return;

        ViewGetStateInfo info;
        link.Read<ViewGetStateInfo>(&info);

        // set view's font state
        font_flags = B_FONT_ALL;
        font.SetFamilyAndStyle(info.fontID);
        font.SetSize(info.fontSize);
        font.SetShear(info.fontShear);
        font.SetRotation(info.fontRotation);
        font.SetFalseBoldWidth(info.fontFalseBoldWidth);
        font.SetSpacing(info.fontSpacing);
        font.SetEncoding(info.fontEncoding);
        font.SetFace(info.fontFace);
        font.SetFlags(info.fontFlags);

        // set view's state
        pen_location = info.viewStateInfo.penLocation;
        pen_size = info.viewStateInfo.penSize;
        high_color = info.viewStateInfo.highColor;
        low_color = info.viewStateInfo.lowColor;
        pattern = info.viewStateInfo.pattern;
        drawing_mode = info.viewStateInfo.drawingMode;
        origin = info.viewStateInfo.origin;
        scale = info.viewStateInfo.scale;
        line_join = info.viewStateInfo.lineJoin;
        line_cap = info.viewStateInfo.lineCap;
        miter_limit = info.viewStateInfo.miterLimit;
        fill_rule = info.viewStateInfo.fillRule;
        alpha_source_mode = info.viewStateInfo.alphaSourceMode;
        alpha_function_mode = info.viewStateInfo.alphaFunctionMode;
        font_aliasing = info.viewStateInfo.fontAntialiasing;

        // BAffineTransform is transmitted as a double array
        double _transform[6];
        link.Read<double[6]>(&_transform);
        if (transform.Unflatten(B_AFFINE_TRANSFORM_TYPE, _transform,
                sizeof(_transform)) != B_OK) {
                return;
        }

        // read the user clipping
        // (that's NOT the current View visible clipping but the additional
        // user specified clipping!)
        link.Read<bool>(&clipping_region_used);
        if (clipping_region_used)
                link.ReadRegion(&clipping_region);
        else
                clipping_region.MakeEmpty();

        valid_flags = ~(B_VIEW_CLIP_REGION_BIT | B_VIEW_PARENT_COMPOSITE_BIT)
                | (valid_flags & B_VIEW_PARENT_COMPOSITE_BIT);
}

}       // namespace BPrivate


//      #pragma mark -


// archiving constants
namespace {
        const char* const kSizesField = "BView:sizes";
                // kSizesField = {min, max, pref}
        const char* const kAlignmentField = "BView:alignment";
        const char* const kLayoutField = "BView:layout";
}


struct BView::LayoutData {
        LayoutData()
                :
                fMinSize(),
                fMaxSize(),
                fPreferredSize(),
                fAlignment(),
                fLayoutInvalidationDisabled(0),
                fLayout(NULL),
                fLayoutContext(NULL),
                fLayoutItems(5),
                fLayoutValid(true),             // TODO: Rethink these initial values!
                fMinMaxValid(true),             //
                fLayoutInProgress(false),
                fNeedsRelayout(true)
        {
        }

        status_t
        AddDataToArchive(BMessage* archive)
        {
                status_t err = archive->AddSize(kSizesField, fMinSize);

                if (err == B_OK)
                        err = archive->AddSize(kSizesField, fMaxSize);

                if (err == B_OK)
                        err = archive->AddSize(kSizesField, fPreferredSize);

                if (err == B_OK)
                        err = archive->AddAlignment(kAlignmentField, fAlignment);

                return err;
        }

        void
        PopulateFromArchive(BMessage* archive)
        {
                archive->FindSize(kSizesField, 0, &fMinSize);
                archive->FindSize(kSizesField, 1, &fMaxSize);
                archive->FindSize(kSizesField, 2, &fPreferredSize);
                archive->FindAlignment(kAlignmentField, &fAlignment);
        }

        BSize                   fMinSize;
        BSize                   fMaxSize;
        BSize                   fPreferredSize;
        BAlignment              fAlignment;
        int                             fLayoutInvalidationDisabled;
        BLayout*                fLayout;
        BLayoutContext* fLayoutContext;
        BObjectList<BLayoutItem> fLayoutItems;
        bool                    fLayoutValid;
        bool                    fMinMaxValid;
        bool                    fLayoutInProgress;
        bool                    fNeedsRelayout;
};


BView::BView(const char* name, uint32 flags, BLayout* layout)
        :
        BHandler(name)
{
        _InitData(BRect(0, 0, -1, -1), name, B_FOLLOW_NONE,
                flags | B_SUPPORTS_LAYOUT);
        SetLayout(layout);
}


BView::BView(BRect frame, const char* name, uint32 resizingMode, uint32 flags)
        :
        BHandler(name)
{
        _InitData(frame, name, resizingMode, flags);
}


BView::BView(BMessage* archive)
        :
        BHandler(BUnarchiver::PrepareArchive(archive))
{
        BUnarchiver unarchiver(archive);
        if (!archive)
                debugger("BView cannot be constructed from a NULL archive.");

        BRect frame;
        archive->FindRect("_frame", &frame);

        uint32 resizingMode;
        if (archive->FindInt32("_resize_mode", (int32*)&resizingMode) != B_OK)
                resizingMode = 0;

        uint32 flags;
        if (archive->FindInt32("_flags", (int32*)&flags) != B_OK)
                flags = 0;

        _InitData(frame, Name(), resizingMode, flags);

        font_family family;
        font_style style;
        if (archive->FindString("_fname", 0, (const char**)&family) == B_OK
                && archive->FindString("_fname", 1, (const char**)&style) == B_OK) {
                BFont font;
                font.SetFamilyAndStyle(family, style);

                float size;
                if (archive->FindFloat("_fflt", 0, &size) == B_OK)
                        font.SetSize(size);

                float shear;
                if (archive->FindFloat("_fflt", 1, &shear) == B_OK
                        && shear >= 45.0 && shear <= 135.0)
                        font.SetShear(shear);

                float rotation;
                if (archive->FindFloat("_fflt", 2, &rotation) == B_OK
                        && rotation >=0 && rotation <= 360)
                        font.SetRotation(rotation);

                SetFont(&font, B_FONT_FAMILY_AND_STYLE | B_FONT_SIZE
                        | B_FONT_SHEAR | B_FONT_ROTATION);
        }

        int32 color = 0;
        if (archive->FindInt32("_color", 0, &color) == B_OK)
                SetHighColor(get_rgb_color(color));
        if (archive->FindInt32("_color", 1, &color) == B_OK)
                SetLowColor(get_rgb_color(color));
        if (archive->FindInt32("_color", 2, &color) == B_OK)
                SetViewColor(get_rgb_color(color));

        float tint = B_NO_TINT;
        if (archive->FindInt32("_uicolor", 0, &color) == B_OK
                && color != B_NO_COLOR) {
                if (archive->FindFloat("_uitint", 0, &tint) != B_OK)
                        tint = B_NO_TINT;

                SetHighUIColor((color_which)color, tint);
        }
        if (archive->FindInt32("_uicolor", 1, &color) == B_OK
                && color != B_NO_COLOR) {
                if (archive->FindFloat("_uitint", 1, &tint) != B_OK)
                        tint = B_NO_TINT;

                SetLowUIColor((color_which)color, tint);
        }
        if (archive->FindInt32("_uicolor", 2, &color) == B_OK
                && color != B_NO_COLOR) {
                if (archive->FindFloat("_uitint", 2, &tint) != B_OK)
                        tint = B_NO_TINT;

                SetViewUIColor((color_which)color, tint);
        }

        uint32 evMask;
        uint32 options;
        if (archive->FindInt32("_evmask", 0, (int32*)&evMask) == B_OK
                && archive->FindInt32("_evmask", 1, (int32*)&options) == B_OK)
                SetEventMask(evMask, options);

        BPoint origin;
        if (archive->FindPoint("_origin", &origin) == B_OK)
                SetOrigin(origin);

        float scale;
        if (archive->FindFloat("_scale", &scale) == B_OK)
                SetScale(scale);

        BAffineTransform transform;
        if (archive->FindFlat("_transform", &transform) == B_OK)
                SetTransform(transform);

        float penSize;
        if (archive->FindFloat("_psize", &penSize) == B_OK)
                SetPenSize(penSize);

        BPoint penLocation;
        if (archive->FindPoint("_ploc", &penLocation) == B_OK)
                MovePenTo(penLocation);

        int16 lineCap;
        int16 lineJoin;
        float lineMiter;
        if (archive->FindInt16("_lmcapjoin", 0, &lineCap) == B_OK
                && archive->FindInt16("_lmcapjoin", 1, &lineJoin) == B_OK
                && archive->FindFloat("_lmmiter", &lineMiter) == B_OK)
                SetLineMode((cap_mode)lineCap, (join_mode)lineJoin, lineMiter);

        int16 fillRule;
        if (archive->FindInt16("_fillrule", &fillRule) == B_OK)
                SetFillRule(fillRule);

        int16 alphaBlend;
        int16 modeBlend;
        if (archive->FindInt16("_blend", 0, &alphaBlend) == B_OK
                && archive->FindInt16("_blend", 1, &modeBlend) == B_OK)
                SetBlendingMode( (source_alpha)alphaBlend, (alpha_function)modeBlend);

        uint32 drawingMode;
        if (archive->FindInt32("_dmod", (int32*)&drawingMode) == B_OK)
                SetDrawingMode((drawing_mode)drawingMode);

        fLayoutData->PopulateFromArchive(archive);

        if (archive->FindInt16("_show", &fShowLevel) != B_OK)
                fShowLevel = 0;

        if (BUnarchiver::IsArchiveManaged(archive)) {
                int32 i = 0;
                while (unarchiver.EnsureUnarchived("_views", i++) == B_OK)
                                ;
                unarchiver.EnsureUnarchived(kLayoutField);

        } else {
                BMessage msg;
                for (int32 i = 0; archive->FindMessage("_views", i, &msg) == B_OK;
                        i++) {
                        BArchivable* object = instantiate_object(&msg);
                        if (BView* child = dynamic_cast<BView*>(object))
                                AddChild(child);
                }
        }
}


BArchivable*
BView::Instantiate(BMessage* data)
{
        if (!validate_instantiation(data , "BView"))
                return NULL;

        return new(std::nothrow) BView(data);
}


status_t
BView::Archive(BMessage* data, bool deep) const
{
        BArchiver archiver(data);
        status_t ret = BHandler::Archive(data, deep);

        if (ret != B_OK)
                return ret;

        if ((fState->archiving_flags & B_VIEW_FRAME_BIT) != 0)
                ret = data->AddRect("_frame", Bounds().OffsetToCopy(fParentOffset));

        if (ret == B_OK)
                ret = data->AddInt32("_resize_mode", ResizingMode());

        if (ret == B_OK)
                ret = data->AddInt32("_flags", Flags());

        if (ret == B_OK && (fState->archiving_flags & B_VIEW_EVENT_MASK_BIT) != 0) {
                ret = data->AddInt32("_evmask", fEventMask);
                if (ret == B_OK)
                        ret = data->AddInt32("_evmask", fEventOptions);
        }

        if (ret == B_OK && (fState->archiving_flags & B_VIEW_FONT_BIT) != 0) {
                BFont font;
                GetFont(&font);

                font_family family;
                font_style style;
                font.GetFamilyAndStyle(&family, &style);
                ret = data->AddString("_fname", family);
                if (ret == B_OK)
                        ret = data->AddString("_fname", style);
                if (ret == B_OK)
                        ret = data->AddFloat("_fflt", font.Size());
                if (ret == B_OK)
                        ret = data->AddFloat("_fflt", font.Shear());
                if (ret == B_OK)
                        ret = data->AddFloat("_fflt", font.Rotation());
        }

        // colors
        if (ret == B_OK)
                ret = data->AddInt32("_color", get_uint32_color(HighColor()));
        if (ret == B_OK)
                ret = data->AddInt32("_color", get_uint32_color(LowColor()));
        if (ret == B_OK)
                ret = data->AddInt32("_color", get_uint32_color(ViewColor()));

        if (ret == B_OK)
                ret = data->AddInt32("_uicolor", (int32)HighUIColor());
        if (ret == B_OK)
                ret = data->AddInt32("_uicolor", (int32)LowUIColor());
        if (ret == B_OK)
                ret = data->AddInt32("_uicolor", (int32)ViewUIColor());

        if (ret == B_OK)
                ret = data->AddFloat("_uitint", fState->which_high_color_tint);
        if (ret == B_OK)
                ret = data->AddFloat("_uitint", fState->which_low_color_tint);
        if (ret == B_OK)
                ret = data->AddFloat("_uitint", fState->which_view_color_tint);

//      NOTE: we do not use this flag any more
//      if ( 1 ){
//              ret = data->AddInt32("_dbuf", 1);
//      }

        if (ret == B_OK && (fState->archiving_flags & B_VIEW_ORIGIN_BIT) != 0)
                ret = data->AddPoint("_origin", Origin());

        if (ret == B_OK && (fState->archiving_flags & B_VIEW_SCALE_BIT) != 0)
                ret = data->AddFloat("_scale", Scale());

        if (ret == B_OK && (fState->archiving_flags & B_VIEW_TRANSFORM_BIT) != 0) {
                BAffineTransform transform = Transform();
                ret = data->AddFlat("_transform", &transform);
        }

        if (ret == B_OK && (fState->archiving_flags & B_VIEW_PEN_SIZE_BIT) != 0)
                ret = data->AddFloat("_psize", PenSize());

        if (ret == B_OK && (fState->archiving_flags & B_VIEW_PEN_LOCATION_BIT) != 0)
                ret = data->AddPoint("_ploc", PenLocation());

        if (ret == B_OK && (fState->archiving_flags & B_VIEW_LINE_MODES_BIT) != 0) {
                ret = data->AddInt16("_lmcapjoin", (int16)LineCapMode());
                if (ret == B_OK)
                        ret = data->AddInt16("_lmcapjoin", (int16)LineJoinMode());
                if (ret == B_OK)
                        ret = data->AddFloat("_lmmiter", LineMiterLimit());
        }

        if (ret == B_OK && (fState->archiving_flags & B_VIEW_FILL_RULE_BIT) != 0)
                ret = data->AddInt16("_fillrule", (int16)FillRule());

        if (ret == B_OK && (fState->archiving_flags & B_VIEW_BLENDING_BIT) != 0) {
                source_alpha alphaSourceMode;
                alpha_function alphaFunctionMode;
                GetBlendingMode(&alphaSourceMode, &alphaFunctionMode);

                ret = data->AddInt16("_blend", (int16)alphaSourceMode);
                if (ret == B_OK)
                        ret = data->AddInt16("_blend", (int16)alphaFunctionMode);
        }

        if (ret == B_OK && (fState->archiving_flags & B_VIEW_DRAWING_MODE_BIT) != 0)
                ret = data->AddInt32("_dmod", DrawingMode());

        if (ret == B_OK)
                ret = fLayoutData->AddDataToArchive(data);

        if (ret == B_OK)
                ret = data->AddInt16("_show", fShowLevel);

        if (deep && ret == B_OK) {
                for (BView* child = fFirstChild; child != NULL && ret == B_OK;
                        child = child->fNextSibling)
                        ret = archiver.AddArchivable("_views", child, deep);

                if (ret == B_OK)
                        ret = archiver.AddArchivable(kLayoutField, GetLayout(), deep);
        }

        return archiver.Finish(ret);
}


status_t
BView::AllUnarchived(const BMessage* from)
{
        BUnarchiver unarchiver(from);
        status_t err = B_OK;

        int32 count;
        from->GetInfo("_views", NULL, &count);

        for (int32 i = 0; err == B_OK && i < count; i++) {
                BView* child;
                err = unarchiver.FindObject<BView>("_views", i, child);
                if (err == B_OK)
                        err = _AddChild(child, NULL) ? B_OK : B_ERROR;
        }

        if (err == B_OK) {
                BLayout*& layout = fLayoutData->fLayout;
                err = unarchiver.FindObject(kLayoutField, layout);
                if (err == B_OK && layout) {
                        fFlags |= B_SUPPORTS_LAYOUT;
                        fLayoutData->fLayout->SetOwner(this);
                }
        }

        return err;
}


status_t
BView::AllArchived(BMessage* into) const
{
        return BHandler::AllArchived(into);
}


BView::~BView()
{
        STRACE(("BView(%s)::~BView()\n", this->Name()));

        if (fOwner != NULL) {
                debugger("Trying to delete a view that belongs to a window. "
                        "Call RemoveSelf first.");
        }

        // we also delete all our children

        BView* child = fFirstChild;
        while (child) {
                BView* nextChild = child->fNextSibling;

                delete child;
                child = nextChild;
        }

        SetLayout(NULL);
        _RemoveLayoutItemsFromLayout(true);

        delete fLayoutData;

        _RemoveSelf();

        if (fToolTip != NULL)
                fToolTip->ReleaseReference();

        if (fVerScroller != NULL)
                fVerScroller->SetTarget((BView*)NULL);
        if (fHorScroller != NULL)
                fHorScroller->SetTarget((BView*)NULL);

        SetName(NULL);

        _RemoveCommArray();
        delete fState;
}


BRect
BView::Bounds() const
{
        _CheckLock();

        if (fIsPrinting)
                return fState->print_rect;

        return fBounds;
}


void
BView::_ConvertToParent(BPoint* point, bool checkLock) const
{
        if (!fParent)
                return;

        if (checkLock)
                _CheckLock();

        // - our scrolling offset
        // + our bounds location within the parent
        point->x += -fBounds.left + fParentOffset.x;
        point->y += -fBounds.top + fParentOffset.y;
}


void
BView::ConvertToParent(BPoint* point) const
{
        _ConvertToParent(point, true);
}


BPoint
BView::ConvertToParent(BPoint point) const
{
        ConvertToParent(&point);

        return point;
}


void
BView::_ConvertFromParent(BPoint* point, bool checkLock) const
{
        if (!fParent)
                return;

        if (checkLock)
                _CheckLock();

        // - our bounds location within the parent
        // + our scrolling offset
        point->x += -fParentOffset.x + fBounds.left;
        point->y += -fParentOffset.y + fBounds.top;
}


void
BView::ConvertFromParent(BPoint* point) const
{
        _ConvertFromParent(point, true);
}


BPoint
BView::ConvertFromParent(BPoint point) const
{
        ConvertFromParent(&point);

        return point;
}


void
BView::ConvertToParent(BRect* rect) const
{
        if (!fParent)
                return;

        _CheckLock();

        // - our scrolling offset
        // + our bounds location within the parent
        rect->OffsetBy(-fBounds.left + fParentOffset.x,
                -fBounds.top + fParentOffset.y);
}


BRect
BView::ConvertToParent(BRect rect) const
{
        ConvertToParent(&rect);

        return rect;
}


void
BView::ConvertFromParent(BRect* rect) const
{
        if (!fParent)
                return;

        _CheckLock();

        // - our bounds location within the parent
        // + our scrolling offset
        rect->OffsetBy(-fParentOffset.x + fBounds.left,
                -fParentOffset.y + fBounds.top);
}


BRect
BView::ConvertFromParent(BRect rect) const
{
        ConvertFromParent(&rect);

        return rect;
}


void
BView::_ConvertToScreen(BPoint* point, bool checkLock) const
{
        if (!fParent) {
                if (fOwner)
                        fOwner->ConvertToScreen(point);

                return;
        }

        if (checkLock)
                _CheckOwnerLock();

        _ConvertToParent(point, false);
        fParent->_ConvertToScreen(point, false);
}


void
BView::ConvertToScreen(BPoint* point) const
{
        _ConvertToScreen(point, true);
}


BPoint
BView::ConvertToScreen(BPoint point) const
{
        ConvertToScreen(&point);

        return point;
}


void
BView::_ConvertFromScreen(BPoint* point, bool checkLock) const
{
        if (!fParent) {
                if (fOwner)
                        fOwner->ConvertFromScreen(point);

                return;
        }

        if (checkLock)
                _CheckOwnerLock();

        _ConvertFromParent(point, false);
        fParent->_ConvertFromScreen(point, false);
}


void
BView::ConvertFromScreen(BPoint* point) const
{
        _ConvertFromScreen(point, true);
}


BPoint
BView::ConvertFromScreen(BPoint point) const
{
        ConvertFromScreen(&point);

        return point;
}


void
BView::ConvertToScreen(BRect* rect) const
{
        BPoint offset(0.0, 0.0);
        ConvertToScreen(&offset);
        rect->OffsetBy(offset);
}


BRect
BView::ConvertToScreen(BRect rect) const
{
        ConvertToScreen(&rect);

        return rect;
}


void
BView::ConvertFromScreen(BRect* rect) const
{
        BPoint offset(0.0, 0.0);
        ConvertFromScreen(&offset);
        rect->OffsetBy(offset);
}


BRect
BView::ConvertFromScreen(BRect rect) const
{
        ConvertFromScreen(&rect);

        return rect;
}


uint32
BView::Flags() const
{
        _CheckLock();
        return fFlags & ~_RESIZE_MASK_;
}


void
BView::SetFlags(uint32 flags)
{
        if (Flags() == flags)
                return;

        if (fOwner) {
                if (flags & B_PULSE_NEEDED) {
                        _CheckLock();
                        if (fOwner->fPulseRunner == NULL)
                                fOwner->SetPulseRate(fOwner->PulseRate());
                }

                uint32 changesFlags = flags ^ fFlags;
                if (changesFlags & (B_WILL_DRAW | B_FULL_UPDATE_ON_RESIZE
                                | B_FRAME_EVENTS | B_SUBPIXEL_PRECISE
                                | B_TRANSPARENT_BACKGROUND)) {
                        _CheckLockAndSwitchCurrent();

                        fOwner->fLink->StartMessage(AS_VIEW_SET_FLAGS);
                        fOwner->fLink->Attach<uint32>(flags);
                        fOwner->fLink->Flush();
                }
        }

        /* Some useful info:
                fFlags is a unsigned long (32 bits)
                * bits 1-16 are used for BView's flags
                * bits 17-32 are used for BView' resize mask
                * _RESIZE_MASK_ is used for that. Look into View.h to see how
                        it's defined
        */
        fFlags = (flags & ~_RESIZE_MASK_) | (fFlags & _RESIZE_MASK_);

        fState->archiving_flags |= B_VIEW_FLAGS_BIT;
}


BRect
BView::Frame() const
{
        return Bounds().OffsetToCopy(fParentOffset.x, fParentOffset.y);
}


void
BView::Hide()
{
        if (fOwner && fShowLevel == 0) {
                _CheckLockAndSwitchCurrent();
                fOwner->fLink->StartMessage(AS_VIEW_HIDE);
                fOwner->fLink->Flush();
        }
        fShowLevel++;

        if (fShowLevel == 1)
                _InvalidateParentLayout();
}


void
BView::Show()
{
        fShowLevel--;
        if (fOwner && fShowLevel == 0) {
                _CheckLockAndSwitchCurrent();
                fOwner->fLink->StartMessage(AS_VIEW_SHOW);
                fOwner->fLink->Flush();
        }

        if (fShowLevel == 0)
                _InvalidateParentLayout();
}


bool
BView::IsFocus() const
{
        if (fOwner) {
                _CheckLock();
                return fOwner->CurrentFocus() == this;
        } else
                return false;
}


bool
BView::IsHidden(const BView* lookingFrom) const
{
        if (fShowLevel > 0)
                return true;

        // may we be egocentric?
        if (lookingFrom == this)
                return false;

        // we have the same visibility state as our
        // parent, if there is one
        if (fParent)
                return fParent->IsHidden(lookingFrom);

        // if we're the top view, and we're interested
        // in the "global" view, we're inheriting the
        // state of the window's visibility
        if (fOwner && lookingFrom == NULL)
                return fOwner->IsHidden();

        return false;
}


bool
BView::IsHidden() const
{
        return IsHidden(NULL);
}


bool
BView::IsPrinting() const
{
        return fIsPrinting;
}


BPoint
BView::LeftTop() const
{
        return Bounds().LeftTop();
}


void
BView::SetResizingMode(uint32 mode)
{
        if (fOwner) {
                _CheckLockAndSwitchCurrent();

                fOwner->fLink->StartMessage(AS_VIEW_RESIZE_MODE);
                fOwner->fLink->Attach<uint32>(mode);
        }

        // look at SetFlags() for more info on the below line
        fFlags = (fFlags & ~_RESIZE_MASK_) | (mode & _RESIZE_MASK_);
}


uint32
BView::ResizingMode() const
{
        return fFlags & _RESIZE_MASK_;
}


void
BView::SetViewCursor(const BCursor* cursor, bool sync)
{
        if (cursor == NULL || fOwner == NULL)
                return;

        _CheckLock();

        ViewSetViewCursorInfo info;
        info.cursorToken = cursor->fServerToken;
        info.viewToken = _get_object_token_(this);
        info.sync = sync;

        BPrivate::AppServerLink link;
        link.StartMessage(AS_SET_VIEW_CURSOR);
        link.Attach<ViewSetViewCursorInfo>(info);

        if (sync) {
                // Make sure the server has processed the message.
                int32 code;
                link.FlushWithReply(code);
        }
}


void
BView::Flush() const
{
        if (fOwner)
                fOwner->Flush();
}


void
BView::Sync() const
{
        _CheckOwnerLock();
        if (fOwner)
                fOwner->Sync();
}


BWindow*
BView::Window() const
{
        return fOwner;
}


//      #pragma mark - Hook Functions


void
BView::AttachedToWindow()
{
        // Hook function
        STRACE(("\tHOOK: BView(%s)::AttachedToWindow()\n", Name()));
}


void
BView::AllAttached()
{
        // Hook function
        STRACE(("\tHOOK: BView(%s)::AllAttached()\n", Name()));
}


void
BView::DetachedFromWindow()
{
        // Hook function
        STRACE(("\tHOOK: BView(%s)::DetachedFromWindow()\n", Name()));
}


void
BView::AllDetached()
{
        // Hook function
        STRACE(("\tHOOK: BView(%s)::AllDetached()\n", Name()));
}


void
BView::Draw(BRect updateRect)
{
        // Hook function
        STRACE(("\tHOOK: BView(%s)::Draw()\n", Name()));
}


void
BView::DrawAfterChildren(BRect updateRect)
{
        // Hook function
        STRACE(("\tHOOK: BView(%s)::DrawAfterChildren()\n", Name()));
}


void
BView::FrameMoved(BPoint newPosition)
{
        // Hook function
        STRACE(("\tHOOK: BView(%s)::FrameMoved()\n", Name()));
}


void
BView::FrameResized(float newWidth, float newHeight)
{
        // Hook function
        STRACE(("\tHOOK: BView(%s)::FrameResized()\n", Name()));
}


void
BView::GetPreferredSize(float* _width, float* _height)
{
        STRACE(("\tHOOK: BView(%s)::GetPreferredSize()\n", Name()));

        if (_width != NULL)
                *_width = fBounds.Width();
        if (_height != NULL)
                *_height = fBounds.Height();
}


void
BView::ResizeToPreferred()
{
        STRACE(("\tHOOK: BView(%s)::ResizeToPreferred()\n", Name()));

        float width;
        float height;
        GetPreferredSize(&width, &height);

        ResizeTo(width, height);
}


void
BView::KeyDown(const char* bytes, int32 numBytes)
{
        // Hook function
        STRACE(("\tHOOK: BView(%s)::KeyDown()\n", Name()));

        if (Window())
                Window()->_KeyboardNavigation();
}


void
BView::KeyUp(const char* bytes, int32 numBytes)
{
        // Hook function
        STRACE(("\tHOOK: BView(%s)::KeyUp()\n", Name()));
}


void
BView::MouseDown(BPoint where)
{
        // Hook function
        STRACE(("\tHOOK: BView(%s)::MouseDown()\n", Name()));
}


void
BView::MouseUp(BPoint where)
{
        // Hook function
        STRACE(("\tHOOK: BView(%s)::MouseUp()\n", Name()));
}


void
BView::MouseMoved(BPoint where, uint32 code, const BMessage* dragMessage)
{
        // Hook function
        STRACE(("\tHOOK: BView(%s)::MouseMoved()\n", Name()));
}


void
BView::Pulse()
{
        // Hook function
        STRACE(("\tHOOK: BView(%s)::Pulse()\n", Name()));
}


void
BView::TargetedByScrollView(BScrollView* scroll_view)
{
        // Hook function
        STRACE(("\tHOOK: BView(%s)::TargetedByScrollView()\n", Name()));
}


void
BView::WindowActivated(bool active)
{
        // Hook function
        STRACE(("\tHOOK: BView(%s)::WindowActivated()\n", Name()));
}


//      #pragma mark - Input Functions


void
BView::BeginRectTracking(BRect startRect, uint32 style)
{
        if (_CheckOwnerLockAndSwitchCurrent()) {
                fOwner->fLink->StartMessage(AS_VIEW_BEGIN_RECT_TRACK);
                fOwner->fLink->Attach<BRect>(startRect);
                fOwner->fLink->Attach<uint32>(style);
                fOwner->fLink->Flush();
        }
}


void
BView::EndRectTracking()
{
        if (_CheckOwnerLockAndSwitchCurrent()) {
                fOwner->fLink->StartMessage(AS_VIEW_END_RECT_TRACK);
                fOwner->fLink->Flush();
        }
}


void
BView::DragMessage(BMessage* message, BRect dragRect, BHandler* replyTo)
{
        if (!message)
                return;

        _CheckOwnerLock();

        // calculate the offset
        BPoint offset;
        uint32 buttons;
        BMessage* current = fOwner->CurrentMessage();
        if (!current || current->FindPoint("be:view_where", &offset) != B_OK)
                GetMouse(&offset, &buttons, false);
        offset -= dragRect.LeftTop();

        if (!dragRect.IsValid()) {
                DragMessage(message, NULL, B_OP_BLEND, offset, replyTo);
                return;
        }

        // TODO: that's not really what should happen - the app_server should take
        // the chance *NOT* to need to drag a whole bitmap around but just a frame.

        // create a drag bitmap for the rect
        BBitmap* bitmap = new(std::nothrow) BBitmap(dragRect, B_RGBA32);
        if (bitmap == NULL)
                return;

        uint32* bits = (uint32*)bitmap->Bits();
        uint32 bytesPerRow = bitmap->BytesPerRow();
        uint32 width = dragRect.IntegerWidth() + 1;
        uint32 height = dragRect.IntegerHeight() + 1;
        uint32 lastRow = (height - 1) * width;

        memset(bits, 0x00, height * bytesPerRow);

        // top
        for (uint32 i = 0; i < width; i += 2)
                bits[i] = 0xff000000;

        // bottom
        for (uint32 i = (height % 2 == 0 ? 1 : 0); i < width; i += 2)
                bits[lastRow + i] = 0xff000000;

        // left
        for (uint32 i = 0; i < lastRow; i += width * 2)
                bits[i] = 0xff000000;

        // right
        for (uint32 i = (width % 2 == 0 ? width : 0); i < lastRow; i += width * 2)
                bits[width - 1 + i] = 0xff000000;

        DragMessage(message, bitmap, B_OP_BLEND, offset, replyTo);
}


void
BView::DragMessage(BMessage* message, BBitmap* image, BPoint offset,
        BHandler* replyTo)
{
        DragMessage(message, image, B_OP_COPY, offset, replyTo);
}


void
BView::DragMessage(BMessage* message, BBitmap* image,
        drawing_mode dragMode, BPoint offset, BHandler* replyTo)
{
        if (message == NULL)
                return;

        if (image == NULL) {
                // TODO: workaround for drags without a bitmap - should not be necessary if
                //      we move the rectangle dragging into the app_server
                image = new(std::nothrow) BBitmap(BRect(0, 0, 0, 0), B_RGBA32);
                if (image == NULL)
                        return;
        }

        if (replyTo == NULL)
                replyTo = this;

        if (replyTo->Looper() == NULL)
                debugger("DragMessage: warning - the Handler needs a looper");

        _CheckOwnerLock();

        if (!message->HasInt32("buttons")) {
                BMessage* msg = fOwner->CurrentMessage();
                uint32 buttons;

                if (msg == NULL
                        || msg->FindInt32("buttons", (int32*)&buttons) != B_OK) {
                        BPoint point;
                        GetMouse(&point, &buttons, false);
                }

                message->AddInt32("buttons", buttons);
        }

        BMessage::Private privateMessage(message);
        privateMessage.SetReply(BMessenger(replyTo, replyTo->Looper()));

        int32 bufferSize = message->FlattenedSize();
        char* buffer = new(std::nothrow) char[bufferSize];
        if (buffer != NULL) {
                message->Flatten(buffer, bufferSize);

                fOwner->fLink->StartMessage(AS_VIEW_DRAG_IMAGE);
                fOwner->fLink->Attach<int32>(image->_ServerToken());
                fOwner->fLink->Attach<int32>((int32)dragMode);
                fOwner->fLink->Attach<BPoint>(offset);
                fOwner->fLink->Attach<int32>(bufferSize);
                fOwner->fLink->Attach(buffer, bufferSize);

                // we need to wait for the server
                // to actually process this message
                // before we can delete the bitmap
                int32 code;
                fOwner->fLink->FlushWithReply(code);

                delete [] buffer;
        } else {
                fprintf(stderr, "BView::DragMessage() - no memory to flatten drag "
                        "message\n");
        }

        delete image;
}


void
BView::GetMouse(BPoint* _location, uint32* _buttons, bool checkMessageQueue)
{
        if (_location == NULL && _buttons == NULL)
                return;

        _CheckOwnerLockAndSwitchCurrent();

        uint32 eventOptions = fEventOptions | fMouseEventOptions;
        bool noHistory = eventOptions & B_NO_POINTER_HISTORY;
        bool fullHistory = eventOptions & B_FULL_POINTER_HISTORY;

        if (checkMessageQueue && !noHistory) {
                Window()->UpdateIfNeeded();
                BMessageQueue* queue = Window()->MessageQueue();
                queue->Lock();

                // Look out for mouse update messages

                BMessage* message;
                for (int32 i = 0; (message = queue->FindMessage(i)) != NULL; i++) {
                        switch (message->what) {
                                case B_MOUSE_MOVED:
                                case B_MOUSE_UP:
                                case B_MOUSE_DOWN:
                                        bool deleteMessage;
                                        if (!Window()->_StealMouseMessage(message, deleteMessage))
                                                continue;

                                        if (!fullHistory && message->what == B_MOUSE_MOVED) {
                                                // Check if the message is too old. Some applications
                                                // check the message queue in such a way that mouse
                                                // messages *must* pile up. This check makes them work
                                                // as intended, although these applications could simply
                                                // use the version of BView::GetMouse() that does not
                                                // check the history. Also note that it isn't a problem
                                                // to delete the message in case there is not a newer
                                                // one. If we don't find a message in the queue, we will
                                                // just fall back to asking the app_sever directly. So
                                                // the imposed delay will not be a problem on slower
                                                // computers. This check also prevents another problem,
                                                // when the message that we use is *not* removed from
                                                // the queue. Subsequent calls to GetMouse() would find
                                                // this message over and over!
                                                bigtime_t eventTime;
                                                if (message->FindInt64("when", &eventTime) == B_OK
                                                        && system_time() - eventTime > 10000) {
                                                        // just discard the message
                                                        if (deleteMessage)
                                                                delete message;
                                                        continue;
                                                }
                                        }
                                        if (_location != NULL)
                                                message->FindPoint("screen_where", _location);
                                        if (_buttons != NULL)
                                                message->FindInt32("buttons", (int32*)_buttons);
                                        queue->Unlock();
                                                // we need to hold the queue lock until here, because
                                                // the message might still be used for something else

                                        if (_location != NULL)
                                                ConvertFromScreen(_location);

                                        if (deleteMessage)
                                                delete message;

                                        return;
                        }
                }
                queue->Unlock();
        }

        // If no mouse update message has been found in the message queue,
        // we get the current mouse location and buttons from the app_server

        fOwner->fLink->StartMessage(AS_GET_MOUSE);

        int32 code;
        if (fOwner->fLink->FlushWithReply(code) == B_OK
                && code == B_OK) {
                BPoint location;
                uint32 buttons;
                fOwner->fLink->Read<BPoint>(&location);
                fOwner->fLink->Read<uint32>(&buttons);
                        // TODO: ServerWindow replies with an int32 here

                ConvertFromScreen(&location);
                        // TODO: in beos R5, location is already converted to the view
                        // local coordinate system, so if an app checks the window message
                        // queue by itself, it might not find what it expects.
                        // NOTE: the fact that we have mouse coords in screen space in our
                        // queue avoids the problem that messages already in the queue will
                        // be outdated as soon as a window or even the view moves. The
                        // second situation being quite common actually, also with regards
                        // to scrolling. An app reading these messages would have to know
                        // the locations of the window and view for each message...
                        // otherwise it is potentially broken anyways.
                if (_location != NULL)
                        *_location = location;
                if (_buttons != NULL)
                        *_buttons = buttons;
        } else {
                if (_location != NULL)
                        _location->Set(0, 0);
                if (_buttons != NULL)
                        *_buttons = 0;
        }
}


void
BView::MakeFocus(bool focus)
{
        if (fOwner == NULL)
                return;

        // TODO: If this view has focus and focus == false,
        // will there really be no other view with focus? No
        // cycling to the next one?
        BView* focusView = fOwner->CurrentFocus();
        if (focus) {
                // Unfocus a previous focus view
                if (focusView != NULL && focusView != this)
                        focusView->MakeFocus(false);

                // if we want to make this view the current focus view
                fOwner->_SetFocus(this, true);
        } else {
                // we want to unfocus this view, but only if it actually has focus
                if (focusView == this)
                        fOwner->_SetFocus(NULL, true);
        }
}


BScrollBar*
BView::ScrollBar(orientation direction) const
{
        switch (direction) {
                case B_VERTICAL:
                        return fVerScroller;

                case B_HORIZONTAL:
                        return fHorScroller;

                default:
                        return NULL;
        }
}


void
BView::ScrollBy(float deltaX, float deltaY)
{
        ScrollTo(BPoint(fBounds.left + deltaX, fBounds.top + deltaY));
}


void
BView::ScrollTo(BPoint where)
{
        // scrolling by fractional values is not supported
        where.x = roundf(where.x);
        where.y = roundf(where.y);

        // no reason to process this further if no scroll is intended.
        if (where.x == fBounds.left && where.y == fBounds.top)
                return;

        // make sure scrolling is within valid bounds
        if (fHorScroller) {
                float min, max;
                fHorScroller->GetRange(&min, &max);

                if (where.x < min)
                        where.x = min;
                else if (where.x > max)
                        where.x = max;
        }
        if (fVerScroller) {
                float min, max;
                fVerScroller->GetRange(&min, &max);

                if (where.y < min)
                        where.y = min;
                else if (where.y > max)
                        where.y = max;
        }

        _CheckLockAndSwitchCurrent();

        float xDiff = where.x - fBounds.left;
        float yDiff = where.y - fBounds.top;

        // if we're attached to a window tell app_server about this change
        if (fOwner) {
                fOwner->fLink->StartMessage(AS_VIEW_SCROLL);
                fOwner->fLink->Attach<float>(xDiff);
                fOwner->fLink->Attach<float>(yDiff);

                fOwner->fLink->Flush();

//              fState->valid_flags &= ~B_VIEW_FRAME_BIT;
        }

        // we modify our bounds rectangle by deltaX/deltaY coord units hor/ver.
        fBounds.OffsetTo(where.x, where.y);

        // then set the new values of the scrollbars
        if (fHorScroller && xDiff != 0.0)
                fHorScroller->SetValue(fBounds.left);
        if (fVerScroller && yDiff != 0.0)
                fVerScroller->SetValue(fBounds.top);

}


status_t
BView::SetEventMask(uint32 mask, uint32 options)
{
        if (fEventMask == mask && fEventOptions == options)
                return B_OK;

        // don't change the mask if it's zero and we've got options
        if (mask != 0 || options == 0)
                fEventMask = mask | (fEventMask & 0xffff0000);
        fEventOptions = options;

        fState->archiving_flags |= B_VIEW_EVENT_MASK_BIT;

        if (fOwner) {
                _CheckLockAndSwitchCurrent();

                fOwner->fLink->StartMessage(AS_VIEW_SET_EVENT_MASK);
                fOwner->fLink->Attach<uint32>(mask);
                fOwner->fLink->Attach<uint32>(options);
                fOwner->fLink->Flush();
        }

        return B_OK;
}


uint32
BView::EventMask()
{
        return fEventMask;
}


status_t
BView::SetMouseEventMask(uint32 mask, uint32 options)
{
        // Just don't do anything if the view is not yet attached
        // or we were called outside of BView::MouseDown()
        if (fOwner != NULL
                && fOwner->CurrentMessage() != NULL
                && fOwner->CurrentMessage()->what == B_MOUSE_DOWN) {
                _CheckLockAndSwitchCurrent();
                fMouseEventOptions = options;

                fOwner->fLink->StartMessage(AS_VIEW_SET_MOUSE_EVENT_MASK);
                fOwner->fLink->Attach<uint32>(mask);
                fOwner->fLink->Attach<uint32>(options);
                fOwner->fLink->Flush();
                return B_OK;
        }

        return B_ERROR;
}


//      #pragma mark - Graphic State Functions


void
BView::PushState()
{
        _CheckOwnerLockAndSwitchCurrent();

        fOwner->fLink->StartMessage(AS_VIEW_PUSH_STATE);

        fState->valid_flags &= ~B_VIEW_PARENT_COMPOSITE_BIT;

        // initialize origin, scale and transform, new states start "clean".
        fState->valid_flags |= B_VIEW_SCALE_BIT | B_VIEW_ORIGIN_BIT
                | B_VIEW_TRANSFORM_BIT;
        fState->scale = 1.0f;
        fState->origin.Set(0, 0);
        fState->transform.Reset();
}


void
BView::PopState()
{
        _CheckOwnerLockAndSwitchCurrent();

        fOwner->fLink->StartMessage(AS_VIEW_POP_STATE);
        _FlushIfNotInTransaction();

        // invalidate all flags (except those that are not part of pop/push)
        fState->valid_flags = B_VIEW_VIEW_COLOR_BIT;
}


void
BView::SetOrigin(BPoint where)
{
        SetOrigin(where.x, where.y);
}


void
BView::SetOrigin(float x, float y)
{
        if (fState->IsValid(B_VIEW_ORIGIN_BIT)
                && x == fState->origin.x && y == fState->origin.y)
                return;

        fState->origin.x = x;
        fState->origin.y = y;

        if (_CheckOwnerLockAndSwitchCurrent()) {
                fOwner->fLink->StartMessage(AS_VIEW_SET_ORIGIN);
                fOwner->fLink->Attach<float>(x);
                fOwner->fLink->Attach<float>(y);

                fState->valid_flags |= B_VIEW_ORIGIN_BIT;
        }

        // our local coord system origin has changed, so when archiving we'll add
        // this too
        fState->archiving_flags |= B_VIEW_ORIGIN_BIT;
}


BPoint
BView::Origin() const
{
        if (!fState->IsValid(B_VIEW_ORIGIN_BIT)) {
                // we don't keep graphics state information, therefor
                // we need to ask the server for the origin after PopState()
                _CheckOwnerLockAndSwitchCurrent();

                fOwner->fLink->StartMessage(AS_VIEW_GET_ORIGIN);

                int32 code;
                if (fOwner->fLink->FlushWithReply(code) == B_OK && code == B_OK)
                        fOwner->fLink->Read<BPoint>(&fState->origin);

                fState->valid_flags |= B_VIEW_ORIGIN_BIT;
        }

        return fState->origin;
}


void
BView::SetScale(float scale) const
{
        if (fState->IsValid(B_VIEW_SCALE_BIT) && scale == fState->scale)
                return;

        if (fOwner) {
                _CheckLockAndSwitchCurrent();

                fOwner->fLink->StartMessage(AS_VIEW_SET_SCALE);
                fOwner->fLink->Attach<float>(scale);

                fState->valid_flags |= B_VIEW_SCALE_BIT;
        }

        fState->scale = scale;
        fState->archiving_flags |= B_VIEW_SCALE_BIT;
}


float
BView::Scale() const
{
        if (!fState->IsValid(B_VIEW_SCALE_BIT) && fOwner) {
                _CheckLockAndSwitchCurrent();

                fOwner->fLink->StartMessage(AS_VIEW_GET_SCALE);

                int32 code;
                if (fOwner->fLink->FlushWithReply(code) == B_OK && code == B_OK)
                        fOwner->fLink->Read<float>(&fState->scale);

                fState->valid_flags |= B_VIEW_SCALE_BIT;
        }

        return fState->scale;
}


void
BView::SetTransform(BAffineTransform transform)
{
        if (fState->IsValid(B_VIEW_TRANSFORM_BIT) && transform == fState->transform)
                return;

        if (fOwner != NULL) {
                _CheckLockAndSwitchCurrent();

                fOwner->fLink->StartMessage(AS_VIEW_SET_TRANSFORM);
                fOwner->fLink->AttachAffineTransform(transform);

                fState->valid_flags |= B_VIEW_TRANSFORM_BIT;
        }

        fState->transform = transform;
        fState->archiving_flags |= B_VIEW_TRANSFORM_BIT;
}


BAffineTransform
BView::Transform() const
{
        if (!fState->IsValid(B_VIEW_TRANSFORM_BIT) && fOwner != NULL) {
                _CheckLockAndSwitchCurrent();

                fOwner->fLink->StartMessage(AS_VIEW_GET_TRANSFORM);

                int32 code;
                if (fOwner->fLink->FlushWithReply(code) == B_OK && code == B_OK)
                        fOwner->fLink->ReadAffineTransform(&fState->transform);

                fState->valid_flags |= B_VIEW_TRANSFORM_BIT;
        }

        return fState->transform;
}


BAffineTransform
BView::TransformTo(coordinate_space basis) const
{
        if (basis == B_CURRENT_STATE_COORDINATES)
                return B_AFFINE_IDENTITY_TRANSFORM;

        if (!fState->IsValid(B_VIEW_PARENT_COMPOSITE_BIT) && fOwner != NULL) {
                _CheckLockAndSwitchCurrent();

                fOwner->fLink->StartMessage(AS_VIEW_GET_PARENT_COMPOSITE);

                int32 code;
                if (fOwner->fLink->FlushWithReply(code) == B_OK && code == B_OK) {
                        fOwner->fLink->ReadAffineTransform(&fState->parent_composite_transform);
                        fOwner->fLink->Read<float>(&fState->parent_composite_scale);
                        fOwner->fLink->Read<BPoint>(&fState->parent_composite_origin);
                }

                fState->valid_flags |= B_VIEW_PARENT_COMPOSITE_BIT;
        }

        BAffineTransform transform = fState->parent_composite_transform * Transform();
        float scale = fState->parent_composite_scale * Scale();
        transform.PreScaleBy(scale, scale);
        BPoint origin = Origin();
        origin.x *= fState->parent_composite_scale;
        origin.y *= fState->parent_composite_scale;
        origin += fState->parent_composite_origin;
        transform.TranslateBy(origin);

        if (basis == B_PREVIOUS_STATE_COORDINATES) {
                transform.TranslateBy(-fState->parent_composite_origin);
                transform.PreMultiplyInverse(fState->parent_composite_transform);
                transform.ScaleBy(1.0f / fState->parent_composite_scale);
                return transform;
        }

        if (basis == B_VIEW_COORDINATES)
                return transform;

        origin = B_ORIGIN;

        if (basis == B_PARENT_VIEW_COORDINATES || basis == B_PARENT_VIEW_DRAW_COORDINATES) {
                BView* parent = Parent();
                if (parent != NULL) {
                        ConvertToParent(&origin);
                        transform.TranslateBy(origin);
                        if (basis == B_PARENT_VIEW_DRAW_COORDINATES)
                                transform = transform.PreMultiplyInverse(parent->TransformTo(B_VIEW_COORDINATES));
                        return transform;
                }
                basis = B_WINDOW_COORDINATES;
        }

        ConvertToScreen(&origin);
        if (basis == B_WINDOW_COORDINATES) {
                BWindow* window = Window();
                if (window != NULL)
                        origin -= window->Frame().LeftTop();
        }
        transform.TranslateBy(origin);
        return transform;
}


void
BView::TranslateBy(double x, double y)
{
        if (fOwner != NULL) {
                _CheckLockAndSwitchCurrent();

                fOwner->fLink->StartMessage(AS_VIEW_AFFINE_TRANSLATE);
                fOwner->fLink->Attach<double>(x);
                fOwner->fLink->Attach<double>(y);

                fState->valid_flags &= ~B_VIEW_TRANSFORM_BIT;
        }

        fState->archiving_flags |= B_VIEW_TRANSFORM_BIT;
}


void
BView::ScaleBy(double x, double y)
{
        if (fOwner != NULL) {
                _CheckLockAndSwitchCurrent();

                fOwner->fLink->StartMessage(AS_VIEW_AFFINE_SCALE);
                fOwner->fLink->Attach<double>(x);
                fOwner->fLink->Attach<double>(y);

                fState->valid_flags &= ~B_VIEW_TRANSFORM_BIT;
        }

        fState->archiving_flags |= B_VIEW_TRANSFORM_BIT;
}


void
BView::RotateBy(double angleRadians)
{
        if (fOwner != NULL) {
                _CheckLockAndSwitchCurrent();

                fOwner->fLink->StartMessage(AS_VIEW_AFFINE_ROTATE);
                fOwner->fLink->Attach<double>(angleRadians);

                fState->valid_flags &= ~B_VIEW_TRANSFORM_BIT;
        }

        fState->archiving_flags |= B_VIEW_TRANSFORM_BIT;
}


void
BView::SetLineMode(cap_mode lineCap, join_mode lineJoin, float miterLimit)
{
        if (fState->IsValid(B_VIEW_LINE_MODES_BIT)
                && lineCap == fState->line_cap && lineJoin == fState->line_join
                && miterLimit == fState->miter_limit)
                return;

        if (fOwner) {
                _CheckLockAndSwitchCurrent();

                ViewSetLineModeInfo info;
                info.lineJoin = lineJoin;
                info.lineCap = lineCap;
                info.miterLimit = miterLimit;

                fOwner->fLink->StartMessage(AS_VIEW_SET_LINE_MODE);
                fOwner->fLink->Attach<ViewSetLineModeInfo>(info);

                fState->valid_flags |= B_VIEW_LINE_MODES_BIT;
        }

        fState->line_cap = lineCap;
        fState->line_join = lineJoin;
        fState->miter_limit = miterLimit;

        fState->archiving_flags |= B_VIEW_LINE_MODES_BIT;
}


join_mode
BView::LineJoinMode() const
{
        // This will update the current state, if necessary
        if (!fState->IsValid(B_VIEW_LINE_MODES_BIT))
                LineMiterLimit();

        return fState->line_join;
}


cap_mode
BView::LineCapMode() const
{
        // This will update the current state, if necessary
        if (!fState->IsValid(B_VIEW_LINE_MODES_BIT))
                LineMiterLimit();

        return fState->line_cap;
}


float
BView::LineMiterLimit() const
{
        if (!fState->IsValid(B_VIEW_LINE_MODES_BIT) && fOwner) {
                _CheckLockAndSwitchCurrent();

                fOwner->fLink->StartMessage(AS_VIEW_GET_LINE_MODE);

                int32 code;
                if (fOwner->fLink->FlushWithReply(code) == B_OK && code == B_OK) {

                        ViewSetLineModeInfo info;
                        fOwner->fLink->Read<ViewSetLineModeInfo>(&info);

                        fState->line_cap = info.lineCap;
                        fState->line_join = info.lineJoin;
                        fState->miter_limit = info.miterLimit;
                }

                fState->valid_flags |= B_VIEW_LINE_MODES_BIT;
        }

        return fState->miter_limit;
}


void
BView::SetFillRule(int32 fillRule)
{
        if (fState->IsValid(B_VIEW_FILL_RULE_BIT) && fillRule == fState->fill_rule)
                return;

        if (fOwner) {
                _CheckLockAndSwitchCurrent();

                fOwner->fLink->StartMessage(AS_VIEW_SET_FILL_RULE);
                fOwner->fLink->Attach<int32>(fillRule);

                fState->valid_flags |= B_VIEW_FILL_RULE_BIT;
        }

        fState->fill_rule = fillRule;

        fState->archiving_flags |= B_VIEW_FILL_RULE_BIT;
}


int32
BView::FillRule() const
{
        if (!fState->IsValid(B_VIEW_FILL_RULE_BIT) && fOwner) {
                _CheckLockAndSwitchCurrent();

                fOwner->fLink->StartMessage(AS_VIEW_GET_FILL_RULE);

                int32 code;
                if (fOwner->fLink->FlushWithReply(code) == B_OK && code == B_OK) {

                        int32 fillRule;
                        fOwner->fLink->Read<int32>(&fillRule);

                        fState->fill_rule = fillRule;
                }

                fState->valid_flags |= B_VIEW_FILL_RULE_BIT;
        }

        return fState->fill_rule;
}


void
BView::SetDrawingMode(drawing_mode mode)
{
        if (fState->IsValid(B_VIEW_DRAWING_MODE_BIT)
                && mode == fState->drawing_mode)
                return;

        if (fOwner) {
                _CheckLockAndSwitchCurrent();

                fOwner->fLink->StartMessage(AS_VIEW_SET_DRAWING_MODE);
                fOwner->fLink->Attach<int8>((int8)mode);

                fState->valid_flags |= B_VIEW_DRAWING_MODE_BIT;
        }

        fState->drawing_mode = mode;
        fState->archiving_flags |= B_VIEW_DRAWING_MODE_BIT;
}


drawing_mode
BView::DrawingMode() const
{
        if (!fState->IsValid(B_VIEW_DRAWING_MODE_BIT) && fOwner) {
                _CheckLockAndSwitchCurrent();

                fOwner->fLink->StartMessage(AS_VIEW_GET_DRAWING_MODE);

                int32 code;
                if (fOwner->fLink->FlushWithReply(code) == B_OK
                        && code == B_OK) {
                        int8 drawingMode;
                        fOwner->fLink->Read<int8>(&drawingMode);

                        fState->drawing_mode = (drawing_mode)drawingMode;
                        fState->valid_flags |= B_VIEW_DRAWING_MODE_BIT;
                }
        }

        return fState->drawing_mode;
}


void
BView::SetBlendingMode(source_alpha sourceAlpha, alpha_function alphaFunction)
{
        if (fState->IsValid(B_VIEW_BLENDING_BIT)
                && sourceAlpha == fState->alpha_source_mode
                && alphaFunction == fState->alpha_function_mode)
                return;

        if (fOwner) {
                _CheckLockAndSwitchCurrent();

                ViewBlendingModeInfo info;
                info.sourceAlpha = sourceAlpha;
                info.alphaFunction = alphaFunction;

                fOwner->fLink->StartMessage(AS_VIEW_SET_BLENDING_MODE);
                fOwner->fLink->Attach<ViewBlendingModeInfo>(info);

                fState->valid_flags |= B_VIEW_BLENDING_BIT;
        }

        fState->alpha_source_mode = sourceAlpha;
        fState->alpha_function_mode = alphaFunction;

        fState->archiving_flags |= B_VIEW_BLENDING_BIT;
}


void
BView::GetBlendingMode(source_alpha* _sourceAlpha,
        alpha_function* _alphaFunction) const
{
        if (!fState->IsValid(B_VIEW_BLENDING_BIT) && fOwner) {
                _CheckLockAndSwitchCurrent();

                fOwner->fLink->StartMessage(AS_VIEW_GET_BLENDING_MODE);

                int32 code;
                if (fOwner->fLink->FlushWithReply(code) == B_OK && code == B_OK) {
                        ViewBlendingModeInfo info;
                        fOwner->fLink->Read<ViewBlendingModeInfo>(&info);

                        fState->alpha_source_mode = info.sourceAlpha;
                        fState->alpha_function_mode = info.alphaFunction;

                        fState->valid_flags |= B_VIEW_BLENDING_BIT;
                }
        }

        if (_sourceAlpha)
                *_sourceAlpha = fState->alpha_source_mode;

        if (_alphaFunction)
                *_alphaFunction = fState->alpha_function_mode;
}


void
BView::MovePenTo(BPoint point)
{
        MovePenTo(point.x, point.y);
}


void
BView::MovePenTo(float x, float y)
{
        if (fState->IsValid(B_VIEW_PEN_LOCATION_BIT)
                && x == fState->pen_location.x && y == fState->pen_location.y)
                return;

        if (fOwner) {
                _CheckLockAndSwitchCurrent();

                fOwner->fLink->StartMessage(AS_VIEW_SET_PEN_LOC);
                fOwner->fLink->Attach<BPoint>(BPoint(x, y));

                fState->valid_flags |= B_VIEW_PEN_LOCATION_BIT;
        }

        fState->pen_location.x = x;
        fState->pen_location.y = y;

        fState->archiving_flags |= B_VIEW_PEN_LOCATION_BIT;
}


void
BView::MovePenBy(float x, float y)
{
        // this will update the pen location if necessary
        if (!fState->IsValid(B_VIEW_PEN_LOCATION_BIT))
                PenLocation();

        MovePenTo(fState->pen_location.x + x, fState->pen_location.y + y);
}


BPoint
BView::PenLocation() const
{
        if (!fState->IsValid(B_VIEW_PEN_LOCATION_BIT) && fOwner) {
                _CheckLockAndSwitchCurrent();

                fOwner->fLink->StartMessage(AS_VIEW_GET_PEN_LOC);

                int32 code;
                if (fOwner->fLink->FlushWithReply(code) == B_OK
                        && code == B_OK) {
                        fOwner->fLink->Read<BPoint>(&fState->pen_location);

                        fState->valid_flags |= B_VIEW_PEN_LOCATION_BIT;
                }
        }

        return fState->pen_location;
}


void
BView::SetPenSize(float size)
{
        if (fState->IsValid(B_VIEW_PEN_SIZE_BIT) && size == fState->pen_size)
                return;

        if (fOwner) {
                _CheckLockAndSwitchCurrent();

                fOwner->fLink->StartMessage(AS_VIEW_SET_PEN_SIZE);
                fOwner->fLink->Attach<float>(size);

                fState->valid_flags |= B_VIEW_PEN_SIZE_BIT;
        }

        fState->pen_size = size;
        fState->archiving_flags |= B_VIEW_PEN_SIZE_BIT;
}


float
BView::PenSize() const
{
        if (!fState->IsValid(B_VIEW_PEN_SIZE_BIT) && fOwner) {
                _CheckLockAndSwitchCurrent();

                fOwner->fLink->StartMessage(AS_VIEW_GET_PEN_SIZE);

                int32 code;
                if (fOwner->fLink->FlushWithReply(code) == B_OK
                        && code == B_OK) {
                        fOwner->fLink->Read<float>(&fState->pen_size);

                        fState->valid_flags |= B_VIEW_PEN_SIZE_BIT;
                }
        }

        return fState->pen_size;
}


void
BView::SetHighColor(rgb_color color)
{
        SetHighUIColor(B_NO_COLOR);

        // are we up-to-date already?
        if (fState->IsValid(B_VIEW_HIGH_COLOR_BIT)
                && fState->high_color == color)
                return;

        if (fOwner) {
                _CheckLockAndSwitchCurrent();

                fOwner->fLink->StartMessage(AS_VIEW_SET_HIGH_COLOR);
                fOwner->fLink->Attach<rgb_color>(color);

                fState->valid_flags |= B_VIEW_HIGH_COLOR_BIT;
        }

        fState->high_color = color;

        fState->archiving_flags |= B_VIEW_HIGH_COLOR_BIT;
}


rgb_color
BView::HighColor() const
{
        if (!fState->IsValid(B_VIEW_HIGH_COLOR_BIT) && fOwner) {
                _CheckLockAndSwitchCurrent();

                fOwner->fLink->StartMessage(AS_VIEW_GET_HIGH_COLOR);

                int32 code;
                if (fOwner->fLink->FlushWithReply(code) == B_OK
                        && code == B_OK) {
                        fOwner->fLink->Read<rgb_color>(&fState->high_color);

                        fState->valid_flags |= B_VIEW_HIGH_COLOR_BIT;
                }
        }

        return fState->high_color;
}


void
BView::SetHighUIColor(color_which which, float tint)
{
        if (fState->IsValid(B_VIEW_WHICH_HIGH_COLOR_BIT)
                && fState->which_high_color == which
                && fState->which_high_color_tint == tint)
                return;

        if (fOwner != NULL) {
                _CheckLockAndSwitchCurrent();

                fOwner->fLink->StartMessage(AS_VIEW_SET_HIGH_UI_COLOR);
                fOwner->fLink->Attach<color_which>(which);
                fOwner->fLink->Attach<float>(tint);

                fState->valid_flags |= B_VIEW_WHICH_HIGH_COLOR_BIT;
        }

        fState->which_high_color = which;
        fState->which_high_color_tint = tint;

        if (which != B_NO_COLOR) {
                fState->archiving_flags |= B_VIEW_WHICH_HIGH_COLOR_BIT;
                fState->archiving_flags &= ~B_VIEW_HIGH_COLOR_BIT;
                fState->valid_flags |= B_VIEW_HIGH_COLOR_BIT;

                fState->high_color = tint_color(ui_color(which), tint);
        } else {
                fState->valid_flags &= ~B_VIEW_HIGH_COLOR_BIT;
                fState->archiving_flags &= ~B_VIEW_WHICH_HIGH_COLOR_BIT;
        }
}


color_which
BView::HighUIColor(float* tint) const
{
        if (!fState->IsValid(B_VIEW_WHICH_HIGH_COLOR_BIT)
                && fOwner != NULL) {
                _CheckLockAndSwitchCurrent();

                fOwner->fLink->StartMessage(AS_VIEW_GET_HIGH_UI_COLOR);

                int32 code;
                if (fOwner->fLink->FlushWithReply(code) == B_OK
                        && code == B_OK) {
                        fOwner->fLink->Read<color_which>(&fState->which_high_color);
                        fOwner->fLink->Read<float>(&fState->which_high_color_tint);
                        fOwner->fLink->Read<rgb_color>(&fState->high_color);

                        fState->valid_flags |= B_VIEW_WHICH_HIGH_COLOR_BIT;
                        fState->valid_flags |= B_VIEW_HIGH_COLOR_BIT;
                }
        }

        if (tint != NULL)
                *tint = fState->which_high_color_tint;

        return fState->which_high_color;
}


void
BView::SetLowColor(rgb_color color)
{
        SetLowUIColor(B_NO_COLOR);

        if (fState->IsValid(B_VIEW_LOW_COLOR_BIT)
                && fState->low_color == color)
                return;

        if (fOwner) {
                _CheckLockAndSwitchCurrent();

                fOwner->fLink->StartMessage(AS_VIEW_SET_LOW_COLOR);
                fOwner->fLink->Attach<rgb_color>(color);

                fState->valid_flags |= B_VIEW_LOW_COLOR_BIT;
        }

        fState->low_color = color;

        fState->archiving_flags |= B_VIEW_LOW_COLOR_BIT;
}


rgb_color
BView::LowColor() const
{
        if (!fState->IsValid(B_VIEW_LOW_COLOR_BIT) && fOwner) {
                _CheckLockAndSwitchCurrent();

                fOwner->fLink->StartMessage(AS_VIEW_GET_LOW_COLOR);

                int32 code;
                if (fOwner->fLink->FlushWithReply(code) == B_OK
                        && code == B_OK) {
                        fOwner->fLink->Read<rgb_color>(&fState->low_color);

                        fState->valid_flags |= B_VIEW_LOW_COLOR_BIT;
                }
        }

        return fState->low_color;
}


void
BView::SetLowUIColor(color_which which, float tint)
{
        if (fState->IsValid(B_VIEW_WHICH_LOW_COLOR_BIT)
                && fState->which_low_color == which
                && fState->which_low_color_tint == tint)
                return;

        if (fOwner != NULL) {
                _CheckLockAndSwitchCurrent();

                fOwner->fLink->StartMessage(AS_VIEW_SET_LOW_UI_COLOR);
                fOwner->fLink->Attach<color_which>(which);
                fOwner->fLink->Attach<float>(tint);

                fState->valid_flags |= B_VIEW_WHICH_LOW_COLOR_BIT;
        }

        fState->which_low_color = which;
        fState->which_low_color_tint = tint;

        if (which != B_NO_COLOR) {
                fState->archiving_flags |= B_VIEW_WHICH_LOW_COLOR_BIT;
                fState->archiving_flags &= ~B_VIEW_LOW_COLOR_BIT;
                fState->valid_flags |= B_VIEW_LOW_COLOR_BIT;

                fState->low_color = tint_color(ui_color(which), tint);
        } else {
                fState->valid_flags &= ~B_VIEW_LOW_COLOR_BIT;
                fState->archiving_flags &= ~B_VIEW_WHICH_LOW_COLOR_BIT;
        }
}


color_which
BView::LowUIColor(float* tint) const
{
        if (!fState->IsValid(B_VIEW_WHICH_LOW_COLOR_BIT)
                && fOwner != NULL) {
                _CheckLockAndSwitchCurrent();

                fOwner->fLink->StartMessage(AS_VIEW_GET_LOW_UI_COLOR);

                int32 code;
                if (fOwner->fLink->FlushWithReply(code) == B_OK
                        && code == B_OK) {
                        fOwner->fLink->Read<color_which>(&fState->which_low_color);
                        fOwner->fLink->Read<float>(&fState->which_low_color_tint);
                        fOwner->fLink->Read<rgb_color>(&fState->low_color);

                        fState->valid_flags |= B_VIEW_WHICH_LOW_COLOR_BIT;
                        fState->valid_flags |= B_VIEW_LOW_COLOR_BIT;
                }
        }

        if (tint != NULL)
                *tint = fState->which_low_color_tint;

        return fState->which_low_color;
}


bool
BView::HasDefaultColors() const
{
        // If we don't have any of these flags, then we have default colors
        uint32 testMask = B_VIEW_VIEW_COLOR_BIT | B_VIEW_HIGH_COLOR_BIT
                | B_VIEW_LOW_COLOR_BIT | B_VIEW_WHICH_VIEW_COLOR_BIT
                | B_VIEW_WHICH_HIGH_COLOR_BIT | B_VIEW_WHICH_LOW_COLOR_BIT;

        return (fState->archiving_flags & testMask) == 0;
}


bool
BView::HasSystemColors() const
{
        return fState->which_view_color == B_PANEL_BACKGROUND_COLOR
                && fState->which_high_color == B_PANEL_TEXT_COLOR
                && fState->which_low_color == B_PANEL_BACKGROUND_COLOR
                && fState->which_view_color_tint == B_NO_TINT
                && fState->which_high_color_tint == B_NO_TINT
                && fState->which_low_color_tint == B_NO_TINT;
}


void
BView::AdoptParentColors()
{
        AdoptViewColors(Parent());
}


void
BView::AdoptSystemColors()
{
        SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
        SetLowUIColor(B_PANEL_BACKGROUND_COLOR);
        SetHighUIColor(B_PANEL_TEXT_COLOR);
}


void
BView::AdoptViewColors(BView* view)
{
        if (view == NULL || (view->Window() != NULL && !view->LockLooper()))
                return;

        float tint = B_NO_TINT;
        float viewTint = tint;
        color_which viewWhich = view->ViewUIColor(&viewTint);

        // View color
        if (viewWhich != B_NO_COLOR)
                SetViewUIColor(viewWhich, viewTint);
        else
                SetViewColor(view->ViewColor());

        // Low color
        color_which which = view->LowUIColor(&tint);
        if (which != B_NO_COLOR)
                SetLowUIColor(which, tint);
        else if (viewWhich != B_NO_COLOR)
                SetLowUIColor(viewWhich, viewTint);
        else
                SetLowColor(view->LowColor());

        // High color
        which = view->HighUIColor(&tint);
        if (which != B_NO_COLOR)
                SetHighUIColor(which, tint);
        else
                SetHighColor(view->HighColor());

        if (view->Window() != NULL)
                view->UnlockLooper();
}


void
BView::SetViewColor(rgb_color color)
{
        SetViewUIColor(B_NO_COLOR);

        if (fState->IsValid(B_VIEW_VIEW_COLOR_BIT)
                && fState->view_color == color)
                return;

        if (fOwner) {
                _CheckLockAndSwitchCurrent();

                fOwner->fLink->StartMessage(AS_VIEW_SET_VIEW_COLOR);
                fOwner->fLink->Attach<rgb_color>(color);
                fOwner->fLink->Flush();

                fState->valid_flags |= B_VIEW_VIEW_COLOR_BIT;
        }

        fState->view_color = color;

        fState->archiving_flags |= B_VIEW_VIEW_COLOR_BIT;
}


rgb_color
BView::ViewColor() const
{
        if (!fState->IsValid(B_VIEW_VIEW_COLOR_BIT) && fOwner) {
                _CheckLockAndSwitchCurrent();

                fOwner->fLink->StartMessage(AS_VIEW_GET_VIEW_COLOR);

                int32 code;
                if (fOwner->fLink->FlushWithReply(code) == B_OK
                        && code == B_OK) {
                        fOwner->fLink->Read<rgb_color>(&fState->view_color);

                        fState->valid_flags |= B_VIEW_VIEW_COLOR_BIT;
                }
        }

        return fState->view_color;
}


void
BView::SetViewUIColor(color_which which, float tint)
{
        if (fState->IsValid(B_VIEW_WHICH_VIEW_COLOR_BIT)
                && fState->which_view_color == which
                && fState->which_view_color_tint == tint)
                return;

        if (fOwner != NULL) {
                _CheckLockAndSwitchCurrent();

                fOwner->fLink->StartMessage(AS_VIEW_SET_VIEW_UI_COLOR);
                fOwner->fLink->Attach<color_which>(which);
                fOwner->fLink->Attach<float>(tint);

                fState->valid_flags |= B_VIEW_WHICH_VIEW_COLOR_BIT;
        }

        fState->which_view_color = which;
        fState->which_view_color_tint = tint;

        if (which != B_NO_COLOR) {
                fState->archiving_flags |= B_VIEW_WHICH_VIEW_COLOR_BIT;
                fState->archiving_flags &= ~B_VIEW_VIEW_COLOR_BIT;
                fState->valid_flags |= B_VIEW_VIEW_COLOR_BIT;

                fState->view_color = tint_color(ui_color(which), tint);
        } else {
                fState->valid_flags &= ~B_VIEW_VIEW_COLOR_BIT;
                fState->archiving_flags &= ~B_VIEW_WHICH_VIEW_COLOR_BIT;
        }

        if (!fState->IsValid(B_VIEW_WHICH_LOW_COLOR_BIT))
                SetLowUIColor(which, tint);
}


color_which
BView::ViewUIColor(float* tint) const
{
        if (!fState->IsValid(B_VIEW_WHICH_VIEW_COLOR_BIT)
                && fOwner != NULL) {
                _CheckLockAndSwitchCurrent();

                fOwner->fLink->StartMessage(AS_VIEW_GET_VIEW_UI_COLOR);

                int32 code;
                if (fOwner->fLink->FlushWithReply(code) == B_OK
                        && code == B_OK) {
                        fOwner->fLink->Read<color_which>(&fState->which_view_color);
                        fOwner->fLink->Read<float>(&fState->which_view_color_tint);
                        fOwner->fLink->Read<rgb_color>(&fState->view_color);

                        fState->valid_flags |= B_VIEW_WHICH_VIEW_COLOR_BIT;
                        fState->valid_flags |= B_VIEW_VIEW_COLOR_BIT;
                }
        }

        if (tint != NULL)
                *tint = fState->which_view_color_tint;

        return fState->which_view_color;
}


void
BView::ForceFontAliasing(bool enable)
{
        if (fState->IsValid(B_VIEW_FONT_ALIASING_BIT)
                && enable == fState->font_aliasing)
                return;

        if (fOwner) {
                _CheckLockAndSwitchCurrent();

                fOwner->fLink->StartMessage(AS_VIEW_PRINT_ALIASING);
                fOwner->fLink->Attach<bool>(enable);

                fState->valid_flags |= B_VIEW_FONT_ALIASING_BIT;
        }

        fState->font_aliasing = enable;
        fState->archiving_flags |= B_VIEW_FONT_ALIASING_BIT;
}


void
BView::SetFont(const BFont* font, uint32 mask)
{
        if (!font || mask == 0)
                return;

        if (mask == B_FONT_ALL) {
                fState->font = *font;
        } else {
                // TODO: move this into a BFont method
                if (mask & B_FONT_FAMILY_AND_STYLE)
                        fState->font.SetFamilyAndStyle(font->FamilyAndStyle());

                if (mask & B_FONT_SIZE)
                        fState->font.SetSize(font->Size());

                if (mask & B_FONT_SHEAR)
                        fState->font.SetShear(font->Shear());

                if (mask & B_FONT_ROTATION)
                        fState->font.SetRotation(font->Rotation());

                if (mask & B_FONT_FALSE_BOLD_WIDTH)
                        fState->font.SetFalseBoldWidth(font->FalseBoldWidth());

                if (mask & B_FONT_SPACING)
                        fState->font.SetSpacing(font->Spacing());

                if (mask & B_FONT_ENCODING)
                        fState->font.SetEncoding(font->Encoding());

                if (mask & B_FONT_FACE)
                        fState->font.SetFace(font->Face());

                if (mask & B_FONT_FLAGS)
                        fState->font.SetFlags(font->Flags());
        }

        fState->font_flags |= mask;

        if (fOwner) {
                _CheckLockAndSwitchCurrent();

                fState->UpdateServerFontState(*fOwner->fLink);
                fState->valid_flags |= B_VIEW_FONT_BIT;
        }

        fState->archiving_flags |= B_VIEW_FONT_BIT;
        // TODO: InvalidateLayout() here for convenience?
}


void
BView::GetFont(BFont* font) const
{
        if (!fState->IsValid(B_VIEW_FONT_BIT)) {
                // we don't keep graphics state information, therefor
                // we need to ask the server for the origin after PopState()
                _CheckOwnerLockAndSwitchCurrent();

                // TODO: add a font getter!
                fState->UpdateFrom(*fOwner->fLink);
        }

        *font = fState->font;
}


void
BView::GetFontHeight(font_height* height) const
{
        fState->font.GetHeight(height);
}


void
BView::SetFontSize(float size)
{
        BFont font;
        font.SetSize(size);

        SetFont(&font, B_FONT_SIZE);
}


float
BView::StringWidth(const char* string) const
{
        return fState->font.StringWidth(string);
}


float
BView::StringWidth(const char* string, int32 length) const
{
        return fState->font.StringWidth(string, length);
}


void
BView::GetStringWidths(char* stringArray[], int32 lengthArray[],
        int32 numStrings, float widthArray[]) const
{
        fState->font.GetStringWidths(const_cast<const char**>(stringArray),
                const_cast<const int32*>(lengthArray), numStrings, widthArray);
}


void
BView::TruncateString(BString* string, uint32 mode, float width) const
{
        fState->font.TruncateString(string, mode, width);
}


void
BView::ClipToPicture(BPicture* picture, BPoint where, bool sync)
{
        _ClipToPicture(picture, where, false, sync);
}


void
BView::ClipToInversePicture(BPicture* picture, BPoint where, bool sync)
{
        _ClipToPicture(picture, where, true, sync);
}


void
BView::GetClippingRegion(BRegion* region) const
{
        if (!region)
                return;

        // NOTE: the client has no idea when the clipping in the server
        // changed, so it is always read from the server
        region->MakeEmpty();


        if (fOwner) {
                if (fIsPrinting && _CheckOwnerLock()) {
                        region->Set(fState->print_rect);
                        return;
                }

                _CheckLockAndSwitchCurrent();
                fOwner->fLink->StartMessage(AS_VIEW_GET_CLIP_REGION);

                int32 code;
                if (fOwner->fLink->FlushWithReply(code) == B_OK
                        && code == B_OK) {
                        fOwner->fLink->ReadRegion(region);
                        fState->valid_flags |= B_VIEW_CLIP_REGION_BIT;
                }
        }
}


void
BView::ConstrainClippingRegion(BRegion* region)
{
        if (_CheckOwnerLockAndSwitchCurrent()) {
                fOwner->fLink->StartMessage(AS_VIEW_SET_CLIP_REGION);

                fOwner->fLink->Attach<bool>(region != NULL);
                if (region != NULL)
                        fOwner->fLink->AttachRegion(*region);

                _FlushIfNotInTransaction();

                fState->valid_flags &= ~B_VIEW_CLIP_REGION_BIT;
                fState->archiving_flags |= B_VIEW_CLIP_REGION_BIT;
        }
}


void
BView::ClipToRect(BRect rect)
{
        _ClipToRect(rect, false);
}


void
BView::ClipToInverseRect(BRect rect)
{
        _ClipToRect(rect, true);
}


void
BView::ClipToShape(BShape* shape)
{
        _ClipToShape(shape, false);
}


void
BView::ClipToInverseShape(BShape* shape)
{
        _ClipToShape(shape, true);
}


//      #pragma mark - Drawing Functions


void
BView::DrawBitmapAsync(const BBitmap* bitmap, BRect bitmapRect, BRect viewRect,
        uint32 options)
{
        if (bitmap == NULL || fOwner == NULL
                || !bitmapRect.IsValid() || !viewRect.IsValid())
                return;

        _CheckLockAndSwitchCurrent();

        ViewDrawBitmapInfo info;
        info.bitmapToken = bitmap->_ServerToken();
        info.options = options;
        info.viewRect = viewRect;
        info.bitmapRect = bitmapRect;

        fOwner->fLink->StartMessage(AS_VIEW_DRAW_BITMAP);
        fOwner->fLink->Attach<ViewDrawBitmapInfo>(info);

        _FlushIfNotInTransaction();
}


void
BView::DrawBitmapAsync(const BBitmap* bitmap, BRect bitmapRect, BRect viewRect)
{
        DrawBitmapAsync(bitmap, bitmapRect, viewRect, 0);
}


void
BView::DrawBitmapAsync(const BBitmap* bitmap, BRect viewRect)
{
        if (bitmap && fOwner) {
                DrawBitmapAsync(bitmap, bitmap->Bounds().OffsetToCopy(B_ORIGIN),
                        viewRect, 0);
        }
}


void
BView::DrawBitmapAsync(const BBitmap* bitmap, BPoint where)
{
        if (bitmap == NULL || fOwner == NULL)
                return;

        _CheckLockAndSwitchCurrent();

        ViewDrawBitmapInfo info;
        info.bitmapToken = bitmap->_ServerToken();
        info.options = 0;
        info.bitmapRect = bitmap->Bounds().OffsetToCopy(B_ORIGIN);
        info.viewRect = info.bitmapRect.OffsetToCopy(where);

        fOwner->fLink->StartMessage(AS_VIEW_DRAW_BITMAP);
        fOwner->fLink->Attach<ViewDrawBitmapInfo>(info);

        _FlushIfNotInTransaction();
}


void
BView::DrawBitmapAsync(const BBitmap* bitmap)
{
        DrawBitmapAsync(bitmap, PenLocation());
}


void
BView::DrawBitmap(const BBitmap* bitmap, BRect bitmapRect, BRect viewRect,
        uint32 options)
{
        if (fOwner) {
                DrawBitmapAsync(bitmap, bitmapRect, viewRect, options);
                Sync();
        }
}


void
BView::DrawBitmap(const BBitmap* bitmap, BRect bitmapRect, BRect viewRect)
{
        if (fOwner) {
                DrawBitmapAsync(bitmap, bitmapRect, viewRect, 0);
                Sync();
        }
}


void
BView::DrawBitmap(const BBitmap* bitmap, BRect viewRect)
{
        if (bitmap && fOwner) {
                DrawBitmap(bitmap, bitmap->Bounds().OffsetToCopy(B_ORIGIN), viewRect,
                        0);
        }
}


void
BView::DrawBitmap(const BBitmap* bitmap, BPoint where)
{
        if (fOwner) {
                DrawBitmapAsync(bitmap, where);
                Sync();
        }
}


void
BView::DrawBitmap(const BBitmap* bitmap)
{
        DrawBitmap(bitmap, PenLocation());
}


void
BView::DrawTiledBitmapAsync(const BBitmap* bitmap, BRect viewRect,
        BPoint phase)
{
        if (bitmap == NULL || fOwner == NULL || !viewRect.IsValid())
                return;

        _CheckLockAndSwitchCurrent();

        ViewDrawBitmapInfo info;
        info.bitmapToken = bitmap->_ServerToken();
        info.options = B_TILE_BITMAP;
        info.viewRect = viewRect;
        info.bitmapRect = bitmap->Bounds().OffsetToCopy(phase);

        fOwner->fLink->StartMessage(AS_VIEW_DRAW_BITMAP);
        fOwner->fLink->Attach<ViewDrawBitmapInfo>(info);

        _FlushIfNotInTransaction();
}


void
BView::DrawTiledBitmap(const BBitmap* bitmap, BRect viewRect, BPoint phase)
{
        if (fOwner) {
                DrawTiledBitmapAsync(bitmap, viewRect, phase);
                Sync();
        }
}


void
BView::DrawChar(char c)
{
        DrawString(&c, 1, PenLocation());
}


void
BView::DrawChar(char c, BPoint location)
{
        DrawString(&c, 1, location);
}


void
BView::DrawString(const char* string, escapement_delta* delta)
{
        if (string == NULL)
                return;

        DrawString(string, strlen(string), PenLocation(), delta);
}


void
BView::DrawString(const char* string, BPoint location, escapement_delta* delta)
{
        if (string == NULL)
                return;

        DrawString(string, strlen(string), location, delta);
}


void
BView::DrawString(const char* string, int32 length, escapement_delta* delta)
{
        DrawString(string, length, PenLocation(), delta);
}


void
BView::DrawString(const char* string, int32 length, BPoint location,
        escapement_delta* delta)
{
        if (fOwner == NULL || string == NULL || length < 1)
                return;

        _CheckLockAndSwitchCurrent();

        ViewDrawStringInfo info;
        info.stringLength = length;
        info.location = location;
        if (delta != NULL)
                info.delta = *delta;

        // quite often delta will be NULL
        if (delta)
                fOwner->fLink->StartMessage(AS_DRAW_STRING_WITH_DELTA);
        else
                fOwner->fLink->StartMessage(AS_DRAW_STRING);

        fOwner->fLink->Attach<ViewDrawStringInfo>(info);
        fOwner->fLink->Attach(string, length);

        _FlushIfNotInTransaction();

        // this modifies our pen location, so we invalidate the flag.
        fState->valid_flags &= ~B_VIEW_PEN_LOCATION_BIT;
}


void
BView::DrawString(const char* string, const BPoint* locations,
        int32 locationCount)
{
        if (string == NULL)
                return;

        DrawString(string, strlen(string), locations, locationCount);
}


void
BView::DrawString(const char* string, int32 length, const BPoint* locations,
        int32 locationCount)
{
        if (fOwner == NULL || string == NULL || length < 1 || locations == NULL)
                return;

        _CheckLockAndSwitchCurrent();

        fOwner->fLink->StartMessage(AS_DRAW_STRING_WITH_OFFSETS);

        fOwner->fLink->Attach<int32>(length);
        fOwner->fLink->Attach<int32>(locationCount);
        fOwner->fLink->Attach(string, length);
        fOwner->fLink->Attach(locations, locationCount * sizeof(BPoint));

        _FlushIfNotInTransaction();

        // this modifies our pen location, so we invalidate the flag.
        fState->valid_flags &= ~B_VIEW_PEN_LOCATION_BIT;
}


void
BView::StrokeEllipse(BPoint center, float xRadius, float yRadius,
        ::pattern pattern)
{
        StrokeEllipse(BRect(center.x - xRadius, center.y - yRadius,
                center.x + xRadius, center.y + yRadius), pattern);
}


void
BView::StrokeEllipse(BRect rect, ::pattern pattern)
{
        if (fOwner == NULL)
                return;

        _CheckLockAndSwitchCurrent();
        _UpdatePattern(pattern);

        fOwner->fLink->StartMessage(AS_STROKE_ELLIPSE);
        fOwner->fLink->Attach<BRect>(rect);

        _FlushIfNotInTransaction();
}


void
BView::StrokeEllipse(BPoint center, float xRadius, float yRadius,
        const BGradient& gradient)
{
        StrokeEllipse(BRect(center.x - xRadius, center.y - yRadius,
                center.x + xRadius, center.y + yRadius), gradient);
}


void
BView::StrokeEllipse(BRect rect, const BGradient& gradient)
{
        if (fOwner == NULL)
                return;

        _CheckLockAndSwitchCurrent();

        fOwner->fLink->StartMessage(AS_STROKE_ELLIPSE_GRADIENT);
        fOwner->fLink->Attach<BRect>(rect);
        fOwner->fLink->AttachGradient(gradient);

        _FlushIfNotInTransaction();
}


void
BView::FillEllipse(BPoint center, float xRadius, float yRadius,
        ::pattern pattern)
{
        FillEllipse(BRect(center.x - xRadius, center.y - yRadius,
                center.x + xRadius, center.y + yRadius), pattern);
}


void
BView::FillEllipse(BPoint center, float xRadius, float yRadius,
        const BGradient& gradient)
{
        FillEllipse(BRect(center.x - xRadius, center.y - yRadius,
                center.x + xRadius, center.y + yRadius), gradient);
}


void
BView::FillEllipse(BRect rect, ::pattern pattern)
{
        if (fOwner == NULL)
                return;

        _CheckLockAndSwitchCurrent();
        _UpdatePattern(pattern);

        fOwner->fLink->StartMessage(AS_FILL_ELLIPSE);
        fOwner->fLink->Attach<BRect>(rect);

        _FlushIfNotInTransaction();
}


void
BView::FillEllipse(BRect rect, const BGradient& gradient)
{
        if (fOwner == NULL)
                return;

        _CheckLockAndSwitchCurrent();

        fOwner->fLink->StartMessage(AS_FILL_ELLIPSE_GRADIENT);
        fOwner->fLink->Attach<BRect>(rect);
        fOwner->fLink->AttachGradient(gradient);

        _FlushIfNotInTransaction();
}


void
BView::StrokeArc(BPoint center, float xRadius, float yRadius, float startAngle,
        float arcAngle, ::pattern pattern)
{
        StrokeArc(BRect(center.x - xRadius, center.y - yRadius, center.x + xRadius,
                center.y + yRadius), startAngle, arcAngle, pattern);
}


void
BView::StrokeArc(BRect rect, float startAngle, float arcAngle,
        ::pattern pattern)
{
        if (fOwner == NULL)
                return;

        _CheckLockAndSwitchCurrent();
        _UpdatePattern(pattern);

        fOwner->fLink->StartMessage(AS_STROKE_ARC);
        fOwner->fLink->Attach<BRect>(rect);
        fOwner->fLink->Attach<float>(startAngle);
        fOwner->fLink->Attach<float>(arcAngle);

        _FlushIfNotInTransaction();
}


void
BView::StrokeArc(BPoint center, float xRadius, float yRadius, float startAngle,
        float arcAngle, const BGradient& gradient)
{
        StrokeArc(BRect(center.x - xRadius, center.y - yRadius, center.x + xRadius,
                center.y + yRadius), startAngle, arcAngle, gradient);
}


void
BView::StrokeArc(BRect rect, float startAngle, float arcAngle,
        const BGradient& gradient)
{
        if (fOwner == NULL)
                return;

        _CheckLockAndSwitchCurrent();

        fOwner->fLink->StartMessage(AS_STROKE_ARC_GRADIENT);
        fOwner->fLink->Attach<BRect>(rect);
        fOwner->fLink->Attach<float>(startAngle);
        fOwner->fLink->Attach<float>(arcAngle);
        fOwner->fLink->AttachGradient(gradient);

        _FlushIfNotInTransaction();
}


void
BView::FillArc(BPoint center,float xRadius, float yRadius, float startAngle,
        float arcAngle, ::pattern pattern)
{
        FillArc(BRect(center.x - xRadius, center.y - yRadius, center.x + xRadius,
                center.y + yRadius), startAngle, arcAngle, pattern);
}


void
BView::FillArc(BPoint center,float xRadius, float yRadius, float startAngle,
        float arcAngle, const BGradient& gradient)
{
        FillArc(BRect(center.x - xRadius, center.y - yRadius, center.x + xRadius,
                center.y + yRadius), startAngle, arcAngle, gradient);
}


void
BView::FillArc(BRect rect, float startAngle, float arcAngle,
        ::pattern pattern)
{
        if (fOwner == NULL)
                return;

        _CheckLockAndSwitchCurrent();
        _UpdatePattern(pattern);

        fOwner->fLink->StartMessage(AS_FILL_ARC);
        fOwner->fLink->Attach<BRect>(rect);
        fOwner->fLink->Attach<float>(startAngle);
        fOwner->fLink->Attach<float>(arcAngle);

        _FlushIfNotInTransaction();
}


void
BView::FillArc(BRect rect, float startAngle, float arcAngle,
        const BGradient& gradient)
{
        if (fOwner == NULL)
                return;

        _CheckLockAndSwitchCurrent();

        fOwner->fLink->StartMessage(AS_FILL_ARC_GRADIENT);
        fOwner->fLink->Attach<BRect>(rect);
        fOwner->fLink->Attach<float>(startAngle);
        fOwner->fLink->Attach<float>(arcAngle);
        fOwner->fLink->AttachGradient(gradient);

        _FlushIfNotInTransaction();
}


void
BView::StrokeBezier(BPoint* controlPoints, ::pattern pattern)
{
        if (fOwner == NULL)
                return;

        _CheckLockAndSwitchCurrent();
        _UpdatePattern(pattern);

        fOwner->fLink->StartMessage(AS_STROKE_BEZIER);
        fOwner->fLink->Attach<BPoint>(controlPoints[0]);
        fOwner->fLink->Attach<BPoint>(controlPoints[1]);
        fOwner->fLink->Attach<BPoint>(controlPoints[2]);
        fOwner->fLink->Attach<BPoint>(controlPoints[3]);

        _FlushIfNotInTransaction();
}


void
BView::StrokeBezier(BPoint* controlPoints, const BGradient& gradient)
{
        if (fOwner == NULL)
                return;

        _CheckLockAndSwitchCurrent();

        fOwner->fLink->StartMessage(AS_STROKE_BEZIER_GRADIENT);
        fOwner->fLink->Attach<BPoint>(controlPoints[0]);
        fOwner->fLink->Attach<BPoint>(controlPoints[1]);
        fOwner->fLink->Attach<BPoint>(controlPoints[2]);
        fOwner->fLink->Attach<BPoint>(controlPoints[3]);
        fOwner->fLink->AttachGradient(gradient);

        _FlushIfNotInTransaction();
}


void
BView::FillBezier(BPoint* controlPoints, ::pattern pattern)
{
        if (fOwner == NULL)
                return;

        _CheckLockAndSwitchCurrent();
        _UpdatePattern(pattern);

        fOwner->fLink->StartMessage(AS_FILL_BEZIER);
        fOwner->fLink->Attach<BPoint>(controlPoints[0]);
        fOwner->fLink->Attach<BPoint>(controlPoints[1]);
        fOwner->fLink->Attach<BPoint>(controlPoints[2]);
        fOwner->fLink->Attach<BPoint>(controlPoints[3]);

        _FlushIfNotInTransaction();
}


void
BView::FillBezier(BPoint* controlPoints, const BGradient& gradient)
{
        if (fOwner == NULL)
                return;

        _CheckLockAndSwitchCurrent();

        fOwner->fLink->StartMessage(AS_FILL_BEZIER_GRADIENT);
        fOwner->fLink->Attach<BPoint>(controlPoints[0]);
        fOwner->fLink->Attach<BPoint>(controlPoints[1]);
        fOwner->fLink->Attach<BPoint>(controlPoints[2]);
        fOwner->fLink->Attach<BPoint>(controlPoints[3]);
        fOwner->fLink->AttachGradient(gradient);

        _FlushIfNotInTransaction();
}


void
BView::StrokePolygon(const BPolygon* polygon, bool closed, ::pattern pattern)
{
        if (polygon == NULL)
                return;

        StrokePolygon(polygon->fPoints, polygon->fCount, polygon->Frame(), closed,
                pattern);
}


void
BView::StrokePolygon(const BPoint* pointArray, int32 numPoints, bool closed,
        ::pattern pattern)
{
        if (pointArray == NULL
                || numPoints <= 1
                || fOwner == NULL)
                return;

        BRect bounds = BPolygon::_ComputeBounds(pointArray, numPoints);

        _CheckLockAndSwitchCurrent();
        _UpdatePattern(pattern);

        if (fOwner->fLink->StartMessage(AS_STROKE_POLYGON,
                        numPoints * sizeof(BPoint) + sizeof(BRect) + sizeof(bool)
                                + sizeof(int32)) == B_OK) {
                fOwner->fLink->Attach<BRect>(bounds);
                fOwner->fLink->Attach<bool>(closed);
                fOwner->fLink->Attach<int32>(numPoints);
                fOwner->fLink->Attach(pointArray, numPoints * sizeof(BPoint));

                _FlushIfNotInTransaction();
        } else {
                fprintf(stderr, "ERROR: Can't send polygon to app_server!\n");
        }
}


void
BView::StrokePolygon(const BPoint* pointArray, int32 numPoints, BRect bounds,
        bool closed, ::pattern pattern)
{
        if (pointArray == NULL
                || numPoints <= 1
                || fOwner == NULL)
                return;

        _CheckLockAndSwitchCurrent();
        _UpdatePattern(pattern);

        BPolygon polygon(pointArray, numPoints);
        polygon.MapTo(polygon.Frame(), bounds);

        if (fOwner->fLink->StartMessage(AS_STROKE_POLYGON,
                        polygon.fCount * sizeof(BPoint) + sizeof(BRect) + sizeof(bool)
                                + sizeof(int32)) == B_OK) {
                fOwner->fLink->Attach<BRect>(polygon.Frame());
                fOwner->fLink->Attach<bool>(closed);
                fOwner->fLink->Attach<int32>(polygon.fCount);
                fOwner->fLink->Attach(polygon.fPoints, polygon.fCount * sizeof(BPoint));

                _FlushIfNotInTransaction();
        } else {
                fprintf(stderr, "ERROR: Can't send polygon to app_server!\n");
        }
}


void
BView::StrokePolygon(const BPolygon* polygon, bool closed, const BGradient& gradient)
{
        if (polygon == NULL)
                return;

        StrokePolygon(polygon->fPoints, polygon->fCount, polygon->Frame(), closed,
                gradient);
}


void
BView::StrokePolygon(const BPoint* pointArray, int32 numPoints, bool closed,
        const BGradient& gradient)
{
        if (pointArray == NULL
                || numPoints <= 1
                || fOwner == NULL)
                return;

        BRect bounds = BPolygon::_ComputeBounds(pointArray, numPoints);

        _CheckLockAndSwitchCurrent();

        if (fOwner->fLink->StartMessage(AS_STROKE_POLYGON_GRADIENT,
                        numPoints * sizeof(BPoint) + sizeof(BRect) + sizeof(bool)
                                + sizeof(int32)) == B_OK) {
                fOwner->fLink->Attach<BRect>(bounds);
                fOwner->fLink->Attach<bool>(closed);
                fOwner->fLink->Attach<int32>(numPoints);
                fOwner->fLink->Attach(pointArray, numPoints * sizeof(BPoint));
                fOwner->fLink->AttachGradient(gradient);

                _FlushIfNotInTransaction();
        } else {
                fprintf(stderr, "ERROR: Can't send polygon to app_server!\n");
        }
}


void
BView::StrokePolygon(const BPoint* pointArray, int32 numPoints, BRect bounds,
        bool closed, const BGradient& gradient)
{
        if (pointArray == NULL
                || numPoints <= 1
                || fOwner == NULL)
                return;

        _CheckLockAndSwitchCurrent();

        BPolygon polygon(pointArray, numPoints);
        polygon.MapTo(polygon.Frame(), bounds);

        if (fOwner->fLink->StartMessage(AS_STROKE_POLYGON_GRADIENT,
                        polygon.fCount * sizeof(BPoint) + sizeof(BRect) + sizeof(bool)
                                + sizeof(int32)) == B_OK) {
                fOwner->fLink->Attach<BRect>(polygon.Frame());
                fOwner->fLink->Attach<bool>(closed);
                fOwner->fLink->Attach<int32>(polygon.fCount);
                fOwner->fLink->Attach(polygon.fPoints, polygon.fCount * sizeof(BPoint));
                fOwner->fLink->AttachGradient(gradient);

                _FlushIfNotInTransaction();
        } else {
                fprintf(stderr, "ERROR: Can't send polygon to app_server!\n");
        }
}


void
BView::FillPolygon(const BPolygon* polygon, ::pattern pattern)
{
        if (polygon == NULL
                || polygon->fCount <= 2
                || fOwner == NULL)
                return;

        _CheckLockAndSwitchCurrent();
        _UpdatePattern(pattern);

        if (fOwner->fLink->StartMessage(AS_FILL_POLYGON,
                        polygon->fCount * sizeof(BPoint) + sizeof(BRect) + sizeof(int32))
                                == B_OK) {
                fOwner->fLink->Attach<BRect>(polygon->Frame());
                fOwner->fLink->Attach<int32>(polygon->fCount);
                fOwner->fLink->Attach(polygon->fPoints,
                        polygon->fCount * sizeof(BPoint));

                _FlushIfNotInTransaction();
        } else {
                fprintf(stderr, "ERROR: Can't send polygon to app_server!\n");
        }
}


void
BView::FillPolygon(const BPolygon* polygon, const BGradient& gradient)
{
        if (polygon == NULL
                || polygon->fCount <= 2
                || fOwner == NULL)
                return;

        _CheckLockAndSwitchCurrent();

        if (fOwner->fLink->StartMessage(AS_FILL_POLYGON_GRADIENT,
                        polygon->fCount * sizeof(BPoint) + sizeof(BRect) + sizeof(int32))
                                == B_OK) {
                fOwner->fLink->Attach<BRect>(polygon->Frame());
                fOwner->fLink->Attach<int32>(polygon->fCount);
                fOwner->fLink->Attach(polygon->fPoints,
                        polygon->fCount * sizeof(BPoint));
                fOwner->fLink->AttachGradient(gradient);

                _FlushIfNotInTransaction();
        } else {
                fprintf(stderr, "ERROR: Can't send polygon to app_server!\n");
        }
}


void
BView::FillPolygon(const BPoint* pointArray, int32 numPoints, ::pattern pattern)
{
        if (pointArray == NULL
                || numPoints <= 2
                || fOwner == NULL)
                return;

        BRect bounds = BPolygon::_ComputeBounds(pointArray, numPoints);

        _CheckLockAndSwitchCurrent();
        _UpdatePattern(pattern);

        if (fOwner->fLink->StartMessage(AS_FILL_POLYGON,
                        numPoints * sizeof(BPoint) + sizeof(BRect) + sizeof(int32))
                                == B_OK) {
                fOwner->fLink->Attach<BRect>(bounds);
                fOwner->fLink->Attach<int32>(numPoints);
                fOwner->fLink->Attach(pointArray, numPoints * sizeof(BPoint));

                _FlushIfNotInTransaction();
        } else {
                fprintf(stderr, "ERROR: Can't send polygon to app_server!\n");
        }
}


void
BView::FillPolygon(const BPoint* pointArray, int32 numPoints,
        const BGradient& gradient)
{
        if (pointArray == NULL
                || numPoints <= 2
                || fOwner == NULL)
                return;

        BRect bounds = BPolygon::_ComputeBounds(pointArray, numPoints);

        _CheckLockAndSwitchCurrent();

        if (fOwner->fLink->StartMessage(AS_FILL_POLYGON_GRADIENT,
                        numPoints * sizeof(BPoint) + sizeof(BRect) + sizeof(int32))
                                == B_OK) {
                fOwner->fLink->Attach<BRect>(bounds);
                fOwner->fLink->Attach<int32>(numPoints);
                fOwner->fLink->Attach(pointArray, numPoints * sizeof(BPoint));
                fOwner->fLink->AttachGradient(gradient);

                _FlushIfNotInTransaction();
        } else {
                fprintf(stderr, "ERROR: Can't send polygon to app_server!\n");
        }
}


void
BView::FillPolygon(const BPoint* pointArray, int32 numPoints, BRect bounds,
        ::pattern pattern)
{
        if (pointArray == NULL)
                return;

        BPolygon polygon(pointArray, numPoints);

        polygon.MapTo(polygon.Frame(), bounds);
        FillPolygon(&polygon, pattern);
}


void
BView::FillPolygon(const BPoint* pointArray, int32 numPoints, BRect bounds,
        const BGradient& gradient)
{
        if (pointArray == NULL)
                return;

        BPolygon polygon(pointArray, numPoints);

        polygon.MapTo(polygon.Frame(), bounds);
        FillPolygon(&polygon, gradient);
}


void
BView::StrokeRect(BRect rect, ::pattern pattern)
{
        if (fOwner == NULL)
                return;

        _CheckLockAndSwitchCurrent();
        _UpdatePattern(pattern);

        fOwner->fLink->StartMessage(AS_STROKE_RECT);
        fOwner->fLink->Attach<BRect>(rect);

        _FlushIfNotInTransaction();
}


void
BView::StrokeRect(BRect rect, const BGradient& gradient)
{
        if (fOwner == NULL)
                return;

        _CheckLockAndSwitchCurrent();

        fOwner->fLink->StartMessage(AS_STROKE_RECT_GRADIENT);
        fOwner->fLink->Attach<BRect>(rect);
        fOwner->fLink->AttachGradient(gradient);

        _FlushIfNotInTransaction();
}


void
BView::FillRect(BRect rect, ::pattern pattern)
{
        if (fOwner == NULL)
                return;

        // NOTE: ensuring compatibility with R5,
        // invalid rects are not filled, they are stroked though!
        if (!rect.IsValid())
                return;

        _CheckLockAndSwitchCurrent();
        _UpdatePattern(pattern);

        fOwner->fLink->StartMessage(AS_FILL_RECT);
        fOwner->fLink->Attach<BRect>(rect);

        _FlushIfNotInTransaction();
}


void
BView::FillRect(BRect rect, const BGradient& gradient)
{
        if (fOwner == NULL)
                return;

        // NOTE: ensuring compatibility with R5,
        // invalid rects are not filled, they are stroked though!
        if (!rect.IsValid())
                return;

        _CheckLockAndSwitchCurrent();

        fOwner->fLink->StartMessage(AS_FILL_RECT_GRADIENT);
        fOwner->fLink->Attach<BRect>(rect);
        fOwner->fLink->AttachGradient(gradient);

        _FlushIfNotInTransaction();
}


void
BView::StrokeRoundRect(BRect rect, float xRadius, float yRadius,
        ::pattern pattern)
{
        if (fOwner == NULL)
                return;

        _CheckLockAndSwitchCurrent();
        _UpdatePattern(pattern);

        fOwner->fLink->StartMessage(AS_STROKE_ROUNDRECT);
        fOwner->fLink->Attach<BRect>(rect);
        fOwner->fLink->Attach<float>(xRadius);
        fOwner->fLink->Attach<float>(yRadius);

        _FlushIfNotInTransaction();
}


void
BView::StrokeRoundRect(BRect rect, float xRadius, float yRadius,
        const BGradient& gradient)
{
        if (fOwner == NULL)
                return;

        _CheckLockAndSwitchCurrent();

        fOwner->fLink->StartMessage(AS_STROKE_ROUNDRECT_GRADIENT);
        fOwner->fLink->Attach<BRect>(rect);
        fOwner->fLink->Attach<float>(xRadius);
        fOwner->fLink->Attach<float>(yRadius);
        fOwner->fLink->AttachGradient(gradient);

        _FlushIfNotInTransaction();
}


void
BView::FillRoundRect(BRect rect, float xRadius, float yRadius,
        ::pattern pattern)
{
        if (fOwner == NULL)
                return;

        _CheckLockAndSwitchCurrent();

        _UpdatePattern(pattern);

        fOwner->fLink->StartMessage(AS_FILL_ROUNDRECT);
        fOwner->fLink->Attach<BRect>(rect);
        fOwner->fLink->Attach<float>(xRadius);
        fOwner->fLink->Attach<float>(yRadius);

        _FlushIfNotInTransaction();
}


void
BView::FillRoundRect(BRect rect, float xRadius, float yRadius,
        const BGradient& gradient)
{
        if (fOwner == NULL)
                return;

        _CheckLockAndSwitchCurrent();

        fOwner->fLink->StartMessage(AS_FILL_ROUNDRECT_GRADIENT);
        fOwner->fLink->Attach<BRect>(rect);
        fOwner->fLink->Attach<float>(xRadius);
        fOwner->fLink->Attach<float>(yRadius);
        fOwner->fLink->AttachGradient(gradient);

        _FlushIfNotInTransaction();
}


void
BView::FillRegion(BRegion* region, ::pattern pattern)
{
        if (region == NULL || fOwner == NULL)
                return;

        _CheckLockAndSwitchCurrent();

        _UpdatePattern(pattern);

        fOwner->fLink->StartMessage(AS_FILL_REGION);
        fOwner->fLink->AttachRegion(*region);

        _FlushIfNotInTransaction();
}


void
BView::FillRegion(BRegion* region, const BGradient& gradient)
{
        if (region == NULL || fOwner == NULL)
                return;

        _CheckLockAndSwitchCurrent();

        fOwner->fLink->StartMessage(AS_FILL_REGION_GRADIENT);
        fOwner->fLink->AttachRegion(*region);
        fOwner->fLink->AttachGradient(gradient);

        _FlushIfNotInTransaction();
}


void
BView::StrokeTriangle(BPoint point1, BPoint point2, BPoint point3, BRect bounds,
        ::pattern pattern)
{
        if (fOwner == NULL)
                return;

        _CheckLockAndSwitchCurrent();

        _UpdatePattern(pattern);

        fOwner->fLink->StartMessage(AS_STROKE_TRIANGLE);
        fOwner->fLink->Attach<BPoint>(point1);
        fOwner->fLink->Attach<BPoint>(point2);
        fOwner->fLink->Attach<BPoint>(point3);
        fOwner->fLink->Attach<BRect>(bounds);

        _FlushIfNotInTransaction();
}


void
BView::StrokeTriangle(BPoint point1, BPoint point2, BPoint point3,
        ::pattern pattern)
{
        if (fOwner) {
                // we construct the smallest rectangle that contains the 3 points
                // for the 1st point
                BRect bounds(point1, point1);

                // for the 2nd point
                if (point2.x < bounds.left)
                        bounds.left = point2.x;

                if (point2.y < bounds.top)
                        bounds.top = point2.y;

                if (point2.x > bounds.right)
                        bounds.right = point2.x;

                if (point2.y > bounds.bottom)
                        bounds.bottom = point2.y;

                // for the 3rd point
                if (point3.x < bounds.left)
                        bounds.left = point3.x;

                if (point3.y < bounds.top)
                        bounds.top = point3.y;

                if (point3.x > bounds.right)
                        bounds.right = point3.x;

                if (point3.y > bounds.bottom)
                        bounds.bottom = point3.y;

                StrokeTriangle(point1, point2, point3, bounds, pattern);
        }
}


void
BView::StrokeTriangle(BPoint point1, BPoint point2, BPoint point3, BRect bounds,
        const BGradient& gradient)
{
        if (fOwner == NULL)
                return;

        _CheckLockAndSwitchCurrent();

        fOwner->fLink->StartMessage(AS_STROKE_TRIANGLE_GRADIENT);
        fOwner->fLink->Attach<BPoint>(point1);
        fOwner->fLink->Attach<BPoint>(point2);
        fOwner->fLink->Attach<BPoint>(point3);
        fOwner->fLink->Attach<BRect>(bounds);
        fOwner->fLink->AttachGradient(gradient);

        _FlushIfNotInTransaction();
}


void
BView::StrokeTriangle(BPoint point1, BPoint point2, BPoint point3,
        const BGradient& gradient)
{
        if (fOwner) {
                // we construct the smallest rectangle that contains the 3 points
                // for the 1st point
                BRect bounds(point1, point1);

                // for the 2nd point
                if (point2.x < bounds.left)
                        bounds.left = point2.x;

                if (point2.y < bounds.top)
                        bounds.top = point2.y;

                if (point2.x > bounds.right)
                        bounds.right = point2.x;

                if (point2.y > bounds.bottom)
                        bounds.bottom = point2.y;

                // for the 3rd point
                if (point3.x < bounds.left)
                        bounds.left = point3.x;

                if (point3.y < bounds.top)
                        bounds.top = point3.y;

                if (point3.x > bounds.right)
                        bounds.right = point3.x;

                if (point3.y > bounds.bottom)
                        bounds.bottom = point3.y;

                StrokeTriangle(point1, point2, point3, bounds, gradient);
        }
}


void
BView::FillTriangle(BPoint point1, BPoint point2, BPoint point3,
        ::pattern pattern)
{
        if (fOwner) {
                // we construct the smallest rectangle that contains the 3 points
                // for the 1st point
                BRect bounds(point1, point1);

                // for the 2nd point
                if (point2.x < bounds.left)
                        bounds.left = point2.x;

                if (point2.y < bounds.top)
                        bounds.top = point2.y;

                if (point2.x > bounds.right)
                        bounds.right = point2.x;

                if (point2.y > bounds.bottom)
                        bounds.bottom = point2.y;

                // for the 3rd point
                if (point3.x < bounds.left)
                        bounds.left = point3.x;

                if (point3.y < bounds.top)
                        bounds.top = point3.y;

                if (point3.x > bounds.right)
                        bounds.right = point3.x;

                if (point3.y > bounds.bottom)
                        bounds.bottom = point3.y;

                FillTriangle(point1, point2, point3, bounds, pattern);
        }
}


void
BView::FillTriangle(BPoint point1, BPoint point2, BPoint point3,
        const BGradient& gradient)
{
        if (fOwner) {
                // we construct the smallest rectangle that contains the 3 points
                // for the 1st point
                BRect bounds(point1, point1);

                // for the 2nd point
                if (point2.x < bounds.left)
                        bounds.left = point2.x;

                if (point2.y < bounds.top)
                        bounds.top = point2.y;

                if (point2.x > bounds.right)
                        bounds.right = point2.x;

                if (point2.y > bounds.bottom)
                        bounds.bottom = point2.y;

                // for the 3rd point
                if (point3.x < bounds.left)
                        bounds.left = point3.x;

                if (point3.y < bounds.top)
                        bounds.top = point3.y;

                if (point3.x > bounds.right)
                        bounds.right = point3.x;

                if (point3.y > bounds.bottom)
                        bounds.bottom = point3.y;

                FillTriangle(point1, point2, point3, bounds, gradient);
        }
}


void
BView::FillTriangle(BPoint point1, BPoint point2, BPoint point3,
        BRect bounds, ::pattern pattern)
{
        if (fOwner == NULL)
                return;

        _CheckLockAndSwitchCurrent();
        _UpdatePattern(pattern);

        fOwner->fLink->StartMessage(AS_FILL_TRIANGLE);
        fOwner->fLink->Attach<BPoint>(point1);
        fOwner->fLink->Attach<BPoint>(point2);
        fOwner->fLink->Attach<BPoint>(point3);
        fOwner->fLink->Attach<BRect>(bounds);

        _FlushIfNotInTransaction();
}


void
BView::FillTriangle(BPoint point1, BPoint point2, BPoint point3, BRect bounds,
        const BGradient& gradient)
{
        if (fOwner == NULL)
                return;

        _CheckLockAndSwitchCurrent();
        fOwner->fLink->StartMessage(AS_FILL_TRIANGLE_GRADIENT);
        fOwner->fLink->Attach<BPoint>(point1);
        fOwner->fLink->Attach<BPoint>(point2);
        fOwner->fLink->Attach<BPoint>(point3);
        fOwner->fLink->Attach<BRect>(bounds);
        fOwner->fLink->AttachGradient(gradient);

        _FlushIfNotInTransaction();
}


void
BView::StrokeLine(BPoint toPoint, ::pattern pattern)
{
        StrokeLine(PenLocation(), toPoint, pattern);
}


void
BView::StrokeLine(BPoint start, BPoint end, ::pattern pattern)
{
        if (fOwner == NULL)
                return;

        _CheckLockAndSwitchCurrent();
        _UpdatePattern(pattern);

        ViewStrokeLineInfo info;
        info.startPoint = start;
        info.endPoint = end;

        fOwner->fLink->StartMessage(AS_STROKE_LINE);
        fOwner->fLink->Attach<ViewStrokeLineInfo>(info);

        _FlushIfNotInTransaction();

        // this modifies our pen location, so we invalidate the flag.
        fState->valid_flags &= ~B_VIEW_PEN_LOCATION_BIT;
}


void
BView::StrokeLine(BPoint toPoint, const BGradient& gradient)
{
        StrokeLine(PenLocation(), toPoint, gradient);
}


void
BView::StrokeLine(BPoint start, BPoint end, const BGradient& gradient)
{
        if (fOwner == NULL)
                return;

        _CheckLockAndSwitchCurrent();

        ViewStrokeLineInfo info;
        info.startPoint = start;
        info.endPoint = end;

        fOwner->fLink->StartMessage(AS_STROKE_LINE_GRADIENT);
        fOwner->fLink->Attach<ViewStrokeLineInfo>(info);
        fOwner->fLink->AttachGradient(gradient);

        _FlushIfNotInTransaction();

        // this modifies our pen location, so we invalidate the flag.
        fState->valid_flags &= ~B_VIEW_PEN_LOCATION_BIT;
}


void
BView::StrokeShape(BShape* shape, ::pattern pattern)
{
        if (shape == NULL || fOwner == NULL)
                return;

        shape_data* sd = BShape::Private(*shape).PrivateData();
        if (sd->opCount == 0 || sd->ptCount == 0)
                return;

        _CheckLockAndSwitchCurrent();
        _UpdatePattern(pattern);

        fOwner->fLink->StartMessage(AS_STROKE_SHAPE);
        fOwner->fLink->Attach<BRect>(shape->Bounds());
        fOwner->fLink->AttachShape(*shape);

        _FlushIfNotInTransaction();
}


void
BView::StrokeShape(BShape* shape, const BGradient& gradient)
{
        if (shape == NULL || fOwner == NULL)
                return;

        shape_data* sd = BShape::Private(*shape).PrivateData();
        if (sd->opCount == 0 || sd->ptCount == 0)
                return;

        _CheckLockAndSwitchCurrent();

        fOwner->fLink->StartMessage(AS_STROKE_SHAPE_GRADIENT);
        fOwner->fLink->Attach<BRect>(shape->Bounds());
        fOwner->fLink->AttachShape(*shape);
        fOwner->fLink->AttachGradient(gradient);

        _FlushIfNotInTransaction();
}


void
BView::FillShape(BShape* shape, ::pattern pattern)
{
        if (shape == NULL || fOwner == NULL)
                return;

        shape_data* sd = BShape::Private(*shape).PrivateData();
        if (sd->opCount == 0 || sd->ptCount == 0)
                return;

        _CheckLockAndSwitchCurrent();
        _UpdatePattern(pattern);

        fOwner->fLink->StartMessage(AS_FILL_SHAPE);
        fOwner->fLink->Attach<BRect>(shape->Bounds());
        fOwner->fLink->AttachShape(*shape);

        _FlushIfNotInTransaction();
}


void
BView::FillShape(BShape* shape, const BGradient& gradient)
{
        if (shape == NULL || fOwner == NULL)
                return;

        shape_data* sd = BShape::Private(*shape).PrivateData();
        if (sd->opCount == 0 || sd->ptCount == 0)
                return;

        _CheckLockAndSwitchCurrent();

        fOwner->fLink->StartMessage(AS_FILL_SHAPE_GRADIENT);
        fOwner->fLink->Attach<BRect>(shape->Bounds());
        fOwner->fLink->AttachShape(*shape);
        fOwner->fLink->AttachGradient(gradient);

        _FlushIfNotInTransaction();
}


void
BView::BeginLineArray(int32 count)
{
        if (fOwner == NULL)
                return;

        if (count <= 0)
                debugger("Calling BeginLineArray with a count <= 0");

        _CheckLock();

        if (fCommArray) {
                debugger("Can't nest BeginLineArray calls");
                        // not fatal, but it helps during
                        // development of your app and is in
                        // line with R5...
                delete[] fCommArray->array;
                delete fCommArray;
        }

        // TODO: since this method cannot return failure, and further AddLine()
        //      calls with a NULL fCommArray would drop into the debugger anyway,
        //      we allow the possible std::bad_alloc exceptions here...
        fCommArray = new _array_data_;
        fCommArray->count = 0;

        // Make sure the fCommArray is initialized to reasonable values in cases of
        // bad_alloc. At least the exception can be caught and EndLineArray won't
        // crash.
        fCommArray->array = NULL;
        fCommArray->maxCount = 0;

        fCommArray->array = new ViewLineArrayInfo[count];
        fCommArray->maxCount = count;
}


void
BView::AddLine(BPoint start, BPoint end, rgb_color color)
{
        if (fOwner == NULL)
                return;

        if (!fCommArray)
                debugger("BeginLineArray must be called before using AddLine");

        _CheckLock();

        const uint32 &arrayCount = fCommArray->count;
        if (arrayCount < fCommArray->maxCount) {
                fCommArray->array[arrayCount].startPoint = start;
                fCommArray->array[arrayCount].endPoint = end;
                fCommArray->array[arrayCount].color = color;

                fCommArray->count++;
        }
}


void
BView::EndLineArray()
{
        if (fOwner == NULL)
                return;

        if (fCommArray == NULL)
                debugger("Can't call EndLineArray before BeginLineArray");

        _CheckLockAndSwitchCurrent();

        fOwner->fLink->StartMessage(AS_STROKE_LINEARRAY);
        fOwner->fLink->Attach<int32>(fCommArray->count);
        fOwner->fLink->Attach(fCommArray->array,
                fCommArray->count * sizeof(ViewLineArrayInfo));

        _FlushIfNotInTransaction();

        _RemoveCommArray();
}


void
BView::SetDiskMode(char* filename, long offset)
{
        // TODO: implement
        // One BeBook version has this to say about SetDiskMode():
        //
        // "Begins recording a picture to the file with the given filename
        // at the given offset. Subsequent drawing commands sent to the view
        // will be written to the file until EndPicture() is called. The
        // stored commands may be played from the file with DrawPicture()."
}


void
BView::BeginPicture(BPicture* picture)
{
        if (_CheckOwnerLockAndSwitchCurrent()
                && picture && picture->fUsurped == NULL) {
                picture->Usurp(fCurrentPicture);
                fCurrentPicture = picture;

                fOwner->fLink->StartMessage(AS_VIEW_BEGIN_PICTURE);
        }
}


void
BView::AppendToPicture(BPicture* picture)
{
        _CheckLockAndSwitchCurrent();

        if (picture && picture->fUsurped == NULL) {
                int32 token = picture->Token();

                if (token == -1) {
                        BeginPicture(picture);
                } else {
                        picture->SetToken(-1);
                        picture->Usurp(fCurrentPicture);
                        fCurrentPicture = picture;
                        fOwner->fLink->StartMessage(AS_VIEW_APPEND_TO_PICTURE);
                        fOwner->fLink->Attach<int32>(token);
                }
        }
}


BPicture*
BView::EndPicture()
{
        if (_CheckOwnerLockAndSwitchCurrent() && fCurrentPicture) {
                int32 token;

                fOwner->fLink->StartMessage(AS_VIEW_END_PICTURE);

                int32 code;
                if (fOwner->fLink->FlushWithReply(code) == B_OK
                        && code == B_OK
                        && fOwner->fLink->Read<int32>(&token) == B_OK) {
                        BPicture* picture = fCurrentPicture;
                        fCurrentPicture = picture->StepDown();
                        picture->SetToken(token);

                        // TODO do this more efficient e.g. use a shared area and let the
                        // client write into it
                        picture->_Download();
                        return picture;
                }
        }

        return NULL;
}


void
BView::SetViewBitmap(const BBitmap* bitmap, BRect srcRect, BRect dstRect,
        uint32 followFlags, uint32 options)
{
        _SetViewBitmap(bitmap, srcRect, dstRect, followFlags, options);
}


void
BView::SetViewBitmap(const BBitmap* bitmap, uint32 followFlags, uint32 options)
{
        BRect rect;
        if (bitmap)
                rect = bitmap->Bounds();

        rect.OffsetTo(B_ORIGIN);

        _SetViewBitmap(bitmap, rect, rect, followFlags, options);
}


void
BView::ClearViewBitmap()
{
        _SetViewBitmap(NULL, BRect(), BRect(), 0, 0);
}


status_t
BView::SetViewOverlay(const BBitmap* overlay, BRect srcRect, BRect dstRect,
        rgb_color* colorKey, uint32 followFlags, uint32 options)
{
        if (overlay == NULL || (overlay->fFlags & B_BITMAP_WILL_OVERLAY) == 0)
                return B_BAD_VALUE;

        status_t status = _SetViewBitmap(overlay, srcRect, dstRect, followFlags,
                options | AS_REQUEST_COLOR_KEY);
        if (status == B_OK) {
                // read the color that will be treated as transparent
                fOwner->fLink->Read<rgb_color>(colorKey);
        }

        return status;
}


status_t
BView::SetViewOverlay(const BBitmap* overlay, rgb_color* colorKey,
        uint32 followFlags, uint32 options)
{
        if (overlay == NULL)
                return B_BAD_VALUE;

        BRect rect = overlay->Bounds();
        rect.OffsetTo(B_ORIGIN);

        return SetViewOverlay(overlay, rect, rect, colorKey, followFlags, options);
}


void
BView::ClearViewOverlay()
{
        _SetViewBitmap(NULL, BRect(), BRect(), 0, 0);
}


void
BView::CopyBits(BRect src, BRect dst)
{
        if (fOwner == NULL)
                return;

        if (!src.IsValid() || !dst.IsValid())
                return;

        _CheckLockAndSwitchCurrent();

        fOwner->fLink->StartMessage(AS_VIEW_COPY_BITS);
        fOwner->fLink->Attach<BRect>(src);
        fOwner->fLink->Attach<BRect>(dst);

        _FlushIfNotInTransaction();
}


void
BView::DrawPicture(const BPicture* picture)
{
        if (picture == NULL)
                return;

        DrawPictureAsync(picture, PenLocation());
        Sync();
}


void
BView::DrawPicture(const BPicture* picture, BPoint where)
{
        if (picture == NULL)
                return;

        DrawPictureAsync(picture, where);
        Sync();
}


void
BView::DrawPicture(const char* filename, long offset, BPoint where)
{
        if (!filename)
                return;

        DrawPictureAsync(filename, offset, where);
        Sync();
}


void
BView::DrawPictureAsync(const BPicture* picture)
{
        if (picture == NULL)
                return;

        DrawPictureAsync(picture, PenLocation());
}


void
BView::DrawPictureAsync(const BPicture* picture, BPoint where)
{
        if (picture == NULL)
                return;

        if (_CheckOwnerLockAndSwitchCurrent() && picture->Token() > 0) {
                fOwner->fLink->StartMessage(AS_VIEW_DRAW_PICTURE);
                fOwner->fLink->Attach<int32>(picture->Token());
                fOwner->fLink->Attach<BPoint>(where);

                _FlushIfNotInTransaction();
        }
}


void
BView::DrawPictureAsync(const char* filename, long offset, BPoint where)
{
        if (!filename)
                return;

        // TODO: Test
        BFile file(filename, B_READ_ONLY);
        if (file.InitCheck() < B_OK)
                return;

        file.Seek(offset, SEEK_SET);

        BPicture picture;
        if (picture.Unflatten(&file) < B_OK)
                return;

        DrawPictureAsync(&picture, where);
}


void
BView::BeginLayer(uint8 opacity)
{
        if (_CheckOwnerLockAndSwitchCurrent()) {
                fOwner->fLink->StartMessage(AS_VIEW_BEGIN_LAYER);
                fOwner->fLink->Attach<uint8>(opacity);
                _FlushIfNotInTransaction();
        }
}


void
BView::EndLayer()
{
        if (_CheckOwnerLockAndSwitchCurrent()) {
                fOwner->fLink->StartMessage(AS_VIEW_END_LAYER);
                _FlushIfNotInTransaction();
        }
}


void
BView::Invalidate(BRect invalRect)
{
        if (fOwner == NULL)
                return;

        // NOTE: This rounding of the invalid rect is to stay compatible with BeOS.
        // On the server side, the invalid rect will be converted to a BRegion,
        // which rounds in a different manner, so that it really includes the
        // fractional coordinates of a BRect (ie ceilf(rect.right) &
        // ceilf(rect.bottom)), which is also what BeOS does. So we have to do the
        // different rounding here to stay compatible in both ways.
        invalRect.left = (int)invalRect.left;
        invalRect.top = (int)invalRect.top;
        invalRect.right = (int)invalRect.right;
        invalRect.bottom = (int)invalRect.bottom;
        if (!invalRect.IsValid())
                return;

        _CheckLockAndSwitchCurrent();

        if (!fBounds.Intersects(invalRect))
                return;

        fOwner->fLink->StartMessage(AS_VIEW_INVALIDATE_RECT);
        fOwner->fLink->Attach<BRect>(invalRect);

// TODO: determine why this check isn't working correctly.
#if 0
        if (!fOwner->fUpdateRequested) {
                fOwner->fLink->Flush();
                fOwner->fUpdateRequested = true;
        }
#else
        fOwner->fLink->Flush();
#endif
}


void
BView::Invalidate(const BRegion* region)
{
        if (region == NULL || fOwner == NULL)
                return;

        _CheckLockAndSwitchCurrent();

        if (!fBounds.Intersects(region->Frame()))
                return;

        fOwner->fLink->StartMessage(AS_VIEW_INVALIDATE_REGION);
        fOwner->fLink->AttachRegion(*region);

// TODO: See above.
#if 0
        if (!fOwner->fUpdateRequested) {
                fOwner->fLink->Flush();
                fOwner->fUpdateRequested = true;
        }
#else
        fOwner->fLink->Flush();
#endif
}


void
BView::Invalidate()
{
        Invalidate(Bounds());
}


void
BView::DelayedInvalidate(bigtime_t delay)
{
        DelayedInvalidate(delay, Bounds());
}


void
BView::DelayedInvalidate(bigtime_t delay, BRect invalRect)
{
        if (fOwner == NULL)
                return;

        invalRect.left = (int)invalRect.left;
        invalRect.top = (int)invalRect.top;
        invalRect.right = (int)invalRect.right;
        invalRect.bottom = (int)invalRect.bottom;
        if (!invalRect.IsValid())
                return;

        _CheckLockAndSwitchCurrent();

        fOwner->fLink->StartMessage(AS_VIEW_DELAYED_INVALIDATE_RECT);
        fOwner->fLink->Attach<bigtime_t>(system_time() + delay);
        fOwner->fLink->Attach<BRect>(invalRect);
        fOwner->fLink->Flush();
}


void
BView::InvertRect(BRect rect)
{
        if (fOwner) {
                _CheckLockAndSwitchCurrent();

                fOwner->fLink->StartMessage(AS_VIEW_INVERT_RECT);
                fOwner->fLink->Attach<BRect>(rect);

                _FlushIfNotInTransaction();
        }
}


//      #pragma mark - View Hierarchy Functions


void
BView::AddChild(BView* child, BView* before)
{
        STRACE(("BView(%s)::AddChild(child '%s', before '%s')\n",
                this->Name(),
                child != NULL && child->Name() ? child->Name() : "NULL",
                before != NULL && before->Name() ? before->Name() : "NULL"));

        if (!_AddChild(child, before))
                return;

        if (fLayoutData->fLayout)
                fLayoutData->fLayout->AddView(child);
}


bool
BView::AddChild(BLayoutItem* child)
{
        if (!fLayoutData->fLayout)
                return false;
        return fLayoutData->fLayout->AddItem(child);
}


bool
BView::_AddChild(BView* child, BView* before)
{
        if (!child)
                return false;

        if (child->fParent != NULL) {
                debugger("AddChild failed - the view already has a parent.");
                return false;
        }

        if (child == this) {
                debugger("AddChild failed - cannot add a view to itself.");
                return false;
        }

        bool lockedOwner = false;
        if (fOwner && !fOwner->IsLocked()) {
                fOwner->Lock();
                lockedOwner = true;
        }

        if (!_AddChildToList(child, before)) {
                debugger("AddChild failed!");
                if (lockedOwner)
                        fOwner->Unlock();
                return false;
        }

        if (fOwner) {
                _CheckLockAndSwitchCurrent();

                child->_SetOwner(fOwner);
                child->_CreateSelf();
                child->_Attach();

                if (lockedOwner)
                        fOwner->Unlock();
        }

        InvalidateLayout();

        return true;
}


bool
BView::RemoveChild(BView* child)
{
        STRACE(("BView(%s)::RemoveChild(%s)\n", Name(), child->Name()));

        if (!child)
                return false;

        if (child->fParent != this)
                return false;

        return child->RemoveSelf();
}


int32
BView::CountChildren() const
{
        _CheckLock();

        uint32 count = 0;
        BView* child = fFirstChild;

        while (child != NULL) {
                count++;
                child = child->fNextSibling;
        }

        return count;
}


BView*
BView::ChildAt(int32 index) const
{
        _CheckLock();

        BView* child = fFirstChild;
        while (child != NULL && index-- > 0) {
                child = child->fNextSibling;
        }

        return child;
}


BView*
BView::NextSibling() const
{
        return fNextSibling;
}


BView*
BView::PreviousSibling() const
{
        return fPreviousSibling;
}


bool
BView::RemoveSelf()
{
        _RemoveLayoutItemsFromLayout(false);

        return _RemoveSelf();
}


bool
BView::_RemoveSelf()
{
        STRACE(("BView(%s)::_RemoveSelf()\n", Name()));

        // Remove this child from its parent

        BWindow* owner = fOwner;
        _CheckLock();

        if (owner != NULL) {
                _UpdateStateForRemove();
                _Detach();
        }

        BView* parent = fParent;
        if (!parent || !parent->_RemoveChildFromList(this))
                return false;

        if (owner != NULL && !fTopLevelView) {
                // the top level view is deleted by the app_server automatically
                owner->fLink->StartMessage(AS_VIEW_DELETE);
                owner->fLink->Attach<int32>(_get_object_token_(this));
        }

        parent->InvalidateLayout();

        STRACE(("DONE: BView(%s)::_RemoveSelf()\n", Name()));

        return true;
}


void
BView::_RemoveLayoutItemsFromLayout(bool deleteItems)
{
        if (fParent == NULL || fParent->fLayoutData->fLayout == NULL)
                return;

        int32 index = fLayoutData->fLayoutItems.CountItems();
        while (index-- > 0) {
                BLayoutItem* item = fLayoutData->fLayoutItems.ItemAt(index);
                item->RemoveSelf();
                        // Removes item from fLayoutItems list
                if (deleteItems)
                        delete item;
        }
}


BView*
BView::Parent() const
{
        if (fParent && fParent->fTopLevelView)
                return NULL;

        return fParent;
}


BView*
BView::FindView(const char* name) const
{
        if (name == NULL)
                return NULL;

        if (Name() != NULL && !strcmp(Name(), name))
                return const_cast<BView*>(this);

        BView* child = fFirstChild;
        while (child != NULL) {
                BView* view = child->FindView(name);
                if (view != NULL)
                        return view;

                child = child->fNextSibling;
        }

        return NULL;
}


void
BView::MoveBy(float deltaX, float deltaY)
{
        MoveTo(fParentOffset.x + roundf(deltaX), fParentOffset.y + roundf(deltaY));
}


void
BView::MoveTo(BPoint where)
{
        MoveTo(where.x, where.y);
}


void
BView::MoveTo(float x, float y)
{
        if (x == fParentOffset.x && y == fParentOffset.y)
                return;

        // BeBook says we should do this. And it makes sense.
        x = roundf(x);
        y = roundf(y);

        if (fOwner) {
                _CheckLockAndSwitchCurrent();
                fOwner->fLink->StartMessage(AS_VIEW_MOVE_TO);
                fOwner->fLink->Attach<float>(x);
                fOwner->fLink->Attach<float>(y);

//              fState->valid_flags |= B_VIEW_FRAME_BIT;

                _FlushIfNotInTransaction();
        }

        _MoveTo((int32)x, (int32)y);
}


void
BView::ResizeBy(float deltaWidth, float deltaHeight)
{
        // BeBook says we should do this. And it makes sense.
        deltaWidth = roundf(deltaWidth);
        deltaHeight = roundf(deltaHeight);

        if (deltaWidth == 0 && deltaHeight == 0)
                return;

        if (fOwner) {
                _CheckLockAndSwitchCurrent();
                fOwner->fLink->StartMessage(AS_VIEW_RESIZE_TO);

                fOwner->fLink->Attach<float>(fBounds.Width() + deltaWidth);
                fOwner->fLink->Attach<float>(fBounds.Height() + deltaHeight);

//              fState->valid_flags |= B_VIEW_FRAME_BIT;

                _FlushIfNotInTransaction();
        }

        _ResizeBy((int32)deltaWidth, (int32)deltaHeight);
}


void
BView::ResizeTo(float width, float height)
{
        ResizeBy(width - fBounds.Width(), height - fBounds.Height());
}


void
BView::ResizeTo(BSize size)
{
        ResizeBy(size.width - fBounds.Width(), size.height - fBounds.Height());
}


//      #pragma mark - Inherited Methods (from BHandler)


status_t
BView::GetSupportedSuites(BMessage* data)
{
        if (data == NULL)
                return B_BAD_VALUE;

        status_t status = data->AddString("suites", "suite/vnd.Be-view");
        BPropertyInfo propertyInfo(sViewPropInfo);
        if (status == B_OK)
                status = data->AddFlat("messages", &propertyInfo);
        if (status == B_OK)
                return BHandler::GetSupportedSuites(data);
        return status;
}


BHandler*
BView::ResolveSpecifier(BMessage* message, int32 index, BMessage* specifier,
        int32 what, const char* property)
{
        if (message->what == B_WINDOW_MOVE_BY
                || message->what == B_WINDOW_MOVE_TO) {
                return this;
        }

        BPropertyInfo propertyInfo(sViewPropInfo);
        status_t err = B_BAD_SCRIPT_SYNTAX;
        BMessage replyMsg(B_REPLY);

        switch (propertyInfo.FindMatch(message, index, specifier, what, property)) {
                case 0:
                case 1:
                case 3:
                        return this;

                case 2:
                        if (fShelf) {
                                message->PopSpecifier();
                                return fShelf;
                        }

                        err = B_NAME_NOT_FOUND;
                        replyMsg.AddString("message", "This window doesn't have a shelf");
                        break;

                case 4:
                {
                        if (!fFirstChild) {
                                err = B_NAME_NOT_FOUND;
                                replyMsg.AddString("message", "This window doesn't have "
                                        "children.");
                                break;
                        }
                        BView* child = NULL;
                        switch (what) {
                                case B_INDEX_SPECIFIER:
                                {
                                        int32 index;
                                        err = specifier->FindInt32("index", &index);
                                        if (err == B_OK)
                                                child = ChildAt(index);
                                        break;
                                }
                                case B_REVERSE_INDEX_SPECIFIER:
                                {
                                        int32 rindex;
                                        err = specifier->FindInt32("index", &rindex);
                                        if (err == B_OK)
                                                child = ChildAt(CountChildren() - rindex);
                                        break;
                                }
                                case B_NAME_SPECIFIER:
                                {
                                        const char* name;
                                        err = specifier->FindString("name", &name);
                                        if (err == B_OK)
                                                child = FindView(name);
                                        break;
                                }
                        }

                        if (child != NULL) {
                                message->PopSpecifier();
                                return child;
                        }

                        if (err == B_OK)
                                err = B_BAD_INDEX;

                        replyMsg.AddString("message",
                                "Cannot find view at/with specified index/name.");
                        break;
                }

                default:
                        return BHandler::ResolveSpecifier(message, index, specifier, what,
                                property);
        }

        if (err < B_OK) {
                replyMsg.what = B_MESSAGE_NOT_UNDERSTOOD;

                if (err == B_BAD_SCRIPT_SYNTAX)
                        replyMsg.AddString("message", "Didn't understand the specifier(s)");
                else
                        replyMsg.AddString("message", strerror(err));
        }

        replyMsg.AddInt32("error", err);
        message->SendReply(&replyMsg);
        return NULL;
}


void
BView::MessageReceived(BMessage* message)
{
        if (!message->HasSpecifiers()) {
                switch (message->what) {
                        case B_INVALIDATE:
                        {
                                BRect rect;
                                if (message->FindRect("be:area", &rect) == B_OK)
                                        Invalidate(rect);
                                else
                                        Invalidate();
                                break;
                        }

                        case B_KEY_DOWN:
                        {
                                // TODO: cannot use "string" here if we support having different
                                // font encoding per view (it's supposed to be converted by
                                // BWindow::_HandleKeyDown() one day)
                                const char* string;
                                ssize_t bytes;
                                if (message->FindData("bytes", B_STRING_TYPE,
                                                (const void**)&string, &bytes) == B_OK)
                                        KeyDown(string, bytes - 1);
                                break;
                        }

                        case B_KEY_UP:
                        {
                                // TODO: same as above
                                const char* string;
                                ssize_t bytes;
                                if (message->FindData("bytes", B_STRING_TYPE,
                                                (const void**)&string, &bytes) == B_OK)
                                        KeyUp(string, bytes - 1);
                                break;
                        }

                        case B_VIEW_RESIZED:
                                FrameResized(message->GetInt32("width", 0),
                                        message->GetInt32("height", 0));
                                break;

                        case B_VIEW_MOVED:
                                FrameMoved(fParentOffset);
                                break;

                        case B_MOUSE_DOWN:
                        {
                                BPoint where;
                                message->FindPoint("be:view_where", &where);
                                MouseDown(where);
                                break;
                        }

                        case B_MOUSE_IDLE:
                        {
                                BPoint where;
                                if (message->FindPoint("be:view_where", &where) != B_OK)
                                        break;

                                BToolTip* tip;
                                if (GetToolTipAt(where, &tip))
                                        ShowToolTip(tip);
                                else
                                        BHandler::MessageReceived(message);
                                break;
                        }

                        case B_MOUSE_MOVED:
                        {
                                uint32 eventOptions = fEventOptions | fMouseEventOptions;
                                bool noHistory = eventOptions & B_NO_POINTER_HISTORY;
                                bool dropIfLate = !(eventOptions & B_FULL_POINTER_HISTORY);

                                bigtime_t eventTime;
                                if (message->FindInt64("when", (int64*)&eventTime) < B_OK)
                                        eventTime = system_time();

                                uint32 transit;
                                message->FindInt32("be:transit", (int32*)&transit);
                                // don't drop late messages with these important transit values
                                if (transit == B_ENTERED_VIEW || transit == B_EXITED_VIEW)
                                        dropIfLate = false;

                                // TODO: The dropping code may have the following problem: On
                                // slower computers, 20ms may just be to abitious a delay.
                                // There, we might constantly check the message queue for a
                                // newer message, not find any, and still use the only but later
                                // than 20ms message, which of course makes the whole thing
                                // later than need be. An adaptive delay would be kind of neat,
                                // but would probably use additional BWindow members to count
                                // the successful versus fruitless queue searches and the delay
                                // value itself or something similar.
                                if (noHistory
                                        || (dropIfLate && (system_time() - eventTime > 20000))) {
                                        // filter out older mouse moved messages in the queue
                                        BWindow* window = Window();
                                        window->_DequeueAll();
                                        BMessageQueue* queue = window->MessageQueue();
                                        queue->Lock();

                                        BMessage* moved;
                                        for (int32 i = 0; (moved = queue->FindMessage(i)) != NULL;
                                                 i++) {
                                                if (moved != message && moved->what == B_MOUSE_MOVED) {
                                                        // there is a newer mouse moved message in the
                                                        // queue, just ignore the current one, the newer one
                                                        // will be handled here eventually
                                                        queue->Unlock();
                                                        return;
                                                }
                                        }
                                        queue->Unlock();
                                }

                                BPoint where;
                                uint32 buttons;
                                message->FindPoint("be:view_where", &where);
                                message->FindInt32("buttons", (int32*)&buttons);

                                if (transit == B_EXITED_VIEW || transit == B_OUTSIDE_VIEW)
                                        HideToolTip();

                                BMessage* dragMessage = NULL;
                                if (message->HasMessage("be:drag_message")) {
                                        dragMessage = new BMessage();
                                        if (message->FindMessage("be:drag_message", dragMessage)
                                                != B_OK) {
                                                delete dragMessage;
                                                dragMessage = NULL;
                                        }
                                }

                                MouseMoved(where, transit, dragMessage);
                                delete dragMessage;
                                break;
                        }

                        case B_MOUSE_UP:
                        {
                                BPoint where;
                                message->FindPoint("be:view_where", &where);
                                fMouseEventOptions = 0;
                                MouseUp(where);
                                break;
                        }

                        case B_MOUSE_WHEEL_CHANGED:
                        {
                                BScrollBar* horizontal = ScrollBar(B_HORIZONTAL);
                                BScrollBar* vertical = ScrollBar(B_VERTICAL);
                                if (horizontal == NULL && vertical == NULL) {
                                        // Pass the message to the next handler
                                        BHandler::MessageReceived(message);
                                        break;
                                }

                                float deltaX = 0.0f;
                                float deltaY = 0.0f;

                                if (horizontal != NULL)
                                        message->FindFloat("be:wheel_delta_x", &deltaX);

                                if (vertical != NULL)
                                        message->FindFloat("be:wheel_delta_y", &deltaY);

                                if (deltaX == 0.0f && deltaY == 0.0f)
                                        break;

                                if ((modifiers() & B_CONTROL_KEY) != 0)
                                        std::swap(horizontal, vertical);

                                if (horizontal != NULL && deltaX != 0.0f)
                                        ScrollWithMouseWheelDelta(horizontal, deltaX);

                                if (vertical != NULL && deltaY != 0.0f)
                                        ScrollWithMouseWheelDelta(vertical, deltaY);

                                break;
                        }

                        // prevent message repeats
                        case B_COLORS_UPDATED:
                        case B_FONTS_UPDATED:
                                break;

                        case B_SCREEN_CHANGED:
                        case B_WORKSPACE_ACTIVATED:
                        case B_WORKSPACES_CHANGED:
                        {
                                BWindow* window = Window();
                                if (window == NULL)
                                        break;

                                // propagate message to child views
                                int32 childCount = CountChildren();
                                for (int32 i = 0; i < childCount; i++) {
                                        BView* view = ChildAt(i);
                                        if (view != NULL)
                                                window->PostMessage(message, view);
                                }
                                break;
                        }

                        default:
                                BHandler::MessageReceived(message);
                                break;
                }

                return;
        }

        // Scripting message

        BMessage replyMsg(B_REPLY);
        status_t err = B_BAD_SCRIPT_SYNTAX;
        int32 index;
        BMessage specifier;
        int32 what;
        const char* property;

        if (message->GetCurrentSpecifier(&index, &specifier, &what, &property)
                        != B_OK) {
                return BHandler::MessageReceived(message);
        }

        BPropertyInfo propertyInfo(sViewPropInfo);
        switch (propertyInfo.FindMatch(message, index, &specifier, what,
                        property)) {
                case 0:
                        if (message->what == B_GET_PROPERTY) {
                                err = replyMsg.AddRect("result", Frame());
                        } else if (message->what == B_SET_PROPERTY) {
                                BRect newFrame;
                                err = message->FindRect("data", &newFrame);
                                if (err == B_OK) {
                                        MoveTo(newFrame.LeftTop());
                                        ResizeTo(newFrame.Width(), newFrame.Height());
                                }
                        }
                        break;
                case 1:
                        if (message->what == B_GET_PROPERTY) {
                                err = replyMsg.AddBool("result", IsHidden());
                        } else if (message->what == B_SET_PROPERTY) {
                                bool newHiddenState;
                                err = message->FindBool("data", &newHiddenState);
                                if (err == B_OK) {
                                        if (newHiddenState == true)
                                                Hide();
                                        else
                                                Show();
                                }
                        }
                        break;
                case 3:
                        err = replyMsg.AddInt32("result", CountChildren());
                        break;
                default:
                        return BHandler::MessageReceived(message);
        }

        if (err != B_OK) {
                replyMsg.what = B_MESSAGE_NOT_UNDERSTOOD;

                if (err == B_BAD_SCRIPT_SYNTAX)
                        replyMsg.AddString("message", "Didn't understand the specifier(s)");
                else
                        replyMsg.AddString("message", strerror(err));

                replyMsg.AddInt32("error", err);
        }

        message->SendReply(&replyMsg);
}


status_t
BView::Perform(perform_code code, void* _data)
{
        switch (code) {
                case PERFORM_CODE_MIN_SIZE:
                        ((perform_data_min_size*)_data)->return_value
                                = BView::MinSize();
                        return B_OK;
                case PERFORM_CODE_MAX_SIZE:
                        ((perform_data_max_size*)_data)->return_value
                                = BView::MaxSize();
                        return B_OK;
                case PERFORM_CODE_PREFERRED_SIZE:
                        ((perform_data_preferred_size*)_data)->return_value
                                = BView::PreferredSize();
                        return B_OK;
                case PERFORM_CODE_LAYOUT_ALIGNMENT:
                        ((perform_data_layout_alignment*)_data)->return_value
                                = BView::LayoutAlignment();
                        return B_OK;
                case PERFORM_CODE_HAS_HEIGHT_FOR_WIDTH:
                        ((perform_data_has_height_for_width*)_data)->return_value
                                = BView::HasHeightForWidth();
                        return B_OK;
                case PERFORM_CODE_GET_HEIGHT_FOR_WIDTH:
                {
                        perform_data_get_height_for_width* data
                                = (perform_data_get_height_for_width*)_data;
                        BView::GetHeightForWidth(data->width, &data->min, &data->max,
                                &data->preferred);
                        return B_OK;
                }
                case PERFORM_CODE_SET_LAYOUT:
                {
                        perform_data_set_layout* data = (perform_data_set_layout*)_data;
                        BView::SetLayout(data->layout);
                        return B_OK;
                }
                case PERFORM_CODE_LAYOUT_INVALIDATED:
                {
                        perform_data_layout_invalidated* data
                                = (perform_data_layout_invalidated*)_data;
                        BView::LayoutInvalidated(data->descendants);
                        return B_OK;
                }
                case PERFORM_CODE_DO_LAYOUT:
                {
                        BView::DoLayout();
                        return B_OK;
                }
                case PERFORM_CODE_LAYOUT_CHANGED:
                {
                        BView::LayoutChanged();
                        return B_OK;
                }
                case PERFORM_CODE_GET_TOOL_TIP_AT:
                {
                        perform_data_get_tool_tip_at* data
                                = (perform_data_get_tool_tip_at*)_data;
                        data->return_value
                                = BView::GetToolTipAt(data->point, data->tool_tip);
                        return B_OK;
                }
                case PERFORM_CODE_ALL_UNARCHIVED:
                {
                        perform_data_all_unarchived* data =
                                (perform_data_all_unarchived*)_data;

                        data->return_value = BView::AllUnarchived(data->archive);
                        return B_OK;
                }
                case PERFORM_CODE_ALL_ARCHIVED:
                {
                        perform_data_all_archived* data =
                                (perform_data_all_archived*)_data;

                        data->return_value = BView::AllArchived(data->archive);
                        return B_OK;
                }
        }

        return BHandler::Perform(code, _data);
}


// #pragma mark - Layout Functions


BSize
BView::MinSize()
{
        // TODO: make sure this works correctly when some methods are overridden
        float width, height;
        GetPreferredSize(&width, &height);

        return BLayoutUtils::ComposeSize(fLayoutData->fMinSize,
                (fLayoutData->fLayout ? fLayoutData->fLayout->MinSize()
                        : BSize(width, height)));
}


BSize
BView::MaxSize()
{
        return BLayoutUtils::ComposeSize(fLayoutData->fMaxSize,
                (fLayoutData->fLayout ? fLayoutData->fLayout->MaxSize()
                        : BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED)));
}


BSize
BView::PreferredSize()
{
        // TODO: make sure this works correctly when some methods are overridden
        float width, height;
        GetPreferredSize(&width, &height);

        return BLayoutUtils::ComposeSize(fLayoutData->fPreferredSize,
                (fLayoutData->fLayout ? fLayoutData->fLayout->PreferredSize()
                        : BSize(width, height)));
}


BAlignment
BView::LayoutAlignment()
{
        return BLayoutUtils::ComposeAlignment(fLayoutData->fAlignment,
                (fLayoutData->fLayout ? fLayoutData->fLayout->Alignment()
                        : BAlignment(B_ALIGN_HORIZONTAL_CENTER, B_ALIGN_VERTICAL_CENTER)));
}


void
BView::SetExplicitMinSize(BSize size)
{
        fLayoutData->fMinSize = size;
        InvalidateLayout();
}


void
BView::SetExplicitMaxSize(BSize size)
{
        fLayoutData->fMaxSize = size;
        InvalidateLayout();
}


void
BView::SetExplicitPreferredSize(BSize size)
{
        fLayoutData->fPreferredSize = size;
        InvalidateLayout();
}


void
BView::SetExplicitSize(BSize size)
{
        fLayoutData->fMinSize = size;
        fLayoutData->fMaxSize = size;
        fLayoutData->fPreferredSize = size;
        InvalidateLayout();
}


void
BView::SetExplicitAlignment(BAlignment alignment)
{
        fLayoutData->fAlignment = alignment;
        InvalidateLayout();
}


BSize
BView::ExplicitMinSize() const
{
        return fLayoutData->fMinSize;
}


BSize
BView::ExplicitMaxSize() const
{
        return fLayoutData->fMaxSize;
}


BSize
BView::ExplicitPreferredSize() const
{
        return fLayoutData->fPreferredSize;
}


BAlignment
BView::ExplicitAlignment() const
{
        return fLayoutData->fAlignment;
}


bool
BView::HasHeightForWidth()
{
        return (fLayoutData->fLayout
                ? fLayoutData->fLayout->HasHeightForWidth() : false);
}


void
BView::GetHeightForWidth(float width, float* min, float* max, float* preferred)
{
        if (fLayoutData->fLayout)
                fLayoutData->fLayout->GetHeightForWidth(width, min, max, preferred);
}


void
BView::SetLayout(BLayout* layout)
{
        if (layout == fLayoutData->fLayout)
                return;

        if (layout && layout->Layout())
                debugger("BView::SetLayout() failed, layout is already in use.");

        fFlags |= B_SUPPORTS_LAYOUT;

        // unset and delete the old layout
        if (fLayoutData->fLayout) {
                fLayoutData->fLayout->RemoveSelf();
                fLayoutData->fLayout->SetOwner(NULL);
                delete fLayoutData->fLayout;
        }

        fLayoutData->fLayout = layout;

        if (fLayoutData->fLayout) {
                fLayoutData->fLayout->SetOwner(this);

                // add all children
                int count = CountChildren();
                for (int i = 0; i < count; i++)
                        fLayoutData->fLayout->AddView(ChildAt(i));
        }

        InvalidateLayout();
}


BLayout*
BView::GetLayout() const
{
        return fLayoutData->fLayout;
}


void
BView::InvalidateLayout(bool descendants)
{
        // printf("BView(%p)::InvalidateLayout(%i), valid: %i, inProgress: %i\n",
        //      this, descendants, fLayoutData->fLayoutValid,
        //      fLayoutData->fLayoutInProgress);

        if (!fLayoutData->fMinMaxValid || fLayoutData->fLayoutInProgress
                        || fLayoutData->fLayoutInvalidationDisabled > 0) {
                return;
        }
        fLayoutData->fLayoutValid = false;
        fLayoutData->fMinMaxValid = false;
        LayoutInvalidated(descendants);

        if (descendants) {
                for (BView* child = fFirstChild;
                        child; child = child->fNextSibling) {
                        child->InvalidateLayout(descendants);
                }
        }

        if (fLayoutData->fLayout)
                fLayoutData->fLayout->InvalidateLayout(descendants);
        else
                _InvalidateParentLayout();

        if (fTopLevelView
                && fOwner != NULL)
                fOwner->PostMessage(B_LAYOUT_WINDOW);
}


void
BView::EnableLayoutInvalidation()
{
        if (fLayoutData->fLayoutInvalidationDisabled > 0)
                fLayoutData->fLayoutInvalidationDisabled--;
}


void
BView::DisableLayoutInvalidation()
{
        fLayoutData->fLayoutInvalidationDisabled++;
}


bool
BView::IsLayoutInvalidationDisabled()
{
        if (fLayoutData->fLayoutInvalidationDisabled > 0)
                return true;
        return false;
}


bool
BView::IsLayoutValid() const
{
        return fLayoutData->fLayoutValid;
}


void
BView::ResetLayoutInvalidation()
{
        fLayoutData->fMinMaxValid = true;
}


BLayoutContext*
BView::LayoutContext() const
{
        return fLayoutData->fLayoutContext;
}


void
BView::Layout(bool force)
{
        BLayoutContext context;
        _Layout(force, &context);
}


void
BView::Relayout()
{
        if (fLayoutData->fLayoutValid && !fLayoutData->fLayoutInProgress) {
                fLayoutData->fNeedsRelayout = true;
                if (fLayoutData->fLayout)
                        fLayoutData->fLayout->RequireLayout();

                // Layout() is recursive, that is if the parent view is currently laid
                // out, we don't call layout() on this view, but wait for the parent's
                // Layout() to do that for us.
                if (!fParent || !fParent->fLayoutData->fLayoutInProgress)
                        Layout(false);
        }
}


void
BView::LayoutInvalidated(bool descendants)
{
        // hook method
}


void
BView::DoLayout()
{
        if (fLayoutData->fLayout)
                fLayoutData->fLayout->_LayoutWithinContext(false, LayoutContext());
}


void
BView::SetToolTip(const char* text)
{
        if (text == NULL || text[0] == '\0') {
                SetToolTip((BToolTip*)NULL);
                return;
        }

        if (BTextToolTip* tip = dynamic_cast<BTextToolTip*>(fToolTip))
                tip->SetText(text);
        else
                SetToolTip(new BTextToolTip(text));
}


void
BView::SetToolTip(BToolTip* tip)
{
        if (fToolTip == tip)
                return;
        else if (tip == NULL)
                HideToolTip();

        if (fToolTip != NULL)
                fToolTip->ReleaseReference();

        fToolTip = tip;

        if (fToolTip != NULL)
                fToolTip->AcquireReference();
}


BToolTip*
BView::ToolTip() const
{
        return fToolTip;
}


void
BView::ShowToolTip(BToolTip* tip)
{
        if (tip == NULL)
                return;

        BPoint where;
        GetMouse(&where, NULL, false);

        BToolTipManager::Manager()->ShowTip(tip, ConvertToScreen(where), this);
}


void
BView::HideToolTip()
{
        if (fToolTip == NULL)
                return;

        // TODO: Only hide if ours is the tooltip that's showing!
        BToolTipManager::Manager()->HideTip();
}


bool
BView::GetToolTipAt(BPoint point, BToolTip** _tip)
{
        if (fToolTip != NULL) {
                *_tip = fToolTip;
                return true;
        }

        *_tip = NULL;
        return false;
}


void
BView::LayoutChanged()
{
        // hook method
}


void
BView::_Layout(bool force, BLayoutContext* context)
{
//printf("%p->BView::_Layout(%d, %p)\n", this, force, context);
//printf("  fNeedsRelayout: %d, fLayoutValid: %d, fLayoutInProgress: %d\n",
//fLayoutData->fNeedsRelayout, fLayoutData->fLayoutValid,
//fLayoutData->fLayoutInProgress);
        if (fLayoutData->fNeedsRelayout || !fLayoutData->fLayoutValid || force) {
                fLayoutData->fLayoutValid = false;

                if (fLayoutData->fLayoutInProgress)
                        return;

                BLayoutContext* oldContext = fLayoutData->fLayoutContext;
                fLayoutData->fLayoutContext = context;

                fLayoutData->fLayoutInProgress = true;
                DoLayout();
                fLayoutData->fLayoutInProgress = false;

                fLayoutData->fLayoutValid = true;
                fLayoutData->fMinMaxValid = true;
                fLayoutData->fNeedsRelayout = false;

                // layout children
                for(BView* child = fFirstChild; child; child = child->fNextSibling) {
                        if (!child->IsHidden(child))
                                child->_Layout(force, context);
                }

                LayoutChanged();

                fLayoutData->fLayoutContext = oldContext;

                // invalidate the drawn content, if requested
                if (fFlags & B_INVALIDATE_AFTER_LAYOUT)
                        Invalidate();
        }
}


void
BView::_LayoutLeft(BLayout* deleted)
{
        // If our layout is added to another layout (via BLayout::AddItem())
        // then we share ownership of our layout. In the event that our layout gets
        // deleted by the layout it has been added to, this method is called so
        // that we don't double-delete our layout.
        if (fLayoutData->fLayout == deleted)
                fLayoutData->fLayout = NULL;
        InvalidateLayout();
}


void
BView::_InvalidateParentLayout()
{
        if (!fParent)
                return;

        BLayout* layout = fLayoutData->fLayout;
        BLayout* layoutParent = layout ? layout->Layout() : NULL;
        if (layoutParent) {
                layoutParent->InvalidateLayout();
        } else if (fLayoutData->fLayoutItems.CountItems() > 0) {
                int32 count = fLayoutData->fLayoutItems.CountItems();
                for (int32 i = 0; i < count; i++) {
                        fLayoutData->fLayoutItems.ItemAt(i)->Layout()->InvalidateLayout();
                }
        } else {
                fParent->InvalidateLayout();
        }
}


//      #pragma mark - Private Functions


void
BView::_InitData(BRect frame, const char* name, uint32 resizingMode,
        uint32 flags)
{
        // Info: The name of the view is set by BHandler constructor

        STRACE(("BView::_InitData: enter\n"));

        // initialize members
        if ((resizingMode & ~_RESIZE_MASK_) || (flags & _RESIZE_MASK_))
                printf("%s BView::_InitData(): resizing mode or flags swapped\n", name);

        // There are applications that swap the resize mask and the flags in the
        // BView constructor. This does not cause problems under BeOS as it just
        // ors the two fields to one 32bit flag.
        // For now we do the same but print the above warning message.
        // TODO: this should be removed at some point and the original
        // version restored:
        // fFlags = (resizingMode & _RESIZE_MASK_) | (flags & ~_RESIZE_MASK_);
        fFlags = resizingMode | flags;

        // handle rounding
        frame.left = roundf(frame.left);
        frame.top = roundf(frame.top);
        frame.right = roundf(frame.right);
        frame.bottom = roundf(frame.bottom);

        fParentOffset.Set(frame.left, frame.top);

        fOwner = NULL;
        fParent = NULL;
        fNextSibling = NULL;
        fPreviousSibling = NULL;
        fFirstChild = NULL;

        fShowLevel = 0;
        fTopLevelView = false;

        fCurrentPicture = NULL;
        fCommArray = NULL;

        fVerScroller = NULL;
        fHorScroller = NULL;

        fIsPrinting = false;
        fAttached = false;

        // TODO: Since we cannot communicate failure, we don't use std::nothrow here
        // TODO: Maybe we could auto-delete those views on AddChild() instead?
        fState = new BPrivate::ViewState;

        fBounds = frame.OffsetToCopy(B_ORIGIN);
        fShelf = NULL;

        fEventMask = 0;
        fEventOptions = 0;
        fMouseEventOptions = 0;

        fLayoutData = new LayoutData;

        fToolTip = NULL;

        if ((flags & B_SUPPORTS_LAYOUT) != 0) {
                SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
                SetLowUIColor(ViewUIColor());
                SetHighUIColor(B_PANEL_TEXT_COLOR);
        }
}


void
BView::_RemoveCommArray()
{
        if (fCommArray) {
                delete [] fCommArray->array;
                delete fCommArray;
                fCommArray = NULL;
        }
}


void
BView::_SetOwner(BWindow* newOwner)
{
        if (!newOwner)
                _RemoveCommArray();

        if (fOwner != newOwner && fOwner) {
                if (fOwner->fFocus == this)
                        MakeFocus(false);

                if (fOwner->fLastMouseMovedView == this)
                        fOwner->fLastMouseMovedView = NULL;

                fOwner->RemoveHandler(this);
                if (fShelf)
                        fOwner->RemoveHandler(fShelf);
        }

        if (newOwner && newOwner != fOwner) {
                newOwner->AddHandler(this);
                if (fShelf)
                        newOwner->AddHandler(fShelf);

                if (fTopLevelView)
                        SetNextHandler(newOwner);
                else
                        SetNextHandler(fParent);
        }

        fOwner = newOwner;

        for (BView* child = fFirstChild; child != NULL; child = child->fNextSibling)
                child->_SetOwner(newOwner);
}


void
BView::_ClipToPicture(BPicture* picture, BPoint where, bool invert, bool sync)
{
        if (!_CheckOwnerLockAndSwitchCurrent())
                return;

        if (picture == NULL) {
                fOwner->fLink->StartMessage(AS_VIEW_CLIP_TO_PICTURE);
                fOwner->fLink->Attach<int32>(-1);

                // NOTE: No need to sync here, since the -1 token cannot
                // become invalid on the server.
        } else {
                fOwner->fLink->StartMessage(AS_VIEW_CLIP_TO_PICTURE);
                fOwner->fLink->Attach<int32>(picture->Token());
                fOwner->fLink->Attach<BPoint>(where);
                fOwner->fLink->Attach<bool>(invert);

                // NOTE: "sync" defaults to true in public methods. If you know what
                // you are doing, i.e. if you know your BPicture stays valid, you
                // can avoid the performance impact of syncing. In a use-case where
                // the client creates BPictures on the stack, these BPictures may
                // have issued a AS_DELETE_PICTURE command to the ServerApp when Draw()
                // goes out of scope, and the command is processed earlier in the
                // ServerApp thread than the AS_VIEW_CLIP_TO_PICTURE command in the
                // ServerWindow thread, which will then have the result that no
                // ServerPicture is found of the token.
                if (sync)
                        Sync();
        }
}


void
BView::_ClipToRect(BRect rect, bool inverse)
{
        if (_CheckOwnerLockAndSwitchCurrent()) {
                fOwner->fLink->StartMessage(AS_VIEW_CLIP_TO_RECT);
                fOwner->fLink->Attach<bool>(inverse);
                fOwner->fLink->Attach<BRect>(rect);
                _FlushIfNotInTransaction();
        }
}


void
BView::_ClipToShape(BShape* shape, bool inverse)
{
        if (shape == NULL)
                return;

        shape_data* sd = BShape::Private(*shape).PrivateData();
        if (sd->opCount == 0 || sd->ptCount == 0)
                return;

        if (_CheckOwnerLockAndSwitchCurrent()) {
                fOwner->fLink->StartMessage(AS_VIEW_CLIP_TO_SHAPE);
                fOwner->fLink->Attach<bool>(inverse);
                fOwner->fLink->AttachShape(*shape);
                _FlushIfNotInTransaction();
        }
}


bool
BView::_RemoveChildFromList(BView* child)
{
        if (child->fParent != this)
                return false;

        if (fFirstChild == child) {
                // it's the first view in the list
                fFirstChild = child->fNextSibling;
        } else {
                // there must be a previous sibling
                child->fPreviousSibling->fNextSibling = child->fNextSibling;
        }

        if (child->fNextSibling)
                child->fNextSibling->fPreviousSibling = child->fPreviousSibling;

        child->fParent = NULL;
        child->fNextSibling = NULL;
        child->fPreviousSibling = NULL;

        return true;
}


bool
BView::_AddChildToList(BView* child, BView* before)
{
        if (!child)
                return false;
        if (child->fParent != NULL) {
                debugger("View already belongs to someone else");
                return false;
        }
        if (before != NULL && before->fParent != this) {
                debugger("Invalid before view");
                return false;
        }

        if (before != NULL) {
                // add view before this one
                child->fNextSibling = before;
                child->fPreviousSibling = before->fPreviousSibling;
                if (child->fPreviousSibling != NULL)
                        child->fPreviousSibling->fNextSibling = child;

                before->fPreviousSibling = child;
                if (fFirstChild == before)
                        fFirstChild = child;
        } else {
                // add view to the end of the list
                BView* last = fFirstChild;
                while (last != NULL && last->fNextSibling != NULL) {
                        last = last->fNextSibling;
                }

                if (last != NULL) {
                        last->fNextSibling = child;
                        child->fPreviousSibling = last;
                } else {
                        fFirstChild = child;
                        child->fPreviousSibling = NULL;
                }

                child->fNextSibling = NULL;
        }

        child->fParent = this;
        return true;
}


/*!     \brief Creates the server counterpart of this view.
        This is only done for views that are part of the view hierarchy, ie. when
        they are attached to a window.
        RemoveSelf() deletes the server object again.
*/
bool
BView::_CreateSelf()
{
        // AS_VIEW_CREATE & AS_VIEW_CREATE_ROOT do not use the
        // current view mechanism via _CheckLockAndSwitchCurrent() - the token
        // of the view and its parent are both send to the server.

        if (fTopLevelView)
                fOwner->fLink->StartMessage(AS_VIEW_CREATE_ROOT);
        else
                fOwner->fLink->StartMessage(AS_VIEW_CREATE);

        fOwner->fLink->Attach<int32>(_get_object_token_(this));
        fOwner->fLink->AttachString(Name());
        fOwner->fLink->Attach<BRect>(Frame());
        fOwner->fLink->Attach<BPoint>(LeftTop());
        fOwner->fLink->Attach<uint32>(ResizingMode());
        fOwner->fLink->Attach<uint32>(fEventMask);
        fOwner->fLink->Attach<uint32>(fEventOptions);
        fOwner->fLink->Attach<uint32>(Flags());
        fOwner->fLink->Attach<bool>(IsHidden(this));
        fOwner->fLink->Attach<rgb_color>(fState->view_color);
        if (fTopLevelView)
                fOwner->fLink->Attach<int32>(B_NULL_TOKEN);
        else
                fOwner->fLink->Attach<int32>(_get_object_token_(fParent));
        fOwner->fLink->Flush();

        _CheckOwnerLockAndSwitchCurrent();
        fState->UpdateServerState(*fOwner->fLink);

        // we create all its children, too

        for (BView* child = fFirstChild; child != NULL;
                        child = child->fNextSibling) {
                child->_CreateSelf();
        }

        fOwner->fLink->Flush();
        return true;
}


/*!     Sets the new view position.
        It doesn't contact the server, though - the only case where this
        is called outside of MoveTo() is as reaction of moving a view
        in the server (a.k.a. B_WINDOW_RESIZED).
        It also calls the BView's FrameMoved() hook.
*/
void
BView::_MoveTo(int32 x, int32 y)
{
        fParentOffset.Set(x, y);

        if (Window() != NULL && fFlags & B_FRAME_EVENTS) {
                BMessage moved(B_VIEW_MOVED);
                moved.AddInt64("when", system_time());
                moved.AddPoint("where", BPoint(x, y));

                BMessenger target(this);
                target.SendMessage(&moved);
        }
}


/*!     Computes the actual new frame size and recalculates the size of
        the children as well.
        It doesn't contact the server, though - the only case where this
        is called outside of ResizeBy() is as reaction of resizing a view
        in the server (a.k.a. B_WINDOW_RESIZED).
        It also calls the BView's FrameResized() hook.
*/
void
BView::_ResizeBy(int32 deltaWidth, int32 deltaHeight)
{
        fBounds.right += deltaWidth;
        fBounds.bottom += deltaHeight;

        if (Window() == NULL) {
                // we're not supposed to exercise the resizing code in case
                // we haven't been attached to a window yet
                return;
        }

        // layout the children
        if ((fFlags & B_SUPPORTS_LAYOUT) != 0) {
                Relayout();
        } else {
                for (BView* child = fFirstChild; child; child = child->fNextSibling)
                        child->_ParentResizedBy(deltaWidth, deltaHeight);
        }

        if (fFlags & B_FRAME_EVENTS) {
                BMessage resized(B_VIEW_RESIZED);
                resized.AddInt64("when", system_time());
                resized.AddInt32("width", fBounds.IntegerWidth());
                resized.AddInt32("height", fBounds.IntegerHeight());

                BMessenger target(this);
                target.SendMessage(&resized);
        }
}


/*!     Relayouts the view according to its resizing mode. */
void
BView::_ParentResizedBy(int32 x, int32 y)
{
        uint32 resizingMode = fFlags & _RESIZE_MASK_;
        BRect newFrame = Frame();

        // follow with left side
        if ((resizingMode & 0x0F00U) == _VIEW_RIGHT_ << 8)
                newFrame.left += x;
        else if ((resizingMode & 0x0F00U) == _VIEW_CENTER_ << 8)
                newFrame.left += x / 2;

        // follow with right side
        if ((resizingMode & 0x000FU) == _VIEW_RIGHT_)
                newFrame.right += x;
        else if ((resizingMode & 0x000FU) == _VIEW_CENTER_)
                newFrame.right += x / 2;

        // follow with top side
        if ((resizingMode & 0xF000U) == _VIEW_BOTTOM_ << 12)
                newFrame.top += y;
        else if ((resizingMode & 0xF000U) == _VIEW_CENTER_ << 12)
                newFrame.top += y / 2;

        // follow with bottom side
        if ((resizingMode & 0x00F0U) == _VIEW_BOTTOM_ << 4)
                newFrame.bottom += y;
        else if ((resizingMode & 0x00F0U) == _VIEW_CENTER_ << 4)
                newFrame.bottom += y / 2;

        if (newFrame.LeftTop() != fParentOffset) {
                // move view
                _MoveTo((int32)roundf(newFrame.left), (int32)roundf(newFrame.top));
        }

        if (newFrame != Frame()) {
                // resize view
                int32 widthDiff = (int32)(newFrame.Width() - fBounds.Width());
                int32 heightDiff = (int32)(newFrame.Height() - fBounds.Height());
                _ResizeBy(widthDiff, heightDiff);
        }
}


void
BView::_Activate(bool active)
{
        WindowActivated(active);

        for (BView* child = fFirstChild; child != NULL;
                        child = child->fNextSibling) {
                child->_Activate(active);
        }
}


void
BView::_Attach()
{
        if (fOwner != NULL) {
                // unmask state flags to force [re]syncing with the app_server
                fState->valid_flags &= ~(B_VIEW_WHICH_VIEW_COLOR_BIT
                        | B_VIEW_WHICH_LOW_COLOR_BIT | B_VIEW_WHICH_HIGH_COLOR_BIT);

                if (fState->which_view_color != B_NO_COLOR)
                        SetViewUIColor(fState->which_view_color,
                                fState->which_view_color_tint);

                if (fState->which_high_color != B_NO_COLOR)
                        SetHighUIColor(fState->which_high_color,
                                fState->which_high_color_tint);

                if (fState->which_low_color != B_NO_COLOR)
                        SetLowUIColor(fState->which_low_color,
                                fState->which_low_color_tint);
        }

        AttachedToWindow();

        fAttached = true;

        // after giving the view a chance to do this itself,
        // check for the B_PULSE_NEEDED flag and make sure the
        // window set's up the pulse messaging
        if (fOwner) {
                if (fFlags & B_PULSE_NEEDED) {
                        _CheckLock();
                        if (fOwner->fPulseRunner == NULL)
                                fOwner->SetPulseRate(fOwner->PulseRate());
                }

                if (!fOwner->IsHidden())
                        Invalidate();
        }

        for (BView* child = fFirstChild; child != NULL;
                        child = child->fNextSibling) {
                // we need to check for fAttached as new views could have been
                // added in AttachedToWindow() - and those are already attached
                if (!child->fAttached)
                        child->_Attach();
        }

        AllAttached();
}


void
BView::_ColorsUpdated(BMessage* message)
{
        if (fTopLevelView
                && fLayoutData->fLayout != NULL
                && !fState->IsValid(B_VIEW_WHICH_VIEW_COLOR_BIT)) {
                SetViewUIColor(B_PANEL_BACKGROUND_COLOR);
                SetHighUIColor(B_PANEL_TEXT_COLOR);
        }

        rgb_color color;

        const char* colorName = ui_color_name(fState->which_view_color);
        if (colorName != NULL && message->FindColor(colorName, &color) == B_OK) {
                fState->view_color = tint_color(color, fState->which_view_color_tint);
                fState->valid_flags |= B_VIEW_VIEW_COLOR_BIT;
        }

        colorName = ui_color_name(fState->which_low_color);
        if (colorName != NULL && message->FindColor(colorName, &color) == B_OK) {
                fState->low_color = tint_color(color, fState->which_low_color_tint);
                fState->valid_flags |= B_VIEW_LOW_COLOR_BIT;
        }

        colorName = ui_color_name(fState->which_high_color);
        if (colorName != NULL && message->FindColor(colorName, &color) == B_OK) {
                fState->high_color = tint_color(color, fState->which_high_color_tint);
                fState->valid_flags |= B_VIEW_HIGH_COLOR_BIT;
        }

        MessageReceived(message);

        for (BView* child = fFirstChild; child != NULL;
                        child = child->fNextSibling)
                child->_ColorsUpdated(message);

        Invalidate();
}


void
BView::_Detach()
{
        DetachedFromWindow();
        fAttached = false;

        for (BView* child = fFirstChild; child != NULL;
                        child = child->fNextSibling) {
                child->_Detach();
        }

        AllDetached();

        if (fOwner) {
                _CheckLock();

                if (!fOwner->IsHidden())
                        Invalidate();

                // make sure our owner doesn't need us anymore

                if (fOwner->CurrentFocus() == this) {
                        MakeFocus(false);
                        // MakeFocus() is virtual and might not be
                        // passing through to the BView version,
                        // but we need to make sure at this point
                        // that we are not the focus view anymore.
                        if (fOwner->CurrentFocus() == this)
                                fOwner->_SetFocus(NULL, true);
                }

                if (fOwner->fDefaultButton == this)
                        fOwner->SetDefaultButton(NULL);

                if (fOwner->fKeyMenuBar == this)
                        fOwner->fKeyMenuBar = NULL;

                if (fOwner->fLastMouseMovedView == this)
                        fOwner->fLastMouseMovedView = NULL;

                if (fOwner->fLastViewToken == _get_object_token_(this))
                        fOwner->fLastViewToken = B_NULL_TOKEN;

                _SetOwner(NULL);
        }
}


void
BView::_Draw(BRect updateRect)
{
        if (IsHidden(this) || !(Flags() & B_WILL_DRAW))
                return;

        // NOTE: if ViewColor() == B_TRANSPARENT_COLOR and no B_WILL_DRAW
        // -> View is simply not drawn at all

        _SwitchServerCurrentView();

        ConvertFromScreen(&updateRect);

        // TODO: make states robust (the hook implementation could
        // mess things up if it uses non-matching Push- and PopState(),
        // we would not be guaranteed to still have the same state on
        // the stack after having called Draw())
        PushState();
        Draw(updateRect);
        PopState();
        Flush();
}


void
BView::_DrawAfterChildren(BRect updateRect)
{
        if (IsHidden(this) || !(Flags() & B_WILL_DRAW)
                || !(Flags() & B_DRAW_ON_CHILDREN))
                return;

        _SwitchServerCurrentView();

        ConvertFromScreen(&updateRect);

        // TODO: make states robust (see above)
        PushState();
        DrawAfterChildren(updateRect);
        PopState();
        Flush();
}


void
BView::_FontsUpdated(BMessage* message)
{
        MessageReceived(message);

        for (BView* child = fFirstChild; child != NULL;
                        child = child->fNextSibling) {
                child->_FontsUpdated(message);
        }
}


void
BView::_Pulse()
{
        if ((Flags() & B_PULSE_NEEDED) != 0)
                Pulse();

        for (BView* child = fFirstChild; child != NULL;
                        child = child->fNextSibling) {
                child->_Pulse();
        }
}


void
BView::_UpdateStateForRemove()
{
        // TODO: _CheckLockAndSwitchCurrent() would be good enough, no?
        if (!_CheckOwnerLockAndSwitchCurrent())
                return;

        fState->UpdateFrom(*fOwner->fLink);
//      if (!fState->IsValid(B_VIEW_FRAME_BIT)) {
//              fOwner->fLink->StartMessage(AS_VIEW_GET_COORD);
//
//              status_t code;
//              if (fOwner->fLink->FlushWithReply(code) == B_OK
//                      && code == B_OK) {
//                      fOwner->fLink->Read<BPoint>(&fParentOffset);
//                      fOwner->fLink->Read<BRect>(&fBounds);
//                      fState->valid_flags |= B_VIEW_FRAME_BIT;
//              }
//      }

        // update children as well

        for (BView* child = fFirstChild; child != NULL;
                        child = child->fNextSibling) {
                if (child->fOwner)
                        child->_UpdateStateForRemove();
        }
}


inline void
BView::_UpdatePattern(::pattern pattern)
{
        if (fState->IsValid(B_VIEW_PATTERN_BIT) && pattern == fState->pattern)
                return;

        if (fOwner) {
                _CheckLockAndSwitchCurrent();

                fOwner->fLink->StartMessage(AS_VIEW_SET_PATTERN);
                fOwner->fLink->Attach< ::pattern>(pattern);

                fState->valid_flags |= B_VIEW_PATTERN_BIT;
        }

        fState->pattern = pattern;
}


void
BView::_FlushIfNotInTransaction()
{
        if (!fOwner->fInTransaction) {
                fOwner->Flush();
        }
}


BShelf*
BView::_Shelf() const
{
        return fShelf;
}


void
BView::_SetShelf(BShelf* shelf)
{
        if (fShelf != NULL && fOwner != NULL)
                fOwner->RemoveHandler(fShelf);

        fShelf = shelf;

        if (fShelf != NULL && fOwner != NULL)
                fOwner->AddHandler(fShelf);
}


status_t
BView::_SetViewBitmap(const BBitmap* bitmap, BRect srcRect, BRect dstRect,
        uint32 followFlags, uint32 options)
{
        if (!_CheckOwnerLockAndSwitchCurrent())
                return B_ERROR;

        int32 serverToken = bitmap ? bitmap->_ServerToken() : -1;

        fOwner->fLink->StartMessage(AS_VIEW_SET_VIEW_BITMAP);
        fOwner->fLink->Attach<int32>(serverToken);
        fOwner->fLink->Attach<BRect>(srcRect);
        fOwner->fLink->Attach<BRect>(dstRect);
        fOwner->fLink->Attach<int32>(followFlags);
        fOwner->fLink->Attach<int32>(options);

        status_t status = B_ERROR;
        fOwner->fLink->FlushWithReply(status);

        return status;
}


bool
BView::_CheckOwnerLockAndSwitchCurrent() const
{
        STRACE(("BView(%s)::_CheckOwnerLockAndSwitchCurrent()\n", Name()));

        if (fOwner == NULL) {
                debugger("View method requires owner and doesn't have one.");
                return false;
        }

        _CheckLockAndSwitchCurrent();

        return true;
}


bool
BView::_CheckOwnerLock() const
{
        if (fOwner) {
                fOwner->check_lock();
                return true;
        } else {
                debugger("View method requires owner and doesn't have one.");
                return false;
        }
}


void
BView::_CheckLockAndSwitchCurrent() const
{
        STRACE(("BView(%s)::_CheckLockAndSwitchCurrent()\n", Name()));

        if (!fOwner)
                return;

        fOwner->check_lock();

        _SwitchServerCurrentView();
}


void
BView::_CheckLock() const
{
        if (fOwner)
                fOwner->check_lock();
}


void
BView::_SwitchServerCurrentView() const
{
        int32 serverToken = _get_object_token_(this);

        if (fOwner->fLastViewToken != serverToken) {
                STRACE(("contacting app_server... sending token: %" B_PRId32 "\n",
                        serverToken));
                fOwner->fLink->StartMessage(AS_SET_CURRENT_VIEW);
                fOwner->fLink->Attach<int32>(serverToken);

                fOwner->fLastViewToken = serverToken;
        }
}


status_t
BView::ScrollWithMouseWheelDelta(BScrollBar* scrollBar, float delta)
{
        if (scrollBar == NULL || delta == 0.0f)
                return B_BAD_VALUE;

        float smallStep;
        float largeStep;
        scrollBar->GetSteps(&smallStep, &largeStep);

        // pressing the shift key scrolls faster (following the pseudo-standard set
        // by other desktop environments).
        if ((modifiers() & B_SHIFT_KEY) != 0)
                delta *= largeStep;
        else
                delta *= smallStep * 3;

        scrollBar->SetValue(scrollBar->Value() + delta);

        return B_OK;
}


#if __GNUC__ == 2


extern "C" void
_ReservedView1__5BView(BView* view, BRect rect)
{
        view->BView::DrawAfterChildren(rect);
}


extern "C" void
_ReservedView2__5BView(BView* view)
{
        // MinSize()
        perform_data_min_size data;
        view->Perform(PERFORM_CODE_MIN_SIZE, &data);
}


extern "C" void
_ReservedView3__5BView(BView* view)
{
        // MaxSize()
        perform_data_max_size data;
        view->Perform(PERFORM_CODE_MAX_SIZE, &data);
}


extern "C" BSize
_ReservedView4__5BView(BView* view)
{
        // PreferredSize()
        perform_data_preferred_size data;
        view->Perform(PERFORM_CODE_PREFERRED_SIZE, &data);
        return data.return_value;
}


extern "C" BAlignment
_ReservedView5__5BView(BView* view)
{
        // LayoutAlignment()
        perform_data_layout_alignment data;
        view->Perform(PERFORM_CODE_LAYOUT_ALIGNMENT, &data);
        return data.return_value;
}


extern "C" bool
_ReservedView6__5BView(BView* view)
{
        // HasHeightForWidth()
        perform_data_has_height_for_width data;
        view->Perform(PERFORM_CODE_HAS_HEIGHT_FOR_WIDTH, &data);
        return data.return_value;
}


extern "C" void
_ReservedView7__5BView(BView* view, float width, float* min, float* max,
        float* preferred)
{
        // GetHeightForWidth()
        perform_data_get_height_for_width data;
        data.width = width;
        view->Perform(PERFORM_CODE_GET_HEIGHT_FOR_WIDTH, &data);
        if (min != NULL)
                *min = data.min;
        if (max != NULL)
                *max = data.max;
        if (preferred != NULL)
                *preferred = data.preferred;
}


extern "C" void
_ReservedView8__5BView(BView* view, BLayout* layout)
{
        // SetLayout()
        perform_data_set_layout data;
        data.layout = layout;
        view->Perform(PERFORM_CODE_SET_LAYOUT, &data);
}


extern "C" void
_ReservedView9__5BView(BView* view, bool descendants)
{
        // LayoutInvalidated()
        perform_data_layout_invalidated data;
        data.descendants = descendants;
        view->Perform(PERFORM_CODE_LAYOUT_INVALIDATED, &data);
}


extern "C" void
_ReservedView10__5BView(BView* view)
{
        // DoLayout()
        view->Perform(PERFORM_CODE_DO_LAYOUT, NULL);
}


#endif  // __GNUC__ == 2


extern "C" bool
B_IF_GCC_2(_ReservedView11__5BView, _ZN5BView15_ReservedView11Ev)(
        BView* view, BPoint point, BToolTip** _toolTip)
{
        // GetToolTipAt()
        perform_data_get_tool_tip_at data;
        data.point = point;
        data.tool_tip = _toolTip;
        view->Perform(PERFORM_CODE_GET_TOOL_TIP_AT, &data);
        return data.return_value;
}


extern "C" void
B_IF_GCC_2(_ReservedView12__5BView, _ZN5BView15_ReservedView12Ev)(
        BView* view)
{
        // LayoutChanged();
        view->Perform(PERFORM_CODE_LAYOUT_CHANGED, NULL);
}


void BView::_ReservedView13() {}
void BView::_ReservedView14() {}
void BView::_ReservedView15() {}
void BView::_ReservedView16() {}


BView::BView(const BView& other)
        :
        BHandler()
{
        // this is private and not functional, but exported
}


BView&
BView::operator=(const BView& other)
{
        // this is private and not functional, but exported
        return *this;
}


void
BView::_PrintToStream()
{
        printf("BView::_PrintToStream()\n");
        printf("\tName: %s\n"
                "\tParent: %s\n"
                "\tFirstChild: %s\n"
                "\tNextSibling: %s\n"
                "\tPrevSibling: %s\n"
                "\tOwner(Window): %s\n"
                "\tToken: %" B_PRId32 "\n"
                "\tFlags: %" B_PRId32 "\n"
                "\tView origin: (%f,%f)\n"
                "\tView Bounds rectangle: (%f,%f,%f,%f)\n"
                "\tShow level: %d\n"
                "\tTopView?: %s\n"
                "\tBPicture: %s\n"
                "\tVertical Scrollbar %s\n"
                "\tHorizontal Scrollbar %s\n"
                "\tIs Printing?: %s\n"
                "\tShelf?: %s\n"
                "\tEventMask: %" B_PRId32 "\n"
                "\tEventOptions: %" B_PRId32 "\n",
        Name(),
        fParent ? fParent->Name() : "NULL",
        fFirstChild ? fFirstChild->Name() : "NULL",
        fNextSibling ? fNextSibling->Name() : "NULL",
        fPreviousSibling ? fPreviousSibling->Name() : "NULL",
        fOwner ? fOwner->Name() : "NULL",
        _get_object_token_(this),
        fFlags,
        fParentOffset.x, fParentOffset.y,
        fBounds.left, fBounds.top, fBounds.right, fBounds.bottom,
        fShowLevel,
        fTopLevelView ? "YES" : "NO",
        fCurrentPicture? "YES" : "NULL",
        fVerScroller? "YES" : "NULL",
        fHorScroller? "YES" : "NULL",
        fIsPrinting? "YES" : "NO",
        fShelf? "YES" : "NO",
        fEventMask,
        fEventOptions);

        printf("\tState status:\n"
                "\t\tLocalCoordianteSystem: (%f,%f)\n"
                "\t\tPenLocation: (%f,%f)\n"
                "\t\tPenSize: %f\n"
                "\t\tHighColor: [%d,%d,%d,%d]\n"
                "\t\tLowColor: [%d,%d,%d,%d]\n"
                "\t\tViewColor: [%d,%d,%d,%d]\n"
                "\t\tPattern: %" B_PRIx64 "\n"
                "\t\tDrawingMode: %d\n"
                "\t\tLineJoinMode: %d\n"
                "\t\tLineCapMode: %d\n"
                "\t\tMiterLimit: %f\n"
                "\t\tAlphaSource: %d\n"
                "\t\tAlphaFuntion: %d\n"
                "\t\tScale: %f\n"
                "\t\t(Print)FontAliasing: %s\n"
                "\t\tFont Info:\n",
        fState->origin.x, fState->origin.y,
        fState->pen_location.x, fState->pen_location.y,
        fState->pen_size,
        fState->high_color.red, fState->high_color.blue, fState->high_color.green, fState->high_color.alpha,
        fState->low_color.red, fState->low_color.blue, fState->low_color.green, fState->low_color.alpha,
        fState->view_color.red, fState->view_color.blue, fState->view_color.green, fState->view_color.alpha,
        *((uint64*)&(fState->pattern)),
        fState->drawing_mode,
        fState->line_join,
        fState->line_cap,
        fState->miter_limit,
        fState->alpha_source_mode,
        fState->alpha_function_mode,
        fState->scale,
        fState->font_aliasing? "YES" : "NO");

        fState->font.PrintToStream();

        // TODO: also print the line array.
}


void
BView::_PrintTree()
{
        int32 spaces = 2;
        BView* c = fFirstChild; //c = short for: current
        printf( "'%s'\n", Name() );
        if (c != NULL) {
                while(true) {
                        // action block
                        {
                                for (int i = 0; i < spaces; i++)
                                        printf(" ");

                                printf( "'%s'\n", c->Name() );
                        }

                        // go deep
                        if (c->fFirstChild) {
                                c = c->fFirstChild;
                                spaces += 2;
                        } else {
                                // go right
                                if (c->fNextSibling) {
                                        c = c->fNextSibling;
                                } else {
                                        // go up
                                        while (!c->fParent->fNextSibling && c->fParent != this) {
                                                c = c->fParent;
                                                spaces -= 2;
                                        }

                                        // that enough! We've reached this view.
                                        if (c->fParent == this)
                                                break;

                                        c = c->fParent->fNextSibling;
                                        spaces -= 2;
                                }
                        }
                }
        }
}


// #pragma mark -


BLayoutItem*
BView::Private::LayoutItemAt(int32 index)
{
        return fView->fLayoutData->fLayoutItems.ItemAt(index);
}


int32
BView::Private::CountLayoutItems()
{
        return fView->fLayoutData->fLayoutItems.CountItems();
}


void
BView::Private::RegisterLayoutItem(BLayoutItem* item)
{
        fView->fLayoutData->fLayoutItems.AddItem(item);
}


void
BView::Private::DeregisterLayoutItem(BLayoutItem* item)
{
        fView->fLayoutData->fLayoutItems.RemoveItem(item);
}


bool
BView::Private::MinMaxValid()
{
        return fView->fLayoutData->fMinMaxValid;
}


bool
BView::Private::WillLayout()
{
        BView::LayoutData* data = fView->fLayoutData;
        if (data->fLayoutInProgress)
                return false;
        if (data->fNeedsRelayout || !data->fLayoutValid || !data->fMinMaxValid)
                return true;
        return false;
}