diff --git a/server/editor/attachment/attachmentmodel.cpp b/server/editor/attachment/attachmentmodel.cpp index 0167147..0b73d65 100644 --- a/server/editor/attachment/attachmentmodel.cpp +++ b/server/editor/attachment/attachmentmodel.cpp @@ -1,484 +1,485 @@ /* * This file is part of KMail. * SPDX-FileCopyrightText: 2009 Constantin Berzan * * SPDX-License-Identifier: GPL-2.0-or-later */ #include "attachmentmodel.h" #include #include #include "editor_debug.h" -#include +#include #include #include #include #include #include using namespace MessageComposer; using namespace MessageCore; static Qt::CheckState boolToCheckState(bool checked) // local { if (checked) { return Qt::Checked; } else { return Qt::Unchecked; } } class MessageComposer::AttachmentModel::AttachmentModelPrivate { public: AttachmentModelPrivate(AttachmentModel *qq); ~AttachmentModelPrivate(); AttachmentPart::List parts; QList tempDirs; AttachmentModel *const q; bool modified = false; bool encryptEnabled = false; bool signEnabled = false; bool encryptSelected = false; bool signSelected = false; bool autoDisplayEnabled = false; }; AttachmentModel::AttachmentModelPrivate::AttachmentModelPrivate(AttachmentModel *qq) : q(qq) { } AttachmentModel::AttachmentModelPrivate::~AttachmentModelPrivate() { // There should be an automatic way to manage the lifetime of these... qDeleteAll(tempDirs); } AttachmentModel::AttachmentModel(QObject *parent) : QAbstractItemModel(parent) , d(new AttachmentModelPrivate(this)) { } AttachmentModel::~AttachmentModel() = default; bool AttachmentModel::dropMimeData(const QMimeData *data, Qt::DropAction action, int row, int column, const QModelIndex &parent) { Q_UNUSED(row) Q_UNUSED(column) Q_UNUSED(parent) qCDebug(EDITOR_LOG) << "data has formats" << data->formats() << "urls" << data->urls() << "action" << int(action); if (action == Qt::IgnoreAction) { return true; //} else if( action != Qt::CopyAction ) { // return false; } // The dropped data is a list of URLs. const QList urls = data->urls(); if (!urls.isEmpty()) { Q_EMIT attachUrlsRequested(urls); return true; } else { return false; } } QMimeData *AttachmentModel::mimeData(const QModelIndexList &indexes) const { qCDebug(EDITOR_LOG); QList urls; for (const QModelIndex &index : indexes) { if (index.column() != 0) { // Avoid processing the same attachment more than once, since the entire // row is selected. qCWarning(EDITOR_LOG) << "column != 0. Possibly duplicate rows passed to mimeData()."; continue; } const AttachmentPart::Ptr part = d->parts[index.row()]; QString attachmentName = part->fileName(); if (attachmentName.isEmpty()) { attachmentName = part->name(); } if (attachmentName.isEmpty()) { attachmentName = i18n("unnamed attachment"); } auto tempDir = new QTemporaryDir; // Will remove the directory on destruction. d->tempDirs.append(tempDir); const QString fileName = tempDir->path() + QLatin1Char('/') + attachmentName; QFile f(fileName); if (!f.open(QIODevice::WriteOnly)) { qCWarning(EDITOR_LOG) << "Cannot write attachment:" << f.errorString(); continue; } const QByteArray data = part->data(); if (f.write(data) != data.length()) { qCWarning(EDITOR_LOG) << "Failed to write all data to file!"; continue; } f.setPermissions(f.permissions() | QFileDevice::ReadUser | QFileDevice::WriteUser); f.close(); const QUrl url = QUrl::fromLocalFile(fileName); qCDebug(EDITOR_LOG) << " temporary file " << url; urls.append(url); } auto mimeData = new QMimeData; mimeData->setUrls(urls); return mimeData; } QStringList AttachmentModel::mimeTypes() const { const QStringList types = {QStringLiteral("text/uri-list")}; return types; } Qt::DropActions AttachmentModel::supportedDropActions() const { return Qt::CopyAction | Qt::MoveAction; } bool AttachmentModel::isModified() const { return d->modified; // TODO actually set modified=true sometime... } void AttachmentModel::setModified(bool modified) { d->modified = modified; } bool AttachmentModel::isEncryptEnabled() const { return d->encryptEnabled; } void AttachmentModel::setEncryptEnabled(bool enabled) { d->encryptEnabled = enabled; Q_EMIT encryptEnabled(enabled); } bool AttachmentModel::isAutoDisplayEnabled() const { return d->autoDisplayEnabled; } void AttachmentModel::setAutoDisplayEnabled(bool enabled) { d->autoDisplayEnabled = enabled; Q_EMIT autoDisplayEnabled(enabled); } bool AttachmentModel::isSignEnabled() const { return d->signEnabled; } void AttachmentModel::setSignEnabled(bool enabled) { d->signEnabled = enabled; Q_EMIT signEnabled(enabled); } bool AttachmentModel::isEncryptSelected() const { return d->encryptSelected; } void AttachmentModel::setEncryptSelected(bool selected) { d->encryptSelected = selected; for (AttachmentPart::Ptr part : std::as_const(d->parts)) { part->setEncrypted(selected); } Q_EMIT dataChanged(index(0, EncryptColumn), index(rowCount() - 1, EncryptColumn)); } bool AttachmentModel::isSignSelected() const { return d->signSelected; } void AttachmentModel::setSignSelected(bool selected) { d->signSelected = selected; for (AttachmentPart::Ptr part : std::as_const(d->parts)) { part->setSigned(selected); } Q_EMIT dataChanged(index(0, SignColumn), index(rowCount() - 1, SignColumn)); } QVariant AttachmentModel::data(const QModelIndex &index, int role) const { if (!index.isValid()) { return {}; } const AttachmentPart::Ptr part = d->parts.at(index.row()); + KFormat format; if (role == Qt::DisplayRole) { switch (index.column()) { case NameColumn: return QVariant::fromValue(part->name().isEmpty() ? part->fileName() : part->name()); case SizeColumn: - return QVariant::fromValue(KIO::convertSize(part->size())); + return QVariant::fromValue(format.formatByteSize(part->size())); case EncodingColumn: return QVariant::fromValue(KMime::nameForEncoding(part->encoding())); case MimeTypeColumn: return QVariant::fromValue(part->mimeType()); default: return {}; } } else if (role == Qt::ToolTipRole) { return QVariant::fromValue(i18nc("@info:tooltip", "Name: %1
Size: %2
Encoding: %3
MimeType=%4", part->name().isEmpty() ? part->fileName() : part->name(), - KIO::convertSize(part->size()), + format.formatByteSize(part->size()), KMime::nameForEncoding(part->encoding()), QString::fromLatin1(part->mimeType().data()))); } else if (role == Qt::CheckStateRole) { switch (index.column()) { case CompressColumn: return QVariant::fromValue(int(boolToCheckState(part->isCompressed()))); case EncryptColumn: return QVariant::fromValue(int(boolToCheckState(part->isEncrypted()))); case SignColumn: return QVariant::fromValue(int(boolToCheckState(part->isSigned()))); case AutoDisplayColumn: return QVariant::fromValue(int(boolToCheckState(part->isInline()))); default: return {}; } } else if (role == AttachmentPartRole) { if (index.column() == 0) { return QVariant::fromValue(part); } else { qCWarning(EDITOR_LOG) << "AttachmentPartRole and column != 0."; return {}; } } else if (role == NameRole) { return QVariant::fromValue(part->fileName().isEmpty() ? part->name() : part->fileName()); } else if (role == SizeRole) { - return QVariant::fromValue(KIO::convertSize(part->size())); + return format.formatByteSize(part->size()); } else if (role == EncodingRole) { return QVariant::fromValue(KMime::nameForEncoding(part->encoding())); } else if (role == MimeTypeRole) { return QVariant::fromValue(part->mimeType()); } else if (role == CompressRole) { return QVariant::fromValue(part->isCompressed()); } else if (role == EncryptRole) { return QVariant::fromValue(part->isEncrypted()); } else if (role == SignRole) { return QVariant::fromValue(part->isSigned()); } else if (role == AutoDisplayRole) { return QVariant::fromValue(part->isInline()); } else { return {}; } } bool AttachmentModel::setData(const QModelIndex &index, const QVariant &value, int role) { bool emitDataChanged = true; AttachmentPart::Ptr part = d->parts[index.row()]; if (role == Qt::EditRole) { switch (index.column()) { case NameColumn: if (!value.toString().isEmpty()) { part->setName(value.toString()); } else { return false; } break; default: return false; } } else if (role == Qt::CheckStateRole) { switch (index.column()) { case CompressColumn: { bool toZip = value.toBool(); if (toZip != part->isCompressed()) { Q_EMIT attachmentCompressRequested(part, toZip); emitDataChanged = false; // Will Q_EMIT when the part is updated. } break; } case EncryptColumn: part->setEncrypted(value.toBool()); break; case SignColumn: part->setSigned(value.toBool()); break; case AutoDisplayColumn: part->setInline(value.toBool()); break; default: break; // Do nothing. } } else { return false; } if (emitDataChanged) { Q_EMIT dataChanged(index, index); } return true; } void AttachmentModel::addAttachment(const AttachmentPart::Ptr &part) { Q_ASSERT(!d->parts.contains(part)); if (!part->url().isEmpty()) { for (const AttachmentPart::Ptr &partElement : std::as_const(d->parts)) { if (partElement->url() == part->url()) { return; } } } beginInsertRows(QModelIndex(), rowCount(), rowCount()); d->parts.append(part); endInsertRows(); } bool AttachmentModel::updateAttachment(const AttachmentPart::Ptr &part) { const int idx = d->parts.indexOf(part); if (idx == -1) { qCWarning(EDITOR_LOG) << "Tried to update non-existent part."; return false; } // Emit dataChanged() for the whole row. Q_EMIT dataChanged(index(idx, 0), index(idx, LastColumn - 1)); return true; } bool AttachmentModel::replaceAttachment(const AttachmentPart::Ptr &oldPart, const AttachmentPart::Ptr &newPart) { Q_ASSERT(oldPart != newPart); const int idx = d->parts.indexOf(oldPart); if (idx == -1) { qCWarning(EDITOR_LOG) << "Tried to replace non-existent part."; return false; } d->parts[idx] = newPart; // Emit dataChanged() for the whole row. Q_EMIT dataChanged(index(idx, 0), index(idx, LastColumn - 1)); return true; } bool AttachmentModel::removeAttachment(const AttachmentPart::Ptr &part) { const int idx = d->parts.indexOf(part); if (idx < 0) { qCWarning(EDITOR_LOG) << "Attachment not found."; return false; } beginRemoveRows(QModelIndex(), idx, idx); d->parts.removeAt(idx); endRemoveRows(); Q_EMIT attachmentRemoved(part); return true; } AttachmentPart::List AttachmentModel::attachments() const { return d->parts; } Qt::ItemFlags AttachmentModel::flags(const QModelIndex &index) const { Qt::ItemFlags defaultFlags = QAbstractItemModel::flags(index); if (!index.isValid()) { return Qt::ItemIsDropEnabled | defaultFlags; } if (index.column() == CompressColumn || index.column() == EncryptColumn || index.column() == SignColumn || index.column() == AutoDisplayColumn) { return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | Qt::ItemIsUserCheckable | defaultFlags; } else if (index.column() == NameColumn) { return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | Qt::ItemIsEditable | defaultFlags; } else { return Qt::ItemIsDragEnabled | Qt::ItemIsDropEnabled | defaultFlags; } } QVariant AttachmentModel::headerData(int section, Qt::Orientation orientation, int role) const { if (orientation != Qt::Horizontal || role != Qt::DisplayRole) { return {}; } switch (section) { case NameColumn: return i18nc("@title column attachment name.", "Name"); case SizeColumn: return i18nc("@title column attachment size.", "Size"); case EncodingColumn: return i18nc("@title column attachment encoding.", "Encoding"); case MimeTypeColumn: return i18nc("@title column attachment type.", "Type"); case CompressColumn: return i18nc("@title column attachment compression checkbox.", "Compress"); case EncryptColumn: return i18nc("@title column attachment encryption checkbox.", "Encrypt"); case SignColumn: return i18nc("@title column attachment signed checkbox.", "Sign"); case AutoDisplayColumn: return i18nc("@title column attachment inlined checkbox.", "Suggest Automatic Display"); default: qCWarning(EDITOR_LOG) << "Bad column" << section; return {}; } } QModelIndex AttachmentModel::index(int row, int column, const QModelIndex &parent) const { if (!hasIndex(row, column, parent)) { return {}; } Q_ASSERT(row >= 0 && row < rowCount()); if (parent.isValid()) { qCWarning(EDITOR_LOG) << "Called with weird parent."; return {}; } return createIndex(row, column); } QModelIndex AttachmentModel::parent(const QModelIndex &index) const { Q_UNUSED(index) return {}; // No parent. } int AttachmentModel::rowCount(const QModelIndex &parent) const { if (parent.isValid()) { return 0; // Items have no children. } return d->parts.count(); } int AttachmentModel::columnCount(const QModelIndex &parent) const { Q_UNUSED(parent) return LastColumn; } #include "moc_attachmentmodel.cpp" diff --git a/server/editor/attachment/attachmentpropertiesdialog.cpp b/server/editor/attachment/attachmentpropertiesdialog.cpp index 2e0ad29..2ff91a9 100644 --- a/server/editor/attachment/attachmentpropertiesdialog.cpp +++ b/server/editor/attachment/attachmentpropertiesdialog.cpp @@ -1,393 +1,394 @@ /* SPDX-FileCopyrightText: 2009 Constantin Berzan Based on KMail code by various authors (kmmsgpartdlg). SPDX-License-Identifier: LGPL-2.0-or-later */ #include "attachmentpropertiesdialog.h" #include "attachmentfrommimecontentjob.h" #include "ui_attachmentpropertiesdialog.h" #include "ui_attachmentpropertiesdialog_readonly.h" #include "editor_debug.h" #include #include #include -#include +#include #include #include #include #include #include #include #include #include using namespace MessageCore; class MessageCore::AttachmentPropertiesDialog::AttachmentPropertiesDialogPrivate { public: AttachmentPropertiesDialogPrivate(AttachmentPropertiesDialog *qq) : q(qq) { } ~AttachmentPropertiesDialogPrivate() { delete ui; delete uiReadOnly; } void init(const AttachmentPart::Ptr &part, bool readOnly); void polishUi(); void mimeTypeChanged(const QString &type); // slot void populateEncodings(); void populateMimeTypes(); void populateWhatsThis(); void loadFromPart(); void saveToPart(); AttachmentPropertiesDialog *const q; AttachmentPart::Ptr mPart; Ui::AttachmentPropertiesDialog *ui = nullptr; Ui::AttachmentPropertiesDialogReadOnly *uiReadOnly = nullptr; QVBoxLayout *mainLayout = nullptr; bool mReadOnly = false; }; void AttachmentPropertiesDialog::AttachmentPropertiesDialogPrivate::init(const AttachmentPart::Ptr &part, bool readOnly) { mReadOnly = readOnly; mPart = part; auto widget = new QWidget(q); mainLayout = new QVBoxLayout; q->setLayout(mainLayout); mainLayout->addWidget(widget); if (mReadOnly) { uiReadOnly = new Ui::AttachmentPropertiesDialogReadOnly; uiReadOnly->setupUi(widget); } else { ui = new Ui::AttachmentPropertiesDialog; ui->setupUi(widget); } polishUi(); q->setModal(true); loadFromPart(); } void AttachmentPropertiesDialog::AttachmentPropertiesDialogPrivate::polishUi() { // Tweak the dialog, depending on whether it is read-only or not. QDialogButtonBox *buttonBox = nullptr; if (mReadOnly) { buttonBox = new QDialogButtonBox(QDialogButtonBox::Help | QDialogButtonBox::Close, q); } else { // Update the icon when the selected mime type changes. connect(ui->mimeType, &QComboBox::currentTextChanged, q, [this](const QString &str) { mimeTypeChanged(str); }); populateMimeTypes(); populateEncodings(); buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::Help, q); QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); } q->connect(buttonBox->button(QDialogButtonBox::Help), &QAbstractButton::clicked, q, &AttachmentPropertiesDialog::slotHelp); q->connect(buttonBox, &QDialogButtonBox::accepted, q, &QDialog::accept); q->connect(buttonBox, &QDialogButtonBox::rejected, q, &QDialog::reject); mainLayout->addWidget(buttonBox); populateWhatsThis(); } void AttachmentPropertiesDialog::AttachmentPropertiesDialogPrivate::mimeTypeChanged(const QString &type) { QMimeDatabase db; const QMimeType mimeType = db.mimeTypeForName(type); QPixmap pix = QIcon::fromTheme(mimeType.iconName(), QIcon::fromTheme(QStringLiteral("unknown"))).pixmap(q->style()->pixelMetric(QStyle::PM_MessageBoxIconSize)); if (mReadOnly) { uiReadOnly->mimeIcon->setPixmap(pix); } else { ui->mimeIcon->setPixmap(pix); } } void AttachmentPropertiesDialog::AttachmentPropertiesDialogPrivate::populateWhatsThis() { // FIXME These are such a mess... Make them straightforward and pretty. const QString msgMimeType = i18n( "

The MIME type of the file:

" "

Normally, you do not need to touch this setting, since the " "type of the file is automatically checked; but, sometimes, %1 " "may not detect the type correctly -- here is where you can fix " "that.

", KAboutData::applicationData().componentName()); const QString msgSize = i18n( "

The estimated size of the attachment:

" "

Note that, in an email message, a binary file encoded with " "base64 will take up four thirds the actual size of the file.

"); const QString msgName = i18n( "

The file name of the part:

" "

Although this defaults to the name of the attached file, " "it does not specify the file to be attached; rather, it " "suggests a file name to be used by the recipient's mail agent " "when saving the part to disk.

"); const QString msgDescription = i18n( "

A description of the part:

" "

This is just an informational description of the part, " "much like the Subject is for the whole message; most " "mail agents will show this information in their message " "previews alongside the attachment's icon.

"); const QString msgEncoding = i18n( "

The transport encoding of this part:

" "

Normally, you do not need to change this, since %1 will use " "a decent default encoding, depending on the MIME type; yet, " "sometimes, you can significantly reduce the size of the " "resulting message, e.g. if a PostScript file does not contain " "binary data, but consists of pure text -- in this case, choosing " "\"quoted-printable\" over the default \"base64\" will save up " "to 25% in resulting message size.

", KAboutData::applicationData().componentName()); const QString msgAutoDisplay = i18n( "

Check this option if you want to suggest to the " "recipient the automatic (inline) display of this part in the " "message preview, instead of the default icon view;

" "

Technically, this is carried out by setting this part's " "Content-Disposition header field to \"inline\" " "instead of the default \"attachment\".

"); const QString msgSign = i18n( "

Check this option if you want this message part to be " "signed.

" "

The signature will be made with the key that you associated " "with the currently-selected identity.

"); const QString msgEncrypt = i18n( "

Check this option if you want this message part to be " "encrypted.

" "

The part will be encrypted for the recipients of this " "message.

"); if (mReadOnly) { uiReadOnly->size->setWhatsThis(msgSize); uiReadOnly->name->setWhatsThis(msgName); uiReadOnly->encoding->setWhatsThis(msgEncoding); } else { ui->mimeType->setWhatsThis(msgMimeType); ui->size->setWhatsThis(msgSize); ui->name->setWhatsThis(msgName); ui->encrypt->setWhatsThis(msgEncrypt); ui->sign->setWhatsThis(msgSign); ui->autoDisplay->setWhatsThis(msgAutoDisplay); ui->encoding->setWhatsThis(msgEncoding); ui->description->setWhatsThis(msgDescription); } } void AttachmentPropertiesDialog::AttachmentPropertiesDialogPrivate::populateEncodings() { using namespace KMime; using namespace KMime::Headers; ui->encoding->clear(); ui->encoding->addItem(nameForEncoding(CE7Bit), int(CE7Bit)); ui->encoding->addItem(nameForEncoding(CE8Bit), int(CE8Bit)); ui->encoding->addItem(nameForEncoding(CEquPr), int(CEquPr)); ui->encoding->addItem(nameForEncoding(CEbase64), int(CEbase64)); // TODO 8bit should be disabled if it is disabled in Settings. // Also, if it's a message/* part, base64 and qp should be disabled. // But since this is a dialog for power users anyway, let them shoot // themselves in the foot. (The AttachmentJob will fail when they // try to compose the message.) } void AttachmentPropertiesDialog::AttachmentPropertiesDialogPrivate::populateMimeTypes() { const QStringList list = QStringList() << QStringLiteral("text/html") << QStringLiteral("text/plain") << QStringLiteral("image/gif") << QStringLiteral("image/jpeg") << QStringLiteral("image/png") << QStringLiteral("application/octet-stream") << QStringLiteral("application/x-gunzip") << QStringLiteral("application/zip"); ui->mimeType->addItems(list); } void AttachmentPropertiesDialog::AttachmentPropertiesDialogPrivate::loadFromPart() { Q_ASSERT(mPart); + KFormat format; if (mReadOnly) { uiReadOnly->mimeType->setText(QString::fromLatin1(mPart->mimeType())); mimeTypeChanged(QString::fromLatin1(mPart->mimeType())); - uiReadOnly->size->setText(KIO::convertSize(mPart->size())); + uiReadOnly->size->setText(format.formatByteSize(mPart->size())); uiReadOnly->name->setText(mPart->name().isEmpty() ? mPart->fileName() : mPart->name()); if (mPart->description().isEmpty()) { uiReadOnly->description->hide(); uiReadOnly->descriptionLabel->hide(); } else { uiReadOnly->description->setText(mPart->description()); } uiReadOnly->encoding->setText(KMime::nameForEncoding(mPart->encoding())); } else { const QString mimeType = QString::fromLatin1(mPart->mimeType()); const int index = ui->mimeType->findText(mimeType); if (index == -1) { ui->mimeType->insertItem(0, mimeType); ui->mimeType->setCurrentIndex(0); } else { ui->mimeType->setCurrentIndex(index); } - ui->size->setText(KIO::convertSize(mPart->size())); + ui->size->setText(format.formatByteSize(mPart->size())); ui->name->setText(mPart->name().isEmpty() ? mPart->fileName() : mPart->name()); ui->description->setText(mPart->description()); ui->encoding->setCurrentIndex(int(mPart->encoding())); ui->autoDisplay->setChecked(mPart->isInline()); ui->encrypt->setChecked(mPart->isEncrypted()); ui->sign->setChecked(mPart->isSigned()); } } static QString removeNewlines(const QString &input) { QString ret(input); ret.replace(QLatin1Char('\n'), QLatin1Char(' ')); return ret; } void AttachmentPropertiesDialog::AttachmentPropertiesDialogPrivate::saveToPart() { Q_ASSERT(mPart); Q_ASSERT(!mReadOnly); if (mReadOnly) { return; } mPart->setMimeType(ui->mimeType->currentText().toLatin1()); const QString name = removeNewlines(ui->name->text()); mPart->setName(name); mPart->setFileName(name); mPart->setDescription(removeNewlines(ui->description->text())); mPart->setInline(ui->autoDisplay->isChecked()); mPart->setSigned(ui->sign->isChecked()); mPart->setEncrypted(ui->encrypt->isChecked()); mPart->setInline(ui->autoDisplay->isChecked()); if (ui->mimeType->currentText().startsWith(QLatin1String("message")) && ui->encoding->itemData(ui->encoding->currentIndex()) != KMime::Headers::CE7Bit && ui->encoding->itemData(ui->encoding->currentIndex()) != KMime::Headers::CE8Bit) { qCWarning(EDITOR_LOG) << R"(Encoding on message/rfc822 must be "7bit" or "8bit".)"; } mPart->setEncoding(KMime::Headers::contentEncoding(ui->encoding->itemData(ui->encoding->currentIndex()).toInt())); } AttachmentPropertiesDialog::AttachmentPropertiesDialog(const AttachmentPart::Ptr &part, bool readOnly, QWidget *parent) : QDialog(parent) , d(new AttachmentPropertiesDialogPrivate(this)) { d->init(part, readOnly); setWindowTitle(i18nc("@title:window", "Attachment Properties")); } AttachmentPropertiesDialog::AttachmentPropertiesDialog(const KMime::Content *content, QWidget *parent) : QDialog(parent) , d(new AttachmentPropertiesDialogPrivate(this)) { auto job = new AttachmentFromMimeContentJob(content, this); job->exec(); if (job->error()) { qCCritical(EDITOR_LOG) << "AttachmentFromMimeContentJob failed." << job->errorString(); } const AttachmentPart::Ptr part = job->attachmentPart(); d->init(part, true); setWindowTitle(i18nc("@title:window", "Attachment Properties")); } AttachmentPropertiesDialog::~AttachmentPropertiesDialog() = default; AttachmentPart::Ptr AttachmentPropertiesDialog::attachmentPart() const { return d->mPart; } bool AttachmentPropertiesDialog::isEncryptEnabled() const { if (d->ui) { return d->ui->encrypt->isEnabled(); } return false; } void AttachmentPropertiesDialog::setEncryptEnabled(bool enabled) { if (d->ui) { d->ui->encrypt->setEnabled(enabled); } } bool AttachmentPropertiesDialog::isSignEnabled() const { if (d->ui) { return d->ui->sign->isEnabled(); } return false; } void AttachmentPropertiesDialog::setSignEnabled(bool enabled) { if (d->ui) { d->ui->sign->setEnabled(enabled); } } void AttachmentPropertiesDialog::accept() { if (!d->mReadOnly) { d->saveToPart(); } QDialog::accept(); } // Copy from PimCommon::Util::invokeHelp // Avoid to add PimCommon dep for this method void invokeHelp(const QString &docfile, const QString &anchor) { if (!docfile.isEmpty()) { QUrl url; url = QUrl(QStringLiteral("help:/")).resolved(QUrl(docfile)); if (!anchor.isEmpty()) { QUrlQuery query(url); query.addQueryItem(QStringLiteral("anchor"), anchor); url.setQuery(query); } // launch khelpcenter, or a browser for URIs not handled by khelpcenter QDesktopServices::openUrl(url); } } void AttachmentPropertiesDialog::slotHelp() { invokeHelp(QStringLiteral("kmail2/the-composer-window.html"), QStringLiteral("attachments")); } #include "moc_attachmentpropertiesdialog.cpp" diff --git a/server/editor/attachment/attachmentview.cpp b/server/editor/attachment/attachmentview.cpp index adfaddb..90e6c64 100644 --- a/server/editor/attachment/attachmentview.cpp +++ b/server/editor/attachment/attachmentview.cpp @@ -1,201 +1,202 @@ /* * This file is part of KMail. * SPDX-FileCopyrightText: 2011-2023 Laurent Montel * * SPDX-FileCopyrightText: 2009 Constantin Berzan * * Parts based on KMail code by: * SPDX-FileCopyrightText: 2003 Ingo Kloecker * SPDX-FileCopyrightText: 2007 Thomas McGuire * * SPDX-License-Identifier: GPL-2.0-or-later */ #include "attachmentview.h" #include "attachmentmodel.h" #include "attachmentpart.h" #include #include #include #include #include #include #include #include #include #include #include -#include +#include using MessageCore::AttachmentPart; AttachmentView::AttachmentView(MessageComposer::AttachmentModel *model, QWidget *parent) : QTreeView(parent) , mModel(model) , mToolButton(new QToolButton(this)) , mInfoAttachment(new QLabel(this)) , mWidget(new QWidget()) , grp(KSharedConfig::openConfig()->group(QStringLiteral("AttachmentView"))) { auto lay = new QHBoxLayout(mWidget); lay->setContentsMargins({}); connect(mToolButton, &QAbstractButton::toggled, this, &AttachmentView::slotShowHideAttchementList); mToolButton->setIcon(QIcon::fromTheme(QStringLiteral("mail-attachment"))); mToolButton->setAutoRaise(true); mToolButton->setCheckable(true); lay->addWidget(mToolButton); mInfoAttachment->setContentsMargins({}); mInfoAttachment->setTextFormat(Qt::PlainText); lay->addWidget(mInfoAttachment); connect(model, &MessageComposer::AttachmentModel::encryptEnabled, this, &AttachmentView::setEncryptEnabled); connect(model, &MessageComposer::AttachmentModel::signEnabled, this, &AttachmentView::setSignEnabled); auto sortModel = new QSortFilterProxyModel(this); sortModel->setSortCaseSensitivity(Qt::CaseInsensitive); sortModel->setSourceModel(model); setModel(sortModel); connect(model, &MessageComposer::AttachmentModel::rowsInserted, this, &AttachmentView::hideIfEmpty); connect(model, &MessageComposer::AttachmentModel::rowsRemoved, this, &AttachmentView::hideIfEmpty); connect(model, &MessageComposer::AttachmentModel::rowsRemoved, this, &AttachmentView::selectNewAttachment); connect(model, &MessageComposer::AttachmentModel::dataChanged, this, &AttachmentView::updateAttachmentLabel); setRootIsDecorated(false); setUniformRowHeights(true); setSelectionMode(QAbstractItemView::ExtendedSelection); setDragDropMode(QAbstractItemView::DragDrop); setEditTriggers(QAbstractItemView::EditKeyPressed); setDropIndicatorShown(false); setSortingEnabled(true); header()->setSectionResizeMode(QHeaderView::Interactive); header()->setStretchLastSection(false); restoreHeaderState(); setColumnWidth(0, 200); } AttachmentView::~AttachmentView() { saveHeaderState(); } void AttachmentView::restoreHeaderState() { header()->restoreState(grp.readEntry("State", QByteArray())); } void AttachmentView::saveHeaderState() { grp.writeEntry("State", header()->saveState()); grp.sync(); } void AttachmentView::contextMenuEvent(QContextMenuEvent *event) { Q_UNUSED(event) Q_EMIT contextMenuRequested(); } void AttachmentView::keyPressEvent(QKeyEvent *event) { if (event->key() == Qt::Key_Delete) { // Indexes are based on row numbers, and row numbers change when items are deleted. // Therefore, first we need to make a list of AttachmentParts to delete. AttachmentPart::List toRemove; const QModelIndexList selectedIndexes = selectionModel()->selectedRows(); toRemove.reserve(selectedIndexes.count()); for (const QModelIndex &index : selectedIndexes) { auto part = model()->data(index, MessageComposer::AttachmentModel::AttachmentPartRole).value(); toRemove.append(part); } for (const AttachmentPart::Ptr &part : std::as_const(toRemove)) { mModel->removeAttachment(part); } } else { QTreeView::keyPressEvent(event); } } void AttachmentView::dragEnterEvent(QDragEnterEvent *event) { if (event->source() == this) { // Ignore drags from ourselves. event->ignore(); } else { QTreeView::dragEnterEvent(event); } } void AttachmentView::setEncryptEnabled(bool enabled) { setColumnHidden(MessageComposer::AttachmentModel::EncryptColumn, !enabled); } void AttachmentView::setSignEnabled(bool enabled) { setColumnHidden(MessageComposer::AttachmentModel::SignColumn, !enabled); } void AttachmentView::hideIfEmpty() { const bool needToShowIt = (model()->rowCount() > 0); setVisible(needToShowIt); mToolButton->setChecked(needToShowIt); widget()->setVisible(needToShowIt); if (needToShowIt) { updateAttachmentLabel(); } else { mInfoAttachment->clear(); } Q_EMIT modified(true); } void AttachmentView::updateAttachmentLabel() { const MessageCore::AttachmentPart::List list = mModel->attachments(); qint64 size = 0; for (const MessageCore::AttachmentPart::Ptr &part : list) { size += part->size(); } - mInfoAttachment->setText(i18np("1 attachment (%2)", "%1 attachments (%2)", model()->rowCount(), KIO::convertSize(qMax(0LL, size)))); + KFormat format; + mInfoAttachment->setText(i18np("1 attachment (%2)", "%1 attachments (%2)", model()->rowCount(), format.formatByteSize(qMax(0LL, size)))); } void AttachmentView::selectNewAttachment() { if (selectionModel()->selectedRows().isEmpty()) { selectionModel()->select(selectionModel()->currentIndex(), QItemSelectionModel::Select | QItemSelectionModel::Rows); } } void AttachmentView::startDrag(Qt::DropActions supportedActions) { Q_UNUSED(supportedActions) const QModelIndexList selection = selectionModel()->selectedRows(); if (!selection.isEmpty()) { QMimeData *mimeData = model()->mimeData(selection); auto drag = new QDrag(this); drag->setMimeData(mimeData); drag->exec(Qt::CopyAction); } } QWidget *AttachmentView::widget() const { return mWidget; } void AttachmentView::slotShowHideAttchementList(bool show) { setVisible(show); if (show) { mToolButton->setToolTip(i18n("Hide attachment list")); } else { mToolButton->setToolTip(i18n("Show attachment list")); } } #include "moc_attachmentview.cpp"