1 /* Copyright (c) 2015  Gerald Knizia
2  *
3  * This file is part of the IboView program (see: http://www.iboview.org)
4  *
5  * IboView is free software: you can redistribute it and/or modify
6  * it under the terms of the GNU General Public License as published by
7  * the Free Software Foundation, version 3.
8  *
9  * IboView is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with bfint (LICENSE). If not, see http://www.gnu.org/licenses/
16  *
17  * Please see IboView documentation in README.txt for:
18  * -- A list of included external software and their licenses. The included
19  *    external software's copyright is not touched by this agreement.
20  * -- Notes on re-distribution and contributions to/further development of
21  *    the IboView software
22  */
23 
24 // QPropertyModel
25 // - a class for easily turning any QObject-derived subclass with properties into a one-row model
26 //
27 // Copyright 2013 - Harvey Chapman <hchapman@3gfp.com>
28 // Source: https://gist.github.com/sr105/7955969
29 // License:
30 //   This work is licensed under the Creative Commons Attribution-ShareAlike
31 //   4.0 International License. To view a copy of this license, visit
32 //   http://creativecommons.org/licenses/by-sa/4.0/deed.en_US.
33 //
34 // It's not required, but I'd appreciate it if any improvements were e-mailed
35 // back to me so I can share them with others. This code is specifically not
36 // GPL-like so you can use it commercially without worrying about it tainting
37 // the rest of your proprietary code.
38 // -- Harvey
39 
40 // Notes by cgk:
41 // - Taken from https://gist.github.com/sr105/7955969
42 // - See also: http://stackoverflow.com/questions/18793735/connect-a-signal-to-the-slot-of-a-qmetaproperty
43 //   (which is what I originally wanted to do, but failed to find a viable way, just as the
44 //    author of that question)
45 // - Various slight changes (in particular, un-staticify the property name list/map)
46 
47 #ifndef QPROPERTYMODEL_H
48 #define QPROPERTYMODEL_H
49 
50 #include <QAbstractItemModel>
51 #include <QStringList>
52 #include <QDataWidgetMapper>
53 #include <QMap>
54 #include <QMetaProperty>
55 
56 void LinkPropertyWidgets(QObject *pTarget, QWidget *pWidgetContainer, char const *pPropertyKeyName);
57 
58 class QPropertyModel;
59 
60 // Convenience class that exposes the public methods of QPropertyModel
61 // without requiring casting.
62 class QPropertyDataWidgetMapper : public QDataWidgetMapper
63 {
64     Q_OBJECT
65 public:
QDataWidgetMapper(parent)66     QPropertyDataWidgetMapper(QObject *parent = 0) : QDataWidgetMapper(parent) {}
67 
68     // QDataWidgetMapper::model() re-written to return QPropertyDataWidgetMapper
69     QPropertyModel *model() const;
70     // For convenience, these automatically convert "property" into column numbers
71     void addMapping(QWidget *widget, QString property);
72     void addMapping(QWidget *widget, QString property, const QByteArray &propertyName);
73     // Pass-thru methods to QDataWidgetMapper
74     void addMapping(QWidget *widget, int section);
75     void addMapping(QWidget *widget, int section, const QByteArray &propertyName);
76 };
77 
78 // QPropertyModel creates a single row data model consisting of columns mapping
79 // to properties in a QObject. The column list can be retrieved as a QStringList,
80 // and a method exists to convert the property names to column numbers.
81 class QPropertyModel : public QAbstractItemModel
82 {
83     Q_OBJECT
84 public:
85     explicit QPropertyModel(QObject *source, QObject *parent = 0);
86     ~QPropertyModel();
87 
88     // Return a QPropertyDataWidgetMapper wrapping a new instance of this class.
89     static QPropertyDataWidgetMapper *newMapper(QObject *source, QObject *parent = 0);
90     // Return a QPropertyDataWidgetMapper wrapping this existing instance
91     QPropertyDataWidgetMapper *newMapper();
92 
93     QStringList propertyNames() const;
94     int columnForProperty(QString name) const;
95     QMap<int, QMetaProperty> properties() const;
96 
97 protected:
98     void connectToPropertyNotifySignals();
99 
100     // Pointer to our data source
101     QObject *_source;
102 
103     mutable QStringList _names;
104     mutable QMap<int, QMetaProperty> _properties;
105 protected slots:
106     void columnChanged(int column);
107 
108     // Required virtual function implementations. They mostly map
109     // directly to the (getItem/setItem/itemChanged) methods above.
110 public:
111     // read & write data
112     virtual QVariant data(const QModelIndex &index, int role) const;
113     virtual bool setData(const QModelIndex &index, const QVariant &value, int role);
114 
115     // returns the number of properties in _source
116     virtual int columnCount(const QModelIndex &parent) const;
117 
118     // all hard-coded simple implementations
119     virtual int rowCount(const QModelIndex &parent = QModelIndex()) const;
120     virtual Qt::ItemFlags flags(const QModelIndex &index) const;
121     virtual QModelIndex parent(const QModelIndex &child) const;
122     virtual QModelIndex index(int row, int column, const QModelIndex &) const;
123 
124     // Helper method to make virtual methods easier to code
125     virtual bool hasIndex(const QModelIndex &index) const;
126 };
127 
128 
129 // Until we can come up with something more clever, this little class allows
130 // us to connect each signal in a single QObject to a single slot using
131 // QSignalMapper to pass information to us about which signal was sent.
132 // QSignalMapper maps Objects to data. All of our signals come from the same
133 // object, so that won't work. However, if we create a SignalObject as a
134 // forwareder for each signal, now we have a unique object for each signal
135 // that QSignalMapper can work with.
136 class SignalForwarder: public QObject
137 {
138     Q_OBJECT
139 public:
QObject(parent)140     SignalForwarder(QObject *parent = 0) : QObject(parent) {}
141 signals:
142     void forward();
143 };
144 
145 #endif // QPROPERTYMODEL_H
146