# khtml Library API Documentation

Overview | Class Hierarchy | Classes | Classes (annotated) | Members | Source Files

# khtml_settings.cc

    00001 /* This file is part of the KDE project
    00002    Copyright (C) 1999 David Faure <faure@kde.org>
    00003
    00004    This library is free software; you can redistribute it and/or
    00005    modify it under the terms of the GNU Library General Public
    00006    License as published by the Free Software Foundation; either
    00007    version 2 of the License, or (at your option) any later version.
    00008
    00009    This library is distributed in the hope that it will be useful,
    00010    but WITHOUT ANY WARRANTY; without even the implied warranty of
    00011    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    00012    Library General Public License for more details.
    00013
    00014    You should have received a copy of the GNU Library General Public License
    00015    along with this library; see the file COPYING.LIB.  If not, write to
    00016    the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
    00017    Boston, MA 02111-1307, USA.
    00018 */
    00019
    00020 #include <qfontdatabase.h>
    00021 #include <qregexp.h>
    00022
    00023 #include "khtml_settings.h"
    00024 #include "khtmldefaults.h"
    00025 #include <kglobalsettings.h>
    00026 #include <kconfig.h>
    00027 #include <kglobal.h>
    00028 #include <klocale.h>
    00029 #include <kdebug.h>
    00030 #include <qregexp.h>
    00031
    00032
    00033 typedef QMap<QString,KHTMLSettings::KJavaScriptAdvice> PolicyMap;
    00034
    00035 class KHTMLSettingsPrivate
    00036 {
    00037 public:
    00038     bool m_bChangeCursor : 1;
    00039     bool m_bBackRightClick : 1;
    00040     bool m_underlineLink : 1;
    00041     bool m_hoverLink : 1;
    00042     bool enforceCharset : 1;
    00043     bool m_bAutoLoadImages : 1;
    00044     bool m_bEnableJava : 1;
    00045     bool m_bEnableJavaScript : 1;
    00046     bool m_bEnableJavaScriptDebug : 1;
    00047     bool m_bEnablePlugins : 1;
    00048     bool m_formCompletionEnabled : 1;
    00049     bool m_autoDelayedActionsEnabled: 1;
    00050
    00051     int m_fontSize;
    00052     int m_minFontSize;
    00053     int m_maxFormCompletionItems;
    00054     KHTMLSettings::KAnimationAdvice m_showAnimations;
    00055
    00056     QString m_encoding;
    00057     QString m_userSheet;
    00058
    00059     QColor m_textColor;
    00060     QColor m_linkColor;
    00061     QColor m_vLinkColor;
    00062
    00063     QMap<QString,KHTMLSettings::KJavaScriptAdvice> javaDomainPolicy;
    00064     QMap<QString,KHTMLSettings::KJavaScriptAdvice> javaScriptDomainPolicy;
    00065     QStringList fonts;
    00066     QStringList defaultFonts;
    00067 };
    00068
    00069
    00070 KHTMLSettings::KJavaScriptAdvice KHTMLSettings::strToAdvice(const QString& _str)
    00071 {
    00072   KJavaScriptAdvice ret = KJavaScriptDunno;
    00073
    00074   if (!_str)
    00075         ret = KJavaScriptDunno;
    00076
    00077   if (_str.lower() == QString::fromLatin1("accept"))
    00078         ret = KJavaScriptAccept;
    00079   else if (_str.lower() == QString::fromLatin1("reject"))
    00080         ret = KJavaScriptReject;
    00081
    00082   return ret;
    00083 }
    00084
    00085 const char* KHTMLSettings::adviceToStr(KJavaScriptAdvice _advice)
    00086 {
    00087     switch( _advice ) {
    00088     case KJavaScriptAccept: return I18N_NOOP("Accept");
    00089     case KJavaScriptReject: return I18N_NOOP("Reject");
    00090     default: return 0;
    00091     }
    00092         return 0;
    00093 }
    00094
    00095
    00096 void KHTMLSettings::splitDomainAdvice(const QString& configStr, QString &domain,
    00097                                       KJavaScriptAdvice &javaAdvice, KJavaScriptAdvice& javaScriptAdvice)
    00098 {
    00099     QString tmp(configStr);
    00100     int splitIndex = tmp.find(':');
    00101     if ( splitIndex == -1)
    00102     {
    00103         domain = configStr.lower();
    00104         javaAdvice = KJavaScriptDunno;
    00105         javaScriptAdvice = KJavaScriptDunno;
    00106     }
    00107     else
    00108     {
    00109         domain = tmp.left(splitIndex).lower();
    00110         QString adviceString = tmp.mid( splitIndex+1, tmp.length() );
    00111         int splitIndex2 = adviceString.find( ':' );
    00112         if( splitIndex2 == -1 ) {
    00113             // Java advice only
    00114             javaAdvice = strToAdvice( adviceString );
    00115             javaScriptAdvice = KJavaScriptDunno;
    00116         } else {
    00117             // Java and JavaScript advice
    00118             javaAdvice = strToAdvice( adviceString.left( splitIndex2 ) );
    00119             javaScriptAdvice = strToAdvice( adviceString.mid( splitIndex2+1,
    00120                                                               adviceString.length() ) );
    00121         }
    00122     }
    00123 }
    00124
    00125
    00126 KHTMLSettings::KHTMLSettings()
    00127 {
    00128   d = new KHTMLSettingsPrivate();
    00129   init();
    00130 }
    00131
    00132 KHTMLSettings::KHTMLSettings(const KHTMLSettings &other)
    00133 {
    00134   d = new KHTMLSettingsPrivate();
    00135   *d = *other.d;
    00136 }
    00137
    00138 KHTMLSettings::~KHTMLSettings()
    00139 {
    00140   delete d;
    00141 }
    00142
    00143 bool KHTMLSettings::changeCursor() const
    00144 {
    00145   return d->m_bChangeCursor;
    00146 }
    00147
    00148 bool KHTMLSettings::underlineLink() const
    00149 {
    00150   return d->m_underlineLink;
    00151 }
    00152
    00153 bool KHTMLSettings::hoverLink() const
    00154 {
    00155   return d->m_hoverLink;
    00156 }
    00157
    00158 void KHTMLSettings::init()
    00159 {
    00160   KConfig global( "khtmlrc", true, false );
    00161   init( &global, true );
    00162
    00163   KConfig *local = KGlobal::config();
    00164   if ( !local )
    00165     return;
    00166
    00167   init( local, false );
    00168 }
    00169
    00170 void KHTMLSettings::init( KConfig * config, bool reset )
    00171 {
    00172   QString group_save = config->group();
    00173   if (reset || config->hasGroup("MainView Settings"))
    00174   {
    00175     config->setGroup( "MainView Settings" );
    00176     if ( reset || config->hasKey( "BackRightClick" ) )
    00177         d->m_bBackRightClick = config->readBoolEntry( "BackRightClick", false );
    00178   }
    00179
    00180   if (reset || config->hasGroup("HTML Settings"))
    00181   {
    00182     config->setGroup( "HTML Settings" );
    00183     // Fonts and colors
    00184     if( reset ) {
    00185         d->defaultFonts = QStringList();
    00186         d->defaultFonts.append( config->readEntry( "StandardFont", KGlobalSettings::generalFont().family() ) );
    00187         d->defaultFonts.append( config->readEntry( "FixedFont", KGlobalSettings::fixedFont().family() ) );
    00188         d->defaultFonts.append( config->readEntry( "SerifFont", HTML_DEFAULT_VIEW_SERIF_FONT ) );
    00189         d->defaultFonts.append( config->readEntry( "SansSerifFont", HTML_DEFAULT_VIEW_SANSSERIF_FONT ) );
    00190         d->defaultFonts.append( config->readEntry( "CursiveFont", HTML_DEFAULT_VIEW_CURSIVE_FONT ) );
    00191         d->defaultFonts.append( config->readEntry( "FantasyFont", HTML_DEFAULT_VIEW_FANTASY_FONT ) );
    00192         d->defaultFonts.append( QString( "0" ) ); // font size adjustment
    00193     }
    00194
    00195     if ( reset || config->hasKey( "MinimumFontSize" ) )
    00196         d->m_minFontSize = config->readNumEntry( "MinimumFontSize", HTML_DEFAULT_MIN_FONT_SIZE );
    00197
    00198     if ( reset || config->hasKey( "MediumFontSize" ) )
    00199         d->m_fontSize = config->readNumEntry( "MediumFontSize", 10 );
    00200
    00201     d->fonts = config->readListEntry( "Fonts" );
    00202
    00203     if ( reset || config->hasKey( "DefaultEncoding" ) ) {
    00204         d->m_encoding = config->readEntry( "DefaultEncoding", "" );
    00205         if ( d->m_encoding.isEmpty() )
    00206             d->m_encoding = KGlobal::locale()->encoding();
    00207     }
    00208
    00209     if ( reset || config->hasKey( "EnforceDefaultCharset" ) )
    00210         d->enforceCharset = config->readBoolEntry( "EnforceDefaultCharset", false );
    00211
    00212     // Behaviour
    00213     if ( reset || config->hasKey( "ChangeCursor" ) )
    00214         d->m_bChangeCursor = config->readBoolEntry( "ChangeCursor", KDE_DEFAULT_CHANGECURSOR );
    00215
    00216     if ( reset || config->hasKey("UnderlineLinks") )
    00217         d->m_underlineLink = config->readBoolEntry( "UnderlineLinks", true );
    00218
    00219     if ( reset || config->hasKey( "HoverLinks" ) )
    00220     {
    00221         if ( ( d->m_hoverLink = config->readBoolEntry( "HoverLinks", false ) ) )
    00222             d->m_underlineLink = false;
    00223     }
    00224
    00225     // Other
    00226     if ( reset || config->hasKey( "AutoLoadImages" ) )
    00227       d->m_bAutoLoadImages = config->readBoolEntry( "AutoLoadImages", true );
    00228
    00229     if ( reset || config->hasKey( "ShowAnimations" ) )
    00230     {
    00231       QString value = config->readEntry( "ShowAnimations").lower();
    00232       if (value == "disabled")
    00233          d->m_showAnimations = KAnimationDisabled;
    00234       else if (value == "looponce")
    00235          d->m_showAnimations = KAnimationLoopOnce;
    00236       else
    00237          d->m_showAnimations = KAnimationEnabled;
    00238     }
    00239
    00240     if ( config->readBoolEntry( "UserStyleSheetEnabled", false ) == true ) {
    00241         if ( reset || config->hasKey( "UserStyleSheet" ) )
    00242             d->m_userSheet = config->readEntry( "UserStyleSheet", "" );
    00243     }
    00244
    00245     d->m_formCompletionEnabled = config->readBoolEntry("FormCompletion", true);
    00246     d->m_maxFormCompletionItems = config->readNumEntry("MaxFormCompletionItems", 10);
    00247     d->m_autoDelayedActionsEnabled = config->readBoolEntry ("AutoDelayedActions", true);
    00248   }
    00249
    00250   // Colors
    00251   if ( reset || config->hasGroup( "General" ) )
    00252   {
    00253     config->setGroup( "General" ); // group will be restored by cgs anyway
    00254     if ( reset || config->hasKey( "foreground" ) )
    00255       d->m_textColor = config->readColorEntry( "foreground", &HTML_DEFAULT_TXT_COLOR );
    00256
    00257     if ( reset || config->hasKey( "linkColor" ) )
    00258       d->m_linkColor = config->readColorEntry( "linkColor", &HTML_DEFAULT_LNK_COLOR );
    00259
    00260     if ( reset || config->hasKey( "visitedLinkColor" ) )
    00261       d->m_vLinkColor = config->readColorEntry( "visitedLinkColor", &HTML_DEFAULT_VLNK_COLOR);
    00262   }
    00263
    00264
    00265   if( reset || config->hasGroup( "Java/JavaScript Settings" ) )
    00266   {
    00267     config->setGroup( "Java/JavaScript Settings" );
    00268
    00269     // The global setting for Java
    00270     if ( reset || config->hasKey( "EnableJava" ) )
    00271       d->m_bEnableJava = config->readBoolEntry( "EnableJava", false );
    00272
    00273     // The global setting for JavaScript
    00274     if ( reset || config->hasKey( "EnableJavaScript" ) )
    00275       d->m_bEnableJavaScript = config->readBoolEntry( "EnableJavaScript", true );
    00276
    00277     // The global setting for JavaScript debugging
    00278     if ( reset || config->hasKey( "EnableJavaScriptDebug" ) )
    00279       d->m_bEnableJavaScriptDebug = config->readBoolEntry( "EnableJavaScriptDebug", false );
    00280
    00281     // The global setting for Plugins (there's no local setting yet)
    00282     if ( reset || config->hasKey( "EnablePlugins" ) )
    00283       d->m_bEnablePlugins = config->readBoolEntry( "EnablePlugins", true );
    00284
    00285     // The domain-specific settings.
    00286     bool check_old_java = true;
    00287     if( reset || config->hasKey( "JavaDomainSettings" ) )
    00288     {
    00289       check_old_java = false;
    00290       QStringList domainList = config->readListEntry( "JavaDomainSettings" );
    00291       for ( QStringList::ConstIterator it = domainList.begin();
    00292                 it != domainList.end(); ++it)
    00293       {
    00294         QString domain;
    00295         KJavaScriptAdvice javaAdvice;
    00296         KJavaScriptAdvice javaScriptAdvice;
    00297         splitDomainAdvice(*it, domain, javaAdvice, javaScriptAdvice);
    00298         d->javaDomainPolicy[domain] = javaAdvice;
    00299       }
    00300     }
    00301
    00302     bool check_old_ecma = true;
    00303     if( reset || config->hasKey( "ECMADomainSettings" ) )
    00304     {
    00305       check_old_ecma = false;
    00306       QStringList domainList = config->readListEntry( "ECMADomainSettings" );
    00307       for ( QStringList::ConstIterator it = domainList.begin();
    00308                 it != domainList.end(); ++it)
    00309       {
    00310         QString domain;
    00311         KJavaScriptAdvice javaAdvice;
    00312         KJavaScriptAdvice javaScriptAdvice;
    00313         splitDomainAdvice(*it, domain, javaAdvice, javaScriptAdvice);
    00314         d->javaScriptDomainPolicy[domain] = javaScriptAdvice;
    00315       }
    00316     }
    00317
    00318     if( reset || config->hasKey( "JavaScriptDomainAdvice" )
    00319              && ( check_old_java || check_old_ecma ) )
    00320     {
    00321       QStringList domainList = config->readListEntry( "JavaScriptDomainAdvice" );
    00322       for ( QStringList::ConstIterator it = domainList.begin();
    00323                 it != domainList.end(); ++it)
    00324       {
    00325         QString domain;
    00326         KJavaScriptAdvice javaAdvice;
    00327         KJavaScriptAdvice javaScriptAdvice;
    00328         splitDomainAdvice(*it, domain, javaAdvice, javaScriptAdvice);
    00329         if( check_old_java )
    00330           d->javaDomainPolicy[domain] = javaAdvice;
    00331         if( check_old_ecma )
    00332           d->javaScriptDomainPolicy[domain] = javaScriptAdvice;
    00333       }
    00334
    00335       //save all the settings into the new keywords if they don't exist
    00336       if( check_old_java )
    00337       {
    00338         QStringList domainConfig;
    00339         PolicyMap::Iterator it;
    00340         for( it = d->javaDomainPolicy.begin(); it != d->javaDomainPolicy.end(); ++it )
    00341         {
    00342           QCString javaPolicy = adviceToStr( it.data() );
    00343           QCString javaScriptPolicy = adviceToStr( KJavaScriptDunno );
    00344           domainConfig.append(QString::fromLatin1("%1:%2:%3").arg(it.key()).arg(javaPolicy).arg(javaScriptPolicy));
    00345         }
    00346         config->writeEntry( "JavaDomainSettings", domainConfig );
    00347       }
    00348
    00349       if( check_old_ecma )
    00350       {
    00351         QStringList domainConfig;
    00352         PolicyMap::Iterator it;
    00353         for( it = d->javaScriptDomainPolicy.begin(); it != d->javaScriptDomainPolicy.end(); ++it )
    00354         {
    00355           QCString javaPolicy = adviceToStr( KJavaScriptDunno );
    00356           QCString javaScriptPolicy = adviceToStr( it.data() );
    00357           domainConfig.append(QString::fromLatin1("%1:%2:%3").arg(it.key()).arg(javaPolicy).arg(javaScriptPolicy));
    00358         }
    00359         config->writeEntry( "ECMADomainSettings", domainConfig );
    00360       }
    00361     }
    00362   }
    00363   config->setGroup(group_save);
    00364 }
    00365
    00366
    00367 // Local helper for isJavaEnabled & isJavaScriptEnabled.
    00368 static bool lookup_hostname_policy(const QString& hostname,
    00369                                    const PolicyMap& policy,
    00370                                    const bool default_retval)
    00371 {
    00372   if (hostname.isEmpty()) {
    00373     return default_retval;
    00374   }
    00375
    00376   // First check whether there is a perfect match.
    00377   if( policy.contains( hostname ) ) {
    00378     // yes, use it (unless dunno)
    00379     KHTMLSettings::KJavaScriptAdvice adv = policy[ hostname ];
    00380     if( adv == KHTMLSettings::KJavaScriptReject ) {
    00381       return false;
    00382     } else if( adv == KHTMLSettings::KJavaScriptAccept ) {
    00383       return true;
    00384     }
    00385   }
    00386
    00387   // Now, check for partial match.  Chop host from the left until
    00388   // there's no dots left.
    00389   QString host_part = hostname;
    00390   int dot_idx = -1;
    00391   while( (dot_idx = host_part.find(QChar('.'))) >= 0 ) {
    00392     host_part.remove(0,dot_idx);
    00393     if( policy.contains( host_part ) ) {
    00394       KHTMLSettings::KJavaScriptAdvice adv = policy[ host_part ];
    00395       if( adv == KHTMLSettings::KJavaScriptReject ) {
    00396         return false;
    00397       } else if( adv == KHTMLSettings::KJavaScriptAccept ) {
    00398         return true;
    00399       }
    00400     }
    00401     // assert(host_part[0] == QChar('.'));
    00402     host_part.remove(0,1); // Chop off the dot.
    00403   }
    00404
    00405   // No domain-specific entry, or was dunno: use global setting
    00406   return default_retval;
    00407 }
    00408
    00409 bool KHTMLSettings::isBackRightClickEnabled()
    00410 {
    00411   return d->m_bBackRightClick;
    00412 }
    00413
    00414 bool KHTMLSettings::isJavaEnabled( const QString& hostname )
    00415 {
    00416   return lookup_hostname_policy(hostname.lower(), d->javaDomainPolicy, d->m_bEnableJava);
    00417 }
    00418
    00419 bool KHTMLSettings::isJavaScriptEnabled( const QString& hostname )
    00420 {
    00421   return lookup_hostname_policy(hostname.lower(), d->javaScriptDomainPolicy, d->m_bEnableJavaScript);
    00422 }
    00423
    00424 bool KHTMLSettings::isJavaScriptDebugEnabled( const QString& /*hostname*/ )
    00425 {
    00426   // debug setting is global for now, but could change in the future
    00427   return d->m_bEnableJavaScriptDebug;
    00428 }
    00429
    00430 bool KHTMLSettings::isPluginsEnabled( const QString& /* hostname */ )
    00431 {
    00432   // FIXME: hostname is ignored (dnaber, 2001-01-03)
    00433   return d->m_bEnablePlugins;
    00434 }
    00435
    00436 int KHTMLSettings::mediumFontSize() const
    00437 {
    00438     return d->m_fontSize;
    00439 }
    00440
    00441 int KHTMLSettings::minFontSize() const
    00442 {
    00443   return d->m_minFontSize;
    00444 }
    00445
    00446 QString KHTMLSettings::settingsToCSS() const
    00447 {
    00448     // lets start with the link properties
    00449     QString str = "a:link {\ncolor: ";
    00450     str += d->m_linkColor.name();
    00451     str += ";";
    00452     if(d->m_underlineLink)
    00453         str += "\ntext-decoration: underline;";
    00454
    00455     if( d->m_bChangeCursor )
    00456     {
    00457         str += "\ncursor: pointer;";
    00458         str += "\n}\ninput[type=image] { cursor: pointer;";
    00459     }
    00460     str += "\n}\n";
    00461     str += "a:visited {\ncolor: ";
    00462     str += d->m_vLinkColor.name();
    00463     str += ";";
    00464     if(d->m_underlineLink)
    00465         str += "\ntext-decoration: underline;";
    00466
    00467     if( d->m_bChangeCursor )
    00468         str += "\ncursor: pointer;";
    00469     str += "\n}\n";
    00470
    00471     if(d->m_hoverLink)
    00472         str += "a:link:hover, a:visited:hover { text-decoration: underline; }\n";
    00473
    00474     return str;
    00475 }
    00476
    00477 const QString &KHTMLSettings::availableFamilies()
    00478 {
    00479     if ( !avFamilies ) {
    00480         avFamilies = new QString;
    00481         QFontDatabase db;
    00482         QStringList families = db.families();
    00483         QStringList s;
    00484         QRegExp foundryExp(" \\[.+\\]");
    00485
    00486         //remove foundry info
    00487         for ( QStringList::Iterator f = families.begin(); f != families.end(); ++f ) {
    00488                 (*f).replace( foundryExp, "");
    00489                 if (!s.contains(*f))
    00490                         s << *f;
    00491         }
    00492         s.sort();
    00493
    00494         *avFamilies = s.join(",");
    00495     }
    00496
    00497   return *avFamilies;
    00498 }
    00499
    00500 QString KHTMLSettings::lookupFont(int i) const
    00501 {
    00502     QString font;
    00503     if (d->fonts.count() > (uint) i)
    00504        font = d->fonts[i];
    00505     if (font.isEmpty())
    00506         font = d->defaultFonts[i];
    00507     return font;
    00508 }
    00509
    00510 QString KHTMLSettings::stdFontName() const
    00511 {
    00512     return lookupFont(0);
    00513 }
    00514
    00515 QString KHTMLSettings::fixedFontName() const
    00516 {
    00517     return lookupFont(1);
    00518 }
    00519
    00520 QString KHTMLSettings::serifFontName() const
    00521 {
    00522     return lookupFont(2);
    00523 }
    00524
    00525 QString KHTMLSettings::sansSerifFontName() const
    00526 {
    00527     return lookupFont(3);
    00528 }
    00529
    00530 QString KHTMLSettings::cursiveFontName() const
    00531 {
    00532     return lookupFont(4);
    00533 }
    00534
    00535 QString KHTMLSettings::fantasyFontName() const
    00536 {
    00537     return lookupFont(5);
    00538 }
    00539
    00540 void KHTMLSettings::setStdFontName(const QString &n)
    00541 {
    00542     while(d->fonts.count() <= 0)
    00543         d->fonts.append(QString::null);
    00544     d->fonts[0] = n;
    00545 }
    00546
    00547 void KHTMLSettings::setFixedFontName(const QString &n)
    00548 {
    00549     while(d->fonts.count() <= 1)
    00550         d->fonts.append(QString::null);
    00551     d->fonts[1] = n;
    00552 }
    00553
    00554 QString KHTMLSettings::userStyleSheet() const
    00555 {
    00556     return d->m_userSheet;
    00557 }
    00558
    00559 bool KHTMLSettings::isFormCompletionEnabled() const
    00560 {
    00561   return d->m_formCompletionEnabled;
    00562 }
    00563
    00564 int KHTMLSettings::maxFormCompletionItems() const
    00565 {
    00566   return d->m_maxFormCompletionItems;
    00567 }
    00568
    00569 const QString &KHTMLSettings::encoding() const
    00570 {
    00571   return d->m_encoding;
    00572 }
    00573
    00574 const QColor& KHTMLSettings::textColor() const
    00575 {
    00576   return d->m_textColor;
    00577 }
    00578
    00579 const QColor& KHTMLSettings::linkColor() const
    00580 {
    00581   return d->m_linkColor;
    00582 }
    00583
    00584 const QColor& KHTMLSettings::vLinkColor() const
    00585 {
    00586   return d->m_vLinkColor;
    00587 }
    00588
    00589 bool KHTMLSettings::autoLoadImages() const
    00590 {
    00591   return d->m_bAutoLoadImages;
    00592 }
    00593
    00594 KHTMLSettings::KAnimationAdvice KHTMLSettings::showAnimations() const
    00595 {
    00596   return d->m_showAnimations;
    00597 }
    00598
    00599 bool KHTMLSettings::isAutoDelayedActionsEnabled() const
    00600 {
    00601   return d->m_autoDelayedActionsEnabled;
    00602 }

This file is part of the documentation for kdelibs Version 3.1.5.
Documentation copyright © 1996-2002 the KDE developers.
Generated on Wed Jan 28 13:34:08 2004 by doxygen 1.3.4 written by Dimitri van Heesch, © 1997-2001