Browse Source

bug-fix: loop to setMimeData to clipboard in Windows

Le Tan 8 years ago
parent
commit
00d7f5e013
3 changed files with 147 additions and 6 deletions
  1. 130 0
      src/utils/vclipboardutils.cpp
  2. 10 0
      src/utils/vclipboardutils.h
  3. 7 6
      src/vwebview.cpp

+ 130 - 0
src/utils/vclipboardutils.cpp

@@ -1,6 +1,7 @@
 #include "vclipboardutils.h"
 
 #include <QDebug>
+#include <QMimeData>
 
 #include "vutils.h"
 
@@ -33,3 +34,132 @@ void VClipboardUtils::setImageLoop(QClipboard *p_clipboard,
         VUtils::sleepWait(100 /* ms */);
     }
 }
+
+void VClipboardUtils::setMimeDataToClipboard(QClipboard *p_clipboard,
+                                             QMimeData *p_mimeData,
+                                             QClipboard::Mode p_mode)
+{
+#if defined(Q_OS_WIN)
+    // On Windows, setMimeData() may fail. We will repeatedly retry until succeed.
+    setMimeDataLoop(p_clipboard, p_mimeData, p_mode);
+#else
+    p_clipboard->setMimeData(p_mimeData, p_mode);
+#endif
+}
+
+static QMimeData *cloneMimeData(const QMimeData *p_mimeData)
+{
+    QMimeData *da = new QMimeData();
+    if (p_mimeData->hasUrls()) {
+        da->setUrls(p_mimeData->urls());
+    }
+
+    if (p_mimeData->hasText()) {
+        da->setText(p_mimeData->text());
+    }
+
+    if (p_mimeData->hasColor()) {
+        da->setColorData(p_mimeData->colorData());
+    }
+
+    if (p_mimeData->hasHtml()) {
+        da->setHtml(p_mimeData->html());
+    }
+
+    if (p_mimeData->hasImage()) {
+        da->setImageData(p_mimeData->imageData());
+    }
+
+    return da;
+}
+
+static bool mimeDataEquals(const QMimeData *p_a, const QMimeData *p_b)
+{
+    if ((p_a && !p_b) || (!p_a && p_b)) {
+        return false;
+    }
+
+    if (p_a->hasUrls()) {
+        if (!p_b->hasUrls()) {
+            return false;
+        }
+
+        if (p_a->urls() != p_b->urls()) {
+            return false;
+        }
+    } else if (p_b->hasUrls()) {
+        return false;
+    }
+
+    if (p_a->hasText()) {
+        if (!p_b->hasText()) {
+            return false;
+        }
+
+        if (p_a->text() != p_b->text()) {
+            return false;
+        }
+    } else if (p_b->hasText()) {
+        return false;
+    }
+
+    if (p_a->hasColor()) {
+        if (!p_b->hasColor()) {
+            return false;
+        }
+
+        if (p_a->colorData() != p_b->colorData()) {
+            return false;
+        }
+    } else if (p_b->hasColor()) {
+        return false;
+    }
+
+    if (p_a->hasHtml()) {
+        if (!p_b->hasHtml()) {
+            return false;
+        }
+
+        if (p_a->html() != p_b->html()) {
+            return false;
+        }
+    } else if (p_b->hasHtml()) {
+        return false;
+    }
+
+    if (p_a->hasImage()) {
+        if (!p_b->hasImage()) {
+            return false;
+        }
+
+        if (p_a->imageData() != p_b->imageData()) {
+            return false;
+        }
+    } else if (p_b->hasImage()) {
+        return false;
+    }
+
+    return true;
+}
+
+void VClipboardUtils::setMimeDataLoop(QClipboard *p_clipboard,
+                                      QMimeData *p_mimeData,
+                                      QClipboard::Mode p_mode)
+{
+    while (true) {
+        // Make a backup.
+        QMimeData *tmp = cloneMimeData(p_mimeData);
+
+        p_clipboard->setMimeData(p_mimeData, p_mode);
+        const QMimeData *out = p_clipboard->mimeData(p_mode);
+        if (mimeDataEquals(tmp, out)) {
+            delete tmp;
+            break;
+        }
+
+        qDebug() << "fail to set mimeData, retry";
+        p_mimeData = tmp;
+
+        VUtils::sleepWait(100 /* ms */);
+    }
+}

+ 10 - 0
src/utils/vclipboardutils.h

@@ -4,6 +4,8 @@
 #include <QImage>
 #include <QClipboard>
 
+class QMimeData;
+
 
 class VClipboardUtils
 {
@@ -12,6 +14,10 @@ public:
                                     const QImage &p_image,
                                     QClipboard::Mode p_mode = QClipboard::Clipboard);
 
+    static void setMimeDataToClipboard(QClipboard *p_clipboard,
+                                       QMimeData *p_mimeData,
+                                       QClipboard::Mode p_mode = QClipboard::Clipboard);
+
 private:
     VClipboardUtils()
     {
@@ -20,6 +26,10 @@ private:
     static void setImageLoop(QClipboard *p_clipboard,
                              const QImage &p_image,
                              QClipboard::Mode p_mode);
+
+    static void setMimeDataLoop(QClipboard *p_clipboard,
+                                QMimeData *p_mimeData,
+                                QClipboard::Mode p_mode);
 };
 
 #endif // VCLIPBOARDUTILS_H

+ 7 - 6
src/vwebview.cpp

@@ -108,7 +108,7 @@ void VWebView::copyImage()
         QString imgPath;
         if (mimeData->hasUrls()) {
             QList<QUrl> urls = mimeData->urls();
-            if (urls[0].isLocalFile()) {
+            if (!urls.isEmpty() && urls[0].isLocalFile()) {
                 imgPath = urls[0].toLocalFile();
             }
         }
@@ -149,7 +149,7 @@ void VWebView::handleCopyImageUrlAction()
                 QMimeData *data = new QMimeData();
                 data->setUrls(urls);
                 data->setText(spaceOnlyText);
-                clipboard->setMimeData(data);
+                VClipboardUtils::setMimeDataToClipboard(clipboard, data, QClipboard::Clipboard);
 
                 clipboard->setProperty(c_ClipboardPropertyMark.toLatin1(), true);
                 qDebug() << "clipboard copy image URL altered" << spaceOnlyText;
@@ -160,15 +160,16 @@ void VWebView::handleCopyImageUrlAction()
 
 void VWebView::hideUnusedActions(QMenu *p_menu)
 {
-    const QList<QAction *> actions = p_menu->actions();
-
     QList<QAction *> unusedActions;
 
     // QWebEnginePage uses different actions of Back/Forward/Reload.
     // [Woboq](https://code.woboq.org/qt5/qtwebengine/src/webenginewidgets/api/qwebenginepage.cpp.html#1652)
     // We tell these three actions by name.
-    static const QStringList actionNames({QWebEnginePage::tr("&Back"), QWebEnginePage::tr("&Forward"), QWebEnginePage::tr("&Reload")});
+   const QStringList actionNames({QWebEnginePage::tr("&Back"),
+                                  QWebEnginePage::tr("&Forward"),
+                                  QWebEnginePage::tr("&Reload")});
 
+    const QList<QAction *> actions = p_menu->actions();
     for (auto it : actions) {
         if (actionNames.contains(it->text())) {
             unusedActions.append(it);
@@ -224,7 +225,7 @@ void VWebView::handleCopyAction()
             data->setText(mimeData->text());
         }
 
-        clipboard->setMimeData(data);
+        VClipboardUtils::setMimeDataToClipboard(clipboard, data, QClipboard::Clipboard);
         clipboard->setProperty(c_ClipboardPropertyMark.toLatin1(), true);
         qDebug() << "clipboard copy Html altered" << html;
     }