BornAgain  1.19.0
Simulate and fit neutron and x-ray scattering at grazing incidence
widgetboxcategorylistview.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 **
3 ** Copyright (C) 2012 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the Qt Designer of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
36 **
37 **
38 ** $QT_END_LICENSE$
39 **
40 ****************************************************************************/
41 
43 #include "Base/Utils/Assert.h"
45 
46 #include <QtDesigner/QDesignerFormEditorInterface>
47 #include <QtDesigner/QDesignerWidgetDataBaseInterface>
48 
49 #include <QtXml/QDomDocument>
50 
51 #include <QtGui/QIcon>
52 
53 #include <QItemDelegate>
54 #include <QLineEdit>
55 #include <QListView>
56 #include <QSortFilterProxyModel>
57 
58 #include <QXmlStreamReader>
59 #include <QtCore/QAbstractListModel>
60 #include <QtCore/QList>
61 #include <QtCore/QRegExp>
62 #include <QtCore/QTextStream>
63 
64 static const char* widgetElementC = "widget";
65 static const char* nameAttributeC = "name";
66 static const char* uiOpeningTagC = "<ui>";
67 static const char* uiClosingTagC = "</ui>";
68 
69 #if QT_VERSION < 0x050000
70 #define QStringLiteral QString
71 #endif
72 
73 QT_BEGIN_NAMESPACE
74 
75 enum { FILTER_ROLE = Qt::UserRole + 11 };
76 
77 static QString domToString(const QDomElement& elt)
78 {
79  QString result;
80  QTextStream stream(&result, QIODevice::WriteOnly);
81  elt.save(stream, 2);
82  stream.flush();
83  return result;
84 }
85 
86 static QDomDocument stringToDom(const QString& xml)
87 {
88  QDomDocument result;
89  result.setContent(xml);
90  return result;
91 }
92 
93 namespace qdesigner_internal {
94 
95 // Entry of the model list
96 
99  explicit WidgetBoxCategoryEntry(const QDesignerWidgetBoxInterface::Widget& widget,
100  const QString& filter, const QIcon& icon, bool editable);
101 
102  QDesignerWidgetBoxInterface::Widget widget;
103  QString toolTip;
104  QString whatsThis;
105  QString filter;
106  QIcon icon;
107  bool editable;
108 };
109 
111 
112 WidgetBoxCategoryEntry::WidgetBoxCategoryEntry(const QDesignerWidgetBoxInterface::Widget& w,
113  const QString& filterIn, const QIcon& i, bool e)
114  : widget(w), filter(filterIn), icon(i), editable(e)
115 {
116 }
117 
118 /* WidgetBoxCategoryModel, representing a list of category entries. Uses a
119  * QAbstractListModel since the behaviour depends on the view mode of the list
120  * view, it does not return text in the case of IconMode. */
121 
122 class WidgetBoxCategoryModel : public QAbstractListModel {
123 public:
124  // explicit WidgetBoxCategoryModel(QDesignerFormEditorInterface *core, QObject *parent = 0);
125  explicit WidgetBoxCategoryModel(SampleDesignerInterface* core, QObject* parent = 0);
126 
127  // QAbstractListModel
128  virtual QVariant data(const QModelIndex& index, int role = Qt::DisplayRole) const;
129  virtual int rowCount(const QModelIndex& parent = QModelIndex()) const;
130  virtual bool setData(const QModelIndex& index, const QVariant& value, int role = Qt::EditRole);
131  virtual Qt::ItemFlags flags(const QModelIndex& index) const;
132  virtual bool removeRows(int row, int count, const QModelIndex& parent = QModelIndex());
133 
134  // The model returns no text in icon mode, so, it also needs to know it
135  QListView::ViewMode viewMode() const;
136  void setViewMode(QListView::ViewMode vm);
137 
138  void addWidget(const QDesignerWidgetBoxInterface::Widget& widget, const QIcon& icon,
139  bool editable);
140 
141  QDesignerWidgetBoxInterface::Widget widgetAt(const QModelIndex& index) const;
142  QDesignerWidgetBoxInterface::Widget widgetAt(int row) const;
143 
144  int indexOfWidget(const QString& name);
145 
146  QDesignerWidgetBoxInterface::Category category() const;
147  bool removeCustomWidgets();
148 
149 private:
150  typedef QList<WidgetBoxCategoryEntry> WidgetBoxCategoryEntrys;
151 
153  // QDesignerFormEditorInterface *m_core;
154  // SampleDesignerInterface *m_core;
156  QListView::ViewMode m_viewMode;
157 };
158 
159 // WidgetBoxCategoryModel::WidgetBoxCategoryModel(QDesignerFormEditorInterface *core, QObject
160 // *parent) :
162  : QAbstractListModel(parent)
163  ,
164 #if QT_VERSION >= 0x050000
165  m_classNameRegExp("<widget +class *= *\"([^\"]+)\"")
166  ,
167 #else
168  m_classNameRegExp(QString("<widget +class *= *\"([^\"]+)\""))
169  ,
170 #endif
171 
172  // m_core(core),
173  m_viewMode(QListView::ListMode)
174 {
175  ASSERT(m_classNameRegExp.isValid());
176  Q_UNUSED(core);
177 }
178 
179 QListView::ViewMode WidgetBoxCategoryModel::viewMode() const
180 {
181  return m_viewMode;
182 }
183 
184 void WidgetBoxCategoryModel::setViewMode(QListView::ViewMode vm)
185 {
186  if (m_viewMode == vm)
187  return;
188  const bool empty = m_items.isEmpty();
189  if (!empty)
190  beginResetModel();
191  m_viewMode = vm;
192  if (!empty)
193  endResetModel();
194 }
195 
197 {
198  const int count = m_items.size();
199  for (int i = 0; i < count; i++)
200  if (m_items.at(i).widget.name() == name)
201  return i;
202  return -1;
203 }
204 
205 QDesignerWidgetBoxInterface::Category WidgetBoxCategoryModel::category() const
206 {
207  QDesignerWidgetBoxInterface::Category rc;
208  const WidgetBoxCategoryEntrys::const_iterator cend = m_items.constEnd();
209  for (WidgetBoxCategoryEntrys::const_iterator it = m_items.constBegin(); it != cend; ++it)
210  rc.addWidget(it->widget);
211  return rc;
212 }
213 
215 {
216  // Typically, we are a whole category of custom widgets, so, remove all
217  // and do reset.
218  bool changed = false;
219  for (WidgetBoxCategoryEntrys::iterator it = m_items.begin(); it != m_items.end();)
220  if (it->widget.type() == QDesignerWidgetBoxInterface::Widget::Custom) {
221  if (!changed)
222  beginResetModel();
223  it = m_items.erase(it);
224  changed = true;
225  } else {
226  ++it;
227  }
228  if (changed)
229  endResetModel();
230  return changed;
231 }
232 
233 void WidgetBoxCategoryModel::addWidget(const QDesignerWidgetBoxInterface::Widget& widget,
234  const QIcon& icon, bool editable)
235 {
236  // build item. Filter on name + class name if it is different and not a layout.
237  QString filter = widget.name();
238  if (!filter.contains("Layout") && m_classNameRegExp.indexIn(widget.domXml()) != -1) {
239  const QString className = m_classNameRegExp.cap(1);
240  if (!filter.contains(className))
241  filter += className;
242  }
243  WidgetBoxCategoryEntry item(widget, filter, icon, editable);
244 
245  QXmlStreamReader reader(widget.domXml());
246  while (!reader.atEnd()) {
247  switch (reader.readNext()) {
248  case QXmlStreamReader::StartElement: {
249  const QStringRef tag = reader.name();
250  if (tag == "widget") {
251  const QXmlStreamAttributes attributes = reader.attributes();
252  QString className = attributes.value("class").toString();
253  QString toolTip = ToolTipDataBase::widgetboxToolTip(className);
254  if (!toolTip.isEmpty())
255  item.toolTip = toolTip;
256  }
257  break;
258  }
259  default:
260  break;
261  }
262  }
263 
264  // const QDesignerWidgetDataBaseInterface *db = m_core->widgetDataBase();
265  // const int dbIndex = db->indexOfClassName(widget.name());
266  // if (dbIndex != -1) {
267  // const QDesignerWidgetDataBaseItemInterface *dbItem = db->item(dbIndex);
268  // const QString toolTip = dbItem->toolTip();
269  // if (!toolTip.isEmpty())
270  // item.toolTip = toolTip;
271  // const QString whatsThis = dbItem->whatsThis();
272  // if (!whatsThis.isEmpty())
273  // item.whatsThis = whatsThis;
274  // }
275  // insert
276  const int row = m_items.size();
277  beginInsertRows(QModelIndex(), row, row);
278  m_items.push_back(item);
279  endInsertRows();
280 }
281 
282 QVariant WidgetBoxCategoryModel::data(const QModelIndex& index, int role) const
283 {
284  const int row = index.row();
285  if (row < 0 || row >= m_items.size())
286  return QVariant();
287 
288  const WidgetBoxCategoryEntry& item = m_items.at(row);
289  switch (role) {
290  case Qt::DisplayRole:
291  // No text in icon mode
292  return QVariant(m_viewMode == QListView::ListMode ? item.widget.name() : QString());
293  case Qt::DecorationRole:
294  return QVariant(item.icon);
295  case Qt::EditRole:
296  return QVariant(item.widget.name());
297  case Qt::ToolTipRole: {
298  if (m_viewMode == QListView::ListMode)
299  return QVariant(item.toolTip);
300  // Icon mode tooltip should contain the class name
301  QString tt = item.widget.name();
302  if (!item.toolTip.isEmpty()) {
303  tt += QLatin1Char('\n');
304  tt += item.toolTip;
305  }
306  return QVariant(tt);
307  }
308  case Qt::WhatsThisRole:
309  return QVariant(item.whatsThis);
310  case FILTER_ROLE:
311  return item.filter;
312  }
313  return QVariant();
314 }
315 
316 bool WidgetBoxCategoryModel::setData(const QModelIndex& index, const QVariant& value, int role)
317 {
318  const int row = index.row();
319  if (role != Qt::EditRole || row < 0 || row >= m_items.size()
320  || value.type() != QVariant::String)
321  return false;
322  // Set name and adapt Xml
323  WidgetBoxCategoryEntry& item = m_items[row];
324  const QString newName = value.toString();
325  item.widget.setName(newName);
326 
327  const QDomDocument doc = stringToDom(WidgetBoxCategoryListView::widgetDomXml(item.widget));
328  QDomElement widget_elt = doc.firstChildElement(QLatin1String(widgetElementC));
329  if (!widget_elt.isNull()) {
330  widget_elt.setAttribute(QLatin1String(nameAttributeC), newName);
331  item.widget.setDomXml(domToString(widget_elt));
332  }
333  emit dataChanged(index, index);
334  return true;
335 }
336 
337 Qt::ItemFlags WidgetBoxCategoryModel::flags(const QModelIndex& index) const
338 {
339  Qt::ItemFlags rc = Qt::ItemIsEnabled;
340  const int row = index.row();
341  if (row >= 0 && row < m_items.size())
342  if (m_items.at(row).editable) {
343  rc |= Qt::ItemIsSelectable;
344  // Can change name in list mode only
345  if (m_viewMode == QListView::ListMode)
346  rc |= Qt::ItemIsEditable;
347  }
348  return rc;
349 }
350 
351 int WidgetBoxCategoryModel::rowCount(const QModelIndex& /*parent*/) const
352 {
353  return m_items.size();
354 }
355 
356 bool WidgetBoxCategoryModel::removeRows(int row, int count, const QModelIndex& parent)
357 {
358  if (row < 0 || count < 1)
359  return false;
360  const int size = m_items.size();
361  const int last = row + count - 1;
362  if (row >= size || last >= size)
363  return false;
364  beginRemoveRows(parent, row, last);
365  for (int r = last; r >= row; r--)
366  m_items.removeAt(r);
367  endRemoveRows();
368  return true;
369 }
370 
371 QDesignerWidgetBoxInterface::Widget WidgetBoxCategoryModel::widgetAt(const QModelIndex& index) const
372 {
373  return widgetAt(index.row());
374 }
375 
376 QDesignerWidgetBoxInterface::Widget WidgetBoxCategoryModel::widgetAt(int row) const
377 {
378  if (row < 0 || row >= m_items.size())
379  return QDesignerWidgetBoxInterface::Widget();
380  return m_items.at(row).widget;
381 }
382 
383 /* WidgetSubBoxItemDelegate, ensures a valid name using a regexp validator */
384 
385 class WidgetBoxCategoryEntryDelegate : public QItemDelegate {
386 public:
387  explicit WidgetBoxCategoryEntryDelegate(QWidget* parent = 0) : QItemDelegate(parent) {}
388  QWidget* createEditor(QWidget* parent, const QStyleOptionViewItem& option,
389  const QModelIndex& index) const;
390 };
391 
393  const QStyleOptionViewItem& option,
394  const QModelIndex& index) const
395 {
396  QWidget* result = QItemDelegate::createEditor(parent, option, index);
397  if (QLineEdit* line_edit = qobject_cast<QLineEdit*>(result)) {
398  QRegExp re = QRegExp("[_a-zA-Z][_a-zA-Z0-9]*");
399  ASSERT(re.isValid());
400  line_edit->setValidator(new QRegExpValidator(re, line_edit));
401  }
402  return result;
403 }
404 
405 // ---------------------- WidgetBoxCategoryListView
406 
407 // WidgetBoxCategoryListView::WidgetBoxCategoryListView(QDesignerFormEditorInterface *core, QWidget
408 // *parent) :
410  : QListView(parent)
411  , m_proxyModel(new QSortFilterProxyModel(this))
412  , m_model(new WidgetBoxCategoryModel(core, this))
413 {
414  setFocusPolicy(Qt::NoFocus);
415  setFrameShape(QFrame::NoFrame);
416  // setIconSize(QSize(22, 22));
417  setIconSize(QSize(32, 32));
418  setSpacing(1);
419  setTextElideMode(Qt::ElideMiddle);
420  setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
421  setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
422  setResizeMode(QListView::Adjust);
423  setUniformItemSizes(true);
424 
425  setItemDelegate(new WidgetBoxCategoryEntryDelegate(this));
426 
427  connect(this, SIGNAL(pressed(QModelIndex)), this, SLOT(slotPressed(QModelIndex)));
428  setEditTriggers(QAbstractItemView::AnyKeyPressed);
429 
430  m_proxyModel->setSourceModel(m_model);
431  m_proxyModel->setFilterRole(FILTER_ROLE);
432  setModel(m_proxyModel);
433  connect(m_model, SIGNAL(dataChanged(QModelIndex, QModelIndex)), this,
434  SIGNAL(scratchPadChanged()));
435 }
436 
438 {
439  QListView::setViewMode(vm);
440  m_model->setViewMode(vm);
441 }
442 
444 {
445  const QModelIndex index = am == FILTERED ? m_proxyModel->index(row, 0)
446  : m_proxyModel->mapFromSource(m_model->index(row, 0));
447 
448  if (index.isValid())
449  setCurrentIndex(index);
450 }
451 
452 void WidgetBoxCategoryListView::slotPressed(const QModelIndex& index)
453 {
454  const QDesignerWidgetBoxInterface::Widget wgt =
455  m_model->widgetAt(m_proxyModel->mapToSource(index));
456  if (wgt.isNull())
457  return;
458  emit pressed(wgt.name(), widgetDomXml(wgt), QCursor::pos());
459 }
460 
462 {
463  const QModelIndex index = currentIndex();
464  if (!index.isValid() || !m_proxyModel->removeRow(index.row()))
465  return;
466 
467  // We check the unfiltered item count here, we don't want to get removed if the
468  // filtered view is empty
469  if (m_model->rowCount()) {
470  emit itemRemoved();
471  } else {
472  emit lastItemRemoved();
473  }
474 }
475 
477 {
478  const QModelIndex index = currentIndex();
479  if (index.isValid())
480  edit(index);
481 }
482 
484 {
485  return am == FILTERED ? m_proxyModel->rowCount() : m_model->rowCount();
486 }
487 
489 {
490  const QModelIndex filterIndex = m_proxyModel->index(filterRow, 0);
491  return m_proxyModel->mapToSource(filterIndex).row();
492 }
493 
494 QDesignerWidgetBoxInterface::Widget
495 WidgetBoxCategoryListView::widgetAt(EAccessMode am, const QModelIndex& index) const
496 {
497  const QModelIndex unfilteredIndex = am == FILTERED ? m_proxyModel->mapToSource(index) : index;
498  return m_model->widgetAt(unfilteredIndex);
499 }
500 
501 QDesignerWidgetBoxInterface::Widget WidgetBoxCategoryListView::widgetAt(EAccessMode am,
502  int row) const
503 {
504  return m_model->widgetAt(am == UNFILTERED ? row : mapRowToSource(row));
505 }
506 
508 {
509  m_model->removeRow(am == UNFILTERED ? row : mapRowToSource(row));
510 }
511 
513 {
514  return m_model->indexOfWidget(name) != -1;
515 }
516 
517 void WidgetBoxCategoryListView::addWidget(const QDesignerWidgetBoxInterface::Widget& widget,
518  const QIcon& icon, bool editable)
519 {
520  m_model->addWidget(widget, icon, editable);
521 }
522 
523 QString WidgetBoxCategoryListView::widgetDomXml(const QDesignerWidgetBoxInterface::Widget& widget)
524 {
525  QString domXml = widget.domXml();
526 
527  if (domXml.isEmpty()) {
528  domXml = QLatin1String(uiOpeningTagC);
529  domXml += "<widget class=\"";
530  domXml += widget.name();
531  domXml += "\"/>";
532  domXml += QLatin1String(uiClosingTagC);
533  }
534  return domXml;
535 }
536 
537 void WidgetBoxCategoryListView::filter(const QRegExp& re)
538 {
539  m_proxyModel->setFilterRegExp(re);
540 }
541 
542 QDesignerWidgetBoxInterface::Category WidgetBoxCategoryListView::category() const
543 {
544  return m_model->category();
545 }
546 
548 {
549  return m_model->removeCustomWidgets();
550 }
551 } // namespace qdesigner_internal
552 
553 QT_END_NAMESPACE
Defines the macro ASSERT.
#define ASSERT(condition)
Definition: Assert.h:31
sample designer interface
static QString widgetboxToolTip(const QString &className)
QWidget * createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const
WidgetBoxCategoryListView(SampleDesignerInterface *core, QWidget *parent=0)
QDesignerWidgetBoxInterface::Category category() const
QDesignerWidgetBoxInterface::Widget widgetAt(EAccessMode am, const QModelIndex &index) const
static QString widgetDomXml(const QDesignerWidgetBoxInterface::Widget &widget)
void addWidget(const QDesignerWidgetBoxInterface::Widget &widget, const QIcon &icon, bool editable)
void pressed(const QString &name, const QString &xml, const QPoint &globalPos)
virtual bool setData(const QModelIndex &index, const QVariant &value, int role=Qt::EditRole)
void addWidget(const QDesignerWidgetBoxInterface::Widget &widget, const QIcon &icon, bool editable)
virtual bool removeRows(int row, int count, const QModelIndex &parent=QModelIndex())
QDesignerWidgetBoxInterface::Widget widgetAt(const QModelIndex &index) const
QDesignerWidgetBoxInterface::Category category() const
virtual QVariant data(const QModelIndex &index, int role=Qt::DisplayRole) const
virtual int rowCount(const QModelIndex &parent=QModelIndex()) const
WidgetBoxCategoryModel(SampleDesignerInterface *core, QObject *parent=0)
virtual Qt::ItemFlags flags(const QModelIndex &index) const
QList< WidgetBoxCategoryEntry > WidgetBoxCategoryEntrys
QString const & name(EShape k)
Definition: particles.cpp:21
QVariant DecorationRole(const SessionItem &item)
Returns tooltip for given item.
QVariant ToolTipRole(const SessionItem &item, int ncol=0)
Returns tooltip for given item.
Defines class ToolTipDataBase.
static QDomDocument stringToDom(const QString &xml)
static QString domToString(const QDomElement &elt)
static const char * nameAttributeC
static const char * uiOpeningTagC
static const char * uiClosingTagC
static const char * widgetElementC