annotate src/main.cpp @ 516:449a0355f864 v2.0_osx_deploy

Deployment fixes. Qt on OSX now seems to depend on QtDBus, so copy that in, and also fail if anything is found to depend on an absent Qt framework.
author Chris Cannam
date Fri, 23 Oct 2015 08:50:39 +0100
parents b11b82c2e1af
children 30fbc53d8150
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 Tony
Chris@0 5 An intonation analysis and annotation tool
Chris@0 6 Centre for Digital Music, Queen Mary, University of London.
Chris@0 7 This file copyright 2006-2012 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@0 18 #include "system/System.h"
Chris@0 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@0 23 #include "widgets/TipDialog.h"
Chris@259 24 #include "widgets/InteractiveFileFinder.h"
Chris@70 25 #include "transform/TransformFactory.h"
Chris@0 26
Chris@0 27 #include <QMetaType>
Chris@0 28 #include <QApplication>
Chris@0 29 #include <QDesktopWidget>
Chris@0 30 #include <QMessageBox>
Chris@0 31 #include <QTranslator>
Chris@0 32 #include <QLocale>
Chris@0 33 #include <QSettings>
Chris@0 34 #include <QIcon>
Chris@0 35 #include <QSessionManager>
Chris@0 36 #include <QSplashScreen>
Chris@0 37 #include <QFileOpenEvent>
Chris@0 38 #include <QDir>
Chris@0 39
Chris@0 40 #include <iostream>
Chris@0 41 #include <signal.h>
Chris@458 42 #include <cstdlib>
Chris@0 43
Chris@443 44 #include <vamp-hostsdk/PluginHostAdapter.h>
Chris@443 45
Chris@0 46 static QMutex cleanupMutex;
Chris@70 47 static bool cleanedUp = false;
Chris@0 48
Chris@0 49 static void
Chris@0 50 signalHandler(int /* signal */)
Chris@0 51 {
Chris@0 52 // Avoid this happening more than once across threads
Chris@0 53
Chris@70 54 cerr << "signalHandler: cleaning up and exiting" << endl;
Chris@0 55 cleanupMutex.lock();
Chris@70 56 if (!cleanedUp) {
Chris@70 57 TempDirectory::getInstance()->cleanup();
Chris@70 58 cleanedUp = true;
Chris@70 59 }
Chris@70 60 cleanupMutex.unlock();
Chris@70 61 exit(0);
Chris@0 62 }
Chris@0 63
Chris@0 64 class TonyApplication : public QApplication
Chris@0 65 {
Chris@0 66 public:
Chris@361 67 TonyApplication(int &argc, char **argv) :
Chris@0 68 QApplication(argc, argv),
Chris@0 69 m_mainWindow(0),
Chris@0 70 m_readyForFiles(false)
Chris@0 71 {
Chris@500 72 // tidier without, I reckon
Chris@500 73 setAttribute(Qt::AA_DontShowIconsInMenus);
Chris@0 74 }
Chris@0 75 virtual ~TonyApplication() {
Chris@0 76 }
Chris@0 77
Chris@0 78 void setMainWindow(MainWindow *mw) { m_mainWindow = mw; }
Chris@0 79 void releaseMainWindow() { m_mainWindow = 0; }
Chris@0 80
Chris@0 81 virtual void commitData(QSessionManager &manager) {
Chris@0 82 if (!m_mainWindow) return;
Chris@0 83 bool mayAskUser = manager.allowsInteraction();
Chris@0 84 bool success = m_mainWindow->commitData(mayAskUser);
Chris@0 85 manager.release();
Chris@0 86 if (!success) manager.cancel();
Chris@0 87 }
Chris@0 88
Chris@0 89 void readyForFiles() {
Chris@0 90 m_readyForFiles = true;
Chris@0 91 }
Chris@0 92
Chris@0 93 void handleFilepathArgument(QString path, QSplashScreen *splash);
Chris@0 94
Chris@0 95 void handleQueuedPaths(QSplashScreen *splash) {
Chris@0 96 foreach (QString f, m_filepathQueue) {
Chris@0 97 handleFilepathArgument(f, splash);
Chris@0 98 }
Chris@0 99 }
Chris@0 100
Chris@0 101 protected:
Chris@0 102 MainWindow *m_mainWindow;
Chris@0 103
Chris@0 104 bool m_readyForFiles;
Chris@0 105 QStringList m_filepathQueue;
Chris@0 106
Chris@0 107 virtual bool event(QEvent *event) {
Chris@355 108
Chris@399 109 if (event->type() == QEvent::FileOpen) {
Chris@0 110 QString path = static_cast<QFileOpenEvent *>(event)->file();
Chris@0 111 if (m_readyForFiles) {
Chris@0 112 handleFilepathArgument(path, NULL);
Chris@0 113 } else {
Chris@0 114 m_filepathQueue.append(path);
Chris@0 115 }
Chris@0 116 return true;
Chris@399 117 } else {
Chris@0 118 return QApplication::event(event);
Chris@0 119 }
Chris@0 120 }
Chris@0 121 };
Chris@443 122
Chris@443 123 static void
Chris@443 124 setupTonyVampPath()
Chris@443 125 {
Chris@443 126 #ifdef Q_OS_WIN32
Chris@443 127 QChar sep(';');
Chris@443 128 QString programFiles = getenv("ProgramFiles");
Chris@443 129 if (programFiles == "") programFiles = "C:\\Program Files";
Chris@443 130 QString defaultTonyPath(programFiles + "\\Tony");
Chris@443 131 #else
Chris@443 132 QChar sep(':');
Chris@443 133 #ifdef Q_OS_MAC
Chris@443 134 QString defaultTonyPath;
Chris@443 135 #else
Chris@443 136 QString defaultTonyPath("/usr/local/lib/tony:/usr/lib/tony");
Chris@443 137 #endif
Chris@443 138 #endif
Chris@443 139
Chris@443 140 QString tonyVampPath = getenv("TONY_VAMP_PATH");
Chris@443 141 if (tonyVampPath == "") {
Chris@443 142 tonyVampPath = defaultTonyPath;
Chris@443 143 }
Chris@443 144 if (tonyVampPath == "") {
Chris@443 145 // just use the default Vamp path or VAMP_PATH environment
Chris@443 146 // variable -- leave it up to the Vamp SDK
Chris@443 147 return;
Chris@443 148 }
Chris@443 149
Chris@443 150 std::vector<std::string> vampPathList =
Chris@443 151 Vamp::PluginHostAdapter::getPluginPath();
Chris@443 152
Chris@443 153 QStringList qVampPathList;
Chris@443 154 for (auto p: vampPathList) qVampPathList.push_back(p.c_str());
Chris@443 155 QString vampPath = qVampPathList.join(sep);
Chris@443 156 QString newPath = tonyVampPath + sep + vampPath;
Chris@443 157
Chris@443 158 cerr << "Setting VAMP_PATH to " << newPath << " for Tony plugins" << endl;
Chris@458 159
Chris@458 160 QString env = "VAMP_PATH=" + newPath;
Chris@458 161
Chris@458 162 // Windows lacks setenv, must use putenv (different arg convention)
Chris@458 163 putenv(strdup(env.toLocal8Bit().data()));
Chris@443 164 }
Chris@0 165
Chris@0 166 int
Chris@0 167 main(int argc, char **argv)
Chris@0 168 {
Chris@0 169 svSystemSpecificInitialisation();
Chris@0 170
Chris@98 171 #ifdef Q_OS_MAC
Chris@98 172 if (QSysInfo::MacintoshVersion > QSysInfo::MV_10_8) {
Chris@98 173 // Fix for OS/X 10.9 font problem
Chris@98 174 QFont::insertSubstitution(".Lucida Grande UI", "Lucida Grande");
Chris@98 175 }
Chris@98 176 #endif
Chris@98 177
Chris@443 178 setupTonyVampPath();
Chris@443 179
Chris@0 180 TonyApplication application(argc, argv);
Chris@0 181
Chris@196 182 QStringList args = application.arguments();
Chris@0 183
Chris@0 184 signal(SIGINT, signalHandler);
Chris@0 185 signal(SIGTERM, signalHandler);
Chris@0 186
chris@52 187 #ifndef Q_OS_WIN32
Chris@0 188 signal(SIGHUP, signalHandler);
Chris@0 189 signal(SIGQUIT, signalHandler);
Chris@0 190 #endif
Chris@0 191
Chris@0 192 bool audioOutput = true;
matthiasm@364 193 bool sonification = true;
matthiasm@364 194 bool spectrogram = true;
Chris@0 195
Chris@0 196 if (args.contains("--help") || args.contains("-h") || args.contains("-?")) {
Chris@0 197 std::cerr << QApplication::tr(
matthiasm@366 198 "\nTony is a program for interactive note and pitch analysis and annotation.\n\nUsage:\n\n %1 [--no-audio] [--no-sonification] [--no-spectrogram] [<file> ...]\n\n --no-audio: Do not attempt to open an audio output device\n --no-sonification: Disable sonification of pitch tracks and notes and hide their toggles.\n --no-spectrogram: Disable spectrogram.\n <file>: One or more Tony (.ton) and audio files may be provided.").arg(argv[0]).toStdString() << std::endl;
Chris@0 199 exit(2);
Chris@0 200 }
Chris@0 201
Chris@0 202 if (args.contains("--no-audio")) audioOutput = false;
Chris@0 203
matthiasm@364 204 if (args.contains("--no-sonification")) sonification = false;
matthiasm@364 205
matthiasm@364 206 if (args.contains("--no-spectrogram")) spectrogram = false;
matthiasm@364 207
Chris@0 208 QApplication::setOrganizationName("QMUL");
Chris@0 209 QApplication::setOrganizationDomain("qmul.ac.uk");
Chris@0 210 QApplication::setApplicationName("Tony");
Chris@0 211
Chris@259 212 InteractiveFileFinder::getInstance()->setApplicationSessionExtension("ton");
Chris@259 213
Chris@484 214 QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
Chris@484 215
Chris@0 216 QSplashScreen *splash = 0;
Chris@0 217 // If we had a splash screen, we would show it here
Chris@0 218
Chris@0 219 QIcon icon;
Chris@0 220 int sizes[] = { 16, 22, 24, 32, 48, 64, 128 };
Chris@0 221 for (size_t i = 0; i < sizeof(sizes)/sizeof(sizes[0]); ++i) {
Chris@0 222 icon.addFile(QString(":icons/tony-%1x%2.png").arg(sizes[i]).arg(sizes[i]));
Chris@0 223 }
Chris@0 224 QApplication::setWindowIcon(icon);
Chris@0 225
Chris@0 226 QString language = QLocale::system().name();
Chris@0 227
Chris@0 228 QTranslator qtTranslator;
Chris@0 229 QString qtTrName = QString("qt_%1").arg(language);
Chris@0 230 std::cerr << "Loading " << qtTrName.toStdString() << "..." << std::endl;
Chris@0 231 bool success = false;
Chris@0 232 if (!(success = qtTranslator.load(qtTrName))) {
Chris@0 233 QString qtDir = getenv("QTDIR");
Chris@0 234 if (qtDir != "") {
Chris@0 235 success = qtTranslator.load
Chris@0 236 (qtTrName, QDir(qtDir).filePath("translations"));
Chris@0 237 }
Chris@0 238 }
Chris@0 239 if (!success) {
Chris@0 240 std::cerr << "Failed to load Qt translation for locale" << std::endl;
Chris@0 241 }
Chris@0 242 application.installTranslator(&qtTranslator);
Chris@0 243
Chris@0 244 StoreStartupLocale();
Chris@0 245
Chris@0 246 // Permit size_t and PropertyName to be used as args in queued signal calls
Chris@0 247 qRegisterMetaType<size_t>("size_t");
Chris@0 248 qRegisterMetaType<PropertyContainer::PropertyName>("PropertyContainer::PropertyName");
Chris@0 249
Chris@474 250 MainWindow::SoundOptions options = MainWindow::WithEverything;
Chris@474 251 if (!audioOutput) options = 0;
Chris@474 252
Chris@474 253 MainWindow *gui = new MainWindow(options, sonification, spectrogram);
Chris@0 254 application.setMainWindow(gui);
Chris@0 255 if (splash) {
Chris@0 256 QObject::connect(gui, SIGNAL(hideSplash()), splash, SLOT(hide()));
Chris@0 257 }
Chris@0 258
Chris@0 259 QDesktopWidget *desktop = QApplication::desktop();
Chris@0 260 QRect available = desktop->availableGeometry();
Chris@0 261
Chris@0 262 int width = (available.width() * 2) / 3;
Chris@0 263 int height = available.height() / 2;
Chris@0 264 if (height < 450) height = (available.height() * 2) / 3;
Chris@0 265 if (width > height * 2) width = height * 2;
Chris@0 266
Chris@0 267 QSettings settings;
Chris@0 268 settings.beginGroup("MainWindow");
Chris@0 269 QSize size = settings.value("size", QSize(width, height)).toSize();
Chris@0 270 gui->resizeConstrained(size);
Chris@0 271 if (settings.contains("position")) {
Chris@0 272 QRect prevrect(settings.value("position").toPoint(), size);
Chris@0 273 if (!(available & prevrect).isEmpty()) {
Chris@0 274 gui->move(prevrect.topLeft());
Chris@0 275 }
Chris@0 276 }
Chris@0 277 settings.endGroup();
Chris@0 278
Chris@0 279 gui->show();
Chris@0 280
Chris@0 281 application.readyForFiles();
Chris@0 282
Chris@0 283 for (QStringList::iterator i = args.begin(); i != args.end(); ++i) {
Chris@0 284
Chris@0 285 if (i == args.begin()) continue;
Chris@0 286 if (i->startsWith('-')) continue;
Chris@0 287
Chris@0 288 QString path = *i;
Chris@0 289
Chris@0 290 application.handleFilepathArgument(path, splash);
Chris@0 291 }
Chris@0 292
Chris@0 293 application.handleQueuedPaths(splash);
Chris@0 294
Chris@0 295 if (splash) splash->finish(gui);
Chris@0 296 delete splash;
Chris@0 297
Chris@0 298 int rv = application.exec();
Chris@0 299
Chris@0 300 gui->hide();
Chris@0 301
Chris@0 302 cleanupMutex.lock();
Chris@70 303
Chris@70 304 if (!cleanedUp) {
Chris@70 305 TransformFactory::deleteInstance();
Chris@70 306 TempDirectory::getInstance()->cleanup();
Chris@70 307 cleanedUp = true;
Chris@70 308 }
Chris@70 309
Chris@0 310 application.releaseMainWindow();
Chris@0 311
Chris@0 312 delete gui;
Chris@0 313
Chris@70 314 cleanupMutex.unlock();
Chris@70 315
Chris@0 316 return rv;
Chris@0 317 }
Chris@0 318
Chris@0 319 /** Application-global handler for filepaths passed in, e.g. as
Chris@0 320 * command-line arguments or apple events */
Chris@0 321
Chris@0 322 void TonyApplication::handleFilepathArgument(QString path,
Chris@0 323 QSplashScreen *splash)
Chris@0 324 {
Chris@0 325 static bool haveSession = false;
Chris@0 326 static bool haveMainModel = false;
Chris@0 327 static bool havePriorCommandLineModel = false;
Chris@0 328
Chris@103 329 if (!m_mainWindow) return;
Chris@103 330
Chris@0 331 MainWindow::FileOpenStatus status = MainWindow::FileOpenFailed;
Chris@0 332
Chris@196 333 #ifdef Q_OS_WIN32
Chris@196 334 path.replace("\\", "/");
Chris@196 335 #endif
Chris@196 336
Chris@0 337 if (path.endsWith("ton")) {
Chris@0 338 if (!haveSession) {
Chris@362 339 status = m_mainWindow->openSessionPath(path);
Chris@0 340 if (status == MainWindow::FileOpenSucceeded) {
Chris@0 341 haveSession = true;
Chris@0 342 haveMainModel = true;
Chris@0 343 }
Chris@0 344 } else {
Chris@0 345 std::cerr << "WARNING: Ignoring additional session file argument \"" << path << "\"" << std::endl;
Chris@0 346 status = MainWindow::FileOpenSucceeded;
Chris@0 347 }
Chris@0 348 }
Chris@0 349 if (status != MainWindow::FileOpenSucceeded) {
Chris@0 350 if (!haveMainModel) {
Chris@387 351 status = m_mainWindow->openPath(path, MainWindow::ReplaceSession);
Chris@0 352 if (status == MainWindow::FileOpenSucceeded) {
Chris@0 353 haveMainModel = true;
Chris@0 354 }
Chris@0 355 } else {
Chris@0 356 if (haveSession && !havePriorCommandLineModel) {
Chris@387 357 status = m_mainWindow->openPath(path, MainWindow::AskUser);
Chris@0 358 if (status == MainWindow::FileOpenSucceeded) {
Chris@0 359 havePriorCommandLineModel = true;
Chris@0 360 }
Chris@0 361 } else {
Chris@387 362 status = m_mainWindow->openPath(path, MainWindow::CreateAdditionalModel);
Chris@0 363 }
Chris@0 364 }
Chris@0 365 }
Chris@0 366 if (status == MainWindow::FileOpenFailed) {
Chris@0 367 if (splash) splash->hide();
Chris@0 368 QMessageBox::critical
Chris@0 369 (m_mainWindow, QMessageBox::tr("Failed to open file"),
Chris@0 370 QMessageBox::tr("File or URL \"%1\" could not be opened").arg(path));
Chris@0 371 } else if (status == MainWindow::FileOpenWrongMode) {
Chris@0 372 if (splash) splash->hide();
Chris@0 373 QMessageBox::critical
Chris@0 374 (m_mainWindow, QMessageBox::tr("Failed to open file"),
Chris@0 375 QMessageBox::tr("<b>Audio required</b><p>Please load at least one audio file before importing annotation data"));
Chris@0 376 }
Chris@0 377 }
Chris@0 378