Mercurial > hg > sonic-visualiser
comparison main/main.cpp @ 362:a0ea7bcac15b macness
Now can load file when someone chooses from Mac Finder "Open with > Sonic Visualiser" - but currently *only* if the app is already running.
author | Dan Stowell <dan.stowell@eecs.qmul.ac.uk> |
---|---|
date | Mon, 11 Oct 2010 14:16:54 +0100 |
parents | 3bb7eef5d45c |
children | 43205eda54bd |
comparison
equal
deleted
inserted
replaced
361:101f05420cba | 362:a0ea7bcac15b |
---|---|
20 #include "base/TempDirectory.h" | 20 #include "base/TempDirectory.h" |
21 #include "base/PropertyContainer.h" | 21 #include "base/PropertyContainer.h" |
22 #include "base/Preferences.h" | 22 #include "base/Preferences.h" |
23 #include "widgets/TipDialog.h" | 23 #include "widgets/TipDialog.h" |
24 #include "transform/TransformFactory.h" | 24 #include "transform/TransformFactory.h" |
25 | |
26 #include <ApplicationServices/ApplicationServices.h> | |
27 #include <Carbon/Carbon.h> | |
25 | 28 |
26 #include <QMetaType> | 29 #include <QMetaType> |
27 #include <QApplication> | 30 #include <QApplication> |
28 #include <QDesktopWidget> | 31 #include <QDesktopWidget> |
29 #include <QMessageBox> | 32 #include <QMessageBox> |
169 of the window. | 172 of the window. |
170 | 173 |
171 */ | 174 */ |
172 | 175 |
173 static QMutex cleanupMutex; | 176 static QMutex cleanupMutex; |
177 static MainWindow *gui; | |
174 | 178 |
175 static void | 179 static void |
176 signalHandler(int /* signal */) | 180 signalHandler(int /* signal */) |
177 { | 181 { |
178 // Avoid this happening more than once across threads | 182 // Avoid this happening more than once across threads |
200 bool success = m_mainWindow->commitData(mayAskUser); | 204 bool success = m_mainWindow->commitData(mayAskUser); |
201 manager.release(); | 205 manager.release(); |
202 if (!success) manager.cancel(); | 206 if (!success) manager.cancel(); |
203 } | 207 } |
204 | 208 |
205 protected: | 209 /** Application-global handler for filepaths passed in, e.g. as command-line arguments or apple events */ |
206 MainWindow *m_mainWindow; | 210 static void handleFilepathArgument(QString path, QSplashScreen *splash){ |
207 }; | 211 static bool haveSession = false; |
208 | 212 static bool haveMainModel = false; |
209 int | 213 static bool havePriorCommandLineModel = false; |
210 main(int argc, char **argv) | 214 |
211 { | |
212 #ifdef Q_WS_X11 | |
213 #if QT_VERSION >= 0x040500 | |
214 // QApplication::setGraphicsSystem("raster"); | |
215 #endif | |
216 #endif | |
217 | |
218 SVApplication application(argc, argv); | |
219 | |
220 QStringList args = application.arguments(); | |
221 | |
222 signal(SIGINT, signalHandler); | |
223 signal(SIGTERM, signalHandler); | |
224 | |
225 #ifndef Q_WS_WIN32 | |
226 signal(SIGHUP, signalHandler); | |
227 signal(SIGQUIT, signalHandler); | |
228 #endif | |
229 | |
230 svSystemSpecificInitialisation(); | |
231 | |
232 bool audioOutput = true; | |
233 bool oscSupport = true; | |
234 | |
235 if (args.contains("--help") || args.contains("-h") || args.contains("-?")) { | |
236 std::cerr << QApplication::tr( | |
237 "\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]).toStdString() << std::endl; | |
238 exit(2); | |
239 } | |
240 | |
241 if (args.contains("--no-audio")) audioOutput = false; | |
242 if (args.contains("--no-osc")) oscSupport = false; | |
243 | |
244 QApplication::setOrganizationName("sonic-visualiser"); | |
245 QApplication::setOrganizationDomain("sonicvisualiser.org"); | |
246 QApplication::setApplicationName(QApplication::tr("Sonic Visualiser")); | |
247 | |
248 QSplashScreen *splash = 0; | |
249 | |
250 QSettings settings; | |
251 | |
252 settings.beginGroup("Preferences"); | |
253 if (settings.value("show-splash", true).toBool()) { | |
254 QPixmap pixmap(":/icons/sv-splash.png"); | |
255 QPainter painter; | |
256 painter.begin(&pixmap); | |
257 QString text = QString("v%1").arg(SV_VERSION); | |
258 painter.drawText | |
259 (pixmap.width() - painter.fontMetrics().width(text) - 10, | |
260 10 + painter.fontMetrics().ascent(), | |
261 text); | |
262 painter.end(); | |
263 splash = new QSplashScreen(pixmap); | |
264 splash->show(); | |
265 QTimer::singleShot(5000, splash, SLOT(hide())); | |
266 application.processEvents(); | |
267 } | |
268 settings.endGroup(); | |
269 | |
270 settings.beginGroup("RDF"); | |
271 if (!settings.contains("rdf-indices")) { | |
272 QStringList list; | |
273 list << "http://www.vamp-plugins.org/rdf/plugins/index.txt"; | |
274 settings.setValue("rdf-indices", list); | |
275 } | |
276 settings.endGroup(); | |
277 | |
278 QIcon icon; | |
279 int sizes[] = { 16, 22, 24, 32, 48, 64, 128 }; | |
280 for (int i = 0; i < sizeof(sizes)/sizeof(sizes[0]); ++i) { | |
281 icon.addFile(QString(":icons/sv-%1x%2.png").arg(sizes[i]).arg(sizes[i])); | |
282 } | |
283 QApplication::setWindowIcon(icon); | |
284 | |
285 QString language = QLocale::system().name(); | |
286 | |
287 QTranslator qtTranslator; | |
288 QString qtTrName = QString("qt_%1").arg(language); | |
289 std::cerr << "Loading " << qtTrName.toStdString() << "... "; | |
290 bool success = false; | |
291 if (!(success = qtTranslator.load(qtTrName))) { | |
292 QString qtDir = getenv("QTDIR"); | |
293 if (qtDir != "") { | |
294 success = qtTranslator.load | |
295 (qtTrName, QDir(qtDir).filePath("translations")); | |
296 } | |
297 } | |
298 if (!success) { | |
299 std::cerr << "Failed\nFailed to load Qt translation for locale" << std::endl; | |
300 } else { | |
301 std::cerr << "Done" << std::endl; | |
302 } | |
303 application.installTranslator(&qtTranslator); | |
304 | |
305 QTranslator svTranslator; | |
306 QString svTrName = QString("sonic-visualiser_%1").arg(language); | |
307 std::cerr << "Loading " << svTrName.toStdString() << "... "; | |
308 svTranslator.load(svTrName, ":i18n"); | |
309 std::cerr << "Done" << std::endl; | |
310 application.installTranslator(&svTranslator); | |
311 | |
312 StoreStartupLocale(); | |
313 | |
314 // Permit size_t and PropertyName to be used as args in queued signal calls | |
315 qRegisterMetaType<size_t>("size_t"); | |
316 qRegisterMetaType<PropertyContainer::PropertyName>("PropertyContainer::PropertyName"); | |
317 | |
318 MainWindow *gui = new MainWindow(audioOutput, oscSupport); | |
319 application.setMainWindow(gui); | |
320 if (splash) { | |
321 QObject::connect(gui, SIGNAL(hideSplash()), splash, SLOT(hide())); | |
322 } | |
323 | |
324 QDesktopWidget *desktop = QApplication::desktop(); | |
325 QRect available = desktop->availableGeometry(); | |
326 | |
327 int width = available.width() * 2 / 3; | |
328 int height = available.height() / 2; | |
329 if (height < 450) height = available.height() * 2 / 3; | |
330 if (width > height * 2) width = height * 2; | |
331 | |
332 settings.beginGroup("MainWindow"); | |
333 QSize size = settings.value("size", QSize(width, height)).toSize(); | |
334 gui->resizeConstrained(size); | |
335 if (settings.contains("position")) { | |
336 QRect prevrect(settings.value("position").toPoint(), size); | |
337 if (!(available & prevrect).isEmpty()) { | |
338 gui->move(prevrect.topLeft()); | |
339 } | |
340 } | |
341 settings.endGroup(); | |
342 | |
343 gui->show(); | |
344 | |
345 // The MainWindow class seems to have trouble dealing with this if | |
346 // it tries to adapt to this preference before the constructor is | |
347 // complete. As a lazy hack, apply it explicitly from here | |
348 gui->preferenceChanged("Property Box Layout"); | |
349 | |
350 bool haveSession = false; | |
351 bool haveMainModel = false; | |
352 bool havePriorCommandLineModel = false; | |
353 | |
354 for (QStringList::iterator i = args.begin(); i != args.end(); ++i) { | |
355 | |
356 MainWindow::FileOpenStatus status = MainWindow::FileOpenFailed; | 215 MainWindow::FileOpenStatus status = MainWindow::FileOpenFailed; |
357 | 216 |
358 if (i == args.begin()) continue; | |
359 if (i->startsWith('-')) continue; | |
360 | |
361 QString path = *i; | |
362 | |
363 if (path.endsWith("sv")) { | 217 if (path.endsWith("sv")) { |
364 if (!haveSession) { | 218 if (!haveSession) { |
365 status = gui->openSessionFile(path); | 219 status = gui->openSessionFile(path); |
366 if (status == MainWindow::FileOpenSucceeded) { | 220 if (status == MainWindow::FileOpenSucceeded) { |
367 haveSession = true; | 221 haveSession = true; |
389 } | 243 } |
390 } | 244 } |
391 } | 245 } |
392 if (status == MainWindow::FileOpenFailed) { | 246 if (status == MainWindow::FileOpenFailed) { |
393 if (splash) splash->hide(); | 247 if (splash) splash->hide(); |
394 QMessageBox::critical | 248 QMessageBox::critical |
395 (gui, QMessageBox::tr("Failed to open file"), | 249 (gui, QMessageBox::tr("Failed to open file"), |
396 QMessageBox::tr("File or URL \"%1\" could not be opened").arg(path)); | 250 QMessageBox::tr("File or URL \"%1\" could not be opened").arg(path)); |
397 } else if (status == MainWindow::FileOpenWrongMode) { | 251 } else if (status == MainWindow::FileOpenWrongMode) { |
398 if (splash) splash->hide(); | 252 if (splash) splash->hide(); |
399 QMessageBox::critical | 253 QMessageBox::critical |
400 (gui, QMessageBox::tr("Failed to open file"), | 254 (gui, QMessageBox::tr("Failed to open file"), |
401 QMessageBox::tr("<b>Audio required</b><p>Please load at least one audio file before importing annotation data")); | 255 QMessageBox::tr("<b>Audio required</b><p>Please load at least one audio file before importing annotation data")); |
402 } | 256 } |
403 } | 257 } |
404 | 258 |
259 #ifdef Q_WS_MAC | |
260 static pascal OSErr HandleOpenDocAE (const AppleEvent * theAppleEvent, AppleEvent * /* reply */, SInt32 /* handlerRefcon */) { | |
261 AEDescList docList; | |
262 FSRef theFSRef; | |
263 long count = 0; | |
264 OSErr err; | |
265 | |
266 err = AEGetParamDesc(theAppleEvent, keyDirectObject, typeAEList, &docList); | |
267 err = AECountItems(&docList, &count); | |
268 if(count == 0){ | |
269 std::cerr << "SV warning: received apple open-documents event with no documents (count==0)" << std::endl; | |
270 AEDisposeDesc(&docList); | |
271 return noErr; | |
272 } | |
273 // NB we could be passed a list of files - however, SV on Mac currently can only open one at a time, so we simply open the first. | |
274 | |
275 err = AEGetNthPtr(&docList, 1, typeFSRef, NULL, NULL, &theFSRef, sizeof(theFSRef), NULL); | |
276 | |
277 // Call routine to open document with current reference. | |
278 if(err == noErr){ | |
279 char uPath[1024]; | |
280 FSRefMakePath(&theFSRef, (UInt8*)uPath, 1023); | |
281 std::cerr << "SV received Apple Event request to open " << uPath << std::endl; | |
282 handleFilepathArgument(uPath, NULL); | |
283 } | |
284 | |
285 AEDisposeDesc(&docList); | |
286 return noErr; | |
287 } | |
288 static OSErr InstallMacOSEventHandlers(void) | |
289 { | |
290 OSErr err; | |
291 err = AEInstallEventHandler(kCoreEventClass, kAEOpenDocuments, NewAEEventHandlerUPP(HandleOpenDocAE), 0, false); | |
292 std::cerr << "HandleOpenDocAE installed. " << err << std::endl; | |
293 return err; | |
294 } | |
295 #endif // Q_WS_MAC | |
296 | |
297 protected: | |
298 MainWindow *m_mainWindow; | |
299 }; | |
300 | |
301 int | |
302 main(int argc, char **argv) | |
303 { | |
304 #ifdef Q_WS_X11 | |
305 #if QT_VERSION >= 0x040500 | |
306 // QApplication::setGraphicsSystem("raster"); | |
307 #endif | |
308 #endif | |
309 | |
310 SVApplication application(argc, argv); | |
311 | |
312 QStringList args = application.arguments(); | |
313 | |
314 signal(SIGINT, signalHandler); | |
315 signal(SIGTERM, signalHandler); | |
316 | |
317 #ifndef Q_WS_WIN32 | |
318 signal(SIGHUP, signalHandler); | |
319 signal(SIGQUIT, signalHandler); | |
320 #endif | |
321 | |
322 svSystemSpecificInitialisation(); | |
323 | |
324 bool audioOutput = true; | |
325 bool oscSupport = true; | |
326 | |
327 if (args.contains("--help") || args.contains("-h") || args.contains("-?")) { | |
328 std::cerr << QApplication::tr( | |
329 "\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]).toStdString() << std::endl; | |
330 exit(2); | |
331 } | |
332 | |
333 if (args.contains("--no-audio")) audioOutput = false; | |
334 if (args.contains("--no-osc")) oscSupport = false; | |
335 | |
336 QApplication::setOrganizationName("sonic-visualiser"); | |
337 QApplication::setOrganizationDomain("sonicvisualiser.org"); | |
338 QApplication::setApplicationName(QApplication::tr("Sonic Visualiser")); | |
339 | |
340 QSplashScreen *splash = 0; | |
341 | |
342 QSettings settings; | |
343 | |
344 settings.beginGroup("Preferences"); | |
345 if (settings.value("show-splash", true).toBool()) { | |
346 QPixmap pixmap(":/icons/sv-splash.png"); | |
347 QPainter painter; | |
348 painter.begin(&pixmap); | |
349 QString text = QString("v%1").arg(SV_VERSION); | |
350 painter.drawText | |
351 (pixmap.width() - painter.fontMetrics().width(text) - 10, | |
352 10 + painter.fontMetrics().ascent(), | |
353 text); | |
354 painter.end(); | |
355 splash = new QSplashScreen(pixmap); | |
356 splash->show(); | |
357 QTimer::singleShot(5000, splash, SLOT(hide())); | |
358 application.processEvents(); | |
359 } | |
360 settings.endGroup(); | |
361 | |
362 settings.beginGroup("RDF"); | |
363 if (!settings.contains("rdf-indices")) { | |
364 QStringList list; | |
365 list << "http://www.vamp-plugins.org/rdf/plugins/index.txt"; | |
366 settings.setValue("rdf-indices", list); | |
367 } | |
368 settings.endGroup(); | |
369 | |
370 QIcon icon; | |
371 int sizes[] = { 16, 22, 24, 32, 48, 64, 128 }; | |
372 for (int i = 0; i < sizeof(sizes)/sizeof(sizes[0]); ++i) { | |
373 icon.addFile(QString(":icons/sv-%1x%2.png").arg(sizes[i]).arg(sizes[i])); | |
374 } | |
375 QApplication::setWindowIcon(icon); | |
376 | |
377 QString language = QLocale::system().name(); | |
378 | |
379 QTranslator qtTranslator; | |
380 QString qtTrName = QString("qt_%1").arg(language); | |
381 std::cerr << "Loading " << qtTrName.toStdString() << "... "; | |
382 bool success = false; | |
383 if (!(success = qtTranslator.load(qtTrName))) { | |
384 QString qtDir = getenv("QTDIR"); | |
385 if (qtDir != "") { | |
386 success = qtTranslator.load | |
387 (qtTrName, QDir(qtDir).filePath("translations")); | |
388 } | |
389 } | |
390 if (!success) { | |
391 std::cerr << "Failed\nFailed to load Qt translation for locale" << std::endl; | |
392 } else { | |
393 std::cerr << "Done" << std::endl; | |
394 } | |
395 application.installTranslator(&qtTranslator); | |
396 | |
397 QTranslator svTranslator; | |
398 QString svTrName = QString("sonic-visualiser_%1").arg(language); | |
399 std::cerr << "Loading " << svTrName.toStdString() << "... "; | |
400 svTranslator.load(svTrName, ":i18n"); | |
401 std::cerr << "Done" << std::endl; | |
402 application.installTranslator(&svTranslator); | |
403 | |
404 StoreStartupLocale(); | |
405 | |
406 // Permit size_t and PropertyName to be used as args in queued signal calls | |
407 qRegisterMetaType<size_t>("size_t"); | |
408 qRegisterMetaType<PropertyContainer::PropertyName>("PropertyContainer::PropertyName"); | |
409 | |
410 gui = new MainWindow(audioOutput, oscSupport); | |
411 application.setMainWindow(gui); | |
412 if (splash) { | |
413 QObject::connect(gui, SIGNAL(hideSplash()), splash, SLOT(hide())); | |
414 } | |
415 | |
416 QDesktopWidget *desktop = QApplication::desktop(); | |
417 QRect available = desktop->availableGeometry(); | |
418 | |
419 int width = available.width() * 2 / 3; | |
420 int height = available.height() / 2; | |
421 if (height < 450) height = available.height() * 2 / 3; | |
422 if (width > height * 2) width = height * 2; | |
423 | |
424 settings.beginGroup("MainWindow"); | |
425 QSize size = settings.value("size", QSize(width, height)).toSize(); | |
426 gui->resizeConstrained(size); | |
427 if (settings.contains("position")) { | |
428 QRect prevrect(settings.value("position").toPoint(), size); | |
429 if (!(available & prevrect).isEmpty()) { | |
430 gui->move(prevrect.topLeft()); | |
431 } | |
432 } | |
433 settings.endGroup(); | |
434 | |
435 #ifdef Q_WS_MAC | |
436 application.InstallMacOSEventHandlers(); | |
437 #endif // Q_WS_MAC | |
438 | |
439 gui->show(); | |
440 | |
441 // The MainWindow class seems to have trouble dealing with this if | |
442 // it tries to adapt to this preference before the constructor is | |
443 // complete. As a lazy hack, apply it explicitly from here | |
444 gui->preferenceChanged("Property Box Layout"); | |
445 | |
446 for (QStringList::iterator i = args.begin(); i != args.end(); ++i) { | |
447 | |
448 if (i == args.begin()) continue; | |
449 if (i->startsWith('-')) continue; | |
450 | |
451 QString path = *i; | |
452 | |
453 SVApplication::handleFilepathArgument(path, splash); | |
454 } | |
455 | |
405 #ifdef HAVE_FFTW3F | 456 #ifdef HAVE_FFTW3F |
406 settings.beginGroup("FFTWisdom"); | 457 settings.beginGroup("FFTWisdom"); |
407 QString wisdom = settings.value("wisdom").toString(); | 458 QString wisdom = settings.value("wisdom").toString(); |
408 if (wisdom != "") { | 459 if (wisdom != "") { |
409 fftwf_import_wisdom_from_string(wisdom.toLocal8Bit().data()); | 460 fftwf_import_wisdom_from_string(wisdom.toLocal8Bit().data()); |