annotate main/main.cpp @ 2265:d33dff02b39b sandbox-notarize

Work on sandboxing (possibly) and using the hardened runtime for notarization. Supply appropriate bundle ID for helpers as well as main application, and request inherited sandbox entitlements. Currently works with sandboxing (apparently) but not yet with the hardened runtime, where we can't load plugins signed by third parties even with the com.apple.security.cs.disable-library-validation entitlement because their team IDs don't match the host. Possibly that exception is supposed to be requested some other way?
author Chris Cannam
date Thu, 25 Apr 2019 16:46:02 +0100
parents c517286ee999
children a54082a29106 eb7f4579e5cc
rev   line source
Chris@0 1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
Chris@0 2
Chris@0 3 /*
Chris@0 4 Sonic Visualiser
Chris@0 5 An audio file viewer and annotation editor.
Chris@0 6 Centre for Digital Music, Queen Mary, University of London.
Chris@77 7 This file copyright 2006 Chris Cannam and QMUL.
Chris@0 8
Chris@0 9 This program is free software; you can redistribute it and/or
Chris@0 10 modify it under the terms of the GNU General Public License as
Chris@0 11 published by the Free Software Foundation; either version 2 of the
Chris@0 12 License, or (at your option) any later version. See the file
Chris@0 13 COPYING included with this distribution for more information.
Chris@0 14 */
Chris@0 15
Chris@0 16 #include "MainWindow.h"
Chris@953 17 #include "SVSplash.h"
Chris@0 18
Chris@1 19 #include "system/System.h"
Chris@1 20 #include "system/Init.h"
Chris@0 21 #include "base/TempDirectory.h"
Chris@0 22 #include "base/PropertyContainer.h"
Chris@0 23 #include "base/Preferences.h"
Chris@908 24 #include "data/fileio/FileSource.h"
Chris@120 25 #include "widgets/TipDialog.h"
Chris@763 26 #include "widgets/InteractiveFileFinder.h"
Chris@1837 27 #include "framework/TransformUserConfigurator.h"
Chris@315 28 #include "transform/TransformFactory.h"
Chris@1837 29 #include "plugin/PluginScan.h"
Chris@1837 30 #include "plugin/PluginPathSetter.h"
Chris@0 31
Chris@0 32 #include <QMetaType>
Chris@0 33 #include <QApplication>
Chris@0 34 #include <QDesktopWidget>
Chris@0 35 #include <QMessageBox>
Chris@0 36 #include <QTranslator>
Chris@0 37 #include <QLocale>
Chris@5 38 #include <QSettings>
Chris@7 39 #include <QIcon>
Chris@11 40 #include <QSessionManager>
Chris@165 41 #include <QDir>
Chris@252 42 #include <QTimer>
Chris@331 43 #include <QPainter>
dan@365 44 #include <QFileOpenEvent>
Chris@2232 45 #include <QCommandLineParser>
Chris@331 46
Chris@0 47 #include <iostream>
Chris@0 48 #include <signal.h>
Chris@0 49
Chris@2053 50 #include "../version.h"
Chris@2053 51
Chris@215 52 #ifdef HAVE_FFTW3F
Chris@215 53 #include <fftw3.h>
Chris@215 54 #endif
Chris@215 55
Chris@127 56 /*! \mainpage Sonic Visualiser
Chris@127 57
Chris@127 58 \section interesting Summary of interesting classes
Chris@127 59
Chris@127 60 - Data models: Model and subclasses, e.g. WaveFileModel
Chris@127 61
Chris@127 62 - Graphical layers: Layer and subclasses, displayed on View and its
Chris@127 63 subclass widgets.
Chris@127 64
Chris@127 65 - Main window class, document class, and file parser: MainWindow,
Chris@127 66 Document, SVFileReader
Chris@127 67
Chris@127 68 - Turning one model (e.g. audio) into another (e.g. more audio, or a
Chris@244 69 curve extracted from it): Transform, encapsulating the data that need
Chris@244 70 to be stored to be able to reproduce a given transformation;
Chris@244 71 TransformFactory, for discovering the available types of transform;
Chris@244 72 ModelTransformerFactory, ModelTransformer and subclasses, providing
Chris@244 73 the mechanisms for applying transforms to data models
Chris@127 74
Chris@127 75 - Creating the plugins used by transforms: RealTimePluginFactory,
Chris@129 76 FeatureExtractionPluginFactory. See also the API documentation for
Chris@129 77 Vamp feature extraction plugins at
Chris@129 78 http://www.vamp-plugins.org/code-doc/.
Chris@127 79
Chris@127 80 - File reading and writing code: AudioFileReader and subclasses,
Chris@127 81 WavFileWriter, DataFileReader, SVFileReader
Chris@127 82
Chris@127 83 - FFT calculation and cacheing: FFTModel, FFTDataServer
Chris@127 84
Chris@127 85 - Widgets that show groups of editable properties: PropertyBox for
Chris@127 86 layer properties (contained in a PropertyStack), PluginParameterBox
Chris@127 87 for plugins (contained in a PluginParameterDialog)
Chris@127 88
Chris@127 89 - Audio playback: AudioCallbackPlaySource and subclasses,
Chris@127 90 AudioCallbackPlayTarget and subclasses, AudioGenerator
Chris@127 91
Chris@127 92 \section model Data sources: the Model hierarchy
Chris@127 93
Chris@2232 94 ***!!! todo: update this
Chris@2232 95
Chris@127 96 A Model is something containing, or knowing how to obtain, data.
Chris@127 97
Chris@127 98 For example, WaveFileModel is a model that knows how to get data
Chris@127 99 from an audio file; SparseTimeValueModel is a model containing
Chris@127 100 editable "curve" data.
Chris@127 101
Chris@127 102 Models typically subclass one of a number of abstract subclasses of
Chris@127 103 Model. For example, WaveFileModel subclasses DenseTimeValueModel,
Chris@127 104 which describes an interface for models that have a value at each
Chris@127 105 time point for a given sampling resolution. (Note that
Chris@127 106 WaveFileModel does not actually read the files itself: it uses
Chris@127 107 AudioFileReader classes for that. It just makes data from the
Chris@127 108 files available in a Model.) SparseTimeValueModel uses the
Chris@127 109 SparseModel template class, which provides most of the
Chris@127 110 implementation for models that contain a series of points of some
Chris@127 111 sort -- also used by NoteModel, TextModel, and
Chris@127 112 SparseOneDimensionalModel.
Chris@127 113
Chris@127 114 Everything that goes on the screen originates from a model, via a
Chris@127 115 layer (see below). The models are contained in a Document object.
Chris@127 116 There is no containment hierarchy or ordering of models in the
Chris@127 117 document. One model is the main model, which defines the sample
Chris@127 118 rate for playback.
Chris@127 119
Chris@127 120 A model may also be marked as a "derived" model, which means it was
Chris@127 121 generated from another model using some transform (feature
Chris@127 122 extraction or effect plugin, etc) -- the idea being that they can
Chris@127 123 be re-generated using the same transform if a new source model is
Chris@127 124 loaded.
Chris@127 125
Chris@127 126 \section layer Things that can display data: the Layer hierarchy
Chris@127 127
Chris@127 128 A Layer is something that knows how to draw parts of a model onto a
Chris@127 129 timeline.
Chris@127 130
Chris@127 131 For example, WaveformLayer is a layer which draws waveforms, based
Chris@127 132 on WaveFileModel; TimeValueLayer draws curves, based on
Chris@127 133 SparseTimeValueModel; SpectrogramLayer draws spectrograms, based on
Chris@127 134 WaveFileModel (via FFTModel).
Chris@127 135
Chris@127 136 The most basic functions of a layer are: to draw itself onto a
Chris@127 137 Pane, against a timeline on the x axis; and to permit user
Chris@127 138 interaction. If you were thinking of adding the capability to
Chris@127 139 display a new sort of something, then you would want to add a new
Chris@127 140 layer type. (You may also need a new model type, depending on
Chris@127 141 whether any existing model can capture the data you need.)
Chris@127 142 Depending on the sort of data in question, there are various
Chris@127 143 existing layers that might be appropriate to start from -- for
Chris@127 144 example, a layer that displays images that the user has imported
Chris@127 145 and associated with particular times might have something in common
Chris@127 146 with the existing TextLayer which displays pieces of text that are
Chris@127 147 associated with particular times.
Chris@127 148
Chris@127 149 Although layers are visual objects, they are contained in the
Chris@127 150 Document in Sonic Visualiser rather than being managed together
Chris@127 151 with display widgets. The Sonic Visualiser file format has
Chris@127 152 separate data and layout sections, and the layers are defined in
Chris@127 153 the data section and then referred to in the layout section which
Chris@127 154 determines which layers may go on which panes (see Pane below).
Chris@127 155
Chris@127 156 Once a layer class is defined, some basic data about it needs to be
Chris@127 157 set up in the LayerFactory class, and then it will appear in the
Chris@127 158 menus and so on on the main window.
Chris@127 159
Chris@127 160 \section view Widgets that are used to show layers: The View hierarchy
Chris@127 161
Chris@127 162 A View is a widget that displays a stack of layers. The most
Chris@127 163 important subclass is Pane, the widget that is used to show most of
Chris@127 164 the data in the main window of Sonic Visualiser.
Chris@127 165
Chris@127 166 All a pane really does is contain a set of layers and get them to
Chris@127 167 render themselves (one on top of the other, with the topmost layer
Chris@127 168 being the one that is currently interacted with), cache the
Chris@127 169 results, negotiate user interaction with them, and so on. This is
Chris@127 170 generally fiddly, if not especially interesting. Panes are
Chris@127 171 strictly layout objects and are not stored in the Document class;
Chris@127 172 instead the MainWindow contains a PaneStack widget (the widget that
Chris@127 173 takes up most of Sonic Visualiser's main window) which contains a
Chris@127 174 set of panes stacked vertically.
Chris@127 175
Chris@127 176 Another View subclass is Overview, which is the widget that
Chris@127 177 contains that green waveform showing the entire file at the bottom
Chris@127 178 of the window.
Chris@127 179
Chris@127 180 */
Chris@127 181
Chris@0 182 static QMutex cleanupMutex;
Chris@589 183 static bool cleanedUp = false;
Chris@0 184
Chris@0 185 static void
Chris@0 186 signalHandler(int /* signal */)
Chris@0 187 {
Chris@0 188 // Avoid this happening more than once across threads
Chris@0 189
Chris@665 190 cerr << "signalHandler: cleaning up and exiting" << endl;
Chris@0 191 cleanupMutex.lock();
Chris@589 192 if (!cleanedUp) {
Chris@589 193 TempDirectory::getInstance()->cleanup();
Chris@589 194 cleanedUp = true;
Chris@589 195 }
Chris@589 196 cleanupMutex.unlock();
Chris@589 197 exit(0);
Chris@0 198 }
Chris@0 199
Chris@11 200 class SVApplication : public QApplication
Chris@11 201 {
Chris@11 202 public:
Chris@296 203 SVApplication(int &argc, char **argv) :
Chris@11 204 QApplication(argc, argv),
dan@365 205 m_readyForFiles(false),
dan@365 206 m_filepathQueue(QStringList()),
Chris@2126 207 m_mainWindow(nullptr)
Chris@509 208 {
Chris@509 209 }
Chris@2121 210 ~SVApplication() override { }
Chris@11 211
Chris@11 212 void setMainWindow(MainWindow *mw) { m_mainWindow = mw; }
Chris@2126 213 void releaseMainWindow() { m_mainWindow = nullptr; }
Chris@11 214
Chris@11 215 virtual void commitData(QSessionManager &manager) {
Chris@11 216 if (!m_mainWindow) return;
Chris@11 217 bool mayAskUser = manager.allowsInteraction();
Chris@11 218 bool success = m_mainWindow->commitData(mayAskUser);
Chris@11 219 manager.release();
Chris@11 220 if (!success) manager.cancel();
Chris@11 221 }
Chris@11 222
Chris@953 223 void handleFilepathArgument(QString path, SVSplash *splash);
dan@362 224
dan@365 225 bool m_readyForFiles;
dan@365 226 QStringList m_filepathQueue;
dan@362 227
Chris@11 228 protected:
Chris@11 229 MainWindow *m_mainWindow;
Chris@2121 230 bool event(QEvent *) override;
Chris@11 231 };
Chris@11 232
Chris@0 233 int
Chris@0 234 main(int argc, char **argv)
Chris@0 235 {
Chris@2058 236 if (argc == 2 && (QString(argv[1]) == "--version" ||
Chris@2058 237 QString(argv[1]) == "-v")) {
Chris@2058 238 cerr << SV_VERSION << endl;
Chris@2058 239 exit(0);
Chris@2058 240 }
Chris@2058 241
Chris@376 242 svSystemSpecificInitialisation();
Chris@376 243
Chris@678 244 #ifdef Q_OS_MAC
Chris@678 245 if (QSysInfo::MacintoshVersion > QSysInfo::MV_10_8) {
Chris@678 246 // Fix for OS/X 10.9 font problem
Chris@678 247 QFont::insertSubstitution(".Lucida Grande UI", "Lucida Grande");
Chris@678 248 }
Chris@678 249 #endif
Chris@678 250
Chris@11 251 SVApplication application(argc, argv);
Chris@0 252
Chris@2232 253 QApplication::setOrganizationName("sonic-visualiser");
Chris@2232 254 QApplication::setOrganizationDomain("sonicvisualiser.org");
Chris@2232 255 QApplication::setApplicationName(QApplication::tr("Sonic Visualiser"));
Chris@2232 256 QApplication::setApplicationVersion(SV_VERSION);
Chris@2232 257
Chris@2232 258 //!!! todo hand-update translations
Chris@2232 259 QCommandLineParser parser;
Chris@2241 260 parser.setApplicationDescription(QApplication::tr("\nSonic Visualiser is a program for viewing and exploring audio data\nfor semantic music analysis and annotation."));
Chris@2232 261 parser.addHelpOption();
Chris@2232 262 parser.addVersionOption();
Chris@2232 263
Chris@2232 264 parser.addOptions({
Chris@2232 265 { "no-audio", QApplication::tr
Chris@2232 266 ("Do not attempt to open an audio output device.") },
Chris@2232 267 { "no-osc", QApplication::tr
Chris@2232 268 ("Do not provide an Open Sound Control port for remote control.") },
Chris@2233 269 { "no-splash", QApplication::tr
Chris@2233 270 ("Do not show a splash screen.") },
Chris@2232 271 { "osc-script", QApplication::tr
Chris@2241 272 ("Batch run the Open Sound Control script found in the given file. Supply \"-\" as file to read from stdin. Scripts consist of /command arg1 arg2 ... OSC control lines, optionally interleaved with numbers to specify pauses in seconds."),
Chris@2232 273 "osc.txt" }
Chris@2232 274 });
Chris@2232 275
Chris@2232 276 parser.addPositionalArgument
Chris@2232 277 ("[<file> ...]", QApplication::tr("One or more Sonic Visualiser (.sv) and audio files may be provided."));
Chris@2232 278
Chris@46 279 QStringList args = application.arguments();
Chris@2232 280 if (!parser.parse(args)) {
Chris@2232 281 if (parser.unknownOptionNames().contains("?")) {
Chris@2232 282 // QCommandLineParser only understands -? for help on Windows,
Chris@2232 283 // but we historically accepted it everywhere - provide this
Chris@2232 284 // backward compatibility
Chris@2232 285 parser.showHelp();
Chris@2232 286 }
Chris@2232 287 }
Chris@2232 288
Chris@2232 289 parser.process(args);
Chris@2232 290
Chris@2233 291 bool audioOutput = !(parser.isSet("no-audio"));
Chris@2233 292 bool oscSupport = !(parser.isSet("no-osc"));
Chris@2233 293 bool showSplash = !(parser.isSet("no-splash"));
Chris@2233 294
Chris@2240 295 if (!audioOutput) {
Chris@2240 296 SVDEBUG << "Note: --no-audio flag set, will not use audio device" << endl;
Chris@2240 297 }
Chris@2240 298 if (!oscSupport) {
Chris@2240 299 SVDEBUG << "Note: --no-osc flag set, will not open OSC port" << endl;
Chris@2240 300 }
Chris@2240 301
Chris@2232 302 args = parser.positionalArguments();
Chris@2232 303
Chris@2232 304 QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
Chris@46 305
Chris@0 306 signal(SIGINT, signalHandler);
Chris@0 307 signal(SIGTERM, signalHandler);
Chris@0 308
Chris@640 309 #ifndef Q_OS_WIN32
Chris@0 310 signal(SIGHUP, signalHandler);
Chris@0 311 signal(SIGQUIT, signalHandler);
Chris@0 312 #endif
Chris@0 313
Chris@2126 314 SVSplash *splash = nullptr;
Chris@231 315
Chris@231 316 QSettings settings;
Chris@237 317
Chris@237 318 settings.beginGroup("Preferences");
Chris@1274 319 // Default to using Piper server; can change in preferences
Chris@1274 320 if (!settings.contains("run-vamp-plugins-in-process")) {
Chris@1274 321 settings.setValue("run-vamp-plugins-in-process", false);
Chris@1274 322 }
Chris@1274 323 settings.endGroup();
Chris@1274 324
Chris@1274 325 settings.beginGroup("Preferences");
Chris@2233 326 if (showSplash && settings.value("show-splash", true).toBool()) {
Chris@953 327 splash = new SVSplash();
Chris@283 328 splash->show();
Chris@283 329 QTimer::singleShot(5000, splash, SLOT(hide()));
Chris@231 330 application.processEvents();
Chris@231 331 }
Chris@237 332 settings.endGroup();
Chris@231 333
Chris@278 334 settings.beginGroup("RDF");
Chris@278 335 if (!settings.contains("rdf-indices")) {
Chris@278 336 QStringList list;
Chris@278 337 list << "http://www.vamp-plugins.org/rdf/plugins/index.txt";
Chris@278 338 settings.setValue("rdf-indices", list);
Chris@278 339 }
Chris@278 340 settings.endGroup();
Chris@278 341
Chris@1838 342 PluginPathSetter::initialiseEnvironmentVariables();
Chris@1837 343
Chris@141 344 QIcon icon;
Chris@141 345 int sizes[] = { 16, 22, 24, 32, 48, 64, 128 };
Chris@730 346 for (int i = 0; i < int(sizeof(sizes)/sizeof(sizes[0])); ++i) {
Chris@141 347 icon.addFile(QString(":icons/sv-%1x%2.png").arg(sizes[i]).arg(sizes[i]));
Chris@141 348 }
Chris@141 349 QApplication::setWindowIcon(icon);
Chris@7 350
Chris@0 351 QString language = QLocale::system().name();
Chris@1469 352 SVDEBUG << "System language is: " << language << endl;
Chris@0 353
Chris@658 354 settings.beginGroup("Preferences");
Chris@1469 355 QString prefLanguage = settings.value("locale", language).toString();
Chris@1469 356 if (prefLanguage != QString()) language = prefLanguage;
Chris@658 357 settings.endGroup();
Chris@658 358
Chris@0 359 QTranslator qtTranslator;
Chris@0 360 QString qtTrName = QString("qt_%1").arg(language);
Chris@438 361 SVDEBUG << "Loading " << qtTrName << "... ";
Chris@165 362 bool success = false;
Chris@165 363 if (!(success = qtTranslator.load(qtTrName))) {
Chris@165 364 QString qtDir = getenv("QTDIR");
Chris@165 365 if (qtDir != "") {
Chris@165 366 success = qtTranslator.load
Chris@165 367 (qtTrName, QDir(qtDir).filePath("translations"));
Chris@165 368 }
Chris@165 369 }
Chris@165 370 if (!success) {
Chris@438 371 SVDEBUG << "Failed\nFailed to load Qt translation for locale" << endl;
Chris@253 372 } else {
Chris@665 373 cerr << "Done" << endl;
Chris@165 374 }
Chris@0 375 application.installTranslator(&qtTranslator);
Chris@0 376
Chris@0 377 QTranslator svTranslator;
Chris@0 378 QString svTrName = QString("sonic-visualiser_%1").arg(language);
Chris@438 379 SVDEBUG << "Loading " << svTrName << "... ";
Chris@0 380 svTranslator.load(svTrName, ":i18n");
Chris@438 381 SVDEBUG << "Done" << endl;
Chris@0 382 application.installTranslator(&svTranslator);
Chris@0 383
Chris@187 384 StoreStartupLocale();
Chris@187 385
Chris@1144 386 // Make known-plugins query as early as possible after showing
Chris@1307 387 // splash screen.
Chris@1307 388 PluginScan::getInstance()->scan();
Chris@1144 389
Chris@1412 390 // Permit these types to be used as args in queued signal calls
Chris@0 391 qRegisterMetaType<PropertyContainer::PropertyName>("PropertyContainer::PropertyName");
Chris@1412 392 qRegisterMetaType<ZoomLevel>("ZoomLevel");
Chris@0 393
Chris@1045 394 MainWindow::SoundOptions options = MainWindow::WithEverything;
Chris@1045 395 if (!audioOutput) options = 0;
Chris@1045 396
Chris@1045 397 MainWindow *gui = new MainWindow(options, oscSupport);
Chris@222 398 application.setMainWindow(gui);
Chris@763 399 InteractiveFileFinder::setParentWidget(gui);
Chris@763 400 TransformUserConfigurator::setParentWidget(gui);
Chris@283 401 if (splash) {
Chris@283 402 QObject::connect(gui, SIGNAL(hideSplash()), splash, SLOT(hide()));
Chris@953 403 QObject::connect(gui, SIGNAL(hideSplash(QWidget *)),
Chris@953 404 splash, SLOT(finishSplash(QWidget *)));
Chris@283 405 }
Chris@0 406
Chris@0 407 QDesktopWidget *desktop = QApplication::desktop();
Chris@0 408 QRect available = desktop->availableGeometry();
Chris@0 409
Chris@378 410 int width = (available.width() * 2) / 3;
Chris@0 411 int height = available.height() / 2;
Chris@378 412 if (height < 450) height = (available.height() * 2) / 3;
Chris@0 413 if (width > height * 2) width = height * 2;
Chris@0 414
Chris@237 415 settings.beginGroup("MainWindow");
Chris@624 416
Chris@5 417 QSize size = settings.value("size", QSize(width, height)).toSize();
Chris@319 418 gui->resizeConstrained(size);
Chris@624 419
Chris@5 420 if (settings.contains("position")) {
Chris@297 421 QRect prevrect(settings.value("position").toPoint(), size);
Chris@297 422 if (!(available & prevrect).isEmpty()) {
Chris@297 423 gui->move(prevrect.topLeft());
Chris@297 424 }
Chris@5 425 }
Chris@624 426
Chris@624 427 if (settings.value("maximised", false).toBool()) {
Chris@624 428 gui->setWindowState(Qt::WindowMaximized);
Chris@624 429 }
Chris@624 430
Chris@5 431 settings.endGroup();
Chris@5 432
Chris@222 433 gui->show();
Chris@64 434
Chris@118 435 // The MainWindow class seems to have trouble dealing with this if
Chris@118 436 // it tries to adapt to this preference before the constructor is
Chris@118 437 // complete. As a lazy hack, apply it explicitly from here
Chris@222 438 gui->preferenceChanged("Property Box Layout");
Chris@118 439
dan@365 440 application.m_readyForFiles = true; // Ready to receive files from e.g. Apple Events
dan@365 441
Chris@54 442 for (QStringList::iterator i = args.begin(); i != args.end(); ++i) {
Chris@54 443
Chris@2232 444 // Note QCommandLineParser has now pulled out argv[0] and all
Chris@2232 445 // the options, so in theory everything here from the very
Chris@2232 446 // first arg should be relevant. But let's reject names
Chris@2232 447 // starting with "-" just in case.
Chris@2232 448
Chris@54 449 if (i->startsWith('-')) continue;
Chris@54 450
Chris@54 451 QString path = *i;
Chris@54 452
dan@365 453 application.handleFilepathArgument(path, splash);
dan@365 454 }
dan@365 455
Chris@2232 456 for (QStringList::iterator i = application.m_filepathQueue.begin();
Chris@2232 457 i != application.m_filepathQueue.end(); ++i) {
dan@365 458 QString path = *i;
dan@365 459 application.handleFilepathArgument(path, splash);
Chris@180 460 }
Chris@180 461
Chris@215 462 #ifdef HAVE_FFTW3F
Chris@215 463 settings.beginGroup("FFTWisdom");
Chris@215 464 QString wisdom = settings.value("wisdom").toString();
Chris@215 465 if (wisdom != "") {
Chris@215 466 fftwf_import_wisdom_from_string(wisdom.toLocal8Bit().data());
Chris@215 467 }
Chris@267 468 #ifdef HAVE_FFTW3
Chris@267 469 wisdom = settings.value("wisdom_d").toString();
Chris@267 470 if (wisdom != "") {
Chris@267 471 fftw_import_wisdom_from_string(wisdom.toLocal8Bit().data());
Chris@267 472 }
Chris@267 473 #endif
Chris@215 474 settings.endGroup();
Chris@215 475 #endif
Chris@180 476
Chris@2232 477 QString scriptFile = parser.value("osc-script");
Chris@2232 478 if (scriptFile != "") {
Chris@2240 479 SVDEBUG << "Note: Cueing OSC script from filename \"" << scriptFile
Chris@2240 480 << "\"" << endl;
Chris@2232 481 gui->cueOSCScript(scriptFile);
Chris@2232 482 }
Chris@2232 483
Chris@0 484 int rv = application.exec();
Chris@0 485
Chris@298 486 gui->hide();
Chris@298 487
Chris@0 488 cleanupMutex.lock();
Chris@332 489
Chris@589 490 if (!cleanedUp) {
Chris@589 491 TransformFactory::deleteInstance();
Chris@589 492 TempDirectory::getInstance()->cleanup();
Chris@589 493 cleanedUp = true;
Chris@589 494 }
Chris@589 495
Chris@11 496 application.releaseMainWindow();
Chris@5 497
Chris@215 498 #ifdef HAVE_FFTW3F
Chris@267 499 settings.beginGroup("FFTWisdom");
Chris@215 500 char *cwisdom = fftwf_export_wisdom_to_string();
Chris@215 501 if (cwisdom) {
Chris@215 502 settings.setValue("wisdom", cwisdom);
Chris@332 503 free(cwisdom);
Chris@215 504 }
Chris@267 505 #ifdef HAVE_FFTW3
Chris@267 506 cwisdom = fftw_export_wisdom_to_string();
Chris@267 507 if (cwisdom) {
Chris@267 508 settings.setValue("wisdom_d", cwisdom);
Chris@332 509 free(cwisdom);
Chris@267 510 }
Chris@267 511 #endif
Chris@267 512 settings.endGroup();
Chris@215 513 #endif
Chris@215 514
Chris@908 515 FileSource::debugReport();
Chris@908 516
Chris@222 517 delete gui;
Chris@222 518
Chris@573 519 cleanupMutex.unlock();
Chris@573 520
Chris@0 521 return rv;
Chris@0 522 }
dan@365 523
dan@365 524 bool SVApplication::event(QEvent *event){
Chris@730 525
Chris@730 526 // Avoid warnings/errors with -Wextra because we aren't explicitly
Chris@730 527 // handling all event types (-Wall is OK with this because of the
Chris@730 528 // default but the stricter level insists)
Chris@730 529 #pragma GCC diagnostic ignored "-Wswitch-enum"
Chris@730 530
dan@365 531 QString thePath;
Chris@730 532
dan@365 533 switch (event->type()) {
dan@365 534 case QEvent::FileOpen:
dan@365 535 thePath = static_cast<QFileOpenEvent *>(event)->file();
dan@365 536 if(m_readyForFiles)
Chris@2126 537 handleFilepathArgument(thePath, nullptr);
dan@365 538 else
dan@365 539 m_filepathQueue.append(thePath);
dan@365 540 return true;
dan@365 541 default:
dan@365 542 return QApplication::event(event);
dan@365 543 }
dan@365 544 }
dan@365 545
dan@365 546 /** Application-global handler for filepaths passed in, e.g. as command-line arguments or apple events */
Chris@953 547 void SVApplication::handleFilepathArgument(QString path, SVSplash *splash){
dan@365 548 static bool haveSession = false;
dan@365 549 static bool haveMainModel = false;
dan@365 550 static bool havePriorCommandLineModel = false;
dan@365 551
dan@365 552 MainWindow::FileOpenStatus status = MainWindow::FileOpenFailed;
dan@365 553
Chris@603 554 #ifdef Q_OS_WIN32
Chris@603 555 path.replace("\\", "/");
Chris@603 556 #endif
Chris@603 557
dan@365 558 if (path.endsWith("sv")) {
dan@365 559 if (!haveSession) {
Chris@738 560 status = m_mainWindow->openSessionPath(path);
dan@365 561 if (status == MainWindow::FileOpenSucceeded) {
dan@365 562 haveSession = true;
dan@365 563 haveMainModel = true;
dan@365 564 }
dan@365 565 } else {
Chris@665 566 cerr << "WARNING: Ignoring additional session file argument \"" << path << "\"" << endl;
dan@365 567 status = MainWindow::FileOpenSucceeded;
dan@365 568 }
dan@365 569 }
dan@365 570 if (status != MainWindow::FileOpenSucceeded) {
dan@365 571 if (!haveMainModel) {
Chris@844 572 status = m_mainWindow->openPath(path, MainWindow::ReplaceSession);
dan@365 573 if (status == MainWindow::FileOpenSucceeded) {
dan@365 574 haveMainModel = true;
dan@365 575 }
dan@365 576 } else {
dan@365 577 if (haveSession && !havePriorCommandLineModel) {
Chris@844 578 status = m_mainWindow->openPath(path, MainWindow::AskUser);
dan@365 579 if (status == MainWindow::FileOpenSucceeded) {
dan@365 580 havePriorCommandLineModel = true;
dan@365 581 }
dan@365 582 } else {
Chris@844 583 status = m_mainWindow->openPath(path, MainWindow::CreateAdditionalModel);
dan@365 584 }
dan@365 585 }
dan@365 586 }
dan@365 587 if (status == MainWindow::FileOpenFailed) {
dan@365 588 if (splash) splash->hide();
dan@365 589 QMessageBox::critical
dan@365 590 (m_mainWindow, QMessageBox::tr("Failed to open file"),
dan@365 591 QMessageBox::tr("File or URL \"%1\" could not be opened").arg(path));
dan@365 592 } else if (status == MainWindow::FileOpenWrongMode) {
dan@365 593 if (splash) splash->hide();
dan@365 594 QMessageBox::critical
dan@365 595 (m_mainWindow, QMessageBox::tr("Failed to open file"),
dan@365 596 QMessageBox::tr("<b>Audio required</b><p>Please load at least one audio file before importing annotation data"));
dan@365 597 }
dan@365 598 }