kdecore Library API Documentation

kiconloader.cpp

00001 /* vi: ts=8 sts=4 sw=4 00002 * 00003 * $Id: kiconloader.cpp,v 1.224 2005/01/12 16:37:13 lunakl Exp $ 00004 * 00005 * This file is part of the KDE project, module kdecore. 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 * kiconloader.cpp: An icon loader for KDE with theming functionality. 00014 */ 00015 00016 #include <qstring.h> 00017 #include <qstringlist.h> 00018 #include <qptrlist.h> 00019 #include <qintdict.h> 00020 #include <qpixmap.h> 00021 #include <qpixmapcache.h> 00022 #include <qimage.h> 00023 #include <qfileinfo.h> 00024 #include <qdir.h> 00025 #include <qiconset.h> 00026 #include <qmovie.h> 00027 #include <qbitmap.h> 00028 00029 #include <kdebug.h> 00030 #include <kstandarddirs.h> 00031 #include <kglobal.h> 00032 #include <kconfig.h> 00033 #include <ksimpleconfig.h> 00034 #include <kinstance.h> 00035 00036 #include <kicontheme.h> 00037 #include <kiconloader.h> 00038 #include <kiconeffect.h> 00039 00040 #include <sys/types.h> 00041 #include <stdlib.h> //for abs 00042 #include <unistd.h> //for readlink 00043 #include <dirent.h> 00044 #include <config.h> 00045 #include <assert.h> 00046 00047 #ifdef HAVE_LIBART 00048 #include "svgicons/ksvgiconengine.h" 00049 #include "svgicons/ksvgiconpainter.h" 00050 #endif 00051 00052 /*** KIconThemeNode: A node in the icon theme dependancy tree. ***/ 00053 00054 class KIconThemeNode 00055 { 00056 public: 00057 00058 KIconThemeNode(KIconTheme *_theme); 00059 ~KIconThemeNode(); 00060 00061 void queryIcons(QStringList *lst, int size, KIcon::Context context) const; 00062 void queryIconsByContext(QStringList *lst, int size, KIcon::Context context) const; 00063 KIcon findIcon(const QString& name, int size, KIcon::MatchType match) const; 00064 void printTree(QString& dbgString) const; 00065 00066 KIconTheme *theme; 00067 }; 00068 00069 KIconThemeNode::KIconThemeNode(KIconTheme *_theme) 00070 { 00071 theme = _theme; 00072 } 00073 00074 KIconThemeNode::~KIconThemeNode() 00075 { 00076 delete theme; 00077 } 00078 00079 void KIconThemeNode::printTree(QString& dbgString) const 00080 { 00081 /* This method doesn't have much sense anymore, so maybe it should 00082 be removed in the (near?) future */ 00083 dbgString += "("; 00084 dbgString += theme->name(); 00085 dbgString += ")"; 00086 } 00087 00088 void KIconThemeNode::queryIcons(QStringList *result, 00089 int size, KIcon::Context context) const 00090 { 00091 // add the icons of this theme to it 00092 *result += theme->queryIcons(size, context); 00093 } 00094 00095 void KIconThemeNode::queryIconsByContext(QStringList *result, 00096 int size, KIcon::Context context) const 00097 { 00098 // add the icons of this theme to it 00099 *result += theme->queryIconsByContext(size, context); 00100 } 00101 00102 KIcon KIconThemeNode::findIcon(const QString& name, int size, 00103 KIcon::MatchType match) const 00104 { 00105 return theme->iconPath(name, size, match); 00106 } 00107 00108 00109 /*** KIconGroup: Icon type description. ***/ 00110 00111 struct KIconGroup 00112 { 00113 int size; 00114 bool dblPixels; 00115 bool alphaBlending; 00116 }; 00117 00118 00119 /*** d pointer for KIconLoader. ***/ 00120 00121 struct KIconLoaderPrivate 00122 { 00123 QStringList mThemeList; 00124 QStringList mThemesInTree; 00125 KIconGroup *mpGroups; 00126 KIconThemeNode *mpThemeRoot; 00127 KStandardDirs *mpDirs; 00128 KIconEffect mpEffect; 00129 QDict<QImage> imgDict; 00130 QImage lastImage; // last loaded image without effect applied 00131 QString lastImageKey; // key for icon without effect 00132 int lastIconType; // see KIcon::type 00133 int lastIconThreshold; // see KIcon::threshold 00134 QPtrList<KIconThemeNode> links; 00135 bool extraDesktopIconsLoaded :1; 00136 bool delayedLoading :1; 00137 }; 00138 00139 #define KICONLOADER_CHECKS 00140 #ifdef KICONLOADER_CHECKS 00141 // Keep a list of recently created and destroyed KIconLoader instances in order 00142 // to detect bugs like #68528. 00143 struct KIconLoaderDebug 00144 { 00145 KIconLoaderDebug( KIconLoader* l, const QString& a ) 00146 : loader( l ), appname( a ), valid( true ) 00147 {} 00148 KIconLoaderDebug() {}; // this QValueList feature annoys me 00149 KIconLoader* loader; 00150 QString appname; 00151 bool valid; 00152 QString delete_bt; 00153 }; 00154 00155 static QValueList< KIconLoaderDebug > *kiconloaders; 00156 #endif 00157 00158 /*** KIconLoader: the icon loader ***/ 00159 00160 KIconLoader::KIconLoader(const QString& _appname, KStandardDirs *_dirs) 00161 { 00162 #ifdef KICONLOADER_CHECKS 00163 if( kiconloaders == NULL ) 00164 kiconloaders = new QValueList< KIconLoaderDebug>(); 00165 // check for the (very unlikely case) that new KIconLoader gets allocated 00166 // at exactly same address like some previous one 00167 for( QValueList< KIconLoaderDebug >::Iterator it = kiconloaders->begin(); 00168 it != kiconloaders->end(); 00169 ) 00170 { 00171 if( (*it).loader == this ) 00172 it = kiconloaders->remove( it ); 00173 else 00174 ++it; 00175 } 00176 kiconloaders->append( KIconLoaderDebug( this, _appname )); 00177 #endif 00178 init( _appname, _dirs ); 00179 } 00180 00181 void KIconLoader::reconfigure( const QString& _appname, KStandardDirs *_dirs ) 00182 { 00183 delete d; 00184 init( _appname, _dirs ); 00185 } 00186 00187 void KIconLoader::init( const QString& _appname, KStandardDirs *_dirs ) 00188 { 00189 d = new KIconLoaderPrivate; 00190 d->imgDict.setAutoDelete( true ); 00191 d->links.setAutoDelete(true); 00192 d->extraDesktopIconsLoaded=false; 00193 d->delayedLoading=false; 00194 00195 if (_dirs) 00196 d->mpDirs = _dirs; 00197 else 00198 d->mpDirs = KGlobal::dirs(); 00199 00200 // If this is unequal to 0, the iconloader is initialized 00201 // successfully. 00202 d->mpThemeRoot = 0L; 00203 00204 // Check installed themes. 00205 d->mThemeList = KIconTheme::list(); 00206 if (!d->mThemeList.contains(KIconTheme::defaultThemeName())) 00207 { 00208 kdError(264) << "Error: standard icon theme" 00209 << " \"" << KIconTheme::defaultThemeName() << "\" " 00210 << " not found!" << endl; 00211 d->mpGroups=0L; 00212 00213 return; 00214 } 00215 00216 QString appname = _appname; 00217 if (appname.isEmpty()) 00218 appname = KGlobal::instance()->instanceName(); 00219 00220 // Add the default theme and its base themes to the theme tree 00221 KIconTheme *def = new KIconTheme(KIconTheme::current(), appname); 00222 if (!def->isValid()) 00223 { 00224 delete def; 00225 def = new KIconTheme(KIconTheme::defaultThemeName(), appname); 00226 } 00227 d->mpThemeRoot = new KIconThemeNode(def); 00228 d->links.append(d->mpThemeRoot); 00229 d->mThemesInTree += KIconTheme::current(); 00230 addBaseThemes(d->mpThemeRoot, appname); 00231 00232 // These have to match the order in kicontheme.h 00233 static const char * const groups[] = { "Desktop", "Toolbar", "MainToolbar", "Small", "Panel", 0L }; 00234 KConfig *config = KGlobal::config(); 00235 KConfigGroupSaver cs(config, "dummy"); 00236 00237 // loading config and default sizes 00238 d->mpGroups = new KIconGroup[(int) KIcon::LastGroup]; 00239 for (KIcon::Group i=KIcon::FirstGroup; i<KIcon::LastGroup; i++) 00240 { 00241 if (groups[i] == 0L) 00242 break; 00243 config->setGroup(QString::fromLatin1(groups[i]) + "Icons"); 00244 d->mpGroups[i].size = config->readNumEntry("Size", 0); 00245 d->mpGroups[i].dblPixels = config->readBoolEntry("DoublePixels", false); 00246 if (QPixmap::defaultDepth()>8) 00247 d->mpGroups[i].alphaBlending = config->readBoolEntry("AlphaBlending", true); 00248 else 00249 d->mpGroups[i].alphaBlending = false; 00250 00251 if (!d->mpGroups[i].size) 00252 d->mpGroups[i].size = d->mpThemeRoot->theme->defaultSize(i); 00253 } 00254 00255 // Insert application specific themes at the top. 00256 d->mpDirs->addResourceType("appicon", KStandardDirs::kde_default("data") + 00257 appname + "/pics/"); 00258 // ################## KDE4: consider removing the toolbar directory 00259 d->mpDirs->addResourceType("appicon", KStandardDirs::kde_default("data") + 00260 appname + "/toolbar/"); 00261 00262 // Add legacy icon dirs. 00263 QStringList dirs; 00264 dirs += d->mpDirs->resourceDirs("icon"); 00265 dirs += d->mpDirs->resourceDirs("pixmap"); 00266 for (QStringList::ConstIterator it = dirs.begin(); it != dirs.end(); ++it) 00267 d->mpDirs->addResourceDir("appicon", *it); 00268 00269 #ifndef NDEBUG 00270 QString dbgString = "Theme tree: "; 00271 d->mpThemeRoot->printTree(dbgString); 00272 kdDebug(264) << dbgString << endl; 00273 #endif 00274 } 00275 00276 KIconLoader::~KIconLoader() 00277 { 00278 #ifdef KICONLOADER_CHECKS 00279 for( QValueList< KIconLoaderDebug >::Iterator it = kiconloaders->begin(); 00280 it != kiconloaders->end(); 00281 ++it ) 00282 { 00283 if( (*it).loader == this ) 00284 { 00285 (*it).valid = false; 00286 (*it).delete_bt = kdBacktrace(); 00287 break; 00288 } 00289 } 00290 #endif 00291 /* antlarr: There's no need to delete d->mpThemeRoot as it's already 00292 deleted when the elements of d->links are deleted */ 00293 d->mpThemeRoot=0; 00294 delete[] d->mpGroups; 00295 delete d; 00296 } 00297 00298 void KIconLoader::enableDelayedIconSetLoading( bool enable ) 00299 { 00300 d->delayedLoading = enable; 00301 } 00302 00303 bool KIconLoader::isDelayedIconSetLoadingEnabled() const 00304 { 00305 return d->delayedLoading; 00306 } 00307 00308 void KIconLoader::addAppDir(const QString& appname) 00309 { 00310 d->mpDirs->addResourceType("appicon", KStandardDirs::kde_default("data") + 00311 appname + "/pics/"); 00312 // ################## KDE4: consider removing the toolbar directory 00313 d->mpDirs->addResourceType("appicon", KStandardDirs::kde_default("data") + 00314 appname + "/toolbar/"); 00315 addAppThemes(appname); 00316 } 00317 00318 void KIconLoader::addAppThemes(const QString& appname) 00319 { 00320 if ( KIconTheme::current() != KIconTheme::defaultThemeName() ) 00321 { 00322 KIconTheme *def = new KIconTheme(KIconTheme::current(), appname); 00323 if (def->isValid()) 00324 { 00325 KIconThemeNode* node = new KIconThemeNode(def); 00326 d->links.append(node); 00327 addBaseThemes(node, appname); 00328 } 00329 else 00330 delete def; 00331 } 00332 00333 KIconTheme *def = new KIconTheme(KIconTheme::defaultThemeName(), appname); 00334 KIconThemeNode* node = new KIconThemeNode(def); 00335 d->links.append(node); 00336 addBaseThemes(node, appname); 00337 } 00338 00339 void KIconLoader::addBaseThemes(KIconThemeNode *node, const QString &appname) 00340 { 00341 QStringList lst = node->theme->inherits(); 00342 QStringList::ConstIterator it; 00343 00344 for (it=lst.begin(); it!=lst.end(); ++it) 00345 { 00346 if (!d->mThemeList.contains(*it) || 00347 ( d->mThemesInTree.contains(*it) && (*it) != "hicolor")) 00348 continue; 00349 KIconTheme *theme = new KIconTheme(*it,appname); 00350 if (!theme->isValid()) { 00351 delete theme; 00352 continue; 00353 } 00354 KIconThemeNode *n = new KIconThemeNode(theme); 00355 d->mThemesInTree.append(*it); 00356 addBaseThemes(n, appname); 00357 d->links.append(n); 00358 } 00359 } 00360 00361 void KIconLoader::addExtraDesktopThemes() 00362 { 00363 if ( d->extraDesktopIconsLoaded ) return; 00364 00365 QStringList list; 00366 QStringList icnlibs = KGlobal::dirs()->resourceDirs("icon"); 00367 QStringList::ConstIterator it; 00368 char buf[1000]; 00369 int r; 00370 for (it=icnlibs.begin(); it!=icnlibs.end(); ++it) 00371 { 00372 QDir dir(*it); 00373 if (!dir.exists()) 00374 continue; 00375 QStringList lst = dir.entryList("default.*", QDir::Dirs); 00376 QStringList::ConstIterator it2; 00377 for (it2=lst.begin(); it2!=lst.end(); ++it2) 00378 { 00379 if (!KStandardDirs::exists(*it + *it2 + "/index.desktop") 00380 && !KStandardDirs::exists(*it + *it2 + "/index.theme")) 00381 continue; 00382 r=readlink( QFile::encodeName(*it + *it2) , buf, sizeof(buf)-1); 00383 if ( r>0 ) 00384 { 00385 buf[r]=0; 00386 QDir dir2( buf ); 00387 QString themeName=dir2.dirName(); 00388 00389 if (!list.contains(themeName)) 00390 list.append(themeName); 00391 } 00392 } 00393 } 00394 00395 for (it=list.begin(); it!=list.end(); ++it) 00396 { 00397 if ( d->mThemesInTree.contains(*it) ) 00398 continue; 00399 if ( *it == QString("default.kde") ) continue; 00400 00401 KIconTheme *def = new KIconTheme( *it, "" ); 00402 KIconThemeNode* node = new KIconThemeNode(def); 00403 d->mThemesInTree.append(*it); 00404 d->links.append(node); 00405 addBaseThemes(node, "" ); 00406 } 00407 00408 d->extraDesktopIconsLoaded=true; 00409 00410 } 00411 00412 bool KIconLoader::extraDesktopThemesAdded() const 00413 { 00414 return d->extraDesktopIconsLoaded; 00415 } 00416 00417 QString KIconLoader::removeIconExtension(const QString &name) const 00418 { 00419 int extensionLength=0; 00420 00421 QString ext = name.right(4); 00422 00423 static const QString &png_ext = KGlobal::staticQString(".png"); 00424 static const QString &xpm_ext = KGlobal::staticQString(".xpm"); 00425 if (ext == png_ext || ext == xpm_ext) 00426 extensionLength=4; 00427 #ifdef HAVE_LIBART 00428 else 00429 { 00430 static const QString &svgz_ext = KGlobal::staticQString(".svgz"); 00431 static const QString &svg_ext = KGlobal::staticQString(".svg"); 00432 00433 if (name.right(5) == svgz_ext) 00434 extensionLength=5; 00435 else if (ext == svg_ext) 00436 extensionLength=4; 00437 } 00438 #endif 00439 00440 if ( extensionLength > 0 ) 00441 { 00442 #ifndef NDEBUG 00443 kdDebug(264) << "Application " << KGlobal::instance()->instanceName() 00444 << " loads icon " << name << " with extension." << endl; 00445 #endif 00446 00447 return name.left(name.length() - extensionLength); 00448 } 00449 return name; 00450 } 00451 00452 00453 KIcon KIconLoader::findMatchingIcon(const QString& name, int size) const 00454 { 00455 KIcon icon; 00456 00457 const QString *ext[4]; 00458 int count=0; 00459 static const QString &png_ext = KGlobal::staticQString(".png"); 00460 ext[count++]=&png_ext; 00461 #ifdef HAVE_LIBART 00462 static const QString &svgz_ext = KGlobal::staticQString(".svgz"); 00463 ext[count++]=&svgz_ext; 00464 static const QString &svg_ext = KGlobal::staticQString(".svg"); 00465 ext[count++]=&svg_ext; 00466 #endif 00467 static const QString &xpm_ext = KGlobal::staticQString(".xpm"); 00468 ext[count++]=&xpm_ext; 00469 00470 /* antlarr: Multiple inheritance is a broken concept on icon themes, so 00471 the next code doesn't support it on purpose because in fact, it was 00472 never supported at all. This makes the order in which we look for an 00473 icon as: 00474 00475 png, svgz, svg, xpm exact match 00476 next theme in inheritance tree : png, svgz, svg, xpm exact match 00477 next theme in inheritance tree : png, svgz, svg, xpm exact match 00478 and so on 00479 00480 And if the icon couldn't be found then it tries best match in the same 00481 order. 00482 00483 */ 00484 for ( KIconThemeNode *themeNode = d->links.first() ; themeNode ; 00485 themeNode = d->links.next() ) 00486 { 00487 for (int i = 0 ; i < count ; i++) 00488 { 00489 icon = themeNode->theme->iconPath(name + *ext[i], size, KIcon::MatchExact); 00490 if (icon.isValid()) 00491 return icon; 00492 } 00493 00494 } 00495 00496 for ( KIconThemeNode *themeNode = d->links.first() ; themeNode ; 00497 themeNode = d->links.next() ) 00498 { 00499 for (int i = 0 ; i < count ; i++) 00500 { 00501 icon = themeNode->theme->iconPath(name + *ext[i], size, KIcon::MatchBest); 00502 if (icon.isValid()) 00503 return icon; 00504 } 00505 00506 } 00507 00508 return icon; 00509 } 00510 00511 inline QString KIconLoader::unknownIconPath( int size ) const 00512 { 00513 static const QString &str_unknown = KGlobal::staticQString("unknown"); 00514 00515 KIcon icon = findMatchingIcon(str_unknown, size); 00516 if (!icon.isValid()) 00517 { 00518 kdDebug(264) << "Warning: could not find \"Unknown\" icon for size = " 00519 << size << endl; 00520 return QString::null; 00521 } 00522 return icon.path; 00523 } 00524 00525 // Finds the absolute path to an icon. 00526 00527 QString KIconLoader::iconPath(const QString& _name, int group_or_size, 00528 bool canReturnNull) const 00529 { 00530 if (d->mpThemeRoot == 0L) 00531 return QString::null; 00532 00533 if (!QDir::isRelativePath(_name)) 00534 return _name; 00535 00536 QString name = removeIconExtension( _name ); 00537 00538 QString path; 00539 if (group_or_size == KIcon::User) 00540 { 00541 static const QString &png_ext = KGlobal::staticQString(".png"); 00542 static const QString &xpm_ext = KGlobal::staticQString(".xpm"); 00543 path = d->mpDirs->findResource("appicon", name + png_ext); 00544 00545 #ifdef HAVE_LIBART 00546 static const QString &svgz_ext = KGlobal::staticQString(".svgz"); 00547 static const QString &svg_ext = KGlobal::staticQString(".svg"); 00548 if (path.isEmpty()) 00549 path = d->mpDirs->findResource("appicon", name + svgz_ext); 00550 if (path.isEmpty()) 00551 path = d->mpDirs->findResource("appicon", name + svg_ext); 00552 #endif 00553 if (path.isEmpty()) 00554 path = d->mpDirs->findResource("appicon", name + xpm_ext); 00555 return path; 00556 } 00557 00558 if (group_or_size >= KIcon::LastGroup) 00559 { 00560 kdDebug(264) << "Illegal icon group: " << group_or_size << endl; 00561 return path; 00562 } 00563 00564 int size; 00565 if (group_or_size >= 0) 00566 size = d->mpGroups[group_or_size].size; 00567 else 00568 size = -group_or_size; 00569 00570 if (_name.isEmpty()) { 00571 if (canReturnNull) 00572 return QString::null; 00573 else 00574 return unknownIconPath(size); 00575 } 00576 00577 KIcon icon = findMatchingIcon(name, size); 00578 00579 if (!icon.isValid()) 00580 { 00581 // Try "User" group too. 00582 path = iconPath(name, KIcon::User, true); 00583 if (!path.isEmpty() || canReturnNull) 00584 return path; 00585 00586 if (canReturnNull) 00587 return QString::null; 00588 else 00589 return unknownIconPath(size); 00590 } 00591 return icon.path; 00592 } 00593 00594 QPixmap KIconLoader::loadIcon(const QString& _name, KIcon::Group group, int size, 00595 int state, QString *path_store, bool canReturnNull) const 00596 { 00597 QString name = _name; 00598 QPixmap pix; 00599 QString key; 00600 bool absolutePath=false, favIconOverlay=false; 00601 00602 if (d->mpThemeRoot == 0L) 00603 return pix; 00604 00605 // Special case for absolute path icons. 00606 if (name.startsWith("favicons/")) 00607 { 00608 favIconOverlay = true; 00609 name = locateLocal("cache", name+".png"); 00610 } 00611 if (!QDir::isRelativePath(name)) absolutePath=true; 00612 00613 static const QString &str_unknown = KGlobal::staticQString("unknown"); 00614 00615 // Special case for "User" icons. 00616 if (group == KIcon::User) 00617 { 00618 key = "$kicou_"; 00619 key += QString::number(size); key += '_'; 00620 key += name; 00621 bool inCache = QPixmapCache::find(key, pix); 00622 if (inCache && (path_store == 0L)) 00623 return pix; 00624 00625 QString path = (absolutePath) ? name : 00626 iconPath(name, KIcon::User, canReturnNull); 00627 if (path.isEmpty()) 00628 { 00629 if (canReturnNull) 00630 return pix; 00631 // We don't know the desired size: use small 00632 path = iconPath(str_unknown, KIcon::Small, true); 00633 if (path.isEmpty()) 00634 { 00635 kdDebug(264) << "Warning: Cannot find \"unknown\" icon." << endl; 00636 return pix; 00637 } 00638 } 00639 00640 if (path_store != 0L) 00641 *path_store = path; 00642 if (inCache) 00643 return pix; 00644 QImage img(path); 00645 if (size != 0) 00646 img=img.smoothScale(size,size); 00647 00648 pix.convertFromImage(img); 00649 QPixmapCache::insert(key, pix); 00650 return pix; 00651 } 00652 00653 // Regular case: Check parameters 00654 00655 if ((group < -1) || (group >= KIcon::LastGroup)) 00656 { 00657 kdDebug(264) << "Illegal icon group: " << group << endl; 00658 group = KIcon::Desktop; 00659 } 00660 00661 int overlay = (state & KIcon::OverlayMask); 00662 state &= ~KIcon::OverlayMask; 00663 if ((state < 0) || (state >= KIcon::LastState)) 00664 { 00665 kdDebug(264) << "Illegal icon state: " << state << endl; 00666 state = KIcon::DefaultState; 00667 } 00668 00669 if (size == 0 && group < 0) 00670 { 00671 kdDebug(264) << "Neither size nor group specified!" << endl; 00672 group = KIcon::Desktop; 00673 } 00674 00675 if (!absolutePath) 00676 { 00677 if (!canReturnNull && name.isEmpty()) 00678 name = str_unknown; 00679 else 00680 name = removeIconExtension(name); 00681 } 00682 00683 // If size == 0, use default size for the specified group. 00684 if (size == 0) 00685 { 00686 size = d->mpGroups[group].size; 00687 } 00688 favIconOverlay = favIconOverlay && size > 22; 00689 00690 // Generate a unique cache key for the icon. 00691 00692 key = "$kico_"; 00693 key += name; key += '_'; 00694 key += QString::number(size); key += '_'; 00695 00696 QString overlayStr = QString::number( overlay ); 00697 00698 QString noEffectKey = key + '_' + overlayStr; 00699 00700 if (group >= 0) 00701 { 00702 key += d->mpEffect.fingerprint(group, state); 00703 if (d->mpGroups[group].dblPixels) 00704 key += QString::fromLatin1(":dblsize"); 00705 } else 00706 key += QString::fromLatin1("noeffect"); 00707 key += '_'; 00708 key += overlayStr; 00709 00710 // Is the icon in the cache? 00711 bool inCache = QPixmapCache::find(key, pix); 00712 if (inCache && (path_store == 0L)) 00713 return pix; 00714 00715 QImage *img = 0; 00716 int iconType; 00717 int iconThreshold; 00718 00719 if ( ( path_store != 0L ) || 00720 noEffectKey != d->lastImageKey ) 00721 { 00722 // No? load it. 00723 KIcon icon; 00724 if (absolutePath && !favIconOverlay) 00725 { 00726 icon.context=KIcon::Any; 00727 icon.type=KIcon::Scalable; 00728 icon.path=name; 00729 } 00730 else 00731 { 00732 if (!name.isEmpty()) 00733 icon = findMatchingIcon(favIconOverlay ? QString("www") : name, size); 00734 00735 if (!icon.isValid()) 00736 { 00737 // Try "User" icon too. Some apps expect this. 00738 if (!name.isEmpty()) 00739 pix = loadIcon(name, KIcon::User, size, state, path_store, true); 00740 if (!pix.isNull() || canReturnNull) 00741 return pix; 00742 00743 icon = findMatchingIcon(str_unknown, size); 00744 if (!icon.isValid()) 00745 { 00746 kdDebug(264) 00747 << "Warning: could not find \"Unknown\" icon for size = " 00748 << size << endl; 00749 return pix; 00750 } 00751 } 00752 } 00753 00754 if (path_store != 0L) 00755 *path_store = icon.path; 00756 if (inCache) 00757 return pix; 00758 00759 // Use the extension as the format. Works for XPM and PNG, but not for SVG 00760 QString ext = icon.path.right(3).upper(); 00761 if(ext != "SVG" && ext != "VGZ") 00762 { 00763 img = new QImage(icon.path, ext.latin1()); 00764 if (img->isNull()) { 00765 delete img; 00766 return pix; 00767 } 00768 } 00769 #ifdef HAVE_LIBART 00770 else 00771 { 00772 // Special stuff for SVG icons 00773 KSVGIconEngine *svgEngine = new KSVGIconEngine(); 00774 00775 if(svgEngine->load(size, size, icon.path)) 00776 img = svgEngine->painter()->image(); 00777 else 00778 img = new QImage(); 00779 00780 delete svgEngine; 00781 } 00782 #endif 00783 00784 iconType = icon.type; 00785 iconThreshold = icon.threshold; 00786 00787 d->lastImage = img->copy(); 00788 d->lastImageKey = noEffectKey; 00789 d->lastIconType = iconType; 00790 d->lastIconThreshold = iconThreshold; 00791 } 00792 else 00793 { 00794 img = new QImage( d->lastImage.copy() ); 00795 iconType = d->lastIconType; 00796 iconThreshold = d->lastIconThreshold; 00797 } 00798 00799 // Blend in all overlays 00800 if (overlay) 00801 { 00802 QImage *ovl; 00803 KIconTheme *theme = d->mpThemeRoot->theme; 00804 if ((overlay & KIcon::LockOverlay) && 00805 ((ovl = loadOverlay(theme->lockOverlay(), size)) != 0L)) 00806 KIconEffect::overlay(*img, *ovl); 00807 if ((overlay & KIcon::LinkOverlay) && 00808 ((ovl = loadOverlay(theme->linkOverlay(), size)) != 0L)) 00809 KIconEffect::overlay(*img, *ovl); 00810 if ((overlay & KIcon::ZipOverlay) && 00811 ((ovl = loadOverlay(theme->zipOverlay(), size)) != 0L)) 00812 KIconEffect::overlay(*img, *ovl); 00813 if ((overlay & KIcon::ShareOverlay) && 00814 ((ovl = loadOverlay(theme->shareOverlay(), size)) != 0L)) 00815 KIconEffect::overlay(*img, *ovl); 00816 if (overlay & KIcon::HiddenOverlay) 00817 for (int y = 0; y < img->height(); y++) 00818 { 00819 Q_UINT32 *line = reinterpret_cast<Q_UINT32 *>(img->scanLine(y)); 00820 for (int x = 0; x < img->width(); x++) 00821 line[x] = (line[x] & 0x00ffffff) | (QMIN(0x80, qAlpha(line[x])) << 24); 00822 } 00823 } 00824 00825 // Scale the icon and apply effects if necessary 00826 if (iconType == KIcon::Scalable && size != img->width()) 00827 { 00828 *img = img->smoothScale(size, size); 00829 } 00830 if (iconType == KIcon::Threshold && size != img->width()) 00831 { 00832 if ( abs(size-img->width())>iconThreshold ) 00833 *img = img->smoothScale(size, size); 00834 } 00835 if (group >= 0 && d->mpGroups[group].dblPixels) 00836 { 00837 *img = d->mpEffect.doublePixels(*img); 00838 } 00839 if (group >= 0) 00840 { 00841 *img = d->mpEffect.apply(*img, group, state); 00842 } 00843 00844 pix.convertFromImage(*img); 00845 00846 delete img; 00847 00848 if (favIconOverlay) 00849 { 00850 QPixmap favIcon(name, "PNG"); 00851 int x = pix.width() - favIcon.width() - 1, 00852 y = pix.height() - favIcon.height() - 1; 00853 if (pix.mask()) 00854 { 00855 QBitmap mask = *pix.mask(); 00856 QBitmap fmask; 00857 if (favIcon.mask()) 00858 fmask = *favIcon.mask(); 00859 else { 00860 // expensive, but works 00861 fmask = favIcon.createHeuristicMask(); 00862 } 00863 00864 bitBlt(&mask, x, y, &fmask, 00865 0, 0, favIcon.width(), favIcon.height(), 00866 favIcon.mask() ? Qt::OrROP : Qt::SetROP); 00867 pix.setMask(mask); 00868 } 00869 bitBlt(&pix, x, y, &favIcon); 00870 } 00871 00872 QPixmapCache::insert(key, pix); 00873 return pix; 00874 } 00875 00876 QImage *KIconLoader::loadOverlay(const QString &name, int size) const 00877 { 00878 QString key = name + '_' + QString::number(size); 00879 QImage *image = d->imgDict.find(key); 00880 if (image != 0L) 00881 return image; 00882 00883 KIcon icon = findMatchingIcon(name, size); 00884 if (!icon.isValid()) 00885 { 00886 kdDebug(264) << "Overlay " << name << "not found." << endl; 00887 return 0L; 00888 } 00889 image = new QImage(icon.path); 00890 // In some cases (since size in findMatchingIcon() is more a hint than a 00891 // constraint) image->size can be != size. If so perform rescaling. 00892 if ( size != image->width() ) 00893 *image = image->smoothScale( size, size ); 00894 d->imgDict.insert(key, image); 00895 return image; 00896 } 00897 00898 00899 00900 QMovie KIconLoader::loadMovie(const QString& name, KIcon::Group group, int size) const 00901 { 00902 QString file = moviePath( name, group, size ); 00903 if (file.isEmpty()) 00904 return QMovie(); 00905 int dirLen = file.findRev('/'); 00906 QString icon = iconPath(name, size ? -size : group, true); 00907 if (!icon.isEmpty() && file.left(dirLen) != icon.left(dirLen)) 00908 return QMovie(); 00909 return QMovie(file); 00910 } 00911 00912 QString KIconLoader::moviePath(const QString& name, KIcon::Group group, int size) const 00913 { 00914 if (!d->mpGroups) return QString::null; 00915 00916 if ( (group < -1 || group >= KIcon::LastGroup) && group != KIcon::User ) 00917 { 00918 kdDebug(264) << "Illegal icon group: " << group << endl; 00919 group = KIcon::Desktop; 00920 } 00921 if (size == 0 && group < 0) 00922 { 00923 kdDebug(264) << "Neither size nor group specified!" << endl; 00924 group = KIcon::Desktop; 00925 } 00926 00927 QString file = name + ".mng"; 00928 if (group == KIcon::User) 00929 { 00930 file = d->mpDirs->findResource("appicon", file); 00931 } 00932 else 00933 { 00934 if (size == 0) 00935 size = d->mpGroups[group].size; 00936 00937 KIcon icon; 00938 00939 for ( KIconThemeNode *themeNode = d->links.first() ; themeNode ; 00940 themeNode = d->links.next() ) 00941 { 00942 icon = themeNode->theme->iconPath(file, size, KIcon::MatchExact); 00943 if (icon.isValid()) 00944 break; 00945 } 00946 00947 if ( !icon.isValid() ) 00948 { 00949 for ( KIconThemeNode *themeNode = d->links.first() ; themeNode ; 00950 themeNode = d->links.next() ) 00951 { 00952 icon = themeNode->theme->iconPath(file, size, KIcon::MatchBest); 00953 if (icon.isValid()) 00954 break; 00955 } 00956 } 00957 00958 file = icon.isValid() ? icon.path : QString::null; 00959 } 00960 return file; 00961 } 00962 00963 00964 QStringList KIconLoader::loadAnimated(const QString& name, KIcon::Group group, int size) const 00965 { 00966 QStringList lst; 00967 00968 if (!d->mpGroups) return lst; 00969 00970 if ((group < -1) || (group >= KIcon::LastGroup)) 00971 { 00972 kdDebug(264) << "Illegal icon group: " << group << endl; 00973 group = KIcon::Desktop; 00974 } 00975 if ((size == 0) && (group < 0)) 00976 { 00977 kdDebug(264) << "Neither size nor group specified!" << endl; 00978 group = KIcon::Desktop; 00979 } 00980 00981 QString file = name + "/0001"; 00982 if (group == KIcon::User) 00983 { 00984 file = d->mpDirs->findResource("appicon", file + ".png"); 00985 } else 00986 { 00987 if (size == 0) 00988 size = d->mpGroups[group].size; 00989 KIcon icon = findMatchingIcon(file, size); 00990 file = icon.isValid() ? icon.path : QString::null; 00991 00992 } 00993 if (file.isEmpty()) 00994 return lst; 00995 00996 QString path = file.left(file.length()-8); 00997 DIR* dp = opendir( QFile::encodeName(path) ); 00998 if(!dp) 00999 return lst; 01000 01001 struct dirent* ep; 01002 while( ( ep = readdir( dp ) ) != 0L ) 01003 { 01004 QString fn(QFile::decodeName(ep->d_name)); 01005 if(!(fn.left(4)).toUInt()) 01006 continue; 01007 01008 lst += path + fn; 01009 } 01010 closedir ( dp ); 01011 lst.sort(); 01012 return lst; 01013 } 01014 01015 KIconTheme *KIconLoader::theme() const 01016 { 01017 if (d->mpThemeRoot) return d->mpThemeRoot->theme; 01018 return 0L; 01019 } 01020 01021 int KIconLoader::currentSize(KIcon::Group group) const 01022 { 01023 if (!d->mpGroups) return -1; 01024 01025 if (group < 0 || group >= KIcon::LastGroup) 01026 { 01027 kdDebug(264) << "Illegal icon group: " << group << endl; 01028 return -1; 01029 } 01030 return d->mpGroups[group].size; 01031 } 01032 01033 QStringList KIconLoader::queryIconsByDir( const QString& iconsDir ) const 01034 { 01035 QDir dir(iconsDir); 01036 QStringList lst = dir.entryList("*.png;*.xpm", QDir::Files); 01037 QStringList result; 01038 QStringList::ConstIterator it; 01039 for (it=lst.begin(); it!=lst.end(); ++it) 01040 result += iconsDir + "/" + *it; 01041 return result; 01042 } 01043 01044 QStringList KIconLoader::queryIconsByContext(int group_or_size, 01045 KIcon::Context context) const 01046 { 01047 QStringList result; 01048 if (group_or_size >= KIcon::LastGroup) 01049 { 01050 kdDebug(264) << "Illegal icon group: " << group_or_size << endl; 01051 return result; 01052 } 01053 int size; 01054 if (group_or_size >= 0) 01055 size = d->mpGroups[group_or_size].size; 01056 else 01057 size = -group_or_size; 01058 01059 for ( KIconThemeNode *themeNode = d->links.first() ; themeNode ; 01060 themeNode = d->links.next() ) 01061 themeNode->queryIconsByContext(&result, size, context); 01062 01063 // Eliminate duplicate entries (same icon in different directories) 01064 QString name; 01065 QStringList res2, entries; 01066 QStringList::ConstIterator it; 01067 for (it=result.begin(); it!=result.end(); ++it) 01068 { 01069 int n = (*it).findRev('/'); 01070 if (n == -1) 01071 name = *it; 01072 else 01073 name = (*it).mid(n+1); 01074 name = removeIconExtension(name); 01075 if (!entries.contains(name)) 01076 { 01077 entries += name; 01078 res2 += *it; 01079 } 01080 } 01081 return res2; 01082 01083 } 01084 01085 QStringList KIconLoader::queryIcons(int group_or_size, KIcon::Context context) const 01086 { 01087 QStringList result; 01088 if (group_or_size >= KIcon::LastGroup) 01089 { 01090 kdDebug(264) << "Illegal icon group: " << group_or_size << endl; 01091 return result; 01092 } 01093 int size; 01094 if (group_or_size >= 0) 01095 size = d->mpGroups[group_or_size].size; 01096 else 01097 size = -group_or_size; 01098 01099 for ( KIconThemeNode *themeNode = d->links.first() ; themeNode ; 01100 themeNode = d->links.next() ) 01101 themeNode->queryIcons(&result, size, context); 01102 01103 // Eliminate duplicate entries (same icon in different directories) 01104 QString name; 01105 QStringList res2, entries; 01106 QStringList::ConstIterator it; 01107 for (it=result.begin(); it!=result.end(); ++it) 01108 { 01109 int n = (*it).findRev('/'); 01110 if (n == -1) 01111 name = *it; 01112 else 01113 name = (*it).mid(n+1); 01114 name = removeIconExtension(name); 01115 if (!entries.contains(name)) 01116 { 01117 entries += name; 01118 res2 += *it; 01119 } 01120 } 01121 return res2; 01122 } 01123 01124 KIconEffect * KIconLoader::iconEffect() const 01125 { 01126 return &d->mpEffect; 01127 } 01128 01129 bool KIconLoader::alphaBlending(KIcon::Group group) const 01130 { 01131 if (!d->mpGroups) return false; 01132 01133 if (group < 0 || group >= KIcon::LastGroup) 01134 { 01135 kdDebug(264) << "Illegal icon group: " << group << endl; 01136 return false; 01137 } 01138 return d->mpGroups[group].alphaBlending; 01139 } 01140 01141 QIconSet KIconLoader::loadIconSet(const QString& name, KIcon::Group group, int size) 01142 { 01143 return loadIconSet( name, group, size, false ); 01144 } 01145 01146 /*** class for delayed icon loading for QIconSet ***/ 01147 01148 class KIconFactory 01149 : public QIconFactory 01150 { 01151 public: 01152 KIconFactory( const QString& iconName_P, KIcon::Group group_P, 01153 int size_P, KIconLoader* loader_P ); 01154 virtual QPixmap* createPixmap( const QIconSet&, QIconSet::Size, QIconSet::Mode, QIconSet::State ); 01155 private: 01156 QString iconName; 01157 KIcon::Group group; 01158 int size; 01159 KIconLoader* loader; 01160 }; 01161 01162 01163 QIconSet KIconLoader::loadIconSet( const QString& name, KIcon::Group g, int s, 01164 bool canReturnNull) 01165 { 01166 if ( !d->delayedLoading ) 01167 return loadIconSetNonDelayed( name, g, s, canReturnNull ); 01168 01169 if (g < -1 || g > 6) { 01170 kdDebug() << "KIconLoader::loadIconSet " << name << " " << (int)g << " " << s << endl; 01171 qDebug("%s", kdBacktrace().latin1()); 01172 abort(); 01173 } 01174 01175 if(canReturnNull) 01176 { // we need to find out if the icon actually exists 01177 QPixmap pm = loadIcon( name, g, s, KIcon::DefaultState, NULL, true ); 01178 if( pm.isNull()) 01179 return QIconSet(); 01180 01181 QIconSet ret( pm ); 01182 ret.installIconFactory( new KIconFactory( name, g, s, this )); 01183 return ret; 01184 } 01185 01186 QIconSet ret; 01187 ret.installIconFactory( new KIconFactory( name, g, s, this )); 01188 return ret; 01189 } 01190 01191 QIconSet KIconLoader::loadIconSetNonDelayed( const QString& name, 01192 KIcon::Group g, 01193 int s, bool canReturnNull ) 01194 { 01195 QIconSet iconset; 01196 QPixmap tmp = loadIcon(name, g, s, KIcon::ActiveState, NULL, canReturnNull); 01197 iconset.setPixmap( tmp, QIconSet::Small, QIconSet::Active ); 01198 // we don't use QIconSet's resizing anyway 01199 iconset.setPixmap( tmp, QIconSet::Large, QIconSet::Active ); 01200 tmp = loadIcon(name, g, s, KIcon::DisabledState, NULL, canReturnNull); 01201 iconset.setPixmap( tmp, QIconSet::Small, QIconSet::Disabled ); 01202 iconset.setPixmap( tmp, QIconSet::Large, QIconSet::Disabled ); 01203 tmp = loadIcon(name, g, s, KIcon::DefaultState, NULL, canReturnNull); 01204 iconset.setPixmap( tmp, QIconSet::Small, QIconSet::Normal ); 01205 iconset.setPixmap( tmp, QIconSet::Large, QIconSet::Normal ); 01206 return iconset; 01207 } 01208 01209 KIconFactory::KIconFactory( const QString& iconName_P, KIcon::Group group_P, 01210 int size_P, KIconLoader* loader_P ) 01211 : iconName( iconName_P ), group( group_P ), size( size_P ), loader( loader_P ) 01212 { 01213 setAutoDelete( true ); 01214 } 01215 01216 QPixmap* KIconFactory::createPixmap( const QIconSet&, QIconSet::Size, QIconSet::Mode mode_P, QIconSet::State ) 01217 { 01218 #ifdef KICONLOADER_CHECKS 01219 bool found = false; 01220 for( QValueList< KIconLoaderDebug >::Iterator it = kiconloaders->begin(); 01221 it != kiconloaders->end(); 01222 ++it ) 01223 { 01224 if( (*it).loader == loader ) 01225 { 01226 found = true; 01227 if( !(*it).valid ) 01228 { 01229 #ifdef NDEBUG 01230 loader = KGlobal::iconLoader(); 01231 iconName = "no_way_man_you_will_get_broken_icon"; 01232 #else 01233 kdWarning() << "Using already destroyed KIconLoader for loading an icon!" << endl; 01234 kdWarning() << "Appname:" << (*it).appname << ", icon:" << iconName << endl; 01235 kdWarning() << "Deleted at:" << endl; 01236 kdWarning() << (*it).delete_bt << endl; 01237 kdWarning() << "Current:" << endl; 01238 kdWarning() << kdBacktrace() << endl; 01239 abort(); 01240 return NULL; 01241 #endif 01242 } 01243 break; 01244 } 01245 } 01246 if( !found ) 01247 { 01248 #ifdef NDEBUG 01249 loader = KGlobal::iconLoader(); 01250 iconName = "no_way_man_you_will_get_broken_icon"; 01251 #else 01252 kdWarning() << "Using unknown KIconLoader for loading an icon!" << endl; 01253 kdWarning() << "Icon:" << iconName << endl; 01254 kdWarning() << kdBacktrace() << endl; 01255 abort(); 01256 return NULL; 01257 #endif 01258 } 01259 #endif 01260 // QIconSet::Mode to KIcon::State conversion 01261 static const KIcon::States tbl[] = { KIcon::DefaultState, KIcon::DisabledState, KIcon::ActiveState }; 01262 int state = KIcon::DefaultState; 01263 if( mode_P <= QIconSet::Active ) 01264 state = tbl[ mode_P ]; 01265 if( group >= 0 && state == KIcon::ActiveState ) 01266 { // active and normal icon are usually the same 01267 if( loader->iconEffect()->fingerprint(group, KIcon::ActiveState ) 01268 == loader->iconEffect()->fingerprint(group, KIcon::DefaultState )) 01269 return 0; // so let QIconSet simply duplicate it 01270 } 01271 // ignore passed size 01272 // ignore passed state (i.e. on/off) 01273 QPixmap pm = loader->loadIcon( iconName, group, size, state ); 01274 return new QPixmap( pm ); 01275 } 01276 01277 // Easy access functions 01278 01279 QPixmap DesktopIcon(const QString& name, int force_size, int state, 01280 KInstance *instance) 01281 { 01282 KIconLoader *loader = instance->iconLoader(); 01283 return loader->loadIcon(name, KIcon::Desktop, force_size, state); 01284 } 01285 01286 QPixmap DesktopIcon(const QString& name, KInstance *instance) 01287 { 01288 return DesktopIcon(name, 0, KIcon::DefaultState, instance); 01289 } 01290 01291 QIconSet DesktopIconSet(const QString& name, int force_size, KInstance *instance) 01292 { 01293 KIconLoader *loader = instance->iconLoader(); 01294 return loader->loadIconSet( name, KIcon::Desktop, force_size ); 01295 } 01296 01297 QPixmap BarIcon(const QString& name, int force_size, int state, 01298 KInstance *instance) 01299 { 01300 KIconLoader *loader = instance->iconLoader(); 01301 return loader->loadIcon(name, KIcon::Toolbar, force_size, state); 01302 } 01303 01304 QPixmap BarIcon(const QString& name, KInstance *instance) 01305 { 01306 return BarIcon(name, 0, KIcon::DefaultState, instance); 01307 } 01308 01309 QIconSet BarIconSet(const QString& name, int force_size, KInstance *instance) 01310 { 01311 KIconLoader *loader = instance->iconLoader(); 01312 return loader->loadIconSet( name, KIcon::Toolbar, force_size ); 01313 } 01314 01315 QPixmap SmallIcon(const QString& name, int force_size, int state, 01316 KInstance *instance) 01317 { 01318 KIconLoader *loader = instance->iconLoader(); 01319 return loader->loadIcon(name, KIcon::Small, force_size, state); 01320 } 01321 01322 QPixmap SmallIcon(const QString& name, KInstance *instance) 01323 { 01324 return SmallIcon(name, 0, KIcon::DefaultState, instance); 01325 } 01326 01327 QIconSet SmallIconSet(const QString& name, int force_size, KInstance *instance) 01328 { 01329 KIconLoader *loader = instance->iconLoader(); 01330 return loader->loadIconSet( name, KIcon::Small, force_size ); 01331 } 01332 01333 QPixmap MainBarIcon(const QString& name, int force_size, int state, 01334 KInstance *instance) 01335 { 01336 KIconLoader *loader = instance->iconLoader(); 01337 return loader->loadIcon(name, KIcon::MainToolbar, force_size, state); 01338 } 01339 01340 QPixmap MainBarIcon(const QString& name, KInstance *instance) 01341 { 01342 return MainBarIcon(name, 0, KIcon::DefaultState, instance); 01343 } 01344 01345 QIconSet MainBarIconSet(const QString& name, int force_size, KInstance *instance) 01346 { 01347 KIconLoader *loader = instance->iconLoader(); 01348 return loader->loadIconSet( name, KIcon::MainToolbar, force_size ); 01349 } 01350 01351 QPixmap UserIcon(const QString& name, int state, KInstance *instance) 01352 { 01353 KIconLoader *loader = instance->iconLoader(); 01354 return loader->loadIcon(name, KIcon::User, 0, state); 01355 } 01356 01357 QPixmap UserIcon(const QString& name, KInstance *instance) 01358 { 01359 return UserIcon(name, KIcon::DefaultState, instance); 01360 } 01361 01362 QIconSet UserIconSet(const QString& name, KInstance *instance) 01363 { 01364 KIconLoader *loader = instance->iconLoader(); 01365 return loader->loadIconSet( name, KIcon::User ); 01366 } 01367 01368 int IconSize(KIcon::Group group, KInstance *instance) 01369 { 01370 KIconLoader *loader = instance->iconLoader(); 01371 return loader->currentSize(group); 01372 } 01373 01374 QPixmap KIconLoader::unknown() 01375 { 01376 QPixmap pix; 01377 if ( QPixmapCache::find("unknown", pix) ) 01378 return pix; 01379 01380 QString path = KGlobal::iconLoader()->iconPath("unknown", KIcon::Small, true); 01381 if (path.isEmpty()) 01382 { 01383 kdDebug(264) << "Warning: Cannot find \"unknown\" icon." << endl; 01384 pix.resize(32,32); 01385 } else 01386 { 01387 pix.load(path); 01388 QPixmapCache::insert("unknown", pix); 01389 } 01390 01391 return pix; 01392 }
KDE Logo
This file is part of the documentation for kdecore Library Version 3.4.0.
Documentation copyright © 1996-2004 the KDE developers.
Generated on Thu Apr 14 00:03:29 2005 by doxygen 1.3.7 written by Dimitri van Heesch, © 1997-2003