qttreepropertybrowser.cpp 35 KB

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