소스 검색

support creating new notebook

Thanks to [benjsperry](https://github.com/driftyco/ionicons) for the
icons.

Signed-off-by: Le Tan <[email protected]>
Le Tan 9 년 전
부모
커밋
4d9946bfe1

+ 8 - 6
VNote.pro

@@ -17,10 +17,10 @@ SOURCES += main.cpp\
     vdirectorytree.cpp \
     vnote.cpp \
     vnotebook.cpp \
-    vnewdirdialog.cpp \
+    dialog/vnewdirdialog.cpp \
     vconfigmanager.cpp \
     vfilelist.cpp \
-    vnewfiledialog.cpp \
+    dialog/vnewfiledialog.cpp \
     vtabwidget.cpp \
     vedit.cpp \
     veditor.cpp \
@@ -31,16 +31,17 @@ SOURCES += main.cpp\
     utils/peg-highlight/pmh_parser.c \
     hgmarkdownhighlighter.cpp \
     vstyleparser.cpp \
-    utils/peg-highlight/pmh_styleparser.c
+    utils/peg-highlight/pmh_styleparser.c \
+    dialog/vnewnotebookdialog.cpp
 
 HEADERS  += vmainwindow.h \
     vdirectorytree.h \
     vnote.h \
     vnotebook.h \
-    vnewdirdialog.h \
+    dialog/vnewdirdialog.h \
     vconfigmanager.h \
     vfilelist.h \
-    vnewfiledialog.h \
+    dialog/vnewfiledialog.h \
     vtabwidget.h \
     vedit.h \
     veditor.h \
@@ -53,7 +54,8 @@ HEADERS  += vmainwindow.h \
     hgmarkdownhighlighter.h \
     utils/peg-highlight/pmh_definitions.h \
     vstyleparser.h \
-    utils/peg-highlight/pmh_styleparser.h
+    utils/peg-highlight/pmh_styleparser.h \
+    dialog/vnewnotebookdialog.h
 
 RESOURCES += \
     vnote.qrc

+ 0 - 0
vnewdirdialog.cpp → dialog/vnewdirdialog.cpp


+ 0 - 0
vnewdirdialog.h → dialog/vnewdirdialog.h


+ 0 - 0
vnewfiledialog.cpp → dialog/vnewfiledialog.cpp


+ 0 - 0
vnewfiledialog.h → dialog/vnewfiledialog.h


+ 86 - 0
dialog/vnewnotebookdialog.cpp

@@ -0,0 +1,86 @@
+#include <QtWidgets>
+#include "vnewnotebookdialog.h"
+
+VNewNotebookDialog::VNewNotebookDialog(const QString &title, const QString &info,
+                                       const QString &defaultName, const QString &defaultPath,
+                                       QWidget *parent)
+    : QDialog(parent), title(title), info(info), defaultName(defaultName), defaultPath(defaultPath),
+      infoLabel(NULL)
+{
+    setupUI();
+
+    connect(nameEdit, &QLineEdit::textChanged, this, &VNewNotebookDialog::enableOkButton);
+    connect(pathEdit, &QLineEdit::textChanged, this, &VNewNotebookDialog::enableOkButton);
+    connect(browseBtn, &QPushButton::clicked, this, &VNewNotebookDialog::handleBrowseBtnClicked);
+    connect(okBtn, &QPushButton::clicked, this, &VNewNotebookDialog::accept);
+    connect(cancelBtn, &QPushButton::clicked, this, &VNewNotebookDialog::reject);
+
+    enableOkButton();
+}
+
+void VNewNotebookDialog::setupUI()
+{
+    if (!info.isEmpty()) {
+        infoLabel = new QLabel(info);
+    }
+    nameLabel = new QLabel(tr("&Name"));
+    nameEdit = new QLineEdit(defaultName);
+    nameEdit->selectAll();
+    nameLabel->setBuddy(nameEdit);
+
+    QLabel *pathLabel = new QLabel(tr("&Path"));
+    pathEdit = new QLineEdit(defaultPath);
+    pathLabel->setBuddy(pathEdit);
+    browseBtn = new QPushButton(tr("&Browse"));
+
+    QHBoxLayout *pathLayout = new QHBoxLayout();
+    pathLayout->addWidget(pathEdit);
+    pathLayout->addWidget(browseBtn);
+
+    okBtn = new QPushButton(tr("&OK"));
+    okBtn->setDefault(true);
+    cancelBtn = new QPushButton(tr("&Cancel"));
+
+    QVBoxLayout *topLayout = new QVBoxLayout();
+    if (infoLabel) {
+        topLayout->addWidget(infoLabel);
+    }
+    topLayout->addWidget(nameLabel);
+    topLayout->addWidget(nameEdit);
+    topLayout->addWidget(pathLabel);
+    topLayout->addLayout(pathLayout);
+
+    QHBoxLayout *btmLayout = new QHBoxLayout();
+    btmLayout->addStretch();
+    btmLayout->addWidget(okBtn);
+    btmLayout->addWidget(cancelBtn);
+
+    QVBoxLayout *mainLayout = new QVBoxLayout();
+    mainLayout->addLayout(topLayout);
+    mainLayout->addLayout(btmLayout);
+    setLayout(mainLayout);
+
+    setWindowTitle(title);
+}
+
+void VNewNotebookDialog::enableOkButton()
+{
+    okBtn->setEnabled(!pathEdit->text().isEmpty() && !nameEdit->text().isEmpty());
+}
+
+QString VNewNotebookDialog::getNameInput() const
+{
+    return nameEdit->text();
+}
+
+QString VNewNotebookDialog::getPathInput() const
+{
+    return pathEdit->text();
+}
+
+void VNewNotebookDialog::handleBrowseBtnClicked()
+{
+    QString dirPath = QFileDialog::getExistingDirectory(this, tr("Select a directory as the path of the notebook"),
+                                                        "", QFileDialog::ShowDirsOnly | QFileDialog::DontResolveSymlinks);
+    pathEdit->setText(dirPath);
+}

+ 41 - 0
dialog/vnewnotebookdialog.h

@@ -0,0 +1,41 @@
+#ifndef VNEWNOTEBOOKDIALOG_H
+#define VNEWNOTEBOOKDIALOG_H
+
+#include <QDialog>
+
+class QLabel;
+class QLineEdit;
+class QPushButton;
+class QString;
+
+class VNewNotebookDialog : public QDialog
+{
+    Q_OBJECT
+public:
+    VNewNotebookDialog(const QString &title, const QString &info, const QString &defaultName,
+                       const QString &defaultPath, QWidget *parent = 0);
+    QString getNameInput() const;
+    QString getPathInput() const;
+
+private slots:
+    void enableOkButton();
+    void handleBrowseBtnClicked();
+
+private:
+    void setupUI();
+
+    QLabel *infoLabel;
+    QLabel *nameLabel;
+    QLineEdit *nameEdit;
+    QLineEdit *pathEdit;
+    QPushButton *browseBtn;
+    QPushButton *okBtn;
+    QPushButton *cancelBtn;
+
+    QString title;
+    QString defaultName;
+    QString info;
+    QString defaultPath;
+};
+
+#endif // VNEWNOTEBOOKDIALOG_H

BIN
resources/icons/create_notebook.png


BIN
resources/icons/delete_notebook.png


BIN
resources/icons/notebook_info.png


+ 1 - 1
vdirectorytree.cpp

@@ -1,6 +1,6 @@
 #include <QtWidgets>
 #include "vdirectorytree.h"
-#include "vnewdirdialog.h"
+#include "dialog/vnewdirdialog.h"
 #include "vconfigmanager.h"
 
 VDirectoryTree::VDirectoryTree(QWidget *parent)

+ 1 - 1
vfilelist.cpp

@@ -2,7 +2,7 @@
 #include <QtWidgets>
 #include "vfilelist.h"
 #include "vconfigmanager.h"
-#include "vnewfiledialog.h"
+#include "dialog/vnewfiledialog.h"
 
 VFileList::VFileList(QWidget *parent)
     : QListWidget(parent)

+ 73 - 10
vmainwindow.cpp

@@ -5,17 +5,21 @@
 #include "vfilelist.h"
 #include "vtabwidget.h"
 #include "vconfigmanager.h"
+#include "dialog/vnewnotebookdialog.h"
 
 extern VConfigManager vconfig;
 
 VMainWindow::VMainWindow(QWidget *parent)
     : QMainWindow(parent)
 {
+    // Must be called before those who uses VConfigManager
     vnote = new VNote();
     setupUI();
     initActions();
     initToolBar();
-    updateNotebookComboBox();
+    initMenuBar();
+
+    updateNotebookComboBox(vnote->getNotebooks());
 }
 
 VMainWindow::~VMainWindow()
@@ -28,12 +32,24 @@ void VMainWindow::setupUI()
     // Notebook directory browser tree
     notebookLabel = new QLabel(tr("Notebook"));
     directoryLabel = new QLabel(tr("Directory"));
+    newNotebookBtn = new QPushButton(QIcon(":/resources/icons/create_notebook.png"), "");
+    newNotebookBtn->setToolTip(tr("Create a new notebook"));
+    deleteNotebookBtn = new QPushButton(QIcon(":/resources/icons/delete_notebook.png"), "");
+    deleteNotebookBtn->setToolTip(tr("Delete current notebook"));
+    notebookInfoBtn = new QPushButton(QIcon(":/resources/icons/notebook_info.png"), "");
+    notebookInfoBtn->setToolTip(tr("View and edit current notebook's information"));
     notebookComboBox = new QComboBox();
     notebookComboBox->setSizeAdjustPolicy(QComboBox::AdjustToContents);
     directoryTree = new VDirectoryTree();
 
+    QHBoxLayout *nbBtnLayout = new QHBoxLayout;
+    nbBtnLayout->addWidget(notebookLabel);
+    nbBtnLayout->addStretch();
+    nbBtnLayout->addWidget(newNotebookBtn);
+    nbBtnLayout->addWidget(deleteNotebookBtn);
+    nbBtnLayout->addWidget(notebookInfoBtn);
     QVBoxLayout *nbLayout = new QVBoxLayout;
-    nbLayout->addWidget(notebookLabel);
+    nbLayout->addLayout(nbBtnLayout);
     nbLayout->addWidget(notebookComboBox);
     nbLayout->addWidget(directoryLabel);
     nbLayout->addWidget(directoryTree);
@@ -68,6 +84,10 @@ void VMainWindow::setupUI()
             fileList, &VFileList::setDirectory);
     connect(fileList, &VFileList::fileClicked,
             tabs, &VTabWidget::openFile);
+    connect(newNotebookBtn, &QPushButton::clicked,
+            this, &VMainWindow::onNewNotebookBtnClicked);
+    connect(vnote, &VNote::notebooksChanged,
+            this, &VMainWindow::updateNotebookComboBox);
 
     setCentralWidget(mainSplitter);
     // Create and show the status bar
@@ -94,17 +114,27 @@ void VMainWindow::initActions()
 
 void VMainWindow::initToolBar()
 {
-    fileToolBar = addToolBar(tr("Note"));
+    QToolBar *fileToolBar = addToolBar(tr("Note"));
     fileToolBar->setMovable(false);
     fileToolBar->addAction(editNoteAct);
     fileToolBar->addAction(readNoteAct);
     fileToolBar->addAction(saveNoteAct);
 }
 
-void VMainWindow::updateNotebookComboBox()
+void VMainWindow::initMenuBar()
 {
-    const QVector<VNotebook> &notebooks = vnote->getNotebooks();
+    QMenu *fileMenu = menuBar()->addMenu(tr("&File"));
+    QMenu *helpMenu = menuBar()->addMenu(tr("&Help"));
+
+    // To be implemented
+}
 
+void VMainWindow::updateNotebookComboBox(const QVector<VNotebook> &notebooks)
+{
+    // Clearing and inserting items will emit the signal which corrupt the vconfig's
+    // current index. We save it first and then set the combobox index to the
+    // right one to resrote it.
+    int curIndex = vconfig.getCurNotebookIndex();
     notebookComboBox->clear();
     if (notebooks.isEmpty()) {
         return;
@@ -114,20 +144,53 @@ void VMainWindow::updateNotebookComboBox()
     }
 
     qDebug() << "update notebook combobox with" << notebookComboBox->count()
-             << "items";
-    notebookComboBox->setCurrentIndex(vconfig.getCurNotebookIndex());
+             << "items, current notebook" << curIndex;
+    notebookComboBox->setCurrentIndex(curIndex);
 }
 
 void VMainWindow::setCurNotebookIndex(int index)
 {
     Q_ASSERT(index < vnote->getNotebooks().size());
-    qDebug() << "set current notebook index:" << index;
-    vconfig.setCurNotebookIndex(index);
-
     // Update directoryTree
     QString treePath;
     if (index > -1) {
+        vconfig.setCurNotebookIndex(index);
         treePath = vnote->getNotebooks()[index].getPath();
     }
     emit curNotebookIndexChanged(treePath);
 }
+
+void VMainWindow::onNewNotebookBtnClicked()
+{
+    qDebug() << "request to create a notebook";
+    QString info;
+    QString defaultName("new_notebook");
+    QString defaultPath;
+    do {
+        VNewNotebookDialog dialog(tr("Create a new notebook"), info, defaultName,
+                                  defaultPath, this);
+        if (dialog.exec() == QDialog::Accepted) {
+            QString name = dialog.getNameInput();
+            QString path = dialog.getPathInput();
+            if (isConflictWithExistingNotebooks(name, path)) {
+                info = "Name already exists or the path already has a notebook.";
+                defaultName = name;
+                defaultPath = path;
+                continue;
+            }
+            vnote->createNotebook(name, path);
+        }
+        break;
+    } while (true);
+}
+
+bool VMainWindow::isConflictWithExistingNotebooks(const QString &name, const QString &path)
+{
+    const QVector<VNotebook> &notebooks = vnote->getNotebooks();
+    for (int i = 0; i < notebooks.size(); ++i) {
+        if (notebooks[i].getName() == name || notebooks[i].getPath() == path) {
+            return true;
+        }
+    }
+    return false;
+}

+ 10 - 3
vmainwindow.h

@@ -14,6 +14,8 @@ class VNote;
 class VFileList;
 class VTabWidget;
 class QAction;
+class QPushButton;
+class VNotebook;
 
 class VMainWindow : public QMainWindow
 {
@@ -26,26 +28,31 @@ public:
 private slots:
     // Change current notebook index and update the directory tree
     void setCurNotebookIndex(int index);
+    // Create a notebook
+    void onNewNotebookBtnClicked();
+    void updateNotebookComboBox(const QVector<VNotebook> &notebooks);
 
 signals:
     void curNotebookIndexChanged(const QString &path);
 
 private:
     void setupUI();
-    // Update notebookComboBox according to vnote
-    void updateNotebookComboBox();
     void initActions();
     void initToolBar();
+    void initMenuBar();
+    bool isConflictWithExistingNotebooks(const QString &name, const QString &path);
 
     QLabel *notebookLabel;
     QLabel *directoryLabel;
     QComboBox *notebookComboBox;
+    QPushButton *newNotebookBtn;
+    QPushButton *deleteNotebookBtn;
+    QPushButton *notebookInfoBtn;
     VDirectoryTree *directoryTree;
     VFileList *fileList;
     VTabWidget *tabs;
     QSplitter *mainSplitter;
     VNote *vnote;
-    QToolBar *fileToolBar;
 
     // Actions
     QAction *editNoteAct;

+ 26 - 1
vnote.cpp

@@ -1,5 +1,7 @@
 #include <QSettings>
 #include <QDebug>
+#include <QJsonObject>
+#include <QJsonArray>
 #include "vnote.h"
 #include "utils/vutils.h"
 #include "vconfigmanager.h"
@@ -8,11 +10,12 @@ VConfigManager vconfig;
 
 QString VNote::templateHtml;
 
-VNote::VNote()
+VNote::VNote() : QObject()
 {
     vconfig.initialize();
     decorateTemplate();
     notebooks = vconfig.getNotebooks();
+    emit notebooksChanged(notebooks);
 }
 
 void VNote::decorateTemplate()
@@ -25,3 +28,25 @@ const QVector<VNotebook>& VNote::getNotebooks()
 {
     return notebooks;
 }
+
+void VNote::createNotebook(const QString &name, const QString &path)
+{
+    // Create a directory config file in @path
+    QJsonObject configJson;
+    configJson["version"] = "1";
+    configJson["name"] = name;
+    configJson["sub_directories"] = QJsonArray();
+    configJson["files"] = QJsonArray();
+    if (!vconfig.writeDirectoryConfig(path, configJson)) {
+        return;
+    }
+
+    // Update notebooks settings
+    notebooks.prepend(VNotebook(name, path));
+    vconfig.setNotebooks(notebooks);
+
+    // Set current index to the new notebook
+    vconfig.setCurNotebookIndex(0);
+
+    emit notebooksChanged(notebooks);
+}

+ 8 - 1
vnote.h

@@ -5,10 +5,12 @@
 #include <QVector>
 #include <QSettings>
 #include <QFont>
+#include <QObject>
 #include "vnotebook.h"
 
-class VNote
+class VNote : public QObject
 {
+    Q_OBJECT
 public:
     VNote();
 
@@ -18,6 +20,11 @@ public:
 
     static QString templateHtml;
 
+    void createNotebook(const QString &name, const QString &path);
+
+signals:
+    void notebooksChanged(const QVector<VNotebook> &notebooks);
+
 private:
     QVector<VNotebook> notebooks;
 };

+ 3 - 0
vnote.qrc

@@ -36,5 +36,8 @@
         <file>resources/styles/solarized-light.mdhl</file>
         <file>resources/styles/solarized-dark.mdhl</file>
         <file>resources/vnote.ini</file>
+        <file>resources/icons/create_notebook.png</file>
+        <file>resources/icons/delete_notebook.png</file>
+        <file>resources/icons/notebook_info.png</file>
     </qresource>
 </RCC>