BornAgain  1.19.0
Simulate and fit neutron and x-ray scattering at grazing incidence
widgetboxtreewidget.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 // shared
50 
51 // sdk
52 #include <QtDesigner/QDesignerDnDItemInterface>
53 #include <QtDesigner/QDesignerFormEditorInterface>
54 
55 #if QT_VERSION < QT_VERSION_CHECK(5, 5, 0)
56 #include <QtDesigner/QDesignerCustomWidgetInterface>
57 #else
58 #include <QtUiPlugin/QDesignerCustomWidgetInterface>
59 #endif
60 
61 #include <QAction>
62 #include <QActionGroup>
63 #include <QApplication>
64 #include <QContextMenuEvent>
65 #include <QHeaderView>
66 #include <QMenu>
67 #include <QTreeWidgetItem>
68 
69 #include <QtCore/QDebug>
70 #include <QtCore/QFile>
71 #include <QtCore/QTimer>
72 
73 #include <iostream>
74 
75 static const char* widgetBoxRootElementC = "widgetbox";
76 static const char* widgetElementC = "widget";
77 static const char* uiElementC = "ui";
78 static const char* categoryElementC = "category";
79 static const char* categoryEntryElementC = "categoryentry";
80 static const char* nameAttributeC = "name";
81 static const char* typeAttributeC = "type";
82 static const char* iconAttributeC = "icon";
83 static const char* defaultTypeValueC = "default";
84 static const char* customValueC = "custom";
85 static const char* iconPrefixC = "__qt_icon__";
86 static const char* scratchPadValueC = "scratchpad";
87 static const char* qtLogoC = "qtlogo.png";
88 static const char* invisibleNameC = "[invisible]";
89 
90 QIcon createIconSet(const QString& name)
91 {
92  return QIcon(QString::fromUtf8(":/widgetbox/") + name);
93 }
94 
96 
97 QT_BEGIN_NAMESPACE
98 
99 static void setTopLevelRole(ETopLevelRole tlr, QTreeWidgetItem* item)
100 {
101  item->setData(0, Qt::UserRole, QVariant(tlr));
102 }
103 
104 static ETopLevelRole topLevelRole(const QTreeWidgetItem* item)
105 {
106  return static_cast<ETopLevelRole>(item->data(0, Qt::UserRole).toInt());
107 }
108 
109 namespace qdesigner_internal {
110 
112  : QTreeWidget(parent), m_core(core), m_iconMode(false), m_scratchPadDeleteTimer(nullptr)
113 {
114  setFocusPolicy(Qt::NoFocus);
115  setIndentation(0);
116  setRootIsDecorated(false);
117  setColumnCount(1);
118  header()->hide();
119 #if QT_VERSION >= 0x050000
120  header()->setSectionResizeMode(QHeaderView::Stretch);
121 #endif
122  setTextElideMode(Qt::ElideMiddle);
123  setVerticalScrollMode(ScrollPerPixel);
124 
125  setItemDelegate(new SheetDelegate(this, this));
126 
127  connect(this, SIGNAL(itemPressed(QTreeWidgetItem*, int)), this,
128  SLOT(handleMousePress(QTreeWidgetItem*)));
129 }
130 
131 QIcon WidgetBoxTreeWidget::iconForWidget(QString iconName) const
132 {
133  if (iconName.isEmpty())
134  iconName = QLatin1String(qtLogoC);
135 
136  if (iconName.startsWith(QLatin1String(iconPrefixC))) {
137  const IconCache::const_iterator it = m_pluginIcons.constFind(iconName);
138  if (it != m_pluginIcons.constEnd())
139  return it.value();
140  }
141  return createIconSet(iconName);
142 }
143 
145 {
146  WidgetBoxCategoryListView* rc = nullptr;
147  if (QTreeWidgetItem* cat_item = topLevelItem(idx))
148  if (QTreeWidgetItem* embedItem = cat_item->child(0))
149  rc = qobject_cast<WidgetBoxCategoryListView*>(itemWidget(embedItem, 0));
150  ASSERT(rc);
151  return rc;
152 }
153 
155 {
156  return;
157 }
158 
160 {
161  std::cout << "WidgetBoxTreeWidget::restoreExpandedState() -> XXX Not implemented." << std::endl;
162  return;
163 }
164 
166 {
168 }
169 
170 void WidgetBoxTreeWidget::setFileName(const QString& file_name)
171 {
172  m_file_name = file_name;
173 }
174 
176 {
177  return m_file_name;
178 }
179 
181 {
182  if (fileName().isEmpty())
183  return false;
184 
185  QFile file(fileName());
186  if (!file.open(QIODevice::WriteOnly))
187  return false;
188 
189  CategoryList cat_list;
190  const int count = categoryCount();
191  for (int i = 0; i < count; ++i)
192  cat_list.append(category(i));
193 
194  QXmlStreamWriter writer(&file);
195  writer.setAutoFormatting(true);
196  writer.setAutoFormattingIndent(1);
197  writer.writeStartDocument();
198  writeCategories(writer, cat_list);
199  writer.writeEndDocument();
200 
201  return true;
202 }
203 
205 {
206  save();
207 }
208 
209 void WidgetBoxTreeWidget::handleMousePress(QTreeWidgetItem* item)
210 {
211  if (item == nullptr)
212  return;
213 
214  if (QApplication::mouseButtons() != Qt::LeftButton)
215  return;
216 
217  if (item->parent() == nullptr) {
218 #if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0)
219  item->setExpanded(!item->isExpanded());
220 #else
221  setItemExpanded(item, !isItemExpanded(item));
222 #endif
223  return;
224  }
225 }
226 
228 {
229  const int existingIndex = indexOfScratchpad();
230  if (existingIndex != -1)
231  return existingIndex;
232 
233  QTreeWidgetItem* scratch_item = new QTreeWidgetItem(this);
234  scratch_item->setText(0, "Scratchpad");
235  setTopLevelRole(SCRATCHPAD_ITEM, scratch_item);
236  addCategoryView(scratch_item, false); // Scratchpad in list mode.
237  return categoryCount() - 1;
238 }
239 
241  bool iconMode)
242 {
243  QTreeWidgetItem* embed_item = new QTreeWidgetItem(parent);
244  embed_item->setFlags(Qt::ItemIsEnabled);
246  categoryView->setViewMode(iconMode ? QListView::IconMode : QListView::ListMode);
247  connect(categoryView, SIGNAL(scratchPadChanged()), this, SLOT(slotSave()));
248  connect(categoryView, SIGNAL(pressed(QString, QString, QPoint)), this,
249  SIGNAL(pressed(QString, QString, QPoint)));
250  connect(categoryView, SIGNAL(itemRemoved()), this, SLOT(slotScratchPadItemDeleted()));
251  connect(categoryView, SIGNAL(lastItemRemoved()), this, SLOT(slotLastScratchPadItemDeleted()));
252  setItemWidget(embed_item, 0, categoryView);
253  return categoryView;
254 }
255 
257 {
258  if (const int numTopLevels = topLevelItemCount()) {
259  for (int i = numTopLevels - 1; i >= 0; --i) {
260  if (topLevelRole(topLevelItem(i)) == SCRATCHPAD_ITEM)
261  return i;
262  }
263  }
264  return -1;
265 }
266 
268 {
269  const int topLevelCount = topLevelItemCount();
270  for (int i = 0; i < topLevelCount; ++i) {
271  if (topLevelItem(i)->text(0) == name)
272  return i;
273  }
274  return -1;
275 }
276 
278 {
279  switch (loadMode) {
281  clear();
282  break;
284  addCustomCategories(true);
285  updateGeometries();
286  return true;
287  default:
288  break;
289  }
290  const QString name = fileName();
291 
292  QFile f(name);
293  if (!f.open(QIODevice::ReadOnly)) // Might not exist at first startup
294  return false;
295 
296  const QString contents = QString::fromUtf8(f.readAll());
297  return loadContents(contents);
298 }
299 
300 bool WidgetBoxTreeWidget::loadContents(const QString& contents)
301 {
302  QString errorMessage;
303  CategoryList cat_list;
304  if (!readCategories(m_file_name, contents, &cat_list, &errorMessage)) {
306  return false;
307  }
308  for (const Category& cat : cat_list)
309  addCategory(cat);
310 
311  return true;
312 }
313 
315 {
316  if (replace) {
317  // clear out all existing custom widgets
318  if (const int numTopLevels = topLevelItemCount()) {
319  for (int t = 0; t < numTopLevels; ++t)
321  }
322  }
323  // re-add
324  const CategoryList customList = loadCustomCategoryList();
325  const CategoryList::const_iterator cend = customList.constEnd();
326  for (CategoryList::const_iterator it = customList.constBegin(); it != cend; ++it)
327  addCategory(*it);
328 }
329 
330 static inline QString msgXmlError(const QString& fileName, const QXmlStreamReader& r)
331 {
332  return QString("An error has been encountered at line %1 of %2: %3")
333  .arg(r.lineNumber())
334  .arg(fileName, r.errorString());
335 }
336 
337 bool WidgetBoxTreeWidget::readCategories(const QString& fileName, const QString& contents,
338  CategoryList* cats, QString* errorMessage)
339 {
340  // Read widget box XML:
341  //
342  //<widgetbox version="4.5">
343  // <category name="Layouts">
344  // <categoryentry name="Vertical Layout" icon="win/editvlayout.png" type="default">
345  // <widget class="QListWidget" ...>
346  // ...
347 
348  QXmlStreamReader reader(contents);
349 
350  // Entries of category with name="invisible" should be ignored
351  bool ignoreEntries = false;
352 
353  while (!reader.atEnd()) {
354  switch (reader.readNext()) {
355  case QXmlStreamReader::StartElement: {
356  const QStringRef tag = reader.name();
357  if (tag == QLatin1String(widgetBoxRootElementC)) {
358  //<widgetbox version="4.5">
359  continue;
360  }
361  if (tag == QLatin1String(categoryElementC)) {
362  // <category name="Layouts">
363  const QXmlStreamAttributes attributes = reader.attributes();
364  const QString categoryName =
365  attributes.value(QLatin1String(nameAttributeC)).toString();
366  if (categoryName == QLatin1String(invisibleNameC)) {
367  ignoreEntries = true;
368  } else {
369  Category category(categoryName);
370  if (attributes.value(QLatin1String(typeAttributeC))
371  == QLatin1String(scratchPadValueC))
372  category.setType(Category::Scratchpad);
373  cats->push_back(category);
374  }
375  continue;
376  }
377  if (tag == QLatin1String(categoryEntryElementC)) {
378  // <categoryentry name="Vertical Layout" icon="win/editvlayout.png" type="default">
379  if (!ignoreEntries) {
380  QXmlStreamAttributes attr = reader.attributes();
381  const QString widgetName = attr.value(QLatin1String(nameAttributeC)).toString();
382  const QString widgetIcon = attr.value(QLatin1String(iconAttributeC)).toString();
383  const WidgetBoxTreeWidget::Widget::Type widgetType =
384  attr.value(QLatin1String(typeAttributeC)).toString()
385  == QLatin1String(customValueC)
386  ? WidgetBoxTreeWidget::Widget::Custom
387  : WidgetBoxTreeWidget::Widget::Default;
388 
389  Widget w;
390  w.setName(widgetName);
391  w.setIconName(widgetIcon);
392  w.setType(widgetType);
393  if (!readWidget(&w, contents, reader))
394  continue;
395 
396  cats->back().addWidget(w);
397  } // ignoreEntries
398  continue;
399  }
400  break;
401  }
402  case QXmlStreamReader::EndElement: {
403  const QStringRef tag = reader.name();
404  if (tag == QLatin1String(widgetBoxRootElementC)) {
405  continue;
406  }
407  if (tag == QLatin1String(categoryElementC)) {
408  ignoreEntries = false;
409  continue;
410  }
411  if (tag == QLatin1String(categoryEntryElementC)) {
412  continue;
413  }
414  break;
415  }
416  default:
417  break;
418  }
419  }
420 
421  if (reader.hasError()) {
422  *errorMessage = msgXmlError(fileName, reader);
423  return false;
424  }
425 
426  return true;
427 }
428 
429 /*!
430  * Read out a widget within a category. This can either be
431  * enclosed in a <ui> element or a (legacy) <widget> element which may
432  * contain nested <widget> elements.
433  *
434  * Examples:
435  *
436  * <ui language="c++">
437  * <widget class="MultiPageWidget" name="multipagewidget"> ... </widget>
438  * <customwidgets>...</customwidgets>
439  * <ui>
440  *
441  * or
442  *
443  * <widget>
444  * <widget> ... </widget>
445  * ...
446  * <widget>
447  *
448  * Returns true on success, false if end was reached or an error has been encountered
449  * in which case the reader has its error flag set. If successful, the current item
450  * of the reader will be the closing element (</ui> or </widget>)
451  */
452 bool WidgetBoxTreeWidget::readWidget(Widget* w, const QString& xml, QXmlStreamReader& r)
453 {
454  qint64 startTagPosition = 0, endTagPosition = 0;
455 
456  int nesting = 0;
457  bool endEncountered = false;
458  bool parsedWidgetTag = false;
459  QString outmostElement;
460  while (!endEncountered) {
461  const qint64 currentPosition = r.characterOffset();
462  switch (r.readNext()) {
463  case QXmlStreamReader::StartElement:
464  if (nesting++ == 0) {
465  // First element must be <ui> or (legacy) <widget>
466  const QStringRef name = r.name();
467  if (name == QLatin1String(uiElementC)) {
468  startTagPosition = currentPosition;
469  } else {
470  if (name == QLatin1String(widgetElementC)) {
471  startTagPosition = currentPosition;
472  parsedWidgetTag = true;
473  } else {
474  r.raiseError(QString("Unexpected element <%1> encountered when "
475  "parsing for <widget> or <ui>")
476  .arg(name.toString()));
477  return false;
478  }
479  }
480  } else {
481  // We are within <ui> looking for the first <widget> tag
482  if (!parsedWidgetTag && r.name() == QLatin1String(widgetElementC)) {
483  parsedWidgetTag = true;
484  }
485  }
486  break;
487  case QXmlStreamReader::EndElement:
488  // Reached end of widget?
489  if (--nesting == 0) {
490  endTagPosition = r.characterOffset();
491  endEncountered = true;
492  }
493  break;
494  case QXmlStreamReader::EndDocument:
495  r.raiseError("Unexpected end of file encountered when parsing widgets.");
496  return false;
497  case QXmlStreamReader::Invalid:
498  return false;
499  default:
500  break;
501  }
502  }
503  if (!parsedWidgetTag) {
504  r.raiseError("A widget element could not be found.");
505  return false;
506  }
507  // Oddity: Startposition is 1 off
508  QString widgetXml = xml.mid(startTagPosition, endTagPosition - startTagPosition);
509  const QChar lessThan = QLatin1Char('<');
510  if (!widgetXml.startsWith(lessThan))
511  widgetXml.prepend(lessThan);
512  w->setDomXml(widgetXml);
513  return true;
514 }
515 
516 void WidgetBoxTreeWidget::writeCategories(QXmlStreamWriter& writer,
517  const CategoryList& cat_list) const
518 {
519  const QString widgetbox = QLatin1String(widgetBoxRootElementC);
520  const QString name = QLatin1String(nameAttributeC);
521  const QString type = QLatin1String(typeAttributeC);
522  const QString icon = QLatin1String(iconAttributeC);
523  const QString defaultType = QLatin1String(defaultTypeValueC);
524  const QString category = QLatin1String(categoryElementC);
525  const QString categoryEntry = QLatin1String(categoryEntryElementC);
526  const QString iconPrefix = QLatin1String(iconPrefixC);
527  const QString widgetTag = QLatin1String(widgetElementC);
528 
529  //
530  // <widgetbox>
531  // <category name="Layouts">
532  // <categoryEntry name="Vertical Layout" type="default" icon="win/editvlayout.png">
533  // <ui>
534  // ...
535  // </ui>
536  // </categoryEntry>
537  // ...
538  // </category>
539  // ...
540  // </widgetbox>
541  //
542 
543  writer.writeStartElement(widgetbox);
544 
545  for (const Category& cat : cat_list) {
546  writer.writeStartElement(category);
547  writer.writeAttribute(name, cat.name());
548  if (cat.type() == Category::Scratchpad)
549  writer.writeAttribute(type, QLatin1String(scratchPadValueC));
550 
551  const int widgetCount = cat.widgetCount();
552  for (int i = 0; i < widgetCount; ++i) {
553  const Widget wgt = cat.widget(i);
554  if (wgt.type() == Widget::Custom)
555  continue;
556 
557  writer.writeStartElement(categoryEntry);
558  writer.writeAttribute(name, wgt.name());
559  if (!wgt.iconName().startsWith(iconPrefix))
560  writer.writeAttribute(icon, wgt.iconName());
561  writer.writeAttribute(type, defaultType);
562 
563  const DomUI* domUI = QDesignerWidgetBox::xmlToUi(
564  wgt.name(), WidgetBoxCategoryListView::widgetDomXml(wgt), false);
565  if (domUI) {
566  domUI->write(writer);
567  delete domUI;
568  }
569  writer.writeEndElement(); // categoryEntry
570  }
571  writer.writeEndElement(); // categoryEntry
572  }
573  writer.writeEndElement(); // widgetBox
574 }
575 
577 {
578  CategoryList result;
579 
580  std::cout << "WidgetBoxTreeWidget::loadCustomCategoryList() -> XXX Not implemented."
581  << std::endl;
582  return result;
583 }
584 
585 void WidgetBoxTreeWidget::adjustSubListSize(QTreeWidgetItem* cat_item)
586 {
587  QTreeWidgetItem* embedItem = cat_item->child(0);
588  if (embedItem == nullptr)
589  return;
590 
591  WidgetBoxCategoryListView* list_widget =
592  static_cast<WidgetBoxCategoryListView*>(itemWidget(embedItem, 0));
593  list_widget->setFixedWidth(header()->width());
594  list_widget->doItemsLayout();
595  const int height = qMax(list_widget->contentsSize().height(), 1);
596  list_widget->setFixedHeight(height);
597  embedItem->setSizeHint(0, QSize(-1, height - 1));
598 }
599 
601 {
602  return topLevelItemCount();
603 }
604 
606 {
607  if (cat_idx >= topLevelItemCount())
608  return Category();
609 
610  QTreeWidgetItem* cat_item = topLevelItem(cat_idx);
611 
612  QTreeWidgetItem* embedItem = cat_item->child(0);
613  WidgetBoxCategoryListView* categoryView =
614  static_cast<WidgetBoxCategoryListView*>(itemWidget(embedItem, 0));
615 
616  Category result = categoryView->category();
617  result.setName(cat_item->text(0));
618 
619  switch (topLevelRole(cat_item)) {
620  case SCRATCHPAD_ITEM:
621  result.setType(Category::Scratchpad);
622  break;
623  default:
624  result.setType(Category::Default);
625  break;
626  }
627  return result;
628 }
629 
631 {
632  if (cat.widgetCount() == 0)
633  return;
634 
635  const bool isScratchPad = cat.type() == Category::Scratchpad;
636  WidgetBoxCategoryListView* categoryView;
637  QTreeWidgetItem* cat_item;
638 
639  if (isScratchPad) {
640  const int idx = ensureScratchpad();
641  categoryView = categoryViewAt(idx);
642  cat_item = topLevelItem(idx);
643  } else {
644  const int existingIndex = indexOfCategory(cat.name());
645  if (existingIndex == -1) {
646  cat_item = new QTreeWidgetItem();
647  cat_item->setText(0, cat.name());
648  setTopLevelRole(NORMAL_ITEM, cat_item);
649  // insert before scratchpad
650  const int scratchPadIndex = indexOfScratchpad();
651  if (scratchPadIndex == -1) {
652  addTopLevelItem(cat_item);
653  } else {
654  insertTopLevelItem(scratchPadIndex, cat_item);
655  }
656 #if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0)
657  cat_item->setExpanded(true);
658 #else
659  setItemExpanded(cat_item, true);
660 #endif
661 
662  categoryView = addCategoryView(cat_item, m_iconMode);
663  } else {
664  categoryView = categoryViewAt(existingIndex);
665  cat_item = topLevelItem(existingIndex);
666  }
667  }
668  // The same categories are read from the file $HOME, avoid duplicates
669  const int widgetCount = cat.widgetCount();
670  for (int i = 0; i < widgetCount; ++i) {
671  const Widget w = cat.widget(i);
672  if (!categoryView->containsWidget(w.name()))
673  categoryView->addWidget(w, iconForWidget(w.iconName()), isScratchPad);
674  }
675  adjustSubListSize(cat_item);
676 }
677 
679 {
680  if (cat_idx >= topLevelItemCount())
681  return;
682  delete takeTopLevelItem(cat_idx);
683 }
684 
685 int WidgetBoxTreeWidget::widgetCount(int cat_idx) const
686 {
687  if (cat_idx >= topLevelItemCount())
688  return 0;
689  // SDK functions want unfiltered access
691 }
692 
694 {
695  if (cat_idx >= topLevelItemCount())
696  return Widget();
697  // SDK functions want unfiltered access
698  WidgetBoxCategoryListView* categoryView = categoryViewAt(cat_idx);
699  return categoryView->widgetAt(WidgetBoxCategoryListView::UNFILTERED, wgt_idx);
700 }
701 
702 void WidgetBoxTreeWidget::addWidget(int cat_idx, const Widget& wgt)
703 {
704  if (cat_idx >= topLevelItemCount())
705  return;
706 
707  QTreeWidgetItem* cat_item = topLevelItem(cat_idx);
708  WidgetBoxCategoryListView* categoryView = categoryViewAt(cat_idx);
709 
710  const bool scratch = topLevelRole(cat_item) == SCRATCHPAD_ITEM;
711  categoryView->addWidget(wgt, iconForWidget(wgt.iconName()), scratch);
712  adjustSubListSize(cat_item);
713 }
714 
715 void WidgetBoxTreeWidget::removeWidget(int cat_idx, int wgt_idx)
716 {
717  if (cat_idx >= topLevelItemCount())
718  return;
719 
720  WidgetBoxCategoryListView* categoryView = categoryViewAt(cat_idx);
721 
722  // SDK functions want unfiltered access
724  if (wgt_idx >= categoryView->count(am))
725  return;
726 
727  categoryView->removeRow(am, wgt_idx);
728 }
729 
731 {
732  const int scratch_idx = indexOfScratchpad();
733  QTreeWidgetItem* scratch_item = topLevelItem(scratch_idx);
734  adjustSubListSize(scratch_item);
735  save();
736 }
737 
739 {
740  // Remove the scratchpad in the next idle loop
742  m_scratchPadDeleteTimer = new QTimer(this);
743  m_scratchPadDeleteTimer->setSingleShot(true);
744  m_scratchPadDeleteTimer->setInterval(0);
745  connect(m_scratchPadDeleteTimer, SIGNAL(timeout()), this, SLOT(deleteScratchpad()));
746  }
747  if (!m_scratchPadDeleteTimer->isActive())
748  m_scratchPadDeleteTimer->start();
749 }
750 
752 {
753  const int idx = indexOfScratchpad();
754  if (idx == -1)
755  return;
756  delete takeTopLevelItem(idx);
757  save();
758 }
759 
761 {
762  m_iconMode = false;
763  updateViewMode();
764 }
765 
767 {
768  m_iconMode = true;
769  updateViewMode();
770 }
771 
773 {
774  if (const int numTopLevels = topLevelItemCount()) {
775  for (int i = numTopLevels - 1; i >= 0; --i) {
776  QTreeWidgetItem* topLevel = topLevelItem(i);
777  // Scratch pad stays in list mode.
778  const QListView::ViewMode viewMode =
779  m_iconMode && (topLevelRole(topLevel) != SCRATCHPAD_ITEM) ? QListView::IconMode
780  : QListView::ListMode;
781  WidgetBoxCategoryListView* categoryView = categoryViewAt(i);
782  if (viewMode != categoryView->viewMode()) {
783  categoryView->setViewMode(viewMode);
784  adjustSubListSize(topLevelItem(i));
785  }
786  }
787  }
788  updateGeometries();
789 }
790 
791 void WidgetBoxTreeWidget::resizeEvent(QResizeEvent* e)
792 {
793  QTreeWidget::resizeEvent(e);
794  if (const int numTopLevels = topLevelItemCount()) {
795  for (int i = numTopLevels - 1; i >= 0; --i)
796  adjustSubListSize(topLevelItem(i));
797  }
798 }
799 
800 void WidgetBoxTreeWidget::contextMenuEvent(QContextMenuEvent* e)
801 {
802  QTreeWidgetItem* item = itemAt(e->pos());
803 
804  const bool scratchpad_menu = item != nullptr && item->parent() != nullptr
805  && topLevelRole(item->parent()) == SCRATCHPAD_ITEM;
806 
807  QMenu menu;
808  menu.addAction("Expand all", this, SLOT(expandAll()));
809  menu.addAction("Collapse all", this, SLOT(collapseAll()));
810  menu.addSeparator();
811 
812  QAction* listModeAction = menu.addAction("List View");
813  QAction* iconModeAction = menu.addAction("Icon View");
814  listModeAction->setCheckable(true);
815  iconModeAction->setCheckable(true);
816  QActionGroup* viewModeGroup = new QActionGroup(&menu);
817  viewModeGroup->addAction(listModeAction);
818  viewModeGroup->addAction(iconModeAction);
819  if (m_iconMode)
820  iconModeAction->setChecked(true);
821  else
822  listModeAction->setChecked(true);
823  connect(listModeAction, SIGNAL(triggered()), SLOT(slotListMode()));
824  connect(iconModeAction, SIGNAL(triggered()), SLOT(slotIconMode()));
825 
826  if (scratchpad_menu) {
827  menu.addSeparator();
828  menu.addAction("Remove", itemWidget(item, 0), SLOT(removeCurrentItem()));
829  if (!m_iconMode)
830  menu.addAction("Edit name", itemWidget(item, 0), SLOT(editCurrentItem()));
831  }
832  e->accept();
833  menu.exec(mapToGlobal(e->pos()));
834 }
835 
836 void WidgetBoxTreeWidget::dropWidgets(const QList<QDesignerDnDItemInterface*>& item_list)
837 {
838  QTreeWidgetItem* scratch_item = nullptr;
839  WidgetBoxCategoryListView* categoryView = nullptr;
840  bool added = false;
841 
842  for (QDesignerDnDItemInterface* item : item_list) {
843  QWidget* w = item->widget();
844  if (w == nullptr)
845  continue;
846 
847  DomUI* dom_ui = item->domUi();
848  if (dom_ui == nullptr)
849  continue;
850 
851  const int scratch_idx = ensureScratchpad();
852  scratch_item = topLevelItem(scratch_idx);
853  categoryView = categoryViewAt(scratch_idx);
854 
855  // Temporarily remove the fake toplevel in-between
856  DomWidget* fakeTopLevel = dom_ui->takeElementWidget();
857  DomWidget* firstWidget = nullptr;
858  if (fakeTopLevel && !fakeTopLevel->elementWidget().isEmpty()) {
859  firstWidget = fakeTopLevel->elementWidget().first();
860  dom_ui->setElementWidget(firstWidget);
861  } else {
862  dom_ui->setElementWidget(fakeTopLevel);
863  continue;
864  }
865  // Serialize to XML
866  QString xml;
867  {
868  QXmlStreamWriter writer(&xml);
869  writer.setAutoFormatting(true);
870  writer.setAutoFormattingIndent(1);
871  writer.writeStartDocument();
872  dom_ui->write(writer);
873  writer.writeEndDocument();
874  }
875  // Insert fake toplevel again
876  dom_ui->takeElementWidget();
877  dom_ui->setElementWidget(fakeTopLevel);
878 
879  const Widget wgt = Widget(w->objectName(), xml);
880  categoryView->addWidget(wgt, iconForWidget(wgt.iconName()), true);
881 #if QT_VERSION >= QT_VERSION_CHECK(5, 13, 0)
882  scratch_item->setExpanded(true);
883 #else
884  setItemExpanded(scratch_item, true);
885 #endif
886 
887  added = true;
888  }
889  if (added) {
890  save();
891  QApplication::setActiveWindow(this);
892  // Is the new item visible in filtered mode?
894  if (const int count = categoryView->count(am))
895  categoryView->setCurrentItem(am, count - 1);
896  categoryView->adjustSize(); // XXX
897  adjustSubListSize(scratch_item);
898  }
899 }
900 
901 void WidgetBoxTreeWidget::filter(const QString& f)
902 {
903  const bool empty = f.isEmpty();
904  QRegExp re = empty ? QRegExp() : QRegExp(f, Qt::CaseInsensitive, QRegExp::FixedString);
905  const int numTopLevels = topLevelItemCount();
906  bool changed = false;
907  for (int i = 0; i < numTopLevels; i++) {
908  QTreeWidgetItem* tl = topLevelItem(i);
909  WidgetBoxCategoryListView* categoryView = categoryViewAt(i);
910  // Anything changed? -> Enable the category
911  const int oldCount = categoryView->count(WidgetBoxCategoryListView::FILTERED);
912  categoryView->filter(re);
913  const int newCount = categoryView->count(WidgetBoxCategoryListView::FILTERED);
914  if (oldCount != newCount) {
915  changed = true;
916  const bool categoryEnabled = newCount > 0 || empty;
917  if (categoryEnabled) {
918  categoryView->adjustSize();
919  adjustSubListSize(tl);
920  }
921  setRowHidden(i, QModelIndex(), !categoryEnabled);
922  }
923  }
924  if (changed)
925  updateGeometries();
926 }
927 
928 } // namespace qdesigner_internal
929 
930 QT_END_NAMESPACE
Defines the macro ASSERT.
#define ASSERT(condition)
Definition: Assert.h:31
Definition: ui4_p.h:160
void setElementWidget(DomWidget *a)
DomWidget * takeElementWidget()
void write(QXmlStreamWriter &writer, const QString &tagName="") const
QList< DomWidget * > elementWidget() const
Definition: ui4_p.h:1691
sample designer interface
static DomUI * xmlToUi(const QString &name, const QString &xml, bool insertFakeTopLevel, QString *errorMessage)
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 adjustSubListSize(QTreeWidgetItem *cat_item)
WidgetBoxTreeWidget(SampleDesignerInterface *core, QWidget *parent=0)
Widget widget(int cat_idx, int wgt_idx) const
static bool readWidget(Widget *w, const QString &xml, QXmlStreamReader &r)
void setFileName(const QString &file_name)
bool loadContents(const QString &contents)
WidgetBoxCategoryListView * addCategoryView(QTreeWidgetItem *parent, bool iconMode)
void pressed(const QString name, const QString dom_xml, const QPoint &global_mouse_pos)
void writeCategories(QXmlStreamWriter &writer, const CategoryList &cat_list) const
WidgetBoxCategoryListView * categoryViewAt(int idx) const
QIcon iconForWidget(QString iconName) const
virtual void resizeEvent(QResizeEvent *e)
static bool readCategories(const QString &fileName, const QString &xml, CategoryList *cats, QString *errorMessage)
void addWidget(int cat_idx, const Widget &wgt)
QDesignerWidgetBoxInterface::CategoryList CategoryList
QDesignerWidgetBoxInterface::Widget Widget
virtual void contextMenuEvent(QContextMenuEvent *e)
bool load(QDesignerWidgetBox::LoadMode loadMode)
QDesignerWidgetBoxInterface::Category Category
void handleMousePress(QTreeWidgetItem *item)
int indexOfCategory(const QString &name) const
void removeWidget(int cat_idx, int wgt_idx)
void dropWidgets(const QList< QDesignerDnDItemInterface * > &item_list)
QString const & name(EShape k)
Definition: particles.cpp:21
static QString msgXmlError(const QString &fileName, const QXmlStreamReader &r)
QDESIGNER_SHARED_EXPORT void designerWarning(const QString &message)
static const char * invisibleNameC
static const char * widgetBoxRootElementC
static QT_BEGIN_NAMESPACE void setTopLevelRole(ETopLevelRole tlr, QTreeWidgetItem *item)
static const char * customValueC
static const char * iconPrefixC
QIcon createIconSet(const QString &name)
static const char * scratchPadValueC
static const char * typeAttributeC
static const char * nameAttributeC
static const char * defaultTypeValueC
static const char * qtLogoC
static const char * categoryEntryElementC
static const char * iconAttributeC
static const char * uiElementC
static ETopLevelRole topLevelRole(const QTreeWidgetItem *item)
static const char * widgetElementC
@ NORMAL_ITEM
@ CUSTOM_ITEM
@ SCRATCHPAD_ITEM
static const char * categoryElementC