support copy and paste notes

Signed-off-by: Le Tan <tamlokveer@gmail.com>
This commit is contained in:
Le Tan 2016-11-17 23:04:55 +08:00
parent 810d2f02f4
commit 85d9456f02
16 changed files with 445 additions and 138 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" style="enable-background:new 0 0 512 512;" xml:space="preserve">
<g>
<polygon points="304,96 288,96 288,176 368,176 368,160 304,160 "/>
<path d="M325.3,64H160v48h-48v336h240v-48h48V139L325.3,64z M336,432H128V128h32v272h176V432z M384,384H176V80h142.7l65.3,65.6V384
z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 698 B

View File

@ -0,0 +1,23 @@
<?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>
<path d="M405.178,115.667c13.314-32.667,17.309-64-5.326-83.667L255.726,224l-16.976,23c0,0-27.627,40.011-37.28,58.667
s-19.306,39.333-27.294,54c-7.01,12.871-10.438,15.221-14.322,11.548c-0.506-0.591-1.026-1.168-1.553-1.736
c-0.037-0.047-0.073-0.09-0.11-0.138c-1.143-1.472-2.75-3.002-4.635-4.467C144.195,356.795,132.548,352,119.92,352
C89.037,352,64,380.653,64,416s25.037,64,55.92,64c25.282,0,46.635-19.205,53.553-45.561l-0.004,0.043
c0,0,13.355-41.482,32.661-71.482c19.306-30,49.596-43,49.596-43l31.954-32C287.68,288,391.863,148.334,405.178,115.667z
M119.92,448c-15.418,0-27.918-14.353-27.918-32s12.5-32,27.918-32c15.419,0,27.918,14.353,27.918,32S135.339,448,119.92,448z
M256,288c-8.836,0-16-7.163-16-16c0-8.836,7.164-16,16-16c8.837,0,16,7.164,16,16C272,280.837,264.837,288,256,288z"/>
<path d="M207.28,265.255c9.18-14.114,17.671-26.43,18.304-27.346l0.143-0.208l0.15-0.203l16.976-23l0.038-0.052l0.039-0.052
l2.941-3.918L111.896,32c-22.634,19.667-18.64,51-5.326,83.667C116.523,140.087,177.249,224.29,207.28,265.255z"/>
<path d="M391.828,352c-12.628,0-24.275,4.795-33.637,12.874c-1.885,1.465-3.492,2.995-4.635,4.467
c-0.037,0.048-0.072,0.091-0.109,0.138c-0.526,0.568-1.047,1.146-1.553,1.736c-3.884,3.673-7.312,1.323-14.322-11.548
c-7.988-14.667-17.641-35.344-27.294-54c-1.77-3.421-4.146-7.561-6.843-12.038c-1.272,1.712-2.264,3.043-2.932,3.938l-0.688,0.924
l-0.813,0.815l-28.688,28.729c10.433,6.855,24.565,18.276,35.306,34.965c19.305,30,32.66,71.482,32.66,71.482l-0.004-0.043
C345.193,460.795,366.546,480,391.828,480C422.711,480,448,451.347,448,416S422.711,352,391.828,352z M391.828,448
c-15.42,0-27.918-14.353-27.918-32s12.498-32,27.918-32c15.418,0,27.918,14.353,27.918,32S407.246,448,391.828,448z"/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 2.2 KiB

View File

@ -0,0 +1,14 @@
<?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>
<path d="M255.988,32C160.473,32,78.934,91.804,46.727,176h34.639c9.396-20.484,22.457-39.35,38.868-55.762
C156.497,83.973,204.709,64,255.988,64c51.286,0,99.504,19.973,135.771,56.239C428.027,156.505,448,204.719,448,256
c0,51.285-19.973,99.501-56.239,135.765C355.494,428.029,307.275,448,255.988,448c-51.281,0-99.493-19.971-135.755-56.234
C103.821,375.354,90.76,356.486,81.362,336H46.725c32.206,84.201,113.746,144,209.264,144C379.703,480,480,379.715,480,256
C480,132.298,379.703,32,255.988,32z"/>
<polygon points="206.863,323.883 229.49,346.51 320,256 229.49,165.49 206.862,188.118 258.745,240 32,240 32,272 258.745,272 "/>
</g>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View File

@ -42,7 +42,8 @@ SOURCES += main.cpp\
veditwindow.cpp \ veditwindow.cpp \
vedittab.cpp \ vedittab.cpp \
voutline.cpp \ voutline.cpp \
vtoc.cpp vtoc.cpp \
vfilelocation.cpp
HEADERS += vmainwindow.h \ HEADERS += vmainwindow.h \
vdirectorytree.h \ vdirectorytree.h \
@ -73,7 +74,8 @@ HEADERS += vmainwindow.h \
veditwindow.h \ veditwindow.h \
vedittab.h \ vedittab.h \
voutline.h \ voutline.h \
vtoc.h vtoc.h \
vfilelocation.h
RESOURCES += \ RESOURCES += \
vnote.qrc vnote.qrc

View File

@ -4,6 +4,11 @@
#include <QDebug> #include <QDebug>
#include <QRegularExpression> #include <QRegularExpression>
#include <QRegExp> #include <QRegExp>
#include <QClipboard>
#include <QApplication>
#include <QMimeData>
#include <QJsonObject>
#include <QJsonDocument>
VUtils::VUtils() VUtils::VUtils()
{ {
@ -159,3 +164,42 @@ void VUtils::makeDirectory(const QString &path)
qDebug() << "mkdir" << path; qDebug() << "mkdir" << path;
} }
} }
ClipboardOpType VUtils::opTypeInClipboard()
{
QClipboard *clipboard = QApplication::clipboard();
const QMimeData *mimeData = clipboard->mimeData();
if (mimeData->hasText()) {
QString text = mimeData->text();
QJsonObject clip = QJsonDocument::fromJson(text.toLocal8Bit()).object();
if (clip.contains("operation")) {
return (ClipboardOpType)clip["operation"].toInt();
}
}
return ClipboardOpType::Invalid;
}
bool VUtils::copyFile(const QString &p_srcFilePath, const QString &p_destFilePath, bool p_isCut)
{
QString srcPath = QDir::cleanPath(p_srcFilePath);
QString destPath = QDir::cleanPath(p_destFilePath);
if (srcPath == destPath) {
return true;
}
if (p_isCut) {
QFile file(srcPath);
if (!file.rename(destPath)) {
qWarning() << "error: fail to copy file" << srcPath << destPath;
return false;
}
} else {
if (!QFile::copy(srcPath, destPath)) {
qWarning() << "error: fail to copy file" << srcPath << destPath;
return false;
}
}
return true;
}

View File

@ -6,6 +6,7 @@
#include <QVector> #include <QVector>
#include <QPair> #include <QPair>
#include "vconfigmanager.h" #include "vconfigmanager.h"
#include "vconstants.h"
class VUtils class VUtils
{ {
@ -25,6 +26,8 @@ public:
static QString basePathFromPath(const QString &path); static QString basePathFromPath(const QString &path);
static QVector<QString> imagesFromMarkdownFile(const QString &filePath); static QVector<QString> imagesFromMarkdownFile(const QString &filePath);
static void makeDirectory(const QString &path); static void makeDirectory(const QString &path);
static ClipboardOpType opTypeInClipboard();
static bool copyFile(const QString &p_srcFilePath, const QString &p_destFilePath, bool p_isCut);
private: private:
static inline void addQssVarToMap(QVector<QPair<QString, QString> > &map, static inline void addQssVarToMap(QVector<QPair<QString, QString> > &map,
const QString &key, const QString &value); const QString &key, const QString &value);

View File

@ -2,5 +2,6 @@
#define VCONSTANTS_H #define VCONSTANTS_H
enum class DocType { Html, Markdown }; enum class DocType { Html, Markdown };
enum class ClipboardOpType { Invalid, CopyFile, CopyDir };
#endif #endif

View File

@ -242,13 +242,14 @@ void VEditArea::handleDirectoryRenamed(const QString &notebook, const QString &o
updateWindowStatus(); updateWindowStatus();
} }
void VEditArea::handleFileRenamed(const QString &notebook, void VEditArea::handleFileRenamed(const QString &p_srcNotebook, const QString &p_srcRelativePath,
const QString &oldRelativePath, const QString &newRelativePath) const QString &p_destNotebook, const QString &p_destRelativePath)
{ {
qDebug() << "fileRenamed" << p_srcNotebook << p_srcRelativePath << p_destNotebook << p_destRelativePath;
int nrWin = splitter->count(); int nrWin = splitter->count();
for (int i = 0; i < nrWin; ++i) { for (int i = 0; i < nrWin; ++i) {
VEditWindow *win = getWindow(i); VEditWindow *win = getWindow(i);
win->handleFileRenamed(notebook, oldRelativePath, newRelativePath); win->handleFileRenamed(p_srcNotebook, p_srcRelativePath, p_destNotebook, p_destRelativePath);
} }
updateWindowStatus(); updateWindowStatus();
} }

View File

@ -44,8 +44,8 @@ public slots:
void handleOutlineItemActivated(const VAnchor &anchor); void handleOutlineItemActivated(const VAnchor &anchor);
void handleDirectoryRenamed(const QString &notebook, void handleDirectoryRenamed(const QString &notebook,
const QString &oldRelativePath, const QString &newRelativePath); const QString &oldRelativePath, const QString &newRelativePath);
void handleFileRenamed(const QString &notebook, void handleFileRenamed(const QString &p_srcNotebook, const QString &p_srcRelativePath,
const QString &oldRelativePath, const QString &newRelativePath); const QString &p_destNotebook, const QString &p_destRelativePath);
private slots: private slots:
void handleSplitWindowRequest(VEditWindow *curWindow); void handleSplitWindowRequest(VEditWindow *curWindow);

View File

@ -280,23 +280,23 @@ void VEditWindow::handleDirectoryRenamed(const QString &notebook, const QString
updateTabListMenu(); updateTabListMenu();
} }
void VEditWindow::handleFileRenamed(const QString &notebook, const QString &oldRelativePath, void VEditWindow::handleFileRenamed(const QString &p_srcNotebook, const QString &p_srcRelativePath,
const QString &newRelativePath) const QString &p_destNotebook, const QString &p_destRelativePath)
{ {
QTabBar *tabs = tabBar(); QTabBar *tabs = tabBar();
int nrTabs = tabs->count(); int nrTabs = tabs->count();
for (int i = 0; i < nrTabs; ++i) { for (int i = 0; i < nrTabs; ++i) {
QJsonObject tabJson = tabs->tabData(i).toJsonObject(); QJsonObject tabJson = tabs->tabData(i).toJsonObject();
if (tabJson["notebook"].toString() == notebook) { if (tabJson["notebook"].toString() == p_srcNotebook) {
QString relativePath = tabJson["relative_path"].toString(); QString relativePath = tabJson["relative_path"].toString();
if (relativePath == oldRelativePath) { if (relativePath == p_srcRelativePath) {
VEditTab *tab = getTab(i); VEditTab *tab = getTab(i);
relativePath = newRelativePath; tabJson["notebook"] = p_destNotebook;
tabJson["relative_path"] = relativePath; tabJson["relative_path"] = p_destRelativePath;
tabs->setTabData(i, tabJson); tabs->setTabData(i, tabJson);
tabs->setTabToolTip(i, generateTooltip(tabJson)); tabs->setTabToolTip(i, generateTooltip(tabJson));
tabs->setTabText(i, generateTabText(VUtils::fileNameFromPath(relativePath), tab->isModified())); tabs->setTabText(i, generateTabText(VUtils::fileNameFromPath(p_destRelativePath), tab->isModified()));
QString path = QDir::cleanPath(QDir(vnote->getNotebookPath(notebook)).filePath(relativePath)); QString path = QDir::cleanPath(QDir(vnote->getNotebookPath(p_destNotebook)).filePath(p_destRelativePath));
tab->updatePath(path); tab->updatePath(path);
} }
} }

View File

@ -39,8 +39,8 @@ public:
void scrollCurTab(const VAnchor &anchor); void scrollCurTab(const VAnchor &anchor);
void handleDirectoryRenamed(const QString &notebook, void handleDirectoryRenamed(const QString &notebook,
const QString &oldRelativePath, const QString &newRelativePath); const QString &oldRelativePath, const QString &newRelativePath);
void handleFileRenamed(const QString &notebook, void handleFileRenamed(const QString &p_srcNotebook, const QString &p_srcRelativePath,
const QString &oldRelativePath, const QString &newRelativePath); const QString &p_destNotebook, const QString &p_destRelativePath);
protected: protected:
void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE; void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE;

View File

@ -51,6 +51,24 @@ void VFileList::initActions()
fileInfoAct->setStatusTip(tr("View and edit current note's information")); fileInfoAct->setStatusTip(tr("View and edit current note's information"));
connect(fileInfoAct, &QAction::triggered, connect(fileInfoAct, &QAction::triggered,
this, &VFileList::curFileInfo); this, &VFileList::curFileInfo);
copyAct = new QAction(QIcon(":/resources/icons/copy.svg"),
tr("&Copy"), this);
copyAct->setStatusTip(tr("Copy selected notes"));
connect(copyAct, &QAction::triggered,
this, &VFileList::copySelectedFiles);
cutAct = new QAction(QIcon(":/resources/icons/cut.svg"),
tr("&Cut"), this);
cutAct->setStatusTip(tr("Cut selected notes"));
connect(cutAct, &QAction::triggered,
this, &VFileList::cutSelectedFiles);
pasteAct = new QAction(QIcon(":/resources/icons/paste.svg"),
tr("&Paste"), this);
pasteAct->setStatusTip(tr("Paste notes"));
connect(pasteAct, &QAction::triggered,
this, &VFileList::pasteFilesInCurDir);
} }
void VFileList::setDirectory(QJsonObject dirJson) void VFileList::setDirectory(QJsonObject dirJson)
@ -88,6 +106,7 @@ void VFileList::updateFileList()
{ {
QString path = QDir(rootPath).filePath(relativePath); QString path = QDir(rootPath).filePath(relativePath);
fileList->clear();
if (!QDir(path).exists()) { if (!QDir(path).exists()) {
qDebug() << "invalid notebook directory:" << path; qDebug() << "invalid notebook directory:" << path;
QMessageBox msgBox(QMessageBox::Warning, tr("Warning"), tr("Invalid notebook directory."), QMessageBox msgBox(QMessageBox::Warning, tr("Warning"), tr("Invalid notebook directory."),
@ -144,7 +163,8 @@ void VFileList::fileInfo(const QString &p_notebook, const QString &p_relativePat
defaultName = name; defaultName = name;
continue; continue;
} }
renameFile(p_notebook, p_relativePath, name); copyFile(p_notebook, p_relativePath, p_notebook,
QDir(VUtils::basePathFromPath(p_relativePath)).filePath(name), true);
} }
break; break;
} while (true); } while (true);
@ -252,8 +272,13 @@ void VFileList::contextMenuRequested(QPoint pos)
if (item) { if (item) {
menu.addAction(deleteFileAct); menu.addAction(deleteFileAct);
menu.addAction(fileInfoAct); menu.addAction(fileInfoAct);
menu.addAction(copyAct);
menu.addAction(cutAct);
} }
if (VUtils::opTypeInClipboard() == ClipboardOpType::CopyFile) {
menu.addAction(pasteAct);
}
menu.exec(fileList->mapToGlobal(pos)); menu.exec(fileList->mapToGlobal(pos));
} }
@ -306,52 +331,20 @@ QListWidgetItem* VFileList::createFileAndUpdateList(const QString &name)
file.close(); file.close();
qDebug() << "create file:" << filePath; qDebug() << "create file:" << filePath;
// Update current directory's config file to include this new file if (!addFileInConfig(filePath, 0)) {
QJsonObject dirJson = VConfigManager::readDirectoryConfig(path);
Q_ASSERT(!dirJson.isEmpty());
QJsonObject fileJson;
fileJson["name"] = name;
QJsonArray fileArray = dirJson["files"].toArray();
fileArray.push_front(fileJson);
dirJson["files"] = fileArray;
if (!VConfigManager::writeDirectoryConfig(path, dirJson)) {
qWarning() << "error: fail to update directory's configuration file to add a new file"
<< name;
file.remove(); file.remove();
return NULL; return NULL;
} }
return insertFileListItem(fileJson, true); return insertFileListItem(readFileInConfig(filePath), true);
} }
void VFileList::deleteFileAndUpdateList(const QString &p_notebook, void VFileList::deleteFileAndUpdateList(const QString &p_notebook,
const QString &p_relativePath) const QString &p_relativePath)
{ {
QString path = QDir(vnote->getNotebookPath(p_notebook)).filePath(VUtils::basePathFromPath(p_relativePath)); QString filePath = QDir(vnote->getNotebookPath(p_notebook)).filePath(p_relativePath);
QString fileName = VUtils::fileNameFromPath(p_relativePath);
QString filePath = QDir(path).filePath(fileName);
// Update current directory's config file to exclude this file if (!removeFileInConfig(filePath)) {
QJsonObject dirJson = VConfigManager::readDirectoryConfig(path);
Q_ASSERT(!dirJson.isEmpty());
QJsonArray fileArray = dirJson["files"].toArray();
bool deleted = false;
for (int i = 0; i < fileArray.size(); ++i) {
QJsonObject ele = fileArray[i].toObject();
if (ele["name"].toString() == fileName) {
fileArray.removeAt(i);
deleted = true;
break;
}
}
if (!deleted) {
qWarning() << "error: fail to find" << fileName << "to delete";
return;
}
dirJson["files"] = fileArray;
if (!VConfigManager::writeDirectoryConfig(path, dirJson)) {
qWarning() << "error: fail to update directory's configuration file to delete"
<< fileName;
return; return;
} }
@ -446,86 +439,6 @@ void VFileList::handleDirectoryRenamed(const QString &notebook,
} }
} }
// @p_relativePath contains the flie name
void VFileList::renameFile(const QString &p_notebook,
const QString &p_relativePath, const QString &p_newName)
{
QString name = VUtils::fileNameFromPath(p_relativePath);
// If change the file type, we need to convert it
DocType docType = VUtils::isMarkdown(name) ? DocType::Markdown : DocType::Html;
DocType newDocType = VUtils::isMarkdown(p_newName) ? DocType::Markdown : DocType::Html;
if (docType != newDocType) {
if (editArea->isFileOpened(p_notebook, p_relativePath)) {
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"] = p_notebook;
curItemJson["relative_path"] = p_relativePath;
curItemJson["is_forced"] = false;
if (!editArea->closeFile(curItemJson)) {
return;
}
} else {
return;
}
}
convertFileType(p_notebook, p_relativePath, docType, newDocType);
}
QString path = QDir(vnote->getNotebookPath(p_notebook)).filePath(VUtils::basePathFromPath(p_relativePath));
QFile file(QDir(path).filePath(name));
QString newFilePath(QDir(path).filePath(p_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(p_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"] = p_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" << p_newName;
file.rename(name);
return;
}
// Update item
QListWidgetItem *item = findItem(p_notebook, p_relativePath);
if (item) {
QJsonObject itemJson = item->data(Qt::UserRole).toJsonObject();
itemJson["name"] = p_newName;
item->setData(Qt::UserRole, itemJson);
item->setText(p_newName);
}
QString oldPath = QDir::cleanPath(p_relativePath);
QString newPath = QDir::cleanPath(QDir(VUtils::basePathFromPath(p_relativePath)).filePath(p_newName));
qDebug() << "file renamed" << oldPath << "to" << newPath;
emit fileRenamed(p_notebook, oldPath, newPath);
}
void VFileList::convertFileType(const QString &notebook, const QString &fileRelativePath, void VFileList::convertFileType(const QString &notebook, const QString &fileRelativePath,
DocType oldType, DocType newType) DocType oldType, DocType newType)
{ {
@ -559,3 +472,258 @@ void VFileList::deleteLocalImages(const QString &filePath)
} }
qDebug() << "delete" << deleted << "images for" << filePath; qDebug() << "delete" << deleted << "images for" << filePath;
} }
void VFileList::copySelectedFiles(bool p_isCut)
{
QList<QListWidgetItem *> items = fileList->selectedItems();
if (items.isEmpty()) {
return;
}
QJsonArray files;
QDir dir(relativePath);
for (int i = 0; i < items.size(); ++i) {
QJsonObject itemJson = items[i]->data(Qt::UserRole).toJsonObject();
QString itemName = itemJson["name"].toString();
QJsonObject fileJson;
fileJson["notebook"] = notebook;
fileJson["relative_path"] = dir.filePath(itemName);
files.append(fileJson);
}
copyFileInfoToClipboard(files, p_isCut);
}
void VFileList::cutSelectedFiles()
{
copySelectedFiles(true);
}
void VFileList::copyFileInfoToClipboard(const QJsonArray &p_files, bool p_isCut)
{
QJsonObject clip;
clip["operation"] = (int)ClipboardOpType::CopyFile;
clip["is_cut"] = p_isCut;
clip["sources"] = p_files;
QClipboard *clipboard = QApplication::clipboard();
clipboard->setText(QJsonDocument(clip).toJson(QJsonDocument::Compact));
}
void VFileList::pasteFilesInCurDir()
{
pasteFiles(notebook, relativePath);
}
void VFileList::pasteFiles(const QString &p_notebook, const QString &p_dirRelativePath)
{
qDebug() << "paste files to" << p_notebook << p_dirRelativePath;
QClipboard *clipboard = QApplication::clipboard();
QString text = clipboard->text();
QJsonObject clip = QJsonDocument::fromJson(text.toLocal8Bit()).object();
Q_ASSERT(!clip.isEmpty() && clip["operation"] == (int)ClipboardOpType::CopyFile);
bool isCut = clip["is_cut"].toBool();
QJsonArray sources = clip["sources"].toArray();
int nrFiles = sources.size();
QDir destDir(p_dirRelativePath);
int nrPasted = 0;
for (int i = 0; i < nrFiles; ++i) {
QJsonObject file = sources[i].toObject();
QString srcNotebook = file["notebook"].toString();
QString srcRelativePath = file["relative_path"].toString();
bool ret = copyFile(srcNotebook, srcRelativePath, p_notebook,
destDir.filePath(VUtils::fileNameFromPath(srcRelativePath)), isCut);
if (ret) {
nrPasted++;
}
}
qDebug() << "pasted" << nrPasted << "files sucessfully";
clipboard->clear();
}
bool VFileList::copyFile(const QString &p_srcNotebook, const QString &p_srcRelativePath,
const QString &p_destNotebook, const QString &p_destRelativePath,
bool p_isCut)
{
QString srcPath = QDir(vnote->getNotebookPath(p_srcNotebook)).filePath(p_srcRelativePath);
srcPath = QDir::cleanPath(srcPath);
QString destPath = QDir(vnote->getNotebookPath(p_destNotebook)).filePath(p_destRelativePath);
destPath = QDir::cleanPath(destPath);
if (srcPath == destPath) {
return true;
}
// If change the file type, we need to convert it
bool needConversion = false;
DocType docType = VUtils::isMarkdown(srcPath) ? DocType::Markdown : DocType::Html;
DocType newDocType = VUtils::isMarkdown(destPath) ? DocType::Markdown : DocType::Html;
if (docType != newDocType) {
if (editArea->isFileOpened(p_srcNotebook, p_srcRelativePath)) {
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(VUtils::fileNameFromPath(p_srcRelativePath)));
if (QMessageBox::Ok == msgBox.exec()) {
QJsonObject curItemJson;
curItemJson["notebook"] = p_srcNotebook;
curItemJson["relative_path"] = p_srcRelativePath;
curItemJson["is_forced"] = false;
if (!editArea->closeFile(curItemJson)) {
return false;
}
} else {
return false;
}
}
// Convert it later
needConversion = true;
}
QVector<QString> images;
if (docType == DocType::Markdown) {
images = VUtils::imagesFromMarkdownFile(srcPath);
}
// Copy the file
if (!VUtils::copyFile(srcPath, destPath, p_isCut)) {
QMessageBox msgBox(QMessageBox::Warning, tr("Warning"), QString("Fail to copy %1 from %2.")
.arg(p_srcRelativePath).arg(p_srcNotebook), QMessageBox::Ok, this);
msgBox.setInformativeText(QString("Please check if there already exists a file with the same name"));
msgBox.exec();
return false;
}
if (needConversion) {
convertFileType(p_destNotebook, p_destRelativePath, docType, newDocType);
}
// We need to copy images when it is still markdown
if (!images.isEmpty()) {
if (newDocType == DocType::Markdown) {
QString dirPath = QDir(VUtils::basePathFromPath(destPath)).filePath("images");
VUtils::makeDirectory(dirPath);
int nrPasted = 0;
for (int i = 0; i < images.size(); ++i) {
if (!QFile(images[i]).exists()) {
continue;
}
QString destImagePath = QDir(dirPath).filePath(VUtils::fileNameFromPath(images[i]));
if (VUtils::copyFile(images[i], destImagePath, p_isCut)) {
nrPasted++;
} else {
QMessageBox msgBox(QMessageBox::Warning, tr("Warning"), QString("Fail to copy image %1.")
.arg(images[i]), QMessageBox::Ok, this);
msgBox.setInformativeText(QString("Please check if there already exists a file with the same name and manually copy it"));
msgBox.exec();
}
}
qDebug() << "pasted" << nrPasted << "images sucessfully";
} else {
// Delete the images
for (int i = 0; i < images.size(); ++i) {
QFile file(images[i]);
file.remove();
}
}
}
int idx = -1;
if (p_isCut) {
// Remove src in the config
idx = removeFileInConfig(srcPath);
if (VUtils::basePathFromPath(srcPath) != VUtils::basePathFromPath(destPath)) {
idx = -1;
}
}
// Add dest in the config
addFileInConfig(destPath, idx);
updateFileList();
if (p_isCut) {
emit fileRenamed(p_srcNotebook, p_srcRelativePath,
p_destNotebook, p_destRelativePath);
}
return true;
}
int VFileList::removeFileInConfig(const QString &p_filePath)
{
QString dirPath = VUtils::basePathFromPath(p_filePath);
QString fileName = VUtils::fileNameFromPath(p_filePath);
// Update current directory's config file to exclude this file
QJsonObject dirJson = VConfigManager::readDirectoryConfig(dirPath);
Q_ASSERT(!dirJson.isEmpty());
QJsonArray fileArray = dirJson["files"].toArray();
bool deleted = false;
int idx = -1;
for (int i = 0; i < fileArray.size(); ++i) {
QJsonObject ele = fileArray[i].toObject();
if (ele["name"].toString() == fileName) {
fileArray.removeAt(i);
deleted = true;
idx = i;
break;
}
}
if (!deleted) {
qWarning() << "error: fail to find" << fileName << "to delete";
return idx;
}
dirJson["files"] = fileArray;
if (!VConfigManager::writeDirectoryConfig(dirPath, dirJson)) {
qWarning() << "error: fail to update directory's configuration file to delete"
<< fileName;
return idx;
}
return idx;
}
// @index = -1, add it to the end of the list
bool VFileList::addFileInConfig(const QString &p_filePath, int p_index)
{
QString dirPath = VUtils::basePathFromPath(p_filePath);
QString fileName = VUtils::fileNameFromPath(p_filePath);
// Update current directory's config file to include this file
QJsonObject dirJson = VConfigManager::readDirectoryConfig(dirPath);
Q_ASSERT(!dirJson.isEmpty());
QJsonObject fileJson;
fileJson["name"] = fileName;
QJsonArray fileArray = dirJson["files"].toArray();
if (p_index == -1) {
p_index = fileArray.size();
}
fileArray.insert(p_index, fileJson);
dirJson["files"] = fileArray;
if (!VConfigManager::writeDirectoryConfig(dirPath, dirJson)) {
qWarning() << "error: fail to update directory's configuration file to add a new file"
<< fileName;
return false;
}
return true;
}
QJsonObject VFileList::readFileInConfig(const QString &p_filePath)
{
QString dirPath = VUtils::basePathFromPath(p_filePath);
QString fileName = VUtils::fileNameFromPath(p_filePath);
QJsonObject dirJson = VConfigManager::readDirectoryConfig(dirPath);
Q_ASSERT(!dirJson.isEmpty());
qDebug() << "config" << p_filePath;
QJsonArray fileArray = dirJson["files"].toArray();
for (int i = 0; i < fileArray.size(); ++i) {
QJsonObject ele = fileArray[i].toObject();
if (ele["name"].toString() == fileName) {
return ele;
}
}
return QJsonObject();
}

View File

@ -29,8 +29,8 @@ signals:
void fileClicked(QJsonObject fileJson); void fileClicked(QJsonObject fileJson);
void fileDeleted(QJsonObject fileJson); void fileDeleted(QJsonObject fileJson);
void fileCreated(QJsonObject fileJson); void fileCreated(QJsonObject fileJson);
void fileRenamed(const QString &notebook, const QString &oldPath, void fileRenamed(const QString &p_srcNotebook, const QString &p_srcRelativePath,
const QString &newPath); const QString &p_destNotebook, const QString &p_destRelativePath);
void directoryChanged(const QString &notebook, const QString &relativePath); void directoryChanged(const QString &notebook, const QString &relativePath);
private slots: private slots:
@ -38,6 +38,9 @@ private slots:
void handleItemClicked(QListWidgetItem *currentItem); void handleItemClicked(QListWidgetItem *currentItem);
void curFileInfo(); void curFileInfo();
void deleteCurFile(); void deleteCurFile();
void copySelectedFiles(bool p_isCut = false);
void cutSelectedFiles();
void pasteFilesInCurDir();
public slots: public slots:
void setDirectory(QJsonObject dirJson); void setDirectory(QJsonObject dirJson);
@ -58,12 +61,18 @@ private:
void deleteFileAndUpdateList(const QString &p_notebook, void deleteFileAndUpdateList(const QString &p_notebook,
const QString &p_relativePath); const QString &p_relativePath);
void clearDirectoryInfo(); void clearDirectoryInfo();
void renameFile(const QString &p_notebook,
const QString &p_relativePath, const QString &p_newName);
void convertFileType(const QString &notebook, const QString &fileRelativePath, void convertFileType(const QString &notebook, const QString &fileRelativePath,
DocType oldType, DocType newType); DocType oldType, DocType newType);
QListWidgetItem *findItem(const QString &p_notebook, const QString &p_relativePath); QListWidgetItem *findItem(const QString &p_notebook, const QString &p_relativePath);
void deleteLocalImages(const QString &filePath); void deleteLocalImages(const QString &filePath);
void copyFileInfoToClipboard(const QJsonArray &p_files, bool p_isCut);
void pasteFiles(const QString &p_notebook, const QString &p_dirRelativePath);
bool copyFile(const QString &p_srcNotebook, const QString &p_srcRelativePath,
const QString &p_destNotebook, const QString &p_destRelativePath,
bool p_isCut);
int removeFileInConfig(const QString &p_filePath);
bool addFileInConfig(const QString &p_filePath, int p_index);
QJsonObject readFileInConfig(const QString &p_filePath);
VNote *vnote; VNote *vnote;
QString notebook; QString notebook;
@ -80,6 +89,9 @@ private:
QAction *newFileAct; QAction *newFileAct;
QAction *deleteFileAct; QAction *deleteFileAct;
QAction *fileInfoAct; QAction *fileInfoAct;
QAction *copyAct;
QAction *cutAct;
QAction *pasteAct;
}; };
inline void VFileList::setEditArea(VEditArea *editArea) inline void VFileList::setEditArea(VEditArea *editArea)

10
src/vfilelocation.cpp Normal file
View File

@ -0,0 +1,10 @@
#include "vfilelocation.h"
VFileLocation::VFileLocation()
{
}
VFileLocation::VFileLocation(const QString &p_notebook, const QString &p_relativePath)
: m_notebook(p_notebook), m_relativePath(p_relativePath)
{
}

15
src/vfilelocation.h Normal file
View File

@ -0,0 +1,15 @@
#ifndef VFILELOCATION_H
#define VFILELOCATION_H
#include <QString>
class VFileLocation
{
public:
VFileLocation();
VFileLocation(const QString &p_notebook, const QString &p_relativePath);
QString m_notebook;
QString m_relativePath;
};
#endif // VFILELOCATION_H

View File

@ -64,5 +64,8 @@
<file>resources/vnote.qss</file> <file>resources/vnote.qss</file>
<file>resources/icons/note_info_tb.svg</file> <file>resources/icons/note_info_tb.svg</file>
<file>resources/icons/delete_note_tb.svg</file> <file>resources/icons/delete_note_tb.svg</file>
<file>resources/icons/copy.svg</file>
<file>resources/icons/cut.svg</file>
<file>resources/icons/paste.svg</file>
</qresource> </qresource>
</RCC> </RCC>