annotate main/main.cpp @ 2038:8529a60df1b4

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