src/gui/itemviews/qitemdelegate.cpp

Go to the documentation of this file.
00001 /****************************************************************************
00002 **
00003 ** Copyright (C) 1992-2006 Trolltech ASA. All rights reserved.
00004 **
00005 ** This file is part of the QtGui module of the Qt Toolkit.
00006 **
00007 ** This file may be used under the terms of the GNU General Public
00008 ** License version 2.0 as published by the Free Software Foundation
00009 ** and appearing in the file LICENSE.GPL included in the packaging of
00010 ** this file.  Please review the following information to ensure GNU
00011 ** General Public Licensing requirements will be met:
00012 ** http://www.trolltech.com/products/qt/opensource.html
00013 **
00014 ** If you are unsure which license is appropriate for your use, please
00015 ** review the following information:
00016 ** http://www.trolltech.com/products/qt/licensing.html or contact the
00017 ** sales department at sales@trolltech.com.
00018 **
00019 ** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
00020 ** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
00021 **
00022 ****************************************************************************/
00023 
00024 #include "qitemdelegate.h"
00025 
00026 #ifndef QT_NO_ITEMVIEWS
00027 #include <qabstractitemmodel.h>
00028 #include <qapplication.h>
00029 #include <qbrush.h>
00030 #include <qlineedit.h>
00031 #include <qpainter.h>
00032 #include <qpalette.h>
00033 #include <qpoint.h>
00034 #include <qrect.h>
00035 #include <qsize.h>
00036 #include <qstyle.h>
00037 #include <qstyleoption.h>
00038 #include <qevent.h>
00039 #include <qpixmap.h>
00040 #include <qbitmap.h>
00041 #include <qpixmapcache.h>
00042 #include <qitemeditorfactory.h>
00043 #include <qmetaobject.h>
00044 #include <qtextlayout.h>
00045 #include <private/qobject_p.h>
00046 #include <private/qdnd_p.h>
00047 #include <qdebug.h>
00048 #include <qlocale.h>
00049 
00050 #include <limits.h>
00051 
00052 class QItemDelegatePrivate : public QObjectPrivate
00053 {
00054     Q_DECLARE_PUBLIC(QItemDelegate)
00055 
00056 public:
00057     QItemDelegatePrivate() : f(0), clipPainting(false) {}
00058 
00059     inline const QItemEditorFactory *editorFactory() const
00060         { return f ? f : QItemEditorFactory::defaultFactory(); }
00061 
00062     inline QIcon::Mode iconMode(QStyle::State state) const
00063         {
00064             if (!(state & QStyle::State_Enabled)) return QIcon::Disabled;
00065             if (state & QStyle::State_Selected) return QIcon::Selected;
00066             return QIcon::Normal;
00067         }
00068 
00069     inline QIcon::State iconState(QStyle::State state) const
00070         { return state & QStyle::State_Open ? QIcon::On : QIcon::Off; }
00071 
00072     inline static QString replaceNewLine(QString text)
00073         {
00074             const QChar nl = QLatin1Char('\n');
00075             for (int i = 0; i < text.count(); ++i)
00076                 if (text.at(i) == nl)
00077                     text[i] = QChar::LineSeparator;
00078             return text;
00079         }
00080 
00081     void _q_commitDataAndCloseEditor(QWidget *editor);
00082 
00083     QItemEditorFactory *f;
00084     bool clipPainting;
00085 
00086     QRect textLayoutBounds(const QStyleOptionViewItemV2 &options) const;
00087     QSizeF doTextLayout(int lineWidth) const;
00088     mutable QTextLayout textLayout;
00089     mutable QTextOption textOption;
00090 };
00091 
00092 void QItemDelegatePrivate::_q_commitDataAndCloseEditor(QWidget *editor)
00093 {
00094     Q_Q(QItemDelegate);
00095     emit q->commitData(editor);
00096     emit q->closeEditor(editor, QAbstractItemDelegate::SubmitModelCache);
00097 }
00098 
00099 QRect QItemDelegatePrivate::textLayoutBounds(const QStyleOptionViewItemV2 &option) const
00100 {
00101     QRect rect = option.rect;
00102     const bool wrapText = option.features & QStyleOptionViewItemV2::WrapText;
00103     switch (option.decorationPosition) {
00104     case QStyleOptionViewItem::Left:
00105     case QStyleOptionViewItem::Right:
00106         rect.setWidth(INT_MAX >> 6);
00107         break;
00108     case QStyleOptionViewItem::Top:
00109     case QStyleOptionViewItem::Bottom:
00110         rect.setWidth(wrapText ? option.decorationSize.width() : (INT_MAX >> 6));
00111         break;
00112     }
00113 
00114     return rect;
00115 }
00116 
00117 QSizeF QItemDelegatePrivate::doTextLayout(int lineWidth) const
00118 {
00119     QFontMetrics fontMetrics(textLayout.font());
00120     int leading = fontMetrics.leading();
00121     qreal height = 0;
00122     qreal widthUsed = 0;
00123     textLayout.beginLayout();
00124     while (true) {
00125         QTextLine line = textLayout.createLine();
00126         if (!line.isValid())
00127             break;
00128         line.setLineWidth(lineWidth);
00129         height += leading;
00130         line.setPosition(QPointF(0, height));
00131         height += line.height();
00132         widthUsed = qMax(widthUsed, line.naturalTextWidth());
00133     }
00134     textLayout.endLayout();
00135     return QSizeF(widthUsed, height);
00136 }
00137 
00245 QItemDelegate::QItemDelegate(QObject *parent)
00246     : QAbstractItemDelegate(*new QItemDelegatePrivate(), parent)
00247 {
00248 
00249 }
00250 
00255 QItemDelegate::~QItemDelegate()
00256 {
00257 }
00258 
00269 bool QItemDelegate::hasClipping() const
00270 {
00271     Q_D(const QItemDelegate);
00272     return d->clipPainting;
00273 }
00274 
00275 void QItemDelegate::setClipping(bool clip)
00276 {
00277     Q_D(QItemDelegate);
00278     d->clipPainting = clip;
00279 }
00280 
00306 void QItemDelegate::paint(QPainter *painter,
00307                           const QStyleOptionViewItem &option,
00308                           const QModelIndex &index) const
00309 {
00310     Q_D(const QItemDelegate);
00311     Q_ASSERT(index.isValid());
00312     QStyleOptionViewItemV2 opt = setOptions(index, option);
00313     const QStyleOptionViewItemV2 *v2 = qstyleoption_cast<const QStyleOptionViewItemV2 *>(&option);
00314     opt.features = v2 ? v2->features : QStyleOptionViewItemV2::ViewItemFeatures(QStyleOptionViewItemV2::None);
00315 
00316     // prepare
00317     painter->save();
00318     if (d->clipPainting)
00319         painter->setClipRect(opt.rect);
00320 
00321     // get the data and the rectangles
00322 
00323     QVariant value;
00324 
00325     QIcon icon;
00326     QIcon::Mode iconMode = d->iconMode(option.state);
00327     QIcon::State iconState = d->iconState(option.state);
00328 
00329     QPixmap pixmap;
00330     QRect decorationRect;
00331     value = index.data(Qt::DecorationRole);
00332     if (value.isValid()) {
00333         if (value.type() == QVariant::Icon) {
00334             icon = qvariant_cast<QIcon>(value);
00335             decorationRect = QRect(QPoint(0, 0),
00336                                    icon.actualSize(option.decorationSize, iconMode, iconState));
00337         } else {
00338             pixmap = decoration(opt, value);
00339             decorationRect = QRect(QPoint(0, 0), pixmap.size());
00340         }
00341     }
00342 
00343     QString text;
00344     QRect displayRect;
00345     value = index.data(Qt::DisplayRole);
00346     if (value.isValid()) {
00347         if (value.type() == QVariant::Double)
00348             text = QLocale().toString(value.toDouble());
00349         else
00350             text = QItemDelegatePrivate::replaceNewLine(value.toString());
00351 
00352         displayRect = textRectangle(painter, d->textLayoutBounds(opt), opt.font, text);
00353     }
00354 
00355     QRect checkRect;
00356     Qt::CheckState checkState = Qt::Unchecked;
00357     value = index.data(Qt::CheckStateRole);
00358     if (value.isValid()) {
00359         checkState = static_cast<Qt::CheckState>(value.toInt());
00360         checkRect = check(opt, opt.rect, value);
00361     }
00362 
00363     // do the layout
00364 
00365     doLayout(opt, &checkRect, &decorationRect, &displayRect, false);
00366 
00367     // draw the item
00368 
00369     drawBackground(painter, opt, index);
00370     drawCheck(painter, opt, checkRect, checkState);
00371     if (!icon.isNull())
00372         icon.paint(painter, decorationRect, option.decorationAlignment, iconMode, iconState);
00373     else
00374         drawDecoration(painter, opt, decorationRect, pixmap);
00375     drawDisplay(painter, opt, displayRect, text);
00376     drawFocus(painter, opt, text.isEmpty() ? QRect() : displayRect);
00377 
00378     // done
00379     painter->restore();
00380 }
00381 
00392 QSize QItemDelegate::sizeHint(const QStyleOptionViewItem &option,
00393                               const QModelIndex &index) const
00394 {
00395     QVariant value = index.data(Qt::SizeHintRole);
00396     if (value.isValid())
00397         return qvariant_cast<QSize>(value);
00398     QRect decorationRect = rect(option, index, Qt::DecorationRole);
00399     QRect displayRect = rect(option, index, Qt::DisplayRole);
00400     QRect checkRect = rect(option, index, Qt::CheckStateRole);
00401 
00402     doLayout(option, &checkRect, &decorationRect, &displayRect, true);
00403 
00404     return (decorationRect|displayRect|checkRect).size();
00405 }
00406 
00415 QWidget *QItemDelegate::createEditor(QWidget *parent,
00416                                      const QStyleOptionViewItem &,
00417                                      const QModelIndex &index) const
00418 {
00419     Q_D(const QItemDelegate);
00420     if (!index.isValid())
00421         return 0;
00422     QVariant::Type t = static_cast<QVariant::Type>(index.data(Qt::EditRole).userType());
00423     const QItemEditorFactory *factory = d->f;
00424     if (factory == 0)
00425         factory = QItemEditorFactory::defaultFactory();
00426     return factory->createEditor(t, parent);
00427 }
00428 
00438 void QItemDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
00439 {
00440 #ifdef QT_NO_PROPERTIES
00441     Q_UNUSED(editor);
00442     Q_UNUSED(index);
00443 #else
00444     Q_D(const QItemDelegate);
00445     QVariant v = index.data(Qt::EditRole);
00446     QByteArray n = editor->metaObject()->userProperty().name();
00447 
00448     // ### Remove in Qt 5: A work-around for missing "USER true" in
00449     // qdatetimeedit.h for QTimeEdit's time property and QDateEdit's date
00450     // property. It only triggers if the default user property "dateTime" is
00451     // reported for QTimeEdit and QDateEdit.
00452     if (n == "dateTime") {
00453         if (editor->inherits("QTimeEdit"))
00454             n = "time";
00455         else if (editor->inherits("QDateEdit"))
00456             n = "date";
00457     }
00458 
00459     if (n.isEmpty())
00460         n = d->editorFactory()->valuePropertyName(static_cast<QVariant::Type>(v.userType()));
00461     if (!n.isEmpty())
00462         editor->setProperty(n, v);
00463 #endif
00464 }
00465 
00475 void QItemDelegate::setModelData(QWidget *editor,
00476                                  QAbstractItemModel *model,
00477                                  const QModelIndex &index) const
00478 {
00479 #ifdef QT_NO_PROPERTIES
00480     Q_UNUSED(model);
00481     Q_UNUSED(editor);
00482     Q_UNUSED(index);
00483 #else
00484     Q_D(const QItemDelegate);
00485     Q_ASSERT(model);
00486     Q_ASSERT(editor);
00487     QByteArray n = editor->metaObject()->userProperty().name();
00488     if (n.isEmpty())
00489         n = d->editorFactory()->valuePropertyName(
00490             static_cast<QVariant::Type>(model->data(index, Qt::EditRole).userType()));
00491     if (!n.isEmpty())
00492         model->setData(index, editor->property(n), Qt::EditRole);
00493 #endif
00494 }
00495 
00501 void QItemDelegate::updateEditorGeometry(QWidget *editor,
00502                                          const QStyleOptionViewItem &option,
00503                                          const QModelIndex &index) const
00504 {
00505     if (!editor)
00506         return;
00507     Q_ASSERT(index.isValid());
00508     QPixmap pixmap = decoration(option, index.data(Qt::DecorationRole));
00509     QString text = QItemDelegatePrivate::replaceNewLine(index.data(Qt::DisplayRole).toString());
00510     QRect pixmapRect = QRect(QPoint(0, 0), option.decorationSize).intersected(pixmap.rect());
00511     QRect textRect = textRectangle(0, option.rect, option.font, text);
00512     QRect checkRect = check(option, textRect, index.data(Qt::CheckStateRole));
00513     QStyleOptionViewItem opt = option;
00514     opt.showDecorationSelected = true; // let the editor take up all available space
00515     doLayout(opt, &checkRect, &pixmapRect, &textRect, false);
00516     editor->setGeometry(textRect);
00517 }
00518 
00525 QItemEditorFactory *QItemDelegate::itemEditorFactory() const
00526 {
00527     Q_D(const QItemDelegate);
00528     return d->f;
00529 }
00530 
00538 void QItemDelegate::setItemEditorFactory(QItemEditorFactory *factory)
00539 {
00540     Q_D(QItemDelegate);
00541     d->f = factory;
00542 }
00543 
00549 void QItemDelegate::drawDisplay(QPainter *painter, const QStyleOptionViewItem &option,
00550                                 const QRect &rect, const QString &text) const
00551 {
00552     Q_D(const QItemDelegate);
00553 
00554     if (text.isEmpty())
00555         return;
00556 
00557     QPen pen = painter->pen();
00558     QPalette::ColorGroup cg = option.state & QStyle::State_Enabled
00559                               ? QPalette::Normal : QPalette::Disabled;
00560     if (cg == QPalette::Normal && !(option.state & QStyle::State_Active))
00561         cg = QPalette::Inactive;
00562     if (option.state & QStyle::State_Selected) {
00563         painter->fillRect(rect, option.palette.brush(cg, QPalette::Highlight));
00564         painter->setPen(option.palette.color(cg, QPalette::HighlightedText));
00565     } else {
00566         painter->setPen(option.palette.color(cg, QPalette::Text));
00567     }
00568 
00569     if (option.state & QStyle::State_Editing) {
00570         painter->save();
00571         painter->setPen(option.palette.color(cg, QPalette::Text));
00572         painter->drawRect(rect.adjusted(0, 0, -1, -1));
00573         painter->restore();
00574     }
00575 
00576     const QStyleOptionViewItemV2 opt = option;
00577     const int textMargin = QApplication::style()->pixelMetric(QStyle::PM_FocusFrameHMargin) + 1;
00578     QRect textRect = rect.adjusted(textMargin, 0, -textMargin, 0); // remove width padding
00579     const bool wrapText = opt.features & QStyleOptionViewItemV2::WrapText;
00580     d->textOption.setWrapMode(wrapText ? QTextOption::WordWrap : QTextOption::ManualWrap);
00581     d->textOption.setTextDirection(option.direction);
00582     d->textOption.setAlignment(QStyle::visualAlignment(option.direction, option.displayAlignment));
00583     d->textLayout.setTextOption(d->textOption);
00584     d->textLayout.setFont(option.font);
00585     d->textLayout.setText(QItemDelegatePrivate::replaceNewLine(text));
00586 
00587     QSizeF textLayoutSize = d->doTextLayout(textRect.width());
00588 
00589     if (textRect.width() < textLayoutSize.width()
00590         || textRect.height() < textLayoutSize.height()) {
00591         QString elided;
00592         int start = 0;
00593         int end = text.indexOf(QChar::LineSeparator, start);
00594         if (end == -1) {
00595             elided += option.fontMetrics.elidedText(text, option.textElideMode, textRect.width());
00596         } else while (end != -1) {
00597             elided += option.fontMetrics.elidedText(text.mid(start, end - start),
00598                                                     option.textElideMode, textRect.width());
00599             start = end + 1;
00600             end = text.indexOf(QChar::LineSeparator, start);
00601         }
00602         d->textLayout.setText(elided);
00603         textLayoutSize = d->doTextLayout(textRect.width());
00604     }
00605 
00606     const QSize layoutSize(textRect.width(), int(textLayoutSize.height()));
00607     const QRect layoutRect = QStyle::alignedRect(option.direction, option.displayAlignment,
00608                                                   layoutSize, textRect);
00609     d->textLayout.draw(painter, layoutRect.topLeft(), QVector<QTextLayout::FormatRange>(), layoutRect);
00610 }
00611 
00616 void QItemDelegate::drawDecoration(QPainter *painter, const QStyleOptionViewItem &option,
00617                                    const QRect &rect, const QPixmap &pixmap) const
00618 {
00619     if (pixmap.isNull() || !rect.isValid())
00620         return;
00621     QPoint p = QStyle::alignedRect(option.direction, option.decorationAlignment,
00622                                    pixmap.size(), rect).topLeft();
00623     if (option.state & QStyle::State_Selected) {
00624         QPixmap *pm = selected(pixmap, option.palette, option.state & QStyle::State_Enabled);
00625         painter->drawPixmap(p, *pm);
00626     } else {
00627         painter->drawPixmap(p, pixmap);
00628     }
00629 }
00630 
00636 void QItemDelegate::drawFocus(QPainter *painter,
00637                               const QStyleOptionViewItem &option,
00638                               const QRect &rect) const
00639 {
00640     if ((option.state & QStyle::State_HasFocus) == 0 || !rect.isValid())
00641         return;
00642     QStyleOptionFocusRect o;
00643     o.QStyleOption::operator=(option);
00644     o.rect = rect;
00645     o.state |= QStyle::State_KeyboardFocusChange;
00646     QPalette::ColorGroup cg = (option.state & QStyle::State_Enabled)
00647                               ? QPalette::Normal : QPalette::Disabled;
00648     o.backgroundColor = option.palette.color(cg, (option.state & QStyle::State_Selected)
00649                                              ? QPalette::Highlight : QPalette::Window);
00650     QApplication::style()->drawPrimitive(QStyle::PE_FrameFocusRect, &o, painter);
00651 }
00652 
00659 void QItemDelegate::drawCheck(QPainter *painter,
00660                               const QStyleOptionViewItem &option,
00661                               const QRect &rect, Qt::CheckState state) const
00662 {
00663     if (!rect.isValid())
00664         return;
00665 
00666     QStyleOptionViewItem opt(option);
00667     opt.rect = rect;
00668     opt.state = opt.state & ~QStyle::State_HasFocus;
00669 
00670     switch (state) {
00671     case Qt::Unchecked:
00672         opt.state |= QStyle::State_Off;
00673         break;
00674     case Qt::PartiallyChecked:
00675         opt.state |= QStyle::State_NoChange;
00676         break;
00677     case Qt::Checked:
00678         opt.state |= QStyle::State_On;
00679         break;
00680     }
00681 
00682     QApplication::style()->drawPrimitive(QStyle::PE_IndicatorViewItemCheck, &opt, painter);
00683 }
00684 
00692 void QItemDelegate::drawBackground(QPainter *painter,
00693                                    const QStyleOptionViewItem &option,
00694                                    const QModelIndex &index) const
00695 {
00696     if (option.showDecorationSelected && (option.state & QStyle::State_Selected)) {
00697         QPalette::ColorGroup cg = option.state & QStyle::State_Enabled
00698                                   ? QPalette::Normal : QPalette::Disabled;
00699         if (cg == QPalette::Normal && !(option.state & QStyle::State_Active))
00700             cg = QPalette::Inactive;
00701 
00702         painter->fillRect(option.rect, option.palette.brush(cg, QPalette::Highlight));
00703     } else {
00704         QVariant value = index.data(Qt::BackgroundRole);
00705         if (qVariantCanConvert<QBrush>(value)) {
00706             QPointF oldBO = painter->brushOrigin();
00707             painter->setBrushOrigin(option.rect.topLeft());
00708             painter->fillRect(option.rect, qvariant_cast<QBrush>(value));
00709             painter->setBrushOrigin(oldBO);
00710         }
00711     }
00712 }
00713 
00714 
00719 void QItemDelegate::doLayout(const QStyleOptionViewItem &option,
00720                              QRect *checkRect, QRect *pixmapRect, QRect *textRect,
00721                              bool hint) const
00722 {
00723     Q_ASSERT(checkRect && pixmapRect && textRect);
00724     const int textMargin = QApplication::style()->pixelMetric(QStyle::PM_FocusFrameHMargin) + 1;
00725     int x = option.rect.left();
00726     int y = option.rect.top();
00727     int w, h;
00728 
00729     textRect->adjust(-textMargin, 0, textMargin, 0); // add width padding
00730     if (textRect->height() == 0)
00731         textRect->setHeight(option.fontMetrics.lineSpacing());
00732 
00733     QSize pm(0, 0);
00734     if (pixmapRect->isValid()) {
00735         pm = pixmapRect->size();
00736         pm.rwidth() += 2 * textMargin;
00737     }
00738     if (hint) {
00739         h = qMax(checkRect->height(), qMax(textRect->height(), pm.height()));
00740         if (option.decorationPosition == QStyleOptionViewItem::Left
00741             || option.decorationPosition == QStyleOptionViewItem::Right) {
00742             w = textRect->width() + pm.width();
00743         } else {
00744             w = qMax(textRect->width(), pm.width());
00745         }
00746     } else {
00747         w = option.rect.width();
00748         h = option.rect.height();
00749     }
00750 
00751     int cw = 0;
00752     QRect check;
00753     if (checkRect->isValid()) {
00754         cw = checkRect->width() + 2 * textMargin;
00755         if (hint) w += cw;
00756         if (option.direction == Qt::RightToLeft) {
00757             check.setRect(x + w - cw, y, cw, h);
00758         } else {
00759             check.setRect(x, y, cw, h);
00760         }
00761     }
00762 
00763     // at this point w should be the *total* width
00764 
00765     QRect display;
00766     QRect decoration;
00767     switch (option.decorationPosition) {
00768     case QStyleOptionViewItem::Top: {
00769         if (!pm.isEmpty())
00770             pm.setHeight(pm.height() + textMargin); // add space
00771         h = hint ? textRect->height() : h - pm.height();
00772 
00773         if (option.direction == Qt::RightToLeft) {
00774             decoration.setRect(x, y, w - cw, pm.height());
00775             display.setRect(x, y + pm.height(), w - cw, h);
00776         } else {
00777             decoration.setRect(x + cw, y, w - cw, pm.height());
00778             display.setRect(x + cw, y + pm.height(), w - cw, h);
00779         }
00780         break; }
00781     case QStyleOptionViewItem::Bottom: {
00782         if (!textRect->isEmpty())
00783             textRect->setHeight(textRect->height() + textMargin); // add space
00784         h = hint ? textRect->height() + pm.height() : h;
00785 
00786         if (option.direction == Qt::RightToLeft) {
00787             display.setRect(x, y, w - cw, textRect->height());
00788             decoration.setRect(x, y + textRect->height(), w - cw, h - textRect->height());
00789         } else {
00790             display.setRect(x + cw, y, w - cw, textRect->height());
00791             decoration.setRect(x + cw, y + textRect->height(), w - cw, h - textRect->height());
00792         }
00793         break; }
00794     case QStyleOptionViewItem::Left: {
00795         if (option.direction == Qt::LeftToRight) {
00796             decoration.setRect(x + cw, y, pm.width(), h);
00797             display.setRect(decoration.right() + 1, y, w - pm.width() - cw, h);
00798         } else {
00799             display.setRect(x, y, w - pm.width() - cw, h);
00800             decoration.setRect(display.right() + 1, y, pm.width(), h);
00801         }
00802         break; }
00803     case QStyleOptionViewItem::Right: {
00804         if (option.direction == Qt::LeftToRight) {
00805             display.setRect(x + cw, y, w - pm.width() - cw, h);
00806             decoration.setRect(display.right() + 1, y, pm.width(), h);
00807         } else {
00808             decoration.setRect(x, y, pm.width(), h);
00809             display.setRect(decoration.right() + 1, y, w - pm.width() - cw, h);
00810         }
00811         break; }
00812     default:
00813         qWarning("doLayout: decoration position is invalid");
00814         decoration = *pixmapRect;
00815         break;
00816     }
00817 
00818     if (!hint) { // we only need to do the internal layout if we are going to paint
00819         *checkRect = QStyle::alignedRect(option.direction, Qt::AlignCenter,
00820                                          checkRect->size(), check);
00821         *pixmapRect = QStyle::alignedRect(option.direction, option.decorationAlignment,
00822                                           pixmapRect->size(), decoration);
00823         // the text takes up all awailable space, unless the decoration is not shown as selected
00824         if (option.showDecorationSelected)
00825             *textRect = display;
00826         else
00827             *textRect = QStyle::alignedRect(option.direction, option.displayAlignment,
00828                                             textRect->size().boundedTo(display.size()), display);
00829     } else {
00830         *checkRect = check;
00831         *pixmapRect = decoration;
00832         *textRect = display;
00833     }
00834 }
00835 
00844 QPixmap QItemDelegate::decoration(const QStyleOptionViewItem &option, const QVariant &variant) const
00845 {
00846     Q_D(const QItemDelegate);
00847     switch (variant.type()) {
00848     case QVariant::Icon: {
00849         QIcon::Mode mode = d->iconMode(option.state);
00850         QIcon::State state = d->iconState(option.state);
00851         return qvariant_cast<QIcon>(variant).pixmap(option.decorationSize, mode, state); }
00852     case QVariant::Color: {
00853         static QPixmap pixmap(option.decorationSize);
00854         pixmap.fill(qvariant_cast<QColor>(variant));
00855         return pixmap; }
00856     default:
00857         break;
00858     }
00859 
00860     return qvariant_cast<QPixmap>(variant);
00861 }
00862 
00863 // hacky but faster version of "QString::sprintf("%d-%d", i, enabled)"
00864 static QString qPixmapSerial(quint64 i, bool enabled)
00865 {
00866     ushort arr[] = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, '-', '0' + enabled };
00867     ushort *ptr = &arr[16];
00868 
00869     while (i > 0) {
00870         // hey - it's our internal representation, so use the ascii character after '9'
00871         // instead of 'a' for hex
00872         *(--ptr) = '0' + i % 16;
00873         i >>= 4;
00874     }
00875 
00876     return QString::fromUtf16(ptr, int(&arr[sizeof(arr) / sizeof(ushort)] - ptr));
00877 }
00878 
00885 QPixmap *QItemDelegate::selected(const QPixmap &pixmap, const QPalette &palette, bool enabled) const
00886 {
00887     QString key = qPixmapSerial(qt_pixmap_id(pixmap), enabled);
00888     QPixmap *pm = QPixmapCache::find(key);
00889     if (!pm) {
00890         QImage img = pixmap.toImage().convertToFormat(QImage::Format_ARGB32_Premultiplied);
00891 
00892         QColor color = palette.color(enabled ? QPalette::Normal : QPalette::Disabled,
00893                                      QPalette::Highlight);
00894         color.setAlphaF(0.3);
00895 
00896         QPainter painter(&img);
00897         painter.setCompositionMode(QPainter::CompositionMode_SourceAtop);
00898         painter.fillRect(0, 0, img.width(), img.height(), color);
00899         painter.end();
00900 
00901         QPixmap selected = QPixmap(QPixmap::fromImage(img));
00902         QPixmapCache::insert(key, selected);
00903         pm = QPixmapCache::find(key);
00904     }
00905     return pm;
00906 }
00907 
00912 QRect QItemDelegate::rect(const QStyleOptionViewItem &option,
00913                           const QModelIndex &index, int role) const
00914 {
00915     Q_D(const QItemDelegate);
00916     QVariant value = index.data(role);
00917     if (role == Qt::CheckStateRole)
00918         return check(option, option.rect, value);
00919     if (value.isValid()) {
00920         switch (value.type()) {
00921         case QVariant::Invalid:
00922             break;
00923         case QVariant::Pixmap:
00924             return QRect(QPoint(0, 0), qvariant_cast<QPixmap>(value).size());
00925         case QVariant::Image:
00926             return QRect(QPoint(0, 0), qvariant_cast<QImage>(value).size());
00927         case QVariant::Icon: {
00928             QIcon::Mode mode = d->iconMode(option.state);
00929             QIcon::State state = d->iconState(option.state);
00930             QIcon icon = qvariant_cast<QIcon>(value);
00931             QSize size = icon.actualSize(option.decorationSize, mode, state);
00932             return QRect(QPoint(0, 0), size); }
00933         case QVariant::Color:
00934             return QRect(QPoint(0, 0), option.decorationSize);
00935         case QVariant::String:
00936         default: {
00937             QString text = QItemDelegatePrivate::replaceNewLine(value.toString());
00938             value = index.data(Qt::FontRole);
00939             QFont fnt = qvariant_cast<QFont>(value).resolve(option.font);
00940             return textRectangle(0, d->textLayoutBounds(option), fnt, text); }
00941         }
00942     }
00943     return QRect();
00944 }
00945 
00949 QRect QItemDelegate::check(const QStyleOptionViewItem &option,
00950                            const QRect &bounding, const QVariant &value) const
00951 {
00952     if (value.isValid()) {
00953         QStyleOptionButton opt;
00954         opt.QStyleOption::operator=(option);
00955         opt.rect = bounding;
00956         return QApplication::style()->subElementRect(QStyle::SE_ViewItemCheckIndicator, &opt);
00957     }
00958     return QRect();
00959 }
00960 
00964 QRect QItemDelegate::textRectangle(QPainter * /*painter*/, const QRect &rect,
00965                                    const QFont &font, const QString &text) const
00966 {
00967     Q_D(const QItemDelegate);
00968     d->textOption.setWrapMode(QTextOption::WordWrap);
00969     d->textLayout.setTextOption(d->textOption);
00970     d->textLayout.setFont(font);
00971     d->textLayout.setText(QItemDelegatePrivate::replaceNewLine(text));
00972     const QSize size = d->doTextLayout(rect.width()).toSize();
00973     const int textMargin = QApplication::style()->pixelMetric(QStyle::PM_FocusFrameHMargin) + 1;
00974     return QRect(0, 0, size.width() + 2 * textMargin, size.height());
00975 }
00976 
01005 bool QItemDelegate::eventFilter(QObject *object, QEvent *event)
01006 {
01007     QWidget *editor = ::qobject_cast<QWidget*>(object);
01008     if (!editor)
01009         return false;
01010     if (event->type() == QEvent::KeyPress) {
01011         switch (static_cast<QKeyEvent *>(event)->key()) {
01012         case Qt::Key_Tab:
01013             emit commitData(editor);
01014             emit closeEditor(editor, QAbstractItemDelegate::EditNextItem);
01015             return true;
01016         case Qt::Key_Backtab:
01017             emit commitData(editor);
01018             emit closeEditor(editor, QAbstractItemDelegate::EditPreviousItem);
01019             return true;
01020         case Qt::Key_Enter:
01021         case Qt::Key_Return:
01022             // We want the editor to be able to process the key press
01023             // before committing the data (e.g. so it can do
01024             // validation/fixup of the input).
01025             QMetaObject::invokeMethod(this, "_q_commitDataAndCloseEditor",
01026                                       Qt::QueuedConnection, Q_ARG(QWidget*, editor));
01027             return false;
01028         case Qt::Key_Escape:
01029             // don't commit data
01030             emit closeEditor(editor, QAbstractItemDelegate::RevertModelCache);
01031             break;
01032         default:
01033             return false;
01034         }
01035         if (editor->parentWidget())
01036             editor->parentWidget()->setFocus();
01037         return true;
01038     } else if (event->type() == QEvent::FocusOut) {
01039         if (!editor->isActiveWindow() || (QApplication::focusWidget() != editor)) {
01040             QWidget *w = QApplication::focusWidget();
01041             while (w) { // don't worry about focus changes internally in the editor
01042                 if (w == editor)
01043                     return false;
01044                 w = w->parentWidget();
01045             }
01046 #ifndef QT_NO_DRAGANDDROP
01047             // The window may lose focus during an drag operation.
01048             // i.e when dragging involves the taskbar on Windows.
01049             if (QDragManager::self() && QDragManager::self()->object != 0)
01050                 return false;
01051 #endif
01052             // Opening a modal dialog will start a new eventloop
01053             // that will process the deleteLater event.
01054             if (QApplication::activeModalWidget() && !QApplication::activeModalWidget()->isAncestorOf(editor))
01055                 return false;
01056             emit commitData(editor);
01057             emit closeEditor(editor, NoHint);
01058         }
01059     }
01060     return false;
01061 }
01062 
01067 bool QItemDelegate::editorEvent(QEvent *event,
01068                                 QAbstractItemModel *model,
01069                                 const QStyleOptionViewItem &option,
01070                                 const QModelIndex &index)
01071 {
01072     Q_ASSERT(event);
01073     Q_ASSERT(model);
01074 
01075     // make sure that the item is checkable
01076     Qt::ItemFlags flags = model->flags(index);
01077     if (!(flags & Qt::ItemIsUserCheckable) || !((flags & Qt::ItemIsEnabled)))
01078         return false;
01079 
01080     // make sure that we have a check state
01081     QVariant value = index.data(Qt::CheckStateRole);
01082     if (!value.isValid())
01083         return false;
01084 
01085     // make sure that we have the right event type
01086     if ((event->type() == QEvent::MouseButtonRelease)
01087         || (event->type() == QEvent::MouseButtonDblClick)) {
01088         const int textMargin = QApplication::style()->pixelMetric(QStyle::PM_FocusFrameHMargin) + 1;
01089         QRect checkRect = QStyle::alignedRect(option.direction, Qt::AlignLeft | Qt::AlignVCenter,
01090                                               check(option, option.rect, Qt::Checked).size(),
01091                                               QRect(option.rect.x() + textMargin, option.rect.y(),
01092                                                     option.rect.width(), option.rect.height()));
01093         if (!checkRect.contains(static_cast<QMouseEvent*>(event)->pos()))
01094             return false;
01095 
01096         // eat the double click events inside the check rect
01097         if (event->type() == QEvent::MouseButtonDblClick)
01098             return true;
01099 
01100     } else if (event->type() == QEvent::KeyPress) {
01101         if (static_cast<QKeyEvent*>(event)->key() != Qt::Key_Space
01102          && static_cast<QKeyEvent*>(event)->key() != Qt::Key_Select)
01103             return false;
01104     } else {
01105         return false;
01106     }
01107 
01108     Qt::CheckState state = (static_cast<Qt::CheckState>(value.toInt()) == Qt::Checked
01109                             ? Qt::Unchecked : Qt::Checked);
01110     return model->setData(index, state, Qt::CheckStateRole);
01111 }
01112 
01117 QStyleOptionViewItem QItemDelegate::setOptions(const QModelIndex &index,
01118                                                const QStyleOptionViewItem &option) const
01119 {
01120     QStyleOptionViewItem opt = option;
01121 
01122     // set font
01123     QVariant value = index.data(Qt::FontRole);
01124     if (value.isValid()){
01125         opt.font = qvariant_cast<QFont>(value).resolve(opt.font);
01126         opt.fontMetrics = QFontMetrics(opt.font);
01127     }
01128 
01129     // set text alignment
01130     value = index.data(Qt::TextAlignmentRole);
01131     if (value.isValid())
01132         opt.displayAlignment = (Qt::Alignment)value.toInt();
01133 
01134     // set foreground brush
01135     value = index.data(Qt::ForegroundRole);
01136     if (qVariantCanConvert<QBrush>(value))
01137         opt.palette.setBrush(QPalette::Text, qvariant_cast<QBrush>(value));
01138 
01139     return opt;
01140 }
01141 
01142 #include "moc_qitemdelegate.cpp"
01143 
01144 #endif // QT_NO_ITEMVIEWS

Generated on Thu Mar 15 11:54:48 2007 for Qt 4.2 User's Guide by  doxygen 1.5.1