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>
74 #undef KDE_QT_SVG_RENDERER_FIXED
79 static bool pathIsRelative(
const QString &path)
82 return (!path.isEmpty() && path[0] != QChar(
'/'));
84 return QDir::isRelativePath(path);
109 void printTree(
QString& dbgString)
const;
114 KIconThemeNode::KIconThemeNode(
KIconTheme *_theme)
119 KIconThemeNode::~KIconThemeNode()
124 void KIconThemeNode::printTree(
QString& dbgString)
const
129 dbgString += theme->name();
133 void KIconThemeNode::queryIcons(
QStringList *result,
137 *result += theme->queryIcons(size, context);
140 void KIconThemeNode::queryIconsByContext(
QStringList *result,
144 *result += theme->queryIconsByContext(size, context);
147 K3Icon KIconThemeNode::findIcon(
const QString&
name,
int size,
150 return theme->iconPath(name, size, match);
164 class KIconLoaderPrivate
174 ~KIconLoaderPrivate()
191 bool initIconThemes();
198 K3Icon findMatchingIcon(
const QString& name,
int size)
const;
206 K3Icon findMatchingIconWithGenericFallbacks(
const QString& name,
int size)
const;
212 void addAppThemes(
const QString& appname);
219 void addBaseThemes(KIconThemeNode *node,
const QString &appname);
226 void addInheritedThemes(KIconThemeNode *node,
const QString &appname);
234 void addThemeByName(
const QString &themename,
const QString &appname);
240 QString unknownIconPath(
int size )
const;
262 int size,
int state)
const;
270 QImage createIconImage(
const QString &path,
int size = 0);
276 void insertCachedPixmapWithPath(
const QString &key,
const QPixmap &data,
const QString &path);
283 bool findCachedPixmapWithPath(
const QString &key, QPixmap &data,
QString &path);
288 KIconGroup *mpGroups;
289 KIconThemeNode *mpThemeRoot;
298 QCache<QString, PixmapWithPath> mPixmapCache;
300 bool extraDesktopIconsLoaded :1;
303 bool mIconThemeInited :1;
309 class KIconLoaderGlobalData
312 KIconLoaderGlobalData() {
315 Q_FOREACH(
const QString& file, genericIconsFiles) {
316 parseGenericIconsFiles(file);
321 return m_genericIcons.value(icon);
325 void parseGenericIconsFiles(
const QString& fileName);
329 void KIconLoaderGlobalData::parseGenericIconsFiles(
const QString& fileName)
331 QFile file(fileName);
332 if (file.open(QIODevice::ReadOnly)) {
333 QTextStream stream(&file);
334 stream.setCodec(
"ISO 8859-1");
335 while (!stream.atEnd()) {
336 const QString line = stream.readLine();
337 if (line.isEmpty() || line[0] ==
'#')
339 const int pos = line.indexOf(
':');
342 QString mimeIcon = line.left(pos);
343 const int slashindex = mimeIcon.indexOf(QLatin1Char(
'/'));
344 if (slashindex != -1) {
345 mimeIcon[slashindex] = QLatin1Char(
'-');
348 const QString genericIcon = line.mid(pos+1);
349 m_genericIcons.insert(mimeIcon, genericIcon);
359 if (overlays.isEmpty()) {
363 const int width = pix.size().width();
364 const int height = pix.size().height();
365 const int iconSize = qMin(width, height);
370 }
else if (iconSize <= 48) {
372 }
else if (iconSize <= 96) {
374 }
else if (iconSize < 256) {
380 QPainter painter(&pix);
383 foreach (
const QString& overlay, overlays) {
387 if (overlay.isEmpty()) {
395 const QPixmap pixmap = iconLoader->loadIcon(overlay, group, overlaySize, state,
QStringList(), 0,
true);
397 if (pixmap.isNull()) {
405 startPoint =
QPoint(2, height - overlaySize - 2);
409 startPoint =
QPoint(width - overlaySize - 2,
410 height - overlaySize - 2);
414 startPoint =
QPoint(width - overlaySize - 2, 2);
418 startPoint =
QPoint(2, 2);
422 painter.drawPixmap(startPoint, pixmap);
434 setObjectName(_appname);
435 d =
new KIconLoaderPrivate(
this);
439 d->init( _appname, _dirs );
446 d =
new KIconLoaderPrivate(
this);
455 d->mIconCache->clear();
457 d =
new KIconLoaderPrivate(
this);
458 d->init( _appname, _dirs );
463 extraDesktopIconsLoaded=
false;
464 mIconThemeInited =
false;
473 if (appname.isEmpty())
480 mPixmapCache.setMaxCost(10 * 1024 * 1024);
483 static const char *
const groups[] = {
"Desktop",
"Toolbar",
"MainToolbar",
"Small",
"Panel",
"Dialog", 0L };
488 KIconTheme *defaultSizesTheme = links.empty() ? 0 : links.first()->theme;
491 if (groups[i] == 0L) {
495 KConfigGroup cg(config, QLatin1String(groups[i]) +
"Icons");
496 mpGroups[i].size = cg.readEntry(
"Size", 0);
497 if (QPixmap::defaultDepth() > 8) {
498 mpGroups[i].alphaBlending = cg.readEntry(
"AlphaBlending",
true);
500 mpGroups[i].alphaBlending =
false;
503 if (!mpGroups[i].size && defaultSizesTheme) {
504 mpGroups[i].size = defaultSizesTheme->
defaultSize(i);
509 bool KIconLoaderPrivate::initIconThemes()
511 if (mIconThemeInited) {
513 return (mpThemeRoot != 0);
516 mIconThemeInited =
true;
524 kDebug(264) <<
"Couldn't find current icon theme, falling back to default.";
533 mpThemeRoot =
new KIconThemeNode(def);
535 links.append(mpThemeRoot);
536 addBaseThemes(mpThemeRoot, appname);
539 mpDirs->addResourceType(
"appicon",
"data", appname +
"/pics/");
541 mpDirs->addResourceType(
"appicon",
"data", appname +
"/toolbar/");
545 dirs += mpDirs->resourceDirs(
"icon");
546 dirs += mpDirs->resourceDirs(
"pixmap");
547 dirs += mpDirs->resourceDirs(
"xdgdata-icon");
548 dirs +=
"/usr/share/pixmaps";
550 dirs += mpDirs->resourceDirs(
"xdgdata-pixmap");
551 for (QStringList::ConstIterator it = dirs.constBegin(); it != dirs.constEnd(); ++it)
552 mpDirs->addResourceDir(
"appicon", *it);
555 QString dbgString =
"Theme tree: ";
556 mpThemeRoot->printTree(dbgString);
572 d->mpDirs->addResourceType(
"appicon",
"data", appname +
"/pics/");
574 d->mpDirs->addResourceType(
"appicon",
"data", appname +
"/toolbar/");
575 d->addAppThemes(appname);
578 void KIconLoaderPrivate::addAppThemes(
const QString& appname)
587 KIconThemeNode* node =
new KIconThemeNode(def);
588 bool addedToLinks =
false;
590 if (!mThemesInTree.contains(node->theme->internalName())) {
591 mThemesInTree.append(node->theme->internalName());
595 addBaseThemes(node, appname);
603 void KIconLoaderPrivate::addBaseThemes(KIconThemeNode *node,
const QString &appname)
615 addInheritedThemes(node, appname);
617 addThemeByName(
"hicolor", appname);
620 void KIconLoaderPrivate::addInheritedThemes(KIconThemeNode *node,
const QString &appname)
624 for (QStringList::ConstIterator it = lst.begin(); it != lst.end(); ++it) {
625 if ((*it) ==
"hicolor") {
631 addThemeByName(*it, appname);
635 void KIconLoaderPrivate::addThemeByName(
const QString &themename,
const QString &appname)
637 if (mThemesInTree.contains(themename + appname)) {
645 KIconThemeNode *n =
new KIconThemeNode(theme);
646 mThemesInTree.append(themename + appname);
648 addInheritedThemes(n, appname);
653 if ( d->extraDesktopIconsLoaded )
return;
659 QStringList::ConstIterator it;
662 for (it=icnlibs.begin(); it!=icnlibs.end(); ++it)
668 QStringList::ConstIterator it2;
669 for (it2=lst.begin(); it2!=lst.end(); ++it2)
674 r=readlink( QFile::encodeName(*it + *it2) , buf,
sizeof(buf)-1);
678 const QDir dir2( buf );
679 QString themeName=dir2.dirName();
681 if (!list.contains(themeName))
682 list.append(themeName);
687 for (it = list.constBegin(); it != list.constEnd(); ++it)
690 if (*it == QLatin1String(
"default.kde")
691 || *it == QLatin1String(
"default.kde4")) {
694 d->addThemeByName(*it,
"");
697 d->extraDesktopIconsLoaded=
true;
703 return d->extraDesktopIconsLoaded;
708 d->drawOverlays(
this, group, state, pixmap, overlays);
711 QString KIconLoaderPrivate::removeIconExtension(
const QString &name)
const
713 if (name.endsWith(QLatin1String(
".png"))
714 || name.endsWith(QLatin1String(
".xpm"))
715 || name.endsWith(QLatin1String(
".svg"))) {
716 return name.left(name.length() - 4);
717 }
else if (name.endsWith(QLatin1String(
".svgz"))) {
718 return name.left(name.length() - 5);
724 void KIconLoaderPrivate::normalizeIconMetadata(
KIconLoader::Group &group,
int &size,
int &state)
const
728 kWarning(264) <<
"Illegal icon state: " << state;
742 if ((group < -1) || (group >= KIconLoader::LastGroup))
744 kWarning(264) <<
"Illegal icon group: " << group;
753 kWarning(264) <<
"Neither size nor group specified!";
756 size = mpGroups[group].size;
761 const QStringList &overlays,
int size,
int state)
const
767 ? QLatin1Literal(
"$kicou_")
768 : QLatin1Literal(
"$kico_"))
771 % QString::number(size)
774 % ( group >= 0 ? mpEffect.fingerprint(group, state)
775 : *NULL_EFFECT_FINGERPRINT);
778 QImage KIconLoaderPrivate::createIconImage(
const QString &path,
int size)
782 QString ext = path.right(3).toUpper();
785 if (ext !=
"SVG" && ext !=
"VGZ")
788 img = QImage(path, ext.toLatin1());
790 if (size != 0 && !img.isNull()) {
791 img = img.scaled(size, size, Qt::IgnoreAspectRatio, Qt::SmoothTransformation);
799 if (renderer.isValid()) {
800 img = QImage(size, size, QImage::Format_ARGB32_Premultiplied);
811 void KIconLoaderPrivate::insertCachedPixmapWithPath(
821 output.open(QIODevice::WriteOnly);
823 QDataStream outputStream(&output);
824 outputStream.setVersion(QDataStream::Qt_4_6);
826 outputStream << path;
829 outputStream << data;
834 mIconCache->insert(key, output.buffer());
838 PixmapWithPath *pixmapPath =
new PixmapWithPath;
839 pixmapPath->pixmap = data;
840 pixmapPath->path = path;
842 mPixmapCache.insert(key, pixmapPath, data.width() * data.height() + 1);
845 bool KIconLoaderPrivate::findCachedPixmapWithPath(
const QString &key, QPixmap &data,
QString &path)
849 const PixmapWithPath *pixmapPath = mPixmapCache.object(key);
851 path = pixmapPath->path;
852 data = pixmapPath->pixmap;
861 if (!mIconCache->find(key, &result) || result.isEmpty()) {
866 buffer.setBuffer(&result);
867 buffer.open(QIODevice::ReadOnly);
869 QDataStream inputStream(&buffer);
870 inputStream.setVersion(QDataStream::Qt_4_6);
873 inputStream >> tempPath;
877 inputStream >> tempPixmap;
884 PixmapWithPath *newPixmapWithPath =
new PixmapWithPath;
885 newPixmapWithPath->pixmap = data;
886 newPixmapWithPath->path = path;
888 mPixmapCache.insert(key, newPixmapWithPath, data.width() * data.height() + 1);
897 K3Icon KIconLoaderPrivate::findMatchingIconWithGenericFallbacks(
const QString& name,
int size)
const
899 K3Icon icon = findMatchingIcon(name, size);
903 const QString genericIcon = s_globalData->genericIconFor(name);
904 if (!genericIcon.isEmpty()) {
905 icon = findMatchingIcon(genericIcon, size);
910 K3Icon KIconLoaderPrivate::findMatchingIcon(
const QString& name,
int size)
const
912 const_cast<KIconLoaderPrivate*
>(
this)->initIconThemes();
919 #ifdef KDE_QT_SVG_RENDERER_FIXED
920 const char * ext1[4] = {
".png",
".svgz",
".svg",
".xpm" };
921 const char * ext2[4] = {
".svgz",
".svg",
".png",
".xpm" };
994 const char *
const ext[4] = {
".png",
".svgz",
".svg",
".xpm" };
997 bool genericFallback = name.endsWith(QLatin1String(
"-x-generic"));
999 foreach(KIconThemeNode *themeNode, links)
1003 while (!currentName.isEmpty())
1011 #ifdef KDE_QT_SVG_RENDERER_FIXED
1012 for (
int i = 0 ; i < 4 ; i++)
1019 for (
int i = 0 ; i < 4 ; i++)
1026 for (
int i = 0 ; i < 4 ; i++)
1037 if (genericFallback)
1041 int rindex = currentName.lastIndexOf(
'-');
1043 currentName.truncate(rindex);
1045 if (currentName.endsWith(QLatin1String(
"-x")))
1046 currentName.chop(2);
1050 <<
"text" <<
"application" <<
"image" <<
"audio"
1051 <<
"inode" <<
"video" <<
"message" <<
"model" <<
"multipart"
1052 <<
"x-content" <<
"x-epoc";
1057 if (mediaTypes.contains(currentName)) {
1058 currentName += QLatin1String(
"-x-generic");
1059 genericFallback =
true;
1069 inline QString KIconLoaderPrivate::unknownIconPath(
int size )
const
1073 K3Icon icon = findMatchingIcon(str_unknown, size);
1074 if (!icon.isValid())
1076 kDebug(264) <<
"Warning: could not find \"Unknown\" icon for size = "
1086 bool canReturnNull)
const
1088 if (!d->initIconThemes()) {
1092 if (_name.isEmpty() || !pathIsRelative(_name))
1098 QString name = d->removeIconExtension( _name );
1105 path = d->mpDirs->findResource(
"appicon", name + png_ext);
1110 path = d->mpDirs->findResource(
"appicon", name + svgz_ext);
1112 path = d->mpDirs->findResource(
"appicon", name + svg_ext);
1114 path = d->mpDirs->findResource(
"appicon", name + xpm_ext);
1118 if (group_or_size >= KIconLoader::LastGroup)
1120 kDebug(264) <<
"Illegal icon group: " << group_or_size;
1125 if (group_or_size >= 0)
1126 size = d->mpGroups[group_or_size].size;
1128 size = -group_or_size;
1130 if (_name.isEmpty()) {
1134 return d->unknownIconPath(size);
1137 K3Icon icon = d->findMatchingIconWithGenericFallbacks(name, size);
1139 if (!icon.isValid())
1143 if (!path.isEmpty() || canReturnNull)
1146 return d->unknownIconPath(size);
1155 const int slashindex = iconName.indexOf(QLatin1Char(
'/'));
1156 if (slashindex != -1) {
1157 iconName[slashindex] = QLatin1Char(
'-');
1160 if ( !d->extraDesktopIconsLoaded )
1162 const QPixmap pixmap =
loadIcon( iconName, group, size, state, overlays, path_store,
true );
1163 if (!pixmap.isNull() ) {
1168 const QPixmap pixmap =
loadIcon(iconName, group, size, state, overlays, path_store,
true);
1169 if (pixmap.isNull()) {
1171 return loadIcon(
"application-octet-stream", group, size, state, overlays, path_store,
false);
1178 QString *path_store,
bool canReturnNull)
const
1181 bool favIconOverlay =
false;
1183 if (size < 0 || _name.isEmpty())
1197 if (name.startsWith(QLatin1String(
"favicons/")))
1199 favIconOverlay =
true;
1203 bool absolutePath = !pathIsRelative(name);
1204 if (!absolutePath) {
1205 name = d->removeIconExtension(name);
1209 if (name.isEmpty()) {
1215 d->normalizeIconMetadata(group, size, state);
1218 QString key = d->makeCacheKey(name, group, overlays, size, state);
1220 bool iconWasUnknown =
false;
1225 if (d->findCachedPixmapWithPath(key, pix, icon.path) && !icon.path.isEmpty()) {
1227 *path_store = icon.path;
1234 if (!d->initIconThemes()) {
1238 favIconOverlay = favIconOverlay && size > 22;
1245 if (absolutePath && !favIconOverlay)
1253 icon = d->findMatchingIconWithGenericFallbacks(favIconOverlay ?
QString(
"text-html") : name, size);
1257 if (icon.path.isEmpty()) {
1259 icon.path = (absolutePath) ? name :
1265 if (icon.path.isEmpty() && !canReturnNull) {
1266 icon.path = d->unknownIconPath(size);
1267 iconWasUnknown =
true;
1270 QImage img = d->createIconImage(icon.path, size);
1274 img = d->mpEffect.apply(img, group, state);
1279 QImage favIcon(name,
"PNG");
1280 if (!favIcon.isNull())
1285 QRect r(favIcon.rect());
1286 r.moveBottomRight(img.rect().bottomRight());
1287 r.adjust(-1, -1, -1, -1);
1290 p.drawImage(r, favIcon);
1294 pix = QPixmap::fromImage(img);
1299 d->drawOverlays(
this, group, state, pix, overlays);
1303 if (iconWasUnknown) {
1307 d->insertCachedPixmapWithPath(key, pix, icon.path);
1310 *path_store = icon.path;
1321 int dirLen = file.lastIndexOf(
'/');
1323 if (!icon.isEmpty() && file.left(dirLen) != icon.left(dirLen))
1325 QMovie *movie =
new QMovie(file, QByteArray(), parent);
1326 if (!movie->isValid())
1336 if (!d->mpGroups)
return QString();
1338 d->initIconThemes();
1340 if ( (group < -1 || group >= KIconLoader::LastGroup) && group !=
KIconLoader::User )
1342 kDebug(264) <<
"Illegal icon group: " << group;
1345 if (size == 0 && group < 0)
1347 kDebug(264) <<
"Neither size nor group specified!";
1354 file = d->mpDirs->findResource(
"appicon", file);
1359 size = d->mpGroups[group].size;
1363 foreach(KIconThemeNode *themeNode, d->links)
1370 if ( !icon.isValid() )
1372 foreach(KIconThemeNode *themeNode, d->links)
1380 file = icon.isValid() ? icon.path :
QString();
1390 if (!d->mpGroups)
return lst;
1392 d->initIconThemes();
1394 if ((group < -1) || (group >= KIconLoader::LastGroup))
1396 kDebug(264) <<
"Illegal icon group: " << group;
1399 if ((size == 0) && (group < 0))
1401 kDebug(264) <<
"Neither size nor group specified!";
1405 QString file = name +
"/0001";
1408 file = d->mpDirs->findResource(
"appicon", file +
".png");
1412 size = d->mpGroups[group].size;
1413 K3Icon icon = d->findMatchingIcon(file, size);
1414 file = icon.isValid() ? icon.path :
QString();
1420 QString path = file.left(file.length()-8);
1421 DIR* dp = opendir( QFile::encodeName(path) );
1425 KDE_struct_dirent* ep;
1426 while( ( ep = KDE_readdir( dp ) ) != 0L )
1428 QString fn(QFile::decodeName(ep->d_name));
1429 if(!(fn.left(4)).toUInt())
1441 d->initIconThemes();
1442 if (d->mpThemeRoot)
return d->mpThemeRoot->theme;
1448 if (!d->mpGroups)
return -1;
1450 if (group < 0 || group >= KIconLoader::LastGroup)
1452 kDebug(264) <<
"Illegal icon group: " << group;
1455 return d->mpGroups[group].size;
1460 const QDir dir(iconsDir);
1462 const QStringList lst = dir.entryList(formats, QDir::Files);
1464 QStringList::ConstIterator it;
1465 for (it=lst.begin(); it!=lst.end(); ++it)
1466 result += iconsDir +
'/' + *it;
1473 d->initIconThemes();
1476 if (group_or_size >= KIconLoader::LastGroup)
1478 kDebug(264) <<
"Illegal icon group: " << group_or_size;
1482 if (group_or_size >= 0)
1483 size = d->mpGroups[group_or_size].size;
1485 size = -group_or_size;
1487 foreach(KIconThemeNode *themeNode, d->links)
1488 themeNode->queryIconsByContext(&result, size, context);
1493 QStringList::ConstIterator it;
1494 for (it=result.constBegin(); it!=result.constEnd(); ++it)
1496 int n = (*it).lastIndexOf(
'/');
1500 name = (*it).mid(n+1);
1501 name = d->removeIconExtension(name);
1502 if (!entries.contains(name))
1514 d->initIconThemes();
1517 if (group_or_size >= KIconLoader::LastGroup)
1519 kDebug(264) <<
"Illegal icon group: " << group_or_size;
1523 if (group_or_size >= 0)
1524 size = d->mpGroups[group_or_size].size;
1526 size = -group_or_size;
1528 foreach(KIconThemeNode *themeNode, d->links)
1529 themeNode->queryIcons(&result, size, context);
1534 QStringList::ConstIterator it;
1535 for (it=result.constBegin(); it!=result.constEnd(); ++it)
1537 int n = (*it).lastIndexOf(
'/');
1541 name = (*it).mid(n+1);
1542 name = d->removeIconExtension(name);
1543 if (!entries.contains(name))
1555 d->initIconThemes();
1557 foreach(KIconThemeNode *themeNode, d->links)
1558 if( themeNode->theme->hasContext( context ))
1565 return &d->mpEffect;
1570 if (!d->mpGroups)
return false;
1572 if (group < 0 || group >= KIconLoader::LastGroup)
1574 kDebug(264) <<
"Illegal icon group: " << group;
1577 return d->mpGroups[group].alphaBlending;
1581 #ifndef KDE_NO_DEPRECATED
1583 bool canReturnNull )
1587 iconset.addPixmap( tmp, QIcon::Active, QIcon::On );
1590 iconset.addPixmap( tmp, QIcon::Disabled, QIcon::On );
1592 iconset.addPixmap( tmp, QIcon::Normal, QIcon::On );
1606 #ifndef KDE_NO_DEPRECATED
1621 #ifndef KDE_NO_DEPRECATED
1636 #ifndef KDE_NO_DEPRECATED
1651 #ifndef KDE_NO_DEPRECATED
1666 #ifndef KDE_NO_DEPRECATED
1689 kDebug(264) <<
"Warning: Cannot find \"unknown\" icon.";
1690 pix = QPixmap(32,32);
1705 return globalIconLoader;
1718 #include "kiconloader.moc"