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