/***************************************************************************
 *   Copyright (c) 2004 Werner Mayer <wmayer[at]users.sourceforge.net>     *
 *                                                                         *
 *   This file is part of the FreeCAD CAx development system.              *
 *                                                                         *
 *   This library is free software; you can redistribute it and/or         *
 *   modify it under the terms of the GNU Library General Public           *
 *   License as published by the Free Software Foundation; either          *
 *   version 2 of the License, or (at your option) any later version.      *
 *                                                                         *
 *   This library  is distributed in the hope that it will be useful,      *
 *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
 *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
 *   GNU Library General Public License for more details.                  *
 *                                                                         *
 *   You should have received a copy of the GNU Library General Public     *
 *   License along with this library; see the file COPYING.LIB. If not,    *
 *   write to the Free Software Foundation, Inc., 59 Temple Place,         *
 *   Suite 330, Boston, MA  02111-1307, USA                                *
 *                                                                         *
 ***************************************************************************/


#ifndef GUI_WIDGETFACTORY_H
#define GUI_WIDGETFACTORY_H

#include <vector>
#include <QUiLoader>

#include <Base/Factory.h>
#include <Base/PyObjectBase.h>
#include "DlgPreferencesImp.h"
#include "DlgCustomizeImp.h"
#include "PropertyPage.h"

namespace Gui {
  namespace Dialog{
    class PreferencePage;
  }
/** 
 * The widget factory provides methods for the dynamic creation of widgets.
 * To create these widgets once they must be registered to the factory.
 * To register them use WidgetProducer or any subclasses; to register a
 * preference page use PrefPageProducer instead.
 * \author Werner Mayer
 */
class GuiExport WidgetFactoryInst : public Base::Factory
{
public:
    static WidgetFactoryInst& instance();
    static void destruct ();

    QWidget* createWidget (const char* sName, QWidget* parent=0) const;
    Gui::Dialog::PreferencePage* createPreferencePage (const char* sName, QWidget* parent=0) const;
    QWidget* createPrefWidget(const char* sName, QWidget* parent, const char* sPref);

private:
    static WidgetFactoryInst* _pcSingleton;

    WidgetFactoryInst(){}
    ~WidgetFactoryInst(){}
};

inline WidgetFactoryInst& WidgetFactory()
{
    return WidgetFactoryInst::instance();
}

// --------------------------------------------------------------------

/**
 * The UiLoader class provides the abitlity to use the widget factory 
 * framework of FreeCAD within the framework provided by Qt. This class
 * extends QUiLoader by the creation of FreeCAD specific widgets.
 * @author Werner Mayer
 */
class UiLoader : public QUiLoader
{
public:
    UiLoader(QObject* parent=0);
    virtual ~UiLoader();

    /**
     * Creates a widget of the type \a className withe the parent \a parent.
     * Fore more details see the documentation to QWidgetFactory.
     */
    QWidget* createWidget(const QString & className, QWidget * parent=0, 
                          const QString& name =QString());
private:
    QStringList cw;
};

// --------------------------------------------------------------------

/**
 * The WidgetProducer class is a value-based template class that provides 
 * the ability to create widgets dynamically. 
 * \author Werner Mayer
 */
template <class CLASS>
class WidgetProducer : public Base::AbstractProducer
{
public:
    /** 
     * Register a special type of widget to the WidgetFactoryInst.
     */
    WidgetProducer ()
    {
        const char* cname = CLASS::staticMetaObject.className();
        WidgetFactoryInst::instance().AddProducer(cname, this);
    }

    virtual ~WidgetProducer (){}

    /** 
     * Creates an instance of the specified widget.
     */
    virtual void* Produce () const
    {
        return (void*)(new CLASS);
    }
};

// --------------------------------------------------------------------

/**
 * The PrefPageProducer class is a value-based template class that provides 
 * the ability to create preference pages dynamically. 
 * \author Werner Mayer
 */
template <class CLASS>
class PrefPageProducer : public Base::AbstractProducer
{
public:
    /** 
     * Register a special type of preference page to the WidgetFactoryInst.
     */
    PrefPageProducer (const char* group)
    {
        const char* cname = CLASS::staticMetaObject.className();
        if (strcmp(cname, Gui::Dialog::PreferencePage::staticMetaObject.className()) == 0)
            qWarning("The class '%s' lacks of Q_OBJECT macro", typeid(CLASS).name());
        if (WidgetFactoryInst::instance().CanProduce(cname)) {
            qWarning("The preference page class '%s' is already registered", cname);
        }
        else {
            WidgetFactoryInst::instance().AddProducer(cname, this);
            Gui::Dialog::DlgPreferencesImp::addPage(cname, group);
        }
    }

    virtual ~PrefPageProducer (){}

    /**
     * Creates an instance of the specified widget.
     */
    virtual void* Produce () const
    {
        return (void*)(new CLASS);
    }
};

/**
 * The PrefPageUiProducer class provides the ability to create preference pages
 * dynamically from an external UI file.
 * @author Werner Mayer
 */
class GuiExport PrefPageUiProducer : public Base::AbstractProducer
{
public:
    /** 
     * Register a special type of preference page to the WidgetFactoryInst.
     */
    PrefPageUiProducer (const char* filename, const char* group);
    virtual ~PrefPageUiProducer ();
    /**
     * Creates an instance of the specified widget.
     */
    virtual void* Produce () const;

private:
    QString fn;
};

// --------------------------------------------------------------------

/**
 * The CustomPageProducer class is a value-based template class that provides 
 * the ability to create custom pages dynamically. 
 * \author Werner Mayer
 */
template <class CLASS>
class CustomPageProducer : public Base::AbstractProducer
{
public:
    /** 
     * Register a special type of customize page to the WidgetFactoryInst.
     */
    CustomPageProducer ()
    {
        const char* cname = CLASS::staticMetaObject.className();
        if (strcmp(cname, Gui::Dialog::CustomizeActionPage::staticMetaObject.className()) == 0)
            qWarning("The class '%s' lacks of Q_OBJECT macro", typeid(CLASS).name());
        if (WidgetFactoryInst::instance().CanProduce(cname)) {
            qWarning("The preference page class '%s' is already registered", cname);
        }
        else {
            WidgetFactoryInst::instance().AddProducer(cname, this);
            Gui::Dialog::DlgCustomizeImp::addPage(cname);
        }
    }

    virtual ~CustomPageProducer (){}

    /** 
     * Creates an instance of the specified widget.
     */
    virtual void* Produce () const
    {
        return (void*)(new CLASS);
    }
};

// --------------------------------------------------------------------

/**
 * The widget factory supplier class registers all kinds of
 * preference pages and widgets.
 * \author Werner Mayer
 */
class WidgetFactorySupplier
{
private:
    // Singleton
    WidgetFactorySupplier();
    static WidgetFactorySupplier *_pcSingleton;

public:
    static WidgetFactorySupplier &instance();
    static void destruct();
    friend WidgetFactorySupplier &GetWidgetFactorySupplier();
};

inline WidgetFactorySupplier &GetWidgetFactorySupplier()
{
    return WidgetFactorySupplier::instance();
}

// ----------------------------------------------------

/**
 * The ContainerDialog class acts as a container to embed any kinds of widgets that
 * do not inherit from QDialog. This class also provides an "Ok" and a "Cancel" button.
 * At most this class is used to embed widgets which are created from .ui files.
 * \author Werner Mayer
 */
class ContainerDialog : public QDialog
{
    Q_OBJECT

public:
    ContainerDialog( QWidget* templChild );
    ~ContainerDialog();

    QPushButton* buttonOk; /**< The Ok button. */
    QPushButton* buttonCancel; /**< The cancel button. */

private:
    QGridLayout* MyDialogLayout;
};

// ----------------------------------------------------

/**
 * The PyResource class provides an interface to create widgets or to load .ui files from Python.
 * With
 * \code
 * d = Gui.CreateDialog("test.ui")
 * \endcode
 *
 * you can create a PyResource object containing the widget. If a relative file name 
 * is given PyResource looks first in the current working directory and afterwards in 
 * the home path where FreeCAD resides.
 * 
 * If the appropriate .ui file cannot be found or creation fails an exception is thrown.
 * In case the widget in the .ui file does not inherit from QDialog it is embedded in a
 * \ref ContainerDialog object.
 * To show the widget you can call
 * \code
 * d.Show()
 * \endcode
 *
 * Furthermore it is possible to get or set values from any widgets inside
 * the parent widget or to connect a Python callback function with a widget.
 * \remark The callback function must have exactly one parameter. This parameter
 * points to the dialog you have just created.
 * \code
 * # define a callback function with one argument
 * def TestCall(obj):
 *      # sets the value from lineedit if "Button_Name" was pressed
 *      obj.SetValue("lineedit", "text", "Show this text here!")
 *      print "Button clicked"
 *
 * d = Gui.CreateDialog("test.ui")
 * d.Connect("Button_Name", "clicked()", TestCall)
 * d.Show()
 * \endcode
 *
 * If the button with the name "Button_Name" is clicked the message "Button clicked" is
 * printed.
 * For example if you have a QLineEdit inside your widget you can set the text with
 * \code
 * # sets "Show this text here!" to the text property
 * d.SetValue("lineedit", "text", "Show this text here!")
 * d.Show()
 * \endcode
 *
 * or retrieve the entered text with
 * \code
 * f = d.GetValue("lineedit", "text")
 * print f
 * \endcode
 *
 * \author Werner Mayer
 */
class PyResource : public Base::PyObjectBase
{
    // always start with Py_Header
    Py_Header;

protected:
    ~PyResource();

public:
    PyResource(PyTypeObject *T = &Type);

    void load(const char* name);
    bool connect(const char* sender, const char* signal, PyObject* cb);

    /// for construction in Python
    static PyObject *PyMake(PyObject *, PyObject *);

    //---------------------------------------------------------------------
    // python exports goes here +++++++++++++++++++++++++++++++++++++++++++
    //---------------------------------------------------------------------
    PyObject *_getattr(char *attr);             // __getattr__ function
    // getter setter
    int _setattr(char *attr, PyObject *value);  // __setattr__ function

    // methods
    PYFUNCDEF_D(PyResource, value);
    PYFUNCDEF_D(PyResource, setValue);
    PYFUNCDEF_D(PyResource, show);
    PYFUNCDEF_D(PyResource, connect);

private:
    std::vector<class SignalConnect*> mySingals;
    QDialog* myDlg;
};

/**
 * The SignalConnect class provides the abitlity to make a connection
 * between the callback function of a Python object and the slot onExecute().
 * This mechanism is used in the Python/Qt framework.
 * \author Werner Mayer
 */
class SignalConnect : public QObject
{
    Q_OBJECT

public:
    SignalConnect( Base::PyObjectBase* res, PyObject* cb, QObject* sender);
    ~SignalConnect();

public Q_SLOTS:
    void onExecute();

private:
    PyObject* myResource;
    PyObject* myCallback;
    QObject*  mySender;
};

} // namespace Gui

#endif // GUI_WIDGETFACTORY_H
