annotate main/main.cpp @ 635:41e221cd740e

Update subrepos and adapt to new MainWindowBase API, ensuring that we don't get the recording permission dialog until we actually record
author Chris Cannam
date Wed, 16 Oct 2019 16:03:47 +0100
parents e06cbdc7301c
children de3d628e048e
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@259 247 InteractiveFileFinder::getInstance()->setApplicationSessionExtension("ton");
Chris@259 248
Chris@484 249 QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
Chris@484 250
Chris@0 251 QSplashScreen *splash = 0;
Chris@0 252 // If we had a splash screen, we would show it here
Chris@0 253
Chris@0 254 QIcon icon;
Chris@0 255 int sizes[] = { 16, 22, 24, 32, 48, 64, 128 };
Chris@0 256 for (size_t i = 0; i < sizeof(sizes)/sizeof(sizes[0]); ++i) {
Chris@0 257 icon.addFile(QString(":icons/tony-%1x%2.png").arg(sizes[i]).arg(sizes[i]));
Chris@0 258 }
Chris@0 259 QApplication::setWindowIcon(icon);
Chris@0 260
Chris@0 261 QString language = QLocale::system().name();
Chris@0 262
Chris@0 263 QTranslator qtTranslator;
Chris@0 264 QString qtTrName = QString("qt_%1").arg(language);
Chris@0 265 std::cerr << "Loading " << qtTrName.toStdString() << "..." << std::endl;
Chris@0 266 bool success = false;
Chris@0 267 if (!(success = qtTranslator.load(qtTrName))) {
Chris@0 268 QString qtDir = getenv("QTDIR");
Chris@0 269 if (qtDir != "") {
Chris@0 270 success = qtTranslator.load
Chris@0 271 (qtTrName, QDir(qtDir).filePath("translations"));
Chris@0 272 }
Chris@0 273 }
Chris@0 274 if (!success) {
Chris@0 275 std::cerr << "Failed to load Qt translation for locale" << std::endl;
Chris@0 276 }
Chris@0 277 application.installTranslator(&qtTranslator);
Chris@0 278
Chris@0 279 StoreStartupLocale();
Chris@520 280
Chris@0 281 // Permit size_t and PropertyName to be used as args in queued signal calls
Chris@0 282 qRegisterMetaType<size_t>("size_t");
Chris@0 283 qRegisterMetaType<PropertyContainer::PropertyName>("PropertyContainer::PropertyName");
Chris@0 284
Chris@635 285 MainWindow::AudioMode audioMode =
Chris@635 286 MainWindow::AUDIO_PLAYBACK_NOW_RECORD_LATER;
Chris@635 287
Chris@635 288 if (!audioOutput) {
Chris@635 289 audioMode = MainWindow::AUDIO_NONE;
Chris@635 290 }
Chris@474 291
Chris@635 292 MainWindow *gui = new MainWindow(audioMode, sonification, spectrogram);
Chris@0 293 application.setMainWindow(gui);
Chris@0 294 if (splash) {
Chris@0 295 QObject::connect(gui, SIGNAL(hideSplash()), splash, SLOT(hide()));
Chris@0 296 }
Chris@0 297
Chris@572 298 QScreen *screen = QApplication::primaryScreen();
Chris@572 299 QRect available = screen->availableGeometry();
Chris@0 300
Chris@0 301 int width = (available.width() * 2) / 3;
Chris@0 302 int height = available.height() / 2;
Chris@0 303 if (height < 450) height = (available.height() * 2) / 3;
Chris@0 304 if (width > height * 2) width = height * 2;
Chris@0 305
Chris@0 306 QSettings settings;
Chris@0 307 settings.beginGroup("MainWindow");
Chris@0 308 QSize size = settings.value("size", QSize(width, height)).toSize();
Chris@0 309 gui->resizeConstrained(size);
Chris@0 310 if (settings.contains("position")) {
Chris@0 311 QRect prevrect(settings.value("position").toPoint(), size);
Chris@0 312 if (!(available & prevrect).isEmpty()) {
Chris@0 313 gui->move(prevrect.topLeft());
Chris@0 314 }
Chris@0 315 }
Chris@0 316 settings.endGroup();
Chris@0 317
Chris@0 318 gui->show();
Chris@0 319
Chris@0 320 application.readyForFiles();
Chris@0 321
Chris@0 322 for (QStringList::iterator i = args.begin(); i != args.end(); ++i) {
Chris@0 323
Chris@0 324 if (i == args.begin()) continue;
Chris@0 325 if (i->startsWith('-')) continue;
Chris@0 326
Chris@0 327 QString path = *i;
Chris@0 328
Chris@0 329 application.handleFilepathArgument(path, splash);
Chris@0 330 }
Chris@0 331
Chris@0 332 application.handleQueuedPaths(splash);
Chris@0 333
Chris@0 334 if (splash) splash->finish(gui);
Chris@0 335 delete splash;
Chris@0 336
Chris@0 337 int rv = application.exec();
Chris@0 338
Chris@0 339 gui->hide();
Chris@0 340
Chris@0 341 cleanupMutex.lock();
Chris@70 342
Chris@70 343 if (!cleanedUp) {
Chris@70 344 TransformFactory::deleteInstance();
Chris@70 345 TempDirectory::getInstance()->cleanup();
Chris@70 346 cleanedUp = true;
Chris@70 347 }
Chris@70 348
Chris@0 349 application.releaseMainWindow();
Chris@0 350
Chris@0 351 delete gui;
Chris@0 352
Chris@70 353 cleanupMutex.unlock();
Chris@70 354
Chris@0 355 return rv;
Chris@0 356 }
Chris@0 357
Chris@0 358 /** Application-global handler for filepaths passed in, e.g. as
Chris@0 359 * command-line arguments or apple events */
Chris@0 360
Chris@0 361 void TonyApplication::handleFilepathArgument(QString path,
Chris@0 362 QSplashScreen *splash)
Chris@0 363 {
Chris@0 364 static bool haveSession = false;
Chris@0 365 static bool haveMainModel = false;
Chris@0 366 static bool havePriorCommandLineModel = false;
Chris@0 367
Chris@103 368 if (!m_mainWindow) return;
Chris@103 369
Chris@0 370 MainWindow::FileOpenStatus status = MainWindow::FileOpenFailed;
Chris@0 371
Chris@196 372 #ifdef Q_OS_WIN32
Chris@196 373 path.replace("\\", "/");
Chris@196 374 #endif
Chris@196 375
Chris@0 376 if (path.endsWith("ton")) {
Chris@0 377 if (!haveSession) {
Chris@362 378 status = m_mainWindow->openSessionPath(path);
Chris@0 379 if (status == MainWindow::FileOpenSucceeded) {
Chris@0 380 haveSession = true;
Chris@0 381 haveMainModel = true;
Chris@0 382 }
Chris@0 383 } else {
Chris@0 384 std::cerr << "WARNING: Ignoring additional session file argument \"" << path << "\"" << std::endl;
Chris@0 385 status = MainWindow::FileOpenSucceeded;
Chris@0 386 }
Chris@0 387 }
Chris@0 388 if (status != MainWindow::FileOpenSucceeded) {
Chris@0 389 if (!haveMainModel) {
Chris@387 390 status = m_mainWindow->openPath(path, MainWindow::ReplaceSession);
Chris@0 391 if (status == MainWindow::FileOpenSucceeded) {
Chris@0 392 haveMainModel = true;
Chris@0 393 }
Chris@0 394 } else {
Chris@0 395 if (haveSession && !havePriorCommandLineModel) {
Chris@387 396 status = m_mainWindow->openPath(path, MainWindow::AskUser);
Chris@0 397 if (status == MainWindow::FileOpenSucceeded) {
Chris@0 398 havePriorCommandLineModel = true;
Chris@0 399 }
Chris@0 400 } else {
Chris@387 401 status = m_mainWindow->openPath(path, MainWindow::CreateAdditionalModel);
Chris@0 402 }
Chris@0 403 }
Chris@0 404 }
Chris@0 405 if (status == MainWindow::FileOpenFailed) {
Chris@0 406 if (splash) splash->hide();
Chris@0 407 QMessageBox::critical
Chris@0 408 (m_mainWindow, QMessageBox::tr("Failed to open file"),
Chris@0 409 QMessageBox::tr("File or URL \"%1\" could not be opened").arg(path));
Chris@0 410 } else if (status == MainWindow::FileOpenWrongMode) {
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("<b>Audio required</b><p>Please load at least one audio file before importing annotation data"));
Chris@0 415 }
Chris@0 416 }
Chris@0 417