qtgroupboxpropertybrowser.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 <QGridLayout>
45#include <QGroupBox>
46#include <QLabel>
47#include <QMap>
48#include <QSet>
49#include <QTimer>
50
51QT_BEGIN_NAMESPACE
52
54{
56 Q_DECLARE_PUBLIC(QtGroupBoxPropertyBrowser)
57public:
58 void init(QWidget* parent);
59
63
64 QWidget*
65 createEditor(QtProperty* property, QWidget* parent) const
66 {
67 return q_ptr->createEditor(property, parent);
68 }
69
71 void slotUpdate();
72
74 {
76 widget(0), label(0), widgetLabel(0), groupBox(0), layout(0), line(0), parent(0)
77 {
78 }
79
80 QWidget* widget; // can be null
81 QLabel* label;
82 QLabel* widgetLabel;
83 QGroupBox* groupBox;
84 QGridLayout* layout;
85 QFrame* line;
87 QList<WidgetItem*> children;
88 };
89
90private:
91 void updateLater();
92 void updateItem(WidgetItem* item);
93 void insertRow(QGridLayout* layout, int row) const;
94 void removeRow(QGridLayout* layout, int row) const;
95
96 bool hasHeader(WidgetItem* item) const;
97
98 QMap<QtBrowserItem*, WidgetItem*> m_indexToItem;
99 QMap<WidgetItem*, QtBrowserItem*> m_itemToIndex;
100 QMap<QWidget*, WidgetItem*> m_widgetToItem;
101 QGridLayout* m_mainLayout;
102 QList<WidgetItem*> m_children;
103 QList<WidgetItem*> m_recreateQueue;
104};
105
106void
108{
109 m_mainLayout = new QGridLayout();
110 parent->setLayout(m_mainLayout);
111 QLayoutItem* item = new QSpacerItem(0, 0, QSizePolicy::Fixed, QSizePolicy::Expanding);
112 m_mainLayout->addItem(item, 0, 0);
113}
114
115void
117{
118 QWidget* editor = qobject_cast<QWidget*>(q_ptr->sender());
119
120 if (!editor)
121 {
122 return;
123 }
124
125 if (!m_widgetToItem.contains(editor))
126 {
127 return;
128 }
129
130 m_widgetToItem[editor]->widget = 0;
131 m_widgetToItem.remove(editor);
132}
133
134void
136{
137 QListIterator<WidgetItem*> itItem(m_recreateQueue);
138
139 while (itItem.hasNext())
140 {
141 WidgetItem* item = itItem.next();
142
143 WidgetItem* par = item->parent;
144 QWidget* w = 0;
145 QGridLayout* l = 0;
146 int oldRow = -1;
147
148 if (!par)
149 {
150 w = q_ptr;
151 l = m_mainLayout;
152 oldRow = m_children.indexOf(item);
153 }
154 else
155 {
156 w = par->groupBox;
157 l = par->layout;
158 oldRow = par->children.indexOf(item);
159
160 if (hasHeader(par))
161 {
162 oldRow += 2;
163 }
164 }
165
166 if (item->widget)
167 {
168 item->widget->setParent(w);
169 }
170 else if (item->widgetLabel)
171 {
172 item->widgetLabel->setParent(w);
173 }
174 else
175 {
176 item->widgetLabel = new QLabel(w);
177 item->widgetLabel->setSizePolicy(QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed));
178 item->widgetLabel->setTextFormat(Qt::PlainText);
179 }
180
181 int span = 1;
182
183 if (item->widget)
184 {
185 l->addWidget(item->widget, oldRow, 1, 1, 1);
186 }
187 else if (item->widgetLabel)
188 {
189 l->addWidget(item->widgetLabel, oldRow, 1, 1, 1);
190 }
191 else
192 {
193 span = 2;
194 }
195
196 item->label = new QLabel(w);
197 item->label->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
198 l->addWidget(item->label, oldRow, 0, 1, span);
199
200 updateItem(item);
201 }
202
203 m_recreateQueue.clear();
204}
205
206void
207QtGroupBoxPropertyBrowserPrivate::updateLater()
208{
209 QTimer::singleShot(0, q_ptr, SLOT(slotUpdate()));
210}
211
212void
214{
215 WidgetItem* afterItem = m_indexToItem.value(afterIndex);
216 WidgetItem* parentItem = m_indexToItem.value(index->parent());
217
218 WidgetItem* newItem = new WidgetItem();
219 newItem->parent = parentItem;
220
221 QGridLayout* layout = 0;
222 QWidget* parentWidget = 0;
223 int row = -1;
224
225 if (!afterItem)
226 {
227 row = 0;
228
229 if (parentItem)
230 {
231 parentItem->children.insert(0, newItem);
232 }
233 else
234 {
235 m_children.insert(0, newItem);
236 }
237 }
238 else
239 {
240 if (parentItem)
241 {
242 row = parentItem->children.indexOf(afterItem) + 1;
243 parentItem->children.insert(row, newItem);
244 }
245 else
246 {
247 row = m_children.indexOf(afterItem) + 1;
248 m_children.insert(row, newItem);
249 }
250 }
251
252 if (parentItem && hasHeader(parentItem))
253 {
254 row += 2;
255 }
256
257 if (!parentItem)
258 {
259 layout = m_mainLayout;
260 parentWidget = q_ptr;
261 ;
262 }
263 else
264 {
265 if (!parentItem->groupBox)
266 {
267 m_recreateQueue.removeAll(parentItem);
268 WidgetItem* par = parentItem->parent;
269 QWidget* w = 0;
270 QGridLayout* l = 0;
271 int oldRow = -1;
272
273 if (!par)
274 {
275 w = q_ptr;
276 l = m_mainLayout;
277 oldRow = m_children.indexOf(parentItem);
278 }
279 else
280 {
281 w = par->groupBox;
282 l = par->layout;
283 oldRow = par->children.indexOf(parentItem);
284
285 if (hasHeader(par))
286 {
287 oldRow += 2;
288 }
289 }
290
291 parentItem->groupBox = new QGroupBox(w);
292 parentItem->layout = new QGridLayout();
293 parentItem->groupBox->setLayout(parentItem->layout);
294
295 if (parentItem->label)
296 {
297 l->removeWidget(parentItem->label);
298 delete parentItem->label;
299 parentItem->label = 0;
300 }
301
302 if (parentItem->widget)
303 {
304 l->removeWidget(parentItem->widget);
305 parentItem->widget->setParent(parentItem->groupBox);
306 parentItem->layout->addWidget(parentItem->widget, 0, 0, 1, 2);
307 parentItem->line = new QFrame(parentItem->groupBox);
308 }
309 else if (parentItem->widgetLabel)
310 {
311 l->removeWidget(parentItem->widgetLabel);
312 delete parentItem->widgetLabel;
313 parentItem->widgetLabel = 0;
314 }
315
316 if (parentItem->line)
317 {
318 parentItem->line->setFrameShape(QFrame::HLine);
319 parentItem->line->setFrameShadow(QFrame::Sunken);
320 parentItem->layout->addWidget(parentItem->line, 1, 0, 1, 2);
321 }
322
323 l->addWidget(parentItem->groupBox, oldRow, 0, 1, 2);
324 updateItem(parentItem);
325 }
326
327 layout = parentItem->layout;
328 parentWidget = parentItem->groupBox;
329 }
330
331 newItem->label = new QLabel(parentWidget);
332 newItem->label->setSizePolicy(QSizePolicy(QSizePolicy::Fixed, QSizePolicy::Fixed));
333 newItem->widget = createEditor(index->property(), parentWidget);
334
335 if (!newItem->widget)
336 {
337 newItem->widgetLabel = new QLabel(parentWidget);
338 newItem->widgetLabel->setSizePolicy(QSizePolicy(QSizePolicy::Ignored, QSizePolicy::Fixed));
339 newItem->widgetLabel->setTextFormat(Qt::PlainText);
340 }
341 else
342 {
343 QObject::connect(newItem->widget, SIGNAL(destroyed()), q_ptr, SLOT(slotEditorDestroyed()));
344 m_widgetToItem[newItem->widget] = newItem;
345 }
346
347 insertRow(layout, row);
348 int span = 1;
349
350 if (newItem->widget)
351 {
352 layout->addWidget(newItem->widget, row, 1);
353 }
354 else if (newItem->widgetLabel)
355 {
356 layout->addWidget(newItem->widgetLabel, row, 1);
357 }
358 else
359 {
360 span = 2;
361 }
362
363 layout->addWidget(newItem->label, row, 0, 1, span);
364
365 m_itemToIndex[newItem] = index;
366 m_indexToItem[index] = newItem;
367
368 updateItem(newItem);
369}
370
371void
373{
374 WidgetItem* item = m_indexToItem.value(index);
375
376 m_indexToItem.remove(index);
377 m_itemToIndex.remove(item);
378
379 WidgetItem* parentItem = item->parent;
380
381 int row = -1;
382
383 if (parentItem)
384 {
385 row = parentItem->children.indexOf(item);
386 parentItem->children.removeAt(row);
387
388 if (hasHeader(parentItem))
389 {
390 row += 2;
391 }
392 }
393 else
394 {
395 row = m_children.indexOf(item);
396 m_children.removeAt(row);
397 }
398
399 if (item->widget)
400 {
401 delete item->widget;
402 }
403
404 if (item->label)
405 {
406 delete item->label;
407 }
408
409 if (item->widgetLabel)
410 {
411 delete item->widgetLabel;
412 }
413
414 if (item->groupBox)
415 {
416 delete item->groupBox;
417 }
418
419 if (!parentItem)
420 {
421 removeRow(m_mainLayout, row);
422 }
423 else if (parentItem->children.count() != 0)
424 {
425 removeRow(parentItem->layout, row);
426 }
427 else
428 {
429 WidgetItem* par = parentItem->parent;
430 QGridLayout* l = 0;
431 int oldRow = -1;
432
433 if (!par)
434 {
435 l = m_mainLayout;
436 oldRow = m_children.indexOf(parentItem);
437 }
438 else
439 {
440 l = par->layout;
441 oldRow = par->children.indexOf(parentItem);
442
443 if (hasHeader(par))
444 {
445 oldRow += 2;
446 }
447 }
448
449 if (parentItem->widget)
450 {
451 parentItem->widget->hide();
452 parentItem->widget->setParent(0);
453 }
454 else if (parentItem->widgetLabel)
455 {
456 parentItem->widgetLabel->hide();
457 parentItem->widgetLabel->setParent(0);
458 }
459 else
460 {
461 //parentItem->widgetLabel = new QLabel(w);
462 }
463
464 l->removeWidget(parentItem->groupBox);
465 delete parentItem->groupBox;
466 parentItem->groupBox = 0;
467 parentItem->line = 0;
468 parentItem->layout = 0;
469
470 if (!m_recreateQueue.contains(parentItem))
471 {
472 m_recreateQueue.append(parentItem);
473 }
474
475 updateLater();
476 }
477
478 m_recreateQueue.removeAll(item);
479
480 delete item;
481}
482
483void
484QtGroupBoxPropertyBrowserPrivate::insertRow(QGridLayout* layout, int row) const
485{
486 QMap<QLayoutItem*, QRect> itemToPos;
487 int idx = 0;
488
489 while (idx < layout->count())
490 {
491 int r, c, rs, cs;
492 layout->getItemPosition(idx, &r, &c, &rs, &cs);
493
494 if (r >= row)
495 {
496 itemToPos[layout->takeAt(idx)] = QRect(r + 1, c, rs, cs);
497 }
498 else
499 {
500 idx++;
501 }
502 }
503
504 const QMap<QLayoutItem*, QRect>::ConstIterator icend = itemToPos.constEnd();
505
506 for (QMap<QLayoutItem*, QRect>::ConstIterator it = itemToPos.constBegin(); it != icend; ++it)
507 {
508 const QRect r = it.value();
509 layout->addItem(it.key(), r.x(), r.y(), r.width(), r.height());
510 }
511}
512
513void
514QtGroupBoxPropertyBrowserPrivate::removeRow(QGridLayout* layout, int row) const
515{
516 QMap<QLayoutItem*, QRect> itemToPos;
517 int idx = 0;
518
519 while (idx < layout->count())
520 {
521 int r, c, rs, cs;
522 layout->getItemPosition(idx, &r, &c, &rs, &cs);
523
524 if (r > row)
525 {
526 itemToPos[layout->takeAt(idx)] = QRect(r - 1, c, rs, cs);
527 }
528 else
529 {
530 idx++;
531 }
532 }
533
534 const QMap<QLayoutItem*, QRect>::ConstIterator icend = itemToPos.constEnd();
535
536 for (QMap<QLayoutItem*, QRect>::ConstIterator it = itemToPos.constBegin(); it != icend; ++it)
537 {
538 const QRect r = it.value();
539 layout->addItem(it.key(), r.x(), r.y(), r.width(), r.height());
540 }
541}
542
543bool
544QtGroupBoxPropertyBrowserPrivate::hasHeader(WidgetItem* item) const
545{
546 if (item->widget)
547 {
548 return true;
549 }
550
551 return false;
552}
553
554void
556{
557 WidgetItem* item = m_indexToItem.value(index);
558
559 updateItem(item);
560}
561
562void
563QtGroupBoxPropertyBrowserPrivate::updateItem(WidgetItem* item)
564{
565 QtProperty* property = m_itemToIndex[item]->property();
566
567 if (item->groupBox)
568 {
569 QFont font = item->groupBox->font();
570 font.setUnderline(property->isModified());
571 item->groupBox->setFont(font);
572 item->groupBox->setTitle(property->propertyName());
573 item->groupBox->setToolTip(property->toolTip());
574 item->groupBox->setStatusTip(property->statusTip());
575 item->groupBox->setWhatsThis(property->whatsThis());
576 item->groupBox->setEnabled(property->isEnabled());
577 }
578
579 if (item->label)
580 {
581 QFont font = item->label->font();
582 font.setUnderline(property->isModified());
583 item->label->setFont(font);
584 item->label->setText(property->propertyName());
585 item->label->setToolTip(property->toolTip());
586 item->label->setStatusTip(property->statusTip());
587 item->label->setWhatsThis(property->whatsThis());
588 item->label->setEnabled(property->isEnabled());
589 }
590
591 if (item->widgetLabel)
592 {
593 QFont font = item->widgetLabel->font();
594 font.setUnderline(false);
595 item->widgetLabel->setFont(font);
596 item->widgetLabel->setText(property->valueText());
597 item->widgetLabel->setToolTip(property->valueText());
598 item->widgetLabel->setEnabled(property->isEnabled());
599 }
600
601 if (item->widget)
602 {
603 QFont font = item->widget->font();
604 font.setUnderline(false);
605 item->widget->setFont(font);
606 item->widget->setEnabled(property->isEnabled());
607 item->widget->setToolTip(property->valueText());
608 }
609
610 //item->setIcon(1, property->valueIcon());
611}
612
613/*!
614 \class QtGroupBoxPropertyBrowser
615
616 \brief The QtGroupBoxPropertyBrowser class provides a QGroupBox
617 based property browser.
618
619 A property browser is a widget that enables the user to edit a
620 given set of properties. Each property is represented by a label
621 specifying the property's name, and an editing widget (e.g. a line
622 edit or a combobox) holding its value. A property can have zero or
623 more subproperties.
624
625 QtGroupBoxPropertyBrowser provides group boxes for all nested
626 properties, i.e. subproperties are enclosed by a group box with
627 the parent property's name as its title. For example:
628
629 \image qtgroupboxpropertybrowser.png
630
631 Use the QtAbstractPropertyBrowser API to add, insert and remove
632 properties from an instance of the QtGroupBoxPropertyBrowser
633 class. The properties themselves are created and managed by
634 implementations of the QtAbstractPropertyManager class.
635
636 \sa QtTreePropertyBrowser, QtAbstractPropertyBrowser
637*/
638
639/*!
640 Creates a property browser with the given \a parent.
641*/
644{
646 d_ptr->q_ptr = this;
647
648 d_ptr->init(this);
649}
650
651/*!
652 Destroys this property browser.
653
654 Note that the properties that were inserted into this browser are
655 \e not destroyed since they may still be used in other
656 browsers. The properties are owned by the manager that created
657 them.
658
659 \sa QtProperty, QtAbstractPropertyManager
660*/
662{
663 const QMap<QtGroupBoxPropertyBrowserPrivate::WidgetItem*, QtBrowserItem*>::ConstIterator icend =
664 d_ptr->m_itemToIndex.constEnd();
665
666 for (QMap<QtGroupBoxPropertyBrowserPrivate::WidgetItem*, QtBrowserItem*>::ConstIterator it =
667 d_ptr->m_itemToIndex.constBegin();
668 it != icend;
669 ++it)
670 {
671 delete it.key();
672 }
673
674 delete d_ptr;
675}
676
677/*!
678 \reimp
679*/
680void
682{
683 d_ptr->propertyInserted(item, afterItem);
684}
685
686/*!
687 \reimp
688*/
689void
691{
692 d_ptr->propertyRemoved(item);
693}
694
695/*!
696 \reimp
697*/
698void
700{
701 d_ptr->propertyChanged(item);
702}
703
704QT_END_NAMESPACE
705
706#include "moc_qtgroupboxpropertybrowser.cpp"
uint8_t index
constexpr T c
QtAbstractPropertyBrowser(QWidget *parent=0)
The QtBrowserItem class represents a property in a property browser instance.
void propertyInserted(QtBrowserItem *index, QtBrowserItem *afterIndex)
QWidget * createEditor(QtProperty *property, QWidget *parent) const
The QtGroupBoxPropertyBrowser class provides a QGroupBox based property browser.
void itemRemoved(QtBrowserItem *item) override
void itemInserted(QtBrowserItem *item, QtBrowserItem *afterItem) override
void itemChanged(QtBrowserItem *item) override
The QtProperty class encapsulates an instance of a property.