Browse Source

add shortcuts

1. `Ctrl + W`, `Ctrl + R`, `Ctrl + Q`, `Ctrl + S` for edit and view mode;
2. `Ctrl + E` to toggle expand view;
3. `Ctrl + N` to create note in current directory;
4. `Ctrl + H` in edit mode to delete previous char;
5. `Ctrl + W` in edit mode to delete till the start of previous word;
6. `Ctrl + U` in edit mode to delete till the start of current line;
7. `Tab`, `Shift + Tab` to indent or unindent selected lines;
8. `Ctrl + B`, `Ctrl + I` to make selected text bold or italic;

Signed-off-by: Le Tan <[email protected]>
Le Tan 9 years ago
parent
commit
d2f61bc605
7 changed files with 322 additions and 28 deletions
  1. 22 5
      src/veditoperations.cpp
  2. 13 1
      src/veditoperations.h
  3. 5 0
      src/vmainwindow.cpp
  4. 7 19
      src/vmdedit.cpp
  5. 0 3
      src/vmdedit.h
  6. 265 0
      src/vmdeditoperations.cpp
  7. 10 0
      src/vmdeditoperations.h

+ 22 - 5
src/veditoperations.cpp

@@ -1,21 +1,38 @@
 #include <QTextCursor>
 #include <QTextDocument>
+#include <QFontMetrics>
 #include "vedit.h"
 #include "veditoperations.h"
+#include "vconfigmanager.h"
+
+extern VConfigManager vconfig;
 
 VEditOperations::VEditOperations(VEdit *p_editor, VFile *p_file)
-    : m_editor(p_editor), m_file(p_file)
+    : m_editor(p_editor), m_file(p_file), m_expandTab(false),
+      m_keyState(KeyState::Normal)
 {
+    updateTabSettings();
 }
 
-void VEditOperations::insertTextAtCurPos(const QString &text)
+void VEditOperations::insertTextAtCurPos(const QString &p_text)
 {
-    QTextCursor cursor(m_editor->document());
-    cursor.setPosition(m_editor->textCursor().position());
-    cursor.insertText(text);
+    QTextCursor cursor = m_editor->textCursor();
+    cursor.insertText(p_text);
+    m_editor->setTextCursor(cursor);
 }
 
 VEditOperations::~VEditOperations()
 {
+}
 
+void VEditOperations::updateTabSettings()
+{
+    if (vconfig.getTabStopWidth() > 0) {
+        QFontMetrics metrics(vconfig.getMdEditFont());
+        m_editor->setTabStopWidth(vconfig.getTabStopWidth() * metrics.width(' '));
+    }
+    m_expandTab = vconfig.getIsExpandTab();
+    if (m_expandTab && (vconfig.getTabStopWidth() > 0)) {
+        m_tabSpaces = QString(vconfig.getTabStopWidth(), ' ');
+    }
 }

+ 13 - 1
src/veditoperations.h

@@ -2,10 +2,14 @@
 #define VEDITOPERATIONS_H
 
 #include <QPointer>
+#include <QString>
 #include "vfile.h"
 
 class VEdit;
 class QMimeData;
+class QKeyEvent;
+
+enum class KeyState { Normal = 0, Vim };
 
 class VEditOperations
 {
@@ -15,11 +19,19 @@ public:
     virtual bool insertImageFromMimeData(const QMimeData *source) = 0;
     virtual bool insertURLFromMimeData(const QMimeData *source) = 0;
     virtual bool insertImage() = 0;
+    // Return true if @p_event has been handled and no need to be further
+    // processed.
+    virtual bool handleKeyPressEvent(QKeyEvent *p_event) = 0;
+    void updateTabSettings();
 
 protected:
-    void insertTextAtCurPos(const QString &text);
+    void insertTextAtCurPos(const QString &p_text);
+
     VEdit *m_editor;
     QPointer<VFile> m_file;
+    bool m_expandTab;
+    QString m_tabSpaces;
+    KeyState m_keyState;
 };
 
 #endif // VEDITOPERATIONS_H

+ 5 - 0
src/vmainwindow.cpp

@@ -130,6 +130,7 @@ void VMainWindow::initActions()
     newNoteAct = new QAction(QIcon(":/resources/icons/create_note_tb.svg"),
                              tr("New &Note"), this);
     newNoteAct->setStatusTip(tr("Create a note in current directory"));
+    newNoteAct->setShortcut(QKeySequence("Ctrl+N"));
     connect(newNoteAct, &QAction::triggered,
             fileList, &VFileList::newFile);
 
@@ -219,12 +220,14 @@ void VMainWindow::initEditActions()
     editNoteAct = new QAction(QIcon(":/resources/icons/edit_note.svg"),
                               tr("&Edit"), this);
     editNoteAct->setStatusTip(tr("Edit current note"));
+    editNoteAct->setShortcut(QKeySequence("Ctrl+W"));
     connect(editNoteAct, &QAction::triggered,
             editArea, &VEditArea::editFile);
 
     discardExitAct = new QAction(QIcon(":/resources/icons/discard_exit.svg"),
                                  tr("Discard Changes And Exit"), this);
     discardExitAct->setStatusTip(tr("Discard changes and exit edit mode"));
+    discardExitAct->setShortcut(QKeySequence("Ctrl+Q"));
     connect(discardExitAct, &QAction::triggered,
             editArea, &VEditArea::readFile);
 
@@ -235,6 +238,7 @@ void VMainWindow::initEditActions()
                               tr("Save Changes And Exit"), this);
     saveExitAct->setStatusTip(tr("Save changes and exit edit mode"));
     saveExitAct->setMenu(exitEditMenu);
+    saveExitAct->setShortcut(QKeySequence("Ctrl+R"));
     connect(saveExitAct, &QAction::triggered,
             editArea, &VEditArea::saveAndReadFile);
 
@@ -269,6 +273,7 @@ void VMainWindow::initViewActions()
                                 tr("Expand"), this);
     expandViewAct->setStatusTip(tr("Expand the edit area"));
     expandViewAct->setCheckable(true);
+    expandViewAct->setShortcut(QKeySequence("Ctrl+E"));
     expandViewAct->setMenu(panelMenu);
     connect(expandViewAct, &QAction::triggered,
             this, &VMainWindow::expandPanelView);

+ 7 - 19
src/vmdedit.cpp

@@ -28,7 +28,7 @@ VMdEdit::VMdEdit(VFile *p_file, QWidget *p_parent)
     connect(this, &VMdEdit::cursorPositionChanged,
             this, &VMdEdit::updateCurHeader);
 
-    updateTabSettings();
+    m_editOps->updateTabSettings();
     updateFontAndPalette();
 }
 
@@ -38,21 +38,9 @@ void VMdEdit::updateFontAndPalette()
     setPalette(vconfig.getMdEditPalette());
 }
 
-void VMdEdit::updateTabSettings()
-{
-    if (vconfig.getTabStopWidth() > 0) {
-        QFontMetrics metrics(vconfig.getMdEditFont());
-        setTabStopWidth(vconfig.getTabStopWidth() * metrics.width(' '));
-    }
-    m_expandTab = vconfig.getIsExpandTab();
-    if (m_expandTab && (vconfig.getTabStopWidth() > 0)) {
-        m_tabSpaces = QString(vconfig.getTabStopWidth(), ' ');
-    }
-}
-
 void VMdEdit::beginEdit()
 {
-    updateTabSettings();
+    m_editOps->updateTabSettings();
     updateFontAndPalette();
 
     setFont(vconfig.getMdEditFont());
@@ -93,10 +81,7 @@ void VMdEdit::reloadFile()
 
 void VMdEdit::keyPressEvent(QKeyEvent *event)
 {
-    if ((event->key() == Qt::Key_Tab) && m_expandTab) {
-        QTextCursor cursor(document());
-        cursor.setPosition(textCursor().position());
-        cursor.insertText(m_tabSpaces);
+    if (m_editOps->handleKeyPressEvent(event)) {
         return;
     }
     VEdit::keyPressEvent(event);
@@ -381,6 +366,10 @@ void VMdEdit::updateImagePreviewBlock(int p_block, const QString &p_image)
         return;
     }
     QTextCursor cursor(block);
+    int shift = block.text().indexOf(QChar::ObjectReplacementCharacter);
+    if (shift > 0) {
+        cursor.movePosition(QTextCursor::Right, QTextCursor::MoveAnchor, shift + 1);
+    }
     QTextImageFormat format = cursor.charFormat().toImageFormat();
     Q_ASSERT(format.isValid());
     QString curPath = format.property(ImagePath).toString();
@@ -418,7 +407,6 @@ QString VMdEdit::toPlainTextWithoutImg() const
         }
         start = removeObjectReplacementLine(text, index);
     } while (start < text.size());
-    qDebug() << text;
     return text;
 }
 

+ 0 - 3
src/vmdedit.h

@@ -44,7 +44,6 @@ protected:
 
 private:
     void updateFontAndPalette();
-    void updateTabSettings();
     void initInitImages();
     void clearUnusedImages();
     // p_text[p_index] is QChar::ObjectReplacementCharacter. Remove the line containing it.
@@ -68,8 +67,6 @@ private:
     HGMarkdownHighlighter *m_mdHighlighter;
     QVector<QString> m_insertedImages;
     QVector<QString> m_initImages;
-    bool m_expandTab;
-    QString m_tabSpaces;
     QVector<VHeader> m_headers;
 };
 

+ 265 - 0
src/vmdeditoperations.cpp

@@ -7,6 +7,8 @@
 #include <QImageReader>
 #include <QDir>
 #include <QMessageBox>
+#include <QKeyEvent>
+#include <QTextCursor>
 #include "vmdeditoperations.h"
 #include "dialog/vinsertimagedialog.h"
 #include "utils/vutils.h"
@@ -162,3 +164,266 @@ bool VMdEditOperations::insertImage()
     }
     return true;
 }
+
+bool VMdEditOperations::handleKeyPressEvent(QKeyEvent *p_event)
+{
+    switch (p_event->key()) {
+    case Qt::Key_Tab:
+    {
+        if (handleKeyTab(p_event)) {
+            return true;
+        }
+        break;
+    }
+
+    case Qt::Key_Backtab:
+    {
+        if (handleKeyBackTab(p_event)) {
+            return true;
+        }
+        break;
+    }
+
+    case Qt::Key_H:
+    {
+        if (handleKeyH(p_event)) {
+            return true;
+        }
+        break;
+    }
+
+    case Qt::Key_W:
+    {
+        if (handleKeyW(p_event)) {
+            return true;
+        }
+        break;
+    }
+
+    case Qt::Key_U:
+    {
+        if (handleKeyU(p_event)) {
+            return true;
+        }
+        break;
+    }
+
+    case Qt::Key_B:
+    {
+        if (handleKeyB(p_event)) {
+            return true;
+        }
+        break;
+    }
+
+    case Qt::Key_I:
+    {
+        if (handleKeyI(p_event)) {
+            return true;
+        }
+        break;
+    }
+
+    default:
+        // Fall through.
+        break;
+    }
+
+    m_keyState = KeyState::Normal;
+    return false;
+}
+
+bool VMdEditOperations::handleKeyTab(QKeyEvent *p_event)
+{
+    QTextDocument *doc = m_editor->document();
+    QString text("\t");
+    if (m_expandTab) {
+        text = m_tabSpaces;
+    }
+
+    if (p_event->modifiers() == Qt::NoModifier) {
+        QTextCursor cursor = m_editor->textCursor();
+        cursor.beginEditBlock();
+        if (cursor.hasSelection()) {
+            // Indent each selected line.
+            QTextBlock block = doc->findBlock(cursor.selectionStart());
+            QTextBlock endBlock = doc->findBlock(cursor.selectionEnd());
+            int endBlockNum = endBlock.blockNumber();
+            while (true) {
+                Q_ASSERT(block.isValid());
+                QTextCursor blockCursor(block);
+                blockCursor.insertText(text);
+
+                if (block.blockNumber() == endBlockNum) {
+                    break;
+                }
+                block = block.next();
+            }
+        } else {
+            // Just insert "tab".
+            insertTextAtCurPos(text);
+        }
+        cursor.endEditBlock();
+    } else {
+        return false;
+    }
+    p_event->accept();
+    return true;
+}
+
+bool VMdEditOperations::handleKeyBackTab(QKeyEvent *p_event)
+{
+    QTextDocument *doc = m_editor->document();
+    QTextCursor cursor = m_editor->textCursor();
+    QTextBlock block = doc->findBlock(cursor.selectionStart());
+    QTextBlock endBlock = doc->findBlock(cursor.selectionEnd());
+    int endBlockNum = endBlock.blockNumber();
+    cursor.beginEditBlock();
+    for (; block.isValid() && block.blockNumber() <= endBlockNum;
+         block = block.next()) {
+        QTextCursor blockCursor(block);
+        QString text = block.text();
+        if (text.isEmpty()) {
+            continue;
+        } else if (text[0] == '\t') {
+            blockCursor.deleteChar();
+            continue;
+        } else if (text[0] != ' ') {
+            continue;
+        } else {
+            // Spaces.
+            if (m_expandTab) {
+                int width = m_tabSpaces.size();
+                for (int i = 0; i < width; ++i) {
+                    if (text[i] == ' ') {
+                        blockCursor.deleteChar();
+                    } else {
+                        break;
+                    }
+                }
+                continue;
+            } else {
+                blockCursor.deleteChar();
+                continue;
+            }
+        }
+    }
+    cursor.endEditBlock();
+    p_event->accept();
+    return true;
+}
+
+bool VMdEditOperations::handleKeyB(QKeyEvent *p_event)
+{
+    if (p_event->modifiers() == Qt::ControlModifier) {
+        // Ctrl+B, Bold.
+        QTextCursor cursor = m_editor->textCursor();
+        if (cursor.hasSelection()) {
+            // Insert ** around the selected text.
+            int start = cursor.selectionStart();
+            int end = cursor.selectionEnd();
+            cursor.beginEditBlock();
+            cursor.clearSelection();
+            cursor.setPosition(start, QTextCursor::MoveAnchor);
+            cursor.insertText("**");
+            cursor.setPosition(end + 2, QTextCursor::MoveAnchor);
+            cursor.insertText("**");
+            cursor.endEditBlock();
+            m_editor->setTextCursor(cursor);
+        } else {
+            // Insert **** and place cursor in the middle.
+            cursor.beginEditBlock();
+            cursor.insertText("****");
+            cursor.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, 2);
+            cursor.endEditBlock();
+            m_editor->setTextCursor(cursor);
+        }
+
+        p_event->accept();
+        return true;
+    }
+    return false;
+}
+
+bool VMdEditOperations::handleKeyH(QKeyEvent *p_event)
+{
+    if (p_event->modifiers() == Qt::ControlModifier) {
+        // Ctrl+H, equal to backspace.
+        QTextCursor cursor = m_editor->textCursor();
+        cursor.deletePreviousChar();
+
+        p_event->accept();
+        return true;
+    }
+    return false;
+}
+
+bool VMdEditOperations::handleKeyI(QKeyEvent *p_event)
+{
+    if (p_event->modifiers() == Qt::ControlModifier) {
+        // Ctrl+I, Italic.
+        QTextCursor cursor = m_editor->textCursor();
+        if (cursor.hasSelection()) {
+            // Insert * around the selected text.
+            int start = cursor.selectionStart();
+            int end = cursor.selectionEnd();
+            cursor.beginEditBlock();
+            cursor.clearSelection();
+            cursor.setPosition(start, QTextCursor::MoveAnchor);
+            cursor.insertText("*");
+            cursor.setPosition(end + 1, QTextCursor::MoveAnchor);
+            cursor.insertText("*");
+            cursor.endEditBlock();
+            m_editor->setTextCursor(cursor);
+        } else {
+            // Insert ** and place cursor in the middle.
+            cursor.beginEditBlock();
+            cursor.insertText("**");
+            cursor.movePosition(QTextCursor::Left, QTextCursor::MoveAnchor, 1);
+            cursor.endEditBlock();
+            m_editor->setTextCursor(cursor);
+        }
+
+        p_event->accept();
+        return true;
+    }
+    return false;
+}
+
+bool VMdEditOperations::handleKeyU(QKeyEvent *p_event)
+{
+    if (p_event->modifiers() == Qt::ControlModifier) {
+        // Ctrl+U, delete till the start of line.
+        QTextCursor cursor = m_editor->textCursor();
+        bool ret;
+        if (cursor.atBlockStart()) {
+            ret = cursor.movePosition(QTextCursor::PreviousWord, QTextCursor::KeepAnchor);
+        } else {
+            ret = cursor.movePosition(QTextCursor::StartOfLine, QTextCursor::KeepAnchor);
+        }
+        if (ret) {
+            cursor.removeSelectedText();
+        }
+
+        p_event->accept();
+        return true;
+    }
+    return false;
+}
+
+bool VMdEditOperations::handleKeyW(QKeyEvent *p_event)
+{
+    if (p_event->modifiers() == Qt::ControlModifier) {
+        // Ctrl+W, delete till the start of previous word.
+        QTextCursor cursor = m_editor->textCursor();
+        bool ret = cursor.movePosition(QTextCursor::PreviousWord, QTextCursor::KeepAnchor);
+        if (ret) {
+            cursor.removeSelectedText();
+        }
+
+        p_event->accept();
+        return true;
+    }
+    return false;
+}
+

+ 10 - 0
src/vmdeditoperations.h

@@ -15,11 +15,21 @@ public:
     bool insertImageFromMimeData(const QMimeData *source) Q_DECL_OVERRIDE;
     bool insertURLFromMimeData(const QMimeData *source) Q_DECL_OVERRIDE;
     bool insertImage() Q_DECL_OVERRIDE;
+    bool handleKeyPressEvent(QKeyEvent *p_event) Q_DECL_OVERRIDE;
 
 private:
     bool insertImageFromURL(const QUrl &imageUrl);
     void insertImageFromPath(const QString &title, const QString &path, const QString &oriImagePath);
     void insertImageFromQImage(const QString &title, const QString &path, const QImage &image);
+
+    // Key press handlers.
+    bool handleKeyTab(QKeyEvent *p_event);
+    bool handleKeyBackTab(QKeyEvent *p_event);
+    bool handleKeyB(QKeyEvent *p_event);
+    bool handleKeyH(QKeyEvent *p_event);
+    bool handleKeyI(QKeyEvent *p_event);
+    bool handleKeyU(QKeyEvent *p_event);
+    bool handleKeyW(QKeyEvent *p_event);
 };
 
 #endif // VMDEDITOPERATIONS_H