diff options
-rw-r--r-- | src/plugins/imageviewer/exportdialog.cpp | 204 | ||||
-rw-r--r-- | src/plugins/imageviewer/exportdialog.h | 71 | ||||
-rw-r--r-- | src/plugins/imageviewer/imageview.cpp | 61 | ||||
-rw-r--r-- | src/plugins/imageviewer/imageview.h | 1 | ||||
-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 | 2 | ||||
-rw-r--r-- | src/plugins/imageviewer/imageviewerconstants.h | 1 | ||||
-rw-r--r-- | src/plugins/imageviewer/imageviewerplugin.cpp | 7 | ||||
-rw-r--r-- | src/plugins/imageviewer/imageviewertoolbar.ui | 7 |
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> |