1
0
Эх сурвалжийг харах

VInsertImageDialog: fetch image from clipboard

Support `Ctrl+'` to insert image.
Le Tan 8 жил өмнө
parent
commit
8d6fc6cb4f

+ 146 - 57
src/dialog/vinsertimagedialog.cpp

@@ -2,44 +2,76 @@
 #include <QValidator>
 #include <QRegExp>
 #include <QDebug>
+#include <QTimer>
 #include "vinsertimagedialog.h"
 #include "utils/vutils.h"
 #include "vlineedit.h"
+#include "vdownloader.h"
 
-VInsertImageDialog::VInsertImageDialog(const QString &title, const QString &defaultImageTitle,
-                                       const QString &defaultPath, QWidget *parent)
-    : QDialog(parent), title(title), defaultImageTitle(defaultImageTitle), defaultPath(defaultPath),
-      image(NULL)
+VInsertImageDialog::VInsertImageDialog(const QString &p_title,
+                                       const QString &p_imageTitle,
+                                       const QString &p_imagePath,
+                                       bool p_browsable,
+                                       QWidget *p_parent)
+    : QDialog(p_parent),
+      m_image(NULL),
+      m_browsable(p_browsable),
+      m_timer(NULL)
 {
-    setupUI();
+    setupUI(p_title, p_imageTitle, p_imagePath);
 
     connect(m_imageTitleEdit, &QLineEdit::textChanged,
             this, &VInsertImageDialog::handleInputChanged);
-    connect(pathEdit, &QLineEdit::textChanged,
-            this, &VInsertImageDialog::handleInputChanged);
-    connect(browseBtn, &QPushButton::clicked,
-            this, &VInsertImageDialog::handleBrowseBtnClicked);
+
+    if (m_browsable) {
+        m_timer = new QTimer(this);
+        m_timer->setSingleShot(true);
+        m_timer->setInterval(500 /* ms */);
+        connect(m_timer, &QTimer::timeout,
+                this, &VInsertImageDialog::handlePathEditChanged);
+
+        connect(m_pathEdit, &QLineEdit::textChanged,
+                this, [this]() {
+                    m_timer->stop();
+
+                    setImage(QImage());
+                    if (m_pathEdit->text().isEmpty()) {
+                        return;
+                    }
+
+                    m_timer->start();
+                });
+
+        connect(browseBtn, &QPushButton::clicked,
+                this, &VInsertImageDialog::handleBrowseBtnClicked);
+
+        fetchImageFromClipboard();
+    }
 
     handleInputChanged();
 }
 
 VInsertImageDialog::~VInsertImageDialog()
 {
-    if (image) {
-        delete image;
-        image = NULL;
+    if (m_image) {
+        delete m_image;
+        m_image = NULL;
     }
 }
 
-void VInsertImageDialog::setupUI()
+void VInsertImageDialog::setupUI(const QString &p_title,
+                                 const QString &p_imageTitle,
+                                 const QString &p_imagePath)
 {
-    pathLabel = new QLabel(tr("&From:"));
-    pathEdit = new QLineEdit(defaultPath);
-    pathLabel->setBuddy(pathEdit);
+    QLabel *pathLabel = new QLabel(tr("&From:"));
+    m_pathEdit = new QLineEdit(p_imagePath);
+    pathLabel->setBuddy(m_pathEdit);
     browseBtn = new QPushButton(tr("&Browse"));
+    m_pathEdit->setReadOnly(!m_browsable);
+    browseBtn->setEnabled(m_browsable);
 
-    imageTitleLabel = new QLabel(tr("&Image title:"));
-    m_imageTitleEdit = new VLineEdit(defaultImageTitle);
+    QLabel *imageTitleLabel = new QLabel(tr("&Image title:"));
+    m_imageTitleEdit = new VLineEdit(p_imageTitle);
     m_imageTitleEdit->selectAll();
     imageTitleLabel->setBuddy(m_imageTitleEdit);
     QValidator *validator = new QRegExpValidator(QRegExp(VUtils::c_imageTitleRegExp),
@@ -48,7 +80,7 @@ void VInsertImageDialog::setupUI()
 
     QGridLayout *topLayout = new QGridLayout();
     topLayout->addWidget(pathLabel, 0, 0);
-    topLayout->addWidget(pathEdit, 0, 1);
+    topLayout->addWidget(m_pathEdit, 0, 1);
     topLayout->addWidget(browseBtn, 0, 2);
     topLayout->addWidget(imageTitleLabel, 1, 0);
     topLayout->addWidget(m_imageTitleEdit, 1, 1, 1, 2);
@@ -71,28 +103,19 @@ void VInsertImageDialog::setupUI()
     mainLayout->addWidget(imagePreviewLabel);
     setLayout(mainLayout);
     mainLayout->setSizeConstraint(QLayout::SetFixedSize);
-    setWindowTitle(title);
+    setWindowTitle(p_title);
 
     m_imageTitleEdit->setFocus();
 }
 
 void VInsertImageDialog::handleInputChanged()
 {
-    bool pathOk = true;
-    if (pathEdit->isVisible() && !pathEdit->isReadOnly()) {
-        QString path = pathEdit->text();
-        if (path.isEmpty()
-            || !VUtils::checkPathLegal(path)) {
-            pathOk = false;
-        }
-    }
-
     QString title = m_imageTitleEdit->getEvaluatedText();
     QRegExp reg(VUtils::c_imageTitleRegExp);
     bool titleOk = reg.exactMatch(title);
 
     QPushButton *okBtn = m_btnBox->button(QDialogButtonBox::Ok);
-    okBtn->setEnabled(pathOk && titleOk);
+    okBtn->setEnabled(titleOk && m_image);
 }
 
 QString VInsertImageDialog::getImageTitleInput() const
@@ -102,7 +125,7 @@ QString VInsertImageDialog::getImageTitleInput() const
 
 QString VInsertImageDialog::getPathInput() const
 {
-    return pathEdit->text();
+    return m_pathEdit->text();
 }
 
 void VInsertImageDialog::handleBrowseBtnClicked()
@@ -117,34 +140,39 @@ void VInsertImageDialog::handleBrowseBtnClicked()
     // Update lastPath
     lastPath = QFileInfo(filePath).path();
 
-    pathEdit->setText(filePath);
-    QImage image(filePath);
-    if (image.isNull()) {
-        return;
-    }
-    setImage(image);
+    m_imageType = ImageType::LocalFile;
+
+    m_pathEdit->setText(filePath);
+
+    m_imageTitleEdit->setFocus();
 }
 
 void VInsertImageDialog::setImage(const QImage &image)
 {
     if (image.isNull()) {
-        qWarning() << "set Null image";
+        imagePreviewLabel->setVisible(false);
+        if (m_image) {
+            delete m_image;
+            m_image = NULL;
+        }
+
+        handleInputChanged();
         return;
     }
 
     int width = 512 * VUtils::calculateScaleFactor();
     QSize previewSize(width, width);
-    if (!this->image) {
-        this->image = new QImage(image);
+    if (!m_image) {
+        m_image = new QImage(image);
     } else {
-        *(this->image) = image;
+        *m_image = image;
     }
 
     QPixmap pixmap;
     if (image.width() > width || image.height() > width) {
-        pixmap = QPixmap::fromImage(this->image->scaled(previewSize, Qt::KeepAspectRatio));
+        pixmap = QPixmap::fromImage(m_image->scaled(previewSize, Qt::KeepAspectRatio));
     } else {
-        pixmap = QPixmap::fromImage(*(this->image));
+        pixmap = QPixmap::fromImage(*m_image);
     }
 
     imagePreviewLabel->setPixmap(pixmap);
@@ -153,26 +181,87 @@ void VInsertImageDialog::setImage(const QImage &image)
     handleInputChanged();
 }
 
-void VInsertImageDialog::setBrowseable(bool browseable, bool visible)
-{
-    pathEdit->setReadOnly(!browseable);
-    browseBtn->setEnabled(browseable);
-
-    pathLabel->setVisible(visible);
-    pathEdit->setVisible(visible);
-    browseBtn->setVisible(visible);
-
-    handleInputChanged();
-}
-
 void VInsertImageDialog::imageDownloaded(const QByteArray &data)
 {
+    m_imageType = ImageType::ImageData;
     setImage(QImage::fromData(data));
 }
 
 QImage VInsertImageDialog::getImage() const
 {
-    if (!image) {
+    if (!m_image) {
         return QImage();
-    } else return *image;
+    } else return *m_image;
+}
+
+void VInsertImageDialog::fetchImageFromClipboard()
+{
+    if (!m_browsable || !m_pathEdit->text().isEmpty()) {
+        return;
+    }
+
+    Q_ASSERT(!m_image);
+
+    QClipboard *clipboard = QApplication::clipboard();
+    const QMimeData *mimeData = clipboard->mimeData();
+
+    QUrl url;
+
+    if (mimeData->hasImage()) {
+        QImage im = qvariant_cast<QImage>(mimeData->imageData());
+        if (im.isNull()) {
+            return;
+        }
+
+        setImage(im);
+        m_imageType = ImageType::ImageData;
+        qDebug() << "fetch image data from clipboard to insert";
+        return;
+    } else if (mimeData->hasUrls()) {
+        QList<QUrl> urls = mimeData->urls();
+        if (urls.size() != 1) {
+            return;
+        }
+
+        url = urls[0];
+    } else if (mimeData->hasText()) {
+        url = QUrl(mimeData->text());
+    }
+
+    if (url.isValid()) {
+        if (url.isLocalFile()) {
+            m_pathEdit->setText(url.toLocalFile());
+        } else {
+            m_pathEdit->setText(url.toString());
+        }
+    }
+}
+
+void VInsertImageDialog::handlePathEditChanged()
+{
+    QString text = m_pathEdit->text();
+    QUrl url = QUrl::fromUserInput(text);
+    if (!url.isValid()) {
+        setImage(QImage());
+        return;
+    }
+
+    QImage image(text);
+    if (image.isNull()) {
+        setImage(QImage());
+        // Try to treat it as network image.
+        m_imageType = ImageType::ImageData;
+        VDownloader *downloader = new VDownloader(this);
+        connect(downloader, &VDownloader::downloadFinished,
+                this, &VInsertImageDialog::imageDownloaded);
+        downloader->download(url.toString());
+        qDebug() << "try to fetch network image to insert" << text;
+    } else {
+        // Local image path.
+        setImage(image);
+        m_imageType = ImageType::LocalFile;
+        qDebug() << "fetch local file image to insert" << text;
+    }
+
+    handleInputChanged();
 }

+ 41 - 12
src/dialog/vinsertimagedialog.h

@@ -11,44 +11,73 @@ class QLineEdit;
 class VLineEdit;
 class QPushButton;
 class QDialogButtonBox;
+class QTimer;
 
 class VInsertImageDialog : public QDialog
 {
     Q_OBJECT
 public:
-    VInsertImageDialog(const QString &title, const QString &defaultImageTitle,
-                       const QString &defaultPath,
-                       QWidget *parent = 0);
+    enum ImageType
+    {
+        LocalFile = 0,
+        ImageData
+    };
+
+    VInsertImageDialog(const QString &p_title,
+                       const QString &p_imageTitle,
+                       const QString &p_imagePath,
+                       bool p_browsable = true,
+                       QWidget *p_parent = nullptr);
+
     ~VInsertImageDialog();
+
     QString getImageTitleInput() const;
+
     QString getPathInput() const;
 
     void setImage(const QImage &image);
+
     QImage getImage() const;
-    void setBrowseable(bool browseable, bool visible = false);
+
+    VInsertImageDialog::ImageType getImageType() const;
 
 public slots:
     void imageDownloaded(const QByteArray &data);
 
 private slots:
     void handleInputChanged();
+
     void handleBrowseBtnClicked();
 
+    void handlePathEditChanged();
+
 private:
-    void setupUI();
+    void setupUI(const QString &p_title,
+                 const QString &p_imageTitle,
+                 const QString &p_imagePath);
+
+    void fetchImageFromClipboard();
 
-    QLabel *imageTitleLabel;
     VLineEdit *m_imageTitleEdit;
-    QLabel *pathLabel;
-    QLineEdit *pathEdit;
+    QLineEdit *m_pathEdit;
     QPushButton *browseBtn;
     QDialogButtonBox *m_btnBox;
     QLabel *imagePreviewLabel;
 
-    QString title;
-    QString defaultImageTitle;
-    QString defaultPath;
-    QImage *image;
+    QImage *m_image;
+
+    // Whether enable the browse action.
+    bool m_browsable;
+
+    ImageType m_imageType;
+
+    // Timer for path edit change.
+    QTimer *m_timer;
 };
 
+inline VInsertImageDialog::ImageType VInsertImageDialog::getImageType() const
+{
+    return m_imageType;
+}
+
 #endif // VINSERTIMAGEDIALOG_H

+ 4 - 1
src/vdownloader.cpp

@@ -17,7 +17,10 @@ void VDownloader::handleDownloadFinished(QNetworkReply *reply)
 
 void VDownloader::download(const QUrl &p_url)
 {
-    Q_ASSERT(p_url.isValid());
+    if (!p_url.isValid()) {
+        return;
+    }
+
     QNetworkRequest request(p_url);
     webCtrl.get(request);
     qDebug() << "VDownloader get" << p_url.toString();

+ 1 - 1
src/vmainwindow.cpp

@@ -522,7 +522,7 @@ void VMainWindow::initEditToolBar(QSize p_iconSize)
 
     // Insert image.
     QAction *insertImageAct = new QAction(VIconUtils::toolButtonIcon(":/resources/icons/insert_image.svg"),
-                                          tr("Insert Image"),
+                                          tr("Insert Image\t%1").arg(VUtils::getShortcutText("Ctrl+'")),
                                           this);
     insertImageAct->setStatusTip(tr("Insert an image from file or URL"));
     connect(insertImageAct, &QAction::triggered,

+ 41 - 13
src/vmdeditoperations.cpp

@@ -39,11 +39,12 @@ bool VMdEditOperations::insertImageFromMimeData(const QMimeData *source)
     if (image.isNull()) {
         return false;
     }
+
     VInsertImageDialog dialog(tr("Insert Image From Clipboard"),
                               c_defaultImageTitle,
                               "",
+                              false,
                               m_editor->getEditor());
-    dialog.setBrowseable(false);
     dialog.setImage(image);
     if (dialog.exec() == QDialog::Accepted) {
         insertImageFromQImage(dialog.getImageTitleInput(),
@@ -51,6 +52,7 @@ bool VMdEditOperations::insertImageFromMimeData(const QMimeData *source)
                               m_file->getImageFolderInLink(),
                               image);
     }
+
     return true;
 }
 
@@ -157,9 +159,11 @@ bool VMdEditOperations::insertImageFromURL(const QUrl &imageUrl)
     }
 
 
-    VInsertImageDialog dialog(title, c_defaultImageTitle,
-                              imagePath, m_editor->getEditor());
-    dialog.setBrowseable(false, true);
+    VInsertImageDialog dialog(title,
+                              c_defaultImageTitle,
+                              imagePath,
+                              false,
+                              m_editor->getEditor());
     if (isLocal) {
         dialog.setImage(image);
     } else {
@@ -187,17 +191,30 @@ bool VMdEditOperations::insertImageFromURL(const QUrl &imageUrl)
 
 bool VMdEditOperations::insertImage()
 {
-    VInsertImageDialog dialog(tr("Insert Image From File"),
-                              c_defaultImageTitle, "", m_editor->getEditor());
+    // Use empty title and path to let the dialog auto complete.
+    VInsertImageDialog dialog(tr("Insert Image"),
+                              "",
+                              "",
+                              true,
+                              m_editor->getEditor());
     if (dialog.exec() == QDialog::Accepted) {
-        QString title = dialog.getImageTitleInput();
-        QString imagePath = dialog.getPathInput();
-        qDebug() << "insert image from" << imagePath << "as" << title;
-        insertImageFromPath(title,
-                            m_file->fetchImageFolderPath(),
-                            m_file->getImageFolderInLink(),
-                            imagePath);
+        VInsertImageDialog::ImageType type = dialog.getImageType();
+        if (type == VInsertImageDialog::ImageType::LocalFile) {
+            insertImageFromPath(dialog.getImageTitleInput(),
+                                m_file->fetchImageFolderPath(),
+                                m_file->getImageFolderInLink(),
+                                dialog.getPathInput());
+        } else {
+            QImage img = dialog.getImage();
+            if (!img.isNull()) {
+                insertImageFromQImage(dialog.getImageTitleInput(),
+                                      m_file->fetchImageFolderPath(),
+                                      m_file->getImageFolderInLink(),
+                                      img);
+            }
+        }
     }
+
     return true;
 }
 
@@ -292,6 +309,17 @@ bool VMdEditOperations::handleKeyPressEvent(QKeyEvent *p_event)
         break;
     }
 
+    case Qt::Key_Apostrophe:
+    {
+        if (modifiers == Qt::ControlModifier) {
+            m_editor->insertImage();
+            p_event->accept();
+            ret = true;
+        }
+
+        break;
+    }
+
     case Qt::Key_M:
     {
         if (modifiers == Qt::ControlModifier) {