diff --git a/src/utils/vutils.cpp b/src/utils/vutils.cpp index 1a4b0360..2c714305 100644 --- a/src/utils/vutils.cpp +++ b/src/utils/vutils.cpp @@ -91,3 +91,20 @@ void VUtils::processStyle(QString &style) style.replace("@" + map.first, map.second); } } + +bool VUtils::isMarkdown(const QString &name) +{ + const QVector mdPostfix({"md", "markdown", "mkd"}); + + QStringList list = name.split('.', QString::SkipEmptyParts); + if (list.isEmpty()) { + return false; + } + const QString &postfix = list.last(); + for (int i = 0; i < mdPostfix.size(); ++i) { + if (postfix == mdPostfix[i]) { + return true; + } + } + return false; +} diff --git a/src/utils/vutils.h b/src/utils/vutils.h index 59110517..bde1540c 100644 --- a/src/utils/vutils.h +++ b/src/utils/vutils.h @@ -19,6 +19,7 @@ public: static QString generateImageFileName(const QString &path, const QString &title, const QString &format = "png"); static void processStyle(QString &style); + static bool isMarkdown(const QString &fileName); private: static inline void addQssVarToMap(QVector > &map, const QString &key, const QString &value); diff --git a/src/veditarea.cpp b/src/veditarea.cpp index a2c3739c..9290963c 100644 --- a/src/veditarea.cpp +++ b/src/veditarea.cpp @@ -178,19 +178,22 @@ void VEditArea::updateWindowStatus() win->requestUpdateCurHeader(); } -void VEditArea::closeFile(QJsonObject fileJson) +bool VEditArea::closeFile(QJsonObject fileJson) { if (fileJson.isEmpty()) { - return; + return true; } QString notebook = fileJson["notebook"].toString(); QString relativePath = fileJson["relative_path"].toString(); + bool isForced = fileJson["is_forced"].toBool(); int nrWin = splitter->count(); + bool ret = false; for (int i = 0; i < nrWin; ++i) { VEditWindow *win = getWindow(i); - win->closeFile(notebook, relativePath); + ret = ret || win->closeFile(notebook, relativePath, isForced); } + return ret; } void VEditArea::editFile() @@ -225,6 +228,29 @@ void VEditArea::handleNotebookRenamed(const QVector ¬ebooks, VEditWindow *win = getWindow(i); win->handleNotebookRenamed(notebooks, oldName, newName); } + updateWindowStatus(); +} + +void VEditArea::handleDirectoryRenamed(const QString ¬ebook, const QString &oldRelativePath, + const QString &newRelativePath) +{ + int nrWin = splitter->count(); + for (int i = 0; i < nrWin; ++i) { + VEditWindow *win = getWindow(i); + win->handleDirectoryRenamed(notebook, oldRelativePath, newRelativePath); + } + updateWindowStatus(); +} + +void VEditArea::handleFileRenamed(const QString ¬ebook, + const QString &oldRelativePath, const QString &newRelativePath) +{ + int nrWin = splitter->count(); + for (int i = 0; i < nrWin; ++i) { + VEditWindow *win = getWindow(i); + win->handleFileRenamed(notebook, oldRelativePath, newRelativePath); + } + updateWindowStatus(); } void VEditArea::handleSplitWindowRequest(VEditWindow *curWindow) @@ -305,3 +331,8 @@ void VEditArea::handleOutlineItemActivated(const VAnchor &anchor) // Notice current window getWindow(curWindowIndex)->scrollCurTab(anchor); } + +bool VEditArea::isFileOpened(const QString ¬ebook, const QString &relativePath) +{ + return !findTabsByFile(notebook, relativePath).isEmpty(); +} diff --git a/src/veditarea.h b/src/veditarea.h index db9bf21b..26a8b1ba 100644 --- a/src/veditarea.h +++ b/src/veditarea.h @@ -21,6 +21,7 @@ class VEditArea : public QWidget Q_OBJECT public: explicit VEditArea(VNote *vnote, QWidget *parent = 0); + bool isFileOpened(const QString ¬ebook, const QString &relativePath); signals: void curTabStatusChanged(const QString ¬ebook, const QString &relativePath, @@ -33,8 +34,7 @@ protected: public slots: void openFile(QJsonObject fileJson); - // Close the file forcely - void closeFile(QJsonObject fileJson); + bool closeFile(QJsonObject fileJson); void editFile(); void saveFile(); void readFile(); @@ -42,6 +42,10 @@ public slots: void handleNotebookRenamed(const QVector ¬ebooks, const QString &oldName, const QString &newName); void handleOutlineItemActivated(const VAnchor &anchor); + void handleDirectoryRenamed(const QString ¬ebook, + const QString &oldRelativePath, const QString &newRelativePath); + void handleFileRenamed(const QString ¬ebook, + const QString &oldRelativePath, const QString &newRelativePath); private slots: void handleSplitWindowRequest(VEditWindow *curWindow); diff --git a/src/vedittab.cpp b/src/vedittab.cpp index 3eeb6296..3627f6ac 100644 --- a/src/vedittab.cpp +++ b/src/vedittab.cpp @@ -21,7 +21,7 @@ extern VConfigManager vconfig; VEditTab::VEditTab(const QString &path, bool modifiable, QWidget *parent) : QStackedWidget(parent), mdConverterType(vconfig.getMdConverterType()) { - DocType docType = isMarkdown(path) ? DocType::Markdown : DocType::Html; + DocType docType = VUtils::isMarkdown(path) ? DocType::Markdown : DocType::Html; QString basePath = QFileInfo(path).path(); QString fileName = QFileInfo(path).fileName(); qDebug() << "VEditTab basePath" << basePath << "file" << fileName; @@ -75,23 +75,6 @@ void VEditTab::setupUI() } } -bool VEditTab::isMarkdown(const QString &name) -{ - const QVector mdPostfix({"md", "markdown", "mkd"}); - - QStringList list = name.split('.', QString::SkipEmptyParts); - if (list.isEmpty()) { - return false; - } - const QString &postfix = list.last(); - for (int i = 0; i < mdPostfix.size(); ++i) { - if (postfix == mdPostfix[i]) { - return true; - } - } - return false; -} - void VEditTab::showFileReadMode() { isEditMode = false; @@ -402,3 +385,10 @@ void VEditTab::updateCurHeader(int lineNumber) emit curHeaderChanged(curHeader); } } + +void VEditTab::updatePath(const QString &newPath) +{ + QFileInfo info(newPath); + noteFile->basePath = info.path(); + noteFile->fileName = info.fileName(); +} diff --git a/src/vedittab.h b/src/vedittab.h index 24a7623b..93059376 100644 --- a/src/vedittab.h +++ b/src/vedittab.h @@ -36,6 +36,7 @@ public: void requestUpdateOutline(); void requestUpdateCurHeader(); void scrollToAnchor(const VAnchor& anchor); + void updatePath(const QString &newPath); signals: void getFocused(); @@ -51,7 +52,6 @@ private slots: void updateTocFromHeaders(const QVector &headers); private: - bool isMarkdown(const QString &name); void setupUI(); void showFileReadMode(); void showFileEditMode(); diff --git a/src/veditwindow.cpp b/src/veditwindow.cpp index 7f666778..776f4400 100644 --- a/src/veditwindow.cpp +++ b/src/veditwindow.cpp @@ -123,21 +123,26 @@ out: return idx; } -void VEditWindow::closeFile(const QString ¬ebook, const QString &relativePath) +bool VEditWindow::closeFile(const QString ¬ebook, const QString &relativePath, bool isForced) { // Find if it has been opened already int idx = findTabByFile(notebook, relativePath); if (idx == -1) { - return; + return true; } - // Do not check if modified VEditTab *editor = getTab(idx); Q_ASSERT(editor); - removeTab(idx); - delete editor; - + bool ok = true; + if (!isForced) { + ok = editor->requestClose(); + } + if (ok) { + removeTab(idx); + delete editor; + } updateTabListMenu(); + return ok; } bool VEditWindow::closeAllFiles() @@ -155,17 +160,8 @@ bool VEditWindow::closeAllFiles() int VEditWindow::openFileInTab(const QString ¬ebook, const QString &relativePath, bool modifiable) { - QString rootPath; - const QVector ¬ebooks = vnote->getNotebooks(); - for (int i = 0; i < notebooks.size(); ++i) { - if (notebooks[i].getName() == notebook) { - rootPath = notebooks[i].getPath(); - break; - } - } - - VEditTab *editor = new VEditTab(QDir::cleanPath(QDir(rootPath).filePath(relativePath)), - modifiable); + QString path = QDir::cleanPath(QDir(vnote->getNotebookPath(notebook)).filePath(relativePath)); + VEditTab *editor = new VEditTab(path, modifiable); connect(editor, &VEditTab::getFocused, this, &VEditWindow::getFocused); connect(editor, &VEditTab::outlineChanged, @@ -200,7 +196,6 @@ int VEditWindow::findTabByFile(const QString ¬ebook, const QString &relativeP bool VEditWindow::handleTabCloseRequest(int index) { - qDebug() << "request closing tab" << index; VEditTab *editor = getTab(index); Q_ASSERT(editor); bool ok = editor->requestClose(); @@ -262,6 +257,51 @@ void VEditWindow::handleNotebookRenamed(const QVector ¬ebooks, updateTabListMenu(); } +void VEditWindow::handleDirectoryRenamed(const QString ¬ebook, const QString &oldRelativePath, + const QString &newRelativePath) +{ + QTabBar *tabs = tabBar(); + int nrTabs = tabs->count(); + for (int i = 0; i < nrTabs; ++i) { + QJsonObject tabJson = tabs->tabData(i).toJsonObject(); + if (tabJson["notebook"].toString() == notebook) { + QString relativePath = tabJson["relative_path"].toString(); + if (relativePath.startsWith(oldRelativePath)) { + relativePath.replace(0, oldRelativePath.size(), newRelativePath); + tabJson["relative_path"] = relativePath; + tabs->setTabData(i, tabJson); + tabs->setTabToolTip(i, generateTooltip(tabJson)); + QString path = QDir::cleanPath(QDir(vnote->getNotebookPath(notebook)).filePath(relativePath)); + getTab(i)->updatePath(path); + } + } + } + updateTabListMenu(); +} + +void VEditWindow::handleFileRenamed(const QString ¬ebook, const QString &oldRelativePath, + const QString &newRelativePath) +{ + QTabBar *tabs = tabBar(); + int nrTabs = tabs->count(); + for (int i = 0; i < nrTabs; ++i) { + QJsonObject tabJson = tabs->tabData(i).toJsonObject(); + if (tabJson["notebook"].toString() == notebook) { + QString relativePath = tabJson["relative_path"].toString(); + if (relativePath == oldRelativePath) { + relativePath = newRelativePath; + tabJson["relative_path"] = relativePath; + tabs->setTabData(i, tabJson); + tabs->setTabToolTip(i, generateTooltip(tabJson)); + tabs->setTabText(i, getFileName(relativePath)); + QString path = QDir::cleanPath(QDir(vnote->getNotebookPath(notebook)).filePath(relativePath)); + getTab(i)->updatePath(path); + } + } + } + updateTabListMenu(); +} + void VEditWindow::noticeTabStatus(int index) { if (index == -1) { diff --git a/src/veditwindow.h b/src/veditwindow.h index 5c785153..7d8342b2 100644 --- a/src/veditwindow.h +++ b/src/veditwindow.h @@ -22,8 +22,7 @@ public: int findTabByFile(const QString ¬ebook, const QString &relativePath) const; int openFile(const QString ¬ebook, const QString &relativePath, int mode); - // Close the file forcely - void closeFile(const QString ¬ebook, const QString &relativePath); + bool closeFile(const QString ¬ebook, const QString &relativePath, bool isForced); void editFile(); void saveFile(); void readFile(); @@ -38,6 +37,10 @@ public: // Focus to current tab's editor void focusWindow(); void scrollCurTab(const VAnchor &anchor); + void handleDirectoryRenamed(const QString ¬ebook, + const QString &oldRelativePath, const QString &newRelativePath); + void handleFileRenamed(const QString ¬ebook, + const QString &oldRelativePath, const QString &newRelativePath); protected: void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE; diff --git a/src/vfilelist.cpp b/src/vfilelist.cpp index c4961c15..ddf64d8e 100644 --- a/src/vfilelist.cpp +++ b/src/vfilelist.cpp @@ -5,6 +5,8 @@ #include "dialog/vnewfiledialog.h" #include "dialog/vfileinfodialog.h" #include "vnote.h" +#include "veditarea.h" +#include "utils/vutils.h" VFileList::VFileList(VNote *vnote, QWidget *parent) : QWidget(parent), vnote(vnote) @@ -219,6 +221,7 @@ void VFileList::deleteFile() // First close this file forcely curItemJson["notebook"] = notebook; curItemJson["relative_path"] = QDir::cleanPath(QDir(relativePath).filePath(curItemName)); + curItemJson["is_forced"] = true; emit fileDeleted(curItemJson); deleteFileAndUpdateList(curItem); @@ -399,13 +402,12 @@ void VFileList::handleNotebookRenamed(const QVector ¬ebooks, } } -// FIXME: when @oldRelativePath is part of relativePath, we also need to update relativePath partialy void VFileList::handleDirectoryRenamed(const QString ¬ebook, const QString &oldRelativePath, const QString &newRelativePath) { if (notebook == this->notebook - && oldRelativePath == relativePath) { - relativePath = newRelativePath; + && relativePath.startsWith(oldRelativePath)) { + relativePath.replace(0, oldRelativePath.size(), newRelativePath); } } @@ -417,6 +419,32 @@ void VFileList::renameFile(QListWidgetItem *item, const QString &newName) QJsonObject itemJson = item->data(Qt::UserRole).toJsonObject(); Q_ASSERT(!itemJson.isEmpty()); QString name = itemJson["name"].toString(); + + // If change the file type, we need to convert it + DocType docType = VUtils::isMarkdown(name) ? DocType::Markdown : DocType::Html; + DocType newDocType = VUtils::isMarkdown(newName) ? DocType::Markdown : DocType::Html; + if (docType != newDocType) { + QString fileRelativePath = QDir::cleanPath(QDir(relativePath).filePath(name)); + if (editArea->isFileOpened(notebook, fileRelativePath)) { + QMessageBox msgBox(QMessageBox::Warning, tr("Warning"), QString("Rename will change the note type"), + QMessageBox::Ok | QMessageBox::Cancel, this); + msgBox.setDefaultButton(QMessageBox::Ok); + msgBox.setInformativeText(QString("You should close the note %1 before continue").arg(name)); + if (QMessageBox::Ok == msgBox.exec()) { + QJsonObject curItemJson; + curItemJson["notebook"] = notebook; + curItemJson["relative_path"] = fileRelativePath; + curItemJson["is_forced"] = false; + if (!editArea->closeFile(curItemJson)) { + return; + } + } else { + return; + } + } + convertFileType(notebook, fileRelativePath, docType, newDocType); + } + QString path = QDir(rootPath).filePath(relativePath); QFile file(QDir(path).filePath(name)); QString newFilePath(QDir(path).filePath(newName)); @@ -462,3 +490,20 @@ void VFileList::renameFile(QListWidgetItem *item, const QString &newName) qDebug() << "file renamed" << oldPath << "to" << newPath; emit fileRenamed(notebook, oldPath, newPath); } + +void VFileList::convertFileType(const QString ¬ebook, const QString &fileRelativePath, + DocType oldType, DocType newType) +{ + Q_ASSERT(oldType != newType); + QString filePath = QDir(vnote->getNotebookPath(notebook)).filePath(fileRelativePath); + QString fileText = VUtils::readFileFromDisk(filePath); + QTextEdit editor; + if (oldType == DocType::Markdown) { + editor.setPlainText(fileText); + fileText = editor.toHtml(); + } else { + editor.setHtml(fileText); + fileText = editor.toPlainText(); + } + VUtils::writeFileToDisk(filePath, fileText); +} diff --git a/src/vfilelist.h b/src/vfilelist.h index 41b33a7e..234fdfba 100644 --- a/src/vfilelist.h +++ b/src/vfilelist.h @@ -6,12 +6,14 @@ #include #include #include "vnotebook.h" +#include "vconstants.h" class QAction; class VNote; class QListWidget; class QListWidgetItem; class QPushButton; +class VEditArea; class VFileList : public QWidget { @@ -19,6 +21,7 @@ class VFileList : public QWidget public: explicit VFileList(VNote *vnote, QWidget *parent = 0); bool importFile(const QString &name); + inline void setEditArea(VEditArea *editArea); signals: void fileClicked(QJsonObject fileJson); @@ -54,6 +57,8 @@ private: void clearDirectoryInfo(); inline QString getDirectoryName(); void renameFile(QListWidgetItem *item, const QString &newName); + void convertFileType(const QString ¬ebook, const QString &fileRelativePath, + DocType oldType, DocType newType); VNote *vnote; QString notebook; @@ -62,6 +67,8 @@ private: // Used for cache QString rootPath; + VEditArea *editArea; + QListWidget *fileList; // Actions @@ -78,4 +85,9 @@ inline QString VFileList::getDirectoryName() return QFileInfo(QDir::cleanPath(relativePath)).fileName(); } +inline void VFileList::setEditArea(VEditArea *editArea) +{ + this->editArea = editArea; +} + #endif // VFILELIST_H diff --git a/src/vmainwindow.cpp b/src/vmainwindow.cpp index e5edf9ef..c28676d5 100644 --- a/src/vmainwindow.cpp +++ b/src/vmainwindow.cpp @@ -76,6 +76,7 @@ void VMainWindow::setupUI() editArea = new VEditArea(vnote); editArea->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + fileList->setEditArea(editArea); // Main Splitter mainSplitter = new QSplitter(); @@ -109,6 +110,10 @@ void VMainWindow::setupUI() editArea, &VEditArea::handleNotebookRenamed); connect(editArea, &VEditArea::curTabStatusChanged, this, &VMainWindow::handleCurTabStatusChanged); + connect(directoryTree, &VDirectoryTree::directoryRenamed, + editArea, &VEditArea::handleDirectoryRenamed); + connect(fileList, &VFileList::fileRenamed, + editArea, &VEditArea::handleFileRenamed); connect(newNotebookBtn, &QPushButton::clicked, this, &VMainWindow::onNewNotebookBtnClicked);