diff --git a/messagecomposer/src/imagescaling/imagescalingwidget.cpp b/messagecomposer/src/imagescaling/imagescalingwidget.cpp index 6b6aefe5..0317ffc9 100644 --- a/messagecomposer/src/imagescaling/imagescalingwidget.cpp +++ b/messagecomposer/src/imagescaling/imagescalingwidget.cpp @@ -1,349 +1,349 @@ /* Copyright (C) 2012-2018 Montel Laurent This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; see the file COPYING. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "imagescalingwidget.h" #include "ui_imagescalingwidget.h" #include "settings/messagecomposersettings.h" #include #include #include #include #include #include using namespace MessageComposer; class MessageComposer::ImageScalingWidgetPrivate { public: ImageScalingWidgetPrivate() : ui(new Ui::ImageScalingWidget) { } ~ImageScalingWidgetPrivate() { delete ui; } Ui::ImageScalingWidget *ui = nullptr; QButtonGroup *mSourceFilenameFilterGroup = nullptr; QButtonGroup *mRecipientFilterGroup = nullptr; bool mWasChanged = false; }; ImageScalingWidget::ImageScalingWidget(QWidget *parent) : QWidget(parent) , d(new MessageComposer::ImageScalingWidgetPrivate) { d->ui->setupUi(this); initComboBox(d->ui->CBMaximumWidth); initComboBox(d->ui->CBMaximumHeight); initComboBox(d->ui->CBMinimumWidth); initComboBox(d->ui->CBMinimumHeight); initWriteImageFormat(); connect(d->ui->enabledAutoResize, &QCheckBox::clicked, this, &ImageScalingWidget::changed); connect(d->ui->KeepImageRatio, &QCheckBox::clicked, this, &ImageScalingWidget::changed); connect(d->ui->AskBeforeResizing, &QCheckBox::clicked, this, &ImageScalingWidget::changed); connect(d->ui->EnlargeImageToMinimum, &QCheckBox::clicked, this, &ImageScalingWidget::changed); connect(d->ui->ReduceImageToMaximum, &QCheckBox::clicked, this, &ImageScalingWidget::changed); connect(d->ui->customMaximumWidth, QOverload::of(&QSpinBox::valueChanged), this, &ImageScalingWidget::changed); connect(d->ui->customMaximumHeight, QOverload::of(&QSpinBox::valueChanged), this, &ImageScalingWidget::changed); connect(d->ui->customMinimumWidth, QOverload::of(&QSpinBox::valueChanged), this, &ImageScalingWidget::changed); connect(d->ui->customMinimumHeight, QOverload::of(&QSpinBox::valueChanged), this, &ImageScalingWidget::changed); connect(d->ui->skipImageSizeLower, &QCheckBox::clicked, this, &ImageScalingWidget::changed); connect(d->ui->imageSize, QOverload::of(&QSpinBox::valueChanged), this, &ImageScalingWidget::changed); connect(d->ui->pattern, &KLineEdit::textChanged, this, &ImageScalingWidget::changed); connect(d->ui->CBMaximumWidth, QOverload::of(&QComboBox::currentIndexChanged), this, &ImageScalingWidget::slotComboboxChanged); connect(d->ui->CBMaximumHeight, QOverload::of(&QComboBox::currentIndexChanged), this, &ImageScalingWidget::slotComboboxChanged); connect(d->ui->CBMinimumWidth, QOverload::of(&QComboBox::currentIndexChanged), this, &ImageScalingWidget::slotComboboxChanged); connect(d->ui->CBMinimumHeight, QOverload::of(&QComboBox::currentIndexChanged), this, &ImageScalingWidget::slotComboboxChanged); connect(d->ui->WriteToImageFormat, QOverload::of(&QComboBox::activated), this, &ImageScalingWidget::changed); connect(d->ui->renameResizedImage, &QCheckBox::clicked, this, &ImageScalingWidget::changed); connect(d->ui->renameResizedImage, &QCheckBox::clicked, d->ui->renameResizedImagePattern, &KLineEdit::setEnabled); connect(d->ui->renameResizedImagePattern, &KLineEdit::textChanged, this, &ImageScalingWidget::changed); connect(d->ui->resizeEmailsPattern, &KLineEdit::textChanged, this, &ImageScalingWidget::changed); connect(d->ui->doNotResizePattern, &KLineEdit::textChanged, this, &ImageScalingWidget::changed); connect(d->ui->resizeImageWithFormatsType, &MessageComposer::ImageScalingSelectFormat::textChanged, this, &ImageScalingWidget::changed); connect(d->ui->resizeImageWithFormats, &QCheckBox::clicked, this, &ImageScalingWidget::changed); connect(d->ui->resizeImageWithFormats, &QCheckBox::clicked, d->ui->resizeImageWithFormatsType, &MessageComposer::ImageScalingSelectFormat::setEnabled); d->ui->resizeImageWithFormatsType->setEnabled(false); d->ui->pattern->setEnabled(false); d->mSourceFilenameFilterGroup = new QButtonGroup(d->ui->filterSourceGroupBox); - connect(d->mSourceFilenameFilterGroup, static_cast(&QButtonGroup::buttonClicked), this, &ImageScalingWidget::slotSourceFilterClicked); + connect(d->mSourceFilenameFilterGroup, QOverload::of(&QButtonGroup::buttonClicked), this, &ImageScalingWidget::slotSourceFilterClicked); d->mSourceFilenameFilterGroup->addButton(d->ui->notFilterFilename, MessageComposer::MessageComposerSettings::EnumFilterSourceType::NoFilter); d->mSourceFilenameFilterGroup->addButton(d->ui->includeFilesWithPattern, MessageComposer::MessageComposerSettings::EnumFilterSourceType::IncludeFilesWithPattern); d->mSourceFilenameFilterGroup->addButton(d->ui->excludeFilesWithPattern, MessageComposer::MessageComposerSettings::EnumFilterSourceType::ExcludeFilesWithPattern); d->mRecipientFilterGroup = new QButtonGroup(d->ui->tab_4); - connect(d->mRecipientFilterGroup, static_cast(&QButtonGroup::buttonClicked), this, &ImageScalingWidget::slotRecipientFilterClicked); + connect(d->mRecipientFilterGroup, QOverload::of(&QButtonGroup::buttonClicked), this, &ImageScalingWidget::slotRecipientFilterClicked); d->ui->doNotResizePattern->setEnabled(false); d->ui->resizeEmailsPattern->setEnabled(false); d->mRecipientFilterGroup->addButton(d->ui->doNotFilterRecipients, MessageComposer::MessageComposerSettings::EnumFilterRecipientType::NoFilter); d->mRecipientFilterGroup->addButton(d->ui->resizeEachEmails, MessageComposer::MessageComposerSettings::EnumFilterRecipientType::ResizeEachEmailsContainsPattern); d->mRecipientFilterGroup->addButton(d->ui->resizeOneEmails, MessageComposer::MessageComposerSettings::EnumFilterRecipientType::ResizeOneEmailContainsPattern); d->mRecipientFilterGroup->addButton(d->ui->doNotResizeEachEmails, MessageComposer::MessageComposerSettings::EnumFilterRecipientType::DontResizeEachEmailsContainsPattern); d->mRecipientFilterGroup->addButton(d->ui->doNotResizeOneEmails, MessageComposer::MessageComposerSettings::EnumFilterRecipientType::DontResizeOneEmailContainsPattern); d->ui->help->setText(i18n("How does this work?")); connect(d->ui->help, &QLabel::linkActivated, this, &ImageScalingWidget::slotHelpLinkClicked); d->ui->help->setContextMenuPolicy(Qt::NoContextMenu); } ImageScalingWidget::~ImageScalingWidget() { delete d; } void ImageScalingWidget::slotHelpLinkClicked(const QString &) { const QString help = i18n("" "

Here you can define image filename. " "You can use:

" "
    " "
  • %t set current time
  • " "
  • %d set current date
  • " "
  • %n original filename
  • " "
  • %e original extension
  • " "
  • %x new extension
  • " "
" "
"); QWhatsThis::showText(QCursor::pos(), help); } void ImageScalingWidget::slotSourceFilterClicked(int button) { d->ui->pattern->setEnabled(button != 0); Q_EMIT changed(); } void ImageScalingWidget::slotRecipientFilterClicked(int button) { d->ui->resizeEmailsPattern->setEnabled((button == MessageComposer::MessageComposerSettings::EnumFilterRecipientType::ResizeEachEmailsContainsPattern) || (button == MessageComposer::MessageComposerSettings::EnumFilterRecipientType::ResizeOneEmailContainsPattern)); d->ui->doNotResizePattern->setEnabled((button == MessageComposer::MessageComposerSettings::EnumFilterRecipientType::DontResizeEachEmailsContainsPattern) || (button == MessageComposer::MessageComposerSettings::EnumFilterRecipientType::DontResizeOneEmailContainsPattern)); Q_EMIT changed(); } void ImageScalingWidget::slotComboboxChanged(int index) { QComboBox *combo = qobject_cast< QComboBox * >(sender()); if (combo) { const bool isCustom = combo->itemData(index) == -1; if (combo == d->ui->CBMaximumWidth) { d->ui->customMaximumWidth->setEnabled(isCustom); } else if (combo == d->ui->CBMaximumHeight) { d->ui->customMaximumHeight->setEnabled(isCustom); } else if (combo == d->ui->CBMinimumWidth) { d->ui->customMinimumWidth->setEnabled(isCustom); } else if (combo == d->ui->CBMinimumHeight) { d->ui->customMinimumHeight->setEnabled(isCustom); } Q_EMIT changed(); } } void ImageScalingWidget::initComboBox(QComboBox *combo) { const QList size = { 240, 320, 512, 640, 800, 1024, 1600, 2048 }; for (int val : size) { combo->addItem(QString::number(val), val); } combo->addItem(i18n("Custom"), -1); } void ImageScalingWidget::initWriteImageFormat() { /* Too many format :) QList listWriteFormat = QImageWriter::supportedImageFormats(); Q_FOREACH(const QByteArray& format, listWriteFormat) { d->ui->WriteToImageFormat->addItem(QString::fromLatin1(format)); } */ //known by several mailer. d->ui->WriteToImageFormat->addItem(QStringLiteral("JPG")); d->ui->WriteToImageFormat->addItem(QStringLiteral("PNG")); } void ImageScalingWidget::updateSettings() { d->ui->enabledAutoResize->setChecked(MessageComposer::MessageComposerSettings::self()->autoResizeImageEnabled()); d->ui->KeepImageRatio->setChecked(MessageComposer::MessageComposerSettings::self()->keepImageRatio()); d->ui->AskBeforeResizing->setChecked(MessageComposer::MessageComposerSettings::self()->askBeforeResizing()); d->ui->EnlargeImageToMinimum->setChecked(MessageComposer::MessageComposerSettings::self()->enlargeImageToMinimum()); d->ui->ReduceImageToMaximum->setChecked(MessageComposer::MessageComposerSettings::self()->reduceImageToMaximum()); d->ui->skipImageSizeLower->setChecked(MessageComposer::MessageComposerSettings::self()->skipImageLowerSizeEnabled()); d->ui->imageSize->setValue(MessageComposer::MessageComposerSettings::self()->skipImageLowerSize()); d->ui->customMaximumWidth->setValue(MessageComposer::MessageComposerSettings::self()->customMaximumWidth()); d->ui->customMaximumHeight->setValue(MessageComposer::MessageComposerSettings::self()->customMaximumHeight()); d->ui->customMinimumWidth->setValue(MessageComposer::MessageComposerSettings::self()->customMinimumWidth()); d->ui->customMinimumHeight->setValue(MessageComposer::MessageComposerSettings::self()->customMinimumHeight()); int index = qMax(0, d->ui->CBMaximumWidth->findData(MessageComposer::MessageComposerSettings::self()->maximumWidth())); d->ui->CBMaximumWidth->setCurrentIndex(index); d->ui->customMaximumWidth->setEnabled(d->ui->CBMaximumWidth->itemData(index) == -1); index = qMax(0, d->ui->CBMaximumHeight->findData(MessageComposer::MessageComposerSettings::self()->maximumHeight())); d->ui->CBMaximumHeight->setCurrentIndex(index); d->ui->customMaximumHeight->setEnabled(d->ui->CBMaximumHeight->itemData(index) == -1); index = qMax(0, d->ui->CBMinimumWidth->findData(MessageComposer::MessageComposerSettings::self()->minimumWidth())); d->ui->CBMinimumWidth->setCurrentIndex(index); d->ui->customMinimumWidth->setEnabled(d->ui->CBMinimumWidth->itemData(index) == -1); index = qMax(0, d->ui->CBMinimumHeight->findData(MessageComposer::MessageComposerSettings::self()->minimumHeight())); d->ui->CBMinimumHeight->setCurrentIndex(index); d->ui->customMinimumHeight->setEnabled(d->ui->CBMinimumHeight->itemData(index) == -1); index = d->ui->WriteToImageFormat->findData(MessageComposer::MessageComposerSettings::self()->writeFormat()); if (index == -1) { d->ui->WriteToImageFormat->setCurrentIndex(0); } else { d->ui->WriteToImageFormat->setCurrentIndex(index); } d->ui->pattern->setText(MessageComposer::MessageComposerSettings::self()->filterSourcePattern()); d->ui->renameResizedImage->setChecked(MessageComposer::MessageComposerSettings::self()->renameResizedImages()); d->ui->renameResizedImagePattern->setText(MessageComposer::MessageComposerSettings::self()->renameResizedImagesPattern()); d->ui->renameResizedImagePattern->setEnabled(d->ui->renameResizedImage->isChecked()); d->ui->doNotResizePattern->setText(MessageComposer::MessageComposerSettings::self()->doNotResizeEmailsPattern()); d->ui->resizeEmailsPattern->setText(MessageComposer::MessageComposerSettings::self()->resizeEmailsPattern()); d->ui->resizeImageWithFormats->setChecked(MessageComposer::MessageComposerSettings::self()->resizeImagesWithFormats()); d->ui->resizeImageWithFormatsType->setFormat(MessageComposer::MessageComposerSettings::self()->resizeImagesWithFormatsType()); d->ui->resizeImageWithFormatsType->setEnabled(d->ui->resizeImageWithFormats->isChecked()); updateFilterSourceTypeSettings(); updateEmailsFilterTypeSettings(); } void ImageScalingWidget::loadConfig() { updateSettings(); d->mWasChanged = false; } void ImageScalingWidget::updateFilterSourceTypeSettings() { switch (MessageComposer::MessageComposerSettings::self()->filterSourceType()) { case MessageComposer::MessageComposerSettings::EnumFilterSourceType::NoFilter: d->ui->notFilterFilename->setChecked(true); d->ui->pattern->setEnabled(false); break; case MessageComposer::MessageComposerSettings::EnumFilterSourceType::IncludeFilesWithPattern: d->ui->includeFilesWithPattern->setChecked(true); d->ui->pattern->setEnabled(true); break; case MessageComposer::MessageComposerSettings::EnumFilterSourceType::ExcludeFilesWithPattern: d->ui->excludeFilesWithPattern->setChecked(true); d->ui->pattern->setEnabled(true); break; } } void ImageScalingWidget::updateEmailsFilterTypeSettings() { d->ui->doNotResizePattern->setEnabled(false); d->ui->resizeEmailsPattern->setEnabled(false); switch (MessageComposer::MessageComposerSettings::self()->filterRecipientType()) { case MessageComposer::MessageComposerSettings::EnumFilterRecipientType::NoFilter: d->ui->doNotFilterRecipients->setChecked(true); break; case MessageComposer::MessageComposerSettings::EnumFilterRecipientType::ResizeEachEmailsContainsPattern: d->ui->resizeEachEmails->setChecked(true); d->ui->resizeEmailsPattern->setEnabled(true); break; case MessageComposer::MessageComposerSettings::EnumFilterRecipientType::ResizeOneEmailContainsPattern: d->ui->resizeOneEmails->setChecked(true); d->ui->resizeEmailsPattern->setEnabled(true); break; case MessageComposer::MessageComposerSettings::EnumFilterRecipientType::DontResizeEachEmailsContainsPattern: d->ui->doNotResizeEachEmails->setChecked(true); d->ui->doNotResizePattern->setEnabled(true); break; case MessageComposer::MessageComposerSettings::EnumFilterRecipientType::DontResizeOneEmailContainsPattern: d->ui->doNotResizeOneEmails->setChecked(true); d->ui->doNotResizePattern->setEnabled(false); break; } } void ImageScalingWidget::writeConfig() { if (d->ui->EnlargeImageToMinimum->isChecked() && d->ui->ReduceImageToMaximum->isChecked()) { if ((d->ui->customMinimumWidth->value() >= d->ui->customMaximumWidth->value()) || (d->ui->customMinimumHeight->value() >= d->ui->customMaximumHeight->value())) { KMessageBox::error(this, i18n("Please verify minimum and maximum values."), i18n("Error in minimum Maximum value")); return; } } MessageComposer::MessageComposerSettings::self()->setAutoResizeImageEnabled(d->ui->enabledAutoResize->isChecked()); MessageComposer::MessageComposerSettings::self()->setKeepImageRatio(d->ui->KeepImageRatio->isChecked()); MessageComposer::MessageComposerSettings::self()->setAskBeforeResizing(d->ui->AskBeforeResizing->isChecked()); MessageComposer::MessageComposerSettings::self()->setEnlargeImageToMinimum(d->ui->EnlargeImageToMinimum->isChecked()); MessageComposer::MessageComposerSettings::self()->setReduceImageToMaximum(d->ui->ReduceImageToMaximum->isChecked()); MessageComposer::MessageComposerSettings::self()->setCustomMaximumWidth(d->ui->customMaximumWidth->value()); MessageComposer::MessageComposerSettings::self()->setCustomMaximumHeight(d->ui->customMaximumHeight->value()); MessageComposer::MessageComposerSettings::self()->setCustomMinimumWidth(d->ui->customMinimumWidth->value()); MessageComposer::MessageComposerSettings::self()->setCustomMinimumHeight(d->ui->customMinimumHeight->value()); MessageComposer::MessageComposerSettings::self()->setMaximumWidth(d->ui->CBMaximumWidth->itemData(d->ui->CBMaximumWidth->currentIndex()).toInt()); MessageComposer::MessageComposerSettings::self()->setMaximumHeight(d->ui->CBMaximumHeight->itemData(d->ui->CBMaximumHeight->currentIndex()).toInt()); MessageComposer::MessageComposerSettings::self()->setMinimumWidth(d->ui->CBMinimumWidth->itemData(d->ui->CBMinimumWidth->currentIndex()).toInt()); MessageComposer::MessageComposerSettings::self()->setMinimumHeight(d->ui->CBMinimumHeight->itemData(d->ui->CBMinimumHeight->currentIndex()).toInt()); MessageComposer::MessageComposerSettings::self()->setWriteFormat(d->ui->WriteToImageFormat->currentText()); MessageComposer::MessageComposerSettings::self()->setSkipImageLowerSizeEnabled(d->ui->skipImageSizeLower->isChecked()); MessageComposer::MessageComposerSettings::self()->setSkipImageLowerSize(d->ui->imageSize->value()); MessageComposer::MessageComposerSettings::self()->setFilterSourcePattern(d->ui->pattern->text()); MessageComposer::MessageComposerSettings::self()->setFilterSourceType(d->mSourceFilenameFilterGroup->checkedId()); MessageComposer::MessageComposerSettings::self()->setRenameResizedImages(d->ui->renameResizedImage->isChecked()); MessageComposer::MessageComposerSettings::self()->setRenameResizedImagesPattern(d->ui->renameResizedImagePattern->text()); MessageComposer::MessageComposerSettings::self()->setDoNotResizeEmailsPattern(d->ui->doNotResizePattern->text()); MessageComposer::MessageComposerSettings::self()->setResizeEmailsPattern(d->ui->resizeEmailsPattern->text()); MessageComposer::MessageComposerSettings::self()->setFilterRecipientType(d->mRecipientFilterGroup->checkedId()); MessageComposer::MessageComposerSettings::self()->setResizeImagesWithFormats(d->ui->resizeImageWithFormats->isChecked()); MessageComposer::MessageComposerSettings::self()->setResizeImagesWithFormatsType(d->ui->resizeImageWithFormatsType->format()); d->mWasChanged = false; } void ImageScalingWidget::resetToDefault() { const bool bUseDefaults = MessageComposer::MessageComposerSettings::self()->useDefaults(true); updateSettings(); MessageComposer::MessageComposerSettings::self()->useDefaults(bUseDefaults); } diff --git a/messagelist/src/utils/themeeditor.cpp b/messagelist/src/utils/themeeditor.cpp index 64cfbff7..403522bd 100644 --- a/messagelist/src/utils/themeeditor.cpp +++ b/messagelist/src/utils/themeeditor.cpp @@ -1,1561 +1,1561 @@ /****************************************************************************** * * Copyright 2008 Szymon Tomasz Stefanek * * This program is free softhisare; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Softhisare Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Softhisare * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. * *******************************************************************************/ #include "utils/themeeditor.h" #include "core/theme.h" #include "core/groupheaderitem.h" #include "core/messageitem.h" #include "core/modelinvariantrowmapper.h" #include "core/manager.h" #include "utils/comboboxutils.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include // for time_t #include #include using namespace MessageList::Utils; using namespace MessageList::Core; static const char gThemeContentItemTypeDndMimeDataFormat[] = "application/x-kmail-messagelistview-theme-contentitem-type"; ThemeColumnPropertiesDialog::ThemeColumnPropertiesDialog(QWidget *parent, Theme::Column *column, const QString &title) : QDialog(parent) , mColumn(column) { setWindowModality(Qt::ApplicationModal); // FIXME: Sure ? QVBoxLayout *mainLayout = new QVBoxLayout(this); QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); QPushButton *okButton = buttonBox->button(QDialogButtonBox::Ok); okButton->setDefault(true); okButton->setShortcut(Qt::CTRL | Qt::Key_Return); connect(buttonBox, &QDialogButtonBox::rejected, this, &ThemeColumnPropertiesDialog::reject); setWindowTitle(title); QWidget *base = new QWidget(this); mainLayout->addWidget(base); mainLayout->addWidget(buttonBox); QGridLayout *g = new QGridLayout(base); QLabel *l = new QLabel(i18nc("@label:textbox Property name", "Name:"), base); g->addWidget(l, 0, 0); mNameEdit = new KLineEdit(base); mNameEdit->setToolTip(i18n("The label that will be displayed in the column header.")); g->addWidget(mNameEdit, 0, 1); l = new QLabel(i18n("Header click sorts messages:"), base); g->addWidget(l, 1, 0); mMessageSortingCombo = new KComboBox(base); mMessageSortingCombo->setToolTip(i18n("The sorting order that clicking on this column header will switch to.")); g->addWidget(mMessageSortingCombo, 1, 1); mVisibleByDefaultCheck = new QCheckBox(i18n("Visible by default"), base); mVisibleByDefaultCheck->setToolTip(i18n("Check this if this column should be visible when the theme is selected.")); g->addWidget(mVisibleByDefaultCheck, 2, 1); mIsSenderOrReceiverCheck = new QCheckBox(i18n("Contains \"Sender or Receiver\" field"), base); mIsSenderOrReceiverCheck->setToolTip(i18n("Check this if this column label should be updated depending on the folder \"inbound\"/\"outbound\" type.")); g->addWidget(mIsSenderOrReceiverCheck, 3, 1); g->setColumnStretch(1, 1); g->setRowStretch(10, 1); connect(okButton, &QPushButton::clicked, this, &ThemeColumnPropertiesDialog::slotOkButtonClicked); // Display the current settings mNameEdit->setText(mColumn->label()); mVisibleByDefaultCheck->setChecked(mColumn->visibleByDefault()); mIsSenderOrReceiverCheck->setChecked(mColumn->isSenderOrReceiver()); ComboBoxUtils::fillIntegerOptionCombo(mMessageSortingCombo, SortOrder::enumerateMessageSortingOptions(Aggregation::PerfectReferencesAndSubject)); ComboBoxUtils::setIntegerOptionComboValue(mMessageSortingCombo, mColumn->messageSorting()); } void ThemeColumnPropertiesDialog::slotOkButtonClicked() { QString text = mNameEdit->text(); if (text.isEmpty()) { text = i18n("Unnamed Column"); } mColumn->setLabel(text); mColumn->setVisibleByDefault(mVisibleByDefaultCheck->isChecked()); mColumn->setIsSenderOrReceiver(mIsSenderOrReceiverCheck->isChecked()); mColumn->setMessageSorting( static_cast< SortOrder::MessageSorting >( ComboBoxUtils::getIntegerOptionComboValue(mMessageSortingCombo, SortOrder::NoMessageSorting) ) ); accept(); } ThemeContentItemSourceLabel::ThemeContentItemSourceLabel(QWidget *parent, Theme::ContentItem::Type type) : QLabel(parent) , mType(type) { setFrameStyle(QFrame::StyledPanel | QFrame::Raised); } ThemeContentItemSourceLabel::~ThemeContentItemSourceLabel() { } MessageList::Core::Theme::ContentItem::Type ThemeContentItemSourceLabel::type() const { return mType; } void ThemeContentItemSourceLabel::mousePressEvent(QMouseEvent *e) { if (e->button() == Qt::LeftButton) { mMousePressPoint = e->pos(); } } void ThemeContentItemSourceLabel::mouseMoveEvent(QMouseEvent *e) { if (e->buttons() & Qt::LeftButton) { const QPoint diff = mMousePressPoint - e->pos(); if (diff.manhattanLength() > 4) { startDrag(); } } } void ThemeContentItemSourceLabel::startDrag() { //QPixmap pix = QPixmap::grabWidget( this ); //QPixmap alpha( pix.width(), pix.height() ); //alpha.fill(0x0f0f0f0f); //pix.setAlphaChannel( alpha ); // <-- this crashes... no alpha for dragged pixmap :( QMimeData *data = new QMimeData(); QByteArray arry; arry.resize(sizeof(Theme::ContentItem::Type)); *((Theme::ContentItem::Type *)arry.data()) = mType; data->setData(QLatin1String(gThemeContentItemTypeDndMimeDataFormat), arry); QDrag *drag = new QDrag(this); drag->setMimeData(data); //drag->setPixmap( pix ); //drag->setHotSpot( mapFromGlobal( QCursor::pos() ) ); drag->exec(Qt::CopyAction, Qt::CopyAction); } ThemePreviewDelegate::ThemePreviewDelegate(QAbstractItemView *parent) : ThemeDelegate(parent) { mRowMapper = new ModelInvariantRowMapper(); mSampleGroupHeaderItem = new GroupHeaderItem(i18n("Message Group")); mSampleGroupHeaderItem->setDate(time(nullptr)); mSampleGroupHeaderItem->setMaxDate(time(nullptr) + 31337); mSampleGroupHeaderItem->setSubject(i18n("Very long subject very long subject very long subject very long subject very long subject very long")); mSampleMessageItem = new FakeItem(); mSampleMessageItem->setDate(time(nullptr)); mSampleMessageItem->setSize(0x31337); mSampleMessageItem->setMaxDate(time(nullptr) + 31337); mSampleMessageItem->setSender(i18n("Sender")); mSampleMessageItem->setReceiver(i18n("Receiver")); mSampleMessageItem->setSubject(i18n("Very long subject very long subject very long subject very long subject very long subject very long")); mSampleMessageItem->setSignatureState(MessageItem::FullySigned); mSampleMessageItem->setEncryptionState(MessageItem::FullyEncrypted); QList< MessageItem::Tag * > list; list.append(new MessageItem::Tag(SmallIcon(QStringLiteral("feed-subscribe")), i18n("Sample Tag 1"), QString())); list.append(new MessageItem::Tag(SmallIcon(QStringLiteral("feed-subscribe")), i18n("Sample Tag 2"), QString())); list.append(new MessageItem::Tag(SmallIcon(QStringLiteral("feed-subscribe")), i18n("Sample Tag 3"), QString())); mSampleMessageItem->setFakeTags(list); mRowMapper->createModelInvariantIndex(0, mSampleMessageItem); mSampleGroupHeaderItem->rawAppendChildItem(mSampleMessageItem); mSampleMessageItem->setParent(mSampleGroupHeaderItem); Akonadi::MessageStatus stat; stat.fromQInt32(0x7fffffff); stat.setQueued(false); stat.setSent(false); stat.setSpam(true); stat.setWatched(true); stat.setHasInvitation(); //stat.setHasAttachment( false ); mSampleMessageItem->setStatus(stat); } ThemePreviewDelegate::~ThemePreviewDelegate() { delete mSampleGroupHeaderItem; //delete mSampleMessageItem; (deleted by the parent) delete mRowMapper; } Item *ThemePreviewDelegate::itemFromIndex(const QModelIndex &index) const { if (index.parent().isValid()) { return mSampleMessageItem; } return mSampleGroupHeaderItem; } ThemePreviewWidget::ThemePreviewWidget(QWidget *parent) : QTreeWidget(parent) , mTheme(nullptr) { mSelectedThemeContentItem = nullptr; mSelectedThemeColumn = nullptr; mFirstShow = true; mReadOnly = false; mDelegate = new ThemePreviewDelegate(this); setItemDelegate(mDelegate); setRootIsDecorated(false); viewport()->setAcceptDrops(true); header()->setContextMenuPolicy(Qt::CustomContextMenu); // make sure it's true connect(header(), &QWidget::customContextMenuRequested, this, &ThemePreviewWidget::slotHeaderContextMenuRequested); mGroupHeaderSampleItem = new QTreeWidgetItem(this); mGroupHeaderSampleItem->setText(0, QString()); mGroupHeaderSampleItem->setFlags(Qt::ItemIsEnabled); QTreeWidgetItem *m = new QTreeWidgetItem(mGroupHeaderSampleItem); m->setText(0, QString()); mGroupHeaderSampleItem->setExpanded(true); header()->setSectionsMovable(false); } void ThemePreviewWidget::changeEvent(QEvent *event) { if (event->type() == QEvent::FontChange) { mDelegate->generalFontChanged(); } QTreeWidget::changeEvent(event); } ThemePreviewWidget::~ThemePreviewWidget() { } QSize ThemePreviewWidget::sizeHint() const { return QSize(350, 180); } void ThemePreviewWidget::setReadOnly(bool readOnly) { mReadOnly = readOnly; } void ThemePreviewWidget::applyThemeColumnWidths() { if (!mTheme) { return; } const QList< Theme::Column * > &columns = mTheme->columns(); if (columns.isEmpty()) { viewport()->update(); // trigger a repaint return; } // Now we want to distribute the available width on all the columns. // The algorithm used here is very similar to the one used in View::applyThemeColumns(). // It just takes care of ALL the columns instead of the visible ones. QList< Theme::Column * >::ConstIterator it; // Gather size hints for all sections. int idx = 0; int totalVisibleWidthHint = 0; QList< Theme::Column * >::ConstIterator end(columns.constEnd()); for (it = columns.constBegin(); it != end; ++it) { totalVisibleWidthHint += mDelegate->sizeHintForItemTypeAndColumn(Item::Message, idx).width(); idx++; } if (totalVisibleWidthHint < 16) { totalVisibleWidthHint = 16; // be reasonable } // Now we can compute proportional widths. idx = 0; QList< int > realWidths; realWidths.reserve(columns.count()); int totalVisibleWidth = 0; end = columns.constEnd(); for (it = columns.constBegin(); it != end; ++it) { int hintWidth = mDelegate->sizeHintForItemTypeAndColumn(Item::Message, idx).width(); int realWidth; if ((*it)->containsTextItems()) { // the column contains text items, it should get more space realWidth = ((hintWidth * viewport()->width()) / totalVisibleWidthHint) - 2; // -2 is heuristic if (realWidth < (hintWidth + 2)) { realWidth = hintWidth + 2; // can't be less } } else { // the column contains no text items, it should get just a little bit more than its sizeHint(). realWidth = hintWidth + 2; } realWidths.append(realWidth); totalVisibleWidth += realWidth; idx++; } idx = 0; totalVisibleWidth += 4; // account for some view's border if (totalVisibleWidth < viewport()->width()) { // give the additional space to the text columns // also give more space to the first ones and less space to the last ones int available = viewport()->width() - totalVisibleWidth; for (it = columns.begin(); it != columns.end(); ++it) { if (((*it)->visibleByDefault() || (idx == 0)) && (*it)->containsTextItems()) { // give more space to this column available >>= 1; // eat half of the available space realWidths[ idx ] += available; // and give it to this column } idx++; } // if any space is still available, give it to the first column if (available) { realWidths[ 0 ] += available; } } idx = 0; // We're ready. // Assign widths. Hide the sections that are not visible by default, show the other ones. for (it = columns.begin(); it != columns.end(); ++it) { header()->resizeSection(idx, realWidths[ idx ]); idx++; } #if 0 if (mTheme->viewHeaderPolicy() == Theme::NeverShowHeader) { header()->hide(); } else { header()->show(); } #endif } void ThemePreviewWidget::setTheme(Theme *theme) { bool themeChanged = theme != mTheme; mSelectedThemeContentItem = nullptr; mThemeSelectedContentItemRect = QRect(); mDropIndicatorPoint1 = QPoint(); mDropIndicatorPoint2 = QPoint(); mTheme = theme; mDelegate->setTheme(theme); if (!mTheme) { return; } mGroupHeaderSampleItem->setExpanded(true); const QList< Theme::Column * > &columns = mTheme->columns(); setColumnCount(columns.count()); QStringList headerLabels; headerLabels.reserve(columns.count()); QList< Theme::Column * >::ConstIterator end(columns.constEnd()); for (QList< Theme::Column * >::ConstIterator it = columns.constBegin(); it != end; ++it) { QString label = (*it)->label(); if ((*it)->visibleByDefault()) { label += QStringLiteral(" (%1)").arg(i18nc("Indicates whether or not a header label is visible", "Visible")); } headerLabels.append(label); } setHeaderLabels(headerLabels); if (themeChanged) { applyThemeColumnWidths(); } viewport()->update(); // trigger a repaint } void ThemePreviewWidget::internalHandleDragEnterEvent(QDragEnterEvent *e) { e->ignore(); if (!e->mimeData()) { return; } if (!e->mimeData()->hasFormat(QLatin1String(gThemeContentItemTypeDndMimeDataFormat))) { return; } e->accept(); } void ThemePreviewWidget::showEvent(QShowEvent *e) { QTreeWidget::showEvent(e); if (mFirstShow) { // Make sure we re-apply column widths the first time we're shown. // The first "apply" call was made while the widget was still hidden and // almost surely had wrong sizes. applyThemeColumnWidths(); mFirstShow = false; } } void ThemePreviewWidget::dragEnterEvent(QDragEnterEvent *e) { internalHandleDragEnterEvent(e); mThemeSelectedContentItemRect = QRect(); viewport()->update(); // trigger a repaint } void ThemePreviewWidget::internalHandleDragMoveEvent(QDragMoveEvent *e) { e->ignore(); if (mReadOnly) { return; } if (!e->mimeData()) { return; } if (!e->mimeData()->hasFormat(QLatin1String(gThemeContentItemTypeDndMimeDataFormat))) { return; } QByteArray arry = e->mimeData()->data(QLatin1String(gThemeContentItemTypeDndMimeDataFormat)); if (arry.size() != sizeof(Theme::ContentItem::Type)) { return; // ugh } Theme::ContentItem::Type type = *((Theme::ContentItem::Type *)arry.data()); if (!computeContentItemInsertPosition(e->pos(), type)) { return; } e->accept(); } void ThemePreviewWidget::dragMoveEvent(QDragMoveEvent *e) { if (mReadOnly) { return; } internalHandleDragMoveEvent(e); mThemeSelectedContentItemRect = QRect(); viewport()->update(); // trigger a repaint } void ThemePreviewWidget::dropEvent(QDropEvent *e) { mDropIndicatorPoint1 = mDropIndicatorPoint2; e->ignore(); if (mReadOnly) { return; } if (!e->mimeData()) { return; } if (!e->mimeData()->hasFormat(QLatin1String(gThemeContentItemTypeDndMimeDataFormat))) { return; } QByteArray arry = e->mimeData()->data(QLatin1String(gThemeContentItemTypeDndMimeDataFormat)); if (arry.size() != sizeof(Theme::ContentItem::Type)) { return; // ugh } Theme::ContentItem::Type type = *((Theme::ContentItem::Type *)arry.data()); if (!computeContentItemInsertPosition(e->pos(), type)) { viewport()->update(); return; } Theme::Row *row = nullptr; switch (mRowInsertPosition) { case AboveRow: row = new Theme::Row(); if (mDelegate->hitItem()->type() == Item::Message) { const_cast< Theme::Column * >(mDelegate->hitColumn())->insertMessageRow(mDelegate->hitRowIndex(), row); } else { const_cast< Theme::Column * >(mDelegate->hitColumn())->insertGroupHeaderRow(mDelegate->hitRowIndex(), row); } break; case InsideRow: row = const_cast< Theme::Row * >(mDelegate->hitRow()); break; case BelowRow: row = new Theme::Row(); if (mDelegate->hitItem()->type() == Item::Message) { const_cast< Theme::Column * >(mDelegate->hitColumn())->insertMessageRow(mDelegate->hitRowIndex() + 1, row); } else { const_cast< Theme::Column * >(mDelegate->hitColumn())->insertGroupHeaderRow(mDelegate->hitRowIndex() + 1, row); } break; } if (!row) { return; } Theme::ContentItem *ci = new Theme::ContentItem(type); if (ci->canBeDisabled()) { if (ci->isClickable()) { ci->setSoftenByBlendingWhenDisabled(true); // default to softened } else { ci->setHideWhenDisabled(true); // default to hidden } } int idx; switch (mItemInsertPosition) { case OnLeftOfItem: if (!mDelegate->hitContentItem()) { // bleah delete ci; return; } idx = mDelegate->hitContentItemRight() \ ? row->rightItems().indexOf(const_cast< Theme::ContentItem * >(mDelegate->hitContentItem())) \ : row->leftItems().indexOf(const_cast< Theme::ContentItem * >(mDelegate->hitContentItem())); if (idx < 0) { // bleah delete ci; return; } if (mDelegate->hitContentItemRight()) { row->insertRightItem(idx + 1, ci); } else { row->insertLeftItem(idx, ci); } break; case OnRightOfItem: if (!mDelegate->hitContentItem()) { // bleah delete ci; return; } idx = mDelegate->hitContentItemRight() \ ? row->rightItems().indexOf(const_cast< Theme::ContentItem * >(mDelegate->hitContentItem())) \ : row->leftItems().indexOf(const_cast< Theme::ContentItem * >(mDelegate->hitContentItem())); if (idx < 0) { // bleah delete ci; return; } if (mDelegate->hitContentItemRight()) { row->insertRightItem(idx, ci); } else { row->insertLeftItem(idx + 1, ci); } break; case AsLastLeftItem: row->addLeftItem(ci); break; case AsLastRightItem: row->addRightItem(ci); break; case AsFirstLeftItem: row->insertLeftItem(0, ci); break; case AsFirstRightItem: row->insertRightItem(0, ci); break; default: // should never happen row->addRightItem(ci); break; } e->acceptProposedAction(); mThemeSelectedContentItemRect = QRect(); mDropIndicatorPoint1 = mDropIndicatorPoint2; mSelectedThemeContentItem = nullptr; setTheme(mTheme); // this will reset theme cache and trigger a global update } bool ThemePreviewWidget::computeContentItemInsertPosition(const QPoint &pos, Theme::ContentItem::Type type) { mDropIndicatorPoint1 = mDropIndicatorPoint2; // this marks the position as invalid if (!mDelegate->hitTest(pos, false)) { return false; } if (!mDelegate->hitRow()) { return false; } if (mDelegate->hitRowIsMessageRow()) { if (!Theme::ContentItem::applicableToMessageItems(type)) { return false; } } else { if (!Theme::ContentItem::applicableToGroupHeaderItems(type)) { return false; } } QRect rowRect = mDelegate->hitRowRect(); if (pos.y() < rowRect.top() + 3) { // above a row mRowInsertPosition = AboveRow; if (pos.x() < (rowRect.left() + (rowRect.width() / 2))) { mDropIndicatorPoint1 = rowRect.topLeft(); mItemInsertPosition = AsLastLeftItem; } else { mDropIndicatorPoint1 = rowRect.topRight(); mItemInsertPosition = AsLastRightItem; } mDropIndicatorPoint2 = QPoint(rowRect.left() + (rowRect.width() / 2), rowRect.top()); return true; } if (pos.y() > rowRect.bottom() - 3) { // below a row mRowInsertPosition = BelowRow; if (pos.x() < (rowRect.left() + (rowRect.width() / 2))) { mDropIndicatorPoint1 = rowRect.bottomLeft(); mItemInsertPosition = AsLastLeftItem; } else { mDropIndicatorPoint1 = rowRect.bottomRight(); mItemInsertPosition = AsLastRightItem; } mDropIndicatorPoint2 = QPoint(rowRect.left() + (rowRect.width() / 2), rowRect.bottom()); return true; } mRowInsertPosition = InsideRow; if (!mDelegate->hitContentItem()) { // didn't hit anything... probably no items in the row if (pos.x() < (rowRect.left() + (rowRect.width() / 2))) { mItemInsertPosition = AsLastLeftItem; mDropIndicatorPoint1 = QPoint(rowRect.left(), rowRect.top()); mDropIndicatorPoint2 = QPoint(rowRect.left(), rowRect.bottom()); } else { mItemInsertPosition = AsLastRightItem; mDropIndicatorPoint1 = QPoint(rowRect.right(), rowRect.top()); mDropIndicatorPoint2 = QPoint(rowRect.right(), rowRect.bottom()); } return true; } // hit something, maybe inexactly QRect itemRect = mDelegate->hitContentItemRect(); if (!itemRect.contains(pos)) { // inexact hit: outside an item if (pos.x() > itemRect.right()) { // right side of an item if (mDelegate->hitRow()->rightItems().count() < 1) { // between the last left item and the right side if (pos.x() > (itemRect.right() + ((rowRect.right() - itemRect.right()) / 2))) { // first/last right item mItemInsertPosition = AsFirstRightItem; mDropIndicatorPoint1 = rowRect.topRight(); mDropIndicatorPoint2 = rowRect.bottomRight(); } return true; } // either there were some right items (so the theme delegate knows that the reported item is the closest) // or there were no right items but the position is closest to the left item than the right row end mItemInsertPosition = OnRightOfItem; mDropIndicatorPoint1 = itemRect.topRight(); mDropIndicatorPoint2 = itemRect.bottomRight(); return true; } // left side of an item if (mDelegate->hitRow()->leftItems().count() < 1) { // between the left side and the leftmost right item if (pos.x() < (itemRect.left() - ((itemRect.left() - rowRect.left()) / 2))) { mItemInsertPosition = AsFirstLeftItem; mDropIndicatorPoint1 = rowRect.topLeft(); mDropIndicatorPoint2 = rowRect.bottomLeft(); return true; } } mItemInsertPosition = OnLeftOfItem; mDropIndicatorPoint1 = itemRect.topLeft(); mDropIndicatorPoint2 = itemRect.bottomLeft(); return true; } // exact hit if (pos.x() < (itemRect.left() + (itemRect.width() / 2))) { // left side mItemInsertPosition = OnLeftOfItem; mDropIndicatorPoint1 = itemRect.topLeft(); mDropIndicatorPoint2 = itemRect.bottomLeft(); return true; } // right side mItemInsertPosition = OnRightOfItem; mDropIndicatorPoint1 = itemRect.topRight(); mDropIndicatorPoint2 = itemRect.bottomRight(); return true; } void ThemePreviewWidget::mouseMoveEvent(QMouseEvent *e) { if (!(mSelectedThemeContentItem && (e->buttons() & Qt::LeftButton)) || mReadOnly) { QTreeWidget::mouseMoveEvent(e); return; } if (mSelectedThemeContentItem != mDelegate->hitContentItem()) { QTreeWidget::mouseMoveEvent(e); return; // ugh.. something weird happened } // starting a drag ? const QPoint diff = e->pos() - mMouseDownPoint; if (diff.manhattanLength() <= 4) { QTreeWidget::mouseMoveEvent(e); return; // ugh.. something weird happened } // starting a drag QMimeData *data = new QMimeData(); QByteArray arry; arry.resize(sizeof(Theme::ContentItem::Type)); *((Theme::ContentItem::Type *)arry.data()) = mSelectedThemeContentItem->type(); data->setData(QLatin1String(gThemeContentItemTypeDndMimeDataFormat), arry); QDrag *drag = new QDrag(this); drag->setMimeData(data); // remove the Theme::ContentItem from the Theme if (mDelegate->hitContentItemRight()) { const_cast< Theme::Row * >(mDelegate->hitRow())->removeRightItem(mSelectedThemeContentItem); } else { const_cast< Theme::Row * >(mDelegate->hitRow())->removeLeftItem(mSelectedThemeContentItem); } delete mSelectedThemeContentItem; if (mDelegate->hitRow()->rightItems().isEmpty() && mDelegate->hitRow()->leftItems().isEmpty()) { if (mDelegate->hitItem()->type() == Item::Message) { if (mDelegate->hitColumn()->messageRows().count() > 1) { const_cast< Theme::Column * >(mDelegate->hitColumn())->removeMessageRow(const_cast< Theme::Row * >(mDelegate->hitRow())); delete mDelegate->hitRow(); } } else { if (mDelegate->hitColumn()->groupHeaderRows().count() > 1) { const_cast< Theme::Column * >(mDelegate->hitColumn())->removeGroupHeaderRow(const_cast< Theme::Row * >(mDelegate->hitRow())); delete mDelegate->hitRow(); } } } mSelectedThemeContentItem = nullptr; mThemeSelectedContentItemRect = QRect(); mDropIndicatorPoint1 = mDropIndicatorPoint2; setTheme(mTheme); // this will reset theme cache and trigger a global update // and do drag drag->exec(Qt::CopyAction, Qt::CopyAction); } void ThemePreviewWidget::mousePressEvent(QMouseEvent *e) { if (mReadOnly) { QTreeWidget::mousePressEvent(e); return; } mMouseDownPoint = e->pos(); if (mDelegate->hitTest(mMouseDownPoint)) { mSelectedThemeContentItem = const_cast< Theme::ContentItem * >(mDelegate->hitContentItem()); mThemeSelectedContentItemRect = mSelectedThemeContentItem ? mDelegate->hitContentItemRect() : QRect(); } else { mSelectedThemeContentItem = nullptr; mThemeSelectedContentItemRect = QRect(); } QTreeWidget::mousePressEvent(e); viewport()->update(); if (e->button() == Qt::RightButton) { QMenu menu; if (mSelectedThemeContentItem) { menu.addSection(Theme::ContentItem::description(mSelectedThemeContentItem->type())); if (mSelectedThemeContentItem->displaysText()) { QAction *act = menu.addAction(i18nc("@action:inmenu soften the text color", "Soften")); act->setCheckable(true); act->setChecked(mSelectedThemeContentItem->softenByBlending()); connect(act, &QAction::triggered, this, &ThemePreviewWidget::slotSoftenActionTriggered); QMenu *childmenu = new QMenu(&menu); act = childmenu->addAction(i18nc("@action:inmenu Font setting", "Bold")); act->setData(QVariant(static_cast(Theme::ContentItem::IsBold))); act->setCheckable(true); act->setChecked(mSelectedThemeContentItem->isBold()); act = childmenu->addAction(i18nc("@action:inmenu Font setting", "Italic")); act->setData(QVariant(static_cast(Theme::ContentItem::IsItalic))); act->setCheckable(true); act->setChecked(mSelectedThemeContentItem->isItalic()); connect(childmenu, &QMenu::triggered, this, &ThemePreviewWidget::slotFontMenuTriggered); menu.addMenu(childmenu)->setText(i18n("Font")); } if (mSelectedThemeContentItem->canUseCustomColor()) { QMenu *childmenu = new QMenu(&menu); QActionGroup *grp = new QActionGroup(childmenu); QAction *act = childmenu->addAction(i18nc("@action:inmenu Foreground color setting", "Default")); act->setData(QVariant(static_cast< int >(0))); act->setCheckable(true); act->setChecked(!mSelectedThemeContentItem->useCustomColor()); grp->addAction(act); act = childmenu->addAction(i18nc("@action:inmenu Foreground color setting", "Custom...")); act->setData(QVariant(static_cast< int >(Theme::ContentItem::UseCustomColor))); act->setCheckable(true); act->setChecked(mSelectedThemeContentItem->useCustomColor()); grp->addAction(act); connect(childmenu, &QMenu::triggered, this, &ThemePreviewWidget::slotForegroundColorMenuTriggered); menu.addMenu(childmenu)->setText(i18n("Foreground Color")); } if (mSelectedThemeContentItem->canBeDisabled()) { QMenu *childmenu = new QMenu(&menu); QActionGroup *grp = new QActionGroup(childmenu); QAction *act = childmenu->addAction(i18nc("Hide a mark if the mail does not have the attribute, e.g. Important mark on a non important mail", "Hide")); act->setData(QVariant(static_cast< int >(Theme::ContentItem::HideWhenDisabled))); act->setCheckable(true); act->setChecked(mSelectedThemeContentItem->hideWhenDisabled()); grp->addAction(act); act = childmenu->addAction(i18nc("Keep a empty space in the list if the mail does not have the attribute, e.g. Important mark on a non important mail", "Keep Empty Space")); act->setData(QVariant(static_cast< int >(0))); act->setCheckable(true); act->setChecked(!(mSelectedThemeContentItem->softenByBlendingWhenDisabled() || mSelectedThemeContentItem->hideWhenDisabled())); grp->addAction(act); act = childmenu->addAction(i18nc("Show the icon softened in the list if the mail does not have the attribute, e.g. Important mark on a non important mail", "Keep Softened Icon")); act->setData(QVariant(static_cast< int >(Theme::ContentItem::SoftenByBlendingWhenDisabled))); act->setCheckable(true); act->setChecked(mSelectedThemeContentItem->softenByBlendingWhenDisabled()); grp->addAction(act); connect(childmenu, &QMenu::triggered, this, &ThemePreviewWidget::slotDisabledFlagsMenuTriggered); menu.addMenu(childmenu)->setText(i18n("When Disabled")); } } if (mDelegate->hitItem()) { if (mDelegate->hitItem()->type() == Item::GroupHeader) { menu.addSection(i18n("Group Header")); // Background color (mode) submenu QMenu *childmenu = new QMenu(&menu); QActionGroup *grp = new QActionGroup(childmenu); QAction *act = childmenu->addAction(i18nc("@action:inmenu Group header background color setting", "None")); act->setData(QVariant(static_cast< int >(Theme::Transparent))); act->setCheckable(true); act->setChecked(mTheme->groupHeaderBackgroundMode() == Theme::Transparent); grp->addAction(act); act = childmenu->addAction(i18nc("@action:inmenu Group header background color setting", "Automatic")); act->setData(QVariant(static_cast< int >(Theme::AutoColor))); act->setCheckable(true); act->setChecked(mTheme->groupHeaderBackgroundMode() == Theme::AutoColor); grp->addAction(act); act = childmenu->addAction(i18nc("@action:inmenu Group header background color setting", "Custom...")); act->setData(QVariant(static_cast< int >(Theme::CustomColor))); act->setCheckable(true); act->setChecked(mTheme->groupHeaderBackgroundMode() == Theme::CustomColor); grp->addAction(act); connect(childmenu, &QMenu::triggered, this, &ThemePreviewWidget::slotGroupHeaderBackgroundModeMenuTriggered); menu.addMenu(childmenu)->setText(i18n("Background Color")); // Background style submenu childmenu = new QMenu(&menu); grp = new QActionGroup(childmenu); QList< QPair< QString, int > > styles = Theme::enumerateGroupHeaderBackgroundStyles(); QList< QPair< QString, int > >::ConstIterator end(styles.constEnd()); for (QList< QPair< QString, int > >::ConstIterator it = styles.constBegin(); it != end; ++it) { act = childmenu->addAction((*it).first); act->setData(QVariant((*it).second)); act->setCheckable(true); act->setChecked(mTheme->groupHeaderBackgroundStyle() == static_cast< Theme::GroupHeaderBackgroundStyle >((*it).second)); grp->addAction(act); } connect(childmenu, &QMenu::triggered, this, &ThemePreviewWidget::slotGroupHeaderBackgroundStyleMenuTriggered); act = menu.addMenu(childmenu); act->setText(i18n("Background Style")); if (mTheme->groupHeaderBackgroundMode() == Theme::Transparent) { act->setEnabled(false); } } } if (menu.isEmpty()) { return; } menu.exec(viewport()->mapToGlobal(e->pos())); } } void ThemePreviewWidget::slotDisabledFlagsMenuTriggered(QAction *act) { if (!mSelectedThemeContentItem) { return; } bool ok; const int flags = act->data().toInt(&ok); if (!ok) { return; } mSelectedThemeContentItem->setHideWhenDisabled(flags == Theme::ContentItem::HideWhenDisabled); mSelectedThemeContentItem->setSoftenByBlendingWhenDisabled(flags == Theme::ContentItem::SoftenByBlendingWhenDisabled); setTheme(mTheme); // this will reset theme cache and trigger a global update } void ThemePreviewWidget::slotForegroundColorMenuTriggered(QAction *act) { if (!mSelectedThemeContentItem) { return; } bool ok; const int flag = act->data().toInt(&ok); if (!ok) { return; } if (flag == 0) { mSelectedThemeContentItem->setUseCustomColor(false); setTheme(mTheme); // this will reset theme cache and trigger a global update return; } QColor clr; clr = QColorDialog::getColor(mSelectedThemeContentItem->customColor(), this); if (!clr.isValid()) { return; } mSelectedThemeContentItem->setCustomColor(clr); mSelectedThemeContentItem->setUseCustomColor(true); setTheme(mTheme); // this will reset theme cache and trigger a global update } void ThemePreviewWidget::slotSoftenActionTriggered(bool) { if (!mSelectedThemeContentItem) { return; } mSelectedThemeContentItem->setSoftenByBlending(!mSelectedThemeContentItem->softenByBlending()); setTheme(mTheme); // this will reset theme cache and trigger a global update } void ThemePreviewWidget::slotFontMenuTriggered(QAction *act) { if (!mSelectedThemeContentItem) { return; } bool ok; const int flag = act->data().toInt(&ok); if (!ok) { return; } if (flag == Theme::ContentItem::IsBold && mSelectedThemeContentItem->isBold() != act->isChecked()) { mSelectedThemeContentItem->setBold(act->isChecked()); setTheme(mTheme); } else if (flag == Theme::ContentItem::IsItalic && mSelectedThemeContentItem->isItalic() != act->isChecked()) { mSelectedThemeContentItem->setItalic(act->isChecked()); setTheme(mTheme); } } void ThemePreviewWidget::slotGroupHeaderBackgroundModeMenuTriggered(QAction *act) { bool ok; Theme::GroupHeaderBackgroundMode mode = static_cast< Theme::GroupHeaderBackgroundMode >(act->data().toInt(&ok)); if (!ok) { return; } switch (mode) { case Theme::Transparent: mTheme->setGroupHeaderBackgroundMode(Theme::Transparent); break; case Theme::AutoColor: mTheme->setGroupHeaderBackgroundMode(Theme::AutoColor); break; case Theme::CustomColor: { QColor clr; clr = QColorDialog::getColor(mTheme->groupHeaderBackgroundColor(), this); if (!clr.isValid()) { return; } mTheme->setGroupHeaderBackgroundMode(Theme::CustomColor); mTheme->setGroupHeaderBackgroundColor(clr); break; } } setTheme(mTheme); // this will reset theme cache and trigger a global update } void ThemePreviewWidget::slotGroupHeaderBackgroundStyleMenuTriggered(QAction *act) { bool ok; Theme::GroupHeaderBackgroundStyle mode = static_cast< Theme::GroupHeaderBackgroundStyle >(act->data().toInt(&ok)); if (!ok) { return; } mTheme->setGroupHeaderBackgroundStyle(mode); setTheme(mTheme); // this will reset theme cache and trigger a global update } void ThemePreviewWidget::paintEvent(QPaintEvent *e) { QTreeWidget::paintEvent(e); if ( mThemeSelectedContentItemRect.isValid() || (mDropIndicatorPoint1 != mDropIndicatorPoint2) ) { QPainter painter(viewport()); if (mThemeSelectedContentItemRect.isValid()) { painter.setPen(QPen(Qt::black)); painter.drawRect(mThemeSelectedContentItemRect); } if (mDropIndicatorPoint1 != mDropIndicatorPoint2) { painter.setPen(QPen(Qt::black, 3)); painter.drawLine(mDropIndicatorPoint1, mDropIndicatorPoint2); } } } void ThemePreviewWidget::slotHeaderContextMenuRequested(const QPoint &pos) { if (mReadOnly) { return; } QTreeWidgetItem *hitem = headerItem(); if (!hitem) { return; // ooops } int col = header()->logicalIndexAt(pos); if (col < 0) { return; } if (col >= mTheme->columns().count()) { return; } mSelectedThemeColumn = mTheme->column(col); if (!mSelectedThemeColumn) { return; } QMenu menu; menu.setTitle(mSelectedThemeColumn->label()); QAction *act = menu.addAction(i18n("Column Properties...")); connect(act, &QAction::triggered, this, &ThemePreviewWidget::slotColumnProperties); act = menu.addAction(i18n("Add Column...")); connect(act, &QAction::triggered, this, &ThemePreviewWidget::slotAddColumn); act = menu.addAction(i18n("Delete Column")); connect(act, &QAction::triggered, this, &ThemePreviewWidget::slotDeleteColumn); act->setEnabled(col > 0); menu.addSeparator(); act = menu.addAction(i18n("Move Column to Left")); connect(act, &QAction::triggered, this, &ThemePreviewWidget::slotMoveColumnToLeft); act->setEnabled(col > 0); act = menu.addAction(i18n("Move Column to Right")); connect(act, &QAction::triggered, this, &ThemePreviewWidget::slotMoveColumnToRight); act->setEnabled(col < mTheme->columns().count() - 1); menu.exec(header()->mapToGlobal(pos)); } void ThemePreviewWidget::slotMoveColumnToLeft() { if (!mSelectedThemeColumn) { return; } const int columnIndex = mTheme->columns().indexOf(mSelectedThemeColumn); mTheme->moveColumn(columnIndex, columnIndex - 1); setTheme(mTheme); // this will reset theme cache and trigger a global update } void ThemePreviewWidget::slotMoveColumnToRight() { if (!mSelectedThemeColumn) { return; } const int columnIndex = mTheme->columns().indexOf(mSelectedThemeColumn); mTheme->moveColumn(columnIndex, columnIndex + 1); setTheme(mTheme); // this will reset theme cache and trigger a global update } void ThemePreviewWidget::slotAddColumn() { int newColumnIndex = mTheme->columns().count(); if (mSelectedThemeColumn) { newColumnIndex = mTheme->columns().indexOf(mSelectedThemeColumn); if (newColumnIndex < 0) { newColumnIndex = mTheme->columns().count(); } else { newColumnIndex++; } } mSelectedThemeColumn = new Theme::Column(); mSelectedThemeColumn->setLabel(i18n("New Column")); mSelectedThemeColumn->setVisibleByDefault(true); mSelectedThemeColumn->addMessageRow(new Theme::Row()); mSelectedThemeColumn->addGroupHeaderRow(new Theme::Row()); ThemeColumnPropertiesDialog *dlg = new ThemeColumnPropertiesDialog(this, mSelectedThemeColumn, i18n("Add New Column")); if (dlg->exec() == QDialog::Accepted) { mTheme->insertColumn(newColumnIndex, mSelectedThemeColumn); mSelectedThemeContentItem = nullptr; mThemeSelectedContentItemRect = QRect(); mDropIndicatorPoint1 = mDropIndicatorPoint2; setTheme(mTheme); // this will reset theme cache and trigger a global update } else { delete mSelectedThemeColumn; mSelectedThemeColumn = nullptr; } delete dlg; } void ThemePreviewWidget::slotColumnProperties() { if (!mSelectedThemeColumn) { return; } ThemeColumnPropertiesDialog *dlg = new ThemeColumnPropertiesDialog(this, mSelectedThemeColumn, i18n("Column Properties")); if (dlg->exec() == QDialog::Accepted) { mSelectedThemeContentItem = nullptr; mThemeSelectedContentItemRect = QRect(); mDropIndicatorPoint1 = mDropIndicatorPoint2; setTheme(mTheme); // this will reset theme cache and trigger a global update } delete dlg; } void ThemePreviewWidget::slotDeleteColumn() { if (!mSelectedThemeColumn) { return; } const int idx = mTheme->columns().indexOf(mSelectedThemeColumn); if (idx < 1) { // first column can't be deleted return; } mTheme->removeColumn(mSelectedThemeColumn); delete mSelectedThemeColumn; mSelectedThemeColumn = nullptr; mSelectedThemeContentItem = nullptr; mThemeSelectedContentItemRect = QRect(); mDropIndicatorPoint1 = mDropIndicatorPoint2; setTheme(mTheme); // this will reset theme cache and trigger a global update } ThemeEditor::ThemeEditor(QWidget *parent) : OptionSetEditor(parent) { mCurrentTheme = nullptr; // Appearance tab QWidget *tab = new QWidget(this); addTab(tab, i18n("Appearance")); QGridLayout *tabg = new QGridLayout(tab); QGroupBox *gb = new QGroupBox(i18n("Content Items"), tab); tabg->addWidget(gb, 0, 0); QGridLayout *gblayout = new QGridLayout(gb); Theme dummyTheme; ThemeContentItemSourceLabel *cil = new ThemeContentItemSourceLabel(gb, Theme::ContentItem::Subject); cil->setText(Theme::ContentItem::description(cil->type())); cil->setToolTip(Theme::ContentItem::description(cil->type())); gblayout->addWidget(cil, 0, 0); cil = new ThemeContentItemSourceLabel(gb, Theme::ContentItem::Date); cil->setText(Theme::ContentItem::description(cil->type())); cil->setToolTip(Theme::ContentItem::description(cil->type())); gblayout->addWidget(cil, 1, 0); cil = new ThemeContentItemSourceLabel(gb, Theme::ContentItem::Size); cil->setText(Theme::ContentItem::description(cil->type())); cil->setToolTip(Theme::ContentItem::description(cil->type())); gblayout->addWidget(cil, 2, 0); cil = new ThemeContentItemSourceLabel(gb, Theme::ContentItem::Sender); cil->setText(Theme::ContentItem::description(cil->type())); cil->setToolTip(Theme::ContentItem::description(cil->type())); gblayout->addWidget(cil, 0, 1); cil = new ThemeContentItemSourceLabel(gb, Theme::ContentItem::Receiver); cil->setText(Theme::ContentItem::description(cil->type())); cil->setToolTip(Theme::ContentItem::description(cil->type())); gblayout->addWidget(cil, 1, 1); cil = new ThemeContentItemSourceLabel(gb, Theme::ContentItem::SenderOrReceiver); cil->setText(Theme::ContentItem::description(cil->type())); cil->setToolTip(Theme::ContentItem::description(cil->type())); gblayout->addWidget(cil, 2, 1); cil = new ThemeContentItemSourceLabel(gb, Theme::ContentItem::MostRecentDate); cil->setText(Theme::ContentItem::description(cil->type())); cil->setToolTip(Theme::ContentItem::description(cil->type())); gblayout->addWidget(cil, 0, 2); cil = new ThemeContentItemSourceLabel(gb, Theme::ContentItem::TagList); cil->setText(Theme::ContentItem::description(cil->type())); cil->setToolTip(Theme::ContentItem::description(cil->type())); gblayout->addWidget(cil, 1, 2); cil = new ThemeContentItemSourceLabel(gb, Theme::ContentItem::CombinedReadRepliedStateIcon); cil->setPixmap(*dummyTheme.pixmap(Theme::IconRepliedAndForwarded)); cil->setToolTip(Theme::ContentItem::description(cil->type())); gblayout->addWidget(cil, 0, 3); cil = new ThemeContentItemSourceLabel(gb, Theme::ContentItem::ReadStateIcon); cil->setPixmap(*dummyTheme.pixmap(Theme::IconNew)); cil->setToolTip(Theme::ContentItem::description(cil->type())); gblayout->addWidget(cil, 1, 3); cil = new ThemeContentItemSourceLabel(gb, Theme::ContentItem::RepliedStateIcon); cil->setPixmap(*dummyTheme.pixmap(Theme::IconReplied)); cil->setToolTip(Theme::ContentItem::description(cil->type())); gblayout->addWidget(cil, 2, 3); cil = new ThemeContentItemSourceLabel(gb, Theme::ContentItem::AttachmentStateIcon); cil->setPixmap(*dummyTheme.pixmap(Theme::IconAttachment)); cil->setToolTip(Theme::ContentItem::description(cil->type())); gblayout->addWidget(cil, 0, 4); cil = new ThemeContentItemSourceLabel(gb, Theme::ContentItem::EncryptionStateIcon); cil->setPixmap(*dummyTheme.pixmap(Theme::IconFullyEncrypted)); cil->setToolTip(Theme::ContentItem::description(cil->type())); gblayout->addWidget(cil, 1, 4); cil = new ThemeContentItemSourceLabel(gb, Theme::ContentItem::SignatureStateIcon); cil->setPixmap(*dummyTheme.pixmap(Theme::IconFullySigned)); cil->setToolTip(Theme::ContentItem::description(cil->type())); gblayout->addWidget(cil, 2, 4); cil = new ThemeContentItemSourceLabel(gb, Theme::ContentItem::ActionItemStateIcon); cil->setPixmap(*dummyTheme.pixmap(Theme::IconActionItem)); cil->setToolTip(Theme::ContentItem::description(cil->type())); gblayout->addWidget(cil, 0, 5); cil = new ThemeContentItemSourceLabel(gb, Theme::ContentItem::AnnotationIcon); cil->setPixmap(*dummyTheme.pixmap(Theme::IconAnnotation)); cil->setToolTip(Theme::ContentItem::description(cil->type())); gblayout->addWidget(cil, 1, 5); cil = new ThemeContentItemSourceLabel(gb, Theme::ContentItem::InvitationIcon); cil->setPixmap(*dummyTheme.pixmap(Theme::IconInvitation)); cil->setToolTip(Theme::ContentItem::description(cil->type())); gblayout->addWidget(cil, 2, 5); cil = new ThemeContentItemSourceLabel(gb, Theme::ContentItem::ImportantStateIcon); cil->setPixmap(*dummyTheme.pixmap(Theme::IconImportant)); cil->setToolTip(Theme::ContentItem::description(cil->type())); gblayout->addWidget(cil, 0, 6); cil = new ThemeContentItemSourceLabel(gb, Theme::ContentItem::SpamHamStateIcon); cil->setPixmap(*dummyTheme.pixmap(Theme::IconSpam)); cil->setToolTip(Theme::ContentItem::description(cil->type())); gblayout->addWidget(cil, 1, 6); cil = new ThemeContentItemSourceLabel(gb, Theme::ContentItem::WatchedIgnoredStateIcon); cil->setPixmap(*dummyTheme.pixmap(Theme::IconWatched)); cil->setToolTip(Theme::ContentItem::description(cil->type())); gblayout->addWidget(cil, 2, 6); cil = new ThemeContentItemSourceLabel(gb, Theme::ContentItem::ExpandedStateIcon); cil->setPixmap(*dummyTheme.pixmap(Theme::IconShowMore)); cil->setToolTip(Theme::ContentItem::description(cil->type())); gblayout->addWidget(cil, 0, 7); cil = new ThemeContentItemSourceLabel(gb, Theme::ContentItem::VerticalLine); cil->setPixmap(*dummyTheme.pixmap(Theme::IconVerticalLine)); cil->setToolTip(Theme::ContentItem::description(cil->type())); gblayout->addWidget(cil, 1, 7); cil = new ThemeContentItemSourceLabel(gb, Theme::ContentItem::HorizontalSpacer); cil->setPixmap(*dummyTheme.pixmap(Theme::IconHorizontalSpacer)); cil->setToolTip(Theme::ContentItem::description(cil->type())); gblayout->addWidget(cil, 2, 7); mPreviewWidget = new ThemePreviewWidget(tab); tabg->addWidget(mPreviewWidget, 1, 0); QLabel *l = new QLabel(tab); l->setText(i18n( "Right click on the header to add or modify columns. Drag the content items and drop them on the columns in order to compose your theme. Right click on the items inside the view for more options.")); l->setWordWrap(true); l->setAlignment(Qt::AlignCenter); tabg->addWidget(l, 2, 0); tabg->setRowStretch(1, 1); // Advanced tab tab = new QWidget(this); addTab(tab, i18nc("@title:tab Advanced theme settings", "Advanced")); tabg = new QGridLayout(tab); l = new QLabel(i18n("Header:"), tab); tabg->addWidget(l, 0, 0); mViewHeaderPolicyCombo = new KComboBox(tab); tabg->addWidget(mViewHeaderPolicyCombo, 0, 1); l = new QLabel(i18n("Icon size:"), tab); tabg->addWidget(l, 1, 0); mIconSizeSpinBox = new KPluralHandlingSpinBox(tab); mIconSizeSpinBox->setMinimum(8); mIconSizeSpinBox->setMaximum(64); mIconSizeSpinBox->setSuffix(ki18ncp("suffix in a spinbox", " pixel", " pixels")); - QObject::connect(mIconSizeSpinBox, static_cast(&KPluralHandlingSpinBox::valueChanged), this, &ThemeEditor::slotIconSizeSpinBoxValueChanged); + QObject::connect(mIconSizeSpinBox, QOverload::of(&KPluralHandlingSpinBox::valueChanged), this, &ThemeEditor::slotIconSizeSpinBoxValueChanged); tabg->addWidget(mIconSizeSpinBox, 1, 1); tabg->setColumnStretch(1, 1); tabg->setRowStretch(2, 1); fillViewHeaderPolicyCombo(); } ThemeEditor::~ThemeEditor() { } void ThemeEditor::editTheme(Theme *set) { mCurrentTheme = set; mPreviewWidget->setTheme(mCurrentTheme); if (!mCurrentTheme) { setEnabled(false); return; } setEnabled(true); nameEdit()->setText(set->name()); descriptionEdit()->setPlainText(set->description()); ComboBoxUtils::setIntegerOptionComboValue(mViewHeaderPolicyCombo, (int)mCurrentTheme->viewHeaderPolicy()); mIconSizeSpinBox->setValue(set->iconSize()); setReadOnly(mCurrentTheme->readOnly()); } void ThemeEditor::setReadOnly(bool readOnly) { mPreviewWidget->setReadOnly(readOnly); mViewHeaderPolicyCombo->setEnabled(!readOnly); mIconSizeSpinBox->setEnabled(!readOnly); OptionSetEditor::setReadOnly(readOnly); } void ThemeEditor::commit() { if (!mCurrentTheme || mCurrentTheme->readOnly()) { return; } mCurrentTheme->setName(nameEdit()->text()); mCurrentTheme->setDescription(descriptionEdit()->toPlainText()); mCurrentTheme->setViewHeaderPolicy( (Theme::ViewHeaderPolicy)ComboBoxUtils::getIntegerOptionComboValue(mViewHeaderPolicyCombo, 0) ); mCurrentTheme->setIconSize(mIconSizeSpinBox->value()); // other settings are already committed to this theme } void ThemeEditor::fillViewHeaderPolicyCombo() { ComboBoxUtils::fillIntegerOptionCombo( mViewHeaderPolicyCombo, Theme::enumerateViewHeaderPolicyOptions() ); } void ThemeEditor::slotNameEditTextEdited(const QString &newName) { if (!mCurrentTheme) { return; } mCurrentTheme->setName(newName); Q_EMIT themeNameChanged(); } void ThemeEditor::slotIconSizeSpinBoxValueChanged(int val) { if (!mCurrentTheme) { return; } mCurrentTheme->setIconSize(val); mPreviewWidget->setTheme(mCurrentTheme); // will trigger a cache reset and a view update } MessageList::Core::Theme *ThemeEditor::editedTheme() const { return mCurrentTheme; } diff --git a/messageviewer/src/scamdetection/scamexpandurljob.cpp b/messageviewer/src/scamdetection/scamexpandurljob.cpp index fd606305..9b17037d 100644 --- a/messageviewer/src/scamdetection/scamexpandurljob.cpp +++ b/messageviewer/src/scamdetection/scamexpandurljob.cpp @@ -1,113 +1,112 @@ /* Copyright (c) 2016-2018 Montel Laurent This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "scamexpandurljob.h" #include "messageviewer_debug.h" #include #include #include #include #include #include #include using namespace MessageViewer; class MessageViewer::ScamExpandUrlJobPrivate { public: ScamExpandUrlJobPrivate() { } ~ScamExpandUrlJobPrivate() { } QNetworkAccessManager *mNetworkAccessManager = nullptr; }; ScamExpandUrlJob::ScamExpandUrlJob(QObject *parent) : QObject(parent) , d(new ScamExpandUrlJobPrivate) { d->mNetworkAccessManager = new QNetworkAccessManager(this); connect(d->mNetworkAccessManager, &QNetworkAccessManager::finished, this, &ScamExpandUrlJob::slotExpandFinished); } ScamExpandUrlJob::~ScamExpandUrlJob() { delete d; } void ScamExpandUrlJob::expandedUrl(const QUrl &url) { if (!PimCommon::NetworkManager::self()->networkConfigureManager()->isOnline()) { KPIM::BroadcastStatus::instance()->setStatusMsg(i18n( "No network connection detected, we cannot expand url.")); deleteLater(); return; } const QUrl newUrl(QStringLiteral("http://api.longurl.org/v2/expand?url=%1&format=json").arg( url.url())); qCDebug(MESSAGEVIEWER_LOG) << " newUrl " << newUrl; QNetworkReply *reply = d->mNetworkAccessManager->get(QNetworkRequest(newUrl)); reply->setProperty("shortUrl", url.url()); connect(reply, - static_cast(&QNetworkReply::error), this, + QOverload::of(&QNetworkReply::error), this, &ScamExpandUrlJob::slotError); } void ScamExpandUrlJob::slotExpandFinished(QNetworkReply *reply) { QUrl shortUrl; if (!reply->property("shortUrl").isNull()) { shortUrl.setUrl(reply->property("shortUrl").toString()); } QJsonDocument jsonDoc = QJsonDocument::fromBinaryData(reply->readAll()); reply->deleteLater(); if (!jsonDoc.isNull()) { const QMap map = jsonDoc.toVariant().toMap(); QUrl longUrl; const QVariant longUrlVar = map.value(QStringLiteral("long-url")); if (longUrlVar.isValid()) { longUrl.setUrl(longUrlVar.toString()); } else { deleteLater(); return; } KPIM::BroadcastStatus::instance()->setStatusMsg(i18n("Short url \'%1\' redirects to \'%2\'.", shortUrl.url(), longUrl.toDisplayString())); } deleteLater(); } void ScamExpandUrlJob::slotError(QNetworkReply::NetworkError error) { Q_EMIT expandUrlError(error); deleteLater(); } diff --git a/messageviewer/src/viewer/editorwatcher.cpp b/messageviewer/src/viewer/editorwatcher.cpp index 92b86a33..0fccd793 100644 --- a/messageviewer/src/viewer/editorwatcher.cpp +++ b/messageviewer/src/viewer/editorwatcher.cpp @@ -1,231 +1,230 @@ /* Copyright (c) 2007 Volker Krause This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include #include "editorwatcher.h" #include "messageviewer_debug.h" #include #include #include #include #include #include #include #include #include // inotify stuff taken from kdelibs/kio/kio/kdirwatch.cpp #ifdef HAVE_SYS_INOTIFY_H #include #include #include #include #endif using namespace MessageViewer; EditorWatcher::EditorWatcher(const QUrl &url, const QString &mimeType, OpenWithOption option, QObject *parent, QWidget *parentWidget) : QObject(parent) , mUrl(url) , mMimeType(mimeType) , mEditor(nullptr) , mParentWidget(parentWidget) , mInotifyFd(-1) , mInotifyWatch(-1) , mOpenWithOption(option) , mHaveInotify(false) , mFileOpen(false) , mEditorRunning(false) , mFileModified(true) , // assume the worst unless we know better mDone(false) { assert(mUrl.isLocalFile()); mTimer.setSingleShot(true); connect(&mTimer, &QTimer::timeout, this, &EditorWatcher::checkEditDone); } EditorWatcher::~EditorWatcher() { #ifdef HAVE_SYS_INOTIFY_H ::close(mInotifyFd); #endif } EditorWatcher::ErrorEditorWatcher EditorWatcher::start() { // find an editor QList list; list.append(mUrl); KService::Ptr offer = KMimeTypeTrader::self()->preferredService(mMimeType, QStringLiteral("Application")); if ((mOpenWithOption == OpenWithDialog) || !offer) { std::unique_ptr dlg(new KOpenWithDialog(list, i18n("Edit with:"), QString(), mParentWidget)); const int dlgrc = dlg->exec(); if (dlgrc && dlg) { offer = dlg->service(); } if (!dlgrc) { return Canceled; } if (!offer) { return NoServiceFound; } } #ifdef HAVE_SYS_INOTIFY_H // monitor file mInotifyFd = inotify_init(); if (mInotifyFd > 0) { (void)fcntl(mInotifyFd, F_SETFD, FD_CLOEXEC); mInotifyWatch = inotify_add_watch(mInotifyFd, mUrl.path().toLatin1().constData(), IN_CLOSE | IN_OPEN | IN_MODIFY | IN_ATTRIB); if (mInotifyWatch >= 0) { QSocketNotifier *sn = new QSocketNotifier(mInotifyFd, QSocketNotifier::Read, this); connect(sn, &QSocketNotifier::activated, this, &EditorWatcher::inotifyEvent); mHaveInotify = true; mFileModified = false; } } else { qCWarning(MESSAGEVIEWER_LOG) << "Failed to activate INOTIFY!"; } #endif // start the editor KIO::DesktopExecParser parser(*offer, list); parser.setUrlsAreTempFiles(false); const QStringList params = parser.resultingArguments(); mEditor = new KProcess(this); mEditor->setProgram(params); - connect(mEditor, static_cast(&KProcess::finished), + connect(mEditor, QOverload::of(&KProcess::finished), this, &EditorWatcher::editorExited); mEditor->start(); if (!mEditor->waitForStarted()) { return CannotStart; } mEditorRunning = true; mEditTime.start(); return NoError; } bool EditorWatcher::fileChanged() const { return mFileModified; } QUrl EditorWatcher::url() const { return mUrl; } void EditorWatcher::inotifyEvent() { assert(mHaveInotify); #ifdef HAVE_SYS_INOTIFY_H int pending = -1; int offsetStartRead = 0; // where we read into buffer char buf[8192]; assert(mInotifyFd > -1); ioctl(mInotifyFd, FIONREAD, &pending); while (pending > 0) { const int bytesToRead = qMin(pending, (int)sizeof(buf) - offsetStartRead); int bytesAvailable = read(mInotifyFd, &buf[offsetStartRead], bytesToRead); pending -= bytesAvailable; bytesAvailable += offsetStartRead; offsetStartRead = 0; int offsetCurrent = 0; while (bytesAvailable >= (int)sizeof(struct inotify_event)) { const struct inotify_event *const event = (struct inotify_event *)&buf[offsetCurrent]; const int eventSize = sizeof(struct inotify_event) + event->len; if (bytesAvailable < eventSize) { break; } bytesAvailable -= eventSize; offsetCurrent += eventSize; if (event->mask & IN_OPEN) { mFileOpen = true; } if (event->mask & IN_CLOSE) { mFileOpen = false; } if (event->mask & (IN_MODIFY | IN_ATTRIB)) { mFileModified = true; } } if (bytesAvailable > 0) { // copy partial event to beginning of buffer memmove(buf, &buf[offsetCurrent], bytesAvailable); offsetStartRead = bytesAvailable; } } #endif mTimer.start(500); } void EditorWatcher::editorExited() { mEditorRunning = false; mTimer.start(500); } void EditorWatcher::checkEditDone() { if (mEditorRunning || (mFileOpen && mHaveInotify) || mDone) { return; } static QStringList readOnlyMimeTypes; if (readOnlyMimeTypes.isEmpty()) { readOnlyMimeTypes << QStringLiteral("message/rfc822") << QStringLiteral("application/pdf"); } // protect us against double-deletion by calling this method again while // the subeventloop of the message box is running mDone = true; // check if it's a mime type that's mostly handled read-only const bool isReadOnlyMimeType = (readOnlyMimeTypes.contains(mMimeType) || mMimeType.startsWith(QLatin1String("image/"))); // nobody can edit that fast, we seem to be unable to detect // when the editor will be closed if (mEditTime.elapsed() <= 3000 && !isReadOnlyMimeType) { KMessageBox::information(mParentWidget, i18n("KMail is unable to detect when the chosen editor is closed. " "To avoid data loss, editing the attachment will be aborted."), i18n("Unable to edit attachment"), QStringLiteral("UnableToEditAttachment")); } Q_EMIT editDone(this); deleteLater(); } diff --git a/messageviewer/src/widgets/configurewidget.cpp b/messageviewer/src/widgets/configurewidget.cpp index d58f8a46..1ababa31 100644 --- a/messageviewer/src/widgets/configurewidget.cpp +++ b/messageviewer/src/widgets/configurewidget.cpp @@ -1,124 +1,122 @@ /* Copyright (C) 2009 Klarälvdalens Datakonsult AB, a KDAB Group company, info@kdab.net Copyright (c) 2009 Andras Mantia This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "configurewidget.h" #include "messageviewer_debug.h" #include "ui_settings.h" #include "utils/messageviewerutil.h" #include "settings/messageviewersettings.h" #include #include "MessageCore/MessageCoreSettings" #include using namespace MessageViewer; class MessageViewer::ConfigureWidgetPrivate { public: ConfigureWidgetPrivate() { } ~ConfigureWidgetPrivate() { delete mSettingsUi; mSettingsUi = nullptr; } Ui_Settings *mSettingsUi = nullptr; }; ConfigureWidget::ConfigureWidget(QWidget *parent) : QWidget(parent) , d(new MessageViewer::ConfigureWidgetPrivate) { d->mSettingsUi = new Ui_Settings; d->mSettingsUi->setupUi(this); layout()->setContentsMargins(0, 0, 0, 0); QStringList encodings = MimeTreeParser::NodeHelper::supportedEncodings(false); encodings.prepend(i18n("Auto")); d->mSettingsUi->overrideCharacterEncoding->addItems(encodings); d->mSettingsUi->overrideCharacterEncoding->setCurrentIndex(0); d->mSettingsUi->overrideCharacterEncoding->setWhatsThis( MessageCore::MessageCoreSettings::self()->overrideCharacterEncodingItem()->whatsThis()); d->mSettingsUi->kcfg_ShrinkQuotes->setWhatsThis( MessageViewer::MessageViewerSettings::self()->shrinkQuotesItem()->whatsThis()); d->mSettingsUi->kcfg_ShowExpandQuotesMark->setWhatsThis( MessageViewer::MessageViewerSettings::self()->showExpandQuotesMarkItem()->whatsThis()); - connect(d->mSettingsUi->overrideCharacterEncoding, - static_cast(&KComboBox::currentIndexChanged), this, + connect(d->mSettingsUi->overrideCharacterEncoding, QOverload::of(&KComboBox::currentIndexChanged), this, &ConfigureWidget::settingsChanged); } ConfigureWidget::~ConfigureWidget() { delete d; } void ConfigureWidget::readConfig() { readCurrentOverrideCodec(); d->mSettingsUi->kcfg_CollapseQuoteLevelSpin->setEnabled( MessageViewer::MessageViewerSettings::self()->showExpandQuotesMark()); } void ConfigureWidget::writeConfig() { MessageCore::MessageCoreSettings::self()->setOverrideCharacterEncoding( d->mSettingsUi->overrideCharacterEncoding->currentIndex() == 0 ? QString() : MimeTreeParser::NodeHelper::encodingForName(d->mSettingsUi->overrideCharacterEncoding-> currentText())); } void ConfigureWidget::readCurrentOverrideCodec() { const QString ¤tOverrideEncoding = MessageCore::MessageCoreSettings::self()->overrideCharacterEncoding(); if (currentOverrideEncoding.isEmpty()) { d->mSettingsUi->overrideCharacterEncoding->setCurrentIndex(0); return; } QStringList encodings = MimeTreeParser::NodeHelper::supportedEncodings(false); encodings.prepend(i18n("Auto")); QStringList::ConstIterator it(encodings.constBegin()); const QStringList::ConstIterator end(encodings.constEnd()); int i = 0; for (; it != end; ++it) { if (MimeTreeParser::NodeHelper::encodingForName(*it) == currentOverrideEncoding) { d->mSettingsUi->overrideCharacterEncoding->setCurrentIndex(i); break; } ++i; } if (i == encodings.size()) { // the current value of overrideCharacterEncoding is an unknown encoding => reset to Auto qCWarning(MESSAGEVIEWER_LOG) << "Unknown override character encoding" << currentOverrideEncoding << ". Resetting to Auto."; d->mSettingsUi->overrideCharacterEncoding->setCurrentIndex(0); MessageCore::MessageCoreSettings::self()->setOverrideCharacterEncoding(QString()); } }