1 /* LibraryPluginHandler.cpp */
2
3 /* Copyright (C) 2011-2020 Michael Lugmair (Lucio Carreras)
4 *
5 * This file is part of sayonara player
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21 #include "LibraryPluginHandler.h"
22 #include "Components/LibraryManagement/AbstractLibraryContainer.h"
23
24 #include "Utils/Utils.h"
25 #include "Utils/Algorithm.h"
26 #include "Utils/Settings/Settings.h"
27 #include "Utils/Logger/Logger.h"
28
29 #include <QDir>
30 #include <QPluginLoader>
31 #include <QJsonObject>
32 #include <QVariantMap>
33 #include <QWidget>
34
35 using Library::PluginHandler;
36 using Library::AbstractContainer;
37
38 using ContainerList=QList<AbstractContainer*>;
39
40 namespace Algorithm=Util::Algorithm;
41
42 struct PluginHandler::Private
43 {
44 AbstractContainer* emptyLibrary=nullptr;
45 AbstractContainer* currentLibrary=nullptr;
46 ContainerList libraryContainers;
47
find_libraryPluginHandler::Private48 AbstractContainer* find_library(const QString& name)
49 {
50 spLog(Log::Debug, this) << "Searching for Library " << name;
51
52 auto it = Algorithm::find(libraryContainers, [&name](AbstractContainer* c){
53 return (c->name() == name);
54 });
55
56 if (it != libraryContainers.end())
57 {
58 return *it;
59 }
60
61 return nullptr;
62 }
63 };
64
convert_display_name(const QString & display_name)65 static QString convert_display_name(const QString& display_name)
66 {
67 QString ret = display_name.toLower().trimmed();
68 ret.replace(" ", "-");
69
70 return ret;
71 }
72
73
74 /*************************************************************************/
75
PluginHandler()76 PluginHandler::PluginHandler() :
77 QObject(nullptr)
78 {
79 m = Pimpl::make<Private>();
80 }
81
82 PluginHandler::~PluginHandler() = default;
83
init(const ContainerList & containers,AbstractContainer * fallback_library)84 void PluginHandler::init(const ContainerList& containers, AbstractContainer* fallback_library)
85 {
86 m->emptyLibrary = fallback_library;
87
88 QString last_library = GetSetting(Set::Lib_CurPlugin);
89 initLibraries(containers);
90 initDllLibraries();
91
92 { // First startup handling
93 bool has_local_library = Util::Algorithm::contains(m->libraryContainers, [](AbstractContainer* container){
94 return container->isLocal();
95 });
96
97 AbstractContainer* c = m->find_library(last_library);
98 if(!has_local_library && (c == nullptr))
99 {
100 setCurrentLibrary(-1);
101 }
102
103 else
104 {
105 setCurrentLibrary(last_library);
106 }
107 }
108 }
109
shutdown()110 void PluginHandler::shutdown()
111 {
112 for(const auto& container : m->libraryContainers)
113 {
114 delete container;
115 }
116
117 m->libraryContainers.clear();
118 }
119
initLibraries(const QList<Library::AbstractContainer * > & containers)120 void PluginHandler::initLibraries(const QList<Library::AbstractContainer*>& containers)
121 {
122 for(AbstractContainer* container : containers)
123 {
124 if(!container) {
125 continue;
126 }
127
128 spLog(Log::Debug, this) << "Add library " << container->displayName();
129
130 m->libraryContainers << container;
131 }
132 }
133
initDllLibraries()134 void PluginHandler::initDllLibraries()
135 {
136 // QDir plugin_dir = QDir(Util::libPath());
137 // QStringList dll_filenames = plugin_dir.entryList(QDir::Files);
138
139 // for(const QString& filename : dll_filenames)
140 // {
141 // QString absolute_path = plugin_dir.absoluteFilePath(filename);
142 // QPluginLoader loader(absolute_path);
143 // QJsonObject metadata = loader.metaData();
144 // QVariantMap map = metadata.toVariantMap();
145 // const auto keys = map.keys();
146
147 // bool has_correct_iid = false;
148 // for(const QString& key : keys)
149 // {
150 // if(key.trimmed() != "IID"){
151 // continue;
152 // }
153
154 // QString value = map[key].toString();
155
156 // if(value.startsWith("com.sayonara-player.apiv2."))
157 // {
158 // spLog(Log::Debug, this) << "Have found valid plugin with iid = " << value;
159 // has_correct_iid = true;
160 // }
161
162 // else if(value.startsWith("com.sayonara-player."))
163 // {
164 // spLog(Log::Info, this) << "Ignoring *outdated* plugin with iid = " << value << " at " << absolute_path;
165 // spLog(Log::Info, this) << "You can delete this file";
166 // }
167
168 // else
169 // {
170 // spLog(Log::Warning, this) << "Ignoring *invalid* plugin with iid = " << value << " at " << absolute_path;
171 // }
172
173 // break;
174 // }
175
176 // if(!has_correct_iid)
177 // {
178 // continue;
179 // }
180
181 // QObject* raw_plugin = loader.instance();
182 // if(!raw_plugin)
183 // {
184 // spLog(Log::Warning, this) << "Cannot load plugin: " << filename << ": " << loader.errorString();
185 // loader.unload();
186 // continue;
187 // }
188
189 // AbstractContainer* container = dynamic_cast<AbstractContainer*>(raw_plugin);
190 // if(!container)
191 // {
192 // loader.unload();
193 // continue;
194 // }
195
196 // spLog(Log::Info, this) << "Found library plugin " << container->displayName();
197
198 // m->libraryContainers << container;
199 // }
200 }
201
setCurrentLibrary(const QString & name)202 void PluginHandler::setCurrentLibrary(const QString& name)
203 {
204 setCurrentLibrary( m->find_library(name) );
205 }
206
setCurrentLibrary(int index)207 void PluginHandler::setCurrentLibrary(int index)
208 {
209 AbstractContainer* ret = m->emptyLibrary;
210
211 if(!m->libraryContainers.isEmpty() && index >= 0)
212 {
213 index = std::min(index, m->libraryContainers.size() - 1);
214 ret = m->libraryContainers[index];
215 }
216
217 setCurrentLibrary(ret);
218 }
219
setCurrentLibrary(AbstractContainer * cur_library)220 void PluginHandler::setCurrentLibrary(AbstractContainer* cur_library)
221 {
222 if(!cur_library)
223 {
224 if(m->libraryContainers.isEmpty())
225 {
226 cur_library = m->emptyLibrary;
227 }
228
229 else {
230 cur_library = m->libraryContainers.first();
231 }
232 }
233
234 m->currentLibrary = cur_library;
235
236 if(m->currentLibrary)
237 {
238 m->currentLibrary->init();
239 }
240
241 SetSetting(Set::Lib_CurPlugin, cur_library->name());
242
243 emit sigCurrentLibraryChanged();
244 }
245
currentLibrary() const246 AbstractContainer* PluginHandler::currentLibrary() const
247 {
248 return m->currentLibrary;
249 }
250
currentLibraryWidget() const251 QWidget* PluginHandler::currentLibraryWidget() const
252 {
253 if(!m->currentLibrary){
254 return nullptr;
255 }
256
257 return m->currentLibrary->widget();
258 }
259
addLocalLibrary(Library::AbstractContainer * container)260 void PluginHandler::addLocalLibrary(Library::AbstractContainer* container)
261 {
262 if(container == nullptr) {
263 return;
264 }
265
266 int idx = Algorithm::indexOf(m->libraryContainers, [=](AbstractContainer* c){
267 return (c->isLocal() == false && c != m->emptyLibrary);
268 });
269
270 idx = std::max(idx, 0);
271 m->libraryContainers.insert(idx, container);
272
273 emit sigLibrariesChanged();
274
275 setCurrentLibrary(idx);
276 }
277
renameLocalLibrary(const QString & old_name,const QString & new_name)278 void PluginHandler::renameLocalLibrary(const QString& old_name, const QString& new_name)
279 {
280 AbstractContainer* c = m->find_library(convert_display_name(old_name));
281 if(c && c->isLocal())
282 {
283 c->rename(new_name);
284 emit sigLibrariesChanged();
285 }
286 }
287
removeLocalLibrary(const QString & name)288 void PluginHandler::removeLocalLibrary(const QString& name)
289 {
290 AbstractContainer* c = m->find_library(convert_display_name(name));
291 if(c && c->isLocal())
292 {
293 m->libraryContainers.removeAll(c);
294
295 if(m->currentLibrary == c)
296 {
297 setCurrentLibrary(0);
298 }
299
300 emit sigLibrariesChanged();
301 }
302 }
303
moveLocalLibrary(int old_index,int new_index)304 void PluginHandler::moveLocalLibrary(int old_index, int new_index)
305 {
306 // first index is empty library
307 if( !Util::between(old_index, m->libraryContainers) ||
308 !Util::between(new_index, m->libraryContainers))
309 {
310 return;
311 }
312
313 m->libraryContainers.move(old_index, new_index);
314
315 emit sigLibrariesChanged();
316 }
317
libraries(bool also_empty) const318 QList<Library::AbstractContainer*> PluginHandler::libraries(bool also_empty) const
319 {
320 QList<AbstractContainer*> containers = m->libraryContainers;
321 if(also_empty) {
322 containers.push_front(m->emptyLibrary);
323 }
324
325 return containers;
326 }
327