00001
00002
00003
00004
00005
00006
00007 #include "pluginmanager.h"
00008 #include "scplugin.h"
00009 #include "loadsaveplugin.h"
00010
00011 #include <QDir>
00012 #include <QEvent>
00013
00014 #include "scconfig.h"
00015
00016 #include "scribus.h"
00017 #include "scribusdoc.h"
00018 #include "scribuscore.h"
00019 #include "sctoolbar.h"
00020 #include "menumanager.h"
00021 #include "scraction.h"
00022 #include "splash.h"
00023 #include "prefsmanager.h"
00024 #include "prefsfile.h"
00025 #include "scpaths.h"
00026 #include "commonstrings.h"
00027
00028 #ifdef HAVE_DLFCN_H
00029 #include <dlfcn.h>
00030 #elif defined(DLL_USE_NATIVE_API) && defined(_WIN32)
00031 #include <windows.h>
00032 #else
00033 #include <QLibrary>
00034 #endif
00035
00036
00037 extern ScribusQApp *ScQApp;
00038
00039 PluginManager::PluginManager() :
00040 QObject(0),
00041 prefs(PrefsManager::instance()->prefsFile->getPluginContext("pluginmanager"))
00042 {
00043 }
00044
00045 PluginManager::~PluginManager()
00046 {
00047 }
00048
00049 void* PluginManager::loadDLL( QString plugin )
00050 {
00051 void* lib = NULL;
00052 #ifdef HAVE_DLFCN_H
00053 QString libpath = QDir::convertSeparators( plugin );
00054 lib = dlopen(libpath.toLocal8Bit().data(), RTLD_LAZY | RTLD_GLOBAL);
00055 if (!lib)
00056 {
00057 const char* error = dlerror();
00058 qDebug("%s: %s",
00059 tr("Cannot find plugin", "plugin manager").toLocal8Bit().data(),
00060 error ? error : tr("unknown error","plugin manager").toLocal8Bit().data());
00061 }
00062 dlerror();
00063 #elif defined(DLL_USE_NATIVE_API) && defined(_WIN32)
00064 QString libpath = QDir::convertSeparators( plugin );
00065 HINSTANCE hdll = LoadLibraryW( (const wchar_t*) libpath.utf16() );
00066 lib = (void*) hdll;
00067 #else
00068 if( QFile::exists(plugin) )
00069 lib = (void*) new QLibrary( plugin );
00070 else
00071 qDebug("%s \"%s\"", tr("Cannot find plugin", "plugin manager").toLocal8Bit().data(), plugin.toLocal8Bit().data());
00072 #endif
00073 return lib;
00074 }
00075
00076 void* PluginManager::resolveSym( void* plugin, const char* sym )
00077 {
00078 void* symAddr = NULL;
00079 #ifdef HAVE_DLFCN_H
00080 const char* error;
00081 dlerror();
00082 symAddr = dlsym( plugin, sym );
00083 if ((error = dlerror()) != NULL)
00084 {
00085 qDebug("%s", tr("Cannot find symbol (%1)", "plugin manager").arg(error).toLocal8Bit().data());
00086 symAddr = NULL;
00087 }
00088 #elif defined(DLL_USE_NATIVE_API) && defined(_WIN32)
00089 symAddr = (void* ) GetProcAddress( (HMODULE) plugin, sym);
00090 if ( symAddr == NULL)
00091 qDebug("%s", tr("Cannot find symbol (%1)", "plugin manager").arg(sym).toLocal8Bit().data());
00092 #else
00093 QLibrary* qlib = (QLibrary*) plugin;
00094 if( plugin ) symAddr = qlib->resolve(sym);
00095 if ( symAddr == NULL)
00096 qDebug("%s", tr("Cannot find symbol (%1)", "plugin manager").arg(sym).toLocal8Bit().data());
00097 #endif
00098 return symAddr;
00099 }
00100
00101 void PluginManager::unloadDLL( void* plugin )
00102 {
00103 #ifdef HAVE_DLFCN_H
00104 dlclose( plugin );
00105 dlerror();
00106 #elif defined(DLL_USE_NATIVE_API) && defined(_WIN32)
00107 FreeLibrary( (HMODULE) plugin );
00108 #else
00109 delete ( (QLibrary*) plugin );
00110 #endif
00111 }
00112
00113 void PluginManager::savePreferences()
00114 {
00115
00116 for (PluginMap::Iterator it = pluginMap.begin(); it != pluginMap.end(); ++it)
00117 prefs->set(it.value().pluginName, it.value().enableOnStartup);
00118 }
00119
00120 QString PluginManager::getPluginName(QString fileName)
00121 {
00122
00123
00124
00125
00126 QFileInfo fi(fileName);
00127 QString baseName(fi.baseName());
00128 if (baseName.startsWith("lib"))
00129 baseName = baseName.remove(0,3);
00130 if (baseName.endsWith(platformDllExtension()))
00131 baseName = baseName.left(baseName.length() - 1 - platformDllExtension().length());
00132
00133 for (int i = 0; i < (int)baseName.length(); i++)
00134 if (! baseName[i].isLetterOrNumber() && baseName[i] != '_' )
00135 {
00136 qDebug("Invalid character in plugin name for %s; skipping",
00137 fileName.toLocal8Bit().data());
00138 return QString();
00139 }
00140 return baseName.toLatin1();
00141 }
00142
00143 int PluginManager::initPlugin(const QString fileName)
00144 {
00145 PluginData pda;
00146 pda.pluginFile = QString("%1/%2").arg(ScPaths::instance().pluginDir()).arg(fileName);
00147 pda.pluginName = getPluginName(pda.pluginFile);
00148 if (pda.pluginName.isNull())
00149
00150
00151 return 0;
00152 pda.plugin = 0;
00153 pda.pluginDLL = 0;
00154 pda.enabled = false;
00155 pda.enableOnStartup = prefs->getBool(pda.pluginName, true);
00156 ScCore->setSplashStatus( tr("Plugin: loading %1", "plugin manager").arg(pda.pluginName));
00157 if (loadPlugin(pda))
00158 {
00159 if (pda.enableOnStartup)
00160 enablePlugin(pda);
00161 pluginMap.insert(pda.pluginName, pda);
00162 return 1;
00163 }
00164 return 0;
00165 }
00166
00167 void PluginManager::initPlugs()
00168 {
00169 Q_ASSERT(!pluginMap.count());
00170 QString libPattern = QString("*.%1*").arg(platformDllExtension());
00171 QMap<QString,int> allPlugs;
00172 int loaded = 0;
00173 uint changes = 1;
00174 QStringList failedPlugs;
00175
00179 QDir dirList(ScPaths::instance().pluginDir(),
00180 libPattern, QDir::Name | QDir::Reversed,
00181 (QDir::Filter) PluginManager::platformDllSearchFlags());
00182
00183 if ((!dirList.exists()) || (dirList.count() == 0))
00184 return;
00185 for (uint dc = 0; dc < dirList.count(); ++dc)
00186 {
00187 int res = initPlugin(dirList[dc]);
00188 allPlugs[dirList[dc]] = res;
00189 if (res != 0)
00190 ++loaded;
00191 else
00192 failedPlugs.append(dirList[dc]);
00193 }
00194
00195
00196 QMap<QString,int>::Iterator it;
00197 while (loaded < allPlugs.count() && changes!=0)
00198 {
00199 changes = 0;
00200 for (it = allPlugs.begin(); it != allPlugs.end(); ++it)
00201 {
00202 if (it.value() != 0)
00203 continue;
00204 int res = initPlugin(it.key());
00205 allPlugs[it.key()] = res;
00206 if (res == 1)
00207 {
00208 ++loaded;
00209 ++changes;
00210 failedPlugs.removeAll(it.key());
00211 }
00212 }
00213 }
00214 if (loaded != allPlugs.count())
00215 {
00216 if (ScCore->usingGUI())
00217 {
00218 bool splashShown = ScCore->splashShowing();
00219 QString failedStr("<ul>");
00220 for (QStringList::Iterator it = failedPlugs.begin(); it != failedPlugs.end(); ++it)
00221 failedStr += "<li>" + *it + "</li>";
00222 failedStr += "</ul>";
00223 if (splashShown)
00224 ScCore->showSplash(false);
00225 QMessageBox::warning(ScCore->primaryMainWindow(), CommonStrings::trWarning,
00226 "<qt>" + tr("There is a problem loading %1 of %2 plugins. %3 This is probably caused by some kind of dependency issue or old plugins existing in your install directory. If you clean out your install directory and reinstall and this still occurs, please report it on bugs.scribus.net."
00227 ).arg(allPlugs.count()-loaded).arg(allPlugs.count()).arg(failedStr)
00228 + "</qt>",
00229 CommonStrings::tr_OK);
00230 if (splashShown)
00231 ScCore->showSplash(true);
00232 }
00233 }
00234 }
00235
00236
00237
00238 void PluginManager::enablePlugin(PluginData & pda)
00239 {
00240 Q_ASSERT(pda.enabled == false);
00241 QString failReason;
00242 bool isActionPlugin=false;
00243 if (pda.plugin->inherits("ScActionPlugin"))
00244 {
00245
00246
00247 isActionPlugin=true;
00248
00249
00250
00251
00252
00253 }
00254 else if (pda.plugin->inherits("ScPersistentPlugin"))
00255 {
00256 ScPersistentPlugin* plugin = dynamic_cast<ScPersistentPlugin*>(pda.plugin);
00257 assert(plugin);
00258 pda.enabled = plugin->initPlugin();
00259 if (!pda.enabled)
00260 failReason = tr("init failed", "plugin load error");
00261 }
00262
00263 else if (pda.plugin->inherits("LoadSavePlugin"))
00264 pda.enabled = true;
00265 else
00266 failReason = tr("unknown plugin type", "plugin load error");
00267 if (pda.enabled || isActionPlugin)
00268 ScCore->setSplashStatus(
00269 tr("Plugin: %1 loaded", "plugin manager")
00270 .arg(pda.plugin->fullTrName()));
00271 else
00272 ScCore->setSplashStatus(
00273 tr("Plugin: %1 failed to load: %2", "plugin manager")
00274 .arg(pda.plugin->fullTrName()).arg(failReason));
00275 }
00276
00277 bool PluginManager::setupPluginActions(ScribusMainWindow *mw)
00278 {
00279 Q_CHECK_PTR(mw);
00280 ScActionPlugin* plugin = 0;
00281
00282 mw->scrMenuMgr->addMenuSeparator("Extras");
00283 for (PluginMap::Iterator it = pluginMap.begin(); it != pluginMap.end(); ++it)
00284 {
00285 if (it.value().plugin->inherits("ScActionPlugin"))
00286 {
00287
00288
00289 plugin = dynamic_cast<ScActionPlugin*>(it.value().plugin);
00290 assert(plugin);
00291 ScActionPlugin::ActionInfo ai(plugin->actionInfo());
00292 ScrAction* action = new ScrAction(ScrAction::ActionDLL, ai.icon1, ai.icon2, ai.text, ai.keySequence, mw);
00293 Q_CHECK_PTR(action);
00294 mw->scrActions.insert(ai.name, action);
00295
00296
00297 mw->scrActions[ai.name]->setEnabled(ai.enabledOnStartup);
00298
00299 it.value().enabled = connect( mw->scrActions[ai.name], SIGNAL(triggeredData(ScribusDoc*)),
00300 plugin, SLOT(run(ScribusDoc*)) );
00301
00302 if ( ai.menuAfterName.isEmpty() )
00303 {
00304 if (!ai.menu.isEmpty())
00305 {
00306 if ((!ai.subMenuName.isEmpty()) && (!ai.parentMenu.isEmpty()))
00307 {
00308 if (!mw->scrMenuMgr->menuExists(ai.menu))
00309 mw->scrMenuMgr->createMenu(ai.menu, ai.subMenuName, ai.parentMenu);
00310 }
00311 mw->scrMenuMgr->addMenuItem(mw->scrActions[ai.name], ai.menu);
00312 }
00313 }
00314 else
00315 {
00316 QString actionName(ai.menu.toLower()+ai.menuAfterName);
00317 ScrAction* afterAction=0;
00318 if (mw->scrActions.contains(actionName))
00319 afterAction=mw->scrActions[actionName];
00320 if ((!ai.subMenuName.isEmpty()) && (!ai.parentMenu.isEmpty()))
00321 {
00322 if (!mw->scrMenuMgr->menuExists(ai.menu))
00323 mw->scrMenuMgr->createMenu(ai.menu, ai.subMenuName, ai.parentMenu);
00324 }
00325 mw->scrMenuMgr->addMenuItemAfter(mw->scrActions[ai.name], ai.menu, afterAction);
00326 }
00327 if (!ai.toolbar.isEmpty())
00328 {
00329 QString tbName = QString("ToolBar-%1").arg(ai.toolbar);
00330 if (mw->scrToolBars.contains(tbName))
00331 mw->scrToolBars[tbName]->addAction(mw->scrActions[ai.name]);
00332 else
00333 {
00334 ScToolBar *tb = new ScToolBar(ai.toolBarName, ai.toolbar, mw);
00335 tb->addAction(mw->scrActions[ai.name]);
00336 mw->addScToolBar(tb, tbName);
00337 }
00338 }
00339 if (it.value().enabled)
00340 ScCore->setSplashStatus( tr("Plugin: %1 initialized ok ", "plugin manager").arg(plugin->fullTrName()));
00341 else
00342 ScCore->setSplashStatus( tr("Plugin: %1 failed post initialization", "plugin manager").arg(plugin->fullTrName()));
00343 }
00344 else
00345 {
00346 it.value().plugin->addToMainWindowMenu(mw);
00347 }
00348
00349 }
00350 return true;
00351 }
00352
00353 bool PluginManager::DLLexists(QString name, bool includeDisabled) const
00354 {
00355
00356 if (pluginMap.contains(name))
00357 {
00358
00359 if (pluginMap[name].plugin)
00360 {
00361
00362 if (pluginMap[name].enabled)
00363 return true;
00364 else
00365 return includeDisabled;
00366 }
00367 }
00368 return false;
00369 }
00370
00371 bool PluginManager::loadPlugin(PluginData & pda)
00372 {
00373 typedef int (*getPluginAPIVersionPtr)();
00374 typedef ScPlugin* (*getPluginPtr)();
00375 getPluginAPIVersionPtr getPluginAPIVersion;
00376 getPluginPtr getPlugin;
00377
00378 Q_ASSERT(pda.plugin == 0);
00379 Q_ASSERT(pda.pluginDLL == 0);
00380 Q_ASSERT(!pda.enabled);
00381 pda.plugin = 0;
00382
00383 pda.pluginDLL = loadDLL(pda.pluginFile);
00384 if (!pda.pluginDLL)
00385 return false;
00386
00387 getPluginAPIVersion = (getPluginAPIVersionPtr)
00388 resolveSym(pda.pluginDLL, QString(pda.pluginName + "_getPluginAPIVersion").toLocal8Bit().data());
00389 if (getPluginAPIVersion)
00390 {
00391 int gotVersion = (*getPluginAPIVersion)();
00392 if ( gotVersion != PLUGIN_API_VERSION )
00393 {
00394 qDebug("API version mismatch when loading %s: Got %i, expected %i",
00395 pda.pluginFile.toLocal8Bit().data(), gotVersion, PLUGIN_API_VERSION);
00396 }
00397 else
00398 {
00399 getPlugin = (getPluginPtr)
00400 resolveSym(pda.pluginDLL, QString(pda.pluginName + "_getPlugin").toLocal8Bit().data());
00401 if (getPlugin)
00402 {
00403 pda.plugin = (*getPlugin)();
00404 if (!pda.plugin)
00405 {
00406 qDebug("Unable to get ScPlugin when loading %s",
00407 pda.pluginFile.toLocal8Bit().data());
00408 }
00409 else
00410 return true;
00411 }
00412 }
00413 }
00414 unloadDLL(pda.pluginDLL);
00415 pda.pluginDLL = 0;
00416 Q_ASSERT(!pda.plugin);
00417 return false;
00418 }
00419
00420 void PluginManager::cleanupPlugins()
00421 {
00422 for (PluginMap::Iterator it = pluginMap.begin(); it != pluginMap.end(); ++it)
00423 if (it.value().enabled == true)
00424 finalizePlug(it.value());
00425 }
00426
00427 void PluginManager::finalizePlug(PluginData & pda)
00428 {
00429 typedef void (*freePluginPtr)(ScPlugin* plugin);
00430 if (pda.plugin)
00431 {
00432 if (pda.enabled)
00433 disablePlugin(pda);
00434 Q_ASSERT(!pda.enabled);
00435 freePluginPtr freePlugin =
00436 (freePluginPtr) resolveSym(pda.pluginDLL, QString(pda.pluginName + "_freePlugin").toLocal8Bit().data());
00437 if ( freePlugin )
00438 (*freePlugin)( pda.plugin );
00439 pda.plugin = 0;
00440 }
00441 Q_ASSERT(!pda.enabled);
00442 if (pda.pluginDLL)
00443 {
00444 unloadDLL(pda.pluginDLL);
00445 pda.pluginDLL = 0;
00446 }
00447 }
00448
00449 void PluginManager::disablePlugin(PluginData & pda)
00450 {
00451 Q_ASSERT(pda.enabled);
00452 Q_ASSERT(pda.plugin);
00453 if (pda.plugin->inherits("ScActionPlugin"))
00454 {
00455 ScActionPlugin* plugin = dynamic_cast<ScActionPlugin*>(pda.plugin);
00456 assert(plugin);
00457
00458 delete ScCore->primaryMainWindow()->scrActions[plugin->actionInfo().name];
00459 }
00460 else if (pda.plugin->inherits("ScPersistentPlugin"))
00461 {
00462 ScPersistentPlugin* plugin = dynamic_cast<ScPersistentPlugin*>(pda.plugin);
00463 assert(plugin);
00464 plugin->cleanupPlugin();
00465 }
00466
00467 else if (pda.plugin->inherits("LoadSavePlugin"))
00468 pda.enabled = false;
00469 else
00470 Q_ASSERT(false);
00471 pda.enabled = false;
00472 }
00473
00474 QString PluginManager::platformDllExtension()
00475 {
00476 #ifdef __hpux
00477
00478 return "sl";
00479 #elif defined(__APPLE__) && defined(__MACH__)
00480
00481
00482
00483
00484
00485
00486
00487
00488 return "so";
00489 #elif defined(_WIN32) || defined(_WIN64)
00490 return "dll";
00491 #else
00492
00493 return "so";
00494 #endif
00495 }
00496
00497 int PluginManager::platformDllSearchFlags()
00498 {
00499 #if defined(_WIN32) || defined(_WIN64)
00500 return (QDir::Files | QDir::NoSymLinks);
00501 #else
00502 return (QDir::Files | QDir::Executable | QDir::NoSymLinks);
00503 #endif
00504 }
00505
00506 void PluginManager::changeEvent(QEvent *e)
00507 {
00508 if (e->type() == QEvent::LanguageChange)
00509 {
00510 languageChange();
00511 }
00512 }
00513
00514 void PluginManager::languageChange()
00515 {
00516 ScPlugin* plugin = 0;
00517 ScActionPlugin* ixplug = 0;
00518 ScrAction* pluginAction = 0;
00519 for (PluginMap::Iterator it = pluginMap.begin(); it != pluginMap.end(); ++it)
00520 {
00521 plugin = it.value().plugin;
00522 if (plugin)
00523 {
00524 plugin->languageChange();
00525 ixplug = dynamic_cast<ScActionPlugin*>(plugin);
00526 if (ixplug)
00527 {
00528 ScActionPlugin::ActionInfo ai(ixplug->actionInfo());
00529 pluginAction = ScCore->primaryMainWindow()->scrActions[ai.name];
00530 if (pluginAction != 0)
00531 pluginAction->setText( ai.text );
00532 }
00533 }
00534 }
00535 }
00536
00537 ScPlugin* PluginManager::getPlugin(const QString & pluginName, bool includeDisabled) const
00538 {
00539 if (DLLexists(pluginName, includeDisabled))
00540 return pluginMap[pluginName].plugin;
00541 return 0;
00542 }
00543
00544 PluginManager & PluginManager::instance()
00545 {
00546 return (*ScCore->pluginManager);
00547 }
00548
00549 const QString PluginManager::getPluginPath(const QString & pluginName) const
00550 {
00551
00552
00553 Q_ASSERT(pluginMap.contains(pluginName));
00554 return pluginMap[pluginName].pluginFile;
00555 }
00556
00557 bool & PluginManager::enableOnStartup(const QString & pluginName)
00558 {
00559
00560
00561 Q_ASSERT(pluginMap.contains(pluginName));
00562 return pluginMap[pluginName].enableOnStartup;
00563 }
00564
00565 bool PluginManager::enabled(const QString & pluginName)
00566 {
00567
00568
00569 Q_ASSERT(pluginMap.contains(pluginName));
00570 return pluginMap[pluginName].enabled;
00571 }
00572
00573 QStringList PluginManager::pluginNames(
00574 bool includeDisabled, const char* inherits) const
00575 {
00576
00577 QStringList names;
00578 for (PluginMap::ConstIterator it = pluginMap.constBegin(); it != pluginMap.constEnd(); ++it)
00579 if (includeDisabled || it.value().enabled)
00580
00581
00582 if (!inherits || it.value().plugin->inherits(inherits))
00583 names.append(it.value().pluginName);
00584 return names;
00585 }