KIO

kimagefilepreview.cpp
1 /*
2  This file is part of the KDE project
3  SPDX-FileCopyrightText: 2001 Martin R. Jones <[email protected]>
4  SPDX-FileCopyrightText: 2001 Carsten Pfeiffer <[email protected]>
5  SPDX-FileCopyrightText: 2008 Rafael Fernández López <[email protected]>
6 
7  SPDX-License-Identifier: LGPL-2.0-only
8 */
9 
10 #include "kimagefilepreview.h"
11 
12 #include <QCheckBox>
13 #include <QLabel>
14 #include <QPainter>
15 #include <QResizeEvent>
16 #include <QStyle>
17 #include <QTimeLine>
18 #include <QVBoxLayout>
19 
20 #include <KConfig>
21 #include <KConfigGroup>
22 #include <KIconLoader>
23 #include <KLocalizedString>
24 #include <kfileitem.h>
25 #include <kio/previewjob.h>
26 
27 /**** KImageFilePreview ****/
28 
29 class KImageFilePreviewPrivate
30 {
31 public:
32  KImageFilePreviewPrivate(KImageFilePreview *qq)
33  : q(qq)
34  {
35  if (q->style()->styleHint(QStyle::SH_Widget_Animate, nullptr, q)) {
36  m_timeLine = new QTimeLine(150, q);
37  m_timeLine->setEasingCurve(QEasingCurve::InCurve);
38  m_timeLine->setDirection(QTimeLine::Forward);
39  m_timeLine->setFrameRange(0, 100);
40  }
41  }
42 
43  void slotResult(KJob *);
44  void slotFailed(const KFileItem &);
45  void slotStepAnimation();
46  void slotFinished();
47  void slotActuallyClear();
48 
49  KImageFilePreview *q = nullptr;
50  QUrl currentURL;
51  QUrl lastShownURL;
52  QLabel *imageLabel;
53  KIO::PreviewJob *m_job = nullptr;
54  QTimeLine *m_timeLine = nullptr;
55  QPixmap m_pmCurrent;
56  QPixmap m_pmTransition;
57  float m_pmCurrentOpacity = 1;
58  float m_pmTransitionOpacity = 0;
59  bool clear = true;
60 };
61 
63  : KPreviewWidgetBase(parent)
64  , d(new KImageFilePreviewPrivate(this))
65 {
66  QVBoxLayout *vb = new QVBoxLayout(this);
67  vb->setContentsMargins(0, 0, 0, 0);
68 
69  d->imageLabel = new QLabel(this);
70  d->imageLabel->setAlignment(Qt::AlignHCenter | Qt::AlignVCenter);
71  d->imageLabel->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding));
72  vb->addWidget(d->imageLabel);
73 
74  setSupportedMimeTypes(KIO::PreviewJob::supportedMimeTypes());
75  setMinimumWidth(50);
76 
77  if (d->m_timeLine) {
78  connect(d->m_timeLine, &QTimeLine::frameChanged, this, [this]() {
79  d->slotStepAnimation();
80  });
81  connect(d->m_timeLine, &QTimeLine::finished, this, [this]() {
82  d->slotFinished();
83  });
84  }
85 }
86 
88 {
89  if (d->m_job) {
90  d->m_job->kill();
91  }
92 }
93 
94 void KImageFilePreview::showPreview()
95 {
96  // Pass a copy since clearPreview() will clear currentURL
97  QUrl url = d->currentURL;
98  showPreview(url, true);
99 }
100 
101 // called via KPreviewWidgetBase interface
103 {
104  showPreview(url, false);
105 }
106 
107 void KImageFilePreview::showPreview(const QUrl &url, bool force)
108 {
109  /* clang-format off */
110  if (!url.isValid()
111  || (d->lastShownURL.isValid()
112  && url.matches(d->lastShownURL, QUrl::StripTrailingSlash)
113  && d->currentURL.isValid())) {
114  return;
115  }
116  /* clang-format on*/
117 
118  d->clear = false;
119  d->currentURL = url;
120  d->lastShownURL = url;
121 
122  int w = d->imageLabel->contentsRect().width() - 4;
123  int h = d->imageLabel->contentsRect().height() - 4;
124 
125  if (d->m_job) {
126  disconnect(d->m_job, nullptr, this, nullptr);
127 
128  d->m_job->kill();
129  }
130 
131  d->m_job = createJob(url, w, h);
132  if (force) { // explicitly requested previews shall always be generated!
133  d->m_job->setIgnoreMaximumSize(true);
134  }
135 
136  connect(d->m_job, &KJob::result, this, [this](KJob *job) {
137  d->slotResult(job);
138  });
139  connect(d->m_job, &KIO::PreviewJob::gotPreview, this, &KImageFilePreview::gotPreview);
140  connect(d->m_job, &KIO::PreviewJob::failed, this, [this](const KFileItem &item) {
141  d->slotFailed(item);
142  });
143 }
144 
145 void KImageFilePreview::resizeEvent(QResizeEvent *)
146 {
147  // Nothing to do, if no current preview
148  if (d->imageLabel->pixmap().isNull()) {
149  return;
150  }
151 
152  clearPreview();
153  d->currentURL = QUrl(); // force this to actually happen
154  showPreview(d->lastShownURL);
155 }
156 
158 {
159  return QSize(100, 200);
160 }
161 
162 KIO::PreviewJob *KImageFilePreview::createJob(const QUrl &url, int w, int h)
163 {
164  if (!url.isValid()) {
165  return nullptr;
166  }
167 
168  KFileItemList items;
169  items.append(KFileItem(url));
171 
172  KIO::PreviewJob *previewJob = KIO::filePreview(items, QSize(w, h), &plugins);
173 #if KIOWIDGETS_BUILD_DEPRECATED_SINCE(5, 102)
174  previewJob->setOverlayIconAlpha(0);
175 #endif
177  return previewJob;
178 }
179 
180 void KImageFilePreview::gotPreview(const KFileItem &item, const QPixmap &pm)
181 {
182  if (item.url() != d->currentURL) { // Shouldn't happen
183  return;
184  }
185 
186  if (d->m_timeLine) {
187  if (d->m_timeLine->state() == QTimeLine::Running) {
188  d->m_timeLine->setCurrentTime(0);
189  }
190 
191  d->m_pmTransition = pm;
192  d->m_pmTransitionOpacity = 0;
193  d->m_pmCurrentOpacity = 1;
194  d->m_timeLine->setDirection(QTimeLine::Forward);
195  d->m_timeLine->start();
196  } else {
197  d->imageLabel->setPixmap(pm);
198  }
199 }
200 
201 void KImageFilePreviewPrivate::slotFailed(const KFileItem &item)
202 {
203  if (item.isDir()) {
204  imageLabel->clear();
205  } else if (item.url() == currentURL) { // should always be the case
206  imageLabel->setPixmap(QIcon::fromTheme(QStringLiteral("image-missing")).pixmap(KIconLoader::SizeLarge, QIcon::Disabled));
207  }
208 }
209 
210 void KImageFilePreviewPrivate::slotResult(KJob *job)
211 {
212  if (job == m_job) {
213  m_job = nullptr;
214  }
215 }
216 
217 void KImageFilePreviewPrivate::slotStepAnimation()
218 {
219  const QSize currSize = m_pmCurrent.size();
220  const QSize transitionSize = m_pmTransition.size();
221  const int width = std::max(currSize.width(), transitionSize.width());
222  const int height = std::max(currSize.height(), transitionSize.height());
223  QPixmap pm(QSize(width, height));
224  pm.fill(Qt::transparent);
225 
226  QPainter p(&pm);
227  p.setOpacity(m_pmCurrentOpacity);
228 
229  // If we have a current pixmap
230  if (!m_pmCurrent.isNull()) {
231  p.drawPixmap(QPoint(((float)pm.size().width() - m_pmCurrent.size().width()) / 2.0, ((float)pm.size().height() - m_pmCurrent.size().height()) / 2.0),
232  m_pmCurrent);
233  }
234  if (!m_pmTransition.isNull()) {
235  p.setOpacity(m_pmTransitionOpacity);
236  p.drawPixmap(
237  QPoint(((float)pm.size().width() - m_pmTransition.size().width()) / 2.0, ((float)pm.size().height() - m_pmTransition.size().height()) / 2.0),
238  m_pmTransition);
239  }
240  p.end();
241 
242  imageLabel->setPixmap(pm);
243 
244  m_pmCurrentOpacity = qMax(m_pmCurrentOpacity - 0.4, 0.0); // krazy:exclude=qminmax
245  m_pmTransitionOpacity = qMin(m_pmTransitionOpacity + 0.4, 1.0); // krazy:exclude=qminmax
246 }
247 
248 void KImageFilePreviewPrivate::slotFinished()
249 {
250  m_pmCurrent = m_pmTransition;
251  m_pmTransitionOpacity = 0;
252  m_pmCurrentOpacity = 1;
253  m_pmTransition = QPixmap();
254  // The animation might have lost some frames. Be sure that if the last one
255  // was dropped, the last image shown is the opaque one.
256  imageLabel->setPixmap(m_pmCurrent);
257  clear = false;
258 }
259 
261 {
262  if (d->m_job) {
263  d->m_job->kill();
264  d->m_job = nullptr;
265  }
266 
267  if (d->clear || (d->m_timeLine && d->m_timeLine->state() == QTimeLine::Running)) {
268  return;
269  }
270 
271  if (d->m_timeLine) {
272  d->m_pmTransition = QPixmap();
273  // If we add a previous preview then we run the animation
274  if (!d->m_pmCurrent.isNull()) {
275  d->m_timeLine->setCurrentTime(0);
276  d->m_timeLine->setDirection(QTimeLine::Backward);
277  d->m_timeLine->start();
278  }
279  d->currentURL.clear();
280  d->clear = true;
281  } else {
282  d->imageLabel->clear();
283  }
284 }
285 
286 #include "moc_kimagefilepreview.cpp"
void append(const T &value)
static QStringList supportedMimeTypes()
Returns a list of all supported MIME types.
AlignHCenter
~KImageFilePreview() override
Destroys the image file preview.
Abstract baseclass for all preview widgets.
QSize sizeHint() const override
Returns the size hint for this widget.
bool disconnect(const QObject *sender, const char *signal, const QObject *receiver, const char *method)
void result(KJob *job)
QSize size() const const
static QStringList availablePlugins()
Returns a list of all available preview plugins.
QIcon fromTheme(const QString &name)
@ Scaled
The preview will be scaled to the size specified when constructing the PreviewJob.
Definition: previewjob.h:50
void fill(const QColor &color)
int width() const const
void frameChanged(int frame)
QMetaObject::Connection connect(const QObject *sender, const char *signal, const QObject *receiver, const char *method, Qt::ConnectionType type)
bool matches(const QUrl &url, QUrl::FormattingOptions options) const const
void setScaleType(ScaleType type)
Sets the scale type for the generated preview.
Definition: previewjob.cpp:310
void addWidget(QWidget *widget, int stretch, Qt::Alignment alignment)
bool isValid() const const
KGuiItem clear()
int height() const const
StripTrailingSlash
void failed(const KFileItem &item)
Emitted when a thumbnail for item could not be created, either because a ThumbCreator for its MIME ty...
KImageFilePreview(QWidget *parent=nullptr)
Creates a new image file preview.
void clearPreview() override
Clears the preview.
SH_Widget_Animate
void setOverlayIconAlpha(int alpha)
Sets the alpha-value for the MIME-type icon which overlays the preview.
Definition: previewjob.cpp:295
KIO Job to get a thumbnail picture.
Definition: previewjob.h:30
void setContentsMargins(int left, int top, int right, int bottom)
void gotPreview(const KFileItem &item, const QPixmap &preview)
Emitted when a thumbnail picture for item has been successfully retrieved.
void setMinimumWidth(int minw)
void showPreview(const QUrl &url) override
Shows a preview for the given url.
KIOWIDGETS_EXPORT PreviewJob * filePreview(const KFileItemList &items, int width, int height=0, int iconSize=0, int iconAlpha=70, bool scale=true, bool save=true, const QStringList *enabledPlugins=nullptr)
Creates a PreviewJob to generate or retrieve a preview image for the given URL.
transparent
void finished()
This file is part of the KDE documentation.
Documentation copyright © 1996-2023 The KDE developers.
Generated on Sun Feb 5 2023 04:00:51 by doxygen 1.8.17 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.