• Skip to content
  • Skip to link menu
KDE 4.3 API Reference
  • KDE API Reference
  • kdelibs
  • Sitemap
  • Contact Us
 

KIO

kfileitemdelegate.cpp

Go to the documentation of this file.
00001 /*
00002    This file is part of the KDE project
00003 
00004    Copyright © 2006-2007, 2008 Fredrik Höglund <fredrik@kde.org>
00005 
00006    This library is free software; you can redistribute it and/or
00007    modify it under the terms of the GNU Library General Public
00008    License as published by the Free Software Foundation; either
00009    version 2 of the License, or (at your option) any later version.
00010 
00011    This library is distributed in the hope that it will be useful,
00012    but WITHOUT ANY WARRANTY; without even the implied warranty of
00013    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014    Library General Public License for more details.
00015 
00016    You should have received a copy of the GNU Library General Public License
00017    along with this library; see the file COPYING.LIB.  If not, write to
00018    the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
00019    Boston, MA 02110-1301, USA.
00020 */
00021 
00022 #include "kfileitemdelegate.h"
00023 #include "imagefilter_p.h"
00024 
00025 #include <config.h> // for HAVE_XRENDER
00026 
00027 #include <QApplication>
00028 #include <QStyle>
00029 #include <QModelIndex>
00030 #include <QPainter>
00031 #include <QCache>
00032 #include <QImage>
00033 #include <QPainterPath>
00034 #include <QTextLayout>
00035 #include <QListView>
00036 #include <QPaintEngine>
00037 #include <qmath.h>
00038 
00039 #include <kglobal.h>
00040 #include <klocale.h>
00041 #include <kiconloader.h>
00042 #include <kiconeffect.h>
00043 #include <kdirmodel.h>
00044 #include <kfileitem.h>
00045 #include <kcolorscheme.h>
00046 #include <kglobalsettings.h>
00047 #include <ktextedit.h>
00048 
00049 #include "delegateanimationhandler_p.h"
00050 
00051 #if defined(Q_WS_X11) && defined(HAVE_XRENDER)
00052 #  include <X11/Xlib.h>
00053 #  include <X11/extensions/Xrender.h>
00054 #  include <QX11Info>
00055 #  undef KeyPress
00056 #  undef FocusOut
00057 #endif
00058 
00059 
00060 struct Margin
00061 {
00062     int left, right, top, bottom;
00063 };
00064 
00065 
00066 class KFileItemDelegate::Private
00067 {
00068     public:
00069         enum MarginType { ItemMargin = 0, TextMargin, IconMargin, NMargins };
00070 
00071         Private(KFileItemDelegate *parent);
00072         ~Private() {}
00073 
00074         QSize decorationSizeHint(const QStyleOptionViewItemV4 &option, const QModelIndex &index) const;
00075         QSize displaySizeHint(const QStyleOptionViewItemV4 &option, const QModelIndex &index) const;
00076         QString replaceNewlines(const QString &string) const;
00077         inline KFileItem fileItem(const QModelIndex &index) const;
00078         QString elidedText(QTextLayout &layout, const QStyleOptionViewItemV4 &option, const QSize &maxSize) const;
00079         QSize layoutText(QTextLayout &layout, const QStyleOptionViewItemV4 &option,
00080                          const QString &text, const QSize &constraints) const;
00081         QSize layoutText(QTextLayout &layout, const QString &text, int maxWidth) const;
00082         inline void setLayoutOptions(QTextLayout &layout, const QStyleOptionViewItemV4 &options) const;
00083         inline bool verticalLayout(const QStyleOptionViewItemV4 &option) const;
00084         inline QBrush brush(const QVariant &value, const QStyleOptionViewItemV4 &option) const;
00085         QBrush foregroundBrush(const QStyleOptionViewItemV4 &option, const QModelIndex &index) const;
00086         inline void setActiveMargins(Qt::Orientation layout);
00087         void setVerticalMargin(MarginType type, int left, int right, int top, int bottom);
00088         void setHorizontalMargin(MarginType type, int left, int right, int top, int bottom);
00089         inline void setVerticalMargin(MarginType type, int hor, int ver);
00090         inline void setHorizontalMargin(MarginType type, int hor, int ver);
00091         inline QRect addMargin(const QRect &rect, MarginType type) const;
00092         inline QRect subtractMargin(const QRect &rect, MarginType type) const;
00093         inline QSize addMargin(const QSize &size, MarginType type) const;
00094         inline QSize subtractMargin(const QSize &size, MarginType type) const;
00095         QString itemSize(const QModelIndex &index, const KFileItem &item) const;
00096         QString information(const QStyleOptionViewItemV4 &option, const QModelIndex &index, const KFileItem &item) const;
00097         bool isListView(const QStyleOptionViewItemV4 &option) const;
00098         QString display(const QModelIndex &index) const;
00099         QIcon decoration(const QStyleOptionViewItemV4 &option, const QModelIndex &index) const;
00100         QPoint iconPosition(const QStyleOptionViewItemV4 &option) const;
00101         QRect labelRectangle(const QStyleOptionViewItemV4 &option) const;
00102         void layoutTextItems(const QStyleOptionViewItemV4 &option, const QModelIndex &index,
00103                              QTextLayout *labelLayout, QTextLayout *infoLayout, QRect *textBoundingRect) const;
00104         void drawTextItems(QPainter *painter, const QTextLayout &labelLayout, const QTextLayout &infoLayout,
00105                            const QRect &textBoundingRect) const;
00106         KIO::AnimationState *animationState(const QStyleOptionViewItemV4 &option, const QModelIndex &index,
00107                                             const QAbstractItemView *view) const;
00108         void restartAnimation(KIO::AnimationState* state);
00109         QPixmap applyHoverEffect(const QPixmap &icon) const;
00110         QPixmap transition(const QPixmap &from, const QPixmap &to, qreal amount) const;
00111         void initStyleOption(QStyleOptionViewItemV4 *option, const QModelIndex &index) const;
00112         void drawFocusRect(QPainter *painter, const QStyleOptionViewItemV4 &option, const QRect &rect) const;
00113 
00114         void gotNewIcon(const QModelIndex& index);
00115 
00116     public:
00117         KFileItemDelegate::InformationList informationList;
00118         QColor shadowColor;
00119         QPointF shadowOffset;
00120         qreal shadowBlur;
00121         QSize maximumSize;
00122         bool showToolTipWhenElided;
00123 
00124     private:
00125         KFileItemDelegate * const q;
00126         KIO::DelegateAnimationHandler *animationHandler;
00127         Margin verticalMargin[NMargins];
00128         Margin horizontalMargin[NMargins];
00129         Margin *activeMargins;
00130 };
00131 
00132 
00133 KFileItemDelegate::Private::Private(KFileItemDelegate *parent)
00134      : shadowColor(Qt::transparent), shadowOffset(1, 1), shadowBlur(2), maximumSize(0, 0),
00135        showToolTipWhenElided(true), q(parent),
00136        animationHandler(new KIO::DelegateAnimationHandler(parent)), activeMargins(0)
00137 {
00138 }
00139 
00140 
00141 void KFileItemDelegate::Private::setActiveMargins(Qt::Orientation layout)
00142 {
00143     activeMargins = (layout == Qt::Horizontal ?
00144             horizontalMargin : verticalMargin);
00145 }
00146 
00147 
00148 void KFileItemDelegate::Private::setVerticalMargin(MarginType type, int left, int top, int right, int bottom)
00149 {
00150     verticalMargin[type].left   = left;
00151     verticalMargin[type].right  = right;
00152     verticalMargin[type].top    = top;
00153     verticalMargin[type].bottom = bottom;
00154 }
00155 
00156 
00157 void KFileItemDelegate::Private::setHorizontalMargin(MarginType type, int left, int top, int right, int bottom)
00158 {
00159     horizontalMargin[type].left   = left;
00160     horizontalMargin[type].right  = right;
00161     horizontalMargin[type].top    = top;
00162     horizontalMargin[type].bottom = bottom;
00163 }
00164 
00165 
00166 void KFileItemDelegate::Private::setVerticalMargin(MarginType type, int horizontal, int vertical)
00167 {
00168     setVerticalMargin(type, horizontal, vertical, horizontal, vertical);
00169 }
00170 
00171 
00172 void KFileItemDelegate::Private::setHorizontalMargin(MarginType type, int horizontal, int vertical)
00173 {
00174     setHorizontalMargin(type, horizontal, vertical, horizontal, vertical);
00175 }
00176 
00177 
00178 QRect KFileItemDelegate::Private::addMargin(const QRect &rect, MarginType type) const
00179 {
00180     Q_ASSERT(activeMargins != 0);
00181     const Margin &m = activeMargins[type];
00182     return rect.adjusted(-m.left, -m.top, m.right, m.bottom);
00183 }
00184 
00185 
00186 QRect KFileItemDelegate::Private::subtractMargin(const QRect &rect, MarginType type) const
00187 {
00188     Q_ASSERT(activeMargins != 0);
00189     const Margin &m = activeMargins[type];
00190     return rect.adjusted(m.left, m.top, -m.right, -m.bottom);
00191 }
00192 
00193 
00194 QSize KFileItemDelegate::Private::addMargin(const QSize &size, MarginType type) const
00195 {
00196     Q_ASSERT(activeMargins != 0);
00197     const Margin &m = activeMargins[type];
00198     return QSize(size.width() + m.left + m.right, size.height() + m.top + m.bottom);
00199 }
00200 
00201 
00202 QSize KFileItemDelegate::Private::subtractMargin(const QSize &size, MarginType type) const
00203 {
00204     Q_ASSERT(activeMargins != 0);
00205     const Margin &m = activeMargins[type];
00206     return QSize(size.width() - m.left - m.right, size.height() - m.top - m.bottom);
00207 }
00208 
00209 
00210 // Returns the size of a file, or the number of items in a directory, as a QString
00211 QString KFileItemDelegate::Private::itemSize(const QModelIndex &index, const KFileItem &item) const
00212 {
00213     // Return a formatted string containing the file size, if the item is a file
00214     if (item.isFile())
00215         return KGlobal::locale()->formatByteSize(item.size());
00216 
00217     // Return the number of items in the directory
00218     const QVariant value = index.data(KDirModel::ChildCountRole);
00219     const int count = value.type() == QVariant::Int ? value.toInt() : KDirModel::ChildCountUnknown;
00220 
00221     if (count == KDirModel::ChildCountUnknown) {
00222         // was: i18nc("Items in a folder", "? items");
00223         // but this just looks useless in a remote directory listing,
00224         // better not show anything.
00225         return QString();
00226     }
00227 
00228     return i18ncp("Items in a folder", "1 item", "%1 items", count);
00229 }
00230 
00231 
00232 // Returns the additional information string, if one should be shown, or an empty string otherwise
00233 QString KFileItemDelegate::Private::information(const QStyleOptionViewItemV4 &option, const QModelIndex &index,
00234                                                 const KFileItem &item) const
00235 {
00236     QString string;
00237 
00238     if (informationList.isEmpty() || item.isNull() || !isListView(option))
00239         return string;
00240 
00241     foreach (KFileItemDelegate::Information info, informationList)
00242     {
00243         if (info == KFileItemDelegate::NoInformation)
00244             continue;
00245 
00246         if (!string.isEmpty())
00247             string += QChar::LineSeparator;
00248 
00249         switch (info)
00250         {
00251             case KFileItemDelegate::Size:
00252                 string += itemSize(index, item);
00253                 break;
00254 
00255             case KFileItemDelegate::Permissions:
00256                 string += item.permissionsString();
00257                 break;
00258 
00259             case KFileItemDelegate::OctalPermissions:
00260                 string += QString('0') + QString::number(item.permissions(), 8);
00261                 break;
00262 
00263             case KFileItemDelegate::Owner:
00264                 string += item.user();
00265                 break;
00266 
00267             case KFileItemDelegate::OwnerAndGroup:
00268                 string += item.user() + ':' + item.group();
00269                 break;
00270 
00271             case KFileItemDelegate::CreationTime:
00272                 string += item.timeString(KFileItem::CreationTime);
00273                 break;
00274 
00275             case KFileItemDelegate::ModificationTime:
00276                 string += item.timeString(KFileItem::ModificationTime);
00277                 break;
00278 
00279             case KFileItemDelegate::AccessTime:
00280                 string += item.timeString(KFileItem::AccessTime);
00281                 break;
00282 
00283             case KFileItemDelegate::MimeType:
00284                 string += item.isMimeTypeKnown() ? item.mimetype() : i18nc("@info mimetype","Unknown");
00285                 break;
00286 
00287             case KFileItemDelegate::FriendlyMimeType:
00288                 string += item.isMimeTypeKnown() ? item.mimeComment() : i18nc("@info mimetype","Unknown");
00289                 break;
00290 
00291             default:
00292                 break;
00293         } // switch (info)
00294     } // foreach (info, list)
00295 
00296     return string;
00297 }
00298 
00299 
00300 // Returns the KFileItem for the index
00301 KFileItem KFileItemDelegate::Private::fileItem(const QModelIndex &index) const
00302 {
00303     const QVariant value = index.data(KDirModel::FileItemRole);
00304     return qvariant_cast<KFileItem>(value);
00305 }
00306 
00307 
00308 // Replaces any newline characters in the provided string, with QChar::LineSeparator
00309 QString KFileItemDelegate::Private::replaceNewlines(const QString &text) const
00310 {
00311     QString string = text;
00312     const QChar newline = QLatin1Char('\n');
00313 
00314     for (int i = 0; i < string.length(); i++)
00315         if (string[i] == newline)
00316             string[i] = QChar::LineSeparator;
00317 
00318     return string;
00319 }
00320 
00321 
00322 // Lays the text out in a rectangle no larger than constraints, eliding it as necessary
00323 QSize KFileItemDelegate::Private::layoutText(QTextLayout &layout, const QStyleOptionViewItemV4 &option,
00324                                              const QString &text, const QSize &constraints) const
00325 {
00326     const QSize size = layoutText(layout, text, constraints.width());
00327 
00328     if (size.width() > constraints.width() || size.height() > constraints.height())
00329     {
00330         const QString elided = elidedText(layout, option, constraints);
00331         return layoutText(layout, elided, constraints.width());
00332     }
00333 
00334     return size;
00335 }
00336 
00337 
00338 // Lays the text out in a rectangle no wider than maxWidth
00339 QSize KFileItemDelegate::Private::layoutText(QTextLayout &layout, const QString &text, int maxWidth) const
00340 {
00341     QFontMetrics metrics(layout.font());
00342     int leading     = metrics.leading();
00343     int height      = 0;
00344     qreal widthUsed = 0;
00345     QTextLine line;
00346 
00347     layout.setText(text);
00348 
00349     layout.beginLayout();
00350     while ((line = layout.createLine()).isValid())
00351     {
00352         line.setLineWidth(int(maxWidth));
00353         height += leading;
00354         line.setPosition(QPoint(0, height));
00355         height += int(line.height());
00356         widthUsed = qMax(widthUsed, line.naturalTextWidth());
00357     }
00358     layout.endLayout();
00359 
00360     return QSize(int(widthUsed), height);
00361 }
00362 
00363 
00364 // Elides the text in the layout, by iterating over each line in the layout, eliding
00365 // or word breaking the line if it's wider than the max width, and finally adding an
00366 // ellipses at the end of the last line, if there are more lines than will fit within
00367 // the vertical size constraints.
00368 QString KFileItemDelegate::Private::elidedText(QTextLayout &layout, const QStyleOptionViewItemV4 &option,
00369                                                const QSize &size) const
00370 {
00371     const QString text = layout.text();
00372     int maxWidth       = size.width();
00373     int maxHeight      = size.height();
00374     qreal height       = 0;
00375     bool wrapText      = (option.features & QStyleOptionViewItemV2::WrapText);
00376 
00377     // If the string contains a single line of text that shouldn't be word wrapped
00378     if (!wrapText && text.indexOf(QChar::LineSeparator) == -1)
00379         return option.fontMetrics.elidedText(text, option.textElideMode, maxWidth);
00380 
00381     // Elide each line that has already been laid out in the layout.
00382     QString elided;
00383     elided.reserve(text.length());
00384 
00385     for (int i = 0; i < layout.lineCount(); i++)
00386     {
00387         QTextLine line = layout.lineAt(i);
00388         int start  = line.textStart();
00389         int length = line.textLength();
00390 
00391         height += option.fontMetrics.leading();
00392         if (height + line.height() + option.fontMetrics.lineSpacing() > maxHeight)
00393         {
00394             // Unfortunately, if the line ends because of a line separator, elidedText() will be too
00395             // clever and keep adding lines until it finds one that's too wide.
00396             if (line.naturalTextWidth() < maxWidth && text[start + length - 1] == QChar::LineSeparator)
00397                 elided += text.mid(start, length - 1);
00398             else
00399                 elided += option.fontMetrics.elidedText(text.mid(start), option.textElideMode, maxWidth);
00400             break;
00401         }
00402         else if (line.naturalTextWidth() > maxWidth)
00403         {
00404             elided += option.fontMetrics.elidedText(text.mid(start, length), option.textElideMode, maxWidth);
00405             if (!elided.endsWith(QChar::LineSeparator))
00406                 elided += QChar::LineSeparator;
00407         }
00408         else
00409             elided += text.mid(start, length);
00410 
00411         height += line.height();
00412     }
00413 
00414     return elided;
00415 }
00416 
00417 
00418 void KFileItemDelegate::Private::setLayoutOptions(QTextLayout &layout, const QStyleOptionViewItemV4 &option) const
00419 {
00420     QTextOption textoption;
00421     textoption.setTextDirection(option.direction);
00422     textoption.setAlignment(QStyle::visualAlignment(option.direction, option.displayAlignment));
00423     textoption.setWrapMode((option.features & QStyleOptionViewItemV2::WrapText) ?
00424                            QTextOption::WrapAtWordBoundaryOrAnywhere : QTextOption::NoWrap);
00425 
00426     layout.setFont(option.font);
00427     layout.setTextOption(textoption);
00428 }
00429 
00430 
00431 QSize KFileItemDelegate::Private::displaySizeHint(const QStyleOptionViewItemV4 &option,
00432                                                   const QModelIndex &index) const
00433 {
00434     QString label = option.text;
00435     int maxWidth = 0;
00436     if (maximumSize.isEmpty()) {
00437         maxWidth = verticalLayout(option) && (option.features & QStyleOptionViewItemV2::WrapText)
00438                    ? option.decorationSize.width() + 10 : 32757;
00439     }
00440     else {
00441         const Margin &itemMargin = activeMargins[ItemMargin];
00442         const Margin &textMargin = activeMargins[TextMargin];
00443         maxWidth = maximumSize.width() -
00444                    (itemMargin.left + itemMargin.right) -
00445                    (textMargin.left + textMargin.right);
00446     }
00447 
00448     KFileItem item = fileItem(index);
00449 
00450     // To compute the nominal size for the label + info, we'll just append
00451     // the information string to the label
00452     const QString info = information(option, index, item);
00453     if (!info.isEmpty())
00454         label += QString(QChar::LineSeparator) + info;
00455 
00456     QTextLayout layout;
00457     setLayoutOptions(layout, option);
00458 
00459     QSize size = layoutText(layout, label, maxWidth);
00460     return addMargin(size, TextMargin);
00461 }
00462 
00463 
00464 QSize KFileItemDelegate::Private::decorationSizeHint(const QStyleOptionViewItemV4 &option,
00465                                                      const QModelIndex &index) const
00466 {
00467     Q_UNUSED(index)
00468 
00469     QSize iconSize = option.icon.actualSize(option.decorationSize);
00470     if (!verticalLayout(option))
00471         iconSize.rwidth() = option.decorationSize.width();
00472     else if (iconSize.width() < option.decorationSize.width())
00473         iconSize.rwidth() = qMin(iconSize.width() + 10, option.decorationSize.width());
00474     if (iconSize.height() < option.decorationSize.height())
00475         iconSize.rheight() = option.decorationSize.height();
00476 
00477     return addMargin(iconSize, IconMargin);
00478 }
00479 
00480 
00481 bool KFileItemDelegate::Private::verticalLayout(const QStyleOptionViewItemV4 &option) const
00482 {
00483     return (option.decorationPosition == QStyleOptionViewItem::Top ||
00484             option.decorationPosition == QStyleOptionViewItem::Bottom);
00485 }
00486 
00487 
00488 // Converts a QVariant of type Brush or Color to a QBrush
00489 QBrush KFileItemDelegate::Private::brush(const QVariant &value, const QStyleOptionViewItemV4 &option) const
00490 {
00491     if (value.userType() == qMetaTypeId<KStatefulBrush>())
00492         return qvariant_cast<KStatefulBrush>(value).brush(option.palette);
00493     switch (value.type())
00494     {
00495         case QVariant::Color:
00496             return QBrush(qvariant_cast<QColor>(value));
00497 
00498         case QVariant::Brush:
00499             return qvariant_cast<QBrush>(value);
00500 
00501         default:
00502             return QBrush(Qt::NoBrush);
00503     }
00504 }
00505 
00506 
00507 QBrush KFileItemDelegate::Private::foregroundBrush(const QStyleOptionViewItemV4 &option, const QModelIndex &index) const
00508 {
00509     // Always use the highlight color for selected items
00510     if (option.state & QStyle::State_Selected)
00511         return option.palette.brush(QPalette::HighlightedText);
00512 
00513     // If the model provides its own foreground color/brush for this item
00514     const QVariant value = index.data(Qt::ForegroundRole);
00515     if (value.isValid())
00516         return brush(value, option);
00517 
00518     return option.palette.brush(QPalette::Text);
00519 }
00520 
00521 
00522 bool KFileItemDelegate::Private::isListView(const QStyleOptionViewItemV4 &option) const
00523 {
00524     if (qobject_cast<const QListView*>(option.widget) || verticalLayout(option))
00525         return true;
00526 
00527     return false;
00528 }
00529 
00530 
00531 QPixmap KFileItemDelegate::Private::applyHoverEffect(const QPixmap &icon) const
00532 {
00533     KIconEffect *effect = KIconLoader::global()->iconEffect();
00534 
00535     // Note that in KIconLoader terminology, active = hover.
00536     // ### We're assuming that the icon group is desktop/filemanager, since this
00537     //     is KFileItemDelegate.
00538     if (effect->hasEffect(KIconLoader::Desktop, KIconLoader::ActiveState))
00539         return effect->apply(icon, KIconLoader::Desktop, KIconLoader::ActiveState);
00540 
00541     return icon;
00542 }
00543 
00544 void KFileItemDelegate::Private::gotNewIcon(const QModelIndex& index) {
00545     animationHandler->gotNewIcon(index);
00546 }
00547 
00548 void KFileItemDelegate::Private::restartAnimation(KIO::AnimationState* state) {
00549     animationHandler->restartAnimation(state);
00550 }
00551 
00552 KIO::AnimationState *KFileItemDelegate::Private::animationState(const QStyleOptionViewItemV4 &option,
00553                                                                 const QModelIndex &index,
00554                                                                 const QAbstractItemView *view) const
00555 {
00556     if (!(KGlobalSettings::graphicEffectsLevel() & KGlobalSettings::SimpleAnimationEffects)) {
00557         return NULL;
00558     }
00559 
00560     if (index.column() == KDirModel::Name)
00561         return animationHandler->animationState(option, index, view);
00562 
00563     return NULL;
00564 }
00565 
00566 
00567 QPixmap KFileItemDelegate::Private::transition(const QPixmap &from, const QPixmap &to, qreal amount) const
00568 {
00569     int value = int(0xff * amount);
00570 
00571     if (value == 0)
00572         return from;
00573 
00574     if (value == 1)
00575         return to;
00576 
00577     QColor color;
00578     color.setAlphaF(amount);
00579 
00580     // If the native paint engine supports Porter/Duff compositing and CompositionMode_Plus
00581     if (from.paintEngine()->hasFeature(QPaintEngine::PorterDuff) &&
00582         from.paintEngine()->hasFeature(QPaintEngine::BlendModes))
00583     {
00584         QPixmap under = from;
00585         QPixmap over  = to;
00586 
00587         QPainter p;
00588         p.begin(&over);
00589         p.setCompositionMode(QPainter::CompositionMode_DestinationIn);
00590         p.fillRect(over.rect(), color);
00591         p.end();
00592 
00593         p.begin(&under);
00594         p.setCompositionMode(QPainter::CompositionMode_DestinationOut);
00595         p.fillRect(under.rect(), color);
00596         p.setCompositionMode(QPainter::CompositionMode_Plus);
00597         p.drawPixmap(0, 0, over);
00598         p.end();
00599 
00600         return under;
00601     }
00602 #if defined(Q_WS_X11) && defined(HAVE_XRENDER)
00603     else if (from.paintEngine()->hasFeature(QPaintEngine::PorterDuff)) // We have Xrender support
00604     {
00605         // QX11PaintEngine doesn't implement CompositionMode_Plus in Qt 4.3,
00606         // which we need to be able to do a transition from one pixmap to
00607         // another.
00608         //
00609         // In order to avoid the overhead of converting the pixmaps to images
00610         // and doing the operation entirely in software, this function has a
00611         // specialized path for X11 that uses Xrender directly to do the
00612         // transition. This operation can be fully accelerated in HW.
00613         //
00614         // This specialization can be removed when QX11PaintEngine supports
00615         // CompositionMode_Plus.
00616         QPixmap source(to), destination(from);
00617 
00618         source.detach();
00619         destination.detach();
00620 
00621         Display *dpy = QX11Info::display();
00622 
00623         XRenderPictFormat *format = XRenderFindStandardFormat(dpy, PictStandardA8);
00624         XRenderPictureAttributes pa;
00625         pa.repeat = 1; // RepeatNormal
00626 
00627         // Create a 1x1 8 bit repeating alpha picture
00628         Pixmap pixmap = XCreatePixmap(dpy, destination.handle(), 1, 1, 8);
00629         Picture alpha = XRenderCreatePicture(dpy, pixmap, format, CPRepeat, &pa);
00630         XFreePixmap(dpy, pixmap);
00631 
00632         // Fill the alpha picture with the opacity value
00633         XRenderColor xcolor;
00634         xcolor.alpha = quint16(0xffff * amount);
00635         XRenderFillRectangle(dpy, PictOpSrc, alpha, &xcolor, 0, 0, 1, 1);
00636 
00637         // Reduce the alpha of the destination with 1 - opacity
00638         XRenderComposite(dpy, PictOpOutReverse, alpha, None, destination.x11PictureHandle(),
00639                          0, 0, 0, 0, 0, 0, destination.width(), destination.height());
00640 
00641         // Add source * opacity to the destination
00642         XRenderComposite(dpy, PictOpAdd, source.x11PictureHandle(), alpha,
00643                          destination.x11PictureHandle(),
00644                          0, 0, 0, 0, 0, 0, destination.width(), destination.height());
00645 
00646         XRenderFreePicture(dpy, alpha);
00647         return destination;
00648     }
00649 #endif
00650     else
00651     {
00652         // Fall back to using QRasterPaintEngine to do the transition.
00653         QImage under = from.toImage();
00654         QImage over  = to.toImage();
00655 
00656         QPainter p;
00657         p.begin(&over);
00658         p.setCompositionMode(QPainter::CompositionMode_DestinationIn);
00659         p.fillRect(over.rect(), color);
00660         p.end();
00661 
00662         p.begin(&under);
00663         p.setCompositionMode(QPainter::CompositionMode_DestinationOut);
00664         p.fillRect(under.rect(), color);
00665         p.setCompositionMode(QPainter::CompositionMode_Plus);
00666         p.drawImage(0, 0, over);
00667         p.end();
00668 
00669         return QPixmap::fromImage(under);
00670     }
00671 }
00672 
00673 
00674 void KFileItemDelegate::Private::layoutTextItems(const QStyleOptionViewItemV4 &option, const QModelIndex &index,
00675                                                  QTextLayout *labelLayout, QTextLayout *infoLayout,
00676                                                  QRect *textBoundingRect) const
00677 {
00678     KFileItem item       = fileItem(index);
00679     const QString info   = information(option, index, item);
00680     bool showInformation = false;
00681 
00682     setLayoutOptions(*labelLayout, option);
00683 
00684     const QRect textArea = labelRectangle(option);
00685     QRect textRect       = subtractMargin(textArea, Private::TextMargin);
00686 
00687     // Sizes and constraints for the different text parts
00688     QSize maxLabelSize = textRect.size();
00689     QSize maxInfoSize  = textRect.size();
00690     QSize labelSize;
00691     QSize infoSize;
00692 
00693     // If we have additional info text, and there's space for at least two lines of text,
00694     // adjust the max label size to make room for at least one line of the info text
00695     if (!info.isEmpty() && textRect.height() >= option.fontMetrics.lineSpacing() * 2)
00696     {
00697         infoLayout->setFont(labelLayout->font());
00698         infoLayout->setTextOption(labelLayout->textOption());
00699 
00700         maxLabelSize.rheight() -= option.fontMetrics.lineSpacing();
00701         showInformation = true;
00702     }
00703 
00704     // Lay out the label text, and adjust the max info size based on the label size
00705     labelSize = layoutText(*labelLayout, option, option.text, maxLabelSize);
00706     maxInfoSize.rheight() -= labelSize.height();
00707 
00708     // Lay out the info text
00709     if (showInformation)
00710         infoSize = layoutText(*infoLayout, option, info, maxInfoSize);
00711     else
00712         infoSize = QSize(0, 0);
00713 
00714     // Compute the bounding rect of the text
00715     const QSize size(qMax(labelSize.width(), infoSize.width()), labelSize.height() + infoSize.height());
00716     *textBoundingRect = QStyle::alignedRect(option.direction, option.displayAlignment, size, textRect);
00717 
00718     // Compute the positions where we should draw the layouts
00719     labelLayout->setPosition(QPointF(textRect.x(), textBoundingRect->y()));
00720     infoLayout->setPosition(QPointF(textRect.x(), textBoundingRect->y() + labelSize.height()));
00721 }
00722 
00723 
00724 void KFileItemDelegate::Private::drawTextItems(QPainter *painter, const QTextLayout &labelLayout,
00725                                                const QTextLayout &infoLayout, const QRect &boundingRect) const
00726 {
00727     if (shadowColor.alpha() > 0)
00728     {
00729         QPixmap pixmap(boundingRect.size());
00730         pixmap.fill(Qt::transparent);
00731 
00732         QPainter p(&pixmap);
00733         p.translate(-boundingRect.topLeft());
00734         p.setPen(painter->pen());
00735         labelLayout.draw(&p, QPoint());
00736 
00737         if (!infoLayout.text().isEmpty())
00738         {
00739             QColor color = p.pen().color();
00740             color.setAlphaF(0.6);
00741 
00742             p.setPen(color);
00743             infoLayout.draw(&p, QPoint());
00744         }
00745         p.end();
00746 
00747         int padding = qCeil(shadowBlur);
00748         int blurFactor = qRound(shadowBlur);
00749 
00750         QImage image(boundingRect.size() + QSize(padding * 2, padding * 2), QImage::Format_ARGB32_Premultiplied);
00751         image.fill(0);
00752         p.begin(&image);
00753         p.drawImage(padding, padding, pixmap.toImage());
00754         p.end();
00755 
00756         KIO::ImageFilter::shadowBlur(image, blurFactor, shadowColor);
00757 
00758         painter->drawImage(boundingRect.topLeft() - QPoint(padding, padding) + shadowOffset.toPoint(), image);
00759         painter->drawPixmap(boundingRect.topLeft(), pixmap);
00760         return;
00761     }
00762 
00763     labelLayout.draw(painter, QPoint());
00764 
00765     if (!infoLayout.text().isEmpty())
00766     {
00767         // TODO - for apps not doing funny things with the color palette,
00768         // KColorScheme::InactiveText would be a much more correct choice. We
00769         // should provide an API to specify what color to use for information.
00770         QColor color = painter->pen().color();
00771         color.setAlphaF(0.6);
00772 
00773         painter->setPen(color);
00774         infoLayout.draw(painter, QPoint());
00775     }
00776 }
00777 
00778 
00779 void KFileItemDelegate::Private::initStyleOption(QStyleOptionViewItemV4 *option,
00780                                                  const QModelIndex &index) const
00781 {
00782     const KFileItem item = fileItem(index);
00783     bool updateFontMetrics = false;
00784 
00785     // Try to get the font from the model
00786     QVariant value = index.data(Qt::FontRole);
00787     if (value.isValid()) {
00788         option->font = qvariant_cast<QFont>(value).resolve(option->font);
00789         updateFontMetrics = true;
00790     }
00791 
00792     // Use an italic font for symlinks
00793     if (!item.isNull() && item.isLink()) {
00794         option->font.setItalic(true);
00795         updateFontMetrics = true;
00796     }
00797 
00798     if (updateFontMetrics)
00799         option->fontMetrics = QFontMetrics(option->font);
00800 
00801     // Try to get the alignment for the item from the model
00802     value = index.data(Qt::TextAlignmentRole);
00803     if (value.isValid())
00804         option->displayAlignment = Qt::Alignment(value.toInt());
00805 
00806     value = index.data(Qt::BackgroundRole);
00807     if (value.isValid())
00808         option->backgroundBrush = brush(value, *option);
00809 
00810     option->text = display(index);
00811     if (!option->text.isEmpty())
00812         option->features |= QStyleOptionViewItemV2::HasDisplay;
00813 
00814     option->icon = decoration(*option, index);
00815     if (!option->icon.isNull())
00816         option->features |= QStyleOptionViewItemV2::HasDecoration;
00817 
00818     // ### Make sure this value is always true for now
00819     option->showDecorationSelected = true;
00820 }
00821 
00822 
00823 
00824 
00825 // ---------------------------------------------------------------------------
00826 
00827 
00828 
00829 
00830 KFileItemDelegate::KFileItemDelegate(QObject *parent)
00831     : QAbstractItemDelegate(parent), d(new Private(this))
00832 {
00833     int focusHMargin = QApplication::style()->pixelMetric(QStyle::PM_FocusFrameHMargin);
00834     int focusVMargin = QApplication::style()->pixelMetric(QStyle::PM_FocusFrameVMargin);
00835 
00836     // Margins for horizontal mode (list views, tree views, table views)
00837     const int textMargin = focusHMargin * 4;
00838     if (QApplication::isRightToLeft())
00839         d->setHorizontalMargin(Private::TextMargin, textMargin, focusVMargin, focusHMargin, focusVMargin);
00840     else
00841         d->setHorizontalMargin(Private::TextMargin, focusHMargin, focusVMargin, textMargin, focusVMargin);
00842 
00843     d->setHorizontalMargin(Private::IconMargin, focusHMargin, focusVMargin);
00844     d->setHorizontalMargin(Private::ItemMargin, 0, 0);
00845 
00846     // Margins for vertical mode (icon views)
00847     d->setVerticalMargin(Private::TextMargin, 6, 2);
00848     d->setVerticalMargin(Private::IconMargin, focusHMargin, focusVMargin);
00849     d->setVerticalMargin(Private::ItemMargin, 0, 0);
00850 
00851     setShowInformation(NoInformation);
00852 }
00853 
00854 
00855 KFileItemDelegate::~KFileItemDelegate()
00856 {
00857     delete d;
00858 }
00859 
00860 
00861 QSize KFileItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
00862 {
00863     // If the model wants to provide its own size hint for the item
00864     const QVariant value = index.data(Qt::SizeHintRole);
00865     if (value.isValid())
00866         return qvariant_cast<QSize>(value);
00867 
00868     QStyleOptionViewItemV4 opt(option);
00869     d->initStyleOption(&opt, index);
00870     d->setActiveMargins(d->verticalLayout(opt) ? Qt::Vertical : Qt::Horizontal);
00871 
00872     const QSize displaySize    = d->displaySizeHint(opt, index);
00873     const QSize decorationSize = d->decorationSizeHint(opt, index);
00874 
00875     QSize size;
00876 
00877     if (d->verticalLayout(opt))
00878     {
00879         size.rwidth()  = qMax(displaySize.width(), decorationSize.width());
00880         size.rheight() = decorationSize.height() + displaySize.height() + 1;
00881     }
00882     else
00883     {
00884         size.rwidth()  = decorationSize.width() + displaySize.width() + 1;
00885         size.rheight() = qMax(decorationSize.height(), displaySize.height());
00886     }
00887 
00888     size = d->addMargin(size, Private::ItemMargin);
00889     if (!d->maximumSize.isEmpty())
00890     {
00891         size = size.boundedTo(d->maximumSize);
00892     }
00893 
00894     return size;
00895 }
00896 
00897 
00898 QString KFileItemDelegate::Private::display(const QModelIndex &index) const
00899 {
00900     const QVariant value = index.data(Qt::DisplayRole);
00901 
00902     switch (value.type())
00903     {
00904         case QVariant::String:
00905         {
00906             if (index.column() == KDirModel::Size)
00907                 return itemSize(index, fileItem(index));
00908             else
00909                 return replaceNewlines(value.toString());
00910         }
00911 
00912         case QVariant::Double:
00913             return KGlobal::locale()->formatNumber(value.toDouble());
00914 
00915         case QVariant::Int:
00916         case QVariant::UInt:
00917             return KGlobal::locale()->formatLong(value.toInt());
00918 
00919         default:
00920             return QString();
00921     }
00922 }
00923 
00924 
00925 void KFileItemDelegate::setShowInformation(const InformationList &list)
00926 {
00927     d->informationList = list;
00928 }
00929 
00930 
00931 void KFileItemDelegate::setShowInformation(Information value)
00932 {
00933     if (value != NoInformation)
00934         d->informationList = InformationList() << value;
00935     else
00936         d->informationList = InformationList();
00937 }
00938 
00939 
00940 KFileItemDelegate::InformationList KFileItemDelegate::showInformation() const
00941 {
00942     return d->informationList;
00943 }
00944 
00945 
00946 void KFileItemDelegate::setShadowColor(const QColor &color)
00947 {
00948     d->shadowColor = color;
00949 }
00950 
00951 
00952 QColor KFileItemDelegate::shadowColor() const
00953 {
00954     return d->shadowColor;
00955 }
00956 
00957 
00958 void KFileItemDelegate::setShadowOffset(const QPointF &offset)
00959 {
00960     d->shadowOffset = offset;
00961 }
00962 
00963 
00964 QPointF KFileItemDelegate::shadowOffset() const
00965 {
00966     return d->shadowOffset;
00967 }
00968 
00969 
00970 void KFileItemDelegate::setShadowBlur(qreal factor)
00971 {
00972     d->shadowBlur = factor;
00973 }
00974 
00975 
00976 qreal KFileItemDelegate::shadowBlur() const
00977 {
00978     return d->shadowBlur;
00979 }
00980 
00981 
00982 void KFileItemDelegate::setMaximumSize(const QSize &size)
00983 {
00984     d->maximumSize = size;
00985 }
00986 
00987 
00988 QSize KFileItemDelegate::maximumSize() const
00989 {
00990     return d->maximumSize;
00991 }
00992 
00993 
00994 void KFileItemDelegate::setShowToolTipWhenElided(bool showToolTip)
00995 {
00996     d->showToolTipWhenElided = showToolTip;
00997 }
00998 
00999 
01000 bool KFileItemDelegate::showToolTipWhenElided() const
01001 {
01002     return d->showToolTipWhenElided;
01003 }
01004 
01005 
01006 QIcon KFileItemDelegate::Private::decoration(const QStyleOptionViewItemV4 &option, const QModelIndex &index) const
01007 {
01008     const QVariant value = index.data(Qt::DecorationRole);
01009     QIcon icon;
01010 
01011     switch (value.type())
01012     {
01013     case QVariant::Icon:
01014         icon = qvariant_cast<QIcon>(value);
01015         break;
01016 
01017     case QVariant::Pixmap:
01018         icon.addPixmap(qvariant_cast<QPixmap>(value));
01019         break;
01020 
01021     case QVariant::Color: {
01022         QPixmap pixmap(option.decorationSize);
01023         pixmap.fill(qvariant_cast<QColor>(value));
01024         icon.addPixmap(pixmap);
01025         break;
01026     }
01027 
01028     default:
01029         break;
01030     }
01031 
01032     return icon;
01033 }
01034 
01035 
01036 QRect KFileItemDelegate::Private::labelRectangle(const QStyleOptionViewItemV4 &option) const
01037 {
01038     if (option.icon.isNull())
01039         return subtractMargin(option.rect, Private::ItemMargin);
01040 
01041     const QSize decoSize = addMargin(option.decorationSize, Private::IconMargin);
01042     const QRect itemRect = subtractMargin(option.rect, Private::ItemMargin);
01043     QRect textArea(QPoint(0, 0), itemRect.size());
01044 
01045     switch (option.decorationPosition)
01046     {
01047         case QStyleOptionViewItem::Top:
01048             textArea.setTop(decoSize.height() + 1);
01049             break;
01050 
01051         case QStyleOptionViewItem::Bottom:
01052             textArea.setBottom(itemRect.height() - decoSize.height() - 1);
01053             break;
01054 
01055         case QStyleOptionViewItem::Left:
01056             textArea.setLeft(decoSize.width() + 1);
01057             break;
01058 
01059         case QStyleOptionViewItem::Right:
01060             textArea.setRight(itemRect.width() - decoSize.width() - 1);
01061             break;
01062     }
01063 
01064     textArea.translate(itemRect.topLeft());
01065     return QStyle::visualRect(option.direction, option.rect, textArea);
01066 }
01067 
01068 
01069 QPoint KFileItemDelegate::Private::iconPosition(const QStyleOptionViewItemV4 &option) const
01070 {
01071     const QRect itemRect = subtractMargin(option.rect, Private::ItemMargin);
01072     Qt::Alignment alignment;
01073 
01074     // Convert decorationPosition to the alignment the decoration will have in option.rect
01075     switch (option.decorationPosition)
01076     {
01077         case QStyleOptionViewItem::Top:
01078             alignment = Qt::AlignHCenter | Qt::AlignTop;
01079             break;
01080 
01081         case QStyleOptionViewItem::Bottom:
01082             alignment = Qt::AlignHCenter | Qt::AlignBottom;
01083             break;
01084 
01085         case QStyleOptionViewItem::Left:
01086             alignment = Qt::AlignVCenter | Qt::AlignLeft;
01087             break;
01088 
01089         case QStyleOptionViewItem::Right:
01090             alignment = Qt::AlignVCenter | Qt::AlignRight;
01091             break;
01092     }
01093 
01094     // Compute the nominal decoration rectangle
01095     const QSize size = addMargin(option.decorationSize, Private::IconMargin);
01096     const QRect rect = QStyle::alignedRect(option.direction, alignment, size, itemRect);
01097 
01098     // Position the icon in the center of the rectangle
01099     QRect iconRect = QRect(QPoint(), option.icon.actualSize(option.decorationSize));
01100     iconRect.moveCenter(rect.center());
01101 
01102     return iconRect.topLeft();
01103 }
01104 
01105 
01106 void KFileItemDelegate::Private::drawFocusRect(QPainter *painter, const QStyleOptionViewItemV4 &option,
01107                                                const QRect &rect) const
01108 {
01109     if (!(option.state & QStyle::State_HasFocus))
01110         return;
01111 
01112     QStyleOptionFocusRect opt;
01113     opt.direction       = option.direction;
01114     opt.fontMetrics     = option.fontMetrics;
01115     opt.palette         = option.palette;
01116     opt.rect            = rect;
01117     opt.state           = option.state | QStyle::State_KeyboardFocusChange | QStyle::State_Item;
01118     opt.backgroundColor = option.palette.color(option.state & QStyle::State_Selected ?
01119                                                QPalette::Highlight : QPalette::Base);
01120 
01121     // Apparently some widget styles expect this hint to not be set
01122     painter->setRenderHint(QPainter::Antialiasing, false);
01123 
01124     QStyle *style = option.widget ? option.widget->style() : QApplication::style();
01125     style->drawPrimitive(QStyle::PE_FrameFocusRect, &opt, painter, option.widget);
01126 
01127     painter->setRenderHint(QPainter::Antialiasing);
01128 }
01129 
01130 
01131 void KFileItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
01132                               const QModelIndex &index) const
01133 {
01134     if (!index.isValid())
01135         return;
01136 
01137     QStyleOptionViewItemV4 opt(option);
01138     d->initStyleOption(&opt, index);
01139 
01140     // Unset the mouse over bit if we're not drawing the first column
01141     if (index.column() > 0)
01142         opt.state &= ~QStyle::State_MouseOver;
01143     else
01144         opt.viewItemPosition = QStyleOptionViewItemV4::OnlyOne;
01145 
01146     const QAbstractItemView *view = qobject_cast<const QAbstractItemView*>(opt.widget);
01147 
01148 
01149     // Check if the item is being animated
01150     // ========================================================================
01151     KIO::AnimationState *state = d->animationState(opt, index, view);
01152     KIO::CachedRendering *cache = 0;
01153     qreal progress = ((opt.state & QStyle::State_MouseOver) &&
01154                 index.column() == KDirModel::Name) ? 1.0 : 0.0;
01155     if (state)
01156     {
01157         cache    = state->cachedRendering();
01158         progress = state->hoverProgress();
01159         // Clear the mouse over bit temporarily
01160         opt.state &= ~QStyle::State_MouseOver;
01161 
01162         // If we have a cached rendering, draw the item from the cache
01163         if (cache)
01164         {
01165             if (cache->checkValidity(opt.state) && cache->regular.size() == opt.rect.size())
01166             {
01167                 QPixmap pixmap = d->transition(cache->regular, cache->hover, progress);
01168 
01169                 if (state->cachedRenderingFadeFrom() && state->fadeProgress() != 1.0)
01170                 {
01171                     // Apply icon fading animation
01172                     KIO::CachedRendering* fadeFromCache = state->cachedRenderingFadeFrom();
01173                     const QPixmap fadeFromPixmap = d->transition(fadeFromCache->regular, fadeFromCache->hover, progress);
01174 
01175                     pixmap = d->transition(fadeFromPixmap, pixmap, state->fadeProgress());
01176                 }
01177                 painter->drawPixmap(option.rect.topLeft(), pixmap);
01178                 return;
01179             }
01180 
01181             if (!cache->checkValidity(opt.state))
01182             {
01183                 if ((KGlobalSettings::graphicEffectsLevel() & KGlobalSettings::SimpleAnimationEffects))
01184                 {
01185                     // Fade over from the old icon to the new one
01186                     // Only start a new fade if the previous one is ready
01187                     // Else we may start racing when checkValidity() always returns false
01188                     if (state->fadeProgress() == 1)
01189                         state->setCachedRenderingFadeFrom(state->takeCachedRendering());
01190                 }
01191                 d->gotNewIcon(index);
01192             }
01193             // If it wasn't valid, delete it
01194             state->setCachedRendering(0);
01195         }
01196         else
01197         {
01198             // The cache may have been discarded, but the animation handler still needs to know about new icons
01199             d->gotNewIcon(index);
01200         }
01201     }
01202 
01203     d->setActiveMargins(d->verticalLayout(opt) ? Qt::Vertical : Qt::Horizontal);
01204 
01205 
01206     // Compute the metrics, and lay out the text items
01207     // ========================================================================
01208     const QPoint iconPos   = d->iconPosition(opt);
01209     QIcon::Mode iconMode   = option.state & QStyle::State_Enabled ? QIcon::Normal : QIcon::Disabled;
01210     QIcon::State iconState = option.state & QStyle::State_Open ? QIcon::On : QIcon::Off;
01211     QPixmap icon           = opt.icon.pixmap(opt.decorationSize, iconMode, iconState);
01212     const QPen pen         = QPen(d->foregroundBrush(opt, index), 0);
01213 
01215     //     showDecorationSelected is false.
01216 
01217     QTextLayout labelLayout, infoLayout;
01218     QRect textBoundingRect;
01219 
01220     d->layoutTextItems(opt, index, &labelLayout, &infoLayout, &textBoundingRect);
01221 
01222     QStyle *style = opt.widget ? opt.widget->style() : QApplication::style();
01223 
01224     int focusHMargin = style->pixelMetric(QStyle::PM_FocusFrameHMargin);
01225     int focusVMargin = style->pixelMetric(QStyle::PM_FocusFrameVMargin);
01226     QRect focusRect = textBoundingRect.adjusted(-focusHMargin, -focusVMargin,
01227                                                 +focusHMargin, +focusVMargin);
01228 
01229     // Create a new cached rendering of a hovered and an unhovered item.
01230     // We don't create a new cache for a fully hovered item, since we don't
01231     // know yet if a hover out animation will be run.
01232     // ========================================================================
01233     if (state && (state->hoverProgress() < 1 || state->fadeProgress() < 1))
01234     {
01235         cache = new KIO::CachedRendering(opt.state, option.rect.size(), index);
01236 
01237         QPainter p;
01238         p.begin(&cache->regular);
01239         p.translate(-option.rect.topLeft());
01240         p.setRenderHint(QPainter::Antialiasing);
01241         p.setPen(pen);
01242         style->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, &p, opt.widget);
01243         p.drawPixmap(iconPos, icon);
01244         d->drawTextItems(&p, labelLayout, infoLayout, textBoundingRect);
01245         d->drawFocusRect(&p, opt, focusRect);
01246         p.end();
01247 
01248         opt.state |= QStyle::State_MouseOver;
01249         icon = d->applyHoverEffect(icon);
01250 
01251         p.begin(&cache->hover);
01252         p.translate(-option.rect.topLeft());
01253         p.setRenderHint(QPainter::Antialiasing);
01254         p.setPen(pen);
01255         style->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, &p, opt.widget);
01256         p.drawPixmap(iconPos, icon);
01257         d->drawTextItems(&p, labelLayout, infoLayout, textBoundingRect);
01258         d->drawFocusRect(&p, opt, focusRect);
01259         p.end();
01260 
01261         state->setCachedRendering(cache);
01262 
01263         QPixmap pixmap = d->transition(cache->regular, cache->hover, progress);
01264 
01265         if (state->cachedRenderingFadeFrom() && state->fadeProgress() == 0)
01266         {
01267             // Apply icon fading animation
01268             KIO::CachedRendering* fadeFromCache = state->cachedRenderingFadeFrom();
01269             const QPixmap fadeFromPixmap = d->transition(fadeFromCache->regular, fadeFromCache->hover, progress);
01270 
01271             pixmap = d->transition(fadeFromPixmap, pixmap, state->fadeProgress());
01272 
01273             d->restartAnimation(state);
01274         }
01275 
01276         painter->drawPixmap(option.rect.topLeft(), pixmap);
01277         return;
01278     }
01279 
01280 
01281     // Render the item directly if we're not using a cached rendering
01282     // ========================================================================
01283     painter->save();
01284     painter->setRenderHint(QPainter::Antialiasing);
01285     painter->setPen(pen);
01286 
01287     if (progress > 0 && !(opt.state & QStyle::State_MouseOver))
01288     {
01289         opt.state |= QStyle::State_MouseOver;
01290         icon = d->applyHoverEffect(icon);
01291     }
01292 
01293     style->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, painter, opt.widget);
01294     painter->drawPixmap(iconPos, icon);
01295     d->drawTextItems(painter, labelLayout, infoLayout, textBoundingRect);
01296     d->drawFocusRect(painter, opt, focusRect);
01297 
01298     painter->restore();
01299 }
01300 
01301 
01302 QWidget *KFileItemDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option,
01303                                          const QModelIndex &index) const
01304 {
01305     QStyleOptionViewItemV4 opt(option);
01306     d->initStyleOption(&opt, index);
01307 
01308     KTextEdit *edit = new KTextEdit(parent);
01309     edit->setAcceptRichText(false);
01310     edit->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
01311     edit->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
01312     edit->setAlignment(opt.displayAlignment);
01313     edit->setEnabled(false); //Disable the text-edit to mark it as un-initialized
01314     return edit;
01315 }
01316 
01317 
01318 bool KFileItemDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option,
01319                                     const QModelIndex &index)
01320 {
01321     Q_UNUSED(event)
01322     Q_UNUSED(model)
01323     Q_UNUSED(option)
01324     Q_UNUSED(index)
01325 
01326     return false;
01327 }
01328 
01329 
01330 void KFileItemDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
01331 {
01332     KTextEdit *textedit = qobject_cast<KTextEdit*>(editor);
01333     Q_ASSERT(textedit != 0);
01334     
01335     //Do not update existing text that the user may already have edited.
01336     //The models will call setEditorData(..) whenever the icon has changed,
01337     //and this makes the editing work correctly despite that.
01338     if(textedit->isEnabled()) {
01339         return;
01340     }
01341     textedit->setEnabled(true); //Enable the text-edit to mark it as initialized
01342     
01343     const QVariant value = index.data(Qt::EditRole);
01344     const QString text = value.toString();
01345     textedit->insertPlainText(text);
01346     textedit->selectAll();
01347 
01348     const QString extension = KMimeType::extractKnownExtension(text);
01349     if (!extension.isEmpty()) {
01350         // The filename contains an extension. Assure that only the filename
01351         // gets selected.
01352         const int selectionLength = text.length() - extension.length() - 1;
01353         QTextCursor cursor = textedit->textCursor();
01354         cursor.movePosition(QTextCursor::StartOfBlock);
01355         cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, selectionLength);
01356         textedit->setTextCursor(cursor);
01357     }
01358 }
01359 
01360 
01361 void KFileItemDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
01362 {
01363     KTextEdit *textedit = qobject_cast<KTextEdit*>(editor);
01364     Q_ASSERT(textedit != 0);
01365 
01366     model->setData(index, textedit->toPlainText(), Qt::EditRole);
01367 }
01368 
01369 
01370 void KFileItemDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option,
01371                                              const QModelIndex &index) const
01372 {
01373     QStyleOptionViewItemV4 opt(option);
01374     d->initStyleOption(&opt, index);
01375     d->setActiveMargins(d->verticalLayout(opt) ? Qt::Vertical : Qt::Horizontal);
01376 
01377     QRect r = d->labelRectangle(opt);
01378 
01379     // Use the full available width for the editor when maximumSize is set
01380     if (d->verticalLayout(option) && !d->maximumSize.isEmpty()) {
01381         int diff = qMax(r.width(), d->maximumSize.width()) - r.width();
01382         if (diff > 1)
01383             r.adjust(-(diff / 2), 0, diff / 2, 0);
01384     }
01385 
01386     KTextEdit *textedit = qobject_cast<KTextEdit*>(editor);
01387     Q_ASSERT(textedit != 0);
01388     const int frame = textedit->frameWidth();
01389     r.adjust(-frame, -frame, frame, frame);
01390 
01391     editor->setGeometry(r);
01392 }
01393 
01394 
01395 bool KFileItemDelegate::helpEvent(QHelpEvent *event, QAbstractItemView *view, const QStyleOptionViewItem &option,
01396                                   const QModelIndex &index)
01397 {
01398     Q_UNUSED(event)
01399     Q_UNUSED(view)
01400 
01401     // if the tooltip information the model keeps is different from the display information,
01402     // show it always
01403     const QVariant toolTip = index.data(Qt::ToolTipRole);
01404 
01405     if (!toolTip.isValid()) {
01406         return false;
01407     }
01408 
01409     if (index.data() != toolTip) {
01410         return QAbstractItemDelegate::helpEvent(event, view, option, index);
01411     }
01412 
01413     if (!d->showToolTipWhenElided) {
01414         return false;
01415     }
01416 
01417     // in the case the tooltip information is the same as the display information,
01418     // show it only in the case the display information is elided
01419     QStyleOptionViewItemV4 opt(option);
01420     d->initStyleOption(&opt, index);
01421     d->setActiveMargins(d->verticalLayout(opt) ? Qt::Vertical : Qt::Horizontal);
01422 
01423     QTextLayout labelLayout;
01424     QTextLayout infoLayout;
01425     QRect textBoundingRect;
01426     d->layoutTextItems(opt, index, &labelLayout, &infoLayout, &textBoundingRect);
01427     const QString elidedText = d->elidedText(labelLayout, opt, textBoundingRect.size());
01428 
01429     if (elidedText != d->display(index)) {
01430         return QAbstractItemDelegate::helpEvent(event, view, option, index);
01431     }
01432 
01433     return false;
01434 }
01435 
01436 QRegion KFileItemDelegate::shape(const QStyleOptionViewItem &option, const QModelIndex &index)
01437 {
01438     QStyleOptionViewItemV4 opt(option);
01439     d->initStyleOption(&opt, index);
01440     d->setActiveMargins(d->verticalLayout(opt) ? Qt::Vertical : Qt::Horizontal);
01441 
01442     QTextLayout labelLayout;
01443     QTextLayout infoLayout;
01444     QRect textBoundingRect;
01445     d->layoutTextItems(opt, index, &labelLayout, &infoLayout, &textBoundingRect);
01446 
01447     const QPoint pos = d->iconPosition(opt);
01448     QRect iconRect = QRect(pos, opt.icon.actualSize(opt.decorationSize));
01449 
01450     // Extend the icon rect so it touches the text rect
01451     switch (opt.decorationPosition)
01452     {
01453     case QStyleOptionViewItem::Top:
01454         if (iconRect.width() < textBoundingRect.width())
01455             iconRect.setBottom(textBoundingRect.top());
01456         else
01457             textBoundingRect.setTop(iconRect.bottom());
01458         break;
01459     case QStyleOptionViewItem::Bottom:
01460         if (iconRect.width() < textBoundingRect.width())
01461             iconRect.setTop(textBoundingRect.bottom());
01462         else
01463             textBoundingRect.setBottom(iconRect.top());
01464         break;
01465     case QStyleOptionViewItem::Left:
01466         iconRect.setRight(textBoundingRect.left());
01467         break;
01468     case QStyleOptionViewItem::Right:
01469         iconRect.setLeft(textBoundingRect.right());
01470         break;
01471     }
01472 
01473     QRegion region;
01474     region += iconRect;
01475     region += textBoundingRect;
01476     return region;
01477 }
01478 
01479 bool KFileItemDelegate::eventFilter(QObject *object, QEvent *event)
01480 {
01481     KTextEdit *editor = qobject_cast<KTextEdit*>(object);
01482     if (!editor)
01483         return false;
01484 
01485     switch (event->type())
01486     {
01487     case QEvent::KeyPress:
01488     {
01489         QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
01490         switch (keyEvent->key())
01491         {
01492         case Qt::Key_Tab:
01493         case Qt::Key_Backtab:
01494             emit commitData(editor);
01495             emit closeEditor(editor, NoHint);
01496             return true;
01497 
01498         case Qt::Key_Enter:
01499         case Qt::Key_Return:
01500             if (editor->toPlainText().isEmpty())
01501                 return true; // So a newline doesn't get inserted
01502 
01503             emit commitData(editor);
01504             emit closeEditor(editor, SubmitModelCache);
01505             return true;
01506 
01507         case Qt::Key_Escape:
01508             emit closeEditor(editor, RevertModelCache);
01509             return true;
01510 
01511         default:
01512             return false;
01513         } // switch (keyEvent->key())
01514     } // case QEvent::KeyPress
01515 
01516     case QEvent::FocusOut:
01517     {
01518         emit commitData(editor);
01519         emit closeEditor(editor, NoHint);
01520         return true;
01521     }
01522 
01523     default:
01524         return false;
01525     } // switch (event->type())
01526 }
01527 
01528 
01529 #include "kfileitemdelegate.moc"
01530 
01531 
01532 // kate: space-indent on; indent-width 4; replace-tabs on;

KIO

Skip menu "KIO"
  • Main Page
  • Namespace List
  • Class Hierarchy
  • Alphabetical List
  • Class List
  • File List
  • Namespace Members
  • Class Members
  • Related Pages

kdelibs

Skip menu "kdelibs"
  • DNSSD
  • Interfaces
  •   KHexEdit
  •   KMediaPlayer
  •   KSpeech
  •   KTextEditor
  • Kate
  • kconf_update
  • KDE3Support
  •   KUnitTest
  • KDECore
  • KDED
  • KDEsu
  • KDEUI
  • KDocTools
  • KFile
  • KHTML
  • KImgIO
  • KInit
  • kio
  • KIOSlave
  • KJS
  •   KJS-API
  •   WTF
  • kjsembed
  • KNewStuff
  • KParts
  • KPty
  • Kross
  • KUtils
  • Nepomuk
  • Plasma
  • Solid
  • Sonnet
  • ThreadWeaver
Generated for kdelibs by doxygen 1.6.1
This website is maintained by Adriaan de Groot and Allen Winter.
KDE® and the K Desktop Environment® logo are registered trademarks of KDE e.V. | Legal