KIO

kfileitemdelegate.cpp
1/*
2 This file is part of the KDE project
3 SPDX-FileCopyrightText: 2009 Shaun Reich <shaun.reich@kdemail.net>
4 SPDX-FileCopyrightText: 2006-2007, 2008 Fredrik Höglund <fredrik@kde.org>
5
6 SPDX-License-Identifier: LGPL-2.0-or-later
7*/
8
9#include "kfileitemdelegate.h"
10#include "imagefilter_p.h"
11
12#include <QApplication>
13#include <QCache>
14#include <QImage>
15#include <QListView>
16#include <QLocale>
17#include <QMimeDatabase>
18#include <QModelIndex>
19#include <QPaintEngine>
20#include <QPainter>
21#include <QPainterPath>
22#include <QStyle>
23#include <QTextEdit>
24#include <QTextLayout>
25#include <qmath.h>
26
27#include <KIconEffect>
28#include <KIconLoader>
29#include <KLocalizedString>
30#include <KStatefulBrush>
31#include <KStringHandler>
32#include <kdirmodel.h>
33#include <kfileitem.h>
34
35#include "delegateanimationhandler_p.h"
36
37struct Margin {
38 int left, right, top, bottom;
39};
40
41class Q_DECL_HIDDEN KFileItemDelegate::Private
42{
43public:
44 enum MarginType { ItemMargin = 0, TextMargin, IconMargin, NMargins };
45
46 explicit Private(KFileItemDelegate *parent);
47 ~Private()
48 {
49 }
50
51 QSize decorationSizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const;
52 QSize displaySizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const;
53 QString replaceNewlines(const QString &string) const;
54 inline KFileItem fileItem(const QModelIndex &index) const;
55 QString elidedText(QTextLayout &layout, const QStyleOptionViewItem &option, const QSize &maxSize) const;
56 QSize layoutText(QTextLayout &layout, const QStyleOptionViewItem &option, const QString &text, const QSize &constraints) const;
57 QSize layoutText(QTextLayout &layout, const QString &text, int maxWidth) const;
58 inline void setLayoutOptions(QTextLayout &layout, const QStyleOptionViewItem &options) const;
59 inline bool verticalLayout(const QStyleOptionViewItem &option) const;
60 inline QBrush brush(const QVariant &value, const QStyleOptionViewItem &option) const;
61 QBrush foregroundBrush(const QStyleOptionViewItem &option, const QModelIndex &index) const;
62 inline void setActiveMargins(Qt::Orientation layout);
63 void setVerticalMargin(MarginType type, int left, int right, int top, int bottom);
64 void setHorizontalMargin(MarginType type, int left, int right, int top, int bottom);
65 inline void setVerticalMargin(MarginType type, int hor, int ver);
66 inline void setHorizontalMargin(MarginType type, int hor, int ver);
67 inline QRect addMargin(const QRect &rect, MarginType type) const;
68 inline QRect subtractMargin(const QRect &rect, MarginType type) const;
69 inline QSize addMargin(const QSize &size, MarginType type) const;
70 inline QSize subtractMargin(const QSize &size, MarginType type) const;
71 QString itemSize(const QModelIndex &index, const KFileItem &item) const;
72 QString information(const QStyleOptionViewItem &option, const QModelIndex &index, const KFileItem &item) const;
73 bool isListView(const QStyleOptionViewItem &option) const;
74 QString display(const QModelIndex &index) const;
75 QIcon decoration(const QStyleOptionViewItem &option, const QModelIndex &index) const;
76 QPoint iconPosition(const QStyleOptionViewItem &option) const;
77 QRect labelRectangle(const QStyleOptionViewItem &option, const QModelIndex &index) const;
78 void layoutTextItems(const QStyleOptionViewItem &option,
79 const QModelIndex &index,
80 QTextLayout *labelLayout,
81 QTextLayout *infoLayout,
82 QRect *textBoundingRect) const;
83 void drawTextItems(QPainter *painter,
84 const QTextLayout &labelLayout,
85 const QColor &labelColor,
86 const QTextLayout &infoLayout,
87 const QColor &infoColor,
88 const QRect &textBoundingRect) const;
89 KIO::AnimationState *animationState(const QStyleOptionViewItem &option, const QModelIndex &index, const QAbstractItemView *view) const;
90 void restartAnimation(KIO::AnimationState *state);
91 QPixmap applyHoverEffect(const QPixmap &icon) const;
92 QPixmap transition(const QPixmap &from, const QPixmap &to, qreal amount) const;
93 void initStyleOption(QStyleOptionViewItem *option, const QModelIndex &index) const;
94 void drawFocusRect(QPainter *painter, const QStyleOptionViewItem &option, const QRect &rect) const;
95
96 void gotNewIcon(const QModelIndex &index);
97
98 void paintJobTransfers(QPainter *painter, const qreal &jobAnimationAngle, const QPoint &iconPos, const QStyleOptionViewItem &opt);
99
100public:
102 QColor shadowColor;
103 QPointF shadowOffset;
104 qreal shadowBlur;
105 QSize maximumSize;
106 bool showToolTipWhenElided;
107 QTextOption::WrapMode wrapMode;
108 bool jobTransfersVisible;
109 QIcon downArrowIcon;
110
111private:
112 KIO::DelegateAnimationHandler *animationHandler;
113 Margin verticalMargin[NMargins];
114 Margin horizontalMargin[NMargins];
115 Margin *activeMargins;
116};
117
118KFileItemDelegate::Private::Private(KFileItemDelegate *parent)
119 : shadowColor(Qt::transparent)
120 , shadowOffset(1, 1)
121 , shadowBlur(2)
122 , maximumSize(0, 0)
123 , showToolTipWhenElided(true)
124 , wrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere)
125 , jobTransfersVisible(false)
126 , animationHandler(new KIO::DelegateAnimationHandler(parent))
127 , activeMargins(nullptr)
128{
129}
130
131void KFileItemDelegate::Private::setActiveMargins(Qt::Orientation layout)
132{
133 activeMargins = (layout == Qt::Horizontal ? horizontalMargin : verticalMargin);
134}
135
136void KFileItemDelegate::Private::setVerticalMargin(MarginType type, int left, int top, int right, int bottom)
137{
138 verticalMargin[type].left = left;
139 verticalMargin[type].right = right;
140 verticalMargin[type].top = top;
141 verticalMargin[type].bottom = bottom;
142}
143
144void KFileItemDelegate::Private::setHorizontalMargin(MarginType type, int left, int top, int right, int bottom)
145{
146 horizontalMargin[type].left = left;
147 horizontalMargin[type].right = right;
148 horizontalMargin[type].top = top;
149 horizontalMargin[type].bottom = bottom;
150}
151
152void KFileItemDelegate::Private::setVerticalMargin(MarginType type, int horizontal, int vertical)
153{
154 setVerticalMargin(type, horizontal, vertical, horizontal, vertical);
155}
156
157void KFileItemDelegate::Private::setHorizontalMargin(MarginType type, int horizontal, int vertical)
158{
159 setHorizontalMargin(type, horizontal, vertical, horizontal, vertical);
160}
161
162QRect KFileItemDelegate::Private::addMargin(const QRect &rect, MarginType type) const
163{
164 Q_ASSERT(activeMargins != nullptr);
165 const Margin &m = activeMargins[type];
166 return rect.adjusted(-m.left, -m.top, m.right, m.bottom);
167}
168
169QRect KFileItemDelegate::Private::subtractMargin(const QRect &rect, MarginType type) const
170{
171 Q_ASSERT(activeMargins != nullptr);
172 const Margin &m = activeMargins[type];
173 return rect.adjusted(m.left, m.top, -m.right, -m.bottom);
174}
175
176QSize KFileItemDelegate::Private::addMargin(const QSize &size, MarginType type) const
177{
178 Q_ASSERT(activeMargins != nullptr);
179 const Margin &m = activeMargins[type];
180 return QSize(size.width() + m.left + m.right, size.height() + m.top + m.bottom);
181}
182
183QSize KFileItemDelegate::Private::subtractMargin(const QSize &size, MarginType type) const
184{
185 Q_ASSERT(activeMargins != nullptr);
186 const Margin &m = activeMargins[type];
187 return QSize(size.width() - m.left - m.right, size.height() - m.top - m.bottom);
188}
189
190// Returns the size of a file, or the number of items in a directory, as a QString
191QString KFileItemDelegate::Private::itemSize(const QModelIndex &index, const KFileItem &item) const
192{
193 // Return a formatted string containing the file size, if the item is a file
194 if (item.isFile()) {
195 return KIO::convertSize(item.size());
196 }
197
198 // Return the number of items in the directory
199 const QVariant value = index.data(KDirModel::ChildCountRole);
200 const int count = value.typeId() == QMetaType::Int ? value.toInt() : KDirModel::ChildCountUnknown;
201
202 if (count == KDirModel::ChildCountUnknown) {
203 // was: i18nc("Items in a folder", "? items");
204 // but this just looks useless in a remote directory listing,
205 // better not show anything.
206 return QString();
207 }
208
209 return i18ncp("Items in a folder", "1 item", "%1 items", count);
210}
211
212// Returns the additional information string, if one should be shown, or an empty string otherwise
213QString KFileItemDelegate::Private::information(const QStyleOptionViewItem &option, const QModelIndex &index, const KFileItem &item) const
214{
215 QString string;
216
217 if (informationList.isEmpty() || item.isNull() || !isListView(option)) {
218 return string;
219 }
220
223 continue;
224 }
225
226 if (!string.isEmpty()) {
227 string += QChar::LineSeparator;
228 }
229
230 switch (info) {
232 string += itemSize(index, item);
233 break;
234
236 string += item.permissionsString();
237 break;
238
240 string += QLatin1Char('0') + QString::number(item.permissions(), 8);
241 break;
242
244 string += item.user();
245 break;
246
248 string += item.user() + QLatin1Char(':') + item.group();
249 break;
250
252 string += item.timeString(KFileItem::CreationTime);
253 break;
254
256 string += item.timeString(KFileItem::ModificationTime);
257 break;
258
260 string += item.timeString(KFileItem::AccessTime);
261 break;
262
264 string += item.isMimeTypeKnown() ? item.mimetype() : i18nc("@info mimetype", "Unknown");
265 break;
266
268 string += item.isMimeTypeKnown() ? item.mimeComment() : i18nc("@info mimetype", "Unknown");
269 break;
270
272 string += item.linkDest();
273 break;
274
276 if (!item.localPath().isEmpty()) {
277 string += item.localPath();
278 } else {
279 string += item.url().toDisplayString();
280 }
281 break;
282
284 string += item.comment();
285 break;
286
287 default:
288 break;
289 } // switch (info)
290 } // for (info, list)
291
292 return string;
293}
294
295// Returns the KFileItem for the index
296KFileItem KFileItemDelegate::Private::fileItem(const QModelIndex &index) const
297{
298 const QVariant value = index.data(KDirModel::FileItemRole);
299 return qvariant_cast<KFileItem>(value);
300}
301
302// Replaces any newline characters in the provided string, with QChar::LineSeparator
303QString KFileItemDelegate::Private::replaceNewlines(const QString &text) const
304{
305 QString string = text;
307 return string;
308}
309
310// Lays the text out in a rectangle no larger than constraints, eliding it as necessary
311QSize KFileItemDelegate::Private::layoutText(QTextLayout &layout, const QStyleOptionViewItem &option, const QString &text, const QSize &constraints) const
312{
313 const QSize size = layoutText(layout, text, constraints.width());
314
315 if (size.width() > constraints.width() || size.height() > constraints.height()) {
316 const QString elided = elidedText(layout, option, constraints);
317 return layoutText(layout, elided, constraints.width());
318 }
319
320 return size;
321}
322
323// Lays the text out in a rectangle no wider than maxWidth
324QSize KFileItemDelegate::Private::layoutText(QTextLayout &layout, const QString &text, int maxWidth) const
325{
326 QFontMetrics metrics(layout.font());
327 int leading = metrics.leading();
328 int height = 0;
329 qreal widthUsed = 0;
330 QTextLine line;
331
332 layout.setText(text);
333
334 layout.beginLayout();
335 while ((line = layout.createLine()).isValid()) {
336 line.setLineWidth(maxWidth);
337 height += leading;
338 line.setPosition(QPoint(0, height));
339 height += int(line.height());
340 widthUsed = qMax(widthUsed, line.naturalTextWidth());
341 }
342 layout.endLayout();
343
344 return QSize(qCeil(widthUsed), height);
345}
346
347// Elides the text in the layout, by iterating over each line in the layout, eliding
348// or word breaking the line if it's wider than the max width, and finally adding an
349// ellipses at the end of the last line, if there are more lines than will fit within
350// the vertical size constraints.
351QString KFileItemDelegate::Private::elidedText(QTextLayout &layout, const QStyleOptionViewItem &option, const QSize &size) const
352{
353 const QString text = layout.text();
354 int maxWidth = size.width();
355 int maxHeight = size.height();
356 qreal height = 0;
357 bool wrapText = (option.features & QStyleOptionViewItem::WrapText);
358
359 // If the string contains a single line of text that shouldn't be word wrapped
360 if (!wrapText && text.indexOf(QChar::LineSeparator) == -1) {
361 return option.fontMetrics.elidedText(text, option.textElideMode, maxWidth);
362 }
363
364 // Elide each line that has already been laid out in the layout.
365 QString elided;
366 elided.reserve(text.length());
367
368 for (int i = 0; i < layout.lineCount(); i++) {
369 QTextLine line = layout.lineAt(i);
370 const int start = line.textStart();
371 const int length = line.textLength();
372
373 height += option.fontMetrics.leading();
374 if (height + line.height() + option.fontMetrics.lineSpacing() > maxHeight) {
375 // Unfortunately, if the line ends because of a line separator, elidedText() will be too
376 // clever and keep adding lines until it finds one that's too wide.
377 if (line.naturalTextWidth() < maxWidth && text[start + length - 1] == QChar::LineSeparator) {
378 elided += QStringView(text).mid(start, length - 1);
379 } else {
380 elided += option.fontMetrics.elidedText(text.mid(start), option.textElideMode, maxWidth);
381 }
382 break;
383 } else if (line.naturalTextWidth() > maxWidth) {
384 elided += option.fontMetrics.elidedText(text.mid(start, length), option.textElideMode, maxWidth);
385 if (!elided.endsWith(QChar::LineSeparator)) {
386 elided += QChar::LineSeparator;
387 }
388 } else {
389 elided += QStringView(text).mid(start, length);
390 }
391
392 height += line.height();
393 }
394
395 return elided;
396}
397
398void KFileItemDelegate::Private::setLayoutOptions(QTextLayout &layout, const QStyleOptionViewItem &option) const
399{
400 QTextOption textoption;
401 textoption.setTextDirection(option.direction);
402 textoption.setAlignment(QStyle::visualAlignment(option.direction, option.displayAlignment));
403 textoption.setWrapMode((option.features & QStyleOptionViewItem::WrapText) ? wrapMode : QTextOption::NoWrap);
404
405 layout.setFont(option.font);
406 layout.setTextOption(textoption);
407}
408
409QSize KFileItemDelegate::Private::displaySizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
410{
411 QString label = option.text;
412 int maxWidth = 0;
413 if (maximumSize.isEmpty()) {
414 maxWidth = verticalLayout(option) && (option.features & QStyleOptionViewItem::WrapText) ? option.decorationSize.width() + 10 : 32757;
415 } else {
416 const Margin &itemMargin = activeMargins[ItemMargin];
417 const Margin &textMargin = activeMargins[TextMargin];
418 maxWidth = maximumSize.width() - (itemMargin.left + itemMargin.right) - (textMargin.left + textMargin.right);
419 }
420
421 KFileItem item = fileItem(index);
422
423 // To compute the nominal size for the label + info, we'll just append
424 // the information string to the label
425 const QString info = information(option, index, item);
426 if (!info.isEmpty()) {
428 }
429
430 QTextLayout layout;
431 setLayoutOptions(layout, option);
432
433 QSize size = layoutText(layout, label, maxWidth);
434 if (!info.isEmpty()) {
435 // As soon as additional information is shown, it might be necessary that
436 // the label and/or the additional information must get elided. To prevent
437 // an expensive eliding in the scope of displaySizeHint, the maximum
438 // width is reserved instead.
439 size.setWidth(maxWidth);
440 }
441
442 return addMargin(size, TextMargin);
443}
444
445QSize KFileItemDelegate::Private::decorationSizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const
446{
447 if (index.column() > 0) {
448 return QSize(0, 0);
449 }
450
451 QSize iconSize = option.icon.actualSize(option.decorationSize);
452 if (!verticalLayout(option)) {
453 iconSize.rwidth() = option.decorationSize.width();
454 } else if (iconSize.width() < option.decorationSize.width()) {
455 iconSize.rwidth() = qMin(iconSize.width() + 10, option.decorationSize.width());
456 }
457 if (iconSize.height() < option.decorationSize.height()) {
458 iconSize.rheight() = option.decorationSize.height();
459 }
460
461 return addMargin(iconSize, IconMargin);
462}
463
464bool KFileItemDelegate::Private::verticalLayout(const QStyleOptionViewItem &option) const
465{
466 return (option.decorationPosition == QStyleOptionViewItem::Top || option.decorationPosition == QStyleOptionViewItem::Bottom);
467}
468
469// Converts a QVariant of type Brush or Color to a QBrush
470QBrush KFileItemDelegate::Private::brush(const QVariant &value, const QStyleOptionViewItem &option) const
471{
472 if (value.userType() == qMetaTypeId<KStatefulBrush>()) {
473 return qvariant_cast<KStatefulBrush>(value).brush(option.palette);
474 }
475 switch (value.typeId()) {
477 return QBrush(qvariant_cast<QColor>(value));
478
480 return qvariant_cast<QBrush>(value);
481
482 default:
483 return QBrush(Qt::NoBrush);
484 }
485}
486
487QBrush KFileItemDelegate::Private::foregroundBrush(const QStyleOptionViewItem &option, const QModelIndex &index) const
488{
490 if (!(option.state & QStyle::State_Enabled)) {
492 } else if (!(option.state & QStyle::State_Active)) {
494 }
495
496 // Always use the highlight color for selected items
497 if (option.state & QStyle::State_Selected) {
498 return option.palette.brush(cg, QPalette::HighlightedText);
499 }
500
501 // If the model provides its own foreground color/brush for this item
502 const QVariant value = index.data(Qt::ForegroundRole);
503 if (value.isValid()) {
504 return brush(value, option);
505 }
506
507 return option.palette.brush(cg, QPalette::Text);
508}
509
510bool KFileItemDelegate::Private::isListView(const QStyleOptionViewItem &option) const
511{
512 if (qobject_cast<const QListView *>(option.widget) || verticalLayout(option)) {
513 return true;
514 }
515
516 return false;
517}
518
519QPixmap KFileItemDelegate::Private::applyHoverEffect(const QPixmap &icon) const
520{
522
523 // Note that in KIconLoader terminology, active = hover.
524 // ### We're assuming that the icon group is desktop/filemanager, since this
525 // is KFileItemDelegate.
528 }
529
530 return icon;
531}
532
533void KFileItemDelegate::Private::gotNewIcon(const QModelIndex &index)
534{
535 animationHandler->gotNewIcon(index);
536}
537
538void KFileItemDelegate::Private::restartAnimation(KIO::AnimationState *state)
539{
540 animationHandler->restartAnimation(state);
541}
542
543KIO::AnimationState *
544KFileItemDelegate::Private::animationState(const QStyleOptionViewItem &option, const QModelIndex &index, const QAbstractItemView *view) const
545{
546 if (!option.widget->style()->styleHint(QStyle::SH_Widget_Animate, nullptr, option.widget)) {
547 return nullptr;
548 }
549
550 if (index.column() == KDirModel::Name) {
551 return animationHandler->animationState(option, index, view);
552 }
553
554 return nullptr;
555}
556
557QPixmap KFileItemDelegate::Private::transition(const QPixmap &from, const QPixmap &to, qreal amount) const
558{
559 int value = int(0xff * amount);
560
561 if (value == 0 || to.isNull()) {
562 return from;
563 }
564
565 if (value == 0xff || from.isNull()) {
566 return to;
567 }
568
569 QColor color;
570 color.setAlphaF(amount);
571
572// FIXME: Somehow this doesn't work on Mac OS..
573#if defined(Q_OS_MAC)
574 const bool usePixmap = false;
575#else
577#endif
578
579 // If the native paint engine supports Porter/Duff compositing and CompositionMode_Plus
580 if (usePixmap) {
581 QPixmap under = from;
582 QPixmap over = to;
583
584 QPainter p;
585 p.begin(&over);
587 p.fillRect(over.rect(), color);
588 p.end();
589
590 p.begin(&under);
592 p.fillRect(under.rect(), color);
594 p.drawPixmap(0, 0, over);
595 p.end();
596
597 return under;
598 } else {
599 // Fall back to using QRasterPaintEngine to do the transition.
600 QImage under = from.toImage();
601 QImage over = to.toImage();
602
603 QPainter p;
604 p.begin(&over);
606 p.fillRect(over.rect(), color);
607 p.end();
608
609 p.begin(&under);
611 p.fillRect(under.rect(), color);
613 p.drawImage(0, 0, over);
614 p.end();
615
616 return QPixmap::fromImage(under);
617 }
618}
619
620void KFileItemDelegate::Private::layoutTextItems(const QStyleOptionViewItem &option,
621 const QModelIndex &index,
622 QTextLayout *labelLayout,
623 QTextLayout *infoLayout,
624 QRect *textBoundingRect) const
625{
626 KFileItem item = fileItem(index);
627 const QString info = information(option, index, item);
628 bool showInformation = false;
629
630 setLayoutOptions(*labelLayout, option);
631
632 const QRect textArea = labelRectangle(option, index);
633 QRect textRect = subtractMargin(textArea, Private::TextMargin);
634
635 // Sizes and constraints for the different text parts
636 QSize maxLabelSize = textRect.size();
637 QSize maxInfoSize = textRect.size();
638 QSize labelSize;
639 QSize infoSize;
640
641 // If we have additional info text, and there's space for at least two lines of text,
642 // adjust the max label size to make room for at least one line of the info text
643 if (!info.isEmpty() && textRect.height() >= option.fontMetrics.lineSpacing() * 2) {
644 infoLayout->setFont(labelLayout->font());
645 infoLayout->setTextOption(labelLayout->textOption());
646
647 maxLabelSize.rheight() -= option.fontMetrics.lineSpacing();
648 showInformation = true;
649 }
650
651 // Lay out the label text, and adjust the max info size based on the label size
652 labelSize = layoutText(*labelLayout, option, option.text, maxLabelSize);
653 maxInfoSize.rheight() -= labelSize.height();
654
655 // Lay out the info text
656 if (showInformation) {
657 infoSize = layoutText(*infoLayout, option, info, maxInfoSize);
658 } else {
659 infoSize = QSize(0, 0);
660 }
661
662 // Compute the bounding rect of the text
663 const QSize size(qMax(labelSize.width(), infoSize.width()), labelSize.height() + infoSize.height());
664 *textBoundingRect = QStyle::alignedRect(option.direction, option.displayAlignment, size, textRect);
665
666 // Compute the positions where we should draw the layouts
667 labelLayout->setPosition(QPointF(textRect.x(), textBoundingRect->y()));
668 infoLayout->setPosition(QPointF(textRect.x(), textBoundingRect->y() + labelSize.height()));
669}
670
671void KFileItemDelegate::Private::drawTextItems(QPainter *painter,
672 const QTextLayout &labelLayout,
673 const QColor &labelColor,
674 const QTextLayout &infoLayout,
675 const QColor &infoColor,
676 const QRect &boundingRect) const
677{
678 if (shadowColor.alpha() > 0) {
679 QPixmap pixmap(boundingRect.size());
680 pixmap.fill(Qt::transparent);
681
682 QPainter p(&pixmap);
683 p.translate(-boundingRect.topLeft());
684 p.setPen(labelColor);
685 labelLayout.draw(&p, QPoint());
686
687 if (!infoLayout.text().isEmpty()) {
688 p.setPen(infoColor);
689 infoLayout.draw(&p, QPoint());
690 }
691 p.end();
692
693 int padding = qCeil(shadowBlur);
694 int blurFactor = qRound(shadowBlur);
695
696 QImage image(boundingRect.size() + QSize(padding * 2, padding * 2), QImage::Format_ARGB32_Premultiplied);
697 image.fill(0);
698 p.begin(&image);
699 p.drawImage(padding, padding, pixmap.toImage());
700 p.end();
701
702 KIO::ImageFilter::shadowBlur(image, blurFactor, shadowColor);
703
704 painter->drawImage(boundingRect.topLeft() - QPoint(padding, padding) + shadowOffset.toPoint(), image);
705 painter->drawPixmap(boundingRect.topLeft(), pixmap);
706 return;
707 }
708
709 painter->save();
710 painter->setPen(labelColor);
711
712 labelLayout.draw(painter, QPoint());
713 if (!infoLayout.text().isEmpty()) {
714 // TODO - for apps not doing funny things with the color palette,
715 // KColorScheme::InactiveText would be a much more correct choice. We
716 // should provide an API to specify what color to use for information.
717 painter->setPen(infoColor);
718 infoLayout.draw(painter, QPoint());
719 }
720
721 painter->restore();
722}
723
724void KFileItemDelegate::Private::initStyleOption(QStyleOptionViewItem *option, const QModelIndex &index) const
725{
726 const KFileItem item = fileItem(index);
727 bool updateFontMetrics = false;
728
729 // Try to get the font from the model
730 QVariant value = index.data(Qt::FontRole);
731 if (value.isValid()) {
732 option->font = qvariant_cast<QFont>(value).resolve(option->font);
733 updateFontMetrics = true;
734 }
735
736 // Use an italic font for symlinks
737 if (!item.isNull() && item.isLink()) {
738 option->font.setItalic(true);
739 updateFontMetrics = true;
740 }
741
742 if (updateFontMetrics) {
743 option->fontMetrics = QFontMetrics(option->font);
744 }
745
746 // Try to get the alignment for the item from the model
747 value = index.data(Qt::TextAlignmentRole);
748 if (value.isValid()) {
749 option->displayAlignment = Qt::Alignment(value.toInt());
750 }
751
752 value = index.data(Qt::BackgroundRole);
753 if (value.isValid()) {
754 option->backgroundBrush = brush(value, *option);
755 }
756
757 option->text = display(index);
758 if (!option->text.isEmpty()) {
759 option->features |= QStyleOptionViewItem::HasDisplay;
760 }
761
762 option->icon = decoration(*option, index);
763 // Note that even null icons are still drawn for alignment
764 if (!option->icon.isNull()) {
765 option->features |= QStyleOptionViewItem::HasDecoration;
766 }
767
768 // ### Make sure this value is always true for now
769 option->showDecorationSelected = true;
770}
771
772void KFileItemDelegate::Private::paintJobTransfers(QPainter *painter, const qreal &jobAnimationAngle, const QPoint &iconPos, const QStyleOptionViewItem &opt)
773{
774 painter->save();
775 QSize iconSize = opt.icon.actualSize(opt.decorationSize);
776 QPixmap downArrow = downArrowIcon.pixmap(iconSize * 0.30);
777 // corner (less x and y than bottom-right corner) that we will center the painter around
778 QPoint bottomRightCorner = QPoint(iconPos.x() + iconSize.width() * 0.75, iconPos.y() + iconSize.height() * 0.60);
779
780 QPainter pixmapPainter(&downArrow);
781 // make the icon transparent and such
782 pixmapPainter.setCompositionMode(QPainter::CompositionMode_DestinationIn);
783 pixmapPainter.fillRect(downArrow.rect(), QColor(255, 255, 255, 110));
784
785 painter->translate(bottomRightCorner);
786
787 painter->drawPixmap(-downArrow.size().width() * .50, -downArrow.size().height() * .50, downArrow);
788
789 // animate the circles by rotating the painter around the center point..
790 painter->rotate(jobAnimationAngle);
791 painter->setPen(QColor(20, 20, 20, 80));
792 painter->setBrush(QColor(250, 250, 250, 90));
793
794 int radius = iconSize.width() * 0.04;
795 int spacing = radius * 4.5;
796
797 // left
798 painter->drawEllipse(QPoint(-spacing, 0), radius, radius);
799 // right
800 painter->drawEllipse(QPoint(spacing, 0), radius, radius);
801 // up
802 painter->drawEllipse(QPoint(0, -spacing), radius, radius);
803 // down
804 painter->drawEllipse(QPoint(0, spacing), radius, radius);
805 painter->restore();
806}
807
808// ---------------------------------------------------------------------------
809
812 , d(new Private(this))
813{
816
817 // Margins for horizontal mode (list views, tree views, table views)
818 const int textMargin = focusHMargin * 4;
820 d->setHorizontalMargin(Private::TextMargin, textMargin, focusVMargin, focusHMargin, focusVMargin);
821 } else {
822 d->setHorizontalMargin(Private::TextMargin, focusHMargin, focusVMargin, textMargin, focusVMargin);
823 }
824
825 d->setHorizontalMargin(Private::IconMargin, focusHMargin, focusVMargin);
826 d->setHorizontalMargin(Private::ItemMargin, 0, 0);
827
828 // Margins for vertical mode (icon views)
829 d->setVerticalMargin(Private::TextMargin, 6, 2);
830 d->setVerticalMargin(Private::IconMargin, focusHMargin, focusVMargin);
831 d->setVerticalMargin(Private::ItemMargin, 0, 0);
832
834}
835
837
839{
840 // If the model wants to provide its own size hint for the item
841 const QVariant value = index.data(Qt::SizeHintRole);
842 if (value.isValid()) {
843 return qvariant_cast<QSize>(value);
844 }
845
846 QStyleOptionViewItem opt(option);
847 d->initStyleOption(&opt, index);
848 d->setActiveMargins(d->verticalLayout(opt) ? Qt::Vertical : Qt::Horizontal);
849
850 const QSize displaySize = d->displaySizeHint(opt, index);
851 const QSize decorationSize = d->decorationSizeHint(opt, index);
852
853 QSize size;
854
855 if (d->verticalLayout(opt)) {
856 size.rwidth() = qMax(displaySize.width(), decorationSize.width());
857 size.rheight() = decorationSize.height() + displaySize.height() + 1;
858 } else {
859 size.rwidth() = decorationSize.width() + displaySize.width() + 1;
860 size.rheight() = qMax(decorationSize.height(), displaySize.height());
861 }
862
863 size = d->addMargin(size, Private::ItemMargin);
864 if (!d->maximumSize.isEmpty()) {
865 size = size.boundedTo(d->maximumSize);
866 }
867
868 return size;
869}
870
871QString KFileItemDelegate::Private::display(const QModelIndex &index) const
872{
873 const QVariant value = index.data(Qt::DisplayRole);
874
875 switch (value.typeId()) {
876 case QMetaType::QString: {
877 if (index.column() == KDirModel::Size) {
878 return itemSize(index, fileItem(index));
879 } else {
880 const QString text = replaceNewlines(value.toString());
882 }
883 }
884
886 return QLocale().toString(value.toDouble(), 'f');
887
888 case QMetaType::Int:
889 case QMetaType::UInt:
890 return QLocale().toString(value.toInt());
891
892 default:
893 return QString();
894 }
895}
896
898{
899 d->informationList = list;
900}
901
903{
904 if (value != NoInformation) {
905 d->informationList = InformationList() << value;
906 } else {
907 d->informationList = InformationList();
908 }
909}
910
912{
913 return d->informationList;
914}
915
917{
918 d->shadowColor = color;
919}
920
922{
923 return d->shadowColor;
924}
925
927{
928 d->shadowOffset = offset;
929}
930
932{
933 return d->shadowOffset;
934}
935
937{
938 d->shadowBlur = factor;
939}
940
942{
943 return d->shadowBlur;
944}
945
947{
948 d->maximumSize = size;
949}
950
952{
953 return d->maximumSize;
954}
955
957{
958 d->showToolTipWhenElided = showToolTip;
959}
960
962{
963 return d->showToolTipWhenElided;
964}
965
970
972{
973 return d->wrapMode;
974}
975
977{
978 if (index.column() > 0) {
979 return QRect(0, 0, 0, 0);
980 }
981 QStyleOptionViewItem opt(option);
982 d->initStyleOption(&opt, index);
983 return QRect(d->iconPosition(opt), opt.icon.actualSize(opt.decorationSize));
984}
985
987{
988 d->downArrowIcon = QIcon::fromTheme(QStringLiteral("go-down"));
989 d->jobTransfersVisible = jobTransfersVisible;
990}
991
993{
994 return d->jobTransfersVisible;
995}
996
997QIcon KFileItemDelegate::Private::decoration(const QStyleOptionViewItem &option, const QModelIndex &index) const
998{
999 const QVariant value = index.data(Qt::DecorationRole);
1000 QIcon icon;
1001
1002 switch (value.typeId()) {
1003 case QMetaType::QIcon:
1004 icon = qvariant_cast<QIcon>(value);
1005 break;
1006
1007 case QMetaType::QPixmap:
1008 icon.addPixmap(qvariant_cast<QPixmap>(value));
1009 break;
1010
1011 case QMetaType::QColor: {
1012 QPixmap pixmap(option.decorationSize);
1013 pixmap.fill(qvariant_cast<QColor>(value));
1014 icon.addPixmap(pixmap);
1015 break;
1016 }
1017
1018 default:
1019 break;
1020 }
1021
1022 return icon;
1023}
1024
1025QRect KFileItemDelegate::Private::labelRectangle(const QStyleOptionViewItem &option, const QModelIndex &index) const
1026{
1027 const QSize decoSize = (index.column() == 0) ? addMargin(option.decorationSize, Private::IconMargin) : QSize(0, 0);
1028 const QRect itemRect = subtractMargin(option.rect, Private::ItemMargin);
1029 QRect textArea(QPoint(0, 0), itemRect.size());
1030
1031 switch (option.decorationPosition) {
1033 textArea.setTop(decoSize.height() + 1);
1034 break;
1035
1037 textArea.setBottom(itemRect.height() - decoSize.height() - 1);
1038 break;
1039
1041 textArea.setLeft(decoSize.width() + 1);
1042 break;
1043
1045 textArea.setRight(itemRect.width() - decoSize.width() - 1);
1046 break;
1047 }
1048
1049 textArea.translate(itemRect.topLeft());
1050 return QStyle::visualRect(option.direction, option.rect, textArea);
1051}
1052
1053QPoint KFileItemDelegate::Private::iconPosition(const QStyleOptionViewItem &option) const
1054{
1055 if (option.index.column() > 0) {
1056 return QPoint(0, 0);
1057 }
1058
1059 const QRect itemRect = subtractMargin(option.rect, Private::ItemMargin);
1060 Qt::Alignment alignment;
1061
1062 // Convert decorationPosition to the alignment the decoration will have in option.rect
1063 switch (option.decorationPosition) {
1065 alignment = Qt::AlignHCenter | Qt::AlignTop;
1066 break;
1067
1069 alignment = Qt::AlignHCenter | Qt::AlignBottom;
1070 break;
1071
1073 alignment = Qt::AlignVCenter | Qt::AlignLeft;
1074 break;
1075
1077 alignment = Qt::AlignVCenter | Qt::AlignRight;
1078 break;
1079 }
1080
1081 // Compute the nominal decoration rectangle
1082 const QSize size = addMargin(option.decorationSize, Private::IconMargin);
1083 const QRect rect = QStyle::alignedRect(option.direction, alignment, size, itemRect);
1084
1085 // Position the icon in the center of the rectangle
1086 QRect iconRect = QRect(QPoint(), option.icon.actualSize(option.decorationSize));
1087 iconRect.moveCenter(rect.center());
1088
1089 return iconRect.topLeft();
1090}
1091
1092void KFileItemDelegate::Private::drawFocusRect(QPainter *painter, const QStyleOptionViewItem &option, const QRect &rect) const
1093{
1094 if (!(option.state & QStyle::State_HasFocus)) {
1095 return;
1096 }
1097
1099 opt.direction = option.direction;
1100 opt.fontMetrics = option.fontMetrics;
1101 opt.palette = option.palette;
1102 opt.rect = rect;
1103 opt.state = option.state | QStyle::State_KeyboardFocusChange | QStyle::State_Item;
1104 opt.backgroundColor = option.palette.color(option.state & QStyle::State_Selected ? QPalette::Highlight : QPalette::Base);
1105
1106 // Apparently some widget styles expect this hint to not be set
1107 painter->setRenderHint(QPainter::Antialiasing, false);
1108
1109 QStyle *style = option.widget ? option.widget->style() : QApplication::style();
1110 style->drawPrimitive(QStyle::PE_FrameFocusRect, &opt, painter, option.widget);
1111
1113}
1114
1115void KFileItemDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const
1116{
1117 if (!index.isValid()) {
1118 return;
1119 }
1120
1121 QStyleOptionViewItem opt(option);
1122 d->initStyleOption(&opt, index);
1123 d->setActiveMargins(d->verticalLayout(opt) ? Qt::Vertical : Qt::Horizontal);
1124
1125 if (!(option.state & QStyle::State_Enabled)) {
1126 opt.palette.setCurrentColorGroup(QPalette::Disabled);
1127 }
1128
1129 // Unset the mouse over bit if we're not drawing the first column
1130 if (index.column() > 0) {
1131 opt.state &= ~QStyle::State_MouseOver;
1132 } else {
1133 opt.viewItemPosition = QStyleOptionViewItem::OnlyOne;
1134 }
1135
1136 const QAbstractItemView *view = qobject_cast<const QAbstractItemView *>(opt.widget);
1137
1138 // Check if the item is being animated
1139 // ========================================================================
1140 KIO::AnimationState *state = d->animationState(opt, index, view);
1141 KIO::CachedRendering *cache = nullptr;
1142 qreal progress = ((opt.state & QStyle::State_MouseOver) && index.column() == KDirModel::Name) ? 1.0 : 0.0;
1143 const QPoint iconPos = d->iconPosition(opt);
1144 QIcon::Mode iconMode;
1145
1146 if (!(option.state & QStyle::State_Enabled)) {
1147 iconMode = QIcon::Disabled;
1148 } else if ((option.state & QStyle::State_Selected) && (option.state & QStyle::State_Active)) {
1149 iconMode = QIcon::Selected;
1150 } else {
1151 iconMode = QIcon::Normal;
1152 }
1153
1154 QIcon::State iconState = option.state & QStyle::State_Open ? QIcon::On : QIcon::Off;
1155 QPixmap icon = opt.icon.pixmap(opt.decorationSize, iconMode, iconState);
1156
1157 const KFileItem fileItem = d->fileItem(index);
1158 if (fileItem.isHidden()) {
1160 }
1161
1162 if (state && !state->hasJobAnimation()) {
1163 cache = state->cachedRendering();
1164 progress = state->hoverProgress();
1165 // Clear the mouse over bit temporarily
1166 opt.state &= ~QStyle::State_MouseOver;
1167
1168 // If we have a cached rendering, draw the item from the cache
1169 if (cache) {
1170 if (cache->checkValidity(opt.state) && cache->regular.size() == opt.rect.size()) {
1171 QPixmap pixmap = d->transition(cache->regular, cache->hover, progress);
1172
1173 if (state->cachedRenderingFadeFrom() && state->fadeProgress() != 1.0) {
1174 // Apply icon fading animation
1175 KIO::CachedRendering *fadeFromCache = state->cachedRenderingFadeFrom();
1176 const QPixmap fadeFromPixmap = d->transition(fadeFromCache->regular, fadeFromCache->hover, progress);
1177
1178 pixmap = d->transition(fadeFromPixmap, pixmap, state->fadeProgress());
1179 }
1180 painter->drawPixmap(option.rect.topLeft(), pixmap);
1181 if (d->jobTransfersVisible && index.column() == 0) {
1182 if (index.data(KDirModel::HasJobRole).toBool()) {
1183 d->paintJobTransfers(painter, state->jobAnimationAngle(), iconPos, opt);
1184 }
1185 }
1186 return;
1187 }
1188
1189 if (!cache->checkValidity(opt.state)) {
1190 if (opt.widget->style()->styleHint(QStyle::SH_Widget_Animate, nullptr, opt.widget)) {
1191 // Fade over from the old icon to the new one
1192 // Only start a new fade if the previous one is ready
1193 // Else we may start racing when checkValidity() always returns false
1194 if (state->fadeProgress() == 1) {
1195 state->setCachedRenderingFadeFrom(state->takeCachedRendering());
1196 }
1197 }
1198 d->gotNewIcon(index);
1199 }
1200 // If it wasn't valid, delete it
1201 state->setCachedRendering(nullptr);
1202 } else {
1203 // The cache may have been discarded, but the animation handler still needs to know about new icons
1204 d->gotNewIcon(index);
1205 }
1206 }
1207
1208 // Compute the metrics, and lay out the text items
1209 // ========================================================================
1210 QColor labelColor = d->foregroundBrush(opt, index).color();
1211 QColor infoColor = labelColor;
1212 if (!(option.state & QStyle::State_Selected)) {
1213 // the code below is taken from Dolphin
1214 const QColor c2 = option.palette.base().color();
1215 const int p1 = 70;
1216 const int p2 = 100 - p1;
1217 infoColor = QColor((labelColor.red() * p1 + c2.red() * p2) / 100,
1218 (labelColor.green() * p1 + c2.green() * p2) / 100,
1219 (labelColor.blue() * p1 + c2.blue() * p2) / 100);
1220
1221 if (fileItem.isHidden()) {
1222 labelColor = infoColor;
1223 }
1224 }
1225
1226 // ### Apply the selection effect to the icon when the item is selected and
1227 // showDecorationSelected is false.
1228
1229 QTextLayout labelLayout;
1230 QTextLayout infoLayout;
1231 QRect textBoundingRect;
1232
1233 d->layoutTextItems(opt, index, &labelLayout, &infoLayout, &textBoundingRect);
1234
1235 QStyle *style = opt.widget ? opt.widget->style() : QApplication::style();
1236
1237 int focusHMargin = style->pixelMetric(QStyle::PM_FocusFrameHMargin);
1238 int focusVMargin = style->pixelMetric(QStyle::PM_FocusFrameVMargin);
1239 QRect focusRect = textBoundingRect.adjusted(-focusHMargin, -focusVMargin, +focusHMargin, +focusVMargin);
1240
1241 // Create a new cached rendering of a hovered and an unhovered item.
1242 // We don't create a new cache for a fully hovered item, since we don't
1243 // know yet if a hover out animation will be run.
1244 // ========================================================================
1245 if (state && (state->hoverProgress() < 1 || state->fadeProgress() < 1)) {
1246 const qreal dpr = painter->device()->devicePixelRatioF();
1247
1248 cache = new KIO::CachedRendering(opt.state, option.rect.size(), index, dpr);
1249
1250 QPainter p;
1251 p.begin(&cache->regular);
1252 p.translate(-option.rect.topLeft());
1254 style->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, &p, opt.widget);
1255 p.drawPixmap(iconPos, icon);
1256 d->drawTextItems(&p, labelLayout, labelColor, infoLayout, infoColor, textBoundingRect);
1257 d->drawFocusRect(&p, opt, focusRect);
1258 p.end();
1259
1260 opt.state |= QStyle::State_MouseOver;
1261 icon = d->applyHoverEffect(icon);
1262
1263 p.begin(&cache->hover);
1264 p.translate(-option.rect.topLeft());
1266 style->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, &p, opt.widget);
1267 p.drawPixmap(iconPos, icon);
1268 d->drawTextItems(&p, labelLayout, labelColor, infoLayout, infoColor, textBoundingRect);
1269 d->drawFocusRect(&p, opt, focusRect);
1270 p.end();
1271
1272 state->setCachedRendering(cache);
1273
1274 QPixmap pixmap = d->transition(cache->regular, cache->hover, progress);
1275
1276 if (state->cachedRenderingFadeFrom() && state->fadeProgress() == 0) {
1277 // Apply icon fading animation
1278 KIO::CachedRendering *fadeFromCache = state->cachedRenderingFadeFrom();
1279 const QPixmap fadeFromPixmap = d->transition(fadeFromCache->regular, fadeFromCache->hover, progress);
1280
1281 pixmap = d->transition(fadeFromPixmap, pixmap, state->fadeProgress());
1282
1283 d->restartAnimation(state);
1284 }
1285
1286 painter->drawPixmap(option.rect.topLeft(), pixmap);
1288 if (d->jobTransfersVisible && index.column() == 0) {
1289 if (index.data(KDirModel::HasJobRole).toBool()) {
1290 d->paintJobTransfers(painter, state->jobAnimationAngle(), iconPos, opt);
1291 }
1292 }
1293 return;
1294 }
1295
1296 // Render the item directly if we're not using a cached rendering
1297 // ========================================================================
1298 painter->save();
1300
1301 if (progress > 0 && !(opt.state & QStyle::State_MouseOver)) {
1302 opt.state |= QStyle::State_MouseOver;
1303 icon = d->applyHoverEffect(icon);
1304 }
1305
1306 style->drawPrimitive(QStyle::PE_PanelItemViewItem, &opt, painter, opt.widget);
1307 painter->drawPixmap(iconPos, icon);
1308
1309 d->drawTextItems(painter, labelLayout, labelColor, infoLayout, infoColor, textBoundingRect);
1310 d->drawFocusRect(painter, opt, focusRect);
1311
1312 if (d->jobTransfersVisible && index.column() == 0 && state) {
1313 if (index.data(KDirModel::HasJobRole).toBool()) {
1314 d->paintJobTransfers(painter, state->jobAnimationAngle(), iconPos, opt);
1315 }
1316 }
1317 painter->restore();
1318}
1319
1321{
1322 QStyleOptionViewItem opt(option);
1323 d->initStyleOption(&opt, index);
1324
1325 QTextEdit *edit = new QTextEdit(parent);
1326 edit->setAcceptRichText(false);
1329 edit->setAlignment(opt.displayAlignment);
1330 edit->setEnabled(false); // Disable the text-edit to mark it as un-initialized
1331 return edit;
1332}
1333
1335{
1336 Q_UNUSED(event)
1337 Q_UNUSED(model)
1338 Q_UNUSED(option)
1339 Q_UNUSED(index)
1340
1341 return false;
1342}
1343
1345{
1346 QTextEdit *textedit = qobject_cast<QTextEdit *>(editor);
1347 Q_ASSERT(textedit != nullptr);
1348
1349 // Do not update existing text that the user may already have edited.
1350 // The models will call setEditorData(..) whenever the icon has changed,
1351 // and this makes the editing work correctly despite that.
1352 if (textedit->isEnabled()) {
1353 return;
1354 }
1355 textedit->setEnabled(true); // Enable the text-edit to mark it as initialized
1356
1357 const QVariant value = index.data(Qt::EditRole);
1358 const QString text = value.toString();
1359 textedit->insertPlainText(text);
1360 textedit->selectAll();
1361
1362 QMimeDatabase db;
1363 const QString extension = db.suffixForFileName(text);
1364 if (!extension.isEmpty()) {
1365 // The filename contains an extension. Assure that only the filename
1366 // gets selected.
1367 const int selectionLength = text.length() - extension.length() - 1;
1368 QTextCursor cursor = textedit->textCursor();
1371 textedit->setTextCursor(cursor);
1372 }
1373}
1374
1376{
1377 QTextEdit *textedit = qobject_cast<QTextEdit *>(editor);
1378 Q_ASSERT(textedit != nullptr);
1379
1380 model->setData(index, textedit->toPlainText(), Qt::EditRole);
1381}
1382
1384{
1385 QStyleOptionViewItem opt(option);
1386 d->initStyleOption(&opt, index);
1387 d->setActiveMargins(d->verticalLayout(opt) ? Qt::Vertical : Qt::Horizontal);
1388
1389 QRect r = d->labelRectangle(opt, index);
1390
1391 // Use the full available width for the editor when maximumSize is set
1392 if (!d->maximumSize.isEmpty()) {
1393 if (d->verticalLayout(option)) {
1394 int diff = qMax(r.width(), d->maximumSize.width()) - r.width();
1395 if (diff > 1) {
1396 r.adjust(-(diff / 2), 0, diff / 2, 0);
1397 }
1398 } else {
1399 int diff = qMax(r.width(), d->maximumSize.width() - opt.decorationSize.width()) - r.width();
1400 if (diff > 0) {
1401 if (opt.decorationPosition == QStyleOptionViewItem::Left) {
1402 r.adjust(0, 0, diff, 0);
1403 } else {
1404 r.adjust(-diff, 0, 0, 0);
1405 }
1406 }
1407 }
1408 }
1409
1410 QTextEdit *textedit = qobject_cast<QTextEdit *>(editor);
1411 Q_ASSERT(textedit != nullptr);
1412 const int frame = textedit->frameWidth();
1413 r.adjust(-frame, -frame, frame, frame);
1414
1415 editor->setGeometry(r);
1416}
1417
1419{
1420 Q_UNUSED(event)
1421 Q_UNUSED(view)
1422
1423 // if the tooltip information the model keeps is different from the display information,
1424 // show it always
1425 const QVariant toolTip = index.data(Qt::ToolTipRole);
1426
1427 if (!toolTip.isValid()) {
1428 return false;
1429 }
1430
1431 if (index.data() != toolTip) {
1432 return QAbstractItemDelegate::helpEvent(event, view, option, index);
1433 }
1434
1435 if (!d->showToolTipWhenElided) {
1436 return false;
1437 }
1438
1439 // in the case the tooltip information is the same as the display information,
1440 // show it only in the case the display information is elided
1441 QStyleOptionViewItem opt(option);
1442 d->initStyleOption(&opt, index);
1443 d->setActiveMargins(d->verticalLayout(opt) ? Qt::Vertical : Qt::Horizontal);
1444
1445 QTextLayout labelLayout;
1446 QTextLayout infoLayout;
1447 QRect textBoundingRect;
1448 d->layoutTextItems(opt, index, &labelLayout, &infoLayout, &textBoundingRect);
1449 const QString elidedText = d->elidedText(labelLayout, opt, textBoundingRect.size());
1450
1451 if (elidedText != d->display(index)) {
1452 return QAbstractItemDelegate::helpEvent(event, view, option, index);
1453 }
1454
1455 return false;
1456}
1457
1459{
1460 QStyleOptionViewItem opt(option);
1461 d->initStyleOption(&opt, index);
1462 d->setActiveMargins(d->verticalLayout(opt) ? Qt::Vertical : Qt::Horizontal);
1463
1464 QTextLayout labelLayout;
1465 QTextLayout infoLayout;
1466 QRect textBoundingRect;
1467 d->layoutTextItems(opt, index, &labelLayout, &infoLayout, &textBoundingRect);
1468
1469 const QPoint pos = d->iconPosition(opt);
1470 QRect iconRect = QRect(pos, opt.icon.actualSize(opt.decorationSize));
1471
1472 // Extend the icon rect so it touches the text rect
1473 switch (opt.decorationPosition) {
1475 if (iconRect.width() < textBoundingRect.width()) {
1476 iconRect.setBottom(textBoundingRect.top());
1477 } else {
1478 textBoundingRect.setTop(iconRect.bottom());
1479 }
1480 break;
1482 if (iconRect.width() < textBoundingRect.width()) {
1483 iconRect.setTop(textBoundingRect.bottom());
1484 } else {
1485 textBoundingRect.setBottom(iconRect.top());
1486 }
1487 break;
1489 iconRect.setRight(textBoundingRect.left());
1490 break;
1492 iconRect.setLeft(textBoundingRect.right());
1493 break;
1494 }
1495
1496 QRegion region;
1497 region += iconRect;
1498 region += textBoundingRect;
1499 return region;
1500}
1501
1503{
1504 QTextEdit *editor = qobject_cast<QTextEdit *>(object);
1505 if (!editor) {
1506 return false;
1507 }
1508
1509 switch (event->type()) {
1510 case QEvent::KeyPress: {
1511 QKeyEvent *keyEvent = static_cast<QKeyEvent *>(event);
1512 switch (keyEvent->key()) {
1513 case Qt::Key_Tab:
1514 case Qt::Key_Backtab:
1515 Q_EMIT commitData(editor);
1516 Q_EMIT closeEditor(editor, NoHint);
1517 return true;
1518
1519 case Qt::Key_Enter:
1520 case Qt::Key_Return: {
1521 const QString text = editor->toPlainText();
1522 if (text.isEmpty() || (text == QLatin1Char('.')) || (text == QLatin1String(".."))) {
1523 return true; // So a newline doesn't get inserted
1524 }
1525
1526 Q_EMIT commitData(editor);
1528 return true;
1529 }
1530
1531 case Qt::Key_Escape:
1533 return true;
1534
1535 default:
1536 return false;
1537 } // switch (keyEvent->key())
1538 } // case QEvent::KeyPress
1539
1540 case QEvent::FocusOut: {
1542 if (!w || w->parent() != editor) {
1543 Q_EMIT commitData(editor);
1544 Q_EMIT closeEditor(editor, NoHint);
1545 return true;
1546 } else {
1547 return false;
1548 }
1549 }
1550
1551 default:
1552 return false;
1553 } // switch (event->type())
1554}
1555
1556#include "moc_kfileitemdelegate.cpp"
@ FileItemRole
returns the KFileItem for a given index. roleName is "fileItem".
Definition kdirmodel.h:158
@ ChildCountRole
returns the number of items in a directory, or ChildCountUnknown. roleName is "childCount".
Definition kdirmodel.h:159
@ HasJobRole
returns whether or not there is a job on an item (file/directory). roleName is "hasJob".
Definition kdirmodel.h:160
KFileItemDelegate is intended to be used to provide a KDE file system view, when using one of the sta...
void setShowInformation(const InformationList &list)
Sets the list of information lines that are shown below the icon label in list views.
void setShadowColor(const QColor &color)
Sets the color used for drawing the text shadow.
QPointF shadowOffset
This property holds the horizontal and vertical offset for the text shadow.
QColor shadowColor
This property holds the color used for the text shadow.
InformationList information
This property holds which additional information (if any) should be shown below items in icon views.
void setShadowOffset(const QPointF &offset)
Sets the horizontal and vertical offset for the text shadow.
qreal shadowBlur
This property holds the blur radius for the text shadow.
bool editorEvent(QEvent *event, QAbstractItemModel *model, const QStyleOptionViewItem &option, const QModelIndex &index) override
Reimplemented from QAbstractItemDelegate.
void setMaximumSize(const QSize &size)
Sets the maximum size for KFileItemDelegate::sizeHint().
void setWrapMode(QTextOption::WrapMode wrapMode)
When the contents text needs to be wrapped, wrapMode strategy will be followed.
QRect iconRect(const QStyleOptionViewItem &option, const QModelIndex &index) const
Returns the rectangle of the icon that is aligned inside the decoration rectangle.
Information
This enum defines the additional information that can be displayed below item labels in icon views.
@ Comment
A simple comment that can be displayed to the user as is.
@ OctalPermissions
The permissions as an octal value, e.g. 0644.
@ OwnerAndGroup
The user and group that owns the file, e.g. root:root.
@ LocalPathOrUrl
The local path to the file or the URL in case it is not a local file.
@ AccessTime
The date and time the file/folder was last accessed.
@ FriendlyMimeType
The descriptive name for the MIME type, e.g. HTML Document.
@ CreationTime
The date and time the file/folder was created.
@ ModificationTime
The date and time the file/folder was last modified.
@ Permissions
A UNIX permissions string, e.g. -rwxr-xr-x.
@ Size
The file size for files, and the number of items for folders.
@ MimeType
The MIME type for the item, e.g. text/html.
@ LinkDest
The destination of a symbolic link.
@ NoInformation
No additional information will be shown for items.
@ Owner
The user name of the file owner, e.g. root.
QWidget * createEditor(QWidget *parent, const QStyleOptionViewItem &option, const QModelIndex &index) const override
Reimplemented from QAbstractItemDelegate.
QSize sizeHint(const QStyleOptionViewItem &option, const QModelIndex &index) const override
Returns the nominal size for the item referred to by index, given the provided options.
QSize maximumSize
This property holds the maximum size that can be returned by KFileItemDelegate::sizeHint().
bool showToolTipWhenElided
This property determines whether a tooltip will be shown by the delegate if the display role is elide...
KFileItemDelegate(QObject *parent=nullptr)
Constructs a new KFileItemDelegate.
void updateEditorGeometry(QWidget *editor, const QStyleOptionViewItem &option, const QModelIndex &index) const override
Reimplemented from QAbstractItemDelegate.
void setJobTransfersVisible(bool jobTransfersVisible)
Enable/Disable the displaying of an animated overlay that is shown for any destination urls (in the v...
void setShadowBlur(qreal radius)
Sets the blur radius for the text shadow.
bool jobTransfersVisible
This property determines if there are KIO jobs on a destination URL visible, then they will have a sm...
void setModelData(QWidget *editor, QAbstractItemModel *model, const QModelIndex &index) const override
Reimplemented from QAbstractItemDelegate.
~KFileItemDelegate() override
Destroys the item delegate.
InformationList showInformation() const
Returns the file item information that should be shown below item labels in list views.
void setShowToolTipWhenElided(bool showToolTip)
Sets whether a tooltip should be shown if the display role is elided containing the full display role...
QTextOption::WrapMode wrapMode() const
Returns the wrapping strategy followed to show text when it needs wrapping.
QRegion shape(const QStyleOptionViewItem &option, const QModelIndex &index)
Returns the shape of the item as a region.
bool eventFilter(QObject *object, QEvent *event) override
Reimplemented from QAbstractItemDelegate.
void setEditorData(QWidget *editor, const QModelIndex &index) const override
Reimplemented from QAbstractItemDelegate.
void paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const override
Paints the item indicated by index, using painter.
bool helpEvent(QHelpEvent *event, QAbstractItemView *view, const QStyleOptionViewItem &option, const QModelIndex &index) override
Reimplemented from QAbstractItemDelegate.
A KFileItem is a generic class to handle a file, local or remote.
Definition kfileitem.h:36
KIO::filesize_t size() const
Returns the size of the file, if known.
Q_INVOKABLE QString timeString(KFileItem::FileTimes which=ModificationTime) const
Requests the modification, access or creation time as a string, depending on which.
mode_t permissions() const
Returns the permissions of the file (stat.st_mode containing only permissions).
bool isNull() const
Return true if default-constructed.
QString permissionsString() const
Returns the access permissions for the file as a string.
static void semiTransparent(QImage &image)
QImage apply(const QImage &src, int effect, float value, const QColor &rgb, bool trans) const
bool hasEffect(int group, int state) const
static KIconLoader * global()
KIconEffect * iconEffect() const
Q_SCRIPTABLE Q_NOREPLY void start()
QString i18nc(const char *context, const char *text, const TYPE &arg...)
QString i18ncp(const char *context, const char *singular, const char *plural, const TYPE &arg...)
Type type(const QSqlDatabase &db)
A namespace for KIO globals.
KIOCORE_EXPORT QString convertSize(KIO::filesize_t size)
Converts size from bytes to the string representation.
Definition global.cpp:43
void informationList(QWidget *parent, const QString &text, const QStringList &strlist, const QString &title=QString(), const QString &dontShowAgainName=QString(), Options options=Notify)
void information(QWidget *parent, const QString &text, const QString &title=QString(), const QString &dontShowAgainName=QString(), Options options=Notify)
QString label(StandardShortcut id)
KCOREADDONS_EXPORT QString preProcessWrap(const QString &text)
void closeEditor(QWidget *editor, QAbstractItemDelegate::EndEditHint hint)
void commitData(QWidget *editor)
virtual bool helpEvent(QHelpEvent *event, QAbstractItemView *view, const QStyleOptionViewItem &option, const QModelIndex &index)
virtual bool setData(const QModelIndex &index, const QVariant &value, int role)
void setHorizontalScrollBarPolicy(Qt::ScrollBarPolicy)
void setVerticalScrollBarPolicy(Qt::ScrollBarPolicy)
QWidget * activePopupWidget()
QStyle * style()
int alpha() const const
int blue() const const
int green() const const
int red() const const
void setAlphaF(float alpha)
bool isRightToLeft()
void addPixmap(const QPixmap &pixmap, Mode mode, State state)
QIcon fromTheme(const QString &name)
Format_ARGB32_Premultiplied
QRect rect() const const
QString toString(QDate date, FormatType format) const const
QString suffixForFileName(const QString &fileName) const const
int column() const const
QVariant data(int role) const const
bool isValid() const const
Q_EMITQ_EMIT
virtual bool event(QEvent *e)
QObject * parent() const const
qreal devicePixelRatioF() const const
virtual QPaintEngine * paintEngine() const const=0
bool hasFeature(PaintEngineFeatures feature) const const
CompositionMode_DestinationIn
bool begin(QPaintDevice *device)
QPaintDevice * device() const const
void drawEllipse(const QPoint &center, int rx, int ry)
void drawImage(const QPoint &point, const QImage &image)
void drawPixmap(const QPoint &point, const QPixmap &pixmap)
bool end()
void fillRect(const QRect &rectangle, QGradient::Preset preset)
void restore()
void rotate(qreal angle)
void save()
void setBrush(Qt::BrushStyle style)
void setCompositionMode(CompositionMode mode)
void setPen(Qt::PenStyle style)
void setRenderHint(RenderHint hint, bool on)
void translate(const QPoint &offset)
QPixmap fromImage(QImage &&image, Qt::ImageConversionFlags flags)
bool isNull() const const
QRect rect() const const
QSize size() const const
QImage toImage() const const
int x() const const
int y() const const
QPoint toPoint() const const
void adjust(int dx1, int dy1, int dx2, int dy2)
QRect adjusted(int dx1, int dy1, int dx2, int dy2) const const
int bottom() const const
QPoint center() const const
int height() const const
int left() const const
void moveCenter(const QPoint &position)
int right() const const
void setBottom(int y)
void setLeft(int x)
void setRight(int x)
void setTop(int y)
QSize size() const const
int top() const const
QPoint topLeft() const const
void translate(const QPoint &offset)
int width() const const
int x() const const
int y() const const
QSize boundedTo(const QSize &otherSize) const const
int height() const const
bool isEmpty() const const
int & rheight()
int & rwidth()
void setWidth(int width)
int width() const const
bool endsWith(QChar c, Qt::CaseSensitivity cs) const const
qsizetype indexOf(QChar ch, qsizetype from, Qt::CaseSensitivity cs) const const
bool isEmpty() const const
qsizetype length() const const
QString number(double n, char format, int precision)
QString & replace(QChar before, QChar after, Qt::CaseSensitivity cs)
void reserve(qsizetype size)
QStringView mid(qsizetype start, qsizetype length) const const
PM_FocusFrameHMargin
PE_FrameFocusRect
SH_Widget_Animate
QRect alignedRect(Qt::LayoutDirection direction, Qt::Alignment alignment, const QSize &size, const QRect &rectangle)
virtual void drawPrimitive(PrimitiveElement element, const QStyleOption *option, QPainter *painter, const QWidget *widget) const const=0
virtual int pixelMetric(PixelMetric metric, const QStyleOption *option, const QWidget *widget) const const=0
Qt::Alignment visualAlignment(Qt::LayoutDirection direction, Qt::Alignment alignment)
QRect visualRect(Qt::LayoutDirection direction, const QRect &boundingRectangle, const QRect &logicalRectangle)
typedef Alignment
transparent
ForegroundRole
Orientation
ScrollBarAlwaysOff
QTextStream & left(QTextStream &stream)
QTextStream & right(QTextStream &stream)
bool movePosition(MoveOperation operation, MoveMode mode, int n)
void setAcceptRichText(bool accept)
void insertPlainText(const QString &text)
void selectAll()
void setAlignment(Qt::Alignment a)
void setTextCursor(const QTextCursor &cursor)
QTextCursor textCursor() const const
QString toPlainText() const const
void beginLayout()
QTextLine createLine()
void draw(QPainter *p, const QPointF &pos, const QList< FormatRange > &selections, const QRectF &clip) const const
void endLayout()
QFont font() const const
QTextLine lineAt(int i) const const
int lineCount() const const
void setFont(const QFont &font)
void setPosition(const QPointF &p)
void setText(const QString &string)
void setTextOption(const QTextOption &option)
QString text() const const
const QTextOption & textOption() const const
qreal height() const const
qreal naturalTextWidth() const const
void setLineWidth(qreal width)
void setPosition(const QPointF &pos)
int textLength() const const
int textStart() const const
void setAlignment(Qt::Alignment alignment)
void setTextDirection(Qt::LayoutDirection direction)
void setWrapMode(WrapMode mode)
QString toDisplayString(FormattingOptions options) const const
bool isValid() const const
bool toBool() const const
double toDouble(bool *ok) const const
int toInt(bool *ok) const const
QString toString() const const
int typeId() const const
int userType() const const
void setEnabled(bool)
void setGeometry(const QRect &)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Tue Mar 26 2024 11:18:52 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.