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