aboutsummaryrefslogtreecommitdiffstats
path: root/src/plugins/imageviewer
diff options
context:
space:
mode:
authorFriedemann Kleint <Friedemann.Kleint@theqtcompany.com>2016-03-17 16:02:20 +0100
committerFriedemann Kleint <Friedemann.Kleint@theqtcompany.com>2016-03-21 08:51:52 +0000
commit44820dae1326476a6e1aa693421c61ae67bb13b0 (patch)
tree57c610639a41b7eecc4b6eec2d95c9bbd956548b /src/plugins/imageviewer
parente5262fba5fa705b97314715c2db2e0bb0e40236b (diff)
ImageViewer: Add option to export images from SVG.
Add a tool button showing a dialog with file name and size for exporting images from SVG. [ChangeLog][ImageViewer] Added option to export images from SVG. Change-Id: I84e04dc166e70b0359eba0f19703a75b882a2bc2 Reviewed-by: Leena Miettinen <riitta-leena.miettinen@theqtcompany.com> Reviewed-by: Alessandro Portale <alessandro.portale@theqtcompany.com>
Diffstat (limited to 'src/plugins/imageviewer')
-rw-r--r--src/plugins/imageviewer/exportdialog.cpp204
-rw-r--r--src/plugins/imageviewer/exportdialog.h71
-rw-r--r--src/plugins/imageviewer/imageview.cpp61
-rw-r--r--src/plugins/imageviewer/imageview.h1
-rw-r--r--src/plugins/imageviewer/imageviewer.cpp18
-rw-r--r--src/plugins/imageviewer/imageviewer.h1
-rw-r--r--src/plugins/imageviewer/imageviewer.pro2
-rw-r--r--src/plugins/imageviewer/imageviewerconstants.h1
-rw-r--r--src/plugins/imageviewer/imageviewerplugin.cpp7
-rw-r--r--src/plugins/imageviewer/imageviewertoolbar.ui7
10 files changed, 371 insertions, 2 deletions
diff --git a/src/plugins/imageviewer/exportdialog.cpp b/src/plugins/imageviewer/exportdialog.cpp
new file mode 100644
index 0000000000..b46b3da239
--- /dev/null
+++ b/src/plugins/imageviewer/exportdialog.cpp
@@ -0,0 +1,204 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 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 "exportdialog.h"
+
+#include <coreplugin/coreicons.h>
+
+#include <utils/pathchooser.h>
+
+#include <QApplication>
+#include <QDesktopWidget>
+#include <QDialogButtonBox>
+#include <QFileDialog>
+#include <QFormLayout>
+#include <QHBoxLayout>
+#include <QMessageBox>
+#include <QLabel>
+#include <QLineEdit>
+#include <QPushButton>
+#include <QSpinBox>
+#include <QToolButton>
+#include <QVBoxLayout>
+
+#include <QImageWriter>
+
+#include <QDebug>
+#include <QDir>
+#include <QFileInfo>
+#include <QMimeDatabase>
+#include <QMimeType>
+
+namespace ImageViewer {
+namespace Internal {
+
+enum { exportMinimumSize = 1, exportMaximumSize = 2000 };
+
+static QString imageNameFilterString()
+{
+ static QString result;
+ if (result.isEmpty()) {
+ QMimeDatabase mimeDatabase;
+ const QString separator = QStringLiteral(";;");
+ foreach (const QByteArray &mimeType, QImageWriter::supportedMimeTypes()) {
+ const QString filter = mimeDatabase.mimeTypeForName(QLatin1String(mimeType)).filterString();
+ if (!filter.isEmpty()) {
+ if (mimeType == QByteArrayLiteral("image/png")) {
+ if (!result.isEmpty())
+ result.prepend(separator);
+ result.prepend(filter);
+ } else {
+ if (!result.isEmpty())
+ result.append(separator);
+ result.append(filter);
+ }
+ }
+ }
+ }
+ return result;
+}
+
+ExportDialog::ExportDialog(QWidget *parent)
+ : QDialog(parent)
+ , m_pathChooser(new Utils::PathChooser(this))
+ , m_widthSpinBox(new QSpinBox(this))
+ , m_heightSpinBox(new QSpinBox(this))
+ , m_aspectRatio(1)
+{
+ typedef void (QSpinBox::*QSpinBoxIntSignal)(int);
+
+ 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(imageNameFilterString());
+ formLayout->addRow(tr("File:"), m_pathChooser);
+
+ QHBoxLayout *sizeLayout = new QHBoxLayout;
+ m_widthSpinBox->setMinimum(exportMinimumSize);
+ m_widthSpinBox->setMaximum(exportMaximumSize);
+ connect(m_widthSpinBox, static_cast<QSpinBoxIntSignal>(&QSpinBox::valueChanged),
+ this, &ExportDialog::exportWidthChanged);
+ sizeLayout->addWidget(m_widthSpinBox);
+ //: Multiplication, as in 32x32
+ sizeLayout->addWidget(new QLabel(tr("x")));
+ m_heightSpinBox->setMinimum(exportMinimumSize);
+ m_heightSpinBox->setMaximum(exportMaximumSize);
+ connect(m_heightSpinBox, static_cast<QSpinBoxIntSignal>(&QSpinBox::valueChanged),
+ this, &ExportDialog::exportHeightChanged);
+ sizeLayout->addWidget(m_heightSpinBox);
+ QToolButton *resetButton = new QToolButton(this);
+ resetButton->setIcon(QIcon(QStringLiteral(":/qt-project.org/styles/commonstyle/images/refresh-32.png")));
+ sizeLayout->addWidget(resetButton);
+ sizeLayout->addStretch();
+ connect(resetButton, &QAbstractButton::clicked, this, &ExportDialog::resetExportSize);
+ formLayout->addRow(tr("Size:"), sizeLayout);
+
+ 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 ExportDialog::accept()
+{
+ if (!m_pathChooser->isValid()) {
+ QMessageBox::warning(this, windowTitle(), m_pathChooser->errorMessage());
+ return;
+ }
+ const QString fileName = exportFileName();
+ if (QFileInfo::exists(fileName)) {
+ const QString question = tr("%1 already exists.\nWould you like to overwrite it?")
+ .arg(QDir::toNativeSeparators(fileName));
+ if (QMessageBox::question(this, windowTitle(), question, QMessageBox::Yes | QMessageBox::No) != QMessageBox::Yes)
+ return;
+ }
+ QDialog::accept();
+}
+
+QSize ExportDialog::exportSize() const
+{
+ return QSize(m_widthSpinBox->value(), m_heightSpinBox->value());
+}
+
+void ExportDialog::setExportSize(const QSize &size)
+{
+ m_defaultSize = size;
+ const QSizeF defaultSizeF(m_defaultSize);
+ m_aspectRatio = defaultSizeF.width() / defaultSizeF.height();
+ setExportWidthBlocked(size.width());
+ setExportHeightBlocked(size.height());
+}
+
+void ExportDialog::resetExportSize()
+{
+ setExportWidthBlocked(m_defaultSize.width());
+ setExportHeightBlocked(m_defaultSize.height());
+}
+
+void ExportDialog::setExportWidthBlocked(int width)
+{
+ if (m_widthSpinBox->value() != width) {
+ const bool blockSignals = m_widthSpinBox->blockSignals(true);
+ m_widthSpinBox->setValue(width);
+ m_widthSpinBox->blockSignals(blockSignals);
+ }
+}
+
+void ExportDialog::setExportHeightBlocked(int height)
+{
+ if (m_heightSpinBox->value() != height) {
+ const bool blockSignals = m_heightSpinBox->blockSignals(true);
+ m_heightSpinBox->setValue(height);
+ m_heightSpinBox->blockSignals(blockSignals);
+ }
+}
+
+void ExportDialog::exportWidthChanged(int width)
+{
+ const bool square = m_defaultSize.width() == m_defaultSize.height();
+ setExportHeightBlocked(square ? width : qRound(qreal(width) / m_aspectRatio));
+}
+
+void ExportDialog::exportHeightChanged(int height)
+{
+ const bool square = m_defaultSize.width() == m_defaultSize.height();
+ setExportWidthBlocked(square ? height : qRound(qreal(height) * m_aspectRatio));
+}
+
+QString ExportDialog::exportFileName() const
+{
+ return m_pathChooser->fileName().toString();
+}
+
+void ExportDialog::setExportFileName(const QString &f)
+{
+ m_pathChooser->setFileName(Utils::FileName::fromString(f));
+}
+
+} // namespace Internal
+} // namespace ImageViewer
diff --git a/src/plugins/imageviewer/exportdialog.h b/src/plugins/imageviewer/exportdialog.h
new file mode 100644
index 0000000000..2c929bf468
--- /dev/null
+++ b/src/plugins/imageviewer/exportdialog.h
@@ -0,0 +1,71 @@
+/****************************************************************************
+**
+** Copyright (C) 2016 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.
+**
+****************************************************************************/
+
+#ifndef EXPORTDIALOG_H
+#define EXPORTDIALOG_H
+
+#include <QDialog>
+
+QT_FORWARD_DECLARE_CLASS(QSpinBox)
+
+namespace Utils { class PathChooser; }
+
+namespace ImageViewer {
+namespace Internal {
+
+class ExportDialog : public QDialog
+{
+ Q_OBJECT
+public:
+ explicit ExportDialog(QWidget *parent = nullptr);
+
+ QSize exportSize() const;
+ void setExportSize(const QSize &);
+
+ QString exportFileName() const;
+ void setExportFileName(const QString &);
+
+ void accept() override;
+
+private slots:
+ void resetExportSize();
+ void exportWidthChanged(int width);
+ void exportHeightChanged(int height);
+
+private:
+ void setExportWidthBlocked(int width);
+ void setExportHeightBlocked(int height);
+
+ Utils::PathChooser *m_pathChooser;
+ QSpinBox *m_widthSpinBox;
+ QSpinBox *m_heightSpinBox;
+ QSize m_defaultSize;
+ qreal m_aspectRatio;
+};
+
+} // namespace Internal
+} // namespace ImageViewer
+
+#endif // EXPORTDIALOG_H
diff --git a/src/plugins/imageviewer/imageview.cpp b/src/plugins/imageviewer/imageview.cpp
index 707625ca70..fbfc7006cd 100644
--- a/src/plugins/imageviewer/imageview.cpp
+++ b/src/plugins/imageviewer/imageview.cpp
@@ -50,12 +50,29 @@
#include "imageview.h"
+#include "exportdialog.h"
#include "imageviewerfile.h"
+#include <coreplugin/messagemanager.h>
+
+#include <utils/fileutils.h>
+#include <utils/qtcassert.h>
+
+#include <QSvgRenderer>
+#include <QGraphicsSvgItem>
+
+#include <QMessageBox>
+#include <QGraphicsRectItem>
+
#include <QWheelEvent>
#include <QMouseEvent>
-#include <QGraphicsRectItem>
+#include <QImage>
+#include <QPainter>
#include <QPixmap>
+
+#include <QDir>
+#include <QFileInfo>
+
#include <qmath.h>
namespace ImageViewer {
@@ -137,6 +154,48 @@ void ImageView::drawBackground(QPainter *p, const QRectF &)
p->restore();
}
+void ImageView::exportImage()
+{
+#ifndef QT_NO_SVG
+ QGraphicsSvgItem *svgItem = qgraphicsitem_cast<QGraphicsSvgItem *>(m_imageItem);
+ 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);
+
+ while (true) {
+ if (exportDialog.exec() != QDialog::Accepted)
+ break;
+
+ 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)));
+ }
+ }
+#endif // !QT_NO_SVG
+}
+
void ImageView::setViewBackground(bool enable)
{
m_showBackground = enable;
diff --git a/src/plugins/imageviewer/imageview.h b/src/plugins/imageviewer/imageview.h
index 46bf3d66b0..b120dac26d 100644
--- a/src/plugins/imageviewer/imageview.h
+++ b/src/plugins/imageviewer/imageview.h
@@ -74,6 +74,7 @@ signals:
void imageSizeChanged(const QSize &size);
public slots:
+ void exportImage();
void setViewBackground(bool enable);
void setViewOutline(bool enable);
void zoomIn();
diff --git a/src/plugins/imageviewer/imageviewer.cpp b/src/plugins/imageviewer/imageviewer.cpp
index a2ad9225ca..44f86c44b9 100644
--- a/src/plugins/imageviewer/imageviewer.cpp
+++ b/src/plugins/imageviewer/imageviewer.cpp
@@ -100,10 +100,11 @@ void ImageViewer::ctor()
// toolbar
d->toolbar = new QWidget();
d->ui_toolbar.setupUi(d->toolbar);
+ d->ui_toolbar.toolButtonExportImage->setIcon(QIcon::fromTheme(QLatin1String("document-save"),
+ Core::Icons::SAVEFILE.icon()));
d->ui_toolbar.toolButtonZoomIn->setIcon(Core::Icons::PLUS.icon());
d->ui_toolbar.toolButtonZoomOut->setIcon(Core::Icons::MINUS.icon());
d->ui_toolbar.toolButtonFitToScreen->setIcon(Core::Icons::ZOOM.icon());
-
// icons update - try to use system theme
updateButtonIconByTheme(d->ui_toolbar.toolButtonZoomIn, QLatin1String("zoom-in"));
updateButtonIconByTheme(d->ui_toolbar.toolButtonZoomOut, QLatin1String("zoom-out"));
@@ -115,6 +116,7 @@ void ImageViewer::ctor()
// (photograph has outline - piece of paper)
updateButtonIconByTheme(d->ui_toolbar.toolButtonOutline, QLatin1String("emblem-photos"));
+ d->ui_toolbar.toolButtonExportImage->setCommandId(Constants::ACTION_EXPORT_IMAGE);
d->ui_toolbar.toolButtonZoomIn->setCommandId(Constants::ACTION_ZOOM_IN);
d->ui_toolbar.toolButtonZoomOut->setCommandId(Constants::ACTION_ZOOM_OUT);
d->ui_toolbar.toolButtonOriginalSize->setCommandId(Constants::ACTION_ORIGINAL_SIZE);
@@ -124,6 +126,8 @@ void ImageViewer::ctor()
d->ui_toolbar.toolButtonPlayPause->setCommandId(Constants::ACTION_TOGGLE_ANIMATION);
// connections
+ connect(d->ui_toolbar.toolButtonExportImage, &QAbstractButton::clicked,
+ d->imageView, &ImageView::exportImage);
connect(d->ui_toolbar.toolButtonZoomIn, &QAbstractButton::clicked,
d->imageView, &ImageView::zoomIn);
connect(d->ui_toolbar.toolButtonZoomOut, &QAbstractButton::clicked,
@@ -150,6 +154,12 @@ void ImageViewer::ctor()
this, &ImageViewer::updatePauseAction);
connect(d->imageView, &ImageView::scaleFactorChanged,
this, &ImageViewer::scaleFactorUpdate);
+
+ connect(d->file.data(), &ImageViewerFile::openFinished,
+ this, [this](bool success)
+ {
+ d->ui_toolbar.toolButtonExportImage->setEnabled(success && d->file->type() == ImageViewerFile::TypeSvg);
+ });
}
ImageViewer::~ImageViewer()
@@ -176,6 +186,12 @@ Core::IEditor *ImageViewer::duplicate()
return other;
}
+void ImageViewer::exportImage()
+{
+ if (d->file->type() == ImageViewerFile::TypeSvg)
+ d->ui_toolbar.toolButtonExportImage->click();
+}
+
void ImageViewer::imageSizeUpdated(const QSize &size)
{
QString imageSizeText;
diff --git a/src/plugins/imageviewer/imageviewer.h b/src/plugins/imageviewer/imageviewer.h
index 1e6ea4b248..5d65c6760a 100644
--- a/src/plugins/imageviewer/imageviewer.h
+++ b/src/plugins/imageviewer/imageviewer.h
@@ -56,6 +56,7 @@ public:
IEditor *duplicate() override;
public slots:
+ void exportImage();
void imageSizeUpdated(const QSize &size);
void scaleFactorUpdate(qreal factor);
diff --git a/src/plugins/imageviewer/imageviewer.pro b/src/plugins/imageviewer/imageviewer.pro
index df0bf03215..651fda1fb6 100644
--- a/src/plugins/imageviewer/imageviewer.pro
+++ b/src/plugins/imageviewer/imageviewer.pro
@@ -1,6 +1,7 @@
include(../../qtcreatorplugin.pri)
HEADERS += \
+ exportdialog.h \
imageviewerplugin.h \
imageviewerfactory.h \
imageviewerfile.h \
@@ -9,6 +10,7 @@ HEADERS += \
imageviewerconstants.h
SOURCES += \
+ exportdialog.cpp \
imageviewerplugin.cpp \
imageviewerfactory.cpp \
imageviewerfile.cpp \
diff --git a/src/plugins/imageviewer/imageviewerconstants.h b/src/plugins/imageviewer/imageviewerconstants.h
index fd3c75e2c8..3152295dd3 100644
--- a/src/plugins/imageviewer/imageviewerconstants.h
+++ b/src/plugins/imageviewer/imageviewerconstants.h
@@ -33,6 +33,7 @@ namespace Constants {
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_ZOOM_IN[] = "ImageViewer.ZoomIn";
const char ACTION_ZOOM_OUT[] = "ImageViewer.ZoomOut";
const char ACTION_ORIGINAL_SIZE[] = "ImageViewer.OriginalSize";
diff --git a/src/plugins/imageviewer/imageviewerplugin.cpp b/src/plugins/imageviewer/imageviewerplugin.cpp
index e0420f2bfc..12d0f86524 100644
--- a/src/plugins/imageviewer/imageviewerplugin.cpp
+++ b/src/plugins/imageviewer/imageviewerplugin.cpp
@@ -113,6 +113,13 @@ void ImageViewerPlugin::extensionsInitialized()
if (ImageViewer *iv = currentImageViewer())
iv->togglePlay();
});
+
+ a = registerNewAction(Constants::ACTION_EXPORT_IMAGE, tr("Export Image"),
+ QKeySequence());
+ connect(a, &QAction::triggered, this, [this]() {
+ if (ImageViewer *iv = currentImageViewer())
+ iv->exportImage();
+ });
}
QAction *ImageViewerPlugin::registerNewAction(Core::Id id,
diff --git a/src/plugins/imageviewer/imageviewertoolbar.ui b/src/plugins/imageviewer/imageviewertoolbar.ui
index 3c673fb871..0c86a3e585 100644
--- a/src/plugins/imageviewer/imageviewertoolbar.ui
+++ b/src/plugins/imageviewer/imageviewertoolbar.ui
@@ -18,6 +18,13 @@
<number>0</number>
</property>
<item>
+ <widget class="Core::CommandButton" name="toolButtonExportImage">
+ <property name="toolTipBase">
+ <string>Export as Image</string>
+ </property>
+ </widget>
+ </item>
+ <item>
<widget class="Core::CommandButton" name="toolButtonBackground">
<property name="toolTipBase">
<string>Show Background</string>