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