From 00d7f5e01350c160574ff6754ded5603fc63e72c Mon Sep 17 00:00:00 2001 From: Le Tan Date: Thu, 28 Dec 2017 19:26:43 +0800 Subject: [PATCH] bug-fix: loop to setMimeData to clipboard in Windows --- src/utils/vclipboardutils.cpp | 130 ++++++++++++++++++++++++++++++++++ src/utils/vclipboardutils.h | 10 +++ src/vwebview.cpp | 13 ++-- 3 files changed, 147 insertions(+), 6 deletions(-) diff --git a/src/utils/vclipboardutils.cpp b/src/utils/vclipboardutils.cpp index 8d2e513b..b330e62c 100644 --- a/src/utils/vclipboardutils.cpp +++ b/src/utils/vclipboardutils.cpp @@ -1,6 +1,7 @@ #include "vclipboardutils.h" #include +#include #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 */); + } +} diff --git a/src/utils/vclipboardutils.h b/src/utils/vclipboardutils.h index df50b3bc..e82838ba 100644 --- a/src/utils/vclipboardutils.h +++ b/src/utils/vclipboardutils.h @@ -4,6 +4,8 @@ #include #include +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 diff --git a/src/vwebview.cpp b/src/vwebview.cpp index b0c80ba0..9f0cb101 100644 --- a/src/vwebview.cpp +++ b/src/vwebview.cpp @@ -108,7 +108,7 @@ void VWebView::copyImage() QString imgPath; if (mimeData->hasUrls()) { QList 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 actions = p_menu->actions(); - QList 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 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; }