diff options
author | Friedemann Kleint <Friedemann.Kleint@qt.io> | 2018-01-12 15:32:10 +0100 |
---|---|---|
committer | Friedemann Kleint <Friedemann.Kleint@qt.io> | 2018-02-14 09:37:16 +0000 |
commit | cd85d3aba56a142374226f5fdec8df3b6632357b (patch) | |
tree | e02f51fb9a6d8f78a459d7795b0ff52064f8ed2c /src/plugins/imageviewer | |
parent | 43936b1e8619ec8cc25d7699e53b638d8f0627b1 (diff) |
Add SVG multi export
Add a second SVG export option that allows for exporting
a series of icons of various size in one go. A dialog is shown
that allows for entering a file name pattern with place holders
and a list of sizes.
Change-Id: Ic644a9d402aa44af5899c29cf83051fdd7bba3d1
Reviewed-by: Leena Miettinen <riitta-leena.miettinen@qt.io>
Reviewed-by: Diana de Sousa <diana.desousa@theqtcompany.com>
Reviewed-by: Alessandro Portale <alessandro.portale@qt.io>
Diffstat (limited to 'src/plugins/imageviewer')
-rw-r--r-- | src/plugins/imageviewer/exportdialog.cpp | 8 | ||||
-rw-r--r-- | src/plugins/imageviewer/exportdialog.h | 6 | ||||
-rw-r--r-- | src/plugins/imageviewer/imageview.cpp | 105 | ||||
-rw-r--r-- | src/plugins/imageviewer/imageview.h | 11 | ||||
-rw-r--r-- | src/plugins/imageviewer/imageviewer.cpp | 18 | ||||
-rw-r--r-- | src/plugins/imageviewer/imageviewer.h | 1 | ||||
-rw-r--r-- | src/plugins/imageviewer/imageviewer.pro | 6 | ||||
-rw-r--r-- | src/plugins/imageviewer/imageviewerconstants.h | 1 | ||||
-rw-r--r-- | src/plugins/imageviewer/imageviewerplugin.cpp | 8 | ||||
-rw-r--r-- | src/plugins/imageviewer/imageviewertoolbar.ui | 10 | ||||
-rw-r--r-- | src/plugins/imageviewer/multiexportdialog.cpp | 346 | ||||
-rw-r--r-- | src/plugins/imageviewer/multiexportdialog.h | 79 |
12 files changed, 563 insertions, 36 deletions
diff --git a/src/plugins/imageviewer/exportdialog.cpp b/src/plugins/imageviewer/exportdialog.cpp index 778d90e9ec..1b64159131 100644 --- a/src/plugins/imageviewer/exportdialog.cpp +++ b/src/plugins/imageviewer/exportdialog.cpp @@ -24,6 +24,7 @@ ****************************************************************************/ #include "exportdialog.h" +#include "imageview.h" // ExportData #include <coreplugin/coreicons.h> @@ -56,7 +57,7 @@ namespace Internal { enum { exportMinimumSize = 1, exportMaximumSize = 2000 }; -static QString imageNameFilterString() +QString ExportDialog::imageNameFilterString() { static QString result; if (result.isEmpty()) { @@ -198,5 +199,10 @@ void ExportDialog::setExportFileName(const QString &f) m_pathChooser->setFileName(Utils::FileName::fromString(f)); } +ExportData ExportDialog::exportData() const +{ + return {exportFileName(), exportSize()}; +} + } // namespace Internal } // namespace ImageViewer diff --git a/src/plugins/imageviewer/exportdialog.h b/src/plugins/imageviewer/exportdialog.h index 76296e8ba4..2480c2dcaf 100644 --- a/src/plugins/imageviewer/exportdialog.h +++ b/src/plugins/imageviewer/exportdialog.h @@ -34,6 +34,8 @@ namespace Utils { class PathChooser; } namespace ImageViewer { namespace Internal { +struct ExportData; + class ExportDialog : public QDialog { Q_OBJECT @@ -46,8 +48,12 @@ public: QString exportFileName() const; void setExportFileName(const QString &); + ExportData exportData() const; + void accept() override; + static QString imageNameFilterString(); + private: void resetExportSize(); void exportWidthChanged(int width); diff --git a/src/plugins/imageviewer/imageview.cpp b/src/plugins/imageviewer/imageview.cpp index db7869cd2e..dfa4a547fe 100644 --- a/src/plugins/imageviewer/imageview.cpp +++ b/src/plugins/imageviewer/imageview.cpp @@ -51,6 +51,7 @@ #include "imageview.h" #include "exportdialog.h" +#include "multiexportdialog.h" #include "imageviewerfile.h" #include <coreplugin/messagemanager.h> @@ -156,6 +157,53 @@ void ImageView::drawBackground(QPainter *p, const QRectF &) p->restore(); } +QImage ImageView::renderSvg(const QSize &imageSize) const +{ + QImage image(imageSize, QImage::Format_ARGB32); + image.fill(Qt::transparent); + QPainter painter; + painter.begin(&image); +#ifndef QT_NO_SVG + QGraphicsSvgItem *svgItem = qgraphicsitem_cast<QGraphicsSvgItem *>(m_imageItem); + QTC_ASSERT(svgItem, return image); + svgItem->renderer()->render(&painter, QRectF(QPointF(), QSizeF(imageSize))); +#endif + painter.end(); + return image; +} + +bool ImageView::exportSvg(const ExportData &ed) +{ + const bool result = renderSvg(ed.size).save(ed.fileName); + if (result) { + const QString message = tr("Exported \"%1\", %2x%3, %4 bytes") + .arg(QDir::toNativeSeparators(ed.fileName)) + .arg(ed.size.width()).arg(ed.size.height()) + .arg(QFileInfo(ed.fileName).size()); + Core::MessageManager::write(message); + } else { + const QString message = tr("Could not write file \"%1\".").arg(QDir::toNativeSeparators(ed.fileName)); + QMessageBox::critical(this, tr("Export Image"), message); + } + return result; +} + +static QString suggestedExportFileName(const QFileInfo &fi) +{ + return fi.absolutePath() + QLatin1Char('/') + fi.baseName() + + QStringLiteral(".png"); +} + +QSize ImageView::svgSize() const +{ + QSize result; +#ifndef QT_NO_SVG + if (const QGraphicsSvgItem *svgItem = qgraphicsitem_cast<QGraphicsSvgItem *>(m_imageItem)) + result = svgItem->boundingRect().size().toSize(); +#endif // !QT_NO_SVG + return result; +} + void ImageView::exportImage() { #ifndef QT_NO_SVG @@ -163,37 +211,42 @@ void ImageView::exportImage() QTC_ASSERT(svgItem, return); const QFileInfo origFi = m_file->filePath().toFileInfo(); - const QString suggestedFileName = origFi.absolutePath() + QLatin1Char('/') - + origFi.baseName() + QStringLiteral(".png"); - ExportDialog exportDialog(this); exportDialog.setWindowTitle(tr("Export %1").arg(origFi.fileName())); - exportDialog.setExportSize(svgItem->boundingRect().size().toSize()); - exportDialog.setExportFileName(suggestedFileName); + exportDialog.setExportSize(svgSize()); + exportDialog.setExportFileName(suggestedExportFileName(origFi)); - while (true) { - if (exportDialog.exec() != QDialog::Accepted) - break; + while (exportDialog.exec() == QDialog::Accepted && !exportSvg(exportDialog.exportData())) {} +#endif // !QT_NO_SVG +} - const QSize imageSize = exportDialog.exportSize(); - QImage image(imageSize, QImage::Format_ARGB32); - image.fill(Qt::transparent); - QPainter painter; - painter.begin(&image); - svgItem->renderer()->render(&painter, QRectF(QPointF(), QSizeF(imageSize))); - painter.end(); - - const QString fileName = exportDialog.exportFileName(); - if (image.save(fileName)) { - const QString message = tr("Exported \"%1\", %2x%3, %4 bytes") - .arg(QDir::toNativeSeparators(fileName)).arg(imageSize.width()).arg(imageSize.height()) - .arg(QFileInfo(fileName).size()); - Core::MessageManager::write(message); - break; - } else { - QMessageBox::critical(this, tr("Export Image"), - tr("Could not write file \"%1\".").arg(QDir::toNativeSeparators(fileName))); +void ImageView::exportMultiImages() +{ +#ifndef QT_NO_SVG + QTC_ASSERT(qgraphicsitem_cast<QGraphicsSvgItem *>(m_imageItem), return); + + const QFileInfo origFi = m_file->filePath().toFileInfo(); + const QSize size = svgSize(); + const QString title = + tr("Export a Series of Images from %1 (%2x%3") + .arg(origFi.fileName()).arg(size.width()).arg(size.height()); + MultiExportDialog multiExportDialog; + multiExportDialog.setWindowTitle(title); + multiExportDialog.setExportFileName(suggestedExportFileName(origFi)); + multiExportDialog.setSvgSize(size); + multiExportDialog.suggestSizes(); + + while (multiExportDialog.exec() == QDialog::Accepted) { + const auto exportData = multiExportDialog.exportData(); + bool ok = true; + for (const auto &data : exportData) { + if (!exportSvg(data)) { + ok = false; + break; + } } + if (ok) + break; } #endif // !QT_NO_SVG } diff --git a/src/plugins/imageviewer/imageview.h b/src/plugins/imageviewer/imageview.h index 4e797b6601..76f4a38de6 100644 --- a/src/plugins/imageviewer/imageview.h +++ b/src/plugins/imageviewer/imageview.h @@ -52,11 +52,18 @@ #include <QGraphicsView> +QT_FORWARD_DECLARE_CLASS(QImage) + namespace ImageViewer { namespace Internal { class ImageViewerFile; +struct ExportData { + QString fileName; + QSize size; +}; + class ImageView : public QGraphicsView { Q_OBJECT @@ -69,6 +76,7 @@ public: void createScene(); void exportImage(); + void exportMultiImages(); void setViewBackground(bool enable); void setViewOutline(bool enable); void zoomIn(); @@ -83,6 +91,9 @@ signals: private: void emitScaleFactor(); void doScale(qreal factor); + QSize svgSize() const; + QImage renderSvg(const QSize &imageSize) const; + bool exportSvg(const ExportData &ed); void drawBackground(QPainter *p, const QRectF &rect); void hideEvent(QHideEvent *event); diff --git a/src/plugins/imageviewer/imageviewer.cpp b/src/plugins/imageviewer/imageviewer.cpp index 7c98b7bb6b..ea32b433df 100644 --- a/src/plugins/imageviewer/imageviewer.cpp +++ b/src/plugins/imageviewer/imageviewer.cpp @@ -102,9 +102,8 @@ void ImageViewer::ctor() // toolbar d->toolbar = new QWidget(); d->ui_toolbar.setupUi(d->toolbar); - d->ui_toolbar.toolButtonExportImage->setIcon( - QIcon::fromTheme(QLatin1String("document-save"), - Utils::Icons::SAVEFILE_TOOLBAR.icon())); + d->ui_toolbar.toolButtonExportImage->setIcon(Utils::Icons::EXPORTFILE_TOOLBAR.icon()); + d->ui_toolbar.toolButtonMultiExportImages->setIcon(Utils::Icons::MULTIEXPORTFILE_TOOLBAR.icon()); const Utils::Icon backgroundIcon({ {QLatin1String(":/utils/images/desktopdevicesmall.png"), Utils::Theme::IconsBaseColor}}); d->ui_toolbar.toolButtonBackground->setIcon(backgroundIcon.icon()); @@ -125,6 +124,7 @@ void ImageViewer::ctor() updateButtonIconByTheme(d->ui_toolbar.toolButtonOutline, QLatin1String("emblem-photos")); d->ui_toolbar.toolButtonExportImage->setCommandId(Constants::ACTION_EXPORT_IMAGE); + d->ui_toolbar.toolButtonMultiExportImages->setCommandId(Constants::ACTION_EXPORT_MULTI_IMAGES); d->ui_toolbar.toolButtonZoomIn->setCommandId(Core::Constants::ZOOM_IN); d->ui_toolbar.toolButtonZoomOut->setCommandId(Core::Constants::ZOOM_OUT); d->ui_toolbar.toolButtonOriginalSize->setCommandId(Core::Constants::ZOOM_RESET); @@ -136,6 +136,8 @@ void ImageViewer::ctor() // connections connect(d->ui_toolbar.toolButtonExportImage, &QAbstractButton::clicked, d->imageView, &ImageView::exportImage); + connect(d->ui_toolbar.toolButtonMultiExportImages, &QAbstractButton::clicked, + d->imageView, &ImageView::exportMultiImages); connect(d->ui_toolbar.toolButtonZoomIn, &QAbstractButton::clicked, d->imageView, &ImageView::zoomIn); connect(d->ui_toolbar.toolButtonZoomOut, &QAbstractButton::clicked, @@ -198,6 +200,12 @@ void ImageViewer::exportImage() d->ui_toolbar.toolButtonExportImage->click(); } +void ImageViewer::exportMultiImages() +{ + if (d->file->type() == ImageViewerFile::TypeSvg) + d->ui_toolbar.toolButtonMultiExportImages->click(); +} + void ImageViewer::imageSizeUpdated(const QSize &size) { QString imageSizeText; @@ -244,7 +252,9 @@ void ImageViewer::fitToScreen() void ImageViewer::updateToolButtons() { - d->ui_toolbar.toolButtonExportImage->setEnabled(d->file->type() == ImageViewerFile::TypeSvg); + const bool isSvg = d->file->type() == ImageViewerFile::TypeSvg; + d->ui_toolbar.toolButtonExportImage->setEnabled(isSvg); + d->ui_toolbar.toolButtonMultiExportImages->setEnabled(isSvg); updatePauseAction(); } diff --git a/src/plugins/imageviewer/imageviewer.h b/src/plugins/imageviewer/imageviewer.h index 38ee2ab035..1d3418ac4d 100644 --- a/src/plugins/imageviewer/imageviewer.h +++ b/src/plugins/imageviewer/imageviewer.h @@ -55,6 +55,7 @@ public: IEditor *duplicate() override; void exportImage(); + void exportMultiImages(); void imageSizeUpdated(const QSize &size); void scaleFactorUpdate(qreal factor); diff --git a/src/plugins/imageviewer/imageviewer.pro b/src/plugins/imageviewer/imageviewer.pro index a3d8182588..18e99acf6c 100644 --- a/src/plugins/imageviewer/imageviewer.pro +++ b/src/plugins/imageviewer/imageviewer.pro @@ -7,7 +7,8 @@ HEADERS += \ imageviewerfile.h \ imageviewer.h \ imageview.h \ - imageviewerconstants.h + imageviewerconstants.h \ + multiexportdialog.h SOURCES += \ exportdialog.cpp \ @@ -15,7 +16,8 @@ SOURCES += \ imageviewerfactory.cpp \ imageviewerfile.cpp \ imageviewer.cpp \ - imageview.cpp + imageview.cpp \ + multiexportdialog.cpp !isEmpty(QT.svg.name): QT += svg else: DEFINES += QT_NO_SVG diff --git a/src/plugins/imageviewer/imageviewerconstants.h b/src/plugins/imageviewer/imageviewerconstants.h index 1fac9a6906..d8d5536df1 100644 --- a/src/plugins/imageviewer/imageviewerconstants.h +++ b/src/plugins/imageviewer/imageviewerconstants.h @@ -33,6 +33,7 @@ const char IMAGEVIEWER_ID[] = "Editors.ImageViewer"; const char IMAGEVIEWER_DISPLAY_NAME[] = QT_TRANSLATE_NOOP("OpenWith::Editors", "Image Viewer"); const char ACTION_EXPORT_IMAGE[] = "ImageViewer.ExportImage"; +const char ACTION_EXPORT_MULTI_IMAGES[] = "ImageViewer.ExportMultiImages"; const char ACTION_FIT_TO_SCREEN[] = "ImageViewer.FitToScreen"; const char ACTION_BACKGROUND[] = "ImageViewer.Background"; const char ACTION_OUTLINE[] = "ImageViewer.Outline"; diff --git a/src/plugins/imageviewer/imageviewerplugin.cpp b/src/plugins/imageviewer/imageviewerplugin.cpp index 1e2a493a0f..82f46964f8 100644 --- a/src/plugins/imageviewer/imageviewerplugin.cpp +++ b/src/plugins/imageviewer/imageviewerplugin.cpp @@ -113,6 +113,14 @@ void ImageViewerPlugin::extensionsInitialized() if (ImageViewer *iv = currentImageViewer()) iv->exportImage(); }); + + a = registerNewAction(Constants::ACTION_EXPORT_MULTI_IMAGES, tr("Export Multiple Images"), + QKeySequence()); + connect(a, &QAction::triggered, this, []() { + if (ImageViewer *iv = currentImageViewer()) + iv->exportMultiImages(); + }); + } QAction *ImageViewerPlugin::registerNewAction(Core::Id id, diff --git a/src/plugins/imageviewer/imageviewertoolbar.ui b/src/plugins/imageviewer/imageviewertoolbar.ui index 0a230946d3..3aac796f5d 100644 --- a/src/plugins/imageviewer/imageviewertoolbar.ui +++ b/src/plugins/imageviewer/imageviewertoolbar.ui @@ -25,6 +25,13 @@ </widget> </item> <item> + <widget class="Core::CommandButton" name="toolButtonMultiExportImages"> + <property name="toolTipBase"> + <string>Export Images of Multiple Sizes</string> + </property> + </widget> + </item> + <item> <widget class="Core::CommandButton" name="toolButtonBackground"> <property name="toolTipBase"> <string>Show Background</string> @@ -134,8 +141,5 @@ <header>coreplugin/actionmanager/commandbutton.h</header> </customwidget> </customwidgets> - <resources> - <include location="imageviewer.qrc"/> - </resources> <connections/> </ui> diff --git a/src/plugins/imageviewer/multiexportdialog.cpp b/src/plugins/imageviewer/multiexportdialog.cpp new file mode 100644 index 0000000000..fe3a66a84b --- /dev/null +++ b/src/plugins/imageviewer/multiexportdialog.cpp @@ -0,0 +1,346 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#include "multiexportdialog.h" +#include "exportdialog.h" +#include "imageview.h" // ExportData + +#include <coreplugin/coreicons.h> +#include <coreplugin/icore.h> + +#include <utils/pathchooser.h> +#include <utils/utilsicons.h> + +#include <QApplication> +#include <QDesktopWidget> +#include <QDialogButtonBox> +#include <QFileDialog> +#include <QFormLayout> +#include <QHBoxLayout> +#include <QMessageBox> +#include <QMenu> +#include <QLabel> +#include <QLineEdit> +#include <QPushButton> +#include <QSpinBox> +#include <QToolButton> +#include <QVBoxLayout> +#include <QWidgetAction> + +#include <QImageWriter> + +#include <QDebug> +#include <QDir> +#include <QFileInfo> +#include <QSettings> +#include <QTextStream> + +namespace ImageViewer { +namespace Internal { + +static const int standardIconSizesValues[] = {16, 24, 32, 48, 64, 128, 256}; + +// Helpers to convert a size specifications from QString to QSize +// and vv. The format is '2x4' or '4' as shortcut for '4x4'. +static QSize sizeFromString(const QStringRef &r) +{ + if (r.isEmpty()) + return QSize(); + const int xPos = r.indexOf('x'); + bool ok; + const int width = xPos < 0 + ? r.toInt(&ok) + : r.left(xPos).toInt(&ok); + if (!ok || width <= 0) + return QSize(); + if (xPos < 0) + return QSize(width, width); + const int height = r.mid(xPos + 1).toInt(&ok); + if (!ok || height <= 0) + return QSize(); + return QSize(width, height); +} + +static void appendSizeSpec(const QSize &size, QString *target) +{ + target->append(QString::number(size.width())); + if (size.width() != size.height()) { + target->append('x'); + target->append(QString::number(size.height())); + } +} + +static inline QString sizeToString(const QSize &size) +{ + QString result; + appendSizeSpec(size, &result); + return result; +} + +static QString sizesToString(const QVector<QSize> &sizes) +{ + QString result; + for (int i = 0, size = sizes.size(); i < size; ++i) { + if (i) + result.append(','); + appendSizeSpec(sizes.at(i), &result); + } + return result; +} + +static QVector<QSize> stringToSizes(const QString &s) +{ + QVector<QSize> result; + const QString trimmed = s.trimmed(); + const QVector<QStringRef> &sizes = trimmed.splitRef(',', QString::SkipEmptyParts); + result.reserve(sizes.size()); + for (const QStringRef &sizeSpec : sizes) { + const QSize size = sizeFromString(sizeSpec); + if (!size.isValid() || size.isEmpty()) + return QVector<QSize>(); + else + result.append(size); + } + return result; +} + +static QString fileNameForSize(QString pattern, const QSize &s) +{ + pattern.replace("%1", QString::number(s.width())); + pattern.replace("%2", QString::number(s.height())); + return pattern; +} + +// Helpers for writing/reading the user-specified size specifications +// from/to the settings. +static inline QString settingsGroup() { return QStringLiteral("ExportSvgSizes"); } + +static QVector<QSize> readSettings(const QSize &size) +{ + QVector<QSize> result; + QSettings *settings = Core::ICore::settings(); + settings->beginGroup(settingsGroup()); + const QStringList keys = settings->allKeys(); + const int idx = keys.indexOf(sizeToString(size)); + if (idx >= 0) + result = stringToSizes(settings->value(keys.at(idx)).toString()); + settings->endGroup(); + return result; +} + +static void writeSettings(const QSize &size, const QString &sizeSpec) +{ + QSettings *settings = Core::ICore::settings(); + settings->beginGroup(settingsGroup()); + const QString spec = sizeToString(size); + settings->setValue(spec, QVariant(sizeSpec)); + + // Limit the number of sizes to 10. Remove the + // first element unless it is the newly added spec. + QStringList keys = settings->allKeys(); + while (keys.size() > 10) { + const int existingIndex = keys.indexOf(spec); + const int removeIndex = existingIndex == 0 ? 1 : 0; + settings->remove(keys.takeAt(removeIndex)); + } + settings->endGroup(); +} + +QVector<QSize> MultiExportDialog::standardIconSizes() +{ + QVector<QSize> result; + const int size = int(sizeof(standardIconSizesValues) / sizeof(standardIconSizesValues[0])); + result.reserve(size); + for (int i = 0; i < size; ++i) + result.append(QSize(standardIconSizesValues[i], standardIconSizesValues[i])); + return result; +} + +// --- MultiExportDialog +MultiExportDialog::MultiExportDialog(QWidget *parent) + : QDialog(parent) + , m_pathChooser(new Utils::PathChooser(this)) + , m_sizesLineEdit(new QLineEdit) +{ + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + + QFormLayout *formLayout = new QFormLayout(this); + + m_pathChooser->setMinimumWidth(QApplication::desktop()->availableGeometry(this).width() / 5); + m_pathChooser->setExpectedKind(Utils::PathChooser::SaveFile); + m_pathChooser->setPromptDialogFilter(ExportDialog::imageNameFilterString()); + const QString pathChooserToolTip = + tr("Enter a file name containing place holders %1 " + "which will be replaced by the width and height of the image, respectively.") + .arg("%1, %2"); + m_pathChooser->setToolTip(pathChooserToolTip); + QLabel *pathChooserLabel = new QLabel(tr("File:")); + pathChooserLabel->setToolTip(pathChooserToolTip); + formLayout->addRow(pathChooserLabel, m_pathChooser); + + QToolButton *sizeEditButton = new QToolButton; + sizeEditButton->setFocusPolicy(Qt::NoFocus); + sizeEditButton->setIcon(Utils::Icons::ARROW_DOWN.icon()); + QMenu *sizeEditMenu = new QMenu(this); + sizeEditMenu->addAction(tr("Clear"), + m_sizesLineEdit, &QLineEdit::clear); + sizeEditMenu->addAction(tr("Set Standard Icon Sizes"), this, + &MultiExportDialog::setStandardIconSizes); + sizeEditMenu->addAction(tr("Generate Sizes"), this, + &MultiExportDialog::setGeneratedSizes); + sizeEditButton->setMenu(sizeEditMenu); + sizeEditButton->setPopupMode(QToolButton::InstantPopup); + + const QString sizesToolTip = + tr("A comma-separated list of size specifications of the form \"<width>x<height>\"."); + QLabel *sizesLabel = new QLabel(tr("Sizes:")); + sizesLabel->setToolTip(sizesToolTip); + formLayout->addRow(sizesLabel, m_sizesLineEdit); + m_sizesLineEdit->setToolTip(sizesToolTip); + QWidgetAction *optionsAction = new QWidgetAction(this); + optionsAction->setDefaultWidget(sizeEditButton); + m_sizesLineEdit->addAction(optionsAction, QLineEdit::TrailingPosition); + + QDialogButtonBox *buttonBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel, this); + connect(buttonBox, &QDialogButtonBox::accepted, this, &QDialog::accept); + connect(buttonBox, &QDialogButtonBox::rejected, this, &QDialog::reject); + formLayout->addRow(buttonBox); +} + +void MultiExportDialog::setSizes(const QVector<QSize> &s) +{ + m_sizesLineEdit->setText(sizesToString(s)); +} + +QVector<QSize> MultiExportDialog::sizes() const +{ + return stringToSizes(sizesSpecification()); +} + +void MultiExportDialog::setStandardIconSizes() +{ + setSizes(standardIconSizes()); +} + +void MultiExportDialog::setGeneratedSizes() +{ + QVector<QSize> sizes; + if (m_svgSize.width() >= 16) + sizes.append(m_svgSize / 2); + sizes.append(m_svgSize); + for (int factor = 2; sizes.size() < 4; factor *= 2) + sizes.append(m_svgSize * factor); + setSizes(sizes); +} + +void MultiExportDialog::suggestSizes() +{ + const QVector<QSize> settingsEntries = readSettings(m_svgSize); + if (!settingsEntries.isEmpty()) + setSizes(settingsEntries); + else if (m_svgSize.width() == m_svgSize.height()) // Square: Assume this is an icon + setStandardIconSizes(); + else + setGeneratedSizes(); +} + +QVector<ExportData> MultiExportDialog::exportData() const +{ + const QVector<QSize> sizeList = sizes(); + const QString pattern = exportFileName(); + QVector<ExportData> result; + result.reserve(sizeList.size()); + for (const QSize &s : sizeList) + result.append({fileNameForSize(pattern, s), s}); + return result; +} + +QString MultiExportDialog::sizesSpecification() const +{ + return m_sizesLineEdit->text().trimmed(); +} + +void MultiExportDialog::accept() +{ + if (!m_pathChooser->isValid()) { + QMessageBox::warning(this, windowTitle(), m_pathChooser->errorMessage()); + return; + } + + const QString &sizeSpec = sizesSpecification(); + if (sizeSpec.isEmpty()) { + QMessageBox::warning(this, windowTitle(), tr("Please specify some sizes.")); + return; + } + + const QVector<ExportData> &data = exportData(); + if (data.isEmpty()) { + QMessageBox::warning(this, windowTitle(), + tr("Invalid size specification: %1").arg(sizeSpec)); + return; + } + if (data.size() > 1 && data.at(0).fileName == data.at(1).fileName) { + QMessageBox::warning(this, windowTitle(), + tr("The file name must contain one of the placeholders %1, %2.").arg("%1", "%2")); + return; + } + + writeSettings(m_svgSize, sizeSpec); + + QStringList existingFiles; + for (const ExportData &d : data) { + if (QFileInfo::exists(d.fileName)) + existingFiles.append(d.fileName); + } + if (!existingFiles.isEmpty()) { + const QString message = existingFiles.size() == 1 + ? tr("The file %1 already exists.\nWould you like to overwrite it?") + .arg(QDir::toNativeSeparators(existingFiles.constFirst())) + : tr("The files %1 already exist.\nWould you like to overwrite them?") + .arg(QDir::toNativeSeparators(existingFiles.join(", "))); + QMessageBox messageBox(QMessageBox::Question, windowTitle(), message, + QMessageBox::Yes | QMessageBox::No, this); + if (messageBox.exec() != QMessageBox::Yes) + return; + } + + QDialog::accept(); +} + +QString MultiExportDialog::exportFileName() const +{ + return m_pathChooser->fileName().toString(); +} + +void MultiExportDialog::setExportFileName(QString f) +{ + const int lastDot = f.lastIndexOf('.'); + if (lastDot != -1) + f.insert(lastDot, "-%1"); + m_pathChooser->setFileName(Utils::FileName::fromString(f)); +} + +} // namespace Internal +} // namespace ImageViewer diff --git a/src/plugins/imageviewer/multiexportdialog.h b/src/plugins/imageviewer/multiexportdialog.h new file mode 100644 index 0000000000..cb78c3347a --- /dev/null +++ b/src/plugins/imageviewer/multiexportdialog.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2018 The Qt Company Ltd. +** Contact: https://www.qt.io/licensing/ +** +** This file is part of Qt Creator. +** +** Commercial License Usage +** Licensees holding valid commercial Qt licenses may use this file in +** accordance with the commercial license agreement provided with the +** Software or, alternatively, in accordance with the terms contained in +** a written agreement between you and The Qt Company. For licensing terms +** and conditions see https://www.qt.io/terms-conditions. For further +** information use the contact form at https://www.qt.io/contact-us. +** +** GNU General Public License Usage +** Alternatively, this file may be used under the terms of the GNU +** General Public License version 3 as published by the Free Software +** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT +** included in the packaging of this file. Please review the following +** information to ensure the GNU General Public License requirements will +** be met: https://www.gnu.org/licenses/gpl-3.0.html. +** +****************************************************************************/ + +#pragma once + +#include <QDialog> + +#include <QPair> +#include <QSize> +#include <QVector> + +QT_FORWARD_DECLARE_CLASS(QLineEdit) + +namespace Utils { class PathChooser; } + +namespace ImageViewer { +namespace Internal { + +struct ExportData; + +class MultiExportDialog : public QDialog +{ + Q_OBJECT +public: + explicit MultiExportDialog(QWidget *parent = nullptr); + + QString exportFileName() const; + void setExportFileName(QString); + + void accept() override; + + void setSizes(const QVector<QSize> &); + QVector<QSize> sizes() const; + + QVector<ExportData> exportData() const; + + static QVector<QSize> standardIconSizes(); + + QSize svgSize() const { return m_svgSize; } + void setSvgSize(const QSize &svgSize) { m_svgSize = svgSize; } + +public slots: + void setStandardIconSizes(); + void setGeneratedSizes(); + void suggestSizes(); + +private: + QString sizesSpecification() const; + + Utils::PathChooser *m_pathChooser; + QLineEdit *m_sizesLineEdit; + QSize m_svgSize; + +}; + +} // namespace Internal +} // namespace ImageViewer |