#include "Gwen/Controls/Base.h"
#include "Gwen/Controls/Label.h"
#include "Gwen/Gwen.h"
#include "Gwen/BaseRender.h"
#include "Gwen/Skin.h"
#include "Gwen/Platform.h"
#include "Gwen/DragAndDrop.h"
#include "Gwen/ToolTip.h"
#include "Gwen/Utility.h"
#include <list>

#ifndef GWEN_NO_ANIMATION
#include "Gwen/Anim.h"
#endif

using namespace Gwen;
using namespace Controls;

Base::Base(Base* pParent, const Gwen::String& Name)
{
    m_Parent = NULL;
    m_ActualParent = NULL;
    m_InnerPanel = NULL;
    m_Skin = NULL;
    SetName(Name);
    SetParent(pParent);
    m_bHidden = false;
    m_Bounds = Gwen::Rect(0, 0, 10, 10);
    m_Padding = Padding(0, 0, 0, 0);
    m_Margin = Margin(0, 0, 0, 0);
    m_iDock = 0;
    m_DragAndDrop_Package = NULL;
    RestrictToParent(false);
    SetMouseInputEnabled(true);
    SetKeyboardInputEnabled(false);
    Invalidate();
    SetCursor(Gwen::CursorType::Normal);
    SetToolTip(NULL);
    SetTabable(false);
    SetShouldDrawBackground(true);
    m_bDisabled = false;
    m_bCacheTextureDirty = true;
    m_bCacheToTexture = false;
    m_bIncludeInSize = true;
}

Base::~Base()
{
    {
        Canvas* canvas = GetCanvas();

        if (canvas)
            canvas->PreDeleteCanvas(this);
    }
    Base::List::iterator iter = Children.begin();

    while (iter != Children.end())
    {
        Base* pChild = *iter;
        iter = Children.erase(iter);
        delete pChild;
    }

    for (AccelMap::iterator accelIt = m_Accelerators.begin();
         accelIt != m_Accelerators.end();
         ++accelIt)
    {
        delete accelIt->second;
    }

    m_Accelerators.clear();
    SetParent(NULL);

    if (Gwen::HoveredControl == this)
        Gwen::HoveredControl = NULL;

    if (Gwen::KeyboardFocus == this)
        Gwen::KeyboardFocus = NULL;

    if (Gwen::MouseFocus == this)
        Gwen::MouseFocus = NULL;

    DragAndDrop::ControlDeleted(this);
    ToolTip::ControlDeleted(this);
#ifndef GWEN_NO_ANIMATION
    Anim::Cancel(this);
#endif

    if (m_DragAndDrop_Package)
    {
        delete m_DragAndDrop_Package;
        m_DragAndDrop_Package = NULL;
    }
}

void Base::Invalidate()
{
    m_bNeedsLayout = true;
    m_bCacheTextureDirty = true;
}

void Base::DelayedDelete()
{
    Canvas* canvas = GetCanvas();
    canvas->AddDelayedDelete(this);
}

Canvas* Base::GetCanvas()
{
    Base* pCanvas = m_Parent;

    if (!pCanvas)
        return NULL;

    return pCanvas->GetCanvas();
}

void Base::SetParent(Base* pParent)
{
    if (m_Parent == pParent)
        return;

    if (m_Parent)
        m_Parent->RemoveChild(this);

    m_Parent = pParent;
    m_ActualParent = NULL;

    if (m_Parent)
        m_Parent->AddChild(this);
}

void Base::Dock(int iDock)
{
    if (m_iDock == iDock)
        return;

    m_iDock = iDock;
    Invalidate();
    InvalidateParent();
}

int Base::GetDock()
{
    return m_iDock;
}

bool Base::Hidden() const
{
    return m_bHidden;
}

bool Base::Visible() const
{
    if (Hidden())
        return false;

    if (GetParent())
        return GetParent()->Visible();

    return true;
}

void Base::InvalidateChildren(bool bRecursive)
{
    for (Base::List::iterator it = Children.begin(); it != Children.end(); ++it)
    {
        (*it)->Invalidate();

        if (bRecursive)
            (*it)->InvalidateChildren(bRecursive);
    }

    if (m_InnerPanel)
    {
        for (Base::List::iterator it = m_InnerPanel->Children.begin();
             it != m_InnerPanel->Children.end();
             ++it)
        {
            (*it)->Invalidate();

            if (bRecursive)
                (*it)->InvalidateChildren(bRecursive);
        }
    }
}

void Base::Position(unsigned int pos, int xpadding, int ypadding)
{
    const Rect& bounds = GetParent()->GetInnerBounds();
    const Margin& margin = GetMargin();
    int x = X();
    int y = Y();

    if (pos & Pos::Left)
        x = bounds.x + xpadding + margin.left;

    if (pos & Pos::Right)
        x = bounds.x + (bounds.w - Width() - xpadding - margin.right);

    if (pos & Pos::CenterH)
        x = bounds.x + (bounds.w - Width())/2;

    if (pos & Pos::Top)
        y = bounds.y + ypadding;

    if (pos & Pos::Bottom)
        y = bounds.y + (bounds.h - Height() - ypadding);

    if (pos & Pos::CenterV)
        y = bounds.y + (bounds.h - Height())/2 + ypadding;

    SetPos(x, y);
}

void Base::SendToBack()
{
    if (!m_ActualParent)
        return;

    if (m_ActualParent->Children.front() == this)
        return;

    m_ActualParent->Children.remove(this);
    m_ActualParent->Children.push_front(this);
    InvalidateParent();
}

void Base::BringToFront()
{
    if (!m_ActualParent)
        return;

    if (m_ActualParent->Children.back() == this)
        return;

    m_ActualParent->Children.remove(this);
    m_ActualParent->Children.push_back(this);
    InvalidateParent();
    Redraw();
}

Controls::Base* Base::FindChildByName(const Gwen::String& name, bool bRecursive)
{
    Base::List::iterator iter;

    for (iter = Children.begin(); iter != Children.end(); ++iter)
    {
        Base* pChild = *iter;

        if (!pChild->GetName().empty() && pChild->GetName() == name)
            return pChild;

        if (bRecursive)
        {
            Controls::Base* pSubChild = pChild->FindChildByName(name, true);

            if (pSubChild)
                return pSubChild;
        }
    }

    return NULL;
}

void Base::BringNextToControl(Controls::Base* pChild, bool bBehind)
{
    if (!m_ActualParent)
        return;

    m_ActualParent->Children.remove(this);
    Base::List::iterator it = std::find(
        m_ActualParent->Children.begin(), m_ActualParent->Children.end(), pChild);

    if (it == m_ActualParent->Children.end())
        return BringToFront();

    if (bBehind)
    {
        ++it;

        if (it == m_ActualParent->Children.end())
            return BringToFront();
    }

    m_ActualParent->Children.insert(it, this);
    InvalidateParent();
}

void Base::AddChild(Base* pChild)
{
    if (m_InnerPanel)
    {
        m_InnerPanel->AddChild(pChild);
        return;
    }

    Children.push_back(pChild);
    OnChildAdded(pChild);
    pChild->m_ActualParent = this;
}

void Base::RemoveChild(Base* pChild)
{

    if (m_InnerPanel == pChild)
        m_InnerPanel = NULL;

    if (m_InnerPanel)
        m_InnerPanel->RemoveChild(pChild);

    Children.remove(pChild);
    OnChildRemoved(pChild);
}

void Base::RemoveAllChildren()
{
    while (Children.size() > 0)
    {
        RemoveChild(*Children.begin());
    }
}

unsigned int Base::NumChildren()
{

    return (unsigned int)Children.size();
}

Controls::Base* Base::GetChild(unsigned int i)
{
    if (i >= NumChildren())
        return NULL;

    for (Base::List::iterator iter = Children.begin(); iter != Children.end(); ++iter)
    {
        if (i == 0)
            return *iter;

        i--;
    }

    return NULL;
}

void Base::OnChildAdded(Base* )
{
    Invalidate();
}

void Base::OnChildRemoved(Base* )
{
    Invalidate();
}

Skin::Base* Base::GetSkin(void)
{
    if (m_Skin)
        return m_Skin;

    if (m_Parent)
        return m_Parent->GetSkin();

    Debug::AssertCheck(0, "Base::GetSkin Returning NULL!\n");
    return NULL;
}

void Base::MoveBy(int x, int y)
{
    MoveTo(X()+x, Y()+y);
}

void Base::MoveTo(int x, int y)
{
    if (m_bRestrictToParent && GetParent())
    {
        Base* pParent = GetParent();

        if (x-GetPadding().left < pParent->GetMargin().left)
            x = pParent->GetMargin().left+GetPadding().left;

        if (y-GetPadding().top < pParent->GetMargin().top)
            y = pParent->GetMargin().top+GetPadding().top;

        if (x+Width()+GetPadding().right > pParent->Width()-pParent->GetMargin().right)
            x = pParent->Width()-pParent->GetMargin().right-Width()-GetPadding().right;

        if (y+Height()+GetPadding().bottom > pParent->Height()-pParent->GetMargin().bottom)
            y = pParent->Height()-pParent->GetMargin().bottom-Height()-GetPadding().bottom;
    }

    SetBounds(x, y, Width(), Height());
}

void Base::SetPos(int x, int y)
{
    SetBounds(x, y, Width(), Height());
}

bool Base::SetSize(int w, int h)
{
    return SetBounds(X(), Y(), w, h);
}

bool Base::SetSize(const Point& p)
{
    return SetSize(p.x, p.y);
}

bool Base::SetBounds(const Gwen::Rect& bounds)
{
    if (m_Bounds == bounds)
        return false;

    const Gwen::Rect oldBounds = GetBounds();
    m_Bounds = bounds;
    OnBoundsChanged(oldBounds);
    return true;
}

bool Base::SetBounds(int x, int y, int w, int h)
{
    return SetBounds(Rect(x,y,w,h));
}

void Base::OnBoundsChanged(Gwen::Rect oldBounds)
{

    if (GetParent())
        GetParent()->OnChildBoundsChanged(oldBounds, this);

    if (m_Bounds.w != oldBounds.w || m_Bounds.h != oldBounds.h)
        Invalidate();

    Redraw();
    UpdateRenderBounds();
}

void Base::OnScaleChanged()
{
    for (Base::List::iterator iter = Children.begin(); iter != Children.end(); ++iter)
    {
        (*iter)->OnScaleChanged();
    }
}

void Base::OnChildBoundsChanged(Gwen::Rect , Base* )
{
}

void Base::Render(Gwen::Skin::Base* )
{
}

void Base::DoCacheRender(Gwen::Skin::Base* skin, Gwen::Controls::Base* pMaster)
{
    Gwen::Renderer::Base* render = skin->GetRender();
    Gwen::Renderer::ICacheToTexture* cache = render->GetCTT();

    if (!cache)
        return;

    Gwen::Point pOldRenderOffset = render->GetRenderOffset();
    Gwen::Rect rOldRegion = render->ClipRegion();

    if (this != pMaster)
    {
        render->AddRenderOffset(GetBounds());
        render->AddClipRegion(GetBounds());
    }
    else
    {
        render->SetRenderOffset(Gwen::Point(0, 0));
        render->SetClipRegion(GetBounds());
    }

    if (m_bCacheTextureDirty && render->ClipRegionVisible())
    {
        render->StartClip();
        {
            if (IsCachedToTexture())
                cache->SetupCacheTexture(this);

            Render(skin);

            if (!Children.empty())
            {

                for (Base::List::iterator iter = Children.begin(); iter != Children.end(); ++iter)
                {
                    Base* pChild = *iter;

                    if (!pChild->Hidden())
                    {

                        pChild->DoRender(skin);
                    }
                }
            }

            if (IsCachedToTexture())
            {
                cache->FinishCacheTexture(this);
                m_bCacheTextureDirty = false;
            }
        }
        render->EndClip();
    }

    render->SetClipRegion(rOldRegion);
    render->StartClip();
    {
        render->SetRenderOffset(pOldRenderOffset);
        cache->DrawCachedControlTexture(this);
    }
    render->EndClip();
}

void Base::DoRender(Gwen::Skin::Base* skin)
{

    if (m_Skin)
        skin = m_Skin;

    Think();
    Gwen::Renderer::Base* render = skin->GetRender();

    if (render->GetCTT() && IsCachedToTexture())
    {
        DoCacheRender(skin, this);
        return;
    }

    RenderRecursive(skin, GetBounds());
}

void Base::RenderRecursive(Gwen::Skin::Base* skin, const Gwen::Rect& cliprect)
{
    Gwen::Renderer::Base* render = skin->GetRender();
    Gwen::Point pOldRenderOffset = render->GetRenderOffset();
    render->AddRenderOffset(cliprect);
    RenderUnder(skin);
    Gwen::Rect rOldRegion = render->ClipRegion();

    if (ShouldClip())
    {
        render->AddClipRegion(cliprect);

        if (!render->ClipRegionVisible())
        {
            render->SetRenderOffset(pOldRenderOffset);
            render->SetClipRegion(rOldRegion);
            return;
        }
    }

    render->StartClip();
    {
        Render(skin);

        if (!Children.empty())
        {

            for (Base::List::iterator iter = Children.begin(); iter != Children.end(); ++iter)
            {
                Base* pChild = *iter;

                if (pChild->Hidden())
                    continue;

                pChild->DoRender(skin);
            }
        }
    }
    render->EndClip();

    {
        render->SetClipRegion(rOldRegion);
        render->StartClip();
        {
            RenderOver(skin);
            RenderFocus(skin);
        }
        render->EndClip();
        render->SetRenderOffset(pOldRenderOffset);
    }
}

void Base::SetSkin(Skin::Base* skin, bool doChildren)
{
    if (m_Skin == skin)
        return;

    m_Skin = skin;
    Invalidate();
    Redraw();
    OnSkinChanged(skin);

    if (doChildren)
    {
        for (Base::List::iterator it = Children.begin(); it != Children.end(); ++it)
        {
            (*it)->SetSkin(skin, true);
        }
    }
}

void Base::OnSkinChanged(Skin::Base* )
{

}

bool Base::OnMouseWheeled(int iDelta)
{
    if (m_ActualParent)
        return m_ActualParent->OnMouseWheeled(iDelta);

    return false;
}

void Base::OnMouseMoved(int , int , int , int )
{
}

void Base::OnMouseEnter()
{
    onHoverEnter.Call(this);

    if (GetToolTip())
        ToolTip::Enable(this);
    else if (GetParent() && GetParent()->GetToolTip())
        ToolTip::Enable(GetParent());

    Redraw();
}

void Base::OnMouseLeave()
{
    onHoverLeave.Call(this);

    if (GetToolTip())
        ToolTip::Disable(this);

    Redraw();
}

bool Base::IsHovered()
{
    return Gwen::HoveredControl == this;
}

bool Base::ShouldDrawHover()
{
    return Gwen::MouseFocus == this || Gwen::MouseFocus == NULL;
}

bool Base::HasFocus()
{
    return Gwen::KeyboardFocus == this;
}

void Base::Focus()
{
    if (Gwen::KeyboardFocus == this)
        return;

    if (Gwen::KeyboardFocus)
        Gwen::KeyboardFocus->OnLostKeyboardFocus();

    Gwen::KeyboardFocus = this;
    OnKeyboardFocus();
    Redraw();
}

void Base::Blur()
{
    if (Gwen::KeyboardFocus != this)
        return;

    Gwen::KeyboardFocus = NULL;
    OnLostKeyboardFocus();
    Redraw();
}

bool Base::IsOnTop()
{
    if (!GetParent())
        return false;

    Base::List::iterator iter = GetParent()->Children.begin();
    Base* pChild = *iter;

    if (pChild == this)
        return true;

    return false;
}

void Base::Touch()
{
    if (GetParent())
        GetParent()->OnChildTouched(this);
}

void Base::OnChildTouched(Controls::Base* )
{
    Touch();
}

Base* Base::GetControlAt(int x, int y, bool bOnlyIfMouseEnabled)
{
    if (Hidden())
        return NULL;

    if (x < 0 || y < 0 || x >= Width() || y >= Height())
        return NULL;

    Base::List::reverse_iterator iter;

    for (iter = Children.rbegin(); iter != Children.rend(); ++iter)
    {
        Base* pChild = *iter;
        Base* pFound = NULL;
        pFound = pChild->GetControlAt(x-pChild->X(), y-pChild->Y(), bOnlyIfMouseEnabled);

        if (pFound)
            return pFound;
    }

    if (bOnlyIfMouseEnabled && !GetMouseInputEnabled())
        return NULL;

    return this;
}

void Base::Layout(Skin::Base* skin)
{
    if (skin->GetRender()->GetCTT() && IsCachedToTexture())
        skin->GetRender()->GetCTT()->CreateControlCacheTexture(this);
}

void Base::RecurseLayout(Skin::Base* skin)
{
    if (m_Skin)
        skin = m_Skin;

    if (Hidden())
        return;

    if (NeedsLayout())
    {
        m_bNeedsLayout = false;
        Layout(skin);
    }

    Gwen::Rect rBounds = GetRenderBounds();

    rBounds.x += m_Padding.left;
    rBounds.w -= m_Padding.left + m_Padding.right;
    rBounds.y += m_Padding.top;
    rBounds.h -= m_Padding.top + m_Padding.bottom;

    for (Base::List::iterator iter = Children.begin(); iter != Children.end(); ++iter)
    {
        Base* pChild = *iter;

        if (pChild->Hidden())
            continue;

        int iDock = pChild->GetDock();

        if (iDock & Pos::Fill)
            continue;

        if (iDock & Pos::Top)
        {
            const Margin& margin = pChild->GetMargin();
            pChild->SetBounds(rBounds.x+margin.left,
                              rBounds.y+margin.top,
                              rBounds.w-margin.left-margin.right,
                              pChild->Height());
            int iHeight = margin.top+margin.bottom+pChild->Height();
            rBounds.y += iHeight;
            rBounds.h -= iHeight;
        }

        if (iDock & Pos::Left)
        {
            const Margin& margin = pChild->GetMargin();
            pChild->SetBounds(rBounds.x+margin.left,
                              rBounds.y+margin.top,
                              pChild->Width(),
                              rBounds.h-margin.top-margin.bottom);
            int iWidth = margin.left+margin.right+pChild->Width();
            rBounds.x += iWidth;
            rBounds.w -= iWidth;
        }

        if (iDock & Pos::Right)
        {

            const Margin& margin = pChild->GetMargin();
            pChild->SetBounds((rBounds.x+rBounds.w)-pChild->Width()-margin.right,
                              rBounds.y+margin.top,
                              pChild->Width(),
                              rBounds.h-margin.top-margin.bottom);
            int iWidth = margin.left+margin.right+pChild->Width();
            rBounds.w -= iWidth;
        }

        if (iDock & Pos::Bottom)
        {

            const Margin& margin = pChild->GetMargin();
            pChild->SetBounds(rBounds.x+margin.left,
                              (rBounds.y+rBounds.h)-pChild->Height()-margin.bottom,
                              rBounds.w-margin.left-margin.right,
                              pChild->Height());
            rBounds.h -= pChild->Height()+margin.bottom+margin.top;
        }

        pChild->RecurseLayout(skin);
    }

    m_InnerBounds = rBounds; 

    for (Base::List::iterator iter = Children.begin(); iter != Children.end(); ++iter)
    {
        Base* pChild = *iter;
        int iDock = pChild->GetDock();

        if (!(iDock&Pos::Fill))
            continue;

        const Margin& margin = pChild->GetMargin();
        pChild->SetBounds(rBounds.x+margin.left, rBounds.y+margin.top,
                          rBounds.w-margin.left-margin.right, rBounds.h-margin.top-margin.bottom);
        pChild->RecurseLayout(skin);
    }

    PostLayout(skin);

    if (IsTabable() && !IsDisabled())
    {
        if (!GetCanvas()->FirstTab)
            GetCanvas()->FirstTab = this;

        if (!GetCanvas()->NextTab)
            GetCanvas()->NextTab = this;
    }

    if (Gwen::KeyboardFocus == this)
        GetCanvas()->NextTab = NULL;
}

bool Base::IsChild(Controls::Base* pChild)
{
    for (Base::List::iterator iter = Children.begin(); iter != Children.end(); ++iter)
    {
        if (pChild == (*iter))
            return true;
    }

    return false;
}

Gwen::Point Base::LocalPosToCanvas(const Gwen::Point& pnt)
{
    if (m_Parent)
    {
        int x = pnt.x+X();
        int y = pnt.y+Y();

        if (m_Parent->m_InnerPanel && m_Parent->m_InnerPanel->IsChild(this))
        {
            x += m_Parent->m_InnerPanel->X();
            y += m_Parent->m_InnerPanel->Y();
        }

        return m_Parent->LocalPosToCanvas(Gwen::Point(x, y));
    }

    return pnt;
}

Gwen::Point Base::CanvasPosToLocal(const Gwen::Point& pnt)
{
    if (m_Parent)
    {
        int x = pnt.x-X();
        int y = pnt.y-Y();

        if (m_Parent->m_InnerPanel && m_Parent->m_InnerPanel->IsChild(this))
        {
            x -= m_Parent->m_InnerPanel->X();
            y -= m_Parent->m_InnerPanel->Y();
        }

        return m_Parent->CanvasPosToLocal(Gwen::Point(x, y));
    }

    return pnt;
}

bool Base::IsMenuComponent()
{
    if (!m_Parent)
        return false;

    return m_Parent->IsMenuComponent();
}

void Base::CloseMenus()
{
    for (Base::List::iterator it = Children.begin(); it != Children.end(); ++it)
    {
        (*it)->CloseMenus();
    }
}

void Base::UpdateRenderBounds()
{
    m_RenderBounds.x = 0;
    m_RenderBounds.y = 0;
    m_RenderBounds.w = m_Bounds.w;
    m_RenderBounds.h = m_Bounds.h;
}

void Base::UpdateCursor()
{
    Platform::SetCursor(m_Cursor);
}

DragAndDrop::Package* Base::DragAndDrop_GetPackage(int , int )
{
    return m_DragAndDrop_Package;
}

bool Base::DragAndDrop_HandleDrop(Gwen::DragAndDrop::Package* , int , int )
{
    DragAndDrop::SourceControl->SetParent(this);
    return true;
}

bool Base::DragAndDrop_Draggable()
{
    if (!m_DragAndDrop_Package)
        return false;

    return m_DragAndDrop_Package->draggable;
}

void Base::DragAndDrop_SetPackage(bool bDraggable, const String& strName, void* pUserData)
{
    if (!m_DragAndDrop_Package)
        m_DragAndDrop_Package = new Gwen::DragAndDrop::Package();

    m_DragAndDrop_Package->draggable = bDraggable;
    m_DragAndDrop_Package->name = strName;
    m_DragAndDrop_Package->userdata = pUserData;
}

void Base::DragAndDrop_StartDragging(Gwen::DragAndDrop::Package* pPackage, int x, int y)
{
    pPackage->holdoffset = CanvasPosToLocal(Gwen::Point(x, y));
    pPackage->drawcontrol = this;
}

bool Base::SizeToChildren(bool w, bool h)
{
    Gwen::Point size = ChildrenSize();
    size.y += GetPadding().bottom;
    size.x += GetPadding().right;
    return SetSize(w ? size.x : Width(), h ? size.y : Height());
}

Gwen::Point Base::ChildrenSize()
{
    Gwen::Point size;

    for (Base::List::iterator iter = Children.begin(); iter != Children.end(); ++iter)
    {
        Base* pChild = *iter;

        if (pChild->Hidden())
            continue;

        if (!pChild->ShouldIncludeInSize())
            continue;

        size.x = Gwen::Max(size.x, pChild->Right());
        size.y = Gwen::Max(size.y, pChild->Bottom());
    }

    return size;
}

void Base::SetPadding(const Padding& padding)
{
    if (m_Padding.left == padding.left &&
        m_Padding.top == padding.top &&
        m_Padding.right == padding.right &&
        m_Padding.bottom == padding.bottom)
        return;

    m_Padding = padding;
    Invalidate();
    InvalidateParent();
}

void Base::SetMargin(const Margin& margin)
{
    if (m_Margin.top == margin.top &&
        m_Margin.left == margin.left &&
        m_Margin.bottom == margin.bottom &&
        m_Margin.right == margin.right)
        return;

    m_Margin = margin;
    Invalidate();
    InvalidateParent();
}

bool Base::HandleAccelerator(Gwen::String& accelerator)
{
    if (Gwen::KeyboardFocus == this || !AccelOnlyFocus())
    {
        AccelMap::iterator iter = m_Accelerators.find(accelerator);

        if (iter != m_Accelerators.end())
        {
            iter->second->Call(this);
            return true;
        }
    }

    for (Base::List::iterator it = Children.begin(); it != Children.end(); ++it)
    {
        if ((*it)->HandleAccelerator(accelerator))
            return true;
    }

    return false;
}

bool Base::OnKeyPress(int iKey, bool bPress)
{
    bool bHandled = false;

    switch (iKey)
    {
    case Key::Tab:
        bHandled = OnKeyTab(bPress);
        break;

    case Key::Space:
        bHandled = OnKeySpace(bPress);
        break;

    case Key::Home:
        bHandled = OnKeyHome(bPress);
        break;

    case Key::End:
        bHandled = OnKeyEnd(bPress);
        break;

    case Key::Return:
        bHandled = OnKeyReturn(bPress);
        break;

    case Key::Backspace:
        bHandled = OnKeyBackspace(bPress);
        break;

    case Key::Delete:
        bHandled = OnKeyDelete(bPress);
        break;

    case Key::Right:
        bHandled = OnKeyRight(bPress);
        break;

    case Key::Left:
        bHandled = OnKeyLeft(bPress);
        break;

    case Key::Up:
        bHandled = OnKeyUp(bPress);
        break;

    case Key::Down:
        bHandled = OnKeyDown(bPress);
        break;

    case Key::Escape:
        bHandled = OnKeyEscape(bPress);
        break;

    default:
        break;
    }

    if (!bHandled && GetParent())
        GetParent()->OnKeyPress(iKey, bPress);

    return bHandled;
}

bool Base::OnKeyRelease(int iKey)
{
    return OnKeyPress(iKey, false);
}

bool Base::OnKeyTab(bool bDown)
{
    if (!bDown)
        return true;

    if (GetCanvas()->NextTab)
    {
        GetCanvas()->NextTab->Focus();
        Redraw();
    }

    return true;
}

void Base::RenderFocus(Gwen::Skin::Base* skin)
{
    if (Gwen::KeyboardFocus != this)
        return;

    if (!IsTabable())
        return;

    skin->DrawKeyboardHighlight(this, GetRenderBounds(), 3);
}

void Base::SetToolTip(const String& strText)
{
    Label* tooltip = new Label(this);
    tooltip->SetText(strText);
    tooltip->SetTextColorOverride(GetSkin()->Colors.TooltipText);
    tooltip->SetPadding(Padding(5, 3, 5, 3));
    tooltip->SizeToContents();
    SetToolTip(tooltip);
}

String Base::GetChildValue(const Gwen::String& strName)
{
    Base* pChild = FindChildByName(strName, true);

    if (!pChild)
        return "";

    return pChild->GetValue();
}

String Base::GetValue()
{

    return "";
}

void Base::SetValue(const String& strValue)
{
}

int Base::GetNamedChildren(Gwen::ControlList& list, const Gwen::String& strName, bool bDeep)
{
    int iFound = 0;
    Base::List::iterator iter;

    for (iter = Children.begin(); iter != Children.end(); ++iter)
    {
        Base* pChild = *iter;

        if (!pChild->GetName().empty() && pChild->GetName() == strName)
        {
            list.Add(pChild);
            iFound++;
        }

        if (!bDeep)
            continue;

        iFound += pChild->GetNamedChildren(list, strName, bDeep);
    }

    return iFound;
}

Gwen::ControlList Base::GetNamedChildren(const Gwen::String& strName, bool bDeep)
{
    Gwen::ControlList list;
    GetNamedChildren(list, strName, bDeep);
    return list;
}

#ifndef GWEN_NO_ANIMATION

void Base::Anim_WidthIn(float fLength, float fDelay, float fEase)
{
    Gwen::Anim::Add(this, new Gwen::Anim::Size::Width(0, Width(), fLength, false, fDelay, fEase));
    SetWidth(0);
}

void Base::Anim_HeightIn(float fLength, float fDelay, float fEase)
{
    Gwen::Anim::Add(this,
                    new Gwen::Anim::Size::Height(0, Height(), fLength, false, fDelay, fEase));
    SetHeight(0);
}

void Base::Anim_WidthOut(float fLength, bool bHide, float fDelay, float fEase)
{
    Gwen::Anim::Add(this, new Gwen::Anim::Size::Width(Width(), 0, fLength, bHide, fDelay, fEase));
}

void Base::Anim_HeightOut(float fLength, bool bHide, float fDelay, float fEase)
{
    Gwen::Anim::Add(this,
                    new Gwen::Anim::Size::Height(Height(), 0, fLength, bHide, fDelay, fEase));
}

#endif
/*
 *  GWEN
 *  Copyright (c) 2010 Facepunch Studios
 *  See license in Gwen.h
 */


#include "Gwen/Controls/Base.h"
#include "Gwen/Controls/Label.h"
#include "Gwen/Gwen.h"
#include "Gwen/BaseRender.h"
#include "Gwen/Skin.h"
#include "Gwen/Platform.h"
#include "Gwen/DragAndDrop.h"
#include "Gwen/ToolTip.h"
#include "Gwen/Utility.h"
#include <list>

#ifndef GWEN_NO_ANIMATION
#include "Gwen/Anim.h"
#endif

using namespace Gwen;
using namespace Controls;

Base::Base(Base* pParent, const Gwen::String& Name)
{
    m_Parent = NULL;
    m_ActualParent = NULL;
    m_InnerPanel = NULL;
    m_Skin = NULL;
    SetName(Name);
    SetParent(pParent);
    m_bHidden = false;
    m_Bounds = Gwen::Rect(0, 0, 10, 10);
    m_Padding = Padding(0, 0, 0, 0);
    m_Margin = Margin(0, 0, 0, 0);
    m_iDock = 0;
    m_DragAndDrop_Package = NULL;
    RestrictToParent(false);
    SetMouseInputEnabled(true);
    SetKeyboardInputEnabled(false);
    Invalidate();
    SetCursor(Gwen::CursorType::Normal);
    SetToolTip(NULL);
    SetTabable(false);
    SetShouldDrawBackground(true);
    m_bDisabled = false;
    m_bCacheTextureDirty = true;
    m_bCacheToTexture = false;
    m_bIncludeInSize = true;
}

Base::~Base()
{
    {
        Canvas* canvas = GetCanvas();

        if (canvas)
            canvas->PreDeleteCanvas(this);
    }
    Base::List::iterator iter = Children.begin();

    while (iter != Children.end())
    {
        Base* pChild = *iter;
        iter = Children.erase(iter);
        delete pChild;
    }

    for (AccelMap::iterator accelIt = m_Accelerators.begin();
         accelIt != m_Accelerators.end();
         ++accelIt)
    {
        delete accelIt->second;
    }

    m_Accelerators.clear();
    SetParent(NULL);

    if (Gwen::HoveredControl == this)
        Gwen::HoveredControl = NULL;

    if (Gwen::KeyboardFocus == this)
        Gwen::KeyboardFocus = NULL;

    if (Gwen::MouseFocus == this)
        Gwen::MouseFocus = NULL;

    DragAndDrop::ControlDeleted(this);
    ToolTip::ControlDeleted(this);
#ifndef GWEN_NO_ANIMATION
    Anim::Cancel(this);
#endif

    if (m_DragAndDrop_Package)
    {
        delete m_DragAndDrop_Package;
        m_DragAndDrop_Package = NULL;
    }
}

void Base::Invalidate()
{
    m_bNeedsLayout = true;
    m_bCacheTextureDirty = true;
}

void Base::DelayedDelete()
{
    Canvas* canvas = GetCanvas();
    canvas->AddDelayedDelete(this);
}

Canvas* Base::GetCanvas()
{
    Base* pCanvas = m_Parent;

    if (!pCanvas)
        return NULL;

    return pCanvas->GetCanvas();
}

void Base::SetParent(Base* pParent)
{
    if (m_Parent == pParent)
        return;

    if (m_Parent)
        m_Parent->RemoveChild(this);

    m_Parent = pParent;
    m_ActualParent = NULL;

    if (m_Parent)
        m_Parent->AddChild(this);
}

void Base::Dock(int iDock)
{
    if (m_iDock == iDock)
        return;

    m_iDock = iDock;
    Invalidate();
    InvalidateParent();
}

int Base::GetDock()
{
    return m_iDock;
}

bool Base::Hidden() const
{
    return m_bHidden;
}

bool Base::Visible() const
{
    if (Hidden())
        return false;

    if (GetParent())
        return GetParent()->Visible();

    return true;
}

void Base::InvalidateChildren(bool bRecursive)
{
    for (Base::List::iterator it = Children.begin(); it != Children.end(); ++it)
    {
        (*it)->Invalidate();

        if (bRecursive)
            (*it)->InvalidateChildren(bRecursive);
    }

    if (m_InnerPanel)
    {
        for (Base::List::iterator it = m_InnerPanel->Children.begin();
             it != m_InnerPanel->Children.end();
             ++it)
        {
            (*it)->Invalidate();

            if (bRecursive)
                (*it)->InvalidateChildren(bRecursive);
        }
    }
}

void Base::Position(unsigned int pos, int xpadding, int ypadding)
{
    const Rect& bounds = GetParent()->GetInnerBounds();
    const Margin& margin = GetMargin();
    int x = X();
    int y = Y();

    if (pos & Pos::Left)
        x = bounds.x + xpadding + margin.left;

    if (pos & Pos::Right)
        x = bounds.x + (bounds.w - Width() - xpadding - margin.right);

    if (pos & Pos::CenterH)
        x = bounds.x + (bounds.w - Width())/2;

    if (pos & Pos::Top)
        y = bounds.y + ypadding;

    if (pos & Pos::Bottom)
        y = bounds.y + (bounds.h - Height() - ypadding);

    if (pos & Pos::CenterV)
        y = bounds.y + (bounds.h - Height())/2 + ypadding;

    SetPos(x, y);
}

void Base::SendToBack()
{
    if (!m_ActualParent)
        return;

    if (m_ActualParent->Children.front() == this)
        return;

    m_ActualParent->Children.remove(this);
    m_ActualParent->Children.push_front(this);
    InvalidateParent();
}

void Base::BringToFront()
{
    if (!m_ActualParent)
        return;

    if (m_ActualParent->Children.back() == this)
        return;

    m_ActualParent->Children.remove(this);
    m_ActualParent->Children.push_back(this);
    InvalidateParent();
    Redraw();
}

Controls::Base* Base::FindChildByName(const Gwen::String& name, bool bRecursive)
{
    Base::List::iterator iter;

    for (iter = Children.begin(); iter != Children.end(); ++iter)
    {
        Base* pChild = *iter;

        if (!pChild->GetName().empty() && pChild->GetName() == name)
            return pChild;

        if (bRecursive)
        {
            Controls::Base* pSubChild = pChild->FindChildByName(name, true);

            if (pSubChild)
                return pSubChild;
        }
    }

    return NULL;
}

void Base::BringNextToControl(Controls::Base* pChild, bool bBehind)
{
    if (!m_ActualParent)
        return;

    m_ActualParent->Children.remove(this);
    Base::List::iterator it = std::find(
        m_ActualParent->Children.begin(), m_ActualParent->Children.end(), pChild);

    if (it == m_ActualParent->Children.end())
        return BringToFront();

    if (bBehind)
    {
        ++it;

        if (it == m_ActualParent->Children.end())
            return BringToFront();
    }

    m_ActualParent->Children.insert(it, this);
    InvalidateParent();
}

void Base::AddChild(Base* pChild)
{
    if (m_InnerPanel)
    {
        m_InnerPanel->AddChild(pChild);
        return;
    }

    Children.push_back(pChild);
    OnChildAdded(pChild);
    pChild->m_ActualParent = this;
}

void Base::RemoveChild(Base* pChild)
{
    // If we removed our innerpanel, remove our pointer to it
    if (m_InnerPanel == pChild)
        m_InnerPanel = NULL;

    if (m_InnerPanel)
        m_InnerPanel->RemoveChild(pChild);

    Children.remove(pChild);
    OnChildRemoved(pChild);
}

void Base::RemoveAllChildren()
{
    while (Children.size() > 0)
    {
        RemoveChild(*Children.begin());
    }
}

unsigned int Base::NumChildren()
{
    // Include m_InnerPanel's children here?
    return (unsigned int)Children.size();
}

Controls::Base* Base::GetChild(unsigned int i)
{
    if (i >= NumChildren())
        return NULL;

    for (Base::List::iterator iter = Children.begin(); iter != Children.end(); ++iter)
    {
        if (i == 0)
            return *iter;

        i--;
    }

    // Should never happen.
    return NULL;
}

void Base::OnChildAdded(Base* /*pChild*/)
{
    Invalidate();
}

void Base::OnChildRemoved(Base* /*pChild*/)
{
    Invalidate();
}

Skin::Base* Base::GetSkin(void)
{
    if (m_Skin)
        return m_Skin;

    if (m_Parent)
        return m_Parent->GetSkin();

    Debug::AssertCheck(0, "Base::GetSkin Returning NULL!\n");
    return NULL;
}

void Base::MoveBy(int x, int y)
{
    MoveTo(X()+x, Y()+y);
}

void Base::MoveTo(int x, int y)
{
    if (m_bRestrictToParent && GetParent())
    {
        Base* pParent = GetParent();

        if (x-GetPadding().left < pParent->GetMargin().left)
            x = pParent->GetMargin().left+GetPadding().left;

        if (y-GetPadding().top < pParent->GetMargin().top)
            y = pParent->GetMargin().top+GetPadding().top;

        if (x+Width()+GetPadding().right > pParent->Width()-pParent->GetMargin().right)
            x = pParent->Width()-pParent->GetMargin().right-Width()-GetPadding().right;

        if (y+Height()+GetPadding().bottom > pParent->Height()-pParent->GetMargin().bottom)
            y = pParent->Height()-pParent->GetMargin().bottom-Height()-GetPadding().bottom;
    }

    SetBounds(x, y, Width(), Height());
}

void Base::SetPos(int x, int y)
{
    SetBounds(x, y, Width(), Height());
}

bool Base::SetSize(int w, int h)
{
    return SetBounds(X(), Y(), w, h);
}

bool Base::SetSize(const Point& p)
{
    return SetSize(p.x, p.y);
}

bool Base::SetBounds(const Gwen::Rect& bounds)
{
    if (m_Bounds == bounds)
        return false;
    
    const Gwen::Rect oldBounds = GetBounds();
    m_Bounds = bounds;
    OnBoundsChanged(oldBounds);
    return true;
}

bool Base::SetBounds(int x, int y, int w, int h)
{
    return SetBounds(Rect(x,y,w,h));
}

void Base::OnBoundsChanged(Gwen::Rect oldBounds)
{
    // Anything that needs to update on size changes
    // Iterate my children and tell them I've changed
    //
    if (GetParent())
        GetParent()->OnChildBoundsChanged(oldBounds, this);

    if (m_Bounds.w != oldBounds.w || m_Bounds.h != oldBounds.h)
        Invalidate();

    Redraw();
    UpdateRenderBounds();
}

void Base::OnScaleChanged()
{
    for (Base::List::iterator iter = Children.begin(); iter != Children.end(); ++iter)
    {
        (*iter)->OnScaleChanged();
    }
}

void Base::OnChildBoundsChanged(Gwen::Rect /*oldChildBounds*/, Base* /*pChild*/)
{
}

void Base::Render(Gwen::Skin::Base* /*skin*/)
{
}

void Base::DoCacheRender(Gwen::Skin::Base* skin, Gwen::Controls::Base* pMaster)
{
    Gwen::Renderer::Base* render = skin->GetRender();
    Gwen::Renderer::ICacheToTexture* cache = render->GetCTT();

    if (!cache)
        return;

    Gwen::Point pOldRenderOffset = render->GetRenderOffset();
    Gwen::Rect rOldRegion = render->ClipRegion();

    if (this != pMaster)
    {
        render->AddRenderOffset(GetBounds());
        render->AddClipRegion(GetBounds());
    }
    else
    {
        render->SetRenderOffset(Gwen::Point(0, 0));
        render->SetClipRegion(GetBounds());
    }

    // See if we need to update the cached texture. Dirty?
    if (m_bCacheTextureDirty && render->ClipRegionVisible())
    {
        render->StartClip();
        {
            if (IsCachedToTexture())
                cache->SetupCacheTexture(this);

            // Render myself first
            Render(skin);

            if (!Children.empty())
            {
                // Now render my kids
                for (Base::List::iterator iter = Children.begin(); iter != Children.end(); ++iter)
                {
                    Base* pChild = *iter;

                    if (!pChild->Hidden())
                    {
                        // Draw child control using normal render. If it is cached it will
                        // be handled in the same way as this one.
                        pChild->DoRender(skin);
                    }
                }
            }

            if (IsCachedToTexture())
            {
                cache->FinishCacheTexture(this);
                m_bCacheTextureDirty = false;
            }
        }
        render->EndClip();
    }

    // Draw the cached texture.
    render->SetClipRegion(rOldRegion);
    render->StartClip();
    {
        render->SetRenderOffset(pOldRenderOffset);
        cache->DrawCachedControlTexture(this);
    }
    render->EndClip();
}

void Base::DoRender(Gwen::Skin::Base* skin)
{
    // If this control has a different skin,
    // then so does its children.
    if (m_Skin)
        skin = m_Skin;

    // Do think
    Think();
    Gwen::Renderer::Base* render = skin->GetRender();

    if (render->GetCTT() && IsCachedToTexture())
    {
        DoCacheRender(skin, this);
        return;
    }

    RenderRecursive(skin, GetBounds());
}

void Base::RenderRecursive(Gwen::Skin::Base* skin, const Gwen::Rect& cliprect)
{
    Gwen::Renderer::Base* render = skin->GetRender();
    Gwen::Point pOldRenderOffset = render->GetRenderOffset();
    render->AddRenderOffset(cliprect);
    RenderUnder(skin);
    Gwen::Rect rOldRegion = render->ClipRegion();

    //
    // If this control is clipping, change the clip rect to ourselves
    // ( if not then we still clip using our parents clip rect )
    //
    if (ShouldClip())
    {
        render->AddClipRegion(cliprect);

        if (!render->ClipRegionVisible())
        {
            render->SetRenderOffset(pOldRenderOffset);
            render->SetClipRegion(rOldRegion);
            return;
        }
    }

    //
    // Render this control and children controls
    //
    render->StartClip();
    {
        Render(skin);

        if (!Children.empty())
        {
            // Now render my kids
            for (Base::List::iterator iter = Children.begin(); iter != Children.end(); ++iter)
            {
                Base* pChild = *iter;

                if (pChild->Hidden())
                    continue;

                pChild->DoRender(skin);
            }
        }
    }
    render->EndClip();
    //
    // Render overlay/focus
    //
    {
        render->SetClipRegion(rOldRegion);
        render->StartClip();
        {
            RenderOver(skin);
            RenderFocus(skin);
        }
        render->EndClip();
        render->SetRenderOffset(pOldRenderOffset);
    }
}

void Base::SetSkin(Skin::Base* skin, bool doChildren)
{
    if (m_Skin == skin)
        return;

    m_Skin = skin;
    Invalidate();
    Redraw();
    OnSkinChanged(skin);

    if (doChildren)
    {
        for (Base::List::iterator it = Children.begin(); it != Children.end(); ++it)
        {
            (*it)->SetSkin(skin, true);
        }
    }
}

void Base::OnSkinChanged(Skin::Base* /*skin*/)
{
    // Do something
}

bool Base::OnMouseWheeled(int iDelta)
{
    if (m_ActualParent)
        return m_ActualParent->OnMouseWheeled(iDelta);

    return false;
}

void Base::OnMouseMoved(int /*x*/, int /*y*/, int /*deltaX*/, int /*deltaY*/)
{
}

void Base::OnMouseEnter()
{
    onHoverEnter.Call(this);

    if (GetToolTip())
        ToolTip::Enable(this);
    else if (GetParent() && GetParent()->GetToolTip())
        ToolTip::Enable(GetParent());

    Redraw();
}

void Base::OnMouseLeave()
{
    onHoverLeave.Call(this);

    if (GetToolTip())
        ToolTip::Disable(this);

    Redraw();
}

bool Base::IsHovered()
{
    return Gwen::HoveredControl == this;
}

bool Base::ShouldDrawHover()
{
    return Gwen::MouseFocus == this || Gwen::MouseFocus == NULL;
}

bool Base::HasFocus()
{
    return Gwen::KeyboardFocus == this;
}

void Base::Focus()
{
    if (Gwen::KeyboardFocus == this)
        return;

    if (Gwen::KeyboardFocus)
        Gwen::KeyboardFocus->OnLostKeyboardFocus();

    Gwen::KeyboardFocus = this;
    OnKeyboardFocus();
    Redraw();
}

void Base::Blur()
{
    if (Gwen::KeyboardFocus != this)
        return;

    Gwen::KeyboardFocus = NULL;
    OnLostKeyboardFocus();
    Redraw();
}

bool Base::IsOnTop()
{
    if (!GetParent())
        return false;

    Base::List::iterator iter = GetParent()->Children.begin();
    Base* pChild = *iter;

    if (pChild == this)
        return true;

    return false;
}

void Base::Touch()
{
    if (GetParent())
        GetParent()->OnChildTouched(this);
}

void Base::OnChildTouched(Controls::Base* /*pChild*/)
{
    Touch();
}

Base* Base::GetControlAt(int x, int y, bool bOnlyIfMouseEnabled)
{
    if (Hidden())
        return NULL;

    if (x < 0 || y < 0 || x >= Width() || y >= Height())
        return NULL;

    Base::List::reverse_iterator iter;

    for (iter = Children.rbegin(); iter != Children.rend(); ++iter)
    {
        Base* pChild = *iter;
        Base* pFound = NULL;
        pFound = pChild->GetControlAt(x-pChild->X(), y-pChild->Y(), bOnlyIfMouseEnabled);

        if (pFound)
            return pFound;
    }

    if (bOnlyIfMouseEnabled && !GetMouseInputEnabled())
        return NULL;

    return this;
}

void Base::Layout(Skin::Base* skin)
{
    if (skin->GetRender()->GetCTT() && IsCachedToTexture())
        skin->GetRender()->GetCTT()->CreateControlCacheTexture(this);
}

void Base::RecurseLayout(Skin::Base* skin)
{
    if (m_Skin)
        skin = m_Skin;

    if (Hidden())
        return;

    if (NeedsLayout())
    {
        m_bNeedsLayout = false;
        Layout(skin);
    }

    Gwen::Rect rBounds = GetRenderBounds();

    // Adjust bounds for padding
    rBounds.x += m_Padding.left;
    rBounds.w -= m_Padding.left + m_Padding.right;
    rBounds.y += m_Padding.top;
    rBounds.h -= m_Padding.top + m_Padding.bottom;

    for (Base::List::iterator iter = Children.begin(); iter != Children.end(); ++iter)
    {
        Base* pChild = *iter;

        if (pChild->Hidden())
            continue;

        int iDock = pChild->GetDock();

        if (iDock & Pos::Fill)
            continue;

        if (iDock & Pos::Top)
        {
            const Margin& margin = pChild->GetMargin();
            pChild->SetBounds(rBounds.x+margin.left,
                              rBounds.y+margin.top,
                              rBounds.w-margin.left-margin.right,
                              pChild->Height());
            int iHeight = margin.top+margin.bottom+pChild->Height();
            rBounds.y += iHeight;
            rBounds.h -= iHeight;
        }

        if (iDock & Pos::Left)
        {
            const Margin& margin = pChild->GetMargin();
            pChild->SetBounds(rBounds.x+margin.left,
                              rBounds.y+margin.top,
                              pChild->Width(),
                              rBounds.h-margin.top-margin.bottom);
            int iWidth = margin.left+margin.right+pChild->Width();
            rBounds.x += iWidth;
            rBounds.w -= iWidth;
        }

        if (iDock & Pos::Right)
        {
            // TODO: THIS MARGIN CODE MIGHT NOT BE FULLY FUNCTIONAL
            const Margin& margin = pChild->GetMargin();
            pChild->SetBounds((rBounds.x+rBounds.w)-pChild->Width()-margin.right,
                              rBounds.y+margin.top,
                              pChild->Width(),
                              rBounds.h-margin.top-margin.bottom);
            int iWidth = margin.left+margin.right+pChild->Width();
            rBounds.w -= iWidth;
        }

        if (iDock & Pos::Bottom)
        {
            // TODO: THIS MARGIN CODE MIGHT NOT BE FULLY FUNCTIONAL
            const Margin& margin = pChild->GetMargin();
            pChild->SetBounds(rBounds.x+margin.left,
                              (rBounds.y+rBounds.h)-pChild->Height()-margin.bottom,
                              rBounds.w-margin.left-margin.right,
                              pChild->Height());
            rBounds.h -= pChild->Height()+margin.bottom+margin.top;
        }

        pChild->RecurseLayout(skin);
    }

    m_InnerBounds = rBounds; 

    //
    // Fill uses the left over space, so do that now.
    //
    for (Base::List::iterator iter = Children.begin(); iter != Children.end(); ++iter)
    {
        Base* pChild = *iter;
        int iDock = pChild->GetDock();

        if (!(iDock&Pos::Fill))
            continue;

        const Margin& margin = pChild->GetMargin();
        pChild->SetBounds(rBounds.x+margin.left, rBounds.y+margin.top,
                          rBounds.w-margin.left-margin.right, rBounds.h-margin.top-margin.bottom);
        pChild->RecurseLayout(skin);
    }

    PostLayout(skin);

    if (IsTabable() && !IsDisabled())
    {
        if (!GetCanvas()->FirstTab)
            GetCanvas()->FirstTab = this;

        if (!GetCanvas()->NextTab)
            GetCanvas()->NextTab = this;
    }

    if (Gwen::KeyboardFocus == this)
        GetCanvas()->NextTab = NULL;
}

bool Base::IsChild(Controls::Base* pChild)
{
    for (Base::List::iterator iter = Children.begin(); iter != Children.end(); ++iter)
    {
        if (pChild == (*iter))
            return true;
    }

    return false;
}

Gwen::Point Base::LocalPosToCanvas(const Gwen::Point& pnt)
{
    if (m_Parent)
    {
        int x = pnt.x+X();
        int y = pnt.y+Y();

        // If our parent has an innerpanel and we're a child of it
        // add its offset onto us.
        //
        if (m_Parent->m_InnerPanel && m_Parent->m_InnerPanel->IsChild(this))
        {
            x += m_Parent->m_InnerPanel->X();
            y += m_Parent->m_InnerPanel->Y();
        }

        return m_Parent->LocalPosToCanvas(Gwen::Point(x, y));
    }

    return pnt;
}

Gwen::Point Base::CanvasPosToLocal(const Gwen::Point& pnt)
{
    if (m_Parent)
    {
        int x = pnt.x-X();
        int y = pnt.y-Y();

        // If our parent has an innerpanel and we're a child of it
        // add its offset onto us.
        //
        if (m_Parent->m_InnerPanel && m_Parent->m_InnerPanel->IsChild(this))
        {
            x -= m_Parent->m_InnerPanel->X();
            y -= m_Parent->m_InnerPanel->Y();
        }

        return m_Parent->CanvasPosToLocal(Gwen::Point(x, y));
    }

    return pnt;
}

bool Base::IsMenuComponent()
{
    if (!m_Parent)
        return false;

    return m_Parent->IsMenuComponent();
}

void Base::CloseMenus()
{
    for (Base::List::iterator it = Children.begin(); it != Children.end(); ++it)
    {
        (*it)->CloseMenus();
    }
}

void Base::UpdateRenderBounds()
{
    m_RenderBounds.x = 0;
    m_RenderBounds.y = 0;
    m_RenderBounds.w = m_Bounds.w;
    m_RenderBounds.h = m_Bounds.h;
}

void Base::UpdateCursor()
{
    Platform::SetCursor(m_Cursor);
}

DragAndDrop::Package* Base::DragAndDrop_GetPackage(int /*x*/, int /*y*/)
{
    return m_DragAndDrop_Package;
}

bool Base::DragAndDrop_HandleDrop(Gwen::DragAndDrop::Package* /*pPackage*/, int /*x*/, int /*y*/)
{
    DragAndDrop::SourceControl->SetParent(this);
    return true;
}

bool Base::DragAndDrop_Draggable()
{
    if (!m_DragAndDrop_Package)
        return false;

    return m_DragAndDrop_Package->draggable;
}

void Base::DragAndDrop_SetPackage(bool bDraggable, const String& strName, void* pUserData)
{
    if (!m_DragAndDrop_Package)
        m_DragAndDrop_Package = new Gwen::DragAndDrop::Package();

    m_DragAndDrop_Package->draggable = bDraggable;
    m_DragAndDrop_Package->name = strName;
    m_DragAndDrop_Package->userdata = pUserData;
}

void Base::DragAndDrop_StartDragging(Gwen::DragAndDrop::Package* pPackage, int x, int y)
{
    pPackage->holdoffset = CanvasPosToLocal(Gwen::Point(x, y));
    pPackage->drawcontrol = this;
}

bool Base::SizeToChildren(bool w, bool h)
{
    Gwen::Point size = ChildrenSize();
    size.y += GetPadding().bottom;
    size.x += GetPadding().right;
    return SetSize(w ? size.x : Width(), h ? size.y : Height());
}

Gwen::Point Base::ChildrenSize()
{
    Gwen::Point size;

    for (Base::List::iterator iter = Children.begin(); iter != Children.end(); ++iter)
    {
        Base* pChild = *iter;

        if (pChild->Hidden())
            continue;

        if (!pChild->ShouldIncludeInSize())
            continue;

        size.x = Gwen::Max(size.x, pChild->Right());
        size.y = Gwen::Max(size.y, pChild->Bottom());
    }

    return size;
}

void Base::SetPadding(const Padding& padding)
{
    if (m_Padding.left == padding.left &&
        m_Padding.top == padding.top &&
        m_Padding.right == padding.right &&
        m_Padding.bottom == padding.bottom)
        return;

    m_Padding = padding;
    Invalidate();
    InvalidateParent();
}

void Base::SetMargin(const Margin& margin)
{
    if (m_Margin.top == margin.top &&
        m_Margin.left == margin.left &&
        m_Margin.bottom == margin.bottom &&
        m_Margin.right == margin.right)
        return;

    m_Margin = margin;
    Invalidate();
    InvalidateParent();
}

bool Base::HandleAccelerator(Gwen::String& accelerator)
{
    if (Gwen::KeyboardFocus == this || !AccelOnlyFocus())
    {
        AccelMap::iterator iter = m_Accelerators.find(accelerator);

        if (iter != m_Accelerators.end())
        {
            iter->second->Call(this);
            return true;
        }
    }

    for (Base::List::iterator it = Children.begin(); it != Children.end(); ++it)
    {
        if ((*it)->HandleAccelerator(accelerator))
            return true;
    }

    return false;
}

bool Base::OnKeyPress(int iKey, bool bPress)
{
    bool bHandled = false;

    switch (iKey)
    {
    case Key::Tab:
        bHandled = OnKeyTab(bPress);
        break;

    case Key::Space:
        bHandled = OnKeySpace(bPress);
        break;

    case Key::Home:
        bHandled = OnKeyHome(bPress);
        break;

    case Key::End:
        bHandled = OnKeyEnd(bPress);
        break;

    case Key::Return:
        bHandled = OnKeyReturn(bPress);
        break;

    case Key::Backspace:
        bHandled = OnKeyBackspace(bPress);
        break;

    case Key::Delete:
        bHandled = OnKeyDelete(bPress);
        break;

    case Key::Right:
        bHandled = OnKeyRight(bPress);
        break;

    case Key::Left:
        bHandled = OnKeyLeft(bPress);
        break;

    case Key::Up:
        bHandled = OnKeyUp(bPress);
        break;

    case Key::Down:
        bHandled = OnKeyDown(bPress);
        break;

    case Key::Escape:
        bHandled = OnKeyEscape(bPress);
        break;

    default:
        break;
    }

    if (!bHandled && GetParent())
        GetParent()->OnKeyPress(iKey, bPress);

    return bHandled;
}

bool Base::OnKeyRelease(int iKey)
{
    return OnKeyPress(iKey, false);
}

bool Base::OnKeyTab(bool bDown)
{
    if (!bDown)
        return true;

    if (GetCanvas()->NextTab)
    {
        GetCanvas()->NextTab->Focus();
        Redraw();
    }

    return true;
}

void Base::RenderFocus(Gwen::Skin::Base* skin)
{
    if (Gwen::KeyboardFocus != this)
        return;

    if (!IsTabable())
        return;

    skin->DrawKeyboardHighlight(this, GetRenderBounds(), 3);
}

void Base::SetToolTip(const String& strText)
{
    Label* tooltip = new Label(this);
    tooltip->SetText(strText);
    tooltip->SetTextColorOverride(GetSkin()->Colors.TooltipText);
    tooltip->SetPadding(Padding(5, 3, 5, 3));
    tooltip->SizeToContents();
    SetToolTip(tooltip);
}

String Base::GetChildValue(const Gwen::String& strName)
{
    Base* pChild = FindChildByName(strName, true);

    if (!pChild)
        return "";

    return pChild->GetValue();
}

String Base::GetValue()
{
    // Generic value accessor should be filled in if we have a value to give.
    return "";
}

void Base::SetValue(const String& strValue)
{
}

int Base::GetNamedChildren(Gwen::ControlList& list, const Gwen::String& strName, bool bDeep)
{
    int iFound = 0;
    Base::List::iterator iter;

    for (iter = Children.begin(); iter != Children.end(); ++iter)
    {
        Base* pChild = *iter;

        if (!pChild->GetName().empty() && pChild->GetName() == strName)
        {
            list.Add(pChild);
            iFound++;
        }

        if (!bDeep)
            continue;

        iFound += pChild->GetNamedChildren(list, strName, bDeep);
    }

    return iFound;
}

Gwen::ControlList Base::GetNamedChildren(const Gwen::String& strName, bool bDeep)
{
    Gwen::ControlList list;
    GetNamedChildren(list, strName, bDeep);
    return list;
}

#ifndef GWEN_NO_ANIMATION

void Base::Anim_WidthIn(float fLength, float fDelay, float fEase)
{
    Gwen::Anim::Add(this, new Gwen::Anim::Size::Width(0, Width(), fLength, false, fDelay, fEase));
    SetWidth(0);
}

void Base::Anim_HeightIn(float fLength, float fDelay, float fEase)
{
    Gwen::Anim::Add(this,
                    new Gwen::Anim::Size::Height(0, Height(), fLength, false, fDelay, fEase));
    SetHeight(0);
}

void Base::Anim_WidthOut(float fLength, bool bHide, float fDelay, float fEase)
{
    Gwen::Anim::Add(this, new Gwen::Anim::Size::Width(Width(), 0, fLength, bHide, fDelay, fEase));
}

void Base::Anim_HeightOut(float fLength, bool bHide, float fDelay, float fEase)
{
    Gwen::Anim::Add(this,
                    new Gwen::Anim::Size::Height(Height(), 0, fLength, bHide, fDelay, fEase));
}

#endif // ifndef GWEN_NO_ANIMATION
