Logo Search packages:      
Sourcecode: wengophone version File versions  Download package

iconset.cpp

/*
 * iconset.cpp - various graphics handling classes
 * Copyright (C) 2001-2003  Justin Karneges, Michail Pishchagin
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

#include "iconset.h"
#include "zip.h"

#include <qobject.h>
#include <qfile.h>
#include <qfileinfo.h>
#include <qtimer.h>

#include <qpixmap.h>
#include <qimage.h>
#include <qiconset.h>
#include <qregexp.h>
#include <qmime.h>
#include <qdom.h>
#include <qfile.h>
#include <qfileinfo.h>
#include <qcstring.h>
#include <qptrlist.h>

#include "anim.h"

// sound support
//#define ICONSET_SOUND
#ifdef ICONSET_SOUND
#     include <qdatastream.h>
#     include <qapplication.h>
#     include <qca.h>
#endif

#include<qapplication.h>

//----------------------------------------------------------------------------
// Impix
//----------------------------------------------------------------------------

//! \class Impix iconset.h
//! \brief Combines a QPixmap and QImage into one class
//!
//!  Normally, it is common to use QPixmap for all application graphics.
//!  However, sometimes it is necessary to access pixel data, which means
//!  a time-costly conversion to QImage.  Impix does this conversion on
//!  construction, and keeps a copy of both a QPixmap and QImage for fast
//!  access to each.  What you gain in speed you pay in memory, as an Impix
//!  should occupy twice the space.
//!
//!  An Impix can be conveniently created from either a QImage or QPixmap
//!  source, and can be casted back to either type.
//!
//!  You may also call unload() to free the image data.
//!
//!  \code
//!  Impix i = QImage("icon.png");
//!  QLabel *iconLabel;
//!  iconLabel->setPixmap(i.pixmap());
//!  QImage image = i.image();
//!  \endcode

//static int impix_p_count = 0; // DEBUG
//static int impix_count = 0;

//! \if _hide_doc_
class Impix::Private : public QShared
{
public:
      Private()
      {
            //impix_p_count++; // DEBUG

            pixmap = 0;
            image = 0;
      }

      ~Private()
      {
            unload();

            //qWarning("impix_p_count = %d", --impix_p_count); // DEBUG
      }

      Private(const Private &from)
      : QShared()
      {
            //impix_p_count++; // DEBUG

            pixmap = from.pixmap ? new QPixmap(*from.pixmap) : 0;
            image  = from.image  ? new QImage (*from.image)  : 0;
      }

      void unload()
      {
            if ( pixmap )
                  delete pixmap;
            pixmap = 0;
            if ( image )
                  delete image;
            image = 0;
      }

      QPixmap *pixmap;
      QImage *image;
};
//! \endif

//! \brief Construct a null Impix
//!
//! Constructs an Impix without any image data.
00127 Impix::Impix()
{
      //impix_count++;

      d = new Private;
}

//! \brief Construct an Impix based on a QPixmap
//!
//! Constructs an Impix by making a copy of a QPixmap and creating a QImage from it.
//! \param from - source QPixmap
00138 Impix::Impix(const QPixmap &from)
{
      //impix_count++;

      d = new Private;
      *this = from;
}

//! \brief Construct an Impix based on a QImage
//!
//! Constructs an Impix by making a copy of a QImage and creating a QPixmap from it.
//! \param from - source QImage
00150 Impix::Impix(const QImage &from)
{
      //impix_count++;

      d = new Private;
      *this = from;
}

//!
//! \brief Copy constructor
00160 Impix::Impix(const Impix &from)
{
      //impix_count++;

      d = from.d;
      d->ref();
}

//!
//! Sets the Impix to \a from
00170 Impix & Impix::operator=(const Impix &from)
{
      if ( d->deref() )
            delete d;

      d = from.d;
      d->ref();

      return *this;
}

//!
//! Unloads image data, making it null.
00183 void Impix::unload()
{
      if ( isNull() )
            return;

      detach();
      d->unload();
}

//!
//! Destroys the image.
00194 Impix::~Impix()
{
      //qWarning("impix_count = %d", --impix_count);

      if ( d->deref() )
            delete d;
}

bool Impix::isNull() const
{
      return d->image || d->pixmap ? false: true;
}

const QPixmap & Impix::pixmap() const
{
      if(!d->pixmap)
            d->pixmap = new QPixmap;
      return *d->pixmap;
}

const QImage & Impix::image() const
{
      if(!d->image)
            d->image = new QImage;
      return *d->image;
}

void Impix::setPixmap(const QPixmap &x)
{
      detach();

      d->unload();
      d->pixmap = new QPixmap(x);
      d->image = new QImage(x.convertToImage());
}

void Impix::setImage(const QImage &x)
{
      detach();

      d->unload();
      d->pixmap = new QPixmap;
      d->pixmap->convertFromImage(x);
      d->image = new QImage(x);
}

bool Impix::loadFromData(const QByteArray &ba)
{
      qApp->lock();

      bool ret = false;
      QImage img;
      if ( img.loadFromData( ba ) ) {
            setImage ( img );
            ret = true;
      }

      qApp->unlock();

      return ret;
}

Impix Impix::copy() const
{
      Impix impix;
      delete impix.d;
      impix.d = new Private( *this->d );

      return impix;
}

void Impix::detach()
{
      if ( d->count > 1 )
            *this = copy();
}

//----------------------------------------------------------------------------
// IconSharedObject
//----------------------------------------------------------------------------

class IconSharedObject : public QObject
{
      Q_OBJECT
public:
      IconSharedObject()
#ifdef ICONSET_SOUND
      : QObject(qApp, "IconSharedObject")
#endif
      { }

      QString unpackPath;

signals:
      void playSound(QString);

private:
      friend class Icon;
};

static IconSharedObject *iconSharedObject = 0;

//----------------------------------------------------------------------------
// Icon
//----------------------------------------------------------------------------

/*!
      \class Icon
      \brief Can contain Anim and stuff

      This class can be used for storing application icons as well as emoticons
      (it has special functions in order to do that).

      Icons can contain animation, associated sound files, its own names.

      For implementing emoticon functionality, Icon can have associated text
      values and QRegExp for easy searching.
*/

//! \if _hide_doc_
class Icon::Private : public QObject, public QShared
{
      Q_OBJECT
public:
      Private()
      {
            anim = 0;
            iconSet = 0;
            activatedCount = 0;
            text.setAutoDelete(true);
      }

      ~Private()
      {
            unloadAnim();
            if ( iconSet )
                  delete iconSet;
      }

      Private(const Private &from)
      : QObject(), QShared()
      {
            name = from.name;
            regExp = from.regExp;
            text = from.text;
            sound = from.sound;
            impix = from.impix;
            anim = from.anim ? new Anim ( *from.anim ) : 0;
            iconSet = 0;
      }

      void unloadAnim()
      {
            if ( anim )
                  delete anim;
            anim = 0;
      }

signals:
      void pixmapChanged(const QPixmap &);
      void iconModified(const QPixmap &);

public:
      const QPixmap &pixmap() const
      {
            if ( anim )
                  return anim->framePixmap();
            return impix.pixmap();
      }

public slots:
      void animUpdate() { emit pixmapChanged( pixmap() ); }

public:
      QString name;
      QRegExp regExp;
      QDict<QString> text;
      QString sound;

      Impix impix;
      Anim *anim;
      QIconSet *iconSet;

      int activatedCount;
      friend class Icon;
};
//! \endif

//static int icon_count = 0;

//!
//! Constructs empty Icon.
00386 Icon::Icon()
: QObject(0, 0)
{
      //icon_count++;

      d = new Private;

      connect(d, SIGNAL(pixmapChanged(const QPixmap &)), SIGNAL(pixmapChanged(const QPixmap &)));
      connect(d, SIGNAL(iconModified(const QPixmap &)), SIGNAL(iconModified(const QPixmap &)));
}

//!
//! Destroys Icon.
00399 Icon::~Icon()
{
      //qWarning("icon_count = %d", --icon_count);

      if ( d->deref() )
            delete d;
}

//!
//! Creates new icon, that is a copy of \a from. Note, that if one icon will be changed,
//! other will be changed as well. (that's because image data is shared)
00410 Icon::Icon(const Icon &from)
: QObject(0, 0)
{
      //icon_count++;

      d = from.d;
      d->ref();

      connect(d, SIGNAL(pixmapChanged(const QPixmap &)), SIGNAL(pixmapChanged(const QPixmap &)));
      connect(d, SIGNAL(iconModified(const QPixmap &)), SIGNAL(iconModified(const QPixmap &)));
}

//!
//! Creates new icon, that is a copy of \a from. Note, that if one icon will be changed,
//! other will be changed as well. (that's because image data is shared)
00425 Icon & Icon::operator= (const Icon &from)
{
      if ( d->deref() )
            delete d;

      d = from.d;
      d->ref();

      connect(d, SIGNAL(pixmapChanged(const QPixmap &)), SIGNAL(pixmapChanged(const QPixmap &)));
      connect(d, SIGNAL(iconModified(const QPixmap &)), SIGNAL(iconModified(const QPixmap &)));

      return *this;
}

Icon Icon::copy() const
{
      Icon icon;
      delete icon.d;
      icon.d = new Private( *this->d );

      connect(icon.d, SIGNAL(pixmapChanged(const QPixmap &)), &icon, SIGNAL(pixmapChanged(const QPixmap &)));
      connect(icon.d, SIGNAL(iconModified(const QPixmap &)),  &icon, SIGNAL(iconModified(const QPixmap &)));

      return icon;
}

void Icon::detach()
{
      if ( d->count != 1 ) // only if >1 reference
            *this = copy();
}

//!
//! Returns \c true when icon contains animation.
00459 bool Icon::isAnimated() const
{
      return d->anim != 0;
}

//!
//! Returns QPixmap of current frame.
00466 const QPixmap &Icon::pixmap() const
{
      return d->pixmap();
}

//!
//! Returns QImage of current frame.
00473 const QImage &Icon::image() const
{
      if ( d->anim )
            return d->anim->frameImage();
      return d->impix.image();
}

//!
//! Returns Impix of first animation frame.
//! \sa setImpix()
00483 const Impix &Icon::impix() const
{
      return d->impix;
}

//!
//! Returns Impix of current animation frame.
//! \sa impix()
00491 const Impix &Icon::frameImpix() const
{
      if ( d->anim )
            return d->anim->frameImpix();
      return d->impix;
}

//!
//! Returns QIconSet of first animation frame.
//! TODO: Add automatic greyscale icon generation.
00501 const QIconSet &Icon::iconSet() const
{
      if ( d->iconSet )
            return *d->iconSet;

      d->iconSet = new QIconSet( d->impix.pixmap() );
      return *d->iconSet;
}

//!
//! Sets the Icon impix to \a impix.
//! \sa impix()
00513 void Icon::setImpix(const Impix &impix, bool doDetach)
{
      if ( doDetach )
            detach();

      d->impix = impix;
      emit d->pixmapChanged( pixmap() );
      emit d->iconModified( d->impix.pixmap() );
}

//!
//! Returns pointer to Anim object, or \a 0 if Icon doesn't contain an animation.
00525 const Anim *Icon::anim() const
{
      return d->anim;
}

//!
//! Sets the animation for icon to \a anim. Also sets Impix to be the first frame of animation.
//! If animation have less than two frames, it is deleted.
//! \sa anim()
00534 void Icon::setAnim(const Anim &anim, bool doDetach)
{
      if ( doDetach )
            detach();

      d->unloadAnim();
      d->anim = new Anim(anim);

      if ( d->anim->numFrames() > 0 )
            setImpix( d->anim->frame(0) );

      if ( d->anim->numFrames() < 2 ) {
            delete d->anim;
            d->anim = 0;
      }

      if ( d->anim && d->activatedCount > 0 ) {
            d->activatedCount = 0;
            activated(false); // restart the animation, but don't play the sound
      }

      emit d->pixmapChanged( pixmap() );
      emit d->iconModified( d->impix.pixmap() );
}

//!
//! Removes animation from icon.
//! \sa setAnim()
00562 void Icon::removeAnim(bool doDetach)
{
      if ( doDetach )
            detach();

      if ( !d->anim )
            return;

      d->activatedCount = 0;
      stop();

      delete d->anim;
      d->anim = 0;

      emit d->pixmapChanged( pixmap() );
      //emit d->iconModified( d->impix.pixmap() );
}

//!
//! Returns the number of current animation frame.
//! \sa setAnim()
00583 int Icon::framenumber() const
{
      if ( d->anim )
            return d->anim->framenumber();

      return 0;
}

//!
//! Returns name of the Icon.
//! \sa setName()
00594 const QString &Icon::name() const
{
      return d->name;
}

//!
//! Sets the Icon name to \a name
//! \sa name()
00602 void Icon::setName(const QString &name)
{
      detach();

      d->name = name;
}

void Icon::setName(const char *s)
{
      QObject::setName(s);
}

//!
//! Returns Icon's QRegExp. It is used to store information for emoticons.
//! \sa setRegExp()
00617 const QRegExp &Icon::regExp() const
{
      return d->regExp;
}

//!
//! Sets the Icon QRegExp to \a regExp.
//! \sa regExp()
//! \sa text()
00626 void Icon::setRegExp(const QRegExp &regExp)
{
      detach();

      d->regExp = regExp;
}

//!
//! Returns Icon's text. It is used to store information for emoticons.
//! \sa setText()
//! \sa regExp()
00637 const QDict<QString> &Icon::text() const
{
      return d->text;
}

//!
//! Sets the Icon text to \a t.
//! \sa text()
00645 void Icon::setText(const QDict<QString> &t)
{
      detach();

      d->text = t;
}

//!
//! Returns file name of associated sound.
//! \sa setSound()
//! \sa activated()
00656 const QString &Icon::sound() const
{
      return d->sound;
}

//!
//! Sets the sound file name to be associated with this Icon.
//! \sa sound()
//! \sa activated()
00665 void Icon::setSound(const QString &sound)
{
      detach();

      d->sound = sound;
}

//!
//! Initializes Icon's Impix (or Anim, if \a isAnim equals \c true).
//! Iconset::load uses this function.
00675 bool Icon::loadFromData(const QByteArray &ba, bool isAnim)
{
      detach();

      bool ret = false;
      if ( isAnim ) {
            Anim *anim = new Anim(ba);
            setAnim(*anim);
            ret = anim->numFrames();
            delete anim; // shared data rules ;)
      }

      if ( !ret && d->impix.loadFromData(ba) )
            ret = true;

      if ( ret ) {
            emit d->pixmapChanged( pixmap() );
            emit d->iconModified( d->impix.pixmap() );
      }

      return ret;
}

//!
//! You need to call this function, when Icon is \e triggered, i.e. it is shown on screen
//! and it must start animation (if it has not animation, this function will do nothing).
//! When icon is no longer shown on screen you MUST call stop().
//! NOTE: For EACH activated() function call there must be associated stop() call, or the
//! animation will go crazy. You've been warned.
//! If \a playSound equals \c true, Icon will play associated sound file.
//! \sa stop()
00706 void Icon::activated(bool playSound)
{
      d->activatedCount++;
      //qWarning("%-25s Icon::activated(): count = %d", name().latin1(), d->activatedCount);

#ifdef ICONSET_SOUND
      if ( playSound && !d->sound.isNull() ) {
            if ( !iconSharedObject )
                  iconSharedObject = new IconSharedObject();

            emit iconSharedObject->playSound(d->sound);
      }
#endif

      if ( d->anim ) {
            d->anim->unpause();

            d->anim->disconnectUpdate (d, SLOT(animUpdate())); // ensure, that we're connected to signal exactly one time
            d->anim->connectUpdate (d, SLOT(animUpdate()));
      }
}

//!
//! You need to call this function when Icon is no more shown on screen. It would save
//! processor time, if Icon has animation.
//! NOTE: For EACH activated() function call there must be associated stop() call, or the
//! animation will go crazy. You've been warned.
//! \sa activated()
00734 void Icon::stop()
{
      d->activatedCount--;
      //qWarning("%-25s Icon::stop(): count = %d", name().latin1(), d->activatedCount);

      if ( d->activatedCount <= 0 ) {
            d->activatedCount = 0;
            if ( d->anim ) {
                  d->anim->pause();
                  d->anim->restart();
            }
      }
}

//!
//! As the name says, this function removes the first animation frame. This is used to
//! create system Psi iconsets.
00751 void Icon::stripFirstAnimFrame()
{
      if ( d && d->anim )
            d->anim->stripFirstFrame();
}

//----------------------------------------------------------------------------
// IconsetFactory
//----------------------------------------------------------------------------

/*!
      \class IconsetFactory
      \brief Class for easy application-wide Icon searching

      You can add several Iconsets to IconsetFactory to use multiple Icons
      application-wide with ease.

      You can reference Icons by their name from any place from your application.
*/

//! \if _hide_doc_
class IconsetFactoryPrivate
{
private:
      static QPtrList<Iconset> *iconsets;
      static QPixmap *emptyPixmap;

public:
      static void registerIconset(const Iconset *);
      static void unregisterIconset(const Iconset *);

public:
      static const Icon *icon(const QString &name);

      friend class IconsetFactory;
};
//! \endif

QPtrList<Iconset> *IconsetFactoryPrivate::iconsets = 0;
QPixmap *IconsetFactoryPrivate::emptyPixmap = 0;

void IconsetFactoryPrivate::registerIconset(const Iconset *i)
{
      if ( !iconsets )
            iconsets = new QPtrList<Iconset>;

      if ( iconsets->find(i) < 0 ) {
            //qWarning("IconsetFactoryPrivate::registerIconset(): %s", i->fileName().latin1());
            iconsets->append ( i );
      }
}

void IconsetFactoryPrivate::unregisterIconset(const Iconset *i)
{
      if ( iconsets && iconsets->find(i) >= 0 ) {
            //qWarning("IconsetFactoryPrivate::unregisterIconset(): %s", i->fileName().latin1());
            iconsets->remove ( i );
      }

      if ( !iconsets || !iconsets->count() ) {
            if ( iconsets ) {
                  delete iconsets;
                  iconsets = 0;
            }

            if ( emptyPixmap ) {
                  delete emptyPixmap;
                  emptyPixmap = 0;
            }
      }
}

const Icon *IconsetFactoryPrivate::icon(const QString &name)
{
      if ( !iconsets )
            return 0;

      const Icon *i = 0;
      QPtrListIterator<Iconset> it( *iconsets );
      for (; it.current(); ++it) {
            Iconset *iconset = *it;
            if ( iconset )
                  i = iconset->icon(name);

            if ( i )
                  break;
      }
      return i;
}

//!
//! Returns pointer to Icon with name \a name, or \a 0 if Icon with that name wasn't
//! found in IconsetFactory.
00844 const Icon *IconsetFactory::iconPtr(const QString &name)
{
      const Icon *i = IconsetFactoryPrivate::icon(name);
      if ( !i ) {
            qDebug("WARNING: IconsetFactory::icon(\"%s\"): icon not found", name.latin1());
      }
      return i;
}

//!
//! Returns Icon with name \a name, or empty Icon if Icon with that name wasn't
//! found in IconsetFactory.
00856 Icon IconsetFactory::icon(const QString &name)
{
      const Icon *i = iconPtr(name);
      if ( i )
            return *i;
      return Icon();
}

//!
//! Returns QPixmap of first animation frame of the specified Icon, or the empty
//! QPixmap, if that Icon wasn't found in IconsetFactory.
//! This function is faster than the call to IconsetFactory::icon() and cast to QPixmap,
//! because the intermediate Icon object is not created and destroyed.
00869 const QPixmap &IconsetFactory::iconPixmap(const QString &name)
{
      const Icon *i = iconPtr(name);
      if ( i )
            return i->impix().pixmap();

      if ( !IconsetFactoryPrivate::emptyPixmap )
            IconsetFactoryPrivate::emptyPixmap = new QPixmap();
      return *IconsetFactoryPrivate::emptyPixmap;
}

//!
//! Returns list of all Icon names that are in IconsetFactory.
00882 const QStringList IconsetFactory::icons()
{
      QStringList list;

      QPtrList<Iconset> *iconsets = IconsetFactoryPrivate::iconsets;
      uint count = 0;
      if ( iconsets )
            count = iconsets->count();
      for (uint i = 0; i < count; i++) {
            QPtrListIterator<Icon> it = iconsets->at(i)->iterator();
            for ( ; it.current(); ++it)
                  list << (*it)->name();
      }

      return list;

}

//----------------------------------------------------------------------------
// Iconset
//----------------------------------------------------------------------------

/*!
      \class Iconset
      \brief Class for easy Icon grouping

      This class supports loading Icons from .zip arhives. It also provides additional
      information: name(), authors(), version(), description() and creation() date.

      This class is very handy to manage emoticon sets as well as usual application icons.

      \sa IconsetFactory
*/

//! \if _hide_doc_
class Iconset::Private : public QShared
{
private:
      void init()
      {
            name = "Unnamed";
            //version = "1.0";
            //description = "No description";
            //authors << "I. M. Anonymous";
            //creation = "1900-01-01";
            homeUrl = QString::null;

            dict.setAutoDelete(true);
      }

public:
      QString name, version, description, creation, homeUrl, filename;
      QStringList authors;
      QDict<Icon> dict;
      QPtrList<Icon> list;
      QDict<QString> info;
      //Icon nullIcon;

public:
      Private()
      {
            init();
      }

      Private(const Private &from)
      : QShared()
      {
            init();

            setInformation(from);

            QDictIterator<Icon> it( from.dict );
            for ( ; it.current(); ++it)
                  dict.insert(it.currentKey(), new Icon(*it.current()));

            QPtrListIterator<Icon> it2( from.list );
            for ( ; it2.current(); ++it2)
                  list.append(new Icon(*it2.current()));
      }

      void append(QString n, Icon *icon)
      {
            // all Icon names in Iconset must be unique
            if ( !dict.find(n) ) {
                  dict.replace (n, icon);
                  list.append (icon);
            }
      }

      QByteArray loadData(const QString &fileName, const QString &dir)
      {
            QByteArray ba;

            QFileInfo fi(dir);
            if ( fi.isDir() ) {
                  QFile file ( dir + "/" + fileName );
                  file.open (IO_ReadOnly);

                  ba = file.readAll();
            }
            else if ( fi.extension(false) == "jisp" || fi.extension(false) == "zip" ) {
                  UnZip z(dir);
                  if ( !z.open() )
                        return ba;

                  QString n = fi.baseName(true) + "/" + fileName;
                  if ( !z.readFile(n, &ba) ) {
                        n = "/" + fileName;
                        z.readFile(n, &ba);
                  }
            }

            return ba;
      }

      void loadMeta(const QDomElement &i, const QString &dir)
      {
            Q_UNUSED(dir);

            for (QDomNode node = i.firstChild(); !node.isNull(); node = node.nextSibling()) {
                  QDomElement e = node.toElement();
                  if( e.isNull() )
                        continue;

                  QString tag = e.tagName();
                  if ( tag == "name" ) {
                        name = e.text();
                  }
                  else if ( tag == "version" ) {
                        version = e.text();
                  }
                  else if ( tag == "description" ) {
                        description = e.text();
                  }
                  else if ( tag == "author" ) {
                        QString n = e.text();
                        QString tmp = "<br>&nbsp;&nbsp;";
                        if ( !e.attribute("email").isEmpty() ) {
                              QString s = e.attribute("email");
                              n += tmp + QString("Email: <a href='mailto:%1'>%2</a>").arg( s ).arg( s );
                        }
                        if ( !e.attribute("jid").isEmpty() ) {
                              QString s = e.attribute("jid");
                              n += tmp + QString("JID: <a href='jabber:%1'>%2</a>").arg( s ).arg( s );
                        }
                        if ( !e.attribute("www").isEmpty() ) {
                              QString s = e.attribute("www");
                              n += tmp + QString("WWW: <a href='%1'>%2</a>").arg( s ).arg( s );
                        }
                        authors += n;
                  }
                  else if ( tag == "creation" ) {
                        creation = e.text();
                  }
                  else if ( tag == "home" ) {
                        homeUrl = e.text();
                  }
            }
      }

      static int icon_counter; // used to give unique names to icons

      // will return 'true' when icon is loaded ok
      bool loadIcon(const QDomElement &i, const QString &dir)
      {
            Icon icon;

            QDict<QString> text, graphic, sound, object;
            graphic.setAutoDelete(true);
            sound.setAutoDelete(true);
            object.setAutoDelete(true);

            QString name;
            name.sprintf("icon_%04d", icon_counter++);
            bool isAnimated = false;
            bool isImage = false;

            for(QDomNode n = i.firstChild(); !n.isNull(); n = n.nextSibling()) {
                  QDomElement e = n.toElement();
                  if ( e.isNull() )
                        continue;

                  QString tag = e.tagName();
                  if ( tag == "text" ) {
                        QString lang = e.attribute("xml:lang");
                        if ( lang.isEmpty() )
                              lang = ""; // otherwise there would be many warnings :-(
                        text.insert( lang, new QString(e.text()));
                  }
                  else if ( tag == "object" ) {
                        object.insert( e.attribute("mime"), new QString(e.text()));
                  }
                  else if ( tag == "x" ) {
                        QString attr = e.attribute("xmlns");
                        if ( attr == "name" ) {
                              name = e.text();
                        }
                        else if ( attr == "type" ) {
                              if ( e.text() == "animation" )
                                    isAnimated = true;
                              else if ( e.text() == "image" )
                                    isImage = true;
                        }
                  }
                  // leaved for compatibility with old JEP
                  else if ( tag == "graphic" ) {
                        graphic.insert( e.attribute("mime"), new QString(e.text()));
                  }
                  else if ( tag == "sound" ) {
                        sound.insert( e.attribute("mime"), new QString(e.text()));
                  }
            }

            icon.setText (text);
            icon.setName (name);

            QStringList graphicMime, soundMime, animationMime;
            graphicMime << "image/png"; // first item have higher priority than latter
            graphicMime << "video/x-mng";
            graphicMime << "image/gif";
            graphicMime << "image/x-xpm";
            graphicMime << "image/bmp";
            graphicMime << "image/jpeg";
            // TODO: untested
            graphicMime << "image/svg+xml";

            soundMime << "audio/x-wav";
            soundMime << "audio/x-ogg";
            soundMime << "audio/x-mp3";
            soundMime << "audio/x-midi";

            // MIME-types, that support animations
            animationMime << "image/gif";
            //animationMime << "image/png";
            animationMime << "image/x-mng";

            if ( !object.isEmpty() ) {
                  // fill the graphic & sound tables, if there are some
                  // 'object' entries. inspect the supported mimetypes
                  // and copy mime info and file path to 'graphic' and
                  // 'sound' dictonaries.

                  QStringList::Iterator it = graphicMime.begin();
                  for ( ; it != graphicMime.end(); ++it)
                        if ( object[*it] && !object[*it]->isNull() )
                              graphic.insert( *it, new QString(*object[*it]));

                  it = soundMime.begin();
                  for ( ; it != soundMime.end(); ++it)
                        if ( object[*it] && !object[*it]->isNull() )
                              sound.insert( *it, new QString(*object[*it]));
            }

            bool loadSuccess = false;

            {
                  QStringList::Iterator it = graphicMime.begin();
                  for ( ; it != graphicMime.end(); ++it) {
                        if ( graphic[*it] && !graphic[*it]->isNull() ) {
                              // if format supports animations, then load graphic as animation, and
                              // if there is only one frame, then later it would be converted to single Impix
                              if ( !isAnimated && !isImage ) {
                                    QStringList::Iterator it2 = animationMime.begin();
                                    for ( ; it2 != animationMime.end(); ++it2)
                                          if ( *it == *it2 ) {
                                                isAnimated = true;
                                                break;
                                          }
                              }

                              if ( icon.loadFromData( loadData(*graphic[*it], dir), isAnimated ) ) {
                                    loadSuccess = true;
                                    break;
                              }
                              else {
                                    qDebug("Iconset::load(): Couldn't load %s graphic for the %s icon for the %s iconset", (*it).latin1(), name.latin1(), this->name.latin1());
                                    loadSuccess = false;
                              }
                        }
                  }
            }

            {
                  QFileInfo fi(dir);
                  QStringList::Iterator it = soundMime.begin();
                  for ( ; it != soundMime.end(); ++it) {
                        if ( sound[*it] && !sound[*it]->isNull() ) {
                              if ( !fi.isDir() ) { // it is a .zip file then
#ifdef ICONSET_SOUND
                                    if ( !iconSharedObject )
                                          iconSharedObject = new IconSharedObject();

                                    QString path = iconSharedObject->unpackPath;
                                    if ( path.isEmpty() )
                                          break;

                                    QFileInfo ext(*sound[*it]);
                                    path += "/" + QCA::SHA1::hashToString(QString(fi.absFilePath() + "/" + *sound[*it]).utf8()) + "." + ext.extension();

                                    QFile file ( path );
                                    file.open ( IO_WriteOnly );
                                    QDataStream out ( &file );

                                    QByteArray data = loadData(*sound[*it], dir);
                                    out.writeRawBytes (data, data.size());

                                    icon.setSound ( path );
                                    break;
#endif
                              }
                              else {
                                    icon.setSound ( fi.absFilePath() + "/" + *sound[*it] );
                                    break;
                              }
                        }
                  }
            }

            if ( text.count() )
            {     // construct RegExp
                  QString regexp;
                  QDictIterator<QString> it( text );
                  for ( ; it.current(); ++it ) {
                        if ( !regexp.isEmpty() )
                              regexp += '|';

                        regexp += QRegExp::escape(**it);
                  }

                  // make sure there is some form of whitespace on at least one side of the text string
                  //regexp = QString("(\\b(%1))|((%2)\\b)").arg(regexp).arg(regexp);
                  icon.setRegExp ( regexp );
            }

            if ( loadSuccess )
                  append( name, new Icon(icon) );

            return loadSuccess;
      }

      // would return 'true' on success
      bool load(const QDomDocument &doc, const QString dir)
      {
            QDomElement base = doc.documentElement();
            if ( base.tagName() != "icondef" )
                  return false;

            bool success = true;

            for(QDomNode node = base.firstChild(); !node.isNull(); node = node.nextSibling()) {
                  QDomElement i = node.toElement();
                  if( i.isNull() )
                        continue;

                  QString tag = i.tagName();
                  if ( tag == "meta" ) {
                        loadMeta (i, dir);
                  }
                  else if ( tag == "icon" ) {
                        bool ret = loadIcon (i, dir);
                        if ( !ret )
                              success = false;
                  }
                  else if ( tag == "x" ) {
                        info.insert( i.attribute("xmlns"), new QString(i.text()) );
                  }
            }

            return success;
      }
      
      void setInformation(const Private &from) {
            name = from.name;
            version = from.version;
            description = from.description;
            creation = from.creation;
            homeUrl = from.homeUrl;
            filename = from.filename;
            authors = from.authors;
            info = from.info;
      }
};
//! \endif

int Iconset::Private::icon_counter = 0;

//static int iconset_counter = 0;

//!
//! Creates empty Iconset.
01272 Iconset::Iconset()
{
      //iconset_counter++;

      d = new Private;
}

//!
//! Creates shared copy of Iconset \a from.
01281 Iconset::Iconset(const Iconset &from)
{
      //iconset_counter++;

      d = from.d;
      d->ref();
}

//!
//! Destroys Iconset, and frees all allocated Icons.
01291 Iconset::~Iconset()
{
      //qWarning("iconset_counter = %d", --iconset_counter);

      if ( d->deref() )
            delete d;

      IconsetFactoryPrivate::unregisterIconset(this);
}

//!
//! Copies all Icons as well as additional information from Iconset \a from.
01303 Iconset &Iconset::operator=(const Iconset &from)
{
      if ( d->deref() )
            delete d;

      d = from.d;
      d->ref();

      return *this;
}

Iconset Iconset::copy() const
{
      Iconset is;
      delete is.d;
      is.d = new Private( *this->d );

      return is;
}

void Iconset::detach()
{
      if ( d->count > 1 )
            *this = copy();
}

//!
//! Appends icons from Iconset \a from to this Iconset.
01331 Iconset &Iconset::operator+=(const Iconset &i)
{
      detach();

      QDictIterator<Icon> it( i.d->dict );
      for ( ; it.current(); ++it)
            d->dict.insert(it.currentKey(), new Icon(*it.current()));

      QPtrListIterator<Icon> it2( i.d->list );
      for ( ; it2.current(); ++it2)
            d->list.append(new Icon(*it2.current()));

      return *this;
}

//!
//! Frees all allocated Icons.
01348 void Iconset::clear()
{
      detach();

      d->list.clear();
      d->dict.clear();
}

//!
//! Returns the number of Icons in Iconset.
01358 uint Iconset::count() const
{
      return d->list.count();
}

//!
//! Loads Icons and additional information from directory \a dir. Directory can usual directory,
//! or a .zip/.jisp archive. There must exist file named \c icondef.xml in that directory.
01366 bool Iconset::load(const QString &dir)
{
      detach();

      // make it run okay on windows 9.x (where the pixmap memory is limited)
      QPixmap::Optimization optimization = QPixmap::defaultOptimization();
      QPixmap::setDefaultOptimization( QPixmap::MemoryOptim );

      bool ret = false;

      QByteArray ba;
      ba = d->loadData ("icondef.xml", dir);
      if ( !ba.isEmpty() ) {
            QDomDocument doc;
            if ( doc.setContent(ba, false) ) {
                  if ( d->load(doc, dir) ) {
                        d->filename = dir;
                        ret = true;
                  }
            }
            else
                  qWarning("Iconset::load(): Failed to load iconset: icondef.xml is invalid XML");
      }

      QPixmap::setDefaultOptimization( optimization );

      return ret;
}

//!
//! Returns pointer to Icon, if Icon with name \a name was found in Iconset, or \a 0 otherwise.
//! \sa setIcon()
01398 const Icon *Iconset::icon(const QString &name) const
{
      if ( !d || d->dict.isEmpty() )
            return 0;

      return d->dict.find(name);
}

//!
//! Appends (or replaces, if Icon with that name already exists) Icon to Iconset.
01408 void Iconset::setIcon(const QString &name, const Icon &icon)
{
      detach();

      d->dict.replace (name, new Icon(icon));

      QPtrListIterator<Icon> it( d->list );
      for ( ; it.current(); ++it)
            if ( it.current()->name() == icon.name() ) {
                  Icon *icon = it.current();
                  d->list.remove(icon);
                  break;
            }

      d->list.append (new Icon(icon));
}

//!
//! Removes Icon with the name \a name from Iconset.
01427 void Iconset::removeIcon(const QString &name)
{
      detach();

      d->dict.remove (name);

      QPtrListIterator<Icon> it( d->list );
      for ( ; it.current(); ++it)
            if ( it.current()->name() == name ) {
                  Icon *icon = it.current();
                  d->list.remove(icon);
                  break;
            }
}

//!
//! Returns the Iconset name.
01444 const QString &Iconset::name() const
{
      return d->name;
}

//!
//! Returns the Iconset version.
01451 const QString &Iconset::version() const
{
      return d->version;
}

//!
//! Returns the Iconset description string.
01458 const QString &Iconset::description() const
{
      return d->description;
}

//!
//! Returns the Iconset authors list.
01465 const QStringList &Iconset::authors() const
{
      return d->authors;
}

//!
//! Returns the Iconset creation date.
01472 const QString &Iconset::creation() const
{
      return d->creation;
}

//!
//! Returns the Iconsets' home URL.
01479 const QString &Iconset::homeUrl() const
{
      return d->homeUrl;
}

QPtrListIterator<Icon> Iconset::iterator() const
{
      //detach(); // ???

      QPtrListIterator<Icon> it( d->list );
      return it;
}

//!
//! Returns directory (or .zip/.jisp archive) name from which Iconset was loaded.
01494 const QString &Iconset::fileName() const
{
      return d->filename;
}

//!
//! Sets the Iconset directory (.zip archive) name.
01501 void Iconset::setFileName(const QString &f)
{
      d->filename = f;
}

/**
 * Sets the information (meta-data) of this iconset to the information from the given iconset.
 */
01509 void Iconset::setInformation(const Iconset &from) {
      detach();
      d->setInformation( *(from.d) );
}

//!
//! Returns additional Iconset information.
//! \sa setInfo()
01517 const QDict<QString> Iconset::info() const
{
      return d->info;
}

//!
//! Sets additional Iconset information.
//! \sa info()
01525 void Iconset::setInfo(const QDict<QString> &i)
{
      d->info = i;
}

//!
//! Created QMimeSourceFactory with names of icons and their images.
01532 QMimeSourceFactory *Iconset::createMimeSourceFactory() const
{
      QMimeSourceFactory *m = new QMimeSourceFactory;

      QDictIterator<Icon> it( d->dict );
      for ( ; it.current(); ++it)
            m->setImage(it.currentKey(), it.current()->image());

      return m;
}

//!
//! Adds Iconset to IconsetFactory.
01545 void Iconset::addToFactory() const
{
      IconsetFactoryPrivate::registerIconset(this);
}

//!
//! Removes Iconset from IconsetFactory.
01552 void Iconset::removeFromFactory() const
{
      IconsetFactoryPrivate::unregisterIconset(this);
}

//!
//! Use this function before creation of Iconsets and it will enable the
//! sound playing ability when Icons are activated().
//!
//! \a unpackPath is the path, to where the sound files will be unpacked, if
//! required (i.e. when sound is stored inside packed iconset.zip file). Unpacked
//! file name will be unique, and you can empty the unpack directory at the application
//! exit. Calling application MUST ensure that the \a unpackPath is already created.
//! If specified \a unpackPath is empty ("" or QString::null) then sounds from archives
//! will not be unpacked, and only sounds from already unpacked iconsets (that are not
//! stored in archives) will be played.
//! \a receiver and \a slot are used to specify the object that will be connected to
//! the signal 'playSound(QString fileName)'. Slot should play the specified sound
//! file.
01571 void Iconset::setSoundPrefs(QString unpackPath, QObject *receiver, const char *slot)
{
#ifdef ICONSET_SOUND
      if ( !iconSharedObject )
            iconSharedObject = new IconSharedObject();

      iconSharedObject->unpackPath = unpackPath;
      QObject::connect(iconSharedObject, SIGNAL(playSound(QString)), receiver, slot);
#endif
}

#include "iconset.moc"

Generated by  Doxygen 1.6.0   Back to index