mirror of
https://github.com/keepassxreboot/keepassxc.git
synced 2024-10-01 01:26:01 -04:00
Move attachments view to the separate widget EntryAttachmentsWidget
This commit is contained in:
parent
ed0bda98a6
commit
c490c21cd5
@ -130,6 +130,7 @@ set(keepassx_SOURCES
|
|||||||
gui/entry/EditEntryWidget.cpp
|
gui/entry/EditEntryWidget.cpp
|
||||||
gui/entry/EditEntryWidget_p.h
|
gui/entry/EditEntryWidget_p.h
|
||||||
gui/entry/EntryAttachmentsModel.cpp
|
gui/entry/EntryAttachmentsModel.cpp
|
||||||
|
gui/entry/EntryAttachmentsWidget.cpp
|
||||||
gui/entry/EntryAttributesModel.cpp
|
gui/entry/EntryAttributesModel.cpp
|
||||||
gui/entry/EntryHistoryModel.cpp
|
gui/entry/EntryHistoryModel.cpp
|
||||||
gui/entry/EntryModel.cpp
|
gui/entry/EntryModel.cpp
|
||||||
|
@ -111,8 +111,8 @@ DatabaseWidget::DatabaseWidget(Database* db, QWidget* parent)
|
|||||||
"border-radius: 5px;");
|
"border-radius: 5px;");
|
||||||
|
|
||||||
m_detailsView = new DetailsWidget(this);
|
m_detailsView = new DetailsWidget(this);
|
||||||
connect(m_detailsView, &DetailsWidget::errorOccured, this, [this](const QString& error) {
|
connect(m_detailsView, &DetailsWidget::errorOccurred, this, [this](const QString& error) {
|
||||||
showMessage(error, MessageWidget::MessageType::Error, false);
|
showMessage(error, MessageWidget::MessageType::Error);
|
||||||
});
|
});
|
||||||
|
|
||||||
QVBoxLayout* vLayout = new QVBoxLayout(rightHandSideWidget);
|
QVBoxLayout* vLayout = new QVBoxLayout(rightHandSideWidget);
|
||||||
|
@ -376,7 +376,7 @@ void DetailsWidget::openAttachment(const QModelIndex& index)
|
|||||||
|
|
||||||
if (!saveOk) {
|
if (!saveOk) {
|
||||||
delete tmpFile;
|
delete tmpFile;
|
||||||
emit errorOccured(tr("Unable to open the attachment:\n").append(tmpFile->errorString()));
|
emit errorOccurred(tr("Unable to open the attachment:\n").append(tmpFile->errorString()));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,7 +52,7 @@ public:
|
|||||||
};
|
};
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
void errorOccured(const QString& error);
|
void errorOccurred(const QString& error);
|
||||||
|
|
||||||
private slots:
|
private slots:
|
||||||
void getSelectedEntry(Entry* selectedEntry);
|
void getSelectedEntry(Entry* selectedEntry);
|
||||||
|
@ -70,8 +70,6 @@ EditEntryWidget::EditEntryWidget(QWidget* parent)
|
|||||||
, m_sshAgentWidget(new QWidget())
|
, m_sshAgentWidget(new QWidget())
|
||||||
, m_editWidgetProperties(new EditWidgetProperties())
|
, m_editWidgetProperties(new EditWidgetProperties())
|
||||||
, m_historyWidget(new QWidget())
|
, m_historyWidget(new QWidget())
|
||||||
, m_entryAttachments(new EntryAttachments(this))
|
|
||||||
, m_attachmentsModel(new EntryAttachmentsModel(m_advancedWidget))
|
|
||||||
, m_entryAttributes(new EntryAttributes(this))
|
, m_entryAttributes(new EntryAttributes(this))
|
||||||
, m_attributesModel(new EntryAttributesModel(m_advancedWidget))
|
, m_attributesModel(new EntryAttributesModel(m_advancedWidget))
|
||||||
, m_historyModel(new EntryHistoryModel(this))
|
, m_historyModel(new EntryHistoryModel(this))
|
||||||
@ -140,24 +138,12 @@ void EditEntryWidget::setupAdvanced()
|
|||||||
m_advancedUi->setupUi(m_advancedWidget);
|
m_advancedUi->setupUi(m_advancedWidget);
|
||||||
addPage(tr("Advanced"), FilePath::instance()->icon("categories", "preferences-other"), m_advancedWidget);
|
addPage(tr("Advanced"), FilePath::instance()->icon("categories", "preferences-other"), m_advancedWidget);
|
||||||
|
|
||||||
m_advancedUi->attachmentsView->setAcceptDrops(false);
|
m_advancedUi->attachmentsWidget->setReadOnly(false);
|
||||||
m_advancedUi->attachmentsView->viewport()->setAcceptDrops(true);
|
m_advancedUi->attachmentsWidget->setButtonsVisible(true);
|
||||||
m_advancedUi->attachmentsView->viewport()->installEventFilter(this);
|
|
||||||
|
|
||||||
m_attachmentsModel->setEntryAttachments(m_entryAttachments);
|
connect(m_advancedUi->attachmentsWidget, &EntryAttachmentsWidget::errorOccurred, this, [this](const QString &error) {
|
||||||
m_advancedUi->attachmentsView->setModel(m_attachmentsModel);
|
showMessage(error, MessageWidget::Error);
|
||||||
m_advancedUi->attachmentsView->horizontalHeader()->setStretchLastSection(true);
|
});
|
||||||
m_advancedUi->attachmentsView->horizontalHeader()->resizeSection(0, 600);
|
|
||||||
m_advancedUi->attachmentsView->setSelectionBehavior(QAbstractItemView::SelectRows);
|
|
||||||
m_advancedUi->attachmentsView->setSelectionMode(QAbstractItemView::ExtendedSelection);
|
|
||||||
|
|
||||||
connect(m_advancedUi->attachmentsView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
|
|
||||||
SLOT(updateAttachmentButtonsEnabled()));
|
|
||||||
connect(m_advancedUi->attachmentsView, SIGNAL(doubleClicked(QModelIndex)), SLOT(openAttachment(QModelIndex)));
|
|
||||||
connect(m_advancedUi->saveAttachmentButton, SIGNAL(clicked()), SLOT(saveSelectedAttachments()));
|
|
||||||
connect(m_advancedUi->openAttachmentButton, SIGNAL(clicked()), SLOT(openSelectedAttachments()));
|
|
||||||
connect(m_advancedUi->addAttachmentButton, SIGNAL(clicked()), SLOT(insertAttachments()));
|
|
||||||
connect(m_advancedUi->removeAttachmentButton, SIGNAL(clicked()), SLOT(removeSelectedAttachments()));
|
|
||||||
|
|
||||||
m_attributesModel->setEntryAttributes(m_entryAttributes);
|
m_attributesModel->setEntryAttributes(m_entryAttributes);
|
||||||
m_advancedUi->attributesView->setModel(m_attributesModel);
|
m_advancedUi->attributesView->setModel(m_attributesModel);
|
||||||
@ -581,8 +567,8 @@ void EditEntryWidget::setForms(const Entry* entry, bool restore)
|
|||||||
m_mainUi->togglePasswordGeneratorButton->setChecked(false);
|
m_mainUi->togglePasswordGeneratorButton->setChecked(false);
|
||||||
m_mainUi->togglePasswordGeneratorButton->setDisabled(m_history);
|
m_mainUi->togglePasswordGeneratorButton->setDisabled(m_history);
|
||||||
m_mainUi->passwordGenerator->reset();
|
m_mainUi->passwordGenerator->reset();
|
||||||
m_advancedUi->addAttachmentButton->setEnabled(!m_history);
|
|
||||||
updateAttachmentButtonsEnabled();
|
m_advancedUi->attachmentsWidget->setReadOnly(m_history);
|
||||||
m_advancedUi->addAttributeButton->setEnabled(!m_history);
|
m_advancedUi->addAttributeButton->setEnabled(!m_history);
|
||||||
m_advancedUi->editAttributeButton->setEnabled(false);
|
m_advancedUi->editAttributeButton->setEnabled(false);
|
||||||
m_advancedUi->removeAttributeButton->setEnabled(false);
|
m_advancedUi->removeAttributeButton->setEnabled(false);
|
||||||
@ -613,7 +599,7 @@ void EditEntryWidget::setForms(const Entry* entry, bool restore)
|
|||||||
|
|
||||||
m_mainUi->notesEdit->setPlainText(entry->notes());
|
m_mainUi->notesEdit->setPlainText(entry->notes());
|
||||||
|
|
||||||
m_entryAttachments->copyDataFrom(entry->attachments());
|
m_advancedUi->attachmentsWidget->setEntryAttachments(entry->attachments());
|
||||||
m_entryAttributes->copyCustomKeysFrom(entry->attributes());
|
m_entryAttributes->copyCustomKeysFrom(entry->attributes());
|
||||||
|
|
||||||
if (m_attributesModel->rowCount() != 0) {
|
if (m_attributesModel->rowCount() != 0) {
|
||||||
@ -749,8 +735,8 @@ void EditEntryWidget::acceptEntry()
|
|||||||
void EditEntryWidget::updateEntryData(Entry* entry) const
|
void EditEntryWidget::updateEntryData(Entry* entry) const
|
||||||
{
|
{
|
||||||
entry->attributes()->copyCustomKeysFrom(m_entryAttributes);
|
entry->attributes()->copyCustomKeysFrom(m_entryAttributes);
|
||||||
entry->attachments()->copyDataFrom(m_entryAttachments);
|
entry->attachments()->copyDataFrom(m_advancedUi->attachmentsWidget->entryAttachments());
|
||||||
|
|
||||||
entry->setTitle(m_mainUi->titleEdit->text());
|
entry->setTitle(m_mainUi->titleEdit->text());
|
||||||
entry->setUsername(m_mainUi->usernameEdit->text());
|
entry->setUsername(m_mainUi->usernameEdit->text());
|
||||||
entry->setUrl(m_mainUi->urlEdit->text());
|
entry->setUrl(m_mainUi->urlEdit->text());
|
||||||
@ -807,7 +793,7 @@ void EditEntryWidget::clear()
|
|||||||
m_entry = nullptr;
|
m_entry = nullptr;
|
||||||
m_database = nullptr;
|
m_database = nullptr;
|
||||||
m_entryAttributes->clear();
|
m_entryAttributes->clear();
|
||||||
m_entryAttachments->clear();
|
m_advancedUi->attachmentsWidget->clearAttachments();
|
||||||
m_autoTypeAssoc->clear();
|
m_autoTypeAssoc->clear();
|
||||||
m_historyModel->clear();
|
m_historyModel->clear();
|
||||||
m_iconsWidget->reset();
|
m_iconsWidget->reset();
|
||||||
@ -950,69 +936,6 @@ void EditEntryWidget::displayAttribute(QModelIndex index, bool showProtected)
|
|||||||
m_advancedUi->protectAttributeButton->blockSignals(false);
|
m_advancedUi->protectAttributeButton->blockSignals(false);
|
||||||
}
|
}
|
||||||
|
|
||||||
bool EditEntryWidget::openAttachment(const QModelIndex &index, QString *errorMessage)
|
|
||||||
{
|
|
||||||
const QString filename = m_attachmentsModel->keyByIndex(index);
|
|
||||||
const QByteArray attachmentData = m_entryAttachments->value(filename);
|
|
||||||
|
|
||||||
// tmp file will be removed once the database (or the application) has been closed
|
|
||||||
const QString tmpFileTemplate = QDir::temp().absoluteFilePath(QString("XXXXXX.").append(filename));
|
|
||||||
QTemporaryFile* tmpFile = new QTemporaryFile(tmpFileTemplate, this);
|
|
||||||
|
|
||||||
const bool saveOk = tmpFile->open()
|
|
||||||
&& tmpFile->write(attachmentData) == attachmentData.size()
|
|
||||||
&& tmpFile->flush();
|
|
||||||
if (!saveOk) {
|
|
||||||
if (errorMessage) {
|
|
||||||
*errorMessage = tr("Unable to save the attachment:\n").append(tmpFile->errorString());
|
|
||||||
}
|
|
||||||
delete tmpFile;
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
tmpFile->close();
|
|
||||||
QDesktopServices::openUrl(QUrl::fromLocalFile(tmpFile->fileName()));
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
bool EditEntryWidget::eventFilter(QObject* watched, QEvent* e)
|
|
||||||
{
|
|
||||||
switch (e->type()) {
|
|
||||||
case QEvent::DragEnter:
|
|
||||||
case QEvent::DragMove: {
|
|
||||||
QDropEvent* dropEv = static_cast<QDropEvent*>(e);
|
|
||||||
const QMimeData* mimeData = dropEv->mimeData();
|
|
||||||
if (mimeData->hasUrls()) {
|
|
||||||
dropEv->acceptProposedAction();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
case QEvent::Drop: {
|
|
||||||
QDropEvent* dropEv = static_cast<QDropEvent*>(e);
|
|
||||||
const QMimeData* mimeData = dropEv->mimeData();
|
|
||||||
if (mimeData->hasUrls()) {
|
|
||||||
dropEv->acceptProposedAction();
|
|
||||||
QStringList filenames;
|
|
||||||
for (const QUrl url: mimeData->urls()) {
|
|
||||||
QFileInfo fInfo(url.toLocalFile());
|
|
||||||
if (fInfo.isFile()) {
|
|
||||||
filenames.append(fInfo.absoluteFilePath());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
insertAttachments(filenames);
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
return EditWidget::eventFilter(watched, e);
|
|
||||||
}
|
|
||||||
|
|
||||||
void EditEntryWidget::protectCurrentAttribute(bool state)
|
void EditEntryWidget::protectCurrentAttribute(bool state)
|
||||||
{
|
{
|
||||||
QModelIndex index = m_advancedUi->attributesView->currentIndex();
|
QModelIndex index = m_advancedUi->attributesView->currentIndex();
|
||||||
@ -1043,198 +966,6 @@ void EditEntryWidget::revealCurrentAttribute()
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void EditEntryWidget::insertAttachments(const QStringList& filenames, QString* errorMessage)
|
|
||||||
{
|
|
||||||
QStringList errors;
|
|
||||||
for (const QString &filename: filenames) {
|
|
||||||
const QFileInfo fInfo(filename);
|
|
||||||
QFile file(filename);
|
|
||||||
QByteArray data;
|
|
||||||
const bool readOk = file.open(QIODevice::ReadOnly) && Tools::readAllFromDevice(&file, data);
|
|
||||||
if (readOk) {
|
|
||||||
m_entryAttachments->set(fInfo.fileName(), data);
|
|
||||||
} else {
|
|
||||||
errors.append(QString("%1 - %2").arg(fInfo.fileName(), file.errorString()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (errorMessage && !errors.isEmpty()) {
|
|
||||||
*errorMessage = tr("Unable to open files:\n%1").arg(errors.join('\n'));;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void EditEntryWidget::insertAttachments()
|
|
||||||
{
|
|
||||||
Q_ASSERT(!m_history);
|
|
||||||
|
|
||||||
QString defaultDir = config()->get("LastAttachmentDir").toString();
|
|
||||||
if (defaultDir.isEmpty() || !QDir(defaultDir).exists()) {
|
|
||||||
defaultDir = QStandardPaths::standardLocations(QStandardPaths::DocumentsLocation).value(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
const QStringList filenames = fileDialog()->getOpenFileNames(this, tr("Select files"), defaultDir);
|
|
||||||
if (filenames.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
config()->set("LastAttachmentDir", QFileInfo(filenames.first()).absolutePath());
|
|
||||||
|
|
||||||
QString error;
|
|
||||||
insertAttachments(filenames, &error);
|
|
||||||
if (!error.isEmpty()) {
|
|
||||||
showMessage(error, MessageWidget::Error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void EditEntryWidget::saveSelectedAttachment()
|
|
||||||
{
|
|
||||||
const QModelIndex index = m_advancedUi->attachmentsView->currentIndex();
|
|
||||||
if (!index.isValid()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const QString filename = m_attachmentsModel->keyByIndex(index);
|
|
||||||
QString defaultDirName = config()->get("LastAttachmentDir").toString();
|
|
||||||
if (defaultDirName.isEmpty() || !QDir(defaultDirName).exists()) {
|
|
||||||
defaultDirName = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
|
|
||||||
}
|
|
||||||
|
|
||||||
const QString savePath = fileDialog()->getSaveFileName(this, tr("Save attachment"),
|
|
||||||
QDir(defaultDirName).filePath(filename));
|
|
||||||
if (!savePath.isEmpty()) {
|
|
||||||
config()->set("LastAttachmentDir", QFileInfo(savePath).absolutePath());
|
|
||||||
|
|
||||||
QFile file(savePath);
|
|
||||||
const QByteArray attachmentData = m_entryAttachments->value(filename);
|
|
||||||
const bool saveOk = file.open(QIODevice::WriteOnly) && file.write(attachmentData) == attachmentData.size();
|
|
||||||
if (!saveOk) {
|
|
||||||
showMessage(tr("Unable to save the attachment:\n").append(file.errorString()), MessageWidget::Error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void EditEntryWidget::saveSelectedAttachments()
|
|
||||||
{
|
|
||||||
const QModelIndexList indexes = m_advancedUi->attachmentsView->selectionModel()->selectedRows(0);
|
|
||||||
if (indexes.isEmpty()) {
|
|
||||||
return;
|
|
||||||
} else if (indexes.count() == 1) {
|
|
||||||
saveSelectedAttachment();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString defaultDirName = config()->get("LastAttachmentDir").toString();
|
|
||||||
if (defaultDirName.isEmpty() || !QDir(defaultDirName).exists()) {
|
|
||||||
defaultDirName = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
|
|
||||||
}
|
|
||||||
|
|
||||||
const QString savePath = fileDialog()->getExistingDirectory(this, tr("Save attachments"), defaultDirName);
|
|
||||||
if (savePath.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
QDir saveDir(savePath);
|
|
||||||
if (!saveDir.exists()) {
|
|
||||||
if (saveDir.mkpath(saveDir.absolutePath())) {
|
|
||||||
showMessage(tr("Unable to create the directory:\n").append(saveDir.absolutePath()), MessageWidget::Error);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
config()->set("LastAttachmentDir", QFileInfo(saveDir.absolutePath()).absolutePath());
|
|
||||||
|
|
||||||
QStringList errors;
|
|
||||||
for (const QModelIndex &index: indexes) {
|
|
||||||
const QString filename = m_attachmentsModel->keyByIndex(index);
|
|
||||||
const QString attachmentPath = saveDir.absoluteFilePath(filename);
|
|
||||||
|
|
||||||
if (QFileInfo::exists(attachmentPath)) {
|
|
||||||
const QString question(tr("Are you sure you want to overwrite existing file \"%1\" with the attachment?"));
|
|
||||||
auto ans = MessageBox::question(this, tr("Confirm overwrite"), question.arg(filename),
|
|
||||||
QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel);
|
|
||||||
if (ans == QMessageBox::No) {
|
|
||||||
continue;
|
|
||||||
} else if (ans == QMessageBox::Cancel) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
QFile file(attachmentPath);
|
|
||||||
const QByteArray attachmentData = m_entryAttachments->value(filename);
|
|
||||||
const bool saveOk = file.open(QIODevice::WriteOnly) && file.write(attachmentData) == attachmentData.size();
|
|
||||||
if (!saveOk) {
|
|
||||||
errors.append(QString("%1 - %2").arg(filename, file.errorString()));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!errors.isEmpty()) {
|
|
||||||
showMessage(tr("Unable to save the attachments:\n").append(errors.join('\n')), MessageWidget::Error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void EditEntryWidget::openAttachment(const QModelIndex& index)
|
|
||||||
{
|
|
||||||
if (!index.isValid()) {
|
|
||||||
Q_ASSERT(false);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
QString errorMessage;
|
|
||||||
if (!openAttachment(index, &errorMessage)) {
|
|
||||||
showMessage(errorMessage, MessageWidget::Error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void EditEntryWidget::openSelectedAttachments()
|
|
||||||
{
|
|
||||||
const QModelIndexList indexes = m_advancedUi->attachmentsView->selectionModel()->selectedRows(0);
|
|
||||||
if (indexes.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
QStringList errors;
|
|
||||||
for (const QModelIndex &index: indexes) {
|
|
||||||
QString errorMessage;
|
|
||||||
if (!openAttachment(index, &errorMessage)) {
|
|
||||||
const QString filename = m_attachmentsModel->keyByIndex(index);
|
|
||||||
errors.append(QString("%1 - %2").arg(filename, errorMessage));
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!errors.isEmpty()) {
|
|
||||||
showMessage(tr("Unable to open the attachments:\n").append(errors.join('\n')), MessageWidget::Error);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void EditEntryWidget::removeSelectedAttachments()
|
|
||||||
{
|
|
||||||
Q_ASSERT(!m_history);
|
|
||||||
|
|
||||||
const QModelIndexList indexes = m_advancedUi->attachmentsView->selectionModel()->selectedRows(0);
|
|
||||||
if (indexes.isEmpty()) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const QString question = tr("Are you sure you want to remove %n attachments?", "", indexes.count());
|
|
||||||
QMessageBox::StandardButton ans = MessageBox::question(this, tr("Confirm Remove"),
|
|
||||||
question, QMessageBox::Yes | QMessageBox::No);
|
|
||||||
if (ans == QMessageBox::Yes) {
|
|
||||||
QStringList keys;
|
|
||||||
for (const QModelIndex &index: indexes) {
|
|
||||||
keys.append(m_attachmentsModel->keyByIndex(index));
|
|
||||||
}
|
|
||||||
m_entryAttachments->remove(keys);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void EditEntryWidget::updateAttachmentButtonsEnabled()
|
|
||||||
{
|
|
||||||
const bool hasSelection = m_advancedUi->attachmentsView->selectionModel()->hasSelection();
|
|
||||||
|
|
||||||
m_advancedUi->saveAttachmentButton->setEnabled(hasSelection);
|
|
||||||
m_advancedUi->openAttachmentButton->setEnabled(hasSelection);
|
|
||||||
m_advancedUi->removeAttachmentButton->setEnabled(hasSelection && !m_history);
|
|
||||||
}
|
|
||||||
|
|
||||||
void EditEntryWidget::updateAutoTypeEnabled()
|
void EditEntryWidget::updateAutoTypeEnabled()
|
||||||
{
|
{
|
||||||
bool autoTypeEnabled = m_autoTypeUi->enableButton->isChecked();
|
bool autoTypeEnabled = m_autoTypeUi->enableButton->isChecked();
|
||||||
|
@ -31,8 +31,6 @@ class Database;
|
|||||||
class EditWidgetIcons;
|
class EditWidgetIcons;
|
||||||
class EditWidgetProperties;
|
class EditWidgetProperties;
|
||||||
class Entry;
|
class Entry;
|
||||||
class EntryAttachments;
|
|
||||||
class EntryAttachmentsModel;
|
|
||||||
class EntryAttributes;
|
class EntryAttributes;
|
||||||
class EntryAttributesModel;
|
class EntryAttributesModel;
|
||||||
class EntryHistoryModel;
|
class EntryHistoryModel;
|
||||||
@ -86,14 +84,6 @@ private slots:
|
|||||||
void updateCurrentAttribute();
|
void updateCurrentAttribute();
|
||||||
void protectCurrentAttribute(bool state);
|
void protectCurrentAttribute(bool state);
|
||||||
void revealCurrentAttribute();
|
void revealCurrentAttribute();
|
||||||
void insertAttachments(const QStringList& fileNames, QString* errorMessage = nullptr);
|
|
||||||
void insertAttachments();
|
|
||||||
void saveSelectedAttachment();
|
|
||||||
void saveSelectedAttachments();
|
|
||||||
void openAttachment(const QModelIndex& index);
|
|
||||||
void openSelectedAttachments();
|
|
||||||
void removeSelectedAttachments();
|
|
||||||
void updateAttachmentButtonsEnabled();
|
|
||||||
void updateAutoTypeEnabled();
|
void updateAutoTypeEnabled();
|
||||||
void insertAutoTypeAssoc();
|
void insertAutoTypeAssoc();
|
||||||
void removeAutoTypeAssoc();
|
void removeAutoTypeAssoc();
|
||||||
@ -141,10 +131,6 @@ private:
|
|||||||
|
|
||||||
void displayAttribute(QModelIndex index, bool showProtected);
|
void displayAttribute(QModelIndex index, bool showProtected);
|
||||||
|
|
||||||
bool openAttachment(const QModelIndex& index, QString *errorMessage);
|
|
||||||
|
|
||||||
bool eventFilter(QObject* watched, QEvent* event) override;
|
|
||||||
|
|
||||||
Entry* m_entry;
|
Entry* m_entry;
|
||||||
Database* m_database;
|
Database* m_database;
|
||||||
|
|
||||||
@ -167,8 +153,6 @@ private:
|
|||||||
QWidget* const m_sshAgentWidget;
|
QWidget* const m_sshAgentWidget;
|
||||||
EditWidgetProperties* const m_editWidgetProperties;
|
EditWidgetProperties* const m_editWidgetProperties;
|
||||||
QWidget* const m_historyWidget;
|
QWidget* const m_historyWidget;
|
||||||
EntryAttachments* const m_entryAttachments;
|
|
||||||
EntryAttachmentsModel* const m_attachmentsModel;
|
|
||||||
EntryAttributes* const m_entryAttributes;
|
EntryAttributes* const m_entryAttributes;
|
||||||
EntryAttributesModel* const m_attributesModel;
|
EntryAttributesModel* const m_attributesModel;
|
||||||
EntryHistoryModel* const m_historyModel;
|
EntryHistoryModel* const m_historyModel;
|
||||||
|
@ -2,14 +2,6 @@
|
|||||||
<ui version="4.0">
|
<ui version="4.0">
|
||||||
<class>EditEntryWidgetAdvanced</class>
|
<class>EditEntryWidgetAdvanced</class>
|
||||||
<widget class="QWidget" name="EditEntryWidgetAdvanced">
|
<widget class="QWidget" name="EditEntryWidgetAdvanced">
|
||||||
<property name="geometry">
|
|
||||||
<rect>
|
|
||||||
<x>0</x>
|
|
||||||
<y>0</y>
|
|
||||||
<width>400</width>
|
|
||||||
<height>366</height>
|
|
||||||
</rect>
|
|
||||||
</property>
|
|
||||||
<layout class="QVBoxLayout" name="verticalLayout">
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
<property name="leftMargin">
|
<property name="leftMargin">
|
||||||
<number>0</number>
|
<number>0</number>
|
||||||
@ -153,61 +145,14 @@
|
|||||||
</property>
|
</property>
|
||||||
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
<layout class="QHBoxLayout" name="horizontalLayout_2">
|
||||||
<item>
|
<item>
|
||||||
<widget class="QTableView" name="attachmentsView"/>
|
<widget class="EntryAttachmentsWidget" name="attachmentsWidget" native="true">
|
||||||
</item>
|
<property name="minimumSize">
|
||||||
<item>
|
<size>
|
||||||
<layout class="QVBoxLayout" name="attachmentsButtonLayout">
|
<width>100</width>
|
||||||
<item>
|
<height>100</height>
|
||||||
<widget class="QPushButton" name="addAttachmentButton">
|
</size>
|
||||||
<property name="text">
|
</property>
|
||||||
<string>Add</string>
|
</widget>
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QPushButton" name="removeAttachmentButton">
|
|
||||||
<property name="enabled">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Remove</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QPushButton" name="openAttachmentButton">
|
|
||||||
<property name="enabled">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Open</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<widget class="QPushButton" name="saveAttachmentButton">
|
|
||||||
<property name="enabled">
|
|
||||||
<bool>false</bool>
|
|
||||||
</property>
|
|
||||||
<property name="text">
|
|
||||||
<string>Save</string>
|
|
||||||
</property>
|
|
||||||
</widget>
|
|
||||||
</item>
|
|
||||||
<item>
|
|
||||||
<spacer name="verticalSpacer_2">
|
|
||||||
<property name="orientation">
|
|
||||||
<enum>Qt::Vertical</enum>
|
|
||||||
</property>
|
|
||||||
<property name="sizeHint" stdset="0">
|
|
||||||
<size>
|
|
||||||
<width>20</width>
|
|
||||||
<height>40</height>
|
|
||||||
</size>
|
|
||||||
</property>
|
|
||||||
</spacer>
|
|
||||||
</item>
|
|
||||||
</layout>
|
|
||||||
</item>
|
</item>
|
||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
@ -215,6 +160,12 @@
|
|||||||
</layout>
|
</layout>
|
||||||
</widget>
|
</widget>
|
||||||
<customwidgets>
|
<customwidgets>
|
||||||
|
<customwidget>
|
||||||
|
<class>EntryAttachmentsWidget</class>
|
||||||
|
<extends>QWidget</extends>
|
||||||
|
<header>gui/entry/EntryAttachmentsWidget.h</header>
|
||||||
|
<container>1</container>
|
||||||
|
</customwidget>
|
||||||
<customwidget>
|
<customwidget>
|
||||||
<class>AttributesListView</class>
|
<class>AttributesListView</class>
|
||||||
<extends>QListView</extends>
|
<extends>QListView</extends>
|
||||||
@ -227,10 +178,6 @@
|
|||||||
<tabstop>addAttributeButton</tabstop>
|
<tabstop>addAttributeButton</tabstop>
|
||||||
<tabstop>removeAttributeButton</tabstop>
|
<tabstop>removeAttributeButton</tabstop>
|
||||||
<tabstop>editAttributeButton</tabstop>
|
<tabstop>editAttributeButton</tabstop>
|
||||||
<tabstop>addAttachmentButton</tabstop>
|
|
||||||
<tabstop>removeAttachmentButton</tabstop>
|
|
||||||
<tabstop>openAttachmentButton</tabstop>
|
|
||||||
<tabstop>saveAttachmentButton</tabstop>
|
|
||||||
</tabstops>
|
</tabstops>
|
||||||
<resources/>
|
<resources/>
|
||||||
<connections/>
|
<connections/>
|
||||||
|
353
src/gui/entry/EntryAttachmentsWidget.cpp
Normal file
353
src/gui/entry/EntryAttachmentsWidget.cpp
Normal file
@ -0,0 +1,353 @@
|
|||||||
|
#include "EntryAttachmentsWidget.h"
|
||||||
|
#include "ui_EntryAttachmentsWidget.h"
|
||||||
|
|
||||||
|
#include <QDesktopServices>
|
||||||
|
#include <QDir>
|
||||||
|
#include <QDropEvent>
|
||||||
|
#include <QFile>
|
||||||
|
#include <QFileInfo>
|
||||||
|
#include <QMimeData>
|
||||||
|
#include <QTemporaryFile>
|
||||||
|
|
||||||
|
#include "EntryAttachmentsModel.h"
|
||||||
|
#include "core/Config.h"
|
||||||
|
#include "core/EntryAttachments.h"
|
||||||
|
#include "core/Tools.h"
|
||||||
|
#include "gui/FileDialog.h"
|
||||||
|
#include "gui/MessageBox.h"
|
||||||
|
|
||||||
|
|
||||||
|
EntryAttachmentsWidget::EntryAttachmentsWidget(QWidget* parent) :
|
||||||
|
QWidget(parent)
|
||||||
|
, m_ui(new Ui::EntryAttachmentsWidget)
|
||||||
|
, m_entryAttachments(new EntryAttachments(this))
|
||||||
|
, m_attachmentsModel(new EntryAttachmentsModel(this))
|
||||||
|
, m_readOnly(false)
|
||||||
|
, m_buttonsVisible(true)
|
||||||
|
{
|
||||||
|
m_ui->setupUi(this);
|
||||||
|
|
||||||
|
m_ui->attachmentsView->setAcceptDrops(false);
|
||||||
|
m_ui->attachmentsView->viewport()->setAcceptDrops(true);
|
||||||
|
m_ui->attachmentsView->viewport()->installEventFilter(this);
|
||||||
|
|
||||||
|
m_attachmentsModel->setEntryAttachments(m_entryAttachments);
|
||||||
|
m_ui->attachmentsView->setModel(m_attachmentsModel);
|
||||||
|
m_ui->attachmentsView->verticalHeader()->hide();
|
||||||
|
m_ui->attachmentsView->horizontalHeader()->setStretchLastSection(true);
|
||||||
|
m_ui->attachmentsView->horizontalHeader()->resizeSection(EntryAttachmentsModel::NameColumn, 400);
|
||||||
|
m_ui->attachmentsView->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||||||
|
m_ui->attachmentsView->setSelectionMode(QAbstractItemView::ExtendedSelection);
|
||||||
|
|
||||||
|
m_ui->actionsWidget->setVisible(m_buttonsVisible);
|
||||||
|
connect(this, SIGNAL(buttonsVisibleChanged(bool)), m_ui->actionsWidget, SLOT(setVisible(bool)));
|
||||||
|
|
||||||
|
connect(this, SIGNAL(readOnlyChanged(bool)), SLOT(updateButtonsEnabled()));
|
||||||
|
connect(m_attachmentsModel, SIGNAL(modelReset()), SLOT(updateButtonsEnabled()));
|
||||||
|
connect(m_ui->attachmentsView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)),
|
||||||
|
SLOT(updateButtonsEnabled()));
|
||||||
|
|
||||||
|
connect(m_ui->attachmentsView, SIGNAL(doubleClicked(QModelIndex)), SLOT(openAttachment(QModelIndex)));
|
||||||
|
connect(m_ui->saveAttachmentButton, SIGNAL(clicked()), SLOT(saveSelectedAttachments()));
|
||||||
|
connect(m_ui->openAttachmentButton, SIGNAL(clicked()), SLOT(openSelectedAttachments()));
|
||||||
|
connect(m_ui->addAttachmentButton, SIGNAL(clicked()), SLOT(insertAttachments()));
|
||||||
|
connect(m_ui->removeAttachmentButton, SIGNAL(clicked()), SLOT(removeSelectedAttachments()));
|
||||||
|
|
||||||
|
updateButtonsEnabled();
|
||||||
|
}
|
||||||
|
|
||||||
|
EntryAttachmentsWidget::~EntryAttachmentsWidget()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
const EntryAttachments* EntryAttachmentsWidget::entryAttachments() const
|
||||||
|
{
|
||||||
|
return m_entryAttachments;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EntryAttachmentsWidget::isReadOnly() const
|
||||||
|
{
|
||||||
|
return m_readOnly;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EntryAttachmentsWidget::isButtonsVisible() const
|
||||||
|
{
|
||||||
|
return m_buttonsVisible;
|
||||||
|
}
|
||||||
|
|
||||||
|
void EntryAttachmentsWidget::setEntryAttachments(const EntryAttachments* attachments)
|
||||||
|
{
|
||||||
|
Q_ASSERT(attachments != nullptr);
|
||||||
|
m_entryAttachments->copyDataFrom(attachments);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EntryAttachmentsWidget::clearAttachments()
|
||||||
|
{
|
||||||
|
m_entryAttachments->clear();
|
||||||
|
}
|
||||||
|
|
||||||
|
void EntryAttachmentsWidget::setReadOnly(bool readOnly)
|
||||||
|
{
|
||||||
|
if (m_readOnly == readOnly) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_readOnly = readOnly;
|
||||||
|
emit readOnlyChanged(m_readOnly);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EntryAttachmentsWidget::setButtonsVisible(bool buttonsVisible)
|
||||||
|
{
|
||||||
|
if (m_buttonsVisible == buttonsVisible) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
m_buttonsVisible = buttonsVisible;
|
||||||
|
emit buttonsVisibleChanged(m_buttonsVisible);
|
||||||
|
}
|
||||||
|
|
||||||
|
void EntryAttachmentsWidget::insertAttachments()
|
||||||
|
{
|
||||||
|
Q_ASSERT(!isReadOnly());
|
||||||
|
if (isReadOnly()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString defaultDirPath = config()->get("LastAttachmentDir").toString();
|
||||||
|
const bool dirExists = !defaultDirPath.isEmpty() && QDir(defaultDirPath).exists();
|
||||||
|
if (!dirExists) {
|
||||||
|
defaultDirPath = QStandardPaths::standardLocations(QStandardPaths::DocumentsLocation).first();
|
||||||
|
}
|
||||||
|
|
||||||
|
const QStringList filenames = fileDialog()->getOpenFileNames(this, tr("Select files"), defaultDirPath);
|
||||||
|
if (filenames.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
config()->set("LastAttachmentDir", QFileInfo(filenames.first()).absolutePath());
|
||||||
|
|
||||||
|
QString errorMessage;
|
||||||
|
if (!insertAttachments(filenames, errorMessage)) {
|
||||||
|
errorOccurred(errorMessage);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EntryAttachmentsWidget::removeSelectedAttachments()
|
||||||
|
{
|
||||||
|
Q_ASSERT(!isReadOnly());
|
||||||
|
if (isReadOnly()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QModelIndexList indexes = m_ui->attachmentsView->selectionModel()->selectedRows(0);
|
||||||
|
if (indexes.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString question = tr("Are you sure you want to remove %n attachment(s)?", "", indexes.count());
|
||||||
|
QMessageBox::StandardButton answer = MessageBox::question(this, tr("Confirm Remove"),
|
||||||
|
question, QMessageBox::Yes | QMessageBox::No);
|
||||||
|
if (answer == QMessageBox::Yes) {
|
||||||
|
QStringList keys;
|
||||||
|
for (const QModelIndex& index: indexes) {
|
||||||
|
keys.append(m_attachmentsModel->keyByIndex(index));
|
||||||
|
}
|
||||||
|
m_entryAttachments->remove(keys);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EntryAttachmentsWidget::saveSelectedAttachments()
|
||||||
|
{
|
||||||
|
const QModelIndexList indexes = m_ui->attachmentsView->selectionModel()->selectedRows(0);
|
||||||
|
if (indexes.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString defaultDirPath = config()->get("LastAttachmentDir").toString();
|
||||||
|
const bool dirExists = !defaultDirPath.isEmpty() && QDir(defaultDirPath).exists();
|
||||||
|
if (!dirExists) {
|
||||||
|
defaultDirPath = QStandardPaths::writableLocation(QStandardPaths::DocumentsLocation);
|
||||||
|
}
|
||||||
|
|
||||||
|
const QString saveDirPath = fileDialog()->getExistingDirectory(this, tr("Save attachments"), defaultDirPath);
|
||||||
|
if (saveDirPath.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QDir saveDir(saveDirPath);
|
||||||
|
if (!saveDir.exists()) {
|
||||||
|
if (saveDir.mkpath(saveDir.absolutePath())) {
|
||||||
|
errorOccurred(tr("Unable to create directory:\n%1").arg(saveDir.absolutePath()));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
config()->set("LastAttachmentDir", QFileInfo(saveDir.absolutePath()).absolutePath());
|
||||||
|
|
||||||
|
QStringList errors;
|
||||||
|
for (const QModelIndex& index: indexes) {
|
||||||
|
const QString filename = m_attachmentsModel->keyByIndex(index);
|
||||||
|
const QString attachmentPath = saveDir.absoluteFilePath(filename);
|
||||||
|
|
||||||
|
if (QFileInfo::exists(attachmentPath)) {
|
||||||
|
const QString question(tr("Are you sure you want to overwrite the existing file \"%1\" with the attachment?"));
|
||||||
|
auto answer = MessageBox::question(this, tr("Confirm overwrite"), question.arg(filename),
|
||||||
|
QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel);
|
||||||
|
if (answer == QMessageBox::No) {
|
||||||
|
continue;
|
||||||
|
} else if (answer == QMessageBox::Cancel) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QFile file(attachmentPath);
|
||||||
|
const QByteArray attachmentData = m_entryAttachments->value(filename);
|
||||||
|
const bool saveOk = file.open(QIODevice::WriteOnly) && file.write(attachmentData) == attachmentData.size();
|
||||||
|
if (!saveOk) {
|
||||||
|
errors.append(QString("%1 - %2").arg(filename, file.errorString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!errors.isEmpty()) {
|
||||||
|
errorOccurred(tr("Unable to save attachments:\n%1").arg(errors.join('\n')));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EntryAttachmentsWidget::openAttachment(const QModelIndex& index)
|
||||||
|
{
|
||||||
|
Q_ASSERT(index.isValid());
|
||||||
|
if (!index.isValid()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QString errorMessage;
|
||||||
|
if (!openAttachment(index, errorMessage)) {
|
||||||
|
errorOccurred(tr("Unable to open attachment:\n%1").arg(errorMessage));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EntryAttachmentsWidget::openSelectedAttachments()
|
||||||
|
{
|
||||||
|
const QModelIndexList indexes = m_ui->attachmentsView->selectionModel()->selectedRows(0);
|
||||||
|
if (indexes.isEmpty()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList errors;
|
||||||
|
for (const QModelIndex& index: indexes) {
|
||||||
|
QString errorMessage;
|
||||||
|
if (!openAttachment(index, errorMessage)) {
|
||||||
|
const QString filename = m_attachmentsModel->keyByIndex(index);
|
||||||
|
errors.append(QString("%1 - %2").arg(filename, errorMessage));
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!errors.isEmpty()) {
|
||||||
|
errorOccurred(tr("Unable to open attachments:\n%1").arg(errors.join('\n')));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void EntryAttachmentsWidget::updateButtonsEnabled()
|
||||||
|
{
|
||||||
|
const bool hasSelection = m_ui->attachmentsView->selectionModel()->hasSelection();
|
||||||
|
|
||||||
|
m_ui->addAttachmentButton->setEnabled(!m_readOnly);
|
||||||
|
m_ui->removeAttachmentButton->setEnabled(hasSelection && !m_readOnly);
|
||||||
|
|
||||||
|
m_ui->saveAttachmentButton->setEnabled(hasSelection);
|
||||||
|
m_ui->openAttachmentButton->setEnabled(hasSelection);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EntryAttachmentsWidget::insertAttachments(const QStringList& filenames, QString& errorMessage)
|
||||||
|
{
|
||||||
|
Q_ASSERT(!isReadOnly());
|
||||||
|
if (isReadOnly()) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
QStringList errors;
|
||||||
|
for (const QString &filename: filenames) {
|
||||||
|
QByteArray data;
|
||||||
|
QFile file(filename);
|
||||||
|
const QFileInfo fInfo(filename);
|
||||||
|
const bool readOk = file.open(QIODevice::ReadOnly) && Tools::readAllFromDevice(&file, data);
|
||||||
|
if (readOk) {
|
||||||
|
m_entryAttachments->set(fInfo.fileName(), data);
|
||||||
|
} else {
|
||||||
|
errors.append(QString("%1 - %2").arg(fInfo.fileName(), file.errorString()));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!errors.isEmpty()) {
|
||||||
|
errorMessage = tr("Unable to open files:\n%1").arg(errors.join('\n'));
|
||||||
|
}
|
||||||
|
|
||||||
|
return errors.isEmpty();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EntryAttachmentsWidget::openAttachment(const QModelIndex& index, QString& errorMessage)
|
||||||
|
{
|
||||||
|
const QString filename = m_attachmentsModel->keyByIndex(index);
|
||||||
|
const QByteArray attachmentData = m_entryAttachments->value(filename);
|
||||||
|
|
||||||
|
// tmp file will be removed once the database (or the application) has been closed
|
||||||
|
const QString tmpFileTemplate = QDir::temp().absoluteFilePath(QString("XXXXXX.").append(filename));
|
||||||
|
|
||||||
|
QScopedPointer<QTemporaryFile> tmpFile(new QTemporaryFile(tmpFileTemplate, this));
|
||||||
|
|
||||||
|
const bool saveOk = tmpFile->open()
|
||||||
|
&& tmpFile->write(attachmentData) == attachmentData.size()
|
||||||
|
&& tmpFile->flush();
|
||||||
|
if (!saveOk) {
|
||||||
|
errorMessage = QString("%1 - %2").arg(filename, tmpFile->errorString());
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
tmpFile->close();
|
||||||
|
const bool openOk = QDesktopServices::openUrl(QUrl::fromLocalFile(tmpFile->fileName()));
|
||||||
|
if (!openOk) {
|
||||||
|
errorMessage = QString("Can't open file \"%1\"").arg(filename);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
// take ownership of the tmpFile pointer
|
||||||
|
tmpFile.take();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool EntryAttachmentsWidget::eventFilter(QObject* watched, QEvent* e)
|
||||||
|
{
|
||||||
|
if (watched == m_ui->attachmentsView->viewport() && !isReadOnly()) {
|
||||||
|
const QEvent::Type eventType = e->type();
|
||||||
|
if (eventType == QEvent::DragEnter || eventType == QEvent::DragMove) {
|
||||||
|
QDropEvent* dropEv = static_cast<QDropEvent*>(e);
|
||||||
|
const QMimeData* mimeData = dropEv->mimeData();
|
||||||
|
if (mimeData->hasUrls()) {
|
||||||
|
dropEv->acceptProposedAction();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
} else if (eventType == QEvent::Drop) {
|
||||||
|
QDropEvent* dropEv = static_cast<QDropEvent*>(e);
|
||||||
|
const QMimeData* mimeData = dropEv->mimeData();
|
||||||
|
if (mimeData->hasUrls()) {
|
||||||
|
dropEv->acceptProposedAction();
|
||||||
|
QStringList filenames;
|
||||||
|
const QList<QUrl> urls = mimeData->urls();
|
||||||
|
for (const QUrl& url: urls) {
|
||||||
|
const QFileInfo fInfo(url.toLocalFile());
|
||||||
|
if (fInfo.isFile()) {
|
||||||
|
filenames.append(fInfo.absoluteFilePath());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
QString errorMessage;
|
||||||
|
if (!insertAttachments(filenames, errorMessage)) {
|
||||||
|
errorOccurred(errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return QWidget::eventFilter(watched, e);
|
||||||
|
}
|
59
src/gui/entry/EntryAttachmentsWidget.h
Normal file
59
src/gui/entry/EntryAttachmentsWidget.h
Normal file
@ -0,0 +1,59 @@
|
|||||||
|
#ifndef ENTRYATTACHMENTSWIDGET_H
|
||||||
|
#define ENTRYATTACHMENTSWIDGET_H
|
||||||
|
|
||||||
|
#include <QPointer>
|
||||||
|
#include <QWidget>
|
||||||
|
|
||||||
|
namespace Ui {
|
||||||
|
class EntryAttachmentsWidget;
|
||||||
|
}
|
||||||
|
|
||||||
|
class EntryAttachments;
|
||||||
|
class EntryAttachmentsModel;
|
||||||
|
|
||||||
|
class EntryAttachmentsWidget : public QWidget
|
||||||
|
{
|
||||||
|
Q_OBJECT
|
||||||
|
Q_PROPERTY(bool readOnly READ isReadOnly WRITE setReadOnly NOTIFY readOnlyChanged)
|
||||||
|
Q_PROPERTY(bool isButtonsVisible READ isButtonsVisible WRITE setButtonsVisible NOTIFY buttonsVisibleChanged)
|
||||||
|
public:
|
||||||
|
explicit EntryAttachmentsWidget(QWidget* parent = nullptr);
|
||||||
|
~EntryAttachmentsWidget();
|
||||||
|
|
||||||
|
const EntryAttachments* entryAttachments() const;
|
||||||
|
bool isReadOnly() const;
|
||||||
|
bool isButtonsVisible() const;
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void setEntryAttachments(const EntryAttachments* attachments);
|
||||||
|
void clearAttachments();
|
||||||
|
void setReadOnly(bool readOnly);
|
||||||
|
void setButtonsVisible(bool isButtonsVisible);
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void errorOccurred(const QString& error);
|
||||||
|
void readOnlyChanged(bool readOnly);
|
||||||
|
void buttonsVisibleChanged(bool isButtonsVisible);
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void insertAttachments();
|
||||||
|
void removeSelectedAttachments();
|
||||||
|
void saveSelectedAttachments();
|
||||||
|
void openAttachment(const QModelIndex& index);
|
||||||
|
void openSelectedAttachments();
|
||||||
|
void updateButtonsEnabled();
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool insertAttachments(const QStringList& fileNames, QString& errorMessage);
|
||||||
|
bool openAttachment(const QModelIndex& index, QString& errorMessage);
|
||||||
|
|
||||||
|
bool eventFilter(QObject* watched, QEvent* event) override;
|
||||||
|
|
||||||
|
QScopedPointer<Ui::EntryAttachmentsWidget> m_ui;
|
||||||
|
QPointer<EntryAttachments> m_entryAttachments;
|
||||||
|
QPointer<EntryAttachmentsModel> m_attachmentsModel;
|
||||||
|
bool m_readOnly;
|
||||||
|
bool m_buttonsVisible;
|
||||||
|
};
|
||||||
|
|
||||||
|
#endif // ENTRYATTACHMENTSWIDGET_H
|
99
src/gui/entry/EntryAttachmentsWidget.ui
Normal file
99
src/gui/entry/EntryAttachmentsWidget.ui
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<ui version="4.0">
|
||||||
|
<class>EntryAttachmentsWidget</class>
|
||||||
|
<widget class="QWidget" name="EntryAttachmentsWidget">
|
||||||
|
<property name="windowTitle">
|
||||||
|
<string>Form</string>
|
||||||
|
</property>
|
||||||
|
<layout class="QHBoxLayout" name="horizontalLayout">
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QTableView" name="attachmentsView"/>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QWidget" name="actionsWidget" native="true">
|
||||||
|
<layout class="QVBoxLayout" name="verticalLayout">
|
||||||
|
<property name="leftMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="topMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="rightMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<property name="bottomMargin">
|
||||||
|
<number>0</number>
|
||||||
|
</property>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="addAttachmentButton">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Add</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="removeAttachmentButton">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Remove</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="openAttachmentButton">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Open</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<widget class="QPushButton" name="saveAttachmentButton">
|
||||||
|
<property name="enabled">
|
||||||
|
<bool>false</bool>
|
||||||
|
</property>
|
||||||
|
<property name="text">
|
||||||
|
<string>Save</string>
|
||||||
|
</property>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
<item>
|
||||||
|
<spacer name="verticalSpacer_2">
|
||||||
|
<property name="orientation">
|
||||||
|
<enum>Qt::Vertical</enum>
|
||||||
|
</property>
|
||||||
|
<property name="sizeHint" stdset="0">
|
||||||
|
<size>
|
||||||
|
<width>20</width>
|
||||||
|
<height>173</height>
|
||||||
|
</size>
|
||||||
|
</property>
|
||||||
|
</spacer>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
</item>
|
||||||
|
</layout>
|
||||||
|
</widget>
|
||||||
|
<resources/>
|
||||||
|
<connections/>
|
||||||
|
</ui>
|
Loading…
Reference in New Issue
Block a user