support spliting edit windows

Signed-off-by: Le Tan <tamlokveer@gmail.com>
This commit is contained in:
Le Tan 2016-11-05 13:02:41 +08:00
parent 1bc95bcea0
commit 915c25e1a5
15 changed files with 815 additions and 286 deletions

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="512px" height="512px" viewBox="0 0 512 512" enable-background="new 0 0 512 512" xml:space="preserve">
<g id="Icon_3_">
<g>
<path d="M64,384h384v-42.666H64V384z M64,277.334h384v-42.667H64V277.334z M64,128v42.665h384V128H64z"/>
</g>
</g>
</svg>

After

Width:  |  Height:  |  Size: 626 B

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="512px" height="512px" viewBox="0 0 512 512" enable-background="new 0 0 512 512" xml:space="preserve">
<path d="M458,210.409l-145.267-12.476L256,64l-56.743,133.934L54,210.409l110.192,95.524L131.161,448L256,372.686L380.83,448
l-33.021-142.066L458,210.409z M272.531,345.286L256,335.312l-16.53,9.973l-59.988,36.191l15.879-68.296l4.369-18.79l-14.577-12.637
l-52.994-45.939l69.836-5.998l19.206-1.65l7.521-17.75l27.276-64.381l27.27,64.379l7.52,17.751l19.208,1.65l69.846,5.998
l-52.993,45.939l-14.576,12.636l4.367,18.788l15.875,68.299L272.531,345.286z"/>
</svg>

After

Width:  |  Height:  |  Size: 935 B

View File

@ -0,0 +1,9 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Generator: Adobe Illustrator 16.2.1, SVG Export Plug-In . SVG Version: 6.00 Build 0) -->
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
width="512px" height="512px" viewBox="0 0 512 512" enable-background="new 0 0 512 512" xml:space="preserve">
<path d="M458,210.409l-145.267-12.476L256,64l-56.743,133.934L54,210.409l110.192,95.524L131.161,448L256,372.686L380.83,448
l-33.021-142.066L458,210.409z M272.531,345.287L256,335.313l-0.002-189.277l27.27,64.379l7.52,17.751l19.208,1.65l69.846,5.998
l-52.993,45.939l-14.576,12.636l4.367,18.788l15.875,68.299L272.531,345.287z"/>
</svg>

After

Width:  |  Height:  |  Size: 812 B

View File

@ -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

274
src/veditarea.cpp Normal file
View File

@ -0,0 +1,274 @@
#include <QtWidgets>
#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<QPair<int, int> > VEditArea::findTabsByFile(const QString &notebook, const QString &relativePath)
{
QVector<QPair<int, int> > 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<int, int> match;
match.first = winIdx;
match.second = tabIdx;
tabs.append(match);
}
}
return tabs;
}
int VEditArea::openFileInWindow(int windowIndex, const QString &notebook, 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<VNotebook> &notebooks,
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;
}
}
}

70
src/veditarea.h Normal file
View File

@ -0,0 +1,70 @@
#ifndef VEDITAREA_H
#define VEDITAREA_H
#include <QWidget>
#include <QJsonObject>
#include <QString>
#include <QFileInfo>
#include <QDir>
#include <QVector>
#include <QPair>
#include <QtDebug>
#include <QSplitter>
#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 &notebook, 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<VNotebook> &notebooks, const QString &oldName,
const QString &newName);
private slots:
void handleSplitWindowRequest(VEditWindow *curWindow);
void handleRemoveSplitRequest(VEditWindow *curWindow);
void handleWindowFocused();
private:
void setupUI();
QVector<QPair<int, int> > findTabsByFile(const QString &notebook, const QString &relativePath);
int openFileInWindow(int windowIndex, const QString &notebook, 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<VEditWindow *>(splitter->widget(windowIndex));
}
#endif // VEDITAREA_H

View File

@ -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();
}
}

View File

@ -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

296
src/veditwindow.cpp Normal file
View File

@ -0,0 +1,296 @@
#include <QtWidgets>
#include <QtDebug>
#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 &notebook, 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 &notebook, 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 &notebook, const QString &relativePath,
bool modifiable)
{
QString 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;
}
}
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 &notebook, 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<VNotebook> &notebooks,
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 &notebook, 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);
}

84
src/veditwindow.h Normal file
View File

@ -0,0 +1,84 @@
#ifndef VEDITWINDOW_H
#define VEDITWINDOW_H
#include <QTabWidget>
#include <QJsonObject>
#include <QString>
#include <QFileInfo>
#include <QDir>
#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 &notebook, const QString &relativePath) const;
int openFile(const QString &notebook, const QString &relativePath,
int mode);
// Close the file forcely
void closeFile(const QString &notebook, const QString &relativePath);
void editFile();
void saveFile();
void readFile();
void saveAndReadFile();
void handleNotebookRenamed(const QVector<VNotebook> &notebooks, const QString &oldName,
const QString &newName);
bool closeAllFiles();
void setRemoveSplitEnable(bool enabled);
void getTabStatus(QString &notebook, 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 &notebook, 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 &notebook, 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<VEditor *>(widget(tabIndex));
}
#endif // VEDITWINDOW_H

View File

@ -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"),

View File

@ -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;

View File

@ -53,5 +53,8 @@
<file>resources/icons/expand.svg</file>
<file>resources/icons/two_panels.svg</file>
<file>resources/icons/one_panel.svg</file>
<file>resources/icons/split_window.svg</file>
<file>resources/icons/corner_menu.svg</file>
<file>resources/icons/remove_split.svg</file>
</qresource>
</RCC>

View File

@ -1,210 +0,0 @@
#include <QtWidgets>
#include <QtDebug>
#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 &notebook, const QString &relativePath,
bool modifiable)
{
QString 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;
}
}
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 &notebook, 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<VEditor *>(widget(index));
Q_ASSERT(editor);
bool ok = editor->requestClose();
if (ok) {
removeTab(index);
delete editor;
}
}
void VTabWidget::readFile()
{
VEditor *editor = dynamic_cast<VEditor *>(currentWidget());
Q_ASSERT(editor);
editor->readFile();
onCurrentChanged(currentIndex());
}
void VTabWidget::saveAndReadFile()
{
saveFile();
readFile();
onCurrentChanged(currentIndex());
}
void VTabWidget::editFile()
{
VEditor *editor = dynamic_cast<VEditor *>(currentWidget());
Q_ASSERT(editor);
editor->editFile();
onCurrentChanged(currentIndex());
}
void VTabWidget::saveFile()
{
VEditor *editor = dynamic_cast<VEditor *>(currentWidget());
Q_ASSERT(editor);
editor->saveFile();
}
void VTabWidget::handleNotebookRenamed(const QVector<VNotebook> &notebooks,
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);
}

View File

@ -1,54 +0,0 @@
#ifndef VTABWIDGET_H
#define VTABWIDGET_H
#include <QTabWidget>
#include <QJsonObject>
#include <QString>
#include <QFileInfo>
#include <QDir>
#include "vnotebook.h"
class VNote;
class VTabWidget : public QTabWidget
{
Q_OBJECT
public:
explicit VTabWidget(VNote *vnote, QWidget *parent = 0);
signals:
void tabModeChanged(const QString &notebook, 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<VNotebook> &notebooks, 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 &notebook, const QString &relativePath) const;
int openFileInTab(const QString &notebook, 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