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