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

source code of qtbase/src/widgets/itemviews/qitemeditorfactory.cpp