mirror of
https://gitee.com/vnotex/vnote.git
synced 2025-07-05 13:59:52 +08:00
support copy and paste notes
Signed-off-by: Le Tan <tamlokveer@gmail.com>
This commit is contained in:
parent
810d2f02f4
commit
85d9456f02
11
src/resources/icons/copy.svg
Normal file
11
src/resources/icons/copy.svg
Normal 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 |
23
src/resources/icons/cut.svg
Normal file
23
src/resources/icons/cut.svg
Normal 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 |
14
src/resources/icons/paste.svg
Normal file
14
src/resources/icons/paste.svg
Normal 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 |
@ -42,7 +42,8 @@ SOURCES += main.cpp\
|
||||
veditwindow.cpp \
|
||||
vedittab.cpp \
|
||||
voutline.cpp \
|
||||
vtoc.cpp
|
||||
vtoc.cpp \
|
||||
vfilelocation.cpp
|
||||
|
||||
HEADERS += vmainwindow.h \
|
||||
vdirectorytree.h \
|
||||
@ -73,7 +74,8 @@ HEADERS += vmainwindow.h \
|
||||
veditwindow.h \
|
||||
vedittab.h \
|
||||
voutline.h \
|
||||
vtoc.h
|
||||
vtoc.h \
|
||||
vfilelocation.h
|
||||
|
||||
RESOURCES += \
|
||||
vnote.qrc
|
||||
|
@ -4,6 +4,11 @@
|
||||
#include <QDebug>
|
||||
#include <QRegularExpression>
|
||||
#include <QRegExp>
|
||||
#include <QClipboard>
|
||||
#include <QApplication>
|
||||
#include <QMimeData>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonDocument>
|
||||
|
||||
VUtils::VUtils()
|
||||
{
|
||||
@ -159,3 +164,42 @@ void VUtils::makeDirectory(const QString &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;
|
||||
}
|
||||
|
@ -6,6 +6,7 @@
|
||||
#include <QVector>
|
||||
#include <QPair>
|
||||
#include "vconfigmanager.h"
|
||||
#include "vconstants.h"
|
||||
|
||||
class VUtils
|
||||
{
|
||||
@ -25,6 +26,8 @@ public:
|
||||
static QString basePathFromPath(const QString &path);
|
||||
static QVector<QString> imagesFromMarkdownFile(const QString &filePath);
|
||||
static void makeDirectory(const QString &path);
|
||||
static ClipboardOpType opTypeInClipboard();
|
||||
static bool copyFile(const QString &p_srcFilePath, const QString &p_destFilePath, bool p_isCut);
|
||||
private:
|
||||
static inline void addQssVarToMap(QVector<QPair<QString, QString> > &map,
|
||||
const QString &key, const QString &value);
|
||||
|
@ -2,5 +2,6 @@
|
||||
#define VCONSTANTS_H
|
||||
|
||||
enum class DocType { Html, Markdown };
|
||||
enum class ClipboardOpType { Invalid, CopyFile, CopyDir };
|
||||
|
||||
#endif
|
||||
|
@ -242,13 +242,14 @@ void VEditArea::handleDirectoryRenamed(const QString ¬ebook, const QString &o
|
||||
updateWindowStatus();
|
||||
}
|
||||
|
||||
void VEditArea::handleFileRenamed(const QString ¬ebook,
|
||||
const QString &oldRelativePath, const QString &newRelativePath)
|
||||
void VEditArea::handleFileRenamed(const QString &p_srcNotebook, const QString &p_srcRelativePath,
|
||||
const QString &p_destNotebook, const QString &p_destRelativePath)
|
||||
{
|
||||
qDebug() << "fileRenamed" << p_srcNotebook << p_srcRelativePath << p_destNotebook << p_destRelativePath;
|
||||
int nrWin = splitter->count();
|
||||
for (int i = 0; i < nrWin; ++i) {
|
||||
VEditWindow *win = getWindow(i);
|
||||
win->handleFileRenamed(notebook, oldRelativePath, newRelativePath);
|
||||
win->handleFileRenamed(p_srcNotebook, p_srcRelativePath, p_destNotebook, p_destRelativePath);
|
||||
}
|
||||
updateWindowStatus();
|
||||
}
|
||||
|
@ -44,8 +44,8 @@ public slots:
|
||||
void handleOutlineItemActivated(const VAnchor &anchor);
|
||||
void handleDirectoryRenamed(const QString ¬ebook,
|
||||
const QString &oldRelativePath, const QString &newRelativePath);
|
||||
void handleFileRenamed(const QString ¬ebook,
|
||||
const QString &oldRelativePath, const QString &newRelativePath);
|
||||
void handleFileRenamed(const QString &p_srcNotebook, const QString &p_srcRelativePath,
|
||||
const QString &p_destNotebook, const QString &p_destRelativePath);
|
||||
|
||||
private slots:
|
||||
void handleSplitWindowRequest(VEditWindow *curWindow);
|
||||
|
@ -280,23 +280,23 @@ void VEditWindow::handleDirectoryRenamed(const QString ¬ebook, const QString
|
||||
updateTabListMenu();
|
||||
}
|
||||
|
||||
void VEditWindow::handleFileRenamed(const QString ¬ebook, const QString &oldRelativePath,
|
||||
const QString &newRelativePath)
|
||||
void VEditWindow::handleFileRenamed(const QString &p_srcNotebook, const QString &p_srcRelativePath,
|
||||
const QString &p_destNotebook, const QString &p_destRelativePath)
|
||||
{
|
||||
QTabBar *tabs = tabBar();
|
||||
int nrTabs = tabs->count();
|
||||
for (int i = 0; i < nrTabs; ++i) {
|
||||
QJsonObject tabJson = tabs->tabData(i).toJsonObject();
|
||||
if (tabJson["notebook"].toString() == notebook) {
|
||||
if (tabJson["notebook"].toString() == p_srcNotebook) {
|
||||
QString relativePath = tabJson["relative_path"].toString();
|
||||
if (relativePath == oldRelativePath) {
|
||||
if (relativePath == p_srcRelativePath) {
|
||||
VEditTab *tab = getTab(i);
|
||||
relativePath = newRelativePath;
|
||||
tabJson["relative_path"] = relativePath;
|
||||
tabJson["notebook"] = p_destNotebook;
|
||||
tabJson["relative_path"] = p_destRelativePath;
|
||||
tabs->setTabData(i, tabJson);
|
||||
tabs->setTabToolTip(i, generateTooltip(tabJson));
|
||||
tabs->setTabText(i, generateTabText(VUtils::fileNameFromPath(relativePath), tab->isModified()));
|
||||
QString path = QDir::cleanPath(QDir(vnote->getNotebookPath(notebook)).filePath(relativePath));
|
||||
tabs->setTabText(i, generateTabText(VUtils::fileNameFromPath(p_destRelativePath), tab->isModified()));
|
||||
QString path = QDir::cleanPath(QDir(vnote->getNotebookPath(p_destNotebook)).filePath(p_destRelativePath));
|
||||
tab->updatePath(path);
|
||||
}
|
||||
}
|
||||
|
@ -39,8 +39,8 @@ public:
|
||||
void scrollCurTab(const VAnchor &anchor);
|
||||
void handleDirectoryRenamed(const QString ¬ebook,
|
||||
const QString &oldRelativePath, const QString &newRelativePath);
|
||||
void handleFileRenamed(const QString ¬ebook,
|
||||
const QString &oldRelativePath, const QString &newRelativePath);
|
||||
void handleFileRenamed(const QString &p_srcNotebook, const QString &p_srcRelativePath,
|
||||
const QString &p_destNotebook, const QString &p_destRelativePath);
|
||||
|
||||
protected:
|
||||
void mousePressEvent(QMouseEvent *event) Q_DECL_OVERRIDE;
|
||||
|
@ -51,6 +51,24 @@ void VFileList::initActions()
|
||||
fileInfoAct->setStatusTip(tr("View and edit current note's information"));
|
||||
connect(fileInfoAct, &QAction::triggered,
|
||||
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)
|
||||
@ -88,6 +106,7 @@ void VFileList::updateFileList()
|
||||
{
|
||||
QString path = QDir(rootPath).filePath(relativePath);
|
||||
|
||||
fileList->clear();
|
||||
if (!QDir(path).exists()) {
|
||||
qDebug() << "invalid notebook directory:" << path;
|
||||
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;
|
||||
continue;
|
||||
}
|
||||
renameFile(p_notebook, p_relativePath, name);
|
||||
copyFile(p_notebook, p_relativePath, p_notebook,
|
||||
QDir(VUtils::basePathFromPath(p_relativePath)).filePath(name), true);
|
||||
}
|
||||
break;
|
||||
} while (true);
|
||||
@ -252,8 +272,13 @@ void VFileList::contextMenuRequested(QPoint pos)
|
||||
if (item) {
|
||||
menu.addAction(deleteFileAct);
|
||||
menu.addAction(fileInfoAct);
|
||||
menu.addAction(copyAct);
|
||||
menu.addAction(cutAct);
|
||||
}
|
||||
|
||||
if (VUtils::opTypeInClipboard() == ClipboardOpType::CopyFile) {
|
||||
menu.addAction(pasteAct);
|
||||
}
|
||||
menu.exec(fileList->mapToGlobal(pos));
|
||||
}
|
||||
|
||||
@ -306,52 +331,20 @@ QListWidgetItem* VFileList::createFileAndUpdateList(const QString &name)
|
||||
file.close();
|
||||
qDebug() << "create file:" << filePath;
|
||||
|
||||
// Update current directory's config file to include this new file
|
||||
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;
|
||||
if (!addFileInConfig(filePath, 0)) {
|
||||
file.remove();
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return insertFileListItem(fileJson, true);
|
||||
return insertFileListItem(readFileInConfig(filePath), true);
|
||||
}
|
||||
|
||||
void VFileList::deleteFileAndUpdateList(const QString &p_notebook,
|
||||
const QString &p_relativePath)
|
||||
{
|
||||
QString path = QDir(vnote->getNotebookPath(p_notebook)).filePath(VUtils::basePathFromPath(p_relativePath));
|
||||
QString fileName = VUtils::fileNameFromPath(p_relativePath);
|
||||
QString filePath = QDir(path).filePath(fileName);
|
||||
QString filePath = QDir(vnote->getNotebookPath(p_notebook)).filePath(p_relativePath);
|
||||
|
||||
// Update current directory's config file to exclude this file
|
||||
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;
|
||||
if (!removeFileInConfig(filePath)) {
|
||||
return;
|
||||
}
|
||||
|
||||
@ -446,86 +439,6 @@ void VFileList::handleDirectoryRenamed(const QString ¬ebook,
|
||||
}
|
||||
}
|
||||
|
||||
// @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 ¬ebook, const QString &fileRelativePath,
|
||||
DocType oldType, DocType newType)
|
||||
{
|
||||
@ -559,3 +472,258 @@ void VFileList::deleteLocalImages(const QString &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();
|
||||
}
|
||||
|
@ -29,8 +29,8 @@ signals:
|
||||
void fileClicked(QJsonObject fileJson);
|
||||
void fileDeleted(QJsonObject fileJson);
|
||||
void fileCreated(QJsonObject fileJson);
|
||||
void fileRenamed(const QString ¬ebook, const QString &oldPath,
|
||||
const QString &newPath);
|
||||
void fileRenamed(const QString &p_srcNotebook, const QString &p_srcRelativePath,
|
||||
const QString &p_destNotebook, const QString &p_destRelativePath);
|
||||
void directoryChanged(const QString ¬ebook, const QString &relativePath);
|
||||
|
||||
private slots:
|
||||
@ -38,6 +38,9 @@ private slots:
|
||||
void handleItemClicked(QListWidgetItem *currentItem);
|
||||
void curFileInfo();
|
||||
void deleteCurFile();
|
||||
void copySelectedFiles(bool p_isCut = false);
|
||||
void cutSelectedFiles();
|
||||
void pasteFilesInCurDir();
|
||||
|
||||
public slots:
|
||||
void setDirectory(QJsonObject dirJson);
|
||||
@ -58,12 +61,18 @@ private:
|
||||
void deleteFileAndUpdateList(const QString &p_notebook,
|
||||
const QString &p_relativePath);
|
||||
void clearDirectoryInfo();
|
||||
void renameFile(const QString &p_notebook,
|
||||
const QString &p_relativePath, const QString &p_newName);
|
||||
void convertFileType(const QString ¬ebook, const QString &fileRelativePath,
|
||||
DocType oldType, DocType newType);
|
||||
QListWidgetItem *findItem(const QString &p_notebook, const QString &p_relativePath);
|
||||
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;
|
||||
QString notebook;
|
||||
@ -80,6 +89,9 @@ private:
|
||||
QAction *newFileAct;
|
||||
QAction *deleteFileAct;
|
||||
QAction *fileInfoAct;
|
||||
QAction *copyAct;
|
||||
QAction *cutAct;
|
||||
QAction *pasteAct;
|
||||
};
|
||||
|
||||
inline void VFileList::setEditArea(VEditArea *editArea)
|
||||
|
10
src/vfilelocation.cpp
Normal file
10
src/vfilelocation.cpp
Normal 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
15
src/vfilelocation.h
Normal 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
|
@ -64,5 +64,8 @@
|
||||
<file>resources/vnote.qss</file>
|
||||
<file>resources/icons/note_info_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>
|
||||
</RCC>
|
||||
|
Loading…
x
Reference in New Issue
Block a user