qttreepropertybrowser.cpp 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050
  1. /****************************************************************************
  2. **
  3. ** Copyright (C) 2016 The Qt Company Ltd.
  4. ** Contact: https://www.qt.io/licensing/
  5. **
  6. ** This file is part of the tools applications 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 The Qt Company. For licensing terms
  14. ** and conditions see https://www.qt.io/terms-conditions. For further
  15. ** information use the contact form at https://www.qt.io/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 3 as published by the Free Software
  20. ** Foundation and appearing in the file LICENSE.LGPL3 included in the
  21. ** packaging of this file. Please review the following information to
  22. ** ensure the GNU Lesser General Public License version 3 requirements
  23. ** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
  24. **
  25. ** GNU General Public License Usage
  26. ** Alternatively, this file may be used under the terms of the GNU
  27. ** General Public License version 2.0 or (at your option) the GNU General
  28. ** Public license version 3 or any later version approved by the KDE Free
  29. ** Qt Foundation. The licenses are as published by the Free Software
  30. ** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
  31. ** included in the packaging of this file. Please review the following
  32. ** information to ensure the GNU General Public License requirements will
  33. ** be met: https://www.gnu.org/licenses/gpl-2.0.html and
  34. ** https://www.gnu.org/licenses/gpl-3.0.html.
  35. **
  36. ** $QT_END_LICENSE$
  37. **
  38. ****************************************************************************/
  39. #include "qttreepropertybrowser.h"
  40. #include <QtCore/QSet>
  41. #include <QtGui/QIcon>
  42. #include <QtWidgets/QTreeWidget>
  43. #include <QtWidgets/QItemDelegate>
  44. #include <QtWidgets/QHBoxLayout>
  45. #include <QtWidgets/QHeaderView>
  46. #include <QtGui/QPainter>
  47. #include <QtWidgets/QApplication>
  48. #include <QtGui/QFocusEvent>
  49. #include <QtWidgets/QStyle>
  50. #include <QtGui/QPalette>
  51. QT_BEGIN_NAMESPACE
  52. class QtPropertyEditorView;
  53. class QtTreePropertyBrowserPrivate
  54. {
  55. QtTreePropertyBrowser *q_ptr;
  56. Q_DECLARE_PUBLIC(QtTreePropertyBrowser)
  57. public:
  58. QtTreePropertyBrowserPrivate();
  59. void init(QWidget *parent);
  60. void propertyInserted(QtBrowserItem *index, QtBrowserItem *afterIndex);
  61. void propertyRemoved(QtBrowserItem *index);
  62. void propertyChanged(QtBrowserItem *index);
  63. QWidget *createEditor(QtProperty *property, QWidget *parent) const
  64. { return q_ptr->createEditor(property, parent); }
  65. QtProperty *indexToProperty(const QModelIndex &index) const;
  66. QTreeWidgetItem *indexToItem(const QModelIndex &index) const;
  67. QtBrowserItem *indexToBrowserItem(const QModelIndex &index) const;
  68. bool lastColumn(int column) const;
  69. void disableItem(QTreeWidgetItem *item) const;
  70. void enableItem(QTreeWidgetItem *item) const;
  71. bool hasValue(QTreeWidgetItem *item) const;
  72. void slotCollapsed(const QModelIndex &index);
  73. void slotExpanded(const QModelIndex &index);
  74. QColor calculatedBackgroundColor(QtBrowserItem *item) const;
  75. QtPropertyEditorView *treeWidget() const { return m_treeWidget; }
  76. bool markPropertiesWithoutValue() const { return m_markPropertiesWithoutValue; }
  77. QtBrowserItem *currentItem() const;
  78. void setCurrentItem(QtBrowserItem *browserItem, bool block);
  79. void editItem(QtBrowserItem *browserItem);
  80. void slotCurrentBrowserItemChanged(QtBrowserItem *item);
  81. void slotCurrentTreeItemChanged(QTreeWidgetItem *newItem, QTreeWidgetItem *);
  82. QTreeWidgetItem *editedItem() const;
  83. private:
  84. void updateItem(QTreeWidgetItem *item);
  85. QMap<QtBrowserItem *, QTreeWidgetItem *> m_indexToItem;
  86. QMap<QTreeWidgetItem *, QtBrowserItem *> m_itemToIndex;
  87. QMap<QtBrowserItem *, QColor> m_indexToBackgroundColor;
  88. QtPropertyEditorView *m_treeWidget;
  89. bool m_headerVisible;
  90. QtTreePropertyBrowser::ResizeMode m_resizeMode;
  91. class QtPropertyEditorDelegate *m_delegate;
  92. bool m_markPropertiesWithoutValue;
  93. bool m_browserChangedBlocked;
  94. QIcon m_expandIcon;
  95. };
  96. // ------------ QtPropertyEditorView
  97. class QtPropertyEditorView : public QTreeWidget
  98. {
  99. Q_OBJECT
  100. public:
  101. QtPropertyEditorView(QWidget *parent = 0);
  102. void setEditorPrivate(QtTreePropertyBrowserPrivate *editorPrivate)
  103. { m_editorPrivate = editorPrivate; }
  104. QTreeWidgetItem *indexToItem(const QModelIndex &index) const
  105. { return itemFromIndex(index); }
  106. protected:
  107. void keyPressEvent(QKeyEvent *event);
  108. void mousePressEvent(QMouseEvent *event);
  109. void drawRow(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
  110. private:
  111. QtTreePropertyBrowserPrivate *m_editorPrivate;
  112. };
  113. QtPropertyEditorView::QtPropertyEditorView(QWidget *parent) :
  114. QTreeWidget(parent),
  115. m_editorPrivate(0)
  116. {
  117. connect(header(), SIGNAL(sectionDoubleClicked(int)), this, SLOT(resizeColumnToContents(int)));
  118. }
  119. void QtPropertyEditorView::drawRow(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
  120. {
  121. QStyleOptionViewItem opt = option;
  122. bool hasValue = true;
  123. if (m_editorPrivate) {
  124. QtProperty *property = m_editorPrivate->indexToProperty(index);
  125. if (property)
  126. hasValue = property->hasValue();
  127. }
  128. if (!hasValue && m_editorPrivate->markPropertiesWithoutValue()) {
  129. const QColor c = option.palette.color(QPalette::Dark);
  130. painter->fillRect(option.rect, c);
  131. opt.palette.setColor(QPalette::AlternateBase, c);
  132. } else {
  133. const QColor c = m_editorPrivate->calculatedBackgroundColor(m_editorPrivate->indexToBrowserItem(index));
  134. if (c.isValid()) {
  135. painter->fillRect(option.rect, c);
  136. opt.palette.setColor(QPalette::AlternateBase, c.lighter(112));
  137. }
  138. }
  139. QTreeWidget::drawRow(painter, opt, index);
  140. QColor color = static_cast<QRgb>(QApplication::style()->styleHint(QStyle::SH_Table_GridLineColor, &opt));
  141. painter->save();
  142. painter->setPen(QPen(color));
  143. painter->drawLine(opt.rect.x(), opt.rect.bottom(), opt.rect.right(), opt.rect.bottom());
  144. painter->restore();
  145. }
  146. void QtPropertyEditorView::keyPressEvent(QKeyEvent *event)
  147. {
  148. switch (event->key()) {
  149. case Qt::Key_Return:
  150. case Qt::Key_Enter:
  151. case Qt::Key_Space: // Trigger Edit
  152. if (!m_editorPrivate->editedItem())
  153. if (const QTreeWidgetItem *item = currentItem())
  154. if (item->columnCount() >= 2 && ((item->flags() & (Qt::ItemIsEditable | Qt::ItemIsEnabled)) == (Qt::ItemIsEditable | Qt::ItemIsEnabled))) {
  155. event->accept();
  156. // If the current position is at column 0, move to 1.
  157. QModelIndex index = currentIndex();
  158. if (index.column() == 0) {
  159. index = index.sibling(index.row(), 1);
  160. setCurrentIndex(index);
  161. }
  162. edit(index);
  163. return;
  164. }
  165. break;
  166. default:
  167. break;
  168. }
  169. QTreeWidget::keyPressEvent(event);
  170. }
  171. void QtPropertyEditorView::mousePressEvent(QMouseEvent *event)
  172. {
  173. QTreeWidget::mousePressEvent(event);
  174. QTreeWidgetItem *item = itemAt(event->pos());
  175. if (item) {
  176. if ((item != m_editorPrivate->editedItem()) && (event->button() == Qt::LeftButton)
  177. && (header()->logicalIndexAt(event->pos().x()) == 1)
  178. && ((item->flags() & (Qt::ItemIsEditable | Qt::ItemIsEnabled)) == (Qt::ItemIsEditable | Qt::ItemIsEnabled))) {
  179. editItem(item, 1);
  180. } else if (!m_editorPrivate->hasValue(item) && m_editorPrivate->markPropertiesWithoutValue() && !rootIsDecorated()) {
  181. if (event->pos().x() + header()->offset() < 20)
  182. item->setExpanded(!item->isExpanded());
  183. }
  184. }
  185. }
  186. // ------------ QtPropertyEditorDelegate
  187. class QtPropertyEditorDelegate : public QItemDelegate
  188. {
  189. Q_OBJECT
  190. public:
  191. QtPropertyEditorDelegate(QObject *parent = 0)
  192. : QItemDelegate(parent), m_editorPrivate(0), m_editedItem(0), m_editedWidget(0)
  193. {}
  194. void setEditorPrivate(QtTreePropertyBrowserPrivate *editorPrivate)
  195. { m_editorPrivate = editorPrivate; }
  196. QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option,
  197. const QModelIndex &index) const;
  198. void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option,
  199. const QModelIndex &index) const;
  200. void paint(QPainter *painter, const QStyleOptionViewItem &option,
  201. const QModelIndex &index) const;
  202. QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const;
  203. void setModelData(QWidget *, QAbstractItemModel *,
  204. const QModelIndex &) const {}
  205. void setEditorData(QWidget *, const QModelIndex &) const {}
  206. bool eventFilter(QObject *object, QEvent *event);
  207. void closeEditor(QtProperty *property);
  208. QTreeWidgetItem *editedItem() const { return m_editedItem; }
  209. private slots:
  210. void slotEditorDestroyed(QObject *object);
  211. private:
  212. int indentation(const QModelIndex &index) const;
  213. typedef QMap<QWidget *, QtProperty *> EditorToPropertyMap;
  214. mutable EditorToPropertyMap m_editorToProperty;
  215. typedef QMap<QtProperty *, QWidget *> PropertyToEditorMap;
  216. mutable PropertyToEditorMap m_propertyToEditor;
  217. QtTreePropertyBrowserPrivate *m_editorPrivate;
  218. mutable QTreeWidgetItem *m_editedItem;
  219. mutable QWidget *m_editedWidget;
  220. };
  221. int QtPropertyEditorDelegate::indentation(const QModelIndex &index) const
  222. {
  223. if (!m_editorPrivate)
  224. return 0;
  225. QTreeWidgetItem *item = m_editorPrivate->indexToItem(index);
  226. int indent = 0;
  227. while (item->parent()) {
  228. item = item->parent();
  229. ++indent;
  230. }
  231. if (m_editorPrivate->treeWidget()->rootIsDecorated())
  232. ++indent;
  233. return indent * m_editorPrivate->treeWidget()->indentation();
  234. }
  235. void QtPropertyEditorDelegate::slotEditorDestroyed(QObject *object)
  236. {
  237. if (QWidget *w = qobject_cast<QWidget *>(object)) {
  238. const EditorToPropertyMap::iterator it = m_editorToProperty.find(w);
  239. if (it != m_editorToProperty.end()) {
  240. m_propertyToEditor.remove(it.value());
  241. m_editorToProperty.erase(it);
  242. }
  243. if (m_editedWidget == w) {
  244. m_editedWidget = 0;
  245. m_editedItem = 0;
  246. }
  247. }
  248. }
  249. void QtPropertyEditorDelegate::closeEditor(QtProperty *property)
  250. {
  251. if (QWidget *w = m_propertyToEditor.value(property, 0))
  252. w->deleteLater();
  253. }
  254. QWidget *QtPropertyEditorDelegate::createEditor(QWidget *parent,
  255. const QStyleOptionViewItem &, const QModelIndex &index) const
  256. {
  257. if (index.column() == 1 && m_editorPrivate) {
  258. QtProperty *property = m_editorPrivate->indexToProperty(index);
  259. QTreeWidgetItem *item = m_editorPrivate->indexToItem(index);
  260. if (property && item && (item->flags() & Qt::ItemIsEnabled)) {
  261. QWidget *editor = m_editorPrivate->createEditor(property, parent);
  262. if (editor) {
  263. editor->setAutoFillBackground(true);
  264. editor->installEventFilter(const_cast<QtPropertyEditorDelegate *>(this));
  265. connect(editor, SIGNAL(destroyed(QObject*)), this, SLOT(slotEditorDestroyed(QObject*)));
  266. m_propertyToEditor[property] = editor;
  267. m_editorToProperty[editor] = property;
  268. m_editedItem = item;
  269. m_editedWidget = editor;
  270. }
  271. return editor;
  272. }
  273. }
  274. return 0;
  275. }
  276. void QtPropertyEditorDelegate::updateEditorGeometry(QWidget *editor,
  277. const QStyleOptionViewItem &option, const QModelIndex &index) const
  278. {
  279. Q_UNUSED(index);
  280. editor->setGeometry(option.rect.adjusted(0, 0, 0, -1));
  281. }
  282. void QtPropertyEditorDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
  283. const QModelIndex &index) const
  284. {
  285. bool hasValue = true;
  286. if (m_editorPrivate) {
  287. QtProperty *property = m_editorPrivate->indexToProperty(index);
  288. if (property)
  289. hasValue = property->hasValue();
  290. }
  291. QStyleOptionViewItem opt = option;
  292. if ((m_editorPrivate && index.column() == 0) || !hasValue) {
  293. QtProperty *property = m_editorPrivate->indexToProperty(index);
  294. if (property && property->isModified()) {
  295. opt.font.setBold(true);
  296. opt.fontMetrics = QFontMetrics(opt.font);
  297. }
  298. }
  299. QColor c;
  300. if (!hasValue && m_editorPrivate->markPropertiesWithoutValue()) {
  301. c = opt.palette.color(QPalette::Dark);
  302. opt.palette.setColor(QPalette::Text, opt.palette.color(QPalette::BrightText));
  303. } else {
  304. c = m_editorPrivate->calculatedBackgroundColor(m_editorPrivate->indexToBrowserItem(index));
  305. if (c.isValid() && (opt.features & QStyleOptionViewItem::Alternate))
  306. c = c.lighter(112);
  307. }
  308. if (c.isValid())
  309. painter->fillRect(option.rect, c);
  310. opt.state &= ~QStyle::State_HasFocus;
  311. QItemDelegate::paint(painter, opt, index);
  312. opt.palette.setCurrentColorGroup(QPalette::Active);
  313. QColor color = static_cast<QRgb>(QApplication::style()->styleHint(QStyle::SH_Table_GridLineColor, &opt));
  314. painter->save();
  315. painter->setPen(QPen(color));
  316. if (!m_editorPrivate || (!m_editorPrivate->lastColumn(index.column()) && hasValue)) {
  317. int right = (option.direction == Qt::LeftToRight) ? option.rect.right() : option.rect.left();
  318. painter->drawLine(right, option.rect.y(), right, option.rect.bottom());
  319. }
  320. painter->restore();
  321. }
  322. QSize QtPropertyEditorDelegate::sizeHint(const QStyleOptionViewItem &option,
  323. const QModelIndex &index) const
  324. {
  325. return QItemDelegate::sizeHint(option, index) + QSize(3, 4);
  326. }
  327. bool QtPropertyEditorDelegate::eventFilter(QObject *object, QEvent *event)
  328. {
  329. if (event->type() == QEvent::FocusOut) {
  330. QFocusEvent *fe = static_cast<QFocusEvent *>(event);
  331. if (fe->reason() == Qt::ActiveWindowFocusReason)
  332. return false;
  333. }
  334. return QItemDelegate::eventFilter(object, event);
  335. }
  336. // -------- QtTreePropertyBrowserPrivate implementation
  337. QtTreePropertyBrowserPrivate::QtTreePropertyBrowserPrivate() :
  338. m_treeWidget(0),
  339. m_headerVisible(true),
  340. m_resizeMode(QtTreePropertyBrowser::Stretch),
  341. m_delegate(0),
  342. m_markPropertiesWithoutValue(false),
  343. m_browserChangedBlocked(false)
  344. {
  345. }
  346. // Draw an icon indicating opened/closing branches
  347. static QIcon drawIndicatorIcon(const QPalette &palette, QStyle *style)
  348. {
  349. QPixmap pix(14, 14);
  350. pix.fill(Qt::transparent);
  351. QStyleOption branchOption;
  352. branchOption.rect = QRect(2, 2, 9, 9); // ### hardcoded in qcommonstyle.cpp
  353. branchOption.palette = palette;
  354. branchOption.state = QStyle::State_Children;
  355. QPainter p;
  356. // Draw closed state
  357. p.begin(&pix);
  358. style->drawPrimitive(QStyle::PE_IndicatorBranch, &branchOption, &p);
  359. p.end();
  360. QIcon rc = pix;
  361. rc.addPixmap(pix, QIcon::Selected, QIcon::Off);
  362. // Draw opened state
  363. branchOption.state |= QStyle::State_Open;
  364. pix.fill(Qt::transparent);
  365. p.begin(&pix);
  366. style->drawPrimitive(QStyle::PE_IndicatorBranch, &branchOption, &p);
  367. p.end();
  368. rc.addPixmap(pix, QIcon::Normal, QIcon::On);
  369. rc.addPixmap(pix, QIcon::Selected, QIcon::On);
  370. return rc;
  371. }
  372. void QtTreePropertyBrowserPrivate::init(QWidget *parent)
  373. {
  374. QHBoxLayout *layout = new QHBoxLayout(parent);
  375. layout->setContentsMargins(QMargins());
  376. m_treeWidget = new QtPropertyEditorView(parent);
  377. m_treeWidget->setEditorPrivate(this);
  378. m_treeWidget->setIconSize(QSize(18, 18));
  379. layout->addWidget(m_treeWidget);
  380. m_treeWidget->setColumnCount(2);
  381. QStringList labels;
  382. labels.append(QCoreApplication::translate("QtTreePropertyBrowser", "Property"));
  383. labels.append(QCoreApplication::translate("QtTreePropertyBrowser", "Value"));
  384. m_treeWidget->setHeaderLabels(labels);
  385. m_treeWidget->setAlternatingRowColors(true);
  386. m_treeWidget->setEditTriggers(QAbstractItemView::EditKeyPressed);
  387. m_delegate = new QtPropertyEditorDelegate(parent);
  388. m_delegate->setEditorPrivate(this);
  389. m_treeWidget->setItemDelegate(m_delegate);
  390. m_treeWidget->header()->setSectionsMovable(false);
  391. // 属性表的表格宽度,是否固定不能调整
  392. m_treeWidget->header()->setSectionResizeMode(QHeaderView::Interactive);
  393. m_expandIcon = drawIndicatorIcon(q_ptr->palette(), q_ptr->style());
  394. QObject::connect(m_treeWidget, SIGNAL(collapsed(QModelIndex)), q_ptr, SLOT(slotCollapsed(QModelIndex)));
  395. QObject::connect(m_treeWidget, SIGNAL(expanded(QModelIndex)), q_ptr, SLOT(slotExpanded(QModelIndex)));
  396. QObject::connect(m_treeWidget, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), q_ptr, SLOT(slotCurrentTreeItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)));
  397. }
  398. QtBrowserItem *QtTreePropertyBrowserPrivate::currentItem() const
  399. {
  400. if (QTreeWidgetItem *treeItem = m_treeWidget->currentItem())
  401. return m_itemToIndex.value(treeItem);
  402. return 0;
  403. }
  404. void QtTreePropertyBrowserPrivate::setCurrentItem(QtBrowserItem *browserItem, bool block)
  405. {
  406. const bool blocked = block ? m_treeWidget->blockSignals(true) : false;
  407. if (browserItem == 0)
  408. m_treeWidget->setCurrentItem(0);
  409. else
  410. m_treeWidget->setCurrentItem(m_indexToItem.value(browserItem));
  411. if (block)
  412. m_treeWidget->blockSignals(blocked);
  413. }
  414. QtProperty *QtTreePropertyBrowserPrivate::indexToProperty(const QModelIndex &index) const
  415. {
  416. QTreeWidgetItem *item = m_treeWidget->indexToItem(index);
  417. QtBrowserItem *idx = m_itemToIndex.value(item);
  418. if (idx)
  419. return idx->property();
  420. return 0;
  421. }
  422. QtBrowserItem *QtTreePropertyBrowserPrivate::indexToBrowserItem(const QModelIndex &index) const
  423. {
  424. QTreeWidgetItem *item = m_treeWidget->indexToItem(index);
  425. return m_itemToIndex.value(item);
  426. }
  427. QTreeWidgetItem *QtTreePropertyBrowserPrivate::indexToItem(const QModelIndex &index) const
  428. {
  429. return m_treeWidget->indexToItem(index);
  430. }
  431. bool QtTreePropertyBrowserPrivate::lastColumn(int column) const
  432. {
  433. return m_treeWidget->header()->visualIndex(column) == m_treeWidget->columnCount() - 1;
  434. }
  435. void QtTreePropertyBrowserPrivate::disableItem(QTreeWidgetItem *item) const
  436. {
  437. Qt::ItemFlags flags = item->flags();
  438. if (flags & Qt::ItemIsEnabled) {
  439. flags &= ~Qt::ItemIsEnabled;
  440. item->setFlags(flags);
  441. m_delegate->closeEditor(m_itemToIndex[item]->property());
  442. const int childCount = item->childCount();
  443. for (int i = 0; i < childCount; i++) {
  444. QTreeWidgetItem *child = item->child(i);
  445. disableItem(child);
  446. }
  447. }
  448. }
  449. void QtTreePropertyBrowserPrivate::enableItem(QTreeWidgetItem *item) const
  450. {
  451. Qt::ItemFlags flags = item->flags();
  452. flags |= Qt::ItemIsEnabled;
  453. item->setFlags(flags);
  454. const int childCount = item->childCount();
  455. for (int i = 0; i < childCount; i++) {
  456. QTreeWidgetItem *child = item->child(i);
  457. QtProperty *property = m_itemToIndex[child]->property();
  458. if (property->isEnabled()) {
  459. enableItem(child);
  460. }
  461. }
  462. }
  463. bool QtTreePropertyBrowserPrivate::hasValue(QTreeWidgetItem *item) const
  464. {
  465. QtBrowserItem *browserItem = m_itemToIndex.value(item);
  466. if (browserItem)
  467. return browserItem->property()->hasValue();
  468. return false;
  469. }
  470. void QtTreePropertyBrowserPrivate::propertyInserted(QtBrowserItem *index, QtBrowserItem *afterIndex)
  471. {
  472. QTreeWidgetItem *afterItem = m_indexToItem.value(afterIndex);
  473. QTreeWidgetItem *parentItem = m_indexToItem.value(index->parent());
  474. QTreeWidgetItem *newItem = 0;
  475. if (parentItem) {
  476. newItem = new QTreeWidgetItem(parentItem, afterItem);
  477. } else {
  478. newItem = new QTreeWidgetItem(m_treeWidget, afterItem);
  479. }
  480. m_itemToIndex[newItem] = index;
  481. m_indexToItem[index] = newItem;
  482. newItem->setFlags(newItem->flags() | Qt::ItemIsEditable);
  483. newItem->setExpanded(true);
  484. updateItem(newItem);
  485. }
  486. void QtTreePropertyBrowserPrivate::propertyRemoved(QtBrowserItem *index)
  487. {
  488. QTreeWidgetItem *item = m_indexToItem.value(index);
  489. if (m_treeWidget->currentItem() == item) {
  490. m_treeWidget->setCurrentItem(0);
  491. }
  492. delete item;
  493. m_indexToItem.remove(index);
  494. m_itemToIndex.remove(item);
  495. m_indexToBackgroundColor.remove(index);
  496. }
  497. void QtTreePropertyBrowserPrivate::propertyChanged(QtBrowserItem *index)
  498. {
  499. QTreeWidgetItem *item = m_indexToItem.value(index);
  500. updateItem(item);
  501. }
  502. void QtTreePropertyBrowserPrivate::updateItem(QTreeWidgetItem *item)
  503. {
  504. QtProperty *property = m_itemToIndex[item]->property();
  505. QIcon expandIcon;
  506. if (property->hasValue()) {
  507. const QString valueToolTip = property->valueToolTip();
  508. const QString valueText = property->valueText();
  509. item->setToolTip(1, valueToolTip.isEmpty() ? valueText : valueToolTip);
  510. item->setIcon(1, property->valueIcon());
  511. item->setText(1, valueText);
  512. } else if (markPropertiesWithoutValue() && !m_treeWidget->rootIsDecorated()) {
  513. expandIcon = m_expandIcon;
  514. }
  515. item->setIcon(0, expandIcon);
  516. item->setFirstColumnSpanned(!property->hasValue());
  517. const QString descriptionToolTip = property->descriptionToolTip();
  518. const QString propertyName = property->propertyName();
  519. item->setToolTip(0, descriptionToolTip.isEmpty() ? propertyName : descriptionToolTip);
  520. item->setStatusTip(0, property->statusTip());
  521. item->setWhatsThis(0, property->whatsThis());
  522. item->setText(0, propertyName);
  523. bool wasEnabled = item->flags() & Qt::ItemIsEnabled;
  524. bool isEnabled = wasEnabled;
  525. if (property->isEnabled()) {
  526. QTreeWidgetItem *parent = item->parent();
  527. if (!parent || (parent->flags() & Qt::ItemIsEnabled))
  528. isEnabled = true;
  529. else
  530. isEnabled = false;
  531. } else {
  532. isEnabled = false;
  533. }
  534. if (wasEnabled != isEnabled) {
  535. if (isEnabled)
  536. enableItem(item);
  537. else
  538. disableItem(item);
  539. }
  540. m_treeWidget->viewport()->update();
  541. }
  542. QColor QtTreePropertyBrowserPrivate::calculatedBackgroundColor(QtBrowserItem *item) const
  543. {
  544. QtBrowserItem *i = item;
  545. const QMap<QtBrowserItem *, QColor>::const_iterator itEnd = m_indexToBackgroundColor.constEnd();
  546. while (i) {
  547. QMap<QtBrowserItem *, QColor>::const_iterator it = m_indexToBackgroundColor.constFind(i);
  548. if (it != itEnd)
  549. return it.value();
  550. i = i->parent();
  551. }
  552. return QColor();
  553. }
  554. void QtTreePropertyBrowserPrivate::slotCollapsed(const QModelIndex &index)
  555. {
  556. QTreeWidgetItem *item = indexToItem(index);
  557. QtBrowserItem *idx = m_itemToIndex.value(item);
  558. if (item)
  559. emit q_ptr->collapsed(idx);
  560. }
  561. void QtTreePropertyBrowserPrivate::slotExpanded(const QModelIndex &index)
  562. {
  563. QTreeWidgetItem *item = indexToItem(index);
  564. QtBrowserItem *idx = m_itemToIndex.value(item);
  565. if (item)
  566. emit q_ptr->expanded(idx);
  567. }
  568. void QtTreePropertyBrowserPrivate::slotCurrentBrowserItemChanged(QtBrowserItem *item)
  569. {
  570. if (!m_browserChangedBlocked && item != currentItem())
  571. setCurrentItem(item, true);
  572. }
  573. void QtTreePropertyBrowserPrivate::slotCurrentTreeItemChanged(QTreeWidgetItem *newItem, QTreeWidgetItem *)
  574. {
  575. QtBrowserItem *browserItem = newItem ? m_itemToIndex.value(newItem) : 0;
  576. m_browserChangedBlocked = true;
  577. q_ptr->setCurrentItem(browserItem);
  578. m_browserChangedBlocked = false;
  579. }
  580. QTreeWidgetItem *QtTreePropertyBrowserPrivate::editedItem() const
  581. {
  582. return m_delegate->editedItem();
  583. }
  584. void QtTreePropertyBrowserPrivate::editItem(QtBrowserItem *browserItem)
  585. {
  586. if (QTreeWidgetItem *treeItem = m_indexToItem.value(browserItem, 0)) {
  587. m_treeWidget->setCurrentItem (treeItem, 1);
  588. m_treeWidget->editItem(treeItem, 1);
  589. }
  590. }
  591. /*!
  592. \class QtTreePropertyBrowser
  593. \internal
  594. \inmodule QtDesigner
  595. \since 4.4
  596. \brief The QtTreePropertyBrowser class provides QTreeWidget based
  597. property browser.
  598. A property browser is a widget that enables the user to edit a
  599. given set of properties. Each property is represented by a label
  600. specifying the property's name, and an editing widget (e.g. a line
  601. edit or a combobox) holding its value. A property can have zero or
  602. more subproperties.
  603. QtTreePropertyBrowser provides a tree based view for all nested
  604. properties, i.e. properties that have subproperties can be in an
  605. expanded (subproperties are visible) or collapsed (subproperties
  606. are hidden) state. For example:
  607. \image qttreepropertybrowser.png
  608. Use the QtAbstractPropertyBrowser API to add, insert and remove
  609. properties from an instance of the QtTreePropertyBrowser class.
  610. The properties themselves are created and managed by
  611. implementations of the QtAbstractPropertyManager class.
  612. \sa QtGroupBoxPropertyBrowser, QtAbstractPropertyBrowser
  613. */
  614. /*!
  615. \fn void QtTreePropertyBrowser::collapsed(QtBrowserItem *item)
  616. This signal is emitted when the \a item is collapsed.
  617. \sa expanded(), setExpanded()
  618. */
  619. /*!
  620. \fn void QtTreePropertyBrowser::expanded(QtBrowserItem *item)
  621. This signal is emitted when the \a item is expanded.
  622. \sa collapsed(), setExpanded()
  623. */
  624. /*!
  625. Creates a property browser with the given \a parent.
  626. */
  627. QtTreePropertyBrowser::QtTreePropertyBrowser(QWidget *parent)
  628. : QtAbstractPropertyBrowser(parent), d_ptr(new QtTreePropertyBrowserPrivate)
  629. {
  630. d_ptr->q_ptr = this;
  631. d_ptr->init(this);
  632. connect(this, SIGNAL(currentItemChanged(QtBrowserItem*)), this, SLOT(slotCurrentBrowserItemChanged(QtBrowserItem*)));
  633. }
  634. /*!
  635. Destroys this property browser.
  636. Note that the properties that were inserted into this browser are
  637. \e not destroyed since they may still be used in other
  638. browsers. The properties are owned by the manager that created
  639. them.
  640. \sa QtProperty, QtAbstractPropertyManager
  641. */
  642. QtTreePropertyBrowser::~QtTreePropertyBrowser()
  643. {
  644. }
  645. /*!
  646. \property QtTreePropertyBrowser::indentation
  647. \brief indentation of the items in the tree view.
  648. */
  649. int QtTreePropertyBrowser::indentation() const
  650. {
  651. return d_ptr->m_treeWidget->indentation();
  652. }
  653. void QtTreePropertyBrowser::setIndentation(int i)
  654. {
  655. d_ptr->m_treeWidget->setIndentation(i);
  656. }
  657. /*!
  658. \property QtTreePropertyBrowser::rootIsDecorated
  659. \brief whether to show controls for expanding and collapsing root items.
  660. */
  661. bool QtTreePropertyBrowser::rootIsDecorated() const
  662. {
  663. return d_ptr->m_treeWidget->rootIsDecorated();
  664. }
  665. void QtTreePropertyBrowser::setRootIsDecorated(bool show)
  666. {
  667. d_ptr->m_treeWidget->setRootIsDecorated(show);
  668. for (auto it = d_ptr->m_itemToIndex.cbegin(), end = d_ptr->m_itemToIndex.cend(); it != end; ++it) {
  669. QtProperty *property = it.value()->property();
  670. if (!property->hasValue())
  671. d_ptr->updateItem(it.key());
  672. }
  673. }
  674. /*!
  675. \property QtTreePropertyBrowser::alternatingRowColors
  676. \brief whether to draw the background using alternating colors.
  677. By default this property is set to true.
  678. */
  679. bool QtTreePropertyBrowser::alternatingRowColors() const
  680. {
  681. return d_ptr->m_treeWidget->alternatingRowColors();
  682. }
  683. void QtTreePropertyBrowser::setAlternatingRowColors(bool enable)
  684. {
  685. d_ptr->m_treeWidget->setAlternatingRowColors(enable);
  686. }
  687. /*!
  688. \property QtTreePropertyBrowser::headerVisible
  689. \brief whether to show the header.
  690. */
  691. bool QtTreePropertyBrowser::isHeaderVisible() const
  692. {
  693. return d_ptr->m_headerVisible;
  694. }
  695. void QtTreePropertyBrowser::setHeaderVisible(bool visible)
  696. {
  697. if (d_ptr->m_headerVisible == visible)
  698. return;
  699. d_ptr->m_headerVisible = visible;
  700. d_ptr->m_treeWidget->header()->setVisible(visible);
  701. }
  702. /*!
  703. \enum QtTreePropertyBrowser::ResizeMode
  704. The resize mode specifies the behavior of the header sections.
  705. \value Interactive The user can resize the sections.
  706. The sections can also be resized programmatically using setSplitterPosition().
  707. \value Fixed The user cannot resize the section.
  708. The section can only be resized programmatically using setSplitterPosition().
  709. \value Stretch QHeaderView will automatically resize the section to fill the available space.
  710. The size cannot be changed by the user or programmatically.
  711. \value ResizeToContents QHeaderView will automatically resize the section to its optimal
  712. size based on the contents of the entire column.
  713. The size cannot be changed by the user or programmatically.
  714. \sa setResizeMode()
  715. */
  716. /*!
  717. \property QtTreePropertyBrowser::resizeMode
  718. \brief the resize mode of setions in the header.
  719. */
  720. QtTreePropertyBrowser::ResizeMode QtTreePropertyBrowser::resizeMode() const
  721. {
  722. return d_ptr->m_resizeMode;
  723. }
  724. void QtTreePropertyBrowser::setResizeMode(QtTreePropertyBrowser::ResizeMode mode)
  725. {
  726. if (d_ptr->m_resizeMode == mode)
  727. return;
  728. d_ptr->m_resizeMode = mode;
  729. QHeaderView::ResizeMode m = QHeaderView::Stretch;
  730. switch (mode) {
  731. case QtTreePropertyBrowser::Interactive: m = QHeaderView::Interactive; break;
  732. case QtTreePropertyBrowser::Fixed: m = QHeaderView::Fixed; break;
  733. case QtTreePropertyBrowser::ResizeToContents: m = QHeaderView::ResizeToContents; break;
  734. case QtTreePropertyBrowser::Stretch:
  735. default: m = QHeaderView::Stretch; break;
  736. }
  737. d_ptr->m_treeWidget->header()->setSectionResizeMode(m);
  738. }
  739. /*!
  740. \property QtTreePropertyBrowser::splitterPosition
  741. \brief the position of the splitter between the colunms.
  742. */
  743. int QtTreePropertyBrowser::splitterPosition() const
  744. {
  745. return d_ptr->m_treeWidget->header()->sectionSize(0);
  746. }
  747. void QtTreePropertyBrowser::setSplitterPosition(int position)
  748. {
  749. d_ptr->m_treeWidget->header()->resizeSection(0, position);
  750. }
  751. void QtTreePropertyBrowser::expandAll()
  752. {
  753. d_ptr->treeWidget()->expandAll();
  754. }
  755. void QtTreePropertyBrowser::collapseAll()
  756. {
  757. d_ptr->treeWidget()->collapseAll();
  758. }
  759. /*!
  760. Sets the \a item to either collapse or expanded, depending on the value of \a expanded.
  761. \sa isExpanded(), expanded(), collapsed()
  762. */
  763. void QtTreePropertyBrowser::setExpanded(QtBrowserItem *item, bool expanded)
  764. {
  765. QTreeWidgetItem *treeItem = d_ptr->m_indexToItem.value(item);
  766. if (treeItem)
  767. treeItem->setExpanded(expanded);
  768. }
  769. /*!
  770. Returns true if the \a item is expanded; otherwise returns false.
  771. \sa setExpanded()
  772. */
  773. bool QtTreePropertyBrowser::isExpanded(QtBrowserItem *item) const
  774. {
  775. QTreeWidgetItem *treeItem = d_ptr->m_indexToItem.value(item);
  776. if (treeItem)
  777. return treeItem->isExpanded();
  778. return false;
  779. }
  780. /*!
  781. Returns true if the \a item is visible; otherwise returns false.
  782. \sa setItemVisible()
  783. \since 4.5
  784. */
  785. bool QtTreePropertyBrowser::isItemVisible(QtBrowserItem *item) const
  786. {
  787. if (const QTreeWidgetItem *treeItem = d_ptr->m_indexToItem.value(item))
  788. return !treeItem->isHidden();
  789. return false;
  790. }
  791. /*!
  792. Sets the \a item to be visible, depending on the value of \a visible.
  793. \sa isItemVisible()
  794. \since 4.5
  795. */
  796. void QtTreePropertyBrowser::setItemVisible(QtBrowserItem *item, bool visible)
  797. {
  798. if (QTreeWidgetItem *treeItem = d_ptr->m_indexToItem.value(item))
  799. treeItem->setHidden(!visible);
  800. }
  801. /*!
  802. Sets the \a item's background color to \a color. Note that while item's background
  803. is rendered every second row is being drawn with alternate color (which is a bit lighter than items \a color)
  804. \sa backgroundColor(), calculatedBackgroundColor()
  805. */
  806. void QtTreePropertyBrowser::setBackgroundColor(QtBrowserItem *item, const QColor &color)
  807. {
  808. if (!d_ptr->m_indexToItem.contains(item))
  809. return;
  810. if (color.isValid())
  811. d_ptr->m_indexToBackgroundColor[item] = color;
  812. else
  813. d_ptr->m_indexToBackgroundColor.remove(item);
  814. d_ptr->m_treeWidget->viewport()->update();
  815. }
  816. /*!
  817. Returns the \a item's color. If there is no color set for item it returns invalid color.
  818. \sa calculatedBackgroundColor(), setBackgroundColor()
  819. */
  820. QColor QtTreePropertyBrowser::backgroundColor(QtBrowserItem *item) const
  821. {
  822. return d_ptr->m_indexToBackgroundColor.value(item);
  823. }
  824. /*!
  825. Returns the \a item's color. If there is no color set for item it returns parent \a item's
  826. color (if there is no color set for parent it returns grandparent's color and so on). In case
  827. the color is not set for \a item and it's top level item it returns invalid color.
  828. \sa backgroundColor(), setBackgroundColor()
  829. */
  830. QColor QtTreePropertyBrowser::calculatedBackgroundColor(QtBrowserItem *item) const
  831. {
  832. return d_ptr->calculatedBackgroundColor(item);
  833. }
  834. /*!
  835. \property QtTreePropertyBrowser::propertiesWithoutValueMarked
  836. \brief whether to enable or disable marking properties without value.
  837. When marking is enabled the item's background is rendered in dark color and item's
  838. foreground is rendered with light color.
  839. \sa propertiesWithoutValueMarked()
  840. */
  841. void QtTreePropertyBrowser::setPropertiesWithoutValueMarked(bool mark)
  842. {
  843. if (d_ptr->m_markPropertiesWithoutValue == mark)
  844. return;
  845. d_ptr->m_markPropertiesWithoutValue = mark;
  846. for (auto it = d_ptr->m_itemToIndex.cbegin(), end = d_ptr->m_itemToIndex.cend(); it != end; ++it) {
  847. QtProperty *property = it.value()->property();
  848. if (!property->hasValue())
  849. d_ptr->updateItem(it.key());
  850. }
  851. d_ptr->m_treeWidget->viewport()->update();
  852. }
  853. bool QtTreePropertyBrowser::propertiesWithoutValueMarked() const
  854. {
  855. return d_ptr->m_markPropertiesWithoutValue;
  856. }
  857. /*!
  858. \reimp
  859. */
  860. void QtTreePropertyBrowser::itemInserted(QtBrowserItem *item, QtBrowserItem *afterItem)
  861. {
  862. d_ptr->propertyInserted(item, afterItem);
  863. }
  864. /*!
  865. \reimp
  866. */
  867. void QtTreePropertyBrowser::itemRemoved(QtBrowserItem *item)
  868. {
  869. d_ptr->propertyRemoved(item);
  870. }
  871. /*!
  872. \reimp
  873. */
  874. void QtTreePropertyBrowser::itemChanged(QtBrowserItem *item)
  875. {
  876. d_ptr->propertyChanged(item);
  877. }
  878. /*!
  879. Sets the current item to \a item and opens the relevant editor for it.
  880. */
  881. void QtTreePropertyBrowser::editItem(QtBrowserItem *item)
  882. {
  883. d_ptr->editItem(item);
  884. }
  885. QT_END_NAMESPACE
  886. #include "moc_qttreepropertybrowser.cpp"
  887. #include "qttreepropertybrowser.moc"