diff --git a/src/resources/icons/corner_menu.svg b/src/resources/icons/corner_menu.svg new file mode 100644 index 00000000..edf92f3d --- /dev/null +++ b/src/resources/icons/corner_menu.svg @@ -0,0 +1,11 @@ + + + + + + + + + + diff --git a/src/resources/icons/remove_split.svg b/src/resources/icons/remove_split.svg new file mode 100644 index 00000000..42be227f --- /dev/null +++ b/src/resources/icons/remove_split.svg @@ -0,0 +1,10 @@ + + + + + + diff --git a/src/resources/icons/split_window.svg b/src/resources/icons/split_window.svg new file mode 100644 index 00000000..c68272da --- /dev/null +++ b/src/resources/icons/split_window.svg @@ -0,0 +1,9 @@ + + + + + + diff --git a/src/src.pro b/src/src.pro index ed1aac0e..3812fc37 100644 --- a/src/src.pro +++ b/src/src.pro @@ -21,7 +21,6 @@ SOURCES += main.cpp\ vconfigmanager.cpp \ vfilelist.cpp \ dialog/vnewfiledialog.cpp \ - vtabwidget.cpp \ vedit.cpp \ veditor.cpp \ vnotefile.cpp \ @@ -40,7 +39,9 @@ SOURCES += main.cpp\ veditoperations.cpp \ vmdeditoperations.cpp \ dialog/vinsertimagedialog.cpp \ - vdownloader.cpp + vdownloader.cpp \ + veditarea.cpp \ + veditwindow.cpp HEADERS += vmainwindow.h \ vdirectorytree.h \ @@ -50,7 +51,6 @@ HEADERS += vmainwindow.h \ vconfigmanager.h \ vfilelist.h \ dialog/vnewfiledialog.h \ - vtabwidget.h \ vedit.h \ veditor.h \ vconstants.h \ @@ -71,7 +71,9 @@ HEADERS += vmainwindow.h \ veditoperations.h \ vmdeditoperations.h \ dialog/vinsertimagedialog.h \ - vdownloader.h + vdownloader.h \ + veditarea.h \ + veditwindow.h RESOURCES += \ vnote.qrc diff --git a/src/veditarea.cpp b/src/veditarea.cpp new file mode 100644 index 00000000..01d53e04 --- /dev/null +++ b/src/veditarea.cpp @@ -0,0 +1,274 @@ +#include +#include "veditarea.h" +#include "veditwindow.h" +#include "veditor.h" +#include "vnote.h" +#include "vconfigmanager.h" + +VEditArea::VEditArea(VNote *vnote, QWidget *parent) + : QWidget(parent), vnote(vnote), curWindowIndex(0) +{ + setupUI(); +} + +void VEditArea::setupUI() +{ + splitter = new QSplitter(this); + + // Add a window + insertSplitWindow(0); + setCurrentWindow(0, true); + + QHBoxLayout *mainLayout = new QHBoxLayout(); + mainLayout->addWidget(splitter); + + setLayout(mainLayout); +} + +void VEditArea::insertSplitWindow(int idx) +{ + VEditWindow *win = new VEditWindow(vnote); + splitter->insertWidget(idx, win); + connect(win, &VEditWindow::tabStatusChanged, + this, &VEditArea::curTabStatusChanged); + connect(win, &VEditWindow::requestSplitWindow, + this, &VEditArea::handleSplitWindowRequest); + connect(win, &VEditWindow::requestRemoveSplit, + this, &VEditArea::handleRemoveSplitRequest); + connect(win, &VEditWindow::getFocused, + this, &VEditArea::handleWindowFocused); + + int nrWin = splitter->count(); + if (nrWin == 1) { + // Disable removing split + win->setRemoveSplitEnable(false); + } else { + // Enable removing split + for (int i = 0; i < nrWin; ++i) { + getWindow(i)->setRemoveSplitEnable(true); + } + } +} + +void VEditArea::removeSplitWindow(VEditWindow *win) +{ + if (!win) { + return; + } + win->hide(); + // Should be deleted later + win->deleteLater(); + + int nrWin = splitter->count(); + if (nrWin == 2) { + // Disable removing split + getWindow(0)->setRemoveSplitEnable(false); + getWindow(1)->setRemoveSplitEnable(false); + } else { + // Enable removing split + for (int i = 0; i < nrWin; ++i) { + getWindow(i)->setRemoveSplitEnable(true); + } + } +} + +// A given file can be opened in multiple split windows. A given file could be +// opened at most in one tab inside a window. +void VEditArea::openFile(QJsonObject fileJson) +{ + if (fileJson.isEmpty()) { + return; + } + + QString notebook = fileJson["notebook"].toString(); + QString relativePath = fileJson["relative_path"].toString(); + int mode = OpenFileMode::Read; + if (fileJson.contains("mode")) { + mode = fileJson["mode"].toInt(); + } + + qDebug() << "open notebook" << notebook << "path" << relativePath << mode; + + // Find if it has been opened already + int winIdx, tabIdx; + bool setFocus = false; + auto tabs = findTabsByFile(notebook, relativePath); + if (!tabs.empty()) { + // Current window first + winIdx = tabs[0].first; + tabIdx = tabs[0].second; + for (int i = 0; i < tabs.size(); ++i) { + if (tabs[i].first == curWindowIndex) { + winIdx = tabs[i].first; + tabIdx = tabs[i].second; + break; + } + } + setFocus = true; + goto out; + } + + // Open it in current window + winIdx = curWindowIndex; + tabIdx = openFileInWindow(winIdx, notebook, relativePath, mode); + +out: + setCurrentTab(winIdx, tabIdx, setFocus); +} + +QVector > VEditArea::findTabsByFile(const QString ¬ebook, const QString &relativePath) +{ + QVector > tabs; + int nrWin = splitter->count(); + for (int winIdx = 0; winIdx < nrWin; ++winIdx) { + VEditWindow *win = getWindow(winIdx); + int tabIdx = win->findTabByFile(notebook, relativePath); + if (tabIdx != -1) { + QPair match; + match.first = winIdx; + match.second = tabIdx; + tabs.append(match); + } + } + return tabs; +} + +int VEditArea::openFileInWindow(int windowIndex, const QString ¬ebook, const QString &relativePath, + int mode) +{ + Q_ASSERT(windowIndex < splitter->count()); + VEditWindow *win = getWindow(windowIndex); + return win->openFile(notebook, relativePath, mode); +} + +void VEditArea::setCurrentTab(int windowIndex, int tabIndex, bool setFocus) +{ + setCurrentWindow(windowIndex, setFocus); + + VEditWindow *win = getWindow(windowIndex); + win->setCurrentIndex(tabIndex); +} + +void VEditArea::setCurrentWindow(int windowIndex, bool setFocus) +{ + if (curWindowIndex == windowIndex) { + return; + } + qDebug() << "current window" << windowIndex; + curWindowIndex = windowIndex; + if (setFocus) { + getWindow(windowIndex)->focusWindow(); + } + + // Update tab status + QString notebook, relativePath; + bool editMode, modifiable; + getWindow(curWindowIndex)->getTabStatus(notebook, relativePath, editMode, modifiable); + emit curTabStatusChanged(notebook, relativePath, editMode, modifiable); +} + +void VEditArea::closeFile(QJsonObject fileJson) +{ + if (fileJson.isEmpty()) { + return; + } + QString notebook = fileJson["notebook"].toString(); + QString relativePath = fileJson["relative_path"].toString(); + + int nrWin = splitter->count(); + for (int i = 0; i < nrWin; ++i) { + VEditWindow *win = getWindow(i); + win->closeFile(notebook, relativePath); + } +} + +void VEditArea::editFile() +{ + VEditWindow *win = getWindow(curWindowIndex); + win->editFile(); +} + +void VEditArea::saveFile() +{ + VEditWindow *win = getWindow(curWindowIndex); + win->saveFile(); +} + +void VEditArea::readFile() +{ + VEditWindow *win = getWindow(curWindowIndex); + win->readFile(); +} + +void VEditArea::saveAndReadFile() +{ + VEditWindow *win = getWindow(curWindowIndex); + win->saveAndReadFile(); +} + +void VEditArea::handleNotebookRenamed(const QVector ¬ebooks, + const QString &oldName, const QString &newName) +{ + int nrWin = splitter->count(); + for (int i = 0; i < nrWin; ++i) { + VEditWindow *win = getWindow(i); + win->handleNotebookRenamed(notebooks, oldName, newName); + } +} + +void VEditArea::handleSplitWindowRequest(VEditWindow *curWindow) +{ + if (!curWindow) { + return; + } + int idx = splitter->indexOf(curWindow); + qDebug() << "window" << idx << "requests split itself"; + insertSplitWindow(++idx); + setCurrentWindow(idx, true); +} + +void VEditArea::handleRemoveSplitRequest(VEditWindow *curWindow) +{ + if (!curWindow) { + return; + } + int idx = splitter->indexOf(curWindow); + + removeSplitWindow(curWindow); + + if (idx >= splitter->count()) { + idx = splitter->count() - 1; + } + + // At least one split window left + Q_ASSERT(idx >= 0); + setCurrentWindow(idx, true); +} + +void VEditArea::mousePressEvent(QMouseEvent *event) +{ + return; + qDebug() << "VEditArea press event" << event; + QPoint pos = event->pos(); + int nrWin = splitter->count(); + for (int i = 0; i < nrWin; ++i) { + VEditWindow *win = getWindow(i); + if (win->geometry().contains(pos, true)) { + setCurrentWindow(i, true); + break; + } + } + QWidget::mousePressEvent(event); +} + +void VEditArea::handleWindowFocused() +{ + QObject *winObject = sender(); + int nrWin = splitter->count(); + for (int i = 0; i < nrWin; ++i) { + if (splitter->widget(i) == winObject) { + setCurrentWindow(i, false); + break; + } + } +} diff --git a/src/veditarea.h b/src/veditarea.h new file mode 100644 index 00000000..3c6cb6d2 --- /dev/null +++ b/src/veditarea.h @@ -0,0 +1,70 @@ +#ifndef VEDITAREA_H +#define VEDITAREA_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "vnotebook.h" +#include "veditwindow.h" + +class VNote; + +class VEditArea : public QWidget +{ + Q_OBJECT +public: + explicit VEditArea(VNote *vnote, QWidget *parent = 0); + +signals: + void curTabStatusChanged(const QString ¬ebook, const QString &relativePath, + bool editMode, bool modifiable); + +protected: + void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE; + +public slots: + void openFile(QJsonObject fileJson); + // Close the file forcely + void closeFile(QJsonObject fileJson); + void editFile(); + void saveFile(); + void readFile(); + void saveAndReadFile(); + void handleNotebookRenamed(const QVector ¬ebooks, const QString &oldName, + const QString &newName); + +private slots: + void handleSplitWindowRequest(VEditWindow *curWindow); + void handleRemoveSplitRequest(VEditWindow *curWindow); + void handleWindowFocused(); + +private: + void setupUI(); + QVector > findTabsByFile(const QString ¬ebook, const QString &relativePath); + int openFileInWindow(int windowIndex, const QString ¬ebook, const QString &relativePath, + int mode); + void setCurrentTab(int windowIndex, int tabIndex, bool setFocus); + void setCurrentWindow(int windowIndex, bool setFocus); + inline VEditWindow *getWindow(int windowIndex) const; + void insertSplitWindow(int idx); + void removeSplitWindow(VEditWindow *win); + + VNote *vnote; + int curWindowIndex; + + // Splitter holding multiple split windows + QSplitter *splitter; +}; + +inline VEditWindow* VEditArea::getWindow(int windowIndex) const +{ + return dynamic_cast(splitter->widget(windowIndex)); +} + +#endif // VEDITAREA_H diff --git a/src/veditor.cpp b/src/veditor.cpp index e6754e20..9850b342 100644 --- a/src/veditor.cpp +++ b/src/veditor.cpp @@ -32,6 +32,9 @@ VEditor::VEditor(const QString &path, bool modifiable, QWidget *parent) setupUI(); showFileReadMode(); + + connect(qApp, &QApplication::focusChanged, + this, &VEditor::handleFocusChanged); } VEditor::~VEditor() @@ -148,7 +151,7 @@ void VEditor::readFile() if (textEditor->isModified()) { // Need to save the changes QMessageBox msgBox(this); - msgBox.setText("The note has been modified."); + msgBox.setText(QString("The note \"%1\" has been modified.").arg(noteFile->fileName)); msgBox.setInformativeText("Do you want to save your changes?"); msgBox.setIcon(QMessageBox::Information); msgBox.setStandardButtons(QMessageBox::Save | QMessageBox::Discard | QMessageBox::Cancel); @@ -220,3 +223,15 @@ void VEditor::setupMarkdownPreview() addWidget(webPreviewer); } + +void VEditor::focusTab() +{ + currentWidget()->setFocus(); +} + +void VEditor::handleFocusChanged(QWidget *old, QWidget *now) +{ + if (isChild(now)) { + emit getFocused(); + } +} diff --git a/src/veditor.h b/src/veditor.h index 45de4a13..f0bbd248 100644 --- a/src/veditor.h +++ b/src/veditor.h @@ -16,6 +16,7 @@ class VNote; class VEditor : public QStackedWidget { + Q_OBJECT public: VEditor(const QString &path, bool modifiable, QWidget *parent = 0); ~VEditor(); @@ -29,6 +30,13 @@ public: inline bool getIsEditMode() const; inline bool isModified() const; + void focusTab(); + +signals: + void getFocused(); + +private slots: + void handleFocusChanged(QWidget *old, QWidget *now); private: bool isMarkdown(const QString &name); @@ -37,6 +45,7 @@ private: void showFileEditMode(); void setupMarkdownPreview(); void previewByConverter(); + inline bool isChild(QObject *obj); VNoteFile *noteFile; bool isEditMode; @@ -57,4 +66,15 @@ inline bool VEditor::isModified() const return textEditor->isModified(); } +inline bool VEditor::isChild(QObject *obj) +{ + while (obj) { + if (obj == this) { + return true; + } + obj = obj->parent(); + } + return false; +} + #endif // VEDITOR_H diff --git a/src/veditwindow.cpp b/src/veditwindow.cpp new file mode 100644 index 00000000..e425dc02 --- /dev/null +++ b/src/veditwindow.cpp @@ -0,0 +1,296 @@ +#include +#include +#include "veditwindow.h" +#include "veditor.h" +#include "vnote.h" +#include "vconfigmanager.h" + +extern VConfigManager vconfig; + +VEditWindow::VEditWindow(VNote *vnote, QWidget *parent) + : QTabWidget(parent), vnote(vnote) +{ + setupCornerWidget(); + + setTabsClosable(true); + setMovable(true); + + connect(this, &VEditWindow::tabCloseRequested, + this, &VEditWindow::handleTabCloseRequest); + connect(this, &VEditWindow::tabBarClicked, + this, &VEditWindow::handleTabbarClicked); +} + +void VEditWindow::setupCornerWidget() +{ + // Right corner button + // Actions + splitAct = new QAction(QIcon(":/resources/icons/split_window.svg"), + tr("Split"), this); + splitAct->setStatusTip(tr("Split current window vertically")); + connect(splitAct, &QAction::triggered, + this, &VEditWindow::splitWindow); + + removeSplitAct = new QAction(QIcon(":/resources/icons/remove_split.svg"), + tr("Remove split"), this); + removeSplitAct->setStatusTip(tr("Remove current split window")); + connect(removeSplitAct, &QAction::triggered, + this, &VEditWindow::removeSplit); + + rightBtn = new QPushButton(QIcon(":/resources/icons/corner_menu.svg"), + "", this); + QMenu *rightMenu = new QMenu(this); + rightMenu->addAction(splitAct); + rightMenu->addAction(removeSplitAct); + rightBtn->setMenu(rightMenu); + + setCornerWidget(rightBtn, Qt::TopRightCorner); +} + +void VEditWindow::splitWindow() +{ + emit requestSplitWindow(this); +} + +void VEditWindow::removeSplit() +{ + // Close all the files one by one + // If user do not want to close a file, just stop removing + if (closeAllFiles()) { + Q_ASSERT(count() == 0); + emit requestRemoveSplit(this); + } +} + +void VEditWindow::setRemoveSplitEnable(bool enabled) +{ + removeSplitAct->setVisible(enabled); +} + +void VEditWindow::openWelcomePage() +{ + int idx = openFileInTab("", vconfig.getWelcomePagePath(), false); + setTabText(idx, "Welcome to VNote"); + setTabToolTip(idx, "VNote"); +} + +int VEditWindow::insertTabWithData(int index, QWidget *page, + const QJsonObject &tabData) +{ + QString label = getFileName(tabData["relative_path"].toString()); + int idx = insertTab(index, page, label); + QTabBar *tabs = tabBar(); + tabs->setTabData(idx, tabData); + noticeTabStatus(currentIndex()); + return idx; +} + +int VEditWindow::appendTabWithData(QWidget *page, const QJsonObject &tabData) +{ + return insertTabWithData(count(), page, tabData); +} + +int VEditWindow::openFile(const QString ¬ebook, const QString &relativePath, int mode) +{ + // Find if it has been opened already + int idx = findTabByFile(notebook, relativePath); + if (idx > -1) { + goto out; + } + idx = openFileInTab(notebook, relativePath, true); +out: + setCurrentIndex(idx); + if (mode == OpenFileMode::Edit) { + editFile(); + } + focusWindow(); + return idx; +} + +void VEditWindow::closeFile(const QString ¬ebook, const QString &relativePath) +{ + // Find if it has been opened already + int idx = findTabByFile(notebook, relativePath); + if (idx == -1) { + return; + } + + // Do not check if modified + VEditor *editor = getTab(idx); + Q_ASSERT(editor); + removeTab(idx); + delete editor; +} + +bool VEditWindow::closeAllFiles() +{ + int nrTab = count(); + for (int i = 0; i < nrTab; ++i) { + // Always close the first tab + if (!handleTabCloseRequest(0)) { + return false; + } + } + return true; +} + +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; + } + } + + VEditor *editor = new VEditor(QDir::cleanPath(QDir(rootPath).filePath(relativePath)), + modifiable); + connect(editor, &VEditor::getFocused, + this, &VEditWindow::getFocused); + + QJsonObject tabJson; + tabJson["notebook"] = notebook; + tabJson["relative_path"] = relativePath; + tabJson["modifiable"] = modifiable; + return appendTabWithData(editor, tabJson); +} + +int VEditWindow::findTabByFile(const QString ¬ebook, const QString &relativePath) const +{ + QTabBar *tabs = tabBar(); + int nrTabs = tabs->count(); + + for (int i = 0; i < nrTabs; ++i) { + QJsonObject tabJson = tabs->tabData(i).toJsonObject(); + if (tabJson["notebook"] == notebook && tabJson["relative_path"] == relativePath) { + return i; + } + } + return -1; +} + +bool VEditWindow::handleTabCloseRequest(int index) +{ + qDebug() << "request closing tab" << index; + VEditor *editor = getTab(index); + Q_ASSERT(editor); + bool ok = editor->requestClose(); + if (ok) { + removeTab(index); + delete editor; + } + noticeTabStatus(currentIndex()); + // User clicks the close button. We should make this window + // to be current window. + emit getFocused(); + return ok; +} + +void VEditWindow::readFile() +{ + VEditor *editor = getTab(currentIndex()); + Q_ASSERT(editor); + editor->readFile(); + noticeTabStatus(currentIndex()); +} + +void VEditWindow::saveAndReadFile() +{ + saveFile(); + readFile(); + noticeTabStatus(currentIndex()); +} + +void VEditWindow::editFile() +{ + VEditor *editor = getTab(currentIndex()); + Q_ASSERT(editor); + editor->editFile(); + noticeTabStatus(currentIndex()); +} + +void VEditWindow::saveFile() +{ + VEditor *editor = getTab(currentIndex()); + Q_ASSERT(editor); + editor->saveFile(); +} + +void VEditWindow::handleNotebookRenamed(const QVector ¬ebooks, + const QString &oldName, const QString &newName) +{ + QTabBar *tabs = tabBar(); + int nrTabs = tabs->count(); + for (int i = 0; i < nrTabs; ++i) { + QJsonObject tabJson = tabs->tabData(i).toJsonObject(); + if (tabJson["notebook"] == oldName) { + tabJson["notebook"] = newName; + tabs->setTabData(i, tabJson); + } + } +} + +void VEditWindow::noticeTabStatus(int index) +{ + if (index == -1) { + emit tabStatusChanged("", "", false, false); + return; + } + + QJsonObject tabJson = tabBar()->tabData(index).toJsonObject(); + Q_ASSERT(!tabJson.isEmpty()); + + QString notebook = tabJson["notebook"].toString(); + QString relativePath = tabJson["relative_path"].toString(); + VEditor *editor = getTab(index); + bool editMode = editor->getIsEditMode(); + bool modifiable = tabJson["modifiable"].toBool(); + + emit tabStatusChanged(notebook, relativePath, + editMode, modifiable); +} + +void VEditWindow::getTabStatus(QString ¬ebook, QString &relativePath, + bool &editMode, bool &modifiable) const +{ + int idx = currentIndex(); + if (idx == -1) { + notebook = relativePath = ""; + editMode = modifiable = false; + return; + } + + QJsonObject tabJson = tabBar()->tabData(idx).toJsonObject(); + Q_ASSERT(!tabJson.isEmpty()); + notebook = tabJson["notebook"].toString(); + relativePath = tabJson["relative_path"].toString(); + VEditor *editor = getTab(idx); + editMode = editor->getIsEditMode(); + modifiable = tabJson["modifiable"].toBool(); +} + +void VEditWindow::focusWindow() +{ + int idx = currentIndex(); + if (idx == -1) { + setFocus(); + return; + } + getTab(idx)->focusTab(); +} + +void VEditWindow::handleTabbarClicked(int index) +{ + // The child will emit getFocused here + focusWindow(); + noticeTabStatus(index); +} + +void VEditWindow::mousePressEvent(QMouseEvent *event) +{ + emit getFocused(); + QTabWidget::mousePressEvent(event); +} diff --git a/src/veditwindow.h b/src/veditwindow.h new file mode 100644 index 00000000..e2e41680 --- /dev/null +++ b/src/veditwindow.h @@ -0,0 +1,84 @@ +#ifndef VEDITWINDOW_H +#define VEDITWINDOW_H + +#include +#include +#include +#include +#include +#include "vnotebook.h" +#include "veditor.h" + +class VNote; +class QPushButton; + +class VEditWindow : public QTabWidget +{ + Q_OBJECT +public: + explicit VEditWindow(VNote *vnote, QWidget *parent = 0); + 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); + void editFile(); + void saveFile(); + void readFile(); + void saveAndReadFile(); + void handleNotebookRenamed(const QVector ¬ebooks, const QString &oldName, + const QString &newName); + bool closeAllFiles(); + void setRemoveSplitEnable(bool enabled); + void getTabStatus(QString ¬ebook, QString &relativePath, + bool &editMode, bool &modifiable) const; + // Focus to current tab's editor + void focusWindow(); + +protected: + void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE; + +signals: + void tabStatusChanged(const QString ¬ebook, const QString &relativePath, + bool editMode, bool modifiable); + void requestSplitWindow(VEditWindow *curWindow); + void requestRemoveSplit(VEditWindow *curWindow); + // This widget or its children get the focus + void getFocused(); + +private slots: + bool handleTabCloseRequest(int index); + void splitWindow(); + void removeSplit(); + void handleTabbarClicked(int index); + +private: + void setupCornerWidget(); + void openWelcomePage(); + int insertTabWithData(int index, QWidget *page, const QJsonObject &tabData); + int appendTabWithData(QWidget *page, const QJsonObject &tabData); + int openFileInTab(const QString ¬ebook, const QString &relativePath, bool modifiable); + inline QString getFileName(const QString &relativePath) const; + inline VEditor *getTab(int tabIndex) const; + void noticeTabStatus(int index); + + VNote *vnote; + // Button in the right corner + QPushButton *rightBtn; + + // Actions + QAction *splitAct; + QAction *removeSplitAct; +}; + +inline QString VEditWindow::getFileName(const QString &path) const +{ + return QFileInfo(QDir::cleanPath(path)).fileName(); +} + +inline VEditor* VEditWindow::getTab(int tabIndex) const +{ + return dynamic_cast(widget(tabIndex)); +} + +#endif // VEDITWINDOW_H diff --git a/src/vmainwindow.cpp b/src/vmainwindow.cpp index ec71eb81..9bd7746e 100644 --- a/src/vmainwindow.cpp +++ b/src/vmainwindow.cpp @@ -4,11 +4,11 @@ #include "vdirectorytree.h" #include "vnote.h" #include "vfilelist.h" -#include "vtabwidget.h" #include "vconfigmanager.h" #include "dialog/vnewnotebookdialog.h" #include "dialog/vnotebookinfodialog.h" #include "utils/vutils.h" +#include "veditarea.h" extern VConfigManager vconfig; @@ -79,15 +79,14 @@ void VMainWindow::setupUI() fileList = new VFileList(vnote); fileList->setSizePolicy(QSizePolicy::Maximum, QSizePolicy::Expanding); - // Editor tab widget - tabs = new VTabWidget(vnote); - tabs->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + editArea = new VEditArea(vnote); + editArea->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); // Main Splitter mainSplitter = new QSplitter(); mainSplitter->addWidget(nbContainer); mainSplitter->addWidget(fileList); - mainSplitter->addWidget(tabs); + mainSplitter->addWidget(editArea); mainSplitter->setStretchFactor(0, 0); mainSplitter->setStretchFactor(1, 0); mainSplitter->setStretchFactor(2, 1); @@ -106,14 +105,14 @@ void VMainWindow::setupUI() this, &VMainWindow::handleFileListDirectoryChanged); connect(fileList, &VFileList::fileClicked, - tabs, &VTabWidget::openFile); + editArea, &VEditArea::openFile); connect(fileList, &VFileList::fileDeleted, - tabs, &VTabWidget::closeFile); + editArea, &VEditArea::closeFile); connect(fileList, &VFileList::fileCreated, - tabs, &VTabWidget::openFile); + editArea, &VEditArea::openFile); connect(vnote, &VNote::notebooksRenamed, - tabs, &VTabWidget::handleNotebookRenamed); - connect(tabs, &VTabWidget::tabModeChanged, + editArea, &VEditArea::handleNotebookRenamed); + connect(editArea, &VEditArea::curTabStatusChanged, this, &VMainWindow::updateToolbarFromTabChage); connect(newNotebookBtn, &QPushButton::clicked, @@ -160,26 +159,26 @@ void VMainWindow::initActions() tr("&Edit"), this); editNoteAct->setStatusTip(tr("Edit current note")); connect(editNoteAct, &QAction::triggered, - tabs, &VTabWidget::editFile); + editArea, &VEditArea::editFile); discardExitAct = new QAction(QIcon(":/resources/icons/discard_exit.svg"), tr("Discard changes and exit"), this); discardExitAct->setStatusTip(tr("Discard changes and exit edit mode")); connect(discardExitAct, &QAction::triggered, - tabs, &VTabWidget::readFile); + editArea, &VEditArea::readFile); saveExitAct = new QAction(QIcon(":/resources/icons/save_exit.svg"), tr("Save changes and exit"), this); saveExitAct->setStatusTip(tr("Save changes and exit edit mode")); connect(saveExitAct, &QAction::triggered, - tabs, &VTabWidget::saveAndReadFile); + editArea, &VEditArea::saveAndReadFile); saveNoteAct = new QAction(QIcon(":/resources/icons/save_note.svg"), tr("&Save"), this); saveNoteAct->setStatusTip(tr("Save current note")); saveNoteAct->setShortcut(QKeySequence::Save); connect(saveNoteAct, &QAction::triggered, - tabs, &VTabWidget::saveFile); + editArea, &VEditArea::saveFile); viewAct = new QActionGroup(this); twoPanelViewAct = new QAction(QIcon(":/resources/icons/two_panels.svg"), diff --git a/src/vmainwindow.h b/src/vmainwindow.h index e1a8b1a0..977f754d 100644 --- a/src/vmainwindow.h +++ b/src/vmainwindow.h @@ -11,12 +11,12 @@ class QListWidget; class QTabWidget; class QToolBar; class VNote; -class VTabWidget; class QAction; class QPushButton; class VNotebook; class QActionGroup; class VFileList; +class VEditArea; class VMainWindow : public QMainWindow { @@ -65,6 +65,7 @@ private: // If true, comboBox changes will not trigger any signal out bool notebookComboMuted; + VNote *vnote; QLabel *notebookLabel; QLabel *directoryLabel; @@ -77,9 +78,8 @@ private: QPushButton *dirInfoBtn; VFileList *fileList; VDirectoryTree *directoryTree; - VTabWidget *tabs; QSplitter *mainSplitter; - VNote *vnote; + VEditArea *editArea; // Actions QAction *newNoteAct; diff --git a/src/vnote.qrc b/src/vnote.qrc index 6dd624de..4a40ded6 100644 --- a/src/vnote.qrc +++ b/src/vnote.qrc @@ -53,5 +53,8 @@ resources/icons/expand.svg resources/icons/two_panels.svg resources/icons/one_panel.svg + resources/icons/split_window.svg + resources/icons/corner_menu.svg + resources/icons/remove_split.svg diff --git a/src/vtabwidget.cpp b/src/vtabwidget.cpp deleted file mode 100644 index a655ab64..00000000 --- a/src/vtabwidget.cpp +++ /dev/null @@ -1,210 +0,0 @@ -#include -#include -#include "vtabwidget.h" -#include "veditor.h" -#include "vnote.h" -#include "vconfigmanager.h" - -extern VConfigManager vconfig; - -VTabWidget::VTabWidget(VNote *vnote, QWidget *parent) - : QTabWidget(parent), vnote(vnote) -{ - setTabsClosable(true); - setMovable(true); - connect(this, &VTabWidget::tabCloseRequested, - this, &VTabWidget::handleTabCloseRequest); - connect(this, &VTabWidget::currentChanged, - this, &VTabWidget::onCurrentChanged); -} - -void VTabWidget::openWelcomePage() -{ - int idx = openFileInTab("", vconfig.getWelcomePagePath(), false); - setTabText(idx, "Welcome to VNote"); - setTabToolTip(idx, "VNote"); -} - -int VTabWidget::insertTabWithData(int index, QWidget *page, - const QJsonObject &tabData) -{ - QString label = getFileName(tabData["relative_path"].toString()); - int idx = insertTab(index, page, label); - QTabBar *tabs = tabBar(); - tabs->setTabData(idx, tabData); - - // Need to update again with tabData - onCurrentChanged(idx); - return idx; -} - -int VTabWidget::appendTabWithData(QWidget *page, const QJsonObject &tabData) -{ - return insertTabWithData(count(), page, tabData); -} - -void VTabWidget::openFile(QJsonObject fileJson) -{ - if (fileJson.isEmpty()) { - return; - } - - QString notebook = fileJson["notebook"].toString(); - QString relativePath = fileJson["relative_path"].toString(); - int mode = OpenFileMode::Read; - if (fileJson.contains("mode")) { - mode = fileJson["mode"].toInt(); - } - - qDebug() << "open notebook" << notebook << "path" << relativePath << mode; - - // Find if it has been opened already - int idx = findTabByFile(notebook, relativePath); - if (idx > -1) { - goto out; - } - - idx = openFileInTab(notebook, relativePath, true); - -out: - setCurrentIndex(idx); - if (mode == OpenFileMode::Edit) { - editFile(); - } -} - -void VTabWidget::closeFile(QJsonObject fileJson) -{ - if (fileJson.isEmpty()) { - return; - } - qDebug() << "close file:" << fileJson; - - QString notebook = fileJson["notebook"].toString(); - QString relativePath = fileJson["relative_path"].toString(); - - // Find if it has been opened already - int idx = findTabByFile(notebook, relativePath); - if (idx == -1) { - return; - } - - QWidget* page = widget(idx); - Q_ASSERT(page); - removeTab(idx); - delete page; -} - -int VTabWidget::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; - } - } - - VEditor *editor = new VEditor(QDir::cleanPath(QDir(rootPath).filePath(relativePath)), - modifiable); - QJsonObject tabJson; - tabJson["notebook"] = notebook; - tabJson["relative_path"] = relativePath; - tabJson["modifiable"] = modifiable; - return appendTabWithData(editor, tabJson); -} - -int VTabWidget::findTabByFile(const QString ¬ebook, const QString &relativePath) const -{ - QTabBar *tabs = tabBar(); - int nrTabs = tabs->count(); - - for (int i = 0; i < nrTabs; ++i) { - QJsonObject tabJson = tabs->tabData(i).toJsonObject(); - if (tabJson["notebook"] == notebook && tabJson["relative_path"] == relativePath) { - return i; - } - } - return -1; -} - -void VTabWidget::handleTabCloseRequest(int index) -{ - qDebug() << "request closing tab" << index; - VEditor *editor = dynamic_cast(widget(index)); - Q_ASSERT(editor); - bool ok = editor->requestClose(); - if (ok) { - removeTab(index); - delete editor; - } -} - -void VTabWidget::readFile() -{ - VEditor *editor = dynamic_cast(currentWidget()); - Q_ASSERT(editor); - editor->readFile(); - onCurrentChanged(currentIndex()); -} - -void VTabWidget::saveAndReadFile() -{ - saveFile(); - readFile(); - onCurrentChanged(currentIndex()); -} - -void VTabWidget::editFile() -{ - VEditor *editor = dynamic_cast(currentWidget()); - Q_ASSERT(editor); - editor->editFile(); - onCurrentChanged(currentIndex()); -} - -void VTabWidget::saveFile() -{ - VEditor *editor = dynamic_cast(currentWidget()); - Q_ASSERT(editor); - editor->saveFile(); -} - -void VTabWidget::handleNotebookRenamed(const QVector ¬ebooks, - const QString &oldName, const QString &newName) -{ - QTabBar *tabs = tabBar(); - int nrTabs = tabs->count(); - for (int i = 0; i < nrTabs; ++i) { - QJsonObject tabJson = tabs->tabData(i).toJsonObject(); - if (tabJson["notebook"] == oldName) { - tabJson["notebook"] = newName; - tabs->setTabData(i, tabJson); - } - } -} - -void VTabWidget::onCurrentChanged(int index) -{ - if (index == -1) { - emit tabModeChanged("", "", false, false); - return; - } - - QJsonObject tabJson = tabBar()->tabData(index).toJsonObject(); - if (tabJson.isEmpty()) { - // Maybe the tab data has not been set yet - return; - } - - QString notebook = tabJson["notebook"].toString(); - QString relativePath = tabJson["relative_path"].toString(); - VEditor *editor = (VEditor *)widget(index); - bool editMode = editor->getIsEditMode(); - bool modifiable = tabJson["modifiable"].toBool(); - - emit tabModeChanged(notebook, relativePath, - editMode, modifiable); -} diff --git a/src/vtabwidget.h b/src/vtabwidget.h deleted file mode 100644 index e082228a..00000000 --- a/src/vtabwidget.h +++ /dev/null @@ -1,54 +0,0 @@ -#ifndef VTABWIDGET_H -#define VTABWIDGET_H - -#include -#include -#include -#include -#include -#include "vnotebook.h" - -class VNote; - -class VTabWidget : public QTabWidget -{ - Q_OBJECT -public: - explicit VTabWidget(VNote *vnote, QWidget *parent = 0); - -signals: - void tabModeChanged(const QString ¬ebook, const QString &relativePath, - bool editMode, bool modifiable); - -public slots: - void openFile(QJsonObject fileJson); - // Close the file forcely - void closeFile(QJsonObject fileJson); - void editFile(); - void saveFile(); - void readFile(); - void saveAndReadFile(); - void handleNotebookRenamed(const QVector ¬ebooks, const QString &oldName, - const QString &newName); - -private slots: - void handleTabCloseRequest(int index); - void onCurrentChanged(int index); - -private: - void openWelcomePage(); - int insertTabWithData(int index, QWidget *page, const QJsonObject &tabData); - int appendTabWithData(QWidget *page, const QJsonObject &tabData); - int findTabByFile(const QString ¬ebook, const QString &relativePath) const; - int openFileInTab(const QString ¬ebook, const QString &relativePath, bool modifiable); - inline QString getFileName(const QString &relativePath) const; - - VNote *vnote; -}; - -inline QString VTabWidget::getFileName(const QString &path) const -{ - return QFileInfo(QDir::cleanPath(path)).fileName(); -} - -#endif // VTABWIDGET_H