/****************************************************************************
**
** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
** All rights reserved.
**
** Contact: Nokia Corporation (qt-info@nokia.com)
**
** This file is part of a Qt Solutions component.
**
** You may use this file under the terms of the BSD license as follows:
**
** "Redistribution and use in source and binary forms, with or without
** modification, are permitted provided that the following conditions are
** met:
**   * Redistributions of source code must retain the above copyright
**     notice, this list of conditions and the following disclaimer.
**   * Redistributions in binary form must reproduce the above copyright
**     notice, this list of conditions and the following disclaimer in
**     the documentation and/or other materials provided with the
**     distribution.
**   * Neither the name of Nokia Corporation and its Subsidiary(-ies) nor
**     the names of its contributors may be used to endorse or promote
**     products derived from this software without specific prior written
**     permission.
**
** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
**
****************************************************************************/


#ifndef QTPROPERTYBROWSER_H
#define QTPROPERTYBROWSER_H

#include <QtWidgets/QWidget>
#include <QSet>
#include <QtWidgets/QLineEdit>

#if QT_VERSION >= 0x040400
QT_BEGIN_NAMESPACE
#endif

#if defined(_WIN32)
#    if defined(COMPILE_QTPROPERTYBROWSER)
#		define QT_QTPROPERTYBROWSER_EXPORT __declspec(dllexport)
#    else
#		define QT_QTPROPERTYBROWSER_EXPORT __declspec(dllimport)
#    endif
#else
#define QT_QTPROPERTYBROWSER_EXPORT
#endif

typedef QLineEdit::EchoMode EchoMode;

class QtAbstractPropertyManager;
class QtPropertyPrivate;

/**
 * @ingroup group_sdk_libraries_qtpropertybrowser
 *
    \class QtProperty

    \brief The QtProperty class encapsulates an instance of a property.

    Properties are created by objects of QtAbstractPropertyManager
    subclasses; a manager can create properties of a given type, and
    is used in conjunction with the QtAbstractPropertyBrowser class. A
    property is always owned by the manager that created it, which can
    be retrieved using the propertyManager() function.

    QtProperty contains the most common property attributes, and
    provides functions for retrieving as well as setting their values:

    Getter         |     Setter
    -------------- | -----------------
    propertyName() | setPropertyName()
    statusTip()    | setStatusTip()
    toolTip()      | setToolTip()
    whatsThis()    | setWhatsThis()
    isEnabled()    | setEnabled()
    isModified()   | setModified()
    valueText()    | Nop
    valueIcon()    | Nop

    It is also possible to nest properties: QtProperty provides the
    addSubProperty(), insertSubProperty() and removeSubProperty() functions to
    manipulate the set of subproperties. Use the subProperties()
    function to retrieve a property's current set of subproperties.
    Note that nested properties are not owned by the parent property,
    i.e. each subproperty is owned by the manager that created it.

    \sa QtAbstractPropertyManager, QtBrowserItem
*
 **/
class QT_QTPROPERTYBROWSER_EXPORT QtProperty {
public:
    virtual ~QtProperty();

    QList<QtProperty*> subProperties() const;

    QtAbstractPropertyManager* propertyManager() const;

    QString toolTip() const;
    QString statusTip() const;
    QString whatsThis() const;
    QString propertyName() const;
    bool isEnabled() const;
    bool isModified() const;

    bool hasValue() const;
    QIcon valueIcon() const;
    QString valueText() const;
    QString displayText() const;

    void setToolTip(const QString& text);
    void setStatusTip(const QString& text);
    void setWhatsThis(const QString& text);
    void setPropertyName(const QString& text);
    void setEnabled(bool enable);
    void setModified(bool modified);

    void addSubProperty(QtProperty* property);
    void insertSubProperty(QtProperty* property, QtProperty* afterProperty);
    void removeSubProperty(QtProperty* property);
protected:
    explicit QtProperty(QtAbstractPropertyManager* manager);
    void propertyChanged();
private:
    friend class QtAbstractPropertyManager;
    QtPropertyPrivate* d_ptr;
};

class QtAbstractPropertyManagerPrivate;

class QT_QTPROPERTYBROWSER_EXPORT QtAbstractPropertyManager : public QObject {
    Q_OBJECT
public:

    explicit QtAbstractPropertyManager(QObject* parent = nullptr);
    ~QtAbstractPropertyManager() override;

    QSet<QtProperty*> properties() const;
    void clear() const;

    QtProperty* addProperty(const QString& name = QString());
Q_SIGNALS:

    void propertyInserted(QtProperty* property,
                          QtProperty* parent, QtProperty* after);
    void propertyChanged(QtProperty* property);
    void propertyRemoved(QtProperty* property, QtProperty* parent);
    void propertyDestroyed(QtProperty* property);
protected:
    virtual bool hasValue(const QtProperty* property) const;
    virtual QIcon valueIcon(const QtProperty* property) const;
    virtual QString valueText(const QtProperty* property) const;
    virtual QString displayText(const QtProperty* property) const;
    virtual EchoMode echoMode(const QtProperty*) const;
    virtual void initializeProperty(QtProperty* property) = 0;
    virtual void uninitializeProperty(QtProperty* property);
    virtual QtProperty* createProperty();
private:
    friend class QtProperty;
    QtAbstractPropertyManagerPrivate* d_ptr;
    Q_DECLARE_PRIVATE(QtAbstractPropertyManager)
#if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0)
    Q_DISABLE_COPY_MOVE(QtAbstractPropertyManager)
#else
    Q_DISABLE_COPY(QtAbstractPropertyManager)
#endif
};

class QT_QTPROPERTYBROWSER_EXPORT QtAbstractEditorFactoryBase : public QObject {
    Q_OBJECT
public:
    virtual QWidget* createEditor(QtProperty* property, QWidget* parent) = 0;
protected:
    explicit QtAbstractEditorFactoryBase(QObject* parent = nullptr)
        : QObject(parent) {}

    virtual void breakConnection(QtAbstractPropertyManager* manager) = 0;
protected Q_SLOTS:
    virtual void managerDestroyed(QObject* manager) = 0;

    friend class QtAbstractPropertyBrowser;
};

template <class PropertyManager>
class QtAbstractEditorFactory : public QtAbstractEditorFactoryBase {
public:
    explicit QtAbstractEditorFactory(QObject* parent) : QtAbstractEditorFactoryBase(parent) {}
    QWidget* createEditor(QtProperty* property, QWidget* parent) override {
        QSetIterator<PropertyManager*> it(m_managers);
        while (it.hasNext()) {
            PropertyManager* manager = it.next();
            if (manager == property->propertyManager()) {
                return createEditor(manager, property, parent);
            }
        }
        return nullptr;
    }
    void addPropertyManager(PropertyManager* manager) {
        if (m_managers.contains(manager)) {
            return;
        }
        m_managers.insert(manager);
        connectPropertyManager(manager);
        connect(manager, SIGNAL(destroyed(QObject*)),
                this, SLOT(managerDestroyed(QObject*)));
    }
    void removePropertyManager(PropertyManager* manager) {
        if (!m_managers.contains(manager)) {
            return;
        }
        disconnect(manager, SIGNAL(destroyed(QObject*)),
                   this, SLOT(managerDestroyed(QObject*)));
        disconnectPropertyManager(manager);
        m_managers.remove(manager);
    }
    QSet<PropertyManager*> propertyManagers() const {
        return m_managers;
    }
    PropertyManager* propertyManager(QtProperty* property) const {
        QtAbstractPropertyManager* manager = property->propertyManager();
        QSetIterator<PropertyManager*> itManager(m_managers);
        while (itManager.hasNext()) {
            PropertyManager* m = itManager.next();
            if (m == manager) {
                return m;
            }
        }
        return 0;
    }
protected:
    virtual void connectPropertyManager(PropertyManager* manager) = 0;
    virtual QWidget* createEditor(PropertyManager* manager, QtProperty* property,
                                  QWidget* parent) = 0;
    virtual void disconnectPropertyManager(PropertyManager* manager) = 0;
    void managerDestroyed(QObject* manager) override {
        QSetIterator<PropertyManager*> it(m_managers);
        while (it.hasNext()) {
            PropertyManager* m = it.next();
            if (m == manager) {
                m_managers.remove(m);
                return;
            }
        }
    }
private:
    void breakConnection(QtAbstractPropertyManager* manager) override {
        QSetIterator<PropertyManager*> it(m_managers);
        while (it.hasNext()) {
            PropertyManager* m = it.next();
            if (m == manager) {
                removePropertyManager(m);
                return;
            }
        }
    }
private:
    QSet<PropertyManager*> m_managers;
    friend class QtAbstractPropertyEditor;
};

class QtAbstractPropertyBrowser;
class QtBrowserItemPrivate;

class QT_QTPROPERTYBROWSER_EXPORT QtBrowserItem {
public:
    QtProperty* property() const;
    QtBrowserItem* parent() const;
    QList<QtBrowserItem*> children() const;
    QtAbstractPropertyBrowser* browser() const;
private:
    explicit QtBrowserItem(QtAbstractPropertyBrowser* browser, QtProperty* property, QtBrowserItem* parent);
    ~QtBrowserItem();
    QtBrowserItemPrivate* d_ptr;
    friend class QtAbstractPropertyBrowserPrivate;
};

class QtAbstractPropertyBrowserPrivate;

class QT_QTPROPERTYBROWSER_EXPORT QtAbstractPropertyBrowser : public QWidget {
    Q_OBJECT
public:

    explicit QtAbstractPropertyBrowser(QWidget* parent = nullptr);
    ~QtAbstractPropertyBrowser() override;

    QList<QtProperty*> properties() const;
    QList<QtBrowserItem*> items(QtProperty* property) const;
    QtBrowserItem* topLevelItem(QtProperty* property) const;
    QList<QtBrowserItem*> topLevelItems() const;
    void clear();

    template <class PropertyManager>
    void setFactoryForManager(PropertyManager* manager,
                              QtAbstractEditorFactory<PropertyManager>* factory) {
        QtAbstractPropertyManager* abstractManager = manager;
        QtAbstractEditorFactoryBase* abstractFactory = factory;

        if (addFactory(abstractManager, abstractFactory)) {
            factory->addPropertyManager(manager);
        }
    }

    void unsetFactoryForManager(QtAbstractPropertyManager* manager);

    QtBrowserItem* currentItem() const;
    void setCurrentItem(QtBrowserItem*);

Q_SIGNALS:
    void currentItemChanged(QtBrowserItem*);

public Q_SLOTS:

    QtBrowserItem* addProperty(QtProperty* property);
    QtBrowserItem* insertProperty(QtProperty* property, QtProperty* afterProperty);
    void removeProperty(QtProperty* property);

protected:

    virtual void itemInserted(QtBrowserItem* item, QtBrowserItem* afterItem) = 0;
    virtual void itemRemoved(QtBrowserItem* item) = 0;
    // can be tooltip, statustip, whatsthis, name, icon, text.
    virtual void itemChanged(QtBrowserItem* item) = 0;

    virtual QWidget* createEditor(QtProperty* property, QWidget* parent);
private:

    bool addFactory(QtAbstractPropertyManager* abstractManager,
                    QtAbstractEditorFactoryBase* abstractFactory);

    QtAbstractPropertyBrowserPrivate* d_ptr;
    Q_DECLARE_PRIVATE(QtAbstractPropertyBrowser)
#if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0)
    Q_DISABLE_COPY_MOVE(QtAbstractPropertyBrowser)
#else
    Q_DISABLE_COPY(QtAbstractPropertyBrowser)
#endif
    Q_PRIVATE_SLOT(d_func(), void slotPropertyInserted(QtProperty*,
                   QtProperty*, QtProperty*))
    Q_PRIVATE_SLOT(d_func(), void slotPropertyRemoved(QtProperty*,
                   QtProperty*))
    Q_PRIVATE_SLOT(d_func(), void slotPropertyDestroyed(QtProperty*))
    Q_PRIVATE_SLOT(d_func(), void slotPropertyDataChanged(QtProperty*))

};

#if QT_VERSION >= 0x040400
QT_END_NAMESPACE
#endif

#endif // QTPROPERTYBROWSER_H
