diff --git a/CMakeLists.txt b/CMakeLists.txt index 9460d72..f855b40 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -125,7 +125,8 @@ set(WIDGETS_UI_FILES gui/editortempoptionpage.ui gui/filelayout.ui gui/tageditorwidget.ui - gui/dbquerywidget.ui) + gui/dbquerywidget.ui + gui/imageconversiondialog.ui) set(TEST_HEADER_FILES) set(TEST_SRC_FILES tests/cli.cpp) @@ -187,7 +188,8 @@ set(REQUIRED_ICONS tag-delete system-file-manager document-save - view-media-lyrics) + view-media-lyrics + image-resize-symbolic) # find c++utilities set(CONFIGURATION_PACKAGE_SUFFIX diff --git a/gui/imageconversiondialog.ui b/gui/imageconversiondialog.ui new file mode 100644 index 0000000..47efd12 --- /dev/null +++ b/gui/imageconversiondialog.ui @@ -0,0 +1,174 @@ + + + QtGui::ImageConversionDialog + + + Convert image + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + + + + Size + + + + + + + + 0 + + + 0 + + + 0 + + + 0 + + + + + true + + + px + + + 1 + + + 2048 + + + 512 + + + + + + + x + + + true + + + + + + + px + + + 1 + + + 2048 + + + 512 + + + + + + + + + + Format + + + + + + + + + + Aspect ratio + + + + + + + + + + + + + + 0 + 0 + + + + + + + Qt::Horizontal + + + + 168 + 20 + + + + + + + + background: none; + + + Abort + + + + .. + + + + + + + background: none; + + + Confirm + + + + .. + + + + + + + + + + + diff --git a/gui/picturepreviewselection.cpp b/gui/picturepreviewselection.cpp index 0f2a9cd..537caa2 100644 --- a/gui/picturepreviewselection.cpp +++ b/gui/picturepreviewselection.cpp @@ -3,6 +3,7 @@ #include "../application/settings.h" #include "../misc/utility.h" +#include "ui_imageconversiondialog.h" #include "ui_picturepreviewselection.h" #include @@ -14,6 +15,7 @@ #include #include +#include #include #include @@ -59,6 +61,7 @@ namespace QtGui { PicturePreviewSelection::PicturePreviewSelection(Tag *tag, KnownField field, QWidget *parent) : QWidget(parent) , m_ui(new Ui::PicturePreviewSelection) + , m_imageConversionDialog(nullptr) , m_scene(nullptr) , m_textItem(nullptr) , m_pixmapItem(nullptr) @@ -249,6 +252,71 @@ void PicturePreviewSelection::updateSizeAndMimeType(size_t fileSize, const QSize setToolTip(info.join(QChar('\n'))); } +/*! + * \brief Assigns the specified \a image to the specified \a tagValue using the specified \a format. + * \remarks Shows a message box if an error occurs and returns a "null" QImage. + */ +QImage PicturePreviewSelection::convertTagValueToImage(const TagValue &value) +{ + QImage img; + if (value.mimeType() == "-->") { + const auto fileName(Utility::stringToQString(value.toString(), value.dataEncoding())); + QFile file(fileName); + if (file.open(QFile::ReadOnly)) { + img = QImage::fromData(file.readAll()); + } else { + QMessageBox::warning(this, QCoreApplication::applicationName(), + tr("The attached image can't be found. It is supposed to be stored as external file \"%1\".").arg(fileName)); + return img; + } + } else if (value.dataSize() < numeric_limits::max()) { + img = QImage::fromData(reinterpret_cast(value.dataPointer()), static_cast(value.dataSize())); + } + if (img.isNull()) { + QMessageBox::warning(this, QCoreApplication::applicationName(), tr("The attached image format is not supported.")); + } + return img; +} + +/*! + * \brief Assigns the specified \a image to the specified \a tagValue using the specified \a format. + * \remarks Shows a message box if an error occurs. + */ +void PicturePreviewSelection::assignImageToTagValue(const QImage &image, TagValue &tagValue, const char *format) +{ + // set default MIME type + QString mimeType; + if (strcmp(format, "JPEG") == 0) { + mimeType = QStringLiteral("image/jpeg"); + } else if (strcmp(format, "PNG") == 0) { + mimeType = QStringLiteral("image/png"); + } + + // save image to buffer + QByteArray imageData; + QBuffer buffer(&imageData); + buffer.open(QIODevice::WriteOnly); + if (!image.save(&buffer, format)) { + QMessageBox::critical(this, QCoreApplication::applicationName(), tr("Unable to save image from clipboard.")); + return; + } + + // ask for MIME type + if (mimeType.isEmpty()) { + bool ok; + mimeType + = QInputDialog::getText(this, tr("Enter MIME type"), tr("Enter the MIME type for the pasted image."), QLineEdit::Normal, mimeType, &ok); + if (!ok) { + return; + } + } + + // assign image + const auto mimeTypeUtf8(mimeType.toUtf8()); + tagValue.assignData(imageData.data(), static_cast(imageData.size()), TagDataType::Picture); + tagValue.setMimeType(mimeTypeUtf8.constData()); +} + /*! * \brief Pushes the ID3v2 cover values to the specified \a tag. * \param tag Specifies a tag to push the values to. @@ -405,39 +473,9 @@ void PicturePreviewSelection::pasteOfSelectedType(const char *format) return; } - // set default MIME type - QString mimeType; - if (strcmp(format, "JPEG") == 0) { - mimeType = QStringLiteral("image/jpeg"); - } else if (strcmp(format, "PNG") == 0) { - mimeType = QStringLiteral("image/png"); - } - - // save image to buffer - QByteArray imageData; - QBuffer buffer(&imageData); - buffer.open(QIODevice::WriteOnly); - if (!image.save(&buffer, format)) { - QMessageBox::critical(this, QCoreApplication::applicationName(), tr("Unable to save image from clipboard.")); - return; - } - - // ask for MIME type - if (mimeType.isEmpty()) { - bool ok; - mimeType - = QInputDialog::getText(this, tr("Enter MIME type"), tr("Enter the MIME type for the pasted image."), QLineEdit::Normal, mimeType, &ok); - if (!ok) { - return; - } - } - // assign image assert(m_currentTypeIndex < m_values.size()); - TagValue &selectedCover = m_values[m_currentTypeIndex]; - const auto mimeTypeUtf8(mimeType.toUtf8()); - selectedCover.assignData(imageData.data(), static_cast(imageData.size()), TagDataType::Picture); - selectedCover.setMimeType(mimeTypeUtf8.constData()); + assignImageToTagValue(image, m_values[m_currentTypeIndex], format); updatePreview(m_currentTypeIndex); } @@ -503,22 +541,8 @@ void PicturePreviewSelection::displaySelected() } // load image - QImage img; - if (value.mimeType() == "-->") { - const auto fileName(Utility::stringToQString(value.toString(), value.dataEncoding())); - QFile file(fileName); - if (file.open(QFile::ReadOnly)) { - img = QImage::fromData(file.readAll()); - } else { - QMessageBox::warning(this, QCoreApplication::applicationName(), - tr("The attached image can't be found. It is supposed to be stored as external file \"%1\".").arg(fileName)); - return; - } - } else if (value.dataSize() < numeric_limits::max()) { - img = QImage::fromData(reinterpret_cast(value.dataPointer()), static_cast(value.dataSize())); - } + const auto img = convertTagValueToImage(value); if (img.isNull()) { - QMessageBox::warning(this, QCoreApplication::applicationName(), tr("The attached image can't be displayed.")); return; } @@ -550,8 +574,10 @@ void PicturePreviewSelection::changeMimeTypeOfSelected() auto &selectedCover = m_values[m_currentTypeIndex]; auto mimeType = QString::fromUtf8(selectedCover.mimeType().data()); bool ok; - mimeType = QInputDialog::getText( - this, tr("Enter/confirm mime type"), tr("Confirm or enter the mime type of the selected file."), QLineEdit::Normal, mimeType, &ok); + mimeType = QInputDialog::getText(this, tr("Enter/confirm MIME type"), + tr("Confirm or enter the MIME type of the selected file. This merely changes the assumed format. No image format conversion " + "done."), + QLineEdit::Normal, mimeType, &ok); if (!ok) { return; } @@ -559,6 +585,51 @@ void PicturePreviewSelection::changeMimeTypeOfSelected() updateSizeAndMimeType(m_currentFileSize, m_currentResolution, mimeType); } +void PicturePreviewSelection::convertSelected() +{ + assert(m_currentTypeIndex < m_values.size()); + auto &selectedCover = m_values[m_currentTypeIndex]; + + // load image + const auto img = convertTagValueToImage(selectedCover); + if (img.isNull()) { + return; + } + + // show image conversion dialog + if (!m_imageConversionDialog) { + m_imageConversionDialog = new QDialog(this); + m_imageConversionUI = make_unique(); + m_imageConversionUI->setupUi(m_imageConversionDialog); +#ifdef Q_OS_WIN32 + m_imageConversionDialog->setStyleSheet(dialogStyle()); +#endif + m_imageConversionUI->formatComboBox->addItems({ tr("JPEG"), tr("PNG") }); + m_imageConversionUI->aspectRatioComboBox->addItems({ tr("Ignore"), tr("Keep"), tr("Keep by expanding") }); + m_imageConversionUI->aspectRatioComboBox->setCurrentIndex(1); + connect(m_imageConversionUI->confirmPushButton, &QPushButton::clicked, m_imageConversionDialog, &QDialog::accept); + connect(m_imageConversionUI->abortPushButton, &QPushButton::clicked, m_imageConversionDialog, &QDialog::reject); + } + m_imageConversionUI->widthSpinBox->setValue(img.width()); + m_imageConversionUI->heightSpinBox->setValue(img.height()); + if (m_imageConversionDialog->exec() != QDialog::Accepted) { + return; + } + + // scale image + const auto scaledImg = img.scaled(m_imageConversionUI->widthSpinBox->value(), m_imageConversionUI->heightSpinBox->value(), + static_cast(m_imageConversionUI->aspectRatioComboBox->currentIndex()), Qt::SmoothTransformation); + if (scaledImg.isNull()) { + QMessageBox::warning(this, QCoreApplication::applicationName(), tr("Unable to scale image.")); + return; + } + + // assign image + assignImageToTagValue(scaledImg, selectedCover, m_imageConversionUI->formatComboBox->currentIndex() == 0 ? "JPEG" : "PNG"); + + updatePreview(m_currentTypeIndex); +} + /*! * \brief Sets whether cover buttons are hidden. */ @@ -760,9 +831,12 @@ void PicturePreviewSelection::showContextMenu(const QPoint &position) } #endif if (m_ui->extractButton->isEnabled()) { - QAction *mimeAction = menu.addAction(tr("Change MIME-type")); + auto *const mimeAction = menu.addAction(tr("Change MIME-type")); mimeAction->setIcon(QIcon::fromTheme(QStringLiteral("document-properties"))); connect(mimeAction, &QAction::triggered, this, &PicturePreviewSelection::changeMimeTypeOfSelected); + auto *const convertAction = menu.addAction(tr("Resize/convert assigned image")); + convertAction->setIcon(QIcon::fromTheme(QStringLiteral("image-resize-symbolic"))); + connect(convertAction, &QAction::triggered, this, &PicturePreviewSelection::convertSelected); } menu.addSeparator(); if (m_ui->removeButton->isEnabled()) { diff --git a/gui/picturepreviewselection.h b/gui/picturepreviewselection.h index f4e2708..2709ac0 100644 --- a/gui/picturepreviewselection.h +++ b/gui/picturepreviewselection.h @@ -30,7 +30,8 @@ TAGEDITOR_ENUM_CLASS PreviousValueHandling : int; namespace Ui { class PicturePreviewSelection; -} +class ImageConversionDialog; +} // namespace Ui class PicturePreviewSelection : public QWidget { Q_OBJECT @@ -60,6 +61,7 @@ public slots: void extractSelected(); void displaySelected(); void changeMimeTypeOfSelected(); + void convertSelected(); void setCoverButtonsHidden(bool hideCoverButtons); signals: @@ -81,8 +83,12 @@ private slots: private: bool setup(PreviousValueHandling previousValueHandling = PreviousValueHandling::Clear); void updateSizeAndMimeType(std::size_t fileSize, const QSize &resolution, const QString &mimeType); + QImage convertTagValueToImage(const TagParser::TagValue &value); + void assignImageToTagValue(const QImage &image, TagParser::TagValue &tagValue, const char *format); std::unique_ptr m_ui; + std::unique_ptr m_imageConversionUI; + QDialog *m_imageConversionDialog; QGraphicsScene *m_scene; QGraphicsTextItem *m_textItem; QPixmap m_pixmap;