]> cloud.milkyroute.net Git - dolphin.git/blobdiff - src/kitemviews/kstandarditemlistwidget.cpp
Fix scrolling during inline renaming causes rename of wrong file
[dolphin.git] / src / kitemviews / kstandarditemlistwidget.cpp
index d8b5ad9083cf772631787326980ab98b5c0651aa..7d94a59f511c1bf3432c22730b63d20d58ae5229 100644 (file)
 #include "kfileitemlistview.h"
 #include "kfileitemmodel.h"
 
-#include <KIcon>
+#include <QIcon>
 #include <KIconEffect>
 #include <KIconLoader>
-#include <KLocale>
-#include <kratingpainter.h>
+#include <KRatingPainter>
 #include <KStringHandler>
-#include <KDebug>
 
 #include "private/kfileitemclipboard.h"
 #include "private/kitemlistroleeditor.h"
 #include "private/kpixmapmodifier.h"
 
-#include <QFontMetricsF>
 #include <QGraphicsScene>
 #include <QGraphicsSceneResizeEvent>
 #include <QGraphicsView>
@@ -43,6 +40,7 @@
 #include <QTextLayout>
 #include <QTextLine>
 #include <QPixmapCache>
+#include <QGuiApplication>
 
 // #define KSTANDARDITEMLISTWIDGET_DEBUG
 
@@ -55,19 +53,19 @@ KStandardItemListWidgetInformant::~KStandardItemListWidgetInformant()
 {
 }
 
-void KStandardItemListWidgetInformant::calculateItemSizeHints(QVector<QSizeF>& sizeHints, const KItemListView* view) const
+void KStandardItemListWidgetInformant::calculateItemSizeHints(QVector<qreal>& logicalHeightHints, qreal& logicalWidthHint, const KItemListView* view) const
 {
     switch (static_cast<const KStandardItemListView*>(view)->itemLayout()) {
     case KStandardItemListWidget::IconsLayout:
-        calculateIconsLayoutItemSizeHints(sizeHints, view);
+        calculateIconsLayoutItemSizeHints(logicalHeightHints, logicalWidthHint, view);
         break;
 
     case KStandardItemListWidget::CompactLayout:
-        calculateCompactLayoutItemSizeHints(sizeHints, view);
+        calculateCompactLayoutItemSizeHints(logicalHeightHints, logicalWidthHint, view);
         break;
 
     case KStandardItemListWidget::DetailsLayout:
-        calculateDetailsLayoutItemSizeHints(sizeHints, view);
+        calculateDetailsLayoutItemSizeHints(logicalHeightHints, logicalWidthHint, view);
         break;
 
     default:
@@ -120,6 +118,8 @@ QString KStandardItemListWidgetInformant::itemText(int index, const KItemListVie
 
 bool KStandardItemListWidgetInformant::itemIsLink(int index, const KItemListView* view) const
 {
+    Q_UNUSED(index);
+    Q_UNUSED(view);
     return false;
 }
 
@@ -138,7 +138,7 @@ QFont KStandardItemListWidgetInformant::customizedFontForLinks(const QFont& base
     return baseFont;
 }
 
-void KStandardItemListWidgetInformant::calculateIconsLayoutItemSizeHints(QVector<QSizeF>& sizeHints, const KItemListView* view) const
+void KStandardItemListWidgetInformant::calculateIconsLayoutItemSizeHints(QVector<qreal>& logicalHeightHints, qreal& logicalWidthHint, const KItemListView* view) const
 {
     const KItemListStyleOption& option = view->styleOption();
     const QFont& normalFont = option.font;
@@ -154,8 +154,8 @@ void KStandardItemListWidgetInformant::calculateIconsLayoutItemSizeHints(QVector
     QTextOption textOption(Qt::AlignHCenter);
     textOption.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
 
-    for (int index = 0; index < sizeHints.count(); ++index) {
-        if (!sizeHints.at(index).isEmpty()) {
+    for (int index = 0; index < logicalHeightHints.count(); ++index) {
+        if (logicalHeightHints.at(index) > 0.0) {
             continue;
         }
 
@@ -186,11 +186,13 @@ void KStandardItemListWidgetInformant::calculateIconsLayoutItemSizeHints(QVector
         // Add one line for each additional information
         textHeight += additionalRolesSpacing;
 
-        sizeHints[index] = QSizeF(itemWidth, textHeight + spacingAndIconHeight);
+        logicalHeightHints[index] = textHeight + spacingAndIconHeight;
     }
+
+    logicalWidthHint = itemWidth;
 }
 
-void KStandardItemListWidgetInformant::calculateCompactLayoutItemSizeHints(QVector<QSizeF>& sizeHints, const KItemListView* view) const
+void KStandardItemListWidgetInformant::calculateCompactLayoutItemSizeHints(QVector<qreal>& logicalHeightHints, qreal& logicalWidthHint, const KItemListView* view) const
 {
     const KItemListStyleOption& option = view->styleOption();
     const QFontMetrics& normalFontMetrics = option.fontMetrics;
@@ -204,8 +206,8 @@ void KStandardItemListWidgetInformant::calculateCompactLayoutItemSizeHints(QVect
 
     const QFontMetrics linkFontMetrics(customizedFontForLinks(option.font));
 
-    for (int index = 0; index < sizeHints.count(); ++index) {
-        if (!sizeHints.at(index).isEmpty()) {
+    for (int index = 0; index < logicalHeightHints.count(); ++index) {
+        if (logicalHeightHints.at(index) > 0.0) {
             continue;
         }
 
@@ -232,22 +234,18 @@ void KStandardItemListWidgetInformant::calculateCompactLayoutItemSizeHints(QVect
             width = maxWidth;
         }
 
-        sizeHints[index] = QSizeF(width, height);
+        logicalHeightHints[index] = width;
     }
+
+    logicalWidthHint = height;
 }
 
-void KStandardItemListWidgetInformant::calculateDetailsLayoutItemSizeHints(QVector<QSizeF>& sizeHints, const KItemListView* view) const
+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());
-
-    for (int index = 0; index < sizeHints.count(); ++index) {
-        if (!sizeHints.at(index).isEmpty()) {
-            continue;
-        }
-
-        sizeHints[index] = QSizeF(-1, height);
-    }
+    logicalHeightHints.fill(height);
+    logicalWidthHint = -1.0;
 }
 
 KStandardItemListWidget::KStandardItemListWidget(KItemListWidgetInformant* informant, QGraphicsItem* parent) :
@@ -345,6 +343,7 @@ void KStandardItemListWidget::paint(QPainter* painter, const QStyleOptionGraphic
              */
             // 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);
@@ -354,6 +353,7 @@ void KStandardItemListWidget::paint(QPainter* painter, const QStyleOptionGraphic
 
             // Paint pixmap2 so that pixmap2 = m_hoverPixmap * hoverOpacity()
             QPixmap pixmap2(pixmap1.size());
+            pixmap2.setDevicePixelRatio(pixmap1.devicePixelRatio());
             pixmap2.fill(Qt::transparent);
             {
                 QPainter p(&pixmap2);
@@ -496,6 +496,29 @@ QRectF KStandardItemListWidget::textFocusRect() const
     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();
@@ -658,6 +681,12 @@ void KStandardItemListWidget::dataChanged(const QHash<QByteArray, QVariant>& cur
         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");
@@ -731,10 +760,10 @@ void KStandardItemListWidget::editedRoleChanged(const QByteArray& current, const
         if (m_roleEditor) {
             emit roleEditingCanceled(index(), current, data().value(current));
 
-            disconnect(m_roleEditor, SIGNAL(roleEditingCanceled(QByteArray,QVariant)),
-                       this, SLOT(slotRoleEditingCanceled(QByteArray,QVariant)));
-            disconnect(m_roleEditor, SIGNAL(roleEditingFinished(QByteArray,QVariant)),
-                       this, SLOT(slotRoleEditingFinished(QByteArray,QVariant)));
+            disconnect(m_roleEditor, &KItemListRoleEditor::roleEditingCanceled,
+                       this, &KStandardItemListWidget::slotRoleEditingCanceled);
+            disconnect(m_roleEditor, &KItemListRoleEditor::roleEditingFinished,
+                       this, &KStandardItemListWidget::slotRoleEditingFinished);
 
             if (m_oldRoleEditor) {
                 m_oldRoleEditor->deleteLater();
@@ -769,10 +798,10 @@ void KStandardItemListWidget::editedRoleChanged(const QByteArray& current, const
         m_roleEditor->setTextCursor(cursor);
     }
 
-    connect(m_roleEditor, SIGNAL(roleEditingCanceled(QByteArray,QVariant)),
-            this, SLOT(slotRoleEditingCanceled(QByteArray,QVariant)));
-    connect(m_roleEditor, SIGNAL(roleEditingFinished(QByteArray,QVariant)),
-            this, SLOT(slotRoleEditingFinished(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);
@@ -806,24 +835,41 @@ void KStandardItemListWidget::showEvent(QShowEvent* event)
     // 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);
 
-    connect(clipboard, SIGNAL(cutItemsChanged()),
-            this, SLOT(slotCutItemsChanged()));
+    connect(clipboard, &KFileItemClipboard::cutItemsChanged,
+            this, &KStandardItemListWidget::slotCutItemsChanged);
 }
 
 void KStandardItemListWidget::hideEvent(QHideEvent* event)
 {
-    disconnect(KFileItemClipboard::instance(), SIGNAL(cutItemsChanged()),
-               this, SLOT(slotCutItemsChanged()));
+    disconnect(KFileItemClipboard::instance(), &KFileItemClipboard::cutItemsChanged,
+               this, &KStandardItemListWidget::slotCutItemsChanged);
 
     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()
 {
-    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;
@@ -922,14 +968,15 @@ void KStandardItemListWidget::updatePixmapCache()
             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");
             }
             const QStringList overlays = values["iconOverlays"].toStringList();
-            m_pixmap = pixmapForIcon(iconName, overlays, maxIconHeight);
-        } else if (m_pixmap.width() != maxIconWidth || m_pixmap.height() != maxIconHeight) {
+            m_pixmap = pixmapForIcon(iconName, overlays, maxIconHeight, isSelected() && isActiveWindow() ? 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.
-            KPixmapModifier::scale(m_pixmap, QSize(maxIconWidth, maxIconHeight));
+            KPixmapModifier::scale(m_pixmap, QSize(maxIconWidth, maxIconHeight) * qApp->devicePixelRatio());
         }
 
         if (m_isCut) {
@@ -941,7 +988,7 @@ void KStandardItemListWidget::updatePixmapCache()
             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();
             KIconEffect::colorize(image, color, 0.8f);
@@ -1109,14 +1156,24 @@ void KStandardItemListWidget::updateIconsLayoutTextCache()
             const int textLength = line.textStart() + line.textLength();
             if (textLength < nameText.length()) {
                 // Elide the last line of the text
-                QString lastTextLine = nameText.mid(line.textStart());
-                lastTextLine = m_customizedFontMetrics.elidedText(lastTextLine,
-                                                                  Qt::ElideRight,
-                                                                  maxWidth);
-                const QString elidedText = nameText.left(line.textStart()) + lastTextLine;
-                nameTextInfo->staticText.setText(elidedText);
-
-                const qreal lastLineWidth = m_customizedFontMetrics.boundingRect(lastTextLine).width();
+                qreal elidingWidth = maxWidth;
+                qreal lastLineWidth;
+                do {
+                    QString lastTextLine = nameText.mid(line.textStart());
+                    lastTextLine = m_customizedFontMetrics.elidedText(lastTextLine,
+                                                                      Qt::ElideRight,
+                                                                      elidingWidth);
+                    const QString elidedText = nameText.left(line.textStart()) + lastTextLine;
+                    nameTextInfo->staticText.setText(elidedText);
+
+                    lastLineWidth = m_customizedFontMetrics.boundingRect(lastTextLine).width();
+
+                    // 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;
@@ -1315,9 +1372,10 @@ void KStandardItemListWidget::updateAdditionalInfoTextColor()
 
 void KStandardItemListWidget::drawPixmap(QPainter* painter, const QPixmap& pixmap)
 {
-    if (m_scaledPixmapSize != pixmap.size()) {
+    if (m_scaledPixmapSize != pixmap.size() / pixmap.devicePixelRatio()) {
         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
@@ -1349,7 +1407,7 @@ void KStandardItemListWidget::drawSiblingsInformation(QPainter* painter)
             if (m_isExpandable) {
                 option.state |= QStyle::State_Children;
             }
-            if (data()["isExpanded"].toBool()) {
+            if (data().value("isExpanded").toBool()) {
                 option.state |= QStyle::State_Open;
             }
             isItemSibling = false;
@@ -1378,10 +1436,10 @@ QRectF KStandardItemListWidget::roleEditingRect(const QByteArray& role) const
 
 void KStandardItemListWidget::closeRoleEditor()
 {
-    disconnect(m_roleEditor, SIGNAL(roleEditingCanceled(QByteArray,QVariant)),
-               this, SLOT(slotRoleEditingCanceled(QByteArray,QVariant)));
-    disconnect(m_roleEditor, SIGNAL(roleEditingFinished(QByteArray,QVariant)),
-               this, SLOT(slotRoleEditingFinished(QByteArray,QVariant)));
+    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
@@ -1397,13 +1455,15 @@ void KStandardItemListWidget::closeRoleEditor()
     m_roleEditor = 0;
 }
 
-QPixmap KStandardItemListWidget::pixmapForIcon(const QString& name, const QStringList& overlays, int size)
+QPixmap KStandardItemListWidget::pixmapForIcon(const QString& name, const QStringList& overlays, int size, QIcon::Mode mode)
 {
-    const QString key = "KStandardItemListWidget:" % name % ":" % overlays.join(":") % ":" % QString::number(size);
+    static const QIcon fallbackIcon = QIcon::fromTheme(QStringLiteral("unknown"));
+    size *= qApp->devicePixelRatio();
+    const QString key = "KStandardItemListWidget:" % name % ":" % overlays.join(QStringLiteral(":")) % ":" % QString::number(size) % ":" % QString::number(mode);
     QPixmap pixmap;
 
     if (!QPixmapCache::find(key, pixmap)) {
-        const KIcon icon(name);
+        const QIcon icon = QIcon::fromTheme(name, fallbackIcon);
 
         int requestedSize;
         if (size <= KIconLoader::SizeSmall) {
@@ -1424,7 +1484,7 @@ QPixmap KStandardItemListWidget::pixmapForIcon(const QString& name, const QStrin
             requestedSize = size;
         }
 
-        pixmap = icon.pixmap(requestedSize, requestedSize);
+        pixmap = icon.pixmap(requestedSize / qApp->devicePixelRatio(), requestedSize / qApp->devicePixelRatio(), mode);
         if (requestedSize != size) {
             KPixmapModifier::scale(pixmap, QSize(size, size));
         }
@@ -1445,6 +1505,7 @@ QPixmap KStandardItemListWidget::pixmapForIcon(const QString& name, const QStrin
 
         QPixmapCache::insert(key, pixmap);
     }
+    pixmap.setDevicePixelRatio(qApp->devicePixelRatio());
 
     return pixmap;
 }
@@ -1460,4 +1521,3 @@ qreal KStandardItemListWidget::columnPadding(const KItemListStyleOption& option)
     return option.padding * 6;
 }
 
-#include "kstandarditemlistwidget.moc"