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

source code of qttools/src/shared/qtpropertybrowser/qttreepropertybrowser.cpp