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

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