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