Chris@33
|
1
|
Chris@33
|
2 #include <QApplication>
|
Chris@33
|
3 #include <QString>
|
Chris@33
|
4 #include <QFile>
|
Chris@33
|
5 #include <QDir>
|
Chris@33
|
6
|
Chris@42
|
7 #include <QDialog>
|
Chris@42
|
8 #include <QFrame>
|
Chris@42
|
9 #include <QVBoxLayout>
|
Chris@42
|
10 #include <QCheckBox>
|
Chris@46
|
11 #include <QScrollArea>
|
Chris@42
|
12 #include <QDialogButtonBox>
|
Chris@46
|
13 #include <QLabel>
|
Chris@42
|
14
|
Chris@41
|
15 #include <vamp-hostsdk/PluginHostAdapter.h>
|
Chris@41
|
16
|
Chris@43
|
17 #include <dataquay/BasicStore.h>
|
Chris@43
|
18 #include <dataquay/RDFException.h>
|
Chris@43
|
19
|
Chris@33
|
20 #include <iostream>
|
Chris@43
|
21 #include <set>
|
Chris@43
|
22
|
Chris@33
|
23 using namespace std;
|
Chris@43
|
24 using namespace Dataquay;
|
Chris@32
|
25
|
Chris@42
|
26 QString
|
Chris@42
|
27 getDefaultInstallDirectory()
|
Chris@32
|
28 {
|
Chris@41
|
29 auto pathList = Vamp::PluginHostAdapter::getPluginPath();
|
Chris@41
|
30 if (pathList.empty()) {
|
Chris@41
|
31 cerr << "Failed to look up Vamp plugin path" << endl;
|
Chris@42
|
32 return QString();
|
Chris@41
|
33 }
|
Chris@41
|
34
|
Chris@42
|
35 auto firstPath = *pathList.begin();
|
Chris@42
|
36 QString target = QString::fromUtf8(firstPath.c_str(), firstPath.size());
|
Chris@42
|
37 return target;
|
Chris@42
|
38 }
|
Chris@42
|
39
|
Chris@42
|
40 QStringList
|
Chris@42
|
41 getPluginLibraryList()
|
Chris@42
|
42 {
|
Chris@33
|
43 QDir dir(":out/");
|
Chris@33
|
44 auto entries = dir.entryList({ "*.so", "*.dll", "*.dylib" });
|
Chris@33
|
45
|
Chris@33
|
46 for (auto e: entries) {
|
Chris@33
|
47 cerr << e.toStdString() << endl;
|
Chris@33
|
48 }
|
Chris@33
|
49
|
Chris@42
|
50 return entries;
|
Chris@42
|
51 }
|
Chris@33
|
52
|
Chris@43
|
53 unique_ptr<BasicStore>
|
Chris@43
|
54 loadLibrariesRdf()
|
Chris@43
|
55 {
|
Chris@43
|
56 QDir dir(":out/");
|
Chris@43
|
57 auto entries = dir.entryList({ "*.ttl", "*.n3" });
|
Chris@43
|
58
|
Chris@43
|
59 unique_ptr<BasicStore> store(new BasicStore);
|
Chris@43
|
60
|
Chris@43
|
61 for (auto e: entries) {
|
Chris@43
|
62
|
Chris@43
|
63 QFile f(":out/" + e);
|
Chris@43
|
64 if (!f.open(QFile::ReadOnly | QFile::Text)) {
|
Chris@43
|
65 cerr << "Failed to open RDF resource file "
|
Chris@43
|
66 << e.toStdString() << endl;
|
Chris@43
|
67 continue;
|
Chris@43
|
68 }
|
Chris@43
|
69
|
Chris@43
|
70 QByteArray content = f.readAll();
|
Chris@43
|
71 f.close();
|
Chris@43
|
72
|
Chris@43
|
73 try {
|
Chris@43
|
74 store->importString(QString::fromUtf8(content),
|
Chris@43
|
75 Uri("file:" + e),
|
Chris@43
|
76 BasicStore::ImportIgnoreDuplicates);
|
Chris@43
|
77 } catch (const RDFException &ex) {
|
Chris@43
|
78 cerr << "Failed to import RDF resource file "
|
Chris@43
|
79 << e.toStdString() << ": " << ex.what() << endl;
|
Chris@43
|
80 }
|
Chris@43
|
81 }
|
Chris@43
|
82
|
Chris@43
|
83 return store;
|
Chris@43
|
84 }
|
Chris@43
|
85
|
Chris@43
|
86 struct LibraryInfo {
|
Chris@43
|
87 QString id;
|
Chris@43
|
88 QString fileName;
|
Chris@43
|
89 QString title;
|
Chris@43
|
90 QString maker;
|
Chris@43
|
91 QString description;
|
Chris@47
|
92 QStringList pluginTitles;
|
Chris@43
|
93 };
|
Chris@43
|
94
|
Chris@43
|
95 vector<LibraryInfo>
|
Chris@43
|
96 getLibraryInfo(const Store &store, QStringList libraries)
|
Chris@43
|
97 {
|
Chris@43
|
98 /* e.g.
|
Chris@43
|
99
|
Chris@43
|
100 plugbase:library a vamp:PluginLibrary ;
|
Chris@43
|
101 vamp:identifier "qm-vamp-plugins" ;
|
Chris@43
|
102 dc:title "Queen Mary plugin set"
|
Chris@43
|
103 */
|
Chris@43
|
104
|
Chris@43
|
105 Triples tt = store.match(Triple(Node(),
|
Chris@43
|
106 Uri("a"),
|
Chris@43
|
107 store.expand("vamp:PluginLibrary")));
|
Chris@43
|
108
|
Chris@43
|
109 std::map<QString, QString> wanted; // basename -> full lib name
|
Chris@43
|
110 for (auto lib: libraries) {
|
Chris@43
|
111 wanted[QFileInfo(lib).baseName()] = lib;
|
Chris@43
|
112 }
|
Chris@43
|
113
|
Chris@43
|
114 vector<LibraryInfo> results;
|
Chris@43
|
115
|
Chris@43
|
116 for (auto t: tt) {
|
Chris@43
|
117
|
Chris@43
|
118 Node libId = store.complete(Triple(t.subject(),
|
Chris@43
|
119 store.expand("vamp:identifier"),
|
Chris@43
|
120 Node()));
|
Chris@43
|
121 if (libId.type != Node::Literal) {
|
Chris@43
|
122 continue;
|
Chris@43
|
123 }
|
Chris@43
|
124 auto wi = wanted.find(libId.value);
|
Chris@43
|
125 if (wi == wanted.end()) {
|
Chris@43
|
126 continue;
|
Chris@43
|
127 }
|
Chris@43
|
128
|
Chris@43
|
129 LibraryInfo info;
|
Chris@43
|
130 info.id = wi->first;
|
Chris@43
|
131 info.fileName = wi->second;
|
Chris@43
|
132
|
Chris@43
|
133 Node title = store.complete(Triple(t.subject(),
|
Chris@43
|
134 store.expand("dc:title"),
|
Chris@43
|
135 Node()));
|
Chris@43
|
136 if (title.type == Node::Literal) {
|
Chris@43
|
137 info.title = title.value;
|
Chris@43
|
138 } else {
|
Chris@43
|
139 info.title = info.id;
|
Chris@43
|
140 }
|
Chris@43
|
141
|
Chris@43
|
142 Node maker = store.complete(Triple(t.subject(),
|
Chris@43
|
143 store.expand("foaf:maker"),
|
Chris@43
|
144 Node()));
|
Chris@43
|
145 if (maker.type == Node::Literal) {
|
Chris@43
|
146 info.maker = maker.value;
|
Chris@46
|
147 } else if (maker != Node()) {
|
Chris@46
|
148 maker = store.complete(Triple(maker,
|
Chris@46
|
149 store.expand("foaf:name"),
|
Chris@46
|
150 Node()));
|
Chris@46
|
151 if (maker.type == Node::Literal) {
|
Chris@46
|
152 info.maker = maker.value;
|
Chris@46
|
153 }
|
Chris@43
|
154 }
|
Chris@46
|
155
|
Chris@43
|
156 Node desc = store.complete(Triple(t.subject(),
|
Chris@43
|
157 store.expand("dc:description"),
|
Chris@43
|
158 Node()));
|
Chris@43
|
159 if (desc.type == Node::Literal) {
|
Chris@43
|
160 info.description = desc.value;
|
Chris@43
|
161 }
|
Chris@43
|
162
|
Chris@47
|
163 Triples pp = store.match(Triple(t.subject(),
|
Chris@47
|
164 store.expand("vamp:available_plugin"),
|
Chris@47
|
165 Node()));
|
Chris@47
|
166 for (auto p: pp) {
|
Chris@47
|
167 Node ptitle = store.complete(Triple(p.object(),
|
Chris@47
|
168 store.expand("dc:title"),
|
Chris@47
|
169 Node()));
|
Chris@47
|
170 if (ptitle.type == Node::Literal) {
|
Chris@47
|
171 info.pluginTitles.push_back(ptitle.value);
|
Chris@47
|
172 }
|
Chris@47
|
173 }
|
Chris@47
|
174
|
Chris@43
|
175 results.push_back(info);
|
Chris@43
|
176 }
|
Chris@43
|
177
|
Chris@43
|
178 return results;
|
Chris@43
|
179 }
|
Chris@43
|
180
|
Chris@42
|
181 void
|
Chris@42
|
182 installLibrary(QString library, QString target)
|
Chris@42
|
183 {
|
Chris@42
|
184 QFile f(":out/" + library);
|
Chris@42
|
185 QString destination = target + "/" + library;
|
Chris@42
|
186 cerr << "Copying " << library.toStdString() << " to "
|
Chris@42
|
187 << destination.toStdString() << "..." << endl;
|
Chris@42
|
188 if (!f.copy(destination)) {
|
Chris@42
|
189 cerr << "Failed to copy " << library.toStdString()
|
Chris@42
|
190 << " to target " << destination.toStdString() << endl;
|
Chris@42
|
191 return;
|
Chris@42
|
192 }
|
Chris@42
|
193 if (!QFile::setPermissions
|
Chris@42
|
194 (destination,
|
Chris@42
|
195 QFile::ReadOwner | QFile::WriteOwner | QFile::ExeOwner |
|
Chris@42
|
196 QFile::ReadGroup | QFile::ExeGroup |
|
Chris@42
|
197 QFile::ReadOther | QFile::ExeOther)) {
|
Chris@42
|
198 cerr << "Failed to set permissions on "
|
Chris@42
|
199 << library.toStdString() << endl;
|
Chris@42
|
200 return;
|
Chris@42
|
201 }
|
Chris@42
|
202 }
|
Chris@42
|
203
|
Chris@42
|
204 QStringList
|
Chris@43
|
205 getUserApprovedPluginLibraries(vector<LibraryInfo> libraries)
|
Chris@42
|
206 {
|
Chris@42
|
207 QDialog dialog;
|
Chris@46
|
208
|
Chris@46
|
209 auto mainLayout = new QGridLayout;
|
Chris@47
|
210 mainLayout->setSpacing(0);
|
Chris@46
|
211 dialog.setLayout(mainLayout);
|
Chris@46
|
212
|
Chris@46
|
213 int mainRow = 0;
|
Chris@46
|
214
|
Chris@46
|
215 //!!! at top: title and check/uncheck all button
|
Chris@46
|
216
|
Chris@47
|
217 auto checkAll = new QCheckBox;
|
Chris@47
|
218 mainLayout->addWidget(checkAll, mainRow, 0, Qt::AlignHCenter);
|
Chris@47
|
219 ++mainRow;
|
Chris@47
|
220
|
Chris@47
|
221 auto checkArrow = new QLabel("▼");
|
Chris@47
|
222 checkArrow->setTextFormat(Qt::RichText);
|
Chris@47
|
223 mainLayout->addWidget(checkArrow, mainRow, 0, Qt::AlignHCenter);
|
Chris@47
|
224 ++mainRow;
|
Chris@47
|
225
|
Chris@46
|
226 auto scroll = new QScrollArea;
|
Chris@47
|
227 scroll->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
|
Chris@46
|
228 mainLayout->addWidget(scroll, mainRow, 0, 1, 2);
|
Chris@46
|
229 mainLayout->setRowStretch(mainRow, 10);
|
Chris@46
|
230 ++mainRow;
|
Chris@46
|
231
|
Chris@46
|
232 auto selectionFrame = new QWidget;
|
Chris@46
|
233
|
Chris@46
|
234 auto selectionLayout = new QGridLayout;
|
Chris@46
|
235 selectionFrame->setLayout(selectionLayout);
|
Chris@46
|
236 int selectionRow = 0;
|
Chris@42
|
237
|
Chris@43
|
238 map<QString, QCheckBox *> checkBoxMap;
|
Chris@43
|
239
|
Chris@49
|
240 map<QString, LibraryInfo, std::function<bool (QString, QString)>>
|
Chris@49
|
241 orderedInfo
|
Chris@49
|
242 ([](QString k1, QString k2) {
|
Chris@49
|
243 return k1.localeAwareCompare(k2) < 0;
|
Chris@49
|
244 });
|
Chris@43
|
245 for (auto info: libraries) {
|
Chris@43
|
246 orderedInfo[info.title] = info;
|
Chris@43
|
247 }
|
Chris@42
|
248
|
Chris@43
|
249 for (auto ip: orderedInfo) {
|
Chris@46
|
250
|
Chris@46
|
251 auto cb = new QCheckBox;
|
Chris@47
|
252 selectionLayout->addWidget(cb, selectionRow, 0,
|
Chris@47
|
253 Qt::AlignTop | Qt::AlignHCenter);
|
Chris@46
|
254
|
Chris@43
|
255 LibraryInfo info = ip.second;
|
Chris@47
|
256 /*
|
Chris@47
|
257 int n = info.pluginTitles.size();
|
Chris@47
|
258 QString contents;
|
Chris@47
|
259
|
Chris@47
|
260 if (n > 0) {
|
Chris@47
|
261 int max = 4;
|
Chris@47
|
262 QStringList titles;
|
Chris@47
|
263 for (int i = 0; i < max && i < int(info.pluginTitles.size()); ++i) {
|
Chris@47
|
264 titles.push_back(info.pluginTitles[i]);
|
Chris@47
|
265 }
|
Chris@47
|
266 QString titleText = titles.join(", ");
|
Chris@47
|
267 if (max < int(info.pluginTitles.size())) {
|
Chris@47
|
268 titleText = QObject::tr("%1 ...").arg(titleText);
|
Chris@47
|
269 }
|
Chris@47
|
270 contents = QObject::tr("Plugins: %1").arg(titleText);
|
Chris@47
|
271 }
|
Chris@47
|
272 */
|
Chris@47
|
273 QString text = QObject::tr("<b>%1</b><br><small><i>%2</i><br>%3</small>")
|
Chris@47
|
274 .arg(info.title)
|
Chris@47
|
275 .arg(info.maker)
|
Chris@47
|
276 .arg(info.description);
|
Chris@46
|
277
|
Chris@46
|
278 auto label = new QLabel(text);
|
Chris@47
|
279 label->setWordWrap(true);
|
Chris@47
|
280 label->setMinimumWidth(800);
|
Chris@46
|
281
|
Chris@46
|
282 selectionLayout->addWidget(label, selectionRow, 1, Qt::AlignTop);
|
Chris@46
|
283
|
Chris@46
|
284 ++selectionRow;
|
Chris@46
|
285
|
Chris@43
|
286 checkBoxMap[info.fileName] = cb;
|
Chris@42
|
287 }
|
Chris@42
|
288
|
Chris@46
|
289 scroll->setWidget(selectionFrame);
|
Chris@46
|
290
|
Chris@47
|
291 QObject::connect(checkAll, &QCheckBox::toggled,
|
Chris@47
|
292 [=]() {
|
Chris@47
|
293 bool toCheck = checkAll->isChecked();
|
Chris@47
|
294 for (auto p: checkBoxMap) {
|
Chris@47
|
295 p.second->setChecked(toCheck);
|
Chris@47
|
296 }
|
Chris@47
|
297 });
|
Chris@47
|
298
|
Chris@42
|
299 auto bb = new QDialogButtonBox(QDialogButtonBox::Ok |
|
Chris@42
|
300 QDialogButtonBox::Cancel);
|
Chris@46
|
301 mainLayout->addWidget(bb, mainRow, 0, 1, 2);
|
Chris@46
|
302 ++mainRow;
|
Chris@47
|
303
|
Chris@47
|
304 int cw = 50;
|
Chris@47
|
305 mainLayout->setColumnMinimumWidth(0, cw + 20); //!!!
|
Chris@47
|
306 mainLayout->setColumnStretch(1, 10);
|
Chris@47
|
307 selectionLayout->setColumnMinimumWidth(0, cw); //!!!
|
Chris@47
|
308
|
Chris@42
|
309 QObject::connect(bb, SIGNAL(accepted()), &dialog, SLOT(accept()));
|
Chris@42
|
310 QObject::connect(bb, SIGNAL(rejected()), &dialog, SLOT(reject()));
|
Chris@42
|
311
|
Chris@42
|
312 if (dialog.exec() == QDialog::Accepted) {
|
Chris@42
|
313 cerr << "accepted" << endl;
|
Chris@42
|
314 } else {
|
Chris@42
|
315 cerr << "rejected" << endl;
|
Chris@42
|
316 }
|
Chris@42
|
317
|
Chris@42
|
318 QStringList approved;
|
Chris@42
|
319 for (const auto &p: checkBoxMap) {
|
Chris@42
|
320 if (p.second->isChecked()) {
|
Chris@42
|
321 approved.push_back(p.first);
|
Chris@33
|
322 }
|
Chris@42
|
323 }
|
Chris@42
|
324
|
Chris@42
|
325 return approved;
|
Chris@42
|
326 }
|
Chris@42
|
327
|
Chris@42
|
328 int main(int argc, char **argv)
|
Chris@42
|
329 {
|
Chris@42
|
330 QApplication app(argc, argv);
|
Chris@42
|
331
|
Chris@42
|
332 QString target = getDefaultInstallDirectory();
|
Chris@42
|
333 if (target == "") {
|
Chris@42
|
334 return 1;
|
Chris@42
|
335 }
|
Chris@42
|
336
|
Chris@42
|
337 QStringList libraries = getPluginLibraryList();
|
Chris@42
|
338
|
Chris@43
|
339 auto rdfStore = loadLibrariesRdf();
|
Chris@43
|
340
|
Chris@43
|
341 auto info = getLibraryInfo(*rdfStore, libraries);
|
Chris@43
|
342
|
Chris@43
|
343 QStringList toInstall = getUserApprovedPluginLibraries(info);
|
Chris@42
|
344
|
Chris@42
|
345 for (auto lib: toInstall) {
|
Chris@42
|
346 installLibrary(lib, target);
|
Chris@33
|
347 }
|
Chris@33
|
348
|
Chris@32
|
349 return 0;
|
Chris@32
|
350 }
|