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

source code of qtbase/src/gui/image/qicon.cpp