]> cloud.milkyroute.net Git - dolphin.git/blobdiff - src/kitemviews/kstandarditemlistwidget.cpp
Fix selection rect after porting from QFontMetrics::width()
[dolphin.git] / src / kitemviews / kstandarditemlistwidget.cpp
index 9cdb60e244f3f3028d7dc9fd0fe2e2338cec0a8d..214cc3755b505cb8f3656322001d7ac5052487d3 100644 (file)
 
 #include "kfileitemlistview.h"
 #include "kfileitemmodel.h"
 
 #include "kfileitemlistview.h"
 #include "kfileitemmodel.h"
+#include "private/kfileitemclipboard.h"
+#include "private/kitemlistroleeditor.h"
+#include "private/kpixmapmodifier.h"
 
 
-#include <KIcon>
 #include <KIconEffect>
 #include <KIconLoader>
 #include <KIconEffect>
 #include <KIconLoader>
-#include <KLocale>
-#include <kratingpainter.h>
+#include <KRatingPainter>
 #include <KStringHandler>
 #include <KStringHandler>
-#include <KDebug>
-
-#include "private/kfileitemclipboard.h"
-#include "private/kitemlistroleeditor.h"
-#include "private/kpixmapmodifier.h"
 
 
-#include <QFontMetricsF>
 #include <QGraphicsScene>
 #include <QGraphicsSceneResizeEvent>
 #include <QGraphicsView>
 #include <QGraphicsScene>
 #include <QGraphicsSceneResizeEvent>
 #include <QGraphicsView>
-#include <QPainter>
+#include <QGuiApplication>
+#include <QPixmapCache>
 #include <QStyleOption>
 #include <QStyleOption>
-#include <QTextLayout>
-#include <QTextLine>
 
 // #define KSTANDARDITEMLISTWIDGET_DEBUG
 
 
 // #define KSTANDARDITEMLISTWIDGET_DEBUG
 
@@ -54,77 +48,25 @@ KStandardItemListWidgetInformant::~KStandardItemListWidgetInformant()
 {
 }
 
 {
 }
 
-QSizeF KStandardItemListWidgetInformant::itemSizeHint(int index, const KItemListView* view) const
+void KStandardItemListWidgetInformant::calculateItemSizeHints(QVector<qreal>& logicalHeightHints, qreal& logicalWidthHint, const KItemListView* view) const
 {
 {
-    const QHash<QByteArray, QVariant> values = view->model()->data(index);
-    const KItemListStyleOption& option = view->styleOption();
-    const int additionalRolesCount = qMax(view->visibleRoles().count() - 1, 0);
-
     switch (static_cast<const KStandardItemListView*>(view)->itemLayout()) {
     switch (static_cast<const KStandardItemListView*>(view)->itemLayout()) {
-    case KStandardItemListWidget::IconsLayout: {
-        const QString text = KStringHandler::preProcessWrap(values["text"].toString());
-
-        const qreal itemWidth = view->itemSize().width();
-        const qreal maxWidth = itemWidth - 2 * option.padding;
-        QTextLine line;
-
-        // Calculate the number of lines required for wrapping the name
-        QTextOption textOption(Qt::AlignHCenter);
-        textOption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
-
-        qreal textHeight = 0;
-        QTextLayout layout(text, option.font);
-        layout.setTextOption(textOption);
-        layout.beginLayout();
-        while ((line = layout.createLine()).isValid()) {
-            line.setLineWidth(maxWidth);
-            line.naturalTextWidth();
-            textHeight += line.height();
-        }
-        layout.endLayout();
-
-        // Add one line for each additional information
-        textHeight += additionalRolesCount * option.fontMetrics.lineSpacing();
-
-        const qreal maxTextHeight = option.maxTextSize.height();
-        if (maxTextHeight > 0 && textHeight > maxTextHeight) {
-            textHeight = maxTextHeight;
-        }
-
-        return QSizeF(itemWidth, textHeight + option.iconSize + option.padding * 3);
-    }
-
-    case KStandardItemListWidget::CompactLayout: {
-        // For each row exactly one role is shown. Calculate the maximum required width that is necessary
-        // to show all roles without horizontal clipping.
-        qreal maximumRequiredWidth = 0.0;
-
-        foreach (const QByteArray& role, view->visibleRoles()) {
-            const QString text = roleText(role, values);
-            const qreal requiredWidth = option.fontMetrics.width(text);
-            maximumRequiredWidth = qMax(maximumRequiredWidth, requiredWidth);
-        }
+    case KStandardItemListView::IconsLayout:
+        calculateIconsLayoutItemSizeHints(logicalHeightHints, logicalWidthHint, view);
+        break;
 
 
-        qreal width = option.padding * 4 + option.iconSize + maximumRequiredWidth;
-        const qreal maxWidth = option.maxTextSize.width();
-        if (maxWidth > 0 && width > maxWidth) {
-            width = maxWidth;
-        }
-        const qreal height = option.padding * 2 + qMax(option.iconSize, (1 + additionalRolesCount) * option.fontMetrics.lineSpacing());
-        return QSizeF(width, height);
-    }
+    case KStandardItemListView::CompactLayout:
+        calculateCompactLayoutItemSizeHints(logicalHeightHints, logicalWidthHint, view);
+        break;
 
 
-    case KStandardItemListWidget::DetailsLayout: {
-        const qreal height = option.padding * 2 + qMax(option.iconSize, option.fontMetrics.height());
-        return QSizeF(-1, height);
-    }
+    case KStandardItemListView::DetailsLayout:
+        calculateDetailsLayoutItemSizeHints(logicalHeightHints, logicalWidthHint, view);
+        break;
 
     default:
         Q_ASSERT(false);
         break;
     }
 
     default:
         Q_ASSERT(false);
         break;
     }
-
-    return QSize();
 }
 
 qreal KStandardItemListWidgetInformant::preferredRoleColumnWidth(const QByteArray& role,
 }
 
 qreal KStandardItemListWidgetInformant::preferredRoleColumnWidth(const QByteArray& role,
@@ -137,16 +79,22 @@ qreal KStandardItemListWidgetInformant::preferredRoleColumnWidth(const QByteArra
     const QString text = roleText(role, values);
     qreal width = KStandardItemListWidget::columnPadding(option);
 
     const QString text = roleText(role, values);
     qreal width = KStandardItemListWidget::columnPadding(option);
 
+    const QFontMetrics& normalFontMetrics = option.fontMetrics;
+    const QFontMetrics linkFontMetrics(customizedFontForLinks(option.font));
+
     if (role == "rating") {
         width += KStandardItemListWidget::preferredRatingSize(option).width();
     } else {
     if (role == "rating") {
         width += KStandardItemListWidget::preferredRatingSize(option).width();
     } else {
-        width += option.fontMetrics.width(text);
+        // If current item is a link, we use the customized link font metrics instead of the normal font metrics.
+        const QFontMetrics& fontMetrics = itemIsLink(index, view) ? linkFontMetrics : normalFontMetrics;
+
+        width += fontMetrics.horizontalAdvance(text);
 
         if (role == "text") {
             if (view->supportsItemExpanding()) {
                 // Increase the width by the expansion-toggle and the current expansion level
                 const int expandedParentsCount = values.value("expandedParentsCount", 0).toInt();
 
         if (role == "text") {
             if (view->supportsItemExpanding()) {
                 // Increase the width by the expansion-toggle and the current expansion level
                 const int expandedParentsCount = values.value("expandedParentsCount", 0).toInt();
-                const qreal height = option.padding * 2 + qMax(option.iconSize, option.fontMetrics.height());
+                const qreal height = option.padding * 2 + qMax(option.iconSize, fontMetrics.height());
                 width += (expandedParentsCount + 1) * height;
             }
 
                 width += (expandedParentsCount + 1) * height;
             }
 
@@ -158,6 +106,18 @@ qreal KStandardItemListWidgetInformant::preferredRoleColumnWidth(const QByteArra
     return width;
 }
 
     return width;
 }
 
+QString KStandardItemListWidgetInformant::itemText(int index, const KItemListView* view) const
+{
+    return view->model()->data(index).value("text").toString();
+}
+
+bool KStandardItemListWidgetInformant::itemIsLink(int index, const KItemListView* view) const
+{
+    Q_UNUSED(index)
+    Q_UNUSED(view)
+    return false;
+}
+
 QString KStandardItemListWidgetInformant::roleText(const QByteArray& role,
                                                    const QHash<QByteArray, QVariant>& values) const
 {
 QString KStandardItemListWidgetInformant::roleText(const QByteArray& role,
                                                    const QHash<QByteArray, QVariant>& values) const
 {
@@ -168,6 +128,121 @@ QString KStandardItemListWidgetInformant::roleText(const QByteArray& role,
     return values.value(role).toString();
 }
 
     return values.value(role).toString();
 }
 
+QFont KStandardItemListWidgetInformant::customizedFontForLinks(const QFont& baseFont) const
+{
+    return baseFont;
+}
+
+void KStandardItemListWidgetInformant::calculateIconsLayoutItemSizeHints(QVector<qreal>& logicalHeightHints, qreal& logicalWidthHint, const KItemListView* view) const
+{
+    const KItemListStyleOption& option = view->styleOption();
+    const QFont& normalFont = option.font;
+    const int additionalRolesCount = qMax(view->visibleRoles().count() - 1, 0);
+
+    const qreal itemWidth = view->itemSize().width();
+    const qreal maxWidth = itemWidth - 2 * option.padding;
+    const qreal additionalRolesSpacing = additionalRolesCount * option.fontMetrics.lineSpacing();
+    const qreal spacingAndIconHeight = option.iconSize + option.padding * 3;
+
+    const QFont linkFont = customizedFontForLinks(normalFont);
+
+    QTextOption textOption(Qt::AlignHCenter);
+    textOption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
+
+    for (int index = 0; index < logicalHeightHints.count(); ++index) {
+        if (logicalHeightHints.at(index) > 0.0) {
+            continue;
+        }
+
+        // If the current item is a link, we use the customized link font instead of the normal font.
+        const QFont& font = itemIsLink(index, view) ? linkFont : normalFont;
+
+        const QString& text = KStringHandler::preProcessWrap(itemText(index, view));
+
+        // Calculate the number of lines required for wrapping the name
+        qreal textHeight = 0;
+        QTextLayout layout(text, font);
+        layout.setTextOption(textOption);
+        layout.beginLayout();
+        QTextLine line;
+        int lineCount = 0;
+        while ((line = layout.createLine()).isValid()) {
+            line.setLineWidth(maxWidth);
+            line.naturalTextWidth();
+            textHeight += line.height();
+
+            ++lineCount;
+            if (lineCount == option.maxTextLines) {
+                break;
+            }
+        }
+        layout.endLayout();
+
+        // Add one line for each additional information
+        textHeight += additionalRolesSpacing;
+
+        logicalHeightHints[index] = textHeight + spacingAndIconHeight;
+    }
+
+    logicalWidthHint = itemWidth;
+}
+
+void KStandardItemListWidgetInformant::calculateCompactLayoutItemSizeHints(QVector<qreal>& logicalHeightHints, qreal& logicalWidthHint, const KItemListView* view) const
+{
+    const KItemListStyleOption& option = view->styleOption();
+    const QFontMetrics& normalFontMetrics = option.fontMetrics;
+    const int additionalRolesCount = qMax(view->visibleRoles().count() - 1, 0);
+
+    const QList<QByteArray>& visibleRoles = view->visibleRoles();
+    const bool showOnlyTextRole = (visibleRoles.count() == 1) && (visibleRoles.first() == "text");
+    const qreal maxWidth = option.maxTextWidth;
+    const qreal paddingAndIconWidth = option.padding * 4 + option.iconSize;
+    const qreal height = option.padding * 2 + qMax(option.iconSize, (1 + additionalRolesCount) * normalFontMetrics.lineSpacing());
+
+    const QFontMetrics linkFontMetrics(customizedFontForLinks(option.font));
+
+    for (int index = 0; index < logicalHeightHints.count(); ++index) {
+        if (logicalHeightHints.at(index) > 0.0) {
+            continue;
+        }
+
+        // If the current item is a link, we use the customized link font metrics instead of the normal font metrics.
+        const QFontMetrics& fontMetrics = itemIsLink(index, view) ? linkFontMetrics : normalFontMetrics;
+
+        // For each row exactly one role is shown. Calculate the maximum required width that is necessary
+        // to show all roles without horizontal clipping.
+        qreal maximumRequiredWidth = 0.0;
+
+        if (showOnlyTextRole) {
+            maximumRequiredWidth = fontMetrics.horizontalAdvance(itemText(index, view));
+        } else {
+            const QHash<QByteArray, QVariant>& values = view->model()->data(index);
+            foreach (const QByteArray& role, visibleRoles) {
+                const QString& text = roleText(role, values);
+                const qreal requiredWidth = fontMetrics.horizontalAdvance(text);
+                maximumRequiredWidth = qMax(maximumRequiredWidth, requiredWidth);
+            }
+        }
+
+        qreal width = paddingAndIconWidth + maximumRequiredWidth;
+        if (maxWidth > 0 && width > maxWidth) {
+            width = maxWidth;
+        }
+
+        logicalHeightHints[index] = width;
+    }
+
+    logicalWidthHint = height;
+}
+
+void KStandardItemListWidgetInformant::calculateDetailsLayoutItemSizeHints(QVector<qreal>& logicalHeightHints, qreal& logicalWidthHint, const KItemListView* view) const
+{
+    const KItemListStyleOption& option = view->styleOption();
+    const qreal height = option.padding * 2 + qMax(option.iconSize, option.fontMetrics.height());
+    logicalHeightHints.fill(height);
+    logicalWidthHint = -1.0;
+}
+
 KStandardItemListWidget::KStandardItemListWidget(KItemListWidgetInformant* informant, QGraphicsItem* parent) :
     KItemListWidget(informant, parent),
     m_isCut(false),
 KStandardItemListWidget::KStandardItemListWidget(KItemListWidgetInformant* informant, QGraphicsItem* parent) :
     KItemListWidget(informant, parent),
     m_isCut(false),
@@ -193,7 +268,8 @@ KStandardItemListWidget::KStandardItemListWidget(KItemListWidgetInformant* infor
     m_additionalInfoTextColor(),
     m_overlay(),
     m_rating(),
     m_additionalInfoTextColor(),
     m_overlay(),
     m_rating(),
-    m_roleEditor(0)
+    m_roleEditor(nullptr),
+    m_oldRoleEditor(nullptr)
 {
 }
 
 {
 }
 
@@ -202,7 +278,13 @@ KStandardItemListWidget::~KStandardItemListWidget()
     qDeleteAll(m_textInfo);
     m_textInfo.clear();
 
     qDeleteAll(m_textInfo);
     m_textInfo.clear();
 
-    delete m_roleEditor;
+    if (m_roleEditor) {
+        m_roleEditor->deleteLater();
+    }
+
+    if (m_oldRoleEditor) {
+        m_oldRoleEditor->deleteLater();
+    }
 }
 
 void KStandardItemListWidget::setLayout(Layout layout)
 }
 
 void KStandardItemListWidget::setLayout(Layout layout)
@@ -246,23 +328,65 @@ void KStandardItemListWidget::paint(QPainter* painter, const QStyleOptionGraphic
 
     const KItemListStyleOption& itemListStyleOption = styleOption();
     if (isHovered()) {
 
     const KItemListStyleOption& itemListStyleOption = styleOption();
     if (isHovered()) {
-        // Blend the unhovered and hovered pixmap if the hovering
-        // animation is ongoing
         if (hoverOpacity() < 1.0) {
         if (hoverOpacity() < 1.0) {
-            drawPixmap(painter, m_pixmap);
-        }
+            /*
+             * Linear interpolation between m_pixmap and m_hoverPixmap.
+             *
+             * Note that this cannot be achieved by painting m_hoverPixmap over
+             * m_pixmap, even if the opacities are adjusted. For details see
+             * https://git.reviewboard.kde.org/r/109614/
+             */
+            // Paint pixmap1 so that pixmap1 = m_pixmap * (1.0 - hoverOpacity())
+            QPixmap pixmap1(m_pixmap.size());
+            pixmap1.setDevicePixelRatio(m_pixmap.devicePixelRatio());
+            pixmap1.fill(Qt::transparent);
+            {
+                QPainter p(&pixmap1);
+                p.setOpacity(1.0 - hoverOpacity());
+                p.drawPixmap(0, 0, m_pixmap);
+            }
 
 
-        const qreal opacity = painter->opacity();
-        painter->setOpacity(hoverOpacity() * opacity);
-        drawPixmap(painter, m_hoverPixmap);
-        painter->setOpacity(opacity);
+            // Paint pixmap2 so that pixmap2 = m_hoverPixmap * hoverOpacity()
+            QPixmap pixmap2(pixmap1.size());
+            pixmap2.setDevicePixelRatio(pixmap1.devicePixelRatio());
+            pixmap2.fill(Qt::transparent);
+            {
+                QPainter p(&pixmap2);
+                p.setOpacity(hoverOpacity());
+                p.drawPixmap(0, 0, m_hoverPixmap);
+            }
+
+            // Paint pixmap2 on pixmap1 using CompositionMode_Plus
+            // Now pixmap1 = pixmap2 + m_pixmap * (1.0 - hoverOpacity())
+            //             = m_hoverPixmap * hoverOpacity() + m_pixmap * (1.0 - hoverOpacity())
+            {
+                QPainter p(&pixmap1);
+                p.setCompositionMode(QPainter::CompositionMode_Plus);
+                p.drawPixmap(0, 0, pixmap2);
+            }
+
+            // Finally paint pixmap1 on the widget
+            drawPixmap(painter, pixmap1);
+        } else {
+            drawPixmap(painter, m_hoverPixmap);
+        }
     } else {
         drawPixmap(painter, m_pixmap);
     }
 
     painter->setFont(m_customizedFont);
     } else {
         drawPixmap(painter, m_pixmap);
     }
 
     painter->setFont(m_customizedFont);
-    painter->setPen(m_isHidden ? m_additionalInfoTextColor : textColor());
+    painter->setPen(textColor());
     const TextInfo* textInfo = m_textInfo.value("text");
     const TextInfo* textInfo = m_textInfo.value("text");
+
+    if (!textInfo) {
+        // It seems that we can end up here even if m_textInfo does not contain
+        // the key "text", see bug 306167. According to triggerCacheRefreshing(),
+        // this can only happen if the index is negative. This can happen when
+        // the item is about to be removed, see KItemListView::slotItemsRemoved().
+        // TODO: try to reproduce the crash and find a better fix.
+        return;
+    }
+
     painter->drawStaticText(textInfo->pos, textInfo->staticText);
 
     bool clipAdditionalInfoBounds = false;
     painter->drawStaticText(textInfo->pos, textInfo->staticText);
 
     bool clipAdditionalInfoBounds = false;
@@ -291,7 +415,7 @@ void KStandardItemListWidget::paint(QPainter* painter, const QStyleOptionGraphic
         QPointF pos = ratingTextInfo->pos;
         const Qt::Alignment align = ratingTextInfo->staticText.textOption().alignment();
         if (align & Qt::AlignHCenter) {
         QPointF pos = ratingTextInfo->pos;
         const Qt::Alignment align = ratingTextInfo->staticText.textOption().alignment();
         if (align & Qt::AlignHCenter) {
-            pos.rx() += (size().width() - m_rating.width()) / 2;
+            pos.rx() += (size().width() - m_rating.width()) / 2 - 2;
         }
         painter->drawPixmap(pos, m_rating);
     }
         }
         painter->drawPixmap(pos, m_rating);
     }
@@ -305,6 +429,9 @@ void KStandardItemListWidget::paint(QPainter* painter, const QStyleOptionGraphic
     painter->setPen(Qt::green);
     painter->drawRect(m_iconRect);
 
     painter->setPen(Qt::green);
     painter->drawRect(m_iconRect);
 
+    painter->setPen(Qt::blue);
+    painter->drawRect(m_textRect);
+
     painter->setPen(Qt::red);
     painter->drawText(QPointF(0, m_customizedFontMetrics.height()), QString::number(index()));
     painter->drawRect(rect());
     painter->setPen(Qt::red);
     painter->drawText(QPointF(0, m_customizedFontMetrics.height()), QString::number(index()));
     painter->drawRect(rect());
@@ -351,7 +478,7 @@ QRectF KStandardItemListWidget::textFocusRect() const
         const KItemListStyleOption& option = styleOption();
         if (option.extendedSelectionRegion) {
             const QString text = textInfo->staticText.text();
         const KItemListStyleOption& option = styleOption();
         if (option.extendedSelectionRegion) {
             const QString text = textInfo->staticText.text();
-            rect.setWidth(m_customizedFontMetrics.width(text) + 2 * option.padding);
+            rect.setWidth(m_customizedFontMetrics.horizontalAdvance(text) + 2 * option.padding);
         }
 
         return rect;
         }
 
         return rect;
@@ -364,6 +491,29 @@ QRectF KStandardItemListWidget::textFocusRect() const
     return m_textRect;
 }
 
     return m_textRect;
 }
 
+QRectF KStandardItemListWidget::selectionRect() const
+{
+    const_cast<KStandardItemListWidget*>(this)->triggerCacheRefreshing();
+
+    switch (m_layout) {
+    case IconsLayout:
+        return m_textRect;
+
+    case CompactLayout:
+    case DetailsLayout: {
+        const int padding = styleOption().padding;
+        QRectF adjustedIconRect = iconRect().adjusted(-padding, -padding, padding, padding);
+        return adjustedIconRect | m_textRect;
+    }
+
+    default:
+        Q_ASSERT(false);
+        break;
+    }
+
+    return m_textRect;
+}
+
 QRectF KStandardItemListWidget::expansionToggleRect() const
 {
     const_cast<KStandardItemListWidget*>(this)->triggerCacheRefreshing();
 QRectF KStandardItemListWidget::expansionToggleRect() const
 {
     const_cast<KStandardItemListWidget*>(this)->triggerCacheRefreshing();
@@ -450,7 +600,7 @@ void KStandardItemListWidget::refreshCache()
 
 bool KStandardItemListWidget::isRoleRightAligned(const QByteArray& role) const
 {
 
 bool KStandardItemListWidget::isRoleRightAligned(const QByteArray& role) const
 {
-    Q_UNUSED(role);
+    Q_UNUSED(role)
     return false;
 }
 
     return false;
 }
 
@@ -464,6 +614,11 @@ QFont KStandardItemListWidget::customizedFont(const QFont& baseFont) const
     return baseFont;
 }
 
     return baseFont;
 }
 
+QPalette::ColorRole KStandardItemListWidget::normalTextColorRole() const
+{
+    return QPalette::Text;
+}
+
 void KStandardItemListWidget::setTextColor(const QColor& color)
 {
     if (color != m_customTextColor) {
 void KStandardItemListWidget::setTextColor(const QColor& color)
 {
     if (color != m_customTextColor) {
@@ -475,13 +630,17 @@ void KStandardItemListWidget::setTextColor(const QColor& color)
 
 QColor KStandardItemListWidget::textColor() const
 {
 
 QColor KStandardItemListWidget::textColor() const
 {
-    if (m_customTextColor.isValid() && !isSelected()) {
-        return m_customTextColor;
+    if (!isSelected()) {
+        if (m_isHidden) {
+            return m_additionalInfoTextColor;
+        } else if (m_customTextColor.isValid()) {
+            return m_customTextColor;
+        }
     }
 
     const QPalette::ColorGroup group = isActiveWindow() ? QPalette::Active : QPalette::Inactive;
     }
 
     const QPalette::ColorGroup group = isActiveWindow() ? QPalette::Active : QPalette::Inactive;
-    const QPalette::ColorRole role = isSelected() ? QPalette::HighlightedText : QPalette::Text;
-    return styleOption().palette.brush(group, role).color();
+    const QPalette::ColorRole role = isSelected() ? QPalette::HighlightedText : normalTextColorRole();
+    return styleOption().palette.color(group, role);
 }
 
 void KStandardItemListWidget::setOverlay(const QPixmap& overlay)
 }
 
 void KStandardItemListWidget::setOverlay(const QPixmap& overlay)
@@ -506,7 +665,7 @@ QString KStandardItemListWidget::roleText(const QByteArray& role,
 void KStandardItemListWidget::dataChanged(const QHash<QByteArray, QVariant>& current,
                                           const QSet<QByteArray>& roles)
 {
 void KStandardItemListWidget::dataChanged(const QHash<QByteArray, QVariant>& current,
                                           const QSet<QByteArray>& roles)
 {
-    Q_UNUSED(current);
+    Q_UNUSED(current)
 
     m_dirtyContent = true;
 
 
     m_dirtyContent = true;
 
@@ -517,6 +676,12 @@ void KStandardItemListWidget::dataChanged(const QHash<QByteArray, QVariant>& cur
         dirtyRoles = roles;
     }
 
         dirtyRoles = roles;
     }
 
+    // The URL might have changed (i.e., if the sort order of the items has
+    // been changed). Therefore, the "is cut" state must be updated.
+    KFileItemClipboard* clipboard = KFileItemClipboard::instance();
+    const QUrl itemUrl = data().value("url").toUrl();
+    m_isCut = clipboard->isCut(itemUrl);
+
     // The icon-state might depend from other roles and hence is
     // marked as dirty whenever a role has been changed
     dirtyRoles.insert("iconPixmap");
     // The icon-state might depend from other roles and hence is
     // marked as dirty whenever a role has been changed
     dirtyRoles.insert("iconPixmap");
@@ -532,7 +697,7 @@ void KStandardItemListWidget::dataChanged(const QHash<QByteArray, QVariant>& cur
 void KStandardItemListWidget::visibleRolesChanged(const QList<QByteArray>& current,
                                               const QList<QByteArray>& previous)
 {
 void KStandardItemListWidget::visibleRolesChanged(const QList<QByteArray>& current,
                                               const QList<QByteArray>& previous)
 {
-    Q_UNUSED(previous);
+    Q_UNUSED(previous)
     m_sortedVisibleRoles = current;
     m_dirtyLayout = true;
 }
     m_sortedVisibleRoles = current;
     m_dirtyLayout = true;
 }
@@ -541,38 +706,38 @@ void KStandardItemListWidget::columnWidthChanged(const QByteArray& role,
                                              qreal current,
                                              qreal previous)
 {
                                              qreal current,
                                              qreal previous)
 {
-    Q_UNUSED(role);
-    Q_UNUSED(current);
-    Q_UNUSED(previous);
+    Q_UNUSED(role)
+    Q_UNUSED(current)
+    Q_UNUSED(previous)
     m_dirtyLayout = true;
 }
 
 void KStandardItemListWidget::styleOptionChanged(const KItemListStyleOption& current,
                                              const KItemListStyleOption& previous)
 {
     m_dirtyLayout = true;
 }
 
 void KStandardItemListWidget::styleOptionChanged(const KItemListStyleOption& current,
                                              const KItemListStyleOption& previous)
 {
-    Q_UNUSED(current);
-    Q_UNUSED(previous);
+    Q_UNUSED(current)
+    Q_UNUSED(previous)
     updateAdditionalInfoTextColor();
     m_dirtyLayout = true;
 }
 
 void KStandardItemListWidget::hoveredChanged(bool hovered)
 {
     updateAdditionalInfoTextColor();
     m_dirtyLayout = true;
 }
 
 void KStandardItemListWidget::hoveredChanged(bool hovered)
 {
-    Q_UNUSED(hovered);
+    Q_UNUSED(hovered)
     m_dirtyLayout = true;
 }
 
 void KStandardItemListWidget::selectedChanged(bool selected)
 {
     m_dirtyLayout = true;
 }
 
 void KStandardItemListWidget::selectedChanged(bool selected)
 {
-    Q_UNUSED(selected);
+    Q_UNUSED(selected)
     updateAdditionalInfoTextColor();
     m_dirtyContent = true;
 }
 
 void KStandardItemListWidget::siblingsInformationChanged(const QBitArray& current, const QBitArray& previous)
 {
     updateAdditionalInfoTextColor();
     m_dirtyContent = true;
 }
 
 void KStandardItemListWidget::siblingsInformationChanged(const QBitArray& current, const QBitArray& previous)
 {
-    Q_UNUSED(current);
-    Q_UNUSED(previous);
+    Q_UNUSED(current)
+    Q_UNUSED(previous)
     m_dirtyLayout = true;
 }
 
     m_dirtyLayout = true;
 }
 
@@ -583,14 +748,24 @@ int KStandardItemListWidget::selectionLength(const QString& text) const
 
 void KStandardItemListWidget::editedRoleChanged(const QByteArray& current, const QByteArray& previous)
 {
 
 void KStandardItemListWidget::editedRoleChanged(const QByteArray& current, const QByteArray& previous)
 {
-    Q_UNUSED(previous);
+    Q_UNUSED(previous)
 
 
-   QGraphicsView* parent = scene()->views()[0];
-   if (current.isEmpty() || !parent || current != "text") {
+    QGraphicsView* parent = scene()->views()[0];
+    if (current.isEmpty() || !parent || current != "text") {
         if (m_roleEditor) {
             emit roleEditingCanceled(index(), current, data().value(current));
         if (m_roleEditor) {
             emit roleEditingCanceled(index(), current, data().value(current));
-            m_roleEditor->deleteLater();
-            m_roleEditor = 0;
+
+            disconnect(m_roleEditor, &KItemListRoleEditor::roleEditingCanceled,
+                       this, &KStandardItemListWidget::slotRoleEditingCanceled);
+            disconnect(m_roleEditor, &KItemListRoleEditor::roleEditingFinished,
+                       this, &KStandardItemListWidget::slotRoleEditingFinished);
+
+            if (m_oldRoleEditor) {
+                m_oldRoleEditor->deleteLater();
+            }
+            m_oldRoleEditor = m_roleEditor;
+            m_roleEditor->hide();
+            m_roleEditor = nullptr;
         }
         return;
     }
         }
         return;
     }
@@ -600,7 +775,6 @@ void KStandardItemListWidget::editedRoleChanged(const QByteArray& current, const
     const TextInfo* textInfo = m_textInfo.value("text");
 
     m_roleEditor = new KItemListRoleEditor(parent);
     const TextInfo* textInfo = m_textInfo.value("text");
 
     m_roleEditor = new KItemListRoleEditor(parent);
-    m_roleEditor->setIndex(index());
     m_roleEditor->setRole(current);
     m_roleEditor->setFont(styleOption().font);
 
     m_roleEditor->setRole(current);
     m_roleEditor->setFont(styleOption().font);
 
@@ -619,10 +793,10 @@ void KStandardItemListWidget::editedRoleChanged(const QByteArray& current, const
         m_roleEditor->setTextCursor(cursor);
     }
 
         m_roleEditor->setTextCursor(cursor);
     }
 
-    connect(m_roleEditor, SIGNAL(roleEditingCanceled(int,QByteArray,QVariant)),
-            this, SLOT(slotRoleEditingCanceled(int,QByteArray,QVariant)));
-    connect(m_roleEditor, SIGNAL(roleEditingFinished(int,QByteArray,QVariant)),
-            this, SLOT(slotRoleEditingFinished(int,QByteArray,QVariant)));
+    connect(m_roleEditor, &KItemListRoleEditor::roleEditingCanceled,
+            this, &KStandardItemListWidget::slotRoleEditingCanceled);
+    connect(m_roleEditor, &KItemListRoleEditor::roleEditingFinished,
+            this, &KStandardItemListWidget::slotRoleEditingFinished);
 
     // Adjust the geometry of the editor
     QRectF rect = roleEditingRect(current);
 
     // Adjust the geometry of the editor
     QRectF rect = roleEditingRect(current);
@@ -656,24 +830,41 @@ void KStandardItemListWidget::showEvent(QShowEvent* event)
     // Listen to changes of the clipboard to mark the item as cut/uncut
     KFileItemClipboard* clipboard = KFileItemClipboard::instance();
 
     // Listen to changes of the clipboard to mark the item as cut/uncut
     KFileItemClipboard* clipboard = KFileItemClipboard::instance();
 
-    const KUrl itemUrl = data().value("url").value<KUrl>();
+    const QUrl itemUrl = data().value("url").toUrl();
     m_isCut = clipboard->isCut(itemUrl);
 
     m_isCut = clipboard->isCut(itemUrl);
 
-    connect(clipboard, SIGNAL(cutItemsChanged()),
-            this, SLOT(slotCutItemsChanged()));
+    connect(clipboard, &KFileItemClipboard::cutItemsChanged,
+            this, &KStandardItemListWidget::slotCutItemsChanged);
 }
 
 void KStandardItemListWidget::hideEvent(QHideEvent* event)
 {
 }
 
 void KStandardItemListWidget::hideEvent(QHideEvent* event)
 {
-    disconnect(KFileItemClipboard::instance(), SIGNAL(cutItemsChanged()),
-               this, SLOT(slotCutItemsChanged()));
+    disconnect(KFileItemClipboard::instance(), &KFileItemClipboard::cutItemsChanged,
+               this, &KStandardItemListWidget::slotCutItemsChanged);
 
     KItemListWidget::hideEvent(event);
 }
 
 
     KItemListWidget::hideEvent(event);
 }
 
+bool KStandardItemListWidget::event(QEvent *event)
+{
+    if (event->type() == QEvent::WindowDeactivate || event->type() == QEvent::WindowActivate
+            || event->type() == QEvent::PaletteChange) {
+        m_dirtyContent = true;
+    }
+
+    return KItemListWidget::event(event);
+}
+
+void KStandardItemListWidget::finishRoleEditing()
+{
+    if (!editedRole().isEmpty() && m_roleEditor) {
+        slotRoleEditingFinished(editedRole(), KIO::encodeFileName(m_roleEditor->toPlainText()));
+    }
+}
+
 void KStandardItemListWidget::slotCutItemsChanged()
 {
 void KStandardItemListWidget::slotCutItemsChanged()
 {
-    const KUrl itemUrl = data().value("url").value<KUrl>();
+    const QUrl itemUrl = data().value("url").toUrl();
     const bool isCut = KFileItemClipboard::instance()->isCut(itemUrl);
     if (m_isCut != isCut) {
         m_isCut = isCut;
     const bool isCut = KFileItemClipboard::instance()->isCut(itemUrl);
     if (m_isCut != isCut) {
         m_isCut = isCut;
@@ -683,21 +874,19 @@ void KStandardItemListWidget::slotCutItemsChanged()
     }
 }
 
     }
 }
 
-void KStandardItemListWidget::slotRoleEditingCanceled(int index,
-                                                      const QByteArray& role,
+void KStandardItemListWidget::slotRoleEditingCanceled(const QByteArray& role,
                                                       const QVariant& value)
 {
     closeRoleEditor();
                                                       const QVariant& value)
 {
     closeRoleEditor();
-    emit roleEditingCanceled(index, role, value);
+    emit roleEditingCanceled(index(), role, value);
     setEditedRole(QByteArray());
 }
 
     setEditedRole(QByteArray());
 }
 
-void KStandardItemListWidget::slotRoleEditingFinished(int index,
-                                                      const QByteArray& role,
+void KStandardItemListWidget::slotRoleEditingFinished(const QByteArray& role,
                                                       const QVariant& value)
 {
     closeRoleEditor();
                                                       const QVariant& value)
 {
     closeRoleEditor();
-    emit roleEditingFinished(index, role, value);
+    emit roleEditingFinished(index(), role, value);
     setEditedRole(QByteArray());
 }
 
     setEditedRole(QByteArray());
 }
 
@@ -728,14 +917,14 @@ void KStandardItemListWidget::updateExpansionArea()
 {
     if (m_supportsItemExpanding) {
         const QHash<QByteArray, QVariant> values = data();
 {
     if (m_supportsItemExpanding) {
         const QHash<QByteArray, QVariant> values = data();
-        Q_ASSERT(values.contains("expandedParentsCount"));
         const int expandedParentsCount = values.value("expandedParentsCount", 0).toInt();
         if (expandedParentsCount >= 0) {
         const int expandedParentsCount = values.value("expandedParentsCount", 0).toInt();
         if (expandedParentsCount >= 0) {
+            const KItemListStyleOption& option = styleOption();
             const qreal widgetHeight = size().height();
             const qreal widgetHeight = size().height();
-            const qreal inc = (widgetHeight - KIconLoader::SizeSmall) / 2;
+            const qreal inc = (widgetHeight - option.iconSize) / 2;
             const qreal x = expandedParentsCount * widgetHeight + inc;
             const qreal y = inc;
             const qreal x = expandedParentsCount * widgetHeight + inc;
             const qreal y = inc;
-            m_expansionArea = QRectF(x, y, KIconLoader::SizeSmall, KIconLoader::SizeSmall);
+            m_expansionArea = QRectF(x, y, option.iconSize, option.iconSize);
             return;
         }
     }
             return;
         }
     }
@@ -774,29 +963,15 @@ void KStandardItemListWidget::updatePixmapCache()
             if (iconName.isEmpty()) {
                 // The icon-name has not been not resolved by KFileItemModelRolesUpdater,
                 // use a generic icon as fallback
             if (iconName.isEmpty()) {
                 // The icon-name has not been not resolved by KFileItemModelRolesUpdater,
                 // use a generic icon as fallback
-                iconName = QLatin1String("unknown");
+                iconName = QStringLiteral("unknown");
             }
             }
-            m_pixmap = pixmapForIcon(iconName, maxIconHeight);
-        } else if (m_pixmap.width() != maxIconWidth || m_pixmap.height() != maxIconHeight) {
+            const QStringList overlays = values["iconOverlays"].toStringList();
+            m_pixmap = pixmapForIcon(iconName, overlays, maxIconHeight, m_layout != IconsLayout && isActiveWindow() && isSelected() ? QIcon::Selected : QIcon::Normal);
+
+        } else if (m_pixmap.width() / m_pixmap.devicePixelRatio() != maxIconWidth || m_pixmap.height() / m_pixmap.devicePixelRatio() != maxIconHeight) {
             // A custom pixmap has been applied. Assure that the pixmap
             // is scaled to the maximum available size.
             // A custom pixmap has been applied. Assure that the pixmap
             // is scaled to the maximum available size.
-            KPixmapModifier::scale(m_pixmap, QSize(maxIconWidth, maxIconHeight));
-        }
-
-        const QStringList overlays = values["iconOverlays"].toStringList();
-
-        // Strangely KFileItem::overlays() returns empty string-values, so
-        // we need to check first whether an overlay must be drawn at all.
-        // It is more efficient to do it here, as KIconLoader::drawOverlays()
-        // assumes that an overlay will be drawn and has some additional
-        // setup time.
-        foreach (const QString& overlay, overlays) {
-            if (!overlay.isEmpty()) {
-                // There is at least one overlay, draw all overlays above m_pixmap
-                // and cancel the check
-                KIconLoader::global()->drawOverlays(overlays, m_pixmap, KIconLoader::Desktop);
-                break;
-            }
+            KPixmapModifier::scale(m_pixmap, QSize(maxIconWidth, maxIconHeight) * qApp->devicePixelRatio());
         }
 
         if (m_isCut) {
         }
 
         if (m_isCut) {
@@ -808,17 +983,17 @@ void KStandardItemListWidget::updatePixmapCache()
             KIconEffect::semiTransparent(m_pixmap);
         }
 
             KIconEffect::semiTransparent(m_pixmap);
         }
 
-        if (isSelected()) {
+        if (m_layout == IconsLayout && isSelected()) {
             const QColor color = palette().brush(QPalette::Normal, QPalette::Highlight).color();
             QImage image = m_pixmap.toImage();
             const QColor color = palette().brush(QPalette::Normal, QPalette::Highlight).color();
             QImage image = m_pixmap.toImage();
-            KIconEffect::colorize(image, color, 1.0f);
+            KIconEffect::colorize(image, color, 0.8f);
             m_pixmap = QPixmap::fromImage(image);
         }
     }
 
     if (!m_overlay.isNull()) {
         QPainter painter(&m_pixmap);
             m_pixmap = QPixmap::fromImage(image);
         }
     }
 
     if (!m_overlay.isNull()) {
         QPainter painter(&m_pixmap);
-        painter.drawPixmap(0, m_pixmap.height() - m_overlay.height(), m_overlay);
+        painter.drawPixmap(0, (m_pixmap.height() - m_overlay.height()) / m_pixmap.devicePixelRatio(), m_overlay);
     }
 
     int scaledIconSize = 0;
     }
 
     int scaledIconSize = 0;
@@ -836,19 +1011,26 @@ void KStandardItemListWidget::updatePixmapCache()
     const int maxScaledIconHeight = scaledIconSize;
 
     m_scaledPixmapSize = m_pixmap.size();
     const int maxScaledIconHeight = scaledIconSize;
 
     m_scaledPixmapSize = m_pixmap.size();
-    m_scaledPixmapSize.scale(maxScaledIconWidth, maxScaledIconHeight, Qt::KeepAspectRatio);
+    m_scaledPixmapSize.scale(maxScaledIconWidth * qApp->devicePixelRatio(), maxScaledIconHeight * qApp->devicePixelRatio(), Qt::KeepAspectRatio);
+    m_scaledPixmapSize = m_scaledPixmapSize / qApp->devicePixelRatio();
 
     if (iconOnTop) {
         // Center horizontally and align on bottom within the icon-area
 
     if (iconOnTop) {
         // Center horizontally and align on bottom within the icon-area
-        m_pixmapPos.setX((widgetSize.width() - m_scaledPixmapSize.width()) / 2);
+        m_pixmapPos.setX((widgetSize.width() - m_scaledPixmapSize.width()) / 2.0);
         m_pixmapPos.setY(padding + scaledIconSize - m_scaledPixmapSize.height());
     } else {
         // Center horizontally and vertically within the icon-area
         const TextInfo* textInfo = m_textInfo.value("text");
         m_pixmapPos.setY(padding + scaledIconSize - m_scaledPixmapSize.height());
     } else {
         // Center horizontally and vertically within the icon-area
         const TextInfo* textInfo = m_textInfo.value("text");
-        m_pixmapPos.setX(textInfo->pos.x() - 2 * padding
-                         - (scaledIconSize + m_scaledPixmapSize.width()) / 2);
-        m_pixmapPos.setY(padding
-                         + (scaledIconSize - m_scaledPixmapSize.height()) / 2);
+        m_pixmapPos.setX(textInfo->pos.x() - 2.0 * padding
+                      - (scaledIconSize + m_scaledPixmapSize.width()) / 2.0);
+
+        // Derive icon's vertical center from the center of the text frame, including
+        // any necessary adjustment if the font's midline is offset from the frame center
+        const qreal midlineShift = m_customizedFontMetrics.height() / 2.0
+                    - m_customizedFontMetrics.descent()
+                    - m_customizedFontMetrics.capHeight() / 2.0;
+        m_pixmapPos.setY(m_textRect.center().y() + midlineShift - m_scaledPixmapSize.height() / 2.0);
+
     }
 
     m_iconRect = QRectF(m_pixmapPos, QSizeF(m_scaledPixmapSize));
     }
 
     m_iconRect = QRectF(m_pixmapPos, QSizeF(m_scaledPixmapSize));
@@ -913,15 +1095,17 @@ void KStandardItemListWidget::updateTextsCache()
 
         const qreal availableWidth = (m_layout == DetailsLayout)
                                      ? columnWidth("rating") - columnPadding(option)
 
         const qreal availableWidth = (m_layout == DetailsLayout)
                                      ? columnWidth("rating") - columnPadding(option)
-                                     : m_textRect.width();
+                                     : size().width();
         if (ratingSize.width() > availableWidth) {
             ratingSize.rwidth() = availableWidth;
         }
         if (ratingSize.width() > availableWidth) {
             ratingSize.rwidth() = availableWidth;
         }
-        m_rating = QPixmap(ratingSize.toSize());
+        const qreal dpr = qApp->devicePixelRatio();
+        m_rating = QPixmap(ratingSize.toSize() * dpr);
+        m_rating.setDevicePixelRatio(dpr);
         m_rating.fill(Qt::transparent);
 
         QPainter painter(&m_rating);
         m_rating.fill(Qt::transparent);
 
         QPainter painter(&m_rating);
-        const QRect rect(0, 0, m_rating.width(), m_rating.height());
+        const QRect rect(QPoint(0, 0), ratingSize.toSize());
         const int rating = data().value("rating").toInt();
         KRatingPainter::paintRating(&painter, rect, Qt::AlignJustify | Qt::AlignVCenter, rating);
     } else if (!m_rating.isNull()) {
         const int rating = data().value("rating").toInt();
         KRatingPainter::paintRating(&painter, rect, Qt::AlignJustify | Qt::AlignVCenter, rating);
     } else if (!m_rating.isNull()) {
@@ -929,6 +1113,26 @@ void KStandardItemListWidget::updateTextsCache()
     }
 }
 
     }
 }
 
+QString KStandardItemListWidget::elideRightKeepExtension(const QString &text, int elidingWidth) const
+{
+    const auto extensionIndex = text.lastIndexOf('.');
+    if (extensionIndex != -1) {
+        // has file extension
+        const auto extensionLength = text.length() - extensionIndex;
+        const auto extensionWidth = m_customizedFontMetrics.horizontalAdvance(text.right(extensionLength));
+        if (elidingWidth > extensionWidth && extensionLength < 6 && (float(extensionWidth) / float(elidingWidth)) < 0.3) {
+            // if we have room to display the file extension and the extension is not too long
+            QString ret = m_customizedFontMetrics.elidedText(text.chopped(extensionLength),
+                                                             Qt::ElideRight,
+                                                             elidingWidth - extensionWidth);
+            ret.append(text.right(extensionLength));
+            return ret;
+        }
+    }
+    return m_customizedFontMetrics.elidedText(text,Qt::ElideRight,
+                                              elidingWidth);
+}
+
 void KStandardItemListWidget::updateIconsLayoutTextCache()
 {
     //      +------+
 void KStandardItemListWidget::updateIconsLayoutTextCache()
 {
     //      +------+
@@ -960,9 +1164,6 @@ void KStandardItemListWidget::updateIconsLayoutTextCache()
     qreal nameHeight = 0;
     QTextLine line;
 
     qreal nameHeight = 0;
     QTextLine line;
 
-    const int additionalRolesCount = qMax(visibleRoles().count() - 1, 0);
-    const int maxNameLines = (option.maxTextSize.height() / int(lineSpacing)) - additionalRolesCount;
-
     QTextLayout layout(nameTextInfo->staticText.text(), m_customizedFont);
     layout.setTextOption(nameTextInfo->staticText.textOption());
     layout.beginLayout();
     QTextLayout layout(nameTextInfo->staticText.text(), m_customizedFont);
     layout.setTextOption(nameTextInfo->staticText.textOption());
     layout.beginLayout();
@@ -973,18 +1174,29 @@ void KStandardItemListWidget::updateIconsLayoutTextCache()
         nameHeight += line.height();
 
         ++nameLineIndex;
         nameHeight += line.height();
 
         ++nameLineIndex;
-        if (nameLineIndex == maxNameLines) {
+        if (nameLineIndex == option.maxTextLines) {
             // The maximum number of textlines has been reached. If this is
             // the case provide an elided text if necessary.
             const int textLength = line.textStart() + line.textLength();
             if (textLength < nameText.length()) {
                 // Elide the last line of the text
             // The maximum number of textlines has been reached. If this is
             // the case provide an elided text if necessary.
             const int textLength = line.textStart() + line.textLength();
             if (textLength < nameText.length()) {
                 // Elide the last line of the text
-                QString lastTextLine = nameText.mid(line.textStart(), line.textLength());
-                lastTextLine = m_customizedFontMetrics.elidedText(lastTextLine,
-                                                                  Qt::ElideRight,
-                                                                  line.naturalTextWidth() - 1);
-                const QString elidedText = nameText.left(line.textStart()) + lastTextLine;
-                nameTextInfo->staticText.setText(elidedText);
+                qreal elidingWidth = maxWidth;
+                qreal lastLineWidth;
+                do {
+                    QString lastTextLine = nameText.mid(line.textStart());
+                    lastTextLine = elideRightKeepExtension(lastTextLine, elidingWidth);
+                    const QString elidedText = nameText.left(line.textStart()) + lastTextLine;
+                    nameTextInfo->staticText.setText(elidedText);
+
+                    lastLineWidth = m_customizedFontMetrics.horizontalAdvance(lastTextLine);
+
+                    // We do the text eliding in a loop with decreasing width (1 px / iteration)
+                    // to avoid problems related to different width calculation code paths
+                    // within Qt. (see bug 337104)
+                    elidingWidth -= 1.0;
+                } while (lastLineWidth > maxWidth);
+
+                nameWidth = qMax(nameWidth, lastLineWidth);
             }
             break;
         }
             }
             break;
         }
@@ -992,6 +1204,7 @@ void KStandardItemListWidget::updateIconsLayoutTextCache()
     layout.endLayout();
 
     // Use one line for each additional information
     layout.endLayout();
 
     // Use one line for each additional information
+    const int additionalRolesCount = qMax(visibleRoles().count() - 1, 0);
     nameTextInfo->staticText.setTextWidth(maxWidth);
     nameTextInfo->pos = QPointF(padding, widgetHeight -
                                          nameHeight -
     nameTextInfo->staticText.setTextWidth(maxWidth);
     nameTextInfo->pos = QPointF(padding, widgetHeight -
                                          nameHeight -
@@ -1026,9 +1239,12 @@ void KStandardItemListWidget::updateIconsLayoutTextCache()
             textLine.setLineWidth(maxWidth);
             requiredWidth = textLine.naturalTextWidth();
             if (requiredWidth > maxWidth) {
             textLine.setLineWidth(maxWidth);
             requiredWidth = textLine.naturalTextWidth();
             if (requiredWidth > maxWidth) {
-                const QString elidedText = m_customizedFontMetrics.elidedText(text, Qt::ElideRight, maxWidth);
+                const QString elidedText = elideRightKeepExtension(text, maxWidth);
                 textInfo->staticText.setText(elidedText);
                 textInfo->staticText.setText(elidedText);
-                requiredWidth = m_customizedFontMetrics.width(elidedText);
+                requiredWidth = m_customizedFontMetrics.horizontalAdvance(elidedText);
+            } else if (role == "rating") {
+                // Use the width of the rating pixmap, because the rating text is empty.
+                requiredWidth = m_rating.width();
             }
         }
         layout.endLayout();
             }
         }
         layout.endLayout();
@@ -1069,10 +1285,10 @@ void KStandardItemListWidget::updateCompactLayoutTextCache()
         TextInfo* textInfo = m_textInfo.value(role);
         textInfo->staticText.setText(text);
 
         TextInfo* textInfo = m_textInfo.value(role);
         textInfo->staticText.setText(text);
 
-        qreal requiredWidth = m_customizedFontMetrics.width(text);
+        qreal requiredWidth = m_customizedFontMetrics.horizontalAdvance(text);
         if (requiredWidth > maxWidth) {
             requiredWidth = maxWidth;
         if (requiredWidth > maxWidth) {
             requiredWidth = maxWidth;
-            const QString elidedText = m_customizedFontMetrics.elidedText(text, Qt::ElideRight, maxWidth);
+            const QString elidedText = elideRightKeepExtension(text, maxWidth);
             textInfo->staticText.setText(elidedText);
         }
 
             textInfo->staticText.setText(elidedText);
         }
 
@@ -1119,7 +1335,7 @@ void KStandardItemListWidget::updateDetailsLayoutTextCache()
         QString text = roleText(role, values);
 
         // Elide the text in case it does not fit into the available column-width
         QString text = roleText(role, values);
 
         // Elide the text in case it does not fit into the available column-width
-        qreal requiredWidth = m_customizedFontMetrics.width(text);
+        qreal requiredWidth = m_customizedFontMetrics.horizontalAdvance(text);
         const qreal roleWidth = columnWidth(role);
         qreal availableTextWidth = roleWidth - columnWidthInc;
 
         const qreal roleWidth = columnWidth(role);
         qreal availableTextWidth = roleWidth - columnWidthInc;
 
@@ -1129,8 +1345,8 @@ void KStandardItemListWidget::updateDetailsLayoutTextCache()
         }
 
         if (requiredWidth > availableTextWidth) {
         }
 
         if (requiredWidth > availableTextWidth) {
-            text = m_customizedFontMetrics.elidedText(text, Qt::ElideRight, availableTextWidth);
-            requiredWidth = m_customizedFontMetrics.width(text);
+            text = elideRightKeepExtension(text, availableTextWidth);
+            requiredWidth = m_customizedFontMetrics.horizontalAdvance(text);
         }
 
         TextInfo* textInfo = m_textInfo.value(role);
         }
 
         TextInfo* textInfo = m_textInfo.value(role);
@@ -1178,9 +1394,10 @@ void KStandardItemListWidget::updateAdditionalInfoTextColor()
 
 void KStandardItemListWidget::drawPixmap(QPainter* painter, const QPixmap& pixmap)
 {
 
 void KStandardItemListWidget::drawPixmap(QPainter* painter, const QPixmap& pixmap)
 {
-    if (m_scaledPixmapSize != pixmap.size()) {
+    if (m_scaledPixmapSize != pixmap.size() / pixmap.devicePixelRatio()) {
         QPixmap scaledPixmap = pixmap;
         QPixmap scaledPixmap = pixmap;
-        KPixmapModifier::scale(scaledPixmap, m_scaledPixmapSize);
+        KPixmapModifier::scale(scaledPixmap, m_scaledPixmapSize * qApp->devicePixelRatio());
+        scaledPixmap.setDevicePixelRatio(qApp->devicePixelRatio());
         painter->drawPixmap(m_pixmapPos, scaledPixmap);
 
 #ifdef KSTANDARDITEMLISTWIDGET_DEBUG
         painter->drawPixmap(m_pixmapPos, scaledPixmap);
 
 #ifdef KSTANDARDITEMLISTWIDGET_DEBUG
@@ -1199,6 +1416,7 @@ void KStandardItemListWidget::drawSiblingsInformation(QPainter* painter)
     QRect siblingRect(x, 0, siblingSize, siblingSize);
 
     QStyleOption option;
     QRect siblingRect(x, 0, siblingSize, siblingSize);
 
     QStyleOption option;
+    option.palette.setColor(QPalette::Text, option.palette.color(normalTextColorRole()));
     bool isItemSibling = true;
 
     const QBitArray siblings = siblingsInformation();
     bool isItemSibling = true;
 
     const QBitArray siblings = siblingsInformation();
@@ -1211,7 +1429,7 @@ void KStandardItemListWidget::drawSiblingsInformation(QPainter* painter)
             if (m_isExpandable) {
                 option.state |= QStyle::State_Children;
             }
             if (m_isExpandable) {
                 option.state |= QStyle::State_Children;
             }
-            if (data()["isExpanded"].toBool()) {
+            if (data().value("isExpanded").toBool()) {
                 option.state |= QStyle::State_Open;
             }
             isItemSibling = false;
                 option.state |= QStyle::State_Open;
             }
             isItemSibling = false;
@@ -1240,42 +1458,75 @@ QRectF KStandardItemListWidget::roleEditingRect(const QByteArray& role) const
 
 void KStandardItemListWidget::closeRoleEditor()
 {
 
 void KStandardItemListWidget::closeRoleEditor()
 {
+    disconnect(m_roleEditor, &KItemListRoleEditor::roleEditingCanceled,
+               this, &KStandardItemListWidget::slotRoleEditingCanceled);
+    disconnect(m_roleEditor, &KItemListRoleEditor::roleEditingFinished,
+               this, &KStandardItemListWidget::slotRoleEditingFinished);
+
     if (m_roleEditor->hasFocus()) {
         // If the editing was not ended by a FocusOut event, we have
         // to transfer the keyboard focus back to the KItemListContainer.
         scene()->views()[0]->parentWidget()->setFocus();
     }
     if (m_roleEditor->hasFocus()) {
         // If the editing was not ended by a FocusOut event, we have
         // to transfer the keyboard focus back to the KItemListContainer.
         scene()->views()[0]->parentWidget()->setFocus();
     }
-    m_roleEditor->deleteLater();
-    m_roleEditor = 0;
-}
-
-QPixmap KStandardItemListWidget::pixmapForIcon(const QString& name, int size)
-{
-    const KIcon icon(name);
-
-    int requestedSize;
-    if (size <= KIconLoader::SizeSmall) {
-        requestedSize = KIconLoader::SizeSmall;
-    } else if (size <= KIconLoader::SizeSmallMedium) {
-        requestedSize = KIconLoader::SizeSmallMedium;
-    } else if (size <= KIconLoader::SizeMedium) {
-        requestedSize = KIconLoader::SizeMedium;
-    } else if (size <= KIconLoader::SizeLarge) {
-        requestedSize = KIconLoader::SizeLarge;
-    } else if (size <= KIconLoader::SizeHuge) {
-        requestedSize = KIconLoader::SizeHuge;
-    } else if (size <= KIconLoader::SizeEnormous) {
-        requestedSize = KIconLoader::SizeEnormous;
-    } else if (size <= KIconLoader::SizeEnormous * 2) {
-        requestedSize = KIconLoader::SizeEnormous * 2;
-    } else {
-        requestedSize = size;
+
+    if (m_oldRoleEditor) {
+        m_oldRoleEditor->deleteLater();
     }
     }
+    m_oldRoleEditor = m_roleEditor;
+    m_roleEditor->hide();
+    m_roleEditor = nullptr;
+}
+
+QPixmap KStandardItemListWidget::pixmapForIcon(const QString& name, const QStringList& overlays, int size, QIcon::Mode mode)
+{
+    static const QIcon fallbackIcon = QIcon::fromTheme(QStringLiteral("unknown"));
+
+    size *= qApp->devicePixelRatio();
+
+    const QString key = "KStandardItemListWidget:" % name % ":" % overlays.join(QLatin1Char(':')) % ":" % QString::number(size) % ":" % QString::number(mode);
+    QPixmap pixmap;
+
+    if (!QPixmapCache::find(key, &pixmap)) {
+        const QIcon icon = QIcon::fromTheme(name, fallbackIcon);
+
+        pixmap = icon.pixmap(size / qApp->devicePixelRatio(), size / qApp->devicePixelRatio(), mode);
+        if (pixmap.width() != size || pixmap.height() != size) {
+            KPixmapModifier::scale(pixmap, QSize(size, size));
+        }
+
+        // Strangely KFileItem::overlays() returns empty string-values, so
+        // we need to check first whether an overlay must be drawn at all.
+        // It is more efficient to do it here, as KIconLoader::drawOverlays()
+        // assumes that an overlay will be drawn and has some additional
+        // setup time.
+        foreach (const QString& overlay, overlays) {
+            if (!overlay.isEmpty()) {
+                int state = KIconLoader::DefaultState;
+
+                switch (mode) {
+                case QIcon::Normal:
+                    break;
+                case QIcon::Active:
+                    state = KIconLoader::ActiveState;
+                    break;
+                case QIcon::Disabled:
+                    state = KIconLoader::DisabledState;
+                    break;
+                case QIcon::Selected:
+                    state = KIconLoader::SelectedState;
+                    break;
+                }
+
+                // There is at least one overlay, draw all overlays above m_pixmap
+                // and cancel the check
+                KIconLoader::global()->drawOverlays(overlays, pixmap, KIconLoader::Desktop, state);
+                break;
+            }
+        }
 
 
-    QPixmap pixmap = icon.pixmap(requestedSize, requestedSize);
-    if (requestedSize != size) {
-        KPixmapModifier::scale(pixmap, QSize(size, size));
+        QPixmapCache::insert(key, pixmap);
     }
     }
+    pixmap.setDevicePixelRatio(qApp->devicePixelRatio());
 
     return pixmap;
 }
 
     return pixmap;
 }
@@ -1291,4 +1542,3 @@ qreal KStandardItemListWidget::columnPadding(const KItemListStyleOption& option)
     return option.padding * 6;
 }
 
     return option.padding * 6;
 }
 
-#include "kstandarditemlistwidget.moc"