source: trunk/tools/shared/qtpropertybrowser/qttreepropertybrowser.cpp@ 987

Last change on this file since 987 was 846, checked in by Dmitry A. Kuminov, 14 years ago

trunk: Merged in qt 4.7.2 sources from branches/vendor/nokia/qt.

File size: 33.6 KB
RevLine 
[2]1/****************************************************************************
2**
[846]3** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies).
[561]4** All rights reserved.
5** Contact: Nokia Corporation ([email protected])
[2]6**
7** This file is part of the tools applications of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial Usage
11** Licensees holding valid Qt Commercial licenses may use this file in
12** accordance with the Qt Commercial License Agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and Nokia.
15**
16** GNU Lesser General Public License Usage
17** Alternatively, this file may be used under the terms of the GNU Lesser
18** General Public License version 2.1 as published by the Free Software
19** Foundation and appearing in the file LICENSE.LGPL included in the
20** packaging of this file. Please review the following information to
21** ensure the GNU Lesser General Public License version 2.1 requirements
22** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
23**
[561]24** In addition, as a special exception, Nokia gives you certain additional
25** rights. These rights are described in the Nokia Qt LGPL Exception
26** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
[2]27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
[561]36** If you have questions regarding the use of this file, please contact
37** Nokia at [email protected].
[2]38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qttreepropertybrowser.h"
43#include <QtCore/QSet>
44#include <QtGui/QIcon>
45#include <QtGui/QTreeWidget>
46#include <QtGui/QItemDelegate>
47#include <QtGui/QHBoxLayout>
48#include <QtGui/QHeaderView>
49#include <QtGui/QPainter>
50#include <QtGui/QApplication>
51#include <QtGui/QFocusEvent>
52#include <QtGui/QStyle>
53#include <QtGui/QPalette>
54
55QT_BEGIN_NAMESPACE
56
57class QtPropertyEditorView;
58
59class QtTreePropertyBrowserPrivate
60{
61 QtTreePropertyBrowser *q_ptr;
62 Q_DECLARE_PUBLIC(QtTreePropertyBrowser)
63
64public:
65 QtTreePropertyBrowserPrivate();
66 void init(QWidget *parent);
67
68 void propertyInserted(QtBrowserItem *index, QtBrowserItem *afterIndex);
69 void propertyRemoved(QtBrowserItem *index);
70 void propertyChanged(QtBrowserItem *index);
71 QWidget *createEditor(QtProperty *property, QWidget *parent) const
72 { return q_ptr->createEditor(property, parent); }
73 QtProperty *indexToProperty(const QModelIndex &index) const;
74 QTreeWidgetItem *indexToItem(const QModelIndex &index) const;
75 QtBrowserItem *indexToBrowserItem(const QModelIndex &index) const;
76 bool lastColumn(int column) const;
77 void disableItem(QTreeWidgetItem *item) const;
78 void enableItem(QTreeWidgetItem *item) const;
79 bool hasValue(QTreeWidgetItem *item) const;
80
81 void slotCollapsed(const QModelIndex &index);
82 void slotExpanded(const QModelIndex &index);
83
84 QColor calculatedBackgroundColor(QtBrowserItem *item) const;
85
86 QtPropertyEditorView *treeWidget() const { return m_treeWidget; }
87 bool markPropertiesWithoutValue() const { return m_markPropertiesWithoutValue; }
88
89 QtBrowserItem *currentItem() const;
90 void setCurrentItem(QtBrowserItem *browserItem, bool block);
91 void editItem(QtBrowserItem *browserItem);
92
93 void slotCurrentBrowserItemChanged(QtBrowserItem *item);
94 void slotCurrentTreeItemChanged(QTreeWidgetItem *newItem, QTreeWidgetItem *);
95
96 QTreeWidgetItem *editedItem() const;
97
98private:
99 void updateItem(QTreeWidgetItem *item);
100
101 QMap<QtBrowserItem *, QTreeWidgetItem *> m_indexToItem;
102 QMap<QTreeWidgetItem *, QtBrowserItem *> m_itemToIndex;
103
104 QMap<QtBrowserItem *, QColor> m_indexToBackgroundColor;
105
106 QtPropertyEditorView *m_treeWidget;
107
108 bool m_headerVisible;
109 QtTreePropertyBrowser::ResizeMode m_resizeMode;
110 class QtPropertyEditorDelegate *m_delegate;
111 bool m_markPropertiesWithoutValue;
112 bool m_browserChangedBlocked;
113 QIcon m_expandIcon;
114};
115
116// ------------ QtPropertyEditorView
117class QtPropertyEditorView : public QTreeWidget
118{
119 Q_OBJECT
120public:
121 QtPropertyEditorView(QWidget *parent = 0);
122
123 void setEditorPrivate(QtTreePropertyBrowserPrivate *editorPrivate)
124 { m_editorPrivate = editorPrivate; }
125
126 QTreeWidgetItem *indexToItem(const QModelIndex &index) const
127 { return itemFromIndex(index); }
128
129protected:
130 void keyPressEvent(QKeyEvent *event);
131 void mousePressEvent(QMouseEvent *event);
132 void drawRow(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const;
133
134private:
135 QtTreePropertyBrowserPrivate *m_editorPrivate;
136};
137
138QtPropertyEditorView::QtPropertyEditorView(QWidget *parent) :
139 QTreeWidget(parent),
140 m_editorPrivate(0)
141{
142 connect(header(), SIGNAL(sectionDoubleClicked(int)), this, SLOT(resizeColumnToContents(int)));
143}
144
145void QtPropertyEditorView::drawRow(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
146{
147 QStyleOptionViewItemV3 opt = option;
148 bool hasValue = true;
149 if (m_editorPrivate) {
150 QtProperty *property = m_editorPrivate->indexToProperty(index);
151 if (property)
152 hasValue = property->hasValue();
153 }
154 if (!hasValue && m_editorPrivate->markPropertiesWithoutValue()) {
155 const QColor c = option.palette.color(QPalette::Dark);
156 painter->fillRect(option.rect, c);
157 opt.palette.setColor(QPalette::AlternateBase, c);
158 } else {
159 const QColor c = m_editorPrivate->calculatedBackgroundColor(m_editorPrivate->indexToBrowserItem(index));
160 if (c.isValid()) {
161 painter->fillRect(option.rect, c);
162 opt.palette.setColor(QPalette::AlternateBase, c.lighter(112));
163 }
164 }
165 QTreeWidget::drawRow(painter, opt, index);
166 QColor color = static_cast<QRgb>(QApplication::style()->styleHint(QStyle::SH_Table_GridLineColor, &opt));
167 painter->save();
168 painter->setPen(QPen(color));
169 painter->drawLine(opt.rect.x(), opt.rect.bottom(), opt.rect.right(), opt.rect.bottom());
170 painter->restore();
171}
172
173void QtPropertyEditorView::keyPressEvent(QKeyEvent *event)
174{
175 switch (event->key()) {
176 case Qt::Key_Return:
177 case Qt::Key_Enter:
178 case Qt::Key_Space: // Trigger Edit
179 if (!m_editorPrivate->editedItem())
180 if (const QTreeWidgetItem *item = currentItem())
181 if (item->columnCount() >= 2 && ((item->flags() & (Qt::ItemIsEditable | Qt::ItemIsEnabled)) == (Qt::ItemIsEditable | Qt::ItemIsEnabled))) {
182 event->accept();
183 // If the current position is at column 0, move to 1.
184 QModelIndex index = currentIndex();
185 if (index.column() == 0) {
186 index = index.sibling(index.row(), 1);
187 setCurrentIndex(index);
188 }
189 edit(index);
190 return;
191 }
192 break;
193 default:
194 break;
195 }
196 QTreeWidget::keyPressEvent(event);
197}
198
199void QtPropertyEditorView::mousePressEvent(QMouseEvent *event)
200{
201 QTreeWidget::mousePressEvent(event);
202 QTreeWidgetItem *item = itemAt(event->pos());
203
204 if (item) {
205 if ((item != m_editorPrivate->editedItem()) && (event->button() == Qt::LeftButton)
206 && (header()->logicalIndexAt(event->pos().x()) == 1)
207 && ((item->flags() & (Qt::ItemIsEditable | Qt::ItemIsEnabled)) == (Qt::ItemIsEditable | Qt::ItemIsEnabled))) {
208 editItem(item, 1);
209 } else if (!m_editorPrivate->hasValue(item) && m_editorPrivate->markPropertiesWithoutValue() && !rootIsDecorated()) {
210 if (event->pos().x() + header()->offset() < 20)
211 item->setExpanded(!item->isExpanded());
212 }
213 }
214}
215
216// ------------ QtPropertyEditorDelegate
217class QtPropertyEditorDelegate : public QItemDelegate
218{
219 Q_OBJECT
220public:
221 QtPropertyEditorDelegate(QObject *parent = 0)
222 : QItemDelegate(parent), m_editorPrivate(0), m_editedItem(0), m_editedWidget(0)
223 {}
224
225 void setEditorPrivate(QtTreePropertyBrowserPrivate *editorPrivate)
226 { m_editorPrivate = editorPrivate; }
227
228 QWidget *createEditor(QWidget *parent, const QStyleOptionViewItem &option,
229 const QModelIndex &index) const;
230
231 void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option,
232 const QModelIndex &index) const;
233
234 void paint(QPainter *painter, const QStyleOptionViewItem &option,
235 const QModelIndex &index) const;
236
237 QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const;
238
239 void setModelData(QWidget *, QAbstractItemModel *,
240 const QModelIndex &) const {}
241
242 void setEditorData(QWidget *, const QModelIndex &) const {}
243
244 bool eventFilter(QObject *object, QEvent *event);
245 void closeEditor(QtProperty *property);
246
247 QTreeWidgetItem *editedItem() const { return m_editedItem; }
248
249private slots:
250 void slotEditorDestroyed(QObject *object);
251
252private:
253 int indentation(const QModelIndex &index) const;
254
255 typedef QMap<QWidget *, QtProperty *> EditorToPropertyMap;
256 mutable EditorToPropertyMap m_editorToProperty;
257
258 typedef QMap<QtProperty *, QWidget *> PropertyToEditorMap;
259 mutable PropertyToEditorMap m_propertyToEditor;
260 QtTreePropertyBrowserPrivate *m_editorPrivate;
261 mutable QTreeWidgetItem *m_editedItem;
262 mutable QWidget *m_editedWidget;
263};
264
265int QtPropertyEditorDelegate::indentation(const QModelIndex &index) const
266{
267 if (!m_editorPrivate)
268 return 0;
269
270 QTreeWidgetItem *item = m_editorPrivate->indexToItem(index);
271 int indent = 0;
272 while (item->parent()) {
273 item = item->parent();
274 ++indent;
275 }
276 if (m_editorPrivate->treeWidget()->rootIsDecorated())
277 ++indent;
278 return indent * m_editorPrivate->treeWidget()->indentation();
279}
280
281void QtPropertyEditorDelegate::slotEditorDestroyed(QObject *object)
282{
283 if (QWidget *w = qobject_cast<QWidget *>(object)) {
284 const EditorToPropertyMap::iterator it = m_editorToProperty.find(w);
285 if (it != m_editorToProperty.end()) {
286 m_propertyToEditor.remove(it.value());
287 m_editorToProperty.erase(it);
288 }
289 if (m_editedWidget == w) {
290 m_editedWidget = 0;
291 m_editedItem = 0;
292 }
293 }
294}
295
296void QtPropertyEditorDelegate::closeEditor(QtProperty *property)
297{
298 if (QWidget *w = m_propertyToEditor.value(property, 0))
299 w->deleteLater();
300}
301
302QWidget *QtPropertyEditorDelegate::createEditor(QWidget *parent,
303 const QStyleOptionViewItem &, const QModelIndex &index) const
304{
305 if (index.column() == 1 && m_editorPrivate) {
306 QtProperty *property = m_editorPrivate->indexToProperty(index);
307 QTreeWidgetItem *item = m_editorPrivate->indexToItem(index);
308 if (property && item && (item->flags() & Qt::ItemIsEnabled)) {
309 QWidget *editor = m_editorPrivate->createEditor(property, parent);
310 if (editor) {
311 editor->setAutoFillBackground(true);
312 editor->installEventFilter(const_cast<QtPropertyEditorDelegate *>(this));
[561]313 connect(editor, SIGNAL(destroyed(QObject*)), this, SLOT(slotEditorDestroyed(QObject*)));
[2]314 m_propertyToEditor[property] = editor;
315 m_editorToProperty[editor] = property;
316 m_editedItem = item;
317 m_editedWidget = editor;
318 }
319 return editor;
320 }
321 }
322 return 0;
323}
324
325void QtPropertyEditorDelegate::updateEditorGeometry(QWidget *editor,
326 const QStyleOptionViewItem &option, const QModelIndex &index) const
327{
328 Q_UNUSED(index)
329 editor->setGeometry(option.rect.adjusted(0, 0, 0, -1));
330}
331
332void QtPropertyEditorDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
333 const QModelIndex &index) const
334{
335 bool hasValue = true;
336 if (m_editorPrivate) {
337 QtProperty *property = m_editorPrivate->indexToProperty(index);
338 if (property)
339 hasValue = property->hasValue();
340 }
341 QStyleOptionViewItemV3 opt = option;
342 if ((m_editorPrivate && index.column() == 0) || !hasValue) {
343 QtProperty *property = m_editorPrivate->indexToProperty(index);
344 if (property && property->isModified()) {
345 opt.font.setBold(true);
346 opt.fontMetrics = QFontMetrics(opt.font);
347 }
348 }
349 QColor c;
350 if (!hasValue && m_editorPrivate->markPropertiesWithoutValue()) {
351 c = opt.palette.color(QPalette::Dark);
352 opt.palette.setColor(QPalette::Text, opt.palette.color(QPalette::BrightText));
353 } else {
354 c = m_editorPrivate->calculatedBackgroundColor(m_editorPrivate->indexToBrowserItem(index));
355 if (c.isValid() && (opt.features & QStyleOptionViewItemV2::Alternate))
356 c = c.lighter(112);
357 }
358 if (c.isValid())
359 painter->fillRect(option.rect, c);
360 opt.state &= ~QStyle::State_HasFocus;
361 QItemDelegate::paint(painter, opt, index);
362
363 opt.palette.setCurrentColorGroup(QPalette::Active);
364 QColor color = static_cast<QRgb>(QApplication::style()->styleHint(QStyle::SH_Table_GridLineColor, &opt));
365 painter->save();
366 painter->setPen(QPen(color));
367 if (!m_editorPrivate || (!m_editorPrivate->lastColumn(index.column()) && hasValue)) {
368 int right = (option.direction == Qt::LeftToRight) ? option.rect.right() : option.rect.left();
369 painter->drawLine(right, option.rect.y(), right, option.rect.bottom());
370 }
371 painter->restore();
372}
373
374QSize QtPropertyEditorDelegate::sizeHint(const QStyleOptionViewItem &option,
375 const QModelIndex &index) const
376{
377 return QItemDelegate::sizeHint(option, index) + QSize(3, 4);
378}
379
380bool QtPropertyEditorDelegate::eventFilter(QObject *object, QEvent *event)
381{
382 if (event->type() == QEvent::FocusOut) {
383 QFocusEvent *fe = static_cast<QFocusEvent *>(event);
384 if (fe->reason() == Qt::ActiveWindowFocusReason)
385 return false;
386 }
387 return QItemDelegate::eventFilter(object, event);
388}
389
390// -------- QtTreePropertyBrowserPrivate implementation
391QtTreePropertyBrowserPrivate::QtTreePropertyBrowserPrivate() :
392 m_treeWidget(0),
393 m_headerVisible(true),
394 m_resizeMode(QtTreePropertyBrowser::Stretch),
395 m_delegate(0),
396 m_markPropertiesWithoutValue(false),
397 m_browserChangedBlocked(false)
398{
399}
400
401// Draw an icon indicating opened/closing branches
402static QIcon drawIndicatorIcon(const QPalette &palette, QStyle *style)
403{
404 QPixmap pix(14, 14);
405 pix.fill(Qt::transparent);
406 QStyleOption branchOption;
407 QRect r(QPoint(0, 0), pix.size());
408 branchOption.rect = QRect(2, 2, 9, 9); // ### hardcoded in qcommonstyle.cpp
409 branchOption.palette = palette;
410 branchOption.state = QStyle::State_Children;
411
412 QPainter p;
413 // Draw closed state
414 p.begin(&pix);
415 style->drawPrimitive(QStyle::PE_IndicatorBranch, &branchOption, &p);
416 p.end();
417 QIcon rc = pix;
418 rc.addPixmap(pix, QIcon::Selected, QIcon::Off);
419 // Draw opened state
420 branchOption.state |= QStyle::State_Open;
421 pix.fill(Qt::transparent);
422 p.begin(&pix);
423 style->drawPrimitive(QStyle::PE_IndicatorBranch, &branchOption, &p);
424 p.end();
425
426 rc.addPixmap(pix, QIcon::Normal, QIcon::On);
427 rc.addPixmap(pix, QIcon::Selected, QIcon::On);
428 return rc;
429}
430
431void QtTreePropertyBrowserPrivate::init(QWidget *parent)
432{
433 QHBoxLayout *layout = new QHBoxLayout(parent);
434 layout->setMargin(0);
435 m_treeWidget = new QtPropertyEditorView(parent);
436 m_treeWidget->setEditorPrivate(this);
437 m_treeWidget->setIconSize(QSize(18, 18));
438 layout->addWidget(m_treeWidget);
439
440 m_treeWidget->setColumnCount(2);
441 QStringList labels;
442 labels.append(QApplication::translate("QtTreePropertyBrowser", "Property", 0, QApplication::UnicodeUTF8));
443 labels.append(QApplication::translate("QtTreePropertyBrowser", "Value", 0, QApplication::UnicodeUTF8));
444 m_treeWidget->setHeaderLabels(labels);
445 m_treeWidget->setAlternatingRowColors(true);
446 m_treeWidget->setEditTriggers(QAbstractItemView::EditKeyPressed);
447 m_delegate = new QtPropertyEditorDelegate(parent);
448 m_delegate->setEditorPrivate(this);
449 m_treeWidget->setItemDelegate(m_delegate);
450 m_treeWidget->header()->setMovable(false);
451 m_treeWidget->header()->setResizeMode(QHeaderView::Stretch);
452
453 m_expandIcon = drawIndicatorIcon(q_ptr->palette(), q_ptr->style());
454
[561]455 QObject::connect(m_treeWidget, SIGNAL(collapsed(QModelIndex)), q_ptr, SLOT(slotCollapsed(QModelIndex)));
456 QObject::connect(m_treeWidget, SIGNAL(expanded(QModelIndex)), q_ptr, SLOT(slotExpanded(QModelIndex)));
[2]457 QObject::connect(m_treeWidget, SIGNAL(currentItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)), q_ptr, SLOT(slotCurrentTreeItemChanged(QTreeWidgetItem*,QTreeWidgetItem*)));
458}
459
460QtBrowserItem *QtTreePropertyBrowserPrivate::currentItem() const
461{
462 if (QTreeWidgetItem *treeItem = m_treeWidget->currentItem())
463 return m_itemToIndex.value(treeItem);
464 return 0;
465}
466
467void QtTreePropertyBrowserPrivate::setCurrentItem(QtBrowserItem *browserItem, bool block)
468{
469 const bool blocked = block ? m_treeWidget->blockSignals(true) : false;
470 if (browserItem == 0)
471 m_treeWidget->setCurrentItem(0);
472 else
473 m_treeWidget->setCurrentItem(m_indexToItem.value(browserItem));
474 if (block)
475 m_treeWidget->blockSignals(blocked);
476}
477
478QtProperty *QtTreePropertyBrowserPrivate::indexToProperty(const QModelIndex &index) const
479{
480 QTreeWidgetItem *item = m_treeWidget->indexToItem(index);
481 QtBrowserItem *idx = m_itemToIndex.value(item);
482 if (idx)
483 return idx->property();
484 return 0;
485}
486
487QtBrowserItem *QtTreePropertyBrowserPrivate::indexToBrowserItem(const QModelIndex &index) const
488{
489 QTreeWidgetItem *item = m_treeWidget->indexToItem(index);
490 return m_itemToIndex.value(item);
491}
492
493QTreeWidgetItem *QtTreePropertyBrowserPrivate::indexToItem(const QModelIndex &index) const
494{
495 return m_treeWidget->indexToItem(index);
496}
497
498bool QtTreePropertyBrowserPrivate::lastColumn(int column) const
499{
500 return m_treeWidget->header()->visualIndex(column) == m_treeWidget->columnCount() - 1;
501}
502
503void QtTreePropertyBrowserPrivate::disableItem(QTreeWidgetItem *item) const
504{
505 Qt::ItemFlags flags = item->flags();
506 if (flags & Qt::ItemIsEnabled) {
507 flags &= ~Qt::ItemIsEnabled;
508 item->setFlags(flags);
509 m_delegate->closeEditor(m_itemToIndex[item]->property());
510 const int childCount = item->childCount();
511 for (int i = 0; i < childCount; i++) {
512 QTreeWidgetItem *child = item->child(i);
513 disableItem(child);
514 }
515 }
516}
517
518void QtTreePropertyBrowserPrivate::enableItem(QTreeWidgetItem *item) const
519{
520 Qt::ItemFlags flags = item->flags();
521 flags |= Qt::ItemIsEnabled;
522 item->setFlags(flags);
523 const int childCount = item->childCount();
524 for (int i = 0; i < childCount; i++) {
525 QTreeWidgetItem *child = item->child(i);
526 QtProperty *property = m_itemToIndex[child]->property();
527 if (property->isEnabled()) {
528 enableItem(child);
529 }
530 }
531}
532
533bool QtTreePropertyBrowserPrivate::hasValue(QTreeWidgetItem *item) const
534{
535 QtBrowserItem *browserItem = m_itemToIndex.value(item);
536 if (browserItem)
537 return browserItem->property()->hasValue();
538 return false;
539}
540
541void QtTreePropertyBrowserPrivate::propertyInserted(QtBrowserItem *index, QtBrowserItem *afterIndex)
542{