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