27 #include <sys/types.h>
33 #include <QtCore/QCache>
34 #include <QtCore/QFileInfo>
35 #include <QtCore/QDir>
36 #include <QtCore/QBuffer>
37 #include <QtCore/QDataStream>
38 #include <QtCore/QByteArray>
39 #include <QtCore/QStringBuilder>
40 #include <QtGui/QIcon>
41 #include <QtGui/QImage>
42 #include <QtGui/QMovie>
43 #include <QtGui/QPainter>
44 #include <QtGui/QPixmap>
45 #include <QtGui/QPixmapCache>
47 #include <QtSvg/QSvgRenderer>
72 static
bool pathIsRelative(const
QString &path)
75 return (!path.isEmpty() && path[0] != QChar(
'/'));
77 return QDir::isRelativePath(path);
102 void printTree(
QString& dbgString)
const;
107 KIconThemeNode::KIconThemeNode(
KIconTheme *_theme)
112 KIconThemeNode::~KIconThemeNode()
117 void KIconThemeNode::printTree(
QString& dbgString)
const
122 dbgString += theme->name();
126 void KIconThemeNode::queryIcons(
QStringList *result,
130 *result += theme->queryIcons(size, context);
133 void KIconThemeNode::queryIconsByContext(
QStringList *result,
137 *result += theme->queryIconsByContext(size, context);
140 K3Icon KIconThemeNode::findIcon(
const QString&
name,
int size,
143 return theme->iconPath(name, size, match);
157 class KIconLoaderPrivate
167 ~KIconLoaderPrivate()
184 bool initIconThemes();
191 K3Icon findMatchingIcon(
const QString& name,
int size)
const;
199 K3Icon findMatchingIconWithGenericFallbacks(
const QString& name,
int size)
const;
205 void addAppThemes(
const QString& appname);
212 void addBaseThemes(KIconThemeNode *node,
const QString &appname);
219 void addInheritedThemes(KIconThemeNode *node,
const QString &appname);
227 void addThemeByName(
const QString &themename,
const QString &appname);
233 QString unknownIconPath(
int size )
const;
255 int size,
int state)
const;
263 QImage createIconImage(
const QString &path,
int size = 0);
269 void insertCachedPixmapWithPath(
const QString &key,
const QPixmap &data,
const QString &path);
276 bool findCachedPixmapWithPath(
const QString &key, QPixmap &data,
QString &path);
281 KIconGroup *mpGroups;
282 KIconThemeNode *mpThemeRoot;
291 QCache<QString, PixmapWithPath> mPixmapCache;
293 bool extraDesktopIconsLoaded :1;
296 bool mIconThemeInited :1;
302 class KIconLoaderGlobalData
305 KIconLoaderGlobalData() {
306 const QStringList genericIconsFiles = KGlobal::dirs()->findAllResources(
"xdgdata-mime",
"generic-icons");
308 Q_FOREACH(
const QString& file, genericIconsFiles) {
309 parseGenericIconsFiles(file);
314 return m_genericIcons.value(icon);
318 void parseGenericIconsFiles(
const QString& fileName);
322 void KIconLoaderGlobalData::parseGenericIconsFiles(
const QString& fileName)
324 QFile file(fileName);
325 if (file.open(QIODevice::ReadOnly)) {
326 QTextStream stream(&file);
327 stream.setCodec(
"ISO 8859-1");
328 while (!stream.atEnd()) {
329 const QString line = stream.readLine();
330 if (line.isEmpty() || line[0] ==
'#')
332 const int pos = line.indexOf(
':');
335 QString mimeIcon = line.left(pos);
336 const int slashindex = mimeIcon.indexOf(QLatin1Char(
'/'));
337 if (slashindex != -1) {
338 mimeIcon[slashindex] = QLatin1Char(
'-');
341 const QString genericIcon = line.mid(pos+1);
342 m_genericIcons.insert(mimeIcon, genericIcon);
352 if (overlays.isEmpty()) {
356 const int width = pix.size().width();
357 const int height = pix.size().height();
358 const int iconSize = qMin(width, height);
363 }
else if (iconSize <= 48) {
365 }
else if (iconSize <= 96) {
367 }
else if (iconSize < 256) {
373 QPainter painter(&pix);
376 foreach (
const QString& overlay, overlays) {
380 if (overlay.isEmpty()) {
388 const QPixmap pixmap = iconLoader->loadIcon(overlay, group, overlaySize, state,
QStringList(), 0,
true);
390 if (pixmap.isNull()) {
398 startPoint =
QPoint(2, height - overlaySize - 2);
402 startPoint =
QPoint(width - overlaySize - 2,
403 height - overlaySize - 2);
407 startPoint =
QPoint(width - overlaySize - 2, 2);
411 startPoint =
QPoint(2, 2);
415 painter.drawPixmap(startPoint, pixmap);
427 setObjectName(_appname);
428 d =
new KIconLoaderPrivate(
this);
432 d->init( _appname, _dirs );
439 d =
new KIconLoaderPrivate(
this);
448 d->mIconCache->clear();
450 d =
new KIconLoaderPrivate(
this);
451 d->init( _appname, _dirs );
456 extraDesktopIconsLoaded=
false;
457 mIconThemeInited =
false;
463 mpDirs = KGlobal::dirs();
466 if (appname.isEmpty())
467 appname = KGlobal::mainComponent().componentName();
473 mPixmapCache.setMaxCost(10 * 1024 * 1024);
476 static const char *
const groups[] = {
"Desktop",
"Toolbar",
"MainToolbar",
"Small",
"Panel",
"Dialog", 0L };
481 KIconTheme *defaultSizesTheme = links.empty() ? 0 : links.first()->theme;
484 if (groups[i] == 0L) {
488 KConfigGroup cg(config, QLatin1String(groups[i]) +
"Icons");
489 mpGroups[i].size = cg.readEntry(
"Size", 0);
490 if (QPixmap::defaultDepth() > 8) {
491 mpGroups[i].alphaBlending = cg.readEntry(
"AlphaBlending",
true);
493 mpGroups[i].alphaBlending =
false;
496 if (!mpGroups[i].size && defaultSizesTheme) {
497 mpGroups[i].size = defaultSizesTheme->
defaultSize(i);
502 bool KIconLoaderPrivate::initIconThemes()
504 if (mIconThemeInited) {
506 return (mpThemeRoot != 0);
509 mIconThemeInited =
true;
517 kDebug(264) <<
"Couldn't find current icon theme, falling back to default.";
526 mpThemeRoot =
new KIconThemeNode(def);
528 links.append(mpThemeRoot);
529 addBaseThemes(mpThemeRoot, appname);
532 mpDirs->addResourceType(
"appicon",
"data", appname +
"/pics/");
534 mpDirs->addResourceType(
"appicon",
"data", appname +
"/toolbar/");
538 dirs += mpDirs->resourceDirs(
"icon");
539 dirs += mpDirs->resourceDirs(
"pixmap");
540 dirs += mpDirs->resourceDirs(
"xdgdata-icon");
541 dirs +=
"/usr/share/pixmaps";
543 dirs += mpDirs->resourceDirs(
"xdgdata-pixmap");
544 for (QStringList::ConstIterator it = dirs.constBegin(); it != dirs.constEnd(); ++it)
545 mpDirs->addResourceDir(
"appicon", *it);
548 QString dbgString =
"Theme tree: ";
549 mpThemeRoot->printTree(dbgString);
565 d->mpDirs->addResourceType(
"appicon",
"data", appname +
"/pics/");
567 d->mpDirs->addResourceType(
"appicon",
"data", appname +
"/toolbar/");
568 d->addAppThemes(appname);
571 void KIconLoaderPrivate::addAppThemes(
const QString& appname)
580 KIconThemeNode* node =
new KIconThemeNode(def);
581 bool addedToLinks =
false;
583 if (!mThemesInTree.contains(node->theme->internalName())) {
584 mThemesInTree.append(node->theme->internalName());
588 addBaseThemes(node, appname);
596 void KIconLoaderPrivate::addBaseThemes(KIconThemeNode *node,
const QString &appname)
608 addInheritedThemes(node, appname);
610 addThemeByName(
"hicolor", appname);
613 void KIconLoaderPrivate::addInheritedThemes(KIconThemeNode *node,
const QString &appname)
617 for (QStringList::ConstIterator it = lst.begin(); it != lst.end(); ++it) {
618 if ((*it) ==
"hicolor") {
624 addThemeByName(*it, appname);
628 void KIconLoaderPrivate::addThemeByName(
const QString &themename,
const QString &appname)
630 if (mThemesInTree.contains(themename + appname)) {
638 KIconThemeNode *n =
new KIconThemeNode(theme);
639 mThemesInTree.append(themename + appname);
641 addInheritedThemes(n, appname);
646 if ( d->extraDesktopIconsLoaded )
return;
651 const QStringList icnlibs = KGlobal::dirs()->resourceDirs(
"icon");
652 QStringList::ConstIterator it;
655 for (it=icnlibs.begin(); it!=icnlibs.end(); ++it)
661 QStringList::ConstIterator it2;
662 for (it2=lst.begin(); it2!=lst.end(); ++it2)
667 r=readlink( QFile::encodeName(*it + *it2) , buf,
sizeof(buf)-1);
671 const QDir dir2( buf );
672 QString themeName=dir2.dirName();
674 if (!list.contains(themeName))
675 list.append(themeName);
680 for (it = list.constBegin(); it != list.constEnd(); ++it)
683 if (*it == QLatin1String(
"default.kde")
684 || *it == QLatin1String(
"default.kde4")) {
687 d->addThemeByName(*it,
"");
690 d->extraDesktopIconsLoaded=
true;
696 return d->extraDesktopIconsLoaded;
701 d->drawOverlays(
this, group, state, pixmap, overlays);
704 QString KIconLoaderPrivate::removeIconExtension(
const QString &name)
const
706 if (name.endsWith(QLatin1String(
".png"))
707 || name.endsWith(QLatin1String(
".xpm"))
708 || name.endsWith(QLatin1String(
".svg"))) {
709 return name.left(name.length() - 4);
710 }
else if (name.endsWith(QLatin1String(
".svgz"))) {
711 return name.left(name.length() - 5);
717 void KIconLoaderPrivate::normalizeIconMetadata(
KIconLoader::Group &group,
int &size,
int &state)
const
721 kWarning(264) <<
"Illegal icon state: " << state;
735 if ((group < -1) || (group >= KIconLoader::LastGroup))
737 kWarning(264) <<
"Illegal icon group: " << group;
746 kWarning(264) <<
"Neither size nor group specified!";
749 size = mpGroups[group].size;
754 const QStringList &overlays,
int size,
int state)
const
760 ? QLatin1Literal(
"$kicou_")
761 : QLatin1Literal(
"$kico_"))
764 % QString::number(size)
767 % ( group >= 0 ? mpEffect.fingerprint(group, state)
768 : *NULL_EFFECT_FINGERPRINT);
771 QImage KIconLoaderPrivate::createIconImage(
const QString &path,
int size)
775 QString ext = path.right(3).toUpper();
778 if (ext !=
"SVG" && ext !=
"VGZ")
781 img = QImage(path, ext.toLatin1());
783 if (size != 0 && !img.isNull()) {
784 img = img.scaled(size, size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
792 if (renderer.isValid()) {
793 img = QImage(size, size, QImage::Format_ARGB32_Premultiplied);
804 void KIconLoaderPrivate::insertCachedPixmapWithPath(
814 output.open(QIODevice::WriteOnly);
816 QDataStream outputStream(&output);
817 outputStream.setVersion(QDataStream::Qt_4_6);
819 outputStream << path;
822 outputStream << data;
827 mIconCache->insert(key, output.buffer());
831 PixmapWithPath *pixmapPath =
new PixmapWithPath;
832 pixmapPath->pixmap = data;
833 pixmapPath->path = path;
835 mPixmapCache.insert(key, pixmapPath, data.width() * data.height() + 1);
838 bool KIconLoaderPrivate::findCachedPixmapWithPath(
const QString &key, QPixmap &data,
QString &path)
842 const PixmapWithPath *pixmapPath = mPixmapCache.object(key);
844 path = pixmapPath->path;
845 data = pixmapPath->pixmap;
854 if (!mIconCache->find(key, &result) || result.isEmpty()) {
859 buffer.setBuffer(&result);
860 buffer.open(QIODevice::ReadOnly);
862 QDataStream inputStream(&buffer);
863 inputStream.setVersion(QDataStream::Qt_4_6);
866 inputStream >> tempPath;
870 inputStream >> tempPixmap;
877 PixmapWithPath *newPixmapWithPath =
new PixmapWithPath;
878 newPixmapWithPath->pixmap = data;
879 newPixmapWithPath->path = path;
881 mPixmapCache.insert(key, newPixmapWithPath, data.width() * data.height() + 1);
890 K3Icon KIconLoaderPrivate::findMatchingIconWithGenericFallbacks(
const QString& name,
int size)
const
892 K3Icon icon = findMatchingIcon(name, size);
896 const QString genericIcon = s_globalData->genericIconFor(name);
897 if (!genericIcon.isEmpty()) {
898 icon = findMatchingIcon(genericIcon, size);
903 K3Icon KIconLoaderPrivate::findMatchingIcon(
const QString& name,
int size)
const
905 const_cast<KIconLoaderPrivate*
>(
this)->initIconThemes();
909 const char *
const ext[4] = {
".png",
".svgz",
".svg",
".xpm" };
910 bool genericFallback = name.endsWith(QLatin1String(
"-x-generic"));
923 foreach (KIconThemeNode *themeNode, links) {
924 for (
int i = 0 ; i < 4 ; i++) {
926 if (icon.isValid()) {
931 if (icon.isValid()) {
936 if (icon.isValid() && icon.path.contains(
"/apps/")) {
941 foreach (KIconThemeNode *themeNode, links) {
944 while (!currentName.isEmpty()) {
947 for (
int i = 0 ; i < 4 ; i++) {
949 if (icon.isValid()) {
954 if (icon.isValid()) {
960 if (genericFallback) {
965 int rindex = currentName.lastIndexOf(
'-');
967 currentName.truncate(rindex);
969 if (currentName.endsWith(QLatin1String(
"-x")))
974 <<
"text" <<
"application" <<
"image" <<
"audio"
975 <<
"inode" <<
"video" <<
"message" <<
"model" <<
"multipart"
976 <<
"x-content" <<
"x-epoc";
981 if (mediaTypes.contains(currentName)) {
982 currentName += QLatin1String(
"-x-generic");
983 genericFallback =
true;
993 inline QString KIconLoaderPrivate::unknownIconPath(
int size )
const
995 static const QString &str_unknown = KGlobal::staticQString(
"unknown");
997 K3Icon icon = findMatchingIcon(str_unknown, size);
1000 kDebug(264) <<
"Warning: could not find \"Unknown\" icon for size = "
1010 bool canReturnNull)
const
1012 if (!d->initIconThemes()) {
1016 if (_name.isEmpty() || !pathIsRelative(_name))
1022 QString name = d->removeIconExtension( _name );
1027 static const QString &png_ext = KGlobal::staticQString(
".png");
1028 static const QString &xpm_ext = KGlobal::staticQString(
".xpm");
1029 path = d->mpDirs->findResource(
"appicon", name + png_ext);
1031 static const QString &svgz_ext = KGlobal::staticQString(
".svgz");
1032 static const QString &svg_ext = KGlobal::staticQString(
".svg");
1034 path = d->mpDirs->findResource(
"appicon", name + svgz_ext);
1036 path = d->mpDirs->findResource(
"appicon", name + svg_ext);
1038 path = d->mpDirs->findResource(
"appicon", name + xpm_ext);
1042 if (group_or_size >= KIconLoader::LastGroup)
1044 kDebug(264) <<
"Illegal icon group: " << group_or_size;
1049 if (group_or_size >= 0)
1050 size = d->mpGroups[group_or_size].size;
1052 size = -group_or_size;
1054 if (_name.isEmpty()) {
1058 return d->unknownIconPath(size);
1061 K3Icon icon = d->findMatchingIconWithGenericFallbacks(name, size);
1063 if (!icon.isValid())
1067 if (!path.isEmpty() || canReturnNull)
1070 return d->unknownIconPath(size);
1079 const int slashindex = iconName.indexOf(QLatin1Char(
'/'));
1080 if (slashindex != -1) {
1081 iconName[slashindex] = QLatin1Char(
'-');
1084 if ( !d->extraDesktopIconsLoaded )
1086 const QPixmap pixmap =
loadIcon( iconName, group, size, state, overlays, path_store,
true );
1087 if (!pixmap.isNull() ) {
1092 const QPixmap pixmap =
loadIcon(iconName, group, size, state, overlays, path_store,
true);
1093 if (pixmap.isNull()) {
1095 return loadIcon(
"application-octet-stream", group, size, state, overlays, path_store,
false);
1102 QString *path_store,
bool canReturnNull)
const
1105 bool favIconOverlay =
false;
1107 if (size < 0 || _name.isEmpty())
1121 if (name.startsWith(QLatin1String(
"favicons/")))
1123 favIconOverlay =
true;
1127 bool absolutePath = !pathIsRelative(name);
1128 if (!absolutePath) {
1129 name = d->removeIconExtension(name);
1133 if (name.isEmpty()) {
1139 d->normalizeIconMetadata(group, size, state);
1142 QString key = d->makeCacheKey(name, group, overlays, size, state);
1144 bool iconWasUnknown =
false;
1149 if (d->findCachedPixmapWithPath(key, pix, icon.path) && !icon.path.isEmpty()) {
1151 *path_store = icon.path;
1158 if (!d->initIconThemes()) {
1162 favIconOverlay = favIconOverlay && size > 22;
1169 if (absolutePath && !favIconOverlay)
1177 icon = d->findMatchingIconWithGenericFallbacks(favIconOverlay ?
QString(
"text-html") : name, size);
1181 if (icon.path.isEmpty()) {
1183 icon.path = (absolutePath) ? name :
1189 if (icon.path.isEmpty() && !canReturnNull) {
1190 icon.path = d->unknownIconPath(size);
1191 iconWasUnknown =
true;
1194 QImage img = d->createIconImage(icon.path, size);
1198 img = d->mpEffect.apply(img, group, state);
1203 QImage favIcon(name,
"PNG");
1204 if (!favIcon.isNull())
1209 QRect r(favIcon.rect());
1210 r.moveBottomRight(img.rect().bottomRight());
1211 r.adjust(-1, -1, -1, -1);
1214 p.drawImage(r, favIcon);
1218 pix = QPixmap::fromImage(img);
1223 d->drawOverlays(
this, group, state, pix, overlays);
1227 if (iconWasUnknown) {
1231 d->insertCachedPixmapWithPath(key, pix, icon.path);
1234 *path_store = icon.path;
1245 int dirLen = file.lastIndexOf(
'/');
1247 if (!icon.isEmpty() && file.left(dirLen) != icon.left(dirLen))
1249 QMovie *movie =
new QMovie(file, QByteArray(), parent);
1250 if (!movie->isValid())
1260 if (!d->mpGroups)
return QString();
1262 d->initIconThemes();
1264 if ( (group < -1 || group >= KIconLoader::LastGroup) && group !=
KIconLoader::User )
1266 kDebug(264) <<
"Illegal icon group: " << group;
1269 if (size == 0 && group < 0)
1271 kDebug(264) <<
"Neither size nor group specified!";
1278 file = d->mpDirs->findResource(
"appicon", file);
1283 size = d->mpGroups[group].size;
1287 foreach(KIconThemeNode *themeNode, d->links)
1294 if ( !icon.isValid() )
1296 foreach(KIconThemeNode *themeNode, d->links)
1304 file = icon.isValid() ? icon.path :
QString();
1314 if (!d->mpGroups)
return lst;
1316 d->initIconThemes();
1318 if ((group < -1) || (group >= KIconLoader::LastGroup))
1320 kDebug(264) <<
"Illegal icon group: " << group;
1323 if ((size == 0) && (group < 0))
1325 kDebug(264) <<
"Neither size nor group specified!";
1329 QString file = name +
"/0001";
1332 file = d->mpDirs->findResource(
"appicon", file +
".png");
1336 size = d->mpGroups[group].size;
1337 K3Icon icon = d->findMatchingIcon(file, size);
1338 file = icon.isValid() ? icon.path :
QString();
1344 QString path = file.left(file.length()-8);
1345 DIR* dp = opendir( QFile::encodeName(path) );
1349 KDE_struct_dirent* ep;
1350 while( ( ep = KDE_readdir( dp ) ) != 0L )
1352 QString fn(QFile::decodeName(ep->d_name));
1353 if(!(fn.left(4)).toUInt())
1365 d->initIconThemes();
1366 if (d->mpThemeRoot)
return d->mpThemeRoot->theme;
1372 if (!d->mpGroups)
return -1;
1374 if (group < 0 || group >= KIconLoader::LastGroup)
1376 kDebug(264) <<
"Illegal icon group: " << group;
1379 return d->mpGroups[group].size;
1384 const QDir dir(iconsDir);
1386 const QStringList lst = dir.entryList(formats, QDir::Files);
1388 QStringList::ConstIterator it;
1389 for (it=lst.begin(); it!=lst.end(); ++it)
1390 result += iconsDir +
'/' + *it;
1397 d->initIconThemes();
1400 if (group_or_size >= KIconLoader::LastGroup)
1402 kDebug(264) <<
"Illegal icon group: " << group_or_size;
1406 if (group_or_size >= 0)
1407 size = d->mpGroups[group_or_size].size;
1409 size = -group_or_size;
1411 foreach(KIconThemeNode *themeNode, d->links)
1412 themeNode->queryIconsByContext(&result, size, context);
1417 QStringList::ConstIterator it;
1418 for (it=result.constBegin(); it!=result.constEnd(); ++it)
1420 int n = (*it).lastIndexOf(
'/');
1424 name = (*it).mid(n+1);
1425 name = d->removeIconExtension(name);
1426 if (!entries.contains(name))
1438 d->initIconThemes();
1441 if (group_or_size >= KIconLoader::LastGroup)
1443 kDebug(264) <<
"Illegal icon group: " << group_or_size;
1447 if (group_or_size >= 0)
1448 size = d->mpGroups[group_or_size].size;
1450 size = -group_or_size;
1452 foreach(KIconThemeNode *themeNode, d->links)
1453 themeNode->queryIcons(&result, size, context);
1458 QStringList::ConstIterator it;
1459 for (it=result.constBegin(); it!=result.constEnd(); ++it)
1461 int n = (*it).lastIndexOf(
'/');
1465 name = (*it).mid(n+1);
1466 name = d->removeIconExtension(name);
1467 if (!entries.contains(name))
1479 d->initIconThemes();
1481 foreach(KIconThemeNode *themeNode, d->links)
1482 if( themeNode->theme->hasContext( context ))
1489 return &d->mpEffect;
1494 if (!d->mpGroups)
return false;
1496 if (group < 0 || group >= KIconLoader::LastGroup)
1498 kDebug(264) <<
"Illegal icon group: " << group;
1501 return d->mpGroups[group].alphaBlending;
1505 #ifndef KDE_NO_DEPRECATED
1507 bool canReturnNull )
1511 iconset.addPixmap( tmp, QIcon::Active, QIcon::On );
1514 iconset.addPixmap( tmp, QIcon::Disabled, QIcon::On );
1516 iconset.addPixmap( tmp, QIcon::Normal, QIcon::On );
1530 #ifndef KDE_NO_DEPRECATED
1545 #ifndef KDE_NO_DEPRECATED
1560 #ifndef KDE_NO_DEPRECATED
1575 #ifndef KDE_NO_DEPRECATED
1590 #ifndef KDE_NO_DEPRECATED
1613 kDebug(264) <<
"Warning: Cannot find \"unknown\" icon.";
1614 pix = QPixmap(32,32);
1629 return globalIconLoader;
1642 #include "kiconloader.moc"