diff --git a/src/dialog/vnewnotebookdialog.cpp b/src/dialog/vnewnotebookdialog.cpp index 1f66a567..b9856c11 100644 --- a/src/dialog/vnewnotebookdialog.cpp +++ b/src/dialog/vnewnotebookdialog.cpp @@ -142,7 +142,8 @@ void VNewNotebookDialog::handleBrowseBtnClicked() } } - QString dirPath = QFileDialog::getExistingDirectory(this, tr("Select Root Folder Of The Notebook"), + QString dirPath = QFileDialog::getExistingDirectory(this, + tr("Select Root Folder Of The Notebook"), defaultPath, QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks); diff --git a/src/dialog/vsettingsdialog.cpp b/src/dialog/vsettingsdialog.cpp index e52f1ea5..3d12ed50 100644 --- a/src/dialog/vsettingsdialog.cpp +++ b/src/dialog/vsettingsdialog.cpp @@ -169,9 +169,6 @@ VGeneralTab::VGeneralTab(QWidget *p_parent) m_langCombo->addItem(lang.second, lang.first); } - QLabel *langLabel = new QLabel(tr("Language:"), this); - langLabel->setToolTip(m_langCombo->toolTip()); - // System tray checkbox. m_systemTray = new QCheckBox(tr("System tray"), this); m_systemTray->setToolTip(tr("Minimized to the system tray after closing VNote" @@ -181,9 +178,13 @@ VGeneralTab::VGeneralTab(QWidget *p_parent) m_systemTray->setEnabled(false); #endif + // Startup pages. + QLayout *startupLayout = setupStartupPagesLayout(); + QFormLayout *optionLayout = new QFormLayout(); - optionLayout->addRow(langLabel, m_langCombo); + optionLayout->addRow(tr("Language:"), m_langCombo); optionLayout->addRow(m_systemTray); + optionLayout->addRow(tr("Startup pages:"), startupLayout); QVBoxLayout *mainLayout = new QVBoxLayout(); mainLayout->addLayout(optionLayout); @@ -191,6 +192,60 @@ VGeneralTab::VGeneralTab(QWidget *p_parent) setLayout(mainLayout); } +QLayout *VGeneralTab::setupStartupPagesLayout() +{ + m_startupPageTypeCombo = new QComboBox(this); + m_startupPageTypeCombo->setToolTip(tr("Restore tabs or open specific notes on startup")); + m_startupPageTypeCombo->addItem(tr("None"), (int)StartupPageType::None); + m_startupPageTypeCombo->addItem(tr("Continue where you left off"), (int)StartupPageType::ContinueLeftOff); + m_startupPageTypeCombo->addItem(tr("Open specific pages"), (int)StartupPageType::SpecificPages); + connect(m_startupPageTypeCombo, static_cast(&QComboBox::activated), + this, [this](int p_index) { + int type = m_startupPageTypeCombo->itemData(p_index).toInt(); + bool pagesEditVisible = type == (int)StartupPageType::SpecificPages; + m_startupPagesEdit->setVisible(pagesEditVisible); + m_startupPagesAddBtn->setVisible(pagesEditVisible); + }); + + m_startupPagesEdit = new QPlainTextEdit(this); + m_startupPagesEdit->setToolTip(tr("Absolute path of the notes to open on startup (one note per line)")); + + m_startupPagesAddBtn = new QPushButton(tr("Browse"), this); + m_startupPagesAddBtn->setToolTip(tr("Select files to add as startup pages")); + connect(m_startupPagesAddBtn, &QPushButton::clicked, + this, [this]() { + static QString lastPath = QDir::homePath(); + QStringList files = QFileDialog::getOpenFileNames(this, + tr("Select Files As Startup Pages"), + lastPath); + if (files.isEmpty()) { + return; + } + + // Update lastPath + lastPath = QFileInfo(files[0]).path(); + + m_startupPagesEdit->appendPlainText(files.join("\n")); + }); + + QHBoxLayout *startupPagesBtnLayout = new QHBoxLayout(); + startupPagesBtnLayout->addStretch(); + startupPagesBtnLayout->addWidget(m_startupPagesAddBtn); + + QVBoxLayout *startupPagesLayout = new QVBoxLayout(); + startupPagesLayout->addWidget(m_startupPagesEdit); + startupPagesLayout->addLayout(startupPagesBtnLayout); + + QVBoxLayout *startupLayout = new QVBoxLayout(); + startupLayout->addWidget(m_startupPageTypeCombo); + startupLayout->addLayout(startupPagesLayout); + + m_startupPagesEdit->hide(); + m_startupPagesAddBtn->hide(); + + return startupLayout; +} + bool VGeneralTab::loadConfiguration() { if (!loadLanguage()) { @@ -201,6 +256,10 @@ bool VGeneralTab::loadConfiguration() return false; } + if (!loadStartupPageType()) { + return false; + } + return true; } @@ -214,6 +273,10 @@ bool VGeneralTab::saveConfiguration() return false; } + if (!saveStartupPageType()) { + return false; + } + return true; } @@ -264,6 +327,42 @@ bool VGeneralTab::saveSystemTray() return true; } +bool VGeneralTab::loadStartupPageType() +{ + StartupPageType type = g_config->getStartupPageType(); + bool found = false; + for (int i = 0; i < m_startupPageTypeCombo->count(); ++i) { + if (m_startupPageTypeCombo->itemData(i).toInt() == (int)type) { + found = true; + m_startupPageTypeCombo->setCurrentIndex(i); + } + } + + Q_ASSERT(found); + + const QStringList &pages = g_config->getStartupPages(); + m_startupPagesEdit->setPlainText(pages.join("\n")); + + bool pagesEditVisible = type == StartupPageType::SpecificPages; + m_startupPagesEdit->setVisible(pagesEditVisible); + m_startupPagesAddBtn->setVisible(pagesEditVisible); + + return true; +} + +bool VGeneralTab::saveStartupPageType() +{ + StartupPageType type = (StartupPageType)m_startupPageTypeCombo->currentData().toInt(); + g_config->setStartupPageType(type); + + if (type == StartupPageType::SpecificPages) { + QStringList pages = m_startupPagesEdit->toPlainText().split("\n"); + g_config->setStartupPages(pages); + } + + return true; +} + VReadEditTab::VReadEditTab(QWidget *p_parent) : QWidget(p_parent) { diff --git a/src/dialog/vsettingsdialog.h b/src/dialog/vsettingsdialog.h index d9b1eeeb..3ed31cda 100644 --- a/src/dialog/vsettingsdialog.h +++ b/src/dialog/vsettingsdialog.h @@ -13,6 +13,8 @@ class QCheckBox; class QLineEdit; class QStackedLayout; class QListWidget; +class QPlainTextEdit; +class QVBoxLayout; class VGeneralTab : public QWidget { @@ -23,18 +25,32 @@ public: bool saveConfiguration(); private: + QLayout *setupStartupPagesLayout(); + bool loadLanguage(); bool saveLanguage(); bool loadSystemTray(); bool saveSystemTray(); + bool loadStartupPageType(); + bool saveStartupPageType(); + // Language QComboBox *m_langCombo; // System tray QCheckBox *m_systemTray; + // Startup page type. + QComboBox *m_startupPageTypeCombo; + + // Startup pages. + QPlainTextEdit *m_startupPagesEdit; + + // Startup pages add files button. + QPushButton *m_startupPagesAddBtn; + static const QVector c_availableLangs; }; diff --git a/src/main.cpp b/src/main.cpp index 98db9020..7089dea2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -116,22 +116,10 @@ int main(int argc, char *argv[]) QApplication app(argc, argv); // The file path passed via command line arguments. - QStringList filePaths; - QStringList args = app.arguments(); - for (int i = 1; i < args.size(); ++i) { - if (QFileInfo::exists(args[i])) { - QString filePath = args[i]; - QFileInfo fi(filePath); - if (fi.isFile()) { - // Need to use absolute path here since VNote may be launched - // in different working directory. - filePath = QDir::cleanPath(fi.absoluteFilePath()); - filePaths.append(filePath); - } - } - } + QStringList filePaths = VUtils::filterFilePathsToOpen(app.arguments().mid(1)); - qDebug() << "command line arguments" << args; + qDebug() << "command line arguments" << app.arguments(); + qDebug() << "files to open from arguments" << filePaths; if (!canRun) { // Ask another instance to open files passed in. @@ -178,7 +166,9 @@ int main(int argc, char *argv[]) w.show(); - w.openExternalFiles(filePaths); + w.openFiles(filePaths); + + w.openStartupPages(); return app.exec(); } diff --git a/src/resources/vnote.ini b/src/resources/vnote.ini index 868c9170..60f5b20d 100644 --- a/src/resources/vnote.ini +++ b/src/resources/vnote.ini @@ -141,6 +141,16 @@ enable_compact_mode=false ; Whether enable tools dock widget tools_dock_checked=true +; Pages to open on startup +; 0 - none; 1 - Continue where you left off; 2 - specific pages +startup_page_type=0 + +; Specific pages to open on startup when startup_page_type is 2 +; A list of file path separated by , +; Notice: should escape \ by \\ +; C:\users\vnote\vnote.md -> C:\\users\\vnote\\vnote.md +startup_pages= + [web] ; Location and configuration for Mathjax mathjax_javascript=https://cdnjs.cloudflare.com/ajax/libs/mathjax/2.7.2/MathJax.js?config=TeX-MML-AM_CHTML diff --git a/src/src.pro b/src/src.pro index 39730061..1ae6900a 100644 --- a/src/src.pro +++ b/src/src.pro @@ -77,7 +77,8 @@ SOURCES += main.cpp\ dialog/vconfirmdeletiondialog.cpp \ vnotefile.cpp \ vattachmentlist.cpp \ - dialog/vsortdialog.cpp + dialog/vsortdialog.cpp \ + vfilesessioninfo.cpp HEADERS += vmainwindow.h \ vdirectorytree.h \ @@ -142,7 +143,8 @@ HEADERS += vmainwindow.h \ dialog/vconfirmdeletiondialog.h \ vnotefile.h \ vattachmentlist.h \ - dialog/vsortdialog.h + dialog/vsortdialog.h \ + vfilesessioninfo.h RESOURCES += \ vnote.qrc \ diff --git a/src/utils/vutils.cpp b/src/utils/vutils.cpp index 4adbbb5f..047bff19 100644 --- a/src/utils/vutils.cpp +++ b/src/utils/vutils.cpp @@ -959,3 +959,30 @@ void VUtils::addErrMsg(QString *p_msg, const QString &p_str) *p_msg = *p_msg + '\n' + p_str; } } + +QStringList VUtils::filterFilePathsToOpen(const QStringList &p_files) +{ + QStringList paths; + for (int i = 0; i < p_files.size(); ++i) { + QString path = validFilePathToOpen(p_files[i]); + if (!path.isEmpty()) { + paths.append(path); + } + } + + return paths; +} + +QString VUtils::validFilePathToOpen(const QString &p_file) +{ + if (QFileInfo::exists(p_file)) { + QFileInfo fi(p_file); + if (fi.isFile()) { + // Need to use absolute path here since VNote may be launched + // in different working directory. + return QDir::cleanPath(fi.absoluteFilePath()); + } + } + + return QString(); +} diff --git a/src/utils/vutils.h b/src/utils/vutils.h index 0bf4a87b..de748473 100644 --- a/src/utils/vutils.h +++ b/src/utils/vutils.h @@ -232,6 +232,13 @@ public: // Assign @p_str to @p_msg if it is not NULL. static void addErrMsg(QString *p_msg, const QString &p_str); + // Check each file of @p_files and return valid ones for VNote to open. + static QStringList filterFilePathsToOpen(const QStringList &p_files); + + // Return the normalized file path of @p_file if it is valid to open. + // Return empty if it is not valid. + static QString validFilePathToOpen(const QString &p_file); + // Regular expression for image link. // ![image title]( http://github.com/tamlok/vnote.jpg "alt \" text" ) // Captured texts (need to be trimmed): diff --git a/src/vcaptain.cpp b/src/vcaptain.cpp index 093f1e6f..9e96c060 100644 --- a/src/vcaptain.cpp +++ b/src/vcaptain.cpp @@ -275,7 +275,7 @@ bool VCaptain::handleKeyPress(int p_key, Qt::KeyboardModifiers p_modifiers) // Remove current window split. m_mainWindow->editArea->removeCurrentWindow(); - QWidget *nextFocus = m_mainWindow->editArea->currentEditTab(); + QWidget *nextFocus = m_mainWindow->editArea->getCurrentTab(); m_widgetBeforeCaptain = nextFocus ? nextFocus : m_mainWindow->getFileList(); break; } @@ -304,7 +304,7 @@ bool VCaptain::handleKeyPress(int p_key, Qt::KeyboardModifiers p_modifiers) m_mainWindow->closeCurrentFile(); // m_widgetBeforeCaptain may be the closed tab which will cause crash. - QWidget *nextFocus = m_mainWindow->editArea->currentEditTab(); + QWidget *nextFocus = m_mainWindow->editArea->getCurrentTab(); m_widgetBeforeCaptain = nextFocus ? nextFocus : m_mainWindow->getFileList(); break; } diff --git a/src/vconfigmanager.cpp b/src/vconfigmanager.cpp index 8ef654b5..0000c689 100644 --- a/src/vconfigmanager.cpp +++ b/src/vconfigmanager.cpp @@ -235,6 +235,18 @@ void VConfigManager::initialize() m_enableCompactMode = getConfigFromSettings("global", "enable_compact_mode").toBool(); + int tmpStartupPageMode = getConfigFromSettings("global", + "startup_page_type").toInt(); + if (tmpStartupPageMode < (int)StartupPageType::Invalid + && tmpStartupPageMode >= (int)StartupPageType::None) { + m_startupPageType = (StartupPageType)tmpStartupPageMode; + } else { + m_startupPageType = StartupPageType::None; + } + + m_startupPages = getConfigFromSettings("global", + "startup_pages").toStringList(); + initFromSessionSettings(); } @@ -242,6 +254,8 @@ void VConfigManager::initSettings() { Q_ASSERT(!userSettings && !defaultSettings && !m_sessionSettings); + const char *codecForIni = "UTF-8"; + // vnote.ini. // First try to read vnote.ini from the directory of the executable. QString userIniPath = QDir(QCoreApplication::applicationDirPath()).filePath(c_defaultConfigFile); @@ -257,15 +271,19 @@ void VConfigManager::initSettings() this); } + userSettings->setIniCodec(codecForIni); + qDebug() << "use user config" << userSettings->fileName(); // Default vnote.ini from resource file. defaultSettings = new QSettings(c_defaultConfigFilePath, QSettings::IniFormat, this); + defaultSettings->setIniCodec(codecForIni); // session.ini. m_sessionSettings = new QSettings(QDir(getConfigFolder()).filePath(c_sessionConfigFile), QSettings::IniFormat, this); + m_sessionSettings->setIniCodec(codecForIni); } void VConfigManager::initFromSessionSettings() @@ -364,7 +382,7 @@ QVariant VConfigManager::getConfigFromSettings(const QString §ion, const QSt // First, look up the user-scoped config file QVariant value = getConfigFromSettingsBySectionKey(userSettings, section, key); if (!value.isNull()) { - qDebug() << "user config:" << (section + "/" + key) << value.toString(); + qDebug() << "user config:" << (section + "/" + key) << value; return value; } @@ -376,13 +394,13 @@ void VConfigManager::setConfigToSettings(const QString §ion, const QString & { // Set the user-scoped config file setConfigToSettingsBySectionKey(userSettings, section, key, value); - qDebug() << "set user config:" << (section + "/" + key) << value.toString(); + qDebug() << "set user config:" << (section + "/" + key) << value; } QVariant VConfigManager::getDefaultConfig(const QString &p_section, const QString &p_key) const { QVariant value = getConfigFromSettingsBySectionKey(defaultSettings, p_section, p_key); - qDebug() << "default config:" << (p_section + "/" + p_key) << value.toString(); + qDebug() << "default config:" << (p_section + "/" + p_key) << value; return value; } @@ -1090,3 +1108,41 @@ void VConfigManager::initDocSuffixes() qDebug() << "doc suffixes" << m_docSuffixes; } + +QVector VConfigManager::getLastOpenedFiles() +{ + QVector files; + int size = m_sessionSettings->beginReadArray("last_opened_files"); + for (int i = 0; i < size; ++i) { + m_sessionSettings->setArrayIndex(i); + files.push_back(VFileSessionInfo::fromSettings(m_sessionSettings)); + } + + m_sessionSettings->endArray(); + qDebug() << "read" << files.size() + << "items from [last_opened_files] section"; + + return files; +} + +void VConfigManager::setLastOpenedFiles(const QVector &p_files) +{ + const QString section("last_opened_files"); + + // Clear it first + m_sessionSettings->beginGroup(section); + m_sessionSettings->remove(""); + m_sessionSettings->endGroup(); + + m_sessionSettings->beginWriteArray(section); + for (int i = 0; i < p_files.size(); ++i) { + m_sessionSettings->setArrayIndex(i); + const VFileSessionInfo &info = p_files[i]; + info.toSettings(m_sessionSettings); + } + + m_sessionSettings->endArray(); + qDebug() << "write" << p_files.size() + << "items in [last_opened_files] section"; + +} diff --git a/src/vconfigmanager.h b/src/vconfigmanager.h index cc4b284b..6cd6a91b 100644 --- a/src/vconfigmanager.h +++ b/src/vconfigmanager.h @@ -11,6 +11,8 @@ #include "hgmarkdownhighlighter.h" #include "vmarkdownconverter.h" #include "vconstants.h" +#include "vfilesessioninfo.h" + class QJsonObject; class QString; @@ -300,6 +302,18 @@ public: bool getEnableCompactMode() const; void setEnableCompactMode(bool p_enabled); + StartupPageType getStartupPageType() const; + void setStartupPageType(StartupPageType p_type); + + const QStringList &getStartupPages() const; + void setStartupPages(const QStringList &p_pages); + + // Read last opened files from [last_opened_files] of session.ini. + QVector getLastOpenedFiles(); + + // Write last opened files to [last_opened_files] of session.ini. + void setLastOpenedFiles(const QVector &p_files); + // Return the configured key sequence of @p_operation. // Return empty if there is no corresponding config. QString getShortcutKeySequence(const QString &p_operation) const; @@ -635,6 +649,12 @@ private: // Whether put folder and note panel in one single column. bool m_enableCompactMode; + // Type of the pages to open on startup. + StartupPageType m_startupPageType; + + // File paths to open on startup. + QStringList m_startupPages; + // The name of the config file in each directory, obsolete. // Use c_dirConfigFile instead. static const QString c_obsoleteDirConfigFile; @@ -1666,4 +1686,34 @@ inline void VConfigManager::setEnableCompactMode(bool p_enabled) setConfigToSettings("global", "enable_compact_mode", m_enableCompactMode); } +inline StartupPageType VConfigManager::getStartupPageType() const +{ + return m_startupPageType; +} + +inline void VConfigManager::setStartupPageType(StartupPageType p_type) +{ + if (m_startupPageType == p_type) { + return; + } + + m_startupPageType = p_type; + setConfigToSettings("global", "startup_page_type", (int)m_startupPageType); +} + +inline const QStringList &VConfigManager::getStartupPages() const +{ + return m_startupPages; +} + +inline void VConfigManager::setStartupPages(const QStringList &p_pages) +{ + if (m_startupPages == p_pages) { + return; + } + + m_startupPages = p_pages; + setConfigToSettings("global", "startup_pages", m_startupPages); +} + #endif // VCONFIGMANAGER_H diff --git a/src/vconstants.h b/src/vconstants.h index ae1a2205..6a326d37 100644 --- a/src/vconstants.h +++ b/src/vconstants.h @@ -1,6 +1,8 @@ #ifndef VCONSTANTS_H #define VCONSTANTS_H +#include + // Html: rich text file; // Markdown: Markdown text file; // List: Infinite list file like WorkFlowy; @@ -22,7 +24,7 @@ namespace ClipboardConfig static const QString c_dirs = "dirs"; } -enum class OpenFileMode {Read = 0, Edit}; +enum class OpenFileMode {Read = 0, Edit, Invalid }; static const qreal c_webZoomFactorMax = 5; static const qreal c_webZoomFactorMin = 0.25; @@ -97,4 +99,13 @@ enum class LineNumberType CodeBlock }; +// Pages to open on start up. +enum class StartupPageType +{ + None = 0, + ContinueLeftOff = 1, + SpecificPages = 2, + Invalid +}; + #endif diff --git a/src/vedit.h b/src/vedit.h index 7c8b4cce..049762c1 100644 --- a/src/vedit.h +++ b/src/vedit.h @@ -163,6 +163,9 @@ signals: // Request the edit tab to close find and replace dialog. void requestCloseFindReplaceDialog(); + // Emit when all initialization is ready. + void ready(); + public slots: virtual void highlightCurrentLine(); diff --git a/src/veditarea.cpp b/src/veditarea.cpp index c92dcb32..bb77c8df 100644 --- a/src/veditarea.cpp +++ b/src/veditarea.cpp @@ -7,6 +7,7 @@ #include "vfile.h" #include "dialog/vfindreplacedialog.h" #include "utils/vutils.h" +#include "vfilesessioninfo.h" extern VConfigManager *g_config; extern VNote *g_vnote; @@ -119,17 +120,17 @@ void VEditArea::removeSplitWindow(VEditWindow *win) win->deleteLater(); } -void VEditArea::openFile(VFile *p_file, OpenFileMode p_mode, bool p_forceMode) +VEditTab *VEditArea::openFile(VFile *p_file, OpenFileMode p_mode, bool p_forceMode) { if (!p_file) { - return; + return NULL; } // If it is DocType::Unknown, open it using system default method. if (p_file->getDocType() == DocType::Unknown) { QUrl url = QUrl::fromLocalFile(p_file->fetchPath()); QDesktopServices::openUrl(url); - return; + return NULL; } // Find if it has been opened already @@ -165,6 +166,8 @@ void VEditArea::openFile(VFile *p_file, OpenFileMode p_mode, bool p_forceMode) tabIdx = openFileInWindow(winIdx, p_file, p_mode); out: + VEditTab *tab = getTab(winIdx, tabIdx); + setCurrentTab(winIdx, tabIdx, setFocus); if (existFile && p_forceMode) { @@ -174,6 +177,8 @@ out: editFile(); } } + + return tab; } QVector > VEditArea::findTabsByFile(const VFile *p_file) @@ -482,13 +487,35 @@ void VEditArea::handleNotebookUpdated(const VNotebook *p_notebook) } } -VEditTab *VEditArea::currentEditTab() +VEditTab *VEditArea::getCurrentTab() const { if (curWindowIndex == -1) { return NULL; } + VEditWindow *win = getWindow(curWindowIndex); - return win->currentEditTab(); + return win->getCurrentTab(); +} + +VEditTab *VEditArea::getTab(int p_winIdx, int p_tabIdx) const +{ + VEditWindow *win = getWindow(p_winIdx); + if (!win) { + return NULL; + } + + return win->getTab(p_tabIdx); +} + +QVector VEditArea::getAllTabsInfo() const +{ + QVector tabs; + int nrWin = splitter->count(); + for (int i = 0; i < nrWin; ++i) { + tabs.append(getWindow(i)->getAllTabsInfo()); + } + + return tabs; } int VEditArea::windowIndex(const VEditWindow *p_window) const @@ -518,7 +545,7 @@ void VEditArea::moveTab(QWidget *p_widget, int p_fromIdx, int p_toIdx) // Only propogate the search in the IncrementalSearch case. void VEditArea::handleFindTextChanged(const QString &p_text, uint p_options) { - VEditTab *tab = currentEditTab(); + VEditTab *tab = getCurrentTab(); if (tab) { if (p_options & FindOption::IncrementalSearch) { tab->findText(p_text, p_options, true); @@ -539,7 +566,7 @@ void VEditArea::handleFindNext(const QString &p_text, uint p_options, bool p_forward) { qDebug() << "find next" << p_text << p_options << p_forward; - VEditTab *tab = currentEditTab(); + VEditTab *tab = getCurrentTab(); if (tab) { tab->findText(p_text, p_options, false, p_forward); } @@ -550,7 +577,7 @@ void VEditArea::handleReplace(const QString &p_text, uint p_options, { qDebug() << "replace" << p_text << p_options << "with" << p_replaceText << p_findNext; - VEditTab *tab = currentEditTab(); + VEditTab *tab = getCurrentTab(); if (tab) { tab->replaceText(p_text, p_options, p_replaceText, p_findNext); } @@ -560,7 +587,7 @@ void VEditArea::handleReplaceAll(const QString &p_text, uint p_options, const QString &p_replaceText) { qDebug() << "replace all" << p_text << p_options << "with" << p_replaceText; - VEditTab *tab = currentEditTab(); + VEditTab *tab = getCurrentTab(); if (tab) { tab->replaceTextAll(p_text, p_options, p_replaceText); } @@ -584,7 +611,7 @@ void VEditArea::handleFindDialogClosed() QString VEditArea::getSelectedText() { - VEditTab *tab = currentEditTab(); + VEditTab *tab = getCurrentTab(); if (tab) { return tab->getSelectedText(); } else { @@ -694,3 +721,29 @@ bool VEditArea::handleKeyNavigation(int p_key, bool &p_succeed) return ret; } +int VEditArea::openFiles(const QVector &p_files) +{ + int nrOpened = 0; + for (auto const & info : p_files) { + QString filePath = VUtils::validFilePathToOpen(info.m_file); + if (filePath.isEmpty()) { + continue; + } + + VFile *file = g_vnote->getFile(filePath); + if (!file) { + continue; + } + + VEditTab *tab = openFile(file, info.m_mode, true); + ++nrOpened; + + VEditTabInfo tabInfo; + tabInfo.m_editTab = tab; + info.toEditTabInfo(&tabInfo); + + tab->tryRestoreFromTabInfo(tabInfo); + } + + return nrOpened; +} diff --git a/src/veditarea.h b/src/veditarea.h index b8ee6824..49343d3d 100644 --- a/src/veditarea.h +++ b/src/veditarea.h @@ -35,17 +35,31 @@ public: bool closeFile(const VFile *p_file, bool p_forced); bool closeFile(const VDirectory *p_dir, bool p_forced); bool closeFile(const VNotebook *p_notebook, bool p_forced); - // Returns current edit tab. - VEditTab *currentEditTab(); - // Returns the count of VEditWindow. - inline int windowCount() const; + + // Return current edit window. + VEditWindow *getCurrentWindow() const; + + // Return current edit tab. + VEditTab *getCurrentTab() const; + + // Return the @p_tabIdx tab in the @p_winIdx window. + VEditTab *getTab(int p_winIdx, int p_tabIdx) const; + + // Return VEditTabInfo of all edit tabs. + QVector getAllTabsInfo() const; + + // Return the count of VEditWindow. + int windowCount() const; + // Returns the index of @p_window. int windowIndex(const VEditWindow *p_window) const; // Move tab widget @p_widget from window @p_fromIdx to @p_toIdx. // @p_widget has been removed from the original window. // If fail, just delete the p_widget. void moveTab(QWidget *p_widget, int p_fromIdx, int p_toIdx); - inline VFindReplaceDialog *getFindReplaceDialog() const; + + VFindReplaceDialog *getFindReplaceDialog() const; + // Return selected text of current edit tab. QString getSelectedText(); void splitCurrentWindow(); @@ -54,7 +68,6 @@ public: // Return the new current window index, otherwise, return -1. int focusNextWindow(int p_biaIdx); void moveCurrentTabOneSplit(bool p_right); - VEditWindow *getCurrentWindow() const; // Implementations for VNavigationMode. void registerNavigation(QChar p_majorKey) Q_DECL_OVERRIDE; @@ -62,6 +75,9 @@ public: void hideNavigation() Q_DECL_OVERRIDE; bool handleKeyNavigation(int p_key, bool &p_succeed) Q_DECL_OVERRIDE; + // Open files @p_files. + int openFiles(const QVector &p_files); + signals: // Emit when current window's tab status updated. void tabStatusUpdated(const VEditTabInfo &p_info); @@ -84,7 +100,7 @@ public slots: // @p_forceMode is true. // A given file can be opened in multiple split windows. A given file could be // opened at most in one tab inside a window. - void openFile(VFile *p_file, OpenFileMode p_mode, bool p_forceMode = false); + VEditTab *openFile(VFile *p_file, OpenFileMode p_mode, bool p_forceMode = false); void editFile(); void saveFile(); @@ -128,7 +144,9 @@ private: int openFileInWindow(int windowIndex, VFile *p_file, OpenFileMode p_mode); void setCurrentTab(int windowIndex, int tabIndex, bool setFocus); void setCurrentWindow(int windowIndex, bool setFocus); - inline VEditWindow *getWindow(int windowIndex) const; + + VEditWindow *getWindow(int windowIndex) const; + void insertSplitWindow(int idx); void removeSplitWindow(VEditWindow *win); diff --git a/src/vedittab.cpp b/src/vedittab.cpp index 0f34297c..8e41c48b 100644 --- a/src/vedittab.cpp +++ b/src/vedittab.cpp @@ -82,13 +82,37 @@ void VEditTab::updateStatus() { m_modified = m_file->isModified(); - emit statusUpdated(createEditTabInfo()); + emit statusUpdated(fetchTabInfo()); } -VEditTabInfo VEditTab::createEditTabInfo() +VEditTabInfo VEditTab::fetchTabInfo() { VEditTabInfo info; info.m_editTab = this; return info; } + +VAnchor VEditTab::getCurrentHeader() const +{ + return m_curHeader; +} + +void VEditTab::tryRestoreFromTabInfo(const VEditTabInfo &p_info) +{ + if (p_info.m_editTab != this) { + // Clear and return. + m_infoToRestore.m_editTab = NULL; + return; + } + + if (restoreFromTabInfo(p_info)) { + // Clear and return. + m_infoToRestore.m_editTab = NULL; + return; + } + + // Save it and restore later. + m_infoToRestore = p_info; + qDebug() << "save info for restore later" << p_info.m_anchorIndex; +} diff --git a/src/vedittab.h b/src/vedittab.h index 116c814b..acf0d470 100644 --- a/src/vedittab.h +++ b/src/vedittab.h @@ -69,7 +69,16 @@ public: virtual void requestUpdateVimStatus() = 0; // Insert decoration markers or decorate selected text. - virtual void decorateText(TextDecoration p_decoration) {Q_UNUSED(p_decoration);}; + virtual void decorateText(TextDecoration p_decoration) {Q_UNUSED(p_decoration);} + + // Create a filled VEditTabInfo. + virtual VEditTabInfo fetchTabInfo(); + + VAnchor getCurrentHeader() const; + + // Restore status from @p_info. + // If this tab is not ready yet, it will restore once it is ready. + void tryRestoreFromTabInfo(const VEditTabInfo &p_info); public slots: // Enter edit mode @@ -87,8 +96,9 @@ protected: // Called to zoom in/out content. virtual void zoom(bool p_zoomIn, qreal p_step = 0.25) = 0; - // Create a filled VEditTabInfo. - virtual VEditTabInfo createEditTabInfo(); + // Restore from @p_fino. + // Return true if succeed. + virtual bool restoreFromTabInfo(const VEditTabInfo &p_info) = 0; // File related to this tab. QPointer m_file; @@ -98,6 +108,9 @@ protected: VAnchor m_curHeader; VEditArea *m_editArea; + // Tab info to restore from once ready. + VEditTabInfo m_infoToRestore; + signals: void getFocused(); diff --git a/src/vedittabinfo.h b/src/vedittabinfo.h index 5fc4003b..2c73e735 100644 --- a/src/vedittabinfo.h +++ b/src/vedittabinfo.h @@ -6,8 +6,13 @@ class VEditTab; struct VEditTabInfo { VEditTabInfo() - : m_editTab(NULL), m_cursorBlockNumber(-1), m_cursorPositionInBlock(-1), - m_blockCount(-1) {} + : m_editTab(NULL), + m_cursorBlockNumber(-1), + m_cursorPositionInBlock(-1), + m_blockCount(-1), + m_anchorIndex(-1) + { + } VEditTab *m_editTab; @@ -15,6 +20,9 @@ struct VEditTabInfo int m_cursorBlockNumber; int m_cursorPositionInBlock; int m_blockCount; + + // Anchor index in outline. + int m_anchorIndex; }; #endif // VEDITTABINFO_H diff --git a/src/veditwindow.cpp b/src/veditwindow.cpp index 857a2606..d2ff0629 100644 --- a/src/veditwindow.cpp +++ b/src/veditwindow.cpp @@ -794,15 +794,30 @@ void VEditWindow::updateNotebookInfo(const VNotebook *p_notebook) } } -VEditTab *VEditWindow::currentEditTab() +VEditTab *VEditWindow::getCurrentTab() const { int idx = currentIndex(); if (idx == -1) { return NULL; } + return getTab(idx); } +QVector VEditWindow::getAllTabsInfo() const +{ + int nrTab = count(); + + QVector tabs; + tabs.reserve(nrTab); + for (int i = 0; i < nrTab; ++i) { + VEditTab *editTab = getTab(i); + tabs.push_back(editTab->fetchTabInfo()); + } + + return tabs; +} + void VEditWindow::handleLocateAct() { int tab = m_locateAct->data().toInt(); @@ -1014,7 +1029,7 @@ void VEditWindow::dropEvent(QDropEvent *p_event) if (!files.isEmpty()) { focusWindow(); - g_vnote->getMainWindow()->openExternalFiles(files); + g_vnote->getMainWindow()->openFiles(files); } p_event->acceptProposedAction(); diff --git a/src/veditwindow.h b/src/veditwindow.h index 40e6b176..762572e9 100644 --- a/src/veditwindow.h +++ b/src/veditwindow.h @@ -40,7 +40,13 @@ public: void updateFileInfo(const VFile *p_file); void updateDirectoryInfo(const VDirectory *p_dir); void updateNotebookInfo(const VNotebook *p_notebook); - VEditTab *currentEditTab(); + + VEditTab *getCurrentTab() const; + + VEditTab *getTab(int tabIndex) const; + + QVector getAllTabsInfo() const; + // Insert a tab with @p_widget. @p_widget is a fully initialized VEditTab. bool addEditTab(QWidget *p_widget); // Set whether it is the current window. @@ -53,7 +59,6 @@ public: bool activateTab(int p_sequence); // Switch to previous activated tab. bool alternateTab(); - VEditTab *getTab(int tabIndex) const; // Ask tab @p_index to update its status and propogate. // The status here means tab status, outline, current header. @@ -124,9 +129,9 @@ 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); - inline QString generateTooltip(const VFile *p_file) const; - inline QString generateTabText(int p_index, const QString &p_name, - bool p_modified, bool p_modifiable) const; + QString generateTooltip(const VFile *p_file) const; + QString generateTabText(int p_index, const QString &p_name, + bool p_modified, bool p_modifiable) const; bool canRemoveSplit(); // Move tab at @p_tabIdx one split window. diff --git a/src/vfilelist.cpp b/src/vfilelist.cpp index 500ee6b1..999fcec3 100644 --- a/src/vfilelist.cpp +++ b/src/vfilelist.cpp @@ -374,7 +374,7 @@ void VFileList::newFile() // Move cursor down if content has been inserted. if (contentInserted) { - const VMdTab *tab = dynamic_cast(editArea->currentEditTab()); + const VMdTab *tab = dynamic_cast(editArea->getCurrentTab()); if (tab) { VMdEdit *edit = dynamic_cast(tab->getEditor()); if (edit && edit->getFile() == file) { diff --git a/src/vfilesessioninfo.cpp b/src/vfilesessioninfo.cpp new file mode 100644 index 00000000..001b9fde --- /dev/null +++ b/src/vfilesessioninfo.cpp @@ -0,0 +1,74 @@ +#include "vfilesessioninfo.h" + +#include + +#include "vedittabinfo.h" +#include "vtoc.h" +#include "vedittab.h" + + +VFileSessionInfo::VFileSessionInfo() + : m_mode(OpenFileMode::Read), + m_anchorIndex(-1), + m_cursorBlockNumber(-1), + m_cursorPositionInBlock(-1) +{ +} + +VFileSessionInfo::VFileSessionInfo(const QString &p_file, + OpenFileMode p_mode) + : m_file(p_file), + m_mode(p_mode), + m_anchorIndex(-1), + m_cursorBlockNumber(-1), + m_cursorPositionInBlock(-1) +{ +} + +// Fetch VFileSessionInfo from @p_tabInfo. +VFileSessionInfo VFileSessionInfo::fromEditTabInfo(const VEditTabInfo *p_tabInfo) +{ + Q_ASSERT(p_tabInfo); + VEditTab *tab = p_tabInfo->m_editTab; + VFileSessionInfo info(tab->getFile()->fetchPath(), + tab->isEditMode() ? OpenFileMode::Edit : OpenFileMode::Read); + info.m_anchorIndex = p_tabInfo->m_anchorIndex; + info.m_cursorBlockNumber = p_tabInfo->m_cursorBlockNumber; + info.m_cursorPositionInBlock = p_tabInfo->m_cursorPositionInBlock; + + return info; +} + +void VFileSessionInfo::toEditTabInfo(VEditTabInfo *p_tabInfo) const +{ + p_tabInfo->m_anchorIndex = m_anchorIndex; + p_tabInfo->m_cursorBlockNumber = m_cursorBlockNumber; + p_tabInfo->m_cursorPositionInBlock = m_cursorPositionInBlock; +} + +VFileSessionInfo VFileSessionInfo::fromSettings(const QSettings *p_settings) +{ + VFileSessionInfo info; + info.m_file = p_settings->value(FileSessionConfig::c_file).toString(); + int tmpMode = p_settings->value(FileSessionConfig::c_mode).toInt(); + if (tmpMode >= (int)OpenFileMode::Read && tmpMode < (int)OpenFileMode::Invalid) { + info.m_mode = (OpenFileMode)tmpMode; + } else { + info.m_mode = OpenFileMode::Read; + } + + info.m_anchorIndex = p_settings->value(FileSessionConfig::c_anchorIndex).toInt(); + info.m_cursorBlockNumber = p_settings->value(FileSessionConfig::c_cursorBlockNumber).toInt(); + info.m_cursorPositionInBlock = p_settings->value(FileSessionConfig::c_cursorPositionInBlock).toInt(); + + return info; +} + +void VFileSessionInfo::toSettings(QSettings *p_settings) const +{ + p_settings->setValue(FileSessionConfig::c_file, m_file); + p_settings->setValue(FileSessionConfig::c_mode, (int)m_mode); + p_settings->setValue(FileSessionConfig::c_anchorIndex, m_anchorIndex); + p_settings->setValue(FileSessionConfig::c_cursorBlockNumber, m_cursorBlockNumber); + p_settings->setValue(FileSessionConfig::c_cursorPositionInBlock, m_cursorPositionInBlock); +} diff --git a/src/vfilesessioninfo.h b/src/vfilesessioninfo.h new file mode 100644 index 00000000..3213fc56 --- /dev/null +++ b/src/vfilesessioninfo.h @@ -0,0 +1,57 @@ +#ifndef VFILESESSIONINFO_H +#define VFILESESSIONINFO_H + +#include "vconstants.h" + +struct VEditTabInfo; +class QSettings; + +namespace FileSessionConfig +{ + static const QString c_file = "file"; + static const QString c_mode = "mode"; + + // Index in outline of the anchor. + static const QString c_anchorIndex = "anchor_index"; + + static const QString c_cursorBlockNumber = "cursor_block_number"; + static const QString c_cursorPositionInBlock = "cursor_position_in_block"; +} + +// Information about an opened file (session). +class VFileSessionInfo +{ +public: + VFileSessionInfo(); + + VFileSessionInfo(const QString &p_file, + OpenFileMode p_mode); + + // Fetch VFileSessionInfo from @p_tabInfo. + static VFileSessionInfo fromEditTabInfo(const VEditTabInfo *p_tabInfo); + + // Fill corresponding fields of @p_tabInfo. + void toEditTabInfo(VEditTabInfo *p_tabInfo) const; + + // Fetch VFileSessionInfo from @p_settings. + static VFileSessionInfo fromSettings(const QSettings *p_settings); + + void toSettings(QSettings *p_settings) const; + + // Absolute path of the file. + QString m_file; + + // Mode of this file in this session. + OpenFileMode m_mode; + + // Index in outline of the anchor. + int m_anchorIndex; + + // Block number of cursor block. + int m_cursorBlockNumber; + + // Position in block of cursor. + int m_cursorPositionInBlock; +}; + +#endif // VFILESESSIONINFO_H diff --git a/src/vhtmltab.cpp b/src/vhtmltab.cpp index d41c32d3..345ea72c 100644 --- a/src/vhtmltab.cpp +++ b/src/vhtmltab.cpp @@ -252,3 +252,12 @@ void VHtmlTab::requestUpdateVimStatus() { m_editor->requestUpdateVimStatus(); } + +bool VHtmlTab::restoreFromTabInfo(const VEditTabInfo &p_info) +{ + if (p_info.m_editTab != this) { + return false; + } + + return true; +} diff --git a/src/vhtmltab.h b/src/vhtmltab.h index 9024dfd6..dafd32fe 100644 --- a/src/vhtmltab.h +++ b/src/vhtmltab.h @@ -78,6 +78,10 @@ private: // Focus the proper child widget. void focusChild() Q_DECL_OVERRIDE; + // Restore from @p_fino. + // Return true if succeed. + bool restoreFromTabInfo(const VEditTabInfo &p_info) Q_DECL_OVERRIDE; + VEdit *m_editor; }; #endif // VHTMLTAB_H diff --git a/src/vmainwindow.cpp b/src/vmainwindow.cpp index 9e70fc90..02bdf218 100644 --- a/src/vmainwindow.cpp +++ b/src/vmainwindow.cpp @@ -29,6 +29,7 @@ #include "vnotefile.h" #include "vbuttonwithwidget.h" #include "vattachmentlist.h" +#include "vfilesessioninfo.h" extern VConfigManager *g_config; @@ -766,7 +767,7 @@ void VMainWindow::initFileMenu() // Update lastPath lastPath = QFileInfo(files[0]).path(); - openExternalFiles(files); + openFiles(VUtils::filterFilePathsToOpen(files)); }); fileMenu->addAction(openAct); @@ -1956,12 +1957,33 @@ void VMainWindow::closeEvent(QCloseEvent *event) } if (isExit || !m_trayIcon->isVisible()) { + // Get all the opened tabs. + bool saveOpenedNotes = g_config->getStartupPageType() == StartupPageType::ContinueLeftOff; + QVector fileInfos; + QVector tabs; + if (saveOpenedNotes) { + tabs = editArea->getAllTabsInfo(); + + fileInfos.reserve(tabs.size()); + + for (auto const & tab : tabs) { + VFileSessionInfo info = VFileSessionInfo::fromEditTabInfo(&tab); + fileInfos.push_back(info); + + qDebug() << "file session:" << info.m_file << (info.m_mode == OpenFileMode::Edit); + } + } + if (!editArea->closeAllFiles(false)) { // Fail to close all the opened files, cancel closing app. event->ignore(); return; } + if (saveOpenedNotes) { + g_config->setLastOpenedFiles(fileInfos); + } + QMainWindow::closeEvent(event); } else { hide(); @@ -2059,7 +2081,7 @@ void VMainWindow::insertImage() if (!m_curTab) { return; } - Q_ASSERT(m_curTab == editArea->currentEditTab()); + Q_ASSERT(m_curTab == editArea->getCurrentTab()); m_curTab->insertImage(); } @@ -2310,9 +2332,8 @@ bool VMainWindow::tryOpenInternalFile(const QString &p_filePath) return false; } -void VMainWindow::openExternalFiles(const QStringList &p_files, bool p_forceOrphan) +void VMainWindow::openFiles(const QStringList &p_files, bool p_forceOrphan) { - qDebug() << "open external files" << p_files; for (int i = 0; i < p_files.size(); ++i) { VFile *file = NULL; if (!p_forceOrphan) { @@ -2344,7 +2365,7 @@ void VMainWindow::checkSharedMemory() QStringList files = m_guard->fetchFilesToOpen(); if (!files.isEmpty()) { qDebug() << "shared memory fetch files" << files; - openExternalFiles(files); + openFiles(files); // Eliminate the signal. m_guard->fetchAskedToShow(); @@ -2416,3 +2437,28 @@ void VMainWindow::showAttachmentList() m_attachmentBtn->showPopupWidget(); } } + +void VMainWindow::openStartupPages() +{ + StartupPageType type = g_config->getStartupPageType(); + switch (type) { + case StartupPageType::ContinueLeftOff: + { + QVector files = g_config->getLastOpenedFiles(); + qDebug() << "open" << files.size() << "last opened files"; + editArea->openFiles(files); + break; + } + + case StartupPageType::SpecificPages: + { + QStringList pagesToOpen = VUtils::filterFilePathsToOpen(g_config->getStartupPages()); + qDebug() << "open startup pages" << pagesToOpen; + openFiles(pagesToOpen); + break; + } + + default: + break; + } +} diff --git a/src/vmainwindow.h b/src/vmainwindow.h index 60a2740f..9753b796 100644 --- a/src/vmainwindow.h +++ b/src/vmainwindow.h @@ -68,10 +68,10 @@ public: // View and edit the information of @p_file, which is an orphan file. void editOrphanFileInfo(VFile *p_file); - // Open external files @p_files as orphan files. + // Open files @p_files as orphan files or internal note files. // If @p_forceOrphan is false, for each file, VNote will try to find out if // it is a note inside VNote. If yes, VNote will open it as internal file. - void openExternalFiles(const QStringList &p_files, bool p_forceOrphan = false); + void openFiles(const QStringList &p_files, bool p_forceOrphan = false); // Try to open @p_filePath as internal note. bool tryOpenInternalFile(const QString &p_filePath); @@ -82,6 +82,9 @@ public: // Popup the attachment list if it is enabled. void showAttachmentList(); + // Open startup pages according to configuration. + void openStartupPages(); + private slots: void importNoteFromFile(); void viewSettings(); diff --git a/src/vmdedit.cpp b/src/vmdedit.cpp index 0bb54218..739e291a 100644 --- a/src/vmdedit.cpp +++ b/src/vmdedit.cpp @@ -539,7 +539,7 @@ void VMdEdit::updateOutline(const QVector &p_headerRegions) updateCurHeader(); } -void VMdEdit::scrollToHeader(const VAnchor &p_anchor) +void VMdEdit::scrollToAnchor(const VAnchor &p_anchor) { if (p_anchor.lineNumber == -1 || p_anchor.m_outlineIndex < 0) { @@ -557,6 +557,20 @@ void VMdEdit::scrollToHeader(const VAnchor &p_anchor) scrollToLine(p_anchor.lineNumber); } +bool VMdEdit::scrollToAnchor(int p_anchorIndex) +{ + if (p_anchorIndex >= 0 && p_anchorIndex < m_headers.size()) { + int lineNumber = m_headers[p_anchorIndex].lineNumber; + if (lineNumber >= 0) { + scrollToLine(lineNumber); + + return true; + } + } + + return false; +} + QString VMdEdit::toPlainTextWithoutImg() { QString text; @@ -838,5 +852,7 @@ void VMdEdit::finishOneAsyncJob(int p_idx) emit statusChanged(); updateOutline(m_mdHighlighter->getHeaderRegions()); + + emit ready(); } } diff --git a/src/vmdedit.h b/src/vmdedit.h index 026bd449..37dcbb08 100644 --- a/src/vmdedit.h +++ b/src/vmdedit.h @@ -32,7 +32,11 @@ public: // @p_path is the absolute path of the inserted image. void imageInserted(const QString &p_path); - void scrollToHeader(const VAnchor &p_anchor); + void scrollToAnchor(const VAnchor &p_anchor); + + // Scroll to anchor given the the index in outline. + // Return true if @p_anchorIndex is valid. + bool scrollToAnchor(int p_anchorIndex); // Like toPlainText(), but remove image preview characters. QString toPlainTextWithoutImg(); diff --git a/src/vmdtab.cpp b/src/vmdtab.cpp index 69d79642..a6bb4adb 100644 --- a/src/vmdtab.cpp +++ b/src/vmdtab.cpp @@ -77,27 +77,46 @@ void VMdTab::showFileReadMode() m_stacks->setCurrentWidget(m_webViewer); clearSearchedWordHighlight(); - scrollWebViewToHeader(outlineIndex); + scrollWebViewToAnchor(outlineIndex); updateStatus(); } -void VMdTab::scrollWebViewToHeader(int p_outlineIndex) +bool VMdTab::scrollWebViewToAnchor(int p_anchorIndex, bool p_strict) { QString anchor; - m_curHeader = VAnchor(m_file, anchor, -1, p_outlineIndex); + VAnchor anch(m_file, anchor, -1, p_anchorIndex); - if (p_outlineIndex < m_toc.headers.size() && p_outlineIndex >= 0) { - QString tmp = m_toc.headers[p_outlineIndex].anchor; - V_ASSERT(!tmp.isEmpty()); - m_curHeader.anchor = tmp; + bool validIndex = false; + if (p_anchorIndex < m_toc.headers.size() && p_anchorIndex >= 0) { + QString tmp = m_toc.headers[p_anchorIndex].anchor; + Q_ASSERT(!tmp.isEmpty()); + anch.anchor = tmp; anchor = tmp.mid(1); + validIndex = true; } - m_document->scrollToAnchor(anchor); + if (validIndex || !p_strict) { + m_curHeader = anch; - emit curHeaderChanged(m_curHeader); + m_document->scrollToAnchor(anchor); + + emit curHeaderChanged(m_curHeader); + + return true; + } else { + return false; + } +} + +bool VMdTab::scrollToAnchor(int p_anchorIndex, bool p_strict) +{ + if (m_isEditMode) { + return dynamic_cast(getEditor())->scrollToAnchor(p_anchorIndex); + } else { + return scrollWebViewToAnchor(p_anchorIndex, p_strict); + } } void VMdTab::viewWebByConverter() @@ -148,7 +167,7 @@ void VMdTab::showFileEditMode() VAnchor anchor(m_file, "", lineNumber, outlineIndex); - mdEdit->scrollToHeader(anchor); + mdEdit->scrollToAnchor(anchor); mdEdit->setFocus(); @@ -290,6 +309,8 @@ void VMdTab::setupMarkdownViewer() this, SLOT(updateCurHeader(const QString &))); connect(m_document, &VDocument::keyPressed, this, &VMdTab::handleWebKeyPressed); + connect(m_document, SIGNAL(logicsFinished(void)), + this, SLOT(restoreFromTabInfo(void))); page->setWebChannel(channel); m_webViewer->setHtml(VUtils::generateHtmlTemplate(m_mdConType, false), @@ -324,9 +345,11 @@ void VMdTab::setupMarkdownEditor() connect(m_editor, &VEdit::vimStatusUpdated, this, &VEditTab::vimStatusUpdated); connect(m_editor, &VEdit::requestCloseFindReplaceDialog, - this, [this](){ + this, [this]() { this->m_editArea->getFindReplaceDialog()->closeDialog(); }); + connect(m_editor, SIGNAL(ready(void)), + this, SLOT(restoreFromTabInfo(void))); m_editor->reloadFile(); m_stacks->addWidget(m_editor); @@ -467,7 +490,7 @@ void VMdTab::scrollToAnchor(const VAnchor &p_anchor) m_curHeader = p_anchor; if (m_isEditMode) { - dynamic_cast(getEditor())->scrollToHeader(p_anchor); + dynamic_cast(getEditor())->scrollToAnchor(p_anchor); } else { if (!p_anchor.anchor.isEmpty()) { m_document->scrollToAnchor(p_anchor.anchor.mid(1)); @@ -682,9 +705,9 @@ void VMdTab::requestUpdateVimStatus() } } -VEditTabInfo VMdTab::createEditTabInfo() +VEditTabInfo VMdTab::fetchTabInfo() { - VEditTabInfo info = VEditTab::createEditTabInfo(); + VEditTabInfo info = VEditTab::fetchTabInfo(); if (m_editor) { QTextCursor cursor = m_editor->textCursor(); @@ -693,6 +716,8 @@ VEditTabInfo VMdTab::createEditTabInfo() info.m_blockCount = m_editor->document()->blockCount(); } + info.m_anchorIndex = m_curHeader.m_outlineIndex; + return info; } @@ -702,3 +727,25 @@ void VMdTab::decorateText(TextDecoration p_decoration) m_editor->decorateText(p_decoration); } } + +bool VMdTab::restoreFromTabInfo(const VEditTabInfo &p_info) +{ + qDebug() << "restoreFromTabInfo" << p_info.m_anchorIndex; + if (p_info.m_editTab != this) { + return false; + } + + // Restore anchor. + int anchorIdx = p_info.m_anchorIndex; + bool ret = scrollToAnchor(anchorIdx, true); + + return ret; +} + +void VMdTab::restoreFromTabInfo() +{ + restoreFromTabInfo(m_infoToRestore); + + // Clear it anyway. + m_infoToRestore.m_editTab = NULL; +} diff --git a/src/vmdtab.h b/src/vmdtab.h index ab7a5fd6..85f375ff 100644 --- a/src/vmdtab.h +++ b/src/vmdtab.h @@ -62,6 +62,9 @@ public: // Insert decoration markers or decorate selected text. void decorateText(TextDecoration p_decoration) Q_DECL_OVERRIDE; + // Create a filled VEditTabInfo. + VEditTabInfo fetchTabInfo() Q_DECL_OVERRIDE; + public slots: // Enter edit mode. void editFile() Q_DECL_OVERRIDE; @@ -91,6 +94,9 @@ private slots: // m_editor requests to discard changes and enter read mode. void discardAndRead(); + // Restore from m_infoToRestore. + void restoreFromTabInfo(); + private: // Setup UI. void setupUI(); @@ -111,8 +117,16 @@ private: void viewWebByConverter(); // Scroll Web view to given header. - // @p_outlineIndex is the index in m_toc.headers. - void scrollWebViewToHeader(int p_outlineIndex); + // @p_anchorIndex is the index in m_toc.headers. + // @p_strict: if true, scroll only when @p_anchorIndex is valid. + // Return true if scroll was made. + bool scrollWebViewToAnchor(int p_anchorIndex, bool p_strict = false); + + // Scroll web/editor to given header. + // @p_anchorIndex is the index in m_toc.headers. + // @p_strict: if true, scroll only when @p_anchorIndex is valid. + // Return true if scroll was made. + bool scrollToAnchor(int p_anchorIndex, bool p_strict = false); // Search text in Web view. void findTextInWebView(const QString &p_text, uint p_options, bool p_peek, @@ -127,12 +141,13 @@ private: // Focus the proper child widget. void focusChild() Q_DECL_OVERRIDE; - // Create a filled VEditTabInfo. - VEditTabInfo createEditTabInfo() Q_DECL_OVERRIDE; - // Get the markdown editor. If not init yet, init and return it. VEdit *getEditor(); + // Restore from @p_fino. + // Return true if succeed. + bool restoreFromTabInfo(const VEditTabInfo &p_info) Q_DECL_OVERRIDE; + VEdit *m_editor; VWebView *m_webViewer; VDocument *m_document; diff --git a/src/vnote.cpp b/src/vnote.cpp index 1c318527..720d5725 100644 --- a/src/vnote.cpp +++ b/src/vnote.cpp @@ -330,6 +330,16 @@ VNoteFile *VNote::getInternalFile(const QString &p_path) return file; } +VFile *VNote::getFile(const QString &p_path) +{ + VFile *file = getInternalFile(p_path); + if (!file) { + file = getOrphanFile(p_path, true, false); + } + + return file; +} + VDirectory *VNote::getInternalDirectory(const QString &p_path) { VDirectory *dir = NULL; diff --git a/src/vnote.h b/src/vnote.h index dd320ce5..81a9abc9 100644 --- a/src/vnote.h +++ b/src/vnote.h @@ -78,6 +78,10 @@ public: QString getNavigationLabelStyle(const QString &p_str) const; + // Given the path of a file, first try to open it as note file, + // then try to open it as orphan file. + VFile *getFile(const QString &p_path); + // Given the path of an external file, create a VOrphanFile struct. VOrphanFile *getOrphanFile(const QString &p_path, bool p_modifiable,