• Skip to content
  • Skip to link menu
KDE 4.6 API Reference
  • KDE API Reference
  • kdelibs
  • KDE Home
  • Contact Us
 

KIO

kfileitemdelegate.cpp

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