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