annotate src/main.cpp @ 70:5310637392f6

Revert to using three separate tool modes (more like SV). This is less like the proposed design for Tony but better than the previous situation with two incomprehensible toggle buttons.
author Chris Cannam
date Tue, 26 Nov 2013 16:59:31 +0000
parents ab10f175b4cb
children b405349d323f
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@70 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@0 32 #include <QSettings>
Chris@0 33 #include <QIcon>
Chris@0 34 #include <QSessionManager>
Chris@0 35 #include <QSplashScreen>
Chris@0 36 #include <QFileOpenEvent>
Chris@0 37 #include <QDir>
Chris@0 38
Chris@0 39 #include <iostream>
Chris@0 40 #include <signal.h>
Chris@0 41
Chris@0 42 static QMutex cleanupMutex;
Chris@70 43 static bool cleanedUp = false;
Chris@0 44
Chris@0 45 static void
Chris@0 46 signalHandler(int /* signal */)
Chris@0 47 {
Chris@0 48 // Avoid this happening more than once across threads
Chris@0 49
Chris@70 50 cerr << "signalHandler: cleaning up and exiting" << endl;
Chris@0 51 cleanupMutex.lock();
Chris@70 52 if (!cleanedUp) {
Chris@70 53 TempDirectory::getInstance()->cleanup();
Chris@70 54 cleanedUp = true;
Chris@70 55 }
Chris@70 56 cleanupMutex.unlock();
Chris@70 57 exit(0);
Chris@0 58 }
Chris@0 59
Chris@0 60 class TonyApplication : public QApplication
Chris@0 61 {
Chris@0 62 public:
Chris@0 63 TonyApplication(int argc, char **argv) :
Chris@0 64 QApplication(argc, argv),
Chris@0 65 m_mainWindow(0),
Chris@0 66 m_readyForFiles(false)
Chris@0 67 {
Chris@0 68 #ifdef Q_OS_MAC
Chris@0 69 // Override the Qt plugin load path. The default contains the
Chris@0 70 // Qt installation location as well as the application
Chris@0 71 // directory, but we don't ever want to load plugins from
Chris@0 72 // outside the app bundle because we don't know for sure what
Chris@0 73 // (potentially different) versions of the Qt framework
Chris@0 74 // libraries they may have dyld dependencies on.
Chris@0 75 QString apploc(applicationFilePath());
Chris@0 76 apploc.truncate(apploc.lastIndexOf(QLatin1Char('/')));
Chris@0 77 apploc = QDir(apploc).canonicalPath();
Chris@0 78 if (QFile::exists(apploc)) {
Chris@0 79 setLibraryPaths(QStringList() << apploc);
Chris@0 80 } else {
Chris@0 81 setLibraryPaths(QStringList());
Chris@0 82 }
Chris@0 83 #endif
Chris@0 84 }
Chris@0 85 virtual ~TonyApplication() {
Chris@0 86 }
Chris@0 87
Chris@0 88 void setMainWindow(MainWindow *mw) { m_mainWindow = mw; }
Chris@0 89 void releaseMainWindow() { m_mainWindow = 0; }
Chris@0 90
Chris@0 91 virtual void commitData(QSessionManager &manager) {
Chris@0 92 if (!m_mainWindow) return;
Chris@0 93 bool mayAskUser = manager.allowsInteraction();
Chris@0 94 bool success = m_mainWindow->commitData(mayAskUser);
Chris@0 95 manager.release();
Chris@0 96 if (!success) manager.cancel();
Chris@0 97 }
Chris@0 98
Chris@0 99 void readyForFiles() {
Chris@0 100 m_readyForFiles = true;
Chris@0 101 }
Chris@0 102
Chris@0 103 void handleFilepathArgument(QString path, QSplashScreen *splash);
Chris@0 104
Chris@0 105 void handleQueuedPaths(QSplashScreen *splash) {
Chris@0 106 foreach (QString f, m_filepathQueue) {
Chris@0 107 handleFilepathArgument(f, splash);
Chris@0 108 }
Chris@0 109 }
Chris@0 110
Chris@0 111 protected:
Chris@0 112 MainWindow *m_mainWindow;
Chris@0 113
Chris@0 114 bool m_readyForFiles;
Chris@0 115 QStringList m_filepathQueue;
Chris@0 116
Chris@0 117 virtual bool event(QEvent *event) {
Chris@0 118 switch (event->type()) {
Chris@0 119 case QEvent::FileOpen:
Chris@0 120 {
Chris@0 121 QString path = static_cast<QFileOpenEvent *>(event)->file();
Chris@0 122 if (m_readyForFiles) {
Chris@0 123 handleFilepathArgument(path, NULL);
Chris@0 124 } else {
Chris@0 125 m_filepathQueue.append(path);
Chris@0 126 }
Chris@0 127 return true;
Chris@0 128 }
Chris@0 129 default:
Chris@0 130 return QApplication::event(event);
Chris@0 131 }
Chris@0 132 }
Chris@0 133 };
Chris@0 134
Chris@0 135 int
Chris@0 136 main(int argc, char **argv)
Chris@0 137 {
Chris@0 138 svSystemSpecificInitialisation();
Chris@0 139
Chris@0 140 TonyApplication application(argc, argv);
Chris@0 141
Chris@0 142 QStringList args = application.arguments();
Chris@0 143
Chris@0 144 signal(SIGINT, signalHandler);
Chris@0 145 signal(SIGTERM, signalHandler);
Chris@0 146
chris@52 147 #ifndef Q_OS_WIN32
Chris@0 148 signal(SIGHUP, signalHandler);
Chris@0 149 signal(SIGQUIT, signalHandler);
Chris@0 150 #endif
Chris@0 151
Chris@0 152 bool audioOutput = true;
Chris@0 153
Chris@0 154 if (args.contains("--help") || args.contains("-h") || args.contains("-?")) {
Chris@0 155 std::cerr << QApplication::tr(
Chris@0 156 "\nTony is a program for interactive note and pitch 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 <file>: One or more Tony (.ton) and audio files may be provided.\n").arg(argv[0]).toStdString() << std::endl;
Chris@0 157 exit(2);
Chris@0 158 }
Chris@0 159
Chris@0 160 if (args.contains("--no-audio")) audioOutput = false;
Chris@0 161
Chris@0 162 QApplication::setOrganizationName("QMUL");
Chris@0 163 QApplication::setOrganizationDomain("qmul.ac.uk");
Chris@0 164 QApplication::setApplicationName("Tony");
Chris@0 165
Chris@0 166 QSplashScreen *splash = 0;
Chris@0 167 // If we had a splash screen, we would show it here
Chris@0 168
Chris@0 169 QIcon icon;
Chris@0 170 int sizes[] = { 16, 22, 24, 32, 48, 64, 128 };
Chris@0 171 for (size_t i = 0; i < sizeof(sizes)/sizeof(sizes[0]); ++i) {
Chris@0 172 icon.addFile(QString(":icons/tony-%1x%2.png").arg(sizes[i]).arg(sizes[i]));
Chris@0 173 }
Chris@0 174 QApplication::setWindowIcon(icon);
Chris@0 175
Chris@0 176 QString language = QLocale::system().name();
Chris@0 177
Chris@0 178 QTranslator qtTranslator;
Chris@0 179 QString qtTrName = QString("qt_%1").arg(language);
Chris@0 180 std::cerr << "Loading " << qtTrName.toStdString() << "..." << std::endl;
Chris@0 181 bool success = false;
Chris@0 182 if (!(success = qtTranslator.load(qtTrName))) {
Chris@0 183 QString qtDir = getenv("QTDIR");
Chris@0 184 if (qtDir != "") {
Chris@0 185 success = qtTranslator.load
Chris@0 186 (qtTrName, QDir(qtDir).filePath("translations"));
Chris@0 187 }
Chris@0 188 }
Chris@0 189 if (!success) {
Chris@0 190 std::cerr << "Failed to load Qt translation for locale" << std::endl;
Chris@0 191 }
Chris@0 192 application.installTranslator(&qtTranslator);
Chris@0 193
Chris@0 194 StoreStartupLocale();
Chris@0 195
Chris@0 196 // Permit size_t and PropertyName to be used as args in queued signal calls
Chris@0 197 qRegisterMetaType<size_t>("size_t");
Chris@0 198 qRegisterMetaType<PropertyContainer::PropertyName>("PropertyContainer::PropertyName");
Chris@0 199
Chris@0 200 MainWindow *gui = new MainWindow(audioOutput, false); // no osc support
Chris@0 201 application.setMainWindow(gui);
Chris@0 202 if (splash) {
Chris@0 203 QObject::connect(gui, SIGNAL(hideSplash()), splash, SLOT(hide()));
Chris@0 204 }
Chris@0 205
Chris@0 206 QDesktopWidget *desktop = QApplication::desktop();
Chris@0 207 QRect available = desktop->availableGeometry();
Chris@0 208
Chris@0 209 int width = (available.width() * 2) / 3;
Chris@0 210 int height = available.height() / 2;
Chris@0 211 if (height < 450) height = (available.height() * 2) / 3;
Chris@0 212 if (width > height * 2) width = height * 2;
Chris@0 213
Chris@0 214 QSettings settings;
Chris@0 215 settings.beginGroup("MainWindow");
Chris@0 216 QSize size = settings.value("size", QSize(width, height)).toSize();
Chris@0 217 gui->resizeConstrained(size);
Chris@0 218 if (settings.contains("position")) {
Chris@0 219 QRect prevrect(settings.value("position").toPoint(), size);
Chris@0 220 if (!(available & prevrect).isEmpty()) {
Chris@0 221 gui->move(prevrect.topLeft());
Chris@0 222 }
Chris@0 223 }
Chris@0 224 settings.endGroup();
Chris@0 225
Chris@0 226 gui->show();
Chris@0 227
Chris@0 228 application.readyForFiles();
Chris@0 229
Chris@0 230 for (QStringList::iterator i = args.begin(); i != args.end(); ++i) {
Chris@0 231
Chris@0 232 if (i == args.begin()) continue;
Chris@0 233 if (i->startsWith('-')) continue;
Chris@0 234
Chris@0 235 QString path = *i;
Chris@0 236
Chris@0 237 application.handleFilepathArgument(path, splash);
Chris@0 238 }
Chris@0 239
Chris@0 240 application.handleQueuedPaths(splash);
Chris@0 241
Chris@0 242 if (splash) splash->finish(gui);
Chris@0 243 delete splash;
Chris@0 244
Chris@0 245 int rv = application.exec();
Chris@0 246
Chris@0 247 gui->hide();
Chris@0 248
Chris@0 249 cleanupMutex.lock();
Chris@70 250
Chris@70 251 if (!cleanedUp) {
Chris@70 252 TransformFactory::deleteInstance();
Chris@70 253 TempDirectory::getInstance()->cleanup();
Chris@70 254 cleanedUp = true;
Chris@70 255 }
Chris@70 256
Chris@0 257 application.releaseMainWindow();
Chris@0 258
Chris@0 259 delete gui;
Chris@0 260
Chris@70 261 cleanupMutex.unlock();
Chris@70 262
Chris@0 263 return rv;
Chris@0 264 }
Chris@0 265
Chris@0 266 /** Application-global handler for filepaths passed in, e.g. as
Chris@0 267 * command-line arguments or apple events */
Chris@0 268
Chris@0 269 void TonyApplication::handleFilepathArgument(QString path,
Chris@0 270 QSplashScreen *splash)
Chris@0 271 {
Chris@0 272 static bool haveSession = false;
Chris@0 273 static bool haveMainModel = false;
Chris@0 274 static bool havePriorCommandLineModel = false;
Chris@0 275
Chris@0 276 MainWindow::FileOpenStatus status = MainWindow::FileOpenFailed;
Chris@0 277
Chris@0 278 if (path.endsWith("ton")) {
Chris@0 279 if (!haveSession) {
Chris@0 280 status = m_mainWindow->openSessionFile(path);
Chris@0 281 if (status == MainWindow::FileOpenSucceeded) {
Chris@0 282 haveSession = true;
Chris@0 283 haveMainModel = true;
Chris@0 284 }
Chris@0 285 } else {
Chris@0 286 std::cerr << "WARNING: Ignoring additional session file argument \"" << path << "\"" << std::endl;
Chris@0 287 status = MainWindow::FileOpenSucceeded;
Chris@0 288 }
Chris@0 289 }
Chris@0 290 if (status != MainWindow::FileOpenSucceeded) {
Chris@0 291 if (!haveMainModel) {
Chris@0 292 status = m_mainWindow->open(path, MainWindow::ReplaceSession);
Chris@0 293 if (status == MainWindow::FileOpenSucceeded) {
Chris@0 294 haveMainModel = true;
Chris@0 295 }
Chris@0 296 } else {
Chris@0 297 if (haveSession && !havePriorCommandLineModel) {
Chris@0 298 status = m_mainWindow->open(path, MainWindow::AskUser);
Chris@0 299 if (status == MainWindow::FileOpenSucceeded) {
Chris@0 300 havePriorCommandLineModel = true;
Chris@0 301 }
Chris@0 302 } else {
Chris@0 303 status = m_mainWindow->open(path, MainWindow::CreateAdditionalModel);
Chris@0 304 }
Chris@0 305 }
Chris@0 306 }
Chris@0 307 if (status == MainWindow::FileOpenFailed) {
Chris@0 308 if (splash) splash->hide();
Chris@0 309 QMessageBox::critical
Chris@0 310 (m_mainWindow, QMessageBox::tr("Failed to open file"),
Chris@0 311 QMessageBox::tr("File or URL \"%1\" could not be opened").arg(path));
Chris@0 312 } else if (status == MainWindow::FileOpenWrongMode) {
Chris@0 313 if (splash) splash->hide();
Chris@0 314 QMessageBox::critical
Chris@0 315 (m_mainWindow, QMessageBox::tr("Failed to open file"),
Chris@0 316 QMessageBox::tr("<b>Audio required</b><p>Please load at least one audio file before importing annotation data"));
Chris@0 317 }
Chris@0 318 }
Chris@0 319