Browse Source

refactor logics about adding notebook

Le Tan 8 years ago
parent
commit
707814909a

+ 169 - 56
src/dialog/vnewnotebookdialog.cpp

@@ -3,23 +3,26 @@
 #include "vnewnotebookdialog.h"
 #include "vconfigmanager.h"
 #include "utils/vutils.h"
+#include "vnotebook.h"
 
 extern VConfigManager vconfig;
 
 VNewNotebookDialog::VNewNotebookDialog(const QString &title, const QString &info,
                                        const QString &defaultName, const QString &defaultPath,
+                                       const QVector<VNotebook *> &p_notebooks,
                                        QWidget *parent)
     : QDialog(parent), infoLabel(NULL),
-      title(title), info(info), defaultName(defaultName), defaultPath(defaultPath)
+      title(title), info(info), defaultName(defaultName), defaultPath(defaultPath),
+      m_importNotebook(false), m_manualPath(false), m_manualName(false),
+      m_notebooks(p_notebooks)
 {
     setupUI();
 
-    connect(nameEdit, &QLineEdit::textChanged, this, &VNewNotebookDialog::enableOkButton);
-    connect(pathEdit, &QLineEdit::textChanged, this, &VNewNotebookDialog::handlePathChanged);
+    connect(nameEdit, &QLineEdit::textChanged, this, &VNewNotebookDialog::handleInputChanged);
+    connect(pathEdit, &QLineEdit::textChanged, this, &VNewNotebookDialog::handleInputChanged);
     connect(browseBtn, &QPushButton::clicked, this, &VNewNotebookDialog::handleBrowseBtnClicked);
 
-    enableOkButton();
-    checkRootFolder(pathEdit->text());
+    handleInputChanged();
 }
 
 void VNewNotebookDialog::setupUI()
@@ -38,11 +41,6 @@ void VNewNotebookDialog::setupUI()
     pathLabel->setBuddy(pathEdit);
     browseBtn = new QPushButton(tr("&Browse"));
 
-    importCheck = new QCheckBox(tr("Import existing notebook"));
-    importCheck->setToolTip(tr("When checked, VNote will read the configuration file to import an existing notebook"));
-    connect(importCheck, &QCheckBox::stateChanged,
-            this, &VNewNotebookDialog::importCheckChanged);
-
     QLabel *imageFolderLabel = new QLabel(tr("&Image folder:"));
     m_imageFolderEdit = new QLineEdit();
     m_imageFolderEdit->setPlaceholderText(tr("Use global configuration (%1)")
@@ -61,9 +59,13 @@ void VNewNotebookDialog::setupUI()
     topLayout->addWidget(pathLabel, 1, 0);
     topLayout->addWidget(pathEdit, 1, 1);
     topLayout->addWidget(browseBtn, 1, 2);
-    topLayout->addWidget(importCheck, 2, 1);
-    topLayout->addWidget(imageFolderLabel, 3, 0);
-    topLayout->addWidget(m_imageFolderEdit, 3, 1);
+    topLayout->addWidget(imageFolderLabel, 2, 0);
+    topLayout->addWidget(m_imageFolderEdit, 2, 1);
+
+    // Warning label.
+    m_warnLabel = new QLabel();
+    m_warnLabel->setWordWrap(true);
+    m_warnLabel->hide();
 
     // Ok is the default button.
     m_btnBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
@@ -73,14 +75,6 @@ void VNewNotebookDialog::setupUI()
     QPushButton *okBtn = m_btnBox->button(QDialogButtonBox::Ok);
     pathEdit->setMinimumWidth(okBtn->sizeHint().width() * 3);
 
-    // Warning label.
-    m_warnLabel = new QLabel(tr("<span style=\"%1\">WARNING</span>: The folder you choose is NOT empty! "
-                                "It is highly recommended to use an EMPTY and EXCLUSIVE folder for a notebook. "
-                                "Ignore this warning if you do want to import an existing VNote notebook folder.")
-                               .arg(vconfig.c_warningTextStyle));
-    m_warnLabel->setWordWrap(true);
-    m_warnLabel->hide();
-
     QVBoxLayout *mainLayout = new QVBoxLayout(this);
     if (infoLabel) {
         mainLayout->addWidget(infoLabel);
@@ -88,20 +82,13 @@ void VNewNotebookDialog::setupUI()
     mainLayout->addLayout(topLayout);
     mainLayout->addWidget(m_warnLabel);
     mainLayout->addWidget(m_btnBox);
+
     // Will set the parent of above widgets properly.
     setLayout(mainLayout);
     mainLayout->setSizeConstraint(QLayout::SetFixedSize);
     setWindowTitle(title);
 }
 
-void VNewNotebookDialog::enableOkButton()
-{
-    QPushButton *okBtn = m_btnBox->button(QDialogButtonBox::Ok);
-    okBtn->setEnabled(!pathEdit->text().isEmpty()
-                      && !nameEdit->text().isEmpty()
-                      && QDir(pathEdit->text()).exists());
-}
-
 QString VNewNotebookDialog::getNameInput() const
 {
     return nameEdit->text();
@@ -109,27 +96,42 @@ QString VNewNotebookDialog::getNameInput() const
 
 QString VNewNotebookDialog::getPathInput() const
 {
-    return pathEdit->text();
+    return QDir::cleanPath(pathEdit->text());
 }
 
 QString VNewNotebookDialog::getImageFolder() const
 {
-    return m_imageFolderEdit->text();
+    if (m_imageFolderEdit->isEnabled()) {
+        return m_imageFolderEdit->text();
+    } else {
+        return QString();
+    }
 }
 
 void VNewNotebookDialog::handleBrowseBtnClicked()
 {
+    static QString defaultPath;
+    if (defaultPath.isEmpty()) {
+        defaultPath = vconfig.getVnoteNotebookFolderPath();
+        if (!QFileInfo::exists(defaultPath)) {
+            defaultPath = QDir::homePath();
+        }
+    }
+
     QString dirPath = QFileDialog::getExistingDirectory(this, tr("Select Root Folder Of The Notebook"),
-                                                        QDir::homePath(), QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
+                                                        defaultPath,
+                                                        QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
 
     if (!dirPath.isEmpty()) {
+        m_manualPath = true;
         pathEdit->setText(dirPath);
+        defaultPath = VUtils::basePathFromPath(dirPath);
     }
 }
 
-bool VNewNotebookDialog::getImportCheck() const
+bool VNewNotebookDialog::isImportExistingNotebook() const
 {
-    return importCheck->isChecked();
+    return m_importNotebook;
 }
 
 void VNewNotebookDialog::showEvent(QShowEvent *event)
@@ -138,38 +140,149 @@ void VNewNotebookDialog::showEvent(QShowEvent *event)
     QDialog::showEvent(event);
 }
 
-void VNewNotebookDialog::handlePathChanged(const QString &p_text)
+void VNewNotebookDialog::handleInputChanged()
 {
-    enableOkButton();
-    checkRootFolder(p_text);
-}
+    QString warnText = tr("<span style=\"%1\">WARNING</span>: The folder chosen is NOT empty! "
+                          "It is highly recommended to use an EMPTY and EXCLUSIVE folder for a new notebook.")
+                          .arg(vconfig.c_warningTextStyle);
+    QString infoText = tr("<span style=\"%1\">INFO</span>: The folder chosen seems to be a root "
+                          "folder of a notebook created by VNote before. "
+                          "VNote will try to import it by reading the configuration file.")
+                          .arg("font-weight:bold;");
+    bool pathOk = false;
+    bool configExist = false;
+    bool showWarnLabel = false;
 
-void VNewNotebookDialog::importCheckChanged(int p_state)
-{
-    // If import existing notebook, disable setting new configs.
-    bool checked = p_state == Qt::Checked;
+    // User has input some texts.
+    if (pathEdit->isModified()) {
+        m_manualPath = true;
+    }
+
+    if (nameEdit->isModified()) {
+        m_manualName = true;
+    }
 
-    m_imageFolderEdit->setEnabled(!checked);
+    if (autoComplete()) {
+        return;
+    }
+
+    QString path = pathEdit->text();
+    if (!path.isEmpty()) {
+        if (QFileInfo::exists(path)) {
+            QDir dir(path);
+            QStringList files = dir.entryList(QDir::NoDotAndDotDot | QDir::AllEntries | QDir::Hidden);
+            if (!files.isEmpty()) {
+                // Folder is not empty.
+                configExist = VConfigManager::directoryConfigExist(path);
+                showWarnLabel = true;
+            }
+        } else {
+            pathOk = true;
+        }
+    }
+
+    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);
+        if (!pathOk) {
+            showWarnLabel = true;
+            QString tmp = tr("<span style=\"%1\">WARNING</span>: The path seems to be illegal. "
+                             "Please choose another one.")
+                            .arg(vconfig.c_warningTextStyle);
+            m_warnLabel->setText(tmp);
+        }
+    }
+
+    if (pathOk) {
+        // Check if this path has been in VNote.
+        int idx = -1;
+        path = QDir::cleanPath(path);
+        for (idx = 0; idx < m_notebooks.size(); ++idx) {
+            if (QDir::cleanPath(m_notebooks[idx]->getPath()) == path) {
+                break;
+            }
+        }
+
+        if (idx < m_notebooks.size()) {
+            pathOk = false;
+            showWarnLabel = true;
+            QString existText = tr("<span style=\"%1\">WARNING</span>: The folder chosen has already been a root folder "
+                                   "of existing notebook <span style=\"%2\">%3</span> in VNote.")
+                                   .arg(vconfig.c_warningTextStyle)
+                                   .arg(vconfig.c_dataTextStyle)
+                                   .arg(m_notebooks[idx]->getName());
+            m_warnLabel->setText(existText);
+        }
+    }
+
+    QString name = nameEdit->text();
+    bool nameOk = !name.isEmpty();
+    if (pathOk && nameOk) {
+        // Check if the name conflicts with existing notebook name.
+        int idx = -1;
+        for (idx = 0; idx < m_notebooks.size(); ++idx) {
+            if (m_notebooks[idx]->getName() == name) {
+                break;
+            }
+        }
+
+        if (idx < m_notebooks.size()) {
+            nameOk = false;
+            showWarnLabel = true;
+            QString nameConflictText = tr("<span style=\"%1\">WARNING</span>: Name already exists. "
+                                          "Please choose another name.")
+                                          .arg(vconfig.c_warningTextStyle);
+            m_warnLabel->setText(nameConflictText);
+        }
+    }
+
+    m_warnLabel->setVisible(showWarnLabel);
+    m_importNotebook = configExist;
+    m_imageFolderEdit->setEnabled(!m_importNotebook);
+
+    QPushButton *okBtn = m_btnBox->button(QDialogButtonBox::Ok);
+    okBtn->setEnabled(nameOk && pathOk);
 }
 
-void VNewNotebookDialog::checkRootFolder(const QString &p_path)
+bool VNewNotebookDialog::autoComplete()
 {
-    bool existConfig = false;
+    if (m_manualPath) {
+        return false;
+    }
 
-    if (!p_path.isEmpty()) {
-        QDir dir(p_path);
-        QStringList files = dir.entryList(QDir::NoDotAndDotDot | QDir::AllEntries | QDir::Hidden);
-        if (!files.isEmpty()) {
-            m_warnLabel->show();
-        } else {
-            m_warnLabel->hide();
+    QString vnoteFolder = vconfig.getVnoteNotebookFolderPath();
+    QString pathText = pathEdit->text();
+    if (!pathText.isEmpty() && vnoteFolder != VUtils::basePathFromPath(pathText)) {
+        return false;
+    }
+
+    bool ret = false;
+    QString nameText = nameEdit->text();
+    if (nameText.isEmpty()) {
+        if (m_manualName) {
+            return false;
         }
 
-        existConfig = VConfigManager::directoryConfigExist(p_path);
+        // Get a folder name under vnoteFolder and set it as the name of the notebook.
+        QString name = "vnotebook";
+        name = VUtils::getFileNameWithSequence(vnoteFolder, name);
+        nameEdit->setText(name);
+        ret = true;
     } else {
-        m_warnLabel->hide();
+        // Use the name as the folder name under vnoteFolder.
+        QString autoPath = QDir::cleanPath(QDir(vnoteFolder).filePath(nameText));
+        if (autoPath != pathText) {
+            pathEdit->setText(autoPath);
+            ret = true;
+        }
     }
 
-    importCheck->setChecked(existConfig);
-    importCheck->setEnabled(existConfig);
+    return ret;
 }

+ 33 - 7
src/dialog/vnewnotebookdialog.h

@@ -2,43 +2,57 @@
 #define VNEWNOTEBOOKDIALOG_H
 
 #include <QDialog>
+#include <QVector>
 
 class QLabel;
 class QLineEdit;
 class QPushButton;
 class QString;
-class QCheckBox;
 class QDialogButtonBox;
+class VNotebook;
 
 class VNewNotebookDialog : public QDialog
 {
     Q_OBJECT
 public:
     VNewNotebookDialog(const QString &title, const QString &info, const QString &defaultName,
-                       const QString &defaultPath, QWidget *parent = 0);
+                       const QString &defaultPath, const QVector<VNotebook *> &p_notebooks,
+                       QWidget *parent = 0);
+
     QString getNameInput() const;
+
     QString getPathInput() const;
-    bool getImportCheck() const;
+
+    // Whether import existing notebook by reading the config file.
+    bool isImportExistingNotebook() const;
+
+    // Get the custom image folder for this notebook.
+    // Empty string indicates using global config.
     QString getImageFolder() const;
 
 private slots:
-    void enableOkButton();
     void handleBrowseBtnClicked();
-    void handlePathChanged(const QString &p_text);
-    void importCheckChanged(int p_state);
+
+    // Handle the change of the name and path input.
+    void handleInputChanged();
 
 protected:
     void showEvent(QShowEvent *event) Q_DECL_OVERRIDE;
 
 private:
     void setupUI();
+
+    // Should be called before enableOkButton() when path changed.
     void checkRootFolder(const QString &p_path);
 
+    // Try to figure out name and path.
+    // Returns true if name or path is modified.
+    bool autoComplete();
+
     QLabel *infoLabel;
     QLabel *nameLabel;
     QLineEdit *nameEdit;
     QLineEdit *pathEdit;
-    QCheckBox *importCheck;
     QPushButton *browseBtn;
     QLabel *m_warnLabel;
     QLineEdit *m_imageFolderEdit;
@@ -48,6 +62,18 @@ private:
     QString info;
     QString defaultName;
     QString defaultPath;
+
+    // Whether import existing notebook config file.
+    bool m_importNotebook;
+
+    // True if user has change the content of the path edit.
+    bool m_manualPath;
+
+    // True if user has change the content of the name edit.
+    bool m_manualName;
+
+    // All existing notebooks in VNote.
+    const QVector<VNotebook *> &m_notebooks;
 };
 
 #endif // VNEWNOTEBOOKDIALOG_H

+ 44 - 7
src/dialog/vnotebookinfodialog.cpp

@@ -6,16 +6,20 @@
 
 extern VConfigManager vconfig;
 
-VNotebookInfoDialog::VNotebookInfoDialog(const QString &p_title, const QString &p_info,
-                                         const VNotebook *p_notebook, QWidget *p_parent)
-    : QDialog(p_parent), m_notebook(p_notebook), m_infoLabel(NULL)
+VNotebookInfoDialog::VNotebookInfoDialog(const QString &p_title,
+                                         const QString &p_info,
+                                         const VNotebook *p_notebook,
+                                         const QVector<VNotebook *> &p_notebooks,
+                                         QWidget *p_parent)
+    : QDialog(p_parent), m_notebook(p_notebook), m_infoLabel(NULL),
+      m_notebooks(p_notebooks)
 {
     setupUI(p_title, p_info);
 
     connect(m_nameEdit, &QLineEdit::textChanged,
-            this, &VNotebookInfoDialog::enableOkButton);
+            this, &VNotebookInfoDialog::handleInputChanged);
 
-    enableOkButton();
+    handleInputChanged();
 }
 
 void VNotebookInfoDialog::setupUI(const QString &p_title, const QString &p_info)
@@ -46,10 +50,16 @@ void VNotebookInfoDialog::setupUI(const QString &p_title, const QString &p_info)
     QValidator *validator = new QRegExpValidator(QRegExp(VUtils::c_fileNameRegExp), m_imageFolderEdit);
     m_imageFolderEdit->setValidator(validator);
 
+    // Warning label.
+    m_warnLabel = new QLabel();
+    m_warnLabel->setWordWrap(true);
+    m_warnLabel->hide();
+
     QFormLayout *topLayout = new QFormLayout();
     topLayout->addRow(nameLabel, m_nameEdit);
     topLayout->addRow(pathLabel, m_pathEdit);
     topLayout->addRow(imageFolderLabel, m_imageFolderEdit);
+    topLayout->addRow(m_warnLabel);
 
     // Ok is the default button.
     m_btnBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel);
@@ -63,17 +73,44 @@ void VNotebookInfoDialog::setupUI(const QString &p_title, const QString &p_info)
     if (m_infoLabel) {
         mainLayout->addWidget(m_infoLabel);
     }
+
     mainLayout->addLayout(topLayout);
     mainLayout->addWidget(m_btnBox);
+
     setLayout(mainLayout);
     mainLayout->setSizeConstraint(QLayout::SetFixedSize);
     setWindowTitle(p_title);
 }
 
-void VNotebookInfoDialog::enableOkButton()
+void VNotebookInfoDialog::handleInputChanged()
 {
+    QString name = m_nameEdit->text();
+    bool nameOk = !name.isEmpty();
+    bool showWarnLabel = false;
+
+    if (nameOk && name != m_notebook->getName()) {
+        // Check if the name conflicts with existing notebook name.
+        int idx = -1;
+        for (idx = 0; idx < m_notebooks.size(); ++idx) {
+            if (m_notebooks[idx]->getName() == name) {
+                break;
+            }
+        }
+
+        if (idx < m_notebooks.size()) {
+            nameOk = false;
+            showWarnLabel = true;
+            QString nameConflictText = tr("<span style=\"%1\">WARNING</span>: Name already exists. "
+                                          "Please choose another name.")
+                                          .arg(vconfig.c_warningTextStyle);
+            m_warnLabel->setText(nameConflictText);
+        }
+    }
+
+    m_warnLabel->setVisible(showWarnLabel);
+
     QPushButton *okBtn = m_btnBox->button(QDialogButtonBox::Ok);
-    okBtn->setEnabled(!m_nameEdit->text().isEmpty());
+    okBtn->setEnabled(nameOk);
 }
 
 QString VNotebookInfoDialog::getName() const

+ 13 - 3
src/dialog/vnotebookinfodialog.h

@@ -2,6 +2,7 @@
 #define VNOTEBOOKINFODIALOG_H
 
 #include <QDialog>
+#include <QVector>
 
 class QLabel;
 class QLineEdit;
@@ -13,14 +14,21 @@ class VNotebookInfoDialog : public QDialog
 {
     Q_OBJECT
 public:
-    VNotebookInfoDialog(const QString &p_title, const QString &p_info,
-                        const VNotebook *p_notebook, QWidget *p_parent = 0);
+    VNotebookInfoDialog(const QString &p_title,
+                        const QString &p_info,
+                        const VNotebook *p_notebook,
+                        const QVector<VNotebook *> &p_notebooks,
+                        QWidget *p_parent = 0);
 
     QString getName() const;
+
+    // Get the custom image folder for this notebook.
+    // Empty string indicates using global config.
     QString getImageFolder() const;
 
 private slots:
-    void enableOkButton();
+    // Handle the change of the name and path input.
+    void handleInputChanged();
 
 protected:
     void showEvent(QShowEvent *p_event) Q_DECL_OVERRIDE;
@@ -34,7 +42,9 @@ private:
     QLineEdit *m_nameEdit;
     QLineEdit *m_pathEdit;
     QLineEdit *m_imageFolderEdit;
+    QLabel *m_warnLabel;
     QDialogButtonBox *m_btnBox;
+    const QVector<VNotebook *> &m_notebooks;
 };
 
 #endif // VNOTEBOOKINFODIALOG_H

+ 63 - 0
src/utils/vutils.cpp

@@ -17,6 +17,9 @@
 #include <QLocale>
 #include <QPushButton>
 #include <QElapsedTimer>
+#include <QValidator>
+#include <QRegExpValidator>
+#include <QRegExp>
 
 #include "vfile.h"
 #include "vnote.h"
@@ -552,3 +555,63 @@ QString VUtils::generateHtmlTemplate(MarkdownConverterType p_conType, bool p_exp
 
     return htmlTemplate;
 }
+
+QString VUtils::getFileNameWithSequence(const QString &p_directory,
+                                        const QString &p_baseFileName)
+{
+    QDir dir(p_directory);
+    if (!dir.exists() || !dir.exists(p_baseFileName)) {
+        return p_baseFileName;
+    }
+
+    // Append a sequence.
+    QFileInfo fi(p_baseFileName);
+    QString baseName = fi.baseName();
+    QString suffix = fi.completeSuffix();
+    int seq = 1;
+    QString fileName;
+    do {
+        fileName = QString("%1_%2").arg(baseName).arg(QString::number(seq++), 3, '0');
+        if (!suffix.isEmpty()) {
+            fileName = fileName + "." + suffix;
+        }
+    } while (dir.exists(fileName));
+
+    return fileName;
+}
+
+bool VUtils::checkPathLegal(const QString &p_path)
+{
+    // Ensure every part of the p_path is a valid file name until we come to
+    // an existing parent directory.
+    if (p_path.isEmpty()) {
+        return false;
+    }
+
+    if (QFileInfo::exists(p_path)) {
+        return true;
+    }
+
+    bool ret = false;
+    int pos;
+    QString basePath = basePathFromPath(p_path);
+    QString fileName = fileNameFromPath(p_path);
+    QValidator *validator = new QRegExpValidator(QRegExp(c_fileNameRegExp));
+    while (!fileName.isEmpty()) {
+        QValidator::State validFile = validator->validate(fileName, pos);
+        if (validFile != QValidator::Acceptable) {
+            break;
+        }
+
+        if (QFileInfo::exists(basePath)) {
+            ret = true;
+            break;
+        }
+
+        fileName = fileNameFromPath(basePath);
+        basePath = basePathFromPath(basePath);
+    }
+
+    delete validator;
+    return ret;
+}

+ 9 - 0
src/utils/vutils.h

@@ -97,6 +97,15 @@ public:
     // Generate HTML template.
     static QString generateHtmlTemplate(MarkdownConverterType p_conType, bool p_exportPdf);
 
+    // Get an available file name in @p_directory with base @p_baseFileName.
+    // If there already exists a file named @p_baseFileName, try to add sequence
+    // suffix to the name, such as _001.
+    static QString getFileNameWithSequence(const QString &p_directory,
+                                           const QString &p_baseFileName);
+
+    // Try to check if @p_path is legal.
+    static bool checkPathLegal(const QString &p_path);
+
     // Regular expression for image link.
     // ![image title]( http://github.com/tamlok/vnote.jpg "alt \" text" )
     // Captured texts (need to be trimmed):

+ 6 - 0
src/vconfigmanager.cpp

@@ -26,6 +26,7 @@ const QString VConfigManager::c_warningTextStyle = QString("color: red; font: bo
 const QString VConfigManager::c_dataTextStyle = QString("font: bold");
 const QString VConfigManager::c_dangerBtnStyle = QString("QPushButton {color: #fff; border-color: #d43f3a; background-color: #d9534f;}"
                                                          "QPushButton::hover {color: #fff; border-color: #ac2925; background-color: #c9302c;}");
+const QString VConfigManager::c_vnoteNotebookFolderName = QString("vnote_notebooks");
 
 VConfigManager::VConfigManager()
     : userSettings(NULL), defaultSettings(NULL)
@@ -654,3 +655,8 @@ void VConfigManager::setEditorStyle(const QString &p_style)
     setConfigToSettings("global", "editor_style", m_editorStyle);
     updateEditStyle();
 }
+
+QString VConfigManager::getVnoteNotebookFolderPath()
+{
+    return QDir::home().filePath(c_vnoteNotebookFolderName);
+}

+ 6 - 0
src/vconfigmanager.h

@@ -44,6 +44,9 @@ public:
 
     static QString getLogFilePath();
 
+    // Get the path of the folder used to store default notebook.
+    static QString getVnoteNotebookFolderPath();
+
     // Constants
     static const QString orgName;
     static const QString appName;
@@ -362,6 +365,9 @@ private:
     static const QString c_defaultMdhlFile;
     static const QString c_solarizedDarkMdhlFile;
     static const QString c_solarizedLightMdhlFile;
+
+    // The folder name to store all notebooks if user does not specify one.
+    static const QString c_vnoteNotebookFolderName;
 };
 
 

+ 5 - 0
src/vnotebook.cpp

@@ -107,6 +107,9 @@ VNotebook *VNotebook::createNotebook(const QString &p_name, const QString &p_pat
                                      QObject *p_parent)
 {
     VNotebook *nb = new VNotebook(p_name, p_path, p_parent);
+
+    // If @p_imageFolder is empty, it will report global configured folder as
+    // its image folder.
     nb->setImageFolder(p_imageFolder);
 
     // Check if there alread exists a config file.
@@ -116,6 +119,8 @@ VNotebook *VNotebook::createNotebook(const QString &p_name, const QString &p_pat
         return nb;
     }
 
+    VUtils::makePath(p_path);
+
     if (!nb->writeToConfig()) {
         delete nb;
         return NULL;

+ 40 - 57
src/vnotebookselector.cpp

@@ -168,41 +168,24 @@ bool VNotebookSelector::newNotebook()
     info += tr("The root folder should be used EXCLUSIVELY by VNote and "
                "it is recommended to be EMPTY.");
 
-    QString defaultName("new_notebook");
+    QString defaultName;
     QString defaultPath;
 
+    // Use empty default name and path to let the dialog to auto generate a name
+    // under the default VNote notebook folder.
     VNewNotebookDialog dialog(tr("Add Notebook"), info, defaultName,
-                              defaultPath, this);
-    do {
-        if (dialog.exec() == QDialog::Accepted) {
-            QString name = dialog.getNameInput();
-            QString path = dialog.getPathInput();
-            if (findNotebook(name)) {
-                VUtils::showMessage(QMessageBox::Warning, tr("Warning"),
-                                    tr("Name already exists. Please choose another name."), "",
-                                    QMessageBox::Ok, QMessageBox::Ok, this);
-                continue;
-            }
-
-            createNotebook(name, path, dialog.getImportCheck(), dialog.getImageFolder());
-            return true;
-        }
-        break;
-    } while (true);
+                              defaultPath, m_notebooks, this);
+    if (dialog.exec() == QDialog::Accepted) {
+        createNotebook(dialog.getNameInput(),
+                       dialog.getPathInput(),
+                       dialog.isImportExistingNotebook(),
+                       dialog.getImageFolder());
+        return true;
+    }
 
     return false;
 }
 
-VNotebook *VNotebookSelector::findNotebook(const QString &p_name)
-{
-    for (int i = 0; i < m_notebooks.size(); ++i) {
-        if (m_notebooks[i]->getName() == p_name) {
-            return m_notebooks[i];
-        }
-    }
-    return NULL;
-}
-
 void VNotebookSelector::createNotebook(const QString &p_name,
                                        const QString &p_path,
                                        bool p_import,
@@ -210,6 +193,15 @@ void VNotebookSelector::createNotebook(const QString &p_name,
 {
     VNotebook *nb = VNotebook::createNotebook(p_name, p_path, p_import,
                                               p_imageFolder, m_vnote);
+    if (!nb) {
+        VUtils::showMessage(QMessageBox::Warning, tr("Warning"),
+                            tr("Fail to create notebook "
+                               "<span style=\"%1\">%2</span> in <span style=\"%1\">%3</span>.")
+                            .arg(vconfig.c_dataTextStyle).arg(p_name).arg(p_path), "",
+                            QMessageBox::Ok, QMessageBox::Ok, this);
+        return;
+    }
+
     m_notebooks.append(nb);
     vconfig.setNotebooks(m_notebooks);
 
@@ -293,38 +285,29 @@ void VNotebookSelector::editNotebookInfo()
     VNotebook *notebook = getNotebookFromComboIndex(index);
     QString curName = notebook->getName();
 
-    VNotebookInfoDialog dialog(tr("Notebook Information"), "", notebook, this);
-    do {
-        if (dialog.exec() == QDialog::Accepted) {
-            bool updated = false;
-            QString name = dialog.getName();
-            if (name != curName) {
-                if (findNotebook(name)) {
-                    VUtils::showMessage(QMessageBox::Warning, tr("Warning"),
-                                        tr("Name already exists. Please choose another name."), "",
-                                        QMessageBox::Ok, QMessageBox::Ok, this);
-                    continue;
-                }
-
-                updated = true;
-                notebook->rename(name);
-                updateComboBoxItem(index, name);
-                vconfig.setNotebooks(m_notebooks);
-            }
+    VNotebookInfoDialog dialog(tr("Notebook Information"), "", notebook,
+                               m_notebooks, this);
+    if (dialog.exec() == QDialog::Accepted) {
+        bool updated = false;
+        QString name = dialog.getName();
+        if (name != curName) {
+            updated = true;
+            notebook->rename(name);
+            updateComboBoxItem(index, name);
+            vconfig.setNotebooks(m_notebooks);
+        }
 
-            QString imageFolder = dialog.getImageFolder();
-            if (imageFolder != notebook->getImageFolderConfig()) {
-                updated = true;
-                notebook->setImageFolder(imageFolder);
-                notebook->writeConfig();
-            }
+        QString imageFolder = dialog.getImageFolder();
+        if (imageFolder != notebook->getImageFolderConfig()) {
+            updated = true;
+            notebook->setImageFolder(imageFolder);
+            notebook->writeConfig();
+        }
 
-            if (updated) {
-                emit notebookUpdated(notebook);
-            }
+        if (updated) {
+            emit notebookUpdated(notebook);
         }
-        break;
-    } while (true);
+    }
 }
 
 void VNotebookSelector::addNotebookItem(const QString &p_name)

+ 1 - 1
src/vnotebookselector.h

@@ -52,7 +52,7 @@ private slots:
 private:
     void initActions();
     void updateComboBox();
-    VNotebook *findNotebook(const QString &p_name);
+
     // Return the index of @p_notebook in m_noteboks.
     int indexOfNotebook(const VNotebook *p_notebook);