Browse Source

support Insert Link tool bar button

Support Ctrl+L to insert a link.
Le Tan 8 years ago
parent
commit
e66b70b6ff

+ 132 - 0
src/dialog/vinsertlinkdialog.cpp

@@ -0,0 +1,132 @@
+#include "vinsertlinkdialog.h"
+
+#include <QtWidgets>
+
+#include "vlineedit.h"
+
+VInsertLinkDialog::VInsertLinkDialog(const QString &p_title,
+                                     const QString &p_text,
+                                     const QString &p_info,
+                                     const QString &p_linkText,
+                                     const QString &p_linkUrl,
+                                     QWidget *p_parent)
+    : QDialog(p_parent)
+{
+    setupUI(p_title, p_text, p_info, p_linkText, p_linkUrl);
+
+    fetchLinkFromClipboard();
+
+    handleInputChanged();
+}
+
+void VInsertLinkDialog::setupUI(const QString &p_title,
+                                const QString &p_text,
+                                const QString &p_info,
+                                const QString &p_linkText,
+                                const QString &p_linkUrl)
+{
+    QLabel *textLabel = NULL;
+    if (!p_text.isEmpty()) {
+        textLabel = new QLabel(p_text);
+        textLabel->setWordWrap(true);
+    }
+
+    QLabel *infoLabel = NULL;
+    if (!p_info.isEmpty()) {
+        infoLabel = new QLabel(p_info);
+        infoLabel->setWordWrap(true);
+    }
+
+    m_linkTextEdit = new VLineEdit(p_linkText);
+    m_linkTextEdit->selectAll();
+
+    m_linkUrlEdit = new QLineEdit(p_linkUrl);
+    m_linkUrlEdit->setToolTip(tr("Absolute or relative path of the link"));
+
+    QFormLayout *inputLayout = new QFormLayout();
+    inputLayout->addRow(tr("&Text:"), m_linkTextEdit);
+    inputLayout->addRow(tr("&URL:"), m_linkUrlEdit);
+
+    // Ok is the default button.
+    m_btnBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
+    connect(m_btnBox, &QDialogButtonBox::accepted, this, &QDialog::accept);
+    connect(m_btnBox, &QDialogButtonBox::rejected, this, &QDialog::reject);
+
+    QPushButton *okBtn = m_btnBox->button(QDialogButtonBox::Ok);
+    m_linkTextEdit->setMinimumWidth(okBtn->sizeHint().width() * 3);
+
+    QVBoxLayout *mainLayout = new QVBoxLayout;
+    if (textLabel) {
+        mainLayout->addWidget(textLabel);
+    }
+
+    if (infoLabel) {
+        mainLayout->addWidget(infoLabel);
+    }
+
+    mainLayout->addLayout(inputLayout);
+    mainLayout->addWidget(m_btnBox);
+
+    setLayout(mainLayout);
+    setWindowTitle(p_title);
+
+    connect(m_linkTextEdit, &QLineEdit::textChanged,
+            this, &VInsertLinkDialog::handleInputChanged);
+    connect(m_linkUrlEdit, &QLineEdit::textChanged,
+            this, &VInsertLinkDialog::handleInputChanged);
+}
+
+void VInsertLinkDialog::handleInputChanged()
+{
+    bool textOk = true;
+    if (m_linkTextEdit->getEvaluatedText().isEmpty()) {
+        textOk = false;
+    }
+
+    bool urlOk = true;
+    if (m_linkUrlEdit->text().isEmpty()) {
+        urlOk = false;
+    }
+
+    QPushButton *okBtn = m_btnBox->button(QDialogButtonBox::Ok);
+    okBtn->setEnabled(textOk && urlOk);
+}
+
+void VInsertLinkDialog::fetchLinkFromClipboard()
+{
+    if (!m_linkUrlEdit->text().isEmpty()
+        || !m_linkTextEdit->text().isEmpty()) {
+        return;
+    }
+
+    QClipboard *clipboard = QApplication::clipboard();
+    const QMimeData *mimeData = clipboard->mimeData();
+
+    if (!mimeData->hasText()) {
+        return;
+    }
+
+    QString text = mimeData->text();
+    if (text.isEmpty()) {
+        return;
+    }
+
+    QUrl url = QUrl::fromUserInput(text);
+    if (url.isValid()) {
+        if (m_linkUrlEdit->text().isEmpty()) {
+            m_linkUrlEdit->setText(text);
+        }
+    } else if (m_linkTextEdit->text().isEmpty()) {
+        m_linkTextEdit->setText(text);
+    }
+}
+
+QString VInsertLinkDialog::getLinkText() const
+{
+    return m_linkTextEdit->getEvaluatedText();
+}
+
+QString VInsertLinkDialog::getLinkUrl() const
+{
+    return m_linkUrlEdit->text();
+}

+ 46 - 0
src/dialog/vinsertlinkdialog.h

@@ -0,0 +1,46 @@
+#ifndef VINSERTLINKDIALOG_H
+#define VINSERTLINKDIALOG_H
+
+#include <QDialog>
+#include <QString>
+
+class VLineEdit;
+class QLineEdit;
+class QDialogButtonBox;
+
+class VInsertLinkDialog : public QDialog
+{
+    Q_OBJECT
+public:
+    VInsertLinkDialog(const QString &p_title,
+                      const QString &p_text,
+                      const QString &p_info,
+                      const QString &p_linkText,
+                      const QString &p_linkUrl,
+                      QWidget *p_parent = nullptr);
+
+    QString getLinkText() const;
+
+    QString getLinkUrl() const;
+
+private slots:
+    void handleInputChanged();
+
+private:
+    void setupUI(const QString &p_title,
+                 const QString &p_text,
+                 const QString &p_info,
+                 const QString &p_linkText,
+                 const QString &p_linkUrl);
+
+    // Infer link text/url from clipboard only when text and url are both empty.
+    void fetchLinkFromClipboard();
+
+    VLineEdit *m_linkTextEdit;
+
+    QLineEdit *m_linkUrlEdit;
+
+    QDialogButtonBox *m_btnBox;
+};
+
+#endif // VINSERTLINKDIALOG_H

+ 15 - 0
src/resources/icons/link.svg

@@ -0,0 +1,15 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 16.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0)  -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+	 width="512px" height="512px" viewBox="0 0 512 512" style="enable-background:new 0 0 512 512;" xml:space="preserve">
+<g>
+	<path d="M256.5,208H256v0C256.2,208,256.3,208,256.5,208z"/>
+	<path d="M368.5,160H320c0,0,26,17,31.6,48H368h0.5c17.6,0,31.5,13.9,31.5,31.5v32c0,17.6-13.9,32.5-31.5,32.5h-112
+		c-17.6,0-32.5-14.9-32.5-32.5V240h-48v31.5c0,11.5,2.5,22.5,6.9,32.5c12.6,28.2,40.9,48,73.6,48h112c44.2,0,79.5-36.3,79.5-80.5
+		v-32C448,195.3,412.7,160,368.5,160z"/>
+	<path d="M329.6,208c-12.1-28.3-40.1-48-73.1-48h-112c-44.2,0-80.5,35.3-80.5,79.5v32c0,44.2,36.3,80.5,80.5,80.5H192
+		c0,0-25.8-17-32.1-48h-15.4c-17.6,0-32.5-14.9-32.5-32.5v-32c0-17.6,14.9-31.5,32.5-31.5H256h0.5c17.6,0,31.5,13.9,31.5,31.5v32
+		c0,0.2,0,0.3,0,0.5h48c0-0.2,0-0.3,0-0.5v-32C336,228.3,333.7,217.6,329.6,208z"/>
+</g>
+</svg>

+ 4 - 2
src/src.pro

@@ -80,7 +80,8 @@ SOURCES += main.cpp\
     vfilesessioninfo.cpp \
     vtableofcontent.cpp \
     utils/vmetawordmanager.cpp \
-    vlineedit.cpp
+    vlineedit.cpp \
+    dialog/vinsertlinkdialog.cpp
 
 HEADERS  += vmainwindow.h \
     vdirectorytree.h \
@@ -148,7 +149,8 @@ HEADERS  += vmainwindow.h \
     vfilesessioninfo.h \
     vtableofcontent.h \
     utils/vmetawordmanager.h \
-    vlineedit.h
+    vlineedit.h \
+    dialog/vinsertlinkdialog.h
 
 RESOURCES += \
     vnote.qrc \

+ 44 - 1
src/vedit.cpp

@@ -10,7 +10,7 @@
 #include "utils/vmetawordmanager.h"
 #include "veditoperations.h"
 #include "vedittab.h"
-
+#include "dialog/vinsertlinkdialog.h"
 
 extern VConfigManager *g_config;
 
@@ -199,6 +199,49 @@ void VEdit::insertImage()
     }
 }
 
+void VEdit::insertLink()
+{
+    if (!m_editOps) {
+        return;
+    }
+
+    QString text;
+    QString linkText, linkUrl;
+    QTextCursor cursor = textCursor();
+    if (cursor.hasSelection()) {
+        text = VEditUtils::selectedText(cursor).trimmed();
+        // Only pure space is accepted.
+        QRegExp reg("[\\S ]*");
+        if (reg.exactMatch(text)) {
+            QUrl url = QUrl::fromUserInput(text,
+                                           m_file->fetchBasePath());
+            QRegExp urlReg("[\\.\\\\/]");
+            if (url.isValid()
+                && text.contains(urlReg)) {
+                // Url.
+                linkUrl = text;
+            } else {
+                // Text.
+                linkText = text;
+            }
+        }
+    }
+
+    VInsertLinkDialog dialog(tr("Insert Link"),
+                             "",
+                             "",
+                             linkText,
+                             linkUrl,
+                             this);
+    if (dialog.exec() == QDialog::Accepted) {
+        linkText = dialog.getLinkText();
+        linkUrl = dialog.getLinkUrl();
+        Q_ASSERT(!linkText.isEmpty() && !linkUrl.isEmpty());
+
+        m_editOps->insertLink(linkText, linkUrl);
+    }
+}
+
 bool VEdit::peekText(const QString &p_text, uint p_options, bool p_forward)
 {
     if (p_text.isEmpty()) {

+ 3 - 0
src/vedit.h

@@ -91,6 +91,9 @@ public:
     // User requests to insert an image.
     virtual void insertImage();
 
+    // User requests to insert a link.
+    virtual void insertLink();
+
     // Used for incremental search.
     // User has enter the content to search, but does not enter the "find" button yet.
     bool peekText(const QString &p_text, uint p_options, bool p_forward = true);

+ 7 - 0
src/veditoperations.cpp

@@ -90,3 +90,10 @@ void VEditOperations::requestUpdateVimStatus()
 {
     emit vimStatusUpdated(m_vim);
 }
+
+void VEditOperations::setVimMode(VimMode p_mode)
+{
+    if (m_vim && m_editConfig->m_enableVimMode) {
+        m_vim->setMode(p_mode);
+    }
+}

+ 7 - 7
src/veditoperations.h

@@ -18,11 +18,18 @@ class VEditOperations: public QObject
     Q_OBJECT
 public:
     VEditOperations(VEdit *p_editor, VFile *p_file);
+
     virtual ~VEditOperations();
+
     virtual bool insertImageFromMimeData(const QMimeData *source) = 0;
+
     virtual bool insertImage() = 0;
+
     virtual bool insertImageFromURL(const QUrl &p_imageUrl) = 0;
 
+    virtual bool insertLink(const QString &p_linkText,
+                            const QString &p_linkUrl) = 0;
+
     // Return true if @p_event has been handled and no need to be further
     // processed.
     virtual bool handleKeyPressEvent(QKeyEvent *p_event) = 0;
@@ -63,11 +70,4 @@ protected:
     VVim *m_vim;
 };
 
-inline void VEditOperations::setVimMode(VimMode p_mode)
-{
-    if (m_vim) {
-        m_vim->setMode(p_mode);
-    }
-}
-
 #endif // VEDITOPERATIONS_H

+ 4 - 0
src/vedittab.cpp

@@ -120,3 +120,7 @@ bool VEditTab::tabHasFocus() const
     QWidget *wid = QApplication::focusWidget();
     return wid == this || isAncestorOf(wid);
 }
+
+void VEditTab::insertLink()
+{
+}

+ 3 - 0
src/vedittab.h

@@ -49,6 +49,9 @@ public:
     // User requests to insert image.
     virtual void insertImage() = 0;
 
+    // User requests to insert link.
+    virtual void insertLink();
+
     // Search @p_text in current note.
     virtual void findText(const QString &p_text, uint p_options, bool p_peek,
                           bool p_forward = true) = 0;

+ 13 - 0
src/vmainwindow.cpp

@@ -463,6 +463,19 @@ void VMainWindow::initEditToolBar(QSize p_iconSize)
 
     m_editToolBar->addAction(inlineCodeAct);
 
+    // Insert link.
+    QAction *insetLinkAct = new QAction(QIcon(":/resources/icons/link.svg"),
+                                        tr("Insert Link (Ctrl+L)"), this);
+    insetLinkAct->setStatusTip(tr("Insert a link"));
+    connect(insetLinkAct, &QAction::triggered,
+            this, [this]() {
+                if (m_curTab) {
+                    m_curTab->insertLink();
+                }
+            });
+
+    m_editToolBar->addAction(insetLinkAct);
+
     // Insert image.
     QAction *insertImageAct = new QAction(QIcon(":/resources/icons/insert_image.svg"),
                                           tr("Insert Image"),

+ 24 - 0
src/vmdeditoperations.cpp

@@ -278,6 +278,17 @@ bool VMdEditOperations::handleKeyPressEvent(QKeyEvent *p_event)
         break;
     }
 
+    case Qt::Key_L:
+    {
+        if (modifiers == Qt::ControlModifier) {
+            m_editor->insertLink();
+            p_event->accept();
+            ret = true;
+        }
+
+        break;
+    }
+
     case Qt::Key_O:
     {
         if (modifiers == Qt::ControlModifier) {
@@ -832,3 +843,16 @@ void VMdEditOperations::decorateStrikethrough()
     cursor.endEditBlock();
     m_editor->setTextCursor(cursor);
 }
+
+bool VMdEditOperations::insertLink(const QString &p_linkText,
+                                   const QString &p_linkUrl)
+{
+    QString link = QString("[%1](%2)").arg(p_linkText).arg(p_linkUrl);
+    QTextCursor cursor = m_editor->textCursor();
+    cursor.insertText(link);
+    m_editor->setTextCursor(cursor);
+
+    setVimMode(VimMode::Insert);
+
+    return true;
+}

+ 7 - 0
src/vmdeditoperations.h

@@ -16,11 +16,18 @@ class VMdEditOperations : public VEditOperations
     Q_OBJECT
 public:
     VMdEditOperations(VEdit *p_editor, VFile *p_file);
+
     bool insertImageFromMimeData(const QMimeData *source) Q_DECL_OVERRIDE;
+
     bool insertImage() Q_DECL_OVERRIDE;
+
     bool handleKeyPressEvent(QKeyEvent *p_event) Q_DECL_OVERRIDE;
+
     bool insertImageFromURL(const QUrl &p_imageUrl) Q_DECL_OVERRIDE;
 
+    bool insertLink(const QString &p_linkText,
+                    const QString &p_linkUrl);
+
     // Insert decoration markers or decorate selected text.
     // If it is Vim Normal mode, change to Insert mode first.
     void decorateText(TextDecoration p_decoration) Q_DECL_OVERRIDE;

+ 10 - 0
src/vmdtab.cpp

@@ -469,6 +469,16 @@ void VMdTab::insertImage()
     m_editor->insertImage();
 }
 
+void VMdTab::insertLink()
+{
+    if (!m_isEditMode) {
+        return;
+    }
+
+    Q_ASSERT(m_editor);
+    m_editor->insertLink();
+}
+
 void VMdTab::findText(const QString &p_text, uint p_options, bool p_peek,
                       bool p_forward)
 {

+ 2 - 0
src/vmdtab.h

@@ -36,6 +36,8 @@ public:
 
     void insertImage() Q_DECL_OVERRIDE;
 
+    void insertLink() Q_DECL_OVERRIDE;
+
     // Search @p_text in current note.
     void findText(const QString &p_text, uint p_options, bool p_peek,
                   bool p_forward = true) Q_DECL_OVERRIDE;

+ 1 - 0
src/vnote.qrc

@@ -132,5 +132,6 @@
         <file>resources/icons/create_subdir.svg</file>
         <file>resources/icons/compact_mode.svg</file>
         <file>resources/icons/heading_sequence.svg</file>
+        <file>resources/icons/link.svg</file>
     </qresource>
 </RCC>