qtgroupboxpropertybrowser.cpp 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535
  1. /****************************************************************************
  2. **
  3. ** Copyright (C) 2013 Digia Plc and/or its subsidiary(-ies).
  4. ** Contact: http://www.qt-project.org/legal
  5. **
  6. ** This file is part of the Qt Solutions component.
  7. **
  8. ** $QT_BEGIN_LICENSE:BSD$
  9. ** You may use this file under the terms of the BSD license as follows:
  10. **
  11. ** "Redistribution and use in source and binary forms, with or without
  12. ** modification, are permitted provided that the following conditions are
  13. ** met:
  14. ** * Redistributions of source code must retain the above copyright
  15. ** notice, this list of conditions and the following disclaimer.
  16. ** * Redistributions in binary form must reproduce the above copyright
  17. ** notice, this list of conditions and the following disclaimer in
  18. ** the documentation and/or other materials provided with the
  19. ** distribution.
  20. ** * Neither the name of Digia Plc and its Subsidiary(-ies) nor the names
  21. ** of its contributors may be used to endorse or promote products derived
  22. ** from this software without specific prior written permission.
  23. **
  24. **
  25. ** THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
  26. ** "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
  27. ** LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
  28. ** A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
  29. ** OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
  30. ** SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
  31. ** LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
  32. ** DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
  33. ** THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
  34. ** (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
  35. ** OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE."
  36. **
  37. ** $QT_END_LICENSE$
  38. **
  39. ****************************************************************************/
  40. #pragma execution_character_set("utf-8")
  41. #include "qtgroupboxpropertybrowser.h"
  42. #include <QSet>
  43. #include <QGridLayout>
  44. #include <QLabel>
  45. #include <QGroupBox>
  46. #include <QTimer>
  47. #include <QMap>
  48. #if QT_VERSION >= 0x040400
  49. QT_BEGIN_NAMESPACE
  50. #endif
  51. class QtGroupBoxPropertyBrowserPrivate
  52. {
  53. QtGroupBoxPropertyBrowser *q_ptr = nullptr;
  54. Q_DECLARE_PUBLIC(QtGroupBoxPropertyBrowser)
  55. public:
  56. void init(QWidget *parent);
  57. void propertyInserted(QtBrowserItem *index, QtBrowserItem *afterIndex);
  58. void propertyRemoved(QtBrowserItem *index);
  59. void propertyChanged(QtBrowserItem *index);
  60. QWidget *createEditor(QtProperty *property, QWidget *parent) const
  61. { return q_ptr->createEditor(property, parent); }
  62. void slotEditorDestroyed();
  63. void slotUpdate();
  64. struct WidgetItem
  65. {
  66. WidgetItem() : widget(0), label(0), widgetLabel(0),
  67. groupBox(0), layout(0), line(0), parent(0) { }
  68. QWidget *widget; // can be null
  69. QLabel *label;
  70. QLabel *widgetLabel;
  71. QGroupBox *groupBox;
  72. QGridLayout *layout;
  73. QFrame *line;
  74. WidgetItem *parent;
  75. QList<WidgetItem *> children;
  76. };
  77. private:
  78. void updateLater();
  79. void updateItem(WidgetItem *item);
  80. void insertRow(QGridLayout *layout, int row) const;
  81. void removeRow(QGridLayout *layout, int row) const;
  82. bool hasHeader(WidgetItem *item) const;
  83. QMap<QtBrowserItem *, WidgetItem *> m_indexToItem;
  84. QMap<WidgetItem *, QtBrowserItem *> m_itemToIndex;
  85. QMap<QWidget *, WidgetItem *> m_widgetToItem;
  86. QGridLayout *m_mainLayout = nullptr;
  87. QList<WidgetItem *> m_children;
  88. QList<WidgetItem *> m_recreateQueue;
  89. };
  90. void QtGroupBoxPropertyBrowserPrivate::init(QWidget *parent)
  91. {
  92. m_mainLayout = new QGridLayout();
  93. parent->setLayout(m_mainLayout);
  94. QLayoutItem *item = new QSpacerItem(0, 0,
  95. QSizePolicy::Fixed, QSizePolicy::Expanding);
  96. m_mainLayout->addItem(item, 0, 0);
  97. }
  98. void QtGroupBoxPropertyBrowserPrivate::slotEditorDestroyed()
  99. {
  100. QWidget *editor = qobject_cast<QWidget *>(q_ptr->sender());
  101. if (!editor)
  102. return;
  103. if (!m_widgetToItem.contains(editor))
  104. return;
  105. m_widgetToItem[editor]->widget = 0;
  106. m_widgetToItem.remove(editor);
  107. }
  108. void QtGroupBoxPropertyBrowserPrivate::slotUpdate()
  109. {
  110. QListIterator<WidgetItem *> itItem(m_recreateQueue);
  111. while (itItem.hasNext()) {
  112. WidgetItem *item = itItem.next();
  113. WidgetItem *par = item->parent;
  114. QWidget *w = 0;
  115. QGridLayout *l = 0;
  116. int oldRow = -1;
  117. if (!par) {
  118. w = q_ptr;
  119. l = m_mainLayout;
  120. oldRow = m_children.indexOf(item);
  121. } else {
  122. w = par->groupBox;
  123. l = par->layout;
  124. oldRow = par->children.indexOf(item);
  125. if (hasHeader(par))
  126. oldRow += 2;
  127. }
  128. if (item->widget) {
  129. item->widget->setParent(w);
  130. } else if (item->widgetLabel) {
  131. item->widgetLabel->setParent(w);
  132. } else {
  133. item->widgetLabel = new QLabel(w);
  134. item->widgetLabel->setSizePolicy(QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed));
  135. item->widgetLabel->setTextFormat(Qt::PlainText);
  136. }
  137. int span = 1;
  138. if (item->widget)
  139. l->addWidget(item->widget, oldRow, 1, 1, 1);
  140. else if (item->widgetLabel)
  141. l->addWidget(item->widgetLabel, oldRow, 1, 1, 1);
  142. else
  143. span = 2;
  144. item->label = new QLabel(w);
  145. item->label->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
  146. l->addWidget(item->label, oldRow, 0, 1, span);
  147. updateItem(item);
  148. }
  149. m_recreateQueue.clear();
  150. }
  151. void QtGroupBoxPropertyBrowserPrivate::updateLater()
  152. {
  153. QTimer::singleShot(0, q_ptr, SLOT(slotUpdate()));
  154. }
  155. void QtGroupBoxPropertyBrowserPrivate::propertyInserted(QtBrowserItem *index, QtBrowserItem *afterIndex)
  156. {
  157. WidgetItem *afterItem = m_indexToItem.value(afterIndex);
  158. WidgetItem *parentItem = m_indexToItem.value(index->parent());
  159. WidgetItem *newItem = new WidgetItem();
  160. newItem->parent = parentItem;
  161. QGridLayout *layout = 0;
  162. QWidget *parentWidget = 0;
  163. int row = -1;
  164. if (!afterItem) {
  165. row = 0;
  166. if (parentItem)
  167. parentItem->children.insert(0, newItem);
  168. else
  169. m_children.insert(0, newItem);
  170. } else {
  171. if (parentItem) {
  172. row = parentItem->children.indexOf(afterItem) + 1;
  173. parentItem->children.insert(row, newItem);
  174. } else {
  175. row = m_children.indexOf(afterItem) + 1;
  176. m_children.insert(row, newItem);
  177. }
  178. }
  179. if (parentItem && hasHeader(parentItem))
  180. row += 2;
  181. if (!parentItem) {
  182. layout = m_mainLayout;
  183. parentWidget = q_ptr;;
  184. } else {
  185. if (!parentItem->groupBox) {
  186. m_recreateQueue.removeAll(parentItem);
  187. WidgetItem *par = parentItem->parent;
  188. QWidget *w = 0;
  189. QGridLayout *l = 0;
  190. int oldRow = -1;
  191. if (!par) {
  192. w = q_ptr;
  193. l = m_mainLayout;
  194. oldRow = m_children.indexOf(parentItem);
  195. } else {
  196. w = par->groupBox;
  197. l = par->layout;
  198. oldRow = par->children.indexOf(parentItem);
  199. if (hasHeader(par))
  200. oldRow += 2;
  201. }
  202. parentItem->groupBox = new QGroupBox(w);
  203. parentItem->layout = new QGridLayout();
  204. parentItem->groupBox->setLayout(parentItem->layout);
  205. if (parentItem->label) {
  206. l->removeWidget(parentItem->label);
  207. delete parentItem->label;
  208. parentItem->label = 0;
  209. }
  210. if (parentItem->widget) {
  211. l->removeWidget(parentItem->widget);
  212. parentItem->widget->setParent(parentItem->groupBox);
  213. parentItem->layout->addWidget(parentItem->widget, 0, 0, 1, 2);
  214. parentItem->line = new QFrame(parentItem->groupBox);
  215. } else if (parentItem->widgetLabel) {
  216. l->removeWidget(parentItem->widgetLabel);
  217. delete parentItem->widgetLabel;
  218. parentItem->widgetLabel = 0;
  219. }
  220. if (parentItem->line) {
  221. parentItem->line->setFrameShape(QFrame::HLine);
  222. parentItem->line->setFrameShadow(QFrame::Sunken);
  223. parentItem->layout->addWidget(parentItem->line, 1, 0, 1, 2);
  224. }
  225. l->addWidget(parentItem->groupBox, oldRow, 0, 1, 2);
  226. updateItem(parentItem);
  227. }
  228. layout = parentItem->layout;
  229. parentWidget = parentItem->groupBox;
  230. }
  231. newItem->label = new QLabel(parentWidget);
  232. newItem->label->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
  233. newItem->widget = createEditor(index->property(), parentWidget);
  234. if (!newItem->widget) {
  235. newItem->widgetLabel = new QLabel(parentWidget);
  236. newItem->widgetLabel->setSizePolicy(QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed));
  237. newItem->widgetLabel->setTextFormat(Qt::PlainText);
  238. } else {
  239. QObject::connect(newItem->widget, SIGNAL(destroyed()), q_ptr, SLOT(slotEditorDestroyed()));
  240. m_widgetToItem[newItem->widget] = newItem;
  241. }
  242. insertRow(layout, row);
  243. int span = 1;
  244. if (newItem->widget)
  245. layout->addWidget(newItem->widget, row, 1);
  246. else if (newItem->widgetLabel)
  247. layout->addWidget(newItem->widgetLabel, row, 1);
  248. else
  249. span = 2;
  250. layout->addWidget(newItem->label, row, 0, 1, span);
  251. m_itemToIndex[newItem] = index;
  252. m_indexToItem[index] = newItem;
  253. updateItem(newItem);
  254. }
  255. void QtGroupBoxPropertyBrowserPrivate::propertyRemoved(QtBrowserItem *index)
  256. {
  257. WidgetItem *item = m_indexToItem.value(index);
  258. m_indexToItem.remove(index);
  259. m_itemToIndex.remove(item);
  260. WidgetItem *parentItem = item->parent;
  261. int row = -1;
  262. if (parentItem) {
  263. row = parentItem->children.indexOf(item);
  264. parentItem->children.removeAt(row);
  265. if (hasHeader(parentItem))
  266. row += 2;
  267. } else {
  268. row = m_children.indexOf(item);
  269. m_children.removeAt(row);
  270. }
  271. if (item->widget)
  272. delete item->widget;
  273. if (item->label)
  274. delete item->label;
  275. if (item->widgetLabel)
  276. delete item->widgetLabel;
  277. if (item->groupBox)
  278. delete item->groupBox;
  279. if (!parentItem) {
  280. removeRow(m_mainLayout, row);
  281. } else if (parentItem->children.count() != 0) {
  282. removeRow(parentItem->layout, row);
  283. } else {
  284. WidgetItem *par = parentItem->parent;
  285. QGridLayout *l = 0;
  286. int oldRow = -1;
  287. if (!par) {
  288. l = m_mainLayout;
  289. oldRow = m_children.indexOf(parentItem);
  290. } else {
  291. l = par->layout;
  292. oldRow = par->children.indexOf(parentItem);
  293. if (hasHeader(par))
  294. oldRow += 2;
  295. }
  296. if (parentItem->widget) {
  297. parentItem->widget->hide();
  298. parentItem->widget->setParent(0);
  299. } else if (parentItem->widgetLabel) {
  300. parentItem->widgetLabel->hide();
  301. parentItem->widgetLabel->setParent(0);
  302. } else {
  303. //parentItem->widgetLabel = new QLabel(w);
  304. }
  305. l->removeWidget(parentItem->groupBox);
  306. delete parentItem->groupBox;
  307. parentItem->groupBox = 0;
  308. parentItem->line = 0;
  309. parentItem->layout = 0;
  310. if (!m_recreateQueue.contains(parentItem))
  311. m_recreateQueue.append(parentItem);
  312. updateLater();
  313. }
  314. m_recreateQueue.removeAll(item);
  315. delete item;
  316. }
  317. void QtGroupBoxPropertyBrowserPrivate::insertRow(QGridLayout *layout, int row) const
  318. {
  319. QMap<QLayoutItem *, QRect> itemToPos;
  320. int idx = 0;
  321. while (idx < layout->count()) {
  322. int r, c, rs, cs;
  323. layout->getItemPosition(idx, &r, &c, &rs, &cs);
  324. if (r >= row) {
  325. itemToPos[layout->takeAt(idx)] = QRect(r + 1, c, rs, cs);
  326. } else {
  327. idx++;
  328. }
  329. }
  330. const QMap<QLayoutItem *, QRect>::ConstIterator icend = itemToPos.constEnd();
  331. for (QMap<QLayoutItem *, QRect>::ConstIterator it = itemToPos.constBegin(); it != icend; ++it) {
  332. const QRect r = it.value();
  333. layout->addItem(it.key(), r.x(), r.y(), r.width(), r.height());
  334. }
  335. }
  336. void QtGroupBoxPropertyBrowserPrivate::removeRow(QGridLayout *layout, int row) const
  337. {
  338. QMap<QLayoutItem *, QRect> itemToPos;
  339. int idx = 0;
  340. while (idx < layout->count()) {
  341. int r, c, rs, cs;
  342. layout->getItemPosition(idx, &r, &c, &rs, &cs);
  343. if (r > row) {
  344. itemToPos[layout->takeAt(idx)] = QRect(r - 1, c, rs, cs);
  345. } else {
  346. idx++;
  347. }
  348. }
  349. const QMap<QLayoutItem *, QRect>::ConstIterator icend = itemToPos.constEnd();
  350. for (QMap<QLayoutItem *, QRect>::ConstIterator it = itemToPos.constBegin(); it != icend; ++it) {
  351. const QRect r = it.value();
  352. layout->addItem(it.key(), r.x(), r.y(), r.width(), r.height());
  353. }
  354. }
  355. bool QtGroupBoxPropertyBrowserPrivate::hasHeader(WidgetItem *item) const
  356. {
  357. if (item->widget)
  358. return true;
  359. return false;
  360. }
  361. void QtGroupBoxPropertyBrowserPrivate::propertyChanged(QtBrowserItem *index)
  362. {
  363. WidgetItem *item = m_indexToItem.value(index);
  364. updateItem(item);
  365. }
  366. void QtGroupBoxPropertyBrowserPrivate::updateItem(WidgetItem *item)
  367. {
  368. QtProperty *property = m_itemToIndex[item]->property();
  369. if (item->groupBox) {
  370. QFont font = item->groupBox->font();
  371. font.setUnderline(property->isModified());
  372. item->groupBox->setFont(font);
  373. item->groupBox->setTitle(property->propertyName());
  374. item->groupBox->setToolTip(property->toolTip());
  375. item->groupBox->setStatusTip(property->statusTip());
  376. item->groupBox->setWhatsThis(property->whatsThis());
  377. item->groupBox->setEnabled(property->isEnabled());
  378. }
  379. if (item->label) {
  380. QFont font = item->label->font();
  381. font.setUnderline(property->isModified());
  382. item->label->setFont(font);
  383. item->label->setText(property->propertyName());
  384. item->label->setToolTip(property->toolTip());
  385. item->label->setStatusTip(property->statusTip());
  386. item->label->setWhatsThis(property->whatsThis());
  387. item->label->setEnabled(property->isEnabled());
  388. }
  389. if (item->widgetLabel) {
  390. QFont font = item->widgetLabel->font();
  391. font.setUnderline(false);
  392. item->widgetLabel->setFont(font);
  393. item->widgetLabel->setText(property->valueText());
  394. item->widgetLabel->setToolTip(property->valueText());
  395. item->widgetLabel->setEnabled(property->isEnabled());
  396. }
  397. if (item->widget) {
  398. QFont font = item->widget->font();
  399. font.setUnderline(false);
  400. item->widget->setFont(font);
  401. item->widget->setEnabled(property->isEnabled());
  402. item->widget->setToolTip(property->valueText());
  403. }
  404. //item->setIcon(1, property->valueIcon());
  405. }
  406. /*!
  407. \class QtGroupBoxPropertyBrowser
  408. \brief The QtGroupBoxPropertyBrowser class provides a QGroupBox
  409. based property browser.
  410. A property browser is a widget that enables the user to edit a
  411. given set of properties. Each property is represented by a label
  412. specifying the property's name, and an editing widget (e.g. a line
  413. edit or a combobox) holding its value. A property can have zero or
  414. more subproperties.
  415. QtGroupBoxPropertyBrowser provides group boxes for all nested
  416. properties, i.e. subproperties are enclosed by a group box with
  417. the parent property's name as its title. For example:
  418. \image qtgroupboxpropertybrowser.png
  419. Use the QtAbstractPropertyBrowser API to add, insert and remove
  420. properties from an instance of the QtGroupBoxPropertyBrowser
  421. class. The properties themselves are created and managed by
  422. implementations of the QtAbstractPropertyManager class.
  423. \sa QtTreePropertyBrowser, QtAbstractPropertyBrowser
  424. */
  425. /*!
  426. Creates a property browser with the given \a parent.
  427. */
  428. QtGroupBoxPropertyBrowser::QtGroupBoxPropertyBrowser(QWidget *parent)
  429. : QtAbstractPropertyBrowser(parent)
  430. {
  431. d_ptr = new QtGroupBoxPropertyBrowserPrivate;
  432. d_ptr->q_ptr = this;
  433. d_ptr->init(this);
  434. }
  435. /*!
  436. Destroys this property browser.
  437. Note that the properties that were inserted into this browser are
  438. \e not destroyed since they may still be used in other
  439. browsers. The properties are owned by the manager that created
  440. them.
  441. \sa QtProperty, QtAbstractPropertyManager
  442. */
  443. QtGroupBoxPropertyBrowser::~QtGroupBoxPropertyBrowser()
  444. {
  445. const QMap<QtGroupBoxPropertyBrowserPrivate::WidgetItem *, QtBrowserItem *>::ConstIterator icend = d_ptr->m_itemToIndex.constEnd();
  446. for (QMap<QtGroupBoxPropertyBrowserPrivate::WidgetItem *, QtBrowserItem *>::ConstIterator it = d_ptr->m_itemToIndex.constBegin(); it != icend; ++it)
  447. delete it.key();
  448. delete d_ptr;
  449. }
  450. /*!
  451. \reimp
  452. */
  453. void QtGroupBoxPropertyBrowser::itemInserted(QtBrowserItem *item, QtBrowserItem *afterItem)
  454. {
  455. d_ptr->propertyInserted(item, afterItem);
  456. }
  457. /*!
  458. \reimp
  459. */
  460. void QtGroupBoxPropertyBrowser::itemRemoved(QtBrowserItem *item)
  461. {
  462. d_ptr->propertyRemoved(item);
  463. }
  464. /*!
  465. \reimp
  466. */
  467. void QtGroupBoxPropertyBrowser::itemChanged(QtBrowserItem *item)
  468. {
  469. d_ptr->propertyChanged(item);
  470. }
  471. #if QT_VERSION >= 0x040400
  472. QT_END_NAMESPACE
  473. #endif
  474. #include "moc_qtgroupboxpropertybrowser.cpp"