comparison main/main.cpp @ 580:f52766aa747b

Rename src -> main for consistency with SV/Sonic Lineup
author Chris Cannam
date Wed, 14 Aug 2019 11:57:06 +0100
parents src/main.cpp@47f96711069f
children 2090dd9bfbc1
comparison
equal deleted inserted replaced
579:47f96711069f 580:f52766aa747b
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
2
3 /*
4 Tony
5 An intonation analysis and annotation tool
6 Centre for Digital Music, Queen Mary, University of London.
7 This file copyright 2006-2012 Chris Cannam and QMUL.
8
9 This program is free software; you can redistribute it and/or
10 modify it under the terms of the GNU General Public License as
11 published by the Free Software Foundation; either version 2 of the
12 License, or (at your option) any later version. See the file
13 COPYING included with this distribution for more information.
14 */
15
16 #include "MainWindow.h"
17
18 #include "system/System.h"
19 #include "system/Init.h"
20 #include "base/TempDirectory.h"
21 #include "base/PropertyContainer.h"
22 #include "base/Preferences.h"
23 #include "widgets/TipDialog.h"
24 #include "widgets/InteractiveFileFinder.h"
25 #include "transform/TransformFactory.h"
26 #include "svcore/plugin/PluginScan.h"
27
28 #include <QMetaType>
29 #include <QApplication>
30 #include <QScreen>
31 #include <QMessageBox>
32 #include <QTranslator>
33 #include <QLocale>
34 #include <QSettings>
35 #include <QIcon>
36 #include <QSessionManager>
37 #include <QSplashScreen>
38 #include <QFileOpenEvent>
39 #include <QDir>
40
41 #include <iostream>
42 #include <signal.h>
43 #include <cstdlib>
44
45 #include <vamp-hostsdk/PluginHostAdapter.h>
46
47 static QMutex cleanupMutex;
48 static bool cleanedUp = false;
49
50 static void
51 signalHandler(int /* signal */)
52 {
53 // Avoid this happening more than once across threads
54
55 cerr << "signalHandler: cleaning up and exiting" << endl;
56 cleanupMutex.lock();
57 if (!cleanedUp) {
58 TempDirectory::getInstance()->cleanup();
59 cleanedUp = true;
60 }
61 cleanupMutex.unlock();
62 exit(0);
63 }
64
65 class TonyApplication : public QApplication
66 {
67 public:
68 TonyApplication(int &argc, char **argv) :
69 QApplication(argc, argv),
70 m_mainWindow(0),
71 m_readyForFiles(false)
72 {
73 // tidier without, I reckon
74 setAttribute(Qt::AA_DontShowIconsInMenus);
75 }
76 virtual ~TonyApplication() {
77 }
78
79 void setMainWindow(MainWindow *mw) { m_mainWindow = mw; }
80 void releaseMainWindow() { m_mainWindow = 0; }
81
82 virtual void commitData(QSessionManager &manager) {
83 if (!m_mainWindow) return;
84 bool mayAskUser = manager.allowsInteraction();
85 bool success = m_mainWindow->commitData(mayAskUser);
86 manager.release();
87 if (!success) manager.cancel();
88 }
89
90 void readyForFiles() {
91 m_readyForFiles = true;
92 }
93
94 void handleFilepathArgument(QString path, QSplashScreen *splash);
95
96 void handleQueuedPaths(QSplashScreen *splash) {
97 foreach (QString f, m_filepathQueue) {
98 handleFilepathArgument(f, splash);
99 }
100 }
101
102 protected:
103 MainWindow *m_mainWindow;
104
105 bool m_readyForFiles;
106 QStringList m_filepathQueue;
107
108 virtual bool event(QEvent *event) {
109
110 if (event->type() == QEvent::FileOpen) {
111 QString path = static_cast<QFileOpenEvent *>(event)->file();
112 if (m_readyForFiles) {
113 handleFilepathArgument(path, NULL);
114 } else {
115 m_filepathQueue.append(path);
116 }
117 return true;
118 } else {
119 return QApplication::event(event);
120 }
121 }
122 };
123
124 static QString
125 getEnvQStr(QString variable)
126 {
127 #ifdef Q_OS_WIN32
128 std::wstring wvar = variable.toStdWString();
129 wchar_t *value = _wgetenv(wvar.c_str());
130 if (!value) return QString();
131 else return QString::fromStdWString(std::wstring(value));
132 #else
133 std::string var = variable.toStdString();
134 return QString::fromUtf8(qgetenv(var.c_str()));
135 #endif
136 }
137
138 static void
139 putEnvQStr(QString assignment)
140 {
141 #ifdef Q_OS_WIN32
142 std::wstring wassignment = assignment.toStdWString();
143 _wputenv(_wcsdup(wassignment.c_str()));
144 #else
145 putenv(strdup(assignment.toUtf8().data()));
146 #endif
147 }
148
149 static void
150 setupTonyVampPath()
151 {
152 QString tonyVampPath = getEnvQStr("TONY_VAMP_PATH");
153
154 #ifdef Q_OS_WIN32
155 QChar sep(';');
156 #else
157 QChar sep(':');
158 #endif
159
160 if (tonyVampPath == "") {
161 tonyVampPath = QApplication::applicationDirPath();
162
163 #ifdef Q_OS_WIN32
164 QString programFiles = getEnvQStr("ProgramFiles");
165 if (programFiles == "") programFiles = "C:\\Program Files";
166 QString defaultTonyPath(programFiles + "\\Tony");
167 tonyVampPath = tonyVampPath + sep + defaultTonyPath;
168 #else
169 #ifdef Q_OS_MAC
170 tonyVampPath = tonyVampPath + "/../Resources:" + tonyVampPath;
171 #else
172 QString defaultTonyPath("/usr/local/lib/tony:/usr/lib/tony");
173 tonyVampPath = tonyVampPath + sep + defaultTonyPath;
174 #endif
175 #endif
176 }
177
178 std::vector<std::string> vampPathList =
179 Vamp::PluginHostAdapter::getPluginPath();
180
181 for (auto p: vampPathList) {
182 tonyVampPath = tonyVampPath + sep + QString::fromUtf8(p.c_str());
183 }
184
185 SVCERR << "Setting VAMP_PATH to " << tonyVampPath
186 << " for Tony plugins" << endl;
187
188 QString env = "VAMP_PATH=" + tonyVampPath;
189
190 // Windows lacks setenv, must use putenv (different arg convention)
191 putEnvQStr(env);
192 }
193
194 int
195 main(int argc, char **argv)
196 {
197 svSystemSpecificInitialisation();
198
199 #ifdef Q_OS_MAC
200 if (QSysInfo::MacintoshVersion > QSysInfo::MV_10_8) {
201 // Fix for OS/X 10.9 font problem
202 QFont::insertSubstitution(".Lucida Grande UI", "Lucida Grande");
203 }
204 #endif
205
206 TonyApplication application(argc, argv);
207
208 QApplication::setOrganizationName("sonic-visualiser");
209 QApplication::setOrganizationDomain("sonicvisualiser.org");
210 QApplication::setApplicationName("Tony");
211
212 setupTonyVampPath();
213
214 QStringList args = application.arguments();
215
216 signal(SIGINT, signalHandler);
217 signal(SIGTERM, signalHandler);
218
219 #ifndef Q_OS_WIN32
220 signal(SIGHUP, signalHandler);
221 signal(SIGQUIT, signalHandler);
222 #endif
223
224 bool audioOutput = true;
225 bool sonification = true;
226 bool spectrogram = true;
227
228 if (args.contains("--help") || args.contains("-h") || args.contains("-?")) {
229 std::cerr << QApplication::tr(
230 "\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;
231 exit(2);
232 }
233
234 if (args.contains("--no-audio")) audioOutput = false;
235
236 if (args.contains("--no-sonification")) sonification = false;
237
238 if (args.contains("--no-spectrogram")) spectrogram = false;
239
240 InteractiveFileFinder::getInstance()->setApplicationSessionExtension("ton");
241
242 QApplication::setAttribute(Qt::AA_UseHighDpiPixmaps);
243
244 QSplashScreen *splash = 0;
245 // If we had a splash screen, we would show it here
246
247 QIcon icon;
248 int sizes[] = { 16, 22, 24, 32, 48, 64, 128 };
249 for (size_t i = 0; i < sizeof(sizes)/sizeof(sizes[0]); ++i) {
250 icon.addFile(QString(":icons/tony-%1x%2.png").arg(sizes[i]).arg(sizes[i]));
251 }
252 QApplication::setWindowIcon(icon);
253
254 QString language = QLocale::system().name();
255
256 QTranslator qtTranslator;
257 QString qtTrName = QString("qt_%1").arg(language);
258 std::cerr << "Loading " << qtTrName.toStdString() << "..." << std::endl;
259 bool success = false;
260 if (!(success = qtTranslator.load(qtTrName))) {
261 QString qtDir = getenv("QTDIR");
262 if (qtDir != "") {
263 success = qtTranslator.load
264 (qtTrName, QDir(qtDir).filePath("translations"));
265 }
266 }
267 if (!success) {
268 std::cerr << "Failed to load Qt translation for locale" << std::endl;
269 }
270 application.installTranslator(&qtTranslator);
271
272 StoreStartupLocale();
273
274 // Permit size_t and PropertyName to be used as args in queued signal calls
275 qRegisterMetaType<size_t>("size_t");
276 qRegisterMetaType<PropertyContainer::PropertyName>("PropertyContainer::PropertyName");
277
278 MainWindow::SoundOptions options = MainWindow::WithEverything;
279 if (!audioOutput) options = 0;
280
281 MainWindow *gui = new MainWindow(options, sonification, spectrogram);
282 application.setMainWindow(gui);
283 if (splash) {
284 QObject::connect(gui, SIGNAL(hideSplash()), splash, SLOT(hide()));
285 }
286
287 QScreen *screen = QApplication::primaryScreen();
288 QRect available = screen->availableGeometry();
289
290 int width = (available.width() * 2) / 3;
291 int height = available.height() / 2;
292 if (height < 450) height = (available.height() * 2) / 3;
293 if (width > height * 2) width = height * 2;
294
295 QSettings settings;
296 settings.beginGroup("MainWindow");
297 QSize size = settings.value("size", QSize(width, height)).toSize();
298 gui->resizeConstrained(size);
299 if (settings.contains("position")) {
300 QRect prevrect(settings.value("position").toPoint(), size);
301 if (!(available & prevrect).isEmpty()) {
302 gui->move(prevrect.topLeft());
303 }
304 }
305 settings.endGroup();
306
307 gui->show();
308
309 application.readyForFiles();
310
311 for (QStringList::iterator i = args.begin(); i != args.end(); ++i) {
312
313 if (i == args.begin()) continue;
314 if (i->startsWith('-')) continue;
315
316 QString path = *i;
317
318 application.handleFilepathArgument(path, splash);
319 }
320
321 application.handleQueuedPaths(splash);
322
323 if (splash) splash->finish(gui);
324 delete splash;
325
326 int rv = application.exec();
327
328 gui->hide();
329
330 cleanupMutex.lock();
331
332 if (!cleanedUp) {
333 TransformFactory::deleteInstance();
334 TempDirectory::getInstance()->cleanup();
335 cleanedUp = true;
336 }
337
338 application.releaseMainWindow();
339
340 delete gui;
341
342 cleanupMutex.unlock();
343
344 return rv;
345 }
346
347 /** Application-global handler for filepaths passed in, e.g. as
348 * command-line arguments or apple events */
349
350 void TonyApplication::handleFilepathArgument(QString path,
351 QSplashScreen *splash)
352 {
353 static bool haveSession = false;
354 static bool haveMainModel = false;
355 static bool havePriorCommandLineModel = false;
356
357 if (!m_mainWindow) return;
358
359 MainWindow::FileOpenStatus status = MainWindow::FileOpenFailed;
360
361 #ifdef Q_OS_WIN32
362 path.replace("\\", "/");
363 #endif
364
365 if (path.endsWith("ton")) {
366 if (!haveSession) {
367 status = m_mainWindow->openSessionPath(path);
368 if (status == MainWindow::FileOpenSucceeded) {
369 haveSession = true;
370 haveMainModel = true;
371 }
372 } else {
373 std::cerr << "WARNING: Ignoring additional session file argument \"" << path << "\"" << std::endl;
374 status = MainWindow::FileOpenSucceeded;
375 }
376 }
377 if (status != MainWindow::FileOpenSucceeded) {
378 if (!haveMainModel) {
379 status = m_mainWindow->openPath(path, MainWindow::ReplaceSession);
380 if (status == MainWindow::FileOpenSucceeded) {
381 haveMainModel = true;
382 }
383 } else {
384 if (haveSession && !havePriorCommandLineModel) {
385 status = m_mainWindow->openPath(path, MainWindow::AskUser);
386 if (status == MainWindow::FileOpenSucceeded) {
387 havePriorCommandLineModel = true;
388 }
389 } else {
390 status = m_mainWindow->openPath(path, MainWindow::CreateAdditionalModel);
391 }
392 }
393 }
394 if (status == MainWindow::FileOpenFailed) {
395 if (splash) splash->hide();
396 QMessageBox::critical
397 (m_mainWindow, QMessageBox::tr("Failed to open file"),
398 QMessageBox::tr("File or URL \"%1\" could not be opened").arg(path));
399 } else if (status == MainWindow::FileOpenWrongMode) {
400 if (splash) splash->hide();
401 QMessageBox::critical
402 (m_mainWindow, QMessageBox::tr("Failed to open file"),
403 QMessageBox::tr("<b>Audio required</b><p>Please load at least one audio file before importing annotation data"));
404 }
405 }
406