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