1 #include "pluginmanagerimpl.h"
2 #include "plugins/scriptingplugin.h"
3 #include "plugins/genericplugin.h"
4 #include "services/notifymanager.h"
5 #include "common/unused.h"
6 #include "translations.h"
7 #include <QCoreApplication>
8 #include <QDir>
9 #include <QDebug>
10 #include <QJsonArray>
11 #include <QJsonValue>
12
PluginManagerImpl()13 PluginManagerImpl::PluginManagerImpl()
14 {
15 }
16
~PluginManagerImpl()17 PluginManagerImpl::~PluginManagerImpl()
18 {
19 }
20
init()21 void PluginManagerImpl::init()
22 {
23 if (getDistributionType() != DistributionType::OS_MANAGED)
24 pluginDirs += qApp->applicationDirPath() + "/plugins";
25
26 pluginDirs += "/usr/local/lib/sqlitestudio";
27
28 QString envDirs = SQLITESTUDIO->getEnv("SQLITESTUDIO_PLUGINS");
29 if (!envDirs.isNull())
30 pluginDirs += envDirs.split(PATH_LIST_SEPARATOR);
31
32 #ifdef PLUGINS_DIR
33 pluginDirs += STRINGIFY(PLUGINS_DIR);
34 #endif
35
36 #ifdef SYS_PLUGINS_DIR
37 pluginDirs += STRINGIFY(SYS_PLUGINS_DIR);
38 #endif
39
40 #ifdef Q_OS_MACX
41 pluginDirs += QCoreApplication::applicationDirPath()+"/../PlugIns";
42 #endif
43
44 scanPlugins();
45 loadPlugins();
46 }
47
deinit()48 void PluginManagerImpl::deinit()
49 {
50 emit aboutToQuit();
51
52 // Plugin containers and their plugins
53 for (PluginContainer* container : pluginContainer.values())
54 {
55 if (container->builtIn)
56 {
57 container->plugin->deinit();
58 delete container->plugin;
59 }
60 else
61 unload(container->name);
62 }
63
64 for (PluginContainer* container : pluginContainer.values())
65 delete container;
66
67 pluginContainer.clear();
68
69 // Types
70 for (PluginType* type : registeredPluginTypes)
71 delete type;
72
73 registeredPluginTypes.clear();
74 pluginCategories.clear();
75 }
76
getPluginTypes() const77 QList<PluginType*> PluginManagerImpl::getPluginTypes() const
78 {
79 return registeredPluginTypes;
80 }
81
getPluginDirs() const82 QStringList PluginManagerImpl::getPluginDirs() const
83 {
84 return pluginDirs;
85 }
86
getFilePath(Plugin * plugin) const87 QString PluginManagerImpl::getFilePath(Plugin* plugin) const
88 {
89 if (!pluginContainer.contains(plugin->getName()))
90 return QString();
91
92 return pluginContainer[plugin->getName()]->filePath;
93 }
94
loadBuiltInPlugin(Plugin * plugin)95 bool PluginManagerImpl::loadBuiltInPlugin(Plugin* plugin)
96 {
97 bool res = initPlugin(plugin);
98 res &= plugin->init();
99 return res;
100 }
101
getPluginType(Plugin * plugin) const102 PluginType* PluginManagerImpl::getPluginType(Plugin* plugin) const
103 {
104 if (!pluginContainer.contains(plugin->getName()))
105 return nullptr;
106
107 return pluginContainer[plugin->getName()]->type;
108 }
109
scanPlugins()110 void PluginManagerImpl::scanPlugins()
111 {
112 QStringList nameFilters;
113 nameFilters << "*.so" << "*.dll" << "*.dylib";
114
115 QPluginLoader* loader = nullptr;
116 for (QString pluginDirPath : pluginDirs)
117 {
118 QDir pluginDir(pluginDirPath);
119 for (QString fileName : pluginDir.entryList(nameFilters, QDir::Files))
120 {
121 fileName = pluginDir.absoluteFilePath(fileName);
122 loader = new QPluginLoader(fileName);
123 loader->setLoadHints(QLibrary::ExportExternalSymbolsHint|QLibrary::ResolveAllSymbolsHint);
124
125 if (!initPlugin(loader, fileName))
126 {
127 qDebug() << "File" << fileName << "was loaded as plugin, but SQLiteStudio couldn't initialize plugin.";
128 delete loader;
129 }
130 }
131 }
132
133 QStringList names;
134 for (PluginContainer* container : pluginContainer.values())
135 {
136 if (!container->builtIn)
137 names << container->name;
138 }
139
140 qDebug() << "Following plugins found:" << names;
141 }
142
loadPlugins()143 void PluginManagerImpl::loadPlugins()
144 {
145 QStringList alreadyAttempted;
146 for (const QString& pluginName : pluginContainer.keys())
147 {
148 if (shouldAutoLoad(pluginName))
149 load(pluginName, alreadyAttempted);
150 }
151
152 pluginsAreInitiallyLoaded = true;
153 emit pluginsInitiallyLoaded();
154 }
155
initPlugin(QPluginLoader * loader,const QString & fileName)156 bool PluginManagerImpl::initPlugin(QPluginLoader* loader, const QString& fileName)
157 {
158 QJsonObject pluginMetaData = loader->metaData();
159 QString pluginTypeName = pluginMetaData.value("MetaData").toObject().value("type").toString();
160 PluginType* pluginType = nullptr;
161 for (PluginType* type : registeredPluginTypes)
162 {
163 if (type->getName() == pluginTypeName)
164 {
165 pluginType = type;
166 break;
167 }
168 }
169
170 if (!pluginType)
171 {
172 qWarning() << "Could not load plugin" << fileName << "because its type was not recognized:" << pluginTypeName;
173 return false;
174 }
175
176 QString pluginName = pluginMetaData.value("className").toString();
177 QJsonObject metaObject = pluginMetaData.value("MetaData").toObject();
178
179 if (!checkPluginRequirements(pluginName, metaObject))
180 return false;
181
182 PluginContainer* container = new PluginContainer;
183 container->type = pluginType;
184 container->filePath = fileName;
185 container->loaded = false;
186 container->loader = loader;
187 pluginCategories[pluginType] << container;
188 pluginContainer[pluginName] = container;
189
190 if (!readDependencies(pluginName, container, metaObject.value("dependencies")))
191 return false;
192
193 if (!readConflicts(pluginName, container, metaObject.value("conflicts")))
194 return false;
195
196 if (!readMetaData(container))
197 {
198 delete container;
199 return false;
200 }
201
202 return true;
203 }
204
checkPluginRequirements(const QString & pluginName,const QJsonObject & metaObject)205 bool PluginManagerImpl::checkPluginRequirements(const QString& pluginName, const QJsonObject& metaObject)
206 {
207 if (metaObject.value("gui").toBool(false) && !SQLITESTUDIO->isGuiAvailable())
208 {
209 qDebug() << "Plugin" << pluginName << "skipped, because it requires GUI and this is not GUI client running.";
210 return false;
211 }
212
213 int minVer = metaObject.value("minQtVersion").toInt(0);
214 if (QT_VERSION_CHECK(minVer / 10000, minVer / 100 % 100, minVer % 10000) > QT_VERSION)
215 {
216 qDebug() << "Plugin" << pluginName << "skipped, because it requires at least Qt version" << toPrintableVersion(minVer) << ", but got" << QT_VERSION_STR;
217 return false;
218 }
219
220 int maxVer = metaObject.value("maxQtVersion").toInt(999999);
221 if (QT_VERSION_CHECK(maxVer / 10000, maxVer / 100 % 100, maxVer % 10000) < QT_VERSION)
222 {
223 qDebug() << "Plugin" << pluginName << "skipped, because it requires at most Qt version" << toPrintableVersion(maxVer) << ", but got" << QT_VERSION_STR;
224 return false;
225 }
226
227 minVer = metaObject.value("minAppVersion").toInt(0);
228 if (SQLITESTUDIO->getVersion() < minVer)
229 {
230 qDebug() << "Plugin" << pluginName << "skipped, because it requires at least SQLiteStudio version" << toPrintableVersion(minVer) << ", but got"
231 << SQLITESTUDIO->getVersionString();
232 return false;
233 }
234
235 maxVer = metaObject.value("maxAppVersion").toInt(999999);
236 if (SQLITESTUDIO->getVersion() > maxVer)
237 {
238 qDebug() << "Plugin" << pluginName << "skipped, because it requires at most SQLiteStudio version" << toPrintableVersion(maxVer) << ", but got"
239 << SQLITESTUDIO->getVersionString();
240 return false;
241 }
242
243 return true;
244 }
245
readDependencies(const QString & pluginName,PluginManagerImpl::PluginContainer * container,const QJsonValue & depsValue)246 bool PluginManagerImpl::readDependencies(const QString& pluginName, PluginManagerImpl::PluginContainer* container, const QJsonValue& depsValue)
247 {
248 if (depsValue.isUndefined())
249 return true;
250
251 QJsonArray depsArray;
252 if (depsValue.type() == QJsonValue::Array)
253 depsArray = depsValue.toArray();
254 else
255 depsArray.append(depsValue);
256
257 PluginDependency dep;
258 QJsonObject depObject;
259 for (const QJsonValue& value : depsArray)
260 {
261 if (value.type() == QJsonValue::Object)
262 {
263 depObject = value.toObject();
264 if (!depObject.contains("name"))
265 {
266 qWarning() << "Invalid dependency entry in plugin" << pluginName << " - doesn't contain 'name' of the dependency.";
267 return false;
268 }
269
270 dep.name = depObject.value("name").toString();
271 dep.minVersion = depObject.value("minVersion").toInt(0);
272 dep.maxVersion = depObject.value("maxVersion").toInt(0);
273 }
274 else
275 {
276 dep.maxVersion = 0;
277 dep.minVersion = 0;
278 dep.name = value.toString();
279 }
280 container->dependencies << dep;
281 }
282 return true;
283 }
284
readConflicts(const QString & pluginName,PluginManagerImpl::PluginContainer * container,const QJsonValue & confValue)285 bool PluginManagerImpl::readConflicts(const QString& pluginName, PluginManagerImpl::PluginContainer* container, const QJsonValue& confValue)
286 {
287 UNUSED(pluginName);
288
289 if (confValue.isUndefined())
290 return true;
291
292 QJsonArray confArray;
293 if (confValue.type() == QJsonValue::Array)
294 confArray = confValue.toArray();
295 else
296 confArray.append(confValue);
297
298 for (const QJsonValue& value : confArray)
299 container->conflicts << value.toString();
300
301 return true;
302 }
303
initPlugin(Plugin * plugin)304 bool PluginManagerImpl::initPlugin(Plugin* plugin)
305 {
306 QString pluginName = plugin->getName();
307 PluginType* pluginType = nullptr;
308 for (PluginType* type : registeredPluginTypes)
309 {
310 if (type->test(plugin))
311 {
312 pluginType = type;
313 break;
314 }
315 }
316
317 if (!pluginType)
318 {
319 qWarning() << "Could not load built-in plugin" << pluginName << "because its type was not recognized.";
320 return false;
321 }
322
323 PluginContainer* container = new PluginContainer;
324 container->type = pluginType;
325 container->loaded = true;
326 container->builtIn = true;
327 container->plugin = plugin;
328 pluginCategories[pluginType] << container;
329 pluginContainer[pluginName] = container;
330 if (!readMetaData(container))
331 {
332 delete container;
333 return false;
334 }
335
336 pluginLoaded(container);
337 return true;
338 }
339
shouldAutoLoad(const QString & pluginName)340 bool PluginManagerImpl::shouldAutoLoad(const QString& pluginName)
341 {
342 QStringList loadedPlugins = CFG_CORE.General.LoadedPlugins.get().split(",",
343 #if (QT_VERSION >= QT_VERSION_CHECK(5, 14, 0))
344 Qt::SkipEmptyParts
345 #else
346 QString::SkipEmptyParts
347 #endif
348 );
349 QStringList pair;
350 for (const QString& loadedPlugin : loadedPlugins)
351 {
352 pair = loadedPlugin.split("=");
353 if (pair.size() != 2)
354 {
355 qWarning() << "Invalid entry in config General.LoadedPlugins:" << loadedPlugin;
356 continue;
357 }
358
359 if (pair[0] == pluginName)
360 return (bool)pair[1].toInt();
361 }
362
363 return pluginContainer[pluginName]->loadByDefault;
364 }
365
getAllPluginNames(PluginType * type) const366 QStringList PluginManagerImpl::getAllPluginNames(PluginType* type) const
367 {
368 QStringList names;
369 if (!pluginCategories.contains(type))
370 return names;
371
372 for (PluginContainer* container : pluginCategories[type])
373 names << container->name;
374
375 return names;
376 }
377
getAllPluginNames() const378 QStringList PluginManagerImpl::getAllPluginNames() const
379 {
380 return pluginContainer.keys();
381 }
382
getPluginType(const QString & pluginName) const383 PluginType* PluginManagerImpl::getPluginType(const QString& pluginName) const
384 {
385 if (!pluginContainer.contains(pluginName))
386 return nullptr;
387
388 return pluginContainer[pluginName]->type;
389 }
390
getAuthor(const QString & pluginName) const391 QString PluginManagerImpl::getAuthor(const QString& pluginName) const
392 {
393 if (!pluginContainer.contains(pluginName))
394 return QString();
395
396 return pluginContainer[pluginName]->author;
397 }
398
getTitle(const QString & pluginName) const399 QString PluginManagerImpl::getTitle(const QString& pluginName) const
400 {
401 if (!pluginContainer.contains(pluginName))
402 return QString();
403
404 return pluginContainer[pluginName]->title;
405 }
406
getPrintableVersion(const QString & pluginName) const407 QString PluginManagerImpl::getPrintableVersion(const QString& pluginName) const
408 {
409 if (!pluginContainer.contains(pluginName))
410 return QString();
411
412 return pluginContainer[pluginName]->printableVersion;
413 }
414
getVersion(const QString & pluginName) const415 int PluginManagerImpl::getVersion(const QString& pluginName) const
416 {
417 if (!pluginContainer.contains(pluginName))
418 return 0;
419
420 return pluginContainer[pluginName]->version;
421 }
422
getDescription(const QString & pluginName) const423 QString PluginManagerImpl::getDescription(const QString& pluginName) const
424 {
425 if (!pluginContainer.contains(pluginName))
426 return QString();
427
428 return pluginContainer[pluginName]->description;
429 }
430
unload(Plugin * plugin)431 void PluginManagerImpl::unload(Plugin* plugin)
432 {
433 if (!plugin)
434 return;
435
436 unload(plugin->getName());
437 }
438
unload(const QString & pluginName)439 void PluginManagerImpl::unload(const QString& pluginName)
440 {
441 if (!pluginContainer.contains(pluginName))
442 {
443 qWarning() << "No such plugin in containers:" << pluginName << "while trying to unload plugin.";
444 return;
445 }
446
447 // Checking preconditions
448 PluginContainer* container = pluginContainer[pluginName];
449 if (container->builtIn)
450 return;
451
452 if (!container->loaded)
453 return;
454
455 // Unloading depdendent plugins
456 for (PluginContainer* otherContainer : pluginContainer.values())
457 {
458 if (otherContainer == container)
459 continue;
460
461 for (const PluginDependency& dep : otherContainer->dependencies)
462 {
463 if (dep.name == pluginName)
464 {
465 unload(otherContainer->name);
466 break;
467 }
468 }
469 }
470
471 // Removing from fast-lookup collections
472 removePluginFromCollections(container->plugin);
473
474 // Deinitializing and unloading plugin
475 emit aboutToUnload(container->plugin, container->type);
476 container->plugin->deinit();
477 unloadTranslation(container->name);
478
479 QPluginLoader* loader = container->loader;
480 if (!loader->isLoaded())
481 {
482 qWarning() << "QPluginLoader says the plugin is not loaded. Weird.";
483 emit unloaded(container->name, container->type);
484 return;
485 }
486
487 loader->unload();
488
489 container->plugin = nullptr;
490 container->loaded = false;
491
492 emit unloaded(container->name, container->type);
493
494 qDebug() << pluginName << "unloaded:" << container->filePath;
495 }
496
load(const QString & pluginName)497 bool PluginManagerImpl::load(const QString& pluginName)
498 {
499 QStringList alreadyAttempted;
500 bool res = load(pluginName, alreadyAttempted);
501 if (!res)
502 emit failedToLoad(pluginName);
503
504 return res;
505 }
506
load(const QString & pluginName,QStringList & alreadyAttempted,int minVersion,int maxVersion)507 bool PluginManagerImpl::load(const QString& pluginName, QStringList& alreadyAttempted, int minVersion, int maxVersion)
508 {
509 if (alreadyAttempted.contains(pluginName))
510 return false;
511
512 // Checking initial conditions
513 if (!pluginContainer.contains(pluginName))
514 {
515 qWarning() << "No such plugin in containers:" << pluginName << "while trying to load plugin.";
516 alreadyAttempted.append(pluginName);
517 return false;
518 }
519
520 PluginContainer* container = pluginContainer[pluginName];
521
522 if (minVersion > 0 && container->version < minVersion)
523 {
524 qWarning() << "Requested plugin" << pluginName << "in version at least" << minVersion << "but have:" << container->version;
525 return false;
526 }
527
528 if (maxVersion > 0 && container->version > maxVersion)
529 {
530 qWarning() << "Requested plugin" << pluginName << "in version at most" << maxVersion << "but have:" << container->version;
531 return false;
532 }
533
534 if (container->builtIn)
535 return true;
536
537 QPluginLoader* loader = container->loader;
538 if (loader->isLoaded())
539 return true;
540
541 // Checking for conflicting plugins
542 for (PluginContainer* otherContainer : pluginContainer.values())
543 {
544 if (!otherContainer->loaded || otherContainer->name == pluginName)
545 continue;
546
547 if (container->conflicts.contains(otherContainer->name) || otherContainer->conflicts.contains(pluginName))
548 {
549 notifyWarn(tr("Cannot load plugin %1, because it's in conflict with plugin %2.").arg(pluginName, otherContainer->name));
550 alreadyAttempted.append(pluginName);
551 return false;
552 }
553 }
554
555 // Loading depended plugins
556 for (const PluginDependency& dep : container->dependencies)
557 {
558 if (!load(dep.name, alreadyAttempted, dep.minVersion, dep.maxVersion))
559 {
560 notifyWarn(tr("Cannot load plugin %1, because its dependency was not loaded: %2.").arg(pluginName, dep.name));
561 alreadyAttempted.append(pluginName);
562 return false;
563 }
564 }
565
566 // Loading pluginName
567 if (!loader->load())
568 {
569 notifyWarn(tr("Cannot load plugin %1. Error details: %2").arg(pluginName, loader->errorString()));
570 alreadyAttempted.append(pluginName);
571 return false;
572 }
573
574 // Initializing loaded plugin
575 Plugin* plugin = dynamic_cast<Plugin*>(container->loader->instance());
576 GenericPlugin* genericPlugin = dynamic_cast<GenericPlugin*>(plugin);
577 if (genericPlugin)
578 {
579 genericPlugin->loadMetaData(container->loader->metaData());
580 }
581
582 if (!plugin->init())
583 {
584 loader->unload();
585 notifyWarn(tr("Cannot load plugin %1 (error while initializing plugin).").arg(pluginName));
586 alreadyAttempted.append(pluginName);
587 return false;
588 }
589
590 pluginLoaded(container);
591
592 return true;
593 }
594
pluginLoaded(PluginManagerImpl::PluginContainer * container)595 void PluginManagerImpl::pluginLoaded(PluginManagerImpl::PluginContainer* container)
596 {
597 if (!container->builtIn)
598 {
599 loadTranslation(container->name);
600 container->plugin = dynamic_cast<Plugin*>(container->loader->instance());
601 container->loaded = true;
602 }
603 addPluginToCollections(container->plugin);
604
605 emit loaded(container->plugin, container->type);
606 if (!container->builtIn)
607 qDebug() << container->name << "loaded:" << container->filePath;
608 }
609
addPluginToCollections(Plugin * plugin)610 void PluginManagerImpl::addPluginToCollections(Plugin* plugin)
611 {
612 ScriptingPlugin* scriptingPlugin = dynamic_cast<ScriptingPlugin*>(plugin);
613 if (scriptingPlugin)
614 scriptingPlugins[scriptingPlugin->getLanguage()] = scriptingPlugin;
615 }
616
removePluginFromCollections(Plugin * plugin)617 void PluginManagerImpl::removePluginFromCollections(Plugin* plugin)
618 {
619 ScriptingPlugin* scriptingPlugin = dynamic_cast<ScriptingPlugin*>(plugin);
620 if (scriptingPlugin && scriptingPlugins.contains(scriptingPlugin->getLanguage()))
621 scriptingPlugins.remove(plugin->getName());
622 }
623
readMetaData(PluginManagerImpl::PluginContainer * container)624 bool PluginManagerImpl::readMetaData(PluginManagerImpl::PluginContainer* container)
625 {
626 if (container->loader)
627 {
628 QHash<QString, QVariant> metaData = readMetaData(container->loader->metaData());
629 container->name = metaData["name"].toString();
630 container->version = metaData["version"].toInt();
631 container->printableVersion = toPrintableVersion(metaData["version"].toInt());
632 container->author = metaData["author"].toString();
633 container->description = metaData["description"].toString();
634 container->title = metaData["title"].toString();
635 container->loadByDefault = metaData.contains("loadByDefault") ? metaData["loadByDefault"].toBool() : true;
636 }
637 else if (container->plugin)
638 {
639 container->name = container->plugin->getName();
640 container->version = container->plugin->getVersion();
641 container->printableVersion = container->plugin->getPrintableVersion();
642 container->author = container->plugin->getAuthor();
643 container->description = container->plugin->getDescription();
644 container->title = container->plugin->getTitle();
645 container->loadByDefault = true;
646 }
647 else
648 {
649 qCritical() << "Could not read metadata for some plugin. It has no loader or plugin object defined.";
650 return false;
651 }
652 return true;
653 }
654
isLoaded(const QString & pluginName) const655 bool PluginManagerImpl::isLoaded(const QString& pluginName) const
656 {
657 if (!pluginContainer.contains(pluginName))
658 {
659 qWarning() << "No such plugin in containers:" << pluginName << "while trying to get plugin 'loaded' status.";
660 return false;
661 }
662
663 return pluginContainer[pluginName]->loaded;
664 }
665
isBuiltIn(const QString & pluginName) const666 bool PluginManagerImpl::isBuiltIn(const QString& pluginName) const
667 {
668 if (!pluginContainer.contains(pluginName))
669 {
670 qWarning() << "No such plugin in containers:" << pluginName << "while trying to get plugin 'builtIn' status.";
671 return false;
672 }
673
674 return pluginContainer[pluginName]->builtIn;
675 }
676
getLoadedPlugin(const QString & pluginName) const677 Plugin* PluginManagerImpl::getLoadedPlugin(const QString& pluginName) const
678 {
679 if (!pluginContainer.contains(pluginName))
680 return nullptr;
681
682 if (!pluginContainer[pluginName]->loaded)
683 return nullptr;
684
685 return pluginContainer[pluginName]->plugin;
686 }
687
getLoadedPlugins(PluginType * type) const688 QList<Plugin*> PluginManagerImpl::getLoadedPlugins(PluginType* type) const
689 {
690 QList<Plugin*> list;
691 if (!pluginCategories.contains(type))
692 return list;
693
694 for (PluginContainer* container : pluginCategories[type])
695 {
696 if (container->loaded)
697 list << container->plugin;
698 }
699
700 return list;
701 }
702
getScriptingPlugin(const QString & languageName) const703 ScriptingPlugin* PluginManagerImpl::getScriptingPlugin(const QString& languageName) const
704 {
705 if (scriptingPlugins.contains(languageName))
706 return scriptingPlugins[languageName];
707
708 return nullptr;
709 }
710
readMetaData(const QJsonObject & metaData)711 QHash<QString, QVariant> PluginManagerImpl::readMetaData(const QJsonObject& metaData)
712 {
713 QHash<QString, QVariant> results;
714 results["name"] = metaData.value("className").toString();
715
716 QJsonObject root = metaData.value("MetaData").toObject();
717 for (const QString& k : root.keys()) {
718 results[k] = root.value(k).toVariant();
719 }
720 return results;
721 }
722
toPrintableVersion(int version) const723 QString PluginManagerImpl::toPrintableVersion(int version) const
724 {
725 static const QString versionStr = QStringLiteral("%1.%2.%3");
726 return versionStr.arg(version / 10000)
727 .arg(version / 100 % 100)
728 .arg(version % 100);
729 }
730
getDependencies(const QString & pluginName) const731 QStringList PluginManagerImpl::getDependencies(const QString& pluginName) const
732 {
733 if (!pluginContainer.contains(pluginName))
734 return QStringList();
735
736 static const QString verTpl = QStringLiteral(" (%1)");
737 QString minVerTpl = tr("min: %1", "plugin dependency version");
738 QString maxVerTpl = tr("max: %1", "plugin dependency version");
739 QStringList outputList;
740 QString depStr;
741 QStringList depVerList;
742 for (const PluginDependency& dep : pluginContainer[pluginName]->dependencies)
743 {
744 depStr = dep.name;
745 if (dep.minVersion > 0 || dep.maxVersion > 0)
746 {
747 depVerList.clear();
748 if (dep.minVersion > 0)
749 depVerList << minVerTpl.arg(toPrintableVersion(dep.minVersion));
750
751 if (dep.maxVersion > 0)
752 depVerList << minVerTpl.arg(toPrintableVersion(dep.maxVersion));
753
754 depStr += verTpl.arg(depVerList.join(", "));
755 }
756 outputList << depStr;
757 }
758
759 return outputList;
760 }
761
getConflicts(const QString & pluginName) const762 QStringList PluginManagerImpl::getConflicts(const QString& pluginName) const
763 {
764 if (!pluginContainer.contains(pluginName))
765 return QStringList();
766
767 return pluginContainer[pluginName]->conflicts;
768 }
769
arePluginsInitiallyLoaded() const770 bool PluginManagerImpl::arePluginsInitiallyLoaded() const
771 {
772 return pluginsAreInitiallyLoaded;
773 }
774
getLoadedPlugins() const775 QList<Plugin*> PluginManagerImpl::getLoadedPlugins() const
776 {
777 QList<Plugin*> plugins;
778 for (PluginContainer* container : pluginContainer.values())
779 {
780 if (container->loaded)
781 plugins << container->plugin;
782 }
783 return plugins;
784 }
785
getLoadedPluginNames() const786 QStringList PluginManagerImpl::getLoadedPluginNames() const
787 {
788 QStringList names;
789 for (PluginContainer* container : pluginContainer.values())
790 {
791 if (container->loaded)
792 names << container->name;
793 }
794 return names;
795 }
796
getAllPluginDetails() const797 QList<PluginManager::PluginDetails> PluginManagerImpl::getAllPluginDetails() const
798 {
799 QList<PluginManager::PluginDetails> results;
800 PluginManager::PluginDetails details;
801 for (PluginContainer* container : pluginContainer.values())
802 {
803 details.name = container->name;
804 details.title = container->title;
805 details.description = container->description;
806 details.builtIn = container->builtIn;
807 details.version = container->version;
808 details.filePath = container->filePath;
809 details.versionString = formatVersion(container->version);
810 results << details;
811 }
812 return results;
813 }
814
getLoadedPluginDetails() const815 QList<PluginManager::PluginDetails> PluginManagerImpl::getLoadedPluginDetails() const
816 {
817 QList<PluginManager::PluginDetails> results = getAllPluginDetails();
818 QMutableListIterator<PluginManager::PluginDetails> it(results);
819 while (it.hasNext())
820 {
821 if (!isLoaded(it.next().name))
822 it.remove();
823 }
824 return results;
825 }
826
registerPluginType(PluginType * type)827 void PluginManagerImpl::registerPluginType(PluginType* type)
828 {
829 registeredPluginTypes << type;
830 }
831