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;
KDE 4.6 API Reference