/****************************************************************************
** $Id:  qt/qiconset.cpp   3.0.0-beta6   edited Aug 10 17:12 $
**
** Implementation of QIconSet class
**
** Created : 980318
**
** Copyright (C) 1992-2000 Trolltech AS.  All rights reserved.
**
** This file is part of the kernel module of the Qt GUI Toolkit.
**
** This file may be distributed under the terms of the Q Public License
** as defined by Trolltech AS of Norway and appearing in the file
** LICENSE.QPL included in the packaging of this file.
**
** This file may be distributed and/or modified under the terms of the
** GNU General Public License version 2 as published by the Free Software
** Foundation and appearing in the file LICENSE.GPL included in the
** packaging of this file.
**
** Licensees holding valid Qt Enterprise Edition or Qt Professional Edition
** licenses may use this file in accordance with the Qt Commercial License
** Agreement provided with the Software.
**
** This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
** WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
**
** See http://www.trolltech.com/pricing.html or email sales@trolltech.com for
**   information about Qt Commercial License Agreements.
** See http://www.trolltech.com/qpl/ for QPL licensing information.
** See http://www.trolltech.com/gpl/ for GPL licensing information.
**
** Contact info@trolltech.com if any conditions of this licensing are
** not clear to you.
**
**********************************************************************/

#include "qiconset.h"

#ifndef QT_NO_ICONSET

#include "qimage.h"
#include "qbitmap.h"
#include "qapplication.h"
#include "qpainter.h"
#include "qcleanuphandler.h"


class QIconSetPrivate: public QShared
{
public:
    QIconSetPrivate() { instantiated = TRUE; }
    struct Variant {
	Variant(): pm(0), generated(0) {}
	~Variant()
	{
	    delete pm;
	}
	void clearGenerate()
	{
	    if ( generated ) {
		delete pm;
		pm = 0;
		generated = FALSE;
	    }
	}
	QPixmap * pm;
	bool generated;
    };
    Variant vsmall;
    Variant vlarge;
    Variant smallActive;
    Variant largeActive;
    Variant smallDisabled;
    Variant largeDisabled;

    QPixmap defpm;

    Variant on_vsmall;
    Variant on_vlarge;
    Variant on_smallActive;
    Variant on_largeActive;
    Variant on_smallDisabled;
    Variant on_largeDisabled;

    static QSize *large_size;
    static QSize *small_size;
    static bool instantiated; // detect instantiation of QIconSets

#if defined(Q_FULL_TEMPLATE_INSTANTIATION)
    bool operator==( const QIconSetPrivate& ) const { return FALSE; }
#endif
};

/*! \class QIconSet qiconset.h

  \brief The QIconSet class provides a set of icons with different styles and sizes.

  \ingroup graphics
  \ingroup images
  \ingroup shared
  \mainclass

  A QIconSet can generate smaller, larger, active, and disabled pixmaps
  from the set of icons it is given. Such pixmaps are used by
  QToolButton, QHeader, QPopupMenu, etc. to show an icon representing a
  particular action.

  The simplest use of QIconSet is to create one from a QPixmap and then
  use it, allowing Qt to work out all the required icon styles and
  sizes. For example:

  \code
  QToolButton *tb = new QToolButton( QIconSet( QPixmap("open.xpm") ), ... );
  \endcode

  Using whichever pixmap(s) you specify as a base,
  QIconSet provides a set of six icons, each with
    a \link QIconSet::Size Size\endlink and
    a \link QIconSet::Mode Mode\endlink:
  \list
  \i <i>Small Normal</i> - can only be calculated from Large Normal.
  \i <i>Small Disabled</i> - calculated from Large Disabled or Small Normal.
  \i <i>Small Active</i> - same as Small Normal unless you set it.
  \i <i>Large Normal</i> - can only be calculated from Small Normal.
  \i <i>Large Disabled</i> - calculated from Small Disabled or Large Normal.
  \i <i>Large Active</i> - same as Large Normal unless you set it.
  \endlist

  An additional set of six icons can be provided for widgets that have
  an "On" or "Off" state, like checkable menu items or toggleable
  toolbuttons. If you provide pixmaps for the "On" state, but not for
  the "Off" state, the QIconSet will provide the "Off" pixmaps. You may
  specify icons for both states in you wish.

  You can set any of the icons using setPixmap().

  When you retrieve a pixmap using pixmap(Size,Mode,State), QIconSet
  will return the icon that has been set or previously generated for
  that size, mode and state combination. If no pixmap has been set or
  previously generated for the combination QIconSet will generate a
  pixmap based on the pixmap(s) it has been given, cache the generated
  pixmap for later use, and return it. The isGenerated() function
  returns TRUE if an icon was generated by QIconSet.

  The \c Disabled appearance is computed using a "shadow" algorithm
  that produces results very similar to those used in Microsoft
  Windows 95.

  The \c Active appearance is identical to the \c Normal appearance
  unless you use setPixmap() to set it to something special.

  When scaling icons, QIconSet uses \link QImage::smoothScale()
  smooth scaling\endlink, which can partially blend the color component
  of pixmaps.  If the results look poor, the best solution
  is to supply pixmaps in both large and small sizes.

  You can use the static function setIconSize() to set the preferred
  size of the generated large/small icons. The default small size is
  22x22 (compatible with Qt 2.x), while the default large size
  is 32x32. Please note that these sizes only affect generated icons.

  QIconSet provides a function, isGenerated(), that indicates whether
  an icon was set by the application programmer or computed by
  QIconSet itself.

  <h3>Making Classes that use QIconSet</h3>

  If you write your own widgets that have an option to set a small
  pixmap, consider allowing a QIconSet to be set for that pixmap.  The
  Qt class QToolButton is an example of such a widget.

  Provide a method to set a QIconSet, and when you draw the icon, choose
  whichever icon is appropriate for the current state of your widget.
  For example:
  \code
  void MyWidget::drawIcon( QPainter* p, QPoint pos )
  {
      p->drawPixmap( pos, icons->pixmap(QIconSet::Small, isEnabled()) );
  }
  \endcode

  You might also make use of the Active mode, perhaps making your widget
  Active when the mouse is over the widget (see \l{QWidget::enterEvent()}),
  while the mouse is pressed pending the release that will activate
  the function, or when it is the currently selected item. If the widget
  can be toggled, the "On" mode might be used to draw a different icon.

  \sa QPixmap QLabel QToolButton QPopupMenu QMainWindow::setUsesBigPixmaps()
      \link guibooks.html#fowler GUI Design Handbook: Iconic Label \endlink
      \link http://cgl.microsoft.com/clipgallerylive/cgl30/eula.asp?nInterface=0
	    Microsoft Icon Gallery \endlink
*/


/*!
  \enum QIconSet::Size

  This enum type describes the size at which a pixmap is intended to be
  used.
  The currently defined sizes are:

    \value Automatic  The size of the pixmap is determined from its
		    pixel size. This is a useful default.
    \value Small  The pixmap is the smaller of two.
    \value Large  The pixmap is the larger of two.

  If a Small pixmap is not set by QIconSet::setPixmap(), the Large
  pixmap will be automatically scaled down to the size of a small pixmap
  to generate the Small pixmap when required.  Similarly, a Small pixmap
  will be automatically scaled up to generate a Large pixmap. The
  preferred sizes for large/small generated icons can be set using
  setIconSize().

  \sa setIconSize() iconSize() setPixmap() pixmap() QMainWindow::setUsesBigPixmaps()
*/

/*!
  \enum QIconSet::Mode

  This enum type describes the mode for which a pixmap is intended to be
  used.
  The currently defined modes are:

    \value Normal
	 Display the pixmap when the user is
	not interacting with the icon, but the
	functionality represented by the icon is available.
    \value Disabled
	 Display the pixmap when the
	functionality represented by the icon is not available.
    \value Active
	 Display the pixmap when the
	functionality represented by the icon is available and
	the user is interacting with the icon, for example, moving the
	mouse over it or clicking it.
*/

/*!
  \enum QIconSet::State

  This enum describes the state for which a pixmap is intended to be
  used. The \e state can be:

  \value Off  Display the pixmap when the widget is in an "off" state
  \value On  Display the pixmap when the widget is in an "on" state

  \sa setPixmap() pixmap()
*/

/*!
  Constructs a null icon set.
  Use setPixmap(), reset(), or operator=() to set some pixmaps.

  \sa reset()
*/
QIconSet::QIconSet()
: d(0)
{
}


/*!  Constructs an icon set for which the Normal pixmap is
  \a pixmap, which is assumed to be of size \a size.

  The default for \a size is \c Automatic, which means that
  QIconSet will determine whether the pixmap is Small or Large
  from its pixel size.
  Pixmaps less than the width of a small generated icon are
  considered to be Small. You can use setIconSize() to set the preferred
  size of a generated icon.

  \sa setIconSize() reset()
*/
QIconSet::QIconSet( const QPixmap& pixmap, Size size )
: d(0)
{
    reset( pixmap, size );
}


/*!
  Constructs a copy of \a other.  This is very fast.
*/
QIconSet::QIconSet( const QIconSet& other )
{
    d = other.d;
    if ( d )
	d->ref();
}

/*!  Creates an iconset which uses the pixmap \a smallPix for for
  displaying a small icon, and the pixmap \a largePix for displaying a
  large icon.
*/

QIconSet::QIconSet( const QPixmap &smallPix, const QPixmap &largePix )
: d(0)
{
    reset( smallPix, Small );
    reset( largePix, Large );
}

/*!
  Destroys the icon set and frees any allocated resources.
*/
QIconSet::~QIconSet()
{
    if ( d && d->deref() )
	delete d;
}

/*!
  Assigns \a other to this icon set and returns a reference to this
  icon set.

  This is very fast.

  \sa detach()
*/
QIconSet &QIconSet::operator=( const QIconSet &other )
{
    if ( other.d ) {
	other.d->ref();
	if ( d && d->deref() )
	    delete d;
	d = other.d;
	return *this;
    } else {
	if ( d && d->deref() )
	    delete d;
	d = 0;
	return *this;
    }
}

/*!
  Returns TRUE if the icon set is empty; otherwise returns FALSE.
*/
bool QIconSet::isNull() const
{
    return !d;
}

/*!
  Sets this icon set to use pixmap \a pm for the Normal pixmap,
  assuming it to be of size \a size.

  This is equivalent to assigning QIconSet(\a pm, \a size) to this
  icon set.

  This function does nothing if \a pm is a null pixmap.
*/
void QIconSet::reset( const QPixmap & pm, Size size )
{
    if ( pm.isNull() )
	return;

    detach();
    if ( size == Small ||
	 ( size == Automatic && pm.width() <= iconSize( Small ).width() ) )
	setPixmap( pm, Small, Normal );
    else
	setPixmap( pm, Large, Normal );
    d->defpm = pm;
}


/*!
  Sets this icon set to provide pixmap \a pm for size \a size, mode \a
  mode and state \a state. The icon set may also use \a pm for
  generating other pixmaps if they are not explicitly set.

  The \a size can be one of Automatic, Large or Small.  If Automatic is
  used, QIconSet will determine if the pixmap is Small or Large from its
  pixel size.

  Pixmaps less than the width of a small generated icon are
  considered to be Small. You can use setIconSize() to set the preferred
  size of a generated icon.

  This function does nothing if \a pm is a null pixmap.

  \sa reset()
*/
void QIconSet::setPixmap( const QPixmap & pm, Size size, Mode mode, State state )
{
    if ( pm.isNull() )
	return;

    detach();
    if ( d ) {
	if ( state == Off ) {
	    d->vsmall.clearGenerate();
	    d->vlarge.clearGenerate();
	    d->smallDisabled.clearGenerate();
	    d->largeDisabled.clearGenerate();
	    d->smallActive.clearGenerate();
	    d->largeActive.clearGenerate();
	} else {
	    d->on_vsmall.clearGenerate();
	    d->on_vlarge.clearGenerate();
	    d->on_smallDisabled.clearGenerate();
	    d->on_largeDisabled.clearGenerate();
	    d->on_smallActive.clearGenerate();
	    d->on_largeActive.clearGenerate();
	}
    } else {
	d = new QIconSetPrivate;
    }
    if ( size == Large || (size == Automatic && pm.width() > iconSize( Small ).width()) ) {
	switch( mode ) {
	case Active:
	    if ( state == Off ) {
		if ( !d->largeActive.pm )
		    d->largeActive.pm = new QPixmap;
		*d->largeActive.pm = pm;
	    } else {
		if ( !d->on_largeActive.pm )
		    d->on_largeActive.pm = new QPixmap;
		*d->on_largeActive.pm = pm;
	    }
	    break;
	case Disabled:
	    if ( state == Off ) {
		if ( !d->largeDisabled.pm )
		    d->largeDisabled.pm = new QPixmap;
		*d->largeDisabled.pm = pm;
	    } else {
		if ( !d->on_largeDisabled.pm )
		    d->on_largeDisabled.pm = new QPixmap;
		*d->on_largeDisabled.pm = pm;
	    }
	    break;
	case Normal:
	default:
	    if ( state == Off ) {
		if ( !d->vlarge.pm )
		    d->vlarge.pm = new QPixmap;
		*d->vlarge.pm = pm;
	    } else {
		if ( !d->on_vlarge.pm )
		    d->on_vlarge.pm = new QPixmap;
		*d->on_vlarge.pm = pm;
	    }
	    break;
	}
    } else if ( size == Small  ||
		(size == Automatic && pm.width() <= iconSize( Small ).width()) )
    {
	switch( mode ) {
	case Active:
	    if ( state == Off ) {
		if ( !d->smallActive.pm )
		    d->smallActive.pm = new QPixmap;
		*d->smallActive.pm = pm;
	    } else {
		if ( !d->on_smallActive.pm )
		    d->on_smallActive.pm = new QPixmap;
		*d->on_smallActive.pm = pm;
	    }
	    break;
	case Disabled:
	    if ( state == Off ) {
		if ( !d->smallDisabled.pm )
		    d->smallDisabled.pm = new QPixmap;
		*d->smallDisabled.pm = pm;
	    } else {
		if ( !d->on_smallDisabled.pm )
		    d->on_smallDisabled.pm = new QPixmap;
		*d->on_smallDisabled.pm = pm;
	    }
	    break;
	case Normal:
	default:
	    if ( state == Off ) {
		if ( !d->vsmall.pm )
		    d->vsmall.pm = new QPixmap;
		*d->vsmall.pm = pm;
	    } else {
		if ( !d->on_vsmall.pm )
		    d->on_vsmall.pm = new QPixmap;
		*d->on_vsmall.pm = pm;
	    }
	    break;
	}
#if defined(QT_CHECK_RANGE)
    } else {
	qWarning("QIconSet::setPixmap: invalid size passed");
#endif
    }
}


/*!
    \overload
  Sets this icon set to load the file called \a fileName as a pixmap and
  use it for size \a size, mode \a mode and state \a state. The icon set
  may also use this pixmap for generating other pixmaps if they are not
  explicitly set.

  The \a size can be one of Automatic, Large or Small.  If Automatic is
  used, QIconSet will determine if the pixmap is Small or Large from its
  pixel size.
  Pixmaps less than the width of a small generated icon are
  considered to be Small. You can use setIconSize() to set the preferred
  size of a generated icon.
*/
void QIconSet::setPixmap( const QString &fileName, Size size, Mode mode, State state )
{
    QPixmap p;
    p.load( fileName );
    if ( !p.isNull() )
	setPixmap( p, size, mode, state );
}


/*!
  Returns a pixmap with size \a size, mode \a mode and state \a state,
  generating one if necessary. Generated pixmaps are cached.
*/
QPixmap QIconSet::pixmap( Size size, Mode mode, State state ) const
{
    if ( !d )
	return QPixmap();

    QImage i;
    QIconSetPrivate * p = ((QIconSet *)this)->d;
    QPixmap * pm = 0;
    if ( state == Off ) {
	if ( size == Large ) {
	    switch( mode ) {
	    case Normal:
		if ( !p->vlarge.pm ) {
		    Q_ASSERT( p->vsmall.pm );
		    i = p->vsmall.pm->convertToImage();
		    i = i.smoothScale( iconSize( Large ).width(), iconSize( Large ).height() );
		    p->vlarge.pm = new QPixmap;
		    p->vlarge.generated = TRUE;
		    p->vlarge.pm->convertFromImage( i );
		    if ( !p->vlarge.pm->mask() ) {
			i = i.createHeuristicMask();
			QBitmap tmp;
			tmp.convertFromImage( i, Qt::MonoOnly + Qt::ThresholdDither );
			p->vlarge.pm->setMask( tmp );
		    }
		}
		pm = p->vlarge.pm;
		break;
	    case Active:
		if ( !p->largeActive.pm ) {
		    p->largeActive.pm = new QPixmap( pixmap(Large, Normal) );
		    p->largeActive.generated = TRUE;
		}
		pm = p->largeActive.pm;
		break;
	    case Disabled:
		if ( !p->largeDisabled.pm ) {
		    QBitmap tmp;
		    if ( ( !p->vlarge.pm || p->vlarge.generated ) && !p->smallDisabled.generated &&
			 p->smallDisabled.pm && !p->smallDisabled.pm->isNull() ) {
			// if there's a hand-drawn disabled small image,
			// but the normal big one is null or generated, use the
			// hand-drawn one to generate this one.
			i = p->smallDisabled.pm->convertToImage();
			i = i.smoothScale( iconSize( Large ).width(), iconSize( Large ).height() );
			p->largeDisabled.pm = new QPixmap;
			p->largeDisabled.generated = TRUE;
			p->largeDisabled.pm->convertFromImage( i );
			if ( !p->largeDisabled.pm->mask() ) {
			    i = i.createHeuristicMask();
			    tmp.convertFromImage( i, Qt::MonoOnly + Qt::ThresholdDither );
			}
		    } else {
			if (pixmap( Large, Normal).mask())
			    tmp = *pixmap( Large, Normal).mask();
			else {
			    QPixmap conv = pixmap( Large, Normal );
			    if ( !conv.isNull() ) {
				i = conv.convertToImage();
				i = i.createHeuristicMask();
				tmp.convertFromImage( i, Qt::MonoOnly + Qt::ThresholdDither );
			    }
			}
			p->largeDisabled.pm
			    = new QPixmap( p->vlarge.pm->width() + 1,
					   p->vlarge.pm->height() + 1 );
			QColorGroup dis( QApplication::palette().disabled() );
			p->largeDisabled.pm->fill( dis.background() );
			QPainter painter( p->largeDisabled.pm );
			painter.setPen( dis.base() );
			painter.drawPixmap( 1, 1, tmp );
			painter.setPen( dis.foreground() );
			painter.drawPixmap( 0, 0, tmp );
		    }
		    if ( !p->largeDisabled.pm->mask() ) {
			if ( !tmp.mask() )
			    tmp.setMask( tmp );
			QBitmap mask( d->largeDisabled.pm->size() );
			mask.fill( Qt::color0 );
			QPainter painter( &mask );
			painter.drawPixmap( 0, 0, tmp );
			painter.drawPixmap( 1, 1, tmp );
			painter.end();
			p->largeDisabled.pm->setMask( mask );
		    }
		    p->largeDisabled.generated = TRUE;
		}
		pm = p->largeDisabled.pm;
		break;
	    }
	} else { // size == Small
	    switch( mode ) {
	    case Normal:
		if ( !p->vsmall.pm ) {
		    Q_ASSERT( p->vlarge.pm );
		    i = p->vlarge.pm->convertToImage();
		    i = i.smoothScale( iconSize( Small ).width(), iconSize( Small ).height() );
		    p->vsmall.pm = new QPixmap;
		    p->vsmall.generated = TRUE;
		    p->vsmall.pm->convertFromImage( i );
		    if ( !p->vsmall.pm->mask() ) {
			i = i.createHeuristicMask();
			QBitmap tmp;
			tmp.convertFromImage( i, Qt::MonoOnly + Qt::ThresholdDither );
			p->vsmall.pm->setMask( tmp );
		    }
		}
		pm = p->vsmall.pm;
		break;
	    case Active:
		if ( !p->smallActive.pm ) {
		    p->smallActive.pm = new QPixmap( pixmap(Small, Normal) );
		    p->smallActive.generated = TRUE;
		}
		pm = p->smallActive.pm;
		break;
	    case Disabled:
		if ( !p->smallDisabled.pm ) {
		    QBitmap tmp;
		    if ( ( !p->vsmall.pm || p->vsmall.generated ) && !p->largeDisabled.generated &&
			 p->largeDisabled.pm && !p->largeDisabled.pm->isNull() ) {
			// if there's a hand-drawn disabled large image,
			// but the normal small one is NULL or generated, use the
			// hand-drawn one to generate this one.
			i = p->largeDisabled.pm->convertToImage();
			i = i.smoothScale( iconSize( Large ).width(), iconSize( Large ).height() );
			p->smallDisabled.pm = new QPixmap;
			p->smallDisabled.generated = TRUE;
			p->smallDisabled.pm->convertFromImage( i );
			if ( !p->smallDisabled.pm->mask() ) {
			    i = i.createHeuristicMask();
			    tmp.convertFromImage( i, Qt::MonoOnly + Qt::ThresholdDither );
			}
		    } else {
			if ( pixmap( Small, Normal).mask())
			    tmp = *pixmap( Small, Normal).mask();
			else {
			    QPixmap conv = pixmap( Small, Normal );
			    if ( !conv.isNull() ) {
				i = conv.convertToImage();
				i = i.createHeuristicMask();
				tmp.convertFromImage( i, Qt::MonoOnly + Qt::ThresholdDither );
			    }
			}
			p->smallDisabled.pm
			    = new QPixmap( p->vsmall.pm->width() + 1,
					   p->vsmall.pm->height() + 1);
			QColorGroup dis( QApplication::palette().disabled() );
			p->smallDisabled.pm->fill( dis.background() );
			QPainter painter( p->smallDisabled.pm );
			painter.setPen( dis.base() );
			painter.drawPixmap( 1, 1, tmp );
			painter.setPen( dis.foreground() );
			painter.drawPixmap( 0, 0, tmp );
		    }
		    if ( !p->smallDisabled.pm->mask() ) {
			if ( !tmp.mask() )
			    tmp.setMask( tmp );
			QBitmap mask( d->smallDisabled.pm->size() );
			mask.fill( Qt::color0 );
			QPainter painter( &mask );
			painter.drawPixmap( 0, 0, tmp );
			painter.drawPixmap( 1, 1, tmp );
			painter.end();
			p->smallDisabled.pm->setMask( mask );
		    }

		    p->smallDisabled.generated = TRUE;
		}
		pm = p->smallDisabled.pm;
		break;
	    }
	}
    } else { // state == On
	if ( size == Large ) {
	    switch( mode ) {
	    case Normal:
		if ( !p->on_vlarge.pm ) {
		    QPixmap *fallback = 0;
		    if ( !(fallback = p->on_vsmall.pm ) ) {
			pm = p->on_vlarge.pm = new QPixmap( pixmap(Large, Normal, Off) );
			p->on_vlarge.generated = TRUE;
			break;
		    }
		    Q_ASSERT( fallback );
		    i = fallback->convertToImage();
		    i = i.smoothScale( iconSize( Large ).width(), iconSize( Large ).height() );
		    p->on_vlarge.pm = new QPixmap;
		    p->on_vlarge.generated = TRUE;
		    p->on_vlarge.pm->convertFromImage( i );
		    if ( !p->on_vlarge.pm->mask() ) {
			i = i.createHeuristicMask();
			QBitmap tmp;
			tmp.convertFromImage( i, Qt::MonoOnly + Qt::ThresholdDither );
			p->on_vlarge.pm->setMask( tmp );
		    }
		}
		pm = p->on_vlarge.pm;
		break;
	    case Active:
		if ( !p->on_largeActive.pm ) {
		    p->on_largeActive.pm = new QPixmap( pixmap(Large, Normal, On) );
		    p->on_largeActive.generated = TRUE;
		}
		pm = p->on_largeActive.pm;
		break;
	    case Disabled:
		if ( !p->on_largeDisabled.pm ) { // have to generate
		    QBitmap tmp;
		    QPixmap *disBase = 0;
		    bool mustScale = FALSE;
		    // use any non-generated disabled pixmap we have
		    if ( p->largeDisabled.pm && !p->largeDisabled.pm->isNull() && !p->largeDisabled.generated ) {
			disBase = p->largeDisabled.pm;
		    } else if ( p->on_smallDisabled.pm && !p->on_smallDisabled.pm->isNull() && !p->on_smallDisabled.generated ) {
			disBase = p->on_smallDisabled.pm;
			mustScale = TRUE;
		    } else if ( p->smallDisabled.pm && !p->smallDisabled.pm->isNull() && !p->smallDisabled.generated ) {
			disBase = p->smallDisabled.pm;
			mustScale = TRUE;
		    }

		    if ( disBase ) {
			i = disBase->convertToImage();
			if ( mustScale )
			    i = i.smoothScale( iconSize( Large ).width(), iconSize( Large ).height() );
			p->on_largeDisabled.pm = new QPixmap;
			p->on_largeDisabled.generated = TRUE;
			p->on_largeDisabled.pm->convertFromImage( i );
			if ( !p->on_largeDisabled.pm->mask() ) {
			    i = i.createHeuristicMask();
			    tmp.convertFromImage( i, Qt::MonoOnly + Qt::ThresholdDither );
			}
		    } else {
			if (pixmap( Large, Normal, On ).mask())
			    tmp = *pixmap( Large, Normal, On ).mask();
			else {
			    QPixmap conv = pixmap( Large, Normal, On );
			    if ( !conv.isNull() ) {
				i = conv.convertToImage();
				i = i.createHeuristicMask();
				tmp.convertFromImage( i, Qt::MonoOnly + Qt::ThresholdDither );
			    }
			}
			p->on_largeDisabled.pm
			    = new QPixmap( p->on_vlarge.pm->width() + 1,
					   p->on_vlarge.pm->height() + 1);
			QColorGroup dis( QApplication::palette().disabled() );
			p->on_largeDisabled.pm->fill( dis.background() );
			QPainter painter( p->on_largeDisabled.pm );
			painter.setPen( dis.base() );
			painter.drawPixmap( 1, 1, tmp );
			painter.setPen( dis.foreground() );
			painter.drawPixmap( 0, 0, tmp );
		    }
		    if ( !p->on_largeDisabled.pm->mask() ) {
			if ( !tmp.mask() )
			    tmp.setMask( tmp );
			QBitmap mask( d->on_largeDisabled.pm->size() );
			mask.fill( Qt::color0 );
			QPainter painter( &mask );
			painter.drawPixmap( 0, 0, tmp );
			painter.drawPixmap( 1, 1, tmp );
			painter.end();
			p->on_largeDisabled.pm->setMask( mask );
		    }
		    p->on_largeDisabled.generated = TRUE;
		}
		pm = p->on_largeDisabled.pm;
		break;
	    }
	} else { // on, small
	    switch( mode ) {
	    case Normal:
		if ( !p->on_vsmall.pm ) {
		    QPixmap *fallback = 0;
		    if ( !( fallback = p->on_vlarge.pm ) ) {
			pm = p->on_vsmall.pm = new QPixmap( pixmap(Small, Normal, Off) );
			p->on_vsmall.generated = TRUE;
			break;
		    }
		    Q_ASSERT( fallback );
		    i = fallback->convertToImage();
		    i = i.smoothScale( iconSize( Small ).width(), iconSize( Small ).height() );
		    p->on_vsmall.pm = new QPixmap;
		    p->on_vsmall.generated = TRUE;
		    p->on_vsmall.pm->convertFromImage( i );
		    if ( !p->on_vsmall.pm->mask() ) {
			i = i.createHeuristicMask();
			QBitmap tmp;
			tmp.convertFromImage( i, Qt::MonoOnly + Qt::ThresholdDither );
			p->on_vsmall.pm->setMask( tmp );
		    }
		}
		pm = p->on_vsmall.pm;
		break;
	    case Active:
		if ( !p->on_smallActive.pm ) {
		    p->on_smallActive.pm = new QPixmap( pixmap(Small, Normal, On) );
		    p->on_smallActive.generated = TRUE;
		}
		pm = p->on_smallActive.pm;
		break;
	    case Disabled:
		if ( !p->on_smallDisabled.pm ) { // must generate
		    QBitmap tmp;
		    QPixmap *disBase = 0;
		    bool mustScale = FALSE;
		    // use any non-generated disabled pixmap we have
		    if ( p->smallDisabled.pm && !p->smallDisabled.pm->isNull() && !p->smallDisabled.generated ) {
			disBase = p->smallDisabled.pm;
		    } else if ( p->on_largeDisabled.pm && !p->on_largeDisabled.pm->isNull() && !p->on_largeDisabled.generated ) {
			disBase = p->on_largeDisabled.pm;
			mustScale = TRUE;
		    } else if ( p->largeDisabled.pm && !p->largeDisabled.pm->isNull() && !p->largeDisabled.generated ) {
			disBase = p->largeDisabled.pm;
			mustScale = TRUE;
		    }

		    if ( disBase ) {
			i = disBase->convertToImage();
			if ( mustScale )
			    i = i.smoothScale( iconSize( Small ).width(), iconSize( Small ).height() );
			p->on_smallDisabled.pm = new QPixmap;
			p->on_smallDisabled.generated = TRUE;
			p->on_smallDisabled.pm->convertFromImage( i );
			if ( !p->on_smallDisabled.pm->mask() ) {
			    i = i.createHeuristicMask();
			    tmp.convertFromImage( i, Qt::MonoOnly + Qt::ThresholdDither );
			}
		    } else {
			if ( pixmap( Small, Normal, On ).mask())
			    tmp = *pixmap( Small, Normal, On ).mask();
			else {
			    QPixmap conv = pixmap( Small, Normal );
			    if ( !conv.isNull() ) {
				i = conv.convertToImage();
				i = i.createHeuristicMask();
				tmp.convertFromImage( i, Qt::MonoOnly + Qt::ThresholdDither );
			    }
			}
			p->on_smallDisabled.pm
			    = new QPixmap( p->on_vsmall.pm->width() + 1,
					   p->on_vsmall.pm->height() + 1);
			QColorGroup dis( QApplication::palette().disabled() );
			p->on_smallDisabled.pm->fill( dis.background() );
			QPainter painter( p->on_smallDisabled.pm );
			painter.setPen( dis.base() );
			painter.drawPixmap( 1, 1, tmp );
			painter.setPen( dis.foreground() );
			painter.drawPixmap( 0, 0, tmp );
		    }
		    if ( !p->on_smallDisabled.pm->mask() ) {
			if ( !tmp.mask() )
			    tmp.setMask( tmp );
			QBitmap mask( d->on_smallDisabled.pm->size() );
			mask.fill( Qt::color0 );
			QPainter painter( &mask );
			painter.drawPixmap( 0, 0, tmp );
			painter.drawPixmap( 1, 1, tmp );
			painter.end();
			p->on_smallDisabled.pm->setMask( mask );
		    }

		    p->on_smallDisabled.generated = TRUE;
		}
		pm = p->on_smallDisabled.pm;
		break;
	    }
	}
    }
    Q_ASSERT( pm );
    return *pm;
}


/*!
  \overload
  Returns a pixmap with size \a size, state \a state and a Mode which is
  Normal if \a enabled is TRUE, or Disabled if \a enabled is FALSE.
*/
QPixmap QIconSet::pixmap( Size size, bool enabled, State state ) const
{
    return pixmap( size, enabled ? Normal : Disabled, state );
}


/*!
  Returns TRUE if the pixmap with size \a size, mode \a mode and state
  \a state has been generated; otherwise returns FALSE.
*/
bool QIconSet::isGenerated( Size size, Mode mode, State state ) const
{
    if ( !d )
	return TRUE;

    if ( state == Off ) {
	if ( size == Large ) {
	    if ( mode == Disabled )
		return d->largeDisabled.generated || !d->largeDisabled.pm;
	    else if ( mode == Active )
		return d->largeActive.generated || !d->largeActive.pm;
	    else
		return d->vlarge.generated || !d->vlarge.pm;
	} else if ( size == Small ) {
	    if ( mode == Disabled )
		return d->smallDisabled.generated || !d->smallDisabled.pm;
	    else if ( mode == Active )
		return d->smallActive.generated || !d->smallActive.pm;
	    else
		return d->vsmall.generated || !d->vsmall.pm;
	}
    } else {
	if ( size == Large ) {
	    if ( mode == Disabled )
		return d->on_largeDisabled.generated || !d->on_largeDisabled.pm;
	    else if ( mode == Active )
		return d->on_largeActive.generated || !d->on_largeActive.pm;
	    else
		return d->on_vlarge.generated || !d->on_vlarge.pm;
	} else if ( size == Small ) {
	    if ( mode == Disabled )
		return d->on_smallDisabled.generated || !d->on_smallDisabled.pm;
	    else if ( mode == Active )
		return d->on_smallActive.generated || !d->on_smallActive.pm;
	    else
		return d->on_vsmall.generated || !d->on_vsmall.pm;
	}
    }
    return FALSE;
}

/*!
  Clears all generated pixmaps.
*/
void QIconSet::clearGenerated()
{
    if ( !d )
	return;

    d->vsmall.clearGenerate();
    d->vlarge.clearGenerate();
    d->smallActive.clearGenerate();
    d->largeActive.clearGenerate();
    d->smallDisabled.clearGenerate();
    d->largeDisabled.clearGenerate();

    d->on_vsmall.clearGenerate();
    d->on_vlarge.clearGenerate();
    d->on_smallActive.clearGenerate();
    d->on_largeActive.clearGenerate();
    d->on_smallDisabled.clearGenerate();
    d->on_largeDisabled.clearGenerate();
}

/*!
  \overload

  Returns the pixmap originally provided to the constructor or
  to reset().  This is the Normal pixmap of unspecified Size.

  \sa reset()
*/

QPixmap QIconSet::pixmap() const
{
    if ( !d )
	return QPixmap();

    return d->defpm;
}


/*!
  Detaches this icon set from others with which it may share data.

  You will never need to call this function; other QIconSet functions
  call it as necessary.
*/
void QIconSet::detach()
{
    if ( !d ) {
	d = new QIconSetPrivate;
	return;
    }

    if ( d->count == 1 )
	return;

    QIconSetPrivate * p = new QIconSetPrivate;
    p->vsmall.pm = d->vsmall.pm;
    p->vsmall.generated = d->vsmall.generated;
    p->smallActive.pm = d->smallActive.pm;
    p->smallActive.generated = d->smallActive.generated;
    p->smallDisabled.pm = d->smallDisabled.pm;
    p->smallDisabled.generated = d->smallDisabled.generated;
    p->vlarge.pm = d->vlarge.pm;
    p->vlarge.generated = d->vlarge.generated;
    p->largeActive.pm = d->largeActive.pm;
    p->largeActive.generated = d->largeActive.generated;
    p->largeDisabled.pm = d->largeDisabled.pm;
    p->largeDisabled.generated = d->largeDisabled.generated;
    p->defpm = d->defpm;

    p->on_vsmall.pm = d->on_vsmall.pm;
    p->on_vsmall.generated = d->on_vsmall.generated;
    p->on_smallActive.pm = d->on_smallActive.pm;
    p->on_smallActive.generated = d->on_smallActive.generated;
    p->on_smallDisabled.pm = d->on_smallDisabled.pm;
    p->on_smallDisabled.generated = d->on_smallDisabled.generated;
    p->on_vlarge.pm = d->on_vlarge.pm;
    p->on_vlarge.generated = d->on_vlarge.generated;
    p->on_largeActive.pm = d->on_largeActive.pm;
    p->on_largeActive.generated = d->on_largeActive.generated;
    p->on_largeDisabled.pm = d->on_largeDisabled.pm;
    p->on_largeDisabled.generated = d->on_largeDisabled.generated;

    d->deref();
    d = p;
}

// static stuff

QSize *QIconSetPrivate::small_size = 0;
QSize *QIconSetPrivate::large_size = 0;
bool   QIconSetPrivate::instantiated = FALSE;

QCleanupHandler<QSize> qt_iconset_sizes_cleanup;

/*!
  Set the preferred size for all small or large icons that are generated
  after this call. If \a s is Small, sets the preferred size of small
  generated icons to \a size. Similarly, if \a s is Large, sets the
  preferred size of large generated icons to \a size.

  Note that cached icons will not be regenerated, so it is recommended
  that you set the preferred icon sizes before generating any icon sets.

  \sa iconSize()
*/
void QIconSet::setIconSize( Size s, const QSize & size )
{
    if ( QIconSetPrivate::instantiated )
	qWarning( "QIconSet: setIconSize() is called after QIconSets have "
		  "been instantiated." );

    if ( !QIconSetPrivate::small_size ) {
	QIconSetPrivate::small_size = new QSize( 22, 22 ); // default small size
	QIconSetPrivate::large_size = new QSize( 32, 32 ); // default large size
	qt_iconset_sizes_cleanup.add( &QIconSetPrivate::small_size );
	qt_iconset_sizes_cleanup.add( &QIconSetPrivate::large_size );
    }
    if ( s == Small )
	*QIconSetPrivate::small_size = size;
    else if ( s == Large )
	*QIconSetPrivate::large_size = size;
}

/*!
    If \a s is Small, returns the preferred size of a small generated
    icon; if \a s is Large, returns the preferred size of a large
    generated icon.

  \sa setIconSize()
*/
const QSize & QIconSet::iconSize( Size s )
{
    if ( !QIconSetPrivate::small_size ) {
	QIconSetPrivate::small_size = new QSize( 22, 22 );
	QIconSetPrivate::large_size = new QSize( 32, 32 );
	qt_iconset_sizes_cleanup.add( &QIconSetPrivate::small_size );
	qt_iconset_sizes_cleanup.add( &QIconSetPrivate::large_size );
    }
    if ( s == Small )
	return *QIconSetPrivate::small_size;
    else
	return *QIconSetPrivate::large_size;
}
#endif // QT_NO_ICONSET
