mirror of
https://gitee.com/vnotex/vnote.git
synced 2025-07-05 13:59:52 +08:00
628 lines
21 KiB
C++
628 lines
21 KiB
C++
#include "vattachmentlist.h"
|
|
|
|
#include <QtWidgets>
|
|
|
|
#include "vconfigmanager.h"
|
|
#include "utils/vutils.h"
|
|
#include "vbuttonwithwidget.h"
|
|
#include "vmainwindow.h"
|
|
#include "dialog/vconfirmdeletiondialog.h"
|
|
#include "dialog/vsortdialog.h"
|
|
#include "utils/vimnavigationforwidget.h"
|
|
#include "utils/viconutils.h"
|
|
#include "vlineedit.h"
|
|
|
|
extern VConfigManager *g_config;
|
|
extern VMainWindow *g_mainWin;
|
|
|
|
VAttachmentList::VAttachmentList(QWidget *p_parent)
|
|
: QWidget(p_parent), VButtonPopupWidget(this), m_file(NULL)
|
|
{
|
|
setupUI();
|
|
|
|
initActions();
|
|
|
|
updateContent();
|
|
}
|
|
|
|
void VAttachmentList::setupUI()
|
|
{
|
|
m_addBtn = new QPushButton(VIconUtils::buttonIcon(":/resources/icons/add_attachment.svg"), "");
|
|
m_addBtn->setToolTip(tr("Add"));
|
|
m_addBtn->setProperty("FlatBtn", true);
|
|
m_addBtn->setDefault(true);
|
|
connect(m_addBtn, &QPushButton::clicked,
|
|
this, &VAttachmentList::addAttachment);
|
|
|
|
m_clearBtn = new QPushButton(VIconUtils::buttonDangerIcon(":/resources/icons/clear_attachment.svg"), "");
|
|
m_clearBtn->setToolTip(tr("Clear"));
|
|
m_clearBtn->setProperty("FlatBtn", true);
|
|
connect(m_clearBtn, &QPushButton::clicked,
|
|
this, [this]() {
|
|
if (m_file && m_attachmentList->count() > 0) {
|
|
int ret = VUtils::showMessage(QMessageBox::Warning, tr("Warning"),
|
|
tr("Are you sure to clear attachments of note "
|
|
"<span style=\"%1\">%2</span>?")
|
|
.arg(g_config->c_dataTextStyle)
|
|
.arg(m_file->getName()),
|
|
tr("<span style=\"%1\">WARNING</span>: "
|
|
"VNote will delete all the files in directory "
|
|
"<span style=\"%2\">%3</span>."
|
|
"Deleted files could be found in the recycle bin "
|
|
"of this note.<br>The operation is IRREVERSIBLE!")
|
|
.arg(g_config->c_warningTextStyle)
|
|
.arg(g_config->c_dataTextStyle)
|
|
.arg(m_file->fetchAttachmentFolderPath()),
|
|
QMessageBox::Ok | QMessageBox::Cancel,
|
|
QMessageBox::Ok,
|
|
g_mainWin,
|
|
MessageBoxType::Danger);
|
|
if (ret == QMessageBox::Ok) {
|
|
if (!m_file->deleteAttachments()) {
|
|
VUtils::showMessage(QMessageBox::Warning,
|
|
tr("Warning"),
|
|
tr("Fail to clear attachments of note <span style=\"%1\">%2</span>.")
|
|
.arg(g_config->c_dataTextStyle)
|
|
.arg(m_file->getName()),
|
|
tr("Please check the attachments folder and "
|
|
"maintain the configuration file manually."),
|
|
QMessageBox::Ok,
|
|
QMessageBox::Ok,
|
|
g_mainWin);
|
|
}
|
|
|
|
m_attachmentList->clear();
|
|
|
|
updateButtonState();
|
|
}
|
|
}
|
|
});
|
|
|
|
m_locateBtn = new QPushButton(VIconUtils::buttonIcon(":/resources/icons/locate_attachment.svg"), "");
|
|
m_locateBtn->setToolTip(tr("Open Folder"));
|
|
m_locateBtn->setProperty("FlatBtn", true);
|
|
connect(m_locateBtn, &QPushButton::clicked,
|
|
this, [this]() {
|
|
if (m_file && !m_file->getAttachmentFolder().isEmpty()) {
|
|
QUrl url = QUrl::fromLocalFile(m_file->fetchAttachmentFolderPath());
|
|
QDesktopServices::openUrl(url);
|
|
}
|
|
});
|
|
|
|
m_numLabel = new QLabel();
|
|
|
|
QHBoxLayout *btnLayout = new QHBoxLayout;
|
|
btnLayout->addWidget(m_addBtn);
|
|
btnLayout->addWidget(m_clearBtn);
|
|
btnLayout->addWidget(m_locateBtn);
|
|
btnLayout->addStretch();
|
|
btnLayout->addWidget(m_numLabel);
|
|
|
|
m_attachmentList = new QListWidget;
|
|
m_attachmentList->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
m_attachmentList->setSelectionMode(QAbstractItemView::ExtendedSelection);
|
|
m_attachmentList->setEditTriggers(QAbstractItemView::SelectedClicked);
|
|
m_attachmentList->setAttribute(Qt::WA_MacShowFocusRect, false);
|
|
m_attachmentList->setItemDelegate(&m_listDelegate);
|
|
connect(m_attachmentList, &QListWidget::customContextMenuRequested,
|
|
this, &VAttachmentList::handleContextMenuRequested);
|
|
connect(m_attachmentList, &QListWidget::itemActivated,
|
|
this, &VAttachmentList::handleItemActivated);
|
|
connect(m_attachmentList->itemDelegate(), &QAbstractItemDelegate::commitData,
|
|
this, &VAttachmentList::handleListItemCommitData);
|
|
|
|
QVBoxLayout *mainLayout = new QVBoxLayout();
|
|
mainLayout->addLayout(btnLayout);
|
|
mainLayout->addWidget(m_attachmentList);
|
|
|
|
setLayout(mainLayout);
|
|
}
|
|
|
|
void VAttachmentList::initActions()
|
|
{
|
|
m_openAct = new QAction(tr("&Open"), this);
|
|
m_openAct->setToolTip(tr("Open current attachment file"));
|
|
connect(m_openAct, &QAction::triggered,
|
|
this, [this]() {
|
|
QListWidgetItem *item = m_attachmentList->currentItem();
|
|
handleItemActivated(item);
|
|
});
|
|
|
|
m_deleteAct = new QAction(VIconUtils::menuDangerIcon(":/resources/icons/delete_attachment.svg"),
|
|
tr("&Delete"),
|
|
this);
|
|
m_deleteAct->setToolTip(tr("Delete selected attachments"));
|
|
connect(m_deleteAct, &QAction::triggered,
|
|
this, &VAttachmentList::deleteSelectedItems);
|
|
|
|
m_sortAct = new QAction(VIconUtils::menuIcon(":/resources/icons/sort.svg"),
|
|
tr("&Sort"),
|
|
this);
|
|
m_sortAct->setToolTip(tr("Sort attachments manually"));
|
|
connect(m_sortAct, &QAction::triggered,
|
|
this, &VAttachmentList::sortItems);
|
|
}
|
|
|
|
void VAttachmentList::setFile(VNoteFile *p_file)
|
|
{
|
|
m_file = p_file;
|
|
|
|
updateButtonState();
|
|
}
|
|
|
|
void VAttachmentList::updateContent()
|
|
{
|
|
bool enableAdd = true, enableDelete = true, enableClear = true, enableLocate = true;
|
|
m_attachmentList->clear();
|
|
|
|
if (!m_file) {
|
|
enableAdd = enableDelete = enableClear = enableLocate = false;
|
|
} else {
|
|
QString folder = m_file->getAttachmentFolder();
|
|
const QVector<VAttachment> &attas = m_file->getAttachments();
|
|
|
|
if (folder.isEmpty()) {
|
|
Q_ASSERT(attas.isEmpty());
|
|
enableDelete = enableClear = enableLocate = false;
|
|
} else if (attas.isEmpty()) {
|
|
enableDelete = enableClear = false;
|
|
} else {
|
|
fillAttachmentList(attas);
|
|
}
|
|
}
|
|
|
|
m_addBtn->setEnabled(enableAdd);
|
|
m_clearBtn->setEnabled(enableClear);
|
|
m_locateBtn->setEnabled(enableLocate);
|
|
|
|
int cnt = m_attachmentList->count();
|
|
if (cnt > 0) {
|
|
m_numLabel->setText(tr("%1 %2").arg(cnt).arg(cnt > 1 ? tr("Files") : tr("File")));
|
|
m_attachmentList->setFocus();
|
|
} else {
|
|
m_numLabel->setText("");
|
|
|
|
if (m_file) {
|
|
m_addBtn->setFocus();
|
|
}
|
|
}
|
|
}
|
|
|
|
void VAttachmentList::fillAttachmentList(const QVector<VAttachment> &p_attachments)
|
|
{
|
|
Q_ASSERT(m_attachmentList->count() == 0);
|
|
for (int i = 0; i < p_attachments.size(); ++i) {
|
|
const VAttachment &atta = p_attachments[i];
|
|
QListWidgetItem *item = new QListWidgetItem(atta.m_name);
|
|
item->setFlags(item->flags() | Qt::ItemIsEditable);
|
|
item->setData(Qt::UserRole, atta.m_name);
|
|
|
|
m_attachmentList->addItem(item);
|
|
}
|
|
}
|
|
|
|
void VAttachmentList::addAttachment()
|
|
{
|
|
if (!m_file) {
|
|
return;
|
|
}
|
|
|
|
static QString lastPath = QDir::homePath();
|
|
QStringList files = QFileDialog::getOpenFileNames(g_mainWin,
|
|
tr("Select Files As Attachments"),
|
|
lastPath);
|
|
if (files.isEmpty()) {
|
|
return;
|
|
}
|
|
|
|
// Update lastPath
|
|
lastPath = QFileInfo(files[0]).path();
|
|
|
|
addAttachments(files);
|
|
|
|
updateButtonState();
|
|
|
|
updateContent();
|
|
}
|
|
|
|
void VAttachmentList::addAttachments(const QStringList &p_files)
|
|
{
|
|
Q_ASSERT(m_file);
|
|
int addedFiles = 0;
|
|
for (int i = 0; i < p_files.size(); ++i) {
|
|
if (!m_file->addAttachment(p_files[i])) {
|
|
VUtils::showMessage(QMessageBox::Warning,
|
|
tr("Warning"),
|
|
tr("Fail to add attachment %1 for note <span style=\"%2\">%3</span>.")
|
|
.arg(p_files[i])
|
|
.arg(g_config->c_dataTextStyle)
|
|
.arg(m_file->getName()),
|
|
"",
|
|
QMessageBox::Ok,
|
|
QMessageBox::Ok,
|
|
g_mainWin);
|
|
} else {
|
|
++addedFiles;
|
|
}
|
|
}
|
|
|
|
if (addedFiles > 0) {
|
|
g_mainWin->showStatusMessage(tr("%1 %2 added as attachments")
|
|
.arg(addedFiles)
|
|
.arg(addedFiles > 1 ? tr("files") : tr("file")));
|
|
}
|
|
}
|
|
|
|
void VAttachmentList::handleContextMenuRequested(QPoint p_pos)
|
|
{
|
|
// @p_pos is the position in the coordinate of VAttachmentList, no m_attachmentList.
|
|
QListWidgetItem *item = m_attachmentList->itemAt(m_attachmentList->mapFromParent(p_pos));
|
|
QMenu menu(this);
|
|
menu.setToolTipsVisible(true);
|
|
|
|
if (!m_file) {
|
|
return;
|
|
}
|
|
|
|
if (item) {
|
|
if (!item->isSelected()) {
|
|
m_attachmentList->setCurrentItem(item, QItemSelectionModel::ClearAndSelect);
|
|
}
|
|
|
|
if (m_attachmentList->selectedItems().size() == 1) {
|
|
menu.addAction(m_openAct);
|
|
}
|
|
|
|
menu.addAction(m_deleteAct);
|
|
}
|
|
|
|
m_attachmentList->update();
|
|
|
|
if (m_file->getAttachments().size() > 1) {
|
|
if (!menu.actions().isEmpty()) {
|
|
menu.addSeparator();
|
|
}
|
|
|
|
menu.addAction(m_sortAct);
|
|
}
|
|
|
|
if (!menu.actions().isEmpty()) {
|
|
menu.exec(mapToGlobal(p_pos));
|
|
}
|
|
}
|
|
|
|
void VAttachmentList::handleItemActivated(QListWidgetItem *p_item)
|
|
{
|
|
if (p_item) {
|
|
Q_ASSERT(m_file);
|
|
|
|
QString name = p_item->text();
|
|
QString folderPath = m_file->fetchAttachmentFolderPath();
|
|
QUrl url = QUrl::fromLocalFile(QDir(folderPath).filePath(name));
|
|
QDesktopServices::openUrl(url);
|
|
}
|
|
}
|
|
|
|
void VAttachmentList::deleteSelectedItems()
|
|
{
|
|
QVector<ConfirmItemInfo> items;
|
|
const QList<QListWidgetItem *> selectedItems = m_attachmentList->selectedItems();
|
|
|
|
if (selectedItems.isEmpty()) {
|
|
return;
|
|
}
|
|
|
|
for (auto const & item : selectedItems) {
|
|
items.push_back(ConfirmItemInfo(item->text(),
|
|
item->text(),
|
|
"",
|
|
NULL));
|
|
}
|
|
|
|
QString text = tr("Are you sure to delete these attachments of note "
|
|
"<span style=\"%1\">%2</span>?")
|
|
.arg(g_config->c_dataTextStyle).arg(m_file->getName());
|
|
|
|
QString info = tr("Deleted files could be found in the recycle "
|
|
"bin of this note.<br>"
|
|
"Click \"Cancel\" to leave them untouched.");
|
|
|
|
VConfirmDeletionDialog dialog(tr("Confirm Deleting Attachments"),
|
|
text,
|
|
info,
|
|
items,
|
|
false,
|
|
false,
|
|
false,
|
|
g_mainWin);
|
|
if (dialog.exec()) {
|
|
items = dialog.getConfirmedItems();
|
|
|
|
QVector<QString> names;
|
|
for (auto const & item : items) {
|
|
names.push_back(item.m_name);
|
|
}
|
|
|
|
if (!m_file->deleteAttachments(names)) {
|
|
VUtils::showMessage(QMessageBox::Warning,
|
|
tr("Warning"),
|
|
tr("Fail to delete attachments of note <span style=\"%1\">%2</span>.")
|
|
.arg(g_config->c_dataTextStyle)
|
|
.arg(m_file->getName()),
|
|
tr("Please check the attachments folder and "
|
|
"maintain the configuration file manually."),
|
|
QMessageBox::Ok,
|
|
QMessageBox::Ok,
|
|
g_mainWin);
|
|
}
|
|
|
|
updateButtonState();
|
|
|
|
updateContent();
|
|
}
|
|
}
|
|
|
|
void VAttachmentList::sortItems()
|
|
{
|
|
const QVector<VAttachment> &attas = m_file->getAttachments();
|
|
if (attas.size() < 2) {
|
|
return;
|
|
}
|
|
|
|
VSortDialog dialog(tr("Sort Attachments"),
|
|
tr("Sort attachments of note <span style=\"%1\">%2</span> "
|
|
"in the configuration file.")
|
|
.arg(g_config->c_dataTextStyle)
|
|
.arg(m_file->getName()),
|
|
g_mainWin);
|
|
QTreeWidget *tree = dialog.getTreeWidget();
|
|
tree->clear();
|
|
tree->setColumnCount(1);
|
|
QStringList headers;
|
|
headers << tr("Name");
|
|
tree->setHeaderLabels(headers);
|
|
|
|
for (int i = 0; i < attas.size(); ++i) {
|
|
QTreeWidgetItem *item = new QTreeWidgetItem(tree, QStringList(attas[i].m_name));
|
|
|
|
item->setData(0, Qt::UserRole, i);
|
|
}
|
|
|
|
dialog.treeUpdated();
|
|
|
|
if (dialog.exec()) {
|
|
QVector<QVariant> data = dialog.getSortedData();
|
|
Q_ASSERT(data.size() == attas.size());
|
|
QVector<int> sortedIdx(data.size(), -1);
|
|
for (int i = 0; i < data.size(); ++i) {
|
|
sortedIdx[i] = data[i].toInt();
|
|
}
|
|
|
|
if (!m_file->sortAttachments(sortedIdx)) {
|
|
VUtils::showMessage(QMessageBox::Warning,
|
|
tr("Warning"),
|
|
tr("Fail to sort attachments of note <span style=\"%1\">%2</span>.")
|
|
.arg(g_config->c_dataTextStyle)
|
|
.arg(m_file->getName()),
|
|
"",
|
|
QMessageBox::Ok,
|
|
QMessageBox::Ok,
|
|
this);
|
|
}
|
|
}
|
|
}
|
|
|
|
void VAttachmentList::handleListItemCommitData(QWidget *p_itemEdit)
|
|
{
|
|
QString text = reinterpret_cast<VLineEdit *>(p_itemEdit)->text();
|
|
QListWidgetItem *item = m_attachmentList->currentItem();
|
|
Q_ASSERT(item && item->text() == text);
|
|
|
|
QString oldText = item->data(Qt::UserRole).toString();
|
|
|
|
if (oldText == text) {
|
|
return;
|
|
}
|
|
|
|
bool legalName = true;
|
|
if (text.isEmpty()) {
|
|
legalName = false;
|
|
} else {
|
|
QRegExp reg(VUtils::c_fileNameRegExp);
|
|
if (!reg.exactMatch(text)) {
|
|
legalName = false;
|
|
}
|
|
}
|
|
|
|
if (!legalName) {
|
|
// Recover to old name.
|
|
item->setText(oldText);
|
|
return;
|
|
}
|
|
|
|
if (!(oldText.toLower() == text.toLower())
|
|
&& m_file->findAttachment(text, false) > -1) {
|
|
// Name conflict.
|
|
// Recover to old name.
|
|
item->setText(oldText);
|
|
} else {
|
|
if (!m_file->renameAttachment(oldText, text)) {
|
|
VUtils::showMessage(QMessageBox::Information,
|
|
tr("Rename Attachment"),
|
|
tr("Fail to rename attachment <span style=\"%1\">%2</span>.")
|
|
.arg(g_config->c_dataTextStyle)
|
|
.arg(oldText),
|
|
"",
|
|
QMessageBox::Ok,
|
|
QMessageBox::Ok,
|
|
this);
|
|
// Recover to old name.
|
|
item->setText(oldText);
|
|
} else {
|
|
// Change the data.
|
|
item->setData(Qt::UserRole, text);
|
|
}
|
|
}
|
|
}
|
|
|
|
void VAttachmentList::keyPressEvent(QKeyEvent *p_event)
|
|
{
|
|
if (VimNavigationForWidget::injectKeyPressEventForVim(m_attachmentList,
|
|
p_event,
|
|
this)) {
|
|
return;
|
|
}
|
|
|
|
QWidget::keyPressEvent(p_event);
|
|
}
|
|
|
|
bool VAttachmentList::isAcceptDrops() const
|
|
{
|
|
return true;
|
|
}
|
|
|
|
bool VAttachmentList::handleDragEnterEvent(QDragEnterEvent *p_event)
|
|
{
|
|
if (!m_file) {
|
|
return false;
|
|
}
|
|
|
|
if (p_event->mimeData()->hasFormat("text/uri-list")) {
|
|
p_event->acceptProposedAction();
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool VAttachmentList::handleDropEvent(QDropEvent *p_event)
|
|
{
|
|
if (!m_file) {
|
|
return false;
|
|
}
|
|
|
|
const QMimeData *mime = p_event->mimeData();
|
|
if (mime->hasFormat("text/uri-list") && mime->hasUrls()) {
|
|
// Add attachments.
|
|
QStringList files;
|
|
QList<QUrl> urls = mime->urls();
|
|
for (int i = 0; i < urls.size(); ++i) {
|
|
QString file;
|
|
if (urls[i].isLocalFile()) {
|
|
file = urls[i].toLocalFile();
|
|
QFileInfo fi(file);
|
|
if (fi.exists() && fi.isFile()) {
|
|
file = QDir::cleanPath(fi.absoluteFilePath());
|
|
files.append(file);
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!files.isEmpty()) {
|
|
addAttachments(files);
|
|
|
|
updateButtonState();
|
|
}
|
|
|
|
p_event->acceptProposedAction();
|
|
return true;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void VAttachmentList::handleAboutToShow()
|
|
{
|
|
updateContent();
|
|
|
|
checkAttachments();
|
|
}
|
|
|
|
void VAttachmentList::updateButtonState() const
|
|
{
|
|
VButtonWithWidget *btn = getButton();
|
|
Q_ASSERT(btn);
|
|
if (!btn) {
|
|
return;
|
|
}
|
|
|
|
int numOfAttachments = -1;
|
|
if (m_file) {
|
|
numOfAttachments = m_file->getAttachments().size();
|
|
if (numOfAttachments == 0) {
|
|
numOfAttachments = -1;
|
|
}
|
|
}
|
|
|
|
btn->setBubbleNumber(numOfAttachments);
|
|
}
|
|
|
|
void VAttachmentList::checkAttachments()
|
|
{
|
|
if (!m_file) {
|
|
return;
|
|
}
|
|
|
|
QVector<QString> missingAttas = m_file->checkAttachments();
|
|
if (missingAttas.isEmpty()) {
|
|
return;
|
|
}
|
|
|
|
QVector<ConfirmItemInfo> items;
|
|
for (auto const & atta : missingAttas) {
|
|
items.push_back(ConfirmItemInfo(atta,
|
|
atta,
|
|
"",
|
|
NULL));
|
|
}
|
|
|
|
QString text = tr("VNote detects that these attachments of note "
|
|
"<span style=\"%1\">%2</span> are missing in disk. "
|
|
"Would you like to remove them from the note?")
|
|
.arg(g_config->c_dataTextStyle)
|
|
.arg(m_file->getName());
|
|
|
|
QString info = tr("Click \"Cancel\" to leave them untouched.");
|
|
|
|
VConfirmDeletionDialog dialog(tr("Confirm Deleting Attachments"),
|
|
text,
|
|
info,
|
|
items,
|
|
false,
|
|
false,
|
|
false,
|
|
g_mainWin);
|
|
if (dialog.exec()) {
|
|
items = dialog.getConfirmedItems();
|
|
|
|
QVector<QString> names;
|
|
for (auto const & item : items) {
|
|
names.push_back(item.m_name);
|
|
}
|
|
|
|
if (!m_file->deleteAttachments(names, true)) {
|
|
VUtils::showMessage(QMessageBox::Warning,
|
|
tr("Warning"),
|
|
tr("Fail to delete attachments of note <span style=\"%1\">%2</span>.")
|
|
.arg(g_config->c_dataTextStyle)
|
|
.arg(m_file->getName()),
|
|
tr("Please check the attachments folder and "
|
|
"maintain the configuration file manually."),
|
|
QMessageBox::Ok,
|
|
QMessageBox::Ok,
|
|
g_mainWin);
|
|
}
|
|
|
|
updateButtonState();
|
|
|
|
updateContent();
|
|
}
|
|
}
|
|
|
|
void VAttachmentList::showEvent(QShowEvent *p_event)
|
|
{
|
|
QWidget::showEvent(p_event);
|
|
|
|
processShowEvent(p_event);
|
|
}
|