kcmoduleloader.cpp
00001 /* 00002 Copyright (c) 1999 Matthias Hoelzer-Kluepfel <hoelzer@kde.org> 00003 Copyright (c) 2000 Matthias Elter <elter@kde.org> 00004 Copyright (c) 2003,2004 Matthias Kretz <kretz@kde.org> 00005 Copyright (c) 2004 Frans Englich <frans.englich@telia.com> 00006 00007 This file is part of the KDE project 00008 00009 This library is free software; you can redistribute it and/or 00010 modify it under the terms of the GNU Library General Public 00011 License version 2, as published by the Free Software Foundation. 00012 00013 This library is distributed in the hope that it will be useful, 00014 but WITHOUT ANY WARRANTY; without even the implied warranty of 00015 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 00016 Library General Public License for more details. 00017 00018 You should have received a copy of the GNU Library General Public License 00019 along with this library; see the file COPYING.LIB. If not, write to 00020 the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, 00021 Boston, MA 02110-1301, USA. 00022 */ 00023 00024 #include <qfile.h> 00025 #include <qlabel.h> 00026 #include <qlayout.h> 00027 00028 #include <kapplication.h> 00029 #include <kdebug.h> 00030 #include <klocale.h> 00031 #include <kmessagebox.h> 00032 #include <kparts/componentfactory.h> 00033 00034 #include "kcmoduleloader.h" 00035 00036 00037 /***************************************************************/ 00042 class KCMError : public KCModule 00043 { 00044 public: 00045 KCMError( const QString& msg, const QString& details, QWidget* parent ) 00046 : KCModule( parent, "KCMError" ) 00047 { 00048 QVBoxLayout* topLayout = new QVBoxLayout( this ); 00049 topLayout->addWidget( new QLabel( msg, this ) ); 00050 topLayout->addWidget( new QLabel( details, this ) ); 00051 } 00052 }; 00053 /***************************************************************/ 00054 00055 00056 00057 00058 KCModule* KCModuleLoader::load(const KCModuleInfo &mod, const QString &libname, 00059 KLibLoader *loader, ErrorReporting report, QWidget * parent, 00060 const char * name, const QStringList & args ) 00061 { 00062 // attempt to load modules with ComponentFactory, only if the symbol init_<lib> exists 00063 // (this is because some modules, e.g. kcmkio with multiple modules in the library, 00064 // cannot be ported to KGenericFactory) 00065 KLibrary *lib = loader->library(QFile::encodeName(libname.arg(mod.library()))); 00066 if (lib) { 00067 QString initSym("init_"); 00068 initSym += libname.arg(mod.library()); 00069 00070 if ( lib->hasSymbol(QFile::encodeName(initSym)) ) 00071 { 00072 KLibFactory *factory = lib->factory(); 00073 if ( factory ) 00074 { 00075 KCModule *module = KParts::ComponentFactory::createInstanceFromFactory<KCModule>( factory, parent, name ? name : mod.handle().latin1(), args ); 00076 if (module) 00077 return module; 00078 } 00079 // else do a fallback 00080 kdDebug(1208) << "Unable to load module using ComponentFactory. Falling back to old loader." << endl; 00081 } 00082 00083 // get the create_ function 00084 QString factory("create_%1"); 00085 void *create = lib->symbol(QFile::encodeName(factory.arg(mod.handle()))); 00086 00087 if (create) 00088 { 00089 // create the module 00090 KCModule* (*func)(QWidget *, const char *); 00091 func = (KCModule* (*)(QWidget *, const char *)) create; 00092 return func( parent, name ? name : mod.handle().latin1() ); 00093 } 00094 else 00095 { 00096 QString libFileName = lib->fileName(); 00097 lib->unload(); 00098 return reportError( report, i18n("<qt>There was an error when loading the module '%1'.<br><br>" 00099 "The desktop file (%2) as well as the library (%3) was found but " 00100 "yet the module could not be loaded properly. Most likely " 00101 "the factory declaration was wrong, or the " 00102 "create_* function was missing.</qt>") 00103 .arg( mod.moduleName() ) 00104 .arg( mod.fileName() ) 00105 .arg( libFileName ), 00106 QString::null, parent ); 00107 } 00108 00109 lib->unload(); 00110 } 00111 return reportError( report, i18n("The specified library %1 could not be found.") 00112 .arg( mod.library() ), QString::null, parent ); 00113 return 0; 00114 } 00115 00116 KCModule* KCModuleLoader::loadModule(const KCModuleInfo &mod, bool withfallback, QWidget * parent, const char * name, const QStringList & args ) 00117 { 00118 return loadModule( mod, None, withfallback, parent, name, args ); 00119 } 00120 00121 KCModule* KCModuleLoader::loadModule(const KCModuleInfo &mod, ErrorReporting report, bool withfallback, QWidget * parent, const char * name, const QStringList & args ) 00122 { 00123 /* 00124 * Simple libraries as modules are the easiest case: 00125 * We just have to load the library and get the module 00126 * from the factory. 00127 */ 00128 00129 if ( !mod.service() ) 00130 { 00131 return reportError( report, 00132 i18n("The module %1 could not be found.") 00133 .arg( mod.moduleName() ), i18n("<qt><p>The diagnostics is:<br>The desktop file %1 could not be found.</qt>").arg(mod.fileName()), parent ); 00134 } 00135 00136 if (!mod.library().isEmpty()) 00137 { 00138 // get the library loader instance 00139 00140 KLibLoader *loader = KLibLoader::self(); 00141 00142 KCModule *module = load(mod, "kcm_%1", loader, report, parent, name, args ); 00143 /* 00144 * Only try to load libkcm_* if it exists, otherwise KLibLoader::lastErrorMessage would say 00145 * "libkcm_foo not found" instead of the real problem with loading kcm_foo. 00146 */ 00147 if (!KLibLoader::findLibrary( QCString( "libkcm_" ) + QFile::encodeName( mod.library() ) ).isEmpty() ) 00148 module = load(mod, "libkcm_%1", loader, report, parent, name, args ); 00149 if (module) 00150 return module; 00151 return reportError( report, 00152 i18n("The module %1 could not be loaded.") 00153 .arg( mod.moduleName() ), QString::null, parent ); 00154 } 00155 00156 /* 00157 * Ok, we could not load the library. 00158 * Try to run it as an executable. 00159 * This must not be done when calling from kcmshell, or you'll 00160 * have infinite recursion 00161 * (startService calls kcmshell which calls modloader which calls startService...) 00162 * 00163 */ 00164 if(withfallback) 00165 { 00166 KApplication::startServiceByDesktopPath(mod.fileName(), QString::null); 00167 } 00168 else 00169 { 00170 return reportError( report, 00171 i18n("The module %1 is not a valid configuration module.") 00172 .arg( mod.moduleName() ), i18n("<qt><p>The diagnostics is:<br>The desktop file %1 does not specify a library.</qt>").arg(mod.fileName()), parent ); 00173 } 00174 00175 return 0; 00176 } 00177 00178 KCModule* KCModuleLoader::loadModule(const QString &module, QWidget *parent, 00179 const char *name, const QStringList & args) 00180 { 00181 return loadModule(KCModuleInfo(module), None, false, parent, name, args); 00182 } 00183 00184 KCModule* KCModuleLoader::loadModule(const QString &module, ErrorReporting 00185 report, QWidget *parent, const char *name, const QStringList & args) 00186 { 00187 return loadModule(KCModuleInfo(module), report, false, parent, name, args); 00188 } 00189 00190 void KCModuleLoader::unloadModule(const KCModuleInfo &mod) 00191 { 00192 // get the library loader instance 00193 KLibLoader *loader = KLibLoader::self(); 00194 00195 // try to unload the library 00196 QString libname("libkcm_%1"); 00197 loader->unloadLibrary(QFile::encodeName(libname.arg(mod.library()))); 00198 00199 libname = "kcm_%1"; 00200 loader->unloadLibrary(QFile::encodeName(libname.arg(mod.library()))); 00201 } 00202 00203 void KCModuleLoader::showLastLoaderError(QWidget *parent) 00204 { 00205 KMessageBox::detailedError(parent, 00206 i18n("There was an error loading the module."),i18n("<qt><p>The diagnostics is:<br>%1" 00207 "<p>Possible reasons:</p><ul><li>An error occurred during your last " 00208 "KDE upgrade leaving an orphaned control module<li>You have old third party " 00209 "modules lying around.</ul><p>Check these points carefully and try to remove " 00210 "the module mentioned in the error message. If this fails, consider contacting " 00211 "your distributor or packager.</p></qt>") 00212 .arg(KLibLoader::self()->lastErrorMessage())); 00213 00214 } 00215 00216 bool KCModuleLoader::testModule( const QString& module ) 00217 { 00218 return testModule( KCModuleInfo( module ) ); 00219 } 00220 00221 bool KCModuleLoader::testModule( const KCModuleInfo& module ) 00222 { 00223 if (!module.service()) 00224 { 00225 kdDebug(1208) << "Module '" << module.fileName() << "' not found." << endl; 00226 return true; 00227 } 00228 00229 bool doLoad = module.service()->property( "X-KDE-Test-Module", QVariant::Bool ).toBool(); 00230 if( !doLoad ) 00231 { 00232 return true; 00233 } 00234 else 00235 { 00242 KLibLoader* loader = KLibLoader::self(); 00243 KLibrary* library = loader->library( QFile::encodeName((QString("kcm_%1").arg(module.library()))) ); 00244 if( library ) 00245 { 00246 void *test_func = library->symbol( QString("test_%1").arg(module.factoryName()).utf8() ); 00247 if( test_func ) 00248 { 00249 bool (*func)() = (bool(*)())test_func; 00250 if( func() ) 00251 { 00252 return true; 00253 } 00254 else 00255 { 00256 return false; 00257 } 00258 } 00259 else 00260 { 00261 kdDebug(1208) << "The test function for module '" << module.fileName() << "' could not be found." << endl; 00262 return true; 00263 } 00264 } 00265 kdDebug(1208) << "The library '" << module.library() << "' could not be found." << endl; 00266 return true; 00267 } 00268 } 00269 00270 KCModule* KCModuleLoader::reportError( ErrorReporting report, const QString & text, 00271 QString details, QWidget * parent ) 00272 { 00273 if( details.isNull() ) 00274 details = i18n("<qt><p>The diagnostics is:<br>%1" 00275 "<p>Possible reasons:</p><ul><li>An error occurred during your last " 00276 "KDE upgrade leaving an orphaned control module<li>You have old third party " 00277 "modules lying around.</ul><p>Check these points carefully and try to remove " 00278 "the module mentioned in the error message. If this fails, consider contacting " 00279 "your distributor or packager.</p></qt>").arg(KLibLoader::self()->lastErrorMessage()); 00280 if( report & Dialog ) 00281 KMessageBox::detailedError( parent, text, details ); 00282 if( report & Inline ) 00283 return new KCMError( text, details, parent ); 00284 return 0; 00285 } 00286 00287 // vim: ts=2 sw=2 et 00288