1 | /**************************************************************************** |
2 | ** |
3 | ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies). |
4 | ** Contact: http://www.qt-project.org/legal |
5 | ** |
6 | ** This file is part of the QtGui 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 Digia. For licensing terms and |
14 | ** conditions see http://qt.digia.com/licensing. For further information |
15 | ** use the contact form at http://qt.digia.com/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 2.1 as published by the Free Software |
20 | ** Foundation and appearing in the file LICENSE.LGPL included in the |
21 | ** packaging of this file. Please review the following information to |
22 | ** ensure the GNU Lesser General Public License version 2.1 requirements |
23 | ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. |
24 | ** |
25 | ** In addition, as a special exception, Digia gives you certain additional |
26 | ** rights. These rights are described in the Digia Qt LGPL Exception |
27 | ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. |
28 | ** |
29 | ** GNU General Public License Usage |
30 | ** Alternatively, this file may be used under the terms of the GNU |
31 | ** General Public License version 3.0 as published by the Free Software |
32 | ** Foundation and appearing in the file LICENSE.GPL included in the |
33 | ** packaging of this file. Please review the following information to |
34 | ** ensure the GNU General Public License version 3.0 requirements will be |
35 | ** met: http://www.gnu.org/copyleft/gpl.html. |
36 | ** |
37 | ** |
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 | #include <qmath.h> |
73 | |
74 | #include <limits.h> |
75 | |
76 | #ifndef DBL_DIG |
77 | # define DBL_DIG 10 |
78 | #endif |
79 | |
80 | QT_BEGIN_NAMESPACE |
81 | |
82 | class QItemDelegatePrivate : public QObjectPrivate |
83 | { |
84 | Q_DECLARE_PUBLIC(QItemDelegate) |
85 | |
86 | public: |
87 | QItemDelegatePrivate() : f(0), clipPainting(true) {} |
88 | |
89 | inline const QItemEditorFactory *editorFactory() const |
90 | { return f ? f : QItemEditorFactory::defaultFactory(); } |
91 | |
92 | inline QIcon::Mode iconMode(QStyle::State state) const |
93 | { |
94 | if (!(state & QStyle::State_Enabled)) return QIcon::Disabled; |
95 | if (state & QStyle::State_Selected) return QIcon::Selected; |
96 | return QIcon::Normal; |
97 | } |
98 | |
99 | inline QIcon::State iconState(QStyle::State state) const |
100 | { return state & QStyle::State_Open ? QIcon::On : QIcon::Off; } |
101 | |
102 | inline static QString replaceNewLine(QString text) |
103 | { |
104 | const QChar nl = QLatin1Char('\n'); |
105 | for (int i = 0; i < text.count(); ++i) |
106 | if (text.at(i) == nl) |
107 | text[i] = QChar::LineSeparator; |
108 | return text; |
109 | } |
110 | |
111 | static QString valueToText(const QVariant &value, const QStyleOptionViewItemV4 &option); |
112 | |
113 | void _q_commitDataAndCloseEditor(QWidget *editor); |
114 | |
115 | QItemEditorFactory *f; |
116 | bool clipPainting; |
117 | |
118 | QRect textLayoutBounds(const QStyleOptionViewItemV2 &options) const; |
119 | QSizeF doTextLayout(int lineWidth) const; |
120 | mutable QTextLayout textLayout; |
121 | mutable QTextOption textOption; |
122 | |
123 | const QWidget *widget(const QStyleOptionViewItem &option) const |
124 | { |
125 | if (const QStyleOptionViewItemV3 *v3 = qstyleoption_cast<const QStyleOptionViewItemV3 *>(&option)) |
126 | return v3->widget; |
127 | |
128 | return 0; |
129 | } |
130 | |
131 | // ### temporary hack until we have QStandardItemDelegate |
132 | mutable struct Icon { |
133 | QIcon icon; |
134 | QIcon::Mode mode; |
135 | QIcon::State state; |
136 | } tmp; |
137 | }; |
138 | |
139 | void QItemDelegatePrivate::_q_commitDataAndCloseEditor(QWidget *editor) |
140 | { |
141 | Q_Q(QItemDelegate); |
142 | emit q->commitData(editor); |
143 | emit q->closeEditor(editor, QAbstractItemDelegate::SubmitModelCache); |
144 | } |
145 | |
146 | QRect QItemDelegatePrivate::textLayoutBounds(const QStyleOptionViewItemV2 &option) const |
147 | { |
148 | QRect rect = option.rect; |
149 | const bool wrapText = option.features & QStyleOptionViewItemV2::WrapText; |
150 | switch (option.decorationPosition) { |
151 | case QStyleOptionViewItem::Left: |
152 | case QStyleOptionViewItem::Right: |
153 | rect.setWidth(wrapText && rect.isValid() ? rect.width() : (QFIXED_MAX)); |
154 | break; |
155 | case QStyleOptionViewItem::Top: |
156 | case QStyleOptionViewItem::Bottom: |
157 | rect.setWidth(wrapText ? option.decorationSize.width() : (QFIXED_MAX)); |
158 | break; |
159 | } |
160 | |
161 | return rect; |
162 | } |
163 | |
164 | QSizeF QItemDelegatePrivate::doTextLayout(int lineWidth) const |
165 | { |
166 | qreal height = 0; |
167 | qreal widthUsed = 0; |
168 | textLayout.beginLayout(); |
169 | while (true) { |
170 | QTextLine line = textLayout.createLine(); |
171 | if (!line.isValid()) |
172 | break; |
173 | line.setLineWidth(lineWidth); |
174 | line.setPosition(QPointF(0, height)); |
175 | height += line.height(); |
176 | widthUsed = qMax(widthUsed, line.naturalTextWidth()); |
177 | } |
178 | textLayout.endLayout(); |
179 | return QSizeF(widthUsed, height); |
180 | } |
181 | |
182 | /*! |
183 | \class QItemDelegate |
184 | |
185 | \brief The QItemDelegate class provides display and editing facilities for |
186 | data items from a model. |
187 | |
188 | \ingroup model-view |
189 | |
190 | |
191 | QItemDelegate can be used to provide custom display features and editor |
192 | widgets for item views based on QAbstractItemView subclasses. Using a |
193 | delegate for this purpose allows the display and editing mechanisms to be |
194 | customized and developed independently from the model and view. |
195 | |
196 | The QItemDelegate class is one of the \l{Model/View Classes} and |
197 | is part of Qt's \l{Model/View Programming}{model/view framework}. |
198 | Note that QStyledItemDelegate has taken over the job of drawing |
199 | Qt's item views. We recommend the use of QStyledItemDelegate when |
200 | creating new delegates. |
201 | |
202 | When displaying items from a custom model in a standard view, it is |
203 | often sufficient to simply ensure that the model returns appropriate |
204 | data for each of the \l{Qt::ItemDataRole}{roles} that determine the |
205 | appearance of items in views. The default delegate used by Qt's |
206 | standard views uses this role information to display items in most |
207 | of the common forms expected by users. However, it is sometimes |
208 | necessary to have even more control over the appearance of items than |
209 | the default delegate can provide. |
210 | |
211 | This class provides default implementations of the functions for |
212 | painting item data in a view and editing data from item models. |
213 | Default implementations of the paint() and sizeHint() virtual |
214 | functions, defined in QAbstractItemDelegate, are provided to |
215 | ensure that the delegate implements the correct basic behavior |
216 | expected by views. You can reimplement these functions in |
217 | subclasses to customize the appearance of items. |
218 | |
219 | When editing data in an item view, QItemDelegate provides an |
220 | editor widget, which is a widget that is placed on top of the view |
221 | while editing takes place. Editors are created with a |
222 | QItemEditorFactory; a default static instance provided by |
223 | QItemEditorFactory is installed on all item delegates. You can set |
224 | a custom factory using setItemEditorFactory() or set a new default |
225 | factory with QItemEditorFactory::setDefaultFactory(). It is the |
226 | data stored in the item model with the Qt::EditRole that is edited. |
227 | |
228 | Only the standard editing functions for widget-based delegates are |
229 | reimplemented here: |
230 | |
231 | \list |
232 | \o createEditor() returns the widget used to change data from the model |
233 | and can be reimplemented to customize editing behavior. |
234 | \o setEditorData() provides the widget with data to manipulate. |
235 | \o updateEditorGeometry() ensures that the editor is displayed correctly |
236 | with respect to the item view. |
237 | \o setModelData() returns updated data to the model. |
238 | \endlist |
239 | |
240 | The closeEditor() signal indicates that the user has completed editing the data, |
241 | and that the editor widget can be destroyed. |
242 | |
243 | \section1 Standard Roles and Data Types |
244 | |
245 | The default delegate used by the standard views supplied with Qt |
246 | associates each standard role (defined by Qt::ItemDataRole) with certain |
247 | data types. Models that return data in these types can influence the |
248 | appearance of the delegate as described in the following table. |
249 | |
250 | \table |
251 | \header \o Role \o Accepted Types |
252 | \omit |
253 | \row \o \l Qt::AccessibleDescriptionRole \o QString |
254 | \row \o \l Qt::AccessibleTextRole \o QString |
255 | \endomit |
256 | \row \o \l Qt::BackgroundRole \o QBrush |
257 | \row \o \l Qt::BackgroundColorRole \o QColor (obsolete; use Qt::BackgroundRole instead) |
258 | \row \o \l Qt::CheckStateRole \o Qt::CheckState |
259 | \row \o \l Qt::DecorationRole \o QIcon, QPixmap and QColor |
260 | \row \o \l Qt::DisplayRole \o QString and types with a string representation |
261 | \row \o \l Qt::EditRole \o See QItemEditorFactory for details |
262 | \row \o \l Qt::FontRole \o QFont |
263 | \row \o \l Qt::SizeHintRole \o QSize |
264 | \omit |
265 | \row \o \l Qt::StatusTipRole \o |
266 | \endomit |
267 | \row \o \l Qt::TextAlignmentRole \o Qt::Alignment |
268 | \row \o \l Qt::ForegroundRole \o QBrush |
269 | \row \o \l Qt::TextColorRole \o QColor (obsolete; use Qt::ForegroundRole instead) |
270 | \omit |
271 | \row \o \l Qt::ToolTipRole |
272 | \row \o \l Qt::WhatsThisRole |
273 | \endomit |
274 | \endtable |
275 | |
276 | If the default delegate does not allow the level of customization that |
277 | you need, either for display purposes or for editing data, it is possible to |
278 | subclass QItemDelegate to implement the desired behavior. |
279 | |
280 | \section1 Subclassing |
281 | |
282 | When subclassing QItemDelegate to create a delegate that displays items |
283 | using a custom renderer, it is important to ensure that the delegate can |
284 | render items suitably for all the required states; e.g. selected, |
285 | disabled, checked. The documentation for the paint() function contains |
286 | some hints to show how this can be achieved. |
287 | |
288 | You can provide custom editors by using a QItemEditorFactory. The |
289 | \l{Color Editor Factory Example} shows how a custom editor can be |
290 | made available to delegates with the default item editor |
291 | factory. This way, there is no need to subclass QItemDelegate. An |
292 | alternative is to reimplement createEditor(), setEditorData(), |
293 | setModelData(), and updateEditorGeometry(). This process is |
294 | described in the \l{Spin Box Delegate Example}. |
295 | |
296 | \section1 QStyledItemDelegate vs. QItemDelegate |
297 | |
298 | Since Qt 4.4, there are two delegate classes: QItemDelegate and |
299 | QStyledItemDelegate. However, the default delegate is QStyledItemDelegate. |
300 | These two classes are independent alternatives to painting and providing |
301 | editors for items in views. The difference between them is that |
302 | QStyledItemDelegate uses the current style to paint its items. We therefore |
303 | recommend using QStyledItemDelegate as the base class when implementing |
304 | custom delegates or when working with Qt style sheets. The code required |
305 | for either class should be equal unless the custom delegate needs to use |
306 | the style for drawing. |
307 | |
308 | \sa {Delegate Classes}, QStyledItemDelegate, QAbstractItemDelegate, |
309 | {Spin Box Delegate Example}, {Settings Editor Example}, |
310 | {Icons Example} |
311 | */ |
312 | |
313 | /*! |
314 | Constructs an item delegate with the given \a parent. |
315 | */ |
316 | |
317 | QItemDelegate::QItemDelegate(QObject *parent) |
318 | : QAbstractItemDelegate(*new QItemDelegatePrivate(), parent) |
319 | { |
320 | |
321 | } |
322 | |
323 | /*! |
324 | Destroys the item delegate. |
325 | */ |
326 | |
327 | QItemDelegate::~QItemDelegate() |
328 | { |
329 | } |
330 | |
331 | /*! |
332 | \property QItemDelegate::clipping |
333 | \brief if the delegate should clip the paint events |
334 | \since 4.2 |
335 | |
336 | This property will set the paint clip to the size of the item. |
337 | The default value is on. It is useful for cases such |
338 | as when images are larger than the size of the item. |
339 | */ |
340 | |
341 | bool QItemDelegate::hasClipping() const |
342 | { |
343 | Q_D(const QItemDelegate); |
344 | return d->clipPainting; |
345 | } |
346 | |
347 | void QItemDelegate::setClipping(bool clip) |
348 | { |
349 | Q_D(QItemDelegate); |
350 | d->clipPainting = clip; |
351 | } |
352 | |
353 | QString QItemDelegatePrivate::valueToText(const QVariant &value, const QStyleOptionViewItemV4 &option) |
354 | { |
355 | QString text; |
356 | switch (value.userType()) { |
357 | case QMetaType::Float: |
358 | text = option.locale.toString(value.toFloat(), 'g'); |
359 | break; |
360 | case QVariant::Double: |
361 | text = option.locale.toString(value.toDouble(), 'g', DBL_DIG); |
362 | break; |
363 | case QVariant::Int: |
364 | case QVariant::LongLong: |
365 | text = option.locale.toString(value.toLongLong()); |
366 | break; |
367 | case QVariant::UInt: |
368 | case QVariant::ULongLong: |
369 | text = option.locale.toString(value.toULongLong()); |
370 | break; |
371 | case QVariant::Date: |
372 | text = option.locale.toString(value.toDate(), QLocale::ShortFormat); |
373 | break; |
374 | case QVariant::Time: |
375 | text = option.locale.toString(value.toTime(), QLocale::ShortFormat); |
376 | break; |
377 | case QVariant::DateTime: |
378 | text = option.locale.toString(value.toDateTime().date(), QLocale::ShortFormat); |
379 | text += QLatin1Char(' '); |
380 | text += option.locale.toString(value.toDateTime().time(), QLocale::ShortFormat); |
381 | break; |
382 | default: |
383 | text = replaceNewLine(value.toString()); |
384 | break; |
385 | } |
386 | return text; |
387 | } |
388 | |
389 | /*! |
390 | Renders the delegate using the given \a painter and style \a option for |
391 | the item specified by \a index. |
392 | |
393 | When reimplementing this function in a subclass, you should update the area |
394 | held by the option's \l{QStyleOption::rect}{rect} variable, using the |
395 | option's \l{QStyleOption::state}{state} variable to determine the state of |
396 | the item to be displayed, and adjust the way it is painted accordingly. |
397 | |
398 | For example, a selected item may need to be displayed differently to |
399 | unselected items, as shown in the following code: |
400 | |
401 | \snippet examples/itemviews/pixelator/pixeldelegate.cpp 2 |
402 | \dots |
403 | |
404 | After painting, you should ensure that the painter is returned to its |
405 | the state it was supplied in when this function was called. For example, |
406 | it may be useful to call QPainter::save() before painting and |
407 | QPainter::restore() afterwards. |
408 | |
409 | \sa QStyle::State |
410 | */ |
411 | void QItemDelegate::paint(QPainter *painter, |
412 | const QStyleOptionViewItem &option, |
413 | const QModelIndex &index) const |
414 | { |
415 | Q_D(const QItemDelegate); |
416 | Q_ASSERT(index.isValid()); |
417 | |
418 | QStyleOptionViewItemV4 opt = setOptions(index, option); |
419 | |
420 | const QStyleOptionViewItemV2 *v2 = qstyleoption_cast<const QStyleOptionViewItemV2 *>(&option); |
421 | opt.features = v2 ? v2->features |
422 | : QStyleOptionViewItemV2::ViewItemFeatures(QStyleOptionViewItemV2::None); |
423 | const QStyleOptionViewItemV3 *v3 = qstyleoption_cast<const QStyleOptionViewItemV3 *>(&option); |
424 | opt.locale = v3 ? v3->locale : QLocale(); |
425 | opt.widget = v3 ? v3->widget : 0; |
426 | |
427 | // prepare |
428 | painter->save(); |
429 | if (d->clipPainting) |
430 | painter->setClipRect(opt.rect); |
431 | |
432 | // get the data and the rectangles |
433 | |
434 | QVariant value; |
435 | |
436 | QPixmap pixmap; |
437 | QRect decorationRect; |
438 | value = index.data(Qt::DecorationRole); |
439 | if (value.isValid()) { |
440 | // ### we need the pixmap to call the virtual function |
441 | pixmap = decoration(opt, value); |
442 | if (value.type() == QVariant::Icon) { |
443 | d->tmp.icon = qvariant_cast<QIcon>(value); |
444 | d->tmp.mode = d->iconMode(option.state); |
445 | d->tmp.state = d->iconState(option.state); |
446 | const QSize size = d->tmp.icon.actualSize(option.decorationSize, |
447 | d->tmp.mode, d->tmp.state); |
448 | decorationRect = QRect(QPoint(0, 0), size); |
449 | } else { |
450 | d->tmp.icon = QIcon(); |
451 | decorationRect = QRect(QPoint(0, 0), pixmap.size()); |
452 | } |
453 | } else { |
454 | d->tmp.icon = QIcon(); |
455 | decorationRect = QRect(); |
456 | } |
457 | |
458 | QString text; |
459 | QRect displayRect; |
460 | value = index.data(Qt::DisplayRole); |
461 | if (value.isValid() && !value.isNull()) { |
462 | text = QItemDelegatePrivate::valueToText(value, opt); |
463 | displayRect = textRectangle(painter, d->textLayoutBounds(opt), opt.font, text); |
464 | } |
465 | |
466 | QRect checkRect; |
467 | Qt::CheckState checkState = Qt::Unchecked; |
468 | value = index.data(Qt::CheckStateRole); |
469 | if (value.isValid()) { |
470 | checkState = static_cast<Qt::CheckState>(value.toInt()); |
471 | checkRect = check(opt, opt.rect, value); |
472 | } |
473 | |
474 | // do the layout |
475 | |
476 | doLayout(opt, &checkRect, &decorationRect, &displayRect, false); |
477 | |
478 | // draw the item |
479 | |
480 | drawBackground(painter, opt, index); |
481 | drawCheck(painter, opt, checkRect, checkState); |
482 | drawDecoration(painter, opt, decorationRect, pixmap); |
483 | drawDisplay(painter, opt, displayRect, text); |
484 | drawFocus(painter, opt, displayRect); |
485 | |
486 | // done |
487 | painter->restore(); |
488 | } |
489 | |
490 | /*! |
491 | Returns the size needed by the delegate to display the item |
492 | specified by \a index, taking into account the style information |
493 | provided by \a option. |
494 | |
495 | When reimplementing this function, note that in case of text |
496 | items, QItemDelegate adds a margin (i.e. 2 * |
497 | QStyle::PM_FocusFrameHMargin) to the length of the text. |
498 | */ |
499 | |
500 | QSize QItemDelegate::sizeHint(const QStyleOptionViewItem &option, |
501 | const QModelIndex &index) const |
502 | { |
503 | QVariant value = index.data(Qt::SizeHintRole); |
504 | if (value.isValid()) |
505 | return qvariant_cast<QSize>(value); |
506 | QRect decorationRect = rect(option, index, Qt::DecorationRole); |
507 | QRect displayRect = rect(option, index, Qt::DisplayRole); |
508 | QRect checkRect = rect(option, index, Qt::CheckStateRole); |
509 | |
510 | doLayout(option, &checkRect, &decorationRect, &displayRect, true); |
511 | |
512 | return (decorationRect|displayRect|checkRect).size(); |
513 | } |
514 | |
515 | /*! |
516 | Returns the widget used to edit the item specified by \a index |
517 | for editing. The \a parent widget and style \a option are used to |
518 | control how the editor widget appears. |
519 | |
520 | \sa QAbstractItemDelegate::createEditor() |
521 | */ |
522 | |
523 | QWidget *QItemDelegate::createEditor(QWidget *parent, |
524 | const QStyleOptionViewItem &, |
525 | const QModelIndex &index) const |
526 | { |
527 | Q_D(const QItemDelegate); |
528 | if (!index.isValid()) |
529 | return 0; |
530 | QVariant::Type t = static_cast<QVariant::Type>(index.data(Qt::EditRole).userType()); |
531 | const QItemEditorFactory *factory = d->f; |
532 | if (factory == 0) |
533 | factory = QItemEditorFactory::defaultFactory(); |
534 | return factory->createEditor(t, parent); |
535 | } |
536 | |
537 | /*! |
538 | Sets the data to be displayed and edited by the \a editor from the |
539 | data model item specified by the model \a index. |
540 | |
541 | The default implementation stores the data in the \a editor |
542 | widget's \l {Qt's Property System} {user property}. |
543 | |
544 | \sa QMetaProperty::isUser() |
545 | */ |
546 | |
547 | void QItemDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const |
548 | { |
549 | #ifdef QT_NO_PROPERTIES |
550 | Q_UNUSED(editor); |
551 | Q_UNUSED(index); |
552 | #else |
553 | Q_D(const QItemDelegate); |
554 | QVariant v = index.data(Qt::EditRole); |
555 | QByteArray n = editor->metaObject()->userProperty().name(); |
556 | |
557 | // ### Qt 5: remove |
558 | // A work-around for missing "USER true" in qdatetimeedit.h for |
559 | // QTimeEdit's time property and QDateEdit's date property. |
560 | // It only triggers if the default user property "dateTime" is |
561 | // reported for QTimeEdit and QDateEdit. |
562 | if (n == "dateTime" ) { |
563 | if (editor->inherits("QTimeEdit" )) |
564 | n = "time" ; |
565 | else if (editor->inherits("QDateEdit" )) |
566 | n = "date" ; |
567 | } |
568 | |
569 | // ### Qt 5: give QComboBox a USER property |
570 | if (n.isEmpty() && editor->inherits("QComboBox" )) |
571 | n = d->editorFactory()->valuePropertyName(static_cast<QVariant::Type>(v.userType())); |
572 | if (!n.isEmpty()) { |
573 | if (!v.isValid()) |
574 | v = QVariant(editor->property(n).userType(), (const void *)0); |
575 | editor->setProperty(n, v); |
576 | } |
577 | #endif |
578 | } |
579 | |
580 | /*! |
581 | Gets data from the \a editor widget and stores it in the specified |
582 | \a model at the item \a index. |
583 | |
584 | The default implementation gets the value to be stored in the data |
585 | model from the \a editor widget's \l {Qt's Property System} {user |
586 | property}. |
587 | |
588 | \sa QMetaProperty::isUser() |
589 | */ |
590 | |
591 | void QItemDelegate::setModelData(QWidget *editor, |
592 | QAbstractItemModel *model, |
593 | const QModelIndex &index) const |
594 | { |
595 | #ifdef QT_NO_PROPERTIES |
596 | Q_UNUSED(model); |
597 | Q_UNUSED(editor); |
598 | Q_UNUSED(index); |
599 | #else |
600 | Q_D(const QItemDelegate); |
601 | Q_ASSERT(model); |
602 | Q_ASSERT(editor); |
603 | QByteArray n = editor->metaObject()->userProperty().name(); |
604 | if (n.isEmpty()) |
605 | n = d->editorFactory()->valuePropertyName( |
606 | static_cast<QVariant::Type>(model->data(index, Qt::EditRole).userType())); |
607 | if (!n.isEmpty()) |
608 | model->setData(index, editor->property(n), Qt::EditRole); |
609 | #endif |
610 | } |
611 | |
612 | /*! |
613 | Updates the \a editor for the item specified by \a index |
614 | according to the style \a option given. |
615 | */ |
616 | |
617 | void QItemDelegate::updateEditorGeometry(QWidget *editor, |
618 | const QStyleOptionViewItem &option, |
619 | const QModelIndex &index) const |
620 | { |
621 | if (!editor) |
622 | return; |
623 | Q_ASSERT(index.isValid()); |
624 | QPixmap pixmap = decoration(option, index.data(Qt::DecorationRole)); |
625 | QString text = QItemDelegatePrivate::replaceNewLine(index.data(Qt::DisplayRole).toString()); |
626 | QRect pixmapRect = QRect(QPoint(0, 0), option.decorationSize).intersected(pixmap.rect()); |
627 | QRect textRect = textRectangle(0, option.rect, option.font, text); |
628 | QRect checkRect = check(option, textRect, index.data(Qt::CheckStateRole)); |
629 | QStyleOptionViewItem opt = option; |
630 | opt.showDecorationSelected = true; // let the editor take up all available space |
631 | doLayout(opt, &checkRect, &pixmapRect, &textRect, false); |
632 | editor->setGeometry(textRect); |
633 | } |
634 | |
635 | /*! |
636 | Returns the editor factory used by the item delegate. |
637 | If no editor factory is set, the function will return null. |
638 | |
639 | \sa setItemEditorFactory() |
640 | */ |
641 | QItemEditorFactory *QItemDelegate::itemEditorFactory() const |
642 | { |
643 | Q_D(const QItemDelegate); |
644 | return d->f; |
645 | } |
646 | |
647 | /*! |
648 | Sets the editor factory to be used by the item delegate to be the \a factory |
649 | specified. If no editor factory is set, the item delegate will use the |
650 | default editor factory. |
651 | |
652 | \sa itemEditorFactory() |
653 | */ |
654 | void QItemDelegate::setItemEditorFactory(QItemEditorFactory *factory) |
655 | { |
656 | Q_D(QItemDelegate); |
657 | d->f = factory; |
658 | } |
659 | |
660 | /*! |
661 | Renders the item view \a text within the rectangle specified by \a rect |
662 | using the given \a painter and style \a option. |
663 | */ |
664 | |
665 | void QItemDelegate::drawDisplay(QPainter *painter, const QStyleOptionViewItem &option, |
666 | const QRect &rect, const QString &text) const |
667 | { |
668 | Q_D(const QItemDelegate); |
669 | |
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 (value.canConvert<QBrush>()) { |
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, '-', ushort('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((const QChar *)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 | QSizeF fpSize = d->doTextLayout(rect.width()); |
1152 | const QSize size = QSize(qCeil(fpSize.width()), qCeil(fpSize.height())); |
1153 | // ###: textRectangle should take style option as argument |
1154 | const int textMargin = QApplication::style()->pixelMetric(QStyle::PM_FocusFrameHMargin) + 1; |
1155 | return QRect(0, 0, size.width() + 2 * textMargin, size.height()); |
1156 | } |
1157 | |
1158 | /*! |
1159 | \fn bool QItemDelegate::eventFilter(QObject *editor, QEvent *event) |
1160 | |
1161 | Returns true if the given \a editor is a valid QWidget and the |
1162 | given \a event is handled; otherwise returns false. The following |
1163 | key press events are handled by default: |
1164 | |
1165 | \list |
1166 | \o \gui Tab |
1167 | \o \gui Backtab |
1168 | \o \gui Enter |
1169 | \o \gui Return |
1170 | \o \gui Esc |
1171 | \endlist |
1172 | |
1173 | In the case of \gui Tab, \gui Backtab, \gui Enter and \gui Return |
1174 | key press events, the \a editor's data is comitted to the model |
1175 | and the editor is closed. If the \a event is a \gui Tab key press |
1176 | the view will open an editor on the next item in the |
1177 | view. Likewise, if the \a event is a \gui Backtab key press the |
1178 | view will open an editor on the \e previous item in the view. |
1179 | |
1180 | If the event is a \gui Esc key press event, the \a editor is |
1181 | closed \e without committing its data. |
1182 | |
1183 | \sa commitData(), closeEditor() |
1184 | */ |
1185 | |
1186 | bool QItemDelegate::eventFilter(QObject *object, QEvent *event) |
1187 | { |
1188 | QWidget *editor = qobject_cast<QWidget*>(object); |
1189 | if (!editor) |
1190 | return false; |
1191 | if (event->type() == QEvent::KeyPress) { |
1192 | switch (static_cast<QKeyEvent *>(event)->key()) { |
1193 | case Qt::Key_Tab: |
1194 | emit commitData(editor); |
1195 | emit closeEditor(editor, QAbstractItemDelegate::EditNextItem); |
1196 | return true; |
1197 | case Qt::Key_Backtab: |
1198 | emit commitData(editor); |
1199 | emit closeEditor(editor, QAbstractItemDelegate::EditPreviousItem); |
1200 | return true; |
1201 | case Qt::Key_Enter: |
1202 | case Qt::Key_Return: |
1203 | #ifndef QT_NO_TEXTEDIT |
1204 | if (qobject_cast<QTextEdit *>(editor) || qobject_cast<QPlainTextEdit *>(editor)) |
1205 | return false; // don't filter enter key events for QTextEdit |
1206 | // We want the editor to be able to process the key press |
1207 | // before committing the data (e.g. so it can do |
1208 | // validation/fixup of the input). |
1209 | #endif // QT_NO_TEXTEDIT |
1210 | #ifndef QT_NO_LINEEDIT |
1211 | if (QLineEdit *e = qobject_cast<QLineEdit*>(editor)) |
1212 | if (!e->hasAcceptableInput()) |
1213 | return false; |
1214 | #endif // QT_NO_LINEEDIT |
1215 | QMetaObject::invokeMethod(this, "_q_commitDataAndCloseEditor" , |
1216 | Qt::QueuedConnection, Q_ARG(QWidget*, editor)); |
1217 | return false; |
1218 | case Qt::Key_Escape: |
1219 | // don't commit data |
1220 | emit closeEditor(editor, QAbstractItemDelegate::RevertModelCache); |
1221 | break; |
1222 | default: |
1223 | return false; |
1224 | } |
1225 | if (editor->parentWidget()) |
1226 | editor->parentWidget()->setFocus(); |
1227 | return true; |
1228 | } else if (event->type() == QEvent::FocusOut || (event->type() == QEvent::Hide && editor->isWindow())) { |
1229 | //the Hide event will take care of he editors that are in fact complete dialogs |
1230 | if (!editor->isActiveWindow() || (QApplication::focusWidget() != editor)) { |
1231 | QWidget *w = QApplication::focusWidget(); |
1232 | while (w) { // don't worry about focus changes internally in the editor |
1233 | if (w == editor) |
1234 | return false; |
1235 | w = w->parentWidget(); |
1236 | } |
1237 | #ifndef QT_NO_DRAGANDDROP |
1238 | // The window may lose focus during an drag operation. |
1239 | // i.e when dragging involves the taskbar on Windows. |
1240 | if (QDragManager::self() && QDragManager::self()->object != 0) |
1241 | return false; |
1242 | #endif |
1243 | |
1244 | emit commitData(editor); |
1245 | emit closeEditor(editor, NoHint); |
1246 | } |
1247 | } else if (event->type() == QEvent::ShortcutOverride) { |
1248 | if (static_cast<QKeyEvent*>(event)->key() == Qt::Key_Escape) { |
1249 | event->accept(); |
1250 | return true; |
1251 | } |
1252 | } |
1253 | return false; |
1254 | } |
1255 | |
1256 | /*! |
1257 | \reimp |
1258 | */ |
1259 | |
1260 | bool QItemDelegate::editorEvent(QEvent *event, |
1261 | QAbstractItemModel *model, |
1262 | const QStyleOptionViewItem &option, |
1263 | const QModelIndex &index) |
1264 | { |
1265 | Q_ASSERT(event); |
1266 | Q_ASSERT(model); |
1267 | |
1268 | // make sure that the item is checkable |
1269 | Qt::ItemFlags flags = model->flags(index); |
1270 | if (!(flags & Qt::ItemIsUserCheckable) || !(option.state & QStyle::State_Enabled) |
1271 | || !(flags & Qt::ItemIsEnabled)) |
1272 | return false; |
1273 | |
1274 | // make sure that we have a check state |
1275 | QVariant value = index.data(Qt::CheckStateRole); |
1276 | if (!value.isValid()) |
1277 | return false; |
1278 | |
1279 | // make sure that we have the right event type |
1280 | if ((event->type() == QEvent::MouseButtonRelease) |
1281 | || (event->type() == QEvent::MouseButtonDblClick) |
1282 | || (event->type() == QEvent::MouseButtonPress)) { |
1283 | QRect checkRect = check(option, option.rect, Qt::Checked); |
1284 | QRect emptyRect; |
1285 | doLayout(option, &checkRect, &emptyRect, &emptyRect, false); |
1286 | QMouseEvent *me = static_cast<QMouseEvent*>(event); |
1287 | if (me->button() != Qt::LeftButton || !checkRect.contains(me->pos())) |
1288 | return false; |
1289 | |
1290 | // eat the double click events inside the check rect |
1291 | if ((event->type() == QEvent::MouseButtonPress) |
1292 | || (event->type() == QEvent::MouseButtonDblClick)) |
1293 | return true; |
1294 | |
1295 | } else if (event->type() == QEvent::KeyPress) { |
1296 | if (static_cast<QKeyEvent*>(event)->key() != Qt::Key_Space |
1297 | && static_cast<QKeyEvent*>(event)->key() != Qt::Key_Select) |
1298 | return false; |
1299 | } else { |
1300 | return false; |
1301 | } |
1302 | |
1303 | Qt::CheckState state = (static_cast<Qt::CheckState>(value.toInt()) == Qt::Checked |
1304 | ? Qt::Unchecked : Qt::Checked); |
1305 | return model->setData(index, state, Qt::CheckStateRole); |
1306 | } |
1307 | |
1308 | /*! |
1309 | \internal |
1310 | */ |
1311 | |
1312 | QStyleOptionViewItem QItemDelegate::setOptions(const QModelIndex &index, |
1313 | const QStyleOptionViewItem &option) const |
1314 | { |
1315 | QStyleOptionViewItem opt = option; |
1316 | |
1317 | // set font |
1318 | QVariant value = index.data(Qt::FontRole); |
1319 | if (value.isValid()){ |
1320 | opt.font = qvariant_cast<QFont>(value).resolve(opt.font); |
1321 | opt.fontMetrics = QFontMetrics(opt.font); |
1322 | } |
1323 | |
1324 | // set text alignment |
1325 | value = index.data(Qt::TextAlignmentRole); |
1326 | if (value.isValid()) |
1327 | opt.displayAlignment = Qt::Alignment(value.toInt()); |
1328 | |
1329 | // set foreground brush |
1330 | value = index.data(Qt::ForegroundRole); |
1331 | if (value.canConvert<QBrush>()) |
1332 | opt.palette.setBrush(QPalette::Text, qvariant_cast<QBrush>(value)); |
1333 | |
1334 | return opt; |
1335 | } |
1336 | |
1337 | QT_END_NAMESPACE |
1338 | |
1339 | #include "moc_qitemdelegate.cpp" |
1340 | |
1341 | #endif // QT_NO_ITEMVIEWS |
1342 | |