1 /*
2 For general Scribus (>=1.3.2) copyright and licensing information please refer
3 to the COPYING file provided with the program. Following this notice may exist
4 a copyright and/or license notice that predates the release of Scribus 1.3.2
5 for which a new license (GPL+exception) is in place.
6 */
7 #include "pluginmanager.h"
8 #include "scplugin.h"
9 #include "loadsaveplugin.h"
10
11 #include <QDir>
12 #include <QEvent>
13 #include <QMessageBox>
14
15 #include "scconfig.h"
16
17
18 #include "scribusdoc.h"
19 #include "scribuscore.h"
20 #include "ui/sctoolbar.h"
21 #include "selection.h"
22 #include "ui/scmwmenumanager.h"
23 #include "scraction.h"
24 #include "ui/splash.h"
25 #include "prefsmanager.h"
26 #include "prefsfile.h"
27 #include "scpaths.h"
28 #include "commonstrings.h"
29 #include "ui/storyeditor.h"
30
31 #ifdef HAVE_DLFCN_H
32 #include <dlfcn.h>
33 #elif defined(DLL_USE_NATIVE_API) && defined(_WIN32)
34 #include <windows.h>
35 #else
36 #include <QLibrary>
37 #endif
38
PluginManager()39 PluginManager::PluginManager() :
40 QObject(nullptr),
41 prefs(PrefsManager::instance().prefsFile->getPluginContext("pluginmanager"))
42 {
43 }
44
~PluginManager()45 PluginManager::~PluginManager()
46 {
47 }
48
loadDLL(const QString & plugin)49 void* PluginManager::loadDLL(const QString& plugin)
50 {
51 void* lib = nullptr;
52 #ifdef HAVE_DLFCN_H
53 QString libpath = QDir::toNativeSeparators(plugin);
54 lib = dlopen(libpath.toLocal8Bit().data(), RTLD_LAZY | RTLD_GLOBAL);
55 if (!lib)
56 {
57 const char* error = dlerror();
58 qDebug() << tr("Error loading plugin", "plugin manager").toLocal8Bit().data();
59 if (error)
60 qDebug("%s", error);
61 else
62 qDebug() << tr("Unknown error","plugin manager").toLocal8Bit().data();
63 }
64 #elif defined(DLL_USE_NATIVE_API) && defined(_WIN32)
65 QString libpath = QDir::toNativeSeparators(plugin);
66 HINSTANCE hdll = LoadLibraryW((const wchar_t*) libpath.utf16());
67 lib = (void*) hdll;
68 #else
69 if (QFile::exists(plugin))
70 lib = (void*) new QLibrary(plugin);
71 else
72 {
73 qDebug() << tr("Error loading plugin", "plugin manager").toLocal8Bit().data();
74 qDebug("%s", plugin.toLocal8Bit().data());
75 }
76 #endif
77 return lib;
78 }
79
resolveSym(void * plugin,const char * sym)80 void* PluginManager::resolveSym(void* plugin, const char* sym)
81 {
82 void* symAddr = nullptr;
83 #ifdef HAVE_DLFCN_H
84 const char* error;
85 dlerror();
86 symAddr = dlsym(plugin, sym);
87 if ((error = dlerror()) != nullptr)
88 {
89 qDebug("%s", tr("Cannot find symbol (%1)", "plugin manager").arg(error).toLocal8Bit().data());
90 symAddr = nullptr;
91 }
92 #elif defined(DLL_USE_NATIVE_API) && defined(_WIN32)
93 symAddr = (void* ) GetProcAddress((HMODULE) plugin, sym);
94 if (symAddr == nullptr)
95 qDebug("%s", tr("Cannot find symbol (%1)", "plugin manager").arg(sym).toLocal8Bit().data());
96 #else
97 QLibrary* qlib = (QLibrary*) plugin;
98 if (plugin)
99 symAddr = qlib->resolve(sym);
100 if (symAddr == nullptr)
101 qDebug("%s", tr("Cannot find symbol (%1)", "plugin manager").arg(sym).toLocal8Bit().data());
102 #endif
103 return symAddr;
104 }
105
unloadDLL(void * plugin)106 void PluginManager::unloadDLL(void* plugin)
107 {
108 #ifdef HAVE_DLFCN_H
109 dlclose(plugin);
110 dlerror();
111 #elif defined(DLL_USE_NATIVE_API) && defined(_WIN32)
112 FreeLibrary((HMODULE) plugin);
113 #else
114 delete ((QLibrary*) plugin);
115 #endif
116 }
117
savePreferences()118 void PluginManager::savePreferences()
119 {
120 // write configuration
121 for (auto it = pluginMap.constBegin(); it != pluginMap.constEnd(); ++it)
122 {
123 const PluginData& pluginData = it.value();
124 prefs->set(pluginData.pluginName, pluginData.enableOnStartup);
125 }
126 }
127
getPluginName(const QString & fileName)128 QString PluginManager::getPluginName(const QString& fileName)
129 {
130 // Must return plugin name. Note that this may be platform dependent;
131 // it's likely to need some adjustment for platform naming schemes.
132 // It currently handles:
133 // (lib)?pluginname(\.pluginext)?
134 QFileInfo fi(fileName);
135 QString baseName(fi.baseName());
136 if (baseName.startsWith("lib"))
137 baseName = baseName.remove(0, 3);
138 if (baseName.endsWith(platformDllExtension()))
139 baseName.chop(1 + platformDllExtension().length());
140 // check name
141 for (int i = 0; i < (int) baseName.length(); i++)
142 {
143 if (! baseName[i].isLetterOrNumber() && baseName[i] != '_' )
144 {
145 qDebug("Invalid character in plugin name for %s; skipping",
146 fileName.toLocal8Bit().data());
147 return QString();
148 }
149 }
150 return baseName.toLatin1();
151 }
152
initPlugin(const QString & fileName)153 int PluginManager::initPlugin(const QString& fileName)
154 {
155 PluginData pda;
156 pda.pluginFile = QString("%1/%2").arg(ScPaths::instance().pluginDir(), fileName);
157 pda.pluginName = getPluginName(pda.pluginFile);
158 if (pda.pluginName.isNull())
159 // Couldn't determine plugname from filename. We've already complained, so
160 // move on to the next one.
161 return 0;
162 pda.plugin = nullptr;
163 pda.pluginDLL = nullptr;
164 pda.enabled = false;
165 pda.enableOnStartup = prefs->getBool(pda.pluginName, false);
166 ScCore->setSplashStatus( tr("Plugin: loading %1", "plugin manager").arg(pda.pluginName));
167 if (loadPlugin(pda))
168 {
169 //HACK: Always enable our only persistent plugin, scripter
170 if (pda.plugin->inherits("ScPersistentPlugin"))
171 pda.enableOnStartup = true;
172 if (pda.enableOnStartup)
173 enablePlugin(pda);
174 pluginMap.insert(pda.pluginName, pda);
175 return 1;
176 }
177 return 0;
178 }
179
initPlugs()180 void PluginManager::initPlugs()
181 {
182 Q_ASSERT(!pluginMap.count());
183 QString libPattern = QString("*.%1*").arg(platformDllExtension());
184 QMap<QString, int> allPlugs;
185 int loaded = 0;
186 uint changes = 1;
187 QStringList failedPlugs; // output string for warn dialog
188
189 /*! \note QDir::Reversed is there due the Mac plugin dependency.
190 barcode depends on psimport. and load on that platform expect the
191 psimp before barcode.You know, security by obscurity ;) PV */
192 QDir dirList(ScPaths::instance().pluginDir(),
193 libPattern, QDir::Name | QDir::Reversed,
194 (QDir::Filter) PluginManager::platformDllSearchFlags());
195
196 if ((!dirList.exists()) || (dirList.count() == 0))
197 return;
198 for (uint i = 0; i < dirList.count(); ++i)
199 {
200 int res = initPlugin(dirList[i]);
201 allPlugs[dirList[i]] = res;
202 if (res != 0)
203 ++loaded;
204 else
205 failedPlugs.append(dirList[i]);
206 }
207 /* Re-try the failed plugins again and again until it promote
208 any progress (changes variable is changing ;)) */
209 QMap<QString, int>::Iterator it;
210 while (loaded < allPlugs.count() && changes!=0)
211 {
212 changes = 0;
213 for (it = allPlugs.begin(); it != allPlugs.end(); ++it)
214 {
215 if (it.value() != 0)
216 continue;
217 int res = initPlugin(it.key());
218 allPlugs[it.key()] = res;
219 if (res == 1)
220 {
221 ++loaded;
222 ++changes;
223 failedPlugs.removeAll(it.key());
224 }
225 }
226 }
227 if (loaded != allPlugs.count())
228 {
229 if (ScCore->usingGUI())
230 {
231 bool splashShown = ScCore->splashShowing();
232 QString failedStr("<ul>");
233 for (QStringList::Iterator it = failedPlugs.begin(); it != failedPlugs.end(); ++it)
234 failedStr += "<li>" + *it + "</li>";
235 failedStr += "</ul>";
236 if (splashShown)
237 ScCore->showSplash(false);
238 ScMessageBox::warning(ScCore->primaryMainWindow(), CommonStrings::trWarning,
239 "<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."
240 ).arg(allPlugs.count() - loaded).arg(allPlugs.count()).arg(failedStr)
241 + "</qt>");
242 if (splashShown)
243 ScCore->showSplash(true);
244 }
245 }
246 }
247
248 // After a plugin has been initialized, this method calls its setup
249 // routines and connects it to the application.
enablePlugin(PluginData & pda)250 void PluginManager::enablePlugin(PluginData & pda)
251 {
252 Q_ASSERT(pda.enabled == false);
253 QString failReason;
254 bool isActionPlugin = false;
255 if (pda.plugin->inherits("ScActionPlugin"))
256 {
257 // ScActionPlugin* plugin = dynamic_cast<ScActionPlugin*>(pda.plugin);
258 // Q_ASSERT(plugin);
259 isActionPlugin = true;
260 /*
261 pda.enabled = setupPluginActions(plugin);
262 if (!pda.enabled)
263 failReason = tr("init failed", "plugin load error");
264 */
265 }
266 else if (pda.plugin->inherits("ScPersistentPlugin"))
267 {
268 ScPersistentPlugin* plugin = qobject_cast<ScPersistentPlugin*>(pda.plugin);
269 assert(plugin);
270 pda.enabled = plugin->initPlugin();
271 if (!pda.enabled)
272 failReason = tr("init failed", "plugin load error");
273 }
274 /* temporary hack to enable the import plugins */
275 else if (pda.plugin->inherits("LoadSavePlugin"))
276 pda.enabled = true;
277 else
278 failReason = tr("unknown plugin type", "plugin load error");
279
280 if (pda.enabled || isActionPlugin)
281 ScCore->setSplashStatus(tr("Plugin: %1 loaded", "plugin manager").arg(pda.plugin->fullTrName()));
282 else
283 ScCore->setSplashStatus(tr("Plugin: %1 failed to load: %2", "plugin manager").arg(pda.plugin->fullTrName(), failReason));
284 }
285
setupPluginActions(ScribusMainWindow * mw)286 bool PluginManager::setupPluginActions(ScribusMainWindow *mw)
287 {
288 Q_CHECK_PTR(mw);
289 ScActionPlugin* plugin = nullptr;
290
291 //mw->scrMenuMgr->addMenuItemString("SEPARATOR", "Extras");
292 for (PluginMap::Iterator it = pluginMap.begin(); it != pluginMap.end(); ++it)
293 {
294 if (!it.value().plugin->inherits("ScActionPlugin"))
295 {
296 it.value().plugin->addToMainWindowMenu(mw);
297 continue;
298 }
299
300 //Add in ScrAction based plugin linkage
301 //Insert DLL Action into Dictionary with values from plugin interface
302 plugin = qobject_cast<ScActionPlugin*>(it.value().plugin);
303 assert(plugin);
304 ScActionPlugin::ActionInfo ai(plugin->actionInfo());
305 ScrAction* action = new ScrAction(ScrAction::ActionDLL, ai.iconPath1, ai.iconPath2, ai.text, ai.keySequence, mw);
306 Q_CHECK_PTR(action);
307 action->setStatusTip(ai.helpText);
308 action->setToolTip(ai.helpText);
309 mw->scrActions.insert(ai.name, action);
310
311 // then enable and connect up the action
312 mw->scrActions[ai.name]->setEnabled(ai.enabledOnStartup);
313
314 // Connect action's activated signal with the plugin's run method
315 it.value().enabled = connect(mw->scrActions[ai.name], SIGNAL(triggeredData(ScribusDoc*)), plugin, SLOT(run(ScribusDoc*)) );
316
317 //Get the menu manager to add the DLL's menu item to the right menu, after the chosen existing item
318 if (ai.menuAfterName.isEmpty())
319 {
320 if (!ai.menu.isEmpty())
321 {
322 if ((!ai.subMenuName.isEmpty()) && (!ai.parentMenu.isEmpty()))
323 {
324 if (!mw->scrMenuMgr->menuExists(ai.menu))
325 {
326 mw->scrMenuMgr->createMenu(ai.menu, ai.subMenuName, ai.parentMenu);
327 }
328 }
329 mw->scrMenuMgr->addMenuItemString(ai.name, ai.menu);
330 }
331 }
332 else
333 {
334 // QString actionName(ai.menu.toLower()+ai.menuAfterName);
335 // QString actionName(ai.menuAfterName);
336 // ScrAction* afterAction = nullptr;
337 // if (mw->scrActions.contains(actionName))
338 // afterAction = mw->scrActions[actionName];
339 if ((!ai.subMenuName.isEmpty()) && (!ai.parentMenu.isEmpty()))
340 {
341 if (!mw->scrMenuMgr->menuExists(ai.menu))
342 mw->scrMenuMgr->createMenu(ai.menu, ai.subMenuName, ai.parentMenu);
343 }
344 mw->scrMenuMgr->addMenuItemStringAfter(ai.name, ai.menuAfterName, ai.menu);
345 }
346 if (!ai.toolbar.isEmpty())
347 {
348 QString tbName = ai.toolbar;
349 if (mw->scrToolBars.contains(tbName))
350 mw->scrToolBars[tbName]->addAction(mw->scrActions[ai.name]);
351 else
352 {
353 ScToolBar *tb = new ScToolBar(ai.toolBarName, ai.toolbar, mw);
354 tb->addAction(mw->scrActions[ai.name]);
355 mw->addScToolBar(tb, tbName);
356 }
357 }
358 if (it.value().enabled)
359 ScCore->setSplashStatus( tr("Plugin: %1 initialized ok ", "plugin manager").arg(plugin->fullTrName()));
360 else
361 ScCore->setSplashStatus( tr("Plugin: %1 failed post initialization", "plugin manager").arg(plugin->fullTrName()));
362 }
363
364 //CB maybe we should just call mw->createMenuBar() here instead...
365 mw->scrMenuMgr->clearMenu("File");
366 mw->scrMenuMgr->addMenuItemStringsToMenuBar("File", mw->scrActions);
367 mw->scrMenuMgr->clearMenu("Edit");
368 mw->scrMenuMgr->addMenuItemStringsToMenuBar("Edit", mw->scrActions);
369 mw->scrMenuMgr->clearMenu("Insert");
370 mw->scrMenuMgr->addMenuItemStringsToMenuBar("Insert", mw->scrActions);
371 mw->scrMenuMgr->clearMenu("Item");
372 mw->scrMenuMgr->addMenuItemStringsToMenuBar("Item", mw->scrActions);
373 mw->scrMenuMgr->clearMenu("Page");
374 mw->scrMenuMgr->addMenuItemStringsToMenuBar("Page", mw->scrActions);
375 mw->scrMenuMgr->clearMenu("ItemTable");
376 mw->scrMenuMgr->addMenuItemStringsToMenuBar("ItemTable", mw->scrActions);
377 mw->scrMenuMgr->clearMenu("Extras");
378 mw->scrMenuMgr->addMenuItemStringsToMenuBar("Extras", mw->scrActions);
379 mw->scrMenuMgr->clearMenu("View");
380 mw->scrMenuMgr->addMenuItemStringsToMenuBar("View", mw->scrActions);
381 mw->scrMenuMgr->clearMenu("Help");
382 mw->scrMenuMgr->addMenuItemStringsToMenuBar("Help", mw->scrActions);
383
384 return true;
385 }
386
setupPluginActions(StoryEditor * sew)387 bool PluginManager::setupPluginActions(StoryEditor *sew)
388 {
389 Q_CHECK_PTR(sew);
390 ScActionPlugin* plugin = nullptr;
391
392 //sew->seMenuMgr->addMenuSeparator("Extras");
393 for (PluginMap::Iterator it = pluginMap.begin(); it != pluginMap.end(); ++it)
394 {
395 if (!it.value().plugin->inherits("ScActionPlugin"))
396 continue;
397
398 //Add in ScrAction based plugin linkage
399 //Insert DLL Action into Dictionary with values from plugin interface
400 plugin = qobject_cast<ScActionPlugin*>(it.value().plugin);
401 assert(plugin);
402 ScActionPlugin::ActionInfo ai(plugin->actionInfo());
403 if (!ai.enabledForStoryEditor)
404 continue;
405
406 ScrAction* action = new ScrAction(ScrAction::ActionDLLSE, ai.iconPath1, ai.iconPath2, ai.text, ai.keySequence, sew);
407 Q_CHECK_PTR(action);
408 sew->seActions.insert(ai.name, action);
409
410 // then enable and connect up the action
411 sew->seActions[ai.name]->setEnabled(ai.enabledForStoryEditor);
412
413 // Connect action's activated signal with the plugin's run method
414 it.value().enabled = connect(sew->seActions[ai.name], SIGNAL(triggeredData(QWidget*, ScribusDoc*)), plugin, SLOT(run(QWidget*, ScribusDoc*)));
415
416 //Get the menu manager to add the DLL's menu item to the right menu, after the chosen existing item
417 if (ai.menuAfterName.isEmpty())
418 {
419 if (!ai.seMenu.isEmpty())
420 {
421 if ((!ai.subMenuName.isEmpty()) && (!ai.parentMenu.isEmpty()))
422 {
423 if (!sew->seMenuMgr->menuExists(ai.seMenu))
424 sew->seMenuMgr->createMenu(ai.seMenu, ai.subMenuName, ai.parentMenu);
425 }
426 // sew->seMenuMgr->addMenuItem(sew->seActions[ai.name], ai.seMenu, true);
427 sew->seMenuMgr->addMenuItemString(ai.name, ai.menu);
428 }
429 }
430 else
431 {
432 // QString actionName(ai.seMenu.toLower()+ai.menuAfterName);
433 // ScrAction* afterAction = nullptr;
434 // if (sew->seActions.contains(actionName))
435 // afterAction = sew->seActions[actionName];
436 if ((!ai.subMenuName.isEmpty()) && (!ai.parentMenu.isEmpty()))
437 {
438 if (!sew->seMenuMgr->menuExists(ai.seMenu))
439 sew->seMenuMgr->createMenu(ai.seMenu, ai.subMenuName, ai.parentMenu);
440 }
441 // sew->seMenuMgr->addMenuItemAfter(sew->seActions[ai.name], ai.seMenu, true, afterAction);
442 sew->seMenuMgr->addMenuItemStringAfter(ai.name, ai.menuAfterName, ai.menu);
443 }
444 }
445 return true;
446 }
447
448
enableOnlyStartupPluginActions(ScribusMainWindow * mw)449 void PluginManager::enableOnlyStartupPluginActions(ScribusMainWindow* mw)
450 {
451 Q_CHECK_PTR(mw);
452 ScActionPlugin* plugin = nullptr;
453 for (PluginMap::Iterator it = pluginMap.begin(); it != pluginMap.end(); ++it)
454 {
455 if (!it.value().plugin->inherits("ScActionPlugin"))
456 continue;
457 plugin = qobject_cast<ScActionPlugin*>(it.value().plugin);
458 assert(plugin);
459 ScActionPlugin::ActionInfo ai(plugin->actionInfo());
460 if (mw->scrActions.contains(ai.name))
461 mw->scrActions[ai.name]->setEnabled(ai.enabledOnStartup);
462 }
463 }
464
enablePluginActionsForSelection(ScribusMainWindow * mw)465 void PluginManager::enablePluginActionsForSelection(ScribusMainWindow* mw)
466 {
467 Q_CHECK_PTR(mw);
468 ScribusDoc* doc = mw->doc;
469 if (!doc)
470 return;
471
472 int selectedType = -1;
473 if (doc->m_Selection->count() > 0)
474 {
475 PageItem *currItem = doc->m_Selection->itemAt(0);
476 selectedType = currItem->itemType();
477 }
478 bool isLayerLocked = doc->layerLocked(doc->activeLayer());
479
480 ScActionPlugin* actionPlug = nullptr;
481 ScrAction* pluginAction = nullptr;
482 for (PluginMap::Iterator it = pluginMap.begin(); it != pluginMap.end(); ++it)
483 {
484 if (!it.value().plugin->inherits("ScActionPlugin"))
485 continue;
486 actionPlug = qobject_cast<ScActionPlugin*>(it.value().plugin);
487 if (!actionPlug)
488 continue;
489
490 ScActionPlugin::ActionInfo actionInfo(actionPlug->actionInfo());
491 pluginAction = mw->scrActions[actionInfo.name];
492 if (pluginAction == nullptr)
493 continue;
494 if (isLayerLocked && !actionInfo.enabledOnStartup)
495 pluginAction->setEnabled(false);
496 else
497 pluginAction->setEnabled(actionPlug->handleSelection(doc, selectedType));
498 }
499 }
500
DLLexists(const QString & name,bool includeDisabled) const501 bool PluginManager::DLLexists(const QString& name, bool includeDisabled) const
502 {
503 // the plugin name must be known
504 if (pluginMap.contains(name))
505 {
506 // the plugin must be loaded
507 if (pluginMap[name].plugin)
508 {
509 // and the plugin must be enabled unless we were told otherwise
510 if (pluginMap[name].enabled)
511 return true;
512 return includeDisabled;
513 }
514 }
515 return false;
516 }
517
loadPlugin(PluginData & pluginData)518 bool PluginManager::loadPlugin(PluginData& pluginData)
519 {
520 typedef int (*getPluginAPIVersionPtr)();
521 typedef ScPlugin* (*getPluginPtr)();
522 getPluginAPIVersionPtr getPluginAPIVersion;
523 getPluginPtr getPlugin;
524
525 Q_ASSERT(pluginData.plugin == nullptr);
526 Q_ASSERT(pluginData.pluginDLL == nullptr);
527 Q_ASSERT(!pluginData.enabled);
528 pluginData.plugin = nullptr;
529
530 pluginData.pluginDLL = loadDLL(pluginData.pluginFile);
531 if (!pluginData.pluginDLL)
532 return false;
533
534 getPluginAPIVersion = (getPluginAPIVersionPtr)
535 resolveSym(pluginData.pluginDLL, QString(pluginData.pluginName + "_getPluginAPIVersion").toLocal8Bit().data());
536 if (getPluginAPIVersion)
537 {
538 int gotVersion = (*getPluginAPIVersion)();
539 if (gotVersion != PLUGIN_API_VERSION)
540 {
541 qDebug("API version mismatch when loading %s: Got %i, expected %i",
542 pluginData.pluginFile.toLocal8Bit().data(), gotVersion, PLUGIN_API_VERSION);
543 }
544 else
545 {
546 getPlugin = (getPluginPtr)
547 resolveSym(pluginData.pluginDLL, QString(pluginData.pluginName + "_getPlugin").toLocal8Bit().data());
548 if (getPlugin)
549 {
550 pluginData.plugin = (*getPlugin)();
551 if (!pluginData.plugin)
552 {
553 qDebug("Unable to get ScPlugin when loading %s",
554 pluginData.pluginFile.toLocal8Bit().data());
555 }
556 else
557 return true;
558 }
559 }
560 }
561 unloadDLL(pluginData.pluginDLL);
562 pluginData.pluginDLL = nullptr;
563 Q_ASSERT(!pluginData.plugin);
564 return false;
565 }
566
cleanupPlugins()567 void PluginManager::cleanupPlugins()
568 {
569 for (PluginMap::Iterator it = pluginMap.begin(); it != pluginMap.end(); ++it)
570 {
571 if (!it.value().enabled)
572 continue;
573 finalizePlug(it.value());
574 }
575 }
576
finalizePlug(PluginData & pluginData)577 void PluginManager::finalizePlug(PluginData & pluginData)
578 {
579 typedef void (*freePluginPtr)(ScPlugin* plugin);
580 if (pluginData.plugin)
581 {
582 if (pluginData.enabled)
583 disablePlugin(pluginData);
584 Q_ASSERT(!pluginData.enabled);
585 freePluginPtr freePlugin = (freePluginPtr) resolveSym(pluginData.pluginDLL, QString(pluginData.pluginName + "_freePlugin").toLocal8Bit().data());
586 if (freePlugin)
587 (*freePlugin)(pluginData.plugin);
588 pluginData.plugin = nullptr;
589 }
590 Q_ASSERT(!pluginData.enabled);
591 if (pluginData.pluginDLL)
592 {
593 unloadDLL(pluginData.pluginDLL);
594 pluginData.pluginDLL = nullptr;
595 }
596 }
597
disablePlugin(PluginData & pda)598 void PluginManager::disablePlugin(PluginData & pda)
599 {
600 Q_ASSERT(pda.enabled);
601 Q_ASSERT(pda.plugin);
602 if (pda.plugin->inherits("ScActionPlugin"))
603 {
604 ScActionPlugin* plugin = qobject_cast<ScActionPlugin*>(pda.plugin);
605 assert(plugin);
606 plugin->cleanupPlugin();
607 // FIXME: Correct way to delete action?
608 delete ScCore->primaryMainWindow()->scrActions[plugin->actionInfo().name];
609 }
610 else if (pda.plugin->inherits("ScPersistentPlugin"))
611 {
612 ScPersistentPlugin* plugin = qobject_cast<ScPersistentPlugin*>(pda.plugin);
613 assert(plugin);
614 plugin->cleanupPlugin();
615 }
616 /* temporary hack to enable the import plugins */
617 else if (pda.plugin->inherits("LoadSavePlugin"))
618 pda.enabled = false;
619 else
620 Q_ASSERT(false); // We shouldn't ever have enabled an unknown plugin type.
621 pda.enabled = false;
622 }
623
platformDllExtension()624 QString PluginManager::platformDllExtension()
625 {
626 #ifdef __hpux
627 // HP/UX
628 return "sl";
629 #elif defined(__APPLE__) && defined(__MACH__)
630 // MacOS/X, Darwin
631
632 // MacOS/X may actually use both 'so' and 'dylib'. .so is usually used
633 // for plugins etc, dylib for system and app libraries We need to
634 // support this distinction in the plugin manager, but for now it's
635 // most appropriate to return the extension used by plugins -- CR
636
637 //return "dylib";
638 return "so";
639 #elif defined(_WIN32) || defined(_WIN64)
640 return "dll";
641 #else
642 // Generic *NIX
643 return "so";
644 #endif
645 }
646
platformDllSearchFlags()647 int PluginManager::platformDllSearchFlags()
648 {
649 #if defined(_WIN32) || defined(_WIN64)
650 return (QDir::Files | QDir::NoSymLinks);
651 #else
652 return (QDir::Files | QDir::Executable | QDir::NoSymLinks);
653 #endif
654 }
655
languageChange()656 void PluginManager::languageChange()
657 {
658 ScPlugin* plugin = nullptr;
659 ScActionPlugin* ixplug = nullptr;
660 ScrAction* pluginAction = nullptr;
661 for (PluginMap::Iterator it = pluginMap.begin(); it != pluginMap.end(); ++it)
662 {
663 plugin = it.value().plugin;
664 if (!plugin)
665 continue;
666 plugin->languageChange();
667
668 ixplug = qobject_cast<ScActionPlugin*>(plugin);
669 if (!ixplug)
670 continue;
671
672 ScActionPlugin::ActionInfo ai(ixplug->actionInfo());
673 pluginAction = ScCore->primaryMainWindow()->scrActions[ai.name];
674 if (pluginAction != nullptr)
675 pluginAction->setText(ai.text);
676 if ((!ai.menu.isEmpty()) && (!ai.subMenuName.isEmpty()))
677 ScCore->primaryMainWindow()->scrMenuMgr->setText(ai.menu, ai.subMenuName);
678 }
679 }
680
getPlugin(const QString & pluginName,bool includeDisabled) const681 ScPlugin* PluginManager::getPlugin(const QString & pluginName, bool includeDisabled) const
682 {
683 if (DLLexists(pluginName, includeDisabled))
684 return pluginMap[pluginName].plugin;
685 return nullptr;
686 }
687
instance()688 PluginManager & PluginManager::instance()
689 {
690 return (*ScCore->pluginManager);
691 }
692
getPluginPath(const QString & pluginName) const693 QString PluginManager::getPluginPath(const QString & pluginName) const
694 {
695 // It is not legal to call this function without a valid
696 // plug in name.
697 Q_ASSERT(pluginMap.contains(pluginName));
698 return pluginMap[pluginName].pluginFile;
699 }
700
enableOnStartup(const QString & pluginName)701 bool & PluginManager::enableOnStartup(const QString & pluginName)
702 {
703 // It is not legal to call this function without a valid
704 // plug in name.
705 Q_ASSERT(pluginMap.contains(pluginName));
706 return pluginMap[pluginName].enableOnStartup;
707 }
708
enabled(const QString & pluginName)709 bool PluginManager::enabled(const QString & pluginName)
710 {
711 // It is not legal to call this function without a valid
712 // plug in name.
713 Q_ASSERT(pluginMap.contains(pluginName));
714 return pluginMap[pluginName].enabled;
715 }
716
pluginNames(bool includeDisabled,const char * inherits) const717 QStringList PluginManager::pluginNames(bool includeDisabled, const char* inherits) const
718 {
719 // Scan the plugin map for plugins...
720 QStringList names;
721 for (PluginMap::ConstIterator it = pluginMap.constBegin(); it != pluginMap.constEnd(); ++it)
722 {
723 if (!includeDisabled && !it.value().enabled)
724 continue;
725
726 // Only including plugins that inherit a named parent (if
727 // specified), using the QMetaObject system.
728 if (!inherits || it.value().plugin->inherits(inherits))
729 names.append(it.value().pluginName);
730 }
731 return names;
732 }
733