root/src/tests/kits/interface/layout/widget_layout_test/View.cpp
/*
 * Copyright 2007, Ingo Weinhold <bonefish@cs.tu-berlin.de>.
 * All rights reserved. Distributed under the terms of the MIT License.
 */

#include "View.h"

#include <stdio.h>

#include <Region.h>
#include <View.h>


View::View()
        : fFrame(0, 0, 0, 0),
          fContainer(NULL),
          fParent(NULL),
          fChildren(),
          fViewColor(B_TRANSPARENT_32_BIT),
          fLayoutValid(false)
{
}


View::View(BRect frame)
        : fFrame(frame),
          fContainer(NULL),
          fParent(NULL),
          fChildren(),
          fViewColor(B_TRANSPARENT_32_BIT),
          fLayoutValid(false)
{
}


View::~View()
{
        // delete children
        for (int32 i = CountChildren() - 1; i >= 0; i--)
                delete RemoveChild(i);
}


void
View::SetFrame(BRect frame)
{
        if (frame != fFrame && frame.IsValid()) {
                BRect oldFrame(frame);
                Invalidate();
                fFrame = frame;
                Invalidate();

                FrameChanged(oldFrame, frame);
        }

        // relayout if necessary
        if (!fLayoutValid) {
                Layout();
                fLayoutValid = true;
        }
}


BRect
View::Frame() const
{
        return fFrame;
}


BRect
View::Bounds() const
{
        return BRect(fFrame).OffsetToCopy(B_ORIGIN);
}


void
View::SetLocation(BPoint location)
{
        SetFrame(fFrame.OffsetToCopy(location));
}


BPoint
View::Location() const
{
        return fFrame.LeftTop();
}


void
View::SetSize(BSize size)
{
        BRect frame(fFrame);
        frame.right = frame.left + size.width;
        frame.bottom = frame.top + size.height;
        SetFrame(frame);
}


BSize
View::Size() const
{
        return Frame().Size();
}


BSize
View::MinSize()
{
        return BSize(-1, -1);
}


BSize
View::MaxSize()
{
        return BSize(B_SIZE_UNLIMITED, B_SIZE_UNLIMITED);
}


BSize
View::PreferredSize()
{
        return MinSize();
}


BAlignment
View::Alignment()
{
        return BAlignment(B_ALIGN_HORIZONTAL_CENTER, B_ALIGN_VERTICAL_CENTER);
}


BPoint
View::LocationInContainer() const
{
        BPoint location = fFrame.LeftTop();
        return (fParent ? fParent->LocationInContainer() + location : location);
}


BRect
View::FrameInContainer() const
{
        BRect frame(fFrame);
        return frame.OffsetToCopy(LocationInContainer());
}


BPoint
View::ConvertFromContainer(BPoint point) const
{
        return point - LocationInContainer();
}


BRect
View::ConvertFromContainer(BRect rect) const
{
        return rect.OffsetBySelf(-LocationInContainer());
}


BPoint
View::ConvertToContainer(BPoint point) const
{
        return point + LocationInContainer();
}


BRect
View::ConvertToContainer(BRect rect) const
{
        return rect.OffsetBySelf(LocationInContainer());
}


View*
View::Parent() const
{
        return fParent;
}


BView*
View::Container() const
{
        return fContainer;
}


bool
View::AddChild(View* child)
{
        if (!child)
                return false;

        if (child->Parent() || child->Container()) {
                fprintf(stderr, "View::AddChild(): view %p already has a parent "
                        "or is the container view\n", child);
                return false;
        }

        if (!fChildren.AddItem(child))
                return false;

        child->_AddedToParent(this);

        child->Invalidate();
        InvalidateLayout();

        return true;
}


bool
View::RemoveChild(View* child)
{
        if (!child)
                return false;

        return RemoveChild(IndexOfChild(child));
}


View*
View::RemoveChild(int32 index)
{
        if (index < 0 || index >= fChildren.CountItems())
                return NULL;

        View* child = ChildAt(index);
        child->Invalidate();
        child->_RemovingFromParent();
        fChildren.RemoveItem(index);

        InvalidateLayout();

        return child;
}


int32
View::CountChildren() const
{
        return fChildren.CountItems();
}


View*
View::ChildAt(int32 index) const
{
        return (View*)fChildren.ItemAt(index);
}


View*
View::ChildAt(BPoint point) const
{
        for (int32 i = 0; View* child = ChildAt(i); i++) {
                if (child->Frame().Contains(point))
                        return child;
        }

        return NULL;
}


View*
View::AncestorAt(BPoint point, BPoint* localPoint) const
{
        if (!Bounds().Contains(point))
                return NULL;

        View* view = const_cast<View*>(this);

        // Iterate deeper down the hierarchy, until we reach a view that
        // doesn't have a child at the location.
        while (true) {
                View* child = view->ChildAt(point);
                if (!child) {
                        if (localPoint)
                                *localPoint = point;
                        return view;
                }

                view = child;
                point -= view->Frame().LeftTop();
        }
}


int32
View::IndexOfChild(View* child) const
{
        return (child ? fChildren.IndexOf(child) : -1);
}


void
View::Invalidate(BRect rect)
{
        if (fContainer) {
                rect = rect & Bounds();
                fContainer->Invalidate(rect.OffsetByCopy(LocationInContainer()));
        }
}


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


void
View::InvalidateLayout()
{
//printf("%p->View::InvalidateLayout(): %d\n", this, fLayoutValid);
        if (fLayoutValid) {
                fLayoutValid = false;
                if (fParent)
                        fParent->InvalidateLayout();
        }
}


bool
View::IsLayoutValid() const
{
        return fLayoutValid;
}


void
View::SetViewColor(rgb_color color)
{
        fViewColor = color;
}


void
View::Draw(BView* container, BRect updateRect)
{
}


void
View::MouseDown(BPoint where, uint32 buttons, int32 modifiers)
{
}


void
View::MouseUp(BPoint where, uint32 buttons, int32 modifiers)
{
}


void
View::MouseMoved(BPoint where, uint32 buttons, int32 modifiers)
{
}


void
View::AddedToContainer()
{
}


void
View::RemovingFromContainer()
{
}


void
View::FrameChanged(BRect oldFrame, BRect newFrame)
{
}


void
View::Layout()
{
        // simply trigger relayouting the children
        for (int32 i = 0; View* child = ChildAt(i); i++)
                child->SetFrame(child->Frame());
}


void
View::_AddedToParent(View* parent)
{
        fParent = parent;

        if (parent->Container()) {
                Invalidate();
                _AddedToContainer(parent->Container());
        }
}


void
View::_RemovingFromParent()
{
        if (fContainer)
                _RemovingFromContainer();

        fParent = NULL;
}


void
View::_AddedToContainer(BView* container)
{
        fContainer = container;

        AddedToContainer();

        for (int32 i = 0; View* child = ChildAt(i); i++)
                child->_AddedToContainer(fContainer);
}


void
View::_RemovingFromContainer()
{
        for (int32 i = 0; View* child = ChildAt(i); i++)
                child->_RemovingFromContainer();

        RemovingFromContainer();

        fContainer = NULL;
}


void
View::_Draw(BView* container, BRect updateRect)
{
        // compute the clipping region
        BRegion region(Bounds());
        for (int32 i = 0; View* child = ChildAt(i); i++)
                region.Exclude(child->Frame());

        if (region.Frame().IsValid()) {
                // set the clipping region
                container->ConstrainClippingRegion(&region);

                // draw the background, if it isn't transparent
                if (fViewColor.alpha != 0) {
                        container->SetLowColor(fViewColor);
                        container->FillRect(updateRect, B_SOLID_LOW);
                }

                // draw this view
                Draw(container, updateRect);

                // revert the clipping region
                region.Set(Bounds());
                container->ConstrainClippingRegion(&region);
        }

        // draw the children
        if (CountChildren() > 0) {
                container->PushState();

                for (int32 i = 0; View* child = ChildAt(i); i++) {
                        BRect childFrame = child->Frame();
                        BRect childUpdateRect = updateRect & childFrame;
                        if (childUpdateRect.IsValid()) {
                                // set origin
                                childUpdateRect.OffsetBy(-childFrame.LeftTop());
                                container->SetOrigin(childFrame.LeftTop());

                                // draw
                                child->_Draw(container, childUpdateRect);
                        }
                }

                container->PopState();
        }
}