mirror of
https://gitee.com/vnotex/vnote.git
synced 2025-07-05 13:59:52 +08:00
support custom startup pages
- Support continuing where user left off on startup; - Support recovering the edit/read mode and the anchor position; - Support opening user-specified files on startup; - Add config startup_page_type, startup_pages, and last_opened_files;
This commit is contained in:
parent
fa870f132e
commit
598e8144bb
@ -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);
|
||||
|
||||
|
@ -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<void(QComboBox::*)(int)>(&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)
|
||||
{
|
||||
|
@ -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<QString> c_availableLangs;
|
||||
};
|
||||
|
||||
|
22
src/main.cpp
22
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();
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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 \
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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.
|
||||
// 
|
||||
// Captured texts (need to be trimmed):
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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<VFileSessionInfo> VConfigManager::getLastOpenedFiles()
|
||||
{
|
||||
QVector<VFileSessionInfo> 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<VFileSessionInfo> &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";
|
||||
|
||||
}
|
||||
|
@ -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<VFileSessionInfo> getLastOpenedFiles();
|
||||
|
||||
// Write last opened files to [last_opened_files] of session.ini.
|
||||
void setLastOpenedFiles(const QVector<VFileSessionInfo> &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
|
||||
|
@ -1,6 +1,8 @@
|
||||
#ifndef VCONSTANTS_H
|
||||
#define VCONSTANTS_H
|
||||
|
||||
#include <QString>
|
||||
|
||||
// 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
|
||||
|
@ -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();
|
||||
|
||||
|
@ -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<QPair<int, int> > 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<VEditTabInfo> VEditArea::getAllTabsInfo() const
|
||||
{
|
||||
QVector<VEditTabInfo> 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<VFileSessionInfo> &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;
|
||||
}
|
||||
|
@ -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<VEditTabInfo> 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<VFileSessionInfo> &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);
|
||||
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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<VFile> 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();
|
||||
|
||||
|
@ -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
|
||||
|
@ -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<VEditTabInfo> VEditWindow::getAllTabsInfo() const
|
||||
{
|
||||
int nrTab = count();
|
||||
|
||||
QVector<VEditTabInfo> 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();
|
||||
|
@ -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<VEditTabInfo> 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.
|
||||
|
@ -374,7 +374,7 @@ void VFileList::newFile()
|
||||
|
||||
// Move cursor down if content has been inserted.
|
||||
if (contentInserted) {
|
||||
const VMdTab *tab = dynamic_cast<VMdTab *>(editArea->currentEditTab());
|
||||
const VMdTab *tab = dynamic_cast<VMdTab *>(editArea->getCurrentTab());
|
||||
if (tab) {
|
||||
VMdEdit *edit = dynamic_cast<VMdEdit *>(tab->getEditor());
|
||||
if (edit && edit->getFile() == file) {
|
||||
|
74
src/vfilesessioninfo.cpp
Normal file
74
src/vfilesessioninfo.cpp
Normal file
@ -0,0 +1,74 @@
|
||||
#include "vfilesessioninfo.h"
|
||||
|
||||
#include <QSettings>
|
||||
|
||||
#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);
|
||||
}
|
57
src/vfilesessioninfo.h
Normal file
57
src/vfilesessioninfo.h
Normal file
@ -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
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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<VFileSessionInfo> fileInfos;
|
||||
QVector<VEditTabInfo> 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<VFileSessionInfo> 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;
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -539,7 +539,7 @@ void VMdEdit::updateOutline(const QVector<VElementRegion> &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();
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
|
@ -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<VMdEdit *>(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<VMdEdit *>(getEditor())->scrollToHeader(p_anchor);
|
||||
dynamic_cast<VMdEdit *>(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;
|
||||
}
|
||||
|
25
src/vmdtab.h
25
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;
|
||||
|
@ -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;
|
||||
|
@ -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,
|
||||
|
Loading…
x
Reference in New Issue
Block a user