static bool isIntegerList(const QString &s)
{
    // Check for empty string or comma-separated list of integers
    static const QRegExp re(QLatin1String("[0-9]+(,[0-9]+)+"));
    Q_ASSERT(re.isValid());
    return s.isEmpty() || re.exactMatch(s);
}

/****************************************************************************
**
**
** This file is part of the Qt Designer of the Qt Toolkit.
**
**
****************************************************************************/

#include "layout_propertysheet.h"

// sdk
#include <QtDesigner/QExtensionManager>
#include <QtDesigner/QDesignerFormEditorInterface>

// shared
#include <ui4_p.h>
#include <qlayout_widget_p.h>
#include <formbuilderextra_p.h>

#include <QtGui/QFormLayout>

#include <QtCore/QHash>
#include <QtCore/QDebug>
#include <QtCore/QTextStream>
#include <QtCore/QByteArray>
#include <QtCore/QRegExp> // Remove once there is an editor for lists

QT_BEGIN_NAMESPACE

#define USE_LAYOUT_SIZE_CONSTRAINT

static const char *leftMargin = "leftMargin";
static const char *topMargin = "topMargin";
static const char *rightMargin = "rightMargin";
static const char *bottomMargin = "bottomMargin";
static const char *horizontalSpacing = "horizontalSpacing";
static const char *verticalSpacing = "verticalSpacing";
static const char *spacing = "spacing";
static const char *margin = "margin";
static const char *sizeConstraint = "sizeConstraint";
static const char *boxStretchPropertyC = "stretch";
static const char *gridRowStretchPropertyC = "rowStretch";
static const char *gridColumnStretchPropertyC = "columnStretch";
static const char *gridRowMinimumHeightPropertyC = "rowMinimumHeight";
static const char *gridColumnMinimumWidthPropertyC = "columnMinimumWidth";

namespace {
    enum LayoutPropertyType {
        LayoutPropertyNone,
        LayoutPropertyMargin, // Deprecated
        LayoutPropertyLeftMargin,
        LayoutPropertyTopMargin,
        LayoutPropertyRightMargin,
        LayoutPropertyBottomMargin,
        LayoutPropertySpacing,
        LayoutPropertyHorizontalSpacing,
        LayoutPropertyVerticalSpacing,
        LayoutPropertySizeConstraint,
        LayoutPropertyBoxStretch,
        LayoutPropertyGridRowStretch,
        LayoutPropertyGridColumnStretch,
        LayoutPropertyGridRowMinimumHeight,
        LayoutPropertyGridColumnMinimumWidth
    };
}

// Check for a  comma-separated list of integers. Used for
// per-cell stretch properties and grid per row/column properties.
// As it works now, they are passed as QByteArray strings. The
// property sheet refuses all invalid values. This could be
// replaced by lists once the property editor can handle them.

// Quick lookup by name
static LayoutPropertyType  layoutPropertyType(const QString &name)
{
    static QHash<QString, LayoutPropertyType> namePropertyMap;
    if (namePropertyMap.empty()) {
        namePropertyMap.insert(QLatin1String(leftMargin), LayoutPropertyLeftMargin);
        namePropertyMap.insert(QLatin1String(topMargin), LayoutPropertyTopMargin);
        namePropertyMap.insert(QLatin1String(rightMargin), LayoutPropertyRightMargin);
        namePropertyMap.insert(QLatin1String(bottomMargin), LayoutPropertyBottomMargin);
        namePropertyMap.insert(QLatin1String(horizontalSpacing), LayoutPropertyHorizontalSpacing);
        namePropertyMap.insert(QLatin1String(verticalSpacing), LayoutPropertyVerticalSpacing);
        namePropertyMap.insert(QLatin1String(spacing), LayoutPropertySpacing);
        namePropertyMap.insert(QLatin1String(margin), LayoutPropertyMargin);
        namePropertyMap.insert(QLatin1String(sizeConstraint), LayoutPropertySizeConstraint);
        namePropertyMap.insert(QLatin1String(boxStretchPropertyC ), LayoutPropertyBoxStretch);
        namePropertyMap.insert(QLatin1String(gridRowStretchPropertyC), LayoutPropertyGridRowStretch);
        namePropertyMap.insert(QLatin1String(gridColumnStretchPropertyC), LayoutPropertyGridColumnStretch);
        namePropertyMap.insert(QLatin1String(gridRowMinimumHeightPropertyC), LayoutPropertyGridRowMinimumHeight);
        namePropertyMap.insert(QLatin1String(gridColumnMinimumWidthPropertyC), LayoutPropertyGridColumnMinimumWidth);
    }
    return namePropertyMap.value(name, LayoutPropertyNone);
}

// return the layout margin if it is  margin
static int getLayoutMargin(const QLayout *l, LayoutPropertyType type)
{
    int left, top, right, bottom;
    l->getContentsMargins(&left, &top, &right, &bottom);
    switch (type) {
    case LayoutPropertyLeftMargin:
        return left;
    case LayoutPropertyTopMargin:
        return top;
    case LayoutPropertyRightMargin:
        return right;
    case LayoutPropertyBottomMargin:
        return bottom;
    default:
        Q_ASSERT(0);
        break;
    }
    return 0;
}

// return the layout margin if it is  margin
static void setLayoutMargin(QLayout *l, LayoutPropertyType type, int margin)
{
    int left, top, right, bottom;
    l->getContentsMargins(&left, &top, &right, &bottom);
    switch (type) {
    case LayoutPropertyLeftMargin:
        left = margin;
        break;
    case LayoutPropertyTopMargin:
        top = margin;
        break;
    case LayoutPropertyRightMargin:
        right = margin;
        break;
    case LayoutPropertyBottomMargin:
        bottom = margin;
        break;
    default:
        Q_ASSERT(0);
        break;
    }
    l->setContentsMargins(left, top, right, bottom);
}

namespace qdesigner_internal {

// ---------- LayoutPropertySheet: This sheet is never visible in
// the property editor. Rather, the sheet pulled for QLayoutWidget
// forwards all properties to it. Some properties (grid spacings) must be handled
// manually, as they are QDOC_PROPERTY only and not visible to introspection. Ditto

LayoutPropertySheet::LayoutPropertySheet(QLayout *l, QObject *parent)
    : QDesignerPropertySheet(l, parent), m_layout(l)
{
    const QString layoutGroup = QLatin1String("Layout");
    int pindex = createFakeProperty(QLatin1String(leftMargin), 0);
    setPropertyGroup(pindex, layoutGroup);

    pindex = createFakeProperty(QLatin1String(topMargin), 0);
    setPropertyGroup(pindex, layoutGroup);

    pindex = createFakeProperty(QLatin1String(rightMargin), 0);
    setPropertyGroup(pindex, layoutGroup);

    pindex = createFakeProperty(QLatin1String(bottomMargin), 0);
    setPropertyGroup(pindex, layoutGroup);

    const int visibleMask = LayoutProperties::visibleProperties(m_layout);
    if (visibleMask & LayoutProperties::HorizSpacingProperty) {
        pindex = createFakeProperty(QLatin1String(horizontalSpacing), 0);
        setPropertyGroup(pindex, layoutGroup);

        pindex = createFakeProperty(QLatin1String(verticalSpacing), 0);
        setPropertyGroup(pindex, layoutGroup);

        setAttribute(indexOf(QLatin1String(spacing)), true);
    }

    setAttribute(indexOf(QLatin1String(margin)), true);
    // Stretch
    if (visibleMask & LayoutProperties::BoxStretchProperty) {
        pindex = createFakeProperty(QLatin1String(boxStretchPropertyC), QByteArray());
        setPropertyGroup(pindex, layoutGroup);
        setAttribute(pindex, true);
    } else {
        // Add the grid per-row/column stretch and size limits
        if (visibleMask & LayoutProperties::GridColumnStretchProperty) {
            const QByteArray empty;
            pindex = createFakeProperty(QLatin1String(gridRowStretchPropertyC), empty);
            setPropertyGroup(pindex, layoutGroup);
            setAttribute(pindex, true);
            pindex = createFakeProperty(QLatin1String(gridColumnStretchPropertyC), empty);
            setPropertyGroup(pindex, layoutGroup);
            setAttribute(pindex, true);
            pindex = createFakeProperty(QLatin1String(gridRowMinimumHeightPropertyC), empty);
            setPropertyGroup(pindex, layoutGroup);
            setAttribute(pindex, true);
            pindex = createFakeProperty(QLatin1String(gridColumnMinimumWidthPropertyC), empty);
            setPropertyGroup(pindex, layoutGroup);
            setAttribute(pindex, true);
        }
    }
#ifdef USE_LAYOUT_SIZE_CONSTRAINT
    // SizeConstraint cannot possibly be handled as a real property
    // as it affects the layout parent widget and thus
    // conflicts with Designer's special layout widget.
    // It will take effect on the preview only.
    pindex = createFakeProperty(QLatin1String(sizeConstraint));
    setPropertyGroup(pindex, layoutGroup);
#endif
}

LayoutPropertySheet::~LayoutPropertySheet()
{
}

void LayoutPropertySheet::setProperty(int index, const QVariant &value)
{
    const LayoutPropertyType type = layoutPropertyType(propertyName(index));
    if (QLayoutWidget *lw = qobject_cast<QLayoutWidget *>(m_layout->parent())) {
        switch (type) {
        case LayoutPropertyLeftMargin:
            lw->setLayoutLeftMargin(value.toInt());
            return;
        case LayoutPropertyTopMargin:
            lw->setLayoutTopMargin(value.toInt());
            return;
        case LayoutPropertyRightMargin:
            lw->setLayoutRightMargin(value.toInt());
            return;
        case LayoutPropertyBottomMargin:
            lw->setLayoutBottomMargin(value.toInt());
            return;
        case LayoutPropertyMargin: {
            const int v = value.toInt();
            lw->setLayoutLeftMargin(v);
            lw->setLayoutTopMargin(v);
            lw->setLayoutRightMargin(v);
            lw->setLayoutBottomMargin(v);
        }
            return;
        default:
            break;
        }
    }
    switch (type) {
    case LayoutPropertyLeftMargin:
    case LayoutPropertyTopMargin:
    case LayoutPropertyRightMargin:
    case LayoutPropertyBottomMargin:
        setLayoutMargin(m_layout, type, value.toInt());
        return;
    case LayoutPropertyHorizontalSpacing:
        if (QGridLayout *grid = qobject_cast<QGridLayout *>(m_layout)) {
            grid->setHorizontalSpacing(value.toInt());
            return;
        }
        if (QFormLayout *form = qobject_cast<QFormLayout *>(m_layout)) {
            form->setHorizontalSpacing(value.toInt());
            return;
        }
        break;
    case LayoutPropertyVerticalSpacing:
        if (QGridLayout *grid = qobject_cast<QGridLayout *>(m_layout)) {
            grid->setVerticalSpacing(value.toInt());
            return;
        }
        if (QFormLayout *form = qobject_cast<QFormLayout *>(m_layout)) {
            form->setVerticalSpacing(value.toInt());
            return;
        }
        break;
    case LayoutPropertyBoxStretch:
        // TODO: Remove the regexp check once a proper editor for integer
        // lists is in place?
        if (QBoxLayout *box = qobject_cast<QBoxLayout *>(m_layout)) {
            const QString stretch = value.toString();
            if (isIntegerList(stretch))
                QFormBuilderExtra::setBoxLayoutStretch(value.toString(), box);
        }
        break;
    case LayoutPropertyGridRowStretch:
        if (QGridLayout *grid = qobject_cast<QGridLayout *>(m_layout)) {
            const QString stretch = value.toString();
            if (isIntegerList(stretch))
                QFormBuilderExtra::setGridLayoutRowStretch(stretch, grid);
        }
        break;
    case LayoutPropertyGridColumnStretch:
        if (QGridLayout *grid = qobject_cast<QGridLayout *>(m_layout)) {
            const QString stretch = value.toString();
            if (isIntegerList(stretch))
                QFormBuilderExtra::setGridLayoutColumnStretch(value.toString(), grid);
        }
        break;
    case LayoutPropertyGridRowMinimumHeight:
        if (QGridLayout *grid = qobject_cast<QGridLayout *>(m_layout)) {
            const QString minSize = value.toString();
            if (isIntegerList(minSize))
                QFormBuilderExtra::setGridLayoutRowMinimumHeight(minSize, grid);
        }
        break;
    case LayoutPropertyGridColumnMinimumWidth:
        if (QGridLayout *grid = qobject_cast<QGridLayout *>(m_layout)) {
            const QString minSize = value.toString();
            if (isIntegerList(minSize))
                QFormBuilderExtra::setGridLayoutColumnMinimumWidth(minSize, grid);
        }
        break;
    default:
        break;
    }
    QDesignerPropertySheet::setProperty(index, value);
}

QVariant LayoutPropertySheet::property(int index) const
{
    const LayoutPropertyType type = layoutPropertyType(propertyName(index));
    if (const QLayoutWidget *lw = qobject_cast<QLayoutWidget *>(m_layout->parent())) {
        switch (type) {
        case LayoutPropertyLeftMargin:
            return lw->layoutLeftMargin();
        case LayoutPropertyTopMargin:
            return lw->layoutTopMargin();
        case LayoutPropertyRightMargin:
            return lw->layoutRightMargin();
        case LayoutPropertyBottomMargin:
             return lw->layoutBottomMargin();
        default:
            break;
        }
    }
    switch (type) {
    case LayoutPropertyLeftMargin:
    case LayoutPropertyTopMargin:
    case LayoutPropertyRightMargin:
    case LayoutPropertyBottomMargin:
        return getLayoutMargin(m_layout, type);
    case LayoutPropertyHorizontalSpacing:
        if (const QGridLayout *grid = qobject_cast<QGridLayout *>(m_layout))
            return grid->horizontalSpacing();
        if (const QFormLayout *form = qobject_cast<QFormLayout *>(m_layout))
            return form->horizontalSpacing();
        break;
    case LayoutPropertyVerticalSpacing:
        if (const QGridLayout *grid = qobject_cast<QGridLayout *>(m_layout))
            return grid->verticalSpacing();
        if (const QFormLayout *form = qobject_cast<QFormLayout *>(m_layout))
            return form->verticalSpacing();
    case LayoutPropertyBoxStretch:
        if (const QBoxLayout *box = qobject_cast<QBoxLayout *>(m_layout))
            return QVariant(QByteArray(QFormBuilderExtra::boxLayoutStretch(box).toUtf8()));
        break;
    case LayoutPropertyGridRowStretch:
        if (const QGridLayout *grid = qobject_cast<QGridLayout *>(m_layout))
            return QVariant(QByteArray(QFormBuilderExtra::gridLayoutRowStretch(grid).toUtf8()));
        break;
    case LayoutPropertyGridColumnStretch:
        if (const QGridLayout *grid = qobject_cast<QGridLayout *>(m_layout))
            return QVariant(QByteArray(QFormBuilderExtra::gridLayoutColumnStretch(grid).toUtf8()));
        break;
    case LayoutPropertyGridRowMinimumHeight:
        if (const QGridLayout *grid = qobject_cast<QGridLayout *>(m_layout))
            return QVariant(QByteArray(QFormBuilderExtra::gridLayoutRowMinimumHeight(grid).toUtf8()));
        break;
    case LayoutPropertyGridColumnMinimumWidth:
        if (const QGridLayout *grid = qobject_cast<QGridLayout *>(m_layout))
            return QVariant(QByteArray(QFormBuilderExtra::gridLayoutColumnMinimumWidth(grid).toUtf8()));
        break;
    default:
        break;
    }
    return QDesignerPropertySheet::property(index);
}

bool LayoutPropertySheet::reset(int index)
{
    int left, top, right, bottom;
    m_layout->getContentsMargins(&left, &top, &right, &bottom);
    const LayoutPropertyType type = layoutPropertyType(propertyName(index));
    bool rc = true;
    switch (type) {
    case LayoutPropertyLeftMargin:
        m_layout->setContentsMargins(-1, top, right, bottom);
        break;
    case LayoutPropertyTopMargin:
        m_layout->setContentsMargins(left, -1, right, bottom);
        break;
    case LayoutPropertyRightMargin:
        m_layout->setContentsMargins(left, top, -1, bottom);
        break;
    case LayoutPropertyBottomMargin:
        m_layout->setContentsMargins(left, top, right, -1);
        break;
    case LayoutPropertyBoxStretch:
        if (QBoxLayout *box = qobject_cast<QBoxLayout *>(m_layout))
            QFormBuilderExtra::clearBoxLayoutStretch(box);
        break;
    case LayoutPropertyGridRowStretch:
        if (QGridLayout *grid = qobject_cast<QGridLayout *>(m_layout))
            QFormBuilderExtra::clearGridLayoutRowStretch(grid);
        break;
    case LayoutPropertyGridColumnStretch:
        if (QGridLayout *grid = qobject_cast<QGridLayout *>(m_layout))
            QFormBuilderExtra::clearGridLayoutColumnStretch(grid);
        break;
    case LayoutPropertyGridRowMinimumHeight:
        if (QGridLayout *grid = qobject_cast<QGridLayout *>(m_layout))
            QFormBuilderExtra::clearGridLayoutRowMinimumHeight(grid);
        break;
    case LayoutPropertyGridColumnMinimumWidth:
        if (QGridLayout *grid = qobject_cast<QGridLayout *>(m_layout))
            QFormBuilderExtra::clearGridLayoutColumnMinimumWidth(grid);
        break;
    default:
        rc = QDesignerPropertySheet::reset(index);
        break;
    }
    return rc;
}

void LayoutPropertySheet::setChanged(int index, bool changed)
{
    const LayoutPropertyType type = layoutPropertyType(propertyName(index));
    switch (type) {
    case LayoutPropertySpacing:
        if (LayoutProperties::visibleProperties(m_layout) & LayoutProperties::HorizSpacingProperty) {
            setChanged(indexOf(QLatin1String(horizontalSpacing)), changed);
            setChanged(indexOf(QLatin1String(verticalSpacing)), changed);
        }
        break;
    case LayoutPropertyMargin:
        setChanged(indexOf(QLatin1String(leftMargin)), changed);
        setChanged(indexOf(QLatin1String(topMargin)), changed);
        setChanged(indexOf(QLatin1String(rightMargin)), changed);
        setChanged(indexOf(QLatin1String(bottomMargin)), changed);
        break;
    default:
        break;
    }
    QDesignerPropertySheet::setChanged(index, changed);
}

void LayoutPropertySheet::stretchAttributesToDom(QDesignerFormEditorInterface *core, QLayout *lt, DomLayout *domLayout)
{
    // Check if the respective stretch properties of the layout are changed.
    // If so, set them to the DOM
    const int visibleMask = LayoutProperties::visibleProperties(lt);
    if (!(visibleMask & (LayoutProperties::BoxStretchProperty|LayoutProperties::GridColumnStretchProperty|LayoutProperties::GridRowStretchProperty)))
        return;
    const QDesignerPropertySheetExtension *sheet = qt_extension<QDesignerPropertySheetExtension*>(core->extensionManager(), lt);
    Q_ASSERT(sheet);

    // Stretch
    if (visibleMask & LayoutProperties::BoxStretchProperty) {
        const int index = sheet->indexOf(QLatin1String(boxStretchPropertyC));
        Q_ASSERT(index != -1);
        if (sheet->isChanged(index))
            domLayout->setAttributeStretch(sheet->property(index).toString());
    }
    if (visibleMask & LayoutProperties::GridColumnStretchProperty) {
        const int index = sheet->indexOf(QLatin1String(gridColumnStretchPropertyC));
        Q_ASSERT(index != -1);
        if (sheet->isChanged(index))
            domLayout->setAttributeColumnStretch(sheet->property(index).toString());
    }
    if (visibleMask & LayoutProperties::GridRowStretchProperty) {
        const int index = sheet->indexOf(QLatin1String(gridRowStretchPropertyC));
        Q_ASSERT(index != -1);
        if (sheet->isChanged(index))
            domLayout->setAttributeRowStretch(sheet->property(index).toString());
    }
    if (visibleMask & LayoutProperties::GridRowMinimumHeightProperty) {
        const int index = sheet->indexOf(QLatin1String(gridRowMinimumHeightPropertyC));
        Q_ASSERT(index != -1);
        if (sheet->isChanged(index))
            domLayout->setAttributeRowMinimumHeight(sheet->property(index).toString());
    }
    if (visibleMask & LayoutProperties::GridColumnMinimumWidthProperty) {
        const int index = sheet->indexOf(QLatin1String(gridColumnMinimumWidthPropertyC));
        Q_ASSERT(index != -1);
        if (sheet->isChanged(index))
            domLayout->setAttributeColumnMinimumWidth(sheet->property(index).toString());
    }
}

void LayoutPropertySheet::markChangedStretchProperties(QDesignerFormEditorInterface *core, QLayout *lt, const DomLayout *domLayout)
{
    // While the actual values are applied by the form builder, we still need
    // to mark them as 'changed'.
    QDesignerPropertySheetExtension *sheet = qt_extension<QDesignerPropertySheetExtension*>(core->extensionManager(), lt);
    Q_ASSERT(sheet);
    if (!domLayout->attributeStretch().isEmpty())
        sheet->setChanged(sheet->indexOf(QLatin1String(boxStretchPropertyC)), true);
    if (!domLayout->attributeRowStretch().isEmpty())
        sheet->setChanged(sheet->indexOf(QLatin1String(gridRowStretchPropertyC)), true);
    if (!domLayout->attributeColumnStretch().isEmpty())
        sheet->setChanged(sheet->indexOf(QLatin1String(gridColumnStretchPropertyC)), true);
   if (!domLayout->attributeColumnMinimumWidth().isEmpty())
        sheet->setChanged(sheet->indexOf(QLatin1String(gridColumnMinimumWidthPropertyC)), true);
   if (!domLayout->attributeRowMinimumHeight().isEmpty())
        sheet->setChanged(sheet->indexOf(QLatin1String(gridRowMinimumHeightPropertyC)), true);
}

}

QT_END_NAMESPACE


switch (type) {
    case LayoutPropertyLeftMargin:
        return left;
    case LayoutPropertyTopMargin:
        return top;
    case LayoutPropertyRightMargin:
        return right;
    case LayoutPropertyBottomMargin:
        return bottom;
    default:
        Q_ASSERT(0);
        break;
    }

/****************************************************************************
**
**
** This file is part of the Qt Designer of the Qt Toolkit.
**
**
****************************************************************************/

#include "layout_propertysheet.h"

// sdk
#include <QtDesigner/QExtensionManager>
#include <QtDesigner/QDesignerFormEditorInterface>

// shared
#include <ui4_p.h>
#include <qlayout_widget_p.h>
#include <formbuilderextra_p.h>

#include <QtGui/QFormLayout>

#include <QtCore/QHash>
#include <QtCore/QDebug>
#include <QtCore/QTextStream>
#include <QtCore/QByteArray>
#include <QtCore/QRegExp> // Remove once there is an editor for lists

QT_BEGIN_NAMESPACE

#define USE_LAYOUT_SIZE_CONSTRAINT

static const char *leftMargin = "leftMargin";
static const char *topMargin = "topMargin";
static const char *rightMargin = "rightMargin";
static const char *bottomMargin = "bottomMargin";
static const char *horizontalSpacing = "horizontalSpacing";
static const char *verticalSpacing = "verticalSpacing";
static const char *spacing = "spacing";
static const char *margin = "margin";
static const char *sizeConstraint = "sizeConstraint";
static const char *boxStretchPropertyC = "stretch";
static const char *gridRowStretchPropertyC = "rowStretch";
static const char *gridColumnStretchPropertyC = "columnStretch";
static const char *gridRowMinimumHeightPropertyC = "rowMinimumHeight";
static const char *gridColumnMinimumWidthPropertyC = "columnMinimumWidth";

namespace {
    enum LayoutPropertyType {
        LayoutPropertyNone,
        LayoutPropertyMargin, // Deprecated
        LayoutPropertyLeftMargin,
        LayoutPropertyTopMargin,
        LayoutPropertyRightMargin,
        LayoutPropertyBottomMargin,
        LayoutPropertySpacing,
        LayoutPropertyHorizontalSpacing,
        LayoutPropertyVerticalSpacing,
        LayoutPropertySizeConstraint,
        LayoutPropertyBoxStretch,
        LayoutPropertyGridRowStretch,
        LayoutPropertyGridColumnStretch,
        LayoutPropertyGridRowMinimumHeight,
        LayoutPropertyGridColumnMinimumWidth
    };
}

// Check for a  comma-separated list of integers. Used for
// per-cell stretch properties and grid per row/column properties.
// As it works now, they are passed as QByteArray strings. The
// property sheet refuses all invalid values. This could be
// replaced by lists once the property editor can handle them.

static bool isIntegerList(const QString &s)
{
    // Check for empty string or comma-separated list of integers
    static const QRegExp re(QLatin1String("[0-9]+(,[0-9]+)+"));
    Q_ASSERT(re.isValid());
    return s.isEmpty() || re.exactMatch(s);
}

// Quick lookup by name
static LayoutPropertyType  layoutPropertyType(const QString &name)
{
    static QHash<QString, LayoutPropertyType> namePropertyMap;
    if (namePropertyMap.empty()) {
        namePropertyMap.insert(QLatin1String(leftMargin), LayoutPropertyLeftMargin);
        namePropertyMap.insert(QLatin1String(topMargin), LayoutPropertyTopMargin);
        namePropertyMap.insert(QLatin1String(rightMargin), LayoutPropertyRightMargin);
        namePropertyMap.insert(QLatin1String(bottomMargin), LayoutPropertyBottomMargin);
        namePropertyMap.insert(QLatin1String(horizontalSpacing), LayoutPropertyHorizontalSpacing);
        namePropertyMap.insert(QLatin1String(verticalSpacing), LayoutPropertyVerticalSpacing);
        namePropertyMap.insert(QLatin1String(spacing), LayoutPropertySpacing);
        namePropertyMap.insert(QLatin1String(margin), LayoutPropertyMargin);
        namePropertyMap.insert(QLatin1String(sizeConstraint), LayoutPropertySizeConstraint);
        namePropertyMap.insert(QLatin1String(boxStretchPropertyC ), LayoutPropertyBoxStretch);
        namePropertyMap.insert(QLatin1String(gridRowStretchPropertyC), LayoutPropertyGridRowStretch);
        namePropertyMap.insert(QLatin1String(gridColumnStretchPropertyC), LayoutPropertyGridColumnStretch);
        namePropertyMap.insert(QLatin1String(gridRowMinimumHeightPropertyC), LayoutPropertyGridRowMinimumHeight);
        namePropertyMap.insert(QLatin1String(gridColumnMinimumWidthPropertyC), LayoutPropertyGridColumnMinimumWidth);
    }
    return namePropertyMap.value(name, LayoutPropertyNone);
}

// return the layout margin if it is  margin
static int getLayoutMargin(const QLayout *l, LayoutPropertyType type)
{
    int left, top, right, bottom;
    l->getContentsMargins(&left, &top, &right, &bottom);

    return 0;
}

// return the layout margin if it is  margin
static void setLayoutMargin(QLayout *l, LayoutPropertyType type, int margin)
{
    int left, top, right, bottom;
    l->getContentsMargins(&left, &top, &right, &bottom);
    switch (type) {
    case LayoutPropertyLeftMargin:
        left = margin;
        break;
    case LayoutPropertyTopMargin:
        top = margin;
        break;
    case LayoutPropertyRightMargin:
        right = margin;
        break;
    case LayoutPropertyBottomMargin:
        bottom = margin;
        break;
    default:
        Q_ASSERT(0);
        break;
    }
    l->setContentsMargins(left, top, right, bottom);
}

namespace qdesigner_internal {

// ---------- LayoutPropertySheet: This sheet is never visible in
// the property editor. Rather, the sheet pulled for QLayoutWidget
// forwards all properties to it. Some properties (grid spacings) must be handled
// manually, as they are QDOC_PROPERTY only and not visible to introspection. Ditto

LayoutPropertySheet::LayoutPropertySheet(QLayout *l, QObject *parent)
    : QDesignerPropertySheet(l, parent), m_layout(l)
{
    const QString layoutGroup = QLatin1String("Layout");
    int pindex = createFakeProperty(QLatin1String(leftMargin), 0);
    setPropertyGroup(pindex, layoutGroup);

    pindex = createFakeProperty(QLatin1String(topMargin), 0);
    setPropertyGroup(pindex, layoutGroup);

    pindex = createFakeProperty(QLatin1String(rightMargin), 0);
    setPropertyGroup(pindex, layoutGroup);

    pindex = createFakeProperty(QLatin1String(bottomMargin), 0);
    setPropertyGroup(pindex, layoutGroup);

    const int visibleMask = LayoutProperties::visibleProperties(m_layout);
    if (visibleMask & LayoutProperties::HorizSpacingProperty) {
        pindex = createFakeProperty(QLatin1String(horizontalSpacing), 0);
        setPropertyGroup(pindex, layoutGroup);

        pindex = createFakeProperty(QLatin1String(verticalSpacing), 0);
        setPropertyGroup(pindex, layoutGroup);

        setAttribute(indexOf(QLatin1String(spacing)), true);
    }

    setAttribute(indexOf(QLatin1String(margin)), true);
    // Stretch
    if (visibleMask & LayoutProperties::BoxStretchProperty) {
        pindex = createFakeProperty(QLatin1String(boxStretchPropertyC), QByteArray());
        setPropertyGroup(pindex, layoutGroup);
        setAttribute(pindex, true);
    } else {
        // Add the grid per-row/column stretch and size limits
        if (visibleMask & LayoutProperties::GridColumnStretchProperty) {
            const QByteArray empty;
            pindex = createFakeProperty(QLatin1String(gridRowStretchPropertyC), empty);
            setPropertyGroup(pindex, layoutGroup);
            setAttribute(pindex, true);
            pindex = createFakeProperty(QLatin1String(gridColumnStretchPropertyC), empty);
            setPropertyGroup(pindex, layoutGroup);
            setAttribute(pindex, true);
            pindex = createFakeProperty(QLatin1String(gridRowMinimumHeightPropertyC), empty);
            setPropertyGroup(pindex, layoutGroup);
            setAttribute(pindex, true);
            pindex = createFakeProperty(QLatin1String(gridColumnMinimumWidthPropertyC), empty);
            setPropertyGroup(pindex, layoutGroup);
            setAttribute(pindex, true);
        }
    }
#ifdef USE_LAYOUT_SIZE_CONSTRAINT
    // SizeConstraint cannot possibly be handled as a real property
    // as it affects the layout parent widget and thus
    // conflicts with Designer's special layout widget.
    // It will take effect on the preview only.
    pindex = createFakeProperty(QLatin1String(sizeConstraint));
    setPropertyGroup(pindex, layoutGroup);
#endif
}

LayoutPropertySheet::~LayoutPropertySheet()
{
}

void LayoutPropertySheet::setProperty(int index, const QVariant &value)
{
    const LayoutPropertyType type = layoutPropertyType(propertyName(index));
    if (QLayoutWidget *lw = qobject_cast<QLayoutWidget *>(m_layout->parent())) {
        switch (type) {
        case LayoutPropertyLeftMargin:
            lw->setLayoutLeftMargin(value.toInt());
            return;
        case LayoutPropertyTopMargin:
            lw->setLayoutTopMargin(value.toInt());
            return;
        case LayoutPropertyRightMargin:
            lw->setLayoutRightMargin(value.toInt());
            return;
        case LayoutPropertyBottomMargin:
            lw->setLayoutBottomMargin(value.toInt());
            return;
        case LayoutPropertyMargin: {
            const int v = value.toInt();
            lw->setLayoutLeftMargin(v);
            lw->setLayoutTopMargin(v);
            lw->setLayoutRightMargin(v);
            lw->setLayoutBottomMargin(v);
        }
            return;
        default:
            break;
        }
    }
    switch (type) {
    case LayoutPropertyLeftMargin:
    case LayoutPropertyTopMargin:
    case LayoutPropertyRightMargin:
    case LayoutPropertyBottomMargin:
        setLayoutMargin(m_layout, type, value.toInt());
        return;
    case LayoutPropertyHorizontalSpacing:
        if (QGridLayout *grid = qobject_cast<QGridLayout *>(m_layout)) {
            grid->setHorizontalSpacing(value.toInt());
            return;
        }
        if (QFormLayout *form = qobject_cast<QFormLayout *>(m_layout)) {
            form->setHorizontalSpacing(value.toInt());
            return;
        }
        break;
    case LayoutPropertyVerticalSpacing:
        if (QGridLayout *grid = qobject_cast<QGridLayout *>(m_layout)) {
            grid->setVerticalSpacing(value.toInt());
            return;
        }
        if (QFormLayout *form = qobject_cast<QFormLayout *>(m_layout)) {
            form->setVerticalSpacing(value.toInt());
            return;
        }
        break;
    case LayoutPropertyBoxStretch:
        // TODO: Remove the regexp check once a proper editor for integer
        // lists is in place?
        if (QBoxLayout *box = qobject_cast<QBoxLayout *>(m_layout)) {
            const QString stretch = value.toString();
            if (isIntegerList(stretch))
                QFormBuilderExtra::setBoxLayoutStretch(value.toString(), box);
        }
        break;
    case LayoutPropertyGridRowStretch:
        if (QGridLayout *grid = qobject_cast<QGridLayout *>(m_layout)) {
            const QString stretch = value.toString();
            if (isIntegerList(stretch))
                QFormBuilderExtra::setGridLayoutRowStretch(stretch, grid);
        }
        break;
    case LayoutPropertyGridColumnStretch:
        if (QGridLayout *grid = qobject_cast<QGridLayout *>(m_layout)) {
            const QString stretch = value.toString();
            if (isIntegerList(stretch))
                QFormBuilderExtra::setGridLayoutColumnStretch(value.toString(), grid);
        }
        break;
    case LayoutPropertyGridRowMinimumHeight:
        if (QGridLayout *grid = qobject_cast<QGridLayout *>(m_layout)) {
            const QString minSize = value.toString();
            if (isIntegerList(minSize))
                QFormBuilderExtra::setGridLayoutRowMinimumHeight(minSize, grid);
        }
        break;
    case LayoutPropertyGridColumnMinimumWidth:
        if (QGridLayout *grid = qobject_cast<QGridLayout *>(m_layout)) {
            const QString minSize = value.toString();
            if (isIntegerList(minSize))
                QFormBuilderExtra::setGridLayoutColumnMinimumWidth(minSize, grid);
        }
        break;
    default:
        break;
    }
    QDesignerPropertySheet::setProperty(index, value);
}

QVariant LayoutPropertySheet::property(int index) const
{
    const LayoutPropertyType type = layoutPropertyType(propertyName(index));
    if (const QLayoutWidget *lw = qobject_cast<QLayoutWidget *>(m_layout->parent())) {
        switch (type) {
        case LayoutPropertyLeftMargin:
            return lw->layoutLeftMargin();
        case LayoutPropertyTopMargin:
            return lw->layoutTopMargin();
        case LayoutPropertyRightMargin:
            return lw->layoutRightMargin();
        case LayoutPropertyBottomMargin:
             return lw->layoutBottomMargin();
        default:
            break;
        }
    }
    switch (type) {
    case LayoutPropertyLeftMargin:
    case LayoutPropertyTopMargin:
    case LayoutPropertyRightMargin:
    case LayoutPropertyBottomMargin:
        return getLayoutMargin(m_layout, type);
    case LayoutPropertyHorizontalSpacing:
        if (const QGridLayout *grid = qobject_cast<QGridLayout *>(m_layout))
            return grid->horizontalSpacing();
        if (const QFormLayout *form = qobject_cast<QFormLayout *>(m_layout))
            return form->horizontalSpacing();
        break;
    case LayoutPropertyVerticalSpacing:
        if (const QGridLayout *grid = qobject_cast<QGridLayout *>(m_layout))
            return grid->verticalSpacing();
        if (const QFormLayout *form = qobject_cast<QFormLayout *>(m_layout))
            return form->verticalSpacing();
    case LayoutPropertyBoxStretch:
        if (const QBoxLayout *box = qobject_cast<QBoxLayout *>(m_layout))
            return QVariant(QByteArray(QFormBuilderExtra::boxLayoutStretch(box).toUtf8()));
        break;
    case LayoutPropertyGridRowStretch:
        if (const QGridLayout *grid = qobject_cast<QGridLayout *>(m_layout))
            return QVariant(QByteArray(QFormBuilderExtra::gridLayoutRowStretch(grid).toUtf8()));
        break;
    case LayoutPropertyGridColumnStretch:
        if (const QGridLayout *grid = qobject_cast<QGridLayout *>(m_layout))
            return QVariant(QByteArray(QFormBuilderExtra::gridLayoutColumnStretch(grid).toUtf8()));
        break;
    case LayoutPropertyGridRowMinimumHeight:
        if (const QGridLayout *grid = qobject_cast<QGridLayout *>(m_layout))
            return QVariant(QByteArray(QFormBuilderExtra::gridLayoutRowMinimumHeight(grid).toUtf8()));
        break;
    case LayoutPropertyGridColumnMinimumWidth:
        if (const QGridLayout *grid = qobject_cast<QGridLayout *>(m_layout))
            return QVariant(QByteArray(QFormBuilderExtra::gridLayoutColumnMinimumWidth(grid).toUtf8()));
        break;
    default:
        break;
    }
    return QDesignerPropertySheet::property(index);
}

bool LayoutPropertySheet::reset(int index)
{
    int left, top, right, bottom;
    m_layout->getContentsMargins(&left, &top, &right, &bottom);
    const LayoutPropertyType type = layoutPropertyType(propertyName(index));
    bool rc = true;
    switch (type) {
    case LayoutPropertyLeftMargin:
        m_layout->setContentsMargins(-1, top, right, bottom);
        break;
    case LayoutPropertyTopMargin:
        m_layout->setContentsMargins(left, -1, right, bottom);
        break;
    case LayoutPropertyRightMargin:
        m_layout->setContentsMargins(left, top, -1, bottom);
        break;
    case LayoutPropertyBottomMargin:
        m_layout->setContentsMargins(left, top, right, -1);
        break;
    case LayoutPropertyBoxStretch:
        if (QBoxLayout *box = qobject_cast<QBoxLayout *>(m_layout))
            QFormBuilderExtra::clearBoxLayoutStretch(box);
        break;
    case LayoutPropertyGridRowStretch:
        if (QGridLayout *grid = qobject_cast<QGridLayout *>(m_layout))
            QFormBuilderExtra::clearGridLayoutRowStretch(grid);
        break;
    case LayoutPropertyGridColumnStretch:
        if (QGridLayout *grid = qobject_cast<QGridLayout *>(m_layout))
            QFormBuilderExtra::clearGridLayoutColumnStretch(grid);
        break;
    case LayoutPropertyGridRowMinimumHeight:
        if (QGridLayout *grid = qobject_cast<QGridLayout *>(m_layout))
            QFormBuilderExtra::clearGridLayoutRowMinimumHeight(grid);
        break;
    case LayoutPropertyGridColumnMinimumWidth:
        if (QGridLayout *grid = qobject_cast<QGridLayout *>(m_layout))
            QFormBuilderExtra::clearGridLayoutColumnMinimumWidth(grid);
        break;
    default:
        rc = QDesignerPropertySheet::reset(index);
        break;
    }
    return rc;
}

void LayoutPropertySheet::setChanged(int index, bool changed)
{
    const LayoutPropertyType type = layoutPropertyType(propertyName(index));
    switch (type) {
    case LayoutPropertySpacing:
        if (LayoutProperties::visibleProperties(m_layout) & LayoutProperties::HorizSpacingProperty) {
            setChanged(indexOf(QLatin1String(horizontalSpacing)), changed);
            setChanged(indexOf(QLatin1String(verticalSpacing)), changed);
        }
        break;
    case LayoutPropertyMargin:
        setChanged(indexOf(QLatin1String(leftMargin)), changed);
        setChanged(indexOf(QLatin1String(topMargin)), changed);
        setChanged(indexOf(QLatin1String(rightMargin)), changed);
        setChanged(indexOf(QLatin1String(bottomMargin)), changed);
        break;
    default:
        break;
    }
    QDesignerPropertySheet::setChanged(index, changed);
}

void LayoutPropertySheet::stretchAttributesToDom(QDesignerFormEditorInterface *core, QLayout *lt, DomLayout *domLayout)
{
    // Check if the respective stretch properties of the layout are changed.
    // If so, set them to the DOM
    const int visibleMask = LayoutProperties::visibleProperties(lt);
    if (!(visibleMask & (LayoutProperties::BoxStretchProperty|LayoutProperties::GridColumnStretchProperty|LayoutProperties::GridRowStretchProperty)))
        return;
    const QDesignerPropertySheetExtension *sheet = qt_extension<QDesignerPropertySheetExtension*>(core->extensionManager(), lt);
    Q_ASSERT(sheet);

    // Stretch
    if (visibleMask & LayoutProperties::BoxStretchProperty) {
        const int index = sheet->indexOf(QLatin1String(boxStretchPropertyC));
        Q_ASSERT(index != -1);
        if (sheet->isChanged(index))
            domLayout->setAttributeStretch(sheet->property(index).toString());
    }
    if (visibleMask & LayoutProperties::GridColumnStretchProperty) {
        const int index = sheet->indexOf(QLatin1String(gridColumnStretchPropertyC));
        Q_ASSERT(index != -1);
        if (sheet->isChanged(index))
            domLayout->setAttributeColumnStretch(sheet->property(index).toString());
    }
    if (visibleMask & LayoutProperties::GridRowStretchProperty) {
        const int index = sheet->indexOf(QLatin1String(gridRowStretchPropertyC));
        Q_ASSERT(index != -1);
        if (sheet->isChanged(index))
            domLayout->setAttributeRowStretch(sheet->property(index).toString());
    }
    if (visibleMask & LayoutProperties::GridRowMinimumHeightProperty) {
        const int index = sheet->indexOf(QLatin1String(gridRowMinimumHeightPropertyC));
        Q_ASSERT(index != -1);
        if (sheet->isChanged(index))
            domLayout->setAttributeRowMinimumHeight(sheet->property(index).toString());
    }
    if (visibleMask & LayoutProperties::GridColumnMinimumWidthProperty) {
        const int index = sheet->indexOf(QLatin1String(gridColumnMinimumWidthPropertyC));
        Q_ASSERT(index != -1);
        if (sheet->isChanged(index))
            domLayout->setAttributeColumnMinimumWidth(sheet->property(index).toString());
    }
}

void LayoutPropertySheet::markChangedStretchProperties(QDesignerFormEditorInterface *core, QLayout *lt, const DomLayout *domLayout)
{
    // While the actual values are applied by the form builder, we still need
    // to mark them as 'changed'.
    QDesignerPropertySheetExtension *sheet = qt_extension<QDesignerPropertySheetExtension*>(core->extensionManager(), lt);
    Q_ASSERT(sheet);
    if (!domLayout->attributeStretch().isEmpty())
        sheet->setChanged(sheet->indexOf(QLatin1String(boxStretchPropertyC)), true);
    if (!domLayout->attributeRowStretch().isEmpty())
        sheet->setChanged(sheet->indexOf(QLatin1String(gridRowStretchPropertyC)), true);
    if (!domLayout->attributeColumnStretch().isEmpty())
        sheet->setChanged(sheet->indexOf(QLatin1String(gridColumnStretchPropertyC)), true);
   if (!domLayout->attributeColumnMinimumWidth().isEmpty())
        sheet->setChanged(sheet->indexOf(QLatin1String(gridColumnMinimumWidthPropertyC)), true);
   if (!domLayout->attributeRowMinimumHeight().isEmpty())
        sheet->setChanged(sheet->indexOf(QLatin1String(gridRowMinimumHeightPropertyC)), true);
}

}

QT_END_NAMESPACE


char CP437::EncodeChar(uint32_t unicode, char fb) {
		auto it = g_reverse.mp.find(unicode);
		if (it == g_reverse.mp.end()) {
			return fb;
		} else {
			SPAssert(cp437map[static_cast<unsigned char>(it->second)] == unicode);
			return it->second;
		}
	}

	std::uint32_t CP437::DecodeChar(char c) {
		auto r = cp437map[static_cast<unsigned char>(c)];
		SPAssert(g_reverse.mp.find(r)->second == c);
		return r;
	}

	std::string CP437::Encode(const std::string &str, char fallback) {
		size_t idx = 0;
		std::string outp;
		outp.reserve(str.size());
		while (idx < str.size()) {
			size_t outNumBytes;
			auto cp = GetCodePointFromUTF8String(str, idx, &outNumBytes);
			idx += outNumBytes;
			outp.push_back(EncodeChar(cp, fallback));
		}
		return outp;
	}

	std::string CP437::Decode(const std::string &str) {
		std::string outp;
		outp.reserve(str.size() * 3);
		auto it = std::back_inserter(outp);
		for (size_t i = 0; i < str.size(); i++) {
			auto cp = DecodeChar(str[i]);
			CodePointToUTF8(it, cp);
		}
		return outp;
	}
} // namespace spades


QString AddressTableModel::labelForAddress(const QString &address) const
{
    {
        LOCK(wallet->cs_wallet);
        CBitcoinAddress address_parsed(address.toStdString());
        std::map<CTxDestination, CAddressBookData>::iterator mi = wallet->mapAddressBook.find(address_parsed.Get());
        if (mi != wallet->mapAddressBook.end())
        {
            return QString::fromStdString(mi->second.name);
        }
    }
    return QString();
}

#include "addresstablemodel.h"

#include "guiutil.h"
#include "walletmodel.h"

#include "base58.h"
#include "wallet/wallet.h"

#include <boost/foreach.hpp>

#include <QFont>
#include <QDebug>

const QString AddressTableModel::Send = "S";
const QString AddressTableModel::Receive = "R";

struct AddressTableEntry
{
    enum Type {
        Sending,
        Receiving,
        Hidden /* QSortFilterProxyModel will filter these out */
    };

    Type type;
    QString label;
    QString address;

    AddressTableEntry() {}
    AddressTableEntry(Type type, const QString &label, const QString &address):
        type(type), label(label), address(address) {}
};

struct AddressTableEntryLessThan
{
    bool operator()(const AddressTableEntry &a, const AddressTableEntry &b) const
    {
        return a.address < b.address;
    }
    bool operator()(const AddressTableEntry &a, const QString &b) const
    {
        return a.address < b;
    }
    bool operator()(const QString &a, const AddressTableEntry &b) const
    {
        return a < b.address;
    }
};

/* Determine address type from address purpose */
static AddressTableEntry::Type translateTransactionType(const QString &strPurpose, bool isMine)
{
    AddressTableEntry::Type addressType = AddressTableEntry::Hidden;
    // "refund" addresses aren't shown, and change addresses aren't in mapAddressBook at all.
    if (strPurpose == "send")
        addressType = AddressTableEntry::Sending;
    else if (strPurpose == "receive")
        addressType = AddressTableEntry::Receiving;
    else if (strPurpose == "unknown" || strPurpose == "") // if purpose not set, guess
        addressType = (isMine ? AddressTableEntry::Receiving : AddressTableEntry::Sending);
    return addressType;
}

// Private implementation
class AddressTablePriv
{
public:
    CWallet *wallet;
    QList<AddressTableEntry> cachedAddressTable;
    AddressTableModel *parent;

    AddressTablePriv(CWallet *wallet, AddressTableModel *parent):
        wallet(wallet), parent(parent) {}

    void refreshAddressTable()
    {
        cachedAddressTable.clear();
        {
            LOCK(wallet->cs_wallet);
            BOOST_FOREACH(const PAIRTYPE(CTxDestination, CAddressBookData)& item, wallet->mapAddressBook)
            {
                const CBitcoinAddress& address = item.first;
                bool fMine = IsMine(*wallet, address.Get());
                AddressTableEntry::Type addressType = translateTransactionType(
                        QString::fromStdString(item.second.purpose), fMine);
                const std::string& strName = item.second.name;
                cachedAddressTable.append(AddressTableEntry(addressType,
                                  QString::fromStdString(strName),
                                  QString::fromStdString(address.ToString())));
            }
        }
        // qLowerBound() and qUpperBound() require our cachedAddressTable list to be sorted in asc order
        // Even though the map is already sorted this re-sorting step is needed because the originating map
        // is sorted by binary address, not by base58() address.
        qSort(cachedAddressTable.begin(), cachedAddressTable.end(), AddressTableEntryLessThan());
    }

    void updateEntry(const QString &address, const QString &label, bool isMine, const QString &purpose, int status)
    {
        // Find address / label in model
        QList<AddressTableEntry>::iterator lower = qLowerBound(
            cachedAddressTable.begin(), cachedAddressTable.end(), address, AddressTableEntryLessThan());
        QList<AddressTableEntry>::iterator upper = qUpperBound(
            cachedAddressTable.begin(), cachedAddressTable.end(), address, AddressTableEntryLessThan());
        int lowerIndex = (lower - cachedAddressTable.begin());
        int upperIndex = (upper - cachedAddressTable.begin());
        bool inModel = (lower != upper);
        AddressTableEntry::Type newEntryType = translateTransactionType(purpose, isMine);

        switch(status)
        {
        case CT_NEW:
            if(inModel)
            {
                qWarning() << "AddressTablePriv::updateEntry: Warning: Got CT_NEW, but entry is already in model";
                break;
            }
            parent->beginInsertRows(QModelIndex(), lowerIndex, lowerIndex);
            cachedAddressTable.insert(lowerIndex, AddressTableEntry(newEntryType, label, address));
            parent->endInsertRows();
            break;
        case CT_UPDATED:
            if(!inModel)
            {
                qWarning() << "AddressTablePriv::updateEntry: Warning: Got CT_UPDATED, but entry is not in model";
                break;
            }
            lower->type = newEntryType;
            lower->label = label;
            parent->emitDataChanged(lowerIndex);
            break;
        case CT_DELETED:
            if(!inModel)
            {
                qWarning() << "AddressTablePriv::updateEntry: Warning: Got CT_DELETED, but entry is not in model";
                break;
            }
            parent->beginRemoveRows(QModelIndex(), lowerIndex, upperIndex-1);
            cachedAddressTable.erase(lower, upper);
            parent->endRemoveRows();
            break;
        }
    }

    int size()
    {
        return cachedAddressTable.size();
    }

    AddressTableEntry *index(int idx)
    {
        if(idx >= 0 && idx < cachedAddressTable.size())
        {
            return &cachedAddressTable[idx];
        }
        else
        {
            return 0;
        }
    }
};

AddressTableModel::AddressTableModel(CWallet *wallet, WalletModel *parent) :
    QAbstractTableModel(parent),walletModel(parent),wallet(wallet),priv(0)
{
    columns << tr("Label") << tr("Address");
    priv = new AddressTablePriv(wallet, this);
    priv->refreshAddressTable();
}

AddressTableModel::~AddressTableModel()
{
    delete priv;
}

int AddressTableModel::rowCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent);
    return priv->size();
}

int AddressTableModel::columnCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent);
    return columns.length();
}

QVariant AddressTableModel::data(const QModelIndex &index, int role) const
{
    if(!index.isValid())
        return QVariant();

    AddressTableEntry *rec = static_cast<AddressTableEntry*>(index.internalPointer());

    if(role == Qt::DisplayRole || role == Qt::EditRole)
    {
        switch(index.column())
        {
        case Label:
            if(rec->label.isEmpty() && role == Qt::DisplayRole)
            {
                return tr("(no label)");
            }
            else
            {
                return rec->label;
            }
        case Address:
            return rec->address;
        }
    }
    else if (role == Qt::FontRole)
    {
        QFont font;
        if(index.column() == Address)
        {
            font = GUIUtil::bitcoinAddressFont();
        }
        return font;
    }
    else if (role == TypeRole)
    {
        switch(rec->type)
        {
        case AddressTableEntry::Sending:
            return Send;
        case AddressTableEntry::Receiving:
            return Receive;
        default: break;
        }
    }
    return QVariant();
}

bool AddressTableModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
    if(!index.isValid())
        return false;
    AddressTableEntry *rec = static_cast<AddressTableEntry*>(index.internalPointer());
    std::string strPurpose = (rec->type == AddressTableEntry::Sending ? "send" : "receive");
    editStatus = OK;

    if(role == Qt::EditRole)
    {
        LOCK(wallet->cs_wallet); /* For SetAddressBook / DelAddressBook */
        CTxDestination curAddress = CBitcoinAddress(rec->address.toStdString()).Get();
        if(index.column() == Label)
        {
            if(rec->label == value.toString())
            {
                editStatus = NO_CHANGES;
                return false;
            }
            wallet->SetAddressBook(curAddress, value.toString().toStdString(), strPurpose);
        } else if(index.column() == Address) {
            CTxDestination newAddress = CBitcoinAddress(value.toString().toStdString()).Get();
            // Refuse to set invalid address, set error status and return false
            if(boost::get<CNoDestination>(&newAddress))
            {
                editStatus = INVALID_ADDRESS;
                return false;
            }
            else if(newAddress == curAddress)
            {
                editStatus = NO_CHANGES;
                return false;
            }
            // Check for duplicate addresses to prevent accidental deletion of addresses, if you try
            // to paste an existing address over another address (with a different label)
            else if(wallet->mapAddressBook.count(newAddress))
            {
                editStatus = DUPLICATE_ADDRESS;
                return false;
            }
            // Double-check that we're not overwriting a receiving address
            else if(rec->type == AddressTableEntry::Sending)
            {
                // Remove old entry
                wallet->DelAddressBook(curAddress);
                // Add new entry with new address
                wallet->SetAddressBook(newAddress, rec->label.toStdString(), strPurpose);
            }
        }
        return true;
    }
    return false;
}

QVariant AddressTableModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    if(orientation == Qt::Horizontal)
    {
        if(role == Qt::DisplayRole && section < columns.size())
        {
            return columns[section];
        }
    }
    return QVariant();
}

Qt::ItemFlags AddressTableModel::flags(const QModelIndex &index) const
{
    if(!index.isValid())
        return 0;
    AddressTableEntry *rec = static_cast<AddressTableEntry*>(index.internalPointer());

    Qt::ItemFlags retval = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
    // Can edit address and label for sending addresses,
    // and only label for receiving addresses.
    if(rec->type == AddressTableEntry::Sending ||
      (rec->type == AddressTableEntry::Receiving && index.column()==Label))
    {
        retval |= Qt::ItemIsEditable;
    }
    return retval;
}

QModelIndex AddressTableModel::index(int row, int column, const QModelIndex &parent) const
{
    Q_UNUSED(parent);
    AddressTableEntry *data = priv->index(row);
    if(data)
    {
        return createIndex(row, column, priv->index(row));
    }
    else
    {
        return QModelIndex();
    }
}

void AddressTableModel::updateEntry(const QString &address,
        const QString &label, bool isMine, const QString &purpose, int status)
{
    // Update address book model from Bitcoin core
    priv->updateEntry(address, label, isMine, purpose, status);
}

QString AddressTableModel::addRow(const QString &type, const QString &label, const QString &address)
{
    std::string strLabel = label.toStdString();
    std::string strAddress = address.toStdString();

    editStatus = OK;

    if(type == Send)
    {
        if(!walletModel->validateAddress(address))
        {
            editStatus = INVALID_ADDRESS;
            return QString();
        }
        // Check for duplicate addresses
        {
            LOCK(wallet->cs_wallet);
            if(wallet->mapAddressBook.count(CBitcoinAddress(strAddress).Get()))
            {
                editStatus = DUPLICATE_ADDRESS;
                return QString();
            }
        }
    }
    else if(type == Receive)
    {
        // Generate a new address to associate with given label
        CPubKey newKey;
        if(!wallet->GetKeyFromPool(newKey))
        {
            WalletModel::UnlockContext ctx(walletModel->requestUnlock());
            if(!ctx.isValid())
            {
                // Unlock wallet failed or was cancelled
                editStatus = WALLET_UNLOCK_FAILURE;
                return QString();
            }
            if(!wallet->GetKeyFromPool(newKey))
            {
                editStatus = KEY_GENERATION_FAILURE;
                return QString();
            }
        }
        strAddress = CBitcoinAddress(newKey.GetID()).ToString();
    }
    else
    {
        return QString();
    }

    // Add entry
    {
        LOCK(wallet->cs_wallet);
        wallet->SetAddressBook(CBitcoinAddress(strAddress).Get(), strLabel,
                               (type == Send ? "send" : "receive"));
    }
    return QString::fromStdString(strAddress);
}

bool AddressTableModel::removeRows(int row, int count, const QModelIndex &parent)
{
    Q_UNUSED(parent);
    AddressTableEntry *rec = priv->index(row);
    if(count != 1 || !rec || rec->type == AddressTableEntry::Receiving)
    {
        // Can only remove one row at a time, and cannot remove rows not in model.
        // Also refuse to remove receiving addresses.
        return false;
    }
    {
        LOCK(wallet->cs_wallet);
        wallet->DelAddressBook(CBitcoinAddress(rec->address.toStdString()).Get());
    }
    return true;
}

/* Look up label for address in address book, if not found return empty string.
 */

int AddressTableModel::lookupAddress(const QString &address) const
{
    QModelIndexList lst = match(index(0, Address, QModelIndex()),
                                Qt::EditRole, address, 1, Qt::MatchExactly);
    if(lst.isEmpty())
    {
        return -1;
    }
    else
    {
        return lst.at(0).row();
    }
}

void AddressTableModel::emitDataChanged(int idx)
{
    Q_EMIT dataChanged(index(idx, 0, QModelIndex()), index(idx, columns.length()-1, QModelIndex()));
}


switch(index.column())
        {
        case Label:
            if(rec->label.isEmpty() && role == Qt::DisplayRole)
            {
                return tr("(no label)");
            }
            else
            {
                return rec->label;
            }
        case Address:
            return rec->address;
        }

#include "addresstablemodel.h"

#include "guiutil.h"
#include "walletmodel.h"

#include "base58.h"
#include "wallet/wallet.h"

#include <boost/foreach.hpp>

#include <QFont>
#include <QDebug>

const QString AddressTableModel::Send = "S";
const QString AddressTableModel::Receive = "R";

struct AddressTableEntry
{
    enum Type {
        Sending,
        Receiving,
        Hidden /* QSortFilterProxyModel will filter these out */
    };

    Type type;
    QString label;
    QString address;

    AddressTableEntry() {}
    AddressTableEntry(Type type, const QString &label, const QString &address):
        type(type), label(label), address(address) {}
};

struct AddressTableEntryLessThan
{
    bool operator()(const AddressTableEntry &a, const AddressTableEntry &b) const
    {
        return a.address < b.address;
    }
    bool operator()(const AddressTableEntry &a, const QString &b) const
    {
        return a.address < b;
    }
    bool operator()(const QString &a, const AddressTableEntry &b) const
    {
        return a < b.address;
    }
};

/* Determine address type from address purpose */
static AddressTableEntry::Type translateTransactionType(const QString &strPurpose, bool isMine)
{
    AddressTableEntry::Type addressType = AddressTableEntry::Hidden;
    // "refund" addresses aren't shown, and change addresses aren't in mapAddressBook at all.
    if (strPurpose == "send")
        addressType = AddressTableEntry::Sending;
    else if (strPurpose == "receive")
        addressType = AddressTableEntry::Receiving;
    else if (strPurpose == "unknown" || strPurpose == "") // if purpose not set, guess
        addressType = (isMine ? AddressTableEntry::Receiving : AddressTableEntry::Sending);
    return addressType;
}

// Private implementation
class AddressTablePriv
{
public:
    CWallet *wallet;
    QList<AddressTableEntry> cachedAddressTable;
    AddressTableModel *parent;

    AddressTablePriv(CWallet *wallet, AddressTableModel *parent):
        wallet(wallet), parent(parent) {}

    void refreshAddressTable()
    {
        cachedAddressTable.clear();
        {
            LOCK(wallet->cs_wallet);
            BOOST_FOREACH(const PAIRTYPE(CTxDestination, CAddressBookData)& item, wallet->mapAddressBook)
            {
                const CBitcoinAddress& address = item.first;
                bool fMine = IsMine(*wallet, address.Get());
                AddressTableEntry::Type addressType = translateTransactionType(
                        QString::fromStdString(item.second.purpose), fMine);
                const std::string& strName = item.second.name;
                cachedAddressTable.append(AddressTableEntry(addressType,
                                  QString::fromStdString(strName),
                                  QString::fromStdString(address.ToString())));
            }
        }
        // qLowerBound() and qUpperBound() require our cachedAddressTable list to be sorted in asc order
        // Even though the map is already sorted this re-sorting step is needed because the originating map
        // is sorted by binary address, not by base58() address.
        qSort(cachedAddressTable.begin(), cachedAddressTable.end(), AddressTableEntryLessThan());
    }

    void updateEntry(const QString &address, const QString &label, bool isMine, const QString &purpose, int status)
    {
        // Find address / label in model
        QList<AddressTableEntry>::iterator lower = qLowerBound(
            cachedAddressTable.begin(), cachedAddressTable.end(), address, AddressTableEntryLessThan());
        QList<AddressTableEntry>::iterator upper = qUpperBound(
            cachedAddressTable.begin(), cachedAddressTable.end(), address, AddressTableEntryLessThan());
        int lowerIndex = (lower - cachedAddressTable.begin());
        int upperIndex = (upper - cachedAddressTable.begin());
        bool inModel = (lower != upper);
        AddressTableEntry::Type newEntryType = translateTransactionType(purpose, isMine);

        switch(status)
        {
        case CT_NEW:
            if(inModel)
            {
                qWarning() << "AddressTablePriv::updateEntry: Warning: Got CT_NEW, but entry is already in model";
                break;
            }
            parent->beginInsertRows(QModelIndex(), lowerIndex, lowerIndex);
            cachedAddressTable.insert(lowerIndex, AddressTableEntry(newEntryType, label, address));
            parent->endInsertRows();
            break;
        case CT_UPDATED:
            if(!inModel)
            {
                qWarning() << "AddressTablePriv::updateEntry: Warning: Got CT_UPDATED, but entry is not in model";
                break;
            }
            lower->type = newEntryType;
            lower->label = label;
            parent->emitDataChanged(lowerIndex);
            break;
        case CT_DELETED:
            if(!inModel)
            {
                qWarning() << "AddressTablePriv::updateEntry: Warning: Got CT_DELETED, but entry is not in model";
                break;
            }
            parent->beginRemoveRows(QModelIndex(), lowerIndex, upperIndex-1);
            cachedAddressTable.erase(lower, upper);
            parent->endRemoveRows();
            break;
        }
    }

    int size()
    {
        return cachedAddressTable.size();
    }

    AddressTableEntry *index(int idx)
    {
        if(idx >= 0 && idx < cachedAddressTable.size())
        {
            return &cachedAddressTable[idx];
        }
        else
        {
            return 0;
        }
    }
};

AddressTableModel::AddressTableModel(CWallet *wallet, WalletModel *parent) :
    QAbstractTableModel(parent),walletModel(parent),wallet(wallet),priv(0)
{
    columns << tr("Label") << tr("Address");
    priv = new AddressTablePriv(wallet, this);
    priv->refreshAddressTable();
}

AddressTableModel::~AddressTableModel()
{
    delete priv;
}

int AddressTableModel::rowCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent);
    return priv->size();
}

int AddressTableModel::columnCount(const QModelIndex &parent) const
{
    Q_UNUSED(parent);
    return columns.length();
}

QVariant AddressTableModel::data(const QModelIndex &index, int role) const
{
    if(!index.isValid())
        return QVariant();

    AddressTableEntry *rec = static_cast<AddressTableEntry*>(index.internalPointer());

    if(role == Qt::DisplayRole || role == Qt::EditRole)
    {

    }
    else if (role == Qt::FontRole)
    {
        QFont font;
        if(index.column() == Address)
        {
            font = GUIUtil::bitcoinAddressFont();
        }
        return font;
    }
    else if (role == TypeRole)
    {
        switch(rec->type)
        {
        case AddressTableEntry::Sending:
            return Send;
        case AddressTableEntry::Receiving:
            return Receive;
        default: break;
        }
    }
    return QVariant();
}

bool AddressTableModel::setData(const QModelIndex &index, const QVariant &value, int role)
{
    if(!index.isValid())
        return false;
    AddressTableEntry *rec = static_cast<AddressTableEntry*>(index.internalPointer());
    std::string strPurpose = (rec->type == AddressTableEntry::Sending ? "send" : "receive");
    editStatus = OK;

    if(role == Qt::EditRole)
    {
        LOCK(wallet->cs_wallet); /* For SetAddressBook / DelAddressBook */
        CTxDestination curAddress = CBitcoinAddress(rec->address.toStdString()).Get();
        if(index.column() == Label)
        {
            if(rec->label == value.toString())
            {
                editStatus = NO_CHANGES;
                return false;
            }
            wallet->SetAddressBook(curAddress, value.toString().toStdString(), strPurpose);
        } else if(index.column() == Address) {
            CTxDestination newAddress = CBitcoinAddress(value.toString().toStdString()).Get();
            // Refuse to set invalid address, set error status and return false
            if(boost::get<CNoDestination>(&newAddress))
            {
                editStatus = INVALID_ADDRESS;
                return false;
            }
            else if(newAddress == curAddress)
            {
                editStatus = NO_CHANGES;
                return false;
            }
            // Check for duplicate addresses to prevent accidental deletion of addresses, if you try
            // to paste an existing address over another address (with a different label)
            else if(wallet->mapAddressBook.count(newAddress))
            {
                editStatus = DUPLICATE_ADDRESS;
                return false;
            }
            // Double-check that we're not overwriting a receiving address
            else if(rec->type == AddressTableEntry::Sending)
            {
                // Remove old entry
                wallet->DelAddressBook(curAddress);
                // Add new entry with new address
                wallet->SetAddressBook(newAddress, rec->label.toStdString(), strPurpose);
            }
        }
        return true;
    }
    return false;
}

QVariant AddressTableModel::headerData(int section, Qt::Orientation orientation, int role) const
{
    if(orientation == Qt::Horizontal)
    {
        if(role == Qt::DisplayRole && section < columns.size())
        {
            return columns[section];
        }
    }
    return QVariant();
}

Qt::ItemFlags AddressTableModel::flags(const QModelIndex &index) const
{
    if(!index.isValid())
        return 0;
    AddressTableEntry *rec = static_cast<AddressTableEntry*>(index.internalPointer());

    Qt::ItemFlags retval = Qt::ItemIsSelectable | Qt::ItemIsEnabled;
    // Can edit address and label for sending addresses,
    // and only label for receiving addresses.
    if(rec->type == AddressTableEntry::Sending ||
      (rec->type == AddressTableEntry::Receiving && index.column()==Label))
    {
        retval |= Qt::ItemIsEditable;
    }
    return retval;
}

QModelIndex AddressTableModel::index(int row, int column, const QModelIndex &parent) const
{
    Q_UNUSED(parent);
    AddressTableEntry *data = priv->index(row);
    if(data)
    {
        return createIndex(row, column, priv->index(row));
    }
    else
    {
        return QModelIndex();
    }
}

void AddressTableModel::updateEntry(const QString &address,
        const QString &label, bool isMine, const QString &purpose, int status)
{
    // Update address book model from Bitcoin core
    priv->updateEntry(address, label, isMine, purpose, status);
}

QString AddressTableModel::addRow(const QString &type, const QString &label, const QString &address)
{
    std::string strLabel = label.toStdString();
    std::string strAddress = address.toStdString();

    editStatus = OK;

    if(type == Send)
    {
        if(!walletModel->validateAddress(address))
        {
            editStatus = INVALID_ADDRESS;
            return QString();
        }
        // Check for duplicate addresses
        {
            LOCK(wallet->cs_wallet);
            if(wallet->mapAddressBook.count(CBitcoinAddress(strAddress).Get()))
            {
                editStatus = DUPLICATE_ADDRESS;
                return QString();
            }
        }
    }
    else if(type == Receive)
    {
        // Generate a new address to associate with given label
        CPubKey newKey;
        if(!wallet->GetKeyFromPool(newKey))
        {
            WalletModel::UnlockContext ctx(walletModel->requestUnlock());
            if(!ctx.isValid())
            {
                // Unlock wallet failed or was cancelled
                editStatus = WALLET_UNLOCK_FAILURE;
                return QString();
            }
            if(!wallet->GetKeyFromPool(newKey))
            {
                editStatus = KEY_GENERATION_FAILURE;
                return QString();
            }
        }
        strAddress = CBitcoinAddress(newKey.GetID()).ToString();
    }
    else
    {
        return QString();
    }

    // Add entry
    {
        LOCK(wallet->cs_wallet);
        wallet->SetAddressBook(CBitcoinAddress(strAddress).Get(), strLabel,
                               (type == Send ? "send" : "receive"));
    }
    return QString::fromStdString(strAddress);
}

bool AddressTableModel::removeRows(int row, int count, const QModelIndex &parent)
{
    Q_UNUSED(parent);
    AddressTableEntry *rec = priv->index(row);
    if(count != 1 || !rec || rec->type == AddressTableEntry::Receiving)
    {
        // Can only remove one row at a time, and cannot remove rows not in model.
        // Also refuse to remove receiving addresses.
        return false;
    }
    {
        LOCK(wallet->cs_wallet);
        wallet->DelAddressBook(CBitcoinAddress(rec->address.toStdString()).Get());
    }
    return true;
}

/* Look up label for address in address book, if not found return empty string.
 */
QString AddressTableModel::labelForAddress(const QString &address) const
{
    {
        LOCK(wallet->cs_wallet);
        CBitcoinAddress address_parsed(address.toStdString());
        std::map<CTxDestination, CAddressBookData>::iterator mi = wallet->mapAddressBook.find(address_parsed.Get());
        if (mi != wallet->mapAddressBook.end())
        {
            return QString::fromStdString(mi->second.name);
        }
    }
    return QString();
}

int AddressTableModel::lookupAddress(const QString &address) const
{
    QModelIndexList lst = match(index(0, Address, QModelIndex()),
                                Qt::EditRole, address, 1, Qt::MatchExactly);
    if(lst.isEmpty())
    {
        return -1;
    }
    else
    {
        return lst.at(0).row();
    }
}

void AddressTableModel::emitDataChanged(int idx)
{
    Q_EMIT dataChanged(index(idx, 0, QModelIndex()), index(idx, columns.length()-1, QModelIndex()));
}


Imf::Int64 ilOStream::tellp()
{
	Imf::Int64 Pos;

	// itellw only returns a 32-bit value!
	Pos = itellw();

	return Pos;
}

// ImageLib Sources
// Filename: src-IL/src/il_exr.cpp
// Description: Reads from an OpenEXR (.exr) file using the OpenEXR library.

#include "il_internal.h"
#ifndef IL_NO_EXR

#ifndef HAVE_CONFIG_H // We are probably on a Windows box .
//#define OPENEXR_DLL
#define HALF_EXPORTS
#endif //HAVE_CONFIG_H

#include "il_exr.h"
#include <ImfRgba.h>
#include <ImfArray.h>
#include <ImfRgbaFile.h>

#if (defined(_WIN32) || defined(_WIN64)) && defined(IL_USE_PRAGMA_LIBS)
	#if defined(_MSC_VER) || defined(__BORLANDC__)
		#ifndef _DEBUG
			#pragma comment(lib, "openexr.lib")
		#else
			#pragma comment(lib, "openexr-d.lib")
		#endif
	#endif
#endif

//! Checks if the file specified in FileName is a valid EXR file.
ILboolean ilIsValidExr(ILconst_string FileName)
{
	ILHANDLE	ExrFile;
	ILboolean	bExr = IL_FALSE;

	if (!iCheckExtension(FileName, IL_TEXT("exr"))) {
		ilSetError(IL_INVALID_EXTENSION);
		return bExr;
	}

	ExrFile = iopenr(FileName);
	if (ExrFile == NULL) {
		ilSetError(IL_COULD_NOT_OPEN_FILE);
		return bExr;
	}

	bExr = ilIsValidExrF(ExrFile);
	icloser(ExrFile);

	return bExr;
}

//! Checks if the ILHANDLE contains a valid EXR file at the current position.
ILboolean ilIsValidExrF(ILHANDLE File)
{
	ILuint		FirstPos;
	ILboolean	bRet;

	iSetInputFile(File);
	FirstPos = itell();
	bRet = iIsValidExr();
	iseek(FirstPos, IL_SEEK_SET);

	return bRet;
}

//! Checks if Lump is a valid EXR lump.
ILboolean ilIsValidExrL(const void *Lump, ILuint Size)
{
	iSetInputLump(Lump, Size);
	return iIsValidExr();
}

// Internal function used to get the EXR header from the current file.
ILboolean iGetExrHead(EXRHEAD *Header)
{
	Header->MagicNumber = GetLittleUInt();
	Header->Version = GetLittleUInt();

	return IL_TRUE;
}

// Internal function to get the header and check it.
ILboolean iIsValidExr()
{
	EXRHEAD Head;

	if (!iGetExrHead(&Head))
		return IL_FALSE;
	iseek(-8, IL_SEEK_CUR);

	return iCheckExr(&Head);
}

// Internal function used to check if the HEADER is a valid EXR header.
ILboolean iCheckExr(EXRHEAD *Header)
{
	// The file magic number (signature) is 0x76, 0x2f, 0x31, 0x01
	if (Header->MagicNumber != 0x01312F76)
		return IL_FALSE;
	// The only valid version so far is version 2.  The upper value has
	//  to do with tiling.
	if (Header->Version != 0x002 && Header->Version != 0x202)
		return IL_FALSE;

	return IL_TRUE;
}

// Nothing to do here in the constructor.
ilIStream::ilIStream() : Imf::IStream("N/A")
{
	return;
}

bool ilIStream::read(char c[], int n)
{
	if (iread(c, 1, n) != n)
		return false;
	return true;
}

//@TODO: Make this work with 64-bit values.
Imf::Int64 ilIStream::tellg()
{
	Imf::Int64 Pos;

	// itell only returns a 32-bit value!
	Pos = itell();

	return Pos;
}

// Note that there is no return value here, even though there probably should be.
//@TODO: Make this work with 64-bit values.
void ilIStream::seekg(Imf::Int64 Pos)
{
	// iseek only uses a 32-bit value!
	iseek((ILint)Pos, IL_SEEK_SET);  // I am assuming this is seeking from the beginning.
	return;
}

void ilIStream::clear()
{
	return;
}

//! Reads an .exr file.
ILboolean ilLoadExr(ILconst_string FileName)
{
	ILHANDLE	ExrFile;
	ILboolean	bExr = IL_FALSE;

	ExrFile = iopenr(FileName);
	if (ExrFile == NULL) {
		ilSetError(IL_COULD_NOT_OPEN_FILE);
		return bExr;
	}

	bExr = ilLoadExrF(ExrFile);
	icloser(ExrFile);

	return bExr;
}

//! Reads an already-opened .exr file
ILboolean ilLoadExrF(ILHANDLE File)
{
	ILuint		FirstPos;
	ILboolean	bRet;

	iSetInputFile(File);
	FirstPos = itell();
	bRet = iLoadExrInternal();
	iseek(FirstPos, IL_SEEK_SET);

	return bRet;
}

//! Reads from a memory "lump" that contains an .exr
ILboolean ilLoadExrL(const void *Lump, ILuint Size)
{
	iSetInputLump(Lump, Size);
	return iLoadExrInternal();
}

using namespace Imath;
using namespace Imf;
using namespace std;

ILboolean iLoadExrInternal()
{
	Array<Rgba> pixels;
	Box2i dataWindow;
	float pixelAspectRatio;
	ILfloat *FloatData;

	ilIStream File;
	RgbaInputFile in(File);

	Rgba a;
    dataWindow = in.dataWindow();
    pixelAspectRatio = in.pixelAspectRatio();

    int dw, dh, dx, dy;

	dw = dataWindow.max.x - dataWindow.min.x + 1;
    dh = dataWindow.max.y - dataWindow.min.y + 1;
    dx = dataWindow.min.x;
    dy = dataWindow.min.y;

    pixels.resizeErase (dw * dh);
	in.setFrameBuffer (pixels - dx - dy * dw, 1, dw);

	try
    {
		in.readPixels (dataWindow.min.y, dataWindow.max.y);
    }
    catch (const exception &e)
    {
	// If some of the pixels in the file cannot be read,
	// print an error message, and return a partial image
	// to the caller.
		ilSetError(IL_LIB_EXR_ERROR);  // Could I use something a bit more descriptive based on e?
		e;  // Prevent the compiler from yelling at us about this being unused.
		return IL_FALSE;
    }

	if (ilTexImage(dw, dh, 1, 4, IL_RGBA, IL_FLOAT, NULL) == IL_FALSE)
		return IL_FALSE;

	// Determine where the origin is in the original file.
	if (in.lineOrder() == INCREASING_Y)
		iCurImage->Origin = IL_ORIGIN_UPPER_LEFT;
	else
		iCurImage->Origin = IL_ORIGIN_LOWER_LEFT;

	// Better to access FloatData instead of recasting everytime.
	FloatData = (ILfloat*)iCurImage->Data;

	for (int i = 0; i < dw * dh; i++)
	{
		// Too much data lost

		// The images look kind of washed out with this.

		// This gives the best results, since no data is lost.
		FloatData[i * 4]     = pixels[i].r;
		FloatData[i * 4 + 1] = pixels[i].g;
		FloatData[i * 4 + 2] = pixels[i].b;
		FloatData[i * 4 + 3] = pixels[i].a;
	}

	// Converts the image to predefined type, format and/or origin if needed.
	return ilFixImage();
}

// Nothing to do here in the constructor.
ilOStream::ilOStream() : Imf::OStream("N/A")
{
	return;
}

void ilOStream::write(const char c[], int n)
{
	iwrite(c, 1, n);  //@TODO: Throw an exception here.
	return;
}

//@TODO: Make this work with 64-bit values.

// Note that there is no return value here, even though there probably should be.
//@TODO: Make this work with 64-bit values.
void ilOStream::seekp(Imf::Int64 Pos)
{
	// iseekw only uses a 32-bit value!
	iseekw((ILint)Pos, IL_SEEK_SET);  // I am assuming this is seeking from the beginning.
	return;
}

//! Writes a Exr file
ILboolean ilSaveExr(const ILstring FileName)
{
	ILHANDLE	ExrFile;
	ILuint		ExrSize;

	if (ilGetBoolean(IL_FILE_MODE) == IL_FALSE) {
		if (iFileExists(FileName)) {
			ilSetError(IL_FILE_ALREADY_EXISTS);
			return IL_FALSE;
		}
	}

	ExrFile = iopenw(FileName);
	if (ExrFile == NULL) {
		ilSetError(IL_COULD_NOT_OPEN_FILE);
		return IL_FALSE;
	}

	ExrSize = ilSaveExrF(ExrFile);
	iclosew(ExrFile);

	if (ExrSize == 0)
		return IL_FALSE;
	return IL_TRUE;
}

//! Writes a Exr to an already-opened file
ILuint ilSaveExrF(ILHANDLE File)
{
	ILuint Pos;
	iSetOutputFile(File);
	Pos = itellw();
	if (iSaveExrInternal() == IL_FALSE)
		return 0;  // Error occurred
	return itellw() - Pos;  // Return the number of bytes written.
}

//! Writes a Exr to a memory "lump"
ILuint ilSaveExrL(void *Lump, ILuint Size)
{
	ILuint Pos = itellw();
	iSetOutputLump(Lump, Size);
	if (iSaveExrInternal() == IL_FALSE)
		return 0;  // Error occurred
	return itellw() - Pos;  // Return the number of bytes written.
}

ILboolean iSaveExrInternal()
{
	Imath::Box2i DataWindow(Imath::V2i(0, 0), Imath::V2i(iCurImage->Width-1, iCurImage->Height-1));
	Imf::LineOrder Order;
	if (iCurImage->Origin == IL_ORIGIN_LOWER_LEFT)
		Order = DECREASING_Y;
	else
		Order = INCREASING_Y;
	Imf::Header Head(iCurImage->Width, iCurImage->Height, DataWindow, 1, Imath::V2f (0, 0), 1, Order);

	ilOStream File;
	Imf::RgbaOutputFile Out(File, Head);
	ILimage *TempImage = iCurImage;

	//@TODO: Can we always assume that Rgba is packed the same?
	Rgba *HalfData = (Rgba*)ialloc(TempImage->Width * TempImage->Height * sizeof(Rgba));
	if (HalfData == NULL)
		return IL_FALSE;

	if (iCurImage->Format != IL_RGBA || iCurImage->Type != IL_FLOAT) {
		TempImage = iConvertImage(iCurImage, IL_RGBA, IL_FLOAT);
		if (TempImage == NULL) {
			ifree(HalfData);
			return IL_FALSE;
		}
	}

	ILuint Offset = 0;
	ILfloat *FloatPtr = (ILfloat*)TempImage->Data;
	for (unsigned int y = 0; y < TempImage->Height; y++) {
		for (unsigned int x = 0; x < TempImage->Width; x++) {
			HalfData[y * TempImage->Width + x].r = FloatPtr[Offset];
			HalfData[y * TempImage->Width + x].g = FloatPtr[Offset + 1];
			HalfData[y * TempImage->Width + x].b = FloatPtr[Offset + 2];
			HalfData[y * TempImage->Width + x].a = FloatPtr[Offset + 3];
			Offset += 4;  // 4 floats
		}
	}

	Out.setFrameBuffer(HalfData, 1, TempImage->Width);
	Out.writePixels(TempImage->Height);  //@TODO: Do each scanline separately to keep from using so much memory.

	// Free our half data.
	ifree(HalfData);
	// Destroy our temporary image if we used one.
	if (TempImage != iCurImage)
		ilCloseImage(TempImage);

	return IL_TRUE;
}

#endif //IL_NO_EXR


try
    {
		in.readPixels (dataWindow.min.y, dataWindow.max.y);
    }
    catch (const exception &e)
    {
	// If some of the pixels in the file cannot be read,
	// print an error message, and return a partial image
	// to the caller.
		ilSetError(IL_LIB_EXR_ERROR);  // Could I use something a bit more descriptive based on e?
		e;  // Prevent the compiler from yelling at us about this being unused.
		return IL_FALSE;
    }

// ImageLib Sources
// Filename: src-IL/src/il_exr.cpp
// Description: Reads from an OpenEXR (.exr) file using the OpenEXR library.

#include "il_internal.h"
#ifndef IL_NO_EXR

#ifndef HAVE_CONFIG_H // We are probably on a Windows box .
//#define OPENEXR_DLL
#define HALF_EXPORTS
#endif //HAVE_CONFIG_H

#include "il_exr.h"
#include <ImfRgba.h>
#include <ImfArray.h>
#include <ImfRgbaFile.h>

#if (defined(_WIN32) || defined(_WIN64)) && defined(IL_USE_PRAGMA_LIBS)
	#if defined(_MSC_VER) || defined(__BORLANDC__)
		#ifndef _DEBUG
			#pragma comment(lib, "openexr.lib")
		#else
			#pragma comment(lib, "openexr-d.lib")
		#endif
	#endif
#endif

//! Checks if the file specified in FileName is a valid EXR file.
ILboolean ilIsValidExr(ILconst_string FileName)
{
	ILHANDLE	ExrFile;
	ILboolean	bExr = IL_FALSE;

	if (!iCheckExtension(FileName, IL_TEXT("exr"))) {
		ilSetError(IL_INVALID_EXTENSION);
		return bExr;
	}

	ExrFile = iopenr(FileName);
	if (ExrFile == NULL) {
		ilSetError(IL_COULD_NOT_OPEN_FILE);
		return bExr;
	}

	bExr = ilIsValidExrF(ExrFile);
	icloser(ExrFile);

	return bExr;
}

//! Checks if the ILHANDLE contains a valid EXR file at the current position.
ILboolean ilIsValidExrF(ILHANDLE File)
{
	ILuint		FirstPos;
	ILboolean	bRet;

	iSetInputFile(File);
	FirstPos = itell();
	bRet = iIsValidExr();
	iseek(FirstPos, IL_SEEK_SET);

	return bRet;
}

//! Checks if Lump is a valid EXR lump.
ILboolean ilIsValidExrL(const void *Lump, ILuint Size)
{
	iSetInputLump(Lump, Size);
	return iIsValidExr();
}

// Internal function used to get the EXR header from the current file.
ILboolean iGetExrHead(EXRHEAD *Header)
{
	Header->MagicNumber = GetLittleUInt();
	Header->Version = GetLittleUInt();

	return IL_TRUE;
}

// Internal function to get the header and check it.
ILboolean iIsValidExr()
{
	EXRHEAD Head;

	if (!iGetExrHead(&Head))
		return IL_FALSE;
	iseek(-8, IL_SEEK_CUR);

	return iCheckExr(&Head);
}

// Internal function used to check if the HEADER is a valid EXR header.
ILboolean iCheckExr(EXRHEAD *Header)
{
	// The file magic number (signature) is 0x76, 0x2f, 0x31, 0x01
	if (Header->MagicNumber != 0x01312F76)
		return IL_FALSE;
	// The only valid version so far is version 2.  The upper value has
	//  to do with tiling.
	if (Header->Version != 0x002 && Header->Version != 0x202)
		return IL_FALSE;

	return IL_TRUE;
}

// Nothing to do here in the constructor.
ilIStream::ilIStream() : Imf::IStream("N/A")
{
	return;
}

bool ilIStream::read(char c[], int n)
{
	if (iread(c, 1, n) != n)
		return false;
	return true;
}

//@TODO: Make this work with 64-bit values.
Imf::Int64 ilIStream::tellg()
{
	Imf::Int64 Pos;

	// itell only returns a 32-bit value!
	Pos = itell();

	return Pos;
}

// Note that there is no return value here, even though there probably should be.
//@TODO: Make this work with 64-bit values.
void ilIStream::seekg(Imf::Int64 Pos)
{
	// iseek only uses a 32-bit value!
	iseek((ILint)Pos, IL_SEEK_SET);  // I am assuming this is seeking from the beginning.
	return;
}

void ilIStream::clear()
{
	return;
}

//! Reads an .exr file.
ILboolean ilLoadExr(ILconst_string FileName)
{
	ILHANDLE	ExrFile;
	ILboolean	bExr = IL_FALSE;

	ExrFile = iopenr(FileName);
	if (ExrFile == NULL) {
		ilSetError(IL_COULD_NOT_OPEN_FILE);
		return bExr;
	}

	bExr = ilLoadExrF(ExrFile);
	icloser(ExrFile);

	return bExr;
}

//! Reads an already-opened .exr file
ILboolean ilLoadExrF(ILHANDLE File)
{
	ILuint		FirstPos;
	ILboolean	bRet;

	iSetInputFile(File);
	FirstPos = itell();
	bRet = iLoadExrInternal();
	iseek(FirstPos, IL_SEEK_SET);

	return bRet;
}

//! Reads from a memory "lump" that contains an .exr
ILboolean ilLoadExrL(const void *Lump, ILuint Size)
{
	iSetInputLump(Lump, Size);
	return iLoadExrInternal();
}

using namespace Imath;
using namespace Imf;
using namespace std;

ILboolean iLoadExrInternal()
{
	Array<Rgba> pixels;
	Box2i dataWindow;
	float pixelAspectRatio;
	ILfloat *FloatData;

	ilIStream File;
	RgbaInputFile in(File);

	Rgba a;
    dataWindow = in.dataWindow();
    pixelAspectRatio = in.pixelAspectRatio();

    int dw, dh, dx, dy;

	dw = dataWindow.max.x - dataWindow.min.x + 1;
    dh = dataWindow.max.y - dataWindow.min.y + 1;
    dx = dataWindow.min.x;
    dy = dataWindow.min.y;

    pixels.resizeErase (dw * dh);
	in.setFrameBuffer (pixels - dx - dy * dw, 1, dw);

	if (ilTexImage(dw, dh, 1, 4, IL_RGBA, IL_FLOAT, NULL) == IL_FALSE)
		return IL_FALSE;

	// Determine where the origin is in the original file.
	if (in.lineOrder() == INCREASING_Y)
		iCurImage->Origin = IL_ORIGIN_UPPER_LEFT;
	else
		iCurImage->Origin = IL_ORIGIN_LOWER_LEFT;

	// Better to access FloatData instead of recasting everytime.
	FloatData = (ILfloat*)iCurImage->Data;

	for (int i = 0; i < dw * dh; i++)
	{
		// Too much data lost

		// The images look kind of washed out with this.

		// This gives the best results, since no data is lost.
		FloatData[i * 4]     = pixels[i].r;
		FloatData[i * 4 + 1] = pixels[i].g;
		FloatData[i * 4 + 2] = pixels[i].b;
		FloatData[i * 4 + 3] = pixels[i].a;
	}

	// Converts the image to predefined type, format and/or origin if needed.
	return ilFixImage();
}

// Nothing to do here in the constructor.
ilOStream::ilOStream() : Imf::OStream("N/A")
{
	return;
}

void ilOStream::write(const char c[], int n)
{
	iwrite(c, 1, n);  //@TODO: Throw an exception here.
	return;
}

//@TODO: Make this work with 64-bit values.
Imf::Int64 ilOStream::tellp()
{
	Imf::Int64 Pos;

	// itellw only returns a 32-bit value!
	Pos = itellw();

	return Pos;
}

// Note that there is no return value here, even though there probably should be.
//@TODO: Make this work with 64-bit values.
void ilOStream::seekp(Imf::Int64 Pos)
{
	// iseekw only uses a 32-bit value!
	iseekw((ILint)Pos, IL_SEEK_SET);  // I am assuming this is seeking from the beginning.
	return;
}

//! Writes a Exr file
ILboolean ilSaveExr(const ILstring FileName)
{
	ILHANDLE	ExrFile;
	ILuint		ExrSize;

	if (ilGetBoolean(IL_FILE_MODE) == IL_FALSE) {
		if (iFileExists(FileName)) {
			ilSetError(IL_FILE_ALREADY_EXISTS);
			return IL_FALSE;
		}
	}

	ExrFile = iopenw(FileName);
	if (ExrFile == NULL) {
		ilSetError(IL_COULD_NOT_OPEN_FILE);
		return IL_FALSE;
	}

	ExrSize = ilSaveExrF(ExrFile);
	iclosew(ExrFile);

	if (ExrSize == 0)
		return IL_FALSE;
	return IL_TRUE;
}

//! Writes a Exr to an already-opened file
ILuint ilSaveExrF(ILHANDLE File)
{
	ILuint Pos;
	iSetOutputFile(File);
	Pos = itellw();
	if (iSaveExrInternal() == IL_FALSE)
		return 0;  // Error occurred
	return itellw() - Pos;  // Return the number of bytes written.
}

//! Writes a Exr to a memory "lump"
ILuint ilSaveExrL(void *Lump, ILuint Size)
{
	ILuint Pos = itellw();
	iSetOutputLump(Lump, Size);
	if (iSaveExrInternal() == IL_FALSE)
		return 0;  // Error occurred
	return itellw() - Pos;  // Return the number of bytes written.
}

ILboolean iSaveExrInternal()
{
	Imath::Box2i DataWindow(Imath::V2i(0, 0), Imath::V2i(iCurImage->Width-1, iCurImage->Height-1));
	Imf::LineOrder Order;
	if (iCurImage->Origin == IL_ORIGIN_LOWER_LEFT)
		Order = DECREASING_Y;
	else
		Order = INCREASING_Y;
	Imf::Header Head(iCurImage->Width, iCurImage->Height, DataWindow, 1, Imath::V2f (0, 0), 1, Order);

	ilOStream File;
	Imf::RgbaOutputFile Out(File, Head);
	ILimage *TempImage = iCurImage;

	//@TODO: Can we always assume that Rgba is packed the same?
	Rgba *HalfData = (Rgba*)ialloc(TempImage->Width * TempImage->Height * sizeof(Rgba));
	if (HalfData == NULL)
		return IL_FALSE;

	if (iCurImage->Format != IL_RGBA || iCurImage->Type != IL_FLOAT) {
		TempImage = iConvertImage(iCurImage, IL_RGBA, IL_FLOAT);
		if (TempImage == NULL) {
			ifree(HalfData);
			return IL_FALSE;
		}
	}

	ILuint Offset = 0;
	ILfloat *FloatPtr = (ILfloat*)TempImage->Data;
	for (unsigned int y = 0; y < TempImage->Height; y++) {
		for (unsigned int x = 0; x < TempImage->Width; x++) {
			HalfData[y * TempImage->Width + x].r = FloatPtr[Offset];
			HalfData[y * TempImage->Width + x].g = FloatPtr[Offset + 1];
			HalfData[y * TempImage->Width + x].b = FloatPtr[Offset + 2];
			HalfData[y * TempImage->Width + x].a = FloatPtr[Offset + 3];
			Offset += 4;  // 4 floats
		}
	}

	Out.setFrameBuffer(HalfData, 1, TempImage->Width);
	Out.writePixels(TempImage->Height);  //@TODO: Do each scanline separately to keep from using so much memory.

	// Free our half data.
	ifree(HalfData);
	// Destroy our temporary image if we used one.
	if (TempImage != iCurImage)
		ilCloseImage(TempImage);

	return IL_TRUE;
}

#endif //IL_NO_EXR