annotate main/main.cpp @ 698:ee97c742d184 tip

Default branch is now named default on git as well as hg, in case we ever want to switch to mirroring in the other direction
author Chris Cannam
date Thu, 27 Aug 2020 15:58:43 +0100
parents de3d628e048e
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 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@520 26 #include "svcore/plugin/PluginScan.h"
Chris@0 27
Chris@0 28 #include <QMetaType>
Chris@0 29 #include <QApplication>
Chris@572 30 #include <QScreen>
Chris@0 31 #include <QMessageBox>
Chris@0 32 #include <QTranslator>
Chris@0 33 #include <QLocale>
Chris@0 34 #include <QSettings>
Chris@0 35 #include <QIcon>
Chris@0 36 #include <QSessionManager>
Chris@0 37 #include <QSplashScreen>
Chris@0 38 #include <QFileOpenEvent>
Chris@0 39 #include <QDir>
Chris@0 40
Chris@0 41 #include <iostream>
Chris@0 42 #include <signal.h>
Chris@458 43 #include <cstdlib>
Chris@0 44
Chris@587 45 #include "../version.h"
Chris@587 46
Chris@443 47 #include <vamp-hostsdk/PluginHostAdapter.h>
Chris@443 48
Chris@0 49 static QMutex cleanupMutex;
Chris@70 50 static bool cleanedUp = false;
Chris@0 51
Chris@0 52 static void
Chris@0 53 signalHandler(int /* signal */)
Chris@0 54 {
Chris@0 55 // Avoid this happening more than once across threads
Chris@0 56
Chris@70 57 cerr << "signalHandler: cleaning up and exiting" << endl;
Chris@609 58
Chris@609 59 if (cleanupMutex.tryLock(5000)) {
Chris@609 60 if (!cleanedUp) {
Chris@609 61 TempDirectory::getInstance()->cleanup();
Chris@609 62 cleanedUp = true;
Chris@609 63 }
Chris@609 64 cleanupMutex.unlock();
Chris@70 65 }
Chris@609 66
Chris@70 67 exit(0);
Chris@0 68 }
Chris@0 69
Chris@0 70 class TonyApplication : public QApplication
Chris@0 71 {
Chris@0 72 public:
Chris@361 73 TonyApplication(int &argc, char **argv) :
Chris@0 74 QApplication(argc, argv),
Chris@0 75 m_mainWindow(0),
Chris@0 76 m_readyForFiles(false)
Chris@0 77 {
Chris@500 78 // tidier without, I reckon
Chris@500 79 setAttribute(Qt::AA_DontShowIconsInMenus);
Chris@0 80 }
Chris@0 81 virtual ~TonyApplication() {
Chris@0 82 }
Chris@0 83
Chris@0 84 void setMainWindow(MainWindow *mw) { m_mainWindow = mw; }
Chris@0 85 void releaseMainWindow() { m_mainWindow = 0; }
Chris@0 86
Chris@0 87 virtual void commitData(QSessionManager &manager) {
Chris@0 88 if (!m_mainWindow) return;
Chris@0 89 bool mayAskUser = manager.allowsInteraction();
Chris@0 90 bool success = m_mainWindow->commitData(mayAskUser);
Chris@0 91 manager.release();
Chris@0 92 if (!success) manager.cancel();
Chris@0 93 }
Chris@0 94
Chris@0 95 void readyForFiles() {
Chris@0 96 m_readyForFiles = true;
Chris@0 97 }
Chris@0 98
Chris@0 99 void handleFilepathArgument(QString path, QSplashScreen *splash);
Chris@0 100
Chris@0 101 void handleQueuedPaths(QSplashScreen *splash) {
Chris@0 102 foreach (QString f, m_filepathQueue) {
Chris@0 103 handleFilepathArgument(f, splash);
Chris@0 104 }
Chris@0 105 }
Chris@0 106
Chris@0 107 protected:
Chris@0 108 MainWindow *m_mainWindow;
Chris@0 109
Chris@0 110 bool m_readyForFiles;
Chris@0 111 QStringList m_filepathQueue;
Chris@0 112
Chris@0 113 virtual bool event(QEvent *event) {
Chris@355 114
Chris@399 115 if (event->type() == QEvent::FileOpen) {
Chris@0 116 QString path = static_cast<QFileOpenEvent *>(event)->file();
Chris@0 117 if (m_readyForFiles) {
Chris@0 118 handleFilepathArgument(path, NULL);
Chris@0 119 } else {
Chris@0 120 m_filepathQueue.append(path);
Chris@0 121 }
Chris@0 122 return true;
Chris@399 123 } else {
Chris@0 124 return QApplication::event(event);
Chris@0 125 }
Chris@0 126 }
Chris@0 127 };
Chris@443 128
Chris@541 129 static QString
Chris@541 130 getEnvQStr(QString variable)
Chris@541 131 {
Chris@541 132 #ifdef Q_OS_WIN32
Chris@541 133 std::wstring wvar = variable.toStdWString();
Chris@541 134 wchar_t *value = _wgetenv(wvar.c_str());
Chris@541 135 if (!value) return QString();
Chris@548 136 else return QString::fromStdWString(std::wstring(value));
Chris@541 137 #else
Chris@541 138 std::string var = variable.toStdString();
Chris@541 139 return QString::fromUtf8(qgetenv(var.c_str()));
Chris@541 140 #endif
Chris@541 141 }
Chris@541 142
Chris@541 143 static void
Chris@541 144 putEnvQStr(QString assignment)
Chris@541 145 {
Chris@541 146 #ifdef Q_OS_WIN32
Chris@541 147 std::wstring wassignment = assignment.toStdWString();
Chris@548 148 _wputenv(_wcsdup(wassignment.c_str()));
Chris@541 149 #else
Chris@541 150 putenv(strdup(assignment.toUtf8().data()));
Chris@541 151 #endif
Chris@541 152 }
Chris@541 153
Chris@443 154 static void
Chris@443 155 setupTonyVampPath()
Chris@443 156 {
Chris@594 157 QString myVampPath = getEnvQStr("TONY_VAMP_PATH");
Chris@541 158
Chris@443 159 #ifdef Q_OS_WIN32
Chris@443 160 QChar sep(';');
Chris@443 161 #else
Chris@443 162 QChar sep(':');
Chris@443 163 #endif
Chris@541 164
Chris@594 165 if (myVampPath == "") {
Chris@592 166
Chris@592 167 QString appName = QApplication::applicationName();
Chris@592 168 QString myDir = QApplication::applicationDirPath();
Chris@592 169 QString binaryName = QFileInfo(QCoreApplication::arguments().at(0))
Chris@592 170 .fileName();
Chris@541 171
Chris@541 172 #ifdef Q_OS_WIN32
Chris@541 173 QString programFiles = getEnvQStr("ProgramFiles");
Chris@541 174 if (programFiles == "") programFiles = "C:\\Program Files";
Chris@592 175 QString pfPath(programFiles + "\\" + appName);
Chris@592 176 myVampPath = myDir + sep + pfPath;
Chris@548 177 #else
cannam@567 178 #ifdef Q_OS_MAC
Chris@592 179 myVampPath = myDir + "/../Resources";
Chris@621 180 (void)sep; // unused
cannam@567 181 #else
Chris@592 182 if (binaryName != "") {
Chris@592 183 myVampPath =
Chris@592 184 myDir + "/../lib/" + binaryName + sep;
Chris@592 185 }
Chris@592 186 myVampPath = myVampPath +
Chris@592 187 myDir + "/../lib/" + appName + sep +
Chris@592 188 myDir;
Chris@541 189 #endif
Chris@548 190 #endif
Chris@443 191 }
Chris@443 192
Chris@594 193 SVCERR << "Setting VAMP_PATH to " << myVampPath
Chris@541 194 << " for Tony plugins" << endl;
Chris@458 195
Chris@594 196 QString env = "VAMP_PATH=" + myVampPath;
Chris@458 197
Chris@458 198 // Windows lacks setenv, must use putenv (different arg convention)
Chris@541 199 putEnvQStr(env);
Chris@443 200 }
Chris@0 201
Chris@0 202 int
Chris@0 203 main(int argc, char **argv)
Chris@0 204 {
Chris@587 205 if (argc == 2 && (QString(argv[1]) == "--version" ||
Chris@587 206 QString(argv[1]) == "-v")) {
Chris@587 207 cerr << TONY_VERSION << endl;
Chris@587 208 exit(0);
Chris@587 209 }
Chris@587 210
Chris@0 211 svSystemSpecificInitialisation();
Chris@98 212
Chris@541 213 TonyApplication application(argc, argv);
Chris@541 214
Chris@579 215 QApplication::setOrganizationName("sonic-visualiser");
Chris@579 216 QApplication::setOrganizationDomain("sonicvisualiser.org");
Chris@579 217 QApplication::setApplicationName("Tony");
Chris@579 218
Chris@443 219 setupTonyVampPath();
Chris@443 220
Chris@196 221 QStringList args = application.arguments();
Chris@0 222
Chris@0 223 signal(SIGINT, signalHandler);
Chris@0 224 signal(SIGTERM, signalHandler);
Chris@0 225
chris@52 226 #ifndef Q_OS_WIN32
Chris@0 227 signal(SIGHUP, signalHandler);
Chris@0 228 signal(SIGQUIT, signalHandler);
Chris@0 229 #endif
Chris@0 230
Chris@0 231 bool audioOutput = true;
matthiasm@364 232 bool sonification = true;
matthiasm@364 233 bool spectrogram = true;
Chris@0 234
Chris@0 235 if (args.contains("--help") || args.contains("-h") || args.contains("-?")) {
Chris@0 236 std::cerr << QApplication::tr(
matthiasm@366 237 "\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 238 exit(2);
Chris@0 239 }
Chris@0 240
Chris@0 241 if (args.contains("--no-audio")) audioOutput = false;
Chris@0 242
matthiasm@364 243 if (args.contains("--no-sonification")) sonification = false;
matthiasm@364 244
matthiasm@364 245 if (args.contains("--no-spectrogram")) spectrogram = false;
matthiasm@364 246
Chris@672 247 if (args.contains("--first-run")) {
Chris@672 248 QSettings settings;
Chris@672 249 settings.clear();
Chris@672 250 }
Chris@672 251
Chris@259 252 InteractiveFileFinder::getInstance()->setApplicationSessionExtension("ton");
Chris@259 253
Chris@484 254 QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
Chris@484 255
Chris@0 256 QSplashScreen *splash = 0;
Chris@0 257 // If we had a splash screen, we would show it here
Chris@0 258
Chris@0 259 QIcon icon;
Chris@0 260 int sizes[] = { 16, 22, 24, 32, 48, 64, 128 };
Chris@0 261 for (size_t i = 0; i < sizeof(sizes)/sizeof(sizes[0]); ++i) {
Chris@0 262 icon.addFile(QString(":icons/tony-%1x%2.png").arg(sizes[i]).arg(sizes[i]));
Chris@0 263 }
Chris@0 264 QApplication::setWindowIcon(icon);
Chris@0 265
Chris@0 266 QString language = QLocale::system().name();
Chris@0 267
Chris@0 268 QTranslator qtTranslator;
Chris@0 269 QString qtTrName = QString("qt_%1").arg(language);
Chris@0 270 std::cerr << "Loading " << qtTrName.toStdString() << "..." << std::endl;
Chris@0 271 bool success = false;
Chris@0 272 if (!(success = qtTranslator.load(qtTrName))) {
Chris@0 273 QString qtDir = getenv("QTDIR");
Chris@0 274 if (qtDir != "") {
Chris@0 275 success = qtTranslator.load
Chris@0 276 (qtTrName, QDir(qtDir).filePath("translations"));
Chris@0 277 }
Chris@0 278 }
Chris@0 279 if (!success) {
Chris@0 280 std::cerr << "Failed to load Qt translation for locale" << std::endl;
Chris@0 281 }
Chris@0 282 application.installTranslator(&qtTranslator);
Chris@0 283
Chris@0 284 StoreStartupLocale();
Chris@520 285
Chris@0 286 // Permit size_t and PropertyName to be used as args in queued signal calls
Chris@0 287 qRegisterMetaType<size_t>("size_t");
Chris@0 288 qRegisterMetaType<PropertyContainer::PropertyName>("PropertyContainer::PropertyName");
Chris@0 289
Chris@635 290 MainWindow::AudioMode audioMode =
Chris@635 291 MainWindow::AUDIO_PLAYBACK_NOW_RECORD_LATER;
Chris@635 292
Chris@635 293 if (!audioOutput) {
Chris@635 294 audioMode = MainWindow::AUDIO_NONE;
Chris@635 295 }
Chris@474 296
Chris@635 297 MainWindow *gui = new MainWindow(audioMode, sonification, spectrogram);
Chris@0 298 application.setMainWindow(gui);
Chris@0 299 if (splash) {
Chris@0 300 QObject::connect(gui, SIGNAL(hideSplash()), splash, SLOT(hide()));
Chris@0 301 }
Chris@0 302
Chris@572 303 QScreen *screen = QApplication::primaryScreen();
Chris@572 304 QRect available = screen->availableGeometry();
Chris@0 305
Chris@0 306 int width = (available.width() * 2) / 3;
Chris@0 307 int height = available.height() / 2;
Chris@0 308 if (height < 450) height = (available.height() * 2) / 3;
Chris@0 309 if (width > height * 2) width = height * 2;
Chris@0 310
Chris@0 311 QSettings settings;
Chris@0 312 settings.beginGroup("MainWindow");
Chris@0 313 QSize size = settings.value("size", QSize(width, height)).toSize();
Chris@0 314 gui->resizeConstrained(size);
Chris@0 315 if (settings.contains("position")) {
Chris@0 316 QRect prevrect(settings.value("position").toPoint(), size);
Chris@0 317 if (!(available & prevrect).isEmpty()) {
Chris@0 318 gui->move(prevrect.topLeft());
Chris@0 319 }
Chris@0 320 }
Chris@0 321 settings.endGroup();
Chris@0 322
Chris@0 323 gui->show();
Chris@0 324
Chris@0 325 application.readyForFiles();
Chris@0 326
Chris@0 327 for (QStringList::iterator i = args.begin(); i != args.end(); ++i) {
Chris@0 328
Chris@0 329 if (i == args.begin()) continue;
Chris@0 330 if (i->startsWith('-')) continue;
Chris@0 331
Chris@0 332 QString path = *i;
Chris@0 333
Chris@0 334 application.handleFilepathArgument(path, splash);
Chris@0 335 }
Chris@0 336
Chris@0 337 application.handleQueuedPaths(splash);
Chris@0 338
Chris@0 339 if (splash) splash->finish(gui);
Chris@0 340 delete splash;
Chris@0 341
Chris@0 342 int rv = application.exec();
Chris@0 343
Chris@0 344 gui->hide();
Chris@0 345
Chris@0 346 cleanupMutex.lock();
Chris@70 347
Chris@70 348 if (!cleanedUp) {
Chris@70 349 TransformFactory::deleteInstance();
Chris@70 350 TempDirectory::getInstance()->cleanup();
Chris@70 351 cleanedUp = true;
Chris@70 352 }
Chris@70 353
Chris@0 354 application.releaseMainWindow();
Chris@0 355
Chris@0 356 delete gui;
Chris@0 357
Chris@70 358 cleanupMutex.unlock();
Chris@70 359
Chris@0 360 return rv;
Chris@0 361 }
Chris@0 362
Chris@0 363 /** Application-global handler for filepaths passed in, e.g. as
Chris@0 364 * command-line arguments or apple events */
Chris@0 365
Chris@0 366 void TonyApplication::handleFilepathArgument(QString path,
Chris@0 367 QSplashScreen *splash)
Chris@0 368 {
Chris@0 369 static bool haveSession = false;
Chris@0 370 static bool haveMainModel = false;
Chris@0 371 static bool havePriorCommandLineModel = false;
Chris@0 372
Chris@103 373 if (!m_mainWindow) return;
Chris@103 374
Chris@0 375 MainWindow::FileOpenStatus status = MainWindow::FileOpenFailed;
Chris@0 376
Chris@196 377 #ifdef Q_OS_WIN32
Chris@196 378 path.replace("\\", "/");
Chris@196 379 #endif
Chris@196 380
Chris@0 381 if (path.endsWith("ton")) {
Chris@0 382 if (!haveSession) {
Chris@362 383 status = m_mainWindow->openSessionPath(path);
Chris@0 384 if (status == MainWindow::FileOpenSucceeded) {
Chris@0 385 haveSession = true;
Chris@0 386 haveMainModel = true;
Chris@0 387 }
Chris@0 388 } else {
Chris@0 389 std::cerr << "WARNING: Ignoring additional session file argument \"" << path << "\"" << std::endl;
Chris@0 390 status = MainWindow::FileOpenSucceeded;
Chris@0 391 }
Chris@0 392 }
Chris@0 393 if (status != MainWindow::FileOpenSucceeded) {
Chris@0 394 if (!haveMainModel) {
Chris@387 395 status = m_mainWindow->openPath(path, MainWindow::ReplaceSession);
Chris@0 396 if (status == MainWindow::FileOpenSucceeded) {
Chris@0 397 haveMainModel = true;
Chris@0 398 }
Chris@0 399 } else {
Chris@0 400 if (haveSession && !havePriorCommandLineModel) {
Chris@387 401 status = m_mainWindow->openPath(path, MainWindow::AskUser);
Chris@0 402 if (status == MainWindow::FileOpenSucceeded) {
Chris@0 403 havePriorCommandLineModel = true;
Chris@0 404 }
Chris@0 405 } else {
Chris@387 406 status = m_mainWindow->openPath(path, MainWindow::CreateAdditionalModel);
Chris@0 407 }
Chris@0 408 }
Chris@0 409 }
Chris@0 410 if (status == MainWindow::FileOpenFailed) {
Chris@0 411 if (splash) splash->hide();
Chris@0 412 QMessageBox::critical
Chris@0 413 (m_mainWindow, QMessageBox::tr("Failed to open file"),
Chris@0 414 QMessageBox::tr("File or URL \"%1\" could not be opened").arg(path));
Chris@0 415 } else if (status == MainWindow::FileOpenWrongMode) {
Chris@0 416 if (splash) splash->hide();
Chris@0 417 QMessageBox::critical
Chris@0 418 (m_mainWindow, QMessageBox::tr("Failed to open file"),
Chris@0 419 QMessageBox::tr("<b>Audio required</b><p>Please load at least one audio file before importing annotation data"));
Chris@0 420 }
Chris@0 421 }
Chris@0 422