Browse Source

add VFileListPanel as a wrapper of VFileList

Signed-off-by: Le Tan <[email protected]>
Le Tan 9 years ago
parent
commit
1f80fb8583

+ 60 - 0
src/dialog/vfileinfodialog.cpp

@@ -0,0 +1,60 @@
+#include <QtWidgets>
+#include "vfileinfodialog.h"
+
+VFileInfoDialog::VFileInfoDialog(const QString &title, const QString &info,
+                                 const QString &defaultName,
+                                 QWidget *parent)
+    : QDialog(parent), infoLabel(NULL), title(title), info(info), defaultName(defaultName)
+{
+    setupUI();
+
+    connect(nameEdit, &QLineEdit::textChanged, this, &VFileInfoDialog::enableOkButton);
+    connect(okBtn, &QPushButton::clicked, this, &VFileInfoDialog::accept);
+    connect(cancelBtn, &QPushButton::clicked, this, &VFileInfoDialog::reject);
+
+    enableOkButton();
+}
+
+void VFileInfoDialog::setupUI()
+{
+    if (!info.isEmpty()) {
+        infoLabel = new QLabel(info);
+    }
+    nameLabel = new QLabel(tr("&Name"));
+    nameEdit = new QLineEdit(defaultName);
+    nameEdit->selectAll();
+    nameLabel->setBuddy(nameEdit);
+
+    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);
+
+    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 VFileInfoDialog::enableOkButton()
+{
+    okBtn->setEnabled(!nameEdit->text().isEmpty());
+}
+
+QString VFileInfoDialog::getNameInput() const
+{
+    return nameEdit->text();
+}

+ 36 - 0
src/dialog/vfileinfodialog.h

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

BIN
src/resources/icons/create_note.png


BIN
src/resources/icons/delete_note.png


BIN
src/resources/icons/note_info.png


+ 6 - 2
src/src.pro

@@ -35,7 +35,9 @@ SOURCES += main.cpp\
     dialog/vnewnotebookdialog.cpp \
     vmarkdownconverter.cpp \
     dialog/vnotebookinfodialog.cpp \
-    dialog/vdirinfodialog.cpp
+    dialog/vdirinfodialog.cpp \
+    vfilelistpanel.cpp \
+    dialog/vfileinfodialog.cpp
 
 HEADERS  += vmainwindow.h \
     vdirectorytree.h \
@@ -61,7 +63,9 @@ HEADERS  += vmainwindow.h \
     dialog/vnewnotebookdialog.h \
     vmarkdownconverter.h \
     dialog/vnotebookinfodialog.h \
-    dialog/vdirinfodialog.h
+    dialog/vdirinfodialog.h \
+    vfilelistpanel.h \
+    dialog/vfileinfodialog.h
 
 RESOURCES += \
     vnote.qrc

+ 13 - 2
src/veditor.cpp

@@ -175,9 +175,20 @@ bool VEditor::saveFile()
     if (!isEditMode || !noteFile->modifiable || !textEditor->isModified()) {
         return true;
     }
+    // Make sure the file already exists. Temporary deal with cases when user delete or move
+    // a file.
+    QString filePath = QDir(noteFile->basePath).filePath(noteFile->fileName);
+    if (!QFile(filePath).exists()) {
+        qWarning() << "error:" << filePath << "being written has been removed";
+        QMessageBox msgBox(QMessageBox::Warning, tr("Fail to save to file"),
+                           QString("%1 being written has been removed.").arg(filePath),
+                           QMessageBox::Ok, this);
+        msgBox.setDefaultButton(QMessageBox::Ok);
+        msgBox.exec();
+        return false;
+    }
     textEditor->saveFile();
-    bool ret = VUtils::writeFileToDisk(QDir(noteFile->basePath).filePath(noteFile->fileName),
-                                       noteFile->content);
+    bool ret = VUtils::writeFileToDisk(filePath, noteFile->content);
     if (!ret) {
         QMessageBox msgBox(QMessageBox::Warning, tr("Fail to save to file"),
                            QString("Fail to write to disk when saving a note. Please try it again."),

+ 1 - 1
src/vfilelist.cpp

@@ -55,7 +55,6 @@ void VFileList::setDirectory(QJsonObject dirJson)
 void VFileList::clearDirectoryInfo()
 {
     notebook = relativePath = rootPath = "";
-    clear();
 }
 
 void VFileList::updateFileList()
@@ -357,6 +356,7 @@ void VFileList::handleNotebookRenamed(const QVector<VNotebook> &notebooks,
     }
 }
 
+// FIXME: when @oldRelativePath is part of relativePath, we also need to update relativePath partialy
 void VFileList::handleDirectoryRenamed(const QString &notebook,
                                        const QString &oldRelativePath, const QString &newRelativePath)
 {

+ 2 - 2
src/vfilelist.h

@@ -23,8 +23,6 @@ signals:
     void fileCreated(QJsonObject fileJson);
 
 private slots:
-    void newFile();
-    void deleteFile();
     void contextMenuRequested(QPoint pos);
     void handleItemClicked(QListWidgetItem *currentItem);
 
@@ -34,6 +32,8 @@ public slots:
                                const QString &newName);
     void handleDirectoryRenamed(const QString &notebook, const QString &oldRelativePath,
                                 const QString &newRelativePath);
+    void newFile();
+    void deleteFile();
 
 private:
     void updateFileList();

+ 232 - 0
src/vfilelistpanel.cpp

@@ -0,0 +1,232 @@
+#include <QtWidgets>
+#include "vfilelistpanel.h"
+#include "vfilelist.h"
+#include "dialog/vfileinfodialog.h"
+#include "vnotebook.h"
+#include "vnote.h"
+#include "vconfigmanager.h"
+
+VFileListPanel::VFileListPanel(VNote *vnote, QWidget *parent)
+    : QWidget(parent), vnote(vnote)
+{
+    setupUI();
+}
+
+void VFileListPanel::setupUI()
+{
+    newFileBtn = new QPushButton(QIcon(":/resources/icons/create_note.png"), "");
+    newFileBtn->setToolTip(tr("Create a new note"));
+    deleteFileBtn = new QPushButton(QIcon(":/resources/icons/delete_note.png"), "");
+    deleteFileBtn->setToolTip(tr("Delete current note"));
+    fileInfoBtn = new QPushButton(QIcon(":/resources/icons/note_info.png"), "");
+    fileInfoBtn->setToolTip(tr("View and edit current note's information"));
+
+    fileList = new VFileList(vnote);
+
+    QHBoxLayout *topLayout = new QHBoxLayout;
+    topLayout->addStretch();
+    topLayout->addWidget(newFileBtn);
+    topLayout->addWidget(deleteFileBtn);
+    topLayout->addWidget(fileInfoBtn);
+
+    QVBoxLayout *mainLayout = new QVBoxLayout;
+    mainLayout->addLayout(topLayout);
+    mainLayout->addWidget(fileList);
+
+    // Signals
+    connect(fileList, &VFileList::fileClicked,
+            this, &VFileListPanel::fileClicked);
+    connect(fileList, &VFileList::fileDeleted,
+            this, &VFileListPanel::fileDeleted);
+    connect(fileList, &VFileList::fileCreated,
+            this, &VFileListPanel::fileCreated);
+
+    connect(newFileBtn, &QPushButton::clicked,
+            this, &VFileListPanel::onNewFileBtnClicked);
+    connect(deleteFileBtn, &QPushButton::clicked,
+            this, &VFileListPanel::onDeleteFileBtnClicked);
+    connect(fileInfoBtn, &QPushButton::clicked,
+            this, &VFileListPanel::onFileInfoBtnClicked);
+
+    setLayout(mainLayout);
+}
+
+void VFileListPanel::setDirectory(QJsonObject dirJson)
+{
+    if (dirJson.isEmpty()) {
+        clearDirectoryInfo();
+        fileList->setDirectory(dirJson);
+        return;
+    }
+
+    notebook = dirJson["notebook"].toString();
+    relativePath = dirJson["relative_path"].toString();
+    rootPath = "";
+    const QVector<VNotebook> &notebooks = vnote->getNotebooks();
+    for (int i = 0; i < notebooks.size(); ++i) {
+        if (notebooks[i].getName() == notebook) {
+            rootPath = notebooks[i].getPath();
+            break;
+        }
+    }
+    Q_ASSERT(!rootPath.isEmpty());
+
+    fileList->setDirectory(dirJson);
+}
+
+void VFileListPanel::clearDirectoryInfo()
+{
+    notebook = relativePath = rootPath = "";
+    fileList->clear();
+}
+
+void VFileListPanel::handleNotebookRenamed(const QVector<VNotebook> &notebooks,
+                                           const QString &oldName, const QString &newName)
+{
+    if (oldName == notebook) {
+        // Update treePath (though treePath actually will not be changed)
+        notebook = newName;
+        rootPath.clear();
+        const QVector<VNotebook> &notebooks = vnote->getNotebooks();
+        for (int i = 0; i < notebooks.size(); ++i) {
+            if (notebooks[i].getName() == notebook) {
+                rootPath = notebooks[i].getPath();
+                break;
+            }
+        }
+        Q_ASSERT(!rootPath.isEmpty());
+    }
+    fileList->handleNotebookRenamed(notebooks, oldName, newName);
+}
+
+void VFileListPanel::handleDirectoryRenamed(const QString &notebook,
+                                            const QString &oldRelativePath, const QString &newRelativePath)
+{
+    if (notebook == this->notebook
+        && oldRelativePath == relativePath) {
+        relativePath = newRelativePath;
+    }
+    fileList->handleDirectoryRenamed(notebook, oldRelativePath,
+                                     newRelativePath);
+}
+
+bool VFileListPanel::importFile(const QString &name)
+{
+    return fileList->importFile(name);
+}
+
+void VFileListPanel::onNewFileBtnClicked()
+{
+    if (relativePath.isEmpty()) {
+        return;
+    }
+    fileList->newFile();
+}
+
+void VFileListPanel::onDeleteFileBtnClicked()
+{
+    QListWidgetItem *curItem = fileList->currentItem();
+    if (!curItem) {
+        return;
+    }
+    fileList->deleteFile();
+}
+
+void VFileListPanel::onFileInfoBtnClicked()
+{
+    QListWidgetItem *curItem = fileList->currentItem();
+    if (!curItem) {
+        return;
+    }
+    QJsonObject curItemJson = curItem->data(Qt::UserRole).toJsonObject();
+    Q_ASSERT(!curItemJson.isEmpty());
+    QString curItemName = curItemJson["name"].toString();
+
+    QString info;
+    QString defaultName = curItemName;
+
+    do {
+        VFileInfoDialog dialog(tr("Note Information"), info, defaultName, this);
+        if (dialog.exec() == QDialog::Accepted) {
+            QString name = dialog.getNameInput();
+            if (name == curItemName) {
+                return;
+            }
+            if (isConflictNameWithExisting(name)) {
+                info = "Name already exists.\nPlease choose another name:";
+                defaultName = name;
+                continue;
+            }
+            renameFile(curItem, name);
+        }
+        break;
+    } while (true);
+}
+
+bool VFileListPanel::isConflictNameWithExisting(const QString &name)
+{
+    int nrChild = fileList->count();
+    for (int i = 0; i < nrChild; ++i) {
+        QListWidgetItem *item = fileList->item(i);
+        QJsonObject itemJson = item->data(Qt::UserRole).toJsonObject();
+        Q_ASSERT(!itemJson.isEmpty());
+        if (itemJson["name"].toString() == name) {
+            return true;
+        }
+    }
+    return false;
+}
+
+void VFileListPanel::renameFile(QListWidgetItem *item, const QString &newName)
+{
+    if (!item) {
+        return;
+    }
+    QJsonObject itemJson = item->data(Qt::UserRole).toJsonObject();
+    Q_ASSERT(!itemJson.isEmpty());
+    QString name = itemJson["name"].toString();
+    QString path = QDir(rootPath).filePath(relativePath);
+    QFile file(QDir(path).filePath(name));
+    QString newFilePath(QDir(path).filePath(newName));
+    Q_ASSERT(file.exists());
+    if (!file.rename(newFilePath)) {
+        qWarning() << "error: fail to rename file" << name << "under" << path;
+        QMessageBox msgBox(QMessageBox::Warning, tr("Warning"), QString("Could not rename note \"%1\" under \"%2\".")
+                           .arg(name).arg(path), QMessageBox::Ok, this);
+        msgBox.setInformativeText(QString("Please check if there already exists a file named \"%1\".").arg(newName));
+        msgBox.exec();
+        return;
+    }
+
+    // Update directory's config file
+    QJsonObject dirJson = VConfigManager::readDirectoryConfig(path);
+    Q_ASSERT(!dirJson.isEmpty());
+    QJsonArray fileArray = dirJson["files"].toArray();
+    int index = 0;
+    for (index = 0; index < fileArray.size(); ++index) {
+        QJsonObject tmp = fileArray[index].toObject();
+        if (tmp["name"].toString() == name) {
+            tmp["name"] = newName;
+            fileArray[index] = tmp;
+            break;
+        }
+    }
+    Q_ASSERT(index != fileArray.size());
+    dirJson["files"] = fileArray;
+    if (!VConfigManager::writeDirectoryConfig(path, dirJson)) {
+        qWarning() << "error: fail to rename file"
+                   << name << "to" << newName;
+        file.rename(name);
+        return;
+    }
+
+    // Update item
+    itemJson["name"] = newName;
+    item->setData(Qt::UserRole, itemJson);
+    item->setText(newName);
+
+    QString oldPath = QDir::cleanPath(QDir(relativePath).filePath(name));
+    QString newPath = QDir::cleanPath(QDir(relativePath).filePath(newName));
+    qDebug() << "file renamed" << oldPath << "to" << newPath;
+    emit fileRenamed(notebook, oldPath, newPath);
+}

+ 70 - 0
src/vfilelistpanel.h

@@ -0,0 +1,70 @@
+#ifndef VFILELISTPANEL_H
+#define VFILELISTPANEL_H
+
+#include <QWidget>
+#include <QString>
+#include <QVector>
+#include <QJsonObject>
+#include <QDir>
+#include <QFile>
+#include "vnotebook.h"
+
+class VNote;
+class VFileList;
+class QPushButton;
+class QListWidgetItem;
+
+class VFileListPanel : public QWidget
+{
+    Q_OBJECT
+public:
+    VFileListPanel(VNote *vnote, QWidget *parent = 0);
+    bool importFile(const QString &name);
+
+signals:
+    void fileClicked(QJsonObject fileJson);
+    void fileDeleted(QJsonObject fileJson);
+    void fileCreated(QJsonObject fileJson);
+    void fileRenamed(const QString &notebook, const QString &oldPath,
+                     const QString &newPath);
+
+public slots:
+    void setDirectory(QJsonObject dirJson);
+    void handleNotebookRenamed(const QVector<VNotebook> &notebooks, const QString &oldName,
+                               const QString &newName);
+    void handleDirectoryRenamed(const QString &notebook, const QString &oldRelativePath,
+                                const QString &newRelativePath);
+
+private slots:
+    void onNewFileBtnClicked();
+    void onDeleteFileBtnClicked();
+    void onFileInfoBtnClicked();
+
+private:
+    void setupUI();
+    bool isConflictNameWithExisting(const QString &name);
+    void renameFile(QListWidgetItem *item, const QString &newName);
+    void clearDirectoryInfo();
+    inline QString getDirectoryName();
+
+    VNote *vnote;
+    QString notebook;
+    // Current directory's relative path
+    QString relativePath;
+    // Used for cache
+    QString rootPath;
+
+    VFileList *fileList;
+    QPushButton *newFileBtn;
+    QPushButton *deleteFileBtn;
+    QPushButton *fileInfoBtn;
+};
+
+inline QString VFileListPanel::getDirectoryName()
+{
+    if (relativePath.isEmpty()) {
+        return "";
+    }
+    return QFileInfo(QDir::cleanPath(relativePath)).fileName();
+}
+#endif // VFILELISTPANEL_H

+ 16 - 13
src/vmainwindow.cpp

@@ -2,7 +2,7 @@
 #include "vmainwindow.h"
 #include "vdirectorytree.h"
 #include "vnote.h"
-#include "vfilelist.h"
+#include "vfilelistpanel.h"
 #include "vtabwidget.h"
 #include "vconfigmanager.h"
 #include "dialog/vnewnotebookdialog.h"
@@ -73,9 +73,8 @@ void VMainWindow::setupUI()
     nbContainer->setLayout(nbLayout);
     nbContainer->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Expanding);
 
-    // File list widget
-    fileList = new VFileList(vnote);
-    fileList->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Expanding);
+    fileListPanel = new VFileListPanel(vnote);
+    fileListPanel->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Expanding);
 
     // Editor tab widget
     tabs = new VTabWidget(vnote);
@@ -85,7 +84,7 @@ void VMainWindow::setupUI()
     // Main Splitter
     mainSplitter = new QSplitter();
     mainSplitter->addWidget(nbContainer);
-    mainSplitter->addWidget(fileList);
+    mainSplitter->addWidget(fileListPanel);
     mainSplitter->addWidget(tabs);
     mainSplitter->setStretchFactor(0, 1);
     mainSplitter->setStretchFactor(1, 1);
@@ -96,17 +95,17 @@ void VMainWindow::setupUI()
             SLOT(setCurNotebookIndex(int)));
 
     connect(vnote, &VNote::notebooksRenamed,
-            fileList, &VFileList::handleNotebookRenamed);
+            fileListPanel, &VFileListPanel::handleNotebookRenamed);
     connect(directoryTree, &VDirectoryTree::currentDirectoryChanged,
-            fileList, &VFileList::setDirectory);
+            fileListPanel, &VFileListPanel::setDirectory);
     connect(directoryTree, &VDirectoryTree::directoryRenamed,
-            fileList, &VFileList::handleDirectoryRenamed);
+            fileListPanel, &VFileListPanel::handleDirectoryRenamed);
 
-    connect(fileList, &VFileList::fileClicked,
+    connect(fileListPanel, &VFileListPanel::fileClicked,
             tabs, &VTabWidget::openFile);
-    connect(fileList, &VFileList::fileDeleted,
+    connect(fileListPanel, &VFileListPanel::fileDeleted,
             tabs, &VTabWidget::closeFile);
-    connect(fileList, &VFileList::fileCreated,
+    connect(fileListPanel, &VFileListPanel::fileCreated,
             tabs, &VTabWidget::openFile);
 
     connect(newNotebookBtn, &QPushButton::clicked,
@@ -383,14 +382,18 @@ void VMainWindow::onNotebookInfoBtnClicked()
 
 void VMainWindow::importNoteFromFile()
 {
+    static QString lastPath = QDir::homePath();
     QStringList files = QFileDialog::getOpenFileNames(this,tr("Select files(HTML or Markdown) to be imported as notes"),
-                                                      QDir::homePath());
+                                                      lastPath);
     if (files.isEmpty()) {
         return;
     }
+    // Update lastPath
+    lastPath = QFileInfo(files[0]).path();
+
     QStringList failedFiles;
     for (int i = 0; i < files.size(); ++i) {
-        bool ret = fileList->importFile(files[i]);
+        bool ret = fileListPanel->importFile(files[i]);
         if (!ret) {
             failedFiles.append(files[i]);
         }

+ 2 - 2
src/vmainwindow.h

@@ -11,12 +11,12 @@ class QListWidget;
 class QTabWidget;
 class QToolBar;
 class VNote;
-class VFileList;
 class VTabWidget;
 class QAction;
 class QPushButton;
 class VNotebook;
 class QActionGroup;
+class VFileListPanel;
 
 class VMainWindow : public QMainWindow
 {
@@ -63,8 +63,8 @@ private:
     QPushButton *newRootDirBtn;
     QPushButton *deleteDirBtn;
     QPushButton *dirInfoBtn;
+    VFileListPanel *fileListPanel;
     VDirectoryTree *directoryTree;
-    VFileList *fileList;
     VTabWidget *tabs;
     QSplitter *mainSplitter;
     VNote *vnote;

+ 3 - 0
src/vnote.qrc

@@ -42,5 +42,8 @@
         <file>resources/icons/create_rootdir.png</file>
         <file>resources/icons/delete_dir.png</file>
         <file>resources/icons/dir_info.png</file>
+        <file>resources/icons/create_note.png</file>
+        <file>resources/icons/delete_note.png</file>
+        <file>resources/icons/note_info.png</file>
     </qresource>
 </RCC>

+ 1 - 1
src/vtabwidget.cpp

@@ -54,7 +54,7 @@ void VTabWidget::openFile(QJsonObject fileJson)
         mode = fileJson["mode"].toInt();
     }
 
-    qDebug() << "open notebook[" << notebook << "] path[" << relativePath << "]" << mode;
+    qDebug() << "open notebook" << notebook << "path" << relativePath << mode;
 
     // Find if it has been opened already
     int idx = findTabByFile(notebook, relativePath);