kservice.cpp

00001 /*  This file is part of the KDE libraries
00002  *  Copyright (C) 1999 - 2001 Waldo Bastian <bastian@kde.org>
00003  *  Copyright (C) 1999        David Faure   <faure@kde.org>
00004  *
00005  *  This library is free software; you can redistribute it and/or
00006  *  modify it under the terms of the GNU Library General Public
00007  *  License version 2 as published by the Free Software Foundation;
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., 51 Franklin Street, Fifth Floor,
00017  *  Boston, MA 02110-1301, USA.
00018  **/
00019 
00020 // $Id$
00021 
00022 #include <config.h>
00023 
00024 #include "kservice.h"
00025 #include "kservice_p.h"
00026 
00027 #include <sys/types.h>
00028 #include <sys/stat.h>
00029 
00030 #include <stddef.h>
00031 #include <unistd.h>
00032 #include <stdlib.h>
00033 
00034 #include <qstring.h>
00035 #include <qfile.h>
00036 #include <qdir.h>
00037 #include <qtl.h>
00038 
00039 #include <ksimpleconfig.h>
00040 #include <kapplication.h>
00041 #include <kdebug.h>
00042 #include <kdesktopfile.h>
00043 #include <kglobal.h>
00044 #include <kiconloader.h>
00045 #include <klocale.h>
00046 #include <kconfigbase.h>
00047 #include <kstandarddirs.h>
00048 #include <dcopclient.h>
00049 
00050 #include "kservicefactory.h"
00051 #include "kservicetypefactory.h"
00052 #include "kservicetype.h"
00053 #include "kuserprofile.h"
00054 #include "ksycoca.h"
00055 
00056 class KService::KServicePrivate
00057 {
00058 public:
00059   QStringList categories;
00060   QString menuId;
00061 };
00062 
00063 KService::KService( const QString & _name, const QString &_exec, const QString &_icon)
00064  : KSycocaEntry( QString::null)
00065 {
00066   d = new KServicePrivate;
00067   m_bValid = true;
00068   m_bDeleted = false;
00069   m_strType = "Application";
00070   m_strName = _name;
00071   m_strExec = _exec;
00072   m_strIcon = _icon;
00073   m_bTerminal = false;
00074   m_bAllowAsDefault = true;
00075   m_initialPreference = 10;
00076 }
00077 
00078 
00079 KService::KService( const QString & _fullpath )
00080  : KSycocaEntry( _fullpath)
00081 {
00082   KDesktopFile config( _fullpath );
00083 
00084   init(&config);
00085 }
00086 
00087 KService::KService( KDesktopFile *config )
00088  : KSycocaEntry( config->fileName())
00089 {
00090   init(config);
00091 }
00092 
00093 void
00094 KService::init( KDesktopFile *config )
00095 {
00096   d = new KServicePrivate;
00097   m_bValid = true;
00098 
00099   bool absPath = !QDir::isRelativePath(entryPath());
00100   bool kde4application = config->fileName().startsWith("/usr/share/applications/kde4/");
00101 
00102   config->setDesktopGroup();
00103 
00104   QMap<QString, QString> entryMap = config->entryMap(config->group());
00105 
00106   entryMap.remove("Encoding"); // reserved as part of Desktop Entry Standard
00107   entryMap.remove("Version");  // reserved as part of Desktop Entry Standard
00108 
00109   m_bDeleted = config->readBoolEntry( "Hidden", false );
00110   entryMap.remove("Hidden");
00111   if (m_bDeleted)
00112   {
00113     //kdDebug() << "Hidden=true for " << entryPath() << endl;
00114     m_bValid = false;
00115     return;
00116   }
00117 
00118   m_strName = config->readEntry( "Name" );
00119   entryMap.remove("Name");
00120   if ( m_strName.isEmpty() )
00121   {
00122     if (config->readEntry( "Exec" ).isEmpty())
00123     {
00124       //kdWarning(7012) << "The desktop entry file " << entryPath()
00125       //              << " has no Name and no Exec" << endl;
00126       m_bValid = false;
00127       return;
00128     }
00129     // Try to make up a name.
00130     m_strName = entryPath();
00131     int i = m_strName.findRev('/');
00132     m_strName = m_strName.mid(i+1);
00133     i = m_strName.findRev('.');
00134     if (i != -1)
00135        m_strName = m_strName.left(i);
00136   }
00137 
00138   m_strType = config->readEntry( "Type" );
00139   entryMap.remove("Type");
00140   if ( m_strType.isEmpty() )
00141   {
00142     /*kdWarning(7012) << "The desktop entry file " << entryPath()
00143                     << " has no Type=... entry."
00144                     << " It should be \"Application\" or \"Service\"" << endl;
00145     m_bValid = false;
00146     return;*/
00147     m_strType = "Application";
00148   } else if ( m_strType != "Application" && m_strType != "Service" )
00149   {
00150     kdWarning(7012) << "The desktop entry file " << entryPath()
00151                     << " has Type=" << m_strType
00152                     << " instead of \"Application\" or \"Service\"" << endl;
00153     m_bValid = false;
00154     return;
00155   }
00156 
00157   // In case Try Exec is set, check if the application is available
00158   if (!config->tryExec()) {
00159       //kdDebug(7012) << "tryExec said false for " << entryPath() << endl;
00160       m_bDeleted = true;
00161       m_bValid = false;
00162       return;
00163   }
00164 
00165   QString resource = config->resource();
00166 
00167   if ( (m_strType == "Application") &&
00168        (!resource.isEmpty()) &&
00169        (resource != "apps") &&
00170        !absPath)
00171   {
00172     kdWarning(7012) << "The desktop entry file " << entryPath()
00173            << " has Type=" << m_strType << " but is located under \"" << resource
00174            << "\" instead of \"apps\"" << endl;
00175     m_bValid = false;
00176     return;
00177   }
00178 
00179   if ( (m_strType == "Service") &&
00180        (!resource.isEmpty()) &&
00181        (resource != "services") &&
00182        !absPath)
00183   {
00184     kdWarning(7012) << "The desktop entry file " << entryPath()
00185            << " has Type=" << m_strType << " but is located under \"" << resource
00186            << "\" instead of \"services\"" << endl;
00187     m_bValid = false;
00188     return;
00189   }
00190 
00191   QString name = entryPath();
00192   int pos = name.findRev('/');
00193   if (pos != -1)
00194      name = name.mid(pos+1);
00195   pos = name.find('.');
00196   if (pos != -1)
00197      name = name.left(pos);
00198 
00199   m_strExec = config->readPathEntry( "Exec" );
00200   if (kde4application && !m_strExec.startsWith("/")) {
00201     m_strExec = "/usr/lib/kde4/bin/"+m_strExec;
00202   } else if (config->readBoolEntry("X-KDE-SubstituteUID")) {
00203     int space = m_strExec.find(" ");
00204     if (space==-1)
00205       m_strExec = KStandardDirs::findExe(m_strExec);
00206     else {
00207       const QString command = m_strExec.left(space);
00208       m_strExec.replace(command,KStandardDirs::findExe(command));
00209     }
00210   }
00211 
00212   entryMap.remove("Exec");
00213 
00214   m_strIcon = config->readEntry( "Icon", "unknown" );
00215   if (kde4application) {
00216     if (QFile::exists("/usr/lib/kde4/share/icons/oxygen/22x22/apps/" + m_strIcon + ".png")) {
00217       m_strIcon = "/usr/lib/kde4/share/icons/oxygen/22x22/apps/" + m_strIcon + ".png";
00218     } else if (QFile::exists("/usr/lib/kde4/share/icons/hicolor/22x22/apps/" + m_strIcon + ".png")) {
00219       m_strIcon = "/usr/lib/kde4/share/icons/hicolor/22x22/apps/" + m_strIcon + ".png";
00220     }
00221   }
00222   entryMap.remove("Icon");
00223   m_bTerminal = (config->readBoolEntry( "Terminal" )); // should be a property IMHO
00224   entryMap.remove("Terminal");
00225   m_strTerminalOptions = config->readEntry( "TerminalOptions" ); // should be a property IMHO
00226   entryMap.remove("TerminalOptions");
00227   m_strPath = config->readPathEntry( "Path" );
00228   entryMap.remove("Path");
00229   m_strComment = config->readEntry( "Comment" );
00230   entryMap.remove("Comment");
00231   m_strGenName = config->readEntry( "GenericName" );
00232   if (kde4application) {
00233     m_strGenName += " (KDE4)";
00234   }
00235   entryMap.remove("GenericName");
00236   QString untranslatedGenericName = config->readEntryUntranslated( "GenericName" );
00237   if (!untranslatedGenericName.isEmpty())
00238     entryMap.insert("UntranslatedGenericName", untranslatedGenericName);
00239 
00240   m_lstKeywords = config->readListEntry("Keywords");
00241   entryMap.remove("Keywords");
00242   d->categories = config->readListEntry("Categories", ';');
00243   entryMap.remove("Categories");
00244   m_strLibrary = config->readEntry( "X-KDE-Library" );
00245   entryMap.remove("X-KDE-Library");
00246   m_strInit = config->readEntry("X-KDE-Init" );
00247   entryMap.remove("X-KDE-Init");
00248 
00249   m_lstServiceTypes = config->readListEntry( "ServiceTypes" );
00250   entryMap.remove("ServiceTypes");
00251   // For compatibility with KDE 1.x
00252   if (!kde4application)
00253      m_lstServiceTypes += config->readListEntry( "MimeType", ';' );
00254   entryMap.remove("MimeType");
00255 
00256   if ( m_strType == "Application" && !m_lstServiceTypes.contains("Application") )
00257     // Applications implement the service type "Application" ;-)
00258     m_lstServiceTypes += "Application";
00259 
00260   QString dcopServiceType = config->readEntry("X-DCOP-ServiceType").lower();
00261   entryMap.remove("X-DCOP-ServiceType");
00262   if (dcopServiceType == "unique")
00263      m_DCOPServiceType = DCOP_Unique;
00264   else if (dcopServiceType == "multi")
00265      m_DCOPServiceType = DCOP_Multi;
00266   else if (dcopServiceType == "wait")
00267      m_DCOPServiceType = DCOP_Wait;
00268   else
00269      m_DCOPServiceType = DCOP_None;
00270 
00271   m_strDesktopEntryName = name.lower();
00272   if (kde4application)
00273      m_strDesktopEntryName = "kde4-" + m_strDesktopEntryName;
00274 
00275   m_bAllowAsDefault = config->readBoolEntry( "AllowDefault", true );
00276   entryMap.remove("AllowDefault");
00277 
00278   m_initialPreference = config->readNumEntry( "InitialPreference", 1 );
00279   entryMap.remove("InitialPreference");
00280 
00281   // Store all additional entries in the property map.
00282   // A QMap<QString,QString> would be easier for this but we can't
00283   // brake BC, so we have to store it in m_mapProps.
00284 //  qWarning("Path = %s", entryPath().latin1());
00285   QMap<QString,QString>::ConstIterator it = entryMap.begin();
00286   for( ; it != entryMap.end();++it)
00287   {
00288      //qDebug("   Key = %s Data = %s", it.key().latin1(), it.data().latin1());
00289       QString key = it.key();
00290       if (kde4application && key=="OnlyShowIn" && it.data()=="KDE;")
00291          key = "NotShowIn";
00292       m_mapProps.insert( key, QVariant( it.data()));
00293   }
00294 }
00295 
00296 KService::KService( QDataStream& _str, int offset ) : KSycocaEntry( _str, offset )
00297 {
00298   d = new KServicePrivate;
00299   load( _str );
00300 }
00301 
00302 KService::~KService()
00303 {
00304   //debug("KService::~KService()");
00305   delete d;
00306 }
00307 
00308 QPixmap KService::pixmap( KIcon::Group _group, int _force_size, int _state, QString * _path ) const
00309 {
00310   KIconLoader *iconLoader=KGlobal::iconLoader();
00311   if (!iconLoader->extraDesktopThemesAdded())
00312   {
00313       QPixmap pixmap=iconLoader->loadIcon( m_strIcon, _group, _force_size, _state, _path, true );
00314       if (!pixmap.isNull() ) return pixmap;
00315 
00316       iconLoader->addExtraDesktopThemes();
00317   }
00318 
00319   return iconLoader->loadIcon( m_strIcon, _group, _force_size, _state, _path );
00320 }
00321 
00322 void KService::load( QDataStream& s )
00323 {
00324   // dummies are here because of fields that were removed, to keep bin compat.
00325   // Feel free to re-use, but fields for Applications only (not generic services)
00326   // should rather be added to application.desktop
00327   Q_INT8 def, term, dummy1, dummy2;
00328   Q_INT8 dst, initpref;
00329   QString dummyStr1, dummyStr2;
00330   int dummyI1, dummyI2;
00331   Q_UINT32 dummyUI32;
00332 
00333   // WARNING: IN KDE 3.x THIS NEEDS TO REMAIN COMPATIBLE WITH KDE 2.x!
00334   // !! This data structure should remain binary compatible at all times !!
00335   // You may add new fields at the end. Make sure to update the version
00336   // number in ksycoca.h
00337   s >> m_strType >> m_strName >> m_strExec >> m_strIcon
00338     >> term >> m_strTerminalOptions
00339     >> m_strPath >> m_strComment >> m_lstServiceTypes >> def >> m_mapProps
00340     >> m_strLibrary >> dummyI1 >> dummyI2
00341     >> dst
00342     >> m_strDesktopEntryName
00343     >> dummy1 >> dummyStr1 >> initpref >> dummyStr2 >> dummy2
00344     >> m_lstKeywords >> m_strInit >> dummyUI32 >> m_strGenName
00345     >> d->categories >> d->menuId;
00346 
00347   m_bAllowAsDefault = def;
00348   m_bTerminal = term;
00349   m_DCOPServiceType = (DCOPServiceType_t) dst;
00350   m_initialPreference = initpref;
00351 
00352   m_bValid = true;
00353 }
00354 
00355 void KService::save( QDataStream& s )
00356 {
00357   KSycocaEntry::save( s );
00358   Q_INT8 def = m_bAllowAsDefault, initpref = m_initialPreference;
00359   Q_INT8 term = m_bTerminal;
00360   Q_INT8 dst = (Q_INT8) m_DCOPServiceType;
00361   Q_INT8 dummy1 = 0, dummy2 = 0; // see ::load
00362   QString dummyStr1, dummyStr2;
00363   int dummyI1 = 0, dummyI2 = 0;
00364   Q_UINT32 dummyUI32 = 0;
00365 
00366   // WARNING: IN KDE 3.x THIS NEEDS TO REMAIN COMPATIBLE WITH KDE 2.x!
00367   // !! This data structure should remain binary compatible at all times !!
00368   // You may add new fields at the end. Make sure to update the version
00369   // number in ksycoca.h
00370   s << m_strType << m_strName << m_strExec << m_strIcon
00371     << term << m_strTerminalOptions
00372     << m_strPath << m_strComment << m_lstServiceTypes << def << m_mapProps
00373     << m_strLibrary << dummyI1 << dummyI2
00374     << dst
00375     << m_strDesktopEntryName
00376     << dummy1 << dummyStr1 << initpref << dummyStr2 << dummy2
00377     << m_lstKeywords << m_strInit << dummyUI32 << m_strGenName
00378     << d->categories << d->menuId;
00379 }
00380 
00381 bool KService::hasServiceType( const QString& _servicetype ) const
00382 {
00383   if (!m_bValid) return false; // safety test
00384 
00385   //kdDebug(7012) << "Testing " << m_strDesktopEntryName << " for " << _servicetype << endl;
00386 
00387   KMimeType::Ptr mimePtr = KMimeType::mimeType( _servicetype );
00388   if ( mimePtr && mimePtr == KMimeType::defaultMimeTypePtr() )
00389       mimePtr = 0;
00390 
00391   bool isNumber;
00392   // For each service type we are associated with, if it doesn't
00393   // match then we try its parent service types.
00394   QStringList::ConstIterator it = m_lstServiceTypes.begin();
00395   for( ; it != m_lstServiceTypes.end(); ++it )
00396   {
00397       (*it).toInt(&isNumber);
00398       if (isNumber)
00399          continue;
00400       //kdDebug(7012) << "    has " << (*it) << endl;
00401       KServiceType::Ptr ptr = KServiceType::serviceType( *it );
00402       if ( ptr && ptr->inherits( _servicetype ) )
00403           return true;
00404 
00405       // The mimetype inheritance ("is also") works the other way.
00406       // e.g. if we're looking for a handler for mimePtr==smb-workgroup
00407       // then a handler for inode/directory is ok.
00408       if ( mimePtr && mimePtr->is( *it ) )
00409           return true;
00410   }
00411   return false;
00412 }
00413 
00414 int KService::initialPreferenceForMimeType( const QString& mimeType ) const
00415 {
00416   if (!m_bValid) return 0; // safety test
00417 
00418   bool isNumber;
00419 
00420   // For each service type we are associated with
00421   QStringList::ConstIterator it = m_lstServiceTypes.begin();
00422   for( ; it != m_lstServiceTypes.end(); ++it )
00423   {
00424       (*it).toInt(&isNumber);
00425       if (isNumber)
00426          continue;
00427       //kdDebug(7012) << "    has " << (*it) << endl;
00428       KServiceType::Ptr ptr = KServiceType::serviceType( *it );
00429       if ( !ptr || !ptr->inherits( mimeType ) )
00430           continue;
00431 
00432       int initalPreference = m_initialPreference;
00433       ++it;
00434       if (it != m_lstServiceTypes.end())
00435       {
00436          int i = (*it).toInt(&isNumber);
00437          if (isNumber)
00438             initalPreference = i;
00439       }
00440       return initalPreference;
00441   }
00442 
00443   KMimeType::Ptr mimePtr = KMimeType::mimeType( mimeType );
00444   if ( mimePtr && mimePtr == KMimeType::defaultMimeTypePtr() )
00445       mimePtr = 0;
00446 
00447   // Try its parent service types.
00448   it = m_lstServiceTypes.begin();
00449   for( ; it != m_lstServiceTypes.end(); ++it )
00450   {
00451       (*it).toInt(&isNumber);
00452       if (isNumber)
00453          continue;
00454 
00455       // The mimetype inheritance ("is also") works the other way.
00456       // e.g. if we're looking for a handler for mimePtr==smb-workgroup
00457       // then a handler for inode/directory is ok.
00458       if ( !mimePtr || !mimePtr->is( *it ) )
00459           continue;
00460 
00461       int initalPreference = m_initialPreference;
00462       ++it;
00463       if (it != m_lstServiceTypes.end())
00464       {
00465          int i = (*it).toInt(&isNumber);
00466          if (isNumber)
00467             initalPreference = i;
00468       }
00469       return initalPreference;
00470   }
00471   return 0;
00472 }
00473 
00474 class KServiceReadProperty : public KConfigBase
00475 {
00476 public:
00477    KServiceReadProperty(const QString &_key, const QCString &_value)
00478     : key(_key), value(_value) { }
00479 
00480    bool internalHasGroup(const QCString &) const { /*qDebug("hasGroup(const QCString &)");*/ return false; }
00481 
00482    QStringList groupList() const { return QStringList(); }
00483 
00484    QMap<QString,QString> entryMap(const QString &group) const
00485       { Q_UNUSED(group); return QMap<QString,QString>(); }
00486 
00487    void reparseConfiguration() { }
00488 
00489    KEntryMap internalEntryMap( const QString &pGroup) const 
00490    { Q_UNUSED(pGroup); return KEntryMap(); }
00491 
00492    KEntryMap internalEntryMap() const { return KEntryMap(); }
00493 
00494    void putData(const KEntryKey &_key, const KEntry& _data, bool _checkGroup) 
00495    { Q_UNUSED(_key); Q_UNUSED(_data); Q_UNUSED(_checkGroup); }
00496 
00497    KEntry lookupData(const KEntryKey &_key) const
00498    { Q_UNUSED(_key); KEntry entry; entry.mValue = value; return entry; }
00499 protected:
00500    QString key;
00501    QCString value;
00502 };
00503 
00504 QVariant KService::property( const QString& _name) const
00505 {
00506    return property( _name, QVariant::Invalid);
00507 }
00508 
00509 // Return a string QVariant if string isn't null, and invalid variant otherwise
00510 // (the variant must be invalid if the field isn't in the .desktop file)
00511 // This allows trader queries like "exist Library" to work.
00512 static QVariant makeStringVariant( const QString& string )
00513 {
00514     // Using isEmpty here would be wrong.
00515     // Empty is "specified but empty", null is "not specified" (in the .desktop file)
00516     return string.isNull() ? QVariant() : QVariant( string );
00517 }
00518 
00519 QVariant KService::property( const QString& _name, QVariant::Type t ) const
00520 {
00521   if ( _name == "Type" )
00522     return QVariant( m_strType ); // can't be null
00523   else if ( _name == "Name" )
00524     return QVariant( m_strName ); // can't be null
00525   else if ( _name == "Exec" )
00526     return makeStringVariant( m_strExec );
00527   else if ( _name == "Icon" )
00528     return makeStringVariant( m_strIcon );
00529   else if ( _name == "Terminal" )
00530     return QVariant( static_cast<int>(m_bTerminal) );
00531   else if ( _name == "TerminalOptions" )
00532     return makeStringVariant( m_strTerminalOptions );
00533   else if ( _name == "Path" )
00534     return makeStringVariant( m_strPath );
00535   else if ( _name == "Comment" )
00536     return makeStringVariant( m_strComment );
00537   else if ( _name == "GenericName" )
00538     return makeStringVariant( m_strGenName );
00539   else if ( _name == "ServiceTypes" )
00540     return QVariant( m_lstServiceTypes );
00541   else if ( _name == "AllowAsDefault" )
00542     return QVariant( static_cast<int>(m_bAllowAsDefault) );
00543   else if ( _name == "InitialPreference" )
00544     return QVariant( m_initialPreference );
00545   else if ( _name == "Library" )
00546     return makeStringVariant( m_strLibrary );
00547   else if ( _name == "DesktopEntryPath" ) // can't be null
00548     return QVariant( entryPath() );
00549   else if ( _name == "DesktopEntryName")
00550     return QVariant( m_strDesktopEntryName ); // can't be null
00551   else if ( _name == "Categories")
00552     return QVariant( d->categories );
00553   else if ( _name == "Keywords")
00554     return QVariant( m_lstKeywords );
00555 
00556   // Ok we need to convert the property from a QString to its real type.
00557   // Maybe the caller helped us.
00558   if (t == QVariant::Invalid)
00559   {
00560     // No luck, let's ask KServiceTypeFactory what the type of this property
00561     // is supposed to be.
00562     t = KServiceTypeFactory::self()->findPropertyTypeByName(_name);
00563     if (t == QVariant::Invalid)
00564     {
00565       kdDebug(7012) << "Request for unknown property '" << _name << "'\n";
00566       return QVariant(); // Unknown property: Invalid variant.
00567     }
00568   }
00569 
00570   // Then we use a homebuild class based on KConfigBase to convert the QString.
00571   // For some often used property types we do the conversion ourselves.
00572   QMap<QString,QVariant>::ConstIterator it = m_mapProps.find( _name );
00573   if ( (it == m_mapProps.end()) || (!it.data().isValid()))
00574   {
00575      //kdDebug(7012) << "Property not found " << _name << endl;
00576      return QVariant(); // No property set.
00577   }
00578 
00579   switch(t)
00580   {
00581     case QVariant::String:
00582         return it.data();
00583     case QVariant::Bool:
00584     case QVariant::Int:
00585         {
00586            QString aValue = it.data().toString();
00587            int val = 0;
00588            if (aValue == "true" || aValue == "on" || aValue == "yes")
00589               val = 1;
00590            else
00591            {
00592               bool bOK;
00593               val = aValue.toInt( &bOK );
00594               if( !bOK )
00595                  val = 0;
00596            }
00597            if (t == QVariant::Bool)
00598            {
00599                return QVariant((bool)val, 1);
00600            }
00601            return QVariant(val);
00602         }
00603     default:
00604         // All others
00605         KServiceReadProperty ksrp(_name, it.data().toString().utf8());
00606         return ksrp.readPropertyEntry(_name, t);
00607   }
00608 }
00609 
00610 QStringList KService::propertyNames() const
00611 {
00612   QStringList res;
00613 
00614   QMap<QString,QVariant>::ConstIterator it = m_mapProps.begin();
00615   for( ; it != m_mapProps.end(); ++it )
00616     res.append( it.key() );
00617 
00618   res.append( "Type" );
00619   res.append( "Name" );
00620   res.append( "Comment" );
00621   res.append( "GenericName" );
00622   res.append( "Icon" );
00623   res.append( "Exec" );
00624   res.append( "Terminal" );
00625   res.append( "TerminalOptions" );
00626   res.append( "Path" );
00627   res.append( "ServiceTypes" );
00628   res.append( "AllowAsDefault" );
00629   res.append( "InitialPreference" );
00630   res.append( "Library" );
00631   res.append( "DesktopEntryPath" );
00632   res.append( "DesktopEntryName" );
00633   res.append( "Keywords" );
00634   res.append( "Categories" );
00635 
00636   return res;
00637 }
00638 
00639 KService::List KService::allServices()
00640 {
00641   return KServiceFactory::self()->allServices();
00642 }
00643 
00644 KService::Ptr KService::serviceByName( const QString& _name )
00645 {
00646   KService * s = KServiceFactory::self()->findServiceByName( _name );
00647   return KService::Ptr( s );
00648 }
00649 
00650 KService::Ptr KService::serviceByDesktopPath( const QString& _name )
00651 {
00652   KService * s = KServiceFactory::self()->findServiceByDesktopPath( _name );
00653   return KService::Ptr( s );
00654 }
00655 
00656 KService::Ptr KService::serviceByDesktopName( const QString& _name )
00657 {
00658   KService * s = KServiceFactory::self()->findServiceByDesktopName( _name.lower() );
00659   if (!s && !_name.startsWith("kde-"))
00660      s = KServiceFactory::self()->findServiceByDesktopName( "kde-"+_name.lower() );
00661   return KService::Ptr( s );
00662 }
00663 
00664 KService::Ptr KService::serviceByMenuId( const QString& _name )
00665 {
00666   KService * s = KServiceFactory::self()->findServiceByMenuId( _name );
00667   return KService::Ptr( s );
00668 }
00669 
00670 KService::Ptr KService::serviceByStorageId( const QString& _storageId )
00671 {
00672   KService::Ptr service = KService::serviceByMenuId( _storageId );
00673   if (service)
00674      return service;
00675 
00676   service = KService::serviceByDesktopPath(_storageId);
00677   if (service)
00678      return service;
00679 
00680   if (!QDir::isRelativePath(_storageId) && QFile::exists(_storageId))
00681      return new KService(_storageId);
00682 
00683   QString tmp = _storageId;
00684   tmp = tmp.mid(tmp.findRev('/')+1); // Strip dir
00685 
00686   if (tmp.endsWith(".desktop"))
00687      tmp.truncate(tmp.length()-8);
00688 
00689   if (tmp.endsWith(".kdelnk"))
00690      tmp.truncate(tmp.length()-7);
00691 
00692   service = KService::serviceByDesktopName(tmp);
00693 
00694   return service;
00695 }
00696 
00697 KService::List KService::allInitServices()
00698 {
00699   return KServiceFactory::self()->allInitServices();
00700 }
00701 
00702 bool KService::substituteUid() const {
00703   QVariant v = property("X-KDE-SubstituteUID", QVariant::Bool);
00704   return v.isValid() && v.toBool();
00705 }
00706 
00707 QString KService::username() const {
00708   // See also KDesktopFile::tryExec()
00709   QString user;
00710   QVariant v = property("X-KDE-Username", QVariant::String);
00711   user = v.isValid() ? v.toString() : QString::null;
00712   if (user.isEmpty())
00713      user = ::getenv("ADMIN_ACCOUNT");
00714   if (user.isEmpty())
00715      user = "root";
00716   return user;
00717 }
00718 
00719 bool KService::noDisplay() const {
00720   QMap<QString,QVariant>::ConstIterator it = m_mapProps.find( "NoDisplay" );
00721   if ( (it != m_mapProps.end()) && (it.data().isValid()))
00722   {
00723      QString aValue = it.data().toString().lower();
00724      if (aValue == "true" || aValue == "on" || aValue == "yes")
00725         return true;
00726   }
00727 
00728   it = m_mapProps.find( "OnlyShowIn" );
00729   if ( (it != m_mapProps.end()) && (it.data().isValid()))
00730   {
00731      QString aValue = it.data().toString();
00732      QStringList aList = QStringList::split(';', aValue);
00733      if (!aList.contains("KDE"))
00734         return true;
00735   }
00736 
00737   it = m_mapProps.find( "NotShowIn" );
00738   if ( (it != m_mapProps.end()) && (it.data().isValid()))
00739   {
00740      QString aValue = it.data().toString();
00741      QStringList aList = QStringList::split(';', aValue);
00742      if (aList.contains("KDE"))
00743         return true;
00744   }
00745   
00746   if (!kapp->authorizeControlModule(d->menuId))
00747      return true;
00748   
00749   return false;
00750 }
00751 
00752 QString KService::untranslatedGenericName() const {
00753   QVariant v = property("UntranslatedGenericName", QVariant::String);
00754   return v.isValid() ? v.toString() : QString::null;
00755 }
00756 
00757 QString KService::parentApp() const {
00758   QMap<QString,QVariant>::ConstIterator it = m_mapProps.find( "X-KDE-ParentApp" );
00759   if ( (it == m_mapProps.end()) || (!it.data().isValid()))
00760   {
00761      return QString::null;
00762   }
00763 
00764   return it.data().toString();
00765 }
00766 
00767 bool KService::allowMultipleFiles() const {
00768   // Can we pass multiple files on the command line or do we have to start the application for every single file ?
00769   if ( m_strExec.find( "%F" ) != -1 || m_strExec.find( "%U" ) != -1 ||
00770        m_strExec.find( "%N" ) != -1 || m_strExec.find( "%D" ) != -1 )
00771     return true;
00772   else
00773     return false;
00774 }
00775 
00776 QStringList KService::categories() const
00777 {
00778   return d->categories;
00779 }
00780 
00781 QString KService::menuId() const
00782 {
00783   return d->menuId;
00784 }
00785 
00786 void KService::setMenuId(const QString &menuId)
00787 {
00788   d->menuId = menuId;
00789 }
00790 
00791 QString KService::storageId() const
00792 {
00793   if (!d->menuId.isEmpty())
00794      return d->menuId;
00795   return entryPath();
00796 }
00797 
00798 QString KService::locateLocal()
00799 {
00800   if (d->menuId.isEmpty() || desktopEntryPath().startsWith(".hidden") ||
00801       (QDir::isRelativePath(desktopEntryPath()) && d->categories.isEmpty()))
00802      return KDesktopFile::locateLocal(desktopEntryPath());
00803 
00804   return ::locateLocal("xdgdata-apps", d->menuId);
00805 }
00806 
00807 QString KService::newServicePath(bool showInMenu, const QString &suggestedName,
00808                                 QString *menuId, const QStringList *reservedMenuIds)
00809 {
00810    QString base = suggestedName;
00811    if (!showInMenu)
00812      base.prepend("kde-");
00813 
00814    QString result;
00815    for(int i = 1; true; i++)
00816    {
00817       if (i == 1)
00818          result = base + ".desktop";
00819       else
00820          result = base + QString("-%1.desktop").arg(i);
00821 
00822       if (reservedMenuIds && reservedMenuIds->contains(result))
00823          continue;
00824 
00825       // Lookup service by menu-id
00826       KService::Ptr s = serviceByMenuId(result);
00827       if (s)
00828          continue;
00829 
00830       if (showInMenu)
00831       {
00832          if (!locate("xdgdata-apps", result).isEmpty())
00833             continue;
00834       }
00835       else
00836       {
00837          QString file = result.mid(4); // Strip "kde-"
00838          if (!locate("apps", ".hidden/"+file).isEmpty())
00839             continue;
00840       }
00841 
00842       break;
00843    }
00844    if (menuId)
00845       *menuId = result;
00846 
00847    if (showInMenu)
00848    {
00849        return ::locateLocal("xdgdata-apps", result);
00850    }
00851    else
00852    {
00853        QString file = result.mid(4); // Strip "kde-"
00854        return ::locateLocal("apps", ".hidden/"+file);
00855    }
00856 }
00857 
00858 
00859 void KService::virtual_hook( int id, void* data )
00860 { KSycocaEntry::virtual_hook( id, data ); }
00861 
00862 
00863 void KService::rebuildKSycoca(QWidget *parent)
00864 {
00865   KServiceProgressDialog dlg(parent, "ksycoca_progress",
00866                       i18n("Updating System Configuration"),
00867                       i18n("Updating system configuration."));
00868 
00869   QByteArray data;
00870   DCOPClient *client = kapp->dcopClient();
00871 
00872   int result = client->callAsync("kded", "kbuildsycoca", "recreate()",
00873                data, &dlg, SLOT(slotFinished()));
00874 
00875   if (result)
00876   {
00877      dlg.exec();
00878   }
00879 }
00880 
00881 KServiceProgressDialog::KServiceProgressDialog(QWidget *parent, const char *name,
00882                           const QString &caption, const QString &text)
00883  : KProgressDialog(parent, name, caption, text, true)
00884 {
00885   connect(&m_timer, SIGNAL(timeout()), this, SLOT(slotProgress()));
00886   progressBar()->setTotalSteps(20);
00887   m_timeStep = 700;
00888   m_timer.start(m_timeStep);
00889   setAutoClose(false);
00890 }
00891 
00892 void
00893 KServiceProgressDialog::slotProgress()
00894 {
00895   int p = progressBar()->progress();
00896   if (p == 18)
00897   {
00898      progressBar()->reset();
00899      progressBar()->setProgress(1);
00900      m_timeStep = m_timeStep * 2;
00901      m_timer.start(m_timeStep);
00902   }
00903   else
00904   {
00905      progressBar()->setProgress(p+1);
00906   }
00907 }
00908 
00909 void
00910 KServiceProgressDialog::slotFinished()
00911 {
00912   progressBar()->setProgress(20);
00913   m_timer.stop();
00914   QTimer::singleShot(1000, this, SLOT(close()));
00915 }
00916 
00917 #include "kservice_p.moc"
KDE Home | KDE Accessibility Home | Description of Access Keys