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 QtWidgets module 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 <qplatformdefs.h>
41#include "qitemeditorfactory.h"
42#include "qitemeditorfactory_p.h"
43
44#if QT_CONFIG(combobox)
45#include <qcombobox.h>
46#endif
47#if QT_CONFIG(datetimeedit)
48#include <qdatetimeedit.h>
49#endif
50#if QT_CONFIG(label)
51#include <qlabel.h>
52#endif
53#if QT_CONFIG(lineedit)
54#include <qlineedit.h>
55#endif
56#if QT_CONFIG(spinbox)
57#include <qspinbox.h>
58#endif
59#include <qstyle.h>
60#include <qstyleoption.h>
61#include <limits.h>
62#include <float.h>
63#include <qapplication.h>
64#include <qdebug.h>
65
66#include <vector>
67#include <algorithm>
68QT_BEGIN_NAMESPACE
69
70
71#if QT_CONFIG(combobox)
72
73class QBooleanComboBox : public QComboBox
74{
75 Q_OBJECT
76 Q_PROPERTY(bool value READ value WRITE setValue USER true)
77
78public:
79 QBooleanComboBox(QWidget *parent);
80 void setValue(bool);
81 bool value() const;
82};
83
84#endif // QT_CONFIG(combobox)
85
86
87#if QT_CONFIG(spinbox)
88
89class QUIntSpinBox : public QSpinBox
90{
91 Q_OBJECT
92 Q_PROPERTY(uint value READ uintValue WRITE setUIntValue NOTIFY uintValueChanged USER true)
93public:
94 explicit QUIntSpinBox(QWidget *parent = nullptr)
95 : QSpinBox(parent)
96 {
97 connect(this, SIGNAL(valueChanged(int)), SIGNAL(uintValueChanged()));
98 }
99
100 uint uintValue()
101 {
102 return value();
103 }
104
105 void setUIntValue(uint value_)
106 {
107 return setValue(value_);
108 }
109
110Q_SIGNALS:
111 void uintValueChanged();
112};
113
114#endif // QT_CONFIG(spinbox)
115
116/*!
117 \class QItemEditorFactory
118 \brief The QItemEditorFactory class provides widgets for editing item data
119 in views and delegates.
120 \since 4.2
121 \ingroup model-view
122 \inmodule QtWidgets
123
124 When editing data in an item view, editors are created and
125 displayed by a delegate. QStyledItemDelegate, which is the delegate by
126 default installed on Qt's item views, uses a QItemEditorFactory to
127 create editors for it. A default unique instance provided by
128 QItemEditorFactory is used by all item delegates. If you set a
129 new default factory with setDefaultFactory(), the new factory will
130 be used by existing and new delegates.
131
132 A factory keeps a collection of QItemEditorCreatorBase
133 instances, which are specialized editors that produce editors
134 for one particular QVariant data type (All Qt models store
135 their data in \l{QVariant}s).
136
137 \section1 Standard Editing Widgets
138
139 The standard factory implementation provides editors for a variety of data
140 types. These are created whenever a delegate needs to provide an editor for
141 data supplied by a model. The following table shows the relationship between
142 types and the standard editors provided.
143
144 \table
145 \header \li Type \li Editor Widget
146 \row \li bool \li QComboBox
147 \row \li double \li QDoubleSpinBox
148 \row \li int \li{1,2} QSpinBox
149 \row \li unsigned int
150 \row \li QDate \li QDateEdit
151 \row \li QDateTime \li QDateTimeEdit
152 \row \li QPixmap \li QLabel
153 \row \li QString \li QLineEdit
154 \row \li QTime \li QTimeEdit
155 \endtable
156
157 Additional editors can be registered with the registerEditor() function.
158
159 \sa QStyledItemDelegate, {Model/View Programming}, {Color Editor Factory Example}
160*/
161
162/*!
163 \fn QItemEditorFactory::QItemEditorFactory()
164
165 Constructs a new item editor factory.
166*/
167
168/*!
169 Creates an editor widget with the given \a parent for the specified \a userType of data,
170 and returns it as a QWidget.
171
172 \sa registerEditor()
173*/
174QWidget *QItemEditorFactory::createEditor(int userType, QWidget *parent) const
175{
176 QItemEditorCreatorBase *creator = creatorMap.value(userType, 0);
177 if (!creator) {
178 const QItemEditorFactory *dfactory = defaultFactory();
179 return dfactory == this ? nullptr : dfactory->createEditor(userType, parent);
180 }
181 return creator->createWidget(parent);
182}
183
184/*!
185 Returns the property name used to access data for the given \a userType of data.
186*/
187QByteArray QItemEditorFactory::valuePropertyName(int userType) const
188{
189 QItemEditorCreatorBase *creator = creatorMap.value(userType, 0);
190 if (!creator) {
191 const QItemEditorFactory *dfactory = defaultFactory();
192 return dfactory == this ? QByteArray() : dfactory->valuePropertyName(userType);
193 }
194 return creator->valuePropertyName();
195}
196
197/*!
198 Destroys the item editor factory.
199*/
200QItemEditorFactory::~QItemEditorFactory()
201{
202 //we make sure we delete all the QItemEditorCreatorBase
203 //this has to be done only once, hence the sort-unique idiom
204 std::vector<QItemEditorCreatorBase*> creators(creatorMap.cbegin(), creatorMap.cend());
205 std::sort(creators.begin(), creators.end());
206 const auto it = std::unique(creators.begin(), creators.end());
207 qDeleteAll(creators.begin(), it);
208}
209
210/*!
211 Registers an item editor creator specified by \a creator for the given \a userType of data.
212
213 \b{Note:} The factory takes ownership of the item editor creator and will destroy
214 it if a new creator for the same type is registered later.
215
216 \sa createEditor()
217*/
218void QItemEditorFactory::registerEditor(int userType, QItemEditorCreatorBase *creator)
219{
220 const auto it = creatorMap.constFind(userType);
221 if (it != creatorMap.cend()) {
222 QItemEditorCreatorBase *oldCreator = it.value();
223 Q_ASSERT(oldCreator);
224 creatorMap.erase(it);
225 if (std::find(creatorMap.cbegin(), creatorMap.cend(), oldCreator) == creatorMap.cend())
226 delete oldCreator; // if it is no more in use we can delete it
227 }
228
229 creatorMap[userType] = creator;
230}
231
232class QDefaultItemEditorFactory : public QItemEditorFactory
233{
234public:
235 inline QDefaultItemEditorFactory() {}
236 QWidget *createEditor(int userType, QWidget *parent) const override;
237 QByteArray valuePropertyName(int) const override;
238};
239
240QWidget *QDefaultItemEditorFactory::createEditor(int userType, QWidget *parent) const
241{
242 switch (userType) {
243#if QT_CONFIG(combobox)
244 case QMetaType::Bool: {
245 QBooleanComboBox *cb = new QBooleanComboBox(parent);
246 cb->setFrame(false);
247 cb->setSizePolicy(QSizePolicy::Ignored, cb->sizePolicy().verticalPolicy());
248 return cb; }
249#endif
250#if QT_CONFIG(spinbox)
251 case QMetaType::UInt: {
252 QSpinBox *sb = new QUIntSpinBox(parent);
253 sb->setFrame(false);
254 sb->setMinimum(0);
255 sb->setMaximum(INT_MAX);
256 sb->setSizePolicy(QSizePolicy::Ignored, sb->sizePolicy().verticalPolicy());
257 return sb; }
258 case QMetaType::Int: {
259 QSpinBox *sb = new QSpinBox(parent);
260 sb->setFrame(false);
261 sb->setMinimum(INT_MIN);
262 sb->setMaximum(INT_MAX);
263 sb->setSizePolicy(QSizePolicy::Ignored, sb->sizePolicy().verticalPolicy());
264 return sb; }
265#endif
266#if QT_CONFIG(datetimeedit)
267 case QMetaType::QDate: {
268 QDateTimeEdit *ed = new QDateEdit(parent);
269 ed->setFrame(false);
270 return ed; }
271 case QMetaType::QTime: {
272 QDateTimeEdit *ed = new QTimeEdit(parent);
273 ed->setFrame(false);
274 return ed; }
275 case QMetaType::QDateTime: {
276 QDateTimeEdit *ed = new QDateTimeEdit(parent);
277 ed->setFrame(false);
278 return ed; }
279#endif
280#if QT_CONFIG(label)
281 case QMetaType::QPixmap:
282 return new QLabel(parent);
283#endif
284#if QT_CONFIG(spinbox)
285 case QMetaType::Double: {
286 QDoubleSpinBox *sb = new QDoubleSpinBox(parent);
287 sb->setFrame(false);
288 sb->setMinimum(-DBL_MAX);
289 sb->setMaximum(DBL_MAX);
290 sb->setSizePolicy(QSizePolicy::Ignored, sb->sizePolicy().verticalPolicy());
291 return sb; }
292#endif
293#if QT_CONFIG(lineedit)
294 case QMetaType::QString:
295 default: {
296 // the default editor is a lineedit
297 QExpandingLineEdit *le = new QExpandingLineEdit(parent);
298 le->setFrame(le->style()->styleHint(QStyle::SH_ItemView_DrawDelegateFrame, nullptr, le));
299 if (!le->style()->styleHint(QStyle::SH_ItemView_ShowDecorationSelected, nullptr, le))
300 le->setWidgetOwnsGeometry(true);
301 return le; }
302#else
303 default:
304 break;
305#endif
306 }
307 return nullptr;
308}
309
310QByteArray QDefaultItemEditorFactory::valuePropertyName(int userType) const
311{
312 switch (userType) {
313#if QT_CONFIG(combobox)
314 case QMetaType::Bool:
315 return "currentIndex";
316#endif
317#if QT_CONFIG(spinbox)
318 case QMetaType::UInt:
319 case QMetaType::Int:
320 case QMetaType::Double:
321 return "value";
322#endif
323#if QT_CONFIG(datetimeedit)
324 case QMetaType::QDate:
325 return "date";
326 case QMetaType::QTime:
327 return "time";
328 case QMetaType::QDateTime:
329 return "dateTime";
330#endif
331 case QMetaType::QString:
332 default:
333 // the default editor is a lineedit
334 return "text";
335 }
336}
337
338static QItemEditorFactory *q_default_factory = nullptr;
339struct QDefaultFactoryCleaner
340{
341 inline QDefaultFactoryCleaner() {}
342 ~QDefaultFactoryCleaner() { delete q_default_factory; q_default_factory = nullptr; }
343};
344
345/*!
346 Returns the default item editor factory.
347
348 \sa setDefaultFactory()
349*/
350const QItemEditorFactory *QItemEditorFactory::defaultFactory()
351{
352 static const QDefaultItemEditorFactory factory;
353 if (q_default_factory)
354 return q_default_factory;
355 return &factory;
356}
357
358/*!
359 Sets the default item editor factory to the given \a factory.
360 Both new and existing delegates will use the new factory.
361
362 \sa defaultFactory()
363*/
364void QItemEditorFactory::setDefaultFactory(QItemEditorFactory *factory)
365{
366 static const QDefaultFactoryCleaner cleaner;
367 delete q_default_factory;
368 q_default_factory = factory;
369}
370
371/*!
372 \class QItemEditorCreatorBase
373 \brief The QItemEditorCreatorBase class provides an abstract base class that
374 must be subclassed when implementing new item editor creators.
375 \since 4.2
376 \ingroup model-view
377 \inmodule QtWidgets
378
379 QItemEditorCreatorBase objects are specialized widget factories that
380 provide editor widgets for one particular QVariant data type. They
381 are used by QItemEditorFactory to create editors for
382 \l{QStyledItemDelegate}s. Creator bases must be registered with
383 QItemEditorFactory::registerEditor().
384
385 An editor should provide a user property for the data it edits.
386 QItemDelagates can then access the property using Qt's
387 \l{Meta-Object System}{meta-object system} to set and retrieve the
388 editing data. A property is set as the user property with the USER
389 keyword:
390
391 \snippet code/src_gui_itemviews_qitemeditorfactory.cpp 0
392
393 If the editor does not provide a user property, it must return the
394 name of the property from valuePropertyName(); delegates will then
395 use the name to access the property. If a user property exists,
396 item delegates will not call valuePropertyName().
397
398 QStandardItemEditorCreator is a convenience template class that can be used
399 to register widgets without the need to subclass QItemEditorCreatorBase.
400
401 \sa QStandardItemEditorCreator, QItemEditorFactory,
402 {Model/View Programming}, {Color Editor Factory Example}
403*/
404
405/*!
406 \fn QItemEditorCreatorBase::~QItemEditorCreatorBase()
407
408 Destroys the editor creator object.
409*/
410QItemEditorCreatorBase::~QItemEditorCreatorBase()
411{
412
413}
414
415/*!
416 \fn QWidget *QItemEditorCreatorBase::createWidget(QWidget *parent) const
417
418 Returns an editor widget with the given \a parent.
419
420 When implementing this function in subclasses of this class, you must
421 construct and return new editor widgets with the parent widget specified.
422*/
423
424/*!
425 \fn QByteArray QItemEditorCreatorBase::valuePropertyName() const
426
427 Returns the name of the property used to get and set values in the creator's
428 editor widgets.
429
430 When implementing this function in subclasses, you must ensure that the
431 editor widget's property specified by this function can accept the type
432 the creator is registered for. For example, a creator which constructs
433 QCheckBox widgets to edit boolean values would return the
434 \l{QCheckBox::checkable}{checkable} property name from this function,
435 and must be registered in the item editor factory for the QMetaType::Bool
436 type.
437
438 Note: Since Qt 4.2 the item delegates query the user property of widgets,
439 and only call this function if the widget has no user property. You can
440 override this behavior by reimplementing QAbstractItemDelegate::setModelData()
441 and QAbstractItemDelegate::setEditorData().
442
443 \sa QMetaObject::userProperty(), QItemEditorFactory::registerEditor()
444*/
445
446/*!
447 \class QItemEditorCreator
448 \brief The QItemEditorCreator class makes it possible to create
449 item editor creator bases without subclassing
450 QItemEditorCreatorBase.
451
452 \since 4.2
453 \ingroup model-view
454 \inmodule QtWidgets
455
456 QItemEditorCreator is a convenience template class. It uses
457 the template class to create editors for QItemEditorFactory.
458 This way, it is not necessary to subclass
459 QItemEditorCreatorBase.
460
461 \snippet code/src_gui_itemviews_qitemeditorfactory.cpp 1
462
463 The constructor takes the name of the property that contains the
464 editing data. QStyledItemDelegate can then access the property by name
465 when it sets and retrieves editing data. Only use this class if
466 your editor does not define a user property (using the USER
467 keyword in the Q_PROPERTY macro). If the widget has a user
468 property, you should use QStandardItemEditorCreator instead.
469
470 \sa QItemEditorCreatorBase, QStandardItemEditorCreator,
471 QItemEditorFactory, {Color Editor Factory Example}
472*/
473
474/*!
475 \fn template <class T> QItemEditorCreator<T>::QItemEditorCreator(const QByteArray &valuePropertyName)
476
477 Constructs an editor creator object using \a valuePropertyName
478 as the name of the property to be used for editing. The
479 property name is used by QStyledItemDelegate when setting and
480 getting editor data.
481
482 Note that the \a valuePropertyName is only used if the editor
483 widget does not have a user property defined.
484*/
485
486/*!
487 \fn template <class T> QWidget *QItemEditorCreator<T>::createWidget(QWidget *parent) const
488 \reimp
489*/
490
491/*!
492 \fn template <class T> QByteArray QItemEditorCreator<T>::valuePropertyName() const
493 \reimp
494*/
495
496/*!
497 \class QStandardItemEditorCreator
498
499 \brief The QStandardItemEditorCreator class provides the
500 possibility to register widgets without having to subclass
501 QItemEditorCreatorBase.
502
503 \since 4.2
504 \ingroup model-view
505 \inmodule QtWidgets
506
507 This convenience template class makes it possible to register widgets without
508 having to subclass QItemEditorCreatorBase.
509
510 Example:
511
512 \snippet code/src_gui_itemviews_qitemeditorfactory.cpp 2
513
514 Setting the \c editorFactory created above in an item delegate via
515 QStyledItemDelegate::setItemEditorFactory() makes sure that all values of type
516 QMetaType::QDateTime will be edited in \c{MyFancyDateTimeEdit}.
517
518 The editor must provide a user property that will contain the
519 editing data. The property is used by \l{QStyledItemDelegate}s to set
520 and retrieve the data (using Qt's \l{Meta-Object
521 System}{meta-object system}). You set the user property with
522 the USER keyword:
523
524 \snippet code/src_gui_itemviews_qitemeditorfactory.cpp 3
525
526 \sa QItemEditorCreatorBase, QItemEditorCreator,
527 QItemEditorFactory, QStyledItemDelegate, {Color Editor Factory Example}
528*/
529
530/*!
531 \fn template <class T> QStandardItemEditorCreator<T>::QStandardItemEditorCreator()
532
533 Constructs an editor creator object.
534*/
535
536/*!
537 \fn template <class T> QWidget *QStandardItemEditorCreator<T>::createWidget(QWidget *parent) const
538 \reimp
539*/
540
541/*!
542 \fn template <class T> QByteArray QStandardItemEditorCreator<T>::valuePropertyName() const
543 \reimp
544*/
545
546#if QT_CONFIG(lineedit)
547
548QExpandingLineEdit::QExpandingLineEdit(QWidget *parent)
549 : QLineEdit(parent), originalWidth(-1), widgetOwnsGeometry(false)
550{
551 connect(this, SIGNAL(textChanged(QString)), this, SLOT(resizeToContents()));
552 updateMinimumWidth();
553}
554
555void QExpandingLineEdit::changeEvent(QEvent *e)
556{
557 switch (e->type())
558 {
559 case QEvent::FontChange:
560 case QEvent::StyleChange:
561 case QEvent::ContentsRectChange:
562 updateMinimumWidth();
563 break;
564 default:
565 break;
566 }
567
568 QLineEdit::changeEvent(e);
569}
570
571void QExpandingLineEdit::updateMinimumWidth()
572{
573 const QMargins tm = textMargins();
574 const QMargins cm = contentsMargins();
575 const int width = tm.left() + tm.right() + cm.left() + cm.right() + 4 /*horizontalMargin in qlineedit.cpp*/;
576
577 QStyleOptionFrame opt;
578 initStyleOption(&opt);
579
580 int minWidth = style()->sizeFromContents(QStyle::CT_LineEdit, &opt, QSize(width, 0), this).width();
581 setMinimumWidth(minWidth);
582}
583
584void QExpandingLineEdit::resizeToContents()
585{
586 int oldWidth = width();
587 if (originalWidth == -1)
588 originalWidth = oldWidth;
589 if (QWidget *parent = parentWidget()) {
590 QPoint position = pos();
591 int hintWidth = minimumWidth() + fontMetrics().horizontalAdvance(displayText());
592 int parentWidth = parent->width();
593 int maxWidth = isRightToLeft() ? position.x() + oldWidth : parentWidth - position.x();
594 int newWidth = qBound(originalWidth, hintWidth, maxWidth);
595 if (widgetOwnsGeometry)
596 setMaximumWidth(newWidth);
597 if (isRightToLeft())
598 move(position.x() - newWidth + oldWidth, position.y());
599 resize(newWidth, height());
600 }
601}
602
603#endif // QT_CONFIG(lineedit)
604
605#if QT_CONFIG(combobox)
606
607QBooleanComboBox::QBooleanComboBox(QWidget *parent)
608 : QComboBox(parent)
609{
610 addItem(QComboBox::tr("False"));
611 addItem(QComboBox::tr("True"));
612}
613
614void QBooleanComboBox::setValue(bool value)
615{
616 setCurrentIndex(value ? 1 : 0);
617}
618
619bool QBooleanComboBox::value() const
620{
621 return (currentIndex() == 1);
622}
623
624#endif // QT_CONFIG(combobox)
625
626QT_END_NAMESPACE
627
628#if QT_CONFIG(lineedit) || QT_CONFIG(combobox)
629#include "qitemeditorfactory.moc"
630#endif
631
632#include "moc_qitemeditorfactory_p.cpp"
633