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 "qitemdelegate.h"
41
42#include <qabstractitemmodel.h>
43#include <qapplication.h>
44#include <qbrush.h>
45#include <qpainter.h>
46#include <qpalette.h>
47#include <qpoint.h>
48#include <qrect.h>
49#include <qsize.h>
50#include <qstyle.h>
51#include <qdatetime.h>
52#include <qstyleoption.h>
53#include <qevent.h>
54#include <qpixmap.h>
55#include <qbitmap.h>
56#include <qpixmapcache.h>
57#include <qitemeditorfactory.h>
58#include <qmetaobject.h>
59#include <qtextlayout.h>
60#include <private/qabstractitemdelegate_p.h>
61#include <private/qtextengine_p.h>
62#include <qdebug.h>
63#include <qlocale.h>
64#include <qmath.h>
65
66#include <limits.h>
67
68// keep in sync with QAbstractItemDelegate::helpEvent()
69#ifndef DBL_DIG
70# define DBL_DIG 10
71#endif
72
73QT_BEGIN_NAMESPACE
74
75class QItemDelegatePrivate : public QAbstractItemDelegatePrivate
76{
77 Q_DECLARE_PUBLIC(QItemDelegate)
78
79public:
80 QItemDelegatePrivate() : f(nullptr), clipPainting(true) {}
81
82 inline const QItemEditorFactory *editorFactory() const
83 { return f ? f : QItemEditorFactory::defaultFactory(); }
84
85 inline QIcon::Mode iconMode(QStyle::State state) const
86 {
87 if (!(state & QStyle::State_Enabled)) return QIcon::Disabled;
88 if (state & QStyle::State_Selected) return QIcon::Selected;
89 return QIcon::Normal;
90 }
91
92 inline QIcon::State iconState(QStyle::State state) const
93 { return state & QStyle::State_Open ? QIcon::On : QIcon::Off; }
94
95 inline static QString replaceNewLine(QString text)
96 {
97 text.replace(QLatin1Char('\n'), QChar::LineSeparator);
98 return text;
99 }
100
101 QString valueToText(const QVariant &value, const QStyleOptionViewItem &option) const;
102
103 QItemEditorFactory *f;
104 bool clipPainting;
105
106 QRect displayRect(const QModelIndex &index, const QStyleOptionViewItem &option,
107 const QRect &decorationRect, const QRect &checkRect) const;
108 QRect textLayoutBounds(const QStyleOptionViewItem &option,
109 const QRect &decorationRect, const QRect &checkRect) const;
110 QSizeF doTextLayout(int lineWidth) const;
111 mutable QTextLayout textLayout;
112 mutable QTextOption textOption;
113
114 const QWidget *widget(const QStyleOptionViewItem &option) const
115 {
116 return option.widget;
117 }
118
119 // ### temporary hack until we have QStandardItemDelegate
120 mutable struct Icon {
121 QIcon icon;
122 QIcon::Mode mode;
123 QIcon::State state;
124 } tmp;
125};
126
127QRect QItemDelegatePrivate::displayRect(const QModelIndex &index, const QStyleOptionViewItem &option,
128 const QRect &decorationRect, const QRect &checkRect) const
129{
130 Q_Q(const QItemDelegate);
131 const QVariant value = index.data(Qt::DisplayRole);
132 if (!value.isValid() || value.isNull())
133 return QRect();
134
135 const QString text = valueToText(value, option);
136 const QVariant fontVal = index.data(Qt::FontRole);
137 const QFont fnt = qvariant_cast<QFont>(fontVal).resolve(option.font);
138 return q->textRectangle(nullptr,
139 textLayoutBounds(option, decorationRect, checkRect),
140 fnt, text);
141}
142
143// similar to QCommonStylePrivate::viewItemSize(Qt::DisplayRole)
144QRect QItemDelegatePrivate::textLayoutBounds(const QStyleOptionViewItem &option,
145 const QRect &decorationRect, const QRect &checkRect) const
146{
147 QRect rect = option.rect;
148 const QWidget *w = widget(option);
149 QStyle *style = w ? w->style() : QApplication::style();
150 const bool wrapText = option.features & QStyleOptionViewItem::WrapText;
151 // see QItemDelegate::drawDisplay
152 const int textMargin = style->pixelMetric(QStyle::PM_FocusFrameHMargin, nullptr, w) + 1;
153 switch (option.decorationPosition) {
154 case QStyleOptionViewItem::Left:
155 case QStyleOptionViewItem::Right:
156 rect.setWidth(wrapText && rect.isValid() ? rect.width() - 2 * textMargin : (QFIXED_MAX));
157 break;
158 case QStyleOptionViewItem::Top:
159 case QStyleOptionViewItem::Bottom:
160 rect.setWidth(wrapText ? option.decorationSize.width() - 2 * textMargin : (QFIXED_MAX));
161 break;
162 }
163
164 if (wrapText) {
165 if (!decorationRect.isNull())
166 rect.setWidth(rect.width() - decorationRect.width() - 2 * textMargin);
167 if (!checkRect.isNull())
168 rect.setWidth(rect.width() - checkRect.width() - 2 * textMargin);
169 // adjust height to be sure that the text fits
170 const QSizeF size = doTextLayout(rect.width());
171 rect.setHeight(qCeil(size.height()));
172 }
173
174 return rect;
175}
176
177QSizeF QItemDelegatePrivate::doTextLayout(int lineWidth) const
178{
179 qreal height = 0;
180 qreal widthUsed = 0;
181 textLayout.beginLayout();
182 while (true) {
183 QTextLine line = textLayout.createLine();
184 if (!line.isValid())
185 break;
186 line.setLineWidth(lineWidth);
187 line.setPosition(QPointF(0, height));
188 height += line.height();
189 widthUsed = qMax(widthUsed, line.naturalTextWidth());
190 }
191 textLayout.endLayout();
192 return QSizeF(widthUsed, height);
193}
194
195/*!
196 \class QItemDelegate
197
198 \brief The QItemDelegate class provides display and editing facilities for
199 data items from a model.
200
201 \ingroup model-view
202 \inmodule QtWidgets
203
204 QItemDelegate can be used to provide custom display features and editor
205 widgets for item views based on QAbstractItemView subclasses. Using a
206 delegate for this purpose allows the display and editing mechanisms to be
207 customized and developed independently from the model and view.
208
209 The QItemDelegate class is one of the \l{Model/View Classes} and
210 is part of Qt's \l{Model/View Programming}{model/view framework}.
211 Note that QStyledItemDelegate has taken over the job of drawing
212 Qt's item views. We recommend the use of QStyledItemDelegate when
213 creating new delegates.
214
215 When displaying items from a custom model in a standard view, it is
216 often sufficient to simply ensure that the model returns appropriate
217 data for each of the \l{Qt::ItemDataRole}{roles} that determine the
218 appearance of items in views. The default delegate used by Qt's
219 standard views uses this role information to display items in most
220 of the common forms expected by users. However, it is sometimes
221 necessary to have even more control over the appearance of items than
222 the default delegate can provide.
223
224 This class provides default implementations of the functions for
225 painting item data in a view and editing data from item models.
226 Default implementations of the paint() and sizeHint() virtual
227 functions, defined in QAbstractItemDelegate, are provided to
228 ensure that the delegate implements the correct basic behavior
229 expected by views. You can reimplement these functions in
230 subclasses to customize the appearance of items.
231
232 When editing data in an item view, QItemDelegate provides an
233 editor widget, which is a widget that is placed on top of the view
234 while editing takes place. Editors are created with a
235 QItemEditorFactory; a default static instance provided by
236 QItemEditorFactory is installed on all item delegates. You can set
237 a custom factory using setItemEditorFactory() or set a new default
238 factory with QItemEditorFactory::setDefaultFactory(). It is the
239 data stored in the item model with the Qt::EditRole that is edited.
240
241 Only the standard editing functions for widget-based delegates are
242 reimplemented here:
243
244 \list
245 \li createEditor() returns the widget used to change data from the model
246 and can be reimplemented to customize editing behavior.
247 \li setEditorData() provides the widget with data to manipulate.
248 \li updateEditorGeometry() ensures that the editor is displayed correctly
249 with respect to the item view.
250 \li setModelData() returns updated data to the model.
251 \endlist
252
253 The closeEditor() signal indicates that the user has completed editing the data,
254 and that the editor widget can be destroyed.
255
256 \section1 Standard Roles and Data Types
257
258 The default delegate used by the standard views supplied with Qt
259 associates each standard role (defined by Qt::ItemDataRole) with certain
260 data types. Models that return data in these types can influence the
261 appearance of the delegate as described in the following table.
262
263 \table
264 \header \li Role \li Accepted Types
265 \omit
266 \row \li \l Qt::AccessibleDescriptionRole \li QString
267 \row \li \l Qt::AccessibleTextRole \li QString
268 \endomit
269 \row \li \l Qt::BackgroundRole \li QBrush (\since 4.2)
270 \row \li \l Qt::CheckStateRole \li Qt::CheckState
271 \row \li \l Qt::DecorationRole \li QIcon, QPixmap and QColor
272 \row \li \l Qt::DisplayRole \li QString and types with a string representation
273 \row \li \l Qt::EditRole \li See QItemEditorFactory for details
274 \row \li \l Qt::FontRole \li QFont
275 \row \li \l Qt::SizeHintRole \li QSize
276 \omit
277 \row \li \l Qt::StatusTipRole \li
278 \endomit
279 \row \li \l Qt::TextAlignmentRole \li Qt::Alignment
280 \row \li \l Qt::ForegroundRole \li QBrush (\since 4.2)
281 \omit
282 \row \li \l Qt::ToolTipRole
283 \row \li \l Qt::WhatsThisRole
284 \endomit
285 \endtable
286
287 If the default delegate does not allow the level of customization that
288 you need, either for display purposes or for editing data, it is possible to
289 subclass QItemDelegate to implement the desired behavior.
290
291 \section1 Subclassing
292
293 When subclassing QItemDelegate to create a delegate that displays items
294 using a custom renderer, it is important to ensure that the delegate can
295 render items suitably for all the required states; e.g. selected,
296 disabled, checked. The documentation for the paint() function contains
297 some hints to show how this can be achieved.
298
299 You can provide custom editors by using a QItemEditorFactory. The
300 \l{Color Editor Factory Example} shows how a custom editor can be
301 made available to delegates with the default item editor
302 factory. This way, there is no need to subclass QItemDelegate. An
303 alternative is to reimplement createEditor(), setEditorData(),
304 setModelData(), and updateEditorGeometry(). This process is
305 described in the \l{Spin Box Delegate Example}.
306
307 \section1 QStyledItemDelegate vs. QItemDelegate
308
309 Since Qt 4.4, there are two delegate classes: QItemDelegate and
310 QStyledItemDelegate. However, the default delegate is QStyledItemDelegate.
311 These two classes are independent alternatives to painting and providing
312 editors for items in views. The difference between them is that
313 QStyledItemDelegate uses the current style to paint its items. We therefore
314 recommend using QStyledItemDelegate as the base class when implementing
315 custom delegates or when working with Qt style sheets. The code required
316 for either class should be equal unless the custom delegate needs to use
317 the style for drawing.
318
319 \sa {Delegate Classes}, QStyledItemDelegate, QAbstractItemDelegate,
320 {Spin Box Delegate Example}, {Settings Editor Example},
321 {Icons Example}
322*/
323
324/*!
325 Constructs an item delegate with the given \a parent.
326*/
327
328QItemDelegate::QItemDelegate(QObject *parent)
329 : QAbstractItemDelegate(*new QItemDelegatePrivate(), parent)
330{
331
332}
333
334/*!
335 Destroys the item delegate.
336*/
337
338QItemDelegate::~QItemDelegate()
339{
340}
341
342/*!
343 \property QItemDelegate::clipping
344 \brief if the delegate should clip the paint events
345 \since 4.2
346
347 This property will set the paint clip to the size of the item.
348 The default value is on. It is useful for cases such
349 as when images are larger than the size of the item.
350*/
351
352bool QItemDelegate::hasClipping() const
353{
354 Q_D(const QItemDelegate);
355 return d->clipPainting;
356}
357
358void QItemDelegate::setClipping(bool clip)
359{
360 Q_D(QItemDelegate);
361 d->clipPainting = clip;
362}
363
364QString QItemDelegatePrivate::valueToText(const QVariant &value, const QStyleOptionViewItem &option) const
365{
366 return textForRole(Qt::DisplayRole, value, option.locale, DBL_DIG);
367}
368
369/*!
370 Renders the delegate using the given \a painter and style \a option for
371 the item specified by \a index.
372
373 When reimplementing this function in a subclass, you should update the area
374 held by the option's \l{QStyleOption::rect}{rect} variable, using the
375 option's \l{QStyleOption::state}{state} variable to determine the state of
376 the item to be displayed, and adjust the way it is painted accordingly.
377
378 For example, a selected item may need to be displayed differently to
379 unselected items, as shown in the following code:
380
381 \snippet itemviews/pixelator/pixeldelegate.cpp 2
382 \dots
383
384 After painting, you should ensure that the painter is returned to its
385 the state it was supplied in when this function was called. For example,
386 it may be useful to call QPainter::save() before painting and
387 QPainter::restore() afterwards.
388
389 \sa QStyle::State
390*/
391void QItemDelegate::paint(QPainter *painter,
392 const QStyleOptionViewItem &option,
393 const QModelIndex &index) const
394{
395 Q_D(const QItemDelegate);
396 Q_ASSERT(index.isValid());
397
398 QStyleOptionViewItem opt = setOptions(index, option);
399
400 // prepare
401 painter->save();
402 if (d->clipPainting)
403 painter->setClipRect(opt.rect);
404
405 // get the data and the rectangles
406
407 QVariant value;
408
409 QPixmap pixmap;
410 QRect decorationRect;
411 value = index.data(Qt::DecorationRole);
412 if (value.isValid()) {
413 // ### we need the pixmap to call the virtual function
414 pixmap = decoration(opt, value);
415 if (value.userType() == QMetaType::QIcon) {
416 d->tmp.icon = qvariant_cast<QIcon>(value);
417 d->tmp.mode = d->iconMode(option.state);
418 d->tmp.state = d->iconState(option.state);
419 const QSize size = d->tmp.icon.actualSize(option.decorationSize,
420 d->tmp.mode, d->tmp.state);
421 decorationRect = QRect(QPoint(0, 0), size);
422 } else {
423 d->tmp.icon = QIcon();
424 decorationRect = QRect(QPoint(0, 0), pixmap.size());
425 }
426 } else {
427 d->tmp.icon = QIcon();
428 decorationRect = QRect();
429 }
430
431 QRect checkRect;
432 Qt::CheckState checkState = Qt::Unchecked;
433 value = index.data(Qt::CheckStateRole);
434 if (value.isValid()) {
435 checkState = static_cast<Qt::CheckState>(value.toInt());
436 checkRect = doCheck(opt, opt.rect, value);
437 }
438
439 QString text;
440 QRect displayRect;
441 value = index.data(Qt::DisplayRole);
442 if (value.isValid() && !value.isNull()) {
443 text = d->valueToText(value, opt);
444 displayRect = d->displayRect(index, opt, decorationRect, checkRect);
445 }
446
447 // do the layout
448
449 doLayout(opt, &checkRect, &decorationRect, &displayRect, false);
450
451 // draw the item
452
453 drawBackground(painter, opt, index);
454 drawCheck(painter, opt, checkRect, checkState);
455 drawDecoration(painter, opt, decorationRect, pixmap);
456 drawDisplay(painter, opt, displayRect, text);
457 drawFocus(painter, opt, displayRect);
458
459 // done
460 painter->restore();
461}
462
463/*!
464 Returns the size needed by the delegate to display the item
465 specified by \a index, taking into account the style information
466 provided by \a option.
467
468 When reimplementing this function, note that in case of text
469 items, QItemDelegate adds a margin (i.e. 2 *
470 QStyle::PM_FocusFrameHMargin) to the length of the text.
471*/
472
473QSize QItemDelegate::sizeHint(const QStyleOptionViewItem &option,
474 const QModelIndex &index) const
475{
476 Q_D(const QItemDelegate);
477 QVariant value = index.data(Qt::SizeHintRole);
478 if (value.isValid())
479 return qvariant_cast<QSize>(value);
480 QRect decorationRect = rect(option, index, Qt::DecorationRole);
481 QRect checkRect = rect(option, index, Qt::CheckStateRole);
482 QRect displayRect = d->displayRect(index, option, decorationRect, checkRect);
483
484 doLayout(option, &checkRect, &decorationRect, &displayRect, true);
485
486 return (decorationRect|displayRect|checkRect).size();
487}
488
489/*!
490 Returns the widget used to edit the item specified by \a index
491 for editing. The \a parent widget and style \a option are used to
492 control how the editor widget appears.
493
494 \sa QAbstractItemDelegate::createEditor()
495*/
496
497QWidget *QItemDelegate::createEditor(QWidget *parent,
498 const QStyleOptionViewItem &,
499 const QModelIndex &index) const
500{
501 Q_D(const QItemDelegate);
502 if (!index.isValid())
503 return nullptr;
504 const QItemEditorFactory *factory = d->f;
505 if (factory == nullptr)
506 factory = QItemEditorFactory::defaultFactory();
507 QWidget *w = factory->createEditor(index.data(Qt::EditRole).userType(), parent);
508 if (w)
509 w->setFocusPolicy(Qt::WheelFocus);
510 return w;
511}
512
513/*!
514 Sets the data to be displayed and edited by the \a editor from the
515 data model item specified by the model \a index.
516
517 The default implementation stores the data in the \a editor
518 widget's \l {Qt's Property System} {user property}.
519
520 \sa QMetaProperty::isUser()
521*/
522
523void QItemDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
524{
525#ifdef QT_NO_PROPERTIES
526 Q_UNUSED(editor);
527 Q_UNUSED(index);
528#else
529 QVariant v = index.data(Qt::EditRole);
530 QByteArray n = editor->metaObject()->userProperty().name();
531
532 if (!n.isEmpty()) {
533 if (!v.isValid())
534 v = QVariant(editor->property(n).metaType());
535 editor->setProperty(n, v);
536 }
537#endif
538}
539
540/*!
541 Gets data from the \a editor widget and stores it in the specified
542 \a model at the item \a index.
543
544 The default implementation gets the value to be stored in the data
545 model from the \a editor widget's \l {Qt's Property System} {user
546 property}.
547
548 \sa QMetaProperty::isUser()
549*/
550
551void QItemDelegate::setModelData(QWidget *editor,
552 QAbstractItemModel *model,
553 const QModelIndex &index) const
554{
555#ifdef QT_NO_PROPERTIES
556 Q_UNUSED(model);
557 Q_UNUSED(editor);
558 Q_UNUSED(index);
559#else
560 Q_D(const QItemDelegate);
561 Q_ASSERT(model);
562 Q_ASSERT(editor);
563 QByteArray n = editor->metaObject()->userProperty().name();
564 if (n.isEmpty())
565 n = d->editorFactory()->valuePropertyName(
566 model->data(index, Qt::EditRole).userType());
567 if (!n.isEmpty())
568 model->setData(index, editor->property(n), Qt::EditRole);
569#endif
570}
571
572/*!
573 Updates the \a editor for the item specified by \a index
574 according to the style \a option given.
575*/
576
577void QItemDelegate::updateEditorGeometry(QWidget *editor,
578 const QStyleOptionViewItem &option,
579 const QModelIndex &index) const
580{
581 if (!editor)
582 return;
583 Q_ASSERT(index.isValid());
584 QPixmap pixmap = decoration(option, index.data(Qt::DecorationRole));
585 QString text = QItemDelegatePrivate::replaceNewLine(index.data(Qt::DisplayRole).toString());
586 QRect pixmapRect = QRect(QPoint(0, 0), option.decorationSize).intersected(pixmap.rect());
587 QRect textRect = textRectangle(nullptr, option.rect, option.font, text);
588 QRect checkRect = doCheck(option, textRect, index.data(Qt::CheckStateRole));
589 QStyleOptionViewItem opt = option;
590 opt.showDecorationSelected = true; // let the editor take up all available space
591 doLayout(opt, &checkRect, &pixmapRect, &textRect, false);
592 editor->setGeometry(textRect);
593}
594
595/*!
596 Returns the editor factory used by the item delegate.
597 If no editor factory is set, the function will return null.
598
599 \sa setItemEditorFactory()
600*/
601QItemEditorFactory *QItemDelegate::itemEditorFactory() const
602{
603 Q_D(const QItemDelegate);
604 return d->f;
605}
606
607/*!
608 Sets the editor factory to be used by the item delegate to be the \a factory
609 specified. If no editor factory is set, the item delegate will use the
610 default editor factory.
611
612 \sa itemEditorFactory()
613*/
614void QItemDelegate::setItemEditorFactory(QItemEditorFactory *factory)
615{
616 Q_D(QItemDelegate);
617 d->f = factory;
618}
619
620/*!
621 Renders the item view \a text within the rectangle specified by \a rect
622 using the given \a painter and style \a option.
623*/
624
625void QItemDelegate::drawDisplay(QPainter *painter, const QStyleOptionViewItem &option,
626 const QRect &rect, const QString &text) const
627{
628 Q_D(const QItemDelegate);
629
630 QPalette::ColorGroup cg = option.state & QStyle::State_Enabled
631 ? QPalette::Normal : QPalette::Disabled;
632 if (cg == QPalette::Normal && !(option.state & QStyle::State_Active))
633 cg = QPalette::Inactive;
634 if (option.state & QStyle::State_Selected) {
635 painter->fillRect(rect, option.palette.brush(cg, QPalette::Highlight));
636 painter->setPen(option.palette.color(cg, QPalette::HighlightedText));
637 } else {
638 painter->setPen(option.palette.color(cg, QPalette::Text));
639 }
640
641 if (text.isEmpty())
642 return;
643
644 if (option.state & QStyle::State_Editing) {
645 painter->save();
646 painter->setPen(option.palette.color(cg, QPalette::Text));
647 painter->drawRect(rect.adjusted(0, 0, -1, -1));
648 painter->restore();
649 }
650
651 const QStyleOptionViewItem opt = option;
652
653 const QWidget *widget = d->widget(option);
654 QStyle *style = widget ? widget->style() : QApplication::style();
655 const int textMargin = style->pixelMetric(QStyle::PM_FocusFrameHMargin, nullptr, widget) + 1;
656 QRect textRect = rect.adjusted(textMargin, 0, -textMargin, 0); // remove width padding
657 const bool wrapText = opt.features & QStyleOptionViewItem::WrapText;
658 d->textOption.setWrapMode(wrapText ? QTextOption::WordWrap : QTextOption::ManualWrap);
659 d->textOption.setTextDirection(option.direction);
660 d->textOption.setAlignment(QStyle::visualAlignment(option.direction, option.displayAlignment));
661 d->textLayout.setTextOption(d->textOption);
662 d->textLayout.setFont(option.font);
663 d->textLayout.setText(QItemDelegatePrivate::replaceNewLine(text));
664
665 QSizeF textLayoutSize = d->doTextLayout(textRect.width());
666
667 if (textRect.width() < textLayoutSize.width()
668 || textRect.height() < textLayoutSize.height()) {
669 QString elided;
670 int start = 0;
671 int end = text.indexOf(QChar::LineSeparator, start);
672 if (end == -1) {
673 elided += option.fontMetrics.elidedText(text, option.textElideMode, textRect.width());
674 } else {
675 while (end != -1) {
676 elided += option.fontMetrics.elidedText(text.mid(start, end - start),
677 option.textElideMode, textRect.width());
678 elided += QChar::LineSeparator;
679 start = end + 1;
680 end = text.indexOf(QChar::LineSeparator, start);
681 }
682 //let's add the last line (after the last QChar::LineSeparator)
683 elided += option.fontMetrics.elidedText(text.mid(start),
684 option.textElideMode, textRect.width());
685 }
686 d->textLayout.setText(elided);
687 textLayoutSize = d->doTextLayout(textRect.width());
688 }
689
690 const QSize layoutSize(textRect.width(), int(textLayoutSize.height()));
691 const QRect layoutRect = QStyle::alignedRect(option.direction, option.displayAlignment,
692 layoutSize, textRect);
693 // if we still overflow even after eliding the text, enable clipping
694 if (!hasClipping() && (textRect.width() < textLayoutSize.width()
695 || textRect.height() < textLayoutSize.height())) {
696 painter->save();
697 painter->setClipRect(layoutRect);
698 d->textLayout.draw(painter, layoutRect.topLeft(), QList<QTextLayout::FormatRange>(),
699 layoutRect);
700 painter->restore();
701 } else {
702 d->textLayout.draw(painter, layoutRect.topLeft(), QList<QTextLayout::FormatRange>(),
703 layoutRect);
704 }
705}
706
707/*!
708 Renders the decoration \a pixmap within the rectangle specified by
709 \a rect using the given \a painter and style \a option.
710*/
711void QItemDelegate::drawDecoration(QPainter *painter, const QStyleOptionViewItem &option,
712 const QRect &rect, const QPixmap &pixmap) const
713{
714 Q_D(const QItemDelegate);
715 // if we have an icon, we ignore the pixmap
716 if (!d->tmp.icon.isNull()) {
717 d->tmp.icon.paint(painter, rect, option.decorationAlignment,
718 d->tmp.mode, d->tmp.state);
719 return;
720 }
721
722 if (pixmap.isNull() || !rect.isValid())
723 return;
724 QPoint p = QStyle::alignedRect(option.direction, option.decorationAlignment,
725 pixmap.size(), rect).topLeft();
726 if (option.state & QStyle::State_Selected) {
727 const QPixmap pm = selectedPixmap(pixmap, option.palette, option.state & QStyle::State_Enabled);
728 painter->drawPixmap(p, pm);
729 } else {
730 painter->drawPixmap(p, pixmap);
731 }
732}
733
734/*!
735 Renders the region within the rectangle specified by \a rect, indicating
736 that it has the focus, using the given \a painter and style \a option.
737*/
738
739void QItemDelegate::drawFocus(QPainter *painter,
740 const QStyleOptionViewItem &option,
741 const QRect &rect) const
742{
743 Q_D(const QItemDelegate);
744 if ((option.state & QStyle::State_HasFocus) == 0 || !rect.isValid())
745 return;
746 QStyleOptionFocusRect o;
747 o.QStyleOption::operator=(option);
748 o.rect = rect;
749 o.state |= QStyle::State_KeyboardFocusChange;
750 o.state |= QStyle::State_Item;
751 QPalette::ColorGroup cg = (option.state & QStyle::State_Enabled)
752 ? QPalette::Normal : QPalette::Disabled;
753 o.backgroundColor = option.palette.color(cg, (option.state & QStyle::State_Selected)
754 ? QPalette::Highlight : QPalette::Window);
755 const QWidget *widget = d->widget(option);
756 QStyle *style = widget ? widget->style() : QApplication::style();
757 style->drawPrimitive(QStyle::PE_FrameFocusRect, &o, painter, widget);
758}
759
760/*!
761 Renders a check indicator within the rectangle specified by \a
762 rect, using the given \a painter and style \a option, using the
763 given \a state.
764*/
765
766void QItemDelegate::drawCheck(QPainter *painter,
767 const QStyleOptionViewItem &option,
768 const QRect &rect, Qt::CheckState state) const
769{
770 Q_D(const QItemDelegate);
771 if (!rect.isValid())
772 return;
773
774 QStyleOptionViewItem opt(option);
775 opt.rect = rect;
776 opt.state = opt.state & ~QStyle::State_HasFocus;
777
778 switch (state) {
779 case Qt::Unchecked:
780 opt.state |= QStyle::State_Off;
781 break;
782 case Qt::PartiallyChecked:
783 opt.state |= QStyle::State_NoChange;
784 break;
785 case Qt::Checked:
786 opt.state |= QStyle::State_On;
787 break;
788 }
789
790 const QWidget *widget = d->widget(option);
791 QStyle *style = widget ? widget->style() : QApplication::style();
792 style->drawPrimitive(QStyle::PE_IndicatorItemViewItemCheck, &opt, painter, widget);
793}
794
795/*!
796 \since 4.2
797
798 Renders the item background for the given \a index,
799 using the given \a painter and style \a option.
800*/
801
802void QItemDelegate::drawBackground(QPainter *painter,
803 const QStyleOptionViewItem &option,
804 const QModelIndex &index) const
805{
806 if (option.showDecorationSelected && (option.state & QStyle::State_Selected)) {
807 QPalette::ColorGroup cg = option.state & QStyle::State_Enabled
808 ? QPalette::Normal : QPalette::Disabled;
809 if (cg == QPalette::Normal && !(option.state & QStyle::State_Active))
810 cg = QPalette::Inactive;
811
812 painter->fillRect(option.rect, option.palette.brush(cg, QPalette::Highlight));
813 } else {
814 QVariant value = index.data(Qt::BackgroundRole);
815 if (value.canConvert<QBrush>()) {
816 QPointF oldBO = painter->brushOrigin();
817 painter->setBrushOrigin(option.rect.topLeft());
818 painter->fillRect(option.rect, qvariant_cast<QBrush>(value));
819 painter->setBrushOrigin(oldBO);
820 }
821 }
822}
823
824
825/*!
826 \internal
827
828 Code duplicated in QCommonStylePrivate::viewItemLayout
829*/
830
831void QItemDelegate::doLayout(const QStyleOptionViewItem &option,
832 QRect *checkRect, QRect *pixmapRect, QRect *textRect,
833 bool hint) const
834{
835 Q_ASSERT(checkRect && pixmapRect && textRect);
836 Q_D(const QItemDelegate);
837 const QWidget *widget = d->widget(option);
838 QStyle *style = widget ? widget->style() : QApplication::style();
839 const bool hasCheck = checkRect->isValid();
840 const bool hasPixmap = pixmapRect->isValid();
841 const bool hasText = textRect->isValid();
842 const bool hasMargin = (hasText | hasPixmap | hasCheck);
843 const int frameHMargin = hasMargin ?
844 style->pixelMetric(QStyle::PM_FocusFrameHMargin, nullptr, widget) + 1 : 0;
845 const int textMargin = hasText ? frameHMargin : 0;
846 const int pixmapMargin = hasPixmap ? frameHMargin : 0;
847 const int checkMargin = hasCheck ? frameHMargin : 0;
848 const int x = option.rect.left();
849 const int y = option.rect.top();
850 int w, h;
851
852 textRect->adjust(-textMargin, 0, textMargin, 0); // add width padding
853 if (textRect->height() == 0 && (!hasPixmap || !hint)) {
854 //if there is no text, we still want to have a decent height for the item sizeHint and the editor size
855 textRect->setHeight(option.fontMetrics.height());
856 }
857
858 QSize pm(0, 0);
859 if (hasPixmap) {
860 pm = pixmapRect->size();
861 pm.rwidth() += 2 * pixmapMargin;
862 }
863 if (hint) {
864 h = qMax(checkRect->height(), qMax(textRect->height(), pm.height()));
865 if (option.decorationPosition == QStyleOptionViewItem::Left
866 || option.decorationPosition == QStyleOptionViewItem::Right) {
867 w = textRect->width() + pm.width();
868 } else {
869 w = qMax(textRect->width(), pm.width());
870 }
871 } else {
872 w = option.rect.width();
873 h = option.rect.height();
874 }
875
876 int cw = 0;
877 QRect check;
878 if (hasCheck) {
879 cw = checkRect->width() + 2 * checkMargin;
880 if (hint) w += cw;
881 if (option.direction == Qt::RightToLeft) {
882 check.setRect(x + w - cw, y, cw, h);
883 } else {
884 check.setRect(x, y, cw, h);
885 }
886 }
887
888 // at this point w should be the *total* width
889
890 QRect display;
891 QRect decoration;
892 switch (option.decorationPosition) {
893 case QStyleOptionViewItem::Top: {
894 if (hasPixmap)
895 pm.setHeight(pm.height() + pixmapMargin); // add space
896 h = hint ? textRect->height() : h - pm.height();
897
898 if (option.direction == Qt::RightToLeft) {
899 decoration.setRect(x, y, w - cw, pm.height());
900 display.setRect(x, y + pm.height(), w - cw, h);
901 } else {
902 decoration.setRect(x + cw, y, w - cw, pm.height());
903 display.setRect(x + cw, y + pm.height(), w - cw, h);
904 }
905 break; }
906 case QStyleOptionViewItem::Bottom: {
907 if (hasText)
908 textRect->setHeight(textRect->height() + textMargin); // add space
909 h = hint ? textRect->height() + pm.height() : h;
910
911 if (option.direction == Qt::RightToLeft) {
912 display.setRect(x, y, w - cw, textRect->height());
913 decoration.setRect(x, y + textRect->height(), w - cw, h - textRect->height());
914 } else {
915 display.setRect(x + cw, y, w - cw, textRect->height());
916 decoration.setRect(x + cw, y + textRect->height(), w - cw, h - textRect->height());
917 }
918 break; }
919 case QStyleOptionViewItem::Left: {
920 if (option.direction == Qt::LeftToRight) {
921 decoration.setRect(x + cw, y, pm.width(), h);
922 display.setRect(decoration.right() + 1, y, w - pm.width() - cw, h);
923 } else {
924 display.setRect(x, y, w - pm.width() - cw, h);
925 decoration.setRect(display.right() + 1, y, pm.width(), h);
926 }
927 break; }
928 case QStyleOptionViewItem::Right: {
929 if (option.direction == Qt::LeftToRight) {
930 display.setRect(x + cw, y, w - pm.width() - cw, h);
931 decoration.setRect(display.right() + 1, y, pm.width(), h);
932 } else {
933 decoration.setRect(x, y, pm.width(), h);
934 display.setRect(decoration.right() + 1, y, w - pm.width() - cw, h);
935 }
936 break; }
937 default:
938 qWarning("doLayout: decoration position is invalid");
939 decoration = *pixmapRect;
940 break;
941 }
942
943 if (!hint) { // we only need to do the internal layout if we are going to paint
944 *checkRect = QStyle::alignedRect(option.direction, Qt::AlignCenter,
945 checkRect->size(), check);
946 *pixmapRect = QStyle::alignedRect(option.direction, option.decorationAlignment,
947 pixmapRect->size(), decoration);
948 // the text takes up all available space, unless the decoration is not shown as selected
949 if (option.showDecorationSelected)
950 *textRect = display;
951 else
952 *textRect = QStyle::alignedRect(option.direction, option.displayAlignment,
953 textRect->size().boundedTo(display.size()), display);
954 } else {
955 *checkRect = check;
956 *pixmapRect = decoration;
957 *textRect = display;
958 }
959}
960
961/*!
962 \internal
963
964 Returns the pixmap used to decorate the root of the item view.
965 The style \a option controls the appearance of the root; the \a variant
966 refers to the data associated with an item.
967*/
968
969QPixmap QItemDelegate::decoration(const QStyleOptionViewItem &option, const QVariant &variant) const
970{
971 Q_D(const QItemDelegate);
972 switch (variant.userType()) {
973 case QMetaType::QIcon: {
974 QIcon::Mode mode = d->iconMode(option.state);
975 QIcon::State state = d->iconState(option.state);
976 return qvariant_cast<QIcon>(variant).pixmap(option.decorationSize, mode, state); }
977 case QMetaType::QColor: {
978 static QPixmap pixmap(option.decorationSize);
979 pixmap.fill(qvariant_cast<QColor>(variant));
980 return pixmap; }
981 default:
982 break;
983 }
984
985 return qvariant_cast<QPixmap>(variant);
986}
987
988// hacky but faster version of "QString::asprintf("%d-%d", i, enabled)"
989static QString qPixmapSerial(quint64 i, bool enabled)
990{
991 ushort arr[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '-', ushort('0' + enabled) };
992 ushort *ptr = &arr[16];
993
994 while (i > 0) {
995 // hey - it's our internal representation, so use the ascii character after '9'
996 // instead of 'a' for hex
997 *(--ptr) = '0' + i % 16;
998 i >>= 4;
999 }
1000
1001 return QString((const QChar *)ptr, int(&arr[sizeof(arr) / sizeof(ushort)] - ptr));
1002}
1003
1004
1005/*!
1006 \internal
1007 Returns the selected version of the given \a pixmap using the given \a palette.
1008 The \a enabled argument decides whether the normal or disabled highlight color of
1009 the palette is used.
1010*/
1011QPixmap QItemDelegate::selectedPixmap(const QPixmap &pixmap, const QPalette &palette, bool enabled)
1012{
1013 const QString key = qPixmapSerial(pixmap.cacheKey(), enabled);
1014 QPixmap pm;
1015 if (!QPixmapCache::find(key, &pm)) {
1016 QImage img = pixmap.toImage().convertToFormat(QImage::Format_ARGB32_Premultiplied);
1017
1018 QColor color = palette.color(enabled ? QPalette::Normal : QPalette::Disabled,
1019 QPalette::Highlight);
1020 color.setAlphaF(0.3f);
1021
1022 QPainter painter(&img);
1023 painter.setCompositionMode(QPainter::CompositionMode_SourceAtop);
1024 painter.fillRect(0, 0, img.width(), img.height(), color);
1025 painter.end();
1026
1027 pm = QPixmap(QPixmap::fromImage(img));
1028 const int n = (img.sizeInBytes() >> 10) + 1;
1029 if (QPixmapCache::cacheLimit() < n)
1030 QPixmapCache::setCacheLimit(n);
1031
1032 QPixmapCache::insert(key, pm);
1033 }
1034 return pm;
1035}
1036
1037/*!
1038 \internal
1039 Only used (and usable) for Qt::DecorationRole and Qt::CheckStateRole
1040*/
1041QRect QItemDelegate::rect(const QStyleOptionViewItem &option,
1042 const QModelIndex &index, int role) const
1043{
1044 Q_D(const QItemDelegate);
1045 QVariant value = index.data(role);
1046 if (role == Qt::CheckStateRole)
1047 return doCheck(option, option.rect, value);
1048 if (value.isValid() && !value.isNull()) {
1049 switch (value.userType()) {
1050 case QMetaType::UnknownType:
1051 break;
1052 case QMetaType::QPixmap: {
1053 const QPixmap &pixmap = qvariant_cast<QPixmap>(value);
1054 return QRect(QPoint(0, 0), pixmap.size() / pixmap.devicePixelRatio() ); }
1055 case QMetaType::QImage: {
1056 const QImage &image = qvariant_cast<QImage>(value);
1057 return QRect(QPoint(0, 0), image.size() / image.devicePixelRatio() ); }
1058 case QMetaType::QIcon: {
1059 QIcon::Mode mode = d->iconMode(option.state);
1060 QIcon::State state = d->iconState(option.state);
1061 QIcon icon = qvariant_cast<QIcon>(value);
1062 QSize size = icon.actualSize(option.decorationSize, mode, state);
1063 return QRect(QPoint(0, 0), size); }
1064 case QMetaType::QColor:
1065 return QRect(QPoint(0, 0), option.decorationSize);
1066 case QMetaType::QString:
1067 default: {
1068 const QString text = d->valueToText(value, option);
1069 value = index.data(Qt::FontRole);
1070 QFont fnt = qvariant_cast<QFont>(value).resolve(option.font);
1071 return textRectangle(nullptr,
1072 d->textLayoutBounds(option, QRect(), QRect()),
1073 fnt, text); }
1074 }
1075 }
1076 return QRect();
1077}
1078
1079/*!
1080 \internal
1081*/
1082QRect QItemDelegate::doCheck(const QStyleOptionViewItem &option,
1083 const QRect &bounding, const QVariant &value) const
1084{
1085 if (value.isValid()) {
1086 Q_D(const QItemDelegate);
1087 QStyleOptionButton opt;
1088 opt.QStyleOption::operator=(option);
1089 opt.rect = bounding;
1090 const QWidget *widget = d->widget(option); // cast
1091 QStyle *style = widget ? widget->style() : QApplication::style();
1092 return style->subElementRect(QStyle::SE_ItemViewItemCheckIndicator, &opt, widget);
1093 }
1094 return QRect();
1095}
1096
1097/*!
1098 \internal
1099*/
1100QRect QItemDelegate::textRectangle(QPainter * /*painter*/, const QRect &rect,
1101 const QFont &font, const QString &text) const
1102{
1103 Q_D(const QItemDelegate);
1104 d->textOption.setWrapMode(QTextOption::WordWrap);
1105 d->textLayout.setTextOption(d->textOption);
1106 d->textLayout.setFont(font);
1107 d->textLayout.setText(QItemDelegatePrivate::replaceNewLine(text));
1108 QSizeF fpSize = d->doTextLayout(rect.width());
1109 const QSize size = QSize(qCeil(fpSize.width()), qCeil(fpSize.height()));
1110 // ###: textRectangle should take style option as argument
1111 const int textMargin = QApplication::style()->pixelMetric(QStyle::PM_FocusFrameHMargin, nullptr) + 1;
1112 return QRect(0, 0, size.width() + 2 * textMargin, size.height());
1113}
1114
1115/*!
1116 \fn bool QItemDelegate::eventFilter(QObject *editor, QEvent *event)
1117
1118 Returns \c true if the given \a editor is a valid QWidget and the
1119 given \a event is handled; otherwise returns \c false. The following
1120 key press events are handled by default:
1121
1122 \list
1123 \li \uicontrol Tab
1124 \li \uicontrol Backtab
1125 \li \uicontrol Enter
1126 \li \uicontrol Return
1127 \li \uicontrol Esc
1128 \endlist
1129
1130 In the case of \uicontrol Tab, \uicontrol Backtab, \uicontrol Enter and \uicontrol Return
1131 key press events, the \a editor's data is committed to the model
1132 and the editor is closed. If the \a event is a \uicontrol Tab key press
1133 the view will open an editor on the next item in the
1134 view. Likewise, if the \a event is a \uicontrol Backtab key press the
1135 view will open an editor on the \e previous item in the view.
1136
1137 If the event is a \uicontrol Esc key press event, the \a editor is
1138 closed \e without committing its data.
1139
1140 \sa commitData(), closeEditor()
1141*/
1142
1143bool QItemDelegate::eventFilter(QObject *object, QEvent *event)
1144{
1145 Q_D(QItemDelegate);
1146 return d->editorEventFilter(object, event);
1147}
1148
1149/*!
1150 \reimp
1151*/
1152
1153bool QItemDelegate::editorEvent(QEvent *event,
1154 QAbstractItemModel *model,
1155 const QStyleOptionViewItem &option,
1156 const QModelIndex &index)
1157{
1158 Q_ASSERT(event);
1159 Q_ASSERT(model);
1160
1161 // make sure that the item is checkable
1162 Qt::ItemFlags flags = model->flags(index);
1163 if (!(flags & Qt::ItemIsUserCheckable) || !(option.state & QStyle::State_Enabled)
1164 || !(flags & Qt::ItemIsEnabled))
1165 return false;
1166
1167 // make sure that we have a check state
1168 QVariant value = index.data(Qt::CheckStateRole);
1169 if (!value.isValid())
1170 return false;
1171
1172 // make sure that we have the right event type
1173 if ((event->type() == QEvent::MouseButtonRelease)
1174 || (event->type() == QEvent::MouseButtonDblClick)
1175 || (event->type() == QEvent::MouseButtonPress)) {
1176 QRect checkRect = doCheck(option, option.rect, Qt::Checked);
1177 QRect emptyRect;
1178 doLayout(option, &checkRect, &emptyRect, &emptyRect, false);
1179 QMouseEvent *me = static_cast<QMouseEvent*>(event);
1180 if (me->button() != Qt::LeftButton || !checkRect.contains(me->position().toPoint()))
1181 return false;
1182
1183 // eat the double click events inside the check rect
1184 if ((event->type() == QEvent::MouseButtonPress)
1185 || (event->type() == QEvent::MouseButtonDblClick))
1186 return true;
1187
1188 } else if (event->type() == QEvent::KeyPress) {
1189 if (static_cast<QKeyEvent*>(event)->key() != Qt::Key_Space
1190 && static_cast<QKeyEvent*>(event)->key() != Qt::Key_Select)
1191 return false;
1192 } else {
1193 return false;
1194 }
1195
1196 Qt::CheckState state = static_cast<Qt::CheckState>(value.toInt());
1197 if (flags & Qt::ItemIsUserTristate)
1198 state = ((Qt::CheckState)((state + 1) % 3));
1199 else
1200 state = (state == Qt::Checked) ? Qt::Unchecked : Qt::Checked;
1201 return model->setData(index, state, Qt::CheckStateRole);
1202}
1203
1204/*!
1205 \internal
1206*/
1207
1208QStyleOptionViewItem QItemDelegate::setOptions(const QModelIndex &index,
1209 const QStyleOptionViewItem &option) const
1210{
1211 QStyleOptionViewItem opt = option;
1212
1213 // set font
1214 QVariant value = index.data(Qt::FontRole);
1215 if (value.isValid()){
1216 opt.font = qvariant_cast<QFont>(value).resolve(opt.font);
1217 opt.fontMetrics = QFontMetrics(opt.font);
1218 }
1219
1220 // set text alignment
1221 value = index.data(Qt::TextAlignmentRole);
1222 if (value.isValid())
1223 opt.displayAlignment = Qt::Alignment(value.toInt());
1224
1225 // set foreground brush
1226 value = index.data(Qt::ForegroundRole);
1227 if (value.canConvert<QBrush>())
1228 opt.palette.setBrush(QPalette::Text, qvariant_cast<QBrush>(value));
1229
1230 // disable style animations for checkboxes etc. within itemviews (QTBUG-30146)
1231 opt.styleObject = nullptr;
1232
1233 return opt;
1234}
1235
1236QT_END_NAMESPACE
1237
1238#include "moc_qitemdelegate.cpp"
1239