From 00feaa13e215dae1db36a74f2c306f3a99be2f28 Mon Sep 17 00:00:00 2001 From: Le Tan Date: Wed, 18 Oct 2017 19:53:46 +0800 Subject: [PATCH] refactor Captain mode - Configuration [shortcuts] and [captain_mode_shortcuts] for shortcuts and shortcuts in Captain mode. --- src/main.cpp | 2 - src/resources/docs/shortcuts_en.md | 104 ++++++-- src/resources/docs/shortcuts_zh.md | 104 ++++++-- src/resources/vnote.ini | 95 +++++-- src/vcaptain.cpp | 406 ++++++++--------------------- src/vcaptain.h | 170 +++++++++--- src/vconfigmanager.cpp | 152 +++++++---- src/vconfigmanager.h | 20 +- src/veditarea.cpp | 184 +++++++++++++ src/veditarea.h | 30 +++ src/veditwindow.cpp | 10 +- src/veditwindow.h | 19 +- src/vmainwindow.cpp | 179 +++++++++---- src/vmainwindow.h | 50 +++- 14 files changed, 996 insertions(+), 529 deletions(-) diff --git a/src/main.cpp b/src/main.cpp index 7089dea2..41d0e590 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -13,7 +13,6 @@ #include "vconfigmanager.h" VConfigManager *g_config; -VMainWindow *g_mainWin; #if defined(QT_NO_DEBUG) QFile g_logFile; @@ -157,7 +156,6 @@ int main(int argc, char *argv[]) } VMainWindow w(&guard); - g_mainWin = &w; QString style = VUtils::readFileFromDisk(":/resources/vnote.qss"); if (!style.isEmpty()) { VUtils::processStyle(style, w.getPalette()); diff --git a/src/resources/docs/shortcuts_en.md b/src/resources/docs/shortcuts_en.md index 0942ac2f..efadd552 100644 --- a/src/resources/docs/shortcuts_en.md +++ b/src/resources/docs/shortcuts_en.md @@ -80,42 +80,102 @@ Expand the selection to the beginning or end of current line. 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`. +VNote supports customing some standard shortcuts, though it is not recommended. VNote stores shortcuts' configuration information in the `[shortcuts]` and `[captain_mode_shortcuts]` sections 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 +; 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+Q is reserved for quitting VNote. + +; Leader key of Captain mode +CaptainMode=Ctrl+E +; Create a note in current folder +NewNote=Ctrl+Alt+N +; Save current note +SaveNote=Ctrl+S +; Save changes and enter read mode +SaveAndRead=Ctrl+T +; Edit current note +EditNote=Ctrl+W +; Close current note +CloseNote= +; Open file/replace dialog +Find=Ctrl+F +; Find next occurence +FindNext=F3 +; Find previous occurence +FindPrevious=Shift+F3 + +[captain_mode_shortcuts] +; Define shortcuts in Captain mode here. +; There shortcuts are the sub-sequence after the CaptainMode key sequence +; in [shortcuts]. + +; Enter Navigation mode +NavigationMode=W +; Show attachment list of current note +AttachmentList=A +; Locate to the folder of current note +LocateCurrentFile=D +; Toggle Expand mode +ExpandMode=E +; Alternate one/two panels view +OnePanelView=P +; Discard changes and enter read mode +DiscardAndRead=Q +; Toggle Tools dock widget +ToolsDock=T +; Close current note +CloseNote=X +; Show shortcuts help document +ShortcutsHelp=? +; Flush the log file +FlushLogFile=";" +; Show opened files list +OpenedFileList=F +; Activate the ith tab +ActivateTab1=1 +ActivateTab2=2 +ActivateTab3=3 +ActivateTab4=4 +ActivateTab5=5 +ActivateTab6=6 +ActivateTab7=7 +ActivateTab8=8 +ActivateTab9=9 +; Alternate between current and last tab +AlternateTab=0 +; Activate next tab +ActivateNextTab=J +; Activate previous tab +ActivatePreviousTab=K +; Activate the window split on the left +ActivateSplitLeft=H +; Activate the window split on the right +ActivateSplitRight=L +; Move current tab one split left +MoveTabSplitLeft=Shift+H +; Move current tab one split right +MoveTabSplitRight=Shift+L +; Create a vertical split +VerticalSplit=V +; Remove current split +RemoveSplit=R ``` -`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. +Each item is in the form `operation=keysequence`, with `keysequence` empty to disable shortcuts for that operation. -Pay attention that `Ctrl+E` is reserved for *Captain Mode* and `Ctrl+Q` is reserved for quitting VNote. +Pay attention that `Ctrl+Q` is reserved for quitting VNote. # Captain Mode To efficiently utilize the shortcuts, VNote supports the **Captain Mode**. Press the leader key `Ctrl+E`, then VNote will enter the Captain Mode, within which VNote supports more efficient shortcuts. -By the way, in this mode, `Ctrl+W` and `W` is equivalent, thus pressing `Ctrl+E+W` equals to `Ctrl+E W`. - - `E` Toggle expanding the edit area. - `P` diff --git a/src/resources/docs/shortcuts_zh.md b/src/resources/docs/shortcuts_zh.md index 926c0aba..431e0ee3 100644 --- a/src/resources/docs/shortcuts_zh.md +++ b/src/resources/docs/shortcuts_zh.md @@ -80,43 +80,103 @@ 扩展选定到笔记开始或结尾处。 ## 自定义快捷键 -VNote支持自定义部分标准快捷键(但并不建议这么做)。VNote将快捷键信息保存在用户配置文件`vnote.ini`中的`[shortcuts]`小节。 +VNote支持自定义部分标准快捷键(但并不建议这么做)。VNote将快捷键信息保存在用户配置文件`vnote.ini`中的`[shortcuts]`和`[captain_mode_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 +; 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+Q is reserved for quitting VNote. + +; Leader key of Captain mode +CaptainMode=Ctrl+E +; Create a note in current folder +NewNote=Ctrl+Alt+N +; Save current note +SaveNote=Ctrl+S +; Save changes and enter read mode +SaveAndRead=Ctrl+T +; Edit current note +EditNote=Ctrl+W +; Close current note +CloseNote= +; Open file/replace dialog +Find=Ctrl+F +; Find next occurence +FindNext=F3 +; Find previous occurence +FindPrevious=Shift+F3 + +[captain_mode_shortcuts] +; Define shortcuts in Captain mode here. +; There shortcuts are the sub-sequence after the CaptainMode key sequence +; in [shortcuts]. + +; Enter Navigation mode +NavigationMode=W +; Show attachment list of current note +AttachmentList=A +; Locate to the folder of current note +LocateCurrentFile=D +; Toggle Expand mode +ExpandMode=E +; Alternate one/two panels view +OnePanelView=P +; Discard changes and enter read mode +DiscardAndRead=Q +; Toggle Tools dock widget +ToolsDock=T +; Close current note +CloseNote=X +; Show shortcuts help document +ShortcutsHelp=? +; Flush the log file +FlushLogFile=";" +; Show opened files list +OpenedFileList=F +; Activate the ith tab +ActivateTab1=1 +ActivateTab2=2 +ActivateTab3=3 +ActivateTab4=4 +ActivateTab5=5 +ActivateTab6=6 +ActivateTab7=7 +ActivateTab8=8 +ActivateTab9=9 +; Alternate between current and last tab +AlternateTab=0 +; Activate next tab +ActivateNextTab=J +; Activate previous tab +ActivatePreviousTab=K +; Activate the window split on the left +ActivateSplitLeft=H +; Activate the window split on the right +ActivateSplitRight=L +; Move current tab one split left +MoveTabSplitLeft=Shift+H +; Move current tab one split right +MoveTabSplitRight=Shift+L +; Create a vertical split +VerticalSplit=V +; Remove current split +RemoveSplit=R ``` -`size=8` 告诉VNote这里定义了8组快捷键,每组快捷键都以一个数字序号开始。通过改变每组快捷键中`keysequence`的值来改变某个操作的默认快捷键。将`keysequence`设置为空(`keysequence=`)则会禁用该操作的任何快捷键。 +每一项配置的形式为`操作=按键序列`。如果`按键序列`为空,则表示禁用该操作的快捷键。 -注意,`Ctrl+E`保留作为*舰长模式*的前导键,`Ctrl+Q`保留为退出VNote。 +注意,`Ctrl+Q`保留为退出VNote。 # 舰长模式 为了更有效地利用快捷键,VNote支持 **舰长模式**。 按前导键`Ctrl+E`后,VNote会进入舰长模式。在舰长模式中,VNote会支持更多高效的快捷操作。 -另外,在该模式中,`Ctrl+W`和`W`是等效的,因此,可以`Ctrl+E+W`来实现`Ctrl+E W`的操作。 - - `E` 是否扩展编辑区域。 - `P` diff --git a/src/resources/vnote.ini b/src/resources/vnote.ini index 673e97eb..f3b7b6c6 100644 --- a/src/resources/vnote.ini +++ b/src/resources/vnote.ini @@ -168,25 +168,82 @@ mathjax_javascript=https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.2/MathJax. size=4 [shortcuts] -; Define shortcuts here, with each item in the form "operation->keysequence". +; 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+Alt+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 + +; Leader key of Captain mode +CaptainMode=Ctrl+E +; Create a note in current folder +NewNote=Ctrl+Alt+N +; Save current note +SaveNote=Ctrl+S +; Save changes and enter read mode +SaveAndRead=Ctrl+T +; Edit current note +EditNote=Ctrl+W +; Close current note +CloseNote= +; Open file/replace dialog +Find=Ctrl+F +; Find next occurence +FindNext=F3 +; Find previous occurence +FindPrevious=Shift+F3 + +[captain_mode_shortcuts] +; Define shortcuts in Captain mode here. +; There shortcuts are the sub-sequence after the CaptainMode key sequence +; in [shortcuts]. + +; Enter Navigation mode +NavigationMode=W +; Show attachment list of current note +AttachmentList=A +; Locate to the folder of current note +LocateCurrentFile=D +; Toggle Expand mode +ExpandMode=E +; Alternate one/two panels view +OnePanelView=P +; Discard changes and enter read mode +DiscardAndRead=Q +; Toggle Tools dock widget +ToolsDock=T +; Close current note +CloseNote=X +; Show shortcuts help document +ShortcutsHelp=? +; Flush the log file +FlushLogFile=";" +; Show opened files list +OpenedFileList=F +; Activate the ith tab +ActivateTab1=1 +ActivateTab2=2 +ActivateTab3=3 +ActivateTab4=4 +ActivateTab5=5 +ActivateTab6=6 +ActivateTab7=7 +ActivateTab8=8 +ActivateTab9=9 +; Alternate between current and last tab +AlternateTab=0 +; Activate next tab +ActivateNextTab=J +; Activate previous tab +ActivatePreviousTab=K +; Activate the window split on the left +ActivateSplitLeft=H +; Activate the window split on the right +ActivateSplitRight=L +; Move current tab one split left +MoveTabSplitLeft=Shift+H +; Move current tab one split right +MoveTabSplitRight=Shift+L +; Create a vertical split +VerticalSplit=V +; Remove current split +RemoveSplit=R diff --git a/src/vcaptain.cpp b/src/vcaptain.cpp index 9e96c060..0bfc6d79 100644 --- a/src/vcaptain.cpp +++ b/src/vcaptain.cpp @@ -1,6 +1,5 @@ #include #include -#include #include #include #include "vcaptain.h" @@ -9,38 +8,39 @@ #include "vedittab.h" #include "vfilelist.h" #include "vnavigationmode.h" +#include "vconfigmanager.h" + +extern VMainWindow *g_mainWin; + +extern VConfigManager *g_config; // 3s pending time after the leader keys. const int c_pendingTime = 3 * 1000; -#if defined(QT_NO_DEBUG) -extern QFile g_logFile; -#endif - -VCaptain::VCaptain(VMainWindow *p_parent) - : QWidget(p_parent), m_mainWindow(p_parent), m_mode(VCaptain::Normal), - m_widgetBeforeCaptain(NULL), m_nextMajorKey('a'), m_ignoreFocusChange(false) +VCaptain::VCaptain(QWidget *p_parent) + : QWidget(p_parent), + m_mode(CaptainMode::Normal), + m_widgetBeforeNavigation(NULL), + m_nextMajorKey('a'), + m_ignoreFocusChange(false), + m_leaderKey(g_config->getShortcutKeySequence("CaptainMode")) { - m_pendingTimer = new QTimer(this); - m_pendingTimer->setSingleShot(true); - m_pendingTimer->setInterval(c_pendingTime); - connect(m_pendingTimer, &QTimer::timeout, - this, &VCaptain::pendingTimerTimeout); + Q_ASSERT(!m_leaderKey.isEmpty()); connect(qApp, &QApplication::focusChanged, this, &VCaptain::handleFocusChanged); - QShortcut *shortcut = new QShortcut(QKeySequence("Ctrl+E"), this, - Q_NULLPTR, Q_NULLPTR); - connect(shortcut, &QShortcut::activated, - this, &VCaptain::trigger); - - qApp->installEventFilter(this); - setWindowFlags(Qt::FramelessWindowHint); + // Make it as small as possible. This widget will stay at the top-left corner // of VMainWindow. resize(1, 1); + + // Register Navigation mode as Captain mode target. + registerCaptainTarget(tr("NavigationMode"), + g_config->getCaptainShortcutKeySequence("NavigationMode"), + this, + navigationModeByCaptain); } QChar VCaptain::getNextMajorKey() @@ -59,53 +59,26 @@ void VCaptain::registerNavigationTarget(VNavigationMode *p_target) QChar key = getNextMajorKey(); if (!key.isNull()) { p_target->registerNavigation(key); - m_targets.append(NaviModeTarget(p_target, true)); + m_naviTargets.push_back(NaviModeTarget(p_target, true)); } } -// In pending mode, if user click other widgets, we need to exit Captain mode. -void VCaptain::handleFocusChanged(QWidget *p_old, QWidget * /* p_now */) +void VCaptain::handleFocusChanged(QWidget *p_old, QWidget * p_now) { - if (!m_ignoreFocusChange && p_old == this) { - exitCaptainMode(); + Q_UNUSED(p_now); + + if (!m_ignoreFocusChange + && !checkMode(CaptainMode::Normal) + && p_old == this) { + exitNavigationMode(); } } -void VCaptain::pendingTimerTimeout() -{ - qDebug() << "Captain mode timeout"; - exitCaptainMode(); - restoreFocus(); -} - -void VCaptain::trigger() -{ - if (m_mode != VCaptain::Normal) { - return; - } - qDebug() << "trigger Captain mode"; - // Focus to listen pending key press. - m_widgetBeforeCaptain = QApplication::focusWidget(); - setFocus(); - m_mode = VCaptain::Pending; - m_pendingTimer->stop(); - m_pendingTimer->start(); - - emit captainModeChanged(true); -} - void VCaptain::keyPressEvent(QKeyEvent *p_event) { int key = p_event->key(); Qt::KeyboardModifiers modifiers = p_event->modifiers(); - if (m_mode == VCaptain::Normal) { - // Should not in focus while in Normal mode. - QWidget::keyPressEvent(p_event); - m_mainWindow->focusNextChild(); - return; - } - if (key == Qt::Key_Control || key == Qt::Key_Shift) { QWidget::keyPressEvent(p_event); return; @@ -120,230 +93,28 @@ void VCaptain::keyPressEvent(QKeyEvent *p_event) bool VCaptain::handleKeyPress(int p_key, Qt::KeyboardModifiers p_modifiers) { - bool ret = true; + if (!checkMode(CaptainMode::Navigation)) { + return false; + } if (p_key == Qt::Key_Escape || (p_key == Qt::Key_BracketLeft && p_modifiers == Qt::ControlModifier)) { - goto exit; + exitNavigationMode(); + return true; } - m_ignoreFocusChange = true; - - if (m_mode == VCaptainMode::Navigation) { - ret = handleKeyPressNavigationMode(p_key, p_modifiers); - m_ignoreFocusChange = false; - return ret; - } - - // In Captain mode, Ctrl key won't make a difference. - switch (p_key) { - case Qt::Key_1: - case Qt::Key_2: - case Qt::Key_3: - case Qt::Key_4: - case Qt::Key_5: - case Qt::Key_6: - case Qt::Key_7: - case Qt::Key_8: - case Qt::Key_9: - { - // Switch to tab . - VEditWindow *win = m_mainWindow->editArea->getCurrentWindow(); - if (win) { - int sequence = p_key - Qt::Key_0; - if (win->activateTab(sequence)) { - m_widgetBeforeCaptain = NULL; - } - } - break; - } - - case Qt::Key_0: - { - // Alternate the tab. - VEditWindow *win = m_mainWindow->editArea->getCurrentWindow(); - if (win) { - if (win->alternateTab()) { - m_widgetBeforeCaptain = NULL; - } - } - break; - } - - case Qt::Key_A: - { - // Show attachment list of current note. - m_mainWindow->showAttachmentList(); - break; - } - - case Qt::Key_D: - // Locate current tab. - if (m_mainWindow->locateCurrentFile()) { - m_widgetBeforeCaptain = NULL; - } - - break; - - case Qt::Key_E: - // Toggle expand view. - m_mainWindow->expandViewAct->trigger(); - break; - - case Qt::Key_F: - { - // Show current window's opened file list. - VEditWindow *win = m_mainWindow->editArea->getCurrentWindow(); - if (win) { - if (win->showOpenedFileList()) { - // showOpenedFileList() already focus the right widget. - m_widgetBeforeCaptain = NULL; - } - } - break; - } - - case Qt::Key_H: - { - if (p_modifiers & Qt::ShiftModifier) { - // Move current tab one split left. - m_mainWindow->editArea->moveCurrentTabOneSplit(false); - } else { - // Focus previous window split. - int idx = m_mainWindow->editArea->focusNextWindow(-1); - if (idx > -1) { - m_widgetBeforeCaptain = NULL; - } - } - break; - } - - case Qt::Key_J: - { - // Focus next tab. - VEditWindow *win = m_mainWindow->editArea->getCurrentWindow(); - if (win) { - win->focusNextTab(true); - // focusNextTab() will focus the right widget. - m_widgetBeforeCaptain = NULL; - } - break; - } - - case Qt::Key_K: - { - // Focus previous tab. - VEditWindow *win = m_mainWindow->editArea->getCurrentWindow(); - if (win) { - win->focusNextTab(false); - // focusNextTab() will focus the right widget. - m_widgetBeforeCaptain = NULL; - } - break; - } - - case Qt::Key_L: - { - if (p_modifiers & Qt::ShiftModifier) { - // Move current tab one split right. - m_mainWindow->editArea->moveCurrentTabOneSplit(true); - } else { - // Focus next window split. - int idx = m_mainWindow->editArea->focusNextWindow(1); - if (idx > -1) { - m_widgetBeforeCaptain = NULL; - } - } - break; - } - - case Qt::Key_P: - // Toggle one/two panel view. - m_mainWindow->toggleOnePanelView(); - break; - - case Qt::Key_Q: - // Discard changes and exit edit mode. - if (m_mainWindow->m_curFile) { - m_mainWindow->discardExitAct->trigger(); - } - break; - - case Qt::Key_R: - { - // Remove current window split. - m_mainWindow->editArea->removeCurrentWindow(); - - QWidget *nextFocus = m_mainWindow->editArea->getCurrentTab(); - m_widgetBeforeCaptain = nextFocus ? nextFocus : m_mainWindow->getFileList(); - break; - } - - case Qt::Key_T: - // Toggle the Tools dock. - m_mainWindow->toolDock->setVisible(!m_mainWindow->toolDock->isVisible()); - break; - - case Qt::Key_V: - // Vertical split current window. - m_mainWindow->editArea->splitCurrentWindow(); - // Do not restore focus. - m_widgetBeforeCaptain = NULL; - break; - - case Qt::Key_W: - // Enter navigation mode. - triggerNavigationMode(); - m_ignoreFocusChange = false; - return ret; - - case Qt::Key_X: - { - // Close current tab. - m_mainWindow->closeCurrentFile(); - - // m_widgetBeforeCaptain may be the closed tab which will cause crash. - QWidget *nextFocus = m_mainWindow->editArea->getCurrentTab(); - m_widgetBeforeCaptain = nextFocus ? nextFocus : m_mainWindow->getFileList(); - break; - } - - case Qt::Key_Question: - { - // Display shortcuts doc. - m_mainWindow->shortcutHelp(); - m_widgetBeforeCaptain = NULL; - break; - } - -#if defined(QT_NO_DEBUG) - case Qt::Key_Comma: - { - // Flush g_logFile. - g_logFile.flush(); - break; - } -#endif - - default: - // Not implemented yet. Just exit Captain mode. - break; - } - -exit: - exitCaptainMode(); - restoreFocus(); - return ret; + return handleKeyPressNavigationMode(p_key, p_modifiers); } bool VCaptain::handleKeyPressNavigationMode(int p_key, Qt::KeyboardModifiers /* p_modifiers */) { - Q_ASSERT(m_mode == VCaptainMode::Navigation); + Q_ASSERT(m_mode == CaptainMode::Navigation); bool hasConsumed = false; bool pending = false; - for (auto &target : m_targets) { + m_ignoreFocusChange = true; + for (auto &target : m_naviTargets) { if (hasConsumed) { target.m_available = false; target.m_target->hideNavigation(); @@ -351,12 +122,13 @@ bool VCaptain::handleKeyPressNavigationMode(int p_key, } if (target.m_available) { bool succeed = false; + // May change focus, so we need to ignore focus change here. bool consumed = target.m_target->handleKeyNavigation(p_key, succeed); if (consumed) { hasConsumed = true; if (succeed) { // Exit. - m_widgetBeforeCaptain = NULL; + m_widgetBeforeNavigation = NULL; } else { // Consumed but not succeed. Need more keys. pending = true; @@ -368,20 +140,23 @@ bool VCaptain::handleKeyPressNavigationMode(int p_key, } } } + + m_ignoreFocusChange = false; if (pending) { return true; } - exitCaptainMode(); - restoreFocus(); + + exitNavigationMode(); return true; } void VCaptain::triggerNavigationMode() { - m_pendingTimer->stop(); - m_mode = VCaptainMode::Navigation; - - for (auto &target : m_targets) { + setMode(CaptainMode::Navigation); + m_widgetBeforeNavigation = QApplication::focusWidget(); + // Focus to listen pending key press. + setFocus(); + for (auto &target : m_naviTargets) { target.m_available = true; target.m_target->showNavigation(); } @@ -389,51 +164,74 @@ void VCaptain::triggerNavigationMode() void VCaptain::exitNavigationMode() { - m_mode = VCaptainMode::Normal; + setMode(CaptainMode::Normal); - for (auto &target : m_targets) { + for (auto &target : m_naviTargets) { target.m_available = true; target.m_target->hideNavigation(); } -} -bool VCaptain::eventFilter(QObject *p_obj, QEvent *p_event) -{ - if (m_mode != VCaptain::Normal && p_event->type() == QEvent::Shortcut) { - qDebug() << "filter" << p_event; - QShortcutEvent *keyEve = dynamic_cast(p_event); - Q_ASSERT(keyEve); - const QKeySequence &keys = keyEve->key(); - if (keys.count() == 1) { - int key = keys[0]; - Qt::KeyboardModifiers modifiers = Qt::KeyboardModifiers(key & (~0x01FFFFFFU)); - key &= 0x01FFFFFFUL; - if (handleKeyPress(key, modifiers)) { - return true; - } - } - exitCaptainMode(); - restoreFocus(); - } - return QWidget::eventFilter(p_obj, p_event); + restoreFocus(); } void VCaptain::restoreFocus() { - if (m_widgetBeforeCaptain) { - m_widgetBeforeCaptain->setFocus(); + if (m_widgetBeforeNavigation) { + m_widgetBeforeNavigation->setFocus(); } } -void VCaptain::exitCaptainMode() +bool VCaptain::registerCaptainTarget(const QString &p_name, + const QString &p_key, + void *p_target, + CaptainFunc p_func) { - if (m_mode == VCaptainMode::Navigation) { - exitNavigationMode(); + if (p_key.isEmpty()) { + return false; } - m_mode = VCaptain::Normal; - m_pendingTimer->stop(); - m_ignoreFocusChange = false; - emit captainModeChanged(false); + QString lowerKey = p_key.toLower(); + + if (m_captainTargets.contains(lowerKey)) { + return false; + } + + // Register shortcuts. + QString sequence = QString("%1,%2").arg(m_leaderKey).arg(p_key); + QShortcut *shortcut = new QShortcut(QKeySequence(sequence), + this); + shortcut->setContext(Qt::ApplicationShortcut); + + connect(shortcut, &QShortcut::activated, + this, std::bind(&VCaptain::triggerCaptainTarget, this, p_key)); + + + CaptainModeTarget target(p_name, + p_key, + p_target, + p_func, + shortcut); + m_captainTargets.insert(lowerKey, target); + + qDebug() << "registered:" << target.toString() << sequence; + + return true; } +void VCaptain::triggerCaptainTarget(const QString &p_key) +{ + auto it = m_captainTargets.find(p_key.toLower()); + Q_ASSERT(it != m_captainTargets.end()); + const CaptainModeTarget &target = it.value(); + + qDebug() << "triggered:" << target.toString(); + + target.m_function(target.m_target, nullptr); +} + +void VCaptain::navigationModeByCaptain(void *p_target, void *p_data) +{ + Q_UNUSED(p_data); + VCaptain *obj = static_cast(p_target); + obj->triggerNavigationMode(); +} diff --git a/src/vcaptain.h b/src/vcaptain.h index dabb69f8..5d961a80 100644 --- a/src/vcaptain.h +++ b/src/vcaptain.h @@ -1,77 +1,165 @@ #ifndef VCAPTAIN_H #define VCAPTAIN_H -#include -#include +#include + +#include +#include +#include -class QTimer; class QKeyEvent; -class VMainWindow; -class QEvent; class VNavigationMode; +// void func(void *p_target, void *p_data); +typedef std::function CaptainFunc; + class VCaptain : public QWidget { Q_OBJECT public: - explicit VCaptain(VMainWindow *p_parent); + explicit VCaptain(QWidget *p_parent); - // Trigger Captain mode. - void trigger(); - - // Register a target for Navigation Mode. + // Register a target for Navigation mode. void registerNavigationTarget(VNavigationMode *p_target); -signals: - void captainModeChanged(bool p_enabled); + // Register a target for Captain mode. + bool registerCaptainTarget(const QString &p_name, + const QString &p_key, + void *p_target, + CaptainFunc p_func); protected: void keyPressEvent(QKeyEvent *p_event) Q_DECL_OVERRIDE; - bool eventFilter(QObject *p_obj, QEvent *p_event) Q_DECL_OVERRIDE; - -public slots: private slots: - void pendingTimerTimeout(); + // Exit Navigation mode if focus lost. void handleFocusChanged(QWidget *p_old, QWidget *p_new); private: - // Restore the focus to m_widgetBeforeCaptain. - void restoreFocus(); - void exitCaptainMode(); - // Return true if finish handling the event; otherwise, let the base widget - // to handle it. - bool handleKeyPress(int p_key, Qt::KeyboardModifiers p_modifiers); - bool handleKeyPressNavigationMode(int p_key, - Qt::KeyboardModifiers p_modifiers); - QChar getNextMajorKey(); - void triggerNavigationMode(); - void exitNavigationMode(); + // A widget target for Navigation mode. + struct NaviModeTarget { + NaviModeTarget() + : m_target(nullptr), m_available(false) + { + } - enum VCaptainMode { + NaviModeTarget(VNavigationMode *p_target, bool p_available) + : m_target(p_target), m_available(p_available) + { + } + + VNavigationMode *m_target; + bool m_available; + }; + + // Modes. + enum class CaptainMode { Normal = 0, Pending, Navigation }; - VMainWindow *m_mainWindow; - QTimer *m_pendingTimer; - int m_mode; - // The widget which has the focus before entering Captain mode. - QWidget* m_widgetBeforeCaptain; + struct CaptainModeTarget { + CaptainModeTarget() + : m_target(nullptr), m_function(nullptr), m_shortcut(nullptr) + { + } - struct NaviModeTarget { - VNavigationMode *m_target; - bool m_available; + CaptainModeTarget(const QString &p_name, + const QString &p_key, + void *p_target, + CaptainFunc p_func, + QShortcut *p_shortcut) + : m_name(p_name), + m_key(p_key), + m_target(p_target), + m_function(p_func), + m_shortcut(p_shortcut) + { + } - NaviModeTarget(VNavigationMode *p_target, bool p_available) - : m_target(p_target), m_available(p_available) {} + QString toString() const + { + return QString("Captain mode target %1 key[%2]").arg(m_name).arg(m_key); + } + + // Name to display. + QString m_name; + + // Key sequence to trigger this target. + // This is the sub-sequence after leader key. + QString m_key; + + // Target. + void *m_target; + + // Function to call when this target is trigger. + CaptainFunc m_function; + + // Shortcut for this target. + QShortcut *m_shortcut; }; - QList m_targets; + + // Restore the focus to m_widgetBeforeNavigation. + void restoreFocus(); + + // Return true if finish handling the event; otherwise, let the base widget + // to handle it. + bool handleKeyPress(int p_key, Qt::KeyboardModifiers p_modifiers); + + // Handle key press event in Navigation mode. + bool handleKeyPressNavigationMode(int p_key, + Qt::KeyboardModifiers p_modifiers); + + // Get next major key to use for Navigation mode. + QChar getNextMajorKey(); + + // Trigger navigation mode to ask all targets show themselves. + void triggerNavigationMode(); + + // Exit navigation mode to ask all targets hide themselves. + void exitNavigationMode(); + + // Called to trigger the action of a Captain target which has + // registered @p_key. + void triggerCaptainTarget(const QString &p_key); + + void setMode(CaptainMode p_mode); + + bool checkMode(CaptainMode p_mode) const; + + static void navigationModeByCaptain(void *p_target, void *p_data); + + // Used to indicate whether we are in Navigation mode. + CaptainMode m_mode; + + // The widget which has the focus before entering Navigation mode. + QWidget* m_widgetBeforeNavigation; + + // Targets for Navigation mode. + QVector m_naviTargets; + QChar m_nextMajorKey; - // Ignore focus change to avoid exiting Captain mode while handling key - // press. + + // Targets for Captain mode. + // Key(lower) -> CaptainModeTarget. + QHash m_captainTargets; + + // Ignore focus change during handling Navigation target actions. bool m_ignoreFocusChange; + + // Leader key sequence for Captain mode. + QString m_leaderKey; }; +inline void VCaptain::setMode(CaptainMode p_mode) +{ + m_mode = p_mode; +} + +inline bool VCaptain::checkMode(CaptainMode p_mode) const +{ + return m_mode == p_mode; +} + #endif // VCAPTAIN_H diff --git a/src/vconfigmanager.cpp b/src/vconfigmanager.cpp index 66b39882..23c257ad 100644 --- a/src/vconfigmanager.cpp +++ b/src/vconfigmanager.cpp @@ -172,6 +172,8 @@ void VConfigManager::initialize() readShortcutsFromSettings(); + readCaptainShortcutsFromSettings(); + initDocSuffixes(); m_markdownHighlightInterval = getConfigFromSettings("global", @@ -1002,76 +1004,120 @@ QString VConfigManager::getVnoteNotebookFolderPath() return QDir::home().filePath(c_vnoteNotebookFolderName); } +QHash VConfigManager::readShortcutsFromSettings(QSettings *p_settings, + const QString &p_group) +{ + QHash ret; + p_settings->beginGroup(p_group); + QStringList keys = p_settings->childKeys(); + for (auto const & key : keys) { + if (key.isEmpty()) { + continue; + } + + QVariant varVal = p_settings->value(key); + QString sequence = varVal.toString(); + if (varVal.type() == QVariant::StringList) { + sequence = varVal.toStringList().join(","); + } + + sequence = sequence.trimmed(); + if (isValidKeySequence(sequence)) { + ret.insert(key, sequence); + } + } + + p_settings->endGroup(); + + return ret; +} + bool VConfigManager::isValidKeySequence(const QString &p_seq) { - QString lower = p_seq.toLower(); - return lower != "ctrl+q" && lower != "ctrl+e"; + return p_seq.toLower() != "ctrl+q" + && !QKeySequence(p_seq).isEmpty(); } void VConfigManager::readShortcutsFromSettings() { + const QString group("shortcuts"); + 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(); + m_shortcuts = readShortcutsFromSettings(defaultSettings, group); - 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"); + // Update default settings according to user settings. + QHash userShortcuts = readShortcutsFromSettings(userSettings, + group); QSet 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; + for (auto it = userShortcuts.begin(); it != userShortcuts.end(); ++it) { + auto defaultIt = m_shortcuts.find(it.key()); + if (defaultIt != m_shortcuts.end()) { + QString sequence = it.value().trimmed(); + if (sequence != defaultIt.value()) { + if (isValidKeySequence(sequence)) { + matched.insert(it.key()); + *defaultIt = sequence; + } } else { - matched.insert(op); - *it = seq; + matched.insert(it.key()); } } } - userSettings->endArray(); - - if (needUpdate || matched.size() < m_shortcuts.size()) { - // Write the combined config to user settings. - writeShortcutsToSettings(); + if (matched.size() < m_shortcuts.size()) { + writeShortcutsToSettings(userSettings, group, m_shortcuts); } + + qDebug() << "shortcuts:" << m_shortcuts; } -void VConfigManager::writeShortcutsToSettings() +void VConfigManager::readCaptainShortcutsFromSettings() { - // Clear it first - userSettings->beginGroup("shortcuts"); - userSettings->remove(""); - userSettings->endGroup(); + const QString group("captain_mode_shortcuts"); - 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()); + m_captainShortcuts.clear(); + m_captainShortcuts = readShortcutsFromSettings(defaultSettings, group); + + // Update default settings according to user settings. + QHash userShortcuts = readShortcutsFromSettings(userSettings, + group); + QSet matched; + matched.reserve(m_captainShortcuts.size()); + for (auto it = userShortcuts.begin(); it != userShortcuts.end(); ++it) { + auto defaultIt = m_captainShortcuts.find(it.key()); + if (defaultIt != m_captainShortcuts.end()) { + QString sequence = it.value().trimmed(); + if (sequence != defaultIt.value()) { + if (isValidKeySequence(sequence)) { + matched.insert(it.key()); + *defaultIt = sequence; + } + } else { + matched.insert(it.key()); + } + } } - userSettings->endArray(); + if (matched.size() < m_captainShortcuts.size()) { + writeShortcutsToSettings(userSettings, group, m_captainShortcuts); + } + + qDebug() << "captain mode shortcuts:" << m_captainShortcuts; +} + +void VConfigManager::writeShortcutsToSettings(QSettings *p_settings, + const QString &p_group, + const QHash &p_shortcuts) +{ + p_settings->beginGroup(p_group); + p_settings->remove(""); + + for (auto it = p_shortcuts.begin(); it != p_shortcuts.end(); ++it) { + p_settings->setValue(it.key(), it.value()); + } + + p_settings->endGroup(); } QString VConfigManager::getShortcutKeySequence(const QString &p_operation) const @@ -1084,6 +1130,16 @@ QString VConfigManager::getShortcutKeySequence(const QString &p_operation) const return *it; } +QString VConfigManager::getCaptainShortcutKeySequence(const QString &p_operation) const +{ + auto it = m_captainShortcuts.find(p_operation); + if (it == m_captainShortcuts.end()) { + return QString(); + } + + return *it; +} + void VConfigManager::initDocSuffixes() { m_docSuffixes.clear(); diff --git a/src/vconfigmanager.h b/src/vconfigmanager.h index 7bef0fa7..702f9271 100644 --- a/src/vconfigmanager.h +++ b/src/vconfigmanager.h @@ -334,6 +334,10 @@ public: // Return empty if there is no corresponding config. QString getShortcutKeySequence(const QString &p_operation) const; + // Return the configured key sequence in Captain mode. + // Return empty if there is no corresponding config. + QString getCaptainShortcutKeySequence(const QString &p_operation) const; + // Get the folder the ini file exists. QString getConfigFolder() const; @@ -418,8 +422,16 @@ private: // write the combined configs to user settings. void readShortcutsFromSettings(); - // Write m_shortcuts to the [shortcuts] section in the user settings. - void writeShortcutsToSettings(); + // Read the [captain_mode_shortcuts] section in the settings to init + // m_captainShortcuts. + void readCaptainShortcutsFromSettings(); + + QHash readShortcutsFromSettings(QSettings *p_settings, + const QString &p_group); + + void writeShortcutsToSettings(QSettings *p_settings, + const QString &p_group, + const QHash &p_shortcuts); // Whether @p_seq is a valid key sequence for shortcuts. bool isValidKeySequence(const QString &p_seq); @@ -592,6 +604,10 @@ private: // Operation -> KeySequence. QHash m_shortcuts; + // Shortcuts config in Captain mode. + // Operation -> KeySequence. + QHash m_captainShortcuts; + // Whether minimize to system tray icon when closing the app. // -1: uninitialized; // 0: do not minimize to the tay; diff --git a/src/veditarea.cpp b/src/veditarea.cpp index 6100ec14..da46c913 100644 --- a/src/veditarea.cpp +++ b/src/veditarea.cpp @@ -8,10 +8,15 @@ #include "dialog/vfindreplacedialog.h" #include "utils/vutils.h" #include "vfilesessioninfo.h" +#include "vmainwindow.h" +#include "vcaptain.h" extern VConfigManager *g_config; + extern VNote *g_vnote; +extern VMainWindow *g_mainWin; + VEditArea::VEditArea(QWidget *parent) : QWidget(parent), VNavigationMode(), @@ -21,6 +26,8 @@ VEditArea::VEditArea(QWidget *parent) insertSplitWindow(0); setCurrentWindow(0, false); + + registerCaptainTargets(); } void VEditArea::setupUI() @@ -749,3 +756,180 @@ int VEditArea::openFiles(const QVector &p_files) return nrOpened; } + +void VEditArea::registerCaptainTargets() +{ + using namespace std::placeholders; + + VCaptain *captain = g_mainWin->getCaptain(); + + captain->registerCaptainTarget(tr("ActivateTab1"), + g_config->getCaptainShortcutKeySequence("ActivateTab1"), + this, + std::bind(activateTabByCaptain, _1, _2, 1)); + captain->registerCaptainTarget(tr("ActivateTab2"), + g_config->getCaptainShortcutKeySequence("ActivateTab2"), + this, + std::bind(activateTabByCaptain, _1, _2, 2)); + captain->registerCaptainTarget(tr("ActivateTab3"), + g_config->getCaptainShortcutKeySequence("ActivateTab3"), + this, + std::bind(activateTabByCaptain, _1, _2, 3)); + captain->registerCaptainTarget(tr("ActivateTab4"), + g_config->getCaptainShortcutKeySequence("ActivateTab4"), + this, + std::bind(activateTabByCaptain, _1, _2, 4)); + captain->registerCaptainTarget(tr("ActivateTab5"), + g_config->getCaptainShortcutKeySequence("ActivateTab5"), + this, + std::bind(activateTabByCaptain, _1, _2, 5)); + captain->registerCaptainTarget(tr("ActivateTab6"), + g_config->getCaptainShortcutKeySequence("ActivateTab6"), + this, + std::bind(activateTabByCaptain, _1, _2, 6)); + captain->registerCaptainTarget(tr("ActivateTab7"), + g_config->getCaptainShortcutKeySequence("ActivateTab7"), + this, + std::bind(activateTabByCaptain, _1, _2, 7)); + captain->registerCaptainTarget(tr("ActivateTab8"), + g_config->getCaptainShortcutKeySequence("ActivateTab8"), + this, + std::bind(activateTabByCaptain, _1, _2, 8)); + captain->registerCaptainTarget(tr("ActivateTab9"), + g_config->getCaptainShortcutKeySequence("ActivateTab9"), + this, + std::bind(activateTabByCaptain, _1, _2, 9)); + captain->registerCaptainTarget(tr("AlternateTab"), + g_config->getCaptainShortcutKeySequence("AlternateTab"), + this, + alternateTabByCaptain); + captain->registerCaptainTarget(tr("OpenedFileList"), + g_config->getCaptainShortcutKeySequence("OpenedFileList"), + this, + showOpenedFileListByCaptain); + captain->registerCaptainTarget(tr("ActivateSplitLeft"), + g_config->getCaptainShortcutKeySequence("ActivateSplitLeft"), + this, + activateSplitLeftByCaptain); + captain->registerCaptainTarget(tr("ActivateSplitRight"), + g_config->getCaptainShortcutKeySequence("ActivateSplitRight"), + this, + activateSplitRightByCaptain); + captain->registerCaptainTarget(tr("MoveTabSplitLeft"), + g_config->getCaptainShortcutKeySequence("MoveTabSplitLeft"), + this, + moveTabSplitLeftByCaptain); + captain->registerCaptainTarget(tr("MoveTabSplitRight"), + g_config->getCaptainShortcutKeySequence("MoveTabSplitRight"), + this, + moveTabSplitRightByCaptain); + captain->registerCaptainTarget(tr("ActivateNextTab"), + g_config->getCaptainShortcutKeySequence("ActivateNextTab"), + this, + activateNextTabByCaptain); + captain->registerCaptainTarget(tr("ActivatePreviousTab"), + g_config->getCaptainShortcutKeySequence("ActivatePreviousTab"), + this, + activatePreviousTabByCaptain); + captain->registerCaptainTarget(tr("VerticalSplit"), + g_config->getCaptainShortcutKeySequence("VerticalSplit"), + this, + verticalSplitByCaptain); + captain->registerCaptainTarget(tr("RemoveSplit"), + g_config->getCaptainShortcutKeySequence("RemoveSplit"), + this, + removeSplitByCaptain); +} + +void VEditArea::activateTabByCaptain(void *p_target, void *p_data, int p_idx) +{ + Q_UNUSED(p_data); + VEditArea *obj = static_cast(p_target); + VEditWindow *win = obj->getCurrentWindow(); + if (win) { + win->activateTab(p_idx); + } +} + +void VEditArea::alternateTabByCaptain(void *p_target, void *p_data) +{ + Q_UNUSED(p_data); + VEditArea *obj = static_cast(p_target); + VEditWindow *win = obj->getCurrentWindow(); + if (win) { + win->alternateTab(); + } +} + +void VEditArea::showOpenedFileListByCaptain(void *p_target, void *p_data) +{ + Q_UNUSED(p_data); + VEditArea *obj = static_cast(p_target); + VEditWindow *win = obj->getCurrentWindow(); + if (win) { + win->showOpenedFileList(); + } +} + +void VEditArea::activateSplitLeftByCaptain(void *p_target, void *p_data) +{ + Q_UNUSED(p_data); + VEditArea *obj = static_cast(p_target); + obj->focusNextWindow(-1); +} + +void VEditArea::activateSplitRightByCaptain(void *p_target, void *p_data) +{ + Q_UNUSED(p_data); + VEditArea *obj = static_cast(p_target); + obj->focusNextWindow(1); +} + +void VEditArea::moveTabSplitLeftByCaptain(void *p_target, void *p_data) +{ + Q_UNUSED(p_data); + VEditArea *obj = static_cast(p_target); + obj->moveCurrentTabOneSplit(false); +} + +void VEditArea::moveTabSplitRightByCaptain(void *p_target, void *p_data) +{ + Q_UNUSED(p_data); + VEditArea *obj = static_cast(p_target); + obj->moveCurrentTabOneSplit(true); +} + +void VEditArea::activateNextTabByCaptain(void *p_target, void *p_data) +{ + Q_UNUSED(p_data); + VEditArea *obj = static_cast(p_target); + VEditWindow *win = obj->getCurrentWindow(); + if (win) { + win->focusNextTab(true); + } +} + +void VEditArea::activatePreviousTabByCaptain(void *p_target, void *p_data) +{ + Q_UNUSED(p_data); + VEditArea *obj = static_cast(p_target); + VEditWindow *win = obj->getCurrentWindow(); + if (win) { + win->focusNextTab(false); + } +} + +void VEditArea::verticalSplitByCaptain(void *p_target, void *p_data) +{ + Q_UNUSED(p_data); + VEditArea *obj = static_cast(p_target); + obj->splitCurrentWindow(); +} + +void VEditArea::removeSplitByCaptain(void *p_target, void *p_data) +{ + Q_UNUSED(p_data); + VEditArea *obj = static_cast(p_target); + obj->removeCurrentWindow(); +} + diff --git a/src/veditarea.h b/src/veditarea.h index 6f3f95fa..75c0687f 100644 --- a/src/veditarea.h +++ b/src/veditarea.h @@ -160,6 +160,36 @@ private: // Update status of current window. void updateWindowStatus(); + // Init targets for Captain mode. + void registerCaptainTargets(); + + // Captain mode functions. + + // Activate tab @p_idx. + static void activateTabByCaptain(void *p_target, void *p_data, int p_idx); + + static void alternateTabByCaptain(void *p_target, void *p_data); + + static void showOpenedFileListByCaptain(void *p_target, void *p_data); + + static void activateSplitLeftByCaptain(void *p_target, void *p_data); + + static void activateSplitRightByCaptain(void *p_target, void *p_data); + + static void moveTabSplitLeftByCaptain(void *p_target, void *p_data); + + static void moveTabSplitRightByCaptain(void *p_target, void *p_data); + + static void activateNextTabByCaptain(void *p_target, void *p_data); + + static void activatePreviousTabByCaptain(void *p_target, void *p_data); + + static void verticalSplitByCaptain(void *p_target, void *p_data); + + static void removeSplitByCaptain(void *p_target, void *p_data); + + // End Captain mode functions. + int curWindowIndex; // Splitter holding multiple split windows diff --git a/src/veditwindow.cpp b/src/veditwindow.cpp index 3cd0a281..563bcf51 100644 --- a/src/veditwindow.cpp +++ b/src/veditwindow.cpp @@ -263,7 +263,9 @@ void VEditWindow::removeEditTab(int p_index) int VEditWindow::insertEditTab(int p_index, VFile *p_file, QWidget *p_page) { - int idx = insertTab(p_index, p_page, p_file->getName()); + int idx = insertTab(p_index, + p_page, + generateTabText(p_index, p_file)); setTabToolTip(idx, generateTooltip(p_file)); return idx; } @@ -484,8 +486,7 @@ void VEditWindow::updateTabInfo(int p_index) const VFile *file = editor->getFile(); bool editMode = editor->isEditMode(); - setTabText(p_index, generateTabText(p_index, file->getName(), - file->isModified(), file->isModifiable())); + setTabText(p_index, generateTabText(p_index, file)); setTabToolTip(p_index, generateTooltip(file)); QString iconUrl(":/resources/icons/reading.svg"); @@ -502,8 +503,7 @@ void VEditWindow::updateAllTabsSequence() for (int i = 0; i < count(); ++i) { VEditTab *editor = getTab(i); const VFile *file = editor->getFile(); - setTabText(i, generateTabText(i, file->getName(), - file->isModified(), file->isModifiable())); + setTabText(i, generateTabText(i, file)); } } diff --git a/src/veditwindow.h b/src/veditwindow.h index d7f35ae7..53781ddb 100644 --- a/src/veditwindow.h +++ b/src/veditwindow.h @@ -140,9 +140,11 @@ private: int insertEditTab(int p_index, VFile *p_file, QWidget *p_page); int appendEditTab(VFile *p_file, QWidget *p_page); int openFileInTab(VFile *p_file, OpenFileMode p_mode); + QString generateTooltip(const VFile *p_file) const; - QString generateTabText(int p_index, const QString &p_name, - bool p_modified, bool p_modifiable) const; + + QString generateTabText(int p_index, const VFile *p_file) const; + bool canRemoveSplit(); // Move tab at @p_tabIdx one split window. @@ -212,11 +214,16 @@ inline QString VEditWindow::generateTooltip(const VFile *p_file) const } } -inline QString VEditWindow::generateTabText(int p_index, const QString &p_name, - bool p_modified, bool p_modifiable) const +inline QString VEditWindow::generateTabText(int p_index, const VFile *p_file) const { - QString seq = QString::number(p_index + c_tabSequenceBase, 10); - return seq + ". " + p_name + (p_modifiable ? (p_modified ? "*" : "") : "#"); + if (!p_file) { + return ""; + } + + return QString("%1.%2%3").arg(QString::number(p_index + c_tabSequenceBase, 10)) + .arg(p_file->getName()) + .arg(p_file->isModifiable() + ? (p_file->isModified() ? "*" : "") : "#"); } #endif // VEDITWINDOW_H diff --git a/src/vmainwindow.cpp b/src/vmainwindow.cpp index 7d529af5..ce519bf7 100644 --- a/src/vmainwindow.cpp +++ b/src/vmainwindow.cpp @@ -31,6 +31,8 @@ #include "vattachmentlist.h" #include "vfilesessioninfo.h" +VMainWindow *g_mainWin; + extern VConfigManager *g_config; VNote *g_vnote; @@ -41,12 +43,15 @@ const int VMainWindow::c_sharedMemTimerInterval = 1000; extern QFile g_logFile; #endif + VMainWindow::VMainWindow(VSingleInstanceGuard *p_guard, QWidget *p_parent) : QMainWindow(p_parent), m_guard(p_guard), m_windowOldState(Qt::WindowNoState), m_requestQuit(false) { qsrand(QDateTime::currentDateTime().toTime_t()); + g_mainWin = this; + setWindowIcon(QIcon(":/resources/icons/vnote.ico")); vnote = new VNote(this); g_vnote = vnote; @@ -59,6 +64,8 @@ VMainWindow::VMainWindow(VSingleInstanceGuard *p_guard, QWidget *p_parent) m_panelViewState = PanelViewState::TwoPanels; } + initCaptain(); + setupUI(); initMenuBar(); @@ -74,9 +81,9 @@ VMainWindow::VMainWindow(VSingleInstanceGuard *p_guard, QWidget *p_parent) notebookSelector->update(); - initCaptain(); - initSharedMemoryWatcher(); + + registerCaptainAndNavigationTargets(); } void VMainWindow::initSharedMemoryWatcher() @@ -95,14 +102,53 @@ void VMainWindow::initCaptain() // VCaptain should be visible to accpet key focus. But VCaptain // may hide other widgets. m_captain = new VCaptain(this); - connect(m_captain, &VCaptain::captainModeChanged, - this, &VMainWindow::handleCaptainModeChanged); +} +void VMainWindow::registerCaptainAndNavigationTargets() +{ m_captain->registerNavigationTarget(notebookSelector); m_captain->registerNavigationTarget(directoryTree); m_captain->registerNavigationTarget(m_fileList); m_captain->registerNavigationTarget(editArea); m_captain->registerNavigationTarget(outline); + + // Register Captain mode targets. + m_captain->registerCaptainTarget(tr("AttachmentList"), + g_config->getCaptainShortcutKeySequence("AttachmentList"), + this, + showAttachmentListByCaptain); + m_captain->registerCaptainTarget(tr("LocateCurrentFile"), + g_config->getCaptainShortcutKeySequence("LocateCurrentFile"), + this, + locateCurrentFileByCaptain); + m_captain->registerCaptainTarget(tr("ExpandMode"), + g_config->getCaptainShortcutKeySequence("ExpandMode"), + this, + toggleExpandModeByCaptain); + m_captain->registerCaptainTarget(tr("OnePanelView"), + g_config->getCaptainShortcutKeySequence("OnePanelView"), + this, + toggleOnePanelViewByCaptain); + m_captain->registerCaptainTarget(tr("DiscardAndRead"), + g_config->getCaptainShortcutKeySequence("DiscardAndRead"), + this, + discardAndReadByCaptain); + m_captain->registerCaptainTarget(tr("ToolsDock"), + g_config->getCaptainShortcutKeySequence("ToolsDock"), + this, + toggleToolsDockByCaptain); + m_captain->registerCaptainTarget(tr("CloseNote"), + g_config->getCaptainShortcutKeySequence("CloseNote"), + this, + closeFileByCaptain); + m_captain->registerCaptainTarget(tr("ShortcutsHelp"), + g_config->getCaptainShortcutKeySequence("ShortcutsHelp"), + this, + shortcutsHelpByCaptain); + m_captain->registerCaptainTarget(tr("FlushLogFile"), + g_config->getCaptainShortcutKeySequence("FlushLogFile"), + this, + flushLogFileByCaptain); } void VMainWindow::setupUI() @@ -488,11 +534,7 @@ void VMainWindow::initFileToolBar(QSize p_iconSize) m_closeNoteShortcut = new QShortcut(QKeySequence(keySeq), this); m_closeNoteShortcut->setContext(Qt::WidgetWithChildrenShortcut); connect(m_closeNoteShortcut, &QShortcut::activated, - this, [this](){ - if (m_curFile) { - editArea->closeFile(m_curFile, false); - } - }); + this, &VMainWindow::closeCurrentFile); editNoteAct = new QAction(QIcon(":/resources/icons/edit_note.svg"), tr("&Edit"), this); @@ -580,7 +622,7 @@ void VMainWindow::initHelpMenu() QAction *shortcutAct = new QAction(tr("&Shortcuts Help"), this); shortcutAct->setToolTip(tr("View information about shortcut keys")); connect(shortcutAct, &QAction::triggered, - this, &VMainWindow::shortcutHelp); + this, &VMainWindow::shortcutsHelp); QAction *mdGuideAct = new QAction(tr("&Markdown Guide"), this); mdGuideAct->setToolTip(tr("A quick guide of Markdown syntax")); @@ -1812,15 +1854,6 @@ void VMainWindow::compactModeView() changePanelView(m_panelViewState); } -void VMainWindow::toggleOnePanelView() -{ - if (m_panelViewState == PanelViewState::TwoPanels) { - onePanelView(); - } else { - twoPanelView(); - } -} - void VMainWindow::enableCompactMode(bool p_enabled) { const int fileListIdx = 1; @@ -2153,15 +2186,6 @@ bool VMainWindow::locateFile(VFile *p_file) return ret; } -bool VMainWindow::locateCurrentFile() -{ - if (m_curFile) { - return locateFile(m_curFile); - } - - return false; -} - void VMainWindow::handleFindDialogTextChanged(const QString &p_text, uint /* p_options */) { bool enabled = true; @@ -2186,18 +2210,6 @@ void VMainWindow::viewSettings() settingsDialog.exec(); } -void VMainWindow::handleCaptainModeChanged(bool p_enabled) -{ - static QString normalBaseColor = m_avatar->getBaseColor(); - static QString captainModeColor = vnote->getColorFromPalette("Purple5"); - - if (p_enabled) { - m_avatar->updateBaseColor(captainModeColor); - } else { - m_avatar->updateBaseColor(normalBaseColor); - } -} - void VMainWindow::closeCurrentFile() { if (m_curFile) { @@ -2255,7 +2267,7 @@ void VMainWindow::enableImageCaption(bool p_checked) g_config->setEnableImageCaption(p_checked); } -void VMainWindow::shortcutHelp() +void VMainWindow::shortcutsHelp() { QString locale = VUtils::getLocale(); QString docName = VNote::c_shortcutsDocFile_en; @@ -2466,13 +2478,6 @@ void VMainWindow::showMainWindow() this->activateWindow(); } -void VMainWindow::showAttachmentList() -{ - if (m_attachmentBtn->isEnabled()) { - m_attachmentBtn->showPopupWidget(); - } -} - void VMainWindow::openStartupPages() { StartupPageType type = g_config->getStartupPageType(); @@ -2513,3 +2518,81 @@ bool VMainWindow::isHeadingSequenceApplicable() const return true; } + +// Popup the attachment list if it is enabled. +void VMainWindow::showAttachmentListByCaptain(void *p_target, void *p_data) +{ + Q_UNUSED(p_data); + VMainWindow *obj = static_cast(p_target); + if (obj->m_attachmentBtn->isEnabled()) { + obj->m_attachmentBtn->showPopupWidget(); + } +} + +void VMainWindow::locateCurrentFileByCaptain(void *p_target, void *p_data) +{ + Q_UNUSED(p_data); + VMainWindow *obj = static_cast(p_target); + if (obj->m_curFile) { + obj->locateFile(obj->m_curFile); + } +} + +void VMainWindow::toggleExpandModeByCaptain(void *p_target, void *p_data) +{ + Q_UNUSED(p_data); + VMainWindow *obj = static_cast(p_target); + obj->expandViewAct->trigger(); +} + +void VMainWindow::toggleOnePanelViewByCaptain(void *p_target, void *p_data) +{ + Q_UNUSED(p_data); + VMainWindow *obj = static_cast(p_target); + if (obj->m_panelViewState == PanelViewState::TwoPanels) { + obj->onePanelView(); + } else { + obj->twoPanelView(); + } +} + +void VMainWindow::discardAndReadByCaptain(void *p_target, void *p_data) +{ + Q_UNUSED(p_data); + VMainWindow *obj = static_cast(p_target); + if (obj->m_curFile) { + obj->discardExitAct->trigger(); + } +} + +void VMainWindow::toggleToolsDockByCaptain(void *p_target, void *p_data) +{ + Q_UNUSED(p_data); + VMainWindow *obj = static_cast(p_target); + obj->toolDock->setVisible(!obj->toolDock->isVisible()); +} + +void VMainWindow::closeFileByCaptain(void *p_target, void *p_data) +{ + Q_UNUSED(p_data); + VMainWindow *obj = static_cast(p_target); + obj->closeCurrentFile(); +} + +void VMainWindow::shortcutsHelpByCaptain(void *p_target, void *p_data) +{ + Q_UNUSED(p_data); + VMainWindow *obj = static_cast(p_target); + obj->shortcutsHelp(); +} + +void VMainWindow::flushLogFileByCaptain(void *p_target, void *p_data) +{ + Q_UNUSED(p_target); + Q_UNUSED(p_data); + +#if defined(QT_NO_DEBUG) + // Flush g_logFile. + g_logFile.flush(); +#endif +} diff --git a/src/vmainwindow.h b/src/vmainwindow.h index 9189a283..cb5bdb53 100644 --- a/src/vmainwindow.h +++ b/src/vmainwindow.h @@ -60,9 +60,6 @@ public: // Returns true if the location succeeds. bool locateFile(VFile *p_file); - // Returns true if the location succeeds. - bool locateCurrentFile(); - VFileList *getFileList() const; VEditArea *getEditArea() const; @@ -81,18 +78,20 @@ public: // Show a temporary message in status bar. void showStatusMessage(const QString &p_msg); - // Popup the attachment list if it is enabled. - void showAttachmentList(); - // Open startup pages according to configuration. void openStartupPages(); + VCaptain *getCaptain() const; + private slots: void importNoteFromFile(); void viewSettings(); void changeMarkdownConverter(QAction *action); void aboutMessage(); - void shortcutHelp(); + + // Display shortcuts help. + void shortcutsHelp(); + void changeExpandTab(bool checked); void setTabStopWidth(QAction *action); void setEditorBackgroundColor(QAction *action); @@ -129,7 +128,6 @@ private slots: void openFindDialog(); void enableMermaid(bool p_checked); void enableMathjax(bool p_checked); - void handleCaptainModeChanged(bool p_enabled); void changeAutoIndent(bool p_checked); void changeAutoList(bool p_checked); void changeVimMode(bool p_checked); @@ -160,6 +158,9 @@ private slots: // Restore main window. void showMainWindow(); + // Close current note. + void closeCurrentFile(); + protected: void closeEvent(QCloseEvent *event) Q_DECL_OVERRIDE; void resizeEvent(QResizeEvent *event) Q_DECL_OVERRIDE; @@ -213,9 +214,10 @@ private: void restoreStateAndGeometry(); void repositionAvatar(); + // Should init VCaptain before setupUI(). void initCaptain(); - void toggleOnePanelView(); - void closeCurrentFile(); + + void registerCaptainAndNavigationTargets(); // Update status bar information. void updateStatusInfo(const VEditTabInfo &p_info); @@ -240,6 +242,29 @@ private: // Only available for writable Markdown file. bool isHeadingSequenceApplicable() const; + // Captain mode functions. + + // Popup the attachment list if it is enabled. + static void showAttachmentListByCaptain(void *p_target, void *p_data); + + static void locateCurrentFileByCaptain(void *p_target, void *p_data); + + static void toggleExpandModeByCaptain(void *p_target, void *p_data); + + static void toggleOnePanelViewByCaptain(void *p_target, void *p_data); + + static void discardAndReadByCaptain(void *p_target, void *p_data); + + static void toggleToolsDockByCaptain(void *p_target, void *p_data); + + static void closeFileByCaptain(void *p_target, void *p_data); + + static void shortcutsHelpByCaptain(void *p_target, void *p_data); + + static void flushLogFileByCaptain(void *p_target, void *p_data); + + // End Captain mode functions. + VNote *vnote; QPointer m_curFile; QPointer m_curTab; @@ -355,4 +380,9 @@ inline VEditArea *VMainWindow::getEditArea() const return editArea; } +inline VCaptain *VMainWindow::getCaptain() const +{ + return m_captain; +} + #endif // VMAINWINDOW_H