/* This file is part of the KDE libraries * Copyright (C) 1999 - 2001 Waldo Bastian * Copyright (C) 1999 David Faure * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License version 2 as published by the Free Software Foundation; * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. **/ // $Id$ #include #include "kservice.h" #include "kservice_p.h" #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "kservicefactory.h" #include "kservicetypefactory.h" #include "kservicetype.h" #include "kuserprofile.h" #include "ksycoca.h" class KService::KServicePrivate { public: TQStringList categories; TQString menuId; }; KService::KService( const TQString & _name, const TQString &_exec, const TQString &_icon) : KSycocaEntry( TQString::null) { d = new KServicePrivate; m_bValid = true; m_bDeleted = false; m_strType = "Application"; m_strName = _name; m_strExec = _exec; m_strIcon = _icon; m_bTerminal = false; m_bAllowAsDefault = true; m_initialPreference = 10; } KService::KService( const TQString & _fullpath ) : KSycocaEntry( _fullpath) { KDesktopFile config( _fullpath ); init(&config); } KService::KService( KDesktopFile *config ) : KSycocaEntry( config->fileName()) { init(config); } void KService::init( KDesktopFile *config ) { d = new KServicePrivate; m_bValid = true; bool absPath = !TQDir::isRelativePath(entryPath()); bool kde4application = config->fileName().startsWith("/usr/share/applications/kde4/"); config->setDesktopGroup(); TQMap entryMap = config->entryMap(config->group()); entryMap.remove("Encoding"); // reserved as part of Desktop Entry Standard entryMap.remove("Version"); // reserved as part of Desktop Entry Standard m_bDeleted = config->readBoolEntry( "Hidden", false ); entryMap.remove("Hidden"); if (m_bDeleted) { //kdDebug() << "Hidden=true for " << entryPath() << endl; m_bValid = false; return; } m_strName = config->readName(); entryMap.remove("Name"); if ( m_strName.isEmpty() ) { if (config->readEntry( "Exec" ).isEmpty()) { //kdWarning(7012) << "The desktop entry file " << entryPath() // << " has no Name and no Exec" << endl; m_bValid = false; return; } // Try to make up a name. m_strName = entryPath(); int i = m_strName.findRev('/'); m_strName = m_strName.mid(i+1); i = m_strName.findRev('.'); if (i != -1) m_strName = m_strName.left(i); } m_strType = config->readType(); entryMap.remove("Type"); if ( m_strType.isEmpty() ) { /*kdWarning(7012) << "The desktop entry file " << entryPath() << " has no Type=... entry." << " It should be \"Application\" or \"Service\"" << endl; m_bValid = false; return;*/ m_strType = "Application"; } else if ( m_strType != "Application" && m_strType != "Service" ) { kdWarning(7012) << "The desktop entry file " << entryPath() << " has Type=" << m_strType << " instead of \"Application\" or \"Service\"" << endl; m_bValid = false; return; } // In case Try Exec is set, check if the application is available if (!config->tryExec()) { //kdDebug(7012) << "tryExec said false for " << entryPath() << endl; m_bDeleted = true; m_bValid = false; return; } TQString resource = config->resource(); if ( (m_strType == "Application") && (!resource.isEmpty()) && (resource != "apps") && !absPath) { kdWarning(7012) << "The desktop entry file " << entryPath() << " has Type=" << m_strType << " but is located under \"" << resource << "\" instead of \"apps\"" << endl; m_bValid = false; return; } if ( (m_strType == "Service") && (!resource.isEmpty()) && (resource != "services") && !absPath) { kdWarning(7012) << "The desktop entry file " << entryPath() << " has Type=" << m_strType << " but is located under \"" << resource << "\" instead of \"services\"" << endl; m_bValid = false; return; } TQString name = entryPath(); int pos = name.findRev('/'); if (pos != -1) name = name.mid(pos+1); pos = name.find('.'); if (pos != -1) name = name.left(pos); m_strExec = config->readPathEntry( "Exec" ); if (kde4application && !m_strExec.startsWith("/")) { m_strExec = "KDEHOME=$HOME/.kde XDG_DATA_DIRS=/usr/share KDEDIRS=/usr/ XDG_CONFIG_DIRS=/etc/xdg/ PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:$PATH "+m_strExec; } else if (config->readBoolEntry("X-KDE-SubstituteUID")) { int space = m_strExec.find(" "); if (space==-1) m_strExec = KStandardDirs::findExe(m_strExec); else { const TQString command = m_strExec.left(space); m_strExec.replace(command,KStandardDirs::findExe(command)); } } entryMap.remove("Exec"); m_strIcon = config->readEntry( "Icon", "unknown" ); if (kde4application) { if (TQFile::exists("/usr/share/icons/oxygen/22x22/apps/" + m_strIcon + ".png")) { m_strIcon = "/usr/share/icons/oxygen/22x22/apps/" + m_strIcon + ".png"; } else if (TQFile::exists("/usr/share/icons/hicolor/22x22/apps/" + m_strIcon + ".png")) { m_strIcon = "/usr/share/icons/hicolor/22x22/apps/" + m_strIcon + ".png"; } } entryMap.remove("Icon"); m_bTerminal = (config->readBoolEntry( "Terminal" )); // should be a property IMHO entryMap.remove("Terminal"); m_strTerminalOptions = config->readEntry( "TerminalOptions" ); // should be a property IMHO entryMap.remove("TerminalOptions"); m_strPath = config->readPath(); entryMap.remove("Path"); m_strComment = config->readComment(); entryMap.remove("Comment"); m_strGenName = config->readGenericName(); if (kde4application) { m_strGenName += " [KDE4]"; } entryMap.remove("GenericName"); TQString untranslatedGenericName = config->readEntryUntranslated( "GenericName" ); if (!untranslatedGenericName.isEmpty()) entryMap.insert("UntranslatedGenericName", untranslatedGenericName); m_lstKeywords = config->readListEntry("Keywords"); entryMap.remove("Keywords"); d->categories = config->readListEntry("Categories", ';'); entryMap.remove("Categories"); m_strLibrary = config->readEntry( "X-KDE-Library" ); entryMap.remove("X-KDE-Library"); m_strInit = config->readEntry("X-KDE-Init" ); entryMap.remove("X-KDE-Init"); m_lstServiceTypes = config->readListEntry( "ServiceTypes" ); entryMap.remove("ServiceTypes"); // For compatibility with KDE 1.x if (!kde4application) m_lstServiceTypes += config->readListEntry( "MimeType", ';' ); entryMap.remove("MimeType"); if ( m_strType == "Application" && !m_lstServiceTypes.contains("Application") ) // Applications implement the service type "Application" ;-) m_lstServiceTypes += "Application"; TQString dcopServiceType = config->readEntry("X-DCOP-ServiceType").lower(); entryMap.remove("X-DCOP-ServiceType"); if (dcopServiceType == "unique") m_DCOPServiceType = DCOP_Unique; else if (dcopServiceType == "multi") m_DCOPServiceType = DCOP_Multi; else if (dcopServiceType == "wait") m_DCOPServiceType = DCOP_Wait; else m_DCOPServiceType = DCOP_None; m_strDesktopEntryName = name.lower(); if (kde4application) m_strDesktopEntryName = "kde4-" + m_strDesktopEntryName; m_bAllowAsDefault = config->readBoolEntry( "AllowDefault", true ); entryMap.remove("AllowDefault"); m_initialPreference = config->readNumEntry( "X-KDE-InitialPreference", 1 ); entryMap.remove("X-KDE-InitialPreference"); if ( m_initialPreference == 1 ) m_initialPreference = config->readNumEntry( "InitialPreference", 1 ); entryMap.remove("InitialPreference"); // Store all additional entries in the property map. // A TQMap would be easier for this but we can't // brake BC, so we have to store it in m_mapProps. // qWarning("Path = %s", entryPath().latin1()); TQMap::ConstIterator it = entryMap.begin(); for( ; it != entryMap.end();++it) { //qDebug(" Key = %s Data = %s", it.key().latin1(), it.data().latin1()); TQString key = it.key(); if (kde4application && key=="OnlyShowIn" && it.data()=="KDE;") key = "NotShowIn"; m_mapProps.insert( key, TQVariant( it.data())); } } KService::KService( TQDataStream& _str, int offset ) : KSycocaEntry( _str, offset ) { d = new KServicePrivate; load( _str ); } KService::~KService() { //debug("KService::~KService()"); delete d; } TQPixmap KService::pixmap( KIcon::Group _group, int _force_size, int _state, TQString * _path ) const { KIconLoader *iconLoader=KGlobal::iconLoader(); if (!iconLoader->extraDesktopThemesAdded()) { TQPixmap pixmap=iconLoader->loadIcon( m_strIcon, _group, _force_size, _state, _path, true ); if (!pixmap.isNull() ) return pixmap; iconLoader->addExtraDesktopThemes(); } return iconLoader->loadIcon( m_strIcon, _group, _force_size, _state, _path ); } void KService::load( TQDataStream& s ) { // dummies are here because of fields that were removed, to keep bin compat. // Feel free to re-use, but fields for Applications only (not generic services) // should rather be added to application.desktop Q_INT8 def, term, dummy1, dummy2; Q_INT8 dst, initpref; TQString dummyStr1, dummyStr2; int dummyI1, dummyI2; Q_UINT32 dummyUI32; // WARNING: IN KDE 3.x THIS NEEDS TO REMAIN COMPATIBLE WITH KDE 2.x! // !! This data structure should remain binary compatible at all times !! // You may add new fields at the end. Make sure to update the version // number in ksycoca.h s >> m_strType >> m_strName >> m_strExec >> m_strIcon >> term >> m_strTerminalOptions >> m_strPath >> m_strComment >> m_lstServiceTypes >> def >> m_mapProps >> m_strLibrary >> dummyI1 >> dummyI2 >> dst >> m_strDesktopEntryName >> dummy1 >> dummyStr1 >> initpref >> dummyStr2 >> dummy2 >> m_lstKeywords >> m_strInit >> dummyUI32 >> m_strGenName >> d->categories >> d->menuId; m_bAllowAsDefault = def; m_bTerminal = term; m_DCOPServiceType = (DCOPServiceType_t) dst; m_initialPreference = initpref; m_bValid = true; } void KService::save( TQDataStream& s ) { KSycocaEntry::save( s ); Q_INT8 def = m_bAllowAsDefault, initpref = m_initialPreference; Q_INT8 term = m_bTerminal; Q_INT8 dst = (Q_INT8) m_DCOPServiceType; Q_INT8 dummy1 = 0, dummy2 = 0; // see ::load TQString dummyStr1, dummyStr2; int dummyI1 = 0, dummyI2 = 0; Q_UINT32 dummyUI32 = 0; // WARNING: IN KDE 3.x THIS NEEDS TO REMAIN COMPATIBLE WITH KDE 2.x! // !! This data structure should remain binary compatible at all times !! // You may add new fields at the end. Make sure to update the version // number in ksycoca.h s << m_strType << m_strName << m_strExec << m_strIcon << term << m_strTerminalOptions << m_strPath << m_strComment << m_lstServiceTypes << def << m_mapProps << m_strLibrary << dummyI1 << dummyI2 << dst << m_strDesktopEntryName << dummy1 << dummyStr1 << initpref << dummyStr2 << dummy2 << m_lstKeywords << m_strInit << dummyUI32 << m_strGenName << d->categories << d->menuId; } bool KService::hasServiceType( const TQString& _servicetype ) const { if (!m_bValid) return false; // safety test //kdDebug(7012) << "Testing " << m_strDesktopEntryName << " for " << _servicetype << endl; KMimeType::Ptr mimePtr = KMimeType::mimeType( _servicetype ); if ( mimePtr && mimePtr == KMimeType::defaultMimeTypePtr() ) mimePtr = 0; bool isNumber; // For each service type we are associated with, if it doesn't // match then we try its parent service types. TQStringList::ConstIterator it = m_lstServiceTypes.begin(); for( ; it != m_lstServiceTypes.end(); ++it ) { (*it).toInt(&isNumber); if (isNumber) continue; //kdDebug(7012) << " has " << (*it) << endl; KServiceType::Ptr ptr = KServiceType::serviceType( *it ); if ( ptr && ptr->inherits( _servicetype ) ) return true; // The mimetype inheritance ("is also") works the other way. // e.g. if we're looking for a handler for mimePtr==smb-workgroup // then a handler for inode/directory is ok. if ( mimePtr && mimePtr->is( *it ) ) return true; } return false; } int KService::initialPreferenceForMimeType( const TQString& mimeType ) const { if (!m_bValid) return 0; // safety test bool isNumber; // For each service type we are associated with TQStringList::ConstIterator it = m_lstServiceTypes.begin(); for( ; it != m_lstServiceTypes.end(); ++it ) { (*it).toInt(&isNumber); if (isNumber) continue; //kdDebug(7012) << " has " << (*it) << endl; KServiceType::Ptr ptr = KServiceType::serviceType( *it ); if ( !ptr || !ptr->inherits( mimeType ) ) continue; int initalPreference = m_initialPreference; ++it; if (it != m_lstServiceTypes.end()) { int i = (*it).toInt(&isNumber); if (isNumber) initalPreference = i; } return initalPreference; } KMimeType::Ptr mimePtr = KMimeType::mimeType( mimeType ); if ( mimePtr && mimePtr == KMimeType::defaultMimeTypePtr() ) mimePtr = 0; // Try its parent service types. it = m_lstServiceTypes.begin(); for( ; it != m_lstServiceTypes.end(); ++it ) { (*it).toInt(&isNumber); if (isNumber) continue; // The mimetype inheritance ("is also") works the other way. // e.g. if we're looking for a handler for mimePtr==smb-workgroup // then a handler for inode/directory is ok. if ( !mimePtr || !mimePtr->is( *it ) ) continue; int initalPreference = m_initialPreference; ++it; if (it != m_lstServiceTypes.end()) { int i = (*it).toInt(&isNumber); if (isNumber) initalPreference = i; } return initalPreference; } return 0; } class KServiceReadProperty : public KConfigBase { public: KServiceReadProperty(const TQString &_key, const TQCString &_value) : key(_key), value(_value) { } bool internalHasGroup(const TQCString &) const { /*qDebug("hasGroup(const TQCString &)");*/ return false; } TQStringList groupList() const { return TQStringList(); } TQMap entryMap(const TQString &group) const { Q_UNUSED(group); return TQMap(); } void reparseConfiguration() { } KEntryMap internalEntryMap( const TQString &pGroup) const { Q_UNUSED(pGroup); return KEntryMap(); } KEntryMap internalEntryMap() const { return KEntryMap(); } void putData(const KEntryKey &_key, const KEntry& _data, bool _checkGroup) { Q_UNUSED(_key); Q_UNUSED(_data); Q_UNUSED(_checkGroup); } KEntry lookupData(const KEntryKey &_key) const { Q_UNUSED(_key); KEntry entry; entry.mValue = value; return entry; } protected: TQString key; TQCString value; }; TQVariant KService::property( const TQString& _name) const { return property( _name, TQVariant::Invalid); } // Return a string TQVariant if string isn't null, and invalid variant otherwise // (the variant must be invalid if the field isn't in the .desktop file) // This allows trader queries like "exist Library" to work. static TQVariant makeStringVariant( const TQString& string ) { // Using isEmpty here would be wrong. // Empty is "specified but empty", null is "not specified" (in the .desktop file) return string.isNull() ? TQVariant() : TQVariant( string ); } TQVariant KService::property( const TQString& _name, TQVariant::Type t ) const { if ( _name == "Type" ) return TQVariant( m_strType ); // can't be null else if ( _name == "Name" ) return TQVariant( m_strName ); // can't be null else if ( _name == "Exec" ) return makeStringVariant( m_strExec ); else if ( _name == "Icon" ) return makeStringVariant( m_strIcon ); else if ( _name == "Terminal" ) return TQVariant( static_cast(m_bTerminal) ); else if ( _name == "TerminalOptions" ) return makeStringVariant( m_strTerminalOptions ); else if ( _name == "Path" ) return makeStringVariant( m_strPath ); else if ( _name == "Comment" ) return makeStringVariant( m_strComment ); else if ( _name == "GenericName" ) return makeStringVariant( m_strGenName ); else if ( _name == "ServiceTypes" ) return TQVariant( m_lstServiceTypes ); else if ( _name == "AllowAsDefault" ) return TQVariant( static_cast(m_bAllowAsDefault) ); else if ( _name == "InitialPreference" ) return TQVariant( m_initialPreference ); else if ( _name == "Library" ) return makeStringVariant( m_strLibrary ); else if ( _name == "DesktopEntryPath" ) // can't be null return TQVariant( entryPath() ); else if ( _name == "DesktopEntryName") return TQVariant( m_strDesktopEntryName ); // can't be null else if ( _name == "Categories") return TQVariant( d->categories ); else if ( _name == "Keywords") return TQVariant( m_lstKeywords ); // Ok we need to convert the property from a TQString to its real type. // Maybe the caller helped us. if (t == TQVariant::Invalid) { // No luck, let's ask KServiceTypeFactory what the type of this property // is supposed to be. t = KServiceTypeFactory::self()->findPropertyTypeByName(_name); if (t == TQVariant::Invalid) { kdDebug(7012) << "Request for unknown property '" << _name << "'\n"; return TQVariant(); // Unknown property: Invalid variant. } } // Then we use a homebuild class based on KConfigBase to convert the TQString. // For some often used property types we do the conversion ourselves. TQMap::ConstIterator it = m_mapProps.find( _name ); if ( (it == m_mapProps.end()) || (!it.data().isValid())) { //kdDebug(7012) << "Property not found " << _name << endl; return TQVariant(); // No property set. } switch(t) { case TQVariant::String: return it.data(); case TQVariant::Bool: case TQVariant::Int: { TQString aValue = it.data().toString(); int val = 0; if (aValue == "true" || aValue == "on" || aValue == "yes") val = 1; else { bool bOK; val = aValue.toInt( &bOK ); if( !bOK ) val = 0; } if (t == TQVariant::Bool) { return TQVariant((bool)val, 1); } return TQVariant(val); } default: // All others KServiceReadProperty ksrp(_name, it.data().toString().utf8()); return ksrp.readPropertyEntry(_name, t); } } TQStringList KService::propertyNames() const { TQStringList res; TQMap::ConstIterator it = m_mapProps.begin(); for( ; it != m_mapProps.end(); ++it ) res.append( it.key() ); res.append( "Type" ); res.append( "Name" ); res.append( "Comment" ); res.append( "GenericName" ); res.append( "Icon" ); res.append( "Exec" ); res.append( "Terminal" ); res.append( "TerminalOptions" ); res.append( "Path" ); res.append( "ServiceTypes" ); res.append( "AllowAsDefault" ); res.append( "InitialPreference" ); res.append( "Library" ); res.append( "DesktopEntryPath" ); res.append( "DesktopEntryName" ); res.append( "Keywords" ); res.append( "Categories" ); return res; } KService::List KService::allServices() { return KServiceFactory::self()->allServices(); } KService::Ptr KService::serviceByName( const TQString& _name ) { KService * s = KServiceFactory::self()->findServiceByName( _name ); return KService::Ptr( s ); } KService::Ptr KService::serviceByDesktopPath( const TQString& _name ) { KService * s = KServiceFactory::self()->findServiceByDesktopPath( _name ); return KService::Ptr( s ); } KService::Ptr KService::serviceByDesktopName( const TQString& _name ) { KService * s = KServiceFactory::self()->findServiceByDesktopName( _name.lower() ); if (!s && !_name.startsWith("kde-")) s = KServiceFactory::self()->findServiceByDesktopName( "kde-"+_name.lower() ); return KService::Ptr( s ); } KService::Ptr KService::serviceByMenuId( const TQString& _name ) { KService * s = KServiceFactory::self()->findServiceByMenuId( _name ); return KService::Ptr( s ); } KService::Ptr KService::serviceByStorageId( const TQString& _storageId ) { KService::Ptr service = KService::serviceByMenuId( _storageId ); if (service) return service; service = KService::serviceByDesktopPath(_storageId); if (service) return service; if (!TQDir::isRelativePath(_storageId) && TQFile::exists(_storageId)) return new KService(_storageId); TQString tmp = _storageId; tmp = tmp.mid(tmp.findRev('/')+1); // Strip dir if (tmp.endsWith(".desktop")) tmp.truncate(tmp.length()-8); if (tmp.endsWith(".kdelnk")) tmp.truncate(tmp.length()-7); service = KService::serviceByDesktopName(tmp); return service; } KService::List KService::allInitServices() { return KServiceFactory::self()->allInitServices(); } bool KService::substituteUid() const { TQVariant v = property("X-KDE-SubstituteUID", TQVariant::Bool); return v.isValid() && v.toBool(); } TQString KService::username() const { // See also KDesktopFile::tryExec() TQString user; TQVariant v = property("X-KDE-Username", TQVariant::String); user = v.isValid() ? v.toString() : TQString::null; if (user.isEmpty()) user = ::getenv("ADMIN_ACCOUNT"); if (user.isEmpty()) user = "root"; return user; } bool KService::noDisplay() const { TQMap::ConstIterator it = m_mapProps.find( "NoDisplay" ); if ( (it != m_mapProps.end()) && (it.data().isValid())) { TQString aValue = it.data().toString().lower(); if (aValue == "true" || aValue == "on" || aValue == "yes") return true; } it = m_mapProps.find( "OnlyShowIn" ); if ( (it != m_mapProps.end()) && (it.data().isValid())) { TQString aValue = it.data().toString(); TQStringList aList = TQStringList::split(';', aValue); if (!aList.contains("KDE")) return true; } it = m_mapProps.find( "NotShowIn" ); if ( (it != m_mapProps.end()) && (it.data().isValid())) { TQString aValue = it.data().toString(); TQStringList aList = TQStringList::split(';', aValue); if (aList.contains("KDE")) return true; } if (!kapp->authorizeControlModule(d->menuId)) return true; return false; } TQString KService::untranslatedGenericName() const { TQVariant v = property("UntranslatedGenericName", TQVariant::String); return v.isValid() ? v.toString() : TQString::null; } TQString KService::parentApp() const { TQMap::ConstIterator it = m_mapProps.find( "X-KDE-ParentApp" ); if ( (it == m_mapProps.end()) || (!it.data().isValid())) { return TQString::null; } return it.data().toString(); } bool KService::allowMultipleFiles() const { // Can we pass multiple files on the command line or do we have to start the application for every single file ? if ( m_strExec.find( "%F" ) != -1 || m_strExec.find( "%U" ) != -1 || m_strExec.find( "%N" ) != -1 || m_strExec.find( "%D" ) != -1 ) return true; else return false; } TQStringList KService::categories() const { return d->categories; } TQString KService::menuId() const { return d->menuId; } void KService::setMenuId(const TQString &menuId) { d->menuId = menuId; } TQString KService::storageId() const { if (!d->menuId.isEmpty()) return d->menuId; return entryPath(); } TQString KService::locateLocal() { if (d->menuId.isEmpty() || desktopEntryPath().startsWith(".hidden") || (TQDir::isRelativePath(desktopEntryPath()) && d->categories.isEmpty())) return KDesktopFile::locateLocal(desktopEntryPath()); return ::locateLocal("xdgdata-apps", d->menuId); } TQString KService::newServicePath(bool showInMenu, const TQString &suggestedName, TQString *menuId, const TQStringList *reservedMenuIds) { TQString base = suggestedName; if (!showInMenu) base.prepend("kde-"); TQString result; for(int i = 1; true; i++) { if (i == 1) result = base + ".desktop"; else result = base + TQString("-%1.desktop").arg(i); if (reservedMenuIds && reservedMenuIds->contains(result)) continue; // Lookup service by menu-id KService::Ptr s = serviceByMenuId(result); if (s) continue; if (showInMenu) { if (!locate("xdgdata-apps", result).isEmpty()) continue; } else { TQString file = result.mid(4); // Strip "kde-" if (!locate("apps", ".hidden/"+file).isEmpty()) continue; } break; } if (menuId) *menuId = result; if (showInMenu) { return ::locateLocal("xdgdata-apps", result); } else { TQString file = result.mid(4); // Strip "kde-" return ::locateLocal("apps", ".hidden/"+file); } } void KService::virtual_hook( int id, void* data ) { KSycocaEntry::virtual_hook( id, data ); } void KService::rebuildKSycoca(TQWidget *parent) { KServiceProgressDialog dlg(parent, "ksycoca_progress", i18n("Updating System Configuration"), i18n("Updating system configuration.")); TQByteArray data; DCOPClient *client = kapp->dcopClient(); int result = client->callAsync("kded", "kbuildsycoca", "recreate()", data, &dlg, TQT_SLOT(slotFinished())); if (result) { dlg.exec(); } } KServiceProgressDialog::KServiceProgressDialog(TQWidget *parent, const char *name, const TQString &caption, const TQString &text) : KProgressDialog(parent, name, caption, text, true) { connect(&m_timer, TQT_SIGNAL(timeout()), this, TQT_SLOT(slotProgress())); progressBar()->setTotalSteps(20); m_timeStep = 700; m_timer.start(m_timeStep); setAutoClose(false); } void KServiceProgressDialog::slotProgress() { int p = progressBar()->progress(); if (p == 18) { progressBar()->reset(); progressBar()->setProgress(1); m_timeStep = m_timeStep * 2; m_timer.start(m_timeStep); } else { progressBar()->setProgress(p+1); } } void KServiceProgressDialog::slotFinished() { progressBar()->setProgress(20); m_timer.stop(); TQTimer::singleShot(1000, this, TQT_SLOT(close())); } #include "kservice_p.moc"