source: trunk/src/gui/image/qicon.cpp@ 284

Last change on this file since 284 was 261, checked in by Dmitry A. Kuminov, 16 years ago

gui: QIcon: Be in line with the documentation and prefer the largest pixmap among smaller ones (instead of the smallest among larger) when searching for the best match for the given icon size in the pixmap icon engine [vendor bug].

File size: 34.0 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information ([email protected])
5**
6** This file is part of the QtGui module of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file. Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you are unsure which license is appropriate for your use, please
37** contact the sales department at [email protected].
38** $QT_END_LICENSE$
39**
40****************************************************************************/
41
42#include "qicon.h"
43#include "qiconengine.h"
44#include "qiconengineplugin.h"
45#include "private/qfactoryloader_p.h"
46#include "qapplication.h"
47#include "qstyleoption.h"
48#include "qpainter.h"
49#include "qfileinfo.h"
50#include "qstyle.h"
51#include "qpixmapcache.h"
52#include "qvariant.h"
53#include "qdebug.h"
54
55#ifdef Q_WS_MAC
56#include <private/qt_mac_p.h>
57#include <Carbon/Carbon.h>
58#endif
59
60QT_BEGIN_NAMESPACE
61
62/*!
63 \enum QIcon::Mode
64
65 This enum type describes the mode for which a pixmap is intended
66 to be used. The currently defined modes are:
67
68 \value Normal
69 Display the pixmap when the user is
70 not interacting with the icon, but the
71 functionality represented by the icon is available.
72 \value Disabled
73 Display the pixmap when the
74 functionality represented by the icon is not available.
75 \value Active
76 Display the pixmap when the
77 functionality represented by the icon is available and
78 the user is interacting with the icon, for example, moving the
79 mouse over it or clicking it.
80 \value Selected
81 Display the pixmap when the item represented by the icon is
82 selected.
83*/
84
85/*!
86 \enum QIcon::State
87
88 This enum describes the state for which a pixmap is intended to be
89 used. The \e state can be:
90
91 \value Off Display the pixmap when the widget is in an "off" state
92 \value On Display the pixmap when the widget is in an "on" state
93*/
94
95static QBasicAtomicInt serialNumCounter = Q_BASIC_ATOMIC_INITIALIZER(1);
96
97class QIconPrivate
98{
99public:
100 QIconPrivate(): engine(0), ref(1), serialNum(serialNumCounter.fetchAndAddRelaxed(1)), detach_no(0), engine_version(2), v1RefCount(0) {}
101
102 ~QIconPrivate() {
103 if (engine_version == 1) {
104 if (!v1RefCount->deref()) {
105 delete engine;
106 delete v1RefCount;
107 }
108 } else if (engine_version == 2) {
109 delete engine;
110 }
111 }
112
113 QIconEngine *engine;
114
115 QAtomicInt ref;
116 int serialNum;
117 int detach_no;
118 int engine_version;
119
120 QAtomicInt *v1RefCount;
121};
122
123
124struct QPixmapIconEngineEntry
125{
126 QPixmapIconEngineEntry():mode(QIcon::Normal), state(QIcon::Off){}
127 QPixmapIconEngineEntry(const QPixmap &pm, QIcon::Mode m = QIcon::Normal, QIcon::State s = QIcon::Off)
128 :pixmap(pm), size(pm.size()), mode(m), state(s){}
129 QPixmapIconEngineEntry(const QString &file, const QSize &sz = QSize(), QIcon::Mode m = QIcon::Normal, QIcon::State s = QIcon::Off)
130 :fileName(file), size(sz), mode(m), state(s){}
131 QPixmap pixmap;
132 QString fileName;
133 QSize size;
134 QIcon::Mode mode;
135 QIcon::State state;
136 bool isNull() const {return (fileName.isEmpty() && pixmap.isNull()); }
137};
138
139class QPixmapIconEngine : public QIconEngineV2 {
140public:
141 QPixmapIconEngine();
142 QPixmapIconEngine(const QPixmapIconEngine &);
143 ~QPixmapIconEngine();
144 void paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state);
145 QPixmap pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state);
146 QPixmapIconEngineEntry *bestMatch(const QSize &size, QIcon::Mode mode, QIcon::State state, bool sizeOnly);
147 QSize actualSize(const QSize &size, QIcon::Mode mode, QIcon::State state);
148 void addPixmap(const QPixmap &pixmap, QIcon::Mode mode, QIcon::State state);
149 void addFile(const QString &fileName, const QSize &size, QIcon::Mode mode, QIcon::State state);
150
151 // v2 functions
152 QString key() const;
153 QIconEngineV2 *clone() const;
154 bool read(QDataStream &in);
155 bool write(QDataStream &out) const;
156 void virtual_hook(int id, void *data);
157
158private:
159 QPixmapIconEngineEntry *tryMatch(const QSize &size, QIcon::Mode mode, QIcon::State state);
160 QVector<QPixmapIconEngineEntry> pixmaps;
161
162 friend QDataStream &operator<<(QDataStream &s, const QIcon &icon);
163};
164
165QPixmapIconEngine::QPixmapIconEngine()
166{
167}
168
169QPixmapIconEngine::QPixmapIconEngine(const QPixmapIconEngine &other)
170 : QIconEngineV2(other), pixmaps(other.pixmaps)
171{
172}
173
174QPixmapIconEngine::~QPixmapIconEngine()
175{
176}
177
178void QPixmapIconEngine::paint(QPainter *painter, const QRect &rect, QIcon::Mode mode, QIcon::State state)
179{
180 QSize pixmapSize = rect.size();
181#if defined(Q_WS_MAC) && !defined(Q_WS_MAC64)
182 pixmapSize *= (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_4) ? HIGetScaleFactor() : 1;
183#endif
184 painter->drawPixmap(rect, pixmap(pixmapSize, mode, state));
185}
186
187static inline int area(const QSize &s) { return s.width() * s.height(); }
188
189// returns the smallest of the two that is still larger than or equal to size.
190
191// According to the QIcon documentation, actualSize() and pixmap() may return
192// icons that are smaller than the requested size but never larger. If we only
193// have a larger icon, the engine will smooth scale it to the requested size.
194// Given that, we should prefer the largerst pixmap that is smaller or equal to
195// the requested size (i.e. the most close one from the group of smaller
196// pixmaps), and if there are no such pixmaps, we return the smallest one
197// from the larger group. This function just does that.
198static QPixmapIconEngineEntry *bestSizeMatch(const QSize &size, QPixmapIconEngineEntry *pa, QPixmapIconEngineEntry *pb)
199{
200 int s = area(size);
201 if (pa->size == QSize() && pa->pixmap.isNull()) {
202 pa->pixmap = QPixmap(pa->fileName);
203 pa->size = pa->pixmap.size();
204 }
205 int a = area(pa->size);
206 if (pb->size == QSize() && pb->pixmap.isNull()) {
207 pb->pixmap = QPixmap(pb->fileName);
208 pb->size = pb->pixmap.size();
209 }
210 int b = area(pb->size);
211
212 // prefer the largest among smaller
213 int res = qMax(a,b);
214 if (res > s) {
215 // fallback to the smallest among larger
216 res = qMin(a,b);
217 }
218
219 if (res == a)
220 return pa;
221 return pb;
222}
223
224QPixmapIconEngineEntry *QPixmapIconEngine::tryMatch(const QSize &size, QIcon::Mode mode, QIcon::State state)
225{
226 QPixmapIconEngineEntry *pe = 0;
227 for (int i = 0; i < pixmaps.count(); ++i)
228 if (pixmaps.at(i).mode == mode && pixmaps.at(i).state == state) {
229 if (pe)
230 pe = bestSizeMatch(size, &pixmaps[i], pe);
231 else
232 pe = &pixmaps[i];
233 }
234 return pe;
235}
236
237
238QPixmapIconEngineEntry *QPixmapIconEngine::bestMatch(const QSize &size, QIcon::Mode mode, QIcon::State state, bool sizeOnly)
239{
240 QPixmapIconEngineEntry *pe = tryMatch(size, mode, state);
241 while (!pe){
242 QIcon::State oppositeState = (state == QIcon::On) ? QIcon::Off : QIcon::On;
243 if (mode == QIcon::Disabled || mode == QIcon::Selected) {
244 QIcon::Mode oppositeMode = (mode == QIcon::Disabled) ? QIcon::Selected : QIcon::Disabled;
245 if ((pe = tryMatch(size, QIcon::Normal, state)))
246 break;
247 if ((pe = tryMatch(size, QIcon::Active, state)))
248 break;
249 if ((pe = tryMatch(size, mode, oppositeState)))
250 break;
251 if ((pe = tryMatch(size, QIcon::Normal, oppositeState)))
252 break;
253 if ((pe = tryMatch(size, QIcon::Active, oppositeState)))
254 break;
255 if ((pe = tryMatch(size, oppositeMode, state)))
256 break;
257 if ((pe = tryMatch(size, oppositeMode, oppositeState)))
258 break;
259 } else {
260 QIcon::Mode oppositeMode = (mode == QIcon::Normal) ? QIcon::Active : QIcon::Normal;
261 if ((pe = tryMatch(size, oppositeMode, state)))
262 break;
263 if ((pe = tryMatch(size, mode, oppositeState)))
264 break;
265 if ((pe = tryMatch(size, oppositeMode, oppositeState)))
266 break;
267 if ((pe = tryMatch(size, QIcon::Disabled, state)))
268 break;
269 if ((pe = tryMatch(size, QIcon::Selected, state)))
270 break;
271 if ((pe = tryMatch(size, QIcon::Disabled, oppositeState)))
272 break;
273 if ((pe = tryMatch(size, QIcon::Selected, oppositeState)))
274 break;
275 }
276
277 if (!pe)
278 return pe;
279 }
280
281 if (sizeOnly ? (pe->size.isNull() || !pe->size.isValid()) : pe->pixmap.isNull()) {
282 pe->pixmap = QPixmap(pe->fileName);
283 if (!pe->pixmap.isNull())
284 pe->size = pe->pixmap.size();
285 }
286
287 return pe;
288}
289
290QPixmap QPixmapIconEngine::pixmap(const QSize &size, QIcon::Mode mode, QIcon::State state)
291{
292 QPixmap pm;
293 QPixmapIconEngineEntry *pe = bestMatch(size, mode, state, false);
294 if (pe)
295 pm = pe->pixmap;
296
297 if (pm.isNull()) {
298 int idx = pixmaps.count();
299 while (--idx >= 0) {
300 if (pe == &pixmaps[idx]) {
301 pixmaps.remove(idx);
302 break;
303 }
304 }
305 if (pixmaps.isEmpty())
306 return pm;
307 else
308 return pixmap(size, mode, state);
309 }
310
311 QSize actualSize = pm.size();
312 if (!actualSize.isNull() && (actualSize.width() > size.width() || actualSize.height() > size.height()))
313 actualSize.scale(size, Qt::KeepAspectRatio);
314
315 QString key = QLatin1String("$qt_icon_")
316 + QString::number(pm.cacheKey())
317 + QString::number(pe->mode)
318 + QString::number(actualSize.width())
319 + QLatin1Char('_')
320 + QString::number(actualSize.height())
321 + QLatin1Char('_');
322
323
324 if (mode == QIcon::Active) {
325 if (QPixmapCache::find(key + QString::number(mode), pm))
326 return pm; // horray
327 if (QPixmapCache::find(key + QString::number(QIcon::Normal), pm)) {
328 QStyleOption opt(0);
329 opt.palette = QApplication::palette();
330 QPixmap active = QApplication::style()->generatedIconPixmap(QIcon::Active, pm, &opt);
331 if (pm.cacheKey() == active.cacheKey())
332 return pm;
333 }
334 }
335
336 if (!QPixmapCache::find(key + QString::number(mode), pm)) {
337 if (pm.size() != actualSize)
338 pm = pm.scaled(actualSize, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
339 if (pe->mode != mode && mode != QIcon::Normal) {
340 QStyleOption opt(0);
341 opt.palette = QApplication::palette();
342 QPixmap generated = QApplication::style()->generatedIconPixmap(mode, pm, &opt);
343 if (!generated.isNull())
344 pm = generated;
345 }
346 QPixmapCache::insert(key + QString::number(mode), pm);
347 }
348 return pm;
349}
350
351QSize QPixmapIconEngine::actualSize(const QSize &size, QIcon::Mode mode, QIcon::State state)
352{
353 QSize actualSize;
354 if (QPixmapIconEngineEntry *pe = bestMatch(size, mode, state, true))
355 actualSize = pe->size;
356
357 if (actualSize.isNull())
358 return actualSize;
359
360 if (actualSize.width() > size.width() || actualSize.height() > size.height())
361 actualSize.scale(size, Qt::KeepAspectRatio);
362 return actualSize;
363}
364
365void QPixmapIconEngine::addPixmap(const QPixmap &pixmap, QIcon::Mode mode, QIcon::State state)
366{
367 if (!pixmap.isNull()) {
368 QPixmapIconEngineEntry *pe = tryMatch(pixmap.size(), mode, state);
369 if(pe && pe->size == pixmap.size()) {
370 pe->pixmap = pixmap;
371 pe->fileName.clear();
372 } else {
373 pixmaps += QPixmapIconEngineEntry(pixmap, mode, state);
374 }
375 }
376}
377
378void QPixmapIconEngine::addFile(const QString &fileName, const QSize &_size, QIcon::Mode mode, QIcon::State state)
379{
380 if (!fileName.isEmpty()) {
381 QSize size = _size;
382 QPixmap pixmap;
383
384 QString abs = fileName;
385 if (fileName.at(0) != QLatin1Char(':'))
386 abs = QFileInfo(fileName).absoluteFilePath();
387
388 for (int i = 0; i < pixmaps.count(); ++i) {
389 if (pixmaps.at(i).mode == mode && pixmaps.at(i).state == state) {
390 QPixmapIconEngineEntry *pe = &pixmaps[i];
391 if(size == QSize()) {
392 pixmap = QPixmap(abs);
393 size = pixmap.size();
394 }
395 if (pe->size == QSize() && pe->pixmap.isNull()) {
396 pe->pixmap = QPixmap(pe->fileName);
397 pe->size = pe->pixmap.size();
398 }
399 if(pe->size == size) {
400 pe->pixmap = pixmap;
401 pe->fileName = abs;
402 return;
403 }
404 }
405 }
406 QPixmapIconEngineEntry e(abs, size, mode, state);
407 e.pixmap = pixmap;
408 pixmaps += e;
409 }
410}
411
412QString QPixmapIconEngine::key() const
413{
414 return QLatin1String("QPixmapIconEngine");
415}
416
417QIconEngineV2 *QPixmapIconEngine::clone() const
418{
419 return new QPixmapIconEngine(*this);
420}
421
422bool QPixmapIconEngine::read(QDataStream &in)
423{
424 int num_entries;
425 QPixmap pm;
426 QString fileName;
427 QSize sz;
428 uint mode;
429 uint state;
430
431 in >> num_entries;
432 for (int i=0; i < num_entries; ++i) {
433 if (in.atEnd()) {
434 pixmaps.clear();
435 return false;
436 }
437 in >> pm;
438 in >> fileName;
439 in >> sz;
440 in >> mode;
441 in >> state;
442 if (pm.isNull())
443 addFile(fileName, sz, QIcon::Mode(mode), QIcon::State(state));
444 else
445 addPixmap(pm, QIcon::Mode(mode), QIcon::State(state));
446 }
447 return true;
448}
449
450bool QPixmapIconEngine::write(QDataStream &out) const
451{
452 int num_entries = pixmaps.size();
453 out << num_entries;
454 for (int i=0; i < num_entries; ++i) {
455 if (pixmaps.at(i).pixmap.isNull())
456 out << QPixmap(pixmaps.at(i).fileName);
457 else
458 out << pixmaps.at(i).pixmap;
459 out << pixmaps.at(i).fileName;
460 out << pixmaps.at(i).size;
461 out << (uint) pixmaps.at(i).mode;
462 out << (uint) pixmaps.at(i).state;
463 }
464 return true;
465}
466
467void QPixmapIconEngine::virtual_hook(int id, void *data)
468{
469 switch (id) {
470 case QIconEngineV2::AvailableSizesHook: {
471 QIconEngineV2::AvailableSizesArgument &arg =
472 *reinterpret_cast<QIconEngineV2::AvailableSizesArgument*>(data);
473 arg.sizes.clear();
474 for (int i = 0; i < pixmaps.size(); ++i) {
475 QPixmapIconEngineEntry &pe = pixmaps[i];
476 if (pe.size == QSize() && pe.pixmap.isNull()) {
477 pe.pixmap = QPixmap(pe.fileName);
478 pe.size = pe.pixmap.size();
479 }
480 if (pe.mode == arg.mode && pe.state == arg.state && !pe.size.isEmpty())
481 arg.sizes.push_back(pe.size);
482 }
483 break;
484 }
485 default:
486 QIconEngineV2::virtual_hook(id, data);
487 }
488}
489
490#if !defined (QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS)
491Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loader,
492 (QIconEngineFactoryInterface_iid, QLatin1String("/iconengines"), Qt::CaseInsensitive))
493Q_GLOBAL_STATIC_WITH_ARGS(QFactoryLoader, loaderV2,
494 (QIconEngineFactoryInterfaceV2_iid, QLatin1String("/iconengines"), Qt::CaseInsensitive))
495#endif
496
497
498
499/*!
500 \class QIcon
501
502 \brief The QIcon class provides scalable icons in different modes
503 and states.
504
505 \ingroup multimedia
506 \ingroup shared
507 \mainclass
508
509 A QIcon can generate smaller, larger, active, and disabled pixmaps
510 from the set of pixmaps it is given. Such pixmaps are used by Qt
511 widgets to show an icon representing a particular action.
512
513 The simplest use of QIcon is to create one from a QPixmap file or
514 resource, and then use it, allowing Qt to work out all the required
515 icon styles and sizes. For example:
516
517 \snippet doc/src/snippets/code/src_gui_image_qicon.cpp 0
518
519 To undo a QIcon, simply set a null icon in its place:
520
521 \snippet doc/src/snippets/code/src_gui_image_qicon.cpp 1
522
523 Use the QImageReader::supportedImageFormats() and
524 QImageWriter::supportedImageFormats() functions to retrieve a
525 complete list of the supported file formats.
526
527 When you retrieve a pixmap using pixmap(QSize, Mode, State), and no
528 pixmap for this given size, mode and state has been added with
529 addFile() or addPixmap(), then QIcon will generate one on the
530 fly. This pixmap generation happens in a QIconEngineV2. The default
531 engine scales pixmaps down if required, but never up, and it uses
532 the current style to calculate a disabled appearance. By using
533 custom icon engines, you can customize every aspect of generated
534 icons. With QIconEnginePluginV2 it is possible to register different
535 icon engines for different file suffixes, making it possible for
536 third parties to provide additional icon engines to those included
537 with Qt.
538
539 \note Since Qt 4.2, an icon engine that supports SVG is included.
540
541 \section1 Making Classes that Use QIcon
542
543 If you write your own widgets that have an option to set a small
544 pixmap, consider allowing a QIcon to be set for that pixmap. The
545 Qt class QToolButton is an example of such a widget.
546
547 Provide a method to set a QIcon, and when you draw the icon, choose
548 whichever pixmap is appropriate for the current state of your widget.
549 For example:
550 \snippet doc/src/snippets/code/src_gui_image_qicon.cpp 2
551
552 You might also make use of the \c Active mode, perhaps making your
553 widget \c Active when the mouse is over the widget (see \l
554 QWidget::enterEvent()), while the mouse is pressed pending the
555 release that will activate the function, or when it is the currently
556 selected item. If the widget can be toggled, the "On" mode might be
557 used to draw a different icon.
558
559 \img icon.png QIcon
560
561 \sa {fowler}{GUI Design Handbook: Iconic Label}, {Icons Example}
562*/
563
564
565/*!
566 Constructs a null icon.
567*/
568QIcon::QIcon()
569 : d(0)
570{
571}
572
573/*!
574 Constructs an icon from a \a pixmap.
575 */
576QIcon::QIcon(const QPixmap &pixmap)
577 :d(0)
578{
579 addPixmap(pixmap);
580}
581
582/*!
583 Constructs a copy of \a other. This is very fast.
584*/
585QIcon::QIcon(const QIcon &other)
586 :d(other.d)
587{
588 if (d)
589 d->ref.ref();
590}
591
592/*!
593 Constructs an icon from the file with the given \a fileName. The
594 file will be loaded on demand.
595
596 If \a fileName contains a relative path (e.g. the filename only)
597 the relevant file must be found relative to the runtime working
598 directory.
599
600 The file name can be either refer to an actual file on disk or to
601 one of the application's embedded resources. See the
602 \l{resources.html}{Resource System} overview for details on how to
603 embed images and other resource files in the application's
604 executable.
605
606 Use the QImageReader::supportedImageFormats() and
607 QImageWriter::supportedImageFormats() functions to retrieve a
608 complete list of the supported file formats.
609*/
610QIcon::QIcon(const QString &fileName)
611 : d(0)
612{
613 addFile(fileName);
614}
615
616
617/*!
618 Creates an icon with a specific icon \a engine. The icon takes
619 ownership of the engine.
620*/
621QIcon::QIcon(QIconEngine *engine)
622 :d(new QIconPrivate)
623{
624 d->engine_version = 1;
625 d->engine = engine;
626 d->v1RefCount = new QAtomicInt(1);
627}
628
629/*!
630 Creates an icon with a specific icon \a engine. The icon takes
631 ownership of the engine.
632*/
633QIcon::QIcon(QIconEngineV2 *engine)
634 :d(new QIconPrivate)
635{
636 d->engine_version = 2;
637 d->engine = engine;
638}
639
640/*!
641 Destroys the icon.
642*/
643QIcon::~QIcon()
644{
645 if (d && !d->ref.deref())
646 delete d;
647}
648
649/*!
650 Assigns the \a other icon to this icon and returns a reference to
651 this icon.
652*/
653QIcon &QIcon::operator=(const QIcon &other)
654{
655 if (other.d)
656 other.d->ref.ref();
657 if (d && !d->ref.deref())
658 delete d;
659 d = other.d;
660 return *this;
661}
662
663/*!
664 Returns the icon as a QVariant.
665*/
666QIcon::operator QVariant() const
667{
668 return QVariant(QVariant::Icon, this);
669}
670
671/*! \obsolete
672
673 Returns a number that identifies the contents of this
674 QIcon object. Distinct QIcon objects can have
675 the same serial number if they refer to the same contents
676 (but they don't have to). Also, the serial number of
677 a QIcon object may change during its lifetime.
678
679 Use cacheKey() instead.
680
681 A null icon always has a serial number of 0.
682
683 Serial numbers are mostly useful in conjunction with caching.
684
685 \sa QPixmap::serialNumber()
686*/
687
688int QIcon::serialNumber() const
689{
690 return d ? d->serialNum : 0;
691}
692
693/*!
694 Returns a number that identifies the contents of this QIcon
695 object. Distinct QIcon objects can have the same key if
696 they refer to the same contents.
697 \since 4.3
698
699 The cacheKey() will change when the icon is altered via
700 addPixmap() or addFile().
701
702 Cache keys are mostly useful in conjunction with caching.
703
704 \sa QPixmap::cacheKey()
705*/
706qint64 QIcon::cacheKey() const
707{
708 if (!d)
709 return 0;
710 return (((qint64) d->serialNum) << 32) | ((qint64) (d->detach_no));
711}
712
713/*!
714 Returns a pixmap with the requested \a size, \a mode, and \a
715 state, generating one if necessary. The pixmap might be smaller than
716 requested, but never larger.
717
718 \sa actualSize(), paint()
719*/
720QPixmap QIcon::pixmap(const QSize &size, Mode mode, State state) const
721{
722 if (!d)
723 return QPixmap();
724 return d->engine->pixmap(size, mode, state);
725}
726
727/*!
728 \fn QPixmap QIcon::pixmap(int w, int h, Mode mode = Normal, State state = Off) const
729
730 \overload
731
732 Returns a pixmap of size QSize(\a w, \a h). The pixmap might be smaller than
733 requested, but never larger.
734*/
735
736/*!
737 \fn QPixmap QIcon::pixmap(int extent, Mode mode = Normal, State state = Off) const
738
739 \overload
740
741 Returns a pixmap of size QSize(\a extent, \a extent). The pixmap might be smaller
742 than requested, but never larger.
743*/
744
745/*! Returns the actual size of the icon for the requested \a size, \a
746 mode, and \a state. The result might be smaller than requested, but
747 never larger.
748
749 \sa pixmap(), paint()
750*/
751QSize QIcon::actualSize(const QSize &size, Mode mode, State state) const
752{
753 if (!d)
754 return QSize();
755 return d->engine->actualSize(size, mode, state);
756}
757
758
759/*!
760 Uses the \a painter to paint the icon with specified \a alignment,
761 required \a mode, and \a state into the rectangle \a rect.
762
763 \sa actualSize(), pixmap()
764*/
765void QIcon::paint(QPainter *painter, const QRect &rect, Qt::Alignment alignment, Mode mode, State state) const
766{
767 if (!d || !painter)
768 return;
769 QRect alignedRect = QStyle::alignedRect(painter->layoutDirection(), alignment, d->engine->actualSize(rect.size(), mode, state), rect);
770 d->engine->paint(painter, alignedRect, mode, state);
771}
772
773/*!
774 \fn void QIcon::paint(QPainter *painter, int x, int y, int w, int h, Qt::Alignment alignment,
775 Mode mode, State state) const
776
777 \overload
778
779 Paints the icon into the rectangle QRect(\a x, \a y, \a w, \a h).
780*/
781
782/*!
783 Returns true if the icon is empty; otherwise returns false.
784
785 An icon is empty if it has neither a pixmap nor a filename.
786
787 Note: Even a non-null icon might not be able to create valid
788 pixmaps, eg. if the file does not exist or cannot be read.
789*/
790bool QIcon::isNull() const
791{
792 return !d;
793}
794
795/*!\internal
796 */
797bool QIcon::isDetached() const
798{
799 return !d || d->ref == 1;
800}
801
802/*! \internal
803 */
804void QIcon::detach()
805{
806 if (d) {
807 if (d->ref != 1) {
808 QIconPrivate *x = new QIconPrivate;
809 if (d->engine_version > 1) {
810 QIconEngineV2 *engine = static_cast<QIconEngineV2 *>(d->engine);
811 x->engine = engine->clone();
812 } else {
813 x->engine = d->engine;
814 x->v1RefCount = d->v1RefCount;
815 x->v1RefCount->ref();
816 }
817 x->engine_version = d->engine_version;
818 if (!d->ref.deref())
819 delete d;
820 d = x;
821 }
822 ++d->detach_no;
823 }
824}
825
826/*!
827 Adds \a pixmap to the icon, as a specialization for \a mode and
828 \a state.
829
830 Custom icon engines are free to ignore additionally added
831 pixmaps.
832
833 \sa addFile()
834*/
835void QIcon::addPixmap(const QPixmap &pixmap, Mode mode, State state)
836{
837 if (pixmap.isNull())
838 return;
839 if (!d) {
840 d = new QIconPrivate;
841 d->engine = new QPixmapIconEngine;
842 } else {
843 detach();
844 }
845 d->engine->addPixmap(pixmap, mode, state);
846}
847
848
849/*! Adds an image from the file with the given \a fileName to the
850 icon, as a specialization for \a size, \a mode and \a state. The
851 file will be loaded on demand. Note: custom icon engines are free
852 to ignore additionally added pixmaps.
853
854 If \a fileName contains a relative path (e.g. the filename only)
855 the relevant file must be found relative to the runtime working
856 directory.
857
858 The file name can be either refer to an actual file on disk or to
859 one of the application's embedded resources. See the
860 \l{resources.html}{Resource System} overview for details on how to
861 embed images and other resource files in the application's
862 executable.
863
864 Use the QImageReader::supportedImageFormats() and
865 QImageWriter::supportedImageFormats() functions to retrieve a
866 complete list of the supported file formats.
867
868 \sa addPixmap()
869 */
870void QIcon::addFile(const QString &fileName, const QSize &size, Mode mode, State state)
871{
872 if (fileName.isEmpty())
873 return;
874 if (!d) {
875#if !defined (QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS)
876 QFileInfo info(fileName);
877 QString suffix = info.suffix();
878 if (!suffix.isEmpty()) {
879 // first try version 2 engines..
880 if (QIconEngineFactoryInterfaceV2 *factory = qobject_cast<QIconEngineFactoryInterfaceV2*>(loaderV2()->instance(suffix))) {
881 if (QIconEngine *engine = factory->create(fileName)) {
882 d = new QIconPrivate;
883 d->engine = engine;
884 }
885 }
886 // ..then fall back and try to load version 1 engines
887 if (!d) {
888 if (QIconEngineFactoryInterface *factory = qobject_cast<QIconEngineFactoryInterface*>(loader()->instance(suffix))) {
889 if (QIconEngine *engine = factory->create(fileName)) {
890 d = new QIconPrivate;
891 d->engine = engine;
892 d->engine_version = 1;
893 d->v1RefCount = new QAtomicInt(1);
894 }
895 }
896 }
897 }
898#endif
899 // ...then fall back to the default engine
900 if (!d) {
901 d = new QIconPrivate;
902 d->engine = new QPixmapIconEngine;
903 }
904 } else {
905 detach();
906 }
907 d->engine->addFile(fileName, size, mode, state);
908}
909
910/*!
911 \since 4.5
912
913 Returns a list of available icon sizes for the specified \a mode and
914 \a state.
915*/
916QList<QSize> QIcon::availableSizes(Mode mode, State state) const
917{
918 if (!d || !d->engine || d->engine_version < 2)
919 return QList<QSize>();
920 QIconEngineV2 *engine = static_cast<QIconEngineV2*>(d->engine);
921 return engine->availableSizes(mode, state);
922}
923
924/*****************************************************************************
925 QIcon stream functions
926 *****************************************************************************/
927#if !defined(QT_NO_DATASTREAM)
928/*!
929 \fn QDataStream &operator<<(QDataStream &stream, const QIcon &icon)
930 \relates QIcon
931 \since 4.2
932
933 Writes the given \a icon to the the given \a stream as a PNG
934 image. If the icon contains more than one image, all images will
935 be written to the stream. Note that writing the stream to a file
936 will not produce a valid image file.
937*/
938
939QDataStream &operator<<(QDataStream &s, const QIcon &icon)
940{
941 if (s.version() >= QDataStream::Qt_4_3) {
942 if (icon.isNull()) {
943 s << QString();
944 } else {
945 if (icon.d->engine_version > 1) {
946 QIconEngineV2 *engine = static_cast<QIconEngineV2 *>(icon.d->engine);
947 s << engine->key();
948 engine->write(s);
949 } else {
950 // not really supported
951 qWarning("QIcon: Cannot stream QIconEngine. Use QIconEngineV2 instead.");
952 }
953 }
954 } else if (s.version() == QDataStream::Qt_4_2) {
955 if (icon.isNull()) {
956 s << 0;
957 } else {
958 QPixmapIconEngine *engine = static_cast<QPixmapIconEngine *>(icon.d->engine);
959 int num_entries = engine->pixmaps.size();
960 s << num_entries;
961 for (int i=0; i < num_entries; ++i) {
962 s << engine->pixmaps.at(i).pixmap;
963 s << engine->pixmaps.at(i).fileName;
964 s << engine->pixmaps.at(i).size;
965 s << (uint) engine->pixmaps.at(i).mode;
966 s << (uint) engine->pixmaps.at(i).state;
967 }
968 }
969 } else {
970 s << QPixmap(icon.pixmap(22,22));
971 }
972 return s;
973}
974
975/*!
976 \fn QDataStream &operator>>(QDataStream &stream, QIcon &icon)
977 \relates QIcon
978 \since 4.2
979
980 Reads an image, or a set of images, from the given \a stream into
981 the given \a icon.
982*/
983
984QDataStream &operator>>(QDataStream &s, QIcon &icon)
985{
986 if (s.version() >= QDataStream::Qt_4_3) {
987 icon = QIcon();
988 QString key;
989 s >> key;
990 if (key == QLatin1String("QPixmapIconEngine")) {
991 icon.d = new QIconPrivate;
992 QIconEngineV2 *engine = new QPixmapIconEngine;
993 icon.d->engine = engine;
994 engine->read(s);
995#if !defined (QT_NO_LIBRARY) && !defined(QT_NO_SETTINGS)
996 } else if (QIconEngineFactoryInterfaceV2 *factory = qobject_cast<QIconEngineFactoryInterfaceV2*>(loaderV2()->instance(key))) {
997 if (QIconEngineV2 *engine= factory->create()) {
998 icon.d = new QIconPrivate;
999 icon.d->engine = engine;
1000 engine->read(s);
1001 }
1002#endif
1003 }
1004 } else if (s.version() == QDataStream::Qt_4_2) {
1005 icon = QIcon();
1006 int num_entries;
1007 QPixmap pm;
1008 QString fileName;
1009 QSize sz;
1010 uint mode;
1011 uint state;
1012
1013 s >> num_entries;
1014 for (int i=0; i < num_entries; ++i) {
1015 s >> pm;
1016 s >> fileName;
1017 s >> sz;
1018 s >> mode;
1019 s >> state;
1020 if (pm.isNull())
1021 icon.addFile(fileName, sz, QIcon::Mode(mode), QIcon::State(state));
1022 else
1023 icon.addPixmap(pm, QIcon::Mode(mode), QIcon::State(state));
1024 }
1025 } else {
1026 QPixmap pm;
1027 s >> pm;
1028 icon.addPixmap(pm);
1029 }
1030 return s;
1031}
1032
1033#endif //QT_NO_DATASTREAM
1034
1035
1036#ifdef QT3_SUPPORT
1037
1038static int widths[2] = { 22, 32 };
1039static int heights[2] = { 22, 32 };
1040
1041static QSize pixmapSizeHelper(QIcon::Size which)
1042{
1043 int i = 0;
1044 if (which == QIcon::Large)
1045 i = 1;
1046 return QSize(widths[i], heights[i]);
1047}
1048
1049/*!
1050 \enum QIcon::Size
1051 \compat
1052
1053 \value Small Use QStyle::pixelMetric(QStyle::PM_SmallIconSize) instead.
1054 \value Large Use QStyle::pixelMetric(QStyle::PM_LargeIconSize) instead.
1055 \value Automatic N/A.
1056*/
1057
1058/*!
1059 Use pixmap(QSize(...), \a mode, \a state), where the first
1060 argument is an appropriate QSize instead of a \l Size value.
1061
1062 \sa pixmapSize()
1063*/
1064QPixmap QIcon::pixmap(Size size, Mode mode, State state) const
1065{ return pixmap(pixmapSizeHelper(size), mode, state); }
1066
1067/*!
1068 Use pixmap(QSize(...), mode, \a state), where the first argument
1069 is an appropriate QSize instead of a \l Size value, and the
1070 second argument is QIcon::Normal or QIcon::Disabled, depending on
1071 the value of \a enabled.
1072
1073 \sa pixmapSize()
1074*/
1075QPixmap QIcon::pixmap(Size size, bool enabled, State state) const
1076{ return pixmap(pixmapSizeHelper(size), enabled ? Normal : Disabled, state); }
1077
1078/*!
1079 Use one of the other pixmap() overloads.
1080*/
1081QPixmap QIcon::pixmap() const
1082{ return pixmap(pixmapSizeHelper(Small), Normal, Off); }
1083
1084/*!
1085 The pixmap() function now takes a QSize instead of a QIcon::Size,
1086 so there is no need for this function in new code.
1087*/
1088void QIcon::setPixmapSize(Size which, const QSize &size)
1089{
1090 int i = 0;
1091 if (which == Large)
1092 i = 1;
1093 widths[i] = size.width();
1094 heights[i] = size.height();
1095}
1096
1097/*!
1098 Use QStyle::pixelMetric() with QStyle::PM_SmallIconSize or
1099 QStyle::PM_LargeIconSize as the first argument, depending on \a
1100 which.
1101*/
1102QSize QIcon::pixmapSize(Size which)
1103{
1104 return pixmapSizeHelper(which);
1105}
1106
1107/*!
1108 \fn void QIcon::reset(const QPixmap &pixmap, Size size)
1109
1110 Use the constructor that takes a QPixmap and operator=().
1111*/
1112
1113/*!
1114 \fn void QIcon::setPixmap(const QPixmap &pixmap, Size size, Mode mode, State state)
1115
1116 Use addPixmap(\a pixmap, \a mode, \a state) instead. The \a size
1117 parameter is ignored.
1118*/
1119
1120/*!
1121 \fn void QIcon::setPixmap(const QString &fileName, Size size, Mode mode, State state)
1122
1123 Use addFile(\a fileName, \a mode, \a state) instead. The \a size
1124 parameter is ignored.
1125*/
1126
1127#endif // QT3_SUPPORT
1128
1129/*!
1130 \fn DataPtr &QIcon::data_ptr()
1131 \internal
1132*/
1133
1134/*!
1135 \typedef QIcon::DataPtr
1136 \internal
1137*/
1138
1139QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.