RioEngine  0.1
My first attempt to create a 3D WYSIWYG Game Engine
qpropertymodel.cpp
Go to the documentation of this file.
1 // *************************************************************************************************
2 //
3 // QPropertyEditor v 0.3
4 //
5 // --------------------------------------
6 // Copyright (C) 2007 Volker Wiendl
7 // Acknowledgements to Roman alias banal from qt-apps.org for the Enum enhancement
8 //
9 //
10 // The QPropertyEditor Library is free software; you can redistribute it and/or modify
11 // it under the terms of the GNU General Public License as published by
12 // the Free Software Foundation version 3 of the License
13 //
14 // The Horde3D Scene Editor is distributed in the hope that it will be useful,
15 // but WITHOUT ANY WARRANTY; without even the implied warranty of
16 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 // GNU General Public License for more details.
18 //
19 // You should have received a copy of the GNU General Public License
20 // along with this program. If not, see <http://www.gnu.org/licenses/>.
21 //
22 // *************************************************************************************************
23 
24 #include "QPropertyModel.h"
25 
26 #include "Property.h"
27 #include "EnumProperty.h"
29 #include "misc/StringHelper.hpp"
31 
32 #include <QtWidgets/QApplication>
33 #include <QtCore/QMetaProperty>
34 #include <QtWidgets/QItemEditorFactory>
35 
37 {
38  PropertyPair(const QMetaObject* obj, QMetaProperty property) : Property(property), Object(obj) {}
39 
40  QMetaProperty Property;
41  const QMetaObject* Object;
42 
43  bool operator==(const PropertyPair& other) const {return QString(other.Property.name()) == QString(Property.name());}
44 };
45 
46 
47 QPropertyModel::QPropertyModel(QObject* parent /*= 0*/) : QAbstractItemModel(parent)
48 {
49  m_rootItem = new Property("Root",0, this);
50 }
51 
52 
54 {
55 }
56 
57 QModelIndex QPropertyModel::index ( int row, int column, const QModelIndex & parent /*= QModelIndex()*/ ) const
58 {
59  Property *parentItem = m_rootItem;
60  if (parent.isValid())
61  parentItem = static_cast<Property*>(parent.internalPointer());
62  if (row >= parentItem->children().size() || row < 0)
63  return QModelIndex();
64  return createIndex(row, column, parentItem->children().at(row));
65 
66 }
67 
68 QModelIndex QPropertyModel::parent ( const QModelIndex & index ) const
69 {
70  if (!index.isValid())
71  return QModelIndex();
72 
73  Property *childItem = static_cast<Property*>(index.internalPointer());
74  Property *parentItem = qobject_cast<Property*>(childItem->parent());
75 
76  if (!parentItem || parentItem == m_rootItem)
77  return QModelIndex();
78 
79  return createIndex(parentItem->row(), 0, parentItem);
80 }
81 
82 int QPropertyModel::rowCount ( const QModelIndex & parent /*= QModelIndex()*/ ) const
83 {
84  Property *parentItem = m_rootItem;
85  if (parent.isValid())
86  parentItem = static_cast<Property*>(parent.internalPointer());
87  return parentItem->children().size();
88 }
89 
90 int QPropertyModel::columnCount ( const QModelIndex & /*parent = QModelIndex()*/ ) const
91 {
92  return 2;
93 }
94 
95 QVariant QPropertyModel::data ( const QModelIndex & index, int role /*= Qt::DisplayRole*/ ) const
96 {
97  if (!index.isValid())
98  return QVariant();
99 
100  Property *item = static_cast<Property*>(index.internalPointer());
101  switch(role)
102  {
103  case Qt::ToolTipRole:
104  case Qt::DecorationRole:
105  case Qt::DisplayRole:
106  case Qt::EditRole:
107  if (index.column() == 0)
108  return item->objectName().replace('_', ' ');
109  if (index.column() == 1)
110  return item->value(role);
111  case Qt::BackgroundRole:
112  if (item->isRoot()) return QApplication::palette("QTreeView").brush(QPalette::Normal, QPalette::Button).color();
113  break;
114  };
115  return QVariant();
116 }
117 
118 // edit methods
119 bool QPropertyModel::setData ( const QModelIndex & index, const QVariant & value, int role /*= Qt::EditRole*/ )
120 {
121  if (index.isValid() && role == Qt::EditRole)
122  {
123  Property *item = static_cast<Property*>(index.internalPointer());
124  item->setValue(value);
125  emit dataChanged(index, index);
126  return true;
127  }
128  return false;
129 }
130 
131 Qt::ItemFlags QPropertyModel::flags ( const QModelIndex & index ) const
132 {
133  if (!index.isValid())
134  return Qt::ItemIsEnabled;
135  Property *item = static_cast<Property*>(index.internalPointer());
136  // only allow change of value attribute
137  if (item->isRoot())
138  return Qt::ItemIsEnabled;
139  else if (item->isReadOnly())
140  return Qt::ItemIsDragEnabled | Qt::ItemIsSelectable;
141  else
142  return Qt::ItemIsDragEnabled | Qt::ItemIsEnabled | Qt::ItemIsSelectable | Qt::ItemIsEditable;
143 }
144 
145 
146 QVariant QPropertyModel::headerData ( int section, Qt::Orientation orientation, int role /*= Qt::DisplayRole*/ ) const
147 {
148  if (orientation == Qt::Horizontal && role == Qt::DisplayRole)
149  {
150  switch (section)
151  {
152  case 0:
153  return tr("Name");
154  case 1:
155  return tr("Value");
156  }
157  }
158  return QVariant();
159 }
160 
161 QModelIndex QPropertyModel::buddy ( const QModelIndex & index ) const
162 {
163  if (index.isValid() && index.column() == 0)
164  return createIndex(index.row(), 1, index.internalPointer());
165  return index;
166 }
167 
168 void QPropertyModel::addItem(QObject *propertyObject)
169 {
170  // first create property <-> class hierarchy
171  QList<PropertyPair> propertyMap;
172  QList<const QMetaObject*> classList;
173  const QMetaObject* metaObject = propertyObject->metaObject();
174  do
175  {
176  int count = metaObject->propertyCount();
177  for (int i=0; i<count; ++i)
178  {
179  QMetaProperty property = metaObject->property(i);
180  if( property.isDesignable() )
181  {
182  PropertyPair pair(metaObject, property);
183  int index = propertyMap.indexOf(pair);
184  if (index != -1)
185  propertyMap[index] = pair;
186  else
187  propertyMap.push_back(pair);
188  }
189  }
190  classList.push_front(metaObject);
191  }
192  while ((metaObject = metaObject->superClass())!=0);
193 
194  QList<const QMetaObject*> finalClassList;
195  // remove empty classes from hierarchy list
196  foreach(const QMetaObject* obj, classList)
197  {
198  bool keep = false;
199  foreach(PropertyPair pair, propertyMap)
200  {
201  if (pair.Object == obj)
202  {
203  keep = true;
204  break;
205  }
206  }
207  if (keep)
208  finalClassList.push_back(obj);
209  }
210 
211  // finally insert properties for classes containing them
212  int i=rowCount();
213  Property* propertyItem = 0;
214  beginInsertRows( QModelIndex(), i, i + finalClassList.count() );
215  // foreach(const QMetaObject* metaObject, finalClassList)
216  // {
217  // Set default name of the hierarchy property to the class name
218  //QString name = metaObject->className();
219  // Check if there is a special name for the class
220  /*int index = metaObject->indexOfClassInfo(qPrintable(name));
221  if (index != -1)
222  name = metaObject->classInfo(index).value();*/
223  // Create Property Item for class node
224  propertyItem = new Property(/*name*/"General", 0, m_rootItem);
225  foreach(PropertyPair pair, propertyMap)
226  {
227  // Check if the property is associated with the current class from the finalClassList
228  // if (pair.Object == metaObject)
229  // {
230  QMetaProperty property(pair.Property);
231  Property* p = 0;
232  if (property.type() == QVariant::UserType && !m_userCallbacks.isEmpty())
233  {
234  QList<QPropertyEditorWidget::UserTypeCB>::iterator iter = m_userCallbacks.begin();
235  while( p == 0 && iter != m_userCallbacks.end() )
236  {
237  p = (*iter)(property.name(), propertyObject, propertyItem);
238  ++iter;
239  }
240  }
241  if( p == 0)
242  {
243  uint classinfo_count = propertyObject->metaObject()->classInfoCount();
244  bool forced_type = false;
245  for(uint i = 0; i < classinfo_count; i++)
246  {
247  QMetaClassInfo info = propertyObject->metaObject()->classInfo(i);
248  if(strcmp(info.name(), property.name()) == 0)
249  {
250  std::vector<std::string> items = StringHelper::split(info.value(), "=");
251  std::string var = items[0];
252  std::string var_value = items[1];
253  if(var == "CustomEnum")
254  {
255  if(var_value == "CClasses")
256  {
257  p = new CObjectTypesEnumProperty(property.name(), propertyObject, propertyItem);
258  auto types = CGameObjectFactory::types();
259  ((CObjectTypesEnumProperty*)p)->setEnumValues(types);
260  forced_type = true;
261  }
262  }
263  }
264  }
265  if(!forced_type)
266  {
267  if(property.isEnumType())
268  {
269  p = new EnumProperty(property.name(), propertyObject, propertyItem);
270  } else {
271  p = new Property(property.name(), propertyObject, propertyItem);
272  }
273  }
274  }
275  int index = metaObject->indexOfClassInfo(property.name());
276  if (index != -1)
277  p->setEditorHints(metaObject->classInfo(index).value());
278  }
279  // }
280  //}
281  endInsertRows();
282  if( propertyItem ) addDynamicProperties( propertyItem, propertyObject );
283 }
284 
285 void QPropertyModel::updateItem ( QObject* propertyObject, const QModelIndex& parent /*= QModelIndex() */ )
286 {
287  Property *parentItem = m_rootItem;
288  if (parent.isValid())
289  parentItem = static_cast<Property*>(parent.internalPointer());
290  if (parentItem->propertyObject() != propertyObject)
291  parentItem = parentItem->findPropertyObject(propertyObject);
292  if (parentItem) // Indicate view that the data for the indices have changed
293  {
294  QModelIndex itemIndex = createIndex(parentItem->row(), 0, static_cast<Property*>(parentItem));
295  dataChanged(itemIndex, createIndex(parentItem->row(), 1, static_cast<Property*>(parentItem)));
296  QList<QByteArray> dynamicProperties = propertyObject->dynamicPropertyNames();
297  QList<QObject*> childs = parentItem->parent()->children();
298  int removed = 0;
299  for(int i = 0; i < childs.count(); ++i )
300  {
301  QObject* obj = childs[i];
302  if( !obj->property("__Dynamic").toBool() || dynamicProperties.contains( obj->objectName().toLocal8Bit() ) )
303  continue;
304  beginRemoveRows(itemIndex.parent(), i - removed, i - removed);
305  ++removed;
306  delete obj;
307  endRemoveRows();
308  }
309  addDynamicProperties(static_cast<Property*>(parentItem->parent()), propertyObject);
310  }
311 }
312 
313 void QPropertyModel::addDynamicProperties( Property* parent, QObject* propertyObject )
314 {
315  // Get dynamic property names
316  QList<QByteArray> dynamicProperties = propertyObject->dynamicPropertyNames();
317 
318  QList<QObject*> childs = parent->children();
319 
320  // Remove already existing properties from list
321  for(int i = 0; i < childs.count(); ++i )
322  {
323  if( !childs[i]->property("__Dynamic").toBool() ) continue;
324 
325  int index = dynamicProperties.indexOf( childs[i]->objectName().toLocal8Bit() );
326  if( index != -1)
327  {
328  dynamicProperties.removeAt(index);
329  continue;
330  }
331  }
332 
333  // Remove invalid properites and those we don't want to add
334  for(int i = 0; i < dynamicProperties.size(); ++i )
335  {
336  QString dynProp = dynamicProperties[i];
337  // Skip properties starting with _ (because there may be dynamic properties from Qt with _q_ and we may
338  // have user defined hidden properties starting with _ too
339  if( dynProp.startsWith("_") || !propertyObject->property( qPrintable(dynProp) ).isValid() )
340  {
341  dynamicProperties.removeAt(i);
342  --i;
343  }
344  }
345 
346  if( dynamicProperties.empty() ) return;
347 
348  QModelIndex parentIndex = createIndex(parent->row(), 0, static_cast<Property*>(parent));
349  int rows = rowCount(parentIndex);
350  beginInsertRows(parentIndex, rows, rows + dynamicProperties.count() - 1 );
351  // Add properties left in the list
352  foreach(QByteArray dynProp, dynamicProperties )
353  {
354  QVariant v = propertyObject->property(dynProp);
355  Property* p = 0;
356  if( v.type() == QVariant::UserType && !m_userCallbacks.isEmpty() )
357  {
358  QList<QPropertyEditorWidget::UserTypeCB>::iterator iter = m_userCallbacks.begin();
359  while( p == 0 && iter != m_userCallbacks.end() )
360  {
361  p = (*iter)(dynProp, propertyObject, parent);
362  ++iter;
363  }
364  }
365  if( p == 0 ) p = new Property(dynProp, propertyObject, parent);
366  p->setProperty("__Dynamic", true);
367  }
368  endInsertRows();
369 }
370 
372 {
373  beginRemoveRows(QModelIndex(), 0, rowCount());
374  delete m_rootItem;
375  m_rootItem = new Property("Root",0, this);
376  endRemoveRows();
377 }
378 
380 {
381  if ( !m_userCallbacks.contains(callback) )
382  m_userCallbacks.push_back(callback);
383 }
384 
386 {
387  int index = m_userCallbacks.indexOf(callback);
388  if( index != -1 )
389  m_userCallbacks.removeAt(index);
390 }
QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const
QAbstractItemModel implementation.
static const QStringList & types()
void unregisterCustomPropertyCB(QPropertyEditorWidget::UserTypeCB callback)
bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::EditRole)
QAbstractItemModel implementation.
int row()
Definition: property.h:84
virtual void setValue(const QVariant &value)
Definition: property.cpp:51
void registerCustomPropertyCB(QPropertyEditorWidget::UserTypeCB callback)
QModelIndex parent(const QModelIndex &index) const
QAbstractItemModel implementation.
bool isRoot()
Definition: property.h:72
virtual ~QPropertyModel()
Destructor.
QMetaProperty Property
QModelIndex index(int row, int column, const QModelIndex &parent=QModelIndex()) const
QAbstractItemModel implementation.
int rowCount(const QModelIndex &parent=QModelIndex()) const
QAbstractItemModel implementation.
void updateItem(QObject *propertyObject, const QModelIndex &parent=QModelIndex())
bool operator==(const PropertyPair &other) const
QPropertyModel(QObject *parent=0)
QModelIndex buddy(const QModelIndex &index) const
QAbstractItemModel implementation.
PropertyPair(const QMetaObject *obj, QMetaProperty property)
Property *(* UserTypeCB)(const QString &name, QObject *propertyObject, Property *parent)
virtual QVariant value(int role=Qt::UserRole) const
Definition: property.cpp:43
int columnCount(const QModelIndex &parent=QModelIndex()) const
QAbstractItemModel implementation.
bool isReadOnly()
Definition: property.cpp:61
QObject * propertyObject()
Definition: property.h:66
QVariant headerData(int section, Qt::Orientation orientation, int role=Qt::DisplayRole) const
QAbstractItemModel implementation.
Qt::ItemFlags flags(const QModelIndex &index) const
QAbstractItemModel implementation.
const QMetaObject * Object
Property * findPropertyObject(QObject *propertyObject)
Definition: property.cpp:139
static std::vector< std::string > split(const std::string &str, const std::string &delimiters)
virtual void setEditorHints(const QString &hints)
Definition: property.h:96
void addItem(QObject *propertyObject)