diff --git a/src/dialog/vfixnotebookdialog.cpp b/src/dialog/vfixnotebookdialog.cpp index 1a84d099..9e75bc41 100644 --- a/src/dialog/vfixnotebookdialog.cpp +++ b/src/dialog/vfixnotebookdialog.cpp @@ -39,6 +39,12 @@ void VFixNotebookDialog::setupUI() connect(m_browseBtn, &QPushButton::clicked, this, &VFixNotebookDialog::handleBrowseBtnClicked); + m_relativePathCB = new QCheckBox(tr("Use relative path"), this); + m_relativePathCB->setToolTip(tr("Use relative path (to VNote's executable) in configuration file")); + m_relativePathCB->setChecked(!QDir::isAbsolutePath(m_notebook->getPathInConfig())); + connect(m_relativePathCB, &QCheckBox::stateChanged, + this, &VFixNotebookDialog::handleInputChanged); + QHBoxLayout *pathLayout = new QHBoxLayout(); pathLayout->addWidget(m_pathEdit); pathLayout->addWidget(m_browseBtn); @@ -46,6 +52,7 @@ void VFixNotebookDialog::setupUI() QFormLayout *topLayout = new QFormLayout(); topLayout->addRow(tr("Notebook name:"), nameLabel); topLayout->addRow(tr("Notebook root folder:"), pathLayout); + topLayout->addRow(m_relativePathCB); // Warning label. m_warnLabel = new QLabel(this); @@ -89,7 +96,12 @@ void VFixNotebookDialog::handleBrowseBtnClicked() QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks); if (!dirPath.isEmpty()) { - m_pathEdit->setText(dirPath); + if (m_pathEdit->text() == dirPath) { + handleInputChanged(); + } else { + m_pathEdit->setText(dirPath); + } + defaultPath = VUtils::basePathFromPath(dirPath); } } @@ -103,7 +115,11 @@ void VFixNotebookDialog::handleInputChanged() QString path = m_pathEdit->text(); if (!path.isEmpty()) { - if (VConfigManager::directoryConfigExist(path)) { + if (!QDir::isAbsolutePath(path)) { + QString tmp = tr("WARNING: Please specify absolute path.") + .arg(g_config->c_warningTextStyle); + m_warnLabel->setText(tmp); + } else if (VConfigManager::directoryConfigExist(path)) { pathOk = true; } } @@ -131,6 +147,18 @@ void VFixNotebookDialog::handleInputChanged() m_warnLabel->setText(warnText); } + if (pathOk && isUseRelativePath()) { + if (!VUtils::inSameDrive(QCoreApplication::applicationDirPath(), path)) { + pathOk = false; + QString existText = tr("WARNING: Please choose a folder in the same drive as " + "%3 when relative path is enabled.") + .arg(g_config->c_warningTextStyle) + .arg(g_config->c_dataTextStyle) + .arg(QCoreApplication::applicationDirPath()); + m_warnLabel->setText(existText); + } + } + m_warnLabel->setVisible(!pathOk); QPushButton *okBtn = m_btnBox->button(QDialogButtonBox::Ok); okBtn->setEnabled(pathOk); @@ -140,5 +168,19 @@ QString VFixNotebookDialog::getPathInput() const { // absoluteFilePath() to convert the drive to upper case. // cleanPath() to remove duplicate separator, '.', and '..'. - return QDir::cleanPath(QFileInfo(m_pathEdit->text()).absoluteFilePath()); + QString ret; + if (isUseRelativePath()) { + // Use relative path in config file. + QDir appDir(QCoreApplication::applicationDirPath()); + ret = QDir::cleanPath(appDir.relativeFilePath(m_pathEdit->text())); + } else { + ret = QDir::cleanPath(QFileInfo(m_pathEdit->text()).absoluteFilePath()); + } + + return ret; +} + +bool VFixNotebookDialog::isUseRelativePath() const +{ + return m_relativePathCB->isChecked(); } diff --git a/src/dialog/vfixnotebookdialog.h b/src/dialog/vfixnotebookdialog.h index 40418032..6d348afe 100644 --- a/src/dialog/vfixnotebookdialog.h +++ b/src/dialog/vfixnotebookdialog.h @@ -8,6 +8,7 @@ class VLineEdit; class QLabel; class QPushButton; class QDialogButtonBox; +class QCheckBox; class VFixNotebookDialog : public QDialog { @@ -27,6 +28,9 @@ private slots: private: void setupUI(); + // Whether relative path will be used in config file. + bool isUseRelativePath() const; + const VNotebook *m_notebook; const QVector &m_notebooks; @@ -35,6 +39,8 @@ private: QPushButton *m_browseBtn; + QCheckBox *m_relativePathCB; + QLabel *m_warnLabel; QDialogButtonBox *m_btnBox; diff --git a/src/dialog/vnewnotebookdialog.cpp b/src/dialog/vnewnotebookdialog.cpp index 6ff575c7..0f17ad99 100644 --- a/src/dialog/vnewnotebookdialog.cpp +++ b/src/dialog/vnewnotebookdialog.cpp @@ -20,7 +20,7 @@ VNewNotebookDialog::VNewNotebookDialog(const QString &title, const QString &info setupUI(title, info); connect(m_nameEdit, &VMetaWordLineEdit::textChanged, this, &VNewNotebookDialog::handleInputChanged); - connect(pathEdit, &VLineEdit::textChanged, this, &VNewNotebookDialog::handleInputChanged); + connect(m_pathEdit, &VLineEdit::textChanged, this, &VNewNotebookDialog::handleInputChanged); connect(browseBtn, &QPushButton::clicked, this, &VNewNotebookDialog::handleBrowseBtnClicked); handleInputChanged(); @@ -42,10 +42,15 @@ void VNewNotebookDialog::setupUI(const QString &p_title, const QString &p_info) nameLabel->setBuddy(m_nameEdit); QLabel *pathLabel = new QLabel(tr("Notebook &root folder:")); - pathEdit = new VLineEdit(defaultPath); - pathLabel->setBuddy(pathEdit); + m_pathEdit = new VLineEdit(defaultPath); + pathLabel->setBuddy(m_pathEdit); browseBtn = new QPushButton(tr("&Browse")); + m_relativePathCB = new QCheckBox(tr("Use relative path")); + m_relativePathCB->setToolTip(tr("Use relative path (to VNote's executable) in configuration file")); + connect(m_relativePathCB, &QCheckBox::stateChanged, + this, &VNewNotebookDialog::handleInputChanged); + QLabel *imageFolderLabel = new QLabel(tr("&Image folder:")); m_imageFolderEdit = new VLineEdit(); imageFolderLabel->setBuddy(m_imageFolderEdit); @@ -72,12 +77,13 @@ void VNewNotebookDialog::setupUI(const QString &p_title, const QString &p_info) topLayout->addWidget(nameLabel, 0, 0); topLayout->addWidget(m_nameEdit, 0, 1, 1, 2); topLayout->addWidget(pathLabel, 1, 0); - topLayout->addWidget(pathEdit, 1, 1); + topLayout->addWidget(m_pathEdit, 1, 1); topLayout->addWidget(browseBtn, 1, 2); - topLayout->addWidget(imageFolderLabel, 2, 0); - topLayout->addWidget(m_imageFolderEdit, 2, 1); - topLayout->addWidget(attachmentFolderLabel, 3, 0); - topLayout->addWidget(m_attachmentFolderEdit, 3, 1); + topLayout->addWidget(m_relativePathCB, 2, 1); + topLayout->addWidget(imageFolderLabel, 3, 0); + topLayout->addWidget(m_imageFolderEdit, 3, 1); + topLayout->addWidget(attachmentFolderLabel, 4, 0); + topLayout->addWidget(m_attachmentFolderEdit, 4, 1); // Warning label. m_warnLabel = new QLabel(); @@ -91,7 +97,7 @@ void VNewNotebookDialog::setupUI(const QString &p_title, const QString &p_info) QPushButton *okBtn = m_btnBox->button(QDialogButtonBox::Ok); okBtn->setProperty("SpecialBtn", true); - pathEdit->setMinimumWidth(okBtn->sizeHint().width() * 3); + m_pathEdit->setMinimumWidth(okBtn->sizeHint().width() * 3); QVBoxLayout *mainLayout = new QVBoxLayout(this); if (infoLabel) { @@ -116,7 +122,16 @@ QString VNewNotebookDialog::getPathInput() const { // absoluteFilePath() to convert the drive to upper case. // cleanPath() to remove duplicate separator, '.', and '..'. - return QDir::cleanPath(QFileInfo(pathEdit->text()).absoluteFilePath()); + QString ret; + if (isUseRelativePath()) { + // Use relative path in config file. + QDir appDir(QCoreApplication::applicationDirPath()); + ret = QDir::cleanPath(appDir.relativeFilePath(m_pathEdit->text())); + } else { + ret = QDir::cleanPath(QFileInfo(m_pathEdit->text()).absoluteFilePath()); + } + + return ret; } QString VNewNotebookDialog::getImageFolder() const @@ -140,6 +155,7 @@ QString VNewNotebookDialog::getAttachmentFolder() const void VNewNotebookDialog::handleBrowseBtnClicked() { static QString defaultPath; + if (defaultPath.isEmpty()) { defaultPath = g_config->getVnoteNotebookFolderPath(); if (!QFileInfo::exists(defaultPath)) { @@ -154,7 +170,12 @@ void VNewNotebookDialog::handleBrowseBtnClicked() if (!dirPath.isEmpty()) { m_manualPath = true; - pathEdit->setText(dirPath); + if (m_pathEdit->text() == dirPath) { + handleInputChanged(); + } else { + m_pathEdit->setText(dirPath); + } + defaultPath = VUtils::basePathFromPath(dirPath); } } @@ -184,7 +205,7 @@ void VNewNotebookDialog::handleInputChanged() bool showWarnLabel = false; // User has input some texts. - if (pathEdit->isModified()) { + if (m_pathEdit->isModified()) { m_manualPath = true; } @@ -196,9 +217,14 @@ void VNewNotebookDialog::handleInputChanged() return; } - QString path = pathEdit->text(); + QString path = m_pathEdit->text(); if (!path.isEmpty()) { - if (QFileInfo::exists(path)) { + if (!QDir::isAbsolutePath(path)) { + showWarnLabel = true; + QString tmp = tr("WARNING: Please specify absolute path.") + .arg(g_config->c_warningTextStyle); + m_warnLabel->setText(tmp); + } else if (QFileInfo::exists(path)) { QDir dir(path); QStringList files = dir.entryList(QDir::NoDotAndDotDot | QDir::AllEntries | QDir::Hidden); if (files.isEmpty()) { @@ -206,6 +232,13 @@ void VNewNotebookDialog::handleInputChanged() } else { // Folder is not empty. configExist = VConfigManager::directoryConfigExist(path); + if (configExist) { + pathOk = true; + m_warnLabel->setText(infoText); + } else { + m_warnLabel->setText(warnText); + } + showWarnLabel = true; } } else { @@ -213,13 +246,6 @@ void VNewNotebookDialog::handleInputChanged() } } - if (configExist) { - pathOk = true; - m_warnLabel->setText(infoText); - } else { - m_warnLabel->setText(warnText); - } - // Try to validate if this is a legal path on the OS. if (pathOk) { pathOk = VUtils::checkPathLegal(path); @@ -253,6 +279,19 @@ void VNewNotebookDialog::handleInputChanged() } } + if (pathOk && isUseRelativePath()) { + if (!VUtils::inSameDrive(QCoreApplication::applicationDirPath(), path)) { + pathOk = false; + showWarnLabel = true; + QString existText = tr("WARNING: Please choose a folder in the same drive as " + "%3 when relative path is enabled.") + .arg(g_config->c_warningTextStyle) + .arg(g_config->c_dataTextStyle) + .arg(QCoreApplication::applicationDirPath()); + m_warnLabel->setText(existText); + } + } + QString name = m_nameEdit->getEvaluatedText(); bool nameOk = !name.isEmpty(); if (pathOk && nameOk) { @@ -310,7 +349,7 @@ bool VNewNotebookDialog::autoComplete() } // Set the name according to user-chosen path. - QString pathText = pathEdit->text(); + QString pathText = m_pathEdit->text(); if (!pathText.isEmpty()) { QString autoName = VUtils::directoryNameFromPath(pathText); if (autoName != nameText) { @@ -323,7 +362,7 @@ bool VNewNotebookDialog::autoComplete() } QString vnoteFolder = g_config->getVnoteNotebookFolderPath(); - QString pathText = pathEdit->text(); + QString pathText = m_pathEdit->text(); if (!pathText.isEmpty() && !VUtils::equalPath(vnoteFolder, VUtils::basePathFromPath(pathText))) { return false; @@ -344,10 +383,15 @@ bool VNewNotebookDialog::autoComplete() // Use the name as the folder name under vnoteFolder. QString autoPath = QDir::cleanPath(QDir(vnoteFolder).filePath(nameText)); if (autoPath != pathText) { - pathEdit->setText(autoPath); + m_pathEdit->setText(autoPath); ret = true; } } return ret; } + +bool VNewNotebookDialog::isUseRelativePath() const +{ + return m_relativePathCB->isChecked(); +} diff --git a/src/dialog/vnewnotebookdialog.h b/src/dialog/vnewnotebookdialog.h index 79143f89..a31e49c6 100644 --- a/src/dialog/vnewnotebookdialog.h +++ b/src/dialog/vnewnotebookdialog.h @@ -10,6 +10,7 @@ class VMetaWordLineEdit; class QPushButton; class QDialogButtonBox; class VNotebook; +class QCheckBox; class VNewNotebookDialog : public QDialog { @@ -53,9 +54,13 @@ private: // Returns true if name or path is modified. bool autoComplete(); + // Whether relative path will be used in config file. + bool isUseRelativePath() const; + VMetaWordLineEdit *m_nameEdit; - VLineEdit *pathEdit; + VLineEdit *m_pathEdit; QPushButton *browseBtn; + QCheckBox *m_relativePathCB; QLabel *m_warnLabel; VLineEdit *m_imageFolderEdit; VLineEdit *m_attachmentFolderEdit; diff --git a/src/utils/vutils.cpp b/src/utils/vutils.cpp index 80f585f7..fa1ee986 100644 --- a/src/utils/vutils.cpp +++ b/src/utils/vutils.cpp @@ -1525,3 +1525,20 @@ QFormLayout *VUtils::getFormLayout() return layout; } + +bool VUtils::inSameDrive(const QString &p_a, const QString &p_b) +{ +#if defined(Q_OS_WIN) + QChar sep(':'); + int ai = p_a.indexOf(sep); + int bi = p_b.indexOf(sep); + + if (ai == -1 || bi == -1) { + return false; + } + + return p_a.left(ai).toLower() == p_b.left(bi).toLower(); +#else + return true; +#endif +} diff --git a/src/utils/vutils.h b/src/utils/vutils.h index 70ef6764..31fbbbd5 100644 --- a/src/utils/vutils.h +++ b/src/utils/vutils.h @@ -331,6 +331,8 @@ public: // Return QFormLayout. static QFormLayout *getFormLayout(); + static bool inSameDrive(const QString &p_a, const QString &p_b); + // Regular expression for image link. // ![image title]( http://github.com/tamlok/vnote.jpg "alt text" =200x100) // Captured texts (need to be trimmed): diff --git a/src/vconfigmanager.cpp b/src/vconfigmanager.cpp index ab5709ea..7a5f12b5 100644 --- a/src/vconfigmanager.cpp +++ b/src/vconfigmanager.cpp @@ -396,7 +396,7 @@ void VConfigManager::writeNotebookToSettings(QSettings *p_settings, p_settings->setArrayIndex(i); const VNotebook ¬ebook = *p_notebooks[i]; p_settings->setValue("name", notebook.getName()); - p_settings->setValue("path", notebook.getPath()); + p_settings->setValue("path", notebook.getPathInConfig()); } p_settings->endArray(); diff --git a/src/vnotebook.cpp b/src/vnotebook.cpp index c7b4a1be..6cceddca 100644 --- a/src/vnotebook.cpp +++ b/src/vnotebook.cpp @@ -1,6 +1,8 @@ #include "vnotebook.h" #include #include +#include + #include "vdirectory.h" #include "utils/vutils.h" #include "vconfigmanager.h" @@ -11,7 +13,7 @@ extern VConfigManager *g_config; VNotebook::VNotebook(const QString &name, const QString &path, QObject *parent) : QObject(parent), m_name(name), m_valid(false) { - m_path = QDir::cleanPath(path); + setPath(path); m_recycleBinFolder = g_config->getRecycleBinFolder(); m_rootDir = new VDirectory(this, NULL, @@ -24,6 +26,16 @@ VNotebook::~VNotebook() delete m_rootDir; } +void VNotebook::setPath(const QString &p_path) +{ + m_pathInConfig = QDir::cleanPath(p_path); + if (QDir::isAbsolutePath(m_pathInConfig)) { + m_path = m_pathInConfig; + } else { + m_path = QDir::cleanPath(QDir(QCoreApplication::applicationDirPath()).absoluteFilePath(m_pathInConfig)); + } +} + bool VNotebook::readConfigNotebook() { QJsonObject configJson = VConfigManager::readDirectoryConfig(m_path); @@ -115,16 +127,6 @@ bool VNotebook::writeConfigNotebook() const return VConfigManager::writeDirectoryConfig(m_path, configJson); } -const QString &VNotebook::getName() const -{ - return m_name; -} - -const QString &VNotebook::getPath() const -{ - return m_path; -} - void VNotebook::close() { m_rootDir->close(); @@ -171,13 +173,13 @@ VNotebook *VNotebook::createNotebook(const QString &p_name, nb->setAttachmentFolder(attachmentFolder); // Check if there alread exists a config file. - if (p_import && VConfigManager::directoryConfigExist(p_path)) { + if (p_import && VConfigManager::directoryConfigExist(nb->getPath())) { qDebug() << "import existing notebook"; nb->readConfigNotebook(); return nb; } - VUtils::makePath(p_path); + VUtils::makePath(nb->getPath()); if (!nb->writeToConfig()) { delete nb; @@ -397,7 +399,7 @@ void VNotebook::updatePath(const QString &p_path) { Q_ASSERT(!isOpened()); m_valid = false; - m_path = QDir::cleanPath(p_path); + setPath(p_path); delete m_rootDir; m_rootDir = new VDirectory(this, NULL, diff --git a/src/vnotebook.h b/src/vnotebook.h index 9fd5359c..55e8991c 100644 --- a/src/vnotebook.h +++ b/src/vnotebook.h @@ -47,6 +47,8 @@ public: const QString &getPath() const; + const QString &getPathInConfig() const; + void updatePath(const QString &p_path); VDirectory *getRootDir() const; @@ -107,9 +109,16 @@ private: // Write current instance to config file. bool writeToConfig() const; + void setPath(const QString &p_path); + QString m_name; + QString m_path; + // Path in vnote.ini. + // May be relative path to VNote's executable. + QString m_pathInConfig; + // Folder name to store images. // If not empty, VNote will store images in this folder within the same directory of the note. // Otherwise, VNote will use the global configured folder. @@ -146,4 +155,19 @@ inline bool VNotebook::isValid() const return m_valid; } +inline const QString &VNotebook::getPath() const +{ + return m_path; +} + +inline const QString &VNotebook::getPathInConfig() const +{ + return m_pathInConfig; +} + +inline const QString &VNotebook::getName() const +{ + return m_name; +} + #endif // VNOTEBOOK_H diff --git a/src/vnotebookselector.cpp b/src/vnotebookselector.cpp index db15a885..129013a0 100644 --- a/src/vnotebookselector.cpp +++ b/src/vnotebookselector.cpp @@ -320,8 +320,11 @@ void VNotebookSelector::createNotebook(const QString &p_name, const QString &p_imageFolder, const QString &p_attachmentFolder) { - VNotebook *nb = VNotebook::createNotebook(p_name, p_path, p_import, - p_imageFolder, p_attachmentFolder, + VNotebook *nb = VNotebook::createNotebook(p_name, + p_path, + p_import, + p_imageFolder, + p_attachmentFolder, g_vnote); if (!nb) { VUtils::showMessage(QMessageBox::Warning, tr("Warning"), diff --git a/src/vnotebookselector.h b/src/vnotebookselector.h index cabc7403..6f3cf889 100644 --- a/src/vnotebookselector.h +++ b/src/vnotebookselector.h @@ -77,8 +77,10 @@ private: // If @p_import is true, we will use the existing config file. // If @p_imageFolder is empty, we will use the global one. // If @p_attachmentFolder is empty, we will use the global one. - void createNotebook(const QString &p_name, const QString &p_path, - bool p_import, const QString &p_imageFolder, + void createNotebook(const QString &p_name, + const QString &p_path, + bool p_import, + const QString &p_imageFolder, const QString &p_attachmentFolder); void deleteNotebook(VNotebook *p_notebook, bool p_deleteFiles);