1 /****************************************************************************
2 **
3 ** Copyright (C) 2016 Denis Mingulov.
4 ** Copyright (C) 2016 The Qt Company Ltd.
5 ** Contact: https://www.qt.io/licensing/
6 **
7 ** This file is part of Qt Creator.
8 **
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and The Qt Company. For licensing terms
14 ** and conditions see https://www.qt.io/terms-conditions. For further
15 ** information use the contact form at https://www.qt.io/contact-us.
16 **
17 ** GNU General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU
19 ** General Public License version 3 as published by the Free Software
20 ** Foundation with exceptions as appearing in the file LICENSE.GPL3-EXCEPT
21 ** included in the packaging of this file. Please review the following
22 ** information to ensure the GNU General Public License requirements will
23 ** be met: https://www.gnu.org/licenses/gpl-3.0.html.
24 **
25 ****************************************************************************/
26
27 #include "imageviewerfile.h"
28 #include "imageviewer.h"
29 #include "imageviewerconstants.h"
30
31 #include <coreplugin/editormanager/documentmodel.h>
32 #include <utils/fileutils.h>
33 #include <utils/mimetypes/mimedatabase.h>
34 #include <utils/qtcassert.h>
35
36 #include <QFileInfo>
37 #include <QGraphicsPixmapItem>
38 #ifndef QT_NO_SVG
39 #include <QGraphicsSvgItem>
40 #endif
41 #include <QImageReader>
42 #include <QMovie>
43 #include <QPainter>
44 #include <QPixmap>
45
46 namespace ImageViewer {
47 namespace Internal {
48
49 class MovieItem : public QObject, public QGraphicsPixmapItem
50 {
51 public:
MovieItem(QMovie * movie)52 MovieItem(QMovie *movie)
53 : m_movie(movie)
54 {
55 setPixmap(m_movie->currentPixmap());
56 connect(m_movie, &QMovie::updated, this, [this](const QRectF &rect) {
57 update(rect);
58 });
59 }
60
paint(QPainter * painter,const QStyleOptionGraphicsItem *,QWidget *)61 void paint(QPainter *painter, const QStyleOptionGraphicsItem *, QWidget *) override
62 {
63 const bool smoothTransform = painter->worldTransform().m11() < 1;
64 painter->setRenderHint(QPainter::SmoothPixmapTransform, smoothTransform);
65 painter->drawPixmap(offset(), m_movie->currentPixmap());
66 }
67
68 private:
69 QMovie *m_movie;
70 };
71
ImageViewerFile()72 ImageViewerFile::ImageViewerFile()
73 {
74 setId(Constants::IMAGEVIEWER_ID);
75 connect(this, &ImageViewerFile::mimeTypeChanged, this, &ImageViewerFile::changed);
76 }
77
~ImageViewerFile()78 ImageViewerFile::~ImageViewerFile()
79 {
80 cleanUp();
81 }
82
open(QString * errorString,const Utils::FilePath & filePath,const Utils::FilePath & realfilePath)83 Core::IDocument::OpenResult ImageViewerFile::open(QString *errorString,
84 const Utils::FilePath &filePath,
85 const Utils::FilePath &realfilePath)
86 {
87 QTC_CHECK(filePath == realfilePath); // does not support auto save
88 OpenResult success = openImpl(errorString, filePath);
89 emit openFinished(success == OpenResult::Success);
90 return success;
91 }
92
openImpl(QString * errorString,const Utils::FilePath & filePath)93 Core::IDocument::OpenResult ImageViewerFile::openImpl(QString *errorString,
94 const Utils::FilePath &filePath)
95 {
96 cleanUp();
97
98 if (!filePath.isReadableFile())
99 return OpenResult::ReadError;
100
101 const QString &fileName = filePath.toString();
102 QByteArray format = QImageReader::imageFormat(fileName);
103 // if it is impossible to recognize a file format - file will not be open correctly
104 if (format.isEmpty()) {
105 if (errorString)
106 *errorString = tr("Image format not supported.");
107 return OpenResult::CannotHandle;
108 }
109
110 #ifndef QT_NO_SVG
111 if (format.startsWith("svg")) {
112 m_tempSvgItem = new QGraphicsSvgItem(fileName);
113 QRectF bound = m_tempSvgItem->boundingRect();
114 if (qFuzzyIsNull(bound.width()) && qFuzzyIsNull(bound.height())) {
115 delete m_tempSvgItem;
116 m_tempSvgItem = nullptr;
117 if (errorString)
118 *errorString = tr("Failed to read SVG image.");
119 return OpenResult::CannotHandle;
120 }
121 m_type = TypeSvg;
122 emit imageSizeChanged(m_tempSvgItem->boundingRect().size().toSize());
123 } else
124 #endif
125 if (QMovie::supportedFormats().contains(format)) {
126 m_type = TypeMovie;
127 m_movie = new QMovie(fileName, QByteArray(), this);
128 m_movie->setCacheMode(QMovie::CacheAll);
129 connect(
130 m_movie,
131 &QMovie::finished,
132 m_movie,
133 [this] {
134 if (m_movie->isValid())
135 m_movie->start();
136 },
137 Qt::QueuedConnection);
138 connect(m_movie, &QMovie::resized, this, &ImageViewerFile::imageSizeChanged);
139 m_movie->start();
140 m_isPaused = false; // force update
141 setPaused(true);
142 } else {
143 m_pixmap = new QPixmap(fileName);
144 if (m_pixmap->isNull()) {
145 if (errorString)
146 *errorString = tr("Failed to read image.");
147 delete m_pixmap;
148 m_pixmap = nullptr;
149 return OpenResult::CannotHandle;
150 }
151 m_type = TypePixmap;
152 emit imageSizeChanged(m_pixmap->size());
153 }
154
155 setFilePath(filePath);
156 setMimeType(Utils::mimeTypeForFile(filePath).name());
157 return OpenResult::Success;
158 }
159
reloadBehavior(ChangeTrigger state,ChangeType type) const160 Core::IDocument::ReloadBehavior ImageViewerFile::reloadBehavior(ChangeTrigger state, ChangeType type) const
161 {
162 if (type == TypeRemoved)
163 return BehaviorSilent;
164 if (type == TypeContents && state == TriggerInternal && !isModified())
165 return BehaviorSilent;
166 return BehaviorAsk;
167 }
168
reload(QString * errorString,Core::IDocument::ReloadFlag flag,Core::IDocument::ChangeType type)169 bool ImageViewerFile::reload(QString *errorString,
170 Core::IDocument::ReloadFlag flag,
171 Core::IDocument::ChangeType type)
172 {
173 Q_UNUSED(type)
174 if (flag == FlagIgnore)
175 return true;
176 emit aboutToReload();
177 bool success = (openImpl(errorString, filePath()) == OpenResult::Success);
178 emit reloadFinished(success);
179 return success;
180 }
181
isPaused() const182 bool ImageViewerFile::isPaused() const
183 {
184 return m_isPaused;
185 }
186
setPaused(bool paused)187 void ImageViewerFile::setPaused(bool paused)
188 {
189 if (!m_movie || m_isPaused == paused)
190 return;
191 m_isPaused = paused;
192 m_movie->setPaused(paused);
193 emit isPausedChanged(m_isPaused);
194 }
195
createGraphicsItem() const196 QGraphicsItem *ImageViewerFile::createGraphicsItem() const
197 {
198 QGraphicsItem *val = nullptr;
199 switch (m_type) {
200 case TypeInvalid:
201 break;
202 case TypeSvg:
203 #ifndef QT_NO_SVG
204 if (m_tempSvgItem) {
205 val = m_tempSvgItem;
206 m_tempSvgItem = nullptr;
207 } else {
208 val = new QGraphicsSvgItem(filePath().toString());
209 }
210 #endif
211 break;
212 case TypeMovie:
213 val = new MovieItem(m_movie);
214 break;
215 case TypePixmap: {
216 auto pixmapItem = new QGraphicsPixmapItem(*m_pixmap);
217 pixmapItem->setTransformationMode(Qt::SmoothTransformation);
218 val = pixmapItem;
219 break;
220 }
221 default:
222 break;
223 }
224 return val;
225 }
226
type() const227 ImageViewerFile::ImageType ImageViewerFile::type() const
228 {
229 return m_type;
230 }
231
updateVisibility()232 void ImageViewerFile::updateVisibility()
233 {
234 if (!m_movie || m_isPaused)
235 return;
236 bool visible = false;
237 foreach (Core::IEditor *editor, Core::DocumentModel::editorsForDocument(this)) {
238 if (editor->widget()->isVisible()) {
239 visible = true;
240 break;
241 }
242 }
243 m_movie->setPaused(!visible);
244 }
245
cleanUp()246 void ImageViewerFile::cleanUp()
247 {
248 delete m_pixmap;
249 m_pixmap = nullptr;
250 delete m_movie;
251 m_movie = nullptr;
252 #ifndef QT_NO_SVG
253 delete m_tempSvgItem;
254 m_tempSvgItem = nullptr;
255 #endif
256 m_type = TypeInvalid;
257 }
258
259 } // namespace Internal
260 } // namespace ImageViewer
261