Switch to side-by-side view

--- a
+++ b/cfgui/confgui.h
@@ -0,0 +1,435 @@
+/* Copyright (C) 2007-2018 J.F.Dockes
+ *   This program is free software; you can redistribute it and/or modify
+ *   it under the terms of the GNU General Public License as published by
+ *   the Free Software Foundation; either version 2 of the License, or
+ *   (at your option) any later version.
+ *
+ *   This program 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 General Public License for more details.
+ *
+ *   You should have received a copy of the GNU General Public License
+ *   along with this program; if not, write to the
+ *   Free Software Foundation, Inc.,
+ *   59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
+ */
+#ifndef _confgui_h_included_
+#define _confgui_h_included_
+/**
+ * This file defines a number of simple classes (virtual base: ConfParamW)
+ * which let the user input configuration parameters.
+ *
+ * Subclasses are defined for entering different kind of data, ie a string,
+ * a file name, an integer, etc.
+ *
+ * Each configuration gui object is linked to the configuration data through
+ * a "link" object which knows the details of interacting with the actual
+ * configuration data, like the parameter name, the actual config object,
+ * the method to call etc.
+ *
+ * The link object is set when the input widget is created and cannot be
+ * changed.
+ *
+ * The widgets are typically linked to a temporary configuration object, which
+ * is then copied to the actual configuration if the data is accepted, or
+ * destroyed and recreated as a copy if Cancel is pressed (you have to
+ * delete/recreate the widgets in this case as the links are no longer valid).
+ *
+ * The set() methods of the link objects are only called if the
+ * current value() differs from the value obtained by get() when the
+ * object was initialized. This can be used to avoid cluttering the
+ * output with values which are unmodified from the defaults.
+ *
+ * The file also defines a multi-tabbed dialog container for the
+ * parameter objects, with simple interface methods to create/add
+ * panels and elements.
+ */
+
+#include <string>
+#include <limits.h>
+
+#include <memory>
+#include <vector>
+#include <string>
+
+#include <QString>
+#include <QWidget>
+#include <QDialog>
+
+class ConfNull;
+class QCheckBox;
+class QComboBox;
+class QDialogButtonBox;
+class QHBoxLayout;
+class QLineEdit;
+class QListWidget;
+class QPushButton;
+class QSpinBox;
+class QTabWidget;
+class QVBoxLayout;
+
+namespace confgui {
+
+/** Interface between the GUI widget and the config storage mechanism: */
+class ConfLinkRep {
+public:
+    virtual ~ConfLinkRep() {}
+    virtual bool set(const std::string& val) = 0;
+    virtual bool get(std::string& val) = 0;
+};
+typedef std::shared_ptr<ConfLinkRep> ConfLink;
+
+/** Link maker class */
+class ConfLinkFact {
+public:
+    virtual ~ConfLinkFact() {}
+    virtual ConfLink operator()(const QString& nm) = 0;
+};
+
+class ConfPanelWIF {
+public:
+    virtual ~ConfPanelWIF() {}
+    virtual void storeValues() = 0;
+    virtual void loadValues() = 0;
+};
+
+class ConfPanelW;
+class ConfParamW;
+
+/** The top level widget has tabs, each tab/panel has multiple widgets
+ *  for setting parameter values */
+class ConfTabsW : public QDialog {
+    Q_OBJECT;
+
+public:
+    ConfTabsW(QWidget *parent, const QString& title, ConfLinkFact *linkfact);
+
+    enum ParamType {CFPT_BOOL, CFPT_INT, CFPT_STR, CFPT_CSTR, CFPT_FN,
+                    CFPT_STRL, CFPT_DNL, CFPT_CSTRL
+    };
+
+    /** Add tab and return its identifier / index */
+    int addPanel(const QString& title);
+
+    /** Add foreign tab where we only know to call loadvalues/storevalues.
+     * The object has to derive from QWidget */
+    int addForeignPanel(ConfPanelWIF* w, const QString& title);
+
+    /** Add parameter setter to specified tab */
+    ConfParamW *addParam(int tabindex, ParamType tp,
+                         const QString& varname, const QString& label,
+                         const QString& tooltip, int isdirorminval = 0,
+                         int maxval = 0, const QStringList* sl = 0);
+    bool enableLink(ConfParamW* boolw, ConfParamW* otherw, bool revert = false);
+    void endOfList(int tabindex);
+
+    /** Find param widget associated with given variable name */
+    ConfParamW *findParamW(const QString& varname);
+
+    void hideButtons();
+                      
+public slots:
+    void acceptChanges();
+    void rejectChanges();
+    void reloadPanels();
+    void setCurrentIndex(int);
+    
+signals:
+    /** This is emitted when acceptChanges() is called, after the
+     *  values have been stored */
+    void sig_prefsChanged();
+
+private:
+    ConfLinkFact *m_makelink{nullptr};
+    std::vector<ConfPanelW *> m_panels;
+    // "Foreign" panels
+    std::vector<ConfPanelWIF *> m_widgets;
+    // All params
+    std::vector<ConfParamW *> m_params;
+    QTabWidget       *tabWidget{nullptr};
+    QDialogButtonBox *buttonBox{nullptr};
+};
+
+/////////////////////////////////////////////////
+// The rest of the class definitions are only useful if you need to
+// access a specific element for customisation (use findParamW() and a
+// dynamic cast).
+
+/** A panel/tab contains multiple controls for parameters */
+class ConfPanelW : public QWidget {
+    Q_OBJECT
+public:
+    ConfPanelW(QWidget *parent);
+    void addWidget(QWidget *w);
+    void storeValues();
+    void loadValues();
+    void endOfList();
+private:
+    QVBoxLayout *m_vboxlayout;
+    std::vector<QWidget *> m_widgets;
+};
+
+/** Config panel element: manages one configuration
+ *  parameter. Subclassed for specific parameter types.
+ */
+class ConfParamW : public QWidget {
+    Q_OBJECT
+public:
+    ConfParamW(const QString& varnm, QWidget *parent, ConfLink cflink)
+        : QWidget(parent), m_varname(varnm),
+          m_cflink(cflink), m_fsencoding(false) {
+    }
+    virtual void loadValue() = 0;
+    virtual void setFsEncoding(bool onoff) {
+        m_fsencoding = onoff;
+    }
+    const QString& getVarName() {
+        return m_varname;
+    }
+public slots:
+    virtual void setEnabled(bool) = 0;
+    virtual void storeValue() = 0;
+
+protected:
+    QString      m_varname;
+    ConfLink     m_cflink;
+    QHBoxLayout *m_hl;
+    // File names are encoded as local8bit in the config files. Other
+    // are encoded as utf-8
+    bool         m_fsencoding;
+    virtual bool createCommon(const QString& lbltxt, const QString& tltptxt);
+    void setValue(const QString& newvalue);
+    void setValue(int newvalue);
+    void setValue(bool newvalue);
+};
+
+//////// Widgets for setting the different types of configuration parameters:
+
+/**  Boolean */
+class ConfParamBoolW : public ConfParamW {
+    Q_OBJECT
+public:
+    ConfParamBoolW(const QString& varnm, QWidget *parent, ConfLink cflink,
+                   const QString& lbltxt,
+                   const QString& tltptxt, bool deflt = false);
+    virtual void loadValue();
+    virtual void storeValue();
+public slots:
+    virtual void setEnabled(bool i) {
+        if (m_cb) {
+            ((QWidget*)m_cb)->setEnabled(i);
+        }
+    }
+public:
+    QCheckBox *m_cb;
+    bool m_dflt;
+    bool m_origvalue;
+};
+
+// Int
+class ConfParamIntW : public ConfParamW {
+    Q_OBJECT
+public:
+    // The default value is only used if none exists in the sample
+    // configuration file. Defaults are normally set in there.
+    ConfParamIntW(const QString& varnm, QWidget *parent, ConfLink cflink,
+                  const QString& lbltxt,
+                  const QString& tltptxt,
+                  int minvalue = INT_MIN,
+                  int maxvalue = INT_MAX,
+                  int defaultvalue = 0);
+    virtual void loadValue();
+    virtual void storeValue();
+public slots:
+    virtual void setEnabled(bool i) {
+        if (m_sb) {
+            ((QWidget*)m_sb)->setEnabled(i);
+        }
+    }
+protected:
+    QSpinBox *m_sb;
+    int       m_defaultvalue;
+    int       m_origvalue;
+};
+
+// Arbitrary string
+class ConfParamStrW : public ConfParamW {
+    Q_OBJECT
+public:
+    ConfParamStrW(const QString& varnm, QWidget *parent, ConfLink cflink,
+                  const QString& lbltxt,
+                  const QString& tltptxt);
+    virtual void loadValue();
+    virtual void storeValue();
+public slots:
+    virtual void setEnabled(bool i) {
+        if (m_le) {
+            ((QWidget*)m_le)->setEnabled(i);
+        }
+    }
+protected:
+    QLineEdit *m_le;
+    QString m_origvalue;
+};
+
+// Constrained string: choose from list
+class ConfParamCStrW : public ConfParamW {
+    Q_OBJECT
+public:
+    ConfParamCStrW(const QString& varnm, QWidget *parent, ConfLink cflink,
+                   const QString& lbltxt,
+                   const QString& tltptxt, const QStringList& sl);
+    virtual void loadValue();
+    virtual void storeValue();
+    virtual void setList(const QStringList& sl);
+public slots:
+    virtual void setEnabled(bool i) {
+        if (m_cmb) {
+            ((QWidget*)m_cmb)->setEnabled(i);
+        }
+    }
+protected:
+    QComboBox *m_cmb;
+    QString m_origvalue;
+};
+
+// File name
+class ConfParamFNW : public ConfParamW {
+    Q_OBJECT
+public:
+    ConfParamFNW(const QString& varnm, QWidget *parent, ConfLink cflink,
+                 const QString& lbltxt,
+                 const QString& tltptxt, bool isdir = false);
+    virtual void loadValue();
+    virtual void storeValue();
+protected slots:
+    void showBrowserDialog();
+public slots:
+    virtual void setEnabled(bool i) {
+        if (m_le) {
+            ((QWidget*)m_le)->setEnabled(i);
+        }
+        if (m_pb) {
+            ((QWidget*)m_pb)->setEnabled(i);
+        }
+    }
+protected:
+    QLineEdit *m_le;
+    QPushButton *m_pb;
+    bool       m_isdir;
+    QString m_origvalue;
+};
+
+// String list
+class ConfParamSLW : public ConfParamW {
+    Q_OBJECT
+public:
+    ConfParamSLW(const QString& varnm, QWidget *parent, ConfLink cflink,
+                 const QString& lbltxt,
+                 const QString& tltptxt);
+    virtual void loadValue();
+    virtual void storeValue();
+    QListWidget *getListBox() {
+        return m_lb;
+    }
+
+public slots:
+    virtual void setEnabled(bool i) {
+        if (m_lb) {
+            ((QWidget*)m_lb)->setEnabled(i);
+        }
+    }
+protected slots:
+    virtual void showInputDialog();
+    void deleteSelected();
+signals:
+    void entryDeleted(QString);
+protected:
+    QListWidget *m_lb;
+    void listToConf();
+    std::string m_origvalue;
+};
+
+// Dir name list
+class ConfParamDNLW : public ConfParamSLW {
+    Q_OBJECT
+public:
+    ConfParamDNLW(const QString& varnm, QWidget *parent, ConfLink cflink,
+                  const QString& lbltxt,
+                  const QString& tltptxt)
+        : ConfParamSLW(varnm, parent, cflink, lbltxt, tltptxt) {
+        m_fsencoding = true;
+    }
+protected slots:
+    virtual void showInputDialog();
+};
+
+// Constrained string list (chose from predefined)
+class ConfParamCSLW : public ConfParamSLW {
+    Q_OBJECT
+public:
+    ConfParamCSLW(const QString& varnm, QWidget *parent, ConfLink cflink,
+                  const QString& lbltxt,
+                  const QString& tltptxt,
+                  const QStringList& sl)
+        : ConfParamSLW(varnm, parent, cflink, lbltxt, tltptxt), m_sl(sl) {
+    }
+protected slots:
+    virtual void showInputDialog();
+protected:
+    const QStringList m_sl;
+};
+
+#ifdef ENABLE_XMLCONF
+/**
+ * Interpret an XML string and create a configuration interface. XML sample:
+ *
+ * <confcomments>
+ *   <filetitle>Configuration file parameters for upmpdcli</filetitle>
+ *   <grouptitle>MPD parameters</grouptitle>
+ *   <var name="mpdhost" type="string">
+ *     <brief>Host MPD runs on.</brief>
+ *     <descr>Defaults to localhost. This can also be specified as -h</descr>
+ *   </var>
+ *   mpdhost = default-host
+ *   <var name="mpdport" type="int" values="0 65635 6600">
+ *     <brief>IP port used by MPD</brief>. 
+ *     <descr>Can also be specified as -p port. Defaults to the...</descr>
+ *   </var>
+ *   mpdport = defport
+ *   <var name="ownqueue" type="bool" values="1">
+ *     <brief>Set if we own the MPD queue.</brief>
+ *     <descr>If this is set (on by default), we own the MPD...</descr>
+ *   </var>
+ *   ownqueue = 
+ * </confcomments>
+ *
+ * <grouptitle> creates a panel in which the following <var> are set.
+ * The <var> attributes should be self-explanatory. "values"
+ * is used for different things depending on the var type
+ * (min/max, default, str list). Check the code about this. 
+ * type values: "bool" "int" "string" "cstr" "cstrl" "fn" "dfn" "strl" "dnl"
+ *
+ * The XML would typically exist as comments inside a reference configuration
+ * file (ConfSimple can extract such comments).
+ *
+ * This means that the reference configuration file can generate both
+ * the documentation and the GUI interface.
+ * 
+ * @param xml the input xml
+ * @param[output] toptxt the top level XML text (text not inside <var>, 
+ *   normally commented variable assignments). This will be evaluated
+ *   as a config for default values.
+ * @lnkf factory to create the objects which link the GUI to the
+ *   storage mechanism.
+ */
+extern ConfTabsW *xmlToConfGUI(const std::string& xml,
+                               std::string& toptxt,
+                               ConfLinkFact* lnkf,
+                               QWidget *parent);
+#endif
+
+}
+
+#endif /* _confgui_h_included_ */