kicontheme.cpp
00001 /* vi: ts=8 sts=4 sw=4 00002 * 00003 * $Id$ 00004 * 00005 * This file is part of the KDE project, module tdecore. 00006 * Copyright (C) 2000 Geert Jansen <jansen@kde.org> 00007 * Antonio Larrosa <larrosa@kde.org> 00008 * 00009 * This is free software; it comes under the GNU Library General 00010 * Public License, version 2. See the file "COPYING.LIB" for the 00011 * exact licensing terms. 00012 * 00013 * kicontheme.cpp: Lowlevel icon theme handling. 00014 */ 00015 00016 #include <sys/stat.h> 00017 #include <unistd.h> 00018 #include <stdlib.h> 00019 #include <config.h> 00020 00021 #include <tqstring.h> 00022 #include <tqstringlist.h> 00023 #include <tqvaluelist.h> 00024 #include <tqmap.h> 00025 #include <tqpixmap.h> 00026 #include <tqpixmapcache.h> 00027 #include <tqimage.h> 00028 #include <tqfileinfo.h> 00029 #include <tqdir.h> 00030 00031 #include <kdebug.h> 00032 #include <kstandarddirs.h> 00033 #include <tdeglobal.h> 00034 #include <tdeconfig.h> 00035 #include <ksimpleconfig.h> 00036 #include <kinstance.h> 00037 00038 #include "kicontheme.h" 00039 00040 class TDEIconThemePrivate 00041 { 00042 public: 00043 TQString example, screenshot; 00044 TQString linkOverlay, lockOverlay, zipOverlay, shareOverlay; 00045 bool hidden; 00046 TDESharedConfig::Ptr sharedConfig; 00047 }; 00048 00052 class TDEIconThemeDir 00053 { 00054 public: 00055 TDEIconThemeDir(const TQString& dir, const TDEConfigBase *config); 00056 00057 bool isValid() const { return mbValid; } 00058 TQString iconPath(const TQString& name) const; 00059 TQStringList iconList() const; 00060 TQString dir() const { return mDir; } 00061 00062 TDEIcon::Context context() const { return mContext; } 00063 TDEIcon::Type type() const { return mType; } 00064 int size() const { return mSize; } 00065 int minSize() const { return mMinSize; } 00066 int maxSize() const { return mMaxSize; } 00067 int threshold() const { return mThreshold; } 00068 00069 private: 00070 bool mbValid; 00071 TDEIcon::Type mType; 00072 TDEIcon::Context mContext; 00073 int mSize, mMinSize, mMaxSize; 00074 int mThreshold; 00075 00076 TQString mDir; 00077 }; 00078 00079 00080 /*** TDEIconTheme ***/ 00081 00082 TDEIconTheme::TDEIconTheme(const TQString& name, const TQString& appName) 00083 { 00084 d = new TDEIconThemePrivate; 00085 00086 TQStringList icnlibs; 00087 TQStringList::ConstIterator it, itDir; 00088 TQStringList themeDirs; 00089 TQString cDir; 00090 00091 // Applications can have local additions to the global "locolor" and 00092 // "hicolor" icon themes. For these, the _global_ theme description 00093 // files are used.. 00094 00095 if (!appName.isEmpty() && 00096 ( name == "crystalsvg" || name== "hicolor" || name == "locolor" ) ) 00097 { 00098 icnlibs = TDEGlobal::dirs()->resourceDirs("data"); 00099 for (it=icnlibs.begin(); it!=icnlibs.end(); ++it) 00100 { 00101 cDir = *it + appName + "/icons/" + name; 00102 if (TQFile::exists( cDir )) 00103 themeDirs += cDir + "/"; 00104 } 00105 } 00106 // Find the theme description file. These are always global. 00107 00108 icnlibs = TDEGlobal::dirs()->resourceDirs("icon"); 00109 icnlibs += TDEGlobal::dirs()->resourceDirs("xdgdata-icon"); 00110 icnlibs += "/usr/share/pixmaps"; 00111 // These are not in the icon spec, but e.g. GNOME puts some icons there anyway. 00112 icnlibs += TDEGlobal::dirs()->resourceDirs("xdgdata-pixmap"); 00113 for (it=icnlibs.begin(); it!=icnlibs.end(); ++it) 00114 { 00115 cDir = *it + name + "/"; 00116 if (TDEStandardDirs::exists(cDir)) 00117 { 00118 themeDirs += cDir; 00119 if (mDir.isEmpty() 00120 && (TDEStandardDirs::exists( cDir + "index.desktop") || TDEStandardDirs::exists( cDir + "index.theme"))) 00121 mDir = cDir; 00122 } 00123 } 00124 00125 if (mDir.isEmpty()) 00126 { 00127 kdDebug(264) << "Icon theme " << name << " not found.\n"; 00128 return; 00129 } 00130 00131 TQString fileName, mainSection; 00132 if(TQFile::exists(mDir + "index.desktop")) { 00133 fileName = mDir + "index.desktop"; 00134 mainSection="KDE Icon Theme"; 00135 } else { 00136 fileName = mDir + "index.theme"; 00137 mainSection="Icon Theme"; 00138 } 00139 // Use TDESharedConfig to avoid parsing the file many times, from each kinstance. 00140 // Need to keep a ref to it to make this useful 00141 d->sharedConfig = TDESharedConfig::openConfig( fileName, true /*readonly*/, false /*useKDEGlobals*/ ); 00142 TDEConfig& cfg = *d->sharedConfig; 00143 //was: KSimpleConfig cfg(fileName); 00144 00145 cfg.setGroup(mainSection); 00146 mName = cfg.readEntry("Name"); 00147 mDesc = cfg.readEntry("Comment"); 00148 mDepth = cfg.readNumEntry("DisplayDepth", 32); 00149 mInherits = cfg.readListEntry("Inherits"); 00150 if ( name != "crystalsvg" ) 00151 for ( TQStringList::Iterator it = mInherits.begin(); it != mInherits.end(); ++it ) 00152 if ( *it == "default" || *it == "hicolor" ) *it="crystalsvg"; 00153 00154 d->hidden = cfg.readBoolEntry("Hidden", false); 00155 d->example = cfg.readPathEntry("Example"); 00156 d->screenshot = cfg.readPathEntry("ScreenShot"); 00157 d->linkOverlay = cfg.readEntry("LinkOverlay", "link"); 00158 d->lockOverlay = cfg.readEntry("LockOverlay", "lock"); 00159 d->zipOverlay = cfg.readEntry("ZipOverlay", "application-vnd.tde.overlay.zip"); 00160 d->shareOverlay = cfg.readEntry("ShareOverlay","share"); 00161 00162 TQStringList dirs = cfg.readPathListEntry("Directories"); 00163 mDirs.setAutoDelete(true); 00164 for (it=dirs.begin(); it!=dirs.end(); ++it) 00165 { 00166 cfg.setGroup(*it); 00167 for (itDir=themeDirs.begin(); itDir!=themeDirs.end(); ++itDir) 00168 { 00169 if (TDEStandardDirs::exists(*itDir + *it + "/")) 00170 { 00171 TDEIconThemeDir *dir = new TDEIconThemeDir(*itDir + *it, &cfg); 00172 if (!dir->isValid()) 00173 { 00174 kdDebug(264) << "Icon directory " << *itDir << " group " << *it << " not valid.\n"; 00175 delete dir; 00176 } 00177 else 00178 mDirs.append(dir); 00179 } 00180 } 00181 } 00182 00183 // Expand available sizes for scalable icons to their full range 00184 int i; 00185 TQMap<int,TQValueList<int> > scIcons; 00186 for (TDEIconThemeDir *dir=mDirs.first(); dir!=0L; dir=mDirs.next()) 00187 { 00188 if ((dir->type() == TDEIcon::Scalable) && !scIcons.contains(dir->size())) 00189 { 00190 TQValueList<int> lst; 00191 for (i=dir->minSize(); i<=dir->maxSize(); i++) 00192 lst += i; 00193 scIcons[dir->size()] = lst; 00194 } 00195 } 00196 00197 TQStringList groups; 00198 groups += "Desktop"; 00199 groups += "Toolbar"; 00200 groups += "MainToolbar"; 00201 groups += "Small"; 00202 groups += "Panel"; 00203 const int defDefSizes[] = { 32, 22, 22, 16, 32 }; 00204 cfg.setGroup(mainSection); 00205 for (it=groups.begin(), i=0; it!=groups.end(); ++it, i++) 00206 { 00207 mDefSize[i] = cfg.readNumEntry(*it + "Default", defDefSizes[i]); 00208 TQValueList<int> exp, lst = cfg.readIntListEntry(*it + "Sizes"); 00209 TQValueList<int>::ConstIterator it2; 00210 for (it2=lst.begin(); it2!=lst.end(); ++it2) 00211 { 00212 if (scIcons.contains(*it2)) 00213 exp += scIcons[*it2]; 00214 else 00215 exp += *it2; 00216 } 00217 mSizes[i] = exp; 00218 } 00219 00220 } 00221 00222 TDEIconTheme::~TDEIconTheme() 00223 { 00224 delete d; 00225 } 00226 00227 bool TDEIconTheme::isValid() const 00228 { 00229 return !mDirs.isEmpty(); 00230 } 00231 00232 bool TDEIconTheme::isHidden() const 00233 { 00234 return d->hidden; 00235 } 00236 00237 TQString TDEIconTheme::example() const { return d->example; } 00238 TQString TDEIconTheme::screenshot() const { return d->screenshot; } 00239 TQString TDEIconTheme::linkOverlay() const { return d->linkOverlay; } 00240 TQString TDEIconTheme::lockOverlay() const { return d->lockOverlay; } 00241 TQString TDEIconTheme::zipOverlay() const { return d->zipOverlay; } 00242 TQString TDEIconTheme::shareOverlay() const { return d->shareOverlay; } 00243 00244 int TDEIconTheme::defaultSize(TDEIcon::Group group) const 00245 { 00246 if ((group < 0) || (group >= TDEIcon::LastGroup)) 00247 { 00248 kdDebug(264) << "Illegal icon group: " << group << "\n"; 00249 return -1; 00250 } 00251 return mDefSize[group]; 00252 } 00253 00254 TQValueList<int> TDEIconTheme::querySizes(TDEIcon::Group group) const 00255 { 00256 TQValueList<int> empty; 00257 if ((group < 0) || (group >= TDEIcon::LastGroup)) 00258 { 00259 kdDebug(264) << "Illegal icon group: " << group << "\n"; 00260 return empty; 00261 } 00262 return mSizes[group]; 00263 } 00264 00265 TQStringList TDEIconTheme::queryIcons(int size, TDEIcon::Context context) const 00266 { 00267 int delta = 1000, dw; 00268 00269 TQPtrListIterator<TDEIconThemeDir> dirs(mDirs); 00270 TDEIconThemeDir *dir; 00271 00272 // Try to find exact match 00273 TQStringList result; 00274 for ( ; dirs.current(); ++dirs) 00275 { 00276 dir = dirs.current(); 00277 if ((context != TDEIcon::Any) && (context != dir->context())) 00278 continue; 00279 if ((dir->type() == TDEIcon::Fixed) && (dir->size() == size)) 00280 { 00281 result += dir->iconList(); 00282 continue; 00283 } 00284 if ((dir->type() == TDEIcon::Scalable) && 00285 (size >= dir->minSize()) && (size <= dir->maxSize())) 00286 { 00287 result += dir->iconList(); 00288 continue; 00289 } 00290 if ((dir->type() == TDEIcon::Threshold) && 00291 (abs(size-dir->size())<dir->threshold())) 00292 result+=dir->iconList(); 00293 } 00294 00295 return result; 00296 00297 dirs.toFirst(); 00298 00299 // Find close match 00300 TDEIconThemeDir *best = 0L; 00301 for ( ; dirs.current(); ++dirs) 00302 { 00303 dir = dirs.current(); 00304 if ((context != TDEIcon::Any) && (context != dir->context())) 00305 continue; 00306 dw = dir->size() - size; 00307 if ((dw > 6) || (abs(dw) >= abs(delta))) 00308 continue; 00309 delta = dw; 00310 best = dir; 00311 } 00312 if (best == 0L) 00313 return TQStringList(); 00314 00315 return best->iconList(); 00316 } 00317 00318 TQStringList TDEIconTheme::queryIconsByContext(int size, TDEIcon::Context context) const 00319 { 00320 TQPtrListIterator<TDEIconThemeDir> dirs(mDirs); 00321 int dw; 00322 TDEIconThemeDir *dir; 00323 00324 // We want all the icons for a given context, but we prefer icons 00325 // of size size . Note that this may (will) include duplicate icons 00326 //TQStringList iconlist[34]; // 33 == 48-16+1 00327 TQStringList iconlist[128]; // 33 == 48-16+1 00328 // Usually, only the 0, 6 (22-16), 10 (32-22), 16 (48-32 or 32-16), 00329 // 26 (48-22) and 32 (48-16) will be used, but who knows if someone 00330 // will make icon themes with different icon sizes. 00331 00332 for ( ; dirs.current(); ++dirs) 00333 { 00334 dir = dirs.current(); 00335 if ((context != TDEIcon::Any) && (context != dir->context())) 00336 continue; 00337 dw = abs(dir->size() - size); 00338 iconlist[(dw<127)?dw:127]+=dir->iconList(); 00339 } 00340 00341 TQStringList iconlistResult; 00342 for (int i=0; i<128; i++) iconlistResult+=iconlist[i]; 00343 00344 return iconlistResult; 00345 } 00346 00347 bool TDEIconTheme::hasContext(TDEIcon::Context context) const 00348 { 00349 TQPtrListIterator<TDEIconThemeDir> dirs(mDirs); 00350 TDEIconThemeDir *dir; 00351 00352 for ( ; dirs.current(); ++dirs) 00353 { 00354 dir = dirs.current(); 00355 if ((context == TDEIcon::Any) || (context == dir->context())) 00356 return true; 00357 } 00358 return false; 00359 } 00360 00361 TDEIcon TDEIconTheme::iconPath(const TQString& name, int size, TDEIcon::MatchType match) const 00362 { 00363 TDEIcon icon; 00364 TQString path; 00365 int delta = -1000, dw; 00366 TDEIconThemeDir *dir; 00367 00368 dw = 1000; // shut up, gcc 00369 TQPtrListIterator<TDEIconThemeDir> dirs(mDirs); 00370 for ( ; dirs.current(); ++dirs) 00371 { 00372 dir = dirs.current(); 00373 00374 if (match == TDEIcon::MatchExact) 00375 { 00376 if ((dir->type() == TDEIcon::Fixed) && (dir->size() != size)) 00377 continue; 00378 if ((dir->type() == TDEIcon::Scalable) && 00379 ((size < dir->minSize()) || (size > dir->maxSize()))) 00380 continue; 00381 if ((dir->type() == TDEIcon::Threshold) && 00382 (abs(dir->size()-size) > dir->threshold())) 00383 continue; 00384 } else 00385 { 00386 // dw < 0 means need to scale up to get an icon of the requested size 00387 if (dir->type() == TDEIcon::Fixed) 00388 { 00389 dw = dir->size() - size; 00390 } else if (dir->type() == TDEIcon::Scalable) 00391 { 00392 if (size < dir->minSize()) 00393 dw = dir->minSize() - size; 00394 else if (size > dir->maxSize()) 00395 dw = dir->maxSize() - size; 00396 else 00397 dw = 0; 00398 } else if (dir->type() == TDEIcon::Threshold) 00399 { 00400 if (size < dir->size() - dir->threshold()) 00401 dw = dir->size() - dir->threshold() - size; 00402 else if (size > dir->size() + dir->threshold()) 00403 dw = dir->size() + dir->threshold() - size; 00404 else 00405 dw = 0; 00406 } 00407 /* Skip this if we've found a closer one, unless 00408 it's a downscale, and we only had upscales befores. 00409 This is to avoid scaling up unless we have to, 00410 since that looks very ugly */ 00411 if (/*(abs(dw) >= abs(delta)) ||*/ 00412 (delta > 0 && dw < 0)) 00413 continue; 00414 } 00415 00416 path = dir->iconPath(name); 00417 if (path.isEmpty()) 00418 continue; 00419 icon.path = path; 00420 icon.size = dir->size(); 00421 icon.type = dir->type(); 00422 icon.threshold = dir->threshold(); 00423 icon.context = dir->context(); 00424 00425 // if we got in MatchExact that far, we find no better 00426 if (match == TDEIcon::MatchExact) 00427 return icon; 00428 else 00429 { 00430 delta = dw; 00431 if (delta==0) return icon; // We won't find a better match anyway 00432 } 00433 } 00434 return icon; 00435 } 00436 00437 // static 00438 TQString *TDEIconTheme::_theme = 0L; 00439 00440 // static 00441 TQStringList *TDEIconTheme::_theme_list = 0L; 00442 00443 // static 00444 TQString TDEIconTheme::current() 00445 { 00446 // Static pointer because of unloading problems wrt DSO's. 00447 if (_theme != 0L) 00448 return *_theme; 00449 00450 _theme = new TQString(); 00451 TDEConfig *config = TDEGlobal::config(); 00452 TDEConfigGroupSaver saver(config, "Icons"); 00453 *_theme = config->readEntry("Theme",defaultThemeName()); 00454 if ( *_theme == TQString::fromLatin1("hicolor") ) *_theme = defaultThemeName(); 00455 /* if (_theme->isEmpty()) 00456 { 00457 if (TQPixmap::defaultDepth() > 8) 00458 *_theme = defaultThemeName(); 00459 else 00460 *_theme = TQString::fromLatin1("locolor"); 00461 }*/ 00462 return *_theme; 00463 } 00464 00465 // static 00466 TQStringList TDEIconTheme::list() 00467 { 00468 // Static pointer because of unloading problems wrt DSO's. 00469 if (_theme_list != 0L) 00470 return *_theme_list; 00471 00472 _theme_list = new TQStringList(); 00473 TQStringList icnlibs = TDEGlobal::dirs()->resourceDirs("icon"); 00474 icnlibs += (TDEGlobal::dirs()->resourceDirs("xdgdata-icon")); 00475 icnlibs += "/usr/share/pixmaps"; 00476 // These are not in the icon spec, but e.g. GNOME puts some icons there anyway. 00477 icnlibs += TDEGlobal::dirs()->resourceDirs("xdgdata-pixmap"); 00478 TQStringList::ConstIterator it; 00479 for (it=icnlibs.begin(); it!=icnlibs.end(); ++it) 00480 { 00481 TQDir dir(*it); 00482 if (!dir.exists()) 00483 continue; 00484 TQStringList lst = dir.entryList(TQDir::Dirs); 00485 TQStringList::ConstIterator it2; 00486 for (it2=lst.begin(); it2!=lst.end(); ++it2) 00487 { 00488 if ((*it2 == ".") || (*it2 == "..") || (*it2).startsWith("default.") ) 00489 continue; 00490 if (!TDEStandardDirs::exists(*it + *it2 + "/index.desktop") && !TDEStandardDirs::exists(*it + *it2 + "/index.theme")) 00491 continue; 00492 TDEIconTheme oink(*it2); 00493 if (!oink.isValid()) continue; 00494 00495 if (!_theme_list->contains(*it2)) 00496 _theme_list->append(*it2); 00497 } 00498 } 00499 return *_theme_list; 00500 } 00501 00502 // static 00503 void TDEIconTheme::reconfigure() 00504 { 00505 delete _theme; 00506 _theme=0L; 00507 delete _theme_list; 00508 _theme_list=0L; 00509 } 00510 00511 // static 00512 TQString TDEIconTheme::defaultThemeName() 00513 { 00514 return TQString::fromLatin1("crystalsvg"); 00515 } 00516 00517 /*** TDEIconThemeDir ***/ 00518 00519 TDEIconThemeDir::TDEIconThemeDir(const TQString& dir, const TDEConfigBase *config) 00520 { 00521 mbValid = false; 00522 mDir = dir; 00523 mSize = config->readNumEntry("Size"); 00524 mMinSize = 1; // just set the variables to something 00525 mMaxSize = 50; // meaningful in case someone calls minSize or maxSize 00526 mType = TDEIcon::Fixed; 00527 00528 if (mSize == 0) 00529 return; 00530 00531 TQString tmp = config->readEntry("Context"); 00532 if (tmp == "Devices") 00533 mContext = TDEIcon::Device; 00534 else if (tmp == "MimeTypes") 00535 mContext = TDEIcon::MimeType; 00536 else if (tmp == "FileSystems") 00537 mContext = TDEIcon::FileSystem; 00538 else if (tmp == "Applications") 00539 mContext = TDEIcon::Application; 00540 else if (tmp == "Actions") 00541 mContext = TDEIcon::Action; 00542 else if (tmp == "Animations") 00543 mContext = TDEIcon::Animation; 00544 else if (tmp == "Categories") 00545 mContext = TDEIcon::Category; 00546 else if (tmp == "Emblems") 00547 mContext = TDEIcon::Emblem; 00548 else if (tmp == "Emotes") 00549 mContext = TDEIcon::Emote; 00550 else if (tmp == "International") 00551 mContext = TDEIcon::International; 00552 else if (tmp == "Places") 00553 mContext = TDEIcon::Place; 00554 else if (tmp == "Status") 00555 mContext = TDEIcon::StatusIcon; 00556 else { 00557 kdDebug(264) << "Invalid Context= line for icon theme: " << mDir << "\n"; 00558 return; 00559 } 00560 tmp = config->readEntry("Type"); 00561 if (tmp == "Fixed") 00562 mType = TDEIcon::Fixed; 00563 else if (tmp == "Scalable") 00564 mType = TDEIcon::Scalable; 00565 else if (tmp == "Threshold") 00566 mType = TDEIcon::Threshold; 00567 else { 00568 kdDebug(264) << "Invalid Type= line for icon theme: " << mDir << "\n"; 00569 return; 00570 } 00571 if (mType == TDEIcon::Scalable) 00572 { 00573 mMinSize = config->readNumEntry("MinSize", mSize); 00574 mMaxSize = config->readNumEntry("MaxSize", mSize); 00575 } else if (mType == TDEIcon::Threshold) 00576 mThreshold = config->readNumEntry("Threshold", 2); 00577 mbValid = true; 00578 } 00579 00580 TQString TDEIconThemeDir::iconPath(const TQString& name) const 00581 { 00582 if (!mbValid) 00583 return TQString::null; 00584 TQString file = mDir + "/" + name; 00585 00586 if (access(TQFile::encodeName(file), R_OK) == 0) 00587 return file; 00588 00589 return TQString::null; 00590 } 00591 00592 TQStringList TDEIconThemeDir::iconList() const 00593 { 00594 TQDir dir(mDir); 00595 #ifdef HAVE_LIBART 00596 TQStringList lst = dir.entryList("*.png;*.svg;*.svgz;*.xpm", TQDir::Files); 00597 #else 00598 TQStringList lst = dir.entryList("*.png;*.xpm", TQDir::Files); 00599 #endif 00600 TQStringList result; 00601 TQStringList::ConstIterator it; 00602 for (it=lst.begin(); it!=lst.end(); ++it) 00603 result += mDir + "/" + *it; 00604 return result; 00605 }