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

KDE's Doxygen guidelines are available online.