comparison src/MainWindow.cpp @ 0:158f40a613a5

Initial import of material from SV and Vect. This builds and runs, but does not yet have the layout we need
author Chris Cannam
date Fri, 16 Nov 2012 11:48:16 +0000
parents
children a11a9c04df44
comparison
equal deleted inserted replaced
-1:000000000000 0:158f40a613a5
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 "../version.h"
17
18 #include "MainWindow.h"
19 #include "framework/Document.h"
20
21 #include "view/Pane.h"
22 #include "view/PaneStack.h"
23 #include "data/model/WaveFileModel.h"
24 #include "data/model/SparseOneDimensionalModel.h"
25 #include "data/model/FFTModel.h"
26 #include "view/ViewManager.h"
27 #include "base/Preferences.h"
28 #include "layer/WaveformLayer.h"
29 #include "layer/TimeRulerLayer.h"
30 #include "layer/TimeInstantLayer.h"
31 #include "layer/TimeValueLayer.h"
32 #include "layer/Colour3DPlotLayer.h"
33 #include "layer/SliceLayer.h"
34 #include "layer/SliceableLayer.h"
35 #include "widgets/Fader.h"
36 #include "view/Overview.h"
37 #include "widgets/PropertyBox.h"
38 #include "widgets/PropertyStack.h"
39 #include "widgets/AudioDial.h"
40 #include "widgets/IconLoader.h"
41 #include "widgets/LayerTree.h"
42 #include "widgets/ListInputDialog.h"
43 #include "widgets/SubdividingMenu.h"
44 #include "widgets/NotifyingPushButton.h"
45 #include "widgets/KeyReference.h"
46 #include "audioio/AudioCallbackPlaySource.h"
47 #include "audioio/AudioCallbackPlayTarget.h"
48 #include "audioio/AudioTargetFactory.h"
49 #include "audioio/PlaySpeedRangeMapper.h"
50 #include "data/fileio/DataFileReaderFactory.h"
51 #include "data/fileio/PlaylistFileReader.h"
52 #include "data/fileio/WavFileWriter.h"
53 #include "data/fileio/CSVFileWriter.h"
54 #include "data/fileio/BZipFileDevice.h"
55 #include "data/fileio/FileSource.h"
56 #include "data/fft/FFTDataServer.h"
57 #include "base/RecentFiles.h"
58 #include "transform/TransformFactory.h"
59 #include "transform/ModelTransformerFactory.h"
60 #include "base/PlayParameterRepository.h"
61 #include "base/XmlExportable.h"
62 #include "widgets/CommandHistory.h"
63 #include "base/Profiler.h"
64 #include "base/Clipboard.h"
65 #include "base/UnitDatabase.h"
66 #include "layer/ColourDatabase.h"
67 #include "data/osc/OSCQueue.h"
68
69 // For version information
70 #include "vamp/vamp.h"
71 #include "vamp-sdk/PluginBase.h"
72 #include "plugin/api/ladspa.h"
73 #include "plugin/api/dssi.h"
74
75 #include <QApplication>
76 #include <QMessageBox>
77 #include <QGridLayout>
78 #include <QLabel>
79 #include <QAction>
80 #include <QMenuBar>
81 #include <QToolBar>
82 #include <QToolButton>
83 #include <QButtonGroup>
84 #include <QInputDialog>
85 #include <QStatusBar>
86 #include <QTreeView>
87 #include <QFile>
88 #include <QFileInfo>
89 #include <QDir>
90 #include <QTextStream>
91 #include <QProcess>
92 #include <QShortcut>
93 #include <QSettings>
94 #include <QDateTime>
95 #include <QProcess>
96 #include <QCheckBox>
97 #include <QRegExp>
98 #include <QScrollArea>
99
100 #include <iostream>
101 #include <cstdio>
102 #include <errno.h>
103
104 using std::cerr;
105 using std::endl;
106
107 using std::vector;
108 using std::map;
109 using std::set;
110
111
112 MainWindow::MainWindow(bool withAudioOutput, bool withOSCSupport) :
113 MainWindowBase(withAudioOutput, withOSCSupport, false),
114 m_overview(0),
115 m_mainMenusCreated(false),
116 m_playbackMenu(0),
117 m_recentFilesMenu(0),
118 m_rightButtonMenu(0),
119 m_rightButtonPlaybackMenu(0),
120 m_deleteSelectedAction(0),
121 m_ffwdAction(0),
122 m_rwdAction(0),
123 m_layerTreeView(0),
124 m_keyReference(new KeyReference())
125 {
126 setWindowTitle(QApplication::applicationName());
127
128 UnitDatabase *udb = UnitDatabase::getInstance();
129 udb->registerUnit("Hz");
130 udb->registerUnit("dB");
131 udb->registerUnit("s");
132
133 ColourDatabase *cdb = ColourDatabase::getInstance();
134 cdb->addColour(Qt::black, tr("Black"));
135 cdb->addColour(Qt::darkRed, tr("Red"));
136 cdb->addColour(Qt::darkBlue, tr("Blue"));
137 cdb->addColour(Qt::darkGreen, tr("Green"));
138 cdb->addColour(QColor(200, 50, 255), tr("Purple"));
139 cdb->addColour(QColor(255, 150, 50), tr("Orange"));
140 cdb->setUseDarkBackground(cdb->addColour(Qt::white, tr("White")), true);
141 cdb->setUseDarkBackground(cdb->addColour(Qt::red, tr("Bright Red")), true);
142 cdb->setUseDarkBackground(cdb->addColour(QColor(30, 150, 255), tr("Bright Blue")), true);
143 cdb->setUseDarkBackground(cdb->addColour(Qt::green, tr("Bright Green")), true);
144 cdb->setUseDarkBackground(cdb->addColour(QColor(225, 74, 255), tr("Bright Purple")), true);
145 cdb->setUseDarkBackground(cdb->addColour(QColor(255, 188, 80), tr("Bright Orange")), true);
146
147 Preferences::getInstance()->setResampleOnLoad(true);
148 Preferences::getInstance()->setSpectrogramSmoothing
149 (Preferences::SpectrogramInterpolated);
150
151 QSettings settings;
152
153 settings.beginGroup("LayerDefaults");
154
155 settings.setValue("waveform",
156 QString("<layer scale=\"%1\" channelMode=\"%2\"/>")
157 .arg(int(WaveformLayer::MeterScale))
158 // .arg(int(WaveformLayer::LinearScale))
159 .arg(int(WaveformLayer::MergeChannels)));
160
161 settings.setValue("timevalues",
162 QString("<layer plotStyle=\"%1\"/>")
163 .arg(int(TimeValueLayer::PlotStems)));
164
165 settings.setValue("spectrogram",
166 QString("<layer channel=\"-1\" windowSize=\"2048\" windowHopLevel=\"2\"/>"));
167
168 settings.setValue("melodicrange",
169 QString("<layer channel=\"-1\" gain=\"10\" normalizeVisibleArea=\"false\" normalizeColumns=\"false\" minFrequency=\"100\" maxFrequency=\"1200\" windowSize=\"4096\" windowOverlap=\"75\" binDisplay=\"0\" />"));
170
171 settings.endGroup();
172
173 settings.beginGroup("MainWindow");
174 settings.setValue("showstatusbar", false);
175 settings.endGroup();
176
177 m_viewManager->setAlignMode(true);
178 m_viewManager->setPlaySoloMode(true);
179 m_viewManager->setToolMode(ViewManager::NavigateMode);
180 m_viewManager->setZoomWheelsEnabled(false);
181 m_viewManager->setIlluminateLocalFeatures(false);
182 m_viewManager->setShowWorkTitle(true);
183
184 QFrame *frame = new QFrame;
185 setCentralWidget(frame);
186
187 QGridLayout *layout = new QGridLayout;
188
189 m_descriptionLabel = new QLabel;
190
191 QScrollArea *scroll = new QScrollArea(frame);
192 scroll->setWidgetResizable(true);
193 scroll->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
194 scroll->setFrameShape(QFrame::NoFrame);
195
196 m_paneStack->setLayoutStyle(PaneStack::NoPropertyStacks);
197 scroll->setWidget(m_paneStack);
198
199 m_overview = new Overview(frame);
200 m_overview->setViewManager(m_viewManager);
201 m_overview->setFixedHeight(40);
202 #ifndef _WIN32
203 // For some reason, the contents of the overview never appear if we
204 // make this setting on Windows. I have no inclination at the moment
205 // to track down the reason why.
206 m_overview->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken);
207 #endif
208 connect(m_overview, SIGNAL(contextHelpChanged(const QString &)),
209 this, SLOT(contextHelpChanged(const QString &)));
210 m_overview->hide();
211
212 m_panLayer = new WaveformLayer;
213 m_panLayer->setChannelMode(WaveformLayer::MergeChannels);
214 m_panLayer->setAggressiveCacheing(true);
215 m_overview->addLayer(m_panLayer);
216
217 if (m_viewManager->getGlobalDarkBackground()) {
218 m_panLayer->setBaseColour
219 (ColourDatabase::getInstance()->getColourIndex(tr("Bright Green")));
220 } else {
221 m_panLayer->setBaseColour
222 (ColourDatabase::getInstance()->getColourIndex(tr("Green")));
223 }
224
225 m_fader = new Fader(frame, false);
226 connect(m_fader, SIGNAL(mouseEntered()), this, SLOT(mouseEnteredWidget()));
227 connect(m_fader, SIGNAL(mouseLeft()), this, SLOT(mouseLeftWidget()));
228
229 m_playSpeed = new AudioDial(frame);
230 m_playSpeed->setMinimum(0);
231 m_playSpeed->setMaximum(200);
232 m_playSpeed->setValue(100);
233 m_playSpeed->setFixedWidth(24);
234 m_playSpeed->setFixedHeight(24);
235 m_playSpeed->setNotchesVisible(true);
236 m_playSpeed->setPageStep(10);
237 m_playSpeed->setObjectName(tr("Playback Speedup"));
238 m_playSpeed->setDefaultValue(100);
239 m_playSpeed->setRangeMapper(new PlaySpeedRangeMapper(0, 200));
240 m_playSpeed->setShowToolTip(true);
241 connect(m_playSpeed, SIGNAL(valueChanged(int)),
242 this, SLOT(playSpeedChanged(int)));
243 connect(m_playSpeed, SIGNAL(mouseEntered()), this, SLOT(mouseEnteredWidget()));
244 connect(m_playSpeed, SIGNAL(mouseLeft()), this, SLOT(mouseLeftWidget()));
245
246 layout->setSpacing(4);
247 layout->addWidget(scroll, 0, 0, 1, 6);
248 layout->addWidget(m_overview, 1, 1);
249 layout->addWidget(m_fader, 1, 2);
250 layout->addWidget(m_playSpeed, 1, 3);
251
252 m_paneStack->setPropertyStackMinWidth
253 (m_fader->width() + m_playSpeed->width() +
254 layout->spacing() * 4);
255
256 layout->setColumnStretch(1, 10);
257
258 frame->setLayout(layout);
259
260 setupMenus();
261 setupToolbars();
262 setupHelpMenu();
263
264 statusBar();
265
266 newSession();
267 }
268
269 MainWindow::~MainWindow()
270 {
271 delete m_keyReference;
272 delete m_layerTreeView;
273 Profiles::getInstance()->dump();
274 }
275
276 void
277 MainWindow::setupMenus()
278 {
279 if (!m_mainMenusCreated) {
280 m_rightButtonMenu = new QMenu();
281 }
282
283 if (!m_mainMenusCreated) {
284 CommandHistory::getInstance()->registerMenu(m_rightButtonMenu);
285 m_rightButtonMenu->addSeparator();
286 }
287
288 setupFileMenu();
289 setupEditMenu();
290 setupViewMenu();
291
292 m_mainMenusCreated = true;
293 }
294
295 void
296 MainWindow::setupFileMenu()
297 {
298 if (m_mainMenusCreated) return;
299
300 QMenu *menu = menuBar()->addMenu(tr("&File"));
301 menu->setTearOffEnabled(true);
302 QToolBar *toolbar = addToolBar(tr("File Toolbar"));
303
304 m_keyReference->setCategory(tr("File and Session Management"));
305
306 IconLoader il;
307
308 QIcon icon = il.load("filenew");
309 icon.addPixmap(il.loadPixmap("filenew-22"));
310 QAction *action = new QAction(icon, tr("&Clear Session"), this);
311 action->setShortcut(tr("Ctrl+N"));
312 action->setStatusTip(tr("Abandon the current session and start a new one"));
313 connect(action, SIGNAL(triggered()), this, SLOT(newSession()));
314 m_keyReference->registerShortcut(action);
315 menu->addAction(action);
316 toolbar->addAction(action);
317
318 icon = il.load("fileopen");
319 icon.addPixmap(il.loadPixmap("fileopen-22"));
320 action = new QAction(icon, tr("&Add File..."), this);
321 action->setShortcut(tr("Ctrl+O"));
322 action->setStatusTip(tr("Add a file"));
323 connect(action, SIGNAL(triggered()), this, SLOT(openFile()));
324 m_keyReference->registerShortcut(action);
325 menu->addAction(action);
326 toolbar->addAction(action);
327
328 action = new QAction(tr("Add Lo&cation..."), this);
329 action->setShortcut(tr("Ctrl+Shift+O"));
330 action->setStatusTip(tr("Add a file from a remote URL"));
331 connect(action, SIGNAL(triggered()), this, SLOT(openLocation()));
332 m_keyReference->registerShortcut(action);
333 menu->addAction(action);
334
335 menu->addSeparator();
336
337 m_recentFilesMenu = menu->addMenu(tr("&Recent Locations"));
338 m_recentFilesMenu->setTearOffEnabled(true);
339 setupRecentFilesMenu();
340 connect(&m_recentFiles, SIGNAL(recentChanged()),
341 this, SLOT(setupRecentFilesMenu()));
342
343 menu->addSeparator();
344 action = new QAction(il.load("exit"), tr("&Quit"), this);
345 action->setShortcut(tr("Ctrl+Q"));
346 action->setStatusTip(tr("Exit %1").arg(QApplication::applicationName()));
347 connect(action, SIGNAL(triggered()), this, SLOT(close()));
348 m_keyReference->registerShortcut(action);
349 menu->addAction(action);
350 }
351
352 void
353 MainWindow::setupEditMenu()
354 {
355 if (m_mainMenusCreated) return;
356
357 QMenu *menu = menuBar()->addMenu(tr("&Edit"));
358 menu->setTearOffEnabled(true);
359 CommandHistory::getInstance()->registerMenu(menu);
360 }
361
362 void
363 MainWindow::setupViewMenu()
364 {
365 if (m_mainMenusCreated) return;
366
367 IconLoader il;
368
369 QAction *action = 0;
370
371 m_keyReference->setCategory(tr("Panning and Navigation"));
372
373 QMenu *menu = menuBar()->addMenu(tr("&View"));
374 menu->setTearOffEnabled(true);
375 action = new QAction(tr("Scroll &Left"), this);
376 action->setShortcut(tr("Left"));
377 action->setStatusTip(tr("Scroll the current pane to the left"));
378 connect(action, SIGNAL(triggered()), this, SLOT(scrollLeft()));
379 connect(this, SIGNAL(canScroll(bool)), action, SLOT(setEnabled(bool)));
380 m_keyReference->registerShortcut(action);
381 menu->addAction(action);
382
383 action = new QAction(tr("Scroll &Right"), this);
384 action->setShortcut(tr("Right"));
385 action->setStatusTip(tr("Scroll the current pane to the right"));
386 connect(action, SIGNAL(triggered()), this, SLOT(scrollRight()));
387 connect(this, SIGNAL(canScroll(bool)), action, SLOT(setEnabled(bool)));
388 m_keyReference->registerShortcut(action);
389 menu->addAction(action);
390
391 action = new QAction(tr("&Jump Left"), this);
392 action->setShortcut(tr("Ctrl+Left"));
393 action->setStatusTip(tr("Scroll the current pane a big step to the left"));
394 connect(action, SIGNAL(triggered()), this, SLOT(jumpLeft()));
395 connect(this, SIGNAL(canScroll(bool)), action, SLOT(setEnabled(bool)));
396 m_keyReference->registerShortcut(action);
397 menu->addAction(action);
398
399 action = new QAction(tr("J&ump Right"), this);
400 action->setShortcut(tr("Ctrl+Right"));
401 action->setStatusTip(tr("Scroll the current pane a big step to the right"));
402 connect(action, SIGNAL(triggered()), this, SLOT(jumpRight()));
403 connect(this, SIGNAL(canScroll(bool)), action, SLOT(setEnabled(bool)));
404 m_keyReference->registerShortcut(action);
405 menu->addAction(action);
406
407 menu->addSeparator();
408
409 m_keyReference->setCategory(tr("Zoom"));
410
411 action = new QAction(il.load("zoom-in"),
412 tr("Zoom &In"), this);
413 action->setShortcut(tr("Up"));
414 action->setStatusTip(tr("Increase the zoom level"));
415 connect(action, SIGNAL(triggered()), this, SLOT(zoomIn()));
416 connect(this, SIGNAL(canZoom(bool)), action, SLOT(setEnabled(bool)));
417 m_keyReference->registerShortcut(action);
418 menu->addAction(action);
419
420 action = new QAction(il.load("zoom-out"),
421 tr("Zoom &Out"), this);
422 action->setShortcut(tr("Down"));
423 action->setStatusTip(tr("Decrease the zoom level"));
424 connect(action, SIGNAL(triggered()), this, SLOT(zoomOut()));
425 connect(this, SIGNAL(canZoom(bool)), action, SLOT(setEnabled(bool)));
426 m_keyReference->registerShortcut(action);
427 menu->addAction(action);
428
429 action = new QAction(tr("Restore &Default Zoom"), this);
430 action->setStatusTip(tr("Restore the zoom level to the default"));
431 connect(action, SIGNAL(triggered()), this, SLOT(zoomDefault()));
432 connect(this, SIGNAL(canZoom(bool)), action, SLOT(setEnabled(bool)));
433 menu->addAction(action);
434
435 action = new QAction(il.load("zoom-fit"),
436 tr("Zoom to &Fit"), this);
437 action->setShortcut(tr("F"));
438 action->setStatusTip(tr("Zoom to show the whole file"));
439 connect(action, SIGNAL(triggered()), this, SLOT(zoomToFit()));
440 connect(this, SIGNAL(canZoom(bool)), action, SLOT(setEnabled(bool)));
441 m_keyReference->registerShortcut(action);
442 menu->addAction(action);
443
444 menu->addSeparator();
445
446 m_keyReference->setCategory(tr("Display Features"));
447
448 QActionGroup *overlayGroup = new QActionGroup(this);
449
450 action = new QAction(tr("Show &No Overlays"), this);
451 action->setShortcut(tr("0"));
452 action->setStatusTip(tr("Hide centre indicator, frame times, layer names and scale"));
453 connect(action, SIGNAL(triggered()), this, SLOT(showNoOverlays()));
454 action->setCheckable(true);
455 action->setChecked(false);
456 overlayGroup->addAction(action);
457 m_keyReference->registerShortcut(action);
458 menu->addAction(action);
459
460 action = new QAction(tr("Show &Minimal Overlays"), this);
461 action->setShortcut(tr("9"));
462 action->setStatusTip(tr("Show centre indicator only"));
463 connect(action, SIGNAL(triggered()), this, SLOT(showMinimalOverlays()));
464 action->setCheckable(true);
465 action->setChecked(false);
466 overlayGroup->addAction(action);
467 m_keyReference->registerShortcut(action);
468 menu->addAction(action);
469
470 action = new QAction(tr("Show &Standard Overlays"), this);
471 action->setShortcut(tr("8"));
472 action->setStatusTip(tr("Show centre indicator, frame times and scale"));
473 connect(action, SIGNAL(triggered()), this, SLOT(showStandardOverlays()));
474 action->setCheckable(true);
475 action->setChecked(true);
476 overlayGroup->addAction(action);
477 m_keyReference->registerShortcut(action);
478 menu->addAction(action);
479
480 action = new QAction(tr("Show &All Overlays"), this);
481 action->setShortcut(tr("7"));
482 action->setStatusTip(tr("Show all texts and scale"));
483 connect(action, SIGNAL(triggered()), this, SLOT(showAllOverlays()));
484 action->setCheckable(true);
485 action->setChecked(false);
486 overlayGroup->addAction(action);
487 m_keyReference->registerShortcut(action);
488 menu->addAction(action);
489
490 menu->addSeparator();
491
492 action = new QAction(tr("Show &Zoom Wheels"), this);
493 action->setShortcut(tr("Z"));
494 action->setStatusTip(tr("Show thumbwheels for zooming horizontally and vertically"));
495 connect(action, SIGNAL(triggered()), this, SLOT(toggleZoomWheels()));
496 action->setCheckable(true);
497 action->setChecked(m_viewManager->getZoomWheelsEnabled());
498 m_keyReference->registerShortcut(action);
499 menu->addAction(action);
500
501 action = new QAction(tr("Show Property Bo&xes"), this);
502 action->setShortcut(tr("X"));
503 action->setStatusTip(tr("Show the layer property boxes at the side of the main window"));
504 connect(action, SIGNAL(triggered()), this, SLOT(togglePropertyBoxes()));
505 action->setCheckable(true);
506 action->setChecked(false); //!!!
507 m_keyReference->registerShortcut(action);
508 menu->addAction(action);
509
510 action = new QAction(tr("Show Status &Bar"), this);
511 action->setStatusTip(tr("Show context help information in the status bar at the bottom of the window"));
512 connect(action, SIGNAL(triggered()), this, SLOT(toggleStatusBar()));
513 action->setCheckable(true);
514 action->setChecked(true);
515 menu->addAction(action);
516
517 QSettings settings;
518 settings.beginGroup("MainWindow");
519 bool sb = settings.value("showstatusbar", true).toBool();
520 if (!sb) {
521 action->setChecked(false);
522 statusBar()->hide();
523 }
524 settings.endGroup();
525
526 menu->addSeparator();
527
528 action = new QAction(tr("Show La&yer Hierarchy"), this);
529 action->setShortcut(tr("H"));
530 action->setStatusTip(tr("Open a window displaying the hierarchy of panes and layers in this session"));
531 connect(action, SIGNAL(triggered()), this, SLOT(showLayerTree()));
532 m_keyReference->registerShortcut(action);
533 menu->addAction(action);
534 }
535
536 void
537 MainWindow::setupHelpMenu()
538 {
539 QMenu *menu = menuBar()->addMenu(tr("&Help"));
540 menu->setTearOffEnabled(true);
541
542 m_keyReference->setCategory(tr("Help"));
543
544 IconLoader il;
545
546 QAction *action = new QAction(il.load("help"),
547 tr("&Help Reference"), this);
548 action->setShortcut(tr("F1"));
549 action->setStatusTip(tr("Open the reference manual"));
550 connect(action, SIGNAL(triggered()), this, SLOT(help()));
551 m_keyReference->registerShortcut(action);
552 menu->addAction(action);
553
554 action = new QAction(tr("&Key and Mouse Reference"), this);
555 action->setShortcut(tr("F2"));
556 action->setStatusTip(tr("Open a window showing the keystrokes you can use"));
557 connect(action, SIGNAL(triggered()), this, SLOT(keyReference()));
558 m_keyReference->registerShortcut(action);
559 menu->addAction(action);
560
561 action = new QAction(tr("Sonic Visualiser on the &Web"), this);
562 action->setStatusTip(tr("Open the Sonic Visualiser website"));
563 connect(action, SIGNAL(triggered()), this, SLOT(website()));
564 menu->addAction(action);
565
566 action = new QAction(tr("&About Sonic Visualiser"), this);
567 action->setStatusTip(tr("Show information about Sonic Visualiser"));
568 connect(action, SIGNAL(triggered()), this, SLOT(about()));
569 menu->addAction(action);
570 }
571
572 void
573 MainWindow::setupRecentFilesMenu()
574 {
575 m_recentFilesMenu->clear();
576 vector<QString> files = m_recentFiles.getRecent();
577 for (size_t i = 0; i < files.size(); ++i) {
578 QAction *action = new QAction(files[i], this);
579 connect(action, SIGNAL(triggered()), this, SLOT(openRecentFile()));
580 if (i == 0) {
581 action->setShortcut(tr("Ctrl+R"));
582 m_keyReference->registerShortcut
583 (tr("Re-open"),
584 action->shortcut(),
585 tr("Re-open the current or most recently opened file"));
586 }
587 m_recentFilesMenu->addAction(action);
588 }
589 }
590
591 void
592 MainWindow::setupToolbars()
593 {
594 m_keyReference->setCategory(tr("Playback and Transport Controls"));
595
596 IconLoader il;
597
598 QMenu *menu = m_playbackMenu = menuBar()->addMenu(tr("Play&back"));
599 menu->setTearOffEnabled(true);
600 m_rightButtonMenu->addSeparator();
601 m_rightButtonPlaybackMenu = m_rightButtonMenu->addMenu(tr("Playback"));
602
603 QToolBar *toolbar = addToolBar(tr("Playback Toolbar"));
604
605 QAction *rwdStartAction = toolbar->addAction(il.load("rewind-start"),
606 tr("Rewind to Start"));
607 rwdStartAction->setShortcut(tr("Home"));
608 rwdStartAction->setStatusTip(tr("Rewind to the start"));
609 connect(rwdStartAction, SIGNAL(triggered()), this, SLOT(rewindStart()));
610 connect(this, SIGNAL(canPlay(bool)), rwdStartAction, SLOT(setEnabled(bool)));
611
612 QAction *m_rwdAction = toolbar->addAction(il.load("rewind"),
613 tr("Rewind"));
614 m_rwdAction->setShortcut(tr("PgUp"));
615 m_rwdAction->setStatusTip(tr("Rewind to the previous time instant or time ruler notch"));
616 connect(m_rwdAction, SIGNAL(triggered()), this, SLOT(rewind()));
617 connect(this, SIGNAL(canRewind(bool)), m_rwdAction, SLOT(setEnabled(bool)));
618
619 QAction *playAction = toolbar->addAction(il.load("playpause"),
620 tr("Play / Pause"));
621 playAction->setCheckable(true);
622 playAction->setShortcut(tr("Space"));
623 playAction->setStatusTip(tr("Start or stop playback from the current position"));
624 connect(playAction, SIGNAL(triggered()), this, SLOT(play()));
625 connect(m_playSource, SIGNAL(playStatusChanged(bool)),
626 playAction, SLOT(setChecked(bool)));
627 connect(this, SIGNAL(canPlay(bool)), playAction, SLOT(setEnabled(bool)));
628
629 m_ffwdAction = toolbar->addAction(il.load("ffwd"),
630 tr("Fast Forward"));
631 m_ffwdAction->setShortcut(tr("PgDown"));
632 m_ffwdAction->setStatusTip(tr("Fast-forward to the next time instant or time ruler notch"));
633 connect(m_ffwdAction, SIGNAL(triggered()), this, SLOT(ffwd()));
634 connect(this, SIGNAL(canFfwd(bool)), m_ffwdAction, SLOT(setEnabled(bool)));
635
636 QAction *ffwdEndAction = toolbar->addAction(il.load("ffwd-end"),
637 tr("Fast Forward to End"));
638 ffwdEndAction->setShortcut(tr("End"));
639 ffwdEndAction->setStatusTip(tr("Fast-forward to the end"));
640 connect(ffwdEndAction, SIGNAL(triggered()), this, SLOT(ffwdEnd()));
641 connect(this, SIGNAL(canPlay(bool)), ffwdEndAction, SLOT(setEnabled(bool)));
642 /*
643 toolbar = addToolBar(tr("Play Mode Toolbar"));
644
645 QAction *psAction = toolbar->addAction(il.load("playselection"),
646 tr("Constrain Playback to Selection"));
647 psAction->setCheckable(true);
648 psAction->setChecked(m_viewManager->getPlaySelectionMode());
649 psAction->setShortcut(tr("s"));
650 psAction->setStatusTip(tr("Constrain playback to the selected regions"));
651 connect(m_viewManager, SIGNAL(playSelectionModeChanged(bool)),
652 psAction, SLOT(setChecked(bool)));
653 connect(psAction, SIGNAL(triggered()), this, SLOT(playSelectionToggled()));
654 connect(this, SIGNAL(canPlaySelection(bool)), psAction, SLOT(setEnabled(bool)));
655
656 QAction *plAction = toolbar->addAction(il.load("playloop"),
657 tr("Loop Playback"));
658 plAction->setCheckable(true);
659 plAction->setChecked(m_viewManager->getPlayLoopMode());
660 plAction->setShortcut(tr("l"));
661 plAction->setStatusTip(tr("Loop playback"));
662 connect(m_viewManager, SIGNAL(playLoopModeChanged(bool)),
663 plAction, SLOT(setChecked(bool)));
664 connect(plAction, SIGNAL(triggered()), this, SLOT(playLoopToggled()));
665 connect(this, SIGNAL(canPlay(bool)), plAction, SLOT(setEnabled(bool)));
666
667 QAction *soAction = toolbar->addAction(il.load("solo"),
668 tr("Solo Current Pane"));
669 soAction->setCheckable(true);
670 soAction->setChecked(m_viewManager->getPlaySoloMode());
671 soAction->setShortcut(tr("o"));
672 soAction->setStatusTip(tr("Solo the current pane during playback"));
673 connect(m_viewManager, SIGNAL(playSoloModeChanged(bool)),
674 soAction, SLOT(setChecked(bool)));
675 connect(soAction, SIGNAL(triggered()), this, SLOT(playSoloToggled()));
676 connect(this, SIGNAL(canPlay(bool)), soAction, SLOT(setEnabled(bool)));
677
678 m_keyReference->registerShortcut(psAction);
679 m_keyReference->registerShortcut(plAction);
680 m_keyReference->registerShortcut(soAction);
681 */
682 m_keyReference->registerShortcut(playAction);
683 m_keyReference->registerShortcut(m_rwdAction);
684 m_keyReference->registerShortcut(m_ffwdAction);
685 m_keyReference->registerShortcut(rwdStartAction);
686 m_keyReference->registerShortcut(ffwdEndAction);
687
688 /*
689 menu->addAction(psAction);
690 menu->addAction(plAction);
691 menu->addAction(soAction);
692 */
693 menu->addAction(playAction);
694 menu->addSeparator();
695 menu->addAction(m_rwdAction);
696 menu->addAction(m_ffwdAction);
697 menu->addSeparator();
698 menu->addAction(rwdStartAction);
699 menu->addAction(ffwdEndAction);
700 menu->addSeparator();
701
702 m_rightButtonPlaybackMenu->addAction(playAction);
703 /*
704 m_rightButtonPlaybackMenu->addAction(psAction);
705 m_rightButtonPlaybackMenu->addAction(plAction);
706 m_rightButtonPlaybackMenu->addAction(soAction);
707 */
708 m_rightButtonPlaybackMenu->addSeparator();
709 m_rightButtonPlaybackMenu->addAction(m_rwdAction);
710 m_rightButtonPlaybackMenu->addAction(m_ffwdAction);
711 m_rightButtonPlaybackMenu->addSeparator();
712 m_rightButtonPlaybackMenu->addAction(rwdStartAction);
713 m_rightButtonPlaybackMenu->addAction(ffwdEndAction);
714 m_rightButtonPlaybackMenu->addSeparator();
715
716 QAction *fastAction = menu->addAction(tr("Speed Up"));
717 fastAction->setShortcut(tr("Ctrl+PgUp"));
718 fastAction->setStatusTip(tr("Time-stretch playback to speed it up without changing pitch"));
719 connect(fastAction, SIGNAL(triggered()), this, SLOT(speedUpPlayback()));
720 connect(this, SIGNAL(canSpeedUpPlayback(bool)), fastAction, SLOT(setEnabled(bool)));
721
722 QAction *slowAction = menu->addAction(tr("Slow Down"));
723 slowAction->setShortcut(tr("Ctrl+PgDown"));
724 slowAction->setStatusTip(tr("Time-stretch playback to slow it down without changing pitch"));
725 connect(slowAction, SIGNAL(triggered()), this, SLOT(slowDownPlayback()));
726 connect(this, SIGNAL(canSlowDownPlayback(bool)), slowAction, SLOT(setEnabled(bool)));
727
728 QAction *normalAction = menu->addAction(tr("Restore Normal Speed"));
729 normalAction->setShortcut(tr("Ctrl+Home"));
730 normalAction->setStatusTip(tr("Restore non-time-stretched playback"));
731 connect(normalAction, SIGNAL(triggered()), this, SLOT(restoreNormalPlayback()));
732 connect(this, SIGNAL(canChangePlaybackSpeed(bool)), normalAction, SLOT(setEnabled(bool)));
733
734 m_keyReference->registerShortcut(fastAction);
735 m_keyReference->registerShortcut(slowAction);
736 m_keyReference->registerShortcut(normalAction);
737
738 m_rightButtonPlaybackMenu->addAction(fastAction);
739 m_rightButtonPlaybackMenu->addAction(slowAction);
740 m_rightButtonPlaybackMenu->addAction(normalAction);
741 /*
742 toolbar = addToolBar(tr("Edit Toolbar"));
743 CommandHistory::getInstance()->registerToolbar(toolbar);
744 */
745
746 Pane::registerShortcuts(*m_keyReference);
747 }
748
749 void
750 MainWindow::updateMenuStates()
751 {
752 MainWindowBase::updateMenuStates();
753
754 Pane *currentPane = 0;
755 Layer *currentLayer = 0;
756
757 if (m_paneStack) currentPane = m_paneStack->getCurrentPane();
758 if (currentPane) currentLayer = currentPane->getSelectedLayer();
759
760 bool haveCurrentPane =
761 (currentPane != 0);
762 bool haveCurrentLayer =
763 (haveCurrentPane &&
764 (currentLayer != 0));
765 bool haveSelection =
766 (m_viewManager &&
767 !m_viewManager->getSelections().empty());
768 bool haveCurrentEditableLayer =
769 (haveCurrentLayer &&
770 currentLayer->isLayerEditable());
771 bool haveCurrentTimeInstantsLayer =
772 (haveCurrentLayer &&
773 dynamic_cast<TimeInstantLayer *>(currentLayer));
774 bool haveCurrentTimeValueLayer =
775 (haveCurrentLayer &&
776 dynamic_cast<TimeValueLayer *>(currentLayer));
777
778 emit canChangePlaybackSpeed(true);
779 int v = m_playSpeed->value();
780 emit canSpeedUpPlayback(v < m_playSpeed->maximum());
781 emit canSlowDownPlayback(v > m_playSpeed->minimum());
782
783 if (m_ffwdAction && m_rwdAction) {
784 if (haveCurrentTimeInstantsLayer) {
785 m_ffwdAction->setText(tr("Fast Forward to Next Instant"));
786 m_ffwdAction->setStatusTip(tr("Fast forward to the next time instant in the current layer"));
787 m_rwdAction->setText(tr("Rewind to Previous Instant"));
788 m_rwdAction->setStatusTip(tr("Rewind to the previous time instant in the current layer"));
789 } else if (haveCurrentTimeValueLayer) {
790 m_ffwdAction->setText(tr("Fast Forward to Next Point"));
791 m_ffwdAction->setStatusTip(tr("Fast forward to the next point in the current layer"));
792 m_rwdAction->setText(tr("Rewind to Previous Point"));
793 m_rwdAction->setStatusTip(tr("Rewind to the previous point in the current layer"));
794 } else {
795 m_ffwdAction->setText(tr("Fast Forward"));
796 m_ffwdAction->setStatusTip(tr("Fast forward"));
797 m_rwdAction->setText(tr("Rewind"));
798 m_rwdAction->setStatusTip(tr("Rewind"));
799 }
800 }
801 }
802
803 void
804 MainWindow::updateDescriptionLabel()
805 {
806 if (!getMainModel()) {
807 m_descriptionLabel->setText(tr("No audio file loaded."));
808 return;
809 }
810
811 QString description;
812
813 size_t ssr = getMainModel()->getSampleRate();
814 size_t tsr = ssr;
815 if (m_playSource) tsr = m_playSource->getTargetSampleRate();
816
817 if (ssr != tsr) {
818 description = tr("%1Hz (resampling to %2Hz)").arg(ssr).arg(tsr);
819 } else {
820 description = QString("%1Hz").arg(ssr);
821 }
822
823 description = QString("%1 - %2")
824 .arg(RealTime::frame2RealTime(getMainModel()->getEndFrame(), ssr)
825 .toText(false).c_str())
826 .arg(description);
827
828 m_descriptionLabel->setText(description);
829 }
830
831 void
832 MainWindow::documentModified()
833 {
834 //!!!
835 MainWindowBase::documentModified();
836 }
837
838 void
839 MainWindow::documentRestored()
840 {
841 //!!!
842 MainWindowBase::documentRestored();
843 }
844
845 void
846 MainWindow::newSession()
847 {
848 if (!checkSaveModified()) return;
849
850 closeSession();
851 createDocument();
852 m_document->setAutoAlignment(true);
853
854 Pane *pane = m_paneStack->addPane();
855
856 connect(pane, SIGNAL(contextHelpChanged(const QString &)),
857 this, SLOT(contextHelpChanged(const QString &)));
858
859 Layer *waveform = m_document->createMainModelLayer(LayerFactory::Waveform);
860 m_document->addLayerToView(pane, waveform);
861
862 m_overview->registerView(pane);
863
864 CommandHistory::getInstance()->clear();
865 CommandHistory::getInstance()->documentSaved();
866 documentRestored();
867 updateMenuStates();
868 }
869
870 void
871 MainWindow::closeSession()
872 {
873 if (!checkSaveModified()) return;
874
875 while (m_paneStack->getPaneCount() > 0) {
876
877 Pane *pane = m_paneStack->getPane(m_paneStack->getPaneCount() - 1);
878
879 while (pane->getLayerCount() > 0) {
880 m_document->removeLayerFromView
881 (pane, pane->getLayer(pane->getLayerCount() - 1));
882 }
883
884 m_overview->unregisterView(pane);
885 m_paneStack->deletePane(pane);
886 }
887
888 while (m_paneStack->getHiddenPaneCount() > 0) {
889
890 Pane *pane = m_paneStack->getHiddenPane
891 (m_paneStack->getHiddenPaneCount() - 1);
892
893 while (pane->getLayerCount() > 0) {
894 m_document->removeLayerFromView
895 (pane, pane->getLayer(pane->getLayerCount() - 1));
896 }
897
898 m_overview->unregisterView(pane);
899 m_paneStack->deletePane(pane);
900 }
901
902 delete m_document;
903 m_document = 0;
904 m_viewManager->clearSelections();
905 m_timeRulerLayer = 0; // document owned this
906
907 m_sessionFile = "";
908
909 CommandHistory::getInstance()->clear();
910 CommandHistory::getInstance()->documentSaved();
911 documentRestored();
912 }
913
914 void
915 MainWindow::openFile()
916 {
917 QString orig = m_audioFile;
918 if (orig == "") orig = ".";
919 else orig = QFileInfo(orig).absoluteDir().canonicalPath();
920
921 QString path = getOpenFileName(FileFinder::AnyFile);
922
923 if (path.isEmpty()) return;
924
925 FileOpenStatus status = open(path, CreateAdditionalModel);
926
927 if (status == FileOpenFailed) {
928 QMessageBox::critical(this, tr("Failed to open file"),
929 tr("<b>File open failed</b><p>File \"%1\" could not be opened").arg(path));
930 } else if (status == FileOpenWrongMode) {
931 QMessageBox::critical(this, tr("Failed to open file"),
932 tr("<b>Audio required</b><p>Please load at least one audio file before importing annotation data"));
933 } else {
934 configureNewPane(m_paneStack->getCurrentPane());
935 }
936 }
937
938 void
939 MainWindow::openLocation()
940 {
941 QSettings settings;
942 settings.beginGroup("MainWindow");
943 QString lastLocation = settings.value("lastremote", "").toString();
944
945 bool ok = false;
946 QString text = QInputDialog::getText
947 (this, tr("Open Location"),
948 tr("Please enter the URL of the location to open:"),
949 QLineEdit::Normal, lastLocation, &ok);
950
951 if (!ok) return;
952
953 settings.setValue("lastremote", text);
954
955 if (text.isEmpty()) return;
956
957 FileOpenStatus status = open(text, CreateAdditionalModel);
958
959 if (status == FileOpenFailed) {
960 QMessageBox::critical(this, tr("Failed to open location"),
961 tr("<b>Open failed</b><p>URL \"%1\" could not be opened").arg(text));
962 } else if (status == FileOpenWrongMode) {
963 QMessageBox::critical(this, tr("Failed to open location"),
964 tr("<b>Audio required</b><p>Please load at least one audio file before importing annotation data"));
965 } else {
966 configureNewPane(m_paneStack->getCurrentPane());
967 }
968 }
969
970 void
971 MainWindow::openRecentFile()
972 {
973 QObject *obj = sender();
974 QAction *action = dynamic_cast<QAction *>(obj);
975
976 if (!action) {
977 std::cerr << "WARNING: MainWindow::openRecentFile: sender is not an action"
978 << std::endl;
979 return;
980 }
981
982 QString path = action->text();
983 if (path == "") return;
984
985 FileOpenStatus status = open(path, CreateAdditionalModel);
986
987 if (status == FileOpenFailed) {
988 QMessageBox::critical(this, tr("Failed to open location"),
989 tr("<b>Open failed</b><p>File or URL \"%1\" could not be opened").arg(path));
990 } else if (status == FileOpenWrongMode) {
991 QMessageBox::critical(this, tr("Failed to open location"),
992 tr("<b>Audio required</b><p>Please load at least one audio file before importing annotation data"));
993 } else {
994 configureNewPane(m_paneStack->getCurrentPane());
995 }
996 }
997
998 void
999 MainWindow::paneAdded(Pane *pane)
1000 {
1001 pane->setPlaybackFollow(PlaybackScrollContinuous);
1002 m_paneStack->sizePanesEqually();
1003 if (m_overview) m_overview->registerView(pane);
1004 }
1005
1006 void
1007 MainWindow::paneHidden(Pane *pane)
1008 {
1009 if (m_overview) m_overview->unregisterView(pane);
1010 }
1011
1012 void
1013 MainWindow::paneAboutToBeDeleted(Pane *pane)
1014 {
1015 if (m_overview) m_overview->unregisterView(pane);
1016 }
1017
1018 void
1019 MainWindow::paneDropAccepted(Pane *pane, QStringList uriList)
1020 {
1021 // if (pane) m_paneStack->setCurrentPane(pane);
1022
1023 for (QStringList::iterator i = uriList.begin(); i != uriList.end(); ++i) {
1024
1025 FileOpenStatus status = open(*i, CreateAdditionalModel);
1026
1027 if (status == FileOpenFailed) {
1028 QMessageBox::critical(this, tr("Failed to open dropped URL"),
1029 tr("<b>Open failed</b><p>Dropped URL \"%1\" could not be opened").arg(*i));
1030 } else if (status == FileOpenWrongMode) {
1031 QMessageBox::critical(this, tr("Failed to open dropped URL"),
1032 tr("<b>Audio required</b><p>Please load at least one audio file before importing annotation data"));
1033 } else {
1034 configureNewPane(m_paneStack->getCurrentPane());
1035 }
1036 }
1037 }
1038
1039 void
1040 MainWindow::paneDropAccepted(Pane *pane, QString text)
1041 {
1042 if (pane) m_paneStack->setCurrentPane(pane);
1043
1044 QUrl testUrl(text);
1045 if (testUrl.scheme() == "file" ||
1046 testUrl.scheme() == "http" ||
1047 testUrl.scheme() == "ftp") {
1048 QStringList list;
1049 list.push_back(text);
1050 paneDropAccepted(pane, list);
1051 return;
1052 }
1053
1054 //!!! open as text -- but by importing as if a CSV, or just adding
1055 //to a text layer?
1056 }
1057
1058 void
1059 MainWindow::configureNewPane(Pane *pane)
1060 {
1061 std::cerr << "MainWindow::configureNewPane(" << pane << ")" << std::endl;
1062
1063 if (!pane) return;
1064
1065 Layer *waveformLayer = 0;
1066
1067 for (int i = 0; i < pane->getLayerCount(); ++i) {
1068 Layer *layer = pane->getLayer(i);
1069 if (!layer) continue;
1070 if (dynamic_cast<WaveformLayer *>(layer)) waveformLayer = layer;
1071 if (dynamic_cast<TimeValueLayer *>(layer)) return;
1072 }
1073 if (!waveformLayer) return;
1074
1075 waveformLayer->setObjectName(tr("Waveform"));
1076
1077 zoomToFit();
1078 }
1079
1080 void
1081 MainWindow::closeEvent(QCloseEvent *e)
1082 {
1083 // std::cerr << "MainWindow::closeEvent" << std::endl;
1084
1085 if (m_openingAudioFile) {
1086 // std::cerr << "Busy - ignoring close event" << std::endl;
1087 e->ignore();
1088 return;
1089 }
1090
1091 if (!m_abandoning && !checkSaveModified()) {
1092 // std::cerr << "Ignoring close event" << std::endl;
1093 e->ignore();
1094 return;
1095 }
1096
1097 QSettings settings;
1098 settings.beginGroup("MainWindow");
1099 settings.setValue("size", size());
1100 settings.setValue("position", pos());
1101 settings.endGroup();
1102
1103 delete m_keyReference;
1104 m_keyReference = 0;
1105
1106 if (m_layerTreeView &&
1107 m_layerTreeView->isVisible()) {
1108 delete m_layerTreeView;
1109 }
1110
1111 closeSession();
1112
1113 e->accept();
1114 return;
1115 }
1116
1117 bool
1118 MainWindow::commitData(bool mayAskUser)
1119 {
1120 if (mayAskUser) {
1121 bool rv = checkSaveModified();
1122 return rv;
1123 } else {
1124 if (!m_documentModified) return true;
1125
1126 // If we can't check with the user first, then we can't save
1127 // to the original session file (even if we have it) -- have
1128 // to use a temporary file
1129
1130 QString svDirBase = ".sv1";
1131 QString svDir = QDir::home().filePath(svDirBase);
1132
1133 if (!QFileInfo(svDir).exists()) {
1134 if (!QDir::home().mkdir(svDirBase)) return false;
1135 } else {
1136 if (!QFileInfo(svDir).isDir()) return false;
1137 }
1138
1139 // This name doesn't have to be unguessable
1140 #ifndef _WIN32
1141 QString fname = QString("tmp-%1-%2.sv")
1142 .arg(QDateTime::currentDateTime().toString("yyyyMMddhhmmsszzz"))
1143 .arg(QProcess().pid());
1144 #else
1145 QString fname = QString("tmp-%1.sv")
1146 .arg(QDateTime::currentDateTime().toString("yyyyMMddhhmmsszzz"));
1147 #endif
1148 QString fpath = QDir(svDir).filePath(fname);
1149 if (saveSessionFile(fpath)) {
1150 m_recentFiles.addFile(fpath);
1151 return true;
1152 } else {
1153 return false;
1154 }
1155 }
1156 }
1157
1158 bool
1159 MainWindow::checkSaveModified()
1160 {
1161 // Called before some destructive operation (e.g. new session,
1162 // exit program). Return true if we can safely proceed, false to
1163 // cancel.
1164
1165 if (!m_documentModified) return true;
1166
1167 int button =
1168 QMessageBox::warning(this,
1169 tr("Session modified"),
1170 tr("The current session has been modified.\nDo you want to save it?"),
1171 QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
1172 QMessageBox::Yes);
1173
1174 if (button == QMessageBox::Yes) {
1175 saveSession();
1176 if (m_documentModified) { // save failed -- don't proceed!
1177 return false;
1178 } else {
1179 return true; // saved, so it's safe to continue now
1180 }
1181 } else if (button == QMessageBox::No) {
1182 m_documentModified = false; // so we know to abandon it
1183 return true;
1184 }
1185
1186 // else cancel
1187 return false;
1188 }
1189
1190 void
1191 MainWindow::saveSession()
1192 {
1193 if (m_sessionFile != "") {
1194 if (!saveSessionFile(m_sessionFile)) {
1195 QMessageBox::critical(this, tr("Failed to save file"),
1196 tr("Session file \"%1\" could not be saved.").arg(m_sessionFile));
1197 } else {
1198 CommandHistory::getInstance()->documentSaved();
1199 documentRestored();
1200 }
1201 } else {
1202 saveSessionAs();
1203 }
1204 }
1205
1206 void
1207 MainWindow::saveSessionAs()
1208 {
1209 QString orig = m_audioFile;
1210 if (orig == "") orig = ".";
1211 else orig = QFileInfo(orig).absoluteDir().canonicalPath();
1212
1213 QString path = getSaveFileName(FileFinder::SessionFile);
1214
1215 if (path == "") return;
1216
1217 if (!saveSessionFile(path)) {
1218 QMessageBox::critical(this, tr("Failed to save file"),
1219 tr("Session file \"%1\" could not be saved.").arg(path));
1220 } else {
1221 setWindowTitle(tr("%1: %2")
1222 .arg(QApplication::applicationName())
1223 .arg(QFileInfo(path).fileName()));
1224 m_sessionFile = path;
1225 CommandHistory::getInstance()->documentSaved();
1226 documentRestored();
1227 m_recentFiles.addFile(path);
1228 }
1229 }
1230
1231 void
1232 MainWindow::renameCurrentLayer()
1233 {
1234 Pane *pane = m_paneStack->getCurrentPane();
1235 if (pane) {
1236 Layer *layer = pane->getSelectedLayer();
1237 if (layer) {
1238 bool ok = false;
1239 QString newName = QInputDialog::getText
1240 (this, tr("Rename Layer"),
1241 tr("New name for this layer:"),
1242 QLineEdit::Normal, layer->objectName(), &ok);
1243 if (ok) {
1244 layer->setObjectName(newName);
1245 }
1246 }
1247 }
1248 }
1249
1250 void
1251 MainWindow::playSpeedChanged(int position)
1252 {
1253 PlaySpeedRangeMapper mapper(0, 200);
1254
1255 float percent = m_playSpeed->mappedValue();
1256 float factor = mapper.getFactorForValue(percent);
1257
1258 std::cerr << "speed = " << position << " percent = " << percent << " factor = " << factor << std::endl;
1259
1260 bool something = (position != 100);
1261
1262 int pc = lrintf(percent);
1263
1264 if (!something) {
1265 contextHelpChanged(tr("Playback speed: Normal"));
1266 } else {
1267 contextHelpChanged(tr("Playback speed: %1%2%")
1268 .arg(position > 100 ? "+" : "")
1269 .arg(pc));
1270 }
1271
1272 m_playSource->setTimeStretch(factor);
1273
1274 updateMenuStates();
1275 }
1276
1277 void
1278 MainWindow::playSharpenToggled()
1279 {
1280 QSettings settings;
1281 settings.beginGroup("MainWindow");
1282 settings.setValue("playsharpen", m_playSharpen->isChecked());
1283 settings.endGroup();
1284
1285 playSpeedChanged(m_playSpeed->value());
1286 }
1287
1288 void
1289 MainWindow::playMonoToggled()
1290 {
1291 QSettings settings;
1292 settings.beginGroup("MainWindow");
1293 settings.setValue("playmono", m_playMono->isChecked());
1294 settings.endGroup();
1295
1296 playSpeedChanged(m_playSpeed->value());
1297 }
1298
1299 void
1300 MainWindow::speedUpPlayback()
1301 {
1302 int value = m_playSpeed->value();
1303 value = value + m_playSpeed->pageStep();
1304 if (value > m_playSpeed->maximum()) value = m_playSpeed->maximum();
1305 m_playSpeed->setValue(value);
1306 }
1307
1308 void
1309 MainWindow::slowDownPlayback()
1310 {
1311 int value = m_playSpeed->value();
1312 value = value - m_playSpeed->pageStep();
1313 if (value < m_playSpeed->minimum()) value = m_playSpeed->minimum();
1314 m_playSpeed->setValue(value);
1315 }
1316
1317 void
1318 MainWindow::restoreNormalPlayback()
1319 {
1320 m_playSpeed->setValue(m_playSpeed->defaultValue());
1321 }
1322
1323 void
1324 MainWindow::updateVisibleRangeDisplay(Pane *p) const
1325 {
1326 if (!getMainModel() || !p) {
1327 return;
1328 }
1329
1330 bool haveSelection = false;
1331 size_t startFrame = 0, endFrame = 0;
1332
1333 if (m_viewManager && m_viewManager->haveInProgressSelection()) {
1334
1335 bool exclusive = false;
1336 Selection s = m_viewManager->getInProgressSelection(exclusive);
1337
1338 if (!s.isEmpty()) {
1339 haveSelection = true;
1340 startFrame = s.getStartFrame();
1341 endFrame = s.getEndFrame();
1342 }
1343 }
1344
1345 if (!haveSelection) {
1346 startFrame = p->getFirstVisibleFrame();
1347 endFrame = p->getLastVisibleFrame();
1348 }
1349
1350 RealTime start = RealTime::frame2RealTime
1351 (startFrame, getMainModel()->getSampleRate());
1352
1353 RealTime end = RealTime::frame2RealTime
1354 (endFrame, getMainModel()->getSampleRate());
1355
1356 RealTime duration = end - start;
1357
1358 QString startStr, endStr, durationStr;
1359 startStr = start.toText(true).c_str();
1360 endStr = end.toText(true).c_str();
1361 durationStr = duration.toText(true).c_str();
1362
1363 if (haveSelection) {
1364 m_myStatusMessage = tr("Selection: %1 to %2 (duration %3)")
1365 .arg(startStr).arg(endStr).arg(durationStr);
1366 } else {
1367 m_myStatusMessage = tr("Visible: %1 to %2 (duration %3)")
1368 .arg(startStr).arg(endStr).arg(durationStr);
1369 }
1370
1371 statusBar()->showMessage(m_myStatusMessage);
1372 }
1373
1374 void
1375 MainWindow::updatePositionStatusDisplays() const
1376 {
1377 if (!statusBar()->isVisible()) return;
1378
1379 }
1380
1381 void
1382 MainWindow::outputLevelsChanged(float left, float right)
1383 {
1384 m_fader->setPeakLeft(left);
1385 m_fader->setPeakRight(right);
1386 }
1387
1388 void
1389 MainWindow::sampleRateMismatch(size_t requested, size_t actual,
1390 bool willResample)
1391 {
1392 if (!willResample) {
1393 //!!! more helpful message needed
1394 QMessageBox::information
1395 (this, tr("Sample rate mismatch"),
1396 tr("The sample rate of this audio file (%1 Hz) does not match\nthe current playback rate (%2 Hz).\n\nThe file will play at the wrong speed and pitch.")
1397 .arg(requested).arg(actual));
1398 }
1399
1400 updateDescriptionLabel();
1401 }
1402
1403 void
1404 MainWindow::audioOverloadPluginDisabled()
1405 {
1406 QMessageBox::information
1407 (this, tr("Audio processing overload"),
1408 tr("<b>Overloaded</b><p>Audio effects plugin auditioning has been disabled due to a processing overload."));
1409 }
1410
1411 void
1412 MainWindow::audioTimeStretchMultiChannelDisabled()
1413 {
1414 static bool shownOnce = false;
1415 if (shownOnce) return;
1416 QMessageBox::information
1417 (this, tr("Audio processing overload"),
1418 tr("<b>Overloaded</b><p>Audio playback speed processing has been reduced to a single channel, due to a processing overload."));
1419 shownOnce = true;
1420 }
1421
1422 void
1423 MainWindow::layerRemoved(Layer *layer)
1424 {
1425 MainWindowBase::layerRemoved(layer);
1426 }
1427
1428 void
1429 MainWindow::layerInAView(Layer *layer, bool inAView)
1430 {
1431 MainWindowBase::layerInAView(layer, inAView);
1432 }
1433
1434 void
1435 MainWindow::modelAdded(Model *model)
1436 {
1437 MainWindowBase::modelAdded(model);
1438 DenseTimeValueModel *dtvm = dynamic_cast<DenseTimeValueModel *>(model);
1439 if (dtvm) {
1440 std::cerr << "A dense time-value model (such as an audio file) has been loaded" << std::endl;
1441 }
1442 }
1443
1444 void
1445 MainWindow::modelReady()
1446 {
1447 QObject *s = sender();
1448 std::cerr << "MainWindow::modelReady(" << s << ")" << std::endl;
1449 }
1450
1451 void
1452 MainWindow::modelAboutToBeDeleted(Model *model)
1453 {
1454 MainWindowBase::modelAboutToBeDeleted(model);
1455 }
1456
1457 void
1458 MainWindow::mainModelChanged(WaveFileModel *model)
1459 {
1460 m_panLayer->setModel(model);
1461
1462 MainWindowBase::mainModelChanged(model);
1463
1464 if (m_playTarget) {
1465 connect(m_fader, SIGNAL(valueChanged(float)),
1466 m_playTarget, SLOT(setOutputGain(float)));
1467 }
1468 }
1469
1470 void
1471 MainWindow::modelGenerationFailed(QString transformName, QString message)
1472 {
1473 if (message != "") {
1474
1475 QMessageBox::warning
1476 (this,
1477 tr("Failed to generate layer"),
1478 tr("<b>Layer generation failed</b><p>Failed to generate derived layer.<p>The layer transform \"%1\" failed:<p>%2")
1479 .arg(transformName).arg(message),
1480 QMessageBox::Ok);
1481 } else {
1482 QMessageBox::warning
1483 (this,
1484 tr("Failed to generate layer"),
1485 tr("<b>Layer generation failed</b><p>Failed to generate a derived layer.<p>The layer transform \"%1\" failed.<p>No error information is available.")
1486 .arg(transformName),
1487 QMessageBox::Ok);
1488 }
1489 }
1490
1491 void
1492 MainWindow::modelGenerationWarning(QString transformName, QString message)
1493 {
1494 QMessageBox::warning
1495 (this, tr("Warning"), message, QMessageBox::Ok);
1496 }
1497
1498 void
1499 MainWindow::modelRegenerationFailed(QString layerName,
1500 QString transformName, QString message)
1501 {
1502 if (message != "") {
1503
1504 QMessageBox::warning
1505 (this,
1506 tr("Failed to regenerate layer"),
1507 tr("<b>Layer generation failed</b><p>Failed to regenerate derived layer \"%1\" using new data model as input.<p>The layer transform \"%2\" failed:<p>%3")
1508 .arg(layerName).arg(transformName).arg(message),
1509 QMessageBox::Ok);
1510 } else {
1511 QMessageBox::warning
1512 (this,
1513 tr("Failed to regenerate layer"),
1514 tr("<b>Layer generation failed</b><p>Failed to regenerate derived layer \"%1\" using new data model as input.<p>The layer transform \"%2\" failed.<p>No error information is available.")
1515 .arg(layerName).arg(transformName),
1516 QMessageBox::Ok);
1517 }
1518 }
1519
1520 void
1521 MainWindow::modelRegenerationWarning(QString layerName,
1522 QString transformName, QString message)
1523 {
1524 QMessageBox::warning
1525 (this, tr("Warning"), tr("<b>Warning when regenerating layer</b><p>When regenerating the derived layer \"%1\" using new data model as input:<p>%2").arg(layerName).arg(message), QMessageBox::Ok);
1526 }
1527
1528 void
1529 MainWindow::alignmentFailed(QString transformName, QString message)
1530 {
1531 QMessageBox::warning
1532 (this,
1533 tr("Failed to calculate alignment"),
1534 tr("<b>Alignment calculation failed</b><p>Failed to calculate an audio alignment using transform \"%1\":<p>%2")
1535 .arg(transformName).arg(message),
1536 QMessageBox::Ok);
1537 }
1538
1539 void
1540 MainWindow::rightButtonMenuRequested(Pane *pane, QPoint position)
1541 {
1542 // std::cerr << "MainWindow::rightButtonMenuRequested(" << pane << ", " << position.x() << ", " << position.y() << ")" << std::endl;
1543 m_paneStack->setCurrentPane(pane);
1544 m_rightButtonMenu->popup(position);
1545 }
1546
1547 void
1548 MainWindow::showLayerTree()
1549 {
1550 if (!m_layerTreeView.isNull()) {
1551 m_layerTreeView->show();
1552 m_layerTreeView->raise();
1553 return;
1554 }
1555
1556 //!!! should use an actual dialog class
1557
1558 m_layerTreeView = new QTreeView();
1559 LayerTreeModel *tree = new LayerTreeModel(m_paneStack);
1560 m_layerTreeView->resize(500, 300); //!!!
1561 m_layerTreeView->setModel(tree);
1562 m_layerTreeView->expandAll();
1563 m_layerTreeView->show();
1564 }
1565
1566 void
1567 MainWindow::handleOSCMessage(const OSCMessage &message)
1568 {
1569 std::cerr << "MainWindow::handleOSCMessage: Not implemented" << std::endl;
1570 }
1571
1572 void
1573 MainWindow::mouseEnteredWidget()
1574 {
1575 QWidget *w = dynamic_cast<QWidget *>(sender());
1576 if (!w) return;
1577
1578 if (w == m_fader) {
1579 contextHelpChanged(tr("Adjust the master playback level"));
1580 } else if (w == m_playSpeed) {
1581 contextHelpChanged(tr("Adjust the master playback speed"));
1582 } else if (w == m_playSharpen && w->isEnabled()) {
1583 contextHelpChanged(tr("Toggle transient sharpening for playback time scaling"));
1584 } else if (w == m_playMono && w->isEnabled()) {
1585 contextHelpChanged(tr("Toggle mono mode for playback time scaling"));
1586 }
1587 }
1588
1589 void
1590 MainWindow::mouseLeftWidget()
1591 {
1592 contextHelpChanged("");
1593 }
1594
1595 void
1596 MainWindow::website()
1597 {
1598 openHelpUrl(tr("http://www.sonicvisualiser.org/"));
1599 }
1600
1601 void
1602 MainWindow::help()
1603 {
1604 openHelpUrl(tr("http://www.sonicvisualiser.org/doc/reference/1.0/en/"));
1605 }
1606
1607 void
1608 MainWindow::about()
1609 {
1610 bool debug = false;
1611 QString version = "(unknown version)";
1612
1613 #ifdef BUILD_DEBUG
1614 debug = true;
1615 #endif
1616 version = tr("Release %1").arg(TONY_VERSION);
1617
1618 QString aboutText;
1619
1620 aboutText += tr("<h3>About Tony</h3>");
1621 aboutText += tr("<p>Tony is a program for interactive note and pitch analysis and annotation.</p>");
1622 aboutText += tr("<p>%1 : %2 configuration</p>")
1623 .arg(version)
1624 .arg(debug ? tr("Debug") : tr("Release"));
1625
1626 #ifndef BUILD_STATIC
1627 aboutText += tr("<br>Using Qt v%1 &copy; Trolltech AS").arg(QT_VERSION_STR);
1628 #else
1629 #ifdef QT_SHARED
1630 aboutText += tr("<br>Using Qt v%1 &copy; Trolltech AS").arg(QT_VERSION_STR);
1631 #endif
1632 #endif
1633
1634 #ifdef BUILD_STATIC
1635 aboutText += tr("<p>Statically linked");
1636 #ifndef QT_SHARED
1637 aboutText += tr("<br>With Qt (v%1) &copy; Trolltech AS").arg(QT_VERSION_STR);
1638 #endif
1639 #ifdef HAVE_JACK
1640 #ifdef JACK_VERSION
1641 aboutText += tr("<br>With JACK audio output (v%1) &copy; Paul Davis and Jack O'Quin").arg(JACK_VERSION);
1642 #else
1643 aboutText += tr("<br>With JACK audio output &copy; Paul Davis and Jack O'Quin");
1644 #endif
1645 #endif
1646 #ifdef HAVE_PORTAUDIO
1647 aboutText += tr("<br>With PortAudio audio output &copy; Ross Bencina and Phil Burk");
1648 #endif
1649 #ifdef HAVE_OGGZ
1650 #ifdef OGGZ_VERSION
1651 aboutText += tr("<br>With Ogg file decoder (oggz v%1, fishsound v%2) &copy; CSIRO Australia").arg(OGGZ_VERSION).arg(FISHSOUND_VERSION);
1652 #else
1653 aboutText += tr("<br>With Ogg file decoder &copy; CSIRO Australia");
1654 #endif
1655 #endif
1656 #ifdef HAVE_MAD
1657 #ifdef MAD_VERSION
1658 aboutText += tr("<br>With MAD mp3 decoder (v%1) &copy; Underbit Technologies Inc").arg(MAD_VERSION);
1659 #else
1660 aboutText += tr("<br>With MAD mp3 decoder &copy; Underbit Technologies Inc");
1661 #endif
1662 #endif
1663 #ifdef HAVE_SAMPLERATE
1664 #ifdef SAMPLERATE_VERSION
1665 aboutText += tr("<br>With libsamplerate (v%1) &copy; Erik de Castro Lopo").arg(SAMPLERATE_VERSION);
1666 #else
1667 aboutText += tr("<br>With libsamplerate &copy; Erik de Castro Lopo");
1668 #endif
1669 #endif
1670 #ifdef HAVE_SNDFILE
1671 #ifdef SNDFILE_VERSION
1672 aboutText += tr("<br>With libsndfile (v%1) &copy; Erik de Castro Lopo").arg(SNDFILE_VERSION);
1673 #else
1674 aboutText += tr("<br>With libsndfile &copy; Erik de Castro Lopo");
1675 #endif
1676 #endif
1677 #ifdef HAVE_FFTW3F
1678 #ifdef FFTW3_VERSION
1679 aboutText += tr("<br>With FFTW3 (v%1) &copy; Matteo Frigo and MIT").arg(FFTW3_VERSION);
1680 #else
1681 aboutText += tr("<br>With FFTW3 &copy; Matteo Frigo and MIT");
1682 #endif
1683 #endif
1684 #ifdef HAVE_VAMP
1685 aboutText += tr("<br>With Vamp plugin support (API v%1, host SDK v%2) &copy; Chris Cannam").arg(VAMP_API_VERSION).arg(VAMP_SDK_VERSION);
1686 #endif
1687 aboutText += tr("<br>With LADSPA plugin support (API v%1) &copy; Richard Furse, Paul Davis, Stefan Westerfeld").arg(LADSPA_VERSION);
1688 aboutText += tr("<br>With DSSI plugin support (API v%1) &copy; Chris Cannam, Steve Harris, Sean Bolton").arg(DSSI_VERSION);
1689 #ifdef HAVE_LIBLO
1690 #ifdef LIBLO_VERSION
1691 aboutText += tr("<br>With liblo Lite OSC library (v%1) &copy; Steve Harris").arg(LIBLO_VERSION);
1692 #else
1693 aboutText += tr("<br>With liblo Lite OSC library &copy; Steve Harris").arg(LIBLO_VERSION);
1694 #endif
1695 if (m_oscQueue && m_oscQueue->isOK()) {
1696 aboutText += tr("<p>The OSC URL for this instance is: \"%1\"").arg(m_oscQueue->getOSCURL());
1697 }
1698 #endif
1699 aboutText += "</p>";
1700 #endif
1701
1702 aboutText +=
1703 "<p>Copyright &copy; 2005&ndash;2012 Chris Cannam and<br>"
1704 "Queen Mary, University of London.</p>"
1705 "<p>This program is free software; you can redistribute it and/or<br>"
1706 "modify it under the terms of the GNU General Public License as<br>"
1707 "published by the Free Software Foundation; either version 2 of the<br>"
1708 "License, or (at your option) any later version.<br>See the file "
1709 "COPYING included with this distribution for more information.</p>";
1710
1711 QMessageBox::about(this, tr("About %1").arg(QApplication::applicationName()), aboutText);
1712 }
1713
1714 void
1715 MainWindow::keyReference()
1716 {
1717 m_keyReference->show();
1718 }
1719
1720