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