annotate installer.cpp @ 58:df3b00f82e8d

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