/*************************************************************************** icons.cpp - description ------------------- begin : Sun Mar 17 2002 copyright : (C) 2002 by Vladimir Shutoff email : vovan@shutoff.ru ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #ifdef HAVE_CONFIG_H # include "config.h" #endif #include #include #include #include #include #include #include #include #include #include #include #include #ifdef USE_KDE # include # include # include #endif #include "sax.h" #include "contacts.h" #include "icons.h" #include "log.h" #include "qzip/qzip.h" #include "unquot.h" const unsigned ICON_COLOR_MASK = 0x000000FF; namespace SIM { typedef QMap PIXMAP_MAP; unsigned Icons::nSmile = 0; struct smileDef { QString smile; QString name; }; /********************* * base IconSet class **********************/ class IconSet { public: IconSet() {}; virtual ~IconSet() {}; virtual PictDef *getPict(const QString &name) = 0; virtual void clear() = 0; void parseSmiles(const QString&, unsigned &start, unsigned &size, QString &name); QStringList getSmile(const QString &name); QString getSmileName(const QString &name); void getSmiles(QStringList &smiles, QStringList &used); protected: PIXMAP_MAP m_icons; QValueList m_smiles; }; class WrkIconSet : public IconSet { public: WrkIconSet() {}; PictDef *getPict(const QString &name); void clear(); protected: PictDef *add(const QString &name, const QImage &pict, unsigned flags); }; class FileIconSet : public IconSet, public SAXParser { public: FileIconSet(const QString &file); ~FileIconSet(); PictDef *getPict(const QString &name); void clear(); protected: virtual void element_start(const QString& el, const QXmlAttributes& attrs); virtual void element_end(const QString& el); virtual void char_data(const QString& str); QString m_name; QString m_file; QString m_smile; QString *m_data; #ifdef USE_KDE QString m_system; #endif unsigned m_flags; UnZip *m_zip; }; class MyMimeSourceFactory : public QMimeSourceFactory { public: MyMimeSourceFactory() {}; ~MyMimeSourceFactory() {}; virtual const QMimeSource* data(const QString &abs_name) const; }; class IconsPrivate { public: QValueList defSets; QValueList customSets; }; /************************************* * functions for easy external access *************************************/ static Icons *icons = NULL; Icons *getIcons() { if(!icons) icons = new Icons; return icons; } void deleteIcons() { delete icons; } PictDef *getPict(const QString &name) { return getIcons()->getPict(name); } static QPixmap getPixmap(PictDef *d, const char*) { QPixmap p; p.convertFromImage(d->image); return p; } QIconSet Icon(const QString &name) { PictDef *pict = getPict(name); if (pict == NULL) return QIconSet(); QIconSet res(getPixmap(pict, name)); QString bigName = "big." + name; pict = getPict(bigName); if (pict) res.setPixmap(getPixmap(pict, bigName), QIconSet::Large); return res; } QImage Image(const QString &name) { PictDef *p = getPict(name); if (p == NULL) return QImage(); return p->image; } QPixmap Pict(const QString &name, const QColor&) { PictDef *p = getPict(name); if (p == NULL) return QPixmap(); return getPixmap(p, name); } /********************* * IconSet **********************/ void IconSet::parseSmiles(const QString &text, unsigned &start, unsigned &size, QString &name) { QValueListIterator it; for (it = m_smiles.begin(); it != m_smiles.end(); ++it){ QString pat = (*it).smile; int n = text.find(pat); if (n < 0) continue; if (((unsigned)n < start) || (((unsigned)n == start) && (pat.length() > size))){ start = n; size = pat.length(); name = (*it).name; } } } void IconSet::getSmiles(QStringList &smiles, QStringList &used) { QString name; bool bOK = false; QValueListIterator it; for (it = m_smiles.begin(); it != m_smiles.end(); ++it){ if (name != (*it).name){ if (bOK && !name.isEmpty()) smiles.push_back(name); name = (*it).name; bOK = true; } const QStringList::const_iterator itu = used.find((*it).smile); if (itu == used.end()){ used.append((*it).smile); }else{ bOK = false; } } if (bOK && !name.isEmpty()) smiles.append(name); } QStringList IconSet::getSmile(const QString &name) { QStringList res; PIXMAP_MAP::iterator it = m_icons.find(name); if (it == m_icons.end()) return res; QValueListIterator its; for (its = m_smiles.begin(); its != m_smiles.end(); ++its){ if ((*its).name != name) continue; res.append((*its).smile); } return res; } QString IconSet::getSmileName(const QString &name) { PIXMAP_MAP::iterator it = m_icons.find(name); if (it == m_icons.end()) return QString::null; QString res = it.data().file; int n = res.find('.'); if (n > 0) res = res.left(n); return res; } /**************************************************** * helper for WrkIconSet - move to class WrkIconSet? ****************************************************/ static QImage makeOffline(unsigned flags, const QImage &p) { QImage image = p.copy(); unsigned swapColor = flags & ICON_COLOR_MASK; unsigned int *data = (image.depth() > 8) ? (unsigned int *)image.bits() : (unsigned int *)image.colorTable(); int pixels = (image.depth() > 8) ? image.width()*image.height() : image.numColors(); for (int i = 0; i < pixels; i++){ QColor c(qRed(data[i]), qGreen(data[i]), qBlue(data[i])); int a = qAlpha(data[i]); int h, s, v; c.hsv(&h, &s, &v); if (swapColor){ h = (swapColor * 2 - h) & 0xFF; c.setHsv(h, s, v); }else{ c.setHsv(h, 0, v * 3 / 4); } data[i] = qRgba(c.red(), c.green(), c.blue(), a); } return image; } static QImage makeInactive(const QImage &p) { QImage image = p.copy(); unsigned int *data = (image.depth() > 8) ? (unsigned int *)image.bits() : (unsigned int *)image.colorTable(); int pixels = (image.depth() > 8) ? image.width()*image.height() : image.numColors(); for (int i = 0; i < pixels; i++){ QColor c(qRed(data[i]), qGreen(data[i]), qBlue(data[i])); int a = qAlpha(data[i]); int h, s, v; c.hsv(&h, &s, &v); c.setHsv(h, s / 8, v); data[i] = qRgba(c.red(), c.green(), c.blue(), a); } return image; } static QImage makeInvisible(unsigned flags, const QImage &p) { QImage image = (p.depth() != 32) ? p.convertDepth(32) : p.copy(); unsigned swapColor = flags & ICON_COLOR_MASK; char shift = (flags >> 8) & 0xFF; unsigned int *data = (unsigned int*)image.bits(); for (int y = 0; y < image.width(); y++){ int x = image.width() / 2 - (y - image.height() / 2) * 2 / 3 + shift; if (x < 0) x = 0; if (x > image.width()) x = image.width(); unsigned int *line = data + y * (image.width()) + x; for (; x < image.width(); x++, line++){ QColor c(qRed(*line), qGreen(*line), qBlue(*line)); int a = qAlpha(*line); int h, s, v; c.hsv(&h, &s, &v); if (swapColor){ h = (swapColor * 2 - h) & 0xFF; c.setHsv(h, s / 2, v * 3 / 4); }else{ c.setHsv(h, s / 2, v * 3 / 4); } *line = qRgba(c.red(), c.green(), c.blue(), a); } } return image; } static QImage merge(const QImage &p1, const QImage &p2) { QImage img1 = (p1.depth() != 32) ? p1.convertDepth(32) : p1.copy(); QImage img2 = (p2.depth() != 32) ? p2.convertDepth(32) : p2.copy(); unsigned int *data1 = (unsigned int*)img1.bits(); unsigned int *data2 = (unsigned int*)img2.bits(); for (int y1 = 0; y1 < img1.height(); y1++){ int y2 = y1 - (img1.height() - img2.height()) / 2; if ((y2 < 0) || (y2 >= img2.height())) continue; unsigned int *line1 = data1 + y1 * img1.width(); unsigned int *line2 = data2 + y2 * img2.width(); int w = img1.width(); if (w < img2.width()){ line2 += (img2.width() - w) / 2; }else if (w > img2.width()){ w = img2.width(); line1 += (img1.width() - w) / 2; } for (int i = 0; i < w; i++, line1++, line2++){ int r1 = qRed(*line1); int g1 = qGreen(*line1); int b1 = qBlue(*line1); int a1 = qAlpha(*line1); int r2 = qRed(*line2); int g2 = qGreen(*line2); int b2 = qBlue(*line2); int a2 = qAlpha(*line2); r1 = (r2 * a2 + r1 * (255 - a2)) / 255; g1 = (g2 * a2 + g1 * (255 - a2)) / 255; b1 = (b2 * a2 + b1 * (255 - a2)) / 255; if (a2 > a1) a1 = a2; *line1 = qRgba(r1, g1, b1, a1); } } return img1; } /************** * WrkIconSet **************/ PictDef *WrkIconSet::getPict(const QString &name) { if (name.isEmpty()) return NULL; PIXMAP_MAP::iterator it = m_icons.find(name); if (it != m_icons.end()) return &it.data(); if (name == "online"){ PictDef *p = NULL; for (unsigned i = 0; i < getContacts()->nClients(); i++){ Client *client = getContacts()->getClient(i); p = SIM::getPict(client->protocol()->description()->icon); if (p) break; } if (p == NULL){ p = SIM::getPict("SIM"); } if (p == NULL) return NULL; return add(name, p->image, p->flags); } if (name == "offline"){ PictDef *p = SIM::getPict("online"); if (p == NULL) return NULL; return add(name, makeOffline(p->flags, p->image), p->flags); } if (name == "inactive"){ PictDef *p = SIM::getPict("online"); if (p == NULL) return NULL; return add(name, makeInactive(p->image), p->flags); } if (name == "invisible"){ PictDef *p = SIM::getPict("online"); if (p == NULL) return NULL; return add(name, makeInvisible(p->flags, p->image), p->flags); } int pos = name.find('_'); if (pos > 0){ PictDef *p = SIM::getPict(name.left(pos)); QImage res; if (p){ if(p->image.isNull()) { log(L_ERROR, "Null Image!"); return false; } QString s = name.mid(pos + 1); if (s == "online"){ res = p->image; }else if (s == "offline"){ res = makeOffline(p->flags, p->image); }else if (s == "invisible"){ res = makeInvisible(p->flags, p->image); }else if (s == "inactive"){ res = makeInactive(p->image); }else{ PictDef *pp = SIM::getPict(s); if (pp) res = merge(p->image, pp->image); } return add(name, res, p->flags); } } if (!name.startsWith("big.")) log(L_DEBUG, "Icon %s not found", name.latin1()); return NULL; } void WrkIconSet::clear() { m_icons.clear(); } PictDef *WrkIconSet::add(const QString &name, const QImage &pict, unsigned flags) { PictDef p; p.image = pict; p.flags = flags; m_icons.insert(PIXMAP_MAP::value_type(name, p)); return &m_icons.find(name).data(); } /************** * FileIconSet **************/ FileIconSet::FileIconSet(const QString &file) { m_zip = new UnZip(app_file(file)); QByteArray arr; m_data = NULL; if (m_zip->open() && (m_zip->readFile("icondef.xml", &arr) || m_zip->readFile(QFileInfo(m_zip->name()).baseName(true) + QDir::separator() + "icondef.xml", &arr))) parse(arr, false); } FileIconSet::~FileIconSet() { delete m_zip; } PictDef *FileIconSet::getPict(const QString &name) { if (name.isEmpty()) return NULL; PIXMAP_MAP::iterator it = m_icons.find(name); if (it == m_icons.end()) return NULL; if (it.data().image.isNull()){ #ifdef USE_KDE if (!it.data().system.isEmpty()){ QPixmap pict; if (!name.startsWith("big.")){ pict = SmallIconSet(it.data().system).pixmap(QIconSet::Small, QIconSet::Normal); }else{ pict = DesktopIconSet(it.data().system).pixmap(QIconSet::Large, QIconSet::Normal); } if (!pict.isNull()){ it.data().image = pict.convertToImage(); return &(it.data()); } } #endif if (it.data().file.isEmpty()) return NULL; QByteArray arr; if (!m_zip->readFile((*it).file, &arr) && !m_zip->readFile(QFileInfo(m_zip->name()).baseName(true) + QDir::separator() + (*it).file, &arr)) return NULL; it.data().image = QImage(arr).convertDepth(32); } return &(it.data()); } void FileIconSet::clear() { for (PIXMAP_MAP::iterator it = m_icons.begin(); it != m_icons.end(); ++it){ it.data().image = QImage(); } } void FileIconSet::element_start(const QString& el, const QXmlAttributes& attrs) { if (el == "icon"){ m_name = QString::null; m_smile = QString::null; m_flags = 0; m_file = QString::null; #ifdef USE_KDE m_system = QString::null; #endif m_name = attrs.value("name"); m_flags = attrs.value("flags").toUInt(); #ifdef USE_KDE m_system = attrs.value("kicon"); #endif if (m_name.isEmpty()){ m_name = "s_"; m_name += QString::number(++Icons::nSmile); } return; } if (el == "object" && m_file.isEmpty()){ QString mime = attrs.value("mime"); if (mime.isEmpty()) return; int n = mime.find('/'); if (n < 0) return; if (!mime.startsWith("image")) return; mime = mime.mid(n + 1); QStringList l = QImage::inputFormatList(); for (unsigned i = 0; i < l.count(); i++){ if (l[i].lower() != mime.lower()) continue; m_data = &m_file; return; } return; } if (el == "text"){ m_smile = QString::null; m_data = &m_smile; } } void FileIconSet::element_end(const QString& el) { if (el == "icon"){ PictDef p; p.file = m_file; p.flags = m_flags; #ifdef USE_KDE p.system = m_system; #endif PIXMAP_MAP::iterator it = m_icons.find(m_name); if (it == m_icons.end()) m_icons.insert(PIXMAP_MAP::value_type(m_name, p)); #ifdef USE_KDE if (!m_name.startsWith("big.")){ QString big_name = "big." + m_name; p.file = QString::null; p.flags = m_flags; p.system = m_system; it = m_icons.find(big_name); if (it == m_icons.end()) m_icons.insert(PIXMAP_MAP::value_type(big_name, p)); } #endif } if (el == "text"){ if (!m_smile.isEmpty() && !m_name.isEmpty()){ smileDef s; s.name = m_name; s.smile = m_smile; m_smiles.push_back(s); } m_smile = QString::null; } m_data = NULL; } void FileIconSet::char_data(const QString& str) { if (m_data) *m_data += str; } /************************************* * MyMimeSourceFactory for QTextEdits *************************************/ const QMimeSource *MyMimeSourceFactory::data(const QString &abs_name) const { QString name = abs_name; if (name.left(5) == "icon:"){ name = name.mid(5); PictDef *p = getPict(name); if (p) ((QMimeSourceFactory*)this)->setImage(abs_name, p->image); } return QMimeSourceFactory::data(abs_name); } /***************** * Icons *****************/ Icons::Icons() { d = new IconsPrivate(); /* This idea came from kapplication.cpp I had a similar idea with setting the old defaultFactory in the destructor but this won't work :( Christian */ QMimeSourceFactory* oldDefaultFactory = QMimeSourceFactory::takeDefaultFactory(); QMimeSourceFactory::setDefaultFactory(new MyMimeSourceFactory()); if (oldDefaultFactory) QMimeSourceFactory::addFactory( oldDefaultFactory ); addIconSet("icons/sim.jisp", true); d->defSets.append(new WrkIconSet); addIconSet("icons/smiles.jisp", false); addIconSet("icons/icqlite.jisp", false); addIconSet("icons/additional.jisp", false); } Icons::~Icons() { QValueListIterator it; for (it = d->customSets.begin(); it != d->customSets.end(); ++it) delete *it; for (it = d->defSets.begin(); it != d->defSets.end(); ++it) delete *it; delete d; } bool Icons::processEvent(Event *e) { if (e->type() == eEventIconChanged){ QValueListIterator it; for (it = d->customSets.begin(); it != d->customSets.end(); ++it) (*it)->clear(); for (it = d->defSets.begin(); it != d->defSets.end(); ++it) (*it)->clear(); } return false; } void Icons::iconChanged(int) { EventIconChanged().process(); } PictDef *Icons::getPict(const QString &name) { QValueListIterator it; for (it = d->customSets.begin(); it != d->customSets.end(); ++it){ PictDef *res = (*it)->getPict(name); if (res) return res; } for (it = d->defSets.begin(); it != d->defSets.end(); ++it){ PictDef *res = (*it)->getPict(name); if (res) return res; } return NULL; } QStringList Icons::getSmile(const QString &name) { QValueListIterator it; for (it = d->customSets.begin(); it != d->customSets.end(); ++it){ QStringList res = (*it)->getSmile(name); if (!res.empty()) return res; } return QStringList(); } QString Icons::getSmileName(const QString &name) { QValueListIterator it; for (it = d->customSets.begin(); it != d->customSets.end(); ++it){ QString res = (*it)->getSmileName(name); if (!res.isEmpty()) return res; } return QString::null; } void Icons::getSmiles(QStringList &smiles) { QStringList used; QValueListIterator it; for (it = d->customSets.begin(); it != d->customSets.end(); ++it) (*it)->getSmiles(smiles, used); } QString Icons::parseSmiles(const QString &str) { QString s = str; QString res; while (!s.isEmpty()){ unsigned start = (unsigned)(-1); unsigned size = 0; QString smile; QValueListIterator it; for (it = d->customSets.begin(); it != d->customSets.end(); ++it){ unsigned pos = ~0U; unsigned length = 0; QString n_smile; (*it)->parseSmiles(s, pos, length, n_smile); if (length == 0) continue; if (pos < start){ start = pos; size = length; smile = n_smile; } } if (size == 0){ res += quoteString(s); break; } res += s.left(start); res += "\"";"; s = s.mid(start + size); } return res; } IconSet *Icons::addIconSet(const QString &name, bool bDefault) { FileIconSet *is = new FileIconSet(name); if (bDefault){ d->defSets.prepend(is); }else{ d->customSets.append(is); } return is; } void Icons::removeIconSet(IconSet *is) { if(!is) { for (QValueListIterator it = d->customSets.begin(); it != d->customSets.end(); ){ IconSet *set = *it; it++; delete set; } d->customSets.clear(); return; } QValueListIterator it; it = d->customSets.find( is ); if(it != d->customSets.end()){ delete is; d->customSets.remove(it); return; } it = d->defSets.find( is ); if(it != d->defSets.end()){ delete is; d->defSets.remove(it); return; } } }; #ifndef NO_MOC_INCLUDES #include "icons.moc" #endif