annotate main/main.cpp @ 485:b6c00a0a618e integration_library

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