forked from qt-creator/qt-creator
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>
This commit is contained in:
BIN
src/libs/utils/images/fileexport.png
Normal file
BIN
src/libs/utils/images/fileexport.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 148 B |
BIN
src/libs/utils/images/fileexport@2x.png
Normal file
BIN
src/libs/utils/images/fileexport@2x.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 195 B |
BIN
src/libs/utils/images/filemultiexport.png
Normal file
BIN
src/libs/utils/images/filemultiexport.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 156 B |
BIN
src/libs/utils/images/filemultiexport@2x.png
Normal file
BIN
src/libs/utils/images/filemultiexport@2x.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 212 B |
@@ -52,6 +52,10 @@
|
||||
<file>images/editpaste@2x.png</file>
|
||||
<file>images/empty14.png</file>
|
||||
<file>images/empty16.png</file>
|
||||
<file>images/fileexport.png</file>
|
||||
<file>images/fileexport@2x.png</file>
|
||||
<file>images/filemultiexport.png</file>
|
||||
<file>images/filemultiexport@2x.png</file>
|
||||
<file>images/filenew.png</file>
|
||||
<file>images/filenew@2x.png</file>
|
||||
<file>images/fileopen.png</file>
|
||||
|
||||
@@ -93,6 +93,12 @@ const Icon SAVEFILE({
|
||||
{QLatin1String(":/utils/images/filesave.png"), Theme::PanelTextColorMid}}, Icon::Tint);
|
||||
const Icon SAVEFILE_TOOLBAR({
|
||||
{QLatin1String(":/utils/images/filesave.png"), Theme::IconsBaseColor}});
|
||||
|
||||
const Icon EXPORTFILE_TOOLBAR({
|
||||
{QLatin1String(":/utils/images/fileexport.png"), Theme::IconsBaseColor}});
|
||||
const Icon MULTIEXPORTFILE_TOOLBAR({
|
||||
{QLatin1String(":/utils/images/filemultiexport.png"), Theme::IconsBaseColor}});
|
||||
|
||||
const Icon UNDO({
|
||||
{QLatin1String(":/utils/images/undo.png"), Theme::PanelTextColorMid}}, Icon::Tint);
|
||||
const Icon UNDO_TOOLBAR({
|
||||
|
||||
@@ -61,6 +61,10 @@ QTCREATOR_UTILS_EXPORT extern const Icon OPENFILE;
|
||||
QTCREATOR_UTILS_EXPORT extern const Icon OPENFILE_TOOLBAR;
|
||||
QTCREATOR_UTILS_EXPORT extern const Icon SAVEFILE;
|
||||
QTCREATOR_UTILS_EXPORT extern const Icon SAVEFILE_TOOLBAR;
|
||||
|
||||
QTCREATOR_UTILS_EXPORT extern const Icon EXPORTFILE_TOOLBAR;
|
||||
QTCREATOR_UTILS_EXPORT extern const Icon MULTIEXPORTFILE_TOOLBAR;
|
||||
|
||||
QTCREATOR_UTILS_EXPORT extern const Icon UNDO;
|
||||
QTCREATOR_UTILS_EXPORT extern const Icon UNDO_TOOLBAR;
|
||||
QTCREATOR_UTILS_EXPORT extern const Icon REDO;
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
void ImageView::exportMultiImages()
|
||||
{
|
||||
#ifndef QT_NO_SVG
|
||||
QTC_ASSERT(qgraphicsitem_cast<QGraphicsSvgItem *>(m_imageItem), return);
|
||||
|
||||
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)));
|
||||
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
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -55,6 +55,7 @@ public:
|
||||
IEditor *duplicate() override;
|
||||
|
||||
void exportImage();
|
||||
void exportMultiImages();
|
||||
void imageSizeUpdated(const QSize &size);
|
||||
void scaleFactorUpdate(qreal factor);
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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";
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -24,6 +24,13 @@
|
||||
</property>
|
||||
</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">
|
||||
@@ -134,8 +141,5 @@
|
||||
<header>coreplugin/actionmanager/commandbutton.h</header>
|
||||
</customwidget>
|
||||
</customwidgets>
|
||||
<resources>
|
||||
<include location="imageviewer.qrc"/>
|
||||
</resources>
|
||||
<connections/>
|
||||
</ui>
|
||||
|
||||
346
src/plugins/imageviewer/multiexportdialog.cpp
Normal file
346
src/plugins/imageviewer/multiexportdialog.cpp
Normal file
@@ -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
|
||||
79
src/plugins/imageviewer/multiexportdialog.h
Normal file
79
src/plugins/imageviewer/multiexportdialog.h
Normal file
@@ -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
|
||||
@@ -9068,4 +9068,40 @@
|
||||
inkscape:connector-curvature="0"
|
||||
sodipodi:nodetypes="ccccccccc" />
|
||||
</g>
|
||||
<g
|
||||
id="src/libs/utils/images/fileexport"
|
||||
transform="translate(80)">
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#backgroundRect"
|
||||
id="use5933-0-4"
|
||||
width="100%"
|
||||
height="100%"
|
||||
transform="translate(1740,132)" />
|
||||
<path
|
||||
sodipodi:nodetypes="ccccccccccc"
|
||||
inkscape:connector-curvature="0"
|
||||
d="m 1730,572 2.5,-2.5 2.5,2.5 m -2.5,-2 v 8 m -1.5,-4.5 h -3.5 v 8 h 10 v -8 h -3.5"
|
||||
style="fill:none;stroke:#000000"
|
||||
id="path6789-4" />
|
||||
</g>
|
||||
<g
|
||||
id="src/libs/utils/images/filemultiexport"
|
||||
transform="translate(96)">
|
||||
<use
|
||||
x="0"
|
||||
y="0"
|
||||
xlink:href="#backgroundRect"
|
||||
id="use5933-0"
|
||||
width="100%"
|
||||
height="100%"
|
||||
transform="translate(1740,132)" />
|
||||
<path
|
||||
sodipodi:nodetypes="ccccccccccccccccc"
|
||||
inkscape:connector-curvature="0"
|
||||
d="m 1730,572 2.5,-2.5 2.5,2.5 m -2.5,-2 v 7 m -4.5,-1.5 h -1.5 v 6 h 12 v -6 h -1.5 m -6,-2 h -2.5 v 6 h 8 v -6 h -2.5"
|
||||
style="fill:none;stroke:#000000"
|
||||
id="path6789" />
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 322 KiB After Width: | Height: | Size: 323 KiB |
Reference in New Issue
Block a user