1 /****************************************************************************
2 **
3 ** Copyright (C) 2015 The Qt Company Ltd.
4 ** Contact: http://www.qt.io/licensing/
5 **
6 ** This file is part of the tools applications of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL21$
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 http://www.qt.io/terms-conditions. For further
15 ** information use the contact form at http://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 2.1 or version 3 as published by the Free
20 ** Software Foundation and appearing in the file LICENSE.LGPLv21 and
21 ** LICENSE.LGPLv3 included in the packaging of this file. Please review the
22 ** following information to ensure the GNU Lesser General Public License
23 ** requirements will be met: https://www.gnu.org/licenses/lgpl.html and
24 ** http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
25 **
26 ** As a special exception, The Qt Company gives you certain additional
27 ** rights. These rights are described in The Qt Company LGPL Exception
28 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
29 **
30 ** $QT_END_LICENSE$
31 **
32 ****************************************************************************/
33 
34 
35 #include "qtgroupboxpropertybrowser.h"
36 #include <QtCore/QSet>
37 #include <QtWidgets/QGridLayout>
38 #include <QtWidgets/QLabel>
39 #include <QtWidgets/QGroupBox>
40 #include <QtCore/QTimer>
41 #include <QtCore/QMap>
42 
43 QT_BEGIN_NAMESPACE
44 
45 class QtGroupBoxPropertyBrowserPrivate
46 {
47     QtGroupBoxPropertyBrowser *q_ptr;
48     Q_DECLARE_PUBLIC(QtGroupBoxPropertyBrowser)
49 public:
50 
51     void init(QWidget *parent);
52 
53     void propertyInserted(QtBrowserItem *index, QtBrowserItem *afterIndex);
54     void propertyRemoved(QtBrowserItem *index);
55     void propertyChanged(QtBrowserItem *index);
createEditor(QtProperty * property,QWidget * parent) const56     QWidget *createEditor(QtProperty *property, QWidget *parent) const
57         { return q_ptr->createEditor(property, parent); }
58 
59     void slotEditorDestroyed();
60     void slotUpdate();
61 
62     struct WidgetItem
63     {
WidgetItemQtGroupBoxPropertyBrowserPrivate::WidgetItem64         WidgetItem() : widget(0), label(0), widgetLabel(0),
65                 groupBox(0), layout(0), line(0), parent(0) { }
66         QWidget *widget; // can be null
67         QLabel *label;
68         QLabel *widgetLabel;
69         QGroupBox *groupBox;
70         QGridLayout *layout;
71         QFrame *line;
72         WidgetItem *parent;
73         QList<WidgetItem *> children;
74     };
75 private:
76     void updateLater();
77     void updateItem(WidgetItem *item);
78     void insertRow(QGridLayout *layout, int row) const;
79     void removeRow(QGridLayout *layout, int row) const;
80 
81     bool hasHeader(WidgetItem *item) const;
82 
83     QMap<QtBrowserItem *, WidgetItem *> m_indexToItem;
84     QMap<WidgetItem *, QtBrowserItem *> m_itemToIndex;
85     QMap<QWidget *, WidgetItem *> m_widgetToItem;
86     QGridLayout *m_mainLayout;
87     QList<WidgetItem *> m_children;
88     QList<WidgetItem *> m_recreateQueue;
89 };
90 
init(QWidget * parent)91 void QtGroupBoxPropertyBrowserPrivate::init(QWidget *parent)
92 {
93     m_mainLayout = new QGridLayout();
94     parent->setLayout(m_mainLayout);
95     QLayoutItem *item = new QSpacerItem(0, 0,
96                 QSizePolicy::Fixed, QSizePolicy::Expanding);
97     m_mainLayout->addItem(item, 0, 0);
98 }
99 
slotEditorDestroyed()100 void QtGroupBoxPropertyBrowserPrivate::slotEditorDestroyed()
101 {
102     QWidget *editor = qobject_cast<QWidget *>(q_ptr->sender());
103     if (!editor)
104         return;
105     if (!m_widgetToItem.contains(editor))
106         return;
107     m_widgetToItem[editor]->widget = 0;
108     m_widgetToItem.remove(editor);
109 }
110 
slotUpdate()111 void QtGroupBoxPropertyBrowserPrivate::slotUpdate()
112 {
113     QListIterator<WidgetItem *> itItem(m_recreateQueue);
114     while (itItem.hasNext()) {
115         WidgetItem *item = itItem.next();
116 
117         WidgetItem *par = item->parent;
118         QWidget *w = 0;
119         QGridLayout *l = 0;
120         int oldRow = -1;
121         if (!par) {
122             w = q_ptr;
123             l = m_mainLayout;
124             oldRow = m_children.indexOf(item);
125         } else {
126             w = par->groupBox;
127             l = par->layout;
128             oldRow = par->children.indexOf(item);
129             if (hasHeader(par))
130                 oldRow += 2;
131         }
132 
133         if (item->widget) {
134             item->widget->setParent(w);
135         } else if (item->widgetLabel) {
136             item->widgetLabel->setParent(w);
137         } else {
138             item->widgetLabel = new QLabel(w);
139             item->widgetLabel->setSizePolicy(QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed));
140             item->widgetLabel->setTextFormat(Qt::PlainText);
141         }
142         int span = 1;
143         if (item->widget)
144             l->addWidget(item->widget, oldRow, 1, 1, 1);
145         else if (item->widgetLabel)
146             l->addWidget(item->widgetLabel, oldRow, 1, 1, 1);
147         else
148             span = 2;
149         item->label = new QLabel(w);
150         item->label->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
151         l->addWidget(item->label, oldRow, 0, 1, span);
152 
153         updateItem(item);
154     }
155     m_recreateQueue.clear();
156 }
157 
updateLater()158 void QtGroupBoxPropertyBrowserPrivate::updateLater()
159 {
160     QTimer::singleShot(0, q_ptr, SLOT(slotUpdate()));
161 }
162 
propertyInserted(QtBrowserItem * index,QtBrowserItem * afterIndex)163 void QtGroupBoxPropertyBrowserPrivate::propertyInserted(QtBrowserItem *index, QtBrowserItem *afterIndex)
164 {
165     WidgetItem *afterItem = m_indexToItem.value(afterIndex);
166     WidgetItem *parentItem = m_indexToItem.value(index->parent());
167 
168     WidgetItem *newItem = new WidgetItem();
169     newItem->parent = parentItem;
170 
171     QGridLayout *layout = 0;
172     QWidget *parentWidget = 0;
173     int row = -1;
174     if (!afterItem) {
175         row = 0;
176         if (parentItem)
177             parentItem->children.insert(0, newItem);
178         else
179             m_children.insert(0, newItem);
180     } else {
181         if (parentItem) {
182             row = parentItem->children.indexOf(afterItem) + 1;
183             parentItem->children.insert(row, newItem);
184         } else {
185             row = m_children.indexOf(afterItem) + 1;
186             m_children.insert(row, newItem);
187         }
188     }
189     if (parentItem && hasHeader(parentItem))
190         row += 2;
191 
192     if (!parentItem) {
193         layout = m_mainLayout;
194         parentWidget = q_ptr;;
195     } else {
196         if (!parentItem->groupBox) {
197             m_recreateQueue.removeAll(parentItem);
198             WidgetItem *par = parentItem->parent;
199             QWidget *w = 0;
200             QGridLayout *l = 0;
201             int oldRow = -1;
202             if (!par) {
203                 w = q_ptr;
204                 l = m_mainLayout;
205                 oldRow = m_children.indexOf(parentItem);
206             } else {
207                 w = par->groupBox;
208                 l = par->layout;
209                 oldRow = par->children.indexOf(parentItem);
210                 if (hasHeader(par))
211                     oldRow += 2;
212             }
213             parentItem->groupBox = new QGroupBox(w);
214             parentItem->layout = new QGridLayout();
215             parentItem->groupBox->setLayout(parentItem->layout);
216             if (parentItem->label) {
217                 l->removeWidget(parentItem->label);
218                 delete parentItem->label;
219                 parentItem->label = 0;
220             }
221             if (parentItem->widget) {
222                 l->removeWidget(parentItem->widget);
223                 parentItem->widget->setParent(parentItem->groupBox);
224                 parentItem->layout->addWidget(parentItem->widget, 0, 0, 1, 2);
225                 parentItem->line = new QFrame(parentItem->groupBox);
226             } else if (parentItem->widgetLabel) {
227                 l->removeWidget(parentItem->widgetLabel);
228                 delete parentItem->widgetLabel;
229                 parentItem->widgetLabel = 0;
230             }
231             if (parentItem->line) {
232                 parentItem->line->setFrameShape(QFrame::HLine);
233                 parentItem->line->setFrameShadow(QFrame::Sunken);
234                 parentItem->layout->addWidget(parentItem->line, 1, 0, 1, 2);
235             }
236             l->addWidget(parentItem->groupBox, oldRow, 0, 1, 2);
237             updateItem(parentItem);
238         }
239         layout = parentItem->layout;
240         parentWidget = parentItem->groupBox;
241     }
242 
243     newItem->label = new QLabel(parentWidget);
244     newItem->label->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
245     newItem->widget = createEditor(index->property(), parentWidget);
246     if (!newItem->widget) {
247         newItem->widgetLabel = new QLabel(parentWidget);
248         newItem->widgetLabel->setSizePolicy(QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed));
249         newItem->widgetLabel->setTextFormat(Qt::PlainText);
250     } else {
251         QObject::connect(newItem->widget, SIGNAL(destroyed()), q_ptr, SLOT(slotEditorDestroyed()));
252         m_widgetToItem[newItem->widget] = newItem;
253     }
254 
255     insertRow(layout, row);
256     int span = 1;
257     if (newItem->widget)
258         layout->addWidget(newItem->widget, row, 1);
259     else if (newItem->widgetLabel)
260         layout->addWidget(newItem->widgetLabel, row, 1);
261     else
262         span = 2;
263     layout->addWidget(newItem->label, row, 0, 1, span);
264 
265     m_itemToIndex[newItem] = index;
266     m_indexToItem[index] = newItem;
267 
268     updateItem(newItem);
269 }
270 
propertyRemoved(QtBrowserItem * index)271 void QtGroupBoxPropertyBrowserPrivate::propertyRemoved(QtBrowserItem *index)
272 {
273     WidgetItem *item = m_indexToItem.value(index);
274 
275     m_indexToItem.remove(index);
276     m_itemToIndex.remove(item);
277 
278     WidgetItem *parentItem = item->parent;
279 
280     int row = -1;
281 
282     if (parentItem) {
283         row = parentItem->children.indexOf(item);
284         parentItem->children.removeAt(row);
285         if (hasHeader(parentItem))
286             row += 2;
287     } else {
288         row = m_children.indexOf(item);
289         m_children.removeAt(row);
290     }
291 
292     if (item->widget)
293         delete item->widget;
294     if (item->label)
295         delete item->label;
296     if (item->widgetLabel)
297         delete item->widgetLabel;
298     if (item->groupBox)
299         delete item->groupBox;
300 
301     if (!parentItem) {
302         removeRow(m_mainLayout, row);
303     } else if (parentItem->children.count() != 0) {
304         removeRow(parentItem->layout, row);
305     } else {
306         WidgetItem *par = parentItem->parent;
307         QGridLayout *l = 0;
308         int oldRow = -1;
309         if (!par) {
310             l = m_mainLayout;
311             oldRow = m_children.indexOf(parentItem);
312         } else {
313             l = par->layout;
314             oldRow = par->children.indexOf(parentItem);
315             if (hasHeader(par))
316                 oldRow += 2;
317         }
318 
319         if (parentItem->widget) {
320             parentItem->widget->hide();
321             parentItem->widget->setParent(0);
322         } else if (parentItem->widgetLabel) {
323             parentItem->widgetLabel->hide();
324             parentItem->widgetLabel->setParent(0);
325         } else {
326             //parentItem->widgetLabel = new QLabel(w);
327         }
328         l->removeWidget(parentItem->groupBox);
329         delete parentItem->groupBox;
330         parentItem->groupBox = 0;
331         parentItem->line = 0;
332         parentItem->layout = 0;
333         if (!m_recreateQueue.contains(parentItem))
334             m_recreateQueue.append(parentItem);
335         updateLater();
336     }
337     m_recreateQueue.removeAll(item);
338 
339     delete item;
340 }
341 
insertRow(QGridLayout * layout,int row) const342 void QtGroupBoxPropertyBrowserPrivate::insertRow(QGridLayout *layout, int row) const
343 {
344     QMap<QLayoutItem *, QRect> itemToPos;
345     int idx = 0;
346     while (idx < layout->count()) {
347         int r, c, rs, cs;
348         layout->getItemPosition(idx, &r, &c, &rs, &cs);
349         if (r >= row) {
350             itemToPos[layout->takeAt(idx)] = QRect(r + 1, c, rs, cs);
351         } else {
352             idx++;
353         }
354     }
355 
356     const QMap<QLayoutItem *, QRect>::ConstIterator icend = itemToPos.constEnd();
357     for (QMap<QLayoutItem *, QRect>::ConstIterator it = itemToPos.constBegin(); it != icend; ++it) {
358         const QRect r = it.value();
359         layout->addItem(it.key(), r.x(), r.y(), r.width(), r.height());
360     }
361 }
362 
removeRow(QGridLayout * layout,int row) const363 void QtGroupBoxPropertyBrowserPrivate::removeRow(QGridLayout *layout, int row) const
364 {
365     QMap<QLayoutItem *, QRect> itemToPos;
366     int idx = 0;
367     while (idx < layout->count()) {
368         int r, c, rs, cs;
369         layout->getItemPosition(idx, &r, &c, &rs, &cs);
370         if (r > row) {
371             itemToPos[layout->takeAt(idx)] = QRect(r - 1, c, rs, cs);
372         } else {
373             idx++;
374         }
375     }
376 
377     const QMap<QLayoutItem *, QRect>::ConstIterator icend = itemToPos.constEnd();
378     for (QMap<QLayoutItem *, QRect>::ConstIterator it = itemToPos.constBegin(); it != icend; ++it) {
379         const QRect r = it.value();
380         layout->addItem(it.key(), r.x(), r.y(), r.width(), r.height());
381     }
382 }
383 
hasHeader(WidgetItem * item) const384 bool QtGroupBoxPropertyBrowserPrivate::hasHeader(WidgetItem *item) const
385 {
386     if (item->widget)
387         return true;
388     return false;
389 }
390 
propertyChanged(QtBrowserItem * index)391 void QtGroupBoxPropertyBrowserPrivate::propertyChanged(QtBrowserItem *index)
392 {
393     WidgetItem *item = m_indexToItem.value(index);
394 
395     updateItem(item);
396 }
397 
updateItem(WidgetItem * item)398 void QtGroupBoxPropertyBrowserPrivate::updateItem(WidgetItem *item)
399 {
400     QtProperty *property = m_itemToIndex[item]->property();
401     if (item->groupBox) {
402         QFont font = item->groupBox->font();
403         font.setUnderline(property->isModified());
404         item->groupBox->setFont(font);
405         item->groupBox->setTitle(property->propertyName());
406         item->groupBox->setToolTip(property->toolTip());
407         item->groupBox->setStatusTip(property->statusTip());
408         item->groupBox->setWhatsThis(property->whatsThis());
409         item->groupBox->setEnabled(property->isEnabled());
410     }
411     if (item->label) {
412         QFont font = item->label->font();
413         font.setUnderline(property->isModified());
414         item->label->setFont(font);
415         item->label->setText(property->propertyName());
416         item->label->setToolTip(property->toolTip());
417         item->label->setStatusTip(property->statusTip());
418         item->label->setWhatsThis(property->whatsThis());
419         item->label->setEnabled(property->isEnabled());
420     }
421     if (item->widgetLabel) {
422         QFont font = item->widgetLabel->font();
423         font.setUnderline(false);
424         item->widgetLabel->setFont(font);
425         item->widgetLabel->setText(property->valueText());
426         item->widgetLabel->setToolTip(property->valueText());
427         item->widgetLabel->setEnabled(property->isEnabled());
428     }
429     if (item->widget) {
430         QFont font = item->widget->font();
431         font.setUnderline(false);
432         item->widget->setFont(font);
433         item->widget->setEnabled(property->isEnabled());
434         item->widget->setToolTip(property->valueText());
435     }
436     //item->setIcon(1, property->valueIcon());
437 }
438 
439 
440 
441 /*!
442     \class QtGroupBoxPropertyBrowser
443 
444     \brief The QtGroupBoxPropertyBrowser class provides a QGroupBox
445     based property browser.
446 
447     A property browser is a widget that enables the user to edit a
448     given set of properties. Each property is represented by a label
449     specifying the property's name, and an editing widget (e.g. a line
450     edit or a combobox) holding its value. A property can have zero or
451     more subproperties.
452 
453     QtGroupBoxPropertyBrowser provides group boxes for all nested
454     properties, i.e. subproperties are enclosed by a group box with
455     the parent property's name as its title. For example:
456 
457     \image qtgroupboxpropertybrowser.png
458 
459     Use the QtAbstractPropertyBrowser API to add, insert and remove
460     properties from an instance of the QtGroupBoxPropertyBrowser
461     class. The properties themselves are created and managed by
462     implementations of the QtAbstractPropertyManager class.
463 
464     \sa QtTreePropertyBrowser, QtAbstractPropertyBrowser
465 */
466 
467 /*!
468     Creates a property browser with the given \a parent.
469 */
QtGroupBoxPropertyBrowser(QWidget * parent)470 QtGroupBoxPropertyBrowser::QtGroupBoxPropertyBrowser(QWidget *parent)
471     : QtAbstractPropertyBrowser(parent)
472 {
473     d_ptr = new QtGroupBoxPropertyBrowserPrivate;
474     d_ptr->q_ptr = this;
475 
476     d_ptr->init(this);
477 }
478 
479 /*!
480     Destroys this property browser.
481 
482     Note that the properties that were inserted into this browser are
483     \e not destroyed since they may still be used in other
484     browsers. The properties are owned by the manager that created
485     them.
486 
487     \sa QtProperty, QtAbstractPropertyManager
488 */
~QtGroupBoxPropertyBrowser()489 QtGroupBoxPropertyBrowser::~QtGroupBoxPropertyBrowser()
490 {
491     const QMap<QtGroupBoxPropertyBrowserPrivate::WidgetItem *, QtBrowserItem *>::ConstIterator icend = d_ptr->m_itemToIndex.constEnd();
492     for (QMap<QtGroupBoxPropertyBrowserPrivate::WidgetItem *, QtBrowserItem *>::ConstIterator it = d_ptr->m_itemToIndex.constBegin(); it != icend; ++it)
493         delete it.key();
494     delete d_ptr;
495 }
496 
497 /*!
498     \reimp
499 */
itemInserted(QtBrowserItem * item,QtBrowserItem * afterItem)500 void QtGroupBoxPropertyBrowser::itemInserted(QtBrowserItem *item, QtBrowserItem *afterItem)
501 {
502     d_ptr->propertyInserted(item, afterItem);
503 }
504 
505 /*!
506     \reimp
507 */
itemRemoved(QtBrowserItem * item)508 void QtGroupBoxPropertyBrowser::itemRemoved(QtBrowserItem *item)
509 {
510     d_ptr->propertyRemoved(item);
511 }
512 
513 /*!
514     \reimp
515 */
itemChanged(QtBrowserItem * item)516 void QtGroupBoxPropertyBrowser::itemChanged(QtBrowserItem *item)
517 {
518     d_ptr->propertyChanged(item);
519 }
520 
521 QT_END_NAMESPACE
522 
523 #include "moc_qtgroupboxpropertybrowser.cpp"
524