00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022 #include "kfileitemdelegate.h"
00023 #include "imagefilter_p.h"
00024
00025 #include <config.h>
00026
00027 #include <QApplication>
00028 #include <QStyle>
00029 #include <QModelIndex>
00030 #include <QPainter>
00031 #include <QCache>
00032 #include <QImage>
00033 #include <QPainterPath>
00034 #include <QTextLayout>
00035 #include <QListView>
00036 #include <QPaintEngine>
00037 #include <qmath.h>
00038
00039 #include <kglobal.h>
00040 #include <klocale.h>
00041 #include <kiconloader.h>
00042 #include <kiconeffect.h>
00043 #include <kdirmodel.h>
00044 #include <kfileitem.h>
00045 #include <kcolorscheme.h>
00046 #include <kglobalsettings.h>
00047 #include <ktextedit.h>
00048
00049 #include "delegateanimationhandler_p.h"
00050
00051 #if defined(Q_WS_X11) && defined(HAVE_XRENDER)
00052 # include <X11/Xlib.h>
00053 # include <X11/extensions/Xrender.h>
00054 # include <QX11Info>
00055 # undef KeyPress
00056 # undef FocusOut
00057 #endif
00058
00059
00060 struct Margin
00061 {
00062 int left, right, top, bottom;
00063 };
00064
00065
00066 class KFileItemDelegate::Private
00067 {
00068 public:
00069 enum MarginType { ItemMargin = 0, TextMargin, IconMargin, NMargins };
00070
00071 Private(KFileItemDelegate *parent);
00072 ~Private() {}
00073
00074 QSize decorationSizeHint(const QStyleOptionViewItemV4 &option, const QModelIndex &index) const;
00075 QSize displaySizeHint(const QStyleOptionViewItemV4 &option, const QModelIndex &index) const;
00076 QString replaceNewlines(const QString &string) const;
00077 inline KFileItem fileItem(const QModelIndex &index) const;
00078 QString elidedText(QTextLayout &layout, const QStyleOptionViewItemV4 &option, const QSize &maxSize) const;
00079 QSize layoutText(QTextLayout &layout, const QStyleOptionViewItemV4 &option,
00080 const QString &text, const QSize &constraints) const;
00081 QSize layoutText(QTextLayout &layout, const QString &text, int maxWidth) const;
00082 inline void setLayoutOptions(QTextLayout &layout, const QStyleOptionViewItemV4 &options) const;
00083 inline bool verticalLayout(const QStyleOptionViewItemV4 &option) const;
00084 inline QBrush brush(const QVariant &value, const QStyleOptionViewItemV4 &option) const;
00085 QBrush foregroundBrush(const QStyleOptionViewItemV4 &option, const QModelIndex &index) const;
00086 inline void setActiveMargins(Qt::Orientation layout);
00087 void setVerticalMargin(MarginType type, int left, int right, int top, int bottom);
00088 void setHorizontalMargin(MarginType type, int left, int right, int top, int bottom);
00089 inline void setVerticalMargin(MarginType type, int hor, int ver);
00090 inline void setHorizontalMargin(MarginType type, int hor, int ver);
00091 inline QRect addMargin(const QRect &rect, MarginType type) const;
00092 inline QRect subtractMargin(const QRect &rect, MarginType type) const;
00093 inline QSize addMargin(const QSize &size, MarginType type) const;
00094 inline QSize subtractMargin(const QSize &size, MarginType type) const;
00095 QString itemSize(const QModelIndex &index, const KFileItem &item) const;
00096 QString information(const QStyleOptionViewItemV4 &option, const QModelIndex &index, const KFileItem &item) const;
00097 bool isListView(const QStyleOptionViewItemV4 &option) const;
00098 QString display(const QModelIndex &index) const;
00099 QIcon decoration(const QStyleOptionViewItemV4 &option, const QModelIndex &index) const;
00100 QPoint iconPosition(const QStyleOptionViewItemV4 &option) const;
00101 QRect labelRectangle(const QStyleOptionViewItemV4 &option) const;
00102 void layoutTextItems(const QStyleOptionViewItemV4 &option, const QModelIndex &index,
00103 QTextLayout *labelLayout, QTextLayout *infoLayout, QRect *textBoundingRect) const;
00104 void drawTextItems(QPainter *painter, const QTextLayout &labelLayout, const QTextLayout &infoLayout,
00105 const QRect &textBoundingRect) const;
00106 KIO::AnimationState *animationState(const QStyleOptionViewItemV4 &option, const QModelIndex &index,
00107 const QAbstractItemView *view) const;
00108 QPixmap applyHoverEffect(const QPixmap &icon) const;
00109 QPixmap transition(const QPixmap &from, const QPixmap &to, qreal amount) const;
00110 void initStyleOption(QStyleOptionViewItemV4 *option, const QModelIndex &index) const;
00111 void drawFocusRect(QPainter *painter, const QStyleOptionViewItemV4 &option, const QRect &rect) const;
00112
00113 public:
00114 KFileItemDelegate::InformationList informationList;
00115 QColor shadowColor;
00116 QPointF shadowOffset;
00117 qreal shadowBlur;
00118 QSize maximumSize;
00119
00120 private:
00121 KFileItemDelegate * const q;
00122 KIO::DelegateAnimationHandler *animationHandler;
00123 Margin verticalMargin[NMargins];
00124 Margin horizontalMargin[NMargins];
00125 Margin *activeMargins;
00126 };
00127
00128
00129 KFileItemDelegate::Private::Private(KFileItemDelegate *parent)
00130 : shadowColor(Qt::transparent), shadowOffset(1, 1), shadowBlur(2), maximumSize(0, 0),
00131 q(parent), animationHandler(new KIO::DelegateAnimationHandler(parent))
00132 {
00133 }
00134
00135
00136 void KFileItemDelegate::Private::setActiveMargins(Qt::Orientation layout)
00137 {
00138 activeMargins = (layout == Qt::Horizontal ?
00139 horizontalMargin : verticalMargin);
00140 }
00141
00142
00143 void KFileItemDelegate::Private::setVerticalMargin(MarginType type, int left, int top, int right, int bottom)
00144 {
00145 verticalMargin[type].left = left;
00146 verticalMargin[type].right = right;
00147 verticalMargin[type].top = top;
00148 verticalMargin[type].bottom = bottom;
00149 }
00150
00151
00152 void KFileItemDelegate::Private::setHorizontalMargin(MarginType type, int left, int top, int right, int bottom)
00153 {
00154 horizontalMargin[type].left = left;
00155 horizontalMargin[type].right = right;
00156 horizontalMargin[type].top = top;
00157 horizontalMargin[type].bottom = bottom;
00158 }
00159
00160
00161 void KFileItemDelegate::Private::setVerticalMargin(MarginType type, int horizontal, int vertical)
00162 {
00163 setVerticalMargin(type, horizontal, vertical, horizontal, vertical);
00164 }
00165
00166
00167 void KFileItemDelegate::Private::setHorizontalMargin(MarginType type, int horizontal, int vertical)
00168 {
00169 setHorizontalMargin(type, horizontal, vertical, horizontal, vertical);
00170 }
00171
00172
00173 QRect KFileItemDelegate::Private::addMargin(const QRect &rect, MarginType type) const
00174 {
00175 const Margin &m = activeMargins[type];
00176 return rect.adjusted(-m.left, -m.top, m.right, m.bottom);
00177 }
00178
00179
00180 QRect KFileItemDelegate::Private::subtractMargin(const QRect &rect, MarginType type) const
00181 {
00182 const Margin &m = activeMargins[type];
00183 return rect.adjusted(m.left, m.top, -m.right, -m.bottom);
00184 }
00185
00186
00187 QSize KFileItemDelegate::Private::addMargin(const QSize &size, MarginType type) const
00188 {
00189 const Margin &m = activeMargins[type];
00190 return QSize(size.width() + m.left + m.right, size.height() + m.top + m.bottom);
00191 }
00192
00193
00194 QSize KFileItemDelegate::Private::subtractMargin(const QSize &size, MarginType type) const
00195 {
00196 const Margin &m = activeMargins[type];
00197 return QSize(size.width() - m.left - m.right, size.height() - m.top - m.bottom);
00198 }
00199
00200
00201
00202 QString KFileItemDelegate::Private::itemSize(const QModelIndex &index, const KFileItem &item) const
00203 {
00204
00205 if (item.isFile())
00206 return KGlobal::locale()->formatByteSize(item.size());
00207
00208
00209 const QVariant value = index.data(KDirModel::ChildCountRole);
00210 const int count = value.type() == QVariant::Int ? value.toInt() : KDirModel::ChildCountUnknown;
00211
00212 if (count == KDirModel::ChildCountUnknown) {
00213
00214
00215
00216 return QString();
00217 }
00218
00219 return i18ncp("Items in a folder", "1 item", "%1 items", count);
00220 }
00221
00222
00223
00224 QString KFileItemDelegate::Private::information(const QStyleOptionViewItemV4 &option, const QModelIndex &index,
00225 const KFileItem &item) const
00226 {
00227 QString string;
00228
00229 if (informationList.isEmpty() || item.isNull() || !isListView(option))
00230 return string;
00231
00232 foreach (KFileItemDelegate::Information info, informationList)
00233 {
00234 if (info == KFileItemDelegate::NoInformation)
00235 continue;
00236
00237 if (!string.isEmpty())
00238 string += QChar::LineSeparator;
00239
00240 switch (info)
00241 {
00242 case KFileItemDelegate::Size:
00243 string += itemSize(index, item);
00244 break;
00245
00246 case KFileItemDelegate::Permissions:
00247 string += item.permissionsString();
00248 break;
00249
00250 case KFileItemDelegate::OctalPermissions:
00251 string += QString('0') + QString::number(item.permissions(), 8);
00252 break;
00253
00254 case KFileItemDelegate::Owner:
00255 string += item.user();
00256 break;
00257
00258 case KFileItemDelegate::OwnerAndGroup:
00259 string += item.user() + ':' + item.group();
00260 break;
00261
00262 case KFileItemDelegate::CreationTime:
00263 string += item.timeString(KFileItem::CreationTime);
00264 break;
00265
00266 case KFileItemDelegate::ModificationTime:
00267 string += item.timeString(KFileItem::ModificationTime);
00268 break;
00269
00270 case KFileItemDelegate::AccessTime:
00271 string += item.timeString(KFileItem::AccessTime);
00272 break;
00273
00274 case KFileItemDelegate::MimeType:
00275 string += item.isMimeTypeKnown() ? item.mimetype() : i18nc("@info mimetype","Unknown");
00276 break;
00277
00278 case KFileItemDelegate::FriendlyMimeType:
00279 string += item.isMimeTypeKnown() ? item.mimeComment() : i18nc("@info mimetype","Unknown");
00280 break;
00281
00282 default:
00283 break;
00284 }
00285 }
00286
00287 return string;
00288 }
00289
00290
00291
00292 KFileItem KFileItemDelegate::Private::fileItem(const QModelIndex &index) const
00293 {
00294 const QVariant value = index.data(KDirModel::FileItemRole);
00295 return qvariant_cast<KFileItem>(value);
00296 }
00297
00298
00299
00300 QString KFileItemDelegate::Private::replaceNewlines(const QString &text) const
00301 {
00302 QString string = text;
00303 const QChar newline = QLatin1Char('\n');
00304
00305 for (int i = 0; i < string.length(); i++)
00306 if (string[i] == newline)
00307 string[i] = QChar::LineSeparator;
00308
00309 return string;
00310 }
00311
00312
00313
00314 QSize KFileItemDelegate::Private::layoutText(QTextLayout &layout, const QStyleOptionViewItemV4 &option,
00315 const QString &text, const QSize &constraints) const
00316 {
00317 const QSize size = layoutText(layout, text, constraints.width());
00318
00319 if (size.width() > constraints.width() || size.height() > constraints.height())
00320 {
00321 const QString elided = elidedText(layout, option, constraints);
00322 return layoutText(layout, elided, constraints.width());
00323 }
00324
00325 return size;
00326 }
00327
00328
00329
00330 QSize KFileItemDelegate::Private::layoutText(QTextLayout &layout, const QString &text, int maxWidth) const
00331 {
00332 QFontMetrics metrics(layout.font());
00333 int leading = metrics.leading();
00334 int height = 0;
00335 qreal widthUsed = 0;
00336 QTextLine line;
00337
00338 layout.setText(text);
00339
00340 layout.beginLayout();
00341 while ((line = layout.createLine()).isValid())
00342 {
00343 line.setLineWidth(int(maxWidth));
00344 height += leading;
00345 line.setPosition(QPoint(0, height));
00346 height += int(line.height());
00347 widthUsed = qMax(widthUsed, line.naturalTextWidth());
00348 }
00349 layout.endLayout();
00350
00351 return QSize(int(widthUsed), height);
00352 }
00353
00354
00355
00356
00357
00358
00359 QString KFileItemDelegate::Private::elidedText(QTextLayout &layout, const QStyleOptionViewItemV4 &option,
00360 const QSize &size) const
00361 {
00362 const QString text = layout.text();
00363 int maxWidth = size.width();
00364 int maxHeight = size.height();
00365 qreal height = 0;
00366 bool wrapText = (option.features & QStyleOptionViewItemV2::WrapText);
00367
00368
00369 if (!wrapText && text.indexOf(QChar::LineSeparator) == -1)
00370 return option.fontMetrics.elidedText(text, option.textElideMode, maxWidth);
00371
00372
00373 QString elided;
00374 elided.reserve(text.length());
00375
00376 for (int i = 0; i < layout.lineCount(); i++)
00377 {
00378 QTextLine line = layout.lineAt(i);
00379 int start = line.textStart();
00380 int length = line.textLength();
00381
00382 height += option.fontMetrics.leading();
00383 if (height + line.height() + option.fontMetrics.lineSpacing() > maxHeight)
00384 {
00385
00386
00387 if (line.naturalTextWidth() < maxWidth && text[start + length - 1] == QChar::LineSeparator)
00388 elided += text.mid(start, length - 1);
00389 else
00390 elided += option.fontMetrics.elidedText(text.mid(start), option.textElideMode, maxWidth);
00391 break;
00392 }
00393 else if (line.naturalTextWidth() > maxWidth)
00394 {
00395 elided += option.fontMetrics.elidedText(text.mid(start, length), option.textElideMode, maxWidth);
00396 if (!elided.endsWith(QChar::LineSeparator))
00397 elided += QChar::LineSeparator;
00398 }
00399 else
00400 elided += text.mid(start, length);
00401
00402 height += line.height();
00403 }
00404
00405 return elided;
00406 }
00407
00408
00409 void KFileItemDelegate::Private::setLayoutOptions(QTextLayout &layout, const QStyleOptionViewItemV4 &option) const
00410 {
00411 QTextOption textoption;
00412 textoption.setTextDirection(option.direction);
00413 textoption.setAlignment(QStyle::visualAlignment(option.direction, option.displayAlignment));
00414 textoption.setWrapMode((option.features & QStyleOptionViewItemV2::WrapText) ?
00415 QTextOption::WrapAtWordBoundaryOrAnywhere : QTextOption::NoWrap);
00416
00417 layout.setFont(option.font);
00418 layout.setTextOption(textoption);
00419 }
00420
00421
00422 QSize KFileItemDelegate::Private::displaySizeHint(const QStyleOptionViewItemV4 &option,
00423 const QModelIndex &index) const
00424 {
00425 QString label = option.text;
00426 int maxWidth = 0;
00427 if (maximumSize.isEmpty()) {
00428 maxWidth = verticalLayout(option) && (option.features & QStyleOptionViewItemV2::WrapText)
00429 ? option.decorationSize.width() + 10 : 32757;
00430 }
00431 else {
00432 const Margin &itemMargin = activeMargins[ItemMargin];
00433 const Margin &textMargin = activeMargins[TextMargin];
00434 maxWidth = maximumSize.width() -
00435 (itemMargin.left + itemMargin.right) -
00436 (textMargin.left + textMargin.right);
00437 }
00438
00439 KFileItem item = fileItem(index);
00440
00441
00442
00443 const QString info = information(option, index, item);
00444 if (!info.isEmpty())
00445 label += QString(QChar::LineSeparator) + info;
00446
00447 QTextLayout layout;
00448 setLayoutOptions(layout, option);
00449
00450 QSize size = layoutText(layout, label, maxWidth);
00451 return addMargin(size, TextMargin);
00452 }
00453
00454
00455 QSize KFileItemDelegate::Private::decorationSizeHint(const QStyleOptionViewItemV4 &option,
00456 const QModelIndex &index) const
00457 {
00458 Q_UNUSED(index)
00459
00460 QSize iconSize = option.icon.actualSize(option.decorationSize);
00461 if (iconSize.width() < option.decorationSize.width())
00462 iconSize.rwidth() = qMin(iconSize.width() + 10, option.decorationSize.width());
00463 if (iconSize.height() < option.decorationSize.height())
00464 iconSize.rheight() = option.decorationSize.height();
00465
00466 return addMargin(iconSize, IconMargin);
00467 }
00468
00469
00470 bool KFileItemDelegate::Private::verticalLayout(const QStyleOptionViewItemV4 &option) const
00471 {
00472 return (option.decorationPosition == QStyleOptionViewItem::Top ||
00473 option.decorationPosition == QStyleOptionViewItem::Bottom);
00474 }
00475
00476
00477
00478 QBrush KFileItemDelegate::Private::brush(const QVariant &value, const QStyleOptionViewItemV4 &option) const
00479 {
00480 if (value.userType() == qMetaTypeId<KStatefulBrush>())
00481 return qvariant_cast<KStatefulBrush>(value).brush(option.palette);
00482 switch (value.type())
00483 {
00484 case QVariant::Color:
00485 return QBrush(qvariant_cast<QColor>(value));
00486
00487 case QVariant::Brush:
00488 return qvariant_cast<QBrush>(value);
00489
00490 default:
00491 return QBrush(Qt::NoBrush);
00492 }
00493 }
00494
00495
00496 QBrush KFileItemDelegate::Private::foregroundBrush(const QStyleOptionViewItemV4 &option, const QModelIndex &index) const
00497 {
00498
00499 if (option.state & QStyle::State_Selected)
00500 return option.palette.brush(QPalette::HighlightedText);
00501
00502
00503 const QVariant value = index.data(Qt::ForegroundRole);
00504 if (value.isValid())
00505 return brush(value, option);
00506
00507 return option.palette.brush(QPalette::Text);
00508 }
00509
00510
00511 bool KFileItemDelegate::Private::isListView(const QStyleOptionViewItemV4 &option) const
00512 {
00513 if (qobject_cast<const QListView*>(option.widget) || verticalLayout(option))
00514 return true;
00515
00516 return false;
00517 }
00518
00519
00520 QPixmap KFileItemDelegate::Private::applyHoverEffect(const QPixmap &icon) const
00521 {
00522 KIconEffect *effect = KIconLoader::global()->iconEffect();
00523
00524
00525
00526
00527 if (effect->hasEffect(KIconLoader::Desktop, KIconLoader::ActiveState))
00528 return effect->apply(icon, KIconLoader::Desktop, KIconLoader::ActiveState);
00529
00530 return icon;
00531 }
00532
00533
00534 KIO::AnimationState *KFileItemDelegate::Private::animationState(const QStyleOptionViewItemV4 &option,
00535 const QModelIndex &index,
00536 const QAbstractItemView *view) const
00537 {
00538 if (!(KGlobalSettings::graphicEffectsLevel() & KGlobalSettings::SimpleAnimationEffects)) {
00539 return NULL;
00540 }
00541
00542 if (index.column() == KDirModel::Name)
00543 return animationHandler->animationState(option, index, view);
00544
00545 return NULL;
00546 }
00547
00548
00549 QPixmap KFileItemDelegate::Private::transition(const QPixmap &from, const QPixmap &to, qreal amount) const
00550 {
00551 int value = int(0xff * amount);
00552
00553 if (value == 0)
00554 return from;
00555
00556 if (value == 1)
00557 return to;
00558
00559 QColor color;
00560 color.setAlphaF(amount);
00561
00562
00563 if (from.paintEngine()->hasFeature(QPaintEngine::PorterDuff) &&
00564 from.paintEngine()->hasFeature(QPaintEngine::BlendModes))
00565 {
00566 QPixmap under = from;
00567 QPixmap over = to;
00568
00569 QPainter p;
00570 p.begin(&over);
00571 p.setCompositionMode(QPainter::CompositionMode_DestinationIn);
00572 p.fillRect(over.rect(), color);
00573 p.end();
00574
00575 p.begin(&under);
00576 p.setCompositionMode(QPainter::CompositionMode_DestinationOut);
00577 p.fillRect(under.rect(), color);
00578 p.setCompositionMode(QPainter::CompositionMode_Plus);
00579 p.drawPixmap(0, 0, over);
00580 p.end();
00581
00582 return under;
00583 }
00584 #if defined(Q_WS_X11) && defined(HAVE_XRENDER)
00585 else if (from.paintEngine()->hasFeature(QPaintEngine::PorterDuff))
00586 {
00587
00588
00589
00590
00591
00592
00593
00594
00595
00596
00597
00598 QPixmap source(to), destination(from);
00599
00600 source.detach();
00601 destination.detach();
00602
00603 Display *dpy = QX11Info::display();
00604
00605 XRenderPictFormat *format = XRenderFindStandardFormat(dpy, PictStandardA8);
00606 XRenderPictureAttributes pa;
00607 pa.repeat = 1;
00608
00609
00610 Pixmap pixmap = XCreatePixmap(dpy, destination.handle(), 1, 1, 8);
00611 Picture alpha = XRenderCreatePicture(dpy, pixmap, format, CPRepeat, &pa);
00612 XFreePixmap(dpy, pixmap);
00613
00614
00615 XRenderColor xcolor;
00616 xcolor.alpha = quint16(0xffff * amount);
00617 XRenderFillRectangle(dpy, PictOpSrc, alpha, &xcolor, 0, 0, 1, 1);
00618
00619
00620 XRenderComposite(dpy, PictOpOutReverse, alpha, None, destination.x11PictureHandle(),
00621 0, 0, 0, 0, 0, 0, destination.width(), destination.height());
00622
00623
00624 XRenderComposite(dpy, PictOpAdd, source.x11PictureHandle(), alpha,
00625 destination.x11PictureHandle(),
00626 0, 0, 0, 0, 0, 0, destination.width(), destination.height());
00627
00628 XRenderFreePicture(dpy, alpha);
00629 return destination;
00630 }
00631 #endif
00632 else
00633 {
00634
00635 QImage under = from.toImage();
00636 QImage over = to.toImage();
00637
00638 QPainter p;
00639 p.begin(&over);
00640 p.setCompositionMode(QPainter::CompositionMode_DestinationIn);
00641 p.fillRect(over.rect(), color);
00642 p.end();
00643
00644 p.begin(&under);
00645 p.setCompositionMode(QPainter::CompositionMode_DestinationOut);
00646 p.fillRect(under.rect(), color);
00647 p.setCompositionMode(QPainter::CompositionMode_Plus);
00648 p.drawImage(0, 0, over);
00649 p.end();
00650
00651 return QPixmap::fromImage(under);
00652 }
00653 }
00654
00655
00656 void KFileItemDelegate::Private::layoutTextItems(const QStyleOptionViewItemV4 &option, const QModelIndex &index,
00657 QTextLayout *labelLayout, QTextLayout *infoLayout,
00658 QRect *textBoundingRect) const
00659 {
00660 KFileItem item = fileItem(index);
00661 const QString info = information(option, index, item);
00662 bool showInformation = false;
00663
00664 setLayoutOptions(*labelLayout, option);
00665
00666 const QRect textArea = labelRectangle(option);
00667 QRect textRect = subtractMargin(textArea, Private::TextMargin);
00668
00669
00670 QSize maxLabelSize = textRect.size();
00671 QSize maxInfoSize = textRect.size();
00672 QSize labelSize;
00673 QSize infoSize;
00674
00675
00676
00677 if (!info.isEmpty() && textRect.height() >= option.fontMetrics.lineSpacing() * 2)
00678 {
00679 infoLayout->setFont(labelLayout->font());
00680 infoLayout->setTextOption(labelLayout->textOption());
00681
00682 maxLabelSize.rheight() -= option.fontMetrics.lineSpacing();
00683 showInformation = true;
00684 }
00685
00686
00687 labelSize = layoutText(*labelLayout, option, option.text, maxLabelSize);
00688 maxInfoSize.rheight() -= labelSize.height();
00689
00690
00691 if (showInformation)
00692 infoSize = layoutText(*infoLayout, option, info, maxInfoSize);
00693 else
00694 infoSize = QSize(0, 0);
00695
00696
00697 const QSize size(qMax(labelSize.width(), infoSize.width()), labelSize.height() + infoSize.height());
00698 *textBoundingRect = QStyle::alignedRect(option.direction, option.displayAlignment, size, textRect);
00699
00700
00701 labelLayout->setPosition(QPointF(textRect.x(), textBoundingRect->y()));
00702 infoLayout->setPosition(QPointF(textRect.x(), textBoundingRect->y() + labelSize.height()));
00703 }
00704
00705
00706 void KFileItemDelegate::Private::drawTextItems(QPainter *painter, const QTextLayout &labelLayout,
00707 const QTextLayout &infoLayout, const QRect &boundingRect) const
00708 {
00709 if (shadowColor.alpha() > 0)
00710 {
00711 QPixmap pixmap(boundingRect.size());
00712 pixmap.fill(Qt::transparent);
00713
00714 QPainter p(&pixmap);
00715 p.translate(-boundingRect.topLeft());
00716 p.setPen(painter->pen());
00717 labelLayout.draw(&p, QPoint());
00718
00719 if (!infoLayout.text().isEmpty())
00720 {
00721 QColor color = p.pen().color();
00722 color.setAlphaF(0.6);
00723
00724 p.setPen(color);
00725 infoLayout.draw(&p, QPoint());
00726 }
00727 p.end();
00728
00729 int padding = qCeil(shadowBlur);
00730 int blurFactor = qRound(shadowBlur);
00731
00732 QImage image(boundingRect.size() + QSize(padding * 2, padding * 2), QImage::Format_ARGB32_Premultiplied);
00733 image.fill(Qt::transparent);
00734 p.begin(&image);
00735 p.drawImage(padding, padding, pixmap.toImage());
00736 p.end();
00737
00738 KIO::ImageFilter::shadowBlur(image, blurFactor, shadowColor);
00739
00740 painter->drawImage(boundingRect.topLeft() - QPoint(padding, padding) + shadowOffset.toPoint(), image);
00741 painter->drawPixmap(boundingRect.topLeft(), pixmap);
00742 return;
00743 }
00744
00745 labelLayout.draw(painter, QPoint());
00746
00747 if (!infoLayout.text().isEmpty())
00748 {
00749
00750
00751
00752 QColor color = painter->pen().color();
00753 color.setAlphaF(0.6);
00754
00755 painter->setPen(color);
00756 infoLayout.draw(painter, QPoint());
00757 }
00758 }
00759
00760
00761 void KFileItemDelegate::Private::initStyleOption(QStyleOptionViewItemV4 *option,
00762 const QModelIndex &index) const
00763 {
00764 const KFileItem item = fileItem(index);
00765 bool updateFontMetrics = false;
00766
00767
00768 QVariant value = index.data(Qt::FontRole);
00769 if (value.isValid()) {
00770 option->font = qvariant_cast<QFont>(value).resolve(option->font);
00771 updateFontMetrics = true;
00772 }
00773
00774
00775 if (!item.isNull() && item.isLink()) {
00776 option->font.setItalic(true);
00777 updateFontMetrics = true;
00778 }
00779
00780 if (updateFontMetrics)
00781 option->fontMetrics = QFontMetrics(option->font);
00782
00783
00784 value = index.data(Qt::TextAlignmentRole);
00785 if (value.isValid())
00786 option->displayAlignment = Qt::Alignment(value.toInt());
00787
00788 value = index.data(Qt::BackgroundRole);
00789 if (value.isValid())
00790 option->backgroundBrush = brush(value, *option);
00791
00792 option->text = display(index);
00793 if (!option->text.isEmpty())
00794 option->features |= QStyleOptionViewItemV2::HasDisplay;
00795
00796 option->icon = decoration(*option, index);
00797 if (!option->icon.isNull())
00798 option->features |= QStyleOptionViewItemV2::HasDecoration;
00799
00800
00801 option->showDecorationSelected = true;
00802 }
00803
00804
00805
00806
00807
00808
00809
00810
00811
00812 KFileItemDelegate::KFileItemDelegate(QObject *parent)
00813 : QAbstractItemDelegate(parent), d(new Private(this))
00814 {
00815 int focusHMargin = QApplication::style()->pixelMetric(QStyle::PM_FocusFrameHMargin);
00816 int focusVMargin = QApplication::style()->pixelMetric(QStyle::PM_FocusFrameVMargin);
00817
00818
00819 const int textMargin = focusHMargin * 4;
00820 if (QApplication::isRightToLeft())
00821 d->setHorizontalMargin(Private::TextMargin, textMargin, focusVMargin, focusHMargin, focusVMargin);
00822 else
00823 d->setHorizontalMargin(Private::TextMargin, focusHMargin, focusVMargin, textMargin, focusVMargin);
00824
00825 d->setHorizontalMargin(Private::IconMargin, focusHMargin, focusVMargin);
00826 d->setHorizontalMargin(Private::ItemMargin, 0, 0);
00827
00828
00829 d->setVerticalMargin(Private::TextMargin, 6, 2);
00830 d->setVerticalMargin(Private::IconMargin, focusHMargin, focusVMargin);
00831 d->setVerticalMargin(Private::ItemMargin, 0, 0);
00832
00833 setShowInformation(NoInformation);
00834 }
00835
00836
00837 KFileItemDelegate::~KFileItemDelegate()
00838 {
00839 delete d;
00840 }
00841
00842
00843 QSize KFileItemDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
00844 {
00845
00846 const QVariant value = index.data(Qt::SizeHintRole);
00847 if (value.isValid())
00848 return qvariant_cast<QSize>(value);
00849
00850 QStyleOptionViewItemV4 opt(option);
00851 d->initStyleOption(&opt, index);
00852 d->setActiveMargins(d->verticalLayout(opt) ? Qt::Vertical : Qt::Horizontal);
00853
00854 const QSize displaySize = d->displaySizeHint(opt, index);
00855 const QSize decorationSize = d->decorationSizeHint(opt, index);
00856
00857 QSize size;
00858
00859 if (d->verticalLayout(opt))
00860 {
00861 size.rwidth() = qMax(displaySize.width(), decorationSize.width());
00862 size.rheight() = decorationSize.height() + displaySize.height() + 1;
00863 }
00864 else
00865 {
00866 size.rwidth() = decorationSize.width() + displaySize.width() + 1;
00867 size.rheight() = qMax(decorationSize.height(), displaySize.height());
00868 }
00869
00870 size = d->addMargin(size, Private::ItemMargin);
00871 if (!d->maximumSize.isEmpty())
00872 {
00873 size = size.boundedTo(d->maximumSize);
00874 }
00875
00876 return size;
00877 }
00878
00879
00880 QString KFileItemDelegate::Private::display(const QModelIndex &index) const
00881 {
00882 const QVariant value = index.data(Qt::DisplayRole);
00883
00884 switch (value.type())
00885 {
00886 case QVariant::String:
00887 {
00888 if (index.column() == KDirModel::Size)
00889 return itemSize(index, fileItem(index));
00890 else
00891 return replaceNewlines(value.toString());
00892 }
00893
00894 case QVariant::Double:
00895 return KGlobal::locale()->formatNumber(value.toDouble());
00896
00897 case QVariant::Int:
00898 case QVariant::UInt:
00899 return KGlobal::locale()->formatLong(value.toInt());
00900
00901 default:
00902 return QString();
00903 }
00904 }
00905
00906
00907 void KFileItemDelegate::setShowInformation(const InformationList &list)
00908 {
00909 d->informationList = list;
00910 }
00911
00912
00913 void KFileItemDelegate::setShowInformation(Information value)
00914 {
00915 if (value != NoInformation)
00916 d->informationList = InformationList() << value;
00917 else
00918 d->informationList = InformationList();
00919 }
00920
00921
00922 KFileItemDelegate::InformationList KFileItemDelegate::showInformation() const
00923 {
00924 return d->informationList;
00925 }
00926
00927
00928 void KFileItemDelegate::setShadowColor(const QColor &color)
00929 {
00930 d->shadowColor = color;
00931 }
00932
00933
00934 QColor KFileItemDelegate::shadowColor() const
00935 {
00936 return d->shadowColor;
00937 }
00938
00939
00940 void KFileItemDelegate::setShadowOffset(const QPointF &offset)
00941 {
00942 d->shadowOffset = offset;
00943 }
00944
00945
00946 QPointF KFileItemDelegate::shadowOffset() const
00947 {
00948 return d->shadowOffset;
00949 }
00950
00951
00952 void KFileItemDelegate::setShadowBlur(qreal factor)
00953 {
00954 d->shadowBlur = factor;
00955 }
00956
00957
00958 qreal KFileItemDelegate::shadowBlur() const
00959 {
00960 return d->shadowBlur;
00961 }
00962
00963
00964 void KFileItemDelegate::setMaximumSize(const QSize &size)
00965 {
00966 d->maximumSize = size;
00967 }
00968
00969
00970 QSize KFileItemDelegate::maximumSize() const
00971 {
00972 return d->maximumSize;
00973 }
00974
00975
00976 QIcon KFileItemDelegate::Private::decoration(const QStyleOptionViewItemV4 &option, const QModelIndex &index) const
00977 {
00978 const QVariant value = index.data(Qt::DecorationRole);
00979 QIcon icon;
00980
00981 switch (value.type())
00982 {
00983 case QVariant::Icon:
00984 icon = qvariant_cast<QIcon>(value);
00985 break;
00986
00987 case QVariant::Pixmap:
00988 icon.addPixmap(qvariant_cast<QPixmap>(value));
00989 break;
00990
00991 case QVariant::Color: {
00992 QPixmap pixmap(option.decorationSize);
00993 pixmap.fill(qvariant_cast<QColor>(value));
00994 icon.addPixmap(pixmap);
00995 break;
00996 }
00997
00998 default:
00999 break;
01000 }
01001
01002 return icon;
01003 }
01004
01005
01006 QRect KFileItemDelegate::Private::labelRectangle(const QStyleOptionViewItemV4 &option) const
01007 {
01008 if (option.icon.isNull())
01009 return subtractMargin(option.rect, Private::ItemMargin);
01010
01011 const QSize decoSize = addMargin(option.decorationSize, Private::IconMargin);
01012 const QRect itemRect = subtractMargin(option.rect, Private::ItemMargin);
01013 QRect textArea(QPoint(0, 0), itemRect.size());
01014
01015 switch (option.decorationPosition)
01016 {
01017 case QStyleOptionViewItem::Top:
01018 textArea.setTop(decoSize.height() + 1);
01019 break;
01020
01021 case QStyleOptionViewItem::Bottom:
01022 textArea.setBottom(itemRect.height() - decoSize.height() - 1);
01023 break;
01024
01025 case QStyleOptionViewItem::Left:
01026 textArea.setLeft(decoSize.width() + 1);
01027 break;
01028
01029 case QStyleOptionViewItem::Right:
01030 textArea.setRight(itemRect.width() - decoSize.width() - 1);
01031 break;
01032 }
01033
01034 textArea.translate(itemRect.topLeft());
01035 return QStyle::visualRect(option.direction, option.rect, textArea);
01036 }
01037
01038
01039 QPoint KFileItemDelegate::Private::iconPosition(const QStyleOptionViewItemV4 &option) const
01040 {
01041 const QRect itemRect = subtractMargin(option.rect, Private::ItemMargin);
01042 Qt::Alignment alignment;
01043
01044
01045 switch (option.decorationPosition)
01046 {
01047 case QStyleOptionViewItem::Top:
01048 alignment = Qt::AlignHCenter | Qt::AlignTop;
01049 break;
01050
01051 case QStyleOptionViewItem::Bottom:
01052 alignment = Qt::AlignHCenter | Qt::AlignBottom;
01053 break;
01054
01055 case QStyleOptionViewItem::Left:
01056 alignment = Qt::AlignVCenter | Qt::AlignLeft;
01057 break;
01058
01059 case QStyleOptionViewItem::Right:
01060 alignment = Qt::AlignVCenter | Qt::AlignRight;
01061 break;
01062 }
01063
01064
01065 const QSize size = addMargin(option.decorationSize, Private::IconMargin);
01066 const QRect rect = QStyle::alignedRect(option.direction, alignment, size, itemRect);
01067
01068
01069 QRect iconRect = QRect(QPoint(), option.icon.actualSize(option.decorationSize));
01070 iconRect.moveCenter(rect.center());
01071
01072 return iconRect.topLeft();
01073 }
01074
01075
01076 void KFileItemDelegate::Private::drawFocusRect(QPainter *painter, const QStyleOptionViewItemV4 &option,
01077 const QRect &rect) const
01078 {
01079 if (!(option.state & QStyle::State_HasFocus))
01080 return;
01081
01082 QStyleOptionFocusRect opt;
01083 opt.direction = option.direction;
01084 opt.fontMetrics = option.fontMetrics;
01085 opt.palette = option.palette;
01086 opt.rect = rect;
01087 opt.state = option.state | QStyle::State_KeyboardFocusChange | QStyle::State_Item;
01088 opt.backgroundColor = option.palette.color(option.state & QStyle::State_Selected ?
01089 QPalette::Highlight : QPalette::Base);
01090
01091
01092 painter->setRenderHint(QPainter::Antialiasing, false);
01093
01094 QStyle *style = option.widget ? option.widget->style() : QApplication::style();
01095 style->drawPrimitive(QStyle::PE_FrameFocusRect, &opt, painter, option.widget);
01096
01097 painter->setRenderHint(QPainter::Antialiasing);
01098 }
01099
01100
01101 void KFileItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option,
01102 const QModelIndex &index) const
01103 {
01104 if (!index.isValid())
01105 return;
01106
01107 QStyleOptionViewItemV4 opt(option);
01108 d->initStyleOption(&opt, index);
01109
01110
01111 if (index.column() > 0)
01112 opt.state &= ~QStyle::State_MouseOver;
01113
01114 const QAbstractItemView *view = qobject_cast<const QAbstractItemView*>(opt.widget);
01115
01116
01117
01118
01119 KIO::AnimationState *state = d->animationState(opt, index, view);
01120 KIO::CachedRendering *cache = 0;
01121 qreal progress = ((opt.state & QStyle::State_MouseOver) &&
01122 index.column() == KDirModel::Name) ? 1.0 : 0.0;
01123
01124 if (state)
01125 {
01126 cache = state->cachedRendering();
01127 progress = state->hoverProgress();
01128
01129
01130 opt.state &= ~QStyle::State_MouseOver;
01131
01132
01133 if (cache)
01134 {
01135 if (cache->checkValidity(opt.state) && cache->regular.size() == opt.rect.size())
01136 {
01137 const QPixmap pixmap = d->transition(cache->regular, cache->hover, progress);
01138 painter->drawPixmap(option.rect.topLeft(), pixmap);
01139 return;
01140 }
01141
01142
01143 state->setCachedRendering(0);
01144 delete cache;
01145 cache = 0;
01146 }
01147 }
01148
01149 d->setActiveMargins(d->verticalLayout(opt) ? Qt::Vertical : Qt::Horizontal);
01150
01151
01152
01153
01154 const QPoint iconPos = d->iconPosition(opt);
01155 QIcon::Mode iconMode = option.state & QStyle::State_Enabled ? QIcon::Normal : QIcon::Disabled;
01156 QIcon::State iconState = option.state & QStyle::State_Open ? QIcon::On : QIcon::Off;
01157 QPixmap icon = opt.icon.pixmap(opt.decorationSize, iconMode, iconState);
01158 const QPen pen = QPen(d->foregroundBrush(opt, index), 0);
01159
01161
01162
01163 QTextLayout labelLayout, infoLayout;
01164 QRect textBoundingRect;
01165
01166 d->layoutTextItems(opt, index, &labelLayout, &infoLayout, &textBoundingRect);
01167
01168 QStyle *style = opt.widget ? opt.widget->style() : QApplication::style();
01169
01170 int focusHMargin = style->pixelMetric(QStyle::PM_FocusFrameHMargin);
01171 int focusVMargin = style->pixelMetric(QStyle::PM_FocusFrameVMargin);
01172 QRect focusRect = textBoundingRect.adjusted(-focusHMargin, -focusVMargin,
01173 +focusHMargin, +focusVMargin);
01174
01175
01176
01177
01178
01179 if (state && progress < 1)
01180 {
01181 cache = new KIO::CachedRendering(opt.state, option.rect.size());
01182
01183 QPainter p;
01184 p.begin(&cache->regular);
01185 p.translate(-option.rect.topLeft());
01186 p.setRenderHint(QPainter::Antialiasing);
01187 p.setPen(pen);
01188 style->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, &p, opt.widget);
01189 p.drawPixmap(iconPos, icon);
01190 d->drawTextItems(&p, labelLayout, infoLayout, textBoundingRect);
01191 d->drawFocusRect(&p, opt, focusRect);
01192 p.end();
01193
01194 opt.state |= QStyle::State_MouseOver;
01195 icon = d->applyHoverEffect(icon);
01196
01197 p.begin(&cache->hover);
01198 p.translate(-option.rect.topLeft());
01199 p.setRenderHint(QPainter::Antialiasing);
01200 p.setPen(pen);
01201 style->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, &p, opt.widget);
01202 p.drawPixmap(iconPos, icon);
01203 d->drawTextItems(&p, labelLayout, infoLayout, textBoundingRect);
01204 d->drawFocusRect(&p, opt, focusRect);
01205 p.end();
01206
01207 state->setCachedRendering(cache);
01208
01209 const QPixmap pixmap = d->transition(cache->regular, cache->hover, progress);
01210 painter->drawPixmap(option.rect.topLeft(), pixmap);
01211 return;
01212 }
01213
01214
01215
01216
01217 painter->save();
01218 painter->setRenderHint(QPainter::Antialiasing);
01219 painter->setPen(pen);
01220
01221 if (progress > 0 && !(opt.state & QStyle::State_MouseOver))
01222 {
01223 opt.state |= QStyle::State_MouseOver;
01224 icon = d->applyHoverEffect(icon);
01225 }
01226
01227 style->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, painter, opt.widget);
01228 painter->drawPixmap(iconPos, icon);
01229 d->drawTextItems(painter, labelLayout, infoLayout, textBoundingRect);
01230 d->drawFocusRect(painter, opt, focusRect);
01231
01232 painter->restore();
01233 }
01234
01235
01236 QWidget *KFileItemDelegate::createEditor(QWidget *parent, const QStyleOptionViewItem &option,
01237 const QModelIndex &index) const
01238 {
01239 QStyleOptionViewItemV4 opt(option);
01240 d->initStyleOption(&opt, index);
01241
01242 KTextEdit *edit = new KTextEdit(parent);
01243 edit->setAcceptRichText(false);
01244 edit->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
01245 edit->setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
01246 edit->setAlignment(opt.displayAlignment);
01247 return edit;
01248 }
01249
01250
01251 bool KFileItemDelegate::editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option,
01252 const QModelIndex &index)
01253 {
01254 Q_UNUSED(event)
01255 Q_UNUSED(model)
01256 Q_UNUSED(option)
01257 Q_UNUSED(index)
01258
01259 return false;
01260 }
01261
01262
01263 void KFileItemDelegate::setEditorData(QWidget *editor, const QModelIndex &index) const
01264 {
01265 KTextEdit *textedit = qobject_cast<KTextEdit*>(editor);
01266 Q_ASSERT(textedit != 0);
01267
01268 const QVariant value = index.data(Qt::EditRole);
01269 const QString text = value.toString();
01270 textedit->insertPlainText(text);
01271 textedit->selectAll();
01272
01273 const QString extension = KMimeType::extractKnownExtension(text);
01274 if (!extension.isEmpty()) {
01275
01276
01277 const int selectionLength = text.length() - extension.length() - 1;
01278 QTextCursor cursor = textedit->textCursor();
01279 cursor.movePosition(QTextCursor::StartOfBlock);
01280 cursor.movePosition(QTextCursor::NextCharacter, QTextCursor::KeepAnchor, selectionLength);
01281 textedit->setTextCursor(cursor);
01282 }
01283 }
01284
01285
01286 void KFileItemDelegate::setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const
01287 {
01288 KTextEdit *textedit = qobject_cast<KTextEdit*>(editor);
01289 Q_ASSERT(textedit != 0);
01290
01291 model->setData(index, textedit->toPlainText(), Qt::EditRole);
01292 }
01293
01294
01295 void KFileItemDelegate::updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option,
01296 const QModelIndex &index) const
01297 {
01298 QStyleOptionViewItemV4 opt(option);
01299 d->initStyleOption(&opt, index);
01300
01301 QRect r = d->labelRectangle(opt);
01302
01303
01304 if (d->verticalLayout(option) && !d->maximumSize.isEmpty()) {
01305 int diff = qMax(r.width(), d->maximumSize.width()) - r.width();
01306 if (diff > 1)
01307 r.adjust(-(diff / 2), 0, diff / 2, 0);
01308 }
01309
01310 KTextEdit *textedit = qobject_cast<KTextEdit*>(editor);
01311 Q_ASSERT(textedit != 0);
01312 const int frame = textedit->frameWidth();
01313 r.adjust(-frame, -frame, frame, frame);
01314
01315 editor->setGeometry(r);
01316 }
01317
01318
01319 bool KFileItemDelegate::helpEvent(QHelpEvent *event, QAbstractItemView *view, const QStyleOptionViewItem &option,
01320 const QModelIndex &index)
01321 {
01322 Q_UNUSED(event)
01323 Q_UNUSED(view)
01324 Q_UNUSED(option)
01325 Q_UNUSED(index)
01326
01327 return false;
01328 }
01329
01330
01331 bool KFileItemDelegate::eventFilter(QObject *object, QEvent *event)
01332 {
01333 KTextEdit *editor = qobject_cast<KTextEdit*>(object);
01334 if (!editor)
01335 return false;
01336
01337 switch (event->type())
01338 {
01339 case QEvent::KeyPress:
01340 {
01341 QKeyEvent *keyEvent = static_cast<QKeyEvent*>(event);
01342 switch (keyEvent->key())
01343 {
01344 case Qt::Key_Tab:
01345 case Qt::Key_Backtab:
01346 emit commitData(editor);
01347 emit closeEditor(editor, NoHint);
01348 return true;
01349
01350 case Qt::Key_Enter:
01351 case Qt::Key_Return:
01352 if (editor->toPlainText().isEmpty())
01353 return true;
01354
01355 emit commitData(editor);
01356 emit closeEditor(editor, SubmitModelCache);
01357 return true;
01358
01359 case Qt::Key_Escape:
01360 emit closeEditor(editor, RevertModelCache);
01361 return true;
01362
01363 default:
01364 return false;
01365 }
01366 }
01367
01368 case QEvent::FocusOut:
01369 {
01370 emit commitData(editor);
01371 emit closeEditor(editor, NoHint);
01372 return true;
01373 }
01374
01375 default:
01376 return false;
01377 }
01378 }
01379
01380
01381 #include "kfileitemdelegate.moc"
01382
01383
01384