qttreepropertybrowser.cpp
Go to the documentation of this file.
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
41
43
44#include <QApplication>
45#include <QFocusEvent>
46#include <QHBoxLayout>
47#include <QHeaderView>
48#include <QIcon>
49#include <QItemDelegate>
50#include <QPainter>
51#include <QPalette>
52#include <QSet>
53#include <QStyle>
54#include <QTreeWidget>
55
56QT_BEGIN_NAMESPACE
57
59
61{
63 Q_DECLARE_PUBLIC(QtTreePropertyBrowser)
64
65public:
67 void init(QWidget* parent);
68
72
73 QWidget*
74 createEditor(QtProperty* property, QWidget* parent) const
75 {
76 return q_ptr->createEditor(property, parent);
77 }
78
79 QtProperty* indexToProperty(const QModelIndex& index) const;
80 QTreeWidgetItem* indexToItem(const QModelIndex& index) const;
81 QtBrowserItem* indexToBrowserItem(const QModelIndex& index) const;
82 bool lastColumn(int column) const;
83 void disableItem(QTreeWidgetItem* item) const;
84 void enableItem(QTreeWidgetItem* item) const;
85 bool hasValue(QTreeWidgetItem* item) const;
86
87 void slotCollapsed(const QModelIndex& index);
88 void slotExpanded(const QModelIndex& index);
89
90 QColor calculatedBackgroundColor(QtBrowserItem* item) const;
91
93 treeWidget() const
94 {
95 return m_treeWidget;
96 }
97
98 bool
100 {
101 return m_markPropertiesWithoutValue;
102 }
103
104 QtBrowserItem* currentItem() const;
105 void setCurrentItem(QtBrowserItem* browserItem, bool block);
106 void editItem(QtBrowserItem* browserItem);
107
109 void slotCurrentTreeItemChanged(QTreeWidgetItem* newItem, QTreeWidgetItem*);
110
111 QTreeWidgetItem* editedItem() const;
112
113private:
114 void updateItem(QTreeWidgetItem* item);
115
116 QMap<QtBrowserItem*, QTreeWidgetItem*> m_indexToItem;
117 QMap<QTreeWidgetItem*, QtBrowserItem*> m_itemToIndex;
118
119 QMap<QtBrowserItem*, QColor> m_indexToBackgroundColor;
120
121 QtPropertyEditorView* m_treeWidget;
122
123 bool m_headerVisible;
125 class QtPropertyEditorDelegate* m_delegate;
126 bool m_markPropertiesWithoutValue;
127 bool m_browserChangedBlocked;
128 QIcon m_expandIcon;
129};
130
131// ------------ QtPropertyEditorView
132class QtPropertyEditorView : public QTreeWidget
133{
134 Q_OBJECT
135public:
136 QtPropertyEditorView(QWidget* parent = 0);
137
138 void
140 {
141 m_editorPrivate = editorPrivate;
142 }
143
144 QTreeWidgetItem*
145 indexToItem(const QModelIndex& index) const
146 {
147 return itemFromIndex(index);
148 }
149
150protected:
151 void keyPressEvent(QKeyEvent* event) override;
152 void mousePressEvent(QMouseEvent* event) override;
153 void drawRow(QPainter* painter,
154 const QStyleOptionViewItem& option,
155 const QModelIndex& index) const override;
156
157private:
158 QtTreePropertyBrowserPrivate* m_editorPrivate;
159};
160
162 QTreeWidget(parent), m_editorPrivate(0)
163{
164 connect(header(), SIGNAL(sectionDoubleClicked(int)), this, SLOT(resizeColumnToContents(int)));
165}
166
167void
169 const QStyleOptionViewItem& option,
170 const QModelIndex& index) const
171{
172 QStyleOptionViewItem opt = option;
173 bool hasValue = true;
174
175 if (m_editorPrivate)
176 {
177 QtProperty* property = m_editorPrivate->indexToProperty(index);
178
179 if (property)
180 {
181 hasValue = property->hasValue();
182 }
183 }
184
185 if (!hasValue && m_editorPrivate->markPropertiesWithoutValue())
186 {
187 const QColor c = option.palette.color(QPalette::Dark);
188 painter->fillRect(option.rect, c);
189 opt.palette.setColor(QPalette::AlternateBase, c);
190 }
191 else
192 {
193 const QColor c =
194 m_editorPrivate->calculatedBackgroundColor(m_editorPrivate->indexToBrowserItem(index));
195
196 if (c.isValid())
197 {
198 painter->fillRect(option.rect, c);
199 opt.palette.setColor(QPalette::AlternateBase, c.lighter(112));
200 }
201 }
202
203 QTreeWidget::drawRow(painter, opt, index);
204 QColor color =
205 static_cast<QRgb>(QApplication::style()->styleHint(QStyle::SH_Table_GridLineColor, &opt));
206 painter->save();
207 painter->setPen(QPen(color));
208 painter->drawLine(opt.rect.x(), opt.rect.bottom(), opt.rect.right(), opt.rect.bottom());
209 painter->restore();
210}
211
212void
214{
215 switch (event->key())
216 {
217 case Qt::Key_Return:
218 case Qt::Key_Enter:
219 case Qt::Key_Space: // Trigger Edit
220 if (!m_editorPrivate->editedItem())
221 if (const QTreeWidgetItem* item = currentItem())
222 if (item->columnCount() >= 2 &&
223 ((item->flags() & (Qt::ItemIsEditable | Qt::ItemIsEnabled)) ==
224 (Qt::ItemIsEditable | Qt::ItemIsEnabled)))
225 {
226 event->accept();
227 // If the current position is at column 0, move to 1.
228 QModelIndex index = currentIndex();
229
230 if (index.column() == 0)
231 {
232 index = index.sibling(index.row(), 1);
233 setCurrentIndex(index);
234 }
235
236 edit(index);
237 return;
238 }
239
240 break;
241
242 default:
243 break;
244 }
245
246 QTreeWidget::keyPressEvent(event);
247}
248
249void
251{
252 QTreeWidget::mousePressEvent(event);
253 QTreeWidgetItem* item = itemAt(event->pos());
254
255 if (item)
256 {
257 if ((item != m_editorPrivate->editedItem()) && (event->button() == Qt::LeftButton) &&
258 (header()->logicalIndexAt(event->pos().x()) == 1) &&
259 ((item->flags() & (Qt::ItemIsEditable | Qt::ItemIsEnabled)) ==
260 (Qt::ItemIsEditable | Qt::ItemIsEnabled)))
261 {
262 editItem(item, 1);
263 }
264 else if (!m_editorPrivate->hasValue(item) &&
265 m_editorPrivate->markPropertiesWithoutValue() && !rootIsDecorated())
266 {
267 if (event->pos().x() + header()->offset() < 20)
268 {
269 item->setExpanded(!item->isExpanded());
270 }
271 }
272 }
273}
274
275// ------------ QtPropertyEditorDelegate
276class QtPropertyEditorDelegate : public QItemDelegate
277{
278 Q_OBJECT
279public:
280 QtPropertyEditorDelegate(QObject* parent = 0) :
281 QItemDelegate(parent),
282 m_editorPrivate(0),
283 m_editedItem(0),
284 m_editedWidget(0),
285 m_disablePainting(false)
286 {
287 }
288
289 void
291 {
292 m_editorPrivate = editorPrivate;
293 }
294
295 QWidget* createEditor(QWidget* parent,
296 const QStyleOptionViewItem& option,
297 const QModelIndex& index) const override;
298
299 void updateEditorGeometry(QWidget* editor,
300 const QStyleOptionViewItem& option,
301 const QModelIndex& index) const override;
302
303 void paint(QPainter* painter,
304 const QStyleOptionViewItem& option,
305 const QModelIndex& index) const override;
306
307 QSize sizeHint(const QStyleOptionViewItem& option, const QModelIndex& index) const override;
308
309 void
310 setModelData(QWidget*, QAbstractItemModel*, const QModelIndex&) const override
311 {
312 }
313
314 void
315 setEditorData(QWidget*, const QModelIndex&) const override
316 {
317 }
318
319 bool eventFilter(QObject* object, QEvent* event) override;
320 void closeEditor(QtProperty* property);
321
322 QTreeWidgetItem*
324 {
325 return m_editedItem;
326 }
327
328protected:
329 void drawDecoration(QPainter* painter,
330 const QStyleOptionViewItem& option,
331 const QRect& rect,
332 const QPixmap& pixmap) const override;
333 void drawDisplay(QPainter* painter,
334 const QStyleOptionViewItem& option,
335 const QRect& rect,
336 const QString& text) const override;
337
338private slots:
339 void slotEditorDestroyed(QObject* object);
340
341private:
342 int indentation(const QModelIndex& index) const;
343
344 using EditorToPropertyMap = QMap<QWidget*, QtProperty*>;
345 mutable EditorToPropertyMap m_editorToProperty;
346
347 using PropertyToEditorMap = QMap<QtProperty*, QWidget*>;
348 mutable PropertyToEditorMap m_propertyToEditor;
349 QtTreePropertyBrowserPrivate* m_editorPrivate;
350 mutable QTreeWidgetItem* m_editedItem;
351 mutable QWidget* m_editedWidget;
352 mutable bool m_disablePainting;
353};
354
355int
356QtPropertyEditorDelegate::indentation(const QModelIndex& index) const
357{
358 if (!m_editorPrivate)
359 {
360 return 0;
361 }
362
363 QTreeWidgetItem* item = m_editorPrivate->indexToItem(index);
364 int indent = 0;
365
366 while (item->parent())
367 {
368 item = item->parent();
369 ++indent;
370 }
371
372 if (m_editorPrivate->treeWidget()->rootIsDecorated())
373 {
374 ++indent;
375 }
376
377 return indent * m_editorPrivate->treeWidget()->indentation();
378}
379
380void
381QtPropertyEditorDelegate::slotEditorDestroyed(QObject* object)
382{
383 if (QWidget* w = qobject_cast<QWidget*>(object))
384 {
385 const EditorToPropertyMap::iterator it = m_editorToProperty.find(w);
386
387 if (it != m_editorToProperty.end())
388 {
389 m_propertyToEditor.remove(it.value());
390 m_editorToProperty.erase(it);
391 }
392
393 if (m_editedWidget == w)
394 {
395 m_editedWidget = 0;
396 m_editedItem = 0;
397 }
398 }
399}
400
401void
403{
404 if (QWidget* w = m_propertyToEditor.value(property, 0))
405 {
406 w->deleteLater();
407 }
408}
409
410QWidget*
412 const QStyleOptionViewItem&,
413 const QModelIndex& index) const
414{
415 if (index.column() == 1 && m_editorPrivate)
416 {
417 QtProperty* property = m_editorPrivate->indexToProperty(index);
418 QTreeWidgetItem* item = m_editorPrivate->indexToItem(index);
419
420 if (property && item && (item->flags() & Qt::ItemIsEnabled))
421 {
422 QWidget* editor = m_editorPrivate->createEditor(property, parent);
423
424 if (editor)
425 {
426 editor->setAutoFillBackground(true);
427 editor->installEventFilter(const_cast<QtPropertyEditorDelegate*>(this));
428 connect(
429 editor, SIGNAL(destroyed(QObject*)), this, SLOT(slotEditorDestroyed(QObject*)));
430 m_propertyToEditor[property] = editor;
431 m_editorToProperty[editor] = property;
432 m_editedItem = item;
433 m_editedWidget = editor;
434 }
435
436 return editor;
437 }
438 }
439
440 return 0;
441}
442
443void
445 const QStyleOptionViewItem& option,
446 const QModelIndex& index) const
447{
448 Q_UNUSED(index)
449 editor->setGeometry(option.rect.adjusted(0, 0, 0, -1));
450}
451
452void
454 const QStyleOptionViewItem& option,
455 const QModelIndex& index) const
456{
457 bool hasValue = true;
458
459 if (m_editorPrivate)
460 {
461 QtProperty* property = m_editorPrivate->indexToProperty(index);
462
463 if (property)
464 {
465 hasValue = property->hasValue();
466 }
467 }
468
469 QStyleOptionViewItem opt = option;
470
471 if ((m_editorPrivate && index.column() == 0) || !hasValue)
472 {
473 QtProperty* property = m_editorPrivate->indexToProperty(index);
474
475 if (property && property->isModified())
476 {
477 opt.font.setBold(true);
478 opt.fontMetrics = QFontMetrics(opt.font);
479 }
480 }
481
482 QColor c;
483
484 if (!hasValue && m_editorPrivate->markPropertiesWithoutValue())
485 {
486 c = opt.palette.color(QPalette::Dark);
487 opt.palette.setColor(QPalette::Text, opt.palette.color(QPalette::BrightText));
488 }
489 else
490 {
491 c = m_editorPrivate->calculatedBackgroundColor(m_editorPrivate->indexToBrowserItem(index));
492
493 if (c.isValid() && (opt.features & QStyleOptionViewItemV2::Alternate))
494 {
495 c = c.lighter(112);
496 }
497 }
498
499 if (c.isValid())
500 {
501 painter->fillRect(option.rect, c);
502 }
503
504 opt.state &= ~QStyle::State_HasFocus;
505
506 if (index.column() == 1)
507 {
508 QTreeWidgetItem* item = m_editorPrivate->indexToItem(index);
509
510 if (m_editedItem && m_editedItem == item)
511 {
512 m_disablePainting = true;
513 }
514 }
515
516 QItemDelegate::paint(painter, opt, index);
517
518 if (option.type)
519 {
520 m_disablePainting = false;
521 }
522
523 opt.palette.setCurrentColorGroup(QPalette::Active);
524 QColor color =
525 static_cast<QRgb>(QApplication::style()->styleHint(QStyle::SH_Table_GridLineColor, &opt));
526 painter->save();
527 painter->setPen(QPen(color));
528
529 if (!m_editorPrivate || (!m_editorPrivate->lastColumn(index.column()) && hasValue))
530 {
531 int right =
532 (option.direction == Qt::LeftToRight) ? option.rect.right() : option.rect.left();
533 painter->drawLine(right, option.rect.y(), right, option.rect.bottom());
534 }
535
536 painter->restore();
537}
538
539void
541 const QStyleOptionViewItem& option,
542 const QRect& rect,
543 const QPixmap& pixmap) const
544{
545 if (m_disablePainting)
546 {
547 return;
548 }
549
550 QItemDelegate::drawDecoration(painter, option, rect, pixmap);
551}
552
553void
555 const QStyleOptionViewItem& option,
556 const QRect& rect,
557 const QString& text) const
558{
559 if (m_disablePainting)
560 {
561 return;
562 }
563
564 QItemDelegate::drawDisplay(painter, option, rect, text);
565}
566
567QSize
568QtPropertyEditorDelegate::sizeHint(const QStyleOptionViewItem& option,
569 const QModelIndex& index) const
570{
571 return QItemDelegate::sizeHint(option, index) + QSize(3, 4);
572}
573
574bool
575QtPropertyEditorDelegate::eventFilter(QObject* object, QEvent* event)
576{
577 if (event->type() == QEvent::FocusOut)
578 {
579 QFocusEvent* fe = static_cast<QFocusEvent*>(event);
580
581 if (fe->reason() == Qt::ActiveWindowFocusReason)
582 {
583 return false;
584 }
585 }
586
587 return QItemDelegate::eventFilter(object, event);
588}
589
590// -------- QtTreePropertyBrowserPrivate implementation
592 m_treeWidget(0),
593 m_headerVisible(true),
594 m_resizeMode(QtTreePropertyBrowser::Stretch),
595 m_delegate(0),
596 m_markPropertiesWithoutValue(false),
597 m_browserChangedBlocked(false)
598{
599}
600
601// Draw an icon indicating opened/closing branches
602static QIcon
603drawIndicatorIcon(const QPalette& palette, QStyle* style)
604{
605 QPixmap pix(14, 14);
606 pix.fill(Qt::transparent);
607 QStyleOption branchOption;
608 QRect r(QPoint(0, 0), pix.size());
609 branchOption.rect = QRect(2, 2, 9, 9); // ### hardcoded in qcommonstyle.cpp
610 branchOption.palette = palette;
611 branchOption.state = QStyle::State_Children;
612
613 QPainter p;
614 // Draw closed state
615 p.begin(&pix);
616 style->drawPrimitive(QStyle::PE_IndicatorBranch, &branchOption, &p);
617 p.end();
618 QIcon rc = pix;
619 rc.addPixmap(pix, QIcon::Selected, QIcon::Off);
620 // Draw opened state
621 branchOption.state |= QStyle::State_Open;
622 pix.fill(Qt::transparent);
623 p.begin(&pix);
624 style->drawPrimitive(QStyle::PE_IndicatorBranch, &branchOption, &p);
625 p.end();
626
627 rc.addPixmap(pix, QIcon::Normal, QIcon::On);
628 rc.addPixmap(pix, QIcon::Selected, QIcon::On);
629 return rc;
630}
631
632void
634{
635 QHBoxLayout* layout = new QHBoxLayout(parent);
636 layout->setMargin(0);
637 m_treeWidget = new QtPropertyEditorView(parent);
638 m_treeWidget->setEditorPrivate(this);
639 m_treeWidget->setIconSize(QSize(18, 18));
640 layout->addWidget(m_treeWidget);
641 parent->setFocusProxy(m_treeWidget);
642
643 m_treeWidget->setColumnCount(2);
644 QStringList labels;
645 labels.append(QCoreApplication::translate("QtTreePropertyBrowser", "Property"));
646 labels.append(QCoreApplication::translate("QtTreePropertyBrowser", "Value"));
647 m_treeWidget->setHeaderLabels(labels);
648 m_treeWidget->setAlternatingRowColors(true);
649 m_treeWidget->setEditTriggers(QAbstractItemView::EditKeyPressed);
650 m_delegate = new QtPropertyEditorDelegate(parent);
651 m_delegate->setEditorPrivate(this);
652 m_treeWidget->setItemDelegate(m_delegate);
653 m_treeWidget->header()->setMovable(false);
654 m_treeWidget->header()->setResizeMode(QHeaderView::Stretch);
655
656 m_expandIcon = drawIndicatorIcon(q_ptr->palette(), q_ptr->style());
657
658 QObject::connect(m_treeWidget,
659 SIGNAL(collapsed(const QModelIndex&)),
660 q_ptr,
661 SLOT(slotCollapsed(const QModelIndex&)));
662 QObject::connect(m_treeWidget,
663 SIGNAL(expanded(const QModelIndex&)),
664 q_ptr,
665 SLOT(slotExpanded(const QModelIndex&)));
666 QObject::connect(m_treeWidget,
667 SIGNAL(currentItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)),
668 q_ptr,
669 SLOT(slotCurrentTreeItemChanged(QTreeWidgetItem*, QTreeWidgetItem*)));
670}
671
674{
675 if (QTreeWidgetItem* treeItem = m_treeWidget->currentItem())
676 {
677 return m_itemToIndex.value(treeItem);
678 }
679
680 return 0;
681}
682
683void
685{
686 const bool blocked = block ? m_treeWidget->blockSignals(true) : false;
687
688 if (browserItem == 0)
689 {
690 m_treeWidget->setCurrentItem(0);
691 }
692 else
693 {
694 m_treeWidget->setCurrentItem(m_indexToItem.value(browserItem));
695 }
696
697 if (block)
698 {
699 m_treeWidget->blockSignals(blocked);
700 }
701}
702
705{
706 QTreeWidgetItem* item = m_treeWidget->indexToItem(index);
707 QtBrowserItem* idx = m_itemToIndex.value(item);
708
709 if (idx)
710 {
711 return idx->property();
712 }
713
714 return 0;
715}
716
719{
720 QTreeWidgetItem* item = m_treeWidget->indexToItem(index);
721 return m_itemToIndex.value(item);
722}
723
724QTreeWidgetItem*
726{
727 return m_treeWidget->indexToItem(index);
728}
729
730bool
732{
733 return m_treeWidget->header()->visualIndex(column) == m_treeWidget->columnCount() - 1;
734}
735
736void
738{
739 Qt::ItemFlags flags = item->flags();
740
741 if (flags & Qt::ItemIsEnabled)
742 {
743 flags &= ~Qt::ItemIsEnabled;
744 item->setFlags(flags);
745 m_delegate->closeEditor(m_itemToIndex[item]->property());
746 const int childCount = item->childCount();
747
748 for (int i = 0; i < childCount; i++)
749 {
750 QTreeWidgetItem* child = item->child(i);
751 disableItem(child);
752 }
753 }
754}
755
756void
758{
759 Qt::ItemFlags flags = item->flags();
760 flags |= Qt::ItemIsEnabled;
761 item->setFlags(flags);
762 const int childCount = item->childCount();
763
764 for (int i = 0; i < childCount; i++)
765 {
766 QTreeWidgetItem* child = item->child(i);
767 QtProperty* property = m_itemToIndex[child]->property();
768
769 if (property->isEnabled())
770 {
771 enableItem(child);
772 }
773 }
774}
775
776bool
777QtTreePropertyBrowserPrivate::hasValue(QTreeWidgetItem* item) const
778{
779 QtBrowserItem* browserItem = m_itemToIndex.value(item);
780
781 if (browserItem)
782 {
783 return browserItem->property()->hasValue();
784 }
785
786 return false;
787}
788
789void
791{
792 QTreeWidgetItem* afterItem = m_indexToItem.value(afterIndex);
793 QTreeWidgetItem* parentItem = m_indexToItem.value(index->parent());
794
795 QTreeWidgetItem* newItem = 0;
796
797 if (parentItem)
798 {
799 newItem = new QTreeWidgetItem(parentItem, afterItem);
800 }
801 else
802 {
803 newItem = new QTreeWidgetItem(m_treeWidget, afterItem);
804 }
805
806 m_itemToIndex[newItem] = index;
807 m_indexToItem[index] = newItem;
808
809 newItem->setFlags(newItem->flags() | Qt::ItemIsEditable);
810 m_treeWidget->setItemExpanded(newItem, true);
811
812 updateItem(newItem);
813}
814
815void
817{
818 QTreeWidgetItem* item = m_indexToItem.value(index);
819
820 if (m_treeWidget->currentItem() == item)
821 {
822 m_treeWidget->setCurrentItem(0);
823 }
824
825 delete item;
826
827 m_indexToItem.remove(index);
828 m_itemToIndex.remove(item);
829 m_indexToBackgroundColor.remove(index);
830}
831
832void
834{
835 QTreeWidgetItem* item = m_indexToItem.value(index);
836
837 updateItem(item);
838}
839
840void
841QtTreePropertyBrowserPrivate::updateItem(QTreeWidgetItem* item)
842{
843 QtProperty* property = m_itemToIndex[item]->property();
844 QIcon expandIcon;
845
846 if (property->hasValue())
847 {
848 QString toolTip = property->toolTip();
849
850 if (toolTip.isEmpty())
851 {
852 toolTip = property->displayText();
853 }
854
855 item->setToolTip(1, toolTip);
856 item->setIcon(1, property->valueIcon());
857 property->displayText().isEmpty() ? item->setText(1, property->valueText())
858 : item->setText(1, property->displayText());
859 }
860 else if (markPropertiesWithoutValue() && !m_treeWidget->rootIsDecorated())
861 {
862 expandIcon = m_expandIcon;
863 }
864
865 item->setIcon(0, expandIcon);
866 item->setFirstColumnSpanned(!property->hasValue());
867 item->setToolTip(0, property->propertyName());
868 item->setStatusTip(0, property->statusTip());
869 item->setWhatsThis(0, property->whatsThis());
870 item->setText(0, property->propertyName());
871 bool wasEnabled = item->flags() & Qt::ItemIsEnabled;
872 bool isEnabled = wasEnabled;
873
874 if (property->isEnabled())
875 {
876 QTreeWidgetItem* parent = item->parent();
877
878 if (!parent || (parent->flags() & Qt::ItemIsEnabled))
879 {
880 isEnabled = true;
881 }
882 else
883 {
884 isEnabled = false;
885 }
886 }
887 else
888 {
889 isEnabled = false;
890 }
891
892 if (wasEnabled != isEnabled)
893 {
894 if (isEnabled)
895 {
896 enableItem(item);
897 }
898 else
899 {
900 disableItem(item);
901 }
902 }
903
904 m_treeWidget->viewport()->update();
905}
906
907QColor
909{
910 QtBrowserItem* i = item;
911 const QMap<QtBrowserItem*, QColor>::const_iterator itEnd = m_indexToBackgroundColor.constEnd();
912
913 while (i)
914 {
915 QMap<QtBrowserItem*, QColor>::const_iterator it = m_indexToBackgroundColor.constFind(i);
916
917 if (it != itEnd)
918 {
919 return it.value();
920 }
921
922 i = i->parent();
923 }
924
925 return QColor();
926}
927
928void
930{
931 QTreeWidgetItem* item = indexToItem(index);
932 QtBrowserItem* idx = m_itemToIndex.value(item);
933
934 if (item)
935 {
936 emit q_ptr->collapsed(idx);
937 }
938}
939
940void
942{
943 QTreeWidgetItem* item = indexToItem(index);
944 QtBrowserItem* idx = m_itemToIndex.value(item);
945
946 if (item)
947 {
948 emit q_ptr->expanded(idx);
949 }
950}
951
952void
954{
955 if (!m_browserChangedBlocked && item != currentItem())
956 {
957 setCurrentItem(item, true);
958 }
959}
960
961void
962QtTreePropertyBrowserPrivate::slotCurrentTreeItemChanged(QTreeWidgetItem* newItem, QTreeWidgetItem*)
963{
964 QtBrowserItem* browserItem = newItem ? m_itemToIndex.value(newItem) : 0;
965 m_browserChangedBlocked = true;
966 q_ptr->setCurrentItem(browserItem);
967 m_browserChangedBlocked = false;
968}
969
970QTreeWidgetItem*
972{
973 return m_delegate->editedItem();
974}
975
976void
978{
979 if (QTreeWidgetItem* treeItem = m_indexToItem.value(browserItem, 0))
980 {
981 m_treeWidget->setCurrentItem(treeItem, 1);
982 m_treeWidget->editItem(treeItem, 1);
983 }
984}
985
986/*!
987 \class QtTreePropertyBrowser
988
989 \brief The QtTreePropertyBrowser class provides QTreeWidget based
990 property browser.
991
992 A property browser is a widget that enables the user to edit a
993 given set of properties. Each property is represented by a label
994 specifying the property's name, and an editing widget (e.g. a line
995 edit or a combobox) holding its value. A property can have zero or
996 more subproperties.
997
998 QtTreePropertyBrowser provides a tree based view for all nested
999 properties, i.e. properties that have subproperties can be in an
1000 expanded (subproperties are visible) or collapsed (subproperties
1001 are hidden) state. For example:
1002
1003 \image qttreepropertybrowser.png
1004
1005 Use the QtAbstractPropertyBrowser API to add, insert and remove
1006 properties from an instance of the QtTreePropertyBrowser class.
1007 The properties themselves are created and managed by
1008 implementations of the QtAbstractPropertyManager class.
1009
1010 \sa QtGroupBoxPropertyBrowser, QtAbstractPropertyBrowser
1011*/
1012
1013/*!
1014 \fn void QtTreePropertyBrowser::collapsed(QtBrowserItem *item)
1015
1016 This signal is emitted when the \a item is collapsed.
1017
1018 \sa expanded(), setExpanded()
1019*/
1020
1021/*!
1022 \fn void QtTreePropertyBrowser::expanded(QtBrowserItem *item)
1023
1024 This signal is emitted when the \a item is expanded.
1025
1026 \sa collapsed(), setExpanded()
1027*/
1028
1029/*!
1030 Creates a property browser with the given \a parent.
1031*/
1033{
1034 d_ptr = new QtTreePropertyBrowserPrivate;
1035 d_ptr->q_ptr = this;
1036
1037 d_ptr->init(this);
1038 connect(this,
1040 this,
1041 SLOT(slotCurrentBrowserItemChanged(QtBrowserItem*)));
1042}
1043
1044/*!
1045 Destroys this property browser.
1046
1047 Note that the properties that were inserted into this browser are
1048 \e not destroyed since they may still be used in other
1049 browsers. The properties are owned by the manager that created
1050 them.
1051
1052 \sa QtProperty, QtAbstractPropertyManager
1053*/
1055{
1056 delete d_ptr;
1057}
1058
1059/*!
1060 \property QtTreePropertyBrowser::indentation
1061 \brief indentation of the items in the tree view.
1062*/
1063int
1065{
1066 return d_ptr->m_treeWidget->indentation();
1067}
1068
1069void
1071{
1072 d_ptr->m_treeWidget->setIndentation(i);
1073}
1074
1075/*!
1076 \property QtTreePropertyBrowser::rootIsDecorated
1077 \brief whether to show controls for expanding and collapsing root items.
1078*/
1079bool
1081{
1082 return d_ptr->m_treeWidget->rootIsDecorated();
1083}
1084
1085void
1087{
1088 d_ptr->m_treeWidget->setRootIsDecorated(show);
1089 QMapIterator<QTreeWidgetItem*, QtBrowserItem*> it(d_ptr->m_itemToIndex);
1090
1091 while (it.hasNext())
1092 {
1093 QtProperty* property = it.next().value()->property();
1094
1095 if (!property->hasValue())
1096 {
1097 d_ptr->updateItem(it.key());
1098 }
1099 }
1100}
1101
1102/*!
1103 \property QtTreePropertyBrowser::alternatingRowColors
1104 \brief whether to draw the background using alternating colors.
1105 By default this property is set to true.
1106*/
1107bool
1109{
1110 return d_ptr->m_treeWidget->alternatingRowColors();
1111}
1112
1113void
1115{
1116 d_ptr->m_treeWidget->setAlternatingRowColors(enable);
1117 QMapIterator<QTreeWidgetItem*, QtBrowserItem*> it(d_ptr->m_itemToIndex);
1118}
1119
1120/*!
1121 \property QtTreePropertyBrowser::headerVisible
1122 \brief whether to show the header.
1123*/
1124bool
1126{
1127 return d_ptr->m_headerVisible;
1128}
1129
1130void
1132{
1133 if (d_ptr->m_headerVisible == visible)
1134 {
1135 return;
1136 }
1137
1138 d_ptr->m_headerVisible = visible;
1139 d_ptr->m_treeWidget->header()->setVisible(visible);
1140}
1141
1142/*!
1143 \enum QtTreePropertyBrowser::ResizeMode
1144
1145 The resize mode specifies the behavior of the header sections.
1146
1147 \value Interactive The user can resize the sections.
1148 The sections can also be resized programmatically using setSplitterPosition().
1149
1150 \value Fixed The user cannot resize the section.
1151 The section can only be resized programmatically using setSplitterPosition().
1152
1153 \value Stretch QHeaderView will automatically resize the section to fill the available space.
1154 The size cannot be changed by the user or programmatically.
1155
1156 \value ResizeToContents QHeaderView will automatically resize the section to its optimal
1157 size based on the contents of the entire column.
1158 The size cannot be changed by the user or programmatically.
1159
1160 \sa setResizeMode()
1161*/
1162
1163/*!
1164 \property QtTreePropertyBrowser::resizeMode
1165 \brief the resize mode of setions in the header.
1166*/
1167
1170{
1171 return d_ptr->m_resizeMode;
1172}
1173
1174void
1176{
1177 if (d_ptr->m_resizeMode == mode)
1178 {
1179 return;
1180 }
1181
1182 d_ptr->m_resizeMode = mode;
1183 QHeaderView::ResizeMode m = QHeaderView::Stretch;
1184
1185 switch (mode)
1186 {
1188 m = QHeaderView::Interactive;
1189 break;
1190
1192 m = QHeaderView::Fixed;
1193 break;
1194
1196 m = QHeaderView::ResizeToContents;
1197 break;
1198
1200 default:
1201 m = QHeaderView::Stretch;
1202 break;
1203 }
1204
1205 d_ptr->m_treeWidget->header()->setResizeMode(m);
1206}
1207
1208/*!
1209 \property QtTreePropertyBrowser::splitterPosition
1210 \brief the position of the splitter between the colunms.
1211*/
1212
1213int
1215{
1216 return d_ptr->m_treeWidget->header()->sectionSize(0);
1217}
1218
1219void
1221{
1222 d_ptr->m_treeWidget->header()->resizeSection(0, position);
1223}
1224
1225/*!
1226 Sets the \a item to either collapse or expanded, depending on the value of \a expanded.
1227
1228 \sa isExpanded(), expanded(), collapsed()
1229*/
1230
1231void
1233{
1234 QTreeWidgetItem* treeItem = d_ptr->m_indexToItem.value(item);
1235
1236 if (treeItem)
1237 {
1238 treeItem->setExpanded(expanded);
1239 }
1240}
1241
1242/*!
1243 Returns true if the \a item is expanded; otherwise returns false.
1244
1245 \sa setExpanded()
1246*/
1247
1248bool
1250{
1251 QTreeWidgetItem* treeItem = d_ptr->m_indexToItem.value(item);
1252
1253 if (treeItem)
1254 {
1255 return treeItem->isExpanded();
1256 }
1257
1258 return false;
1259}
1260
1261/*!
1262 Returns true if the \a item is visible; otherwise returns false.
1263
1264 \sa setItemVisible()
1265 \since 4.5
1266*/
1267
1268bool
1270{
1271 if (const QTreeWidgetItem* treeItem = d_ptr->m_indexToItem.value(item))
1272 {
1273 return !treeItem->isHidden();
1274 }
1275
1276 return false;
1277}
1278
1279/*!
1280 Sets the \a item to be visible, depending on the value of \a visible.
1281
1282 \sa isItemVisible()
1283 \since 4.5
1284*/
1285
1286void
1288{
1289 if (QTreeWidgetItem* treeItem = d_ptr->m_indexToItem.value(item))
1290 {
1291 treeItem->setHidden(!visible);
1292 }
1293}
1294
1295/*!
1296 Sets the \a item's background color to \a color. Note that while item's background
1297 is rendered every second row is being drawn with alternate color (which is a bit lighter than items \a color)
1298
1299 \sa backgroundColor(), calculatedBackgroundColor()
1300*/
1301
1302void
1304{
1305 if (!d_ptr->m_indexToItem.contains(item))
1306 {
1307 return;
1308 }
1309
1310 if (color.isValid())
1311 {
1312 d_ptr->m_indexToBackgroundColor[item] = color;
1313 }
1314 else
1315 {
1316 d_ptr->m_indexToBackgroundColor.remove(item);
1317 }
1318
1319 d_ptr->m_treeWidget->viewport()->update();
1320}
1321
1322/*!
1323 Returns the \a item's color. If there is no color set for item it returns invalid color.
1324
1325 \sa calculatedBackgroundColor(), setBackgroundColor()
1326*/
1327
1328QColor
1330{
1331 return d_ptr->m_indexToBackgroundColor.value(item);
1332}
1333
1334/*!
1335 Returns the \a item's color. If there is no color set for item it returns parent \a item's
1336 color (if there is no color set for parent it returns grandparent's color and so on). In case
1337 the color is not set for \a item and it's top level item it returns invalid color.
1338
1339 \sa backgroundColor(), setBackgroundColor()
1340*/
1341
1342QColor
1344{
1345 return d_ptr->calculatedBackgroundColor(item);
1346}
1347
1348/*!
1349 \property QtTreePropertyBrowser::propertiesWithoutValueMarked
1350 \brief whether to enable or disable marking properties without value.
1351
1352 When marking is enabled the item's background is rendered in dark color and item's
1353 foreground is rendered with light color.
1354
1355 \sa propertiesWithoutValueMarked()
1356*/
1357void
1359{
1360 if (d_ptr->m_markPropertiesWithoutValue == mark)
1361 {
1362 return;
1363 }
1364
1365 d_ptr->m_markPropertiesWithoutValue = mark;
1366 QMapIterator<QTreeWidgetItem*, QtBrowserItem*> it(d_ptr->m_itemToIndex);
1367
1368 while (it.hasNext())
1369 {
1370 QtProperty* property = it.next().value()->property();
1371
1372 if (!property->hasValue())
1373 {
1374 d_ptr->updateItem(it.key());
1375 }
1376 }
1377
1378 d_ptr->m_treeWidget->viewport()->update();
1379}
1380
1381bool
1383{
1384 return d_ptr->m_markPropertiesWithoutValue;
1385}
1386
1387/*!
1388 \reimp
1389*/
1390void
1392{
1393 d_ptr->propertyInserted(item, afterItem);
1394}
1395
1396/*!
1397 \reimp
1398*/
1399void
1401{
1402 d_ptr->propertyRemoved(item);
1403}
1404
1405/*!
1406 \reimp
1407*/
1408void
1410{
1411 d_ptr->propertyChanged(item);
1412}
1413
1414/*!
1415 Sets the current item to \a item and opens the relevant editor for it.
1416*/
1417void
1419{
1420 d_ptr->editItem(item);
1421}
1422
1423QT_END_NAMESPACE
1424
1425#include "moc_qttreepropertybrowser.cpp"
1426#include "qttreepropertybrowser.moc"
uint8_t index
#define option(type, fn)
constexpr T c
QtAbstractPropertyBrowser(QWidget *parent=0)
void currentItemChanged(QtBrowserItem *)
The QtBrowserItem class represents a property in a property browser instance.
QtBrowserItem * parent() const
QtProperty * property() const
void setModelData(QWidget *, QAbstractItemModel *, const QModelIndex &) const override
void closeEditor(QtProperty *property)
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const override
bool eventFilter(QObject *object, QEvent *event) override
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override
void setEditorPrivate(QtTreePropertyBrowserPrivate *editorPrivate)
QtPropertyEditorDelegate(QObject *parent=0)
QWidget * createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override
void setEditorData(QWidget *, const QModelIndex &) const override
void drawDisplay(QPainter *painter, const QStyleOptionViewItem &option, const QRect &rect, const QString &text) const override
void drawDecoration(QPainter *painter, const QStyleOptionViewItem &option, const QRect &rect, const QPixmap &pixmap) const override
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override
QTreeWidgetItem * editedItem() const
QtPropertyEditorView(QWidget *parent=0)
void keyPressEvent(QKeyEvent *event) override
void drawRow(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override
void setEditorPrivate(QtTreePropertyBrowserPrivate *editorPrivate)
void mousePressEvent(QMouseEvent *event) override
QTreeWidgetItem * indexToItem(const QModelIndex &index) const
The QtProperty class encapsulates an instance of a property.
bool hasValue() const
void disableItem(QTreeWidgetItem *item) const
void slotCollapsed(const QModelIndex &index)
QColor calculatedBackgroundColor(QtBrowserItem *item) const
QtPropertyEditorView * treeWidget() const
void propertyChanged(QtBrowserItem *index)
void propertyRemoved(QtBrowserItem *index)
void editItem(QtBrowserItem *browserItem)
void propertyInserted(QtBrowserItem *index, QtBrowserItem *afterIndex)
void setCurrentItem(QtBrowserItem *browserItem, bool block)
void slotCurrentTreeItemChanged(QTreeWidgetItem *newItem, QTreeWidgetItem *)
void slotCurrentBrowserItemChanged(QtBrowserItem *item)
void slotExpanded(const QModelIndex &index)
void enableItem(QTreeWidgetItem *item) const
bool hasValue(QTreeWidgetItem *item) const
QTreeWidgetItem * indexToItem(const QModelIndex &index) const
QtBrowserItem * indexToBrowserItem(const QModelIndex &index) const
QWidget * createEditor(QtProperty *property, QWidget *parent) const
QtProperty * indexToProperty(const QModelIndex &index) const
QTreeWidgetItem * editedItem() const
The QtTreePropertyBrowser class provides QTreeWidget based property browser.
void setPropertiesWithoutValueMarked(bool mark)
bool alternatingRowColors
whether to draw the background using alternating colors. By default this property is set to true.
int splitterPosition
the position of the splitter between the colunms.
bool rootIsDecorated
whether to show controls for expanding and collapsing root items.
QtTreePropertyBrowser(QWidget *parent=0)
ResizeMode resizeMode
the resize mode of setions in the header.
int indentation
indentation of the items in the tree view.
QColor calculatedBackgroundColor(QtBrowserItem *item) const
void setResizeMode(ResizeMode mode)
bool propertiesWithoutValueMarked
whether to enable or disable marking properties without value.
void setSplitterPosition(int position)
void editItem(QtBrowserItem *item)
void setExpanded(QtBrowserItem *item, bool expanded)
void setItemVisible(QtBrowserItem *item, bool visible)
bool isItemVisible(QtBrowserItem *item) const
void setHeaderVisible(bool visible)
void itemRemoved(QtBrowserItem *item) override
void expanded(QtBrowserItem *item)
bool isExpanded(QtBrowserItem *item) const
QColor backgroundColor(QtBrowserItem *item) const
void setBackgroundColor(QtBrowserItem *item, const QColor &color)
void setAlternatingRowColors(bool enable)
void itemInserted(QtBrowserItem *item, QtBrowserItem *afterItem) override
void itemChanged(QtBrowserItem *item) override