aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins/imageviewer
diff options
context:
space:
mode:
authorFriedemann Kleint <Friedemann.Kleint@qt.io>2018-01-12 15:32:10 +0100
committerFriedemann Kleint <Friedemann.Kleint@qt.io>2018-02-14 09:37:16 +0000
commitcd85d3aba56a142374226f5fdec8df3b6632357b (patch)
treee02f51fb9a6d8f78a459d7795b0ff52064f8ed2c /src/plugins/imageviewer
parent43936b1e8619ec8cc25d7699e53b638d8f0627b1 (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.cpp8
-rw-r--r--src/plugins/imageviewer/exportdialog.h6
-rw-r--r--src/plugins/imageviewer/imageview.cpp105
-rw-r--r--src/plugins/imageviewer/imageview.h11
-rw-r--r--src/plugins/imageviewer/imageviewer.cpp18
-rw-r--r--src/plugins/imageviewer/imageviewer.h1
-rw-r--r--src/plugins/imageviewer/imageviewer.pro6
-rw-r--r--src/plugins/imageviewer/imageviewerconstants.h1
-rw-r--r--src/plugins/imageviewer/imageviewerplugin.cpp8
-rw-r--r--src/plugins/imageviewer/imageviewertoolbar.ui10
-rw-r--r--src/plugins/imageviewer/multiexportdialog.cpp346
-rw-r--r--src/plugins/imageviewer/multiexportdialog.h79
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