/****************************************************************************
**
**
** This file is part of the QtDeclarative module of the Qt Toolkit.
**
**
****************************************************************************/

#include "qdeclarativeviewinspector.h"
#include "qdeclarativeviewinspector_p.h"

#include "editor/liveselectiontool.h"
#include "editor/zoomtool.h"
#include "editor/colorpickertool.h"
#include "editor/livelayeritem.h"
#include "editor/boundingrecthighlighter.h"

#include <QtDeclarative/QDeclarativeItem>
#include <QtGui/QWidget>
#include <QtGui/QMouseEvent>
#include <QtGui/QGraphicsObject>

namespace QmlJSDebugger {

QDeclarativeViewInspectorPrivate::QDeclarativeViewInspectorPrivate(QDeclarativeViewInspector *q) :
    q(q)
{
}

QDeclarativeViewInspectorPrivate::~QDeclarativeViewInspectorPrivate()
{
}

QDeclarativeViewInspector::QDeclarativeViewInspector(QDeclarativeView *view,
                                                   QObject *parent) :
    AbstractViewInspector(parent),
    data(new QDeclarativeViewInspectorPrivate(this))
{
    data->view = view;
    data->manipulatorLayer = new LiveLayerItem(view->scene());
    data->selectionTool = new LiveSelectionTool(this);
    data->zoomTool = new ZoomTool(this);
    data->colorPickerTool = new ColorPickerTool(this);
    data->boundingRectHighlighter = new BoundingRectHighlighter(this);
    setCurrentTool(data->selectionTool);

    // to capture ChildRemoved event when viewport changes
    data->view->installEventFilter(this);

    data->setViewport(data->view->viewport());

    connect(data->view, SIGNAL(statusChanged(QDeclarativeView::Status)),
            data.data(), SLOT(_q_onStatusChanged(QDeclarativeView::Status)));

    connect(data->colorPickerTool, SIGNAL(selectedColorChanged(QColor)),
            SIGNAL(selectedColorChanged(QColor)));
    connect(data->colorPickerTool, SIGNAL(selectedColorChanged(QColor)),
            this, SLOT(sendColorChanged(QColor)));

    changeTool(InspectorProtocol::SelectTool);
}

QDeclarativeViewInspector::~QDeclarativeViewInspector()
{
}

void QDeclarativeViewInspector::changeCurrentObjects(const QList<QObject*> &objects)
{
    QList<QGraphicsItem*> items;
    QList<QGraphicsObject*> gfxObjects;
    foreach (QObject *obj, objects) {
        if (QDeclarativeItem *declarativeItem = qobject_cast<QDeclarativeItem*>(obj)) {
            items << declarativeItem;
            gfxObjects << declarativeItem;
        }
    }
    if (designModeBehavior()) {
        data->setSelectedItemsForTools(items);
        data->clearHighlight();
        data->highlight(gfxObjects);
    }
}

void QDeclarativeViewInspector::reloadView()
{
    data->clearHighlight();
    emit reloadRequested();
}

void QDeclarativeViewInspector::changeTool(InspectorProtocol::Tool tool)
{
    switch (tool) {
    case InspectorProtocol::ColorPickerTool:
        data->changeToColorPickerTool();
        break;
    case InspectorProtocol::SelectMarqueeTool:
        data->changeToMarqueeSelectTool();
        break;
    case InspectorProtocol::SelectTool:
        data->changeToSingleSelectTool();
        break;
    case InspectorProtocol::ZoomTool:
        data->changeToZoomTool();
        break;
    }
}

AbstractLiveEditTool *QDeclarativeViewInspector::currentTool() const
{
    return static_cast<AbstractLiveEditTool*>(AbstractViewInspector::currentTool());
}

QDeclarativeEngine *QDeclarativeViewInspector::declarativeEngine() const
{
    return data->view->engine();
}

void QDeclarativeViewInspectorPrivate::setViewport(QWidget *widget)
{
    if (viewport.data() == widget)
        return;

    if (viewport)
        viewport.data()->removeEventFilter(q);

    viewport = widget;
    if (viewport) {
        // make sure we get mouse move events
        viewport.data()->setMouseTracking(true);
        viewport.data()->installEventFilter(q);
    }
}

void QDeclarativeViewInspectorPrivate::clearEditorItems()
{
    clearHighlight();
    setSelectedItems(QList<QGraphicsItem*>());
}

bool QDeclarativeViewInspector::eventFilter(QObject *obj, QEvent *event)
{
    if (obj == data->view) {
        // Event from view
        if (event->type() == QEvent::ChildRemoved) {
            // Might mean that viewport has changed
            if (data->view->viewport() != data->viewport.data())
                data->setViewport(data->view->viewport());
        }
        return QObject::eventFilter(obj, event);
    }

    return AbstractViewInspector::eventFilter(obj, event);
}

bool QDeclarativeViewInspector::leaveEvent(QEvent *event)
{
    data->clearHighlight();
    return AbstractViewInspector::leaveEvent(event);
}

bool QDeclarativeViewInspector::mouseMoveEvent(QMouseEvent *event)
{
    QList<QGraphicsItem*> selItems = data->selectableItems(event->pos());
    if (!selItems.isEmpty()) {
        declarativeView()->setToolTip(currentTool()->titleForItem(selItems.first()));
    } else {
        declarativeView()->setToolTip(QString());
    }

    return AbstractViewInspector::mouseMoveEvent(event);
}

void QDeclarativeViewInspector::reparentQmlObject(QObject *object, QObject *newParent)
{
    if (!newParent)
        return;

    object->setParent(newParent);
    QDeclarativeItem *newParentItem = qobject_cast<QDeclarativeItem*>(newParent);
    QDeclarativeItem *item    = qobject_cast<QDeclarativeItem*>(object);
    if (newParentItem && item)
        item->setParentItem(newParentItem);
}

void QDeclarativeViewInspectorPrivate::_q_removeFromSelection(QObject *obj)
{
    QList<QGraphicsItem*> items = selectedItems();
    if (QGraphicsItem *item = qobject_cast<QGraphicsObject*>(obj))
        items.removeOne(item);
    setSelectedItems(items);
}

void QDeclarativeViewInspectorPrivate::setSelectedItemsForTools(const QList<QGraphicsItem *> &items)
{
    foreach (const QWeakPointer<QGraphicsObject> &obj, currentSelection) {
        if (QGraphicsItem *item = obj.data()) {
            if (!items.contains(item)) {
                QObject::disconnect(obj.data(), SIGNAL(destroyed(QObject*)),
                                    this, SLOT(_q_removeFromSelection(QObject*)));
                currentSelection.removeOne(obj);
            }
        }
    }

    foreach (QGraphicsItem *item, items) {
        if (QGraphicsObject *obj = item->toGraphicsObject()) {
            if (!currentSelection.contains(obj)) {
                QObject::connect(obj, SIGNAL(destroyed(QObject*)),
                                 this, SLOT(_q_removeFromSelection(QObject*)));
                currentSelection.append(obj);
            }
        }
    }

    q->currentTool()->updateSelectedItems();
}

void QDeclarativeViewInspectorPrivate::setSelectedItems(const QList<QGraphicsItem *> &items)
{
    QList<QWeakPointer<QGraphicsObject> > oldList = currentSelection;
    setSelectedItemsForTools(items);
    if (oldList != currentSelection) {
        QList<QObject*> objectList;
        foreach (const QWeakPointer<QGraphicsObject> &graphicsObject, currentSelection) {
            if (graphicsObject)
                objectList << graphicsObject.data();
        }

        q->sendCurrentObjects(objectList);
    }
}

QList<QGraphicsItem *> QDeclarativeViewInspectorPrivate::selectedItems() const
{
    QList<QGraphicsItem *> selection;
    foreach (const QWeakPointer<QGraphicsObject> &selectedObject, currentSelection) {
        if (selectedObject.data())
            selection << selectedObject.data();
    }

    return selection;
}

void QDeclarativeViewInspector::setSelectedItems(QList<QGraphicsItem *> items)
{
    data->setSelectedItems(items);
}

QList<QGraphicsItem *> QDeclarativeViewInspector::selectedItems() const
{
    return data->selectedItems();
}

QDeclarativeView *QDeclarativeViewInspector::declarativeView() const
{
    return data->view;
}

void QDeclarativeViewInspectorPrivate::clearHighlight()
{
    boundingRectHighlighter->clear();
}

void QDeclarativeViewInspectorPrivate::highlight(const QList<QGraphicsObject *> &items)
{
    if (items.isEmpty())
        return;

    QList<QGraphicsObject*> objectList;
    foreach (QGraphicsItem *item, items) {
        QGraphicsItem *child = item;

        if (child) {
            QGraphicsObject *childObject = child->toGraphicsObject();
            if (childObject)
                objectList << childObject;
        }
    }

    boundingRectHighlighter->highlight(objectList);
}

QList<QGraphicsItem*> QDeclarativeViewInspectorPrivate::selectableItems(
    const QPointF &scenePos) const
{
    QList<QGraphicsItem*> itemlist = view->scene()->items(scenePos);
    return filterForSelection(itemlist);
}

QList<QGraphicsItem*> QDeclarativeViewInspectorPrivate::selectableItems(const QPoint &pos) const
{
    QList<QGraphicsItem*> itemlist = view->items(pos);
    return filterForSelection(itemlist);
}

QList<QGraphicsItem*> QDeclarativeViewInspectorPrivate::selectableItems(
    const QRectF &sceneRect, Qt::ItemSelectionMode selectionMode) const
{
    QList<QGraphicsItem*> itemlist = view->scene()->items(sceneRect, selectionMode);
    return filterForSelection(itemlist);
}

void QDeclarativeViewInspectorPrivate::changeToSingleSelectTool()
{
    selectionTool->setRubberbandSelectionMode(false);

    changeToSelectTool();

    emit q->selectToolActivated();
    q->sendCurrentTool(Constants::SelectionToolMode);
}

void QDeclarativeViewInspectorPrivate::changeToSelectTool()
{
    if (q->currentTool() == selectionTool)
        return;

    q->currentTool()->clear();
    q->setCurrentTool(selectionTool);
    q->currentTool()->clear();
    q->currentTool()->updateSelectedItems();
}

void QDeclarativeViewInspectorPrivate::changeToMarqueeSelectTool()
{
    changeToSelectTool();
    selectionTool->setRubberbandSelectionMode(true);

    emit q->marqueeSelectToolActivated();
    q->sendCurrentTool(Constants::MarqueeSelectionToolMode);
}

void QDeclarativeViewInspectorPrivate::changeToZoomTool()
{
    q->currentTool()->clear();
    q->setCurrentTool(zoomTool);
    q->currentTool()->clear();

    emit q->zoomToolActivated();
    q->sendCurrentTool(Constants::ZoomMode);
}

void QDeclarativeViewInspectorPrivate::changeToColorPickerTool()
{
    if (q->currentTool() == colorPickerTool)
        return;

    q->currentTool()->clear();
    q->setCurrentTool(colorPickerTool);
    q->currentTool()->clear();

    emit q->co

    q->sendCurrentTool(Constants::ColorPickerMode);
}

static bool isEditorItem(QGraphicsItem *item)
{
    return (item->type() == Constants::EditorItemType
            || item->type() == Constants::ResizeHandleItemType
            || item->data(Constants::EditorItemDataKey).toBool());
}

QList<QGraphicsItem*> QDeclarativeViewInspectorPrivate::filterForSelection(
    QList<QGraphicsItem*> &itemlist) const
{
    foreach (QGraphicsItem *item, itemlist) {
        if (isEditorItem(item))
            itemlist.removeOne(item);
    }

    return itemlist;
}

void QDeclarativeViewInspectorPrivate::_q_onStatusChanged(QDeclarativeView::Status status)
{
    if (status == QDeclarativeView::Ready)
        q->sendReloaded();
}

// adjusts bounding boxes on edges of screen to be visible
QRectF QDeclarativeViewInspector::adjustToScreenBoundaries(const QRectF &boundingRectInSceneSpace)
{
    int marginFromEdge = 1;
    QRectF boundingRect(boundingRectInSceneSpace);
    if (qAbs(boundingRect.left()) - 1 < 2)
        boundingRect.setLeft(marginFromEdge);

    QRect rect = data->view->rect();

    if (boundingRect.right() >= rect.right())
        boundingRect.setRight(rect.right() - marginFromEdge);

    if (qAbs(boundingRect.top()) - 1 < 2)
        boundingRect.setTop(marginFromEdge);

    if (boundingRect.bottom() >= rect.bottom())
        boundingRect.setBottom(rect.bottom() - marginFromEdge);

    return boundingRect;
}

} // namespace QmlJSDebugger


switch (tool) {
    case InspectorProtocol::ColorPickerTool:
        data->changeToColorPickerTool();
        break;
    case InspectorProtocol::SelectMarqueeTool:
        data->changeToMarqueeSelectTool();
        break;
    case InspectorProtocol::SelectTool:
        data->changeToSingleSelectTool();
        break;
    case InspectorProtocol::ZoomTool:
        data->changeToZoomTool();
        break;
    }

/****************************************************************************
**
**
** This file is part of the QtDeclarative module of the Qt Toolkit.
**
**
****************************************************************************/

#include "qdeclarativeviewinspector.h"
#include "qdeclarativeviewinspector_p.h"

#include "editor/liveselectiontool.h"
#include "editor/zoomtool.h"
#include "editor/colorpickertool.h"
#include "editor/livelayeritem.h"
#include "editor/boundingrecthighlighter.h"

#include <QtDeclarative/QDeclarativeItem>
#include <QtGui/QWidget>
#include <QtGui/QMouseEvent>
#include <QtGui/QGraphicsObject>

namespace QmlJSDebugger {

QDeclarativeViewInspectorPrivate::QDeclarativeViewInspectorPrivate(QDeclarativeViewInspector *q) :
    q(q)
{
}

QDeclarativeViewInspectorPrivate::~QDeclarativeViewInspectorPrivate()
{
}

QDeclarativeViewInspector::QDeclarativeViewInspector(QDeclarativeView *view,
                                                   QObject *parent) :
    AbstractViewInspector(parent),
    data(new QDeclarativeViewInspectorPrivate(this))
{
    data->view = view;
    data->manipulatorLayer = new LiveLayerItem(view->scene());
    data->selectionTool = new LiveSelectionTool(this);
    data->zoomTool = new ZoomTool(this);
    data->colorPickerTool = new ColorPickerTool(this);
    data->boundingRectHighlighter = new BoundingRectHighlighter(this);
    setCurrentTool(data->selectionTool);

    // to capture ChildRemoved event when viewport changes
    data->view->installEventFilter(this);

    data->setViewport(data->view->viewport());

    connect(data->view, SIGNAL(statusChanged(QDeclarativeView::Status)),
            data.data(), SLOT(_q_onStatusChanged(QDeclarativeView::Status)));

    connect(data->colorPickerTool, SIGNAL(selectedColorChanged(QColor)),
            SIGNAL(selectedColorChanged(QColor)));
    connect(data->colorPickerTool, SIGNAL(selectedColorChanged(QColor)),
            this, SLOT(sendColorChanged(QColor)));

    changeTool(InspectorProtocol::SelectTool);
}

QDeclarativeViewInspector::~QDeclarativeViewInspector()
{
}

void QDeclarativeViewInspector::changeCurrentObjects(const QList<QObject*> &objects)
{
    QList<QGraphicsItem*> items;
    QList<QGraphicsObject*> gfxObjects;
    foreach (QObject *obj, objects) {
        if (QDeclarativeItem *declarativeItem = qobject_cast<QDeclarativeItem*>(obj)) {
            items << declarativeItem;
            gfxObjects << declarativeItem;
        }
    }
    if (designModeBehavior()) {
        data->setSelectedItemsForTools(items);
        data->clearHighlight();
        data->highlight(gfxObjects);
    }
}

void QDeclarativeViewInspector::reloadView()
{
    data->clearHighlight();
    emit reloadRequested();
}

void QDeclarativeViewInspector::changeTool(InspectorProtocol::Tool tool)
{

}

AbstractLiveEditTool *QDeclarativeViewInspector::currentTool() const
{
    return static_cast<AbstractLiveEditTool*>(AbstractViewInspector::currentTool());
}

QDeclarativeEngine *QDeclarativeViewInspector::declarativeEngine() const
{
    return data->view->engine();
}

void QDeclarativeViewInspectorPrivate::setViewport(QWidget *widget)
{
    if (viewport.data() == widget)
        return;

    if (viewport)
        viewport.data()->removeEventFilter(q);

    viewport = widget;
    if (viewport) {
        // make sure we get mouse move events
        viewport.data()->setMouseTracking(true);
        viewport.data()->installEventFilter(q);
    }
}

void QDeclarativeViewInspectorPrivate::clearEditorItems()
{
    clearHighlight();
    setSelectedItems(QList<QGraphicsItem*>());
}

bool QDeclarativeViewInspector::eventFilter(QObject *obj, QEvent *event)
{
    if (obj == data->view) {
        // Event from view
        if (event->type() == QEvent::ChildRemoved) {
            // Might mean that viewport has changed
            if (data->view->viewport() != data->viewport.data())
                data->setViewport(data->view->viewport());
        }
        return QObject::eventFilter(obj, event);
    }

    return AbstractViewInspector::eventFilter(obj, event);
}

bool QDeclarativeViewInspector::leaveEvent(QEvent *event)
{
    data->clearHighlight();
    return AbstractViewInspector::leaveEvent(event);
}

bool QDeclarativeViewInspector::mouseMoveEvent(QMouseEvent *event)
{
    QList<QGraphicsItem*> selItems = data->selectableItems(event->pos());
    if (!selItems.isEmpty()) {
        declarativeView()->setToolTip(currentTool()->titleForItem(selItems.first()));
    } else {
        declarativeView()->setToolTip(QString());
    }

    return AbstractViewInspector::mouseMoveEvent(event);
}

void QDeclarativeViewInspector::reparentQmlObject(QObject *object, QObject *newParent)
{
    if (!newParent)
        return;

    object->setParent(newParent);
    QDeclarativeItem *newParentItem = qobject_cast<QDeclarativeItem*>(newParent);
    QDeclarativeItem *item    = qobject_cast<QDeclarativeItem*>(object);
    if (newParentItem && item)
        item->setParentItem(newParentItem);
}

void QDeclarativeViewInspectorPrivate::_q_removeFromSelection(QObject *obj)
{
    QList<QGraphicsItem*> items = selectedItems();
    if (QGraphicsItem *item = qobject_cast<QGraphicsObject*>(obj))
        items.removeOne(item);
    setSelectedItems(items);
}

void QDeclarativeViewInspectorPrivate::setSelectedItemsForTools(const QList<QGraphicsItem *> &items)
{
    foreach (const QWeakPointer<QGraphicsObject> &obj, currentSelection) {
        if (QGraphicsItem *item = obj.data()) {
            if (!items.contains(item)) {
                QObject::disconnect(obj.data(), SIGNAL(destroyed(QObject*)),
                                    this, SLOT(_q_removeFromSelection(QObject*)));
                currentSelection.removeOne(obj);
            }
        }
    }

    foreach (QGraphicsItem *item, items) {
        if (QGraphicsObject *obj = item->toGraphicsObject()) {
            if (!currentSelection.contains(obj)) {
                QObject::connect(obj, SIGNAL(destroyed(QObject*)),
                                 this, SLOT(_q_removeFromSelection(QObject*)));
                currentSelection.append(obj);
            }
        }
    }

    q->currentTool()->updateSelectedItems();
}

void QDeclarativeViewInspectorPrivate::setSelectedItems(const QList<QGraphicsItem *> &items)
{
    QList<QWeakPointer<QGraphicsObject> > oldList = currentSelection;
    setSelectedItemsForTools(items);
    if (oldList != currentSelection) {
        QList<QObject*> objectList;
        foreach (const QWeakPointer<QGraphicsObject> &graphicsObject, currentSelection) {
            if (graphicsObject)
                objectList << graphicsObject.data();
        }

        q->sendCurrentObjects(objectList);
    }
}

QList<QGraphicsItem *> QDeclarativeViewInspectorPrivate::selectedItems() const
{
    QList<QGraphicsItem *> selection;
    foreach (const QWeakPointer<QGraphicsObject> &selectedObject, currentSelection) {
        if (selectedObject.data())
            selection << selectedObject.data();
    }

    return selection;
}

void QDeclarativeViewInspector::setSelectedItems(QList<QGraphicsItem *> items)
{
    data->setSelectedItems(items);
}

QList<QGraphicsItem *> QDeclarativeViewInspector::selectedItems() const
{
    return data->selectedItems();
}

QDeclarativeView *QDeclarativeViewInspector::declarativeView() const
{
    return data->view;
}

void QDeclarativeViewInspectorPrivate::clearHighlight()
{
    boundingRectHighlighter->clear();
}

void QDeclarativeViewInspectorPrivate::highlight(const QList<QGraphicsObject *> &items)
{
    if (items.isEmpty())
        return;

    QList<QGraphicsObject*> objectList;
    foreach (QGraphicsItem *item, items) {
        QGraphicsItem *child = item;

        if (child) {
            QGraphicsObject *childObject = child->toGraphicsObject();
            if (childObject)
                objectList << childObject;
        }
    }

    boundingRectHighlighter->highlight(objectList);
}

QList<QGraphicsItem*> QDeclarativeViewInspectorPrivate::selectableItems(
    const QPointF &scenePos) const
{
    QList<QGraphicsItem*> itemlist = view->scene()->items(scenePos);
    return filterForSelection(itemlist);
}

QList<QGraphicsItem*> QDeclarativeViewInspectorPrivate::selectableItems(const QPoint &pos) const
{
    QList<QGraphicsItem*> itemlist = view->items(pos);
    return filterForSelection(itemlist);
}

QList<QGraphicsItem*> QDeclarativeViewInspectorPrivate::selectableItems(
    const QRectF &sceneRect, Qt::ItemSelectionMode selectionMode) const
{
    QList<QGraphicsItem*> itemlist = view->scene()->items(sceneRect, selectionMode);
    return filterForSelection(itemlist);
}

void QDeclarativeViewInspectorPrivate::changeToSingleSelectTool()
{
    selectionTool->setRubberbandSelectionMode(false);

    changeToSelectTool();

    emit q->selectToolActivated();
    q->sendCurrentTool(Constants::SelectionToolMode);
}

void QDeclarativeViewInspectorPrivate::changeToSelectTool()
{
    if (q->currentTool() == selectionTool)
        return;

    q->currentTool()->clear();
    q->setCurrentTool(selectionTool);
    q->currentTool()->clear();
    q->currentTool()->updateSelectedItems();
}

void QDeclarativeViewInspectorPrivate::changeToMarqueeSelectTool()
{
    changeToSelectTool();
    selectionTool->setRubberbandSelectionMode(true);

    emit q->marqueeSelectToolActivated();
    q->sendCurrentTool(Constants::MarqueeSelectionToolMode);
}

void QDeclarativeViewInspectorPrivate::changeToZoomTool()
{
    q->currentTool()->clear();
    q->setCurrentTool(zoomTool);
    q->currentTool()->clear();

    emit q->zoomToolActivated();
    q->sendCurrentTool(Constants::ZoomMode);
}

void QDeclarativeViewInspectorPrivate::changeToColorPickerTool()
{
    if (q->currentTool() == colorPickerTool)
        return;

    q->currentTool()->clear();
    q->setCurrentTool(colorPickerTool);
    q->currentTool()->clear();

    emit q->colorPickerActivated();
    q->sendCurrentTool(Constants::ColorPickerMode);
}

static bool isEditorItem(QGraphicsItem *item)
{
    return (item->type() == Constants::EditorItemType
            || item->type() == Constants::ResizeHandleItemType
            || item->data(Constants::EditorItemDataKey).toBool());
}

QList<QGraphicsItem*> QDeclarativeViewInspectorPrivate::filterForSelection(
    QList<QGraphicsItem*> &itemlist) const
{
    foreach (QGraphicsItem *item, itemlist) {
        if (isEditorItem(item))
            itemlist.removeOne(item);
    }

    return itemlist;
}

void QDeclarativeViewInspectorPrivate::_q_onStatusChanged(QDeclarativeView::Status status)
{
    if (status == QDeclarativeView::Ready)
        q->sendReloaded();
}

// adjusts bounding boxes on edges of screen to be visible
QRectF QDeclarativeViewInspector::adjustToScreenBoundaries(const QRectF &boundingRectInSceneSpace)
{
    int marginFromEdge = 1;
    QRectF boundingRect(boundingRectInSceneSpace);
    if (qAbs(boundingRect.left()) - 1 < 2)
        boundingRect.setLeft(marginFromEdge);

    QRect rect = data->view->rect();

    if (boundingRect.right() >= rect.right())
        boundingRect.setRight(rect.right() - marginFromEdge);

    if (qAbs(boundingRect.top()) - 1 < 2)
        boundingRect.setTop(marginFromEdge);

    if (boundingRect.bottom() >= rect.bottom())
        boundingRect.setBottom(rect.bottom() - marginFromEdge);

    return boundingRect;
}

} // namespace QmlJSDebugger


//                     The LLVM Compiler Infrastructure

#include <bitset>
#include <cstdlib>
#include <cassert>

#pragma clang diagnostic ignored "-Wtautological-compare"

template <std::size_t N>
std::bitset<N>
make_bitset()
{
    std::bitset<N> v;
    for (std::size_t i = 0; i < N; ++i)
        v[i] = static_cast<bool>(std::rand() & 1);
    return v;
}

template <std::size_t N>
void test_flip_all()
{
    std::bitset<N> v1 = make_bitset<N>();
    std::bitset<N> v2 = v1;
    v2.flip();
    for (std::size_t i = 0; i < N; ++i)
        assert(v2[i] == ~v1[i]);
}

{
    test_flip_all<0>();
    test_flip_all<1>();
    test_flip_all<31>();
    test_flip_all<32>();
    test_flip_all<33>();
    test_flip_all<63>();
    test_flip_all<64>();
    test_flip_all<65>();
    test_flip_all<1000>();
}


{
    std::bitset<N> v1 = make_bitset<N>();
    std::bitset<N> v2 = v1;
    v2.flip();
    for (std::size_t i = 0; i < N; ++i)
        assert(v2[i] == ~v1[i]);
}

//                     The LLVM Compiler Infrastructure

#include <bitset>
#include <cstdlib>
#include <cassert>

#pragma clang diagnostic ignored "-Wtautological-compare"

template <std::size_t N>
std::bitset<N>
make_bitset()
{
    std::bitset<N> v;
    for (std::size_t i = 0; i < N; ++i)
        v[i] = static_cast<bool>(std::rand() & 1);
    return v;
}

template <std::size_t N>
void test_flip_all()

int main()
{
    test_flip_all<0>();
    test_flip_all<1>();
    test_flip_all<31>();
    test_flip_all<32>();
    test_flip_all<33>();
    test_flip_all<63>();
    test_flip_all<64>();
    test_flip_all<65>();
    test_flip_all<1000>();
}


//                     The LLVM Compiler Infrastructure

#include <bitset>
#include <cstdlib>
#include <cassert>

#pragma clang diagnostic ignored "-Wtautological-compare"

template <std::size_t N>
std::bitset<N>
make_bitset()
{
    std::bitset<N> v;
    for (std::size_t i = 0; i < N; ++i)
        v[i] = static_cast<bool>(std::rand() & 1);
    return v;
}

template <std::size_t N>
void test_flip_all()
{
    std::bitset<N> v1 = make_bitset<N>();
    std::bitset<N> v2 = v1;
    v2.flip();
    for (std::size_t i = 0; i < N; ++i)
        assert(v2[i] == ~v1[i]);
}

int main()
{
    test_flip_all

    test_flip_all<1>();
    test_flip_all<31>();
    test_flip_all<32>();
    test_flip_all<33>();
    test_flip_all<63>();
    test_flip_all<64>();
    test_flip_all<65>();
    test_flip_all<1000>();
}


for (std::size_t i = 0; i < N; ++i)
        assert(v2[i] == ~v1[i]);

//                     The LLVM Compiler Infrastructure

#include <bitset>
#include <cstdlib>
#include <cassert>

#pragma clang diagnostic ignored "-Wtautological-compare"

template <std::size_t N>
std::bitset<N>
make_bitset()
{
    std::bitset<N> v;
    for (std::size_t i = 0; i < N; ++i)
        v[i] = static_cast<bool>(std::rand() & 1);
    return v;
}

template <std::size_t N>
void test_flip_all()
{
    std::bitset<N> v1 = make_bitset<N>();
    std::bitset<N> v2 = v1;
    v2.flip();

}

int main()
{
    test_flip_all<0>();
    test_flip_all<1>();
    test_flip_all<31>();
    test_flip_all<32>();
    test_flip_all<33>();
    test_flip_all<63>();
    test_flip_all<64>();
    test_flip_all<65>();
    test_flip_all<1000>();
}


void LayoutManager::calculateLayout(PtrVector<Widget>& widgets, AbstractTopLevelContainer* topLevelContainer)

//  SuperTuxKart - a fun racing game with go-kart

#include "guiengine/layout_manager.hpp"

#include <iostream>

#include <IGUIFont.h>
#include <ITexture.h>

#include "graphics/irr_driver.hpp"
#include "guiengine/abstract_top_level_container.hpp"
#include "guiengine/engine.hpp"
#include "guiengine/scalable_font.hpp"
#include "guiengine/skin.hpp"
#include "guiengine/widget.hpp"
#include "io/file_manager.hpp"
#include "utils/ptr_vector.hpp"
#include "utils/string_utils.hpp"
#include "utils/vs.hpp"

using namespace irr;
using namespace gui;
using namespace video;
using namespace GUIEngine;

/** Like atoi, but on error prints an error message to stderr */
int atoi_p(const char* val)
{
    int i;
    if (StringUtils::parseString<int>(val, &i))
    {
        return i;
    }
    else
    {
        Log::warn("LayoutManager", "Invalid value '%s' found in XML file where integer was expected.", val);
        return 0;
    }
}

bool LayoutManager::convertToCoord(std::string& x, int* absolute /* out */, int* percentage /* out */)
{
    int i = 0;
    if (!StringUtils::fromString<int>(x, i /* out */)) return false;

    if( x[x.size()-1] == '%' ) // percentage
    {
        *percentage = i;
        return true;
    }
    else if( x[x.size()-1] == 'f' ) // font height
    {
        *absolute = i * GUIEngine::getFontHeight();
    	return true;
    }
    else // absolute number
    {
        *absolute = i;
        return true;
    }
}

void LayoutManager::readCoords(Widget* self)
{
    // determine widget position and size if not already done by sizers
    std::string x      = self->m_properties[PROP_X];
    std::string y      = self->m_properties[PROP_Y];
    std::string width  = self->m_properties[PROP_WIDTH];
    std::string height = self->m_properties[PROP_HEIGHT];

    /*
    // retrieve parent size (or screen size if none). Will be useful for layout
    // and especially for percentages.
    unsigned int parent_w, parent_h, parent_x, parent_y;
    if(parent == NULL)
    {
        parent_w = topLevelContainer->getWidth();
        parent_h = topLevelContainer->getHeight();
        parent_x = 0;
        parent_y = 0;
    }
    else
    {
        parent_w = parent->m_w;
        parent_h = parent->m_h;
        parent_x = parent->m_x;
        parent_y = parent->m_y;
    }
    */

    // ---- try converting to number
    // x coord
    {
        int abs_x = 0x7FFFFFFF, percent_x = 0x7FFFFFFF;
        if (convertToCoord(x, &abs_x, &percent_x ))
        {
            if      (abs_x >= 0 && abs_x != 0x7FFFFFFF)
            {
                self->m_absolute_x = abs_x;
            }
            else if (abs_x < 0)
            {
                self->m_absolute_reverse_x = abs(abs_x);
            }
            else if (percent_x >= 0 && percent_x != 0x7FFFFFFF)
            {
                self->m_relative_x = (float)percent_x;
            }
        }
    }

    // y coord
    {
        int abs_y = 0x7FFFFFFF, percent_y = 0x7FFFFFFF;
        if (convertToCoord(y, &abs_y, &percent_y ))
        {
            if      (abs_y >= 0 && abs_y != 0x7FFFFFFF)
            {
                self->m_absolute_y = abs_y;
            }
            else if (abs_y < 0)
            {
                self->m_absolute_reverse_y = abs(abs_y);
            }
            else if (percent_y >= 0 && percent_y != 0x7FFFFFFF)
            {
                self->m_relative_y = (float)percent_y;
            }
        }
    }

    // ---- if this widget has an icon, get icon size. this can helpful determine its optimal size
    int texture_w = -1, texture_h = -1;

    if (self->m_properties[PROP_ICON].size() > 0)
    {
        // PROP_ICON includes paths (e.g. gui/icons/logo.png)
        ITexture* texture = irr_driver->getTexture(
            GUIEngine::getSkin()->getThemedIcon(self->m_properties[PROP_ICON]));

        if (texture != NULL)
        {
            texture_w = texture->getSize().Width;
            texture_h = texture->getSize().Height;
        }
    }

    // ---- if this widget has a label, get text size. this can helpful determine its optimal size
    int label_w = -1, label_h = -1;
    if (self->m_text.size() > 0)
    {
        IGUIFont* font = (self->m_title_font ? GUIEngine::getTitleFont() : GUIEngine::getFont());
        core::dimension2d< u32 > dim = font->getDimension( self->m_text.c_str() );
        label_w = dim.Width + self->getWidthNeededAroundLabel();
        // FIXME - won't work with multiline labels. thus, for now, when multiple
        // lines are required, we need to specify a height explicitely
        label_h = dim.Height + self->getHeightNeededAroundLabel();
    }
    else if (self->getType() == WTYPE_CHECKBOX)
    {
        // User text height to guess checkbox size
        IGUIFont* font = (self->m_title_font ? GUIEngine::getTitleFont() : GUIEngine::getFont());
        core::dimension2d< u32 > dim = font->getDimension( L"X" );
        label_h = dim.Height + self->getHeightNeededAroundLabel();
        label_w = label_h; // a checkbox is square
    }

    if (label_h == -1)
    {
        if (self->getType() == WTYPE_TEXTBOX ||
            self->getType() == WTYPE_BUTTON  ||
            self->getType() == WTYPE_LABEL   ||
            self->getType() == WTYPE_SPINNER)
        {
            IGUIFont* font = (self->m_title_font ? GUIEngine::getTitleFont() : GUIEngine::getFont());

            // get text height, a text box, button or label is always as high as the text it could contain
            core::dimension2d< u32 > dim = font->getDimension( L"X" );
            label_h = dim.Height + self->getHeightNeededAroundLabel();
        }
    }

    // ---- if this widget has children, get their size size. this can helpful determine its optimal size
    if (self->m_properties[PROP_WIDTH] == "fit" || self->m_properties[PROP_HEIGHT] == "fit")
    {
        bool is_vertical_row = (self->m_properties[PROP_LAYOUT] == "vertical-row");
        bool is_horizontal_row = (self->m_properties[PROP_LAYOUT] == "horizontal-row");

        int child_max_width = -1, child_max_height = -1;
        int total_width = 0, total_height = 0;

        for (unsigned int child=0; child<self->m_children.size(); child++)
        {
            if (self->m_children[child].m_absolute_w > -1)
            {
                if (is_horizontal_row)
                {
                    total_width += self->m_children[child].m_absolute_w ;
                }
                else
                {
                    if (child_max_width == -1 || self->m_children[child].m_absolute_w > child_max_width)
                    {
                        child_max_width = self->m_children[child].m_absolute_w;
                    }
                }
            }

            if (self->m_children[child].m_absolute_h > -1)
            {
                if (is_vertical_row)
                {
                    total_height += self->m_children[child].m_absolute_h;
                }
                else
                {
                    if (child_max_height == -1 || self->m_children[child].m_absolute_h > child_max_height)
                    {
                        child_max_height = self->m_children[child].m_absolute_h;
                    }
                }
            }
        }

        //Add padding to <box> elements
        if (self->getType() == WTYPE_DIV && self->m_show_bounding_box)
        {
            int padding = GUIEngine::getFontHeight() / 2;
            if (self->m_properties[PROP_DIV_PADDING].length() > 0)
                padding = atoi(self->m_properties[PROP_DIV_PADDING].c_str());
            child_max_height += padding * 2;
            total_height += padding * 2;
            total_width += padding * 2;
            child_max_width += padding * 2;
        }

        if (self->m_properties[PROP_WIDTH] == "fit")
        {
            self->m_absolute_w = (is_horizontal_row ? total_width : child_max_width);
        }
        if (self->m_properties[PROP_HEIGHT] == "fit")
        {
            self->m_absolute_h = (is_vertical_row ? total_height : child_max_height);
        }
    }

    // width
    {
        int abs_w = -1, percent_w = -1;
        if (width == "font") self->m_absolute_w = GUIEngine::getFontHeight();
        else if (convertToCoord(width, &abs_w, &percent_w ))
        {
            if      (abs_w > -1)     self->m_absolute_w = abs_w;
            else if (percent_w > -1) self->m_relative_w = (float)percent_w;
        }
        else if(texture_w > -1) self->m_absolute_w = texture_w;
        else if(label_w > -1)   self->m_absolute_w = label_w;
    }

    // height
    {
        int abs_h = -1, percent_h = -1;
        if (height == "font") self->m_absolute_h = GUIEngine::getFontHeight();
        else if (convertToCoord(height, &abs_h, &percent_h ))
        {
            if      (abs_h > -1)     self->m_absolute_h = abs_h;
            else if (percent_h > -1) self->m_relative_h = (float)percent_h;
        }
        else if (texture_h > -1 && label_h > -1) self->m_absolute_h = texture_h + label_h; // label + icon
        else if (texture_h > -1)                 self->m_absolute_h = texture_h;
        else if (label_h > -1)                   self->m_absolute_h = label_h;
    }

    if (self->getType() != WTYPE_RIBBON) // Ribbons have their own handling
    {
        // Set vertical inner padding
        self->m_absolute_h += self->m_absolute_h * SkinConfig::getVerticalInnerPadding(self->getType(), self);

        // Set horizontal inner padding
        self->m_absolute_w += self->m_absolute_w * SkinConfig::getHorizontalInnerPadding(self->getType(), self);
    }

}

void LayoutManager::applyCoords(Widget* self, AbstractTopLevelContainer* topLevelContainer, Widget* parent)
{
    // retrieve parent size (or screen size if none). Will be useful for layout
    // and especially for percentages.
    unsigned int parent_w, parent_h, parent_x, parent_y;
    if(parent == NULL)
    {
        parent_w = topLevelContainer->getWidth();
        int left_padding = irr_driver->getDevice()->getLeftPadding();
        int right_padding = irr_driver->getDevice()->getRightPadding();
        if (!topLevelContainer->enableScreenPadding())
            left_padding = right_padding = 0;
        int total_padding = left_padding + right_padding;
        if (parent_w - total_padding > 0)
            parent_w -= total_padding;
        parent_h = topLevelContainer->getHeight();
        parent_x = 0;
        if (left_padding > 0)
            parent_x = left_padding;
        parent_y = 0;
    }
    else
    {
        parent_w = parent->m_w;
        parent_h = parent->m_h;
        parent_x = parent->m_x;
        parent_y = parent->m_y;
    }

    if (parent != NULL && parent->getType() == WTYPE_DIV && parent->m_show_bounding_box)
    {
        int padding = GUIEngine::getFontHeight() / 2;
        if (parent->m_properties[PROP_DIV_PADDING].length() > 0)
            padding = atoi(parent->m_properties[PROP_DIV_PADDING].c_str());

        parent_x += padding;
        parent_y += padding;
        parent_w -= padding*2;
        parent_h -= padding*2;
    }

    if      (self->m_absolute_x > -1)         self->m_x = parent_x + self->m_absolute_x;
    else if (self->m_absolute_reverse_x > -1) self->m_x = parent_x + (parent_w - self->m_absolute_reverse_x);
    else if (self->m_relative_x > -1)         self->m_x = (int)(parent_x + parent_w*self->m_relative_x/100);

    if      (self->m_absolute_y > -1)         self->m_y = parent_y + self->m_absolute_y;
    else if (self->m_absolute_reverse_y > -1) self->m_y = parent_y + (parent_h - self->m_absolute_reverse_y);
    else if (self->m_relative_y > -1)         self->m_y = (int)(parent_y + parent_h*self->m_relative_y/100);

    if (self->m_absolute_w > -1)      self->m_w = self->m_absolute_w;
    else if (self->m_relative_w > -1) self->m_w = (int)roundf(parent_w*self->m_relative_w/100.0f);

    if (self->m_absolute_h > -1)      self->m_h = self->m_absolute_h;
    else if (self->m_relative_h > -1) self->m_h = (int)roundf(parent_h*self->m_relative_h/100.0f);

    // ---- can't make widget bigger than parent
    if (self->m_h > (int)parent_h)
    {
        float ratio = (float)parent_h / self->m_h;

        self->m_w = (int)(self->m_w*ratio);
        self->m_h = (int)(self->m_h*ratio);
    }
    if (self->m_w > (int)parent_w)
    {
        // scale down while keeping aspect ratio (don't do this for text widgets though...)
        float ratio = (float)parent_w / self->m_w;

        self->m_w = (int)(self->m_w * ratio);

        // FIXME: ugly to hardcode widgets types here
        if (self->m_type != WTYPE_LABEL && self->m_type != WTYPE_BUBBLE)
        {
            self->m_h = (int)(self->m_h * ratio);
        }
    }

    // ------ check for given max size
    if (self->m_properties[PROP_MAX_WIDTH].size() > 0)
    {
        const int max_width = atoi_p( self->m_properties[PROP_MAX_WIDTH].c_str() );
        if (self->m_w > max_width) self->m_w = max_width;
    }

    if (self->m_properties[PROP_MAX_HEIGHT].size() > 0)
    {
        const int max_height = atoi_p( self->m_properties[PROP_MAX_HEIGHT].c_str() );
        if (self->m_h > max_height) self->m_h = max_height;
    }
}

void LayoutManager::recursivelyReadCoords(PtrVector<Widget>& widgets)
{
    const unsigned short widgets_amount = widgets.size();

    // ----- deal with containers' children
    for (int n=0; n<widgets_amount; n++)
    {
        if (widgets[n].m_type == WTYPE_DIV) recursivelyReadCoords(widgets[n].m_children);
    }

    for (unsigned short n=0; n<widgets_amount; n++)
    {
        readCoords(widgets.get(n));
    }//next widget
}

{
    recursivelyReadCoords(widgets);
    doCalculateLayout(widgets, topLevelContainer, NULL);
}

void LayoutManager::doCalculateLayout(PtrVector<Widget>& widgets, AbstractTopLevelContainer* topLevelContainer,
                                      Widget* parent)
{
    const unsigned short widgets_amount = widgets.size();

    for (int n=0; n<widgets_amount; n++)
    {
        applyCoords(widgets.get(n), topLevelContainer, parent);
    }

    do // i'm using 'while false' here just to be able to 'break' ...
    {
        if(parent == NULL) break;

        std::string layout_name = parent->m_properties[PROP_LAYOUT];
        if(layout_name.size() < 1) break;

        bool horizontal = false;

        if (!strcmp("horizontal-row", layout_name.c_str()))
            horizontal = true;
        else if(!strcmp("vertical-row", layout_name.c_str()))
            horizontal = false;
        else
        {
            Log::error("LayoutManager::doCalculateLayout", "Unknown layout name: %s", layout_name.c_str());
            break;
        }

        int x = parent->m_x;
        int y = parent->m_y;
        int w = parent->m_w;
        int h = parent->m_h;

        if (parent != NULL && parent->getType() == WTYPE_DIV && parent->m_show_bounding_box)
        {
            int padding = GUIEngine::getFontHeight() / 2;
            if (parent->m_properties[PROP_DIV_PADDING].length() > 0)
                padding = atoi(parent->m_properties[PROP_DIV_PADDING].c_str());

            x += padding;
            y += padding;
            w -= padding*2;
            h -= padding*2;
        }

        // find space left after placing all absolutely-sized widgets in a row
        // (the space left will be divided between remaining widgets later)
        int left_space = (horizontal ? w : h);
        unsigned short total_proportion = 0;
        for(int n=0; n<widgets_amount; n++)
        {
            // relatively-sized widget
            std::string prop = widgets[n].m_properties[ PROP_PROPORTION ];
            if(prop.size() != 0)
            {
                total_proportion += atoi_p( prop.c_str() );
                continue;
            }

            // absolutely-sized widgets
            left_space -= (horizontal ? widgets[n].m_w : widgets[n].m_h);
        } // next widget

        if (left_space < 0)
        {
            Log::warn("LayoutManager", "Statically sized widgets took all the place!!");
            left_space = 0;
        }

        // ---- lay widgets in row
        for (int n=0; n<widgets_amount; n++)
        {
            std::string prop = widgets[n].m_properties[ PROP_PROPORTION ];
            if(prop.size() != 0)
            {
                // proportional size
                int proportion = 1;
                std::istringstream myStream(prop);
                if (!(myStream >> proportion))
                    Log::warn("LayoutManager::doCalculateLayout",
                        "Proportion '%s' is not a number for widget %s", prop.c_str(),
                        widgets[n].m_properties[PROP_ID].c_str());

                const float fraction = (float)proportion/(float)total_proportion;

                if (horizontal)
                {
                    widgets[n].m_x = x;

                    std::string align = widgets[n].m_properties[ PROP_ALIGN ];
                    if (align.size() < 1)
                    {
                        std::string prop_y = widgets[n].m_properties[ PROP_Y ];
                        if (prop_y.size() > 0)
                        {
                            if (prop_y[ prop_y.size()-1 ] == '%')
                            {
                                prop_y = prop_y.substr(0, prop_y.size() - 1);
                                widgets[n].m_y = (int)(y + atoi_p(prop_y.c_str())/100.0f * h);
                            }
                            else if(prop_y[ prop_y.size()-1 ] == 'f')
                            {
                                widgets[n].m_y = y + atoi_p(prop_y.c_str()) * GUIEngine::getFontHeight();
                            }
                            else
                            {
                                widgets[n].m_y = y + atoi_p(prop_y.c_str());
                            }
                        }
                        else
                        {
                            widgets[n].m_y = y;
                        }
                    }
                    else if (align == "top")
                    {
                        widgets[n].m_y = y;
                    }
                    else if (align == "center")
                    {
                        widgets[n].m_y = y + h/2 - widgets[n].m_h/2;
                    }
                    else if (align == "bottom")
                    {
                        widgets[n].m_y = y + h - widgets[n].m_h;
                    }
                    else
                    {
                        Log::warn("LayoutManager::doCalculateLayout",
                            "Alignment '%s' is unknown (widget '%s', in a horiozntal-row layout)",
                            align.c_str(), widgets[n].m_properties[PROP_ID].c_str());
                    }

                    widgets[n].m_w = (int)(left_space*fraction);
                    if (widgets[n].m_properties[PROP_MAX_WIDTH].size() > 0)
                    {
                        const int max_width = atoi_p( widgets[n].m_properties[PROP_MAX_WIDTH].c_str() );
                        if (widgets[n].m_w > max_width) widgets[n].m_w = max_width;
                    }

                    if (widgets[n].m_w <= 0)
                    {
                        Log::warn("LayoutManager", "Widget '%s' has a width of %i (left_space = %i, "
                                  "fraction = %f, max_width = %s)", widgets[n].m_properties[PROP_ID].c_str(),
                                  widgets[n].m_w, left_space, fraction, widgets[n].m_properties[PROP_MAX_WIDTH].c_str());
                        widgets[n].m_w = 1;
                    }

                    x += widgets[n].m_w;
                }
                else
                {
                    widgets[n].m_h = (int)(left_space*fraction);

                    if (widgets[n].m_properties[PROP_MAX_HEIGHT].size() > 0)
                    {
                        const int max_height = atoi_p( widgets[n].m_properties[PROP_MAX_HEIGHT].c_str() );
                        if (widgets[n].m_h > max_height) widgets[n].m_h = max_height;
                    }

                    if (widgets[n].m_h <= 0)
                    {
                        Log::warn("LayoutManager", "Widget '%s' has a height of %i (left_space = %i, "
                                  "fraction = %f, max_width = %s)\n", widgets[n].m_properties[PROP_ID].c_str(),
                                  widgets[n].m_h, left_space, fraction, widgets[n].m_properties[PROP_MAX_WIDTH].c_str());
                        widgets[n].m_h = 1;
                    }

                    std::string align = widgets[n].m_properties[ PROP_ALIGN ];
                    if (align.size() < 1)
                    {
                        std::string prop_x = widgets[n].m_properties[ PROP_X ];
                        if (prop_x.size() > 0)
                        {
                            if (prop_x[ prop_x.size()-1 ] == '%')
                            {
                                prop_x = prop_x.substr(0, prop_x.size() - 1);
                                widgets[n].m_x = (int)(x + atoi_p(prop_x.c_str())/100.0f * w);
                            }
                            else if(prop_x[ prop_x.size()-1 ] == 'f')
                            {
                                widgets[n].m_x = x + atoi_p(prop_x.c_str()) * GUIEngine::getFontHeight();
                            }
                            else
                            {
                                widgets[n].m_x = x + atoi_p(prop_x.c_str());
                            }
                        }
                        else
                        {
                            widgets[n].m_x = x;
                        }
                    }
                    else if (align == "left")
                    {
                        widgets[n].m_x = x;
                    }
                    else if (align == "center")
                    {
                        widgets[n].m_x = x + w/2 - widgets[n].m_w/2;
                    }
                    else if (align == "right")
                    {
                        widgets[n].m_x = x + w - widgets[n].m_w;
                    }
                    else
                    {
                        Log::warn("LayoutManager::doCalculateLayout",
                            "Alignment '%s' is unknown (widget '%s', in a vertical-row layout)",
                            align.c_str(), widgets[n].m_properties[PROP_ID].c_str());
                    }
                    widgets[n].m_y = y;

                    y += widgets[n].m_h;
                }
            }
            else
            {
                // absolute size

                if (horizontal)
                {
                    widgets[n].m_x = x;

                    std::string align = widgets[n].m_properties[ PROP_ALIGN ];

                    if (align.size() < 1)
                    {
                        std::string prop_y = widgets[n].m_properties[ PROP_Y ];

                        if (prop_y.size() > 0)
                        {
                            if (prop_y[ prop_y.size()-1 ] == '%')
                            {
                                prop_y = prop_y.substr(0, prop_y.size() - 1);
                                widgets[n].m_y = (int)(y + atoi_p(prop_y.c_str())/100.0f * h);
                            }
                            else if(prop_y[ prop_y.size()-1 ] == 'f')
                            {
                                widgets[n].m_y = y + atoi_p(prop_y.c_str()) * GUIEngine::getFontHeight();
                            }
                            else
                            {
                                widgets[n].m_y = y + atoi_p(prop_y.c_str());
                            }
                        }
                        else
                        {
                            widgets[n].m_y = y;
                        }
                    }
                    else if (align == "top")
                    {
                        widgets[n].m_y = y;
                    }
                    else if (align == "center")
                    {
                        widgets[n].m_y = y + h/2 - widgets[n].m_h/2;
                    }
                    else if (align == "bottom")
                    {
                        widgets[n].m_y = y + h - widgets[n].m_h;
                    }
                    else
                    {
                        Log::warn("LayoutManager::doCalculateLayout",
                            "Alignment '%s' is unknown in widget '%s'",
                            align.c_str(), widgets[n].m_properties[PROP_ID].c_str());
                    }

                    x += widgets[n].m_w;
                }
                else
                {
                    std::string align = widgets[n].m_properties[ PROP_ALIGN ];

                    if (align.size() < 1)
                    {
                        std::string prop_x = widgets[n].m_properties[ PROP_X ];

                        if (prop_x.size() > 0)
                        {
                            if (prop_x[ prop_x.size()-1 ] == '%')
                            {
                                prop_x = prop_x.substr(0, prop_x.size() - 1);
                                widgets[n].m_x = (int)(x + atoi_p(prop_x.c_str())/100.0f * w);
                            }
                            else if(prop_x[ prop_x.size()-1 ] == 'f')
                            {
                                widgets[n].m_x = x + atoi_p(prop_x.c_str()) * GUIEngine::getFontHeight();
                            }
                            else
                            {
                                widgets[n].m_x = x + atoi_p(prop_x.c_str());
                            }
                        }
                        else
                        {
                            widgets[n].m_x = x;
                        }
                    }
                    else if (align == "left")
                    {
                        widgets[n].m_x = x;
                    }
                    else if (align == "center")
                    {
                        widgets[n].m_x = x + w/2 - widgets[n].m_w/2;
                    }
                    else if (align == "right")
                    {
                        widgets[n].m_x = x + w - widgets[n].m_w;
                    }
                    else
                    {
                        Log::warn("LayoutManager::doCalculateLayout",
                            "Alignment '%s' is unknown in widget '%s'",
                            align.c_str(), widgets[n].m_properties[PROP_ID].c_str());
                    }
                    widgets[n].m_y = y;

                    y += widgets[n].m_h;
                }
            } // end if property or absolute size

        } // next widget

    } while(false);

    // ----- also deal with containers' children
    for (int n=0; n<widgets_amount; n++)
    {
        if (widgets[n].m_type == WTYPE_DIV)
        {
            doCalculateLayout(widgets[n].m_children, topLevelContainer, &widgets[n]);
        }
    }
}   // calculateLayout


{
    recursivelyReadCoords(widgets);
    doCalculateLayout(widgets, topLevelContainer, NULL);
}

//  SuperTuxKart - a fun racing game with go-kart

#include "guiengine/layout_manager.hpp"

#include <iostream>

#include <IGUIFont.h>
#include <ITexture.h>

#include "graphics/irr_driver.hpp"
#include "guiengine/abstract_top_level_container.hpp"
#include "guiengine/engine.hpp"
#include "guiengine/scalable_font.hpp"
#include "guiengine/skin.hpp"
#include "guiengine/widget.hpp"
#include "io/file_manager.hpp"
#include "utils/ptr_vector.hpp"
#include "utils/string_utils.hpp"
#include "utils/vs.hpp"

using namespace irr;
using namespace gui;
using namespace video;
using namespace GUIEngine;

/** Like atoi, but on error prints an error message to stderr */
int atoi_p(const char* val)
{
    int i;
    if (StringUtils::parseString<int>(val, &i))
    {
        return i;
    }
    else
    {
        Log::warn("LayoutManager", "Invalid value '%s' found in XML file where integer was expected.", val);
        return 0;
    }
}

bool LayoutManager::convertToCoord(std::string& x, int* absolute /* out */, int* percentage /* out */)
{
    int i = 0;
    if (!StringUtils::fromString<int>(x, i /* out */)) return false;

    if( x[x.size()-1] == '%' ) // percentage
    {
        *percentage = i;
        return true;
    }
    else if( x[x.size()-1] == 'f' ) // font height
    {
        *absolute = i * GUIEngine::getFontHeight();
    	return true;
    }
    else // absolute number
    {
        *absolute = i;
        return true;
    }
}

void LayoutManager::readCoords(Widget* self)
{
    // determine widget position and size if not already done by sizers
    std::string x      = self->m_properties[PROP_X];
    std::string y      = self->m_properties[PROP_Y];
    std::string width  = self->m_properties[PROP_WIDTH];
    std::string height = self->m_properties[PROP_HEIGHT];

    /*
    // retrieve parent size (or screen size if none). Will be useful for layout
    // and especially for percentages.
    unsigned int parent_w, parent_h, parent_x, parent_y;
    if(parent == NULL)
    {
        parent_w = topLevelContainer->getWidth();
        parent_h = topLevelContainer->getHeight();
        parent_x = 0;
        parent_y = 0;
    }
    else
    {
        parent_w = parent->m_w;
        parent_h = parent->m_h;
        parent_x = parent->m_x;
        parent_y = parent->m_y;
    }
    */

    // ---- try converting to number
    // x coord
    {
        int abs_x = 0x7FFFFFFF, percent_x = 0x7FFFFFFF;
        if (convertToCoord(x, &abs_x, &percent_x ))
        {
            if      (abs_x >= 0 && abs_x != 0x7FFFFFFF)
            {
                self->m_absolute_x = abs_x;
            }
            else if (abs_x < 0)
            {
                self->m_absolute_reverse_x = abs(abs_x);
            }
            else if (percent_x >= 0 && percent_x != 0x7FFFFFFF)
            {
                self->m_relative_x = (float)percent_x;
            }
        }
    }

    // y coord
    {
        int abs_y = 0x7FFFFFFF, percent_y = 0x7FFFFFFF;
        if (convertToCoord(y, &abs_y, &percent_y ))
        {
            if      (abs_y >= 0 && abs_y != 0x7FFFFFFF)
            {
                self->m_absolute_y = abs_y;
            }
            else if (abs_y < 0)
            {
                self->m_absolute_reverse_y = abs(abs_y);
            }
            else if (percent_y >= 0 && percent_y != 0x7FFFFFFF)
            {
                self->m_relative_y = (float)percent_y;
            }
        }
    }

    // ---- if this widget has an icon, get icon size. this can helpful determine its optimal size
    int texture_w = -1, texture_h = -1;

    if (self->m_properties[PROP_ICON].size() > 0)
    {
        // PROP_ICON includes paths (e.g. gui/icons/logo.png)
        ITexture* texture = irr_driver->getTexture(
            GUIEngine::getSkin()->getThemedIcon(self->m_properties[PROP_ICON]));

        if (texture != NULL)
        {
            texture_w = texture->getSize().Width;
            texture_h = texture->getSize().Height;
        }
    }

    // ---- if this widget has a label, get text size. this can helpful determine its optimal size
    int label_w = -1, label_h = -1;
    if (self->m_text.size() > 0)
    {
        IGUIFont* font = (self->m_title_font ? GUIEngine::getTitleFont() : GUIEngine::getFont());
        core::dimension2d< u32 > dim = font->getDimension( self->m_text.c_str() );
        label_w = dim.Width + self->getWidthNeededAroundLabel();
        // FIXME - won't work with multiline labels. thus, for now, when multiple
        // lines are required, we need to specify a height explicitely
        label_h = dim.Height + self->getHeightNeededAroundLabel();
    }
    else if (self->getType() == WTYPE_CHECKBOX)
    {
        // User text height to guess checkbox size
        IGUIFont* font = (self->m_title_font ? GUIEngine::getTitleFont() : GUIEngine::getFont());
        core::dimension2d< u32 > dim = font->getDimension( L"X" );
        label_h = dim.Height + self->getHeightNeededAroundLabel();
        label_w = label_h; // a checkbox is square
    }

    if (label_h == -1)
    {
        if (self->getType() == WTYPE_TEXTBOX ||
            self->getType() == WTYPE_BUTTON  ||
            self->getType() == WTYPE_LABEL   ||
            self->getType() == WTYPE_SPINNER)
        {
            IGUIFont* font = (self->m_title_font ? GUIEngine::getTitleFont() : GUIEngine::getFont());

            // get text height, a text box, button or label is always as high as the text it could contain
            core::dimension2d< u32 > dim = font->getDimension( L"X" );
            label_h = dim.Height + self->getHeightNeededAroundLabel();
        }
    }

    // ---- if this widget has children, get their size size. this can helpful determine its optimal size
    if (self->m_properties[PROP_WIDTH] == "fit" || self->m_properties[PROP_HEIGHT] == "fit")
    {
        bool is_vertical_row = (self->m_properties[PROP_LAYOUT] == "vertical-row");
        bool is_horizontal_row = (self->m_properties[PROP_LAYOUT] == "horizontal-row");

        int child_max_width = -1, child_max_height = -1;
        int total_width = 0, total_height = 0;

        for (unsigned int child=0; child<self->m_children.size(); child++)
        {
            if (self->m_children[child].m_absolute_w > -1)
            {
                if (is_horizontal_row)
                {
                    total_width += self->m_children[child].m_absolute_w ;
                }
                else
                {
                    if (child_max_width == -1 || self->m_children[child].m_absolute_w > child_max_width)
                    {
                        child_max_width = self->m_children[child].m_absolute_w;
                    }
                }
            }

            if (self->m_children[child].m_absolute_h > -1)
            {
                if (is_vertical_row)
                {
                    total_height += self->m_children[child].m_absolute_h;
                }
                else
                {
                    if (child_max_height == -1 || self->m_children[child].m_absolute_h > child_max_height)
                    {
                        child_max_height = self->m_children[child].m_absolute_h;
                    }
                }
            }
        }

        //Add padding to <box> elements
        if (self->getType() == WTYPE_DIV && self->m_show_bounding_box)
        {
            int padding = GUIEngine::getFontHeight() / 2;
            if (self->m_properties[PROP_DIV_PADDING].length() > 0)
                padding = atoi(self->m_properties[PROP_DIV_PADDING].c_str());
            child_max_height += padding * 2;
            total_height += padding * 2;
            total_width += padding * 2;
            child_max_width += padding * 2;
        }

        if (self->m_properties[PROP_WIDTH] == "fit")
        {
            self->m_absolute_w = (is_horizontal_row ? total_width : child_max_width);
        }
        if (self->m_properties[PROP_HEIGHT] == "fit")
        {
            self->m_absolute_h = (is_vertical_row ? total_height : child_max_height);
        }
    }

    // width
    {
        int abs_w = -1, percent_w = -1;
        if (width == "font") self->m_absolute_w = GUIEngine::getFontHeight();
        else if (convertToCoord(width, &abs_w, &percent_w ))
        {
            if      (abs_w > -1)     self->m_absolute_w = abs_w;
            else if (percent_w > -1) self->m_relative_w = (float)percent_w;
        }
        else if(texture_w > -1) self->m_absolute_w = texture_w;
        else if(label_w > -1)   self->m_absolute_w = label_w;
    }

    // height
    {
        int abs_h = -1, percent_h = -1;
        if (height == "font") self->m_absolute_h = GUIEngine::getFontHeight();
        else if (convertToCoord(height, &abs_h, &percent_h ))
        {
            if      (abs_h > -1)     self->m_absolute_h = abs_h;
            else if (percent_h > -1) self->m_relative_h = (float)percent_h;
        }
        else if (texture_h > -1 && label_h > -1) self->m_absolute_h = texture_h + label_h; // label + icon
        else if (texture_h > -1)                 self->m_absolute_h = texture_h;
        else if (label_h > -1)                   self->m_absolute_h = label_h;
    }

    if (self->getType() != WTYPE_RIBBON) // Ribbons have their own handling
    {
        // Set vertical inner padding
        self->m_absolute_h += self->m_absolute_h * SkinConfig::getVerticalInnerPadding(self->getType(), self);

        // Set horizontal inner padding
        self->m_absolute_w += self->m_absolute_w * SkinConfig::getHorizontalInnerPadding(self->getType(), self);
    }

}

void LayoutManager::applyCoords(Widget* self, AbstractTopLevelContainer* topLevelContainer, Widget* parent)
{
    // retrieve parent size (or screen size if none). Will be useful for layout
    // and especially for percentages.
    unsigned int parent_w, parent_h, parent_x, parent_y;
    if(parent == NULL)
    {
        parent_w = topLevelContainer->getWidth();
        int left_padding = irr_driver->getDevice()->getLeftPadding();
        int right_padding = irr_driver->getDevice()->getRightPadding();
        if (!topLevelContainer->enableScreenPadding())
            left_padding = right_padding = 0;
        int total_padding = left_padding + right_padding;
        if (parent_w - total_padding > 0)
            parent_w -= total_padding;
        parent_h = topLevelContainer->getHeight();
        parent_x = 0;
        if (left_padding > 0)
            parent_x = left_padding;
        parent_y = 0;
    }
    else
    {
        parent_w = parent->m_w;
        parent_h = parent->m_h;
        parent_x = parent->m_x;
        parent_y = parent->m_y;
    }

    if (parent != NULL && parent->getType() == WTYPE_DIV && parent->m_show_bounding_box)
    {
        int padding = GUIEngine::getFontHeight() / 2;
        if (parent->m_properties[PROP_DIV_PADDING].length() > 0)
            padding = atoi(parent->m_properties[PROP_DIV_PADDING].c_str());

        parent_x += padding;
        parent_y += padding;
        parent_w -= padding*2;
        parent_h -= padding*2;
    }

    if      (self->m_absolute_x > -1)         self->m_x = parent_x + self->m_absolute_x;
    else if (self->m_absolute_reverse_x > -1) self->m_x = parent_x + (parent_w - self->m_absolute_reverse_x);
    else if (self->m_relative_x > -1)         self->m_x = (int)(parent_x + parent_w*self->m_relative_x/100);

    if      (self->m_absolute_y > -1)         self->m_y = parent_y + self->m_absolute_y;
    else if (self->m_absolute_reverse_y > -1) self->m_y = parent_y + (parent_h - self->m_absolute_reverse_y);
    else if (self->m_relative_y > -1)         self->m_y = (int)(parent_y + parent_h*self->m_relative_y/100);

    if (self->m_absolute_w > -1)      self->m_w = self->m_absolute_w;
    else if (self->m_relative_w > -1) self->m_w = (int)roundf(parent_w*self->m_relative_w/100.0f);

    if (self->m_absolute_h > -1)      self->m_h = self->m_absolute_h;
    else if (self->m_relative_h > -1) self->m_h = (int)roundf(parent_h*self->m_relative_h/100.0f);

    // ---- can't make widget bigger than parent
    if (self->m_h > (int)parent_h)
    {
        float ratio = (float)parent_h / self->m_h;

        self->m_w = (int)(self->m_w*ratio);
        self->m_h = (int)(self->m_h*ratio);
    }
    if (self->m_w > (int)parent_w)
    {
        // scale down while keeping aspect ratio (don't do this for text widgets though...)
        float ratio = (float)parent_w / self->m_w;

        self->m_w = (int)(self->m_w * ratio);

        // FIXME: ugly to hardcode widgets types here
        if (self->m_type != WTYPE_LABEL && self->m_type != WTYPE_BUBBLE)
        {
            self->m_h = (int)(self->m_h * ratio);
        }
    }

    // ------ check for given max size
    if (self->m_properties[PROP_MAX_WIDTH].size() > 0)
    {
        const int max_width = atoi_p( self->m_properties[PROP_MAX_WIDTH].c_str() );
        if (self->m_w > max_width) self->m_w = max_width;
    }

    if (self->m_properties[PROP_MAX_HEIGHT].size() > 0)
    {
        const int max_height = atoi_p( self->m_properties[PROP_MAX_HEIGHT].c_str() );
        if (self->m_h > max_height) self->m_h = max_height;
    }
}

void LayoutManager::recursivelyReadCoords(PtrVector<Widget>& widgets)
{
    const unsigned short widgets_amount = widgets.size();

    // ----- deal with containers' children
    for (int n=0; n<widgets_amount; n++)
    {
        if (widgets[n].m_type == WTYPE_DIV) recursivelyReadCoords(widgets[n].m_children);
    }

    for (unsigned short n=0; n<widgets_amount; n++)
    {
        readCoords(widgets.get(n));
    }//next widget
}

void LayoutManager::calculateLayout(PtrVector<Widget>& widgets, AbstractTopLevelContainer* topLevelContainer)

void LayoutManager::doCalculateLayout(PtrVector<Widget>& widgets, AbstractTopLevelContainer* topLevelContainer,
                                      Widget* parent)
{
    const unsigned short widgets_amount = widgets.size();

    for (int n=0; n<widgets_amount; n++)
    {
        applyCoords(widgets.get(n), topLevelContainer, parent);
    }

    do // i'm using 'while false' here just to be able to 'break' ...
    {
        if(parent == NULL) break;

        std::string layout_name = parent->m_properties[PROP_LAYOUT];
        if(layout_name.size() < 1) break;

        bool horizontal = false;

        if (!strcmp("horizontal-row", layout_name.c_str()))
            horizontal = true;
        else if(!strcmp("vertical-row", layout_name.c_str()))
            horizontal = false;
        else
        {
            Log::error("LayoutManager::doCalculateLayout", "Unknown layout name: %s", layout_name.c_str());
            break;
        }

        int x = parent->m_x;
        int y = parent->m_y;
        int w = parent->m_w;
        int h = parent->m_h;

        if (parent != NULL && parent->getType() == WTYPE_DIV && parent->m_show_bounding_box)
        {
            int padding = GUIEngine::getFontHeight() / 2;
            if (parent->m_properties[PROP_DIV_PADDING].length() > 0)
                padding = atoi(parent->m_properties[PROP_DIV_PADDING].c_str());

            x += padding;
            y += padding;
            w -= padding*2;
            h -= padding*2;
        }

        // find space left after placing all absolutely-sized widgets in a row
        // (the space left will be divided between remaining widgets later)
        int left_space = (horizontal ? w : h);
        unsigned short total_proportion = 0;
        for(int n=0; n<widgets_amount; n++)
        {
            // relatively-sized widget
            std::string prop = widgets[n].m_properties[ PROP_PROPORTION ];
            if(prop.size() != 0)
            {
                total_proportion += atoi_p( prop.c_str() );
                continue;
            }

            // absolutely-sized widgets
            left_space -= (horizontal ? widgets[n].m_w : widgets[n].m_h);
        } // next widget

        if (left_space < 0)
        {
            Log::warn("LayoutManager", "Statically sized widgets took all the place!!");
            left_space = 0;
        }

        // ---- lay widgets in row
        for (int n=0; n<widgets_amount; n++)
        {
            std::string prop = widgets[n].m_properties[ PROP_PROPORTION ];
            if(prop.size() != 0)
            {
                // proportional size
                int proportion = 1;
                std::istringstream myStream(prop);
                if (!(myStream >> proportion))
                    Log::warn("LayoutManager::doCalculateLayout",
                        "Proportion '%s' is not a number for widget %s", prop.c_str(),
                        widgets[n].m_properties[PROP_ID].c_str());

                const float fraction = (float)proportion/(float)total_proportion;

                if (horizontal)
                {
                    widgets[n].m_x = x;

                    std::string align = widgets[n].m_properties[ PROP_ALIGN ];
                    if (align.size() < 1)
                    {
                        std::string prop_y = widgets[n].m_properties[ PROP_Y ];
                        if (prop_y.size() > 0)
                        {
                            if (prop_y[ prop_y.size()-1 ] == '%')
                            {
                                prop_y = prop_y.substr(0, prop_y.size() - 1);
                                widgets[n].m_y = (int)(y + atoi_p(prop_y.c_str())/100.0f * h);
                            }
                            else if(prop_y[ prop_y.size()-1 ] == 'f')
                            {
                                widgets[n].m_y = y + atoi_p(prop_y.c_str()) * GUIEngine::getFontHeight();
                            }
                            else
                            {
                                widgets[n].m_y = y + atoi_p(prop_y.c_str());
                            }
                        }
                        else
                        {
                            widgets[n].m_y = y;
                        }
                    }
                    else if (align == "top")
                    {
                        widgets[n].m_y = y;
                    }
                    else if (align == "center")
                    {
                        widgets[n].m_y = y + h/2 - widgets[n].m_h/2;
                    }
                    else if (align == "bottom")
                    {
                        widgets[n].m_y = y + h - widgets[n].m_h;
                    }
                    else
                    {
                        Log::warn("LayoutManager::doCalculateLayout",
                            "Alignment '%s' is unknown (widget '%s', in a horiozntal-row layout)",
                            align.c_str(), widgets[n].m_properties[PROP_ID].c_str());
                    }

                    widgets[n].m_w = (int)(left_space*fraction);
                    if (widgets[n].m_properties[PROP_MAX_WIDTH].size() > 0)
                    {
                        const int max_width = atoi_p( widgets[n].m_properties[PROP_MAX_WIDTH].c_str() );
                        if (widgets[n].m_w > max_width) widgets[n].m_w = max_width;
                    }

                    if (widgets[n].m_w <= 0)
                    {
                        Log::warn("LayoutManager", "Widget '%s' has a width of %i (left_space = %i, "
                                  "fraction = %f, max_width = %s)", widgets[n].m_properties[PROP_ID].c_str(),
                                  widgets[n].m_w, left_space, fraction, widgets[n].m_properties[PROP_MAX_WIDTH].c_str());
                        widgets[n].m_w = 1;
                    }

                    x += widgets[n].m_w;
                }
                else
                {
                    widgets[n].m_h = (int)(left_space*fraction);

                    if (widgets[n].m_properties[PROP_MAX_HEIGHT].size() > 0)
                    {
                        const int max_height = atoi_p( widgets[n].m_properties[PROP_MAX_HEIGHT].c_str() );
                        if (widgets[n].m_h > max_height) widgets[n].m_h = max_height;
                    }

                    if (widgets[n].m_h <= 0)
                    {
                        Log::warn("LayoutManager", "Widget '%s' has a height of %i (left_space = %i, "
                                  "fraction = %f, max_width = %s)\n", widgets[n].m_properties[PROP_ID].c_str(),
                                  widgets[n].m_h, left_space, fraction, widgets[n].m_properties[PROP_MAX_WIDTH].c_str());
                        widgets[n].m_h = 1;
                    }

                    std::string align = widgets[n].m_properties[ PROP_ALIGN ];
                    if (align.size() < 1)
                    {
                        std::string prop_x = widgets[n].m_properties[ PROP_X ];
                        if (prop_x.size() > 0)
                        {
                            if (prop_x[ prop_x.size()-1 ] == '%')
                            {
                                prop_x = prop_x.substr(0, prop_x.size() - 1);
                                widgets[n].m_x = (int)(x + atoi_p(prop_x.c_str())/100.0f * w);
                            }
                            else if(prop_x[ prop_x.size()-1 ] == 'f')
                            {
                                widgets[n].m_x = x + atoi_p(prop_x.c_str()) * GUIEngine::getFontHeight();
                            }
                            else
                            {
                                widgets[n].m_x = x + atoi_p(prop_x.c_str());
                            }
                        }
                        else
                        {
                            widgets[n].m_x = x;
                        }
                    }
                    else if (align == "left")
                    {
                        widgets[n].m_x = x;
                    }
                    else if (align == "center")
                    {
                        widgets[n].m_x = x + w/2 - widgets[n].m_w/2;
                    }
                    else if (align == "right")
                    {
                        widgets[n].m_x = x + w - widgets[n].m_w;
                    }
                    else
                    {
                        Log::warn("LayoutManager::doCalculateLayout",
                            "Alignment '%s' is unknown (widget '%s', in a vertical-row layout)",
                            align.c_str(), widgets[n].m_properties[PROP_ID].c_str());
                    }
                    widgets[n].m_y = y;

                    y += widgets[n].m_h;
                }
            }
            else
            {
                // absolute size

                if (horizontal)
                {
                    widgets[n].m_x = x;

                    std::string align = widgets[n].m_properties[ PROP_ALIGN ];

                    if (align.size() < 1)
                    {
                        std::string prop_y = widgets[n].m_properties[ PROP_Y ];

                        if (prop_y.size() > 0)
                        {
                            if (prop_y[ prop_y.size()-1 ] == '%')
                            {
                                prop_y = prop_y.substr(0, prop_y.size() - 1);
                                widgets[n].m_y = (int)(y + atoi_p(prop_y.c_str())/100.0f * h);
                            }
                            else if(prop_y[ prop_y.size()-1 ] == 'f')
                            {
                                widgets[n].m_y = y + atoi_p(prop_y.c_str()) * GUIEngine::getFontHeight();
                            }
                            else
                            {
                                widgets[n].m_y = y + atoi_p(prop_y.c_str());
                            }
                        }
                        else
                        {
                            widgets[n].m_y = y;
                        }
                    }
                    else if (align == "top")
                    {
                        widgets[n].m_y = y;
                    }
                    else if (align == "center")
                    {
                        widgets[n].m_y = y + h/2 - widgets[n].m_h/2;
                    }
                    else if (align == "bottom")
                    {
                        widgets[n].m_y = y + h - widgets[n].m_h;
                    }
                    else
                    {
                        Log::warn("LayoutManager::doCalculateLayout",
                            "Alignment '%s' is unknown in widget '%s'",
                            align.c_str(), widgets[n].m_properties[PROP_ID].c_str());
                    }

                    x += widgets[n].m_w;
                }
                else
                {
                    std::string align = widgets[n].m_properties[ PROP_ALIGN ];

                    if (align.size() < 1)
                    {
                        std::string prop_x = widgets[n].m_properties[ PROP_X ];

                        if (prop_x.size() > 0)
                        {
                            if (prop_x[ prop_x.size()-1 ] == '%')
                            {
                                prop_x = prop_x.substr(0, prop_x.size() - 1);
                                widgets[n].m_x = (int)(x + atoi_p(prop_x.c_str())/100.0f * w);
                            }
                            else if(prop_x[ prop_x.size()-1 ] == 'f')
                            {
                                widgets[n].m_x = x + atoi_p(prop_x.c_str()) * GUIEngine::getFontHeight();
                            }
                            else
                            {
                                widgets[n].m_x = x + atoi_p(prop_x.c_str());
                            }
                        }
                        else
                        {
                            widgets[n].m_x = x;
                        }
                    }
                    else if (align == "left")
                    {
                        widgets[n].m_x = x;
                    }
                    else if (align == "center")
                    {
                        widgets[n].m_x = x + w/2 - widgets[n].m_w/2;
                    }
                    else if (align == "right")
                    {
                        widgets[n].m_x = x + w - widgets[n].m_w;
                    }
                    else
                    {
                        Log::warn("LayoutManager::doCalculateLayout",
                            "Alignment '%s' is unknown in widget '%s'",
                            align.c_str(), widgets[n].m_properties[PROP_ID].c_str());
                    }
                    widgets[n].m_y = y;

                    y += widgets[n].m_h;
                }
            } // end if property or absolute size

        } // next widget

    } while(false);

    // ----- also deal with containers' children
    for (int n=0; n<widgets_amount; n++)
    {
        if (widgets[n].m_type == WTYPE_DIV)
        {
            doCalculateLayout(widgets[n].m_children, topLevelContainer, &widgets[n]);
        }
    }
}   // calculateLayout