|
|
@@ -12,9 +12,13 @@
|
|
|
#include "vconfigmanager.h"
|
|
|
#include "vmdedit.h"
|
|
|
#include "vmdtab.h"
|
|
|
+#include "dialog/vconfirmdeletiondialog.h"
|
|
|
+#include "dialog/vsortdialog.h"
|
|
|
+#include "vmainwindow.h"
|
|
|
|
|
|
extern VConfigManager *g_config;
|
|
|
extern VNote *g_vnote;
|
|
|
+extern VMainWindow *g_mainWin;
|
|
|
|
|
|
const QString VFileList::c_infoShortcutSequence = "F2";
|
|
|
const QString VFileList::c_copyShortcutSequence = "Ctrl+C";
|
|
|
@@ -34,7 +38,6 @@ void VFileList::setupUI()
|
|
|
fileList = new QListWidget(this);
|
|
|
fileList->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
|
fileList->setSelectionMode(QAbstractItemView::ExtendedSelection);
|
|
|
- fileList->setDragDropMode(QAbstractItemView::InternalMove);
|
|
|
fileList->setObjectName("FileList");
|
|
|
|
|
|
QVBoxLayout *mainLayout = new QVBoxLayout;
|
|
|
@@ -45,8 +48,6 @@ void VFileList::setupUI()
|
|
|
this, &VFileList::contextMenuRequested);
|
|
|
connect(fileList, &QListWidget::itemClicked,
|
|
|
this, &VFileList::handleItemClicked);
|
|
|
- connect(fileList->model(), &QAbstractItemModel::rowsMoved,
|
|
|
- this, &VFileList::handleRowsMoved);
|
|
|
|
|
|
setLayout(mainLayout);
|
|
|
}
|
|
|
@@ -78,7 +79,7 @@ void VFileList::initShortcuts()
|
|
|
pasteShortcut->setContext(Qt::WidgetWithChildrenShortcut);
|
|
|
connect(pasteShortcut, &QShortcut::activated,
|
|
|
this, [this](){
|
|
|
- pasteFilesInCurDir();
|
|
|
+ pasteFilesFromClipboard();
|
|
|
});
|
|
|
}
|
|
|
|
|
|
@@ -136,7 +137,7 @@ void VFileList::initActions()
|
|
|
tr("&Delete"), this);
|
|
|
deleteFileAct->setToolTip(tr("Delete selected note"));
|
|
|
connect(deleteFileAct, SIGNAL(triggered(bool)),
|
|
|
- this, SLOT(deleteFile()));
|
|
|
+ this, SLOT(deleteSelectedFiles()));
|
|
|
|
|
|
fileInfoAct = new QAction(QIcon(":/resources/icons/note_info.svg"),
|
|
|
tr("&Info\t%1").arg(VUtils::getShortcutText(c_infoShortcutSequence)), this);
|
|
|
@@ -160,12 +161,19 @@ void VFileList::initActions()
|
|
|
tr("&Paste\t%1").arg(VUtils::getShortcutText(c_pasteShortcutSequence)), this);
|
|
|
pasteAct->setToolTip(tr("Paste notes in current folder"));
|
|
|
connect(pasteAct, &QAction::triggered,
|
|
|
- this, &VFileList::pasteFilesInCurDir);
|
|
|
+ this, &VFileList::pasteFilesFromClipboard);
|
|
|
|
|
|
m_openLocationAct = new QAction(tr("&Open Note Location"), this);
|
|
|
m_openLocationAct->setToolTip(tr("Open the folder containing this note in operating system"));
|
|
|
connect(m_openLocationAct, &QAction::triggered,
|
|
|
this, &VFileList::openFileLocation);
|
|
|
+
|
|
|
+ m_sortAct = new QAction(QIcon(":/resources/icons/sort.svg"),
|
|
|
+ tr("&Sort"),
|
|
|
+ this);
|
|
|
+ m_sortAct->setToolTip(tr("Sort notes in this folder manually"));
|
|
|
+ connect(m_sortAct, &QAction::triggered,
|
|
|
+ this, &VFileList::sortItems);
|
|
|
}
|
|
|
|
|
|
void VFileList::setDirectory(VDirectory *p_directory)
|
|
|
@@ -187,7 +195,6 @@ void VFileList::setDirectory(VDirectory *p_directory)
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- qDebug() << "filelist set folder" << m_directory->getName();
|
|
|
updateFileList();
|
|
|
}
|
|
|
|
|
|
@@ -215,10 +222,11 @@ void VFileList::fileInfo()
|
|
|
|
|
|
void VFileList::openFileLocation() const
|
|
|
{
|
|
|
- QListWidgetItem *curItem = fileList->currentItem();
|
|
|
- V_ASSERT(curItem);
|
|
|
- QUrl url = QUrl::fromLocalFile(getVFile(curItem)->fetchBasePath());
|
|
|
- QDesktopServices::openUrl(url);
|
|
|
+ QList<QListWidgetItem *> items = fileList->selectedItems();
|
|
|
+ if (items.size() == 1) {
|
|
|
+ QUrl url = QUrl::fromLocalFile(getVFile(items[0])->fetchBasePath());
|
|
|
+ QDesktopServices::openUrl(url);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
void VFileList::fileInfo(VNoteFile *p_file)
|
|
|
@@ -277,15 +285,26 @@ QListWidgetItem* VFileList::insertFileListItem(VNoteFile *file, bool atFront)
|
|
|
|
|
|
// Qt seems not to update the QListWidget correctly. Manually force it to repaint.
|
|
|
fileList->update();
|
|
|
- qDebug() << "VFileList adds" << file->getName();
|
|
|
return item;
|
|
|
}
|
|
|
|
|
|
-void VFileList::removeFileListItem(QListWidgetItem *item)
|
|
|
+void VFileList::removeFileListItem(VNoteFile *p_file)
|
|
|
{
|
|
|
- fileList->setCurrentRow(-1);
|
|
|
- fileList->removeItemWidget(item);
|
|
|
+ if (!p_file) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ QListWidgetItem *item = findItem(p_file);
|
|
|
+ if (!item) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ int row = fileList->row(item);
|
|
|
+ Q_ASSERT(row >= 0);
|
|
|
+
|
|
|
+ fileList->takeItem(row);
|
|
|
delete item;
|
|
|
+
|
|
|
// Qt seems not to update the QListWidget correctly. Manually force it to repaint.
|
|
|
fileList->update();
|
|
|
}
|
|
|
@@ -383,18 +402,21 @@ QVector<QListWidgetItem *> VFileList::updateFileListAdded()
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
- qDebug() << ret.size() << "items added";
|
|
|
+
|
|
|
return ret;
|
|
|
}
|
|
|
|
|
|
-// Delete the file related to current item
|
|
|
-void VFileList::deleteFile()
|
|
|
+void VFileList::deleteSelectedFiles()
|
|
|
{
|
|
|
QList<QListWidgetItem *> items = fileList->selectedItems();
|
|
|
Q_ASSERT(!items.isEmpty());
|
|
|
- for (int i = 0; i < items.size(); ++i) {
|
|
|
- deleteFile(getVFile(items.at(i)));
|
|
|
+
|
|
|
+ QVector<VNoteFile *> files;
|
|
|
+ for (auto const & item : items) {
|
|
|
+ files.push_back(getVFile(item));
|
|
|
}
|
|
|
+
|
|
|
+ deleteFiles(files);
|
|
|
}
|
|
|
|
|
|
// @p_file may or may not be listed in VFileList
|
|
|
@@ -404,30 +426,83 @@ void VFileList::deleteFile(VNoteFile *p_file)
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- VDirectory *dir = p_file->getDirectory();
|
|
|
- QString fileName = p_file->getName();
|
|
|
- int ret = VUtils::showMessage(QMessageBox::Warning, tr("Warning"),
|
|
|
- tr("Are you sure to delete note <span style=\"%1\">%2</span>?")
|
|
|
- .arg(g_config->c_dataTextStyle).arg(fileName),
|
|
|
- tr("<span style=\"%1\">WARNING</span>: "
|
|
|
- "VNote will delete the note as well as all "
|
|
|
- "its images and attachments managed by VNote. "
|
|
|
- "You could find deleted files in the recycle "
|
|
|
- "bin of this notebook.<br>"
|
|
|
- "The operation is IRREVERSIBLE!")
|
|
|
- .arg(g_config->c_warningTextStyle),
|
|
|
- QMessageBox::Ok | QMessageBox::Cancel,
|
|
|
- QMessageBox::Ok, this, MessageBoxType::Danger);
|
|
|
- if (ret == QMessageBox::Ok) {
|
|
|
- editArea->closeFile(p_file, true);
|
|
|
-
|
|
|
- // Remove the item before deleting it totally, or p_file will be invalid.
|
|
|
- QListWidgetItem *item = findItem(p_file);
|
|
|
- if (item) {
|
|
|
- removeFileListItem(item);
|
|
|
+ QVector<VNoteFile *> files(1, p_file);
|
|
|
+ deleteFiles(files);
|
|
|
+}
|
|
|
+
|
|
|
+void VFileList::deleteFiles(const QVector<VNoteFile *> &p_files)
|
|
|
+{
|
|
|
+ if (p_files.isEmpty()) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ QVector<ConfirmItemInfo> items;
|
|
|
+ for (auto const & file : p_files) {
|
|
|
+ items.push_back(ConfirmItemInfo(file->getName(),
|
|
|
+ file->fetchPath(),
|
|
|
+ file->fetchPath(),
|
|
|
+ (void *)file));
|
|
|
+ }
|
|
|
+
|
|
|
+ QString text = tr("Are you sure to delete these notes?");
|
|
|
+
|
|
|
+ QString info = tr("<span style=\"%1\">WARNING</span>: "
|
|
|
+ "VNote will delete notes as well as all "
|
|
|
+ "their images and attachments managed by VNote. "
|
|
|
+ "You could find deleted files in the recycle "
|
|
|
+ "bin of these notes.<br>"
|
|
|
+ "Click \"Cancel\" to leave them untouched.<br>"
|
|
|
+ "The operation is IRREVERSIBLE!")
|
|
|
+ .arg(g_config->c_warningTextStyle);
|
|
|
+
|
|
|
+ VConfirmDeletionDialog dialog(tr("Confirm Deleting Notes"),
|
|
|
+ text,
|
|
|
+ info,
|
|
|
+ items,
|
|
|
+ false,
|
|
|
+ false,
|
|
|
+ false,
|
|
|
+ this);
|
|
|
+ if (dialog.exec()) {
|
|
|
+ items = dialog.getConfirmedItems();
|
|
|
+ QVector<VNoteFile *> files;
|
|
|
+ for (auto const & item : items) {
|
|
|
+ files.push_back((VNoteFile *)item.m_data);
|
|
|
}
|
|
|
|
|
|
- dir->deleteFile(p_file);
|
|
|
+ int nrDeleted = 0;
|
|
|
+ for (auto file : files) {
|
|
|
+ editArea->closeFile(file, true);
|
|
|
+
|
|
|
+ // Remove the item before deleting it totally, or file will be invalid.
|
|
|
+ removeFileListItem(file);
|
|
|
+
|
|
|
+ QString errMsg;
|
|
|
+ QString fileName = file->getName();
|
|
|
+ QString filePath = file->fetchPath();
|
|
|
+ if (!VNoteFile::deleteFile(file, &errMsg)) {
|
|
|
+ VUtils::showMessage(QMessageBox::Warning,
|
|
|
+ tr("Warning"),
|
|
|
+ tr("Fail to delete note <span style=\"%1\">%2</span>.<br>"
|
|
|
+ "Please check <span style=\"%1\">%3</span> and manually delete it.")
|
|
|
+ .arg(g_config->c_dataTextStyle)
|
|
|
+ .arg(fileName)
|
|
|
+ .arg(filePath),
|
|
|
+ errMsg,
|
|
|
+ QMessageBox::Ok,
|
|
|
+ QMessageBox::Ok,
|
|
|
+ this);
|
|
|
+ } else {
|
|
|
+ Q_ASSERT(errMsg.isEmpty());
|
|
|
+ ++nrDeleted;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ if (nrDeleted > 0) {
|
|
|
+ g_mainWin->showStatusMessage(tr("%1 %2 deleted")
|
|
|
+ .arg(nrDeleted)
|
|
|
+ .arg(nrDeleted > 1 ? tr("notes") : tr("note")));
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
@@ -456,18 +531,22 @@ void VFileList::contextMenuRequested(QPoint pos)
|
|
|
|
|
|
menu.addAction(newFileAct);
|
|
|
|
|
|
+ if (fileList->count() > 1) {
|
|
|
+ menu.addAction(m_sortAct);
|
|
|
+ }
|
|
|
+
|
|
|
if (item) {
|
|
|
- menu.addAction(deleteFileAct);
|
|
|
menu.addSeparator();
|
|
|
+ menu.addAction(deleteFileAct);
|
|
|
menu.addAction(copyAct);
|
|
|
menu.addAction(cutAct);
|
|
|
}
|
|
|
|
|
|
- if (VUtils::opTypeInClipboard() == ClipboardOpType::CopyFile
|
|
|
- && !m_copiedFiles.isEmpty()) {
|
|
|
+ if (pasteAvailable()) {
|
|
|
if (!item) {
|
|
|
menu.addSeparator();
|
|
|
}
|
|
|
+
|
|
|
menu.addAction(pasteAct);
|
|
|
}
|
|
|
|
|
|
@@ -517,29 +596,59 @@ void VFileList::handleItemClicked(QListWidgetItem *currentItem)
|
|
|
emit fileClicked(getVFile(currentItem), g_config->getNoteOpenMode());
|
|
|
}
|
|
|
|
|
|
-bool VFileList::importFile(const QString &p_srcFilePath)
|
|
|
+bool VFileList::importFiles(const QStringList &p_files, QString *p_errMsg)
|
|
|
{
|
|
|
- if (p_srcFilePath.isEmpty()) {
|
|
|
- return false;
|
|
|
- }
|
|
|
- Q_ASSERT(m_directory);
|
|
|
- // Copy file @name to current directory
|
|
|
- QString targetPath = m_directory->fetchPath();
|
|
|
- QString srcName = VUtils::fileNameFromPath(p_srcFilePath);
|
|
|
- if (srcName.isEmpty()) {
|
|
|
- return false;
|
|
|
- }
|
|
|
- QString targetFilePath = QDir(targetPath).filePath(srcName);
|
|
|
- bool ret = VUtils::copyFile(p_srcFilePath, targetFilePath, false);
|
|
|
- if (!ret) {
|
|
|
+ if (p_files.isEmpty()) {
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
- VNoteFile *destFile = m_directory->addFile(srcName, -1);
|
|
|
- if (destFile) {
|
|
|
- return insertFileListItem(destFile, false);
|
|
|
+ bool ret = true;
|
|
|
+ Q_ASSERT(m_directory && m_directory->isOpened());
|
|
|
+ QString dirPath = m_directory->fetchPath();
|
|
|
+ QDir dir(dirPath);
|
|
|
+
|
|
|
+ int nrImported = 0;
|
|
|
+ for (int i = 0; i < p_files.size(); ++i) {
|
|
|
+ const QString &file = p_files[i];
|
|
|
+
|
|
|
+ QFileInfo fi(file);
|
|
|
+ if (!fi.exists() || !fi.isFile()) {
|
|
|
+ VUtils::addErrMsg(p_errMsg, tr("Skip importing non-exist file %1.")
|
|
|
+ .arg(file));
|
|
|
+ ret = false;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ QString name = VUtils::fileNameFromPath(file);
|
|
|
+ Q_ASSERT(!name.isEmpty());
|
|
|
+ name = VUtils::getFileNameWithSequence(dirPath, name);
|
|
|
+ QString targetFilePath = dir.filePath(name);
|
|
|
+ bool ret = VUtils::copyFile(file, targetFilePath, false);
|
|
|
+ if (!ret) {
|
|
|
+ VUtils::addErrMsg(p_errMsg, tr("Fail to copy file %1 as %1.")
|
|
|
+ .arg(file)
|
|
|
+ .arg(targetFilePath));
|
|
|
+ ret = false;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ VNoteFile *destFile = m_directory->addFile(name, -1);
|
|
|
+ if (destFile) {
|
|
|
+ ++nrImported;
|
|
|
+ qDebug() << "imported" << file << "as" << targetFilePath;
|
|
|
+ } else {
|
|
|
+ VUtils::addErrMsg(p_errMsg, tr("Fail to add the note %1 to target folder's configuration.")
|
|
|
+ .arg(file));
|
|
|
+ ret = false;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
}
|
|
|
- return false;
|
|
|
+
|
|
|
+ qDebug() << "imported" << nrImported << "files";
|
|
|
+
|
|
|
+ updateFileList();
|
|
|
+
|
|
|
+ return ret;
|
|
|
}
|
|
|
|
|
|
void VFileList::copySelectedFiles(bool p_isCut)
|
|
|
@@ -548,19 +657,29 @@ void VFileList::copySelectedFiles(bool p_isCut)
|
|
|
if (items.isEmpty()) {
|
|
|
return;
|
|
|
}
|
|
|
+
|
|
|
QJsonArray files;
|
|
|
- m_copiedFiles.clear();
|
|
|
for (int i = 0; i < items.size(); ++i) {
|
|
|
VNoteFile *file = getVFile(items[i]);
|
|
|
- QJsonObject fileJson;
|
|
|
- fileJson["notebook"] = file->getNotebookName();
|
|
|
- fileJson["path"] = file->fetchPath();
|
|
|
- files.append(fileJson);
|
|
|
-
|
|
|
- m_copiedFiles.append(file);
|
|
|
+ files.append(file->fetchPath());
|
|
|
}
|
|
|
|
|
|
- copyFileInfoToClipboard(files, p_isCut);
|
|
|
+ QJsonObject clip;
|
|
|
+ clip[ClipboardConfig::c_magic] = getNewMagic();
|
|
|
+ clip[ClipboardConfig::c_type] = (int)ClipboardOpType::CopyFile;
|
|
|
+ clip[ClipboardConfig::c_isCut] = p_isCut;
|
|
|
+ clip[ClipboardConfig::c_files] = files;
|
|
|
+
|
|
|
+ QClipboard *clipboard = QApplication::clipboard();
|
|
|
+ clipboard->setText(QJsonDocument(clip).toJson(QJsonDocument::Compact));
|
|
|
+
|
|
|
+ qDebug() << "copied files info" << clipboard->text();
|
|
|
+
|
|
|
+ int cnt = files.size();
|
|
|
+ g_mainWin->showStatusMessage(tr("%1 %2 %3")
|
|
|
+ .arg(cnt)
|
|
|
+ .arg(cnt > 1 ? tr("notes") : tr("note"))
|
|
|
+ .arg(p_isCut ? tr("cut") : tr("copied")));
|
|
|
}
|
|
|
|
|
|
void VFileList::cutSelectedFiles()
|
|
|
@@ -568,82 +687,123 @@ void VFileList::cutSelectedFiles()
|
|
|
copySelectedFiles(true);
|
|
|
}
|
|
|
|
|
|
-void VFileList::copyFileInfoToClipboard(const QJsonArray &p_files, bool p_isCut)
|
|
|
+void VFileList::pasteFilesFromClipboard()
|
|
|
{
|
|
|
- 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()
|
|
|
-{
|
|
|
- if (m_copiedFiles.isEmpty()) {
|
|
|
+ if (!pasteAvailable()) {
|
|
|
return;
|
|
|
}
|
|
|
|
|
|
- pasteFiles(m_directory);
|
|
|
+ QJsonObject obj = VUtils::clipboardToJson();
|
|
|
+ QJsonArray files = obj[ClipboardConfig::c_files].toArray();
|
|
|
+ bool isCut = obj[ClipboardConfig::c_isCut].toBool();
|
|
|
+ QVector<QString> filesToPaste(files.size());
|
|
|
+ for (int i = 0; i < files.size(); ++i) {
|
|
|
+ filesToPaste[i] = files[i].toString();
|
|
|
+ }
|
|
|
+
|
|
|
+ pasteFiles(m_directory, filesToPaste, isCut);
|
|
|
}
|
|
|
|
|
|
-void VFileList::pasteFiles(VDirectory *p_destDir)
|
|
|
+void VFileList::pasteFiles(VDirectory *p_destDir,
|
|
|
+ const QVector<QString> &p_files,
|
|
|
+ bool p_isCut)
|
|
|
{
|
|
|
- qDebug() << "paste files to" << p_destDir->getName();
|
|
|
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();
|
|
|
+ if (!p_destDir || p_files.isEmpty()) {
|
|
|
+ clipboard->clear();
|
|
|
+ return;
|
|
|
+ }
|
|
|
|
|
|
int nrPasted = 0;
|
|
|
- for (int i = 0; i < m_copiedFiles.size(); ++i) {
|
|
|
- QPointer<VNoteFile> srcFile = m_copiedFiles[i];
|
|
|
- if (!srcFile) {
|
|
|
+ for (int i = 0; i < p_files.size(); ++i) {
|
|
|
+ VNoteFile *file = g_vnote->getInternalFile(p_files[i]);
|
|
|
+ if (!file) {
|
|
|
+ qWarning() << "Copied file is not an internal note" << p_files[i];
|
|
|
+ VUtils::showMessage(QMessageBox::Warning,
|
|
|
+ tr("Warning"),
|
|
|
+ tr("Fail to copy note <span style=\"%1\">%2</span>.")
|
|
|
+ .arg(g_config->c_dataTextStyle)
|
|
|
+ .arg(p_files[i]),
|
|
|
+ tr("VNote could not find this note in any notebook."),
|
|
|
+ QMessageBox::Ok,
|
|
|
+ QMessageBox::Ok,
|
|
|
+ this);
|
|
|
+
|
|
|
continue;
|
|
|
}
|
|
|
|
|
|
- QString fileName = srcFile->getName();
|
|
|
- VDirectory *srcDir = srcFile->getDirectory();
|
|
|
- if (srcDir == p_destDir && !isCut) {
|
|
|
- // Copy and paste in the same directory.
|
|
|
- // Rename it to xx_copy.md
|
|
|
- fileName = VUtils::generateCopiedFileName(srcDir->fetchPath(), fileName);
|
|
|
- }
|
|
|
- if (copyFile(p_destDir, fileName, srcFile, isCut)) {
|
|
|
- nrPasted++;
|
|
|
+ QString fileName = file->getName();
|
|
|
+ if (file->getDirectory() == p_destDir) {
|
|
|
+ if (p_isCut) {
|
|
|
+ qDebug() << "skip one note to cut and paste in the same folder" << fileName;
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+
|
|
|
+ // Copy and paste in the same folder.
|
|
|
+ // We do not allow this if the note contains local images.
|
|
|
+ if (file->getDocType() == DocType::Markdown) {
|
|
|
+ QVector<ImageLink> images = VUtils::fetchImagesFromMarkdownFile(file,
|
|
|
+ ImageLink::LocalRelativeInternal);
|
|
|
+ if (!images.isEmpty()) {
|
|
|
+ qDebug() << "skip one note with internal images to copy and paste in the same folder"
|
|
|
+ << fileName;
|
|
|
+ VUtils::showMessage(QMessageBox::Warning,
|
|
|
+ tr("Warning"),
|
|
|
+ tr("Fail to copy note <span style=\"%1\">%2</span>.")
|
|
|
+ .arg(g_config->c_dataTextStyle)
|
|
|
+ .arg(p_files[i]),
|
|
|
+ tr("VNote does not allow copy and paste notes with internal images "
|
|
|
+ "in the same folder."),
|
|
|
+ QMessageBox::Ok,
|
|
|
+ QMessageBox::Ok,
|
|
|
+ this);
|
|
|
+ continue;
|
|
|
+ }
|
|
|
+ }
|
|
|
+
|
|
|
+ // Rename it to xxx_copy.md.
|
|
|
+ fileName = VUtils::generateCopiedFileName(file->fetchBasePath(), fileName);
|
|
|
} else {
|
|
|
- VUtils::showMessage(QMessageBox::Warning, tr("Warning"),
|
|
|
- tr("Fail to copy note <span style=\"%1\">%2</span>.")
|
|
|
- .arg(g_config->c_dataTextStyle).arg(srcFile->getName()),
|
|
|
- tr("Please check if there already exists a file with the same name in the target folder."),
|
|
|
- QMessageBox::Ok, QMessageBox::Ok, this);
|
|
|
+ // Rename it to xxx_copy.md if needed.
|
|
|
+ fileName = VUtils::generateCopiedFileName(p_destDir->fetchPath(), fileName);
|
|
|
}
|
|
|
- }
|
|
|
|
|
|
- qDebug() << "pasted" << nrPasted << "files sucessfully";
|
|
|
- clipboard->clear();
|
|
|
- m_copiedFiles.clear();
|
|
|
-}
|
|
|
+ QString msg;
|
|
|
+ VNoteFile *destFile = NULL;
|
|
|
+ bool ret = VNoteFile::copyFile(p_destDir,
|
|
|
+ fileName,
|
|
|
+ file,
|
|
|
+ p_isCut,
|
|
|
+ &destFile,
|
|
|
+ &msg);
|
|
|
+ if (!ret) {
|
|
|
+ VUtils::showMessage(QMessageBox::Warning,
|
|
|
+ tr("Warning"),
|
|
|
+ tr("Fail to copy note <span style=\"%1\">%2</span>.")
|
|
|
+ .arg(g_config->c_dataTextStyle)
|
|
|
+ .arg(p_files[i]),
|
|
|
+ msg,
|
|
|
+ QMessageBox::Ok,
|
|
|
+ QMessageBox::Ok,
|
|
|
+ this);
|
|
|
+ }
|
|
|
|
|
|
-bool VFileList::copyFile(VDirectory *p_destDir, const QString &p_destName, VNoteFile *p_file, bool p_cut)
|
|
|
-{
|
|
|
- QString srcPath = QDir::cleanPath(p_file->fetchPath());
|
|
|
- QString destPath = QDir::cleanPath(QDir(p_destDir->fetchPath()).filePath(p_destName));
|
|
|
- if (VUtils::equalPath(srcPath, destPath)) {
|
|
|
- return true;
|
|
|
+ if (destFile) {
|
|
|
+ ++nrPasted;
|
|
|
+ emit fileUpdated(destFile);
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
- // DocType is not allowed to change.
|
|
|
- Q_ASSERT(p_file->getDocType() == VUtils::docTypeFromName(destPath));
|
|
|
+ qDebug() << "copy" << nrPasted << "files";
|
|
|
+ if (nrPasted > 0) {
|
|
|
+ g_mainWin->showStatusMessage(tr("%1 %2 pasted")
|
|
|
+ .arg(nrPasted)
|
|
|
+ .arg(nrPasted > 1 ? tr("notes") : tr("note")));
|
|
|
+ }
|
|
|
|
|
|
- VNoteFile *destFile = VDirectory::copyFile(p_destDir, p_destName, p_file, p_cut);
|
|
|
updateFileList();
|
|
|
- if (destFile) {
|
|
|
- emit fileUpdated(destFile);
|
|
|
- }
|
|
|
- return destFile != NULL;
|
|
|
+ clipboard->clear();
|
|
|
+ getNewMagic();
|
|
|
}
|
|
|
|
|
|
void VFileList::keyPressEvent(QKeyEvent *event)
|
|
|
@@ -714,30 +874,6 @@ bool VFileList::locateFile(const VNoteFile *p_file)
|
|
|
return false;
|
|
|
}
|
|
|
|
|
|
-void VFileList::handleRowsMoved(const QModelIndex &p_parent, int p_start, int p_end, const QModelIndex &p_destination, int p_row)
|
|
|
-{
|
|
|
- if (p_parent == p_destination) {
|
|
|
- // Items[p_start, p_end] are moved to p_row.
|
|
|
- m_directory->reorderFiles(p_start, p_end, p_row);
|
|
|
- Q_ASSERT(identicalListWithDirectory());
|
|
|
- }
|
|
|
-}
|
|
|
-
|
|
|
-bool VFileList::identicalListWithDirectory() const
|
|
|
-{
|
|
|
- const QVector<VNoteFile *> files = m_directory->getFiles();
|
|
|
- int nrItems = fileList->count();
|
|
|
- if (nrItems != files.size()) {
|
|
|
- return false;
|
|
|
- }
|
|
|
- for (int i = 0; i < nrItems; ++i) {
|
|
|
- if (getVFile(fileList->item(i)) != files.at(i)) {
|
|
|
- return false;
|
|
|
- }
|
|
|
- }
|
|
|
- return true;
|
|
|
-}
|
|
|
-
|
|
|
void VFileList::registerNavigation(QChar p_majorKey)
|
|
|
{
|
|
|
m_majorKey = p_majorKey;
|
|
|
@@ -825,3 +961,105 @@ QList<QListWidgetItem *> VFileList::getVisibleItems() const
|
|
|
return items;
|
|
|
}
|
|
|
|
|
|
+int VFileList::getNewMagic()
|
|
|
+{
|
|
|
+ m_magicForClipboard = (int)QDateTime::currentDateTime().toTime_t();
|
|
|
+ m_magicForClipboard |= qrand();
|
|
|
+
|
|
|
+ return m_magicForClipboard;
|
|
|
+}
|
|
|
+
|
|
|
+bool VFileList::checkMagic(int p_magic) const
|
|
|
+{
|
|
|
+ return m_magicForClipboard == p_magic;
|
|
|
+}
|
|
|
+
|
|
|
+bool VFileList::pasteAvailable() const
|
|
|
+{
|
|
|
+ QJsonObject obj = VUtils::clipboardToJson();
|
|
|
+ if (obj.isEmpty()) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!obj.contains(ClipboardConfig::c_type)) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ ClipboardOpType type = (ClipboardOpType)obj[ClipboardConfig::c_type].toInt();
|
|
|
+ if (type != ClipboardOpType::CopyFile) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!obj.contains(ClipboardConfig::c_magic)
|
|
|
+ || !obj.contains(ClipboardConfig::c_isCut)
|
|
|
+ || !obj.contains(ClipboardConfig::c_files)) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ int magic = obj[ClipboardConfig::c_magic].toInt();
|
|
|
+ if (!checkMagic(magic)) {
|
|
|
+ return false;
|
|
|
+ }
|
|
|
+
|
|
|
+ QJsonArray files = obj[ClipboardConfig::c_files].toArray();
|
|
|
+ return !files.isEmpty();
|
|
|
+}
|
|
|
+
|
|
|
+void VFileList::sortItems()
|
|
|
+{
|
|
|
+ const QVector<VNoteFile *> &files = m_directory->getFiles();
|
|
|
+ if (files.size() < 2) {
|
|
|
+ return;
|
|
|
+ }
|
|
|
+
|
|
|
+ VSortDialog dialog(tr("Sort Notes"),
|
|
|
+ tr("Sort notes in folder <span style=\"%1\">%2</span> "
|
|
|
+ "in the configuration file.")
|
|
|
+ .arg(g_config->c_dataTextStyle)
|
|
|
+ .arg(m_directory->getName()),
|
|
|
+ this);
|
|
|
+ QTreeWidget *tree = dialog.getTreeWidget();
|
|
|
+ tree->clear();
|
|
|
+ tree->setColumnCount(3);
|
|
|
+ tree->header()->setStretchLastSection(true);
|
|
|
+ QStringList headers;
|
|
|
+ headers << tr("Name") << tr("Created Time") << tr("Modified Time");
|
|
|
+ tree->setHeaderLabels(headers);
|
|
|
+
|
|
|
+ for (int i = 0; i < files.size(); ++i) {
|
|
|
+ const VNoteFile *file = files[i];
|
|
|
+ QString createdTime = VUtils::displayDateTime(file->getCreatedTimeUtc().toLocalTime());
|
|
|
+ QString modifiedTime = VUtils::displayDateTime(file->getModifiedTimeUtc().toLocalTime());
|
|
|
+ QStringList cols;
|
|
|
+ cols << file->getName() << createdTime << modifiedTime;
|
|
|
+ QTreeWidgetItem *item = new QTreeWidgetItem(tree, cols);
|
|
|
+
|
|
|
+ item->setData(0, Qt::UserRole, i);
|
|
|
+ }
|
|
|
+
|
|
|
+ dialog.treeUpdated();
|
|
|
+
|
|
|
+ if (dialog.exec()) {
|
|
|
+ QVector<QVariant> data = dialog.getSortedData();
|
|
|
+ Q_ASSERT(data.size() == files.size());
|
|
|
+ QVector<int> sortedIdx(data.size(), -1);
|
|
|
+ for (int i = 0; i < data.size(); ++i) {
|
|
|
+ sortedIdx[i] = data[i].toInt();
|
|
|
+ }
|
|
|
+
|
|
|
+ qDebug() << "sort files" << sortedIdx;
|
|
|
+ if (!m_directory->sortFiles(sortedIdx)) {
|
|
|
+ VUtils::showMessage(QMessageBox::Warning,
|
|
|
+ tr("Warning"),
|
|
|
+ tr("Fail to sort notes in folder <span style=\"%1\">%2</span>.")
|
|
|
+ .arg(g_config->c_dataTextStyle)
|
|
|
+ .arg(m_directory->getName()),
|
|
|
+ "",
|
|
|
+ QMessageBox::Ok,
|
|
|
+ QMessageBox::Ok,
|
|
|
+ this);
|
|
|
+ }
|
|
|
+
|
|
|
+ updateFileList();
|
|
|
+ }
|
|
|
+}
|