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