1/****************************************************************************
2**
3** Copyright (C) 2016 The Qt Company Ltd.
4** Copyright (C) 2015 Olivier Goffart <ogoffart@woboq.com>
5** Contact: https://www.qt.io/licensing/
6**
7** This file is part of the QtGui module of the Qt Toolkit.
8**
9** $QT_BEGIN_LICENSE:LGPL$
10** Commercial License Usage
11** Licensees holding valid commercial Qt licenses may use this file in
12** accordance with the commercial license agreement provided with the
13** Software or, alternatively, in accordance with the terms contained in
14** a written agreement between you and The Qt Company. For licensing terms
15** and conditions see https://www.qt.io/terms-conditions. For further
16** information use the contact form at https://www.qt.io/contact-us.
17**
18** GNU Lesser General Public License Usage
19** Alternatively, this file may be used under the terms of the GNU Lesser
20** General Public License version 3 as published by the Free Software
21** Foundation and appearing in the file LICENSE.LGPL3 included in the
22** packaging of this file. Please review the following information to
23** ensure the GNU Lesser General Public License version 3 requirements
24** will be met: https://www.gnu.org/licenses/lgpl-3.0.html.
25**
26** GNU General Public License Usage
27** Alternatively, this file may be used under the terms of the GNU
28** General Public License version 2.0 or (at your option) the GNU General
29** Public license version 3 or any later version approved by the KDE Free
30** Qt Foundation. The licenses are as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL2 and LICENSE.GPL3
32** included in the packaging of this file. Please review the following
33** information to ensure the GNU General Public License requirements will
34** be met: https://www.gnu.org/licenses/gpl-2.0.html and
35** https://www.gnu.org/licenses/gpl-3.0.html.
36**
37** $QT_END_LICENSE$
38**
39****************************************************************************/
40
41#include "qicon.h"
42#include "qicon_p.h"
43#include "qiconengine.h"
44#include "qiconengineplugin.h"
45#include "qimagereader.h"
46#include "private/qfactoryloader_p.h"
47#include "private/qiconloader_p.h"
48#include "qpainter.h"
49#include "qfileinfo.h"
50#include <qmimedatabase.h>
51#include <qmimetype.h>
52#include "qpixmapcache.h"
53#include "qvariant.h"
54#include "qcache.h"
55#include "qdebug.h"
56#include "qdir.h"
57#include "qpalette.h"
58#include "qmath.h"
59
60#include "private/qhexstring_p.h"
61#include "private/qguiapplication_p.h"
62#include "qpa/qplatformtheme.h"
63
64#ifndef QT_NO_ICON
65QT_BEGIN_NAMESPACE
66
67/*!
68 \enum QIcon::Mode
69
70 This enum type describes the mode for which a pixmap is intended
71 to be used. The currently defined modes are:
72
73 \value Normal
74 Display the pixmap when the user is
75 not interacting with the icon, but the
76 functionality represented by the icon is available.
77 \value Disabled
78 Display the pixmap when the
79 functionality represented by the icon is not available.
80 \value Active
81 Display the pixmap when the
82 functionality represented by the icon is available and
83 the user is interacting with the icon, for example, moving the
84 mouse over it or clicking it.
85 \value Selected
86 Display the pixmap when the item represented by the icon is
87 selected.
88*/
89
90/*!
91 \enum QIcon::State
92
93 This enum describes the state for which a pixmap is intended to be
94 used. The \e state can be:
95
96 \value Off Display the pixmap when the widget is in an "off" state
97 \value On Display the pixmap when the widget is in an "on" state
98*/
99
100static int nextSerialNumCounter()
101{
102 static QBasicAtomicInt serial = Q_BASIC_ATOMIC_INITIALIZER(0);
103 return 1 + serial.fetchAndAddRelaxed(1);
104}
105
106static void qt_cleanup_icon_cache();
107namespace {
108 struct IconCache : public QCache<QString, QIcon>
109 {
110 IconCache()
111 {
112 // ### note: won't readd if QApplication is re-created!
113 qAddPostRoutine(qt_cleanup_icon_cache);
114 }
115 };
116}
117
118Q_GLOBAL_STATIC(IconCache, qtIconCache)
119
120static void qt_cleanup_icon_cache()
121{
122 qtIconCache()->clear();
123}
124
125/*! \internal
126
127 Returns the effective device pixel ratio, using
128 the provided window pointer if possible.
129
130 if Qt::AA_UseHighDpiPixmaps is not set this function
131 returns 1.0 to keep non-hihdpi aware code working.
132*/
133static qreal qt_effective_device_pixel_ratio(QWindow *window = 0)
134{
135 if (!qApp->testAttribute(Qt::AA_UseHighDpiPixmaps))
136 return qreal(1.0);
137
138 if (window)
139 return window->devicePixelRatio();
140
141 return qApp->devicePixelRatio(); // Don't know which window to target.
142}
143
144QIconPrivate::QIconPrivate(QIconEngine *e)
145 : engine(e), ref(1),
146 serialNum(nextSerialNumCounter()),
147 detach_no(0),
148 is_mask(false)
149{
150}
151
152/*! \internal
153 Computes the displayDevicePixelRatio for a pixmap.
154
155 If displayDevicePixelRatio is 1.0 the reurned value is 1.0, always.
156
157 For a displayDevicePixelRatio of 2.0 the returned value will be between
158 1.0 and 2.0, depending on requestedSize and actualsize:
159 * If actualsize < requestedSize : 1.0 (not enough pixels for a normal-dpi pixmap)
160 * If actualsize == requestedSize * 2.0 : 2.0 (enough pixels for a high-dpi pixmap)
161 * else : a scaled value between 1.0 and 2.0. (pixel count is between normal-dpi and high-dpi)
162*/
163qreal QIconPrivate::pixmapDevicePixelRatio(qreal displayDevicePixelRatio, const QSize &requestedSize, const QSize &actualSize)
164{
165 QSize targetSize = requestedSize * displayDevicePixelRatio;
166 qreal scale = 0.5 * (qreal(actualSize.width()) / qreal(targetSize.width()) +
167 qreal(actualSize.height() / qreal(targetSize.height())));
168 return qMax(qreal(1.0), displayDevicePixelRatio *scale);
169}
170
171QPixmapIconEngine::QPixmapIconEngine()
172{
173}
174
175QPixmapIconEngine::QPixmapIconEngine(const QPixmapIconEngine &other)
176 : QIconEngine(other), pixmaps(other.pixmaps)
177{
178}
179
180QPixmapIconEngine::~QPixmapIconEngine()
181{
182}
183
184void QPixmapIconEngine::paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state)
185{
186 QSize pixmapSize = rect.size() * qt_effective_device_pixel_ratio(0);
187 QPixmap px = pixmap(pixmapSize, mode, state);
188 painter->drawPixmap(rect, px);
189}
190
191static inline int area(const QSize &s) { return s.width() * s.height(); }
192
193// returns the smallest of the two that is still larger than or equal to size.
194static QPixmapIconEngineEntry *bestSizeMatch( const QSize &size, QPixmapIconEngineEntry *pa, QPixmapIconEngineEntry *pb)
195{
196 int s = area(size);
197 if (pa->size == QSize() && pa->pixmap.isNull()) {
198 pa->pixmap = QPixmap(pa->fileName);
199 pa->size = pa->pixmap.size();
200 }
201 int a = area(pa->size);
202 if (pb->size == QSize() && pb->pixmap.isNull()) {
203 pb->pixmap = QPixmap(pb->fileName);
204 pb->size = pb->pixmap.size();
205 }
206 int b = area(pb->size);
207 int res = a;
208 if (qMin(a,b) >= s)
209 res = qMin(a,b);
210 else
211 res = qMax(a,b);
212 if (res == a)
213 return pa;
214 return pb;
215}
216
217QPixmapIconEngineEntry *QPixmapIconEngine::tryMatch(const QSize &size, QIcon::Mode mode, QIcon::State state)
218{
219 QPixmapIconEngineEntry *pe = 0;
220 for (int i = 0; i < pixmaps.count(); ++i)
221 if (pixmaps.at(i).mode == mode && pixmaps.at(i).state == state) {
222 if (pe)
223 pe = bestSizeMatch(size, &pixmaps[i], pe);
224 else
225 pe = &pixmaps[i];
226 }
227 return pe;
228}
229
230
231QPixmapIconEngineEntry *QPixmapIconEngine::bestMatch(const QSize &size, QIcon::Mode mode, QIcon::State state, bool sizeOnly)
232{
233 QPixmapIconEngineEntry *pe = tryMatch(size, mode, state);
234 while (!pe){
235 QIcon::State oppositeState = (state == QIcon::On) ? QIcon::Off : QIcon::On;
236 if (mode == QIcon::Disabled || mode == QIcon::Selected) {
237 QIcon::Mode oppositeMode = (mode == QIcon::Disabled) ? QIcon::Selected : QIcon::Disabled;
238 if ((pe = tryMatch(size, QIcon::Normal, state)))
239 break;
240 if ((pe = tryMatch(size, QIcon::Active, state)))
241 break;
242 if ((pe = tryMatch(size, mode, oppositeState)))
243 break;
244 if ((pe = tryMatch(size, QIcon::Normal, oppositeState)))
245 break;
246 if ((pe = tryMatch(size, QIcon::Active, oppositeState)))
247 break;
248 if ((pe = tryMatch(size, oppositeMode, state)))
249 break;
250 if ((pe = tryMatch(size, oppositeMode, oppositeState)))
251 break;
252 } else {
253 QIcon::Mode oppositeMode = (mode == QIcon::Normal) ? QIcon::Active : QIcon::Normal;
254 if ((pe = tryMatch(size, oppositeMode, state)))
255 break;
256 if ((pe = tryMatch(size, mode, oppositeState)))
257 break;
258 if ((pe = tryMatch(size, oppositeMode, oppositeState)))
259 break;
260 if ((pe = tryMatch(size, QIcon::Disabled, state)))
261 break;
262 if ((pe = tryMatch(size, QIcon::Selected, state)))
263 break;
264 if ((pe = tryMatch(size, QIcon::Disabled, oppositeState)))
265 break;
266 if ((pe = tryMatch(size, QIcon::Selected, oppositeState)))
267 break;
268 }
269
270 if (!pe)
271 return pe;
272 }
273
274 if (sizeOnly ? (pe->size.isNull() || !pe->size.isValid()) : pe->pixmap.isNull()) {
275 pe->pixmap = QPixmap(pe->fileName);
276 if (!pe->pixmap.isNull())
277 pe->size = pe->pixmap.size();
278 }
279
280 return pe;
281}
282
283QPixmap QPixmapIconEngine::pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state)
284{
285 QPixmap pm;
286 QPixmapIconEngineEntry *pe = bestMatch(size, mode, state, false);
287 if (pe)
288 pm = pe->pixmap;
289
290 if (pm.isNull()) {
291 int idx = pixmaps.count();
292 while (--idx >= 0) {
293 if (pe == &pixmaps.at(idx)) {
294 pixmaps.remove(idx);
295 break;
296 }
297 }
298 if (pixmaps.isEmpty())
299 return pm;
300 else
301 return pixmap(size, mode, state);
302 }
303
304 QSize actualSize = pm.size();
305 if (!actualSize.isNull() && (actualSize.width() > size.width() || actualSize.height() > size.height()))
306 actualSize.scale(size, Qt::KeepAspectRatio);
307
308 QString key = QLatin1String("qt_")
309 % HexString<quint64>(pm.cacheKey())
310 % HexString<uint>(pe ? pe->mode : QIcon::Normal)
311 % HexString<quint64>(QGuiApplication::palette().cacheKey())
312 % HexString<uint>(actualSize.width())
313 % HexString<uint>(actualSize.height());
314
315 if (mode == QIcon::Active) {
316 if (QPixmapCache::find(key % HexString<uint>(mode), pm))
317 return pm; // horray
318 if (QPixmapCache::find(key % HexString<uint>(QIcon::Normal), pm)) {
319 QPixmap active = pm;
320 if (QGuiApplication *guiApp = qobject_cast<QGuiApplication *>(qApp))
321 active = static_cast<QGuiApplicationPrivate*>(QObjectPrivate::get(guiApp))->applyQIconStyleHelper(QIcon::Active, pm);
322 if (pm.cacheKey() == active.cacheKey())
323 return pm;
324 }
325 }
326
327 if (!QPixmapCache::find(key % HexString<uint>(mode), pm)) {
328 if (pm.size() != actualSize)
329 pm = pm.scaled(actualSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
330 if (pe->mode != mode && mode != QIcon::Normal) {
331 QPixmap generated = pm;
332 if (QGuiApplication *guiApp = qobject_cast<QGuiApplication *>(qApp))
333 generated = static_cast<QGuiApplicationPrivate*>(QObjectPrivate::get(guiApp))->applyQIconStyleHelper(mode, pm);
334 if (!generated.isNull())
335 pm = generated;
336 }
337 QPixmapCache::insert(key % HexString<uint>(mode), pm);
338 }
339 return pm;
340}
341
342QSize QPixmapIconEngine::actualSize(const QSize &size, QIcon::Mode mode, QIcon::State state)
343{
344 QSize actualSize;
345 if (QPixmapIconEngineEntry *pe = bestMatch(size, mode, state, true))
346 actualSize = pe->size;
347
348 if (actualSize.isNull())
349 return actualSize;
350
351 if (!actualSize.isNull() && (actualSize.width() > size.width() || actualSize.height() > size.height()))
352 actualSize.scale(size, Qt::KeepAspectRatio);
353 return actualSize;
354}
355
356void QPixmapIconEngine::addPixmap(const QPixmap &pixmap, QIcon::Mode mode, QIcon::State state)
357{
358 if (!pixmap.isNull()) {
359 QPixmapIconEngineEntry *pe = tryMatch(pixmap.size(), mode, state);
360 if(pe && pe->size == pixmap.size()) {
361 pe->pixmap = pixmap;
362 pe->fileName.clear();
363 } else {
364 pixmaps += QPixmapIconEngineEntry(pixmap, mode, state);
365 }
366 }
367}
368
369// Read out original image depth as set by ICOReader
370static inline int origIcoDepth(const QImage &image)
371{
372 const QString s = image.text(QStringLiteral("_q_icoOrigDepth"));
373 return s.isEmpty() ? 32 : s.toInt();
374}
375
376static inline int findBySize(const QVector<QImage> &images, const QSize &size)
377{
378 for (int i = 0; i < images.size(); ++i) {
379 if (images.at(i).size() == size)
380 return i;
381 }
382 return -1;
383}
384
385// Convenience class providing a bool read() function.
386namespace {
387class ImageReader
388{
389public:
390 ImageReader(const QString &fileName) : m_reader(fileName), m_atEnd(false) {}
391
392 QByteArray format() const { return m_reader.format(); }
393
394 bool read(QImage *image)
395 {
396 if (m_atEnd)
397 return false;
398 *image = m_reader.read();
399 if (!image->size().isValid()) {
400 m_atEnd = true;
401 return false;
402 }
403 m_atEnd = !m_reader.jumpToNextImage();
404 return true;
405 }
406
407private:
408 QImageReader m_reader;
409 bool m_atEnd;
410};
411} // namespace
412
413void QPixmapIconEngine::addFile(const QString &fileName, const QSize &size, QIcon::Mode mode, QIcon::State state)
414{
415 if (fileName.isEmpty())
416 return;
417 const QString abs = fileName.startsWith(QLatin1Char(':')) ? fileName : QFileInfo(fileName).absoluteFilePath();
418 const bool ignoreSize = !size.isValid();
419 ImageReader imageReader(abs);
420 const QByteArray format = imageReader.format();
421 if (format.isEmpty()) // Device failed to open or unsupported format.
422 return;
423 QImage image;
424 if (format != "ico") {
425 if (ignoreSize) { // No size specified: Add all images.
426 while (imageReader.read(&image))
427 pixmaps += QPixmapIconEngineEntry(abs, image, mode, state);
428 } else {
429 // Try to match size. If that fails, add a placeholder with the filename and empty pixmap for the size.
430 while (imageReader.read(&image) && image.size() != size) {}
431 pixmaps += image.size() == size ?
432 QPixmapIconEngineEntry(abs, image, mode, state) : QPixmapIconEngineEntry(abs, size, mode, state);
433 }
434 return;
435 }
436 // Special case for reading Windows ".ico" files. Historically (QTBUG-39287),
437 // these files may contain low-resolution images. As this information is lost,
438 // ICOReader sets the original format as an image text key value. Read all matching
439 // images into a list trying to find the highest quality per size.
440 QVector<QImage> icoImages;
441 while (imageReader.read(&image)) {
442 if (ignoreSize || image.size() == size) {
443 const int position = findBySize(icoImages, image.size());
444 if (position >= 0) { // Higher quality available? -> replace.
445 if (origIcoDepth(image) > origIcoDepth(icoImages.at(position)))
446 icoImages[position] = image;
447 } else {
448 icoImages.append(image);
449 }
450 }
451 }
452 for (const QImage &i : qAsConst(icoImages))
453 pixmaps += QPixmapIconEngineEntry(abs, i, mode, state);
454 if (icoImages.isEmpty() && !ignoreSize) // Add placeholder with the filename and empty pixmap for the size.
455 pixmaps += QPixmapIconEngineEntry(abs, size, mode, state);
456}
457
458QString QPixmapIconEngine::key() const
459{
460 return QLatin1String("QPixmapIconEngine");
461}
462
463QIconEngine *QPixmapIconEngine::clone() const
464{
465 return new QPixmapIconEngine(*this);
466}
467
468bool QPixmapIconEngine::read(QDataStream &in)
469{
470 int num_entries;
471 QPixmap pm;
472 QString fileName;
473 QSize sz;
474 uint mode;
475 uint state;
476
477 in >> num_entries;
478 for (int i=0; i < num_entries; ++i) {
479 if (in.atEnd()) {
480 pixmaps.clear();
481 return false;
482 }
483 in >> pm;
484 in >> fileName;
485 in >> sz;
486 in >> mode;
487 in >> state;
488 if (pm.isNull()) {
489 addFile(fileName, sz, QIcon::Mode(mode), QIcon::State(state));
490 } else {
491 QPixmapIconEngineEntry pe(fileName, sz, QIcon::Mode(mode), QIcon::State(state));
492 pe.pixmap = pm;
493 pixmaps += pe;
494 }
495 }
496 return true;
497}
498
499bool QPixmapIconEngine::write(QDataStream &out) const
500{
501 int num_entries = pixmaps.size();
502 out << num_entries;
503 for (int i=0; i < num_entries; ++i) {
504 if (pixmaps.at(i).pixmap.isNull())
505 out << QPixmap(pixmaps.at(i).fileName);
506 else
507 out << pixmaps.at(i).pixmap;
508 out << pixmaps.at(i).fileName;
509 out << pixmaps.at(i).size;
510 out << (uint) pixmaps.at(i).mode;
511 out << (uint) pixmaps.at(i).state;
512 }
513 return true;
514}
515
516void QPixmapIconEngine::virtual_hook(int id, void *data)
517{
518 switch (id) {
519 case QIconEngine::AvailableSizesHook: {
520 QIconEngine::AvailableSizesArgument &arg =
521 *reinterpret_cast<QIconEngine::AvailableSizesArgument*>(data);
522 arg.sizes.clear();
523 for (int i = 0; i < pixmaps.size(); ++i) {
524 QPixmapIconEngineEntry &pe = pixmaps[i];
525 if (pe.size == QSize() && pe.pixmap.isNull()) {
526 pe.pixmap = QPixmap(pe.fileName);
527 pe.size = pe.pixmap.size();
528 }
529 if (pe.mode == arg.mode && pe.state == arg.state && !pe.size.isEmpty())
530 arg.sizes.push_back(pe.size);
531 }
532 break;
533 }
534 default:
535 QIconEngine::virtual_hook(id, data);
536 }
537}
538
539Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader,
540 (QIconEngineFactoryInterface_iid, QLatin1String("/iconengines"), Qt::CaseInsensitive))
541
542QFactoryLoader *qt_iconEngineFactoryLoader()
543{
544 return loader();
545}
546
547
548/*!
549 \class QIcon
550
551 \brief The QIcon class provides scalable icons in different modes
552 and states.
553
554 \ingroup painting
555 \ingroup shared
556 \inmodule QtGui
557
558 A QIcon can generate smaller, larger, active, and disabled pixmaps
559 from the set of pixmaps it is given. Such pixmaps are used by Qt
560 widgets to show an icon representing a particular action.
561
562 The simplest use of QIcon is to create one from a QPixmap file or
563 resource, and then use it, allowing Qt to work out all the required
564 icon styles and sizes. For example:
565
566 \snippet code/src_gui_image_qicon.cpp 0
567
568 To undo a QIcon, simply set a null icon in its place:
569
570 \snippet code/src_gui_image_qicon.cpp 1
571
572 Use the QImageReader::supportedImageFormats() and
573 QImageWriter::supportedImageFormats() functions to retrieve a
574 complete list of the supported file formats.
575
576 When you retrieve a pixmap using pixmap(QSize, Mode, State), and no
577 pixmap for this given size, mode and state has been added with
578 addFile() or addPixmap(), then QIcon will generate one on the
579 fly. This pixmap generation happens in a QIconEngine. The default
580 engine scales pixmaps down if required, but never up, and it uses
581 the current style to calculate a disabled appearance. By using
582 custom icon engines, you can customize every aspect of generated
583 icons. With QIconEnginePlugin it is possible to register different
584 icon engines for different file suffixes, making it possible for
585 third parties to provide additional icon engines to those included
586 with Qt.
587
588 \note Since Qt 4.2, an icon engine that supports SVG is included.
589
590 \section1 Making Classes that Use QIcon
591
592 If you write your own widgets that have an option to set a small
593 pixmap, consider allowing a QIcon to be set for that pixmap. The
594 Qt class QToolButton is an example of such a widget.
595
596 Provide a method to set a QIcon, and when you draw the icon, choose
597 whichever pixmap is appropriate for the current state of your widget.
598 For example:
599 \snippet code/src_gui_image_qicon.cpp 2
600
601 You might also make use of the \c Active mode, perhaps making your
602 widget \c Active when the mouse is over the widget (see \l
603 QWidget::enterEvent()), while the mouse is pressed pending the
604 release that will activate the function, or when it is the currently
605 selected item. If the widget can be toggled, the "On" mode might be
606 used to draw a different icon.
607
608 \image icon.png QIcon
609
610 \note QIcon needs a QGuiApplication instance before the icon is created.
611
612 \section1 High DPI Icons
613
614 There are two ways that QIcon supports \l {High DPI Displays}{high DPI}
615 icons: via \l addFile() and \l fromTheme().
616
617 \l addFile() is useful if you have your own custom directory structure and do
618 not need to use the \l {Icon Theme Specification}{freedesktop.org Icon Theme
619 Specification}. Icons created via this approach use Qt's \l {High Resolution
620 Versions of Images}{"@nx" high DPI syntax}.
621
622 Using \l fromTheme() is necessary if you plan on following the Icon Theme
623 Specification. To make QIcon use the high DPI version of an image, add an
624 additional entry to the appropriate \c index.theme file:
625
626 \badcode
627 [Icon Theme]
628 Name=Test
629 Comment=Test Theme
630
631 Directories=32x32/actions,32x32@2/actions
632
633 [32x32/actions]
634 Size=32
635 Context=Actions
636 Type=Fixed
637
638 # High DPI version of the entry above.
639 [32x32@2/actions]
640 Size=32
641 Scale=2
642 Type=Fixed
643 \endcode
644
645 Your icon theme directory would then look something like this:
646
647 \badcode
648 ├── 32x32
649 │ └── actions
650 │ └── appointment-new.png
651 ├── 32x32@2
652 │ └── actions
653 │ └── appointment-new.png
654 └── index.theme
655 \endcode
656
657 \sa {fowler}{GUI Design Handbook: Iconic Label}, {Icons Example}
658*/
659
660
661/*!
662 Constructs a null icon.
663*/
664QIcon::QIcon() Q_DECL_NOEXCEPT
665 : d(0)
666{
667}
668
669/*!
670 Constructs an icon from a \a pixmap.
671 */
672QIcon::QIcon(const QPixmap &pixmap)
673 :d(0)
674{
675 addPixmap(pixmap);
676}
677
678/*!
679 Constructs a copy of \a other. This is very fast.
680*/
681QIcon::QIcon(const QIcon &other)
682 :d(other.d)
683{
684 if (d)
685 d->ref.ref();
686}
687
688/*!
689 \fn QIcon::QIcon(QIcon &&other)
690
691 Move-constructs a QIcon instance, making it point to the same object
692 that \a other was pointing to.
693*/
694
695/*!
696 Constructs an icon from the file with the given \a fileName. The
697 file will be loaded on demand.
698
699 If \a fileName contains a relative path (e.g. the filename only)
700 the relevant file must be found relative to the runtime working
701 directory.
702
703 The file name can refer to an actual file on disk or to
704 one of the application's embedded resources. See the
705 \l{resources.html}{Resource System} overview for details on how to
706 embed images and other resource files in the application's
707 executable.
708
709 Use the QImageReader::supportedImageFormats() and
710 QImageWriter::supportedImageFormats() functions to retrieve a
711 complete list of the supported file formats.
712*/
713QIcon::QIcon(const QString &fileName)
714 : d(0)
715{
716 addFile(fileName);
717}
718
719
720/*!
721 Creates an icon with a specific icon \a engine. The icon takes
722 ownership of the engine.
723*/
724QIcon::QIcon(QIconEngine *engine)
725 :d(new QIconPrivate(engine))
726{
727}
728
729/*!
730 Destroys the icon.
731*/
732QIcon::~QIcon()
733{
734 if (d && !d->ref.deref())
735 delete d;
736}
737
738/*!
739 Assigns the \a other icon to this icon and returns a reference to
740 this icon.
741*/
742QIcon &QIcon::operator=(const QIcon &other)
743{
744 if (other.d)
745 other.d->ref.ref();
746 if (d && !d->ref.deref())
747 delete d;
748 d = other.d;
749 return *this;
750}
751
752/*!
753 \fn QIcon &QIcon::operator=(QIcon &&other)
754
755 Move-assigns \a other to this QIcon instance.
756
757 \since 5.2
758*/
759
760/*!
761 \fn void QIcon::swap(QIcon &other)
762 \since 4.8
763
764 Swaps icon \a other with this icon. This operation is very
765 fast and never fails.
766*/
767
768/*!
769 Returns the icon as a QVariant.
770*/
771QIcon::operator QVariant() const
772{
773 return QVariant(QVariant::Icon, this);
774}
775
776/*! \fn int QIcon::serialNumber() const
777 \obsolete
778
779 Returns a number that identifies the contents of this
780 QIcon object. Distinct QIcon objects can have
781 the same serial number if they refer to the same contents
782 (but they don't have to). Also, the serial number of
783 a QIcon object may change during its lifetime.
784
785 Use cacheKey() instead.
786
787 A null icon always has a serial number of 0.
788
789 Serial numbers are mostly useful in conjunction with caching.
790
791 \sa QPixmap::serialNumber()
792*/
793
794/*!
795 Returns a number that identifies the contents of this QIcon
796 object. Distinct QIcon objects can have the same key if
797 they refer to the same contents.
798 \since 4.3
799
800 The cacheKey() will change when the icon is altered via
801 addPixmap() or addFile().
802
803 Cache keys are mostly useful in conjunction with caching.
804
805 \sa QPixmap::cacheKey()
806*/
807qint64 QIcon::cacheKey() const
808{
809 if (!d)
810 return 0;
811 return (((qint64) d->serialNum) << 32) | ((qint64) (d->detach_no));
812}
813
814/*!
815 Returns a pixmap with the requested \a size, \a mode, and \a
816 state, generating one if necessary. The pixmap might be smaller than
817 requested, but never larger.
818
819 Setting the Qt::AA_UseHighDpiPixmaps application attribute enables this
820 function to return pixmaps that are larger than the requested size. Such
821 images will have a devicePixelRatio larger than 1.
822
823 \sa actualSize(), paint()
824*/
825QPixmap QIcon::pixmap(const QSize &size, Mode mode, State state) const
826{
827 if (!d)
828 return QPixmap();
829 return pixmap(0, size, mode, state);
830}
831
832/*!
833 \fn QPixmap QIcon::pixmap(int w, int h, Mode mode = Normal, State state = Off) const
834
835 \overload
836
837 Returns a pixmap of size QSize(\a w, \a h). The pixmap might be smaller than
838 requested, but never larger.
839
840 Setting the Qt::AA_UseHighDpiPixmaps application attribute enables this
841 function to return pixmaps that are larger than the requested size. Such
842 images will have a devicePixelRatio larger than 1.
843*/
844
845/*!
846 \fn QPixmap QIcon::pixmap(int extent, Mode mode = Normal, State state = Off) const
847
848 \overload
849
850 Returns a pixmap of size QSize(\a extent, \a extent). The pixmap might be smaller
851 than requested, but never larger.
852
853 Setting the Qt::AA_UseHighDpiPixmaps application attribute enables this
854 function to return pixmaps that are larger than the requested size. Such
855 images will have a devicePixelRatio larger than 1.
856*/
857
858/*! Returns the actual size of the icon for the requested \a size, \a
859 mode, and \a state. The result might be smaller than requested, but
860 never larger. The returned size is in device-independent pixels (This
861 is relevant for high-dpi pixmaps.)
862
863 \sa pixmap(), paint()
864*/
865QSize QIcon::actualSize(const QSize &size, Mode mode, State state) const
866{
867 if (!d)
868 return QSize();
869 return actualSize(0, size, mode, state);
870}
871
872/*!
873 \since 5.1
874
875 Returns a pixmap with the requested \a window \a size, \a mode, and \a
876 state, generating one if necessary.
877
878 The pixmap can be smaller than the requested size. If \a window is on
879 a high-dpi display the pixmap can be larger. In that case it will have
880 a devicePixelRatio larger than 1.
881
882 \sa actualSize(), paint()
883*/
884QPixmap QIcon::pixmap(QWindow *window, const QSize &size, Mode mode, State state) const
885{
886 if (!d)
887 return QPixmap();
888
889 qreal devicePixelRatio = qt_effective_device_pixel_ratio(window);
890
891 // Handle the simple normal-dpi case:
892 if (!(devicePixelRatio > 1.0)) {
893 QPixmap pixmap = d->engine->pixmap(size, mode, state);
894 pixmap.setDevicePixelRatio(1.0);
895 return pixmap;
896 }
897
898 // Try get a pixmap that is big enough to be displayed at device pixel resolution.
899 QIconEngine::ScaledPixmapArgument scalePixmapArg = { size * devicePixelRatio, mode, state, devicePixelRatio, QPixmap() };
900 d->engine->virtual_hook(QIconEngine::ScaledPixmapHook, reinterpret_cast<void*>(&scalePixmapArg));
901 scalePixmapArg.pixmap.setDevicePixelRatio(d->pixmapDevicePixelRatio(devicePixelRatio, size, scalePixmapArg.pixmap.size()));
902 return scalePixmapArg.pixmap;
903}
904
905/*!
906 \since 5.1
907
908 Returns the actual size of the icon for the requested \a window \a size, \a
909 mode, and \a state.
910
911 The pixmap can be smaller than the requested size. The returned size
912 is in device-independent pixels (This is relevant for high-dpi pixmaps.)
913
914 \sa actualSize(), pixmap(), paint()
915*/
916QSize QIcon::actualSize(QWindow *window, const QSize &size, Mode mode, State state) const
917{
918 if (!d)
919 return QSize();
920
921 qreal devicePixelRatio = qt_effective_device_pixel_ratio(window);
922
923 // Handle the simple normal-dpi case:
924 if (!(devicePixelRatio > 1.0))
925 return d->engine->actualSize(size, mode, state);
926
927 QSize actualSize = d->engine->actualSize(size * devicePixelRatio, mode, state);
928 return actualSize / d->pixmapDevicePixelRatio(devicePixelRatio, size, actualSize);
929}
930
931/*!
932 Uses the \a painter to paint the icon with specified \a alignment,
933 required \a mode, and \a state into the rectangle \a rect.
934
935 \sa actualSize(), pixmap()
936*/
937void QIcon::paint(QPainter *painter, const QRect &rect, Qt::Alignment alignment, Mode mode, State state) const
938{
939 if (!d || !painter)
940 return;
941
942 // Copy of QStyle::alignedRect
943 const QSize size = d->engine->actualSize(rect.size(), mode, state);
944 alignment = QGuiApplicationPrivate::visualAlignment(painter->layoutDirection(), alignment);
945 int x = rect.x();
946 int y = rect.y();
947 int w = size.width();
948 int h = size.height();
949 if ((alignment & Qt::AlignVCenter) == Qt::AlignVCenter)
950 y += rect.size().height()/2 - h/2;
951 else if ((alignment & Qt::AlignBottom) == Qt::AlignBottom)
952 y += rect.size().height() - h;
953 if ((alignment & Qt::AlignRight) == Qt::AlignRight)
954 x += rect.size().width() - w;
955 else if ((alignment & Qt::AlignHCenter) == Qt::AlignHCenter)
956 x += rect.size().width()/2 - w/2;
957 QRect alignedRect(x, y, w, h);
958
959 d->engine->paint(painter, alignedRect, mode, state);
960}
961
962/*!
963 \fn void QIcon::paint(QPainter *painter, int x, int y, int w, int h, Qt::Alignment alignment,
964 Mode mode, State state) const
965
966 \overload
967
968 Paints the icon into the rectangle QRect(\a x, \a y, \a w, \a h).
969*/
970
971/*!
972 Returns \c true if the icon is empty; otherwise returns \c false.
973
974 An icon is empty if it has neither a pixmap nor a filename.
975
976 Note: Even a non-null icon might not be able to create valid
977 pixmaps, eg. if the file does not exist or cannot be read.
978*/
979bool QIcon::isNull() const
980{
981 return !d || d->engine->isNull();
982}
983
984/*!\internal
985 */
986bool QIcon::isDetached() const
987{
988 return !d || d->ref.load() == 1;
989}
990
991/*! \internal
992 */
993void QIcon::detach()
994{
995 if (d) {
996 if (d->engine->isNull()) {
997 if (!d->ref.deref())
998 delete d;
999 d = 0;
1000 return;
1001 } else if (d->ref.load() != 1) {
1002 QIconPrivate *x = new QIconPrivate(d->engine->clone());
1003 if (!d->ref.deref())
1004 delete d;
1005 d = x;
1006 }
1007 ++d->detach_no;
1008 }
1009}
1010
1011/*!
1012 Adds \a pixmap to the icon, as a specialization for \a mode and
1013 \a state.
1014
1015 Custom icon engines are free to ignore additionally added
1016 pixmaps.
1017
1018 \sa addFile()
1019*/
1020void QIcon::addPixmap(const QPixmap &pixmap, Mode mode, State state)
1021{
1022 if (pixmap.isNull())
1023 return;
1024 detach();
1025 if (!d)
1026 d = new QIconPrivate(new QPixmapIconEngine);
1027 d->engine->addPixmap(pixmap, mode, state);
1028}
1029
1030static QIconEngine *iconEngineFromSuffix(const QString &fileName, const QString &suffix)
1031{
1032 if (!suffix.isEmpty()) {
1033 const int index = loader()->indexOf(suffix);
1034 if (index != -1) {
1035 if (QIconEnginePlugin *factory = qobject_cast<QIconEnginePlugin*>(loader()->instance(index))) {
1036 return factory->create(fileName);
1037 }
1038 }
1039 }
1040 return nullptr;
1041}
1042
1043/*! Adds an image from the file with the given \a fileName to the
1044 icon, as a specialization for \a size, \a mode and \a state. The
1045 file will be loaded on demand. Note: custom icon engines are free
1046 to ignore additionally added pixmaps.
1047
1048 If \a fileName contains a relative path (e.g. the filename only)
1049 the relevant file must be found relative to the runtime working
1050 directory.
1051
1052 The file name can refer to an actual file on disk or to
1053 one of the application's embedded resources. See the
1054 \l{resources.html}{Resource System} overview for details on how to
1055 embed images and other resource files in the application's
1056 executable.
1057
1058 Use the QImageReader::supportedImageFormats() and
1059 QImageWriter::supportedImageFormats() functions to retrieve a
1060 complete list of the supported file formats.
1061
1062 If a high resolution version of the image exists (identified by
1063 the suffix \c @2x on the base name), it is automatically loaded
1064 and added with the \e{device pixel ratio} set to a value of 2.
1065 This can be disabled by setting the environment variable
1066 \c QT_HIGHDPI_DISABLE_2X_IMAGE_LOADING (see QImageReader).
1067
1068 \note When you add a non-empty filename to a QIcon, the icon becomes
1069 non-null, even if the file doesn't exist or points to a corrupt file.
1070
1071 \sa addPixmap(), QPixmap::devicePixelRatio()
1072 */
1073void QIcon::addFile(const QString &fileName, const QSize &size, Mode mode, State state)
1074{
1075 if (fileName.isEmpty())
1076 return;
1077 detach();
1078 if (!d) {
1079
1080 QFileInfo info(fileName);
1081 QIconEngine *engine = iconEngineFromSuffix(fileName, info.suffix());
1082#ifndef QT_NO_MIMETYPE
1083 if (!engine)
1084 engine = iconEngineFromSuffix(fileName, QMimeDatabase().mimeTypeForFile(info).preferredSuffix());
1085#endif // !QT_NO_MIMETYPE
1086 d = new QIconPrivate(engine ? engine : new QPixmapIconEngine);
1087 }
1088
1089 d->engine->addFile(fileName, size, mode, state);
1090
1091 // Check if a "@Nx" file exists and add it.
1092 QString atNxFileName = qt_findAtNxFile(fileName, qApp->devicePixelRatio());
1093 if (atNxFileName != fileName)
1094 d->engine->addFile(atNxFileName, size, mode, state);
1095}
1096
1097/*!
1098 \since 4.5
1099
1100 Returns a list of available icon sizes for the specified \a mode and
1101 \a state.
1102*/
1103QList<QSize> QIcon::availableSizes(Mode mode, State state) const
1104{
1105 if (!d || !d->engine)
1106 return QList<QSize>();
1107 return d->engine->availableSizes(mode, state);
1108}
1109
1110/*!
1111 \since 4.7
1112
1113 Returns the name used to create the icon, if available.
1114
1115 Depending on the way the icon was created, it may have an associated
1116 name. This is the case for icons created with fromTheme() or icons
1117 using a QIconEngine which supports the QIconEngine::IconNameHook.
1118
1119 \sa fromTheme(), QIconEngine
1120*/
1121QString QIcon::name() const
1122{
1123 if (!d || !d->engine)
1124 return QString();
1125 return d->engine->iconName();
1126}
1127
1128/*!
1129 \since 4.6
1130
1131 Sets the search paths for icon themes to \a paths.
1132 \sa themeSearchPaths(), fromTheme(), setThemeName()
1133*/
1134void QIcon::setThemeSearchPaths(const QStringList &paths)
1135{
1136 QIconLoader::instance()->setThemeSearchPath(paths);
1137}
1138
1139/*!
1140 \since 4.6
1141
1142 Returns the search paths for icon themes.
1143
1144 The default value will depend on the platform:
1145
1146 On X11, the search path will use the XDG_DATA_DIRS environment
1147 variable if available.
1148
1149 By default all platforms will have the resource directory
1150 \c{:\icons} as a fallback. You can use "rcc -project" to generate a
1151 resource file from your icon theme.
1152
1153 \sa setThemeSearchPaths(), fromTheme(), setThemeName()
1154*/
1155QStringList QIcon::themeSearchPaths()
1156{
1157 return QIconLoader::instance()->themeSearchPaths();
1158}
1159
1160/*!
1161 \since 4.6
1162
1163 Sets the current icon theme to \a name.
1164
1165 The \a name should correspond to a directory name in the
1166 themeSearchPath() containing an index.theme
1167 file describing it's contents.
1168
1169 \sa themeSearchPaths(), themeName()
1170*/
1171void QIcon::setThemeName(const QString &name)
1172{
1173 QIconLoader::instance()->setThemeName(name);
1174}
1175
1176/*!
1177 \since 4.6
1178
1179 Returns the name of the current icon theme.
1180
1181 On X11, the current icon theme depends on your desktop
1182 settings. On other platforms it is not set by default.
1183
1184 \sa setThemeName(), themeSearchPaths(), fromTheme(),
1185 hasThemeIcon()
1186*/
1187QString QIcon::themeName()
1188{
1189 return QIconLoader::instance()->themeName();
1190}
1191
1192/*!
1193 \since 4.6
1194
1195 Returns the QIcon corresponding to \a name in the current
1196 icon theme.
1197
1198 The latest version of the freedesktop icon specification and naming
1199 specification can be obtained here:
1200
1201 \list
1202 \li \l{http://standards.freedesktop.org/icon-theme-spec/icon-theme-spec-latest.html}
1203 \li \l{http://standards.freedesktop.org/icon-naming-spec/icon-naming-spec-latest.html}
1204 \endlist
1205
1206 To fetch an icon from the current icon theme:
1207
1208 \snippet code/src_gui_image_qicon.cpp 3
1209
1210 \note By default, only X11 will support themed icons. In order to
1211 use themed icons on Mac and Windows, you will have to bundle a
1212 compliant theme in one of your themeSearchPaths() and set the
1213 appropriate themeName().
1214
1215 \note Qt will make use of GTK's icon-theme.cache if present to speed up
1216 the lookup. These caches can be generated using gtk-update-icon-cache:
1217 \l{https://developer.gnome.org/gtk3/stable/gtk-update-icon-cache.html}.
1218
1219 \sa themeName(), setThemeName(), themeSearchPaths()
1220*/
1221QIcon QIcon::fromTheme(const QString &name)
1222{
1223 QIcon icon;
1224
1225 if (qtIconCache()->contains(name)) {
1226 icon = *qtIconCache()->object(name);
1227 } else if (QDir::isAbsolutePath(name)) {
1228 return QIcon(name);
1229 } else {
1230 QPlatformTheme * const platformTheme = QGuiApplicationPrivate::platformTheme();
1231 bool hasUserTheme = QIconLoader::instance()->hasUserTheme();
1232 QIconEngine * const engine = (platformTheme && !hasUserTheme) ? platformTheme->createIconEngine(name)
1233 : new QIconLoaderEngine(name);
1234 QIcon *cachedIcon = new QIcon(engine);
1235 icon = *cachedIcon;
1236 qtIconCache()->insert(name, cachedIcon);
1237 }
1238
1239 return icon;
1240}
1241
1242/*!
1243 \overload
1244
1245 Returns the QIcon corresponding to \a name in the current
1246 icon theme. If no such icon is found in the current theme
1247 \a fallback is returned instead.
1248
1249 If you want to provide a guaranteed fallback for platforms that
1250 do not support theme icons, you can use the second argument:
1251
1252 \snippet code/src_gui_image_qicon.cpp 4
1253*/
1254QIcon QIcon::fromTheme(const QString &name, const QIcon &fallback)
1255{
1256 QIcon icon = fromTheme(name);
1257
1258 if (icon.isNull() || icon.availableSizes().isEmpty())
1259 return fallback;
1260
1261 return icon;
1262}
1263
1264/*!
1265 \since 4.6
1266
1267 Returns \c true if there is an icon available for \a name in the
1268 current icon theme, otherwise returns \c false.
1269
1270 \sa themeSearchPaths(), fromTheme(), setThemeName()
1271*/
1272bool QIcon::hasThemeIcon(const QString &name)
1273{
1274 QIcon icon = fromTheme(name);
1275
1276 return icon.name() == name;
1277}
1278
1279/*!
1280 \since 5.6
1281
1282 Indicate that this icon is a mask image(boolean \a isMask), and hence can
1283 potentially be modified based on where it's displayed.
1284 \sa isMask()
1285*/
1286void QIcon::setIsMask(bool isMask)
1287{
1288 if (!d)
1289 d = new QIconPrivate(new QPixmapIconEngine);
1290 else
1291 detach();
1292 d->is_mask = isMask;
1293}
1294
1295/*!
1296 \since 5.6
1297
1298 Returns \c true if this icon has been marked as a mask image.
1299 Certain platforms render mask icons differently (for example,
1300 menu icons on \macos).
1301
1302 \sa setIsMask()
1303*/
1304bool QIcon::isMask() const
1305{
1306 if (!d)
1307 return false;
1308 return d->is_mask;
1309}
1310
1311/*****************************************************************************
1312 QIcon stream functions
1313 *****************************************************************************/
1314#if !defined(QT_NO_DATASTREAM)
1315/*!
1316 \fn QDataStream &operator<<(QDataStream &stream, const QIcon &icon)
1317 \relates QIcon
1318 \since 4.2
1319
1320 Writes the given \a icon to the given \a stream as a PNG
1321 image. If the icon contains more than one image, all images will
1322 be written to the stream. Note that writing the stream to a file
1323 will not produce a valid image file.
1324*/
1325
1326QDataStream &operator<<(QDataStream &s, const QIcon &icon)
1327{
1328 if (s.version() >= QDataStream::Qt_4_3) {
1329 if (icon.isNull()) {
1330 s << QString();
1331 } else {
1332 s << icon.d->engine->key();
1333 icon.d->engine->write(s);
1334 }
1335 } else if (s.version() == QDataStream::Qt_4_2) {
1336 if (icon.isNull()) {
1337 s << 0;
1338 } else {
1339 QPixmapIconEngine *engine = static_cast<QPixmapIconEngine *>(icon.d->engine);
1340 int num_entries = engine->pixmaps.size();
1341 s << num_entries;
1342 for (int i=0; i < num_entries; ++i) {
1343 s << engine->pixmaps.at(i).pixmap;
1344 s << engine->pixmaps.at(i).fileName;
1345 s << engine->pixmaps.at(i).size;
1346 s << (uint) engine->pixmaps.at(i).mode;
1347 s << (uint) engine->pixmaps.at(i).state;
1348 }
1349 }
1350 } else {
1351 s << QPixmap(icon.pixmap(22,22));
1352 }
1353 return s;
1354}
1355
1356/*!
1357 \fn QDataStream &operator>>(QDataStream &stream, QIcon &icon)
1358 \relates QIcon
1359 \since 4.2
1360
1361 Reads an image, or a set of images, from the given \a stream into
1362 the given \a icon.
1363*/
1364
1365QDataStream &operator>>(QDataStream &s, QIcon &icon)
1366{
1367 if (s.version() >= QDataStream::Qt_4_3) {
1368 icon = QIcon();
1369 QString key;
1370 s >> key;
1371 if (key == QLatin1String("QPixmapIconEngine")) {
1372 icon.d = new QIconPrivate(new QPixmapIconEngine);
1373 icon.d->engine->read(s);
1374 } else if (key == QLatin1String("QIconLoaderEngine")) {
1375 icon.d = new QIconPrivate(new QIconLoaderEngine());
1376 icon.d->engine->read(s);
1377 } else {
1378 const int index = loader()->indexOf(key);
1379 if (index != -1) {
1380 if (QIconEnginePlugin *factory = qobject_cast<QIconEnginePlugin*>(loader()->instance(index))) {
1381 if (QIconEngine *engine= factory->create()) {
1382 icon.d = new QIconPrivate(engine);
1383 engine->read(s);
1384 } // factory
1385 } // instance
1386 } // index
1387 }
1388 } else if (s.version() == QDataStream::Qt_4_2) {
1389 icon = QIcon();
1390 int num_entries;
1391 QPixmap pm;
1392 QString fileName;
1393 QSize sz;
1394 uint mode;
1395 uint state;
1396
1397 s >> num_entries;
1398 for (int i=0; i < num_entries; ++i) {
1399 s >> pm;
1400 s >> fileName;
1401 s >> sz;
1402 s >> mode;
1403 s >> state;
1404 if (pm.isNull())
1405 icon.addFile(fileName, sz, QIcon::Mode(mode), QIcon::State(state));
1406 else
1407 icon.addPixmap(pm, QIcon::Mode(mode), QIcon::State(state));
1408 }
1409 } else {
1410 QPixmap pm;
1411 s >> pm;
1412 icon.addPixmap(pm);
1413 }
1414 return s;
1415}
1416
1417#endif //QT_NO_DATASTREAM
1418
1419#ifndef QT_NO_DEBUG_STREAM
1420QDebug operator<<(QDebug dbg, const QIcon &i)
1421{
1422 QDebugStateSaver saver(dbg);
1423 dbg.resetFormat();
1424 dbg.nospace();
1425 dbg << "QIcon(";
1426 if (i.isNull()) {
1427 dbg << "null";
1428 } else {
1429 if (!i.name().isEmpty())
1430 dbg << i.name() << ',';
1431 dbg << "availableSizes[normal,Off]=" << i.availableSizes()
1432 << ",cacheKey=" << showbase << hex << i.cacheKey() << dec << noshowbase;
1433 }
1434 dbg << ')';
1435 return dbg;
1436}
1437#endif
1438
1439/*!
1440 \fn DataPtr &QIcon::data_ptr()
1441 \internal
1442*/
1443
1444/*!
1445 \typedef QIcon::DataPtr
1446 \internal
1447*/
1448
1449/*!
1450 \internal
1451 \since 5.6
1452 Attempts to find a suitable @Nx file for the given \a targetDevicePixelRatio
1453 Returns the the \a baseFileName if no such file was found.
1454
1455 Given base foo.png and a target dpr of 2.5, this function will look for
1456 foo@3x.png, then foo@2x, then fall back to foo.png if not found.
1457
1458 \a sourceDevicePixelRatio will be set to the value of N if the argument is
1459 a non-null pointer
1460*/
1461QString qt_findAtNxFile(const QString &baseFileName, qreal targetDevicePixelRatio,
1462 qreal *sourceDevicePixelRatio)
1463{
1464 if (targetDevicePixelRatio <= 1.0)
1465 return baseFileName;
1466
1467 static bool disableNxImageLoading = !qEnvironmentVariableIsEmpty("QT_HIGHDPI_DISABLE_2X_IMAGE_LOADING");
1468 if (disableNxImageLoading)
1469 return baseFileName;
1470
1471 int dotIndex = baseFileName.lastIndexOf(QLatin1Char('.'));
1472 if (dotIndex == -1) /* no dot */
1473 dotIndex = baseFileName.size(); /* append */
1474
1475 QString atNxfileName = baseFileName;
1476 atNxfileName.insert(dotIndex, QLatin1String("@2x"));
1477 // Check for @Nx, ..., @3x, @2x file versions,
1478 for (int n = qMin(qCeil(targetDevicePixelRatio), 9); n > 1; --n) {
1479 atNxfileName[dotIndex + 1] = QLatin1Char('0' + n);
1480 if (QFile::exists(atNxfileName)) {
1481 if (sourceDevicePixelRatio)
1482 *sourceDevicePixelRatio = n;
1483 return atNxfileName;
1484 }
1485 }
1486
1487 return baseFileName;
1488}
1489
1490QT_END_NAMESPACE
1491#endif //QT_NO_ICON
1492