1 /* ============================================================
2  *
3  * This file is a part of digiKam project
4  * https://www.digikam.org
5  *
6  * Date        : 2010-04-30
7  * Description : Graphics View items for DImg
8  *
9  * Copyright (C) 2010-2011 by Marcel Wiesweg <marcel dot wiesweg at gmx dot de>
10  *
11  * This program is free software; you can redistribute it
12  * and/or modify it under the terms of the GNU General
13  * Public License as published by the Free Software Foundation;
14  * either version 2, or (at your option)
15  * any later version.
16  *
17  * This program is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
20  * GNU General Public License for more details.
21  *
22  * ============================================================ */
23 
24 #include "dimgpreviewitem.h"
25 #include "dimgitems_p.h"
26 
27 // Qt includes
28 
29 #include <QApplication>
30 #include <QScreen>
31 
32 // KDE includes
33 
34 #include <klocalizedstring.h>
35 
36 // Local includes
37 
38 #include "iccsettings.h"
39 #include "loadingcacheinterface.h"
40 #include "loadingdescription.h"
41 #include "previewloadthread.h"
42 #include "previewsettings.h"
43 
44 namespace Digikam
45 {
46 
DImgPreviewItem(QGraphicsItem * const parent)47 DImgPreviewItem::DImgPreviewItem(QGraphicsItem* const parent)
48     : GraphicsDImgItem(*new DImgPreviewItemPrivate, parent)
49 {
50     Q_D(DImgPreviewItem);
51 
52     d->init(this);
53 }
54 
DImgPreviewItem(DImgPreviewItemPrivate & dd,QGraphicsItem * const parent)55 DImgPreviewItem::DImgPreviewItem(DImgPreviewItemPrivate& dd, QGraphicsItem* const parent)
56     : GraphicsDImgItem(dd, parent)
57 {
58     Q_D(DImgPreviewItem);
59 
60     d->init(this);
61 }
62 
DImgPreviewItemPrivate()63 DImgPreviewItem::DImgPreviewItemPrivate::DImgPreviewItemPrivate()
64     : state         (DImgPreviewItem::NoImage),
65       exifRotate    (false),
66       previewSize   (1024),
67       previewThread (nullptr),
68       preloadThread (nullptr)
69 {
70 }
71 
init(DImgPreviewItem * const q)72 void DImgPreviewItem::DImgPreviewItemPrivate::init(DImgPreviewItem* const q)
73 {
74     previewThread = new PreviewLoadThread;
75     preloadThread = new PreviewLoadThread;
76     preloadThread->setPriority(QThread::LowPriority);
77 
78     QObject::connect(previewThread, SIGNAL(signalImageLoaded(LoadingDescription,DImg)),
79                      q, SLOT(slotGotImagePreview(LoadingDescription,DImg)));
80 
81     QObject::connect(preloadThread, SIGNAL(signalImageLoaded(LoadingDescription,DImg)),
82                      q, SLOT(preloadNext()));
83 
84     // get preview size from screen size, but limit from VGA to WQXGA
85 
86     previewSize = qBound(640,
87                          qMax(qApp->primaryScreen()->availableGeometry().height(),
88                               qApp->primaryScreen()->availableGeometry().width()),
89                          2560);
90 
91     LoadingCacheInterface::connectToSignalFileChanged(q, SLOT(slotFileChanged(QString)));
92 
93     QObject::connect(IccSettings::instance(), SIGNAL(signalICCSettingsChanged(ICCSettingsContainer,ICCSettingsContainer)),
94                      q, SLOT(iccSettingsChanged(ICCSettingsContainer,ICCSettingsContainer)));
95 }
96 
~DImgPreviewItem()97 DImgPreviewItem::~DImgPreviewItem()
98 {
99     Q_D(DImgPreviewItem);
100 
101     delete d->previewThread;
102     delete d->preloadThread;
103 }
104 
setDisplayingWidget(QWidget * const widget)105 void DImgPreviewItem::setDisplayingWidget(QWidget* const widget)
106 {
107     Q_D(DImgPreviewItem);
108 
109     d->previewThread->setDisplayingWidget(widget);
110 }
111 
setPreviewSettings(const PreviewSettings & settings)112 void DImgPreviewItem::setPreviewSettings(const PreviewSettings& settings)
113 {
114     Q_D(DImgPreviewItem);
115 
116     if (settings == d->previewSettings)
117     {
118         return;
119     }
120 
121     d->previewSettings = settings;
122     reload();
123 }
124 
path() const125 QString DImgPreviewItem::path() const
126 {
127     Q_D(const DImgPreviewItem);
128 
129     return d->path;
130 }
131 
setPath(const QString & path,bool rePreview)132 void DImgPreviewItem::setPath(const QString& path, bool rePreview)
133 {
134     Q_D(DImgPreviewItem);
135 
136     if ((path == d->path) && !rePreview)
137     {
138         return;
139     }
140 
141     d->path = path;
142 
143     if (d->path.isNull())
144     {
145         d->state = NoImage;
146         emit stateChanged(d->state);
147     }
148     else
149     {
150         d->state = Loading;
151         d->previewThread->load(d->path, d->previewSettings, d->previewSize);
152 
153         emit stateChanged(d->state);
154     }
155 
156     d->preloadThread->stopLoading();
157 }
158 
setPreloadPaths(const QStringList & pathsToPreload)159 void DImgPreviewItem::setPreloadPaths(const QStringList& pathsToPreload)
160 {
161     Q_D(DImgPreviewItem);
162 
163     d->pathsToPreload = pathsToPreload;
164     preloadNext();
165 }
166 
approximates(const QSizeF & s1,const QSizeF & s2)167 static bool approximates(const QSizeF& s1, const QSizeF& s2)
168 {
169     if (s1 == s2)
170     {
171         return true;
172     }
173 
174     double widthRatio = s1.width() / s2.width();
175 
176     if ((widthRatio < 0.98) || (widthRatio > 1.02))
177     {
178         return false;
179     }
180 
181     double heightRatio = s1.height() / s2.height();
182 
183     if ((heightRatio < 0.98) || (heightRatio > 1.02))
184     {
185         return false;
186     }
187 
188     return true;
189 }
190 
userLoadingHint() const191 QString DImgPreviewItem::userLoadingHint() const
192 {
193     Q_D(const DImgPreviewItem);
194 
195     switch (d->state)
196     {
197         case NoImage:
198         {
199             return QString();
200         }
201 
202         case Loading:
203         {
204             return i18n("Loading...");
205         }
206 
207         case ImageLoaded:
208         {
209             if (d->image.detectedFormat() == DImg::RAW)
210             {
211                 if (d->image.attribute(QLatin1String("fromRawEmbeddedPreview")).toBool())
212                 {
213                     return i18n("Embedded JPEG Preview");
214                 }
215                 else
216                 {
217                     return i18n("Half Size Raw Preview");
218                 }
219             }
220             else
221             {
222                 if (approximates(d->image.originalSize(), d->image.size()))
223                 {
224                     //return i18n("Full Size Preview");
225                     return QString();
226                 }
227                 else
228                 {
229                     return i18n("Reduced Size Preview");
230                 }
231             }
232 
233             return QString();   // To please compiler without warnings.
234         }
235 
236         default: // ImageLoadingFailed:
237         {
238             break;
239         }
240     }
241 
242     return i18n("Failed to load image");
243 }
244 
reload()245 void DImgPreviewItem::reload()
246 {
247     Q_D(DImgPreviewItem);
248 
249     QString path = d->path;
250     d->path.clear();
251     setPath(path);
252 }
253 
state() const254 DImgPreviewItem::State DImgPreviewItem::state() const
255 {
256     Q_D(const DImgPreviewItem);
257 
258     return d->state;
259 }
260 
isLoaded() const261 bool DImgPreviewItem::isLoaded() const
262 {
263     Q_D(const DImgPreviewItem);
264 
265     return (d->state == ImageLoaded);
266 }
267 
slotGotImagePreview(const LoadingDescription & description,const DImg & image)268 void DImgPreviewItem::slotGotImagePreview(const LoadingDescription& description, const DImg& image)
269 {
270     Q_D(DImgPreviewItem);
271 
272     if ((description.filePath != d->path) || description.isThumbnail())
273     {
274         return;
275     }
276 
277     if (image.isNull())
278     {
279         setImage(DImg());
280 
281         d->state = ImageLoadingFailed;
282         emit stateChanged(d->state);
283         emit loadingFailed();
284     }
285     else
286     {
287         setImage(image);
288 
289         d->state = ImageLoaded;
290         emit stateChanged(d->state);
291         emit loaded();
292     }
293 
294     preloadNext();
295 }
296 
preloadNext()297 void DImgPreviewItem::preloadNext()
298 {
299     Q_D(DImgPreviewItem);
300 
301     if (!isLoaded() || d->pathsToPreload.isEmpty())
302     {
303         return;
304     }
305 
306     QString preloadPath = d->pathsToPreload.takeFirst();
307     d->preloadThread->load(preloadPath, d->previewSettings, d->previewSize);
308 }
309 
slotFileChanged(const QString & path)310 void DImgPreviewItem::slotFileChanged(const QString& path)
311 {
312     Q_D(DImgPreviewItem);
313 
314     if (d->path == path)
315     {
316         reload();
317     }
318 }
319 
iccSettingsChanged(const ICCSettingsContainer & current,const ICCSettingsContainer & previous)320 void DImgPreviewItem::iccSettingsChanged(const ICCSettingsContainer& current, const ICCSettingsContainer& previous)
321 {
322     if ((current.enableCM != previous.enableCM)                     ||
323         (current.useManagedPreviews != previous.useManagedPreviews) ||
324         (current.monitorProfile != previous.monitorProfile))
325     {
326         reload();
327     }
328 }
329 
330 } // namespace Digikam
331