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 "qabstractitemdelegate.h"
41
42#include <qabstractitemmodel.h>
43#include <qabstractitemview.h>
44#include <qfontmetrics.h>
45#if QT_CONFIG(whatsthis)
46#include <qwhatsthis.h>
47#endif
48#include <qtooltip.h>
49#include <qevent.h>
50#include <qstring.h>
51#include <qdebug.h>
52#if QT_CONFIG(lineedit)
53#include <qlineedit.h>
54#endif
55#if QT_CONFIG(textedit)
56#include <qtextedit.h>
57#include <qplaintextedit.h>
58#endif
59#include <qapplication.h>
60#include <qvalidator.h>
61#include <qjsonvalue.h>
62#include <private/qtextengine_p.h>
63#include <private/qabstractitemdelegate_p.h>
64
65#include <qpa/qplatformintegration.h>
66#if QT_CONFIG(draganddrop)
67#include <qpa/qplatformdrag.h>
68#include <private/qdnd_p.h>
69#endif
70#include <private/qguiapplication_p.h>
71
72QT_BEGIN_NAMESPACE
73
74/*!
75 \class QAbstractItemDelegate
76
77 \brief The QAbstractItemDelegate class is used to display and edit
78 data items from a model.
79
80 \ingroup model-view
81 \inmodule QtWidgets
82
83 A QAbstractItemDelegate provides the interface and common functionality
84 for delegates in the model/view architecture. Delegates display
85 individual items in views, and handle the editing of model data.
86
87 The QAbstractItemDelegate class is one of the \l{Model/View Classes}
88 and is part of Qt's \l{Model/View Programming}{model/view framework}.
89
90 To render an item in a custom way, you must implement paint() and
91 sizeHint(). The QStyledItemDelegate class provides default implementations for
92 these functions; if you do not need custom rendering, subclass that
93 class instead.
94
95 We give an example of drawing a progress bar in items; in our case
96 for a package management program.
97
98 \image widgetdelegate.png
99
100 We create the \c WidgetDelegate class, which inherits from
101 QStyledItemDelegate. We do the drawing in the paint() function:
102
103 \snippet widgetdelegate.cpp 0
104
105 Notice that we use a QStyleOptionProgressBar and initialize its
106 members. We can then use the current QStyle to draw it.
107
108 To provide custom editing, there are two approaches that can be
109 used. The first approach is to create an editor widget and display
110 it directly on top of the item. To do this you must reimplement
111 createEditor() to provide an editor widget, setEditorData() to populate
112 the editor with the data from the model, and setModelData() so that the
113 delegate can update the model with data from the editor.
114
115 The second approach is to handle user events directly by reimplementing
116 editorEvent().
117
118 \sa {model-view-programming}{Model/View Programming}, QStyledItemDelegate,
119 {Pixelator Example}, QStyledItemDelegate, QStyle
120*/
121
122/*!
123 \enum QAbstractItemDelegate::EndEditHint
124
125 This enum describes the different hints that the delegate can give to the
126 model and view components to make editing data in a model a comfortable
127 experience for the user.
128
129 \value NoHint There is no recommended action to be performed.
130
131 These hints let the delegate influence the behavior of the view:
132
133 \value EditNextItem The view should use the delegate to open an
134 editor on the next item in the view.
135 \value EditPreviousItem The view should use the delegate to open an
136 editor on the previous item in the view.
137
138 Note that custom views may interpret the concepts of next and previous
139 differently.
140
141 The following hints are most useful when models are used that cache
142 data, such as those that manipulate data locally in order to increase
143 performance or conserve network bandwidth.
144
145 \value SubmitModelCache If the model caches data, it should write out
146 cached data to the underlying data store.
147 \value RevertModelCache If the model caches data, it should discard
148 cached data and replace it with data from the
149 underlying data store.
150
151 Although models and views should respond to these hints in appropriate
152 ways, custom components may ignore any or all of them if they are not
153 relevant.
154*/
155
156/*!
157 \fn void QAbstractItemDelegate::commitData(QWidget *editor)
158
159 This signal must be emitted when the \a editor widget has completed
160 editing the data, and wants to write it back into the model.
161*/
162
163/*!
164 \fn void QAbstractItemDelegate::closeEditor(QWidget *editor, QAbstractItemDelegate::EndEditHint hint)
165
166 This signal is emitted when the user has finished editing an item using
167 the specified \a editor.
168
169 The \a hint provides a way for the delegate to influence how the model and
170 view behave after editing is completed. It indicates to these components
171 what action should be performed next to provide a comfortable editing
172 experience for the user. For example, if \c EditNextItem is specified,
173 the view should use a delegate to open an editor on the next item in the
174 model.
175
176 \sa EndEditHint
177*/
178
179/*!
180 \fn void QAbstractItemDelegate::sizeHintChanged(const QModelIndex &index)
181 \since 4.4
182
183 This signal must be emitted when the sizeHint() of \a index changed.
184
185 Views automatically connect to this signal and relayout items as necessary.
186*/
187
188
189/*!
190 Creates a new abstract item delegate with the given \a parent.
191*/
192QAbstractItemDelegate::QAbstractItemDelegate(QObject *parent)
193 : QObject(*new QAbstractItemDelegatePrivate, parent)
194{
195
196}
197
198/*!
199 \internal
200
201 Creates a new abstract item delegate with the given \a parent.
202*/
203QAbstractItemDelegate::QAbstractItemDelegate(QObjectPrivate &dd, QObject *parent)
204 : QObject(dd, parent)
205{
206
207}
208
209/*!
210 Destroys the abstract item delegate.
211*/
212QAbstractItemDelegate::~QAbstractItemDelegate()
213{
214
215}
216
217/*!
218 \fn void QAbstractItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const = 0;
219
220 This pure abstract function must be reimplemented if you want to
221 provide custom rendering. Use the \a painter and style \a option to
222 render the item specified by the item \a index.
223
224 If you reimplement this you must also reimplement sizeHint().
225*/
226
227/*!
228 \fn QSize QAbstractItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const = 0
229
230 This pure abstract function must be reimplemented if you want to
231 provide custom rendering. The options are specified by \a option
232 and the model item by \a index.
233
234 If you reimplement this you must also reimplement paint().
235*/
236
237/*!
238 Returns the editor to be used for editing the data item with the
239 given \a index. Note that the index contains information about the
240 model being used. The editor's parent widget is specified by \a parent,
241 and the item options by \a option.
242
243 The base implementation returns \nullptr. If you want custom editing you
244 will need to reimplement this function.
245
246 The returned editor widget should have Qt::StrongFocus;
247 otherwise, \l{QMouseEvent}s received by the widget will propagate
248 to the view. The view's background will shine through unless the
249 editor paints its own background (e.g., with
250 \l{QWidget::}{setAutoFillBackground()}).
251
252 \sa destroyEditor(), setModelData(), setEditorData()
253*/
254QWidget *QAbstractItemDelegate::createEditor(QWidget *,
255 const QStyleOptionViewItem &,
256 const QModelIndex &) const
257{
258 return nullptr;
259}
260
261
262/*!
263 Called when the \a editor is no longer needed for editing the data item
264 with the given \a index and should be destroyed. The default behavior is a
265 call to deleteLater on the editor. It is possible e.g. to avoid this delete by
266 reimplementing this function.
267
268 \since 5.0
269 \sa createEditor()
270*/
271void QAbstractItemDelegate::destroyEditor(QWidget *editor, const QModelIndex &index) const
272{
273 Q_UNUSED(index);
274 editor->deleteLater();
275}
276
277/*!
278 Sets the contents of the given \a editor to the data for the item
279 at the given \a index. Note that the index contains information
280 about the model being used.
281
282 The base implementation does nothing. If you want custom editing
283 you will need to reimplement this function.
284
285 \sa setModelData()
286*/
287void QAbstractItemDelegate::setEditorData(QWidget *,
288 const QModelIndex &) const
289{
290 // do nothing
291}
292
293/*!
294 Sets the data for the item at the given \a index in the \a model
295 to the contents of the given \a editor.
296
297 The base implementation does nothing. If you want custom editing
298 you will need to reimplement this function.
299
300 \sa setEditorData()
301*/
302void QAbstractItemDelegate::setModelData(QWidget *,
303 QAbstractItemModel *,
304 const QModelIndex &) const
305{
306 // do nothing
307}
308
309/*!
310 Updates the geometry of the \a editor for the item with the given
311 \a index, according to the rectangle specified in the \a option.
312 If the item has an internal layout, the editor will be laid out
313 accordingly. Note that the index contains information about the
314 model being used.
315
316 The base implementation does nothing. If you want custom editing
317 you must reimplement this function.
318*/
319void QAbstractItemDelegate::updateEditorGeometry(QWidget *,
320 const QStyleOptionViewItem &,
321 const QModelIndex &) const
322{
323 // do nothing
324}
325
326/*!
327 When editing of an item starts, this function is called with the
328 \a event that triggered the editing, the \a model, the \a index of
329 the item, and the \a option used for rendering the item.
330
331 Mouse events are sent to editorEvent() even if they don't start
332 editing of the item. This can, for instance, be useful if you wish
333 to open a context menu when the right mouse button is pressed on
334 an item.
335
336 The base implementation returns \c false (indicating that it has not
337 handled the event).
338*/
339bool QAbstractItemDelegate::editorEvent(QEvent *,
340 QAbstractItemModel *,
341 const QStyleOptionViewItem &,
342 const QModelIndex &)
343{
344 // do nothing
345 return false;
346}
347
348#if QT_DEPRECATED_SINCE(5, 13)
349/*!
350 \obsolete
351
352 Use QFontMetrics::elidedText() instead.
353
354 \oldcode
355 QFontMetrics fm = ...
356 QString str = QAbstractItemDelegate::elidedText(fm, width, mode, text);
357 \newcode
358 QFontMetrics fm = ...
359 QString str = fm.elidedText(text, mode, width);
360 \endcode
361*/
362
363QString QAbstractItemDelegate::elidedText(const QFontMetrics &fontMetrics, int width,
364 Qt::TextElideMode mode, const QString &text)
365{
366 return fontMetrics.elidedText(text, mode, width);
367}
368#endif
369
370/*!
371 \since 4.3
372 Whenever a help event occurs, this function is called with the \a event
373 \a view \a option and the \a index that corresponds to the item where the
374 event occurs.
375
376 Returns \c true if the delegate can handle the event; otherwise returns \c false.
377 A return value of true indicates that the data obtained using the index had
378 the required role.
379
380 For QEvent::ToolTip and QEvent::WhatsThis events that were handled successfully,
381 the relevant popup may be shown depending on the user's system configuration.
382
383 \sa QHelpEvent
384*/
385bool QAbstractItemDelegate::helpEvent(QHelpEvent *event,
386 QAbstractItemView *view,
387 const QStyleOptionViewItem &option,
388 const QModelIndex &index)
389{
390 if (!event || !view)
391 return false;
392 Q_UNUSED(index);
393 Q_UNUSED(option);
394 switch (event->type()) {
395#ifndef QT_NO_TOOLTIP
396 case QEvent::ToolTip: {
397 Q_D(QAbstractItemDelegate);
398 QHelpEvent *he = static_cast<QHelpEvent*>(event);
399 const int precision = inherits(classname: "QItemDelegate") ? 10 : 6; // keep in sync with DBL_DIG in qitemdelegate.cpp
400 const QString tooltip = index.isValid() ?
401 d->textForRole(role: Qt::ToolTipRole, value: index.data(arole: Qt::ToolTipRole), locale: option.locale, precision) :
402 QString();
403 QRect rect;
404 if (index.isValid()) {
405 const QRect r = view->visualRect(index);
406 rect = QRect(view->mapToGlobal(r.topLeft()), r.size());
407 }
408 QToolTip::showText(pos: he->globalPos(), text: tooltip, w: view, rect);
409 event->setAccepted(!tooltip.isEmpty());
410 break;
411 }
412#endif
413#if QT_CONFIG(whatsthis)
414 case QEvent::QueryWhatsThis:
415 event->setAccepted(index.data(arole: Qt::WhatsThisRole).isValid());
416 break;
417 case QEvent::WhatsThis: {
418 Q_D(QAbstractItemDelegate);
419 QHelpEvent *he = static_cast<QHelpEvent*>(event);
420 const int precision = inherits(classname: "QItemDelegate") ? 10 : 6; // keep in sync with DBL_DIG in qitemdelegate.cpp
421 const QString whatsthis = index.isValid() ?
422 d->textForRole(role: Qt::WhatsThisRole, value: index.data(arole: Qt::WhatsThisRole), locale: option.locale, precision) :
423 QString();
424 QWhatsThis::showText(pos: he->globalPos(), text: whatsthis, w: view);
425 event->setAccepted(!whatsthis.isEmpty());
426 break;
427 }
428#endif
429 case QEvent::None:
430 default:
431 break;
432 }
433 return event->isAccepted();
434}
435
436/*!
437 \internal
438
439 This virtual method is reserved and will be used in Qt 5.1.
440*/
441QVector<int> QAbstractItemDelegate::paintingRoles() const
442{
443 return QVector<int>();
444}
445
446QAbstractItemDelegatePrivate::QAbstractItemDelegatePrivate()
447 : QObjectPrivate()
448{
449}
450
451static bool editorHandlesKeyEvent(QWidget *editor, const QKeyEvent *event)
452{
453#if QT_CONFIG(textedit)
454 // do not filter enter / return / tab / backtab for QTextEdit or QPlainTextEdit
455 if (qobject_cast<QTextEdit *>(object: editor) || qobject_cast<QPlainTextEdit *>(object: editor)) {
456 switch (event->key()) {
457 case Qt::Key_Tab:
458 case Qt::Key_Backtab:
459 case Qt::Key_Enter:
460 case Qt::Key_Return:
461 return true;
462
463 default:
464 break;
465 }
466 }
467#endif // QT_CONFIG(textedit)
468
469 Q_UNUSED(editor);
470 Q_UNUSED(event);
471 return false;
472}
473
474bool QAbstractItemDelegatePrivate::editorEventFilter(QObject *object, QEvent *event)
475{
476 Q_Q(QAbstractItemDelegate);
477
478 QWidget *editor = qobject_cast<QWidget*>(o: object);
479 if (!editor)
480 return false;
481 if (event->type() == QEvent::KeyPress) {
482 QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
483 if (editorHandlesKeyEvent(editor, event: keyEvent))
484 return false;
485
486#ifndef QT_NO_SHORTCUT
487 if (keyEvent->matches(key: QKeySequence::Cancel)) {
488 // don't commit data
489 emit q->closeEditor(editor, hint: QAbstractItemDelegate::RevertModelCache);
490 return true;
491 }
492#endif
493
494 switch (keyEvent->key()) {
495 case Qt::Key_Tab:
496 if (tryFixup(editor)) {
497 emit q->commitData(editor);
498 emit q->closeEditor(editor, hint: QAbstractItemDelegate::EditNextItem);
499 }
500 return true;
501 case Qt::Key_Backtab:
502 if (tryFixup(editor)) {
503 emit q->commitData(editor);
504 emit q->closeEditor(editor, hint: QAbstractItemDelegate::EditPreviousItem);
505 }
506 return true;
507 case Qt::Key_Enter:
508 case Qt::Key_Return:
509 // We want the editor to be able to process the key press
510 // before committing the data (e.g. so it can do
511 // validation/fixup of the input).
512 if (!tryFixup(editor))
513 return true;
514
515 QMetaObject::invokeMethod(obj: q, member: "_q_commitDataAndCloseEditor",
516 type: Qt::QueuedConnection, Q_ARG(QWidget*, editor));
517 return false;
518 default:
519 return false;
520 }
521 } else if (event->type() == QEvent::FocusOut || (event->type() == QEvent::Hide && editor->isWindow())) {
522 //the Hide event will take care of he editors that are in fact complete dialogs
523 if (!editor->isActiveWindow() || (QApplication::focusWidget() != editor)) {
524 QWidget *w = QApplication::focusWidget();
525 while (w) { // don't worry about focus changes internally in the editor
526 if (w == editor)
527 return false;
528 w = w->parentWidget();
529 }
530#if QT_CONFIG(draganddrop)
531 // The window may lose focus during an drag operation.
532 // i.e when dragging involves the taskbar on Windows.
533 QPlatformDrag *platformDrag = QGuiApplicationPrivate::instance()->platformIntegration()->drag();
534 if (platformDrag && platformDrag->currentDrag()) {
535 return false;
536 }
537#endif
538 if (tryFixup(editor))
539 emit q->commitData(editor);
540
541 // If the application loses focus while editing, then the focus needs to go back
542 // to the itemview when the editor closes. This ensures that when the application
543 // is active again it will have the focus on the itemview as expected.
544 const bool manuallyFixFocus = (event->type() == QEvent::FocusOut) && !editor->hasFocus() &&
545 editor->parentWidget() &&
546 (static_cast<QFocusEvent *>(event)->reason() == Qt::ActiveWindowFocusReason);
547 emit q->closeEditor(editor, hint: QAbstractItemDelegate::NoHint);
548 if (manuallyFixFocus)
549 editor->parentWidget()->setFocus();
550 }
551#ifndef QT_NO_SHORTCUT
552 } else if (event->type() == QEvent::ShortcutOverride) {
553 if (static_cast<QKeyEvent*>(event)->matches(key: QKeySequence::Cancel)) {
554 event->accept();
555 return true;
556 }
557#endif
558 }
559 return false;
560}
561
562bool QAbstractItemDelegatePrivate::tryFixup(QWidget *editor)
563{
564#if QT_CONFIG(lineedit)
565 if (QLineEdit *e = qobject_cast<QLineEdit*>(object: editor)) {
566 if (!e->hasAcceptableInput()) {
567#if QT_CONFIG(validator)
568 if (const QValidator *validator = e->validator()) {
569 QString text = e->text();
570 validator->fixup(text);
571 e->setText(text);
572 }
573#endif
574 return e->hasAcceptableInput();
575 }
576 }
577#else
578 Q_UNUSED(editor)
579#endif // QT_CONFIG(lineedit)
580
581 return true;
582}
583
584QString QAbstractItemDelegatePrivate::textForRole(Qt::ItemDataRole role, const QVariant &value, const QLocale &locale, int precision) const
585{
586 const QLocale::FormatType formatType = (role == Qt::DisplayRole) ? QLocale::ShortFormat : QLocale::LongFormat;
587 QString text;
588 switch (value.userType()) {
589 case QMetaType::Float:
590 text = locale.toString(i: value.toFloat());
591 break;
592 case QMetaType::Double:
593 text = locale.toString(i: value.toDouble(), f: 'g', prec: precision);
594 break;
595 case QMetaType::Int:
596 case QMetaType::LongLong:
597 text = locale.toString(i: value.toLongLong());
598 break;
599 case QMetaType::UInt:
600 case QMetaType::ULongLong:
601 text = locale.toString(i: value.toULongLong());
602 break;
603 case QMetaType::QDate:
604 text = locale.toString(date: value.toDate(), format: formatType);
605 break;
606 case QMetaType::QTime:
607 text = locale.toString(time: value.toTime(), format: formatType);
608 break;
609 case QMetaType::QDateTime:
610 text = locale.toString(dateTime: value.toDateTime(), format: formatType);
611 break;
612 case QMetaType::QJsonValue: {
613 const QJsonValue val = value.toJsonValue();
614 if (val.isBool()) {
615 text = QVariant(val.toBool()).toString();
616 break;
617 }
618 if (val.isDouble()) {
619 text = locale.toString(i: val.toDouble(), f: 'g', prec: precision);
620 break;
621 }
622 // val is a string (or null) here
623 Q_FALLTHROUGH();
624 }
625 default: {
626 text = value.toString();
627 if (role == Qt::DisplayRole)
628 text.replace(before: QLatin1Char('\n'), after: QChar::LineSeparator);
629 break;
630 }
631 }
632 return text;
633}
634
635void QAbstractItemDelegatePrivate::_q_commitDataAndCloseEditor(QWidget *editor)
636{
637 Q_Q(QAbstractItemDelegate);
638 emit q->commitData(editor);
639 emit q->closeEditor(editor, hint: QAbstractItemDelegate::SubmitModelCache);
640}
641
642QT_END_NAMESPACE
643
644#include "moc_qabstractitemdelegate.cpp"
645

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