annotate main/main.cpp @ 1257:f2105c5b0b12 piper

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