annotate main/main.cpp @ 1109:ed51b940662c spectrogram-minor-refactor

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