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