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 }
|