Browse Source

support custom shortcuts

Le Tan 8 years ago
parent
commit
ba84489c68

+ 31 - 1
src/resources/docs/shortcuts_en.md

@@ -72,6 +72,36 @@ Expand the selection to the beginning or end of current line.
 - `Ctrl+Shift+Home`, `Ctrl+Shift+End`  
 Expand the selection to the beginning or end of current note.
 
+## Custom Shortcuts
+VNote supports customing some standard shortcuts, though it is not recommended. VNote stores shortcuts' configuration information in the `[shortcuts]` section of user configuration file `vnote.ini`.
+
+For example, the default configruation may look like this:
+
+```ini
+[shortcuts]
+1\operation=NewNote
+1\keysequence=Ctrl+N
+2\operation=SaveNote
+2\keysequence=Ctrl+S
+3\operation=SaveAndRead
+3\keysequence=Ctrl+T
+4\operation=EditNote
+4\keysequence=Ctrl+W
+5\operation=CloseNote
+5\keysequence=
+6\operation=Find
+6\keysequence=Ctrl+F
+7\operation=FindNext
+7\keysequence=F3
+8\operation=FindPrevious
+8\keysequence=Shift+F3
+size=8
+```
+
+`size=8` tells VNote that there are 8 shotcuts defined here, with each beginning with the number sequence. You could change the `keysequence` value to change the default key sequence of a specified operation. Leave the `keysequence` empty (`keysequence=`) to disable shortcut for that operation.
+
+Pay attention that `Ctrl+E` is reserved for *Captain Mode* and `Ctrl+Q` is reserved for quitting VNote.
+
 # Captain Mode
 To efficiently utilize the shortcuts, VNote supports the **Captain Mode**.
 
@@ -117,7 +147,7 @@ Move current tab one split window right.
 Display shortcuts documentation.
 
 ## Navigation Mode
-Within the Captain MOde, `W` will turn VNote into **Navigation Mode**. In this mode, VNote will display at most two characters on some major widgets, and then pressing corresponding characters will jump to that widget.
+Within the Captain Mode, `W` will turn VNote into **Navigation Mode**. In this mode, VNote will display at most two characters on some major widgets, and then pressing corresponding characters will jump to that widget.
 
 # Vim Mode
 VNote supports a simple but useful Vim mode, including **Normal**, **Insert**, **Visual**, and **VisualLine** modes.

+ 31 - 0
src/resources/docs/shortcuts_zh.md

@@ -72,6 +72,37 @@
 - `Ctrl+Shift+Home`, `Ctrl+Shift+End`  
 扩展选定到笔记开始或结尾处。
 
+## 自定义快捷键
+VNote支持自定义部分标准快捷键(但并不建议这么做)。VNote将快捷键信息保存在用户配置文件`vnote.ini`中的`[shortcuts]`小节。
+
+例如,默认的配置可能是这样子的:
+
+
+```ini
+[shortcuts]
+1\operation=NewNote
+1\keysequence=Ctrl+N
+2\operation=SaveNote
+2\keysequence=Ctrl+S
+3\operation=SaveAndRead
+3\keysequence=Ctrl+T
+4\operation=EditNote
+4\keysequence=Ctrl+W
+5\operation=CloseNote
+5\keysequence=
+6\operation=Find
+6\keysequence=Ctrl+F
+7\operation=FindNext
+7\keysequence=F3
+8\operation=FindPrevious
+8\keysequence=Shift+F3
+size=8
+```
+
+`size=8` 告诉VNote这里定义了8组快捷键,每组快捷键都以一个数字序号开始。通过改变每组快捷键中`keysequence`的值来改变某个操作的默认快捷键。将`keysequence`设置为空(`keysequence=`)则会禁用该操作的任何快捷键。
+
+注意,`Ctrl+E`保留作为*舰长模式*的前导键,`Ctrl+Q`保留为退出VNote。
+
 # 舰长模式
 为了更有效地利用快捷键,VNote支持 **舰长模式**。
 

+ 10 - 0
src/resources/icons/close_note_tb.svg

@@ -0,0 +1,10 @@
+<?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">
+<path d="M443.6,387.1L312.4,255.4l131.5-130c5.4-5.4,5.4-14.2,0-19.6l-37.4-37.6c-2.6-2.6-6.1-4-9.8-4c-3.7,0-7.2,1.5-9.8,4
+	L256,197.8L124.9,68.3c-2.6-2.6-6.1-4-9.8-4c-3.7,0-7.2,1.5-9.8,4L68,105.9c-5.4,5.4-5.4,14.2,0,19.6l131.5,130L68.4,387.1
+	c-2.6,2.6-4.1,6.1-4.1,9.8c0,3.7,1.4,7.2,4.1,9.8l37.4,37.6c2.7,2.7,6.2,4.1,9.8,4.1c3.5,0,7.1-1.3,9.8-4.1L256,313.1l130.7,131.1
+	c2.7,2.7,6.2,4.1,9.8,4.1c3.5,0,7.1-1.3,9.8-4.1l37.4-37.6c2.6-2.6,4.1-6.1,4.1-9.8C447.7,393.2,446.2,389.7,443.6,387.1z"/>
+</svg>

+ 1 - 1
src/resources/styles/default.mdhl

@@ -13,7 +13,7 @@ editor
 # Do not use "" to quote the name
 font-family: Hiragino Sans GB, 冬青黑体, Microsoft YaHei, 微软雅黑, Microsoft YaHei UI, WenQuanYi Micro Hei, 文泉驿雅黑, Dengxian, 等线体, STXihei, 华文细黑, Liberation Sans, Droid Sans, NSimSun, 新宋体, SimSun, 宋体, Helvetica, sans-serif, Tahoma, Arial, Verdana, Geneva, Georgia, Times New Roman
 # [VNote] Style for trailing space
-trailing-space: ffebee
+trailing-space: a8a8a8
 font-size: 12
 line-number-background: bdbdbd
 line-number-foreground: 424242

+ 24 - 0
src/resources/vnote.ini

@@ -70,3 +70,27 @@ tools_dock_checked=true
 4\name=LightGrey
 4\rgb=D3D3D3
 size=4
+
+[shortcuts]
+; Define shortcuts here, with each item in the form "operation->keysequence".
+; Leave keysequence empty to disable the shortcut of an operation.
+; Custom shortcuts may conflict with some key bindings in edit mode or Vim mode.
+; Ctrl+E is reserved for Captain Mode.
+; Ctrl+Q is reserved for quitting VNote.
+1\operation=NewNote
+1\keysequence=Ctrl+N
+2\operation=SaveNote
+2\keysequence=Ctrl+S
+3\operation=SaveAndRead
+3\keysequence=Ctrl+T
+4\operation=EditNote
+4\keysequence=Ctrl+W
+5\operation=CloseNote
+5\keysequence=
+6\operation=Find
+6\keysequence=Ctrl+F
+7\operation=FindNext
+7\keysequence=F3
+8\operation=FindPrevious
+8\keysequence=Shift+F3
+size=8

+ 92 - 1
src/vconfigmanager.cpp

@@ -153,6 +153,8 @@ void VConfigManager::initialize()
 
     m_editorLineNumber = getConfigFromSettings("global",
                                                "editor_line_number").toInt();
+
+    readShortcutsFromSettings();
 }
 
 void VConfigManager::readPredefinedColorsFromSettings()
@@ -338,7 +340,7 @@ void VConfigManager::updateMarkdownEditStyle()
     static const QString defaultVimInsertBg = "#CDC0B0";
     static const QString defaultVimVisualBg = "#90CAF9";
     static const QString defaultVimReplaceBg = "#F8BBD0";
-    static const QString defaultTrailingSpaceBackground = "#FFEBEE";
+    static const QString defaultTrailingSpaceBackground = "#A8A8A8";
     static const QString defaultLineNumberBg = "#BDBDBD";
     static const QString defaultLineNumberFg = "#424242";
 
@@ -483,6 +485,13 @@ QString VConfigManager::getConfigFolder() const
     return VUtils::basePathFromPath(iniPath);
 }
 
+QString VConfigManager::getConfigFilePath() const
+{
+    V_ASSERT(userSettings);
+
+    return userSettings->fileName();
+}
+
 QString VConfigManager::getStyleConfigFolder() const
 {
     return getConfigFolder() + QDir::separator() + c_styleConfigFolder;
@@ -690,3 +699,85 @@ QString VConfigManager::getVnoteNotebookFolderPath()
 {
     return QDir::home().filePath(c_vnoteNotebookFolderName);
 }
+
+bool VConfigManager::isValidKeySequence(const QString &p_seq)
+{
+    QString lower = p_seq.toLower();
+    return lower != "ctrl+q" && lower != "ctrl+e";
+}
+
+void VConfigManager::readShortcutsFromSettings()
+{
+    m_shortcuts.clear();
+    int size = defaultSettings->beginReadArray("shortcuts");
+    for (int i = 0; i < size; ++i) {
+        defaultSettings->setArrayIndex(i);
+        QString op = defaultSettings->value("operation").toString();
+        QString seq = defaultSettings->value("keysequence").toString().trimmed();
+
+        if (isValidKeySequence(seq)) {
+            qDebug() << "read shortcut config" << op << seq;
+            m_shortcuts[op] = seq;
+        }
+    }
+
+    defaultSettings->endArray();
+
+    // Whether we need to update user settings.
+    bool needUpdate = false;
+    size = userSettings->beginReadArray("shortcuts");
+    QSet<QString> matched;
+    matched.reserve(m_shortcuts.size());
+    for (int i = 0; i < size; ++i) {
+        userSettings->setArrayIndex(i);
+        QString op = userSettings->value("operation").toString();
+        QString seq = userSettings->value("keysequence").toString().trimmed();
+
+        if (isValidKeySequence(seq)) {
+            qDebug() << "read user shortcut config" << op << seq;
+            auto it = m_shortcuts.find(op);
+            if (it == m_shortcuts.end()) {
+                // Could not find this in default settings.
+                needUpdate = true;
+            } else {
+                matched.insert(op);
+                *it = seq;
+            }
+        }
+    }
+
+    userSettings->endArray();
+
+    if (needUpdate || matched.size() < m_shortcuts.size()) {
+        // Write the combined config to user settings.
+        writeShortcutsToSettings();
+    }
+}
+
+void VConfigManager::writeShortcutsToSettings()
+{
+    // Clear it first
+    userSettings->beginGroup("shortcuts");
+    userSettings->remove("");
+    userSettings->endGroup();
+
+    userSettings->beginWriteArray("shortcuts");
+    int idx = 0;
+    for (auto it = m_shortcuts.begin(); it != m_shortcuts.end(); ++it, ++idx) {
+        userSettings->setArrayIndex(idx);
+        userSettings->setValue("operation", it.key());
+        userSettings->setValue("keysequence", it.value());
+    }
+
+    userSettings->endArray();
+}
+
+QString VConfigManager::getShortcutKeySequence(const QString &p_operation) const
+{
+    auto it = m_shortcuts.find(p_operation);
+    if (it == m_shortcuts.end()) {
+        return QString();
+    }
+
+    return *it;
+}

+ 25 - 0
src/vconfigmanager.h

@@ -205,9 +205,16 @@ public:
     const QString &getEditorLineNumberBg() const;
     const QString &getEditorLineNumberFg() const;
 
+    // Return the configured key sequence of @p_operation.
+    // Return empty if there is no corresponding config.
+    QString getShortcutKeySequence(const QString &p_operation) const;
+
     // Get the folder the ini file exists.
     QString getConfigFolder() const;
 
+    // Get the ini config file path.
+    QString getConfigFilePath() const;
+
     // Get the folder c_styleConfigFolder in the config folder.
     QString getStyleConfigFolder() const;
 
@@ -248,6 +255,20 @@ private:
     // the new one; if not, use the c_dirConfigFile.
     static QString fetchDirConfigFilePath(const QString &p_path);
 
+    // Read the [shortcuts] section in settings to init m_shortcuts.
+    // Will remove invalid config items.
+    // First read the config in default settings;
+    // Second read the config in user settings and overwrite the default ones;
+    // If there is any config in deafult settings that is absent in user settings,
+    // write the combined configs to user settings.
+    void readShortcutsFromSettings();
+
+    // Write m_shortcuts to the [shortcuts] section in the user settings.
+    void writeShortcutsToSettings();
+
+    // Whether @p_seq is a valid key sequence for shortcuts.
+    bool isValidKeySequence(const QString &p_seq);
+
     // Default font and palette.
     QFont m_defaultEditFont;
     QPalette m_defaultEditPalette;
@@ -379,6 +400,10 @@ private:
     // The foreground color of the line number area.
     QString m_editorLineNumberFg;
 
+    // Shortcuts config.
+    // Operation -> KeySequence.
+    QHash<QString, QString> m_shortcuts;
+
     // The name of the config file in each directory, obsolete.
     // Use c_dirConfigFile instead.
     static const QString c_obsoleteDirConfigFile;

+ 65 - 12
src/vmainwindow.cpp

@@ -301,7 +301,9 @@ void VMainWindow::initFileToolBar()
     newNoteAct = new QAction(QIcon(":/resources/icons/create_note_tb.svg"),
                              tr("New &Note"), this);
     newNoteAct->setStatusTip(tr("Create a note in current folder"));
-    newNoteAct->setShortcut(QKeySequence::New);
+    QString keySeq = vconfig.getShortcutKeySequence("NewNote");
+    qDebug() << "set NewNote shortcut to" << keySeq;
+    newNoteAct->setShortcut(QKeySequence(keySeq));
     connect(newNoteAct, &QAction::triggered,
             fileList, &VFileList::newFile);
 
@@ -317,10 +319,25 @@ void VMainWindow::initFileToolBar()
     connect(deleteNoteAct, &QAction::triggered,
             this, &VMainWindow::deleteCurNote);
 
+    m_closeNoteAct = new QAction(QIcon(":/resources/icons/close_note_tb.svg"),
+                                 tr("&Close Note"), this);
+    m_closeNoteAct->setStatusTip(tr("Close current note"));
+    keySeq = vconfig.getShortcutKeySequence("CloseNote");
+    qDebug() << "set CloseNote shortcut to" << keySeq;
+    m_closeNoteAct->setShortcut(QKeySequence(keySeq));
+    connect(m_closeNoteAct, &QAction::triggered,
+            this, [this](){
+                if (m_curFile) {
+                    editArea->closeFile(m_curFile, false);
+                }
+            });
+
     editNoteAct = new QAction(QIcon(":/resources/icons/edit_note.svg"),
                               tr("&Edit"), this);
     editNoteAct->setStatusTip(tr("Edit current note"));
-    editNoteAct->setShortcut(QKeySequence("Ctrl+W"));
+    keySeq = vconfig.getShortcutKeySequence("EditNote");
+    qDebug() << "set EditNote shortcut to" << keySeq;
+    editNoteAct->setShortcut(QKeySequence(keySeq));
     connect(editNoteAct, &QAction::triggered,
             editArea, &VEditArea::editFile);
 
@@ -339,21 +356,26 @@ void VMainWindow::initFileToolBar()
                               tr("Save Changes And Read (Ctrl+T)"), this);
     saveExitAct->setStatusTip(tr("Save changes and exit edit mode"));
     saveExitAct->setMenu(exitEditMenu);
-    saveExitAct->setShortcut(QKeySequence("Ctrl+T"));
+    keySeq = vconfig.getShortcutKeySequence("SaveAndRead");
+    qDebug() << "set SaveAndRead shortcut to" << keySeq;
+    saveExitAct->setShortcut(QKeySequence(keySeq));
     connect(saveExitAct, &QAction::triggered,
             editArea, &VEditArea::saveAndReadFile);
 
     saveNoteAct = new QAction(QIcon(":/resources/icons/save_note.svg"),
                               tr("Save"), this);
     saveNoteAct->setStatusTip(tr("Save changes to current note"));
-    saveNoteAct->setShortcut(QKeySequence::Save);
+    keySeq = vconfig.getShortcutKeySequence("SaveNote");
+    qDebug() << "set SaveNote shortcut to" << keySeq;
+    saveNoteAct->setShortcut(QKeySequence(keySeq));
     connect(saveNoteAct, &QAction::triggered,
             editArea, &VEditArea::saveFile);
 
     newRootDirAct->setEnabled(false);
     newNoteAct->setEnabled(false);
-    noteInfoAct->setEnabled(false);
-    deleteNoteAct->setEnabled(false);
+    noteInfoAct->setVisible(false);
+    deleteNoteAct->setVisible(false);
+    m_closeNoteAct->setVisible(false);
     editNoteAct->setVisible(false);
     saveExitAct->setVisible(false);
     discardExitAct->setVisible(false);
@@ -361,9 +383,10 @@ void VMainWindow::initFileToolBar()
 
     fileToolBar->addAction(newRootDirAct);
     fileToolBar->addAction(newNoteAct);
+    fileToolBar->addSeparator();
     fileToolBar->addAction(noteInfoAct);
     fileToolBar->addAction(deleteNoteAct);
-    fileToolBar->addSeparator();
+    fileToolBar->addAction(m_closeNoteAct);
     fileToolBar->addAction(editNoteAct);
     fileToolBar->addAction(saveExitAct);
     fileToolBar->addAction(saveNoteAct);
@@ -634,6 +657,29 @@ void VMainWindow::initFileMenu()
 
     fileMenu->addAction(settingsAct);
 
+    QAction *customShortcutAct = new QAction(tr("Custom Shortcuts"), this);
+    customShortcutAct->setToolTip(tr("Custom some standard shortcuts"));
+    connect(customShortcutAct, &QAction::triggered,
+            this, [this](){
+                int ret = VUtils::showMessage(QMessageBox::Information,
+                              tr("Custom Shortcuts"),
+                              tr("VNote supports customing some standard shorcuts by "
+                                 "editing user's configuration file (vnote.ini). Please "
+                                 "reference the shortcuts help documentation for more "
+                                 "information."),
+                              tr("Click \"OK\" to custom shortcuts."),
+                              QMessageBox::Ok | QMessageBox::Cancel,
+                              QMessageBox::Ok,
+                              this);
+
+                if (ret == QMessageBox::Ok) {
+                    QUrl url = QUrl::fromLocalFile(vconfig.getConfigFilePath());
+                    QDesktopServices::openUrl(url);
+                }
+            });
+
+    fileMenu->addAction(customShortcutAct);
+
     fileMenu->addSeparator();
 
     // Exit.
@@ -663,19 +709,25 @@ void VMainWindow::initEditMenu()
     m_findReplaceAct = newAction(QIcon(":/resources/icons/find_replace.svg"),
                                  tr("Find/Replace"), this);
     m_findReplaceAct->setToolTip(tr("Open Find/Replace dialog to search in current note"));
-    m_findReplaceAct->setShortcut(QKeySequence::Find);
+    QString keySeq = vconfig.getShortcutKeySequence("Find");
+    qDebug() << "set Find shortcut to" << keySeq;
+    m_findReplaceAct->setShortcut(QKeySequence(keySeq));
     connect(m_findReplaceAct, &QAction::triggered,
             this, &VMainWindow::openFindDialog);
 
     m_findNextAct = new QAction(tr("Find Next"), this);
     m_findNextAct->setToolTip(tr("Find next occurence"));
-    m_findNextAct->setShortcut(QKeySequence::FindNext);
+    keySeq = vconfig.getShortcutKeySequence("FindNext");
+    qDebug() << "set FindNext shortcut to" << keySeq;
+    m_findNextAct->setShortcut(QKeySequence(keySeq));
     connect(m_findNextAct, SIGNAL(triggered(bool)),
             m_findReplaceDialog, SLOT(findNext()));
 
     m_findPreviousAct = new QAction(tr("Find Previous"), this);
     m_findPreviousAct->setToolTip(tr("Find previous occurence"));
-    m_findPreviousAct->setShortcut(QKeySequence::FindPrevious);
+    keySeq = vconfig.getShortcutKeySequence("FindPrevious");
+    qDebug() << "set FindPrevious shortcut to" << keySeq;
+    m_findPreviousAct->setShortcut(QKeySequence(keySeq));
     connect(m_findPreviousAct, SIGNAL(triggered(bool)),
             m_findReplaceDialog, SLOT(findPrevious()));
 
@@ -1279,8 +1331,9 @@ void VMainWindow::updateActionStateFromTabStatusChange(const VFile *p_file,
     discardExitAct->setVisible(p_file && p_editMode);
     saveExitAct->setVisible(p_file && p_editMode);
     saveNoteAct->setVisible(p_file && p_editMode);
-    deleteNoteAct->setEnabled(p_file && p_file->isModifiable());
-    noteInfoAct->setEnabled(p_file && p_file->getType() == FileType::Normal);
+    deleteNoteAct->setVisible(p_file && p_file->isModifiable());
+    noteInfoAct->setVisible(p_file && p_file->getType() == FileType::Normal);
+    m_closeNoteAct->setVisible(p_file);
 
     m_insertImageAct->setEnabled(p_file && p_editMode);
 

+ 1 - 0
src/vmainwindow.h

@@ -178,6 +178,7 @@ private:
     QAction *newNoteAct;
     QAction *noteInfoAct;
     QAction *deleteNoteAct;
+    QAction *m_closeNoteAct;
     QAction *editNoteAct;
     QAction *saveNoteAct;
     QAction *saveExitAct;

+ 1 - 0
src/vnote.qrc

@@ -115,5 +115,6 @@
         <file>resources/icons/italic.svg</file>
         <file>resources/icons/strikethrough.svg</file>
         <file>resources/icons/inline_code.svg</file>
+        <file>resources/icons/close_note_tb.svg</file>
     </qresource>
 </RCC>