To check out this repository please hg clone the following URL, or open the URL using EasyMercurial or your preferred Mercurial client.

Statistics Download as Zip
| Branch: | Revision:

root / main / MainWindow.cpp @ 3:36855d576f53

History | View | Annotate | Download (71.6 KB)

1
/* -*- c-basic-offset: 4 indent-tabs-mode: nil -*-  vi:set ts=8 sts=4 sw=4: */
2

    
3
/*
4
    Sonic Visualiser
5
    An audio file viewer and annotation editor.
6
    Centre for Digital Music, Queen Mary, University of London.
7
    This file copyright 2006 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
#include "PreferencesDialog.h"
21

    
22
#include "view/Pane.h"
23
#include "view/PaneStack.h"
24
#include "data/model/WaveFileModel.h"
25
#include "data/model/SparseOneDimensionalModel.h"
26
#include "data/model/FFTModel.h"
27
#include "base/StorageAdviser.h"
28
#include "view/ViewManager.h"
29
#include "base/Preferences.h"
30
#include "layer/WaveformLayer.h"
31
#include "layer/TimeRulerLayer.h"
32
#include "layer/TimeInstantLayer.h"
33
#include "layer/TimeValueLayer.h"
34
#include "layer/Colour3DPlotLayer.h"
35
#include "layer/SliceLayer.h"
36
#include "layer/SliceableLayer.h"
37
#include "widgets/Fader.h"
38
#include "view/Overview.h"
39
#include "widgets/PropertyBox.h"
40
#include "widgets/PropertyStack.h"
41
#include "widgets/AudioDial.h"
42
#include "widgets/IconLoader.h"
43
#include "widgets/LayerTree.h"
44
#include "widgets/ListInputDialog.h"
45
#include "widgets/SubdividingMenu.h"
46
#include "widgets/NotifyingPushButton.h"
47
#include "widgets/KeyReference.h"
48
#include "audioio/AudioCallbackPlaySource.h"
49
#include "audioio/AudioCallbackPlayTarget.h"
50
#include "audioio/AudioTargetFactory.h"
51
#include "audioio/PlaySpeedRangeMapper.h"
52
#include "data/fileio/DataFileReaderFactory.h"
53
#include "data/fileio/PlaylistFileReader.h"
54
#include "data/fileio/WavFileWriter.h"
55
#include "data/fileio/CSVFileWriter.h"
56
#include "data/fileio/BZipFileDevice.h"
57
#include "data/fileio/FileSource.h"
58
#include "data/fft/FFTDataServer.h"
59
#include "base/RecentFiles.h"
60
#include "transform/TransformFactory.h"
61
#include "transform/ModelTransformerFactory.h"
62
#include "base/PlayParameterRepository.h"
63
#include "base/XmlExportable.h"
64
#include "widgets/CommandHistory.h"
65
#include "base/Profiler.h"
66
#include "base/Clipboard.h"
67
#include "base/UnitDatabase.h"
68
#include "layer/ColourDatabase.h"
69
#include "data/osc/OSCQueue.h"
70
#include "rdf/PluginRDFDescription.h"
71

    
72
//!!!
73
#include "data/model/AggregateWaveModel.h"
74

    
75
// For version information
76
#include "vamp/vamp.h"
77
#include "vamp-sdk/PluginBase.h"
78
#include "plugin/api/ladspa.h"
79
#include "plugin/api/dssi.h"
80

    
81
#include <QApplication>
82
#include <QMessageBox>
83
#include <QGridLayout>
84
#include <QLabel>
85
#include <QAction>
86
#include <QMenuBar>
87
#include <QToolBar>
88
#include <QToolButton>
89
#include <QButtonGroup>
90
#include <QInputDialog>
91
#include <QStatusBar>
92
#include <QTreeView>
93
#include <QFile>
94
#include <QFileInfo>
95
#include <QDir>
96
#include <QTextStream>
97
#include <QProcess>
98
#include <QShortcut>
99
#include <QSettings>
100
#include <QDateTime>
101
#include <QProcess>
102
#include <QCheckBox>
103
#include <QRegExp>
104
#include <QScrollArea>
105

    
106
#include <iostream>
107
#include <cstdio>
108
#include <errno.h>
109

    
110
using std::cerr;
111
using std::endl;
112

    
113
using std::vector;
114
using std::map;
115
using std::set;
116

    
117

    
118
MainWindow::MainWindow(bool withAudioOutput, bool withOSCSupport) :
119
    MainWindowBase(withAudioOutput, withOSCSupport, false),
120
    m_overview(0),
121
    m_mainMenusCreated(false),
122
    m_playbackMenu(0),
123
    m_recentFilesMenu(0),
124
    m_rightButtonMenu(0),
125
    m_rightButtonPlaybackMenu(0),
126
    m_segmentersMenu(0),
127
    m_deleteSelectedAction(0),
128
    m_ffwdAction(0),
129
    m_rwdAction(0),
130
    m_preferencesDialog(0),
131
    m_layerTreeView(0),
132
    m_keyReference(new KeyReference()),
133
    m_displayMode(WaveformMode)
134
{
135
    setWindowTitle(QApplication::applicationName());
136

    
137
    StorageAdviser::setFixedRecommendation
138
        (StorageAdviser::Recommendation(StorageAdviser::UseDisc |
139
                                        StorageAdviser::ConserveSpace));
140

    
141
    UnitDatabase *udb = UnitDatabase::getInstance();
142
    udb->registerUnit("Hz");
143
    udb->registerUnit("dB");
144
    udb->registerUnit("s");
145

    
146
    ColourDatabase *cdb = ColourDatabase::getInstance();
147
    cdb->addColour(Qt::black, tr("Black"));
148
    cdb->addColour(Qt::darkRed, tr("Red"));
149
    cdb->addColour(Qt::darkBlue, tr("Blue"));
150
    cdb->addColour(Qt::darkGreen, tr("Green"));
151
    cdb->addColour(QColor(200, 50, 255), tr("Purple"));
152
    cdb->addColour(QColor(255, 150, 50), tr("Orange"));
153
    cdb->setUseDarkBackground(cdb->addColour(Qt::white, tr("White")), true);
154
    cdb->setUseDarkBackground(cdb->addColour(Qt::red, tr("Bright Red")), true);
155
    cdb->setUseDarkBackground(cdb->addColour(QColor(30, 150, 255), tr("Bright Blue")), true);
156
    cdb->setUseDarkBackground(cdb->addColour(Qt::green, tr("Bright Green")), true);
157
    cdb->setUseDarkBackground(cdb->addColour(QColor(225, 74, 255), tr("Bright Purple")), true);
158
    cdb->setUseDarkBackground(cdb->addColour(QColor(255, 188, 80), tr("Bright Orange")), true);
159

    
160
    Preferences::getInstance()->setResampleOnLoad(true);
161
    Preferences::getInstance()->setSpectrogramSmoothing
162
        (Preferences::SpectrogramInterpolated);
163

    
164
    QSettings settings;
165

    
166
    settings.beginGroup("LayerDefaults");
167

    
168
    settings.setValue("waveform",
169
                      QString("<layer scale=\"%1\" channelMode=\"%2\"/>")
170
//                      .arg(int(WaveformLayer::MeterScale))
171
                      .arg(int(WaveformLayer::LinearScale))
172
                      .arg(int(WaveformLayer::MergeChannels)));
173

    
174
    settings.setValue("timevalues",
175
                      QString("<layer plotStyle=\"%1\"/>")
176
//                      .arg(int(TimeValueLayer::PlotStems)));
177
                      .arg(int(TimeValueLayer::PlotCurve)));
178

    
179
    settings.setValue("spectrogram",
180
                      QString("<layer channel=\"-1\" windowSize=\"2048\" windowHopLevel=\"2\"/>"));
181

    
182
    settings.setValue("melodicrange",
183
                      QString("<layer channel=\"-1\" gain=\"10\" normalizeVisibleArea=\"false\" normalizeColumns=\"false\" minFrequency=\"100\" maxFrequency=\"1200\" windowSize=\"4096\" windowOverlap=\"75\" binDisplay=\"0\" />"));
184

    
185
    settings.endGroup();
186

    
187
    settings.beginGroup("MainWindow");
188
    settings.setValue("showstatusbar", false);
189
    settings.endGroup();
190

    
191
//    m_viewManager->setAlignMode(true);
192
//    m_viewManager->setPlaySoloMode(true);
193
    m_viewManager->setToolMode(ViewManager::NavigateMode);
194
    m_viewManager->setZoomWheelsEnabled(false);
195
    m_viewManager->setIlluminateLocalFeatures(false);
196
    m_viewManager->setShowWorkTitle(true);
197

    
198
#ifndef __APPLE__
199
    m_viewManager->setGlobalDarkBackground(true);
200
#endif
201
    
202
    QFrame *frame = new QFrame;
203
    setCentralWidget(frame);
204

    
205
    QGridLayout *layout = new QGridLayout;
206
    
207
    m_descriptionLabel = new QLabel;
208

    
209
    QScrollArea *scroll = new QScrollArea(frame);
210
    scroll->setWidgetResizable(true);
211
    scroll->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
212
    scroll->setFrameShape(QFrame::NoFrame);
213

    
214
    m_paneStack->setLayoutStyle(PaneStack::NoPropertyStacks);
215
    scroll->setWidget(m_paneStack);
216
    
217
    QButtonGroup *bg = new QButtonGroup;
218
    IconLoader il;
219

    
220
    QFrame *buttonFrame = new QFrame;
221
    QHBoxLayout *buttonLayout = new QHBoxLayout;
222
    buttonLayout->setSpacing(0);
223
    buttonLayout->setMargin(0);
224
    buttonFrame->setLayout(buttonLayout);
225

    
226
    QToolButton *button = new QToolButton;
227
    button->setIcon(il.load("waveform"));
228
    button->setCheckable(true);
229
    button->setChecked(true);
230
    button->setAutoRaise(true);
231
    bg->addButton(button);
232
    buttonLayout->addWidget(button);
233
    connect(button, SIGNAL(clicked()), this, SLOT(waveformModeSelected()));
234

    
235
    button = new QToolButton;
236
    button->setIcon(il.load("values"));
237
    button->setCheckable(true);
238
    button->setChecked(false);
239
    button->setAutoRaise(true);
240
    bg->addButton(button);
241
    buttonLayout->addWidget(button);
242
    connect(button, SIGNAL(clicked()), this, SLOT(curveModeSelected()));
243

    
244
    button = new QToolButton;
245
    button->setIcon(il.load("spectrogram"));
246
    button->setCheckable(true);
247
    button->setChecked(false);
248
    button->setAutoRaise(true);
249
    bg->addButton(button);
250
    buttonLayout->addWidget(button);
251
    connect(button, SIGNAL(clicked()), this, SLOT(spectrogramModeSelected()));
252

    
253
    button = new QToolButton;
254
    button->setIcon(il.load("melodogram"));
255
    button->setCheckable(true);
256
    button->setChecked(false);
257
    button->setAutoRaise(true);
258
    bg->addButton(button);
259
    buttonLayout->addWidget(button);
260
    connect(button, SIGNAL(clicked()), this, SLOT(melodogramModeSelected()));
261

    
262
    layout->addWidget(buttonFrame, 1, 0);
263

    
264
    m_overview = new Overview(frame);
265
    m_overview->setViewManager(m_viewManager);
266
    m_overview->setFixedHeight(40);
267
#ifndef _WIN32
268
    // For some reason, the contents of the overview never appear if we
269
    // make this setting on Windows.  I have no inclination at the moment
270
    // to track down the reason why.
271
    m_overview->setFrameStyle(QFrame::StyledPanel | QFrame::Sunken);
272
#endif
273
    connect(m_overview, SIGNAL(contextHelpChanged(const QString &)),
274
            this, SLOT(contextHelpChanged(const QString &)));
275
    m_overview->hide();
276

    
277
    m_panLayer = new WaveformLayer;
278
    m_panLayer->setChannelMode(WaveformLayer::MergeChannels);
279
    m_panLayer->setAggressiveCacheing(true);
280
    m_overview->addLayer(m_panLayer);
281

    
282
    if (m_viewManager->getGlobalDarkBackground()) {
283
        m_panLayer->setBaseColour
284
            (ColourDatabase::getInstance()->getColourIndex(tr("Bright Green")));
285
    } else {
286
        m_panLayer->setBaseColour
287
            (ColourDatabase::getInstance()->getColourIndex(tr("Green")));
288
    }        
289

    
290
    m_fader = new Fader(frame, false);
291
    connect(m_fader, SIGNAL(mouseEntered()), this, SLOT(mouseEnteredWidget()));
292
    connect(m_fader, SIGNAL(mouseLeft()), this, SLOT(mouseLeftWidget()));
293

    
294
    m_playSpeed = new AudioDial(frame);
295
    m_playSpeed->setMinimum(0);
296
    m_playSpeed->setMaximum(200);
297
    m_playSpeed->setValue(100);
298
    m_playSpeed->setFixedWidth(24);
299
    m_playSpeed->setFixedHeight(24);
300
    m_playSpeed->setNotchesVisible(true);
301
    m_playSpeed->setPageStep(10);
302
    m_playSpeed->setObjectName(tr("Playback Speedup"));
303
    m_playSpeed->setDefaultValue(100);
304
    m_playSpeed->setRangeMapper(new PlaySpeedRangeMapper(0, 200));
305
    m_playSpeed->setShowToolTip(true);
306
    connect(m_playSpeed, SIGNAL(valueChanged(int)),
307
            this, SLOT(playSpeedChanged(int)));
308
    connect(m_playSpeed, SIGNAL(mouseEntered()), this, SLOT(mouseEnteredWidget()));
309
    connect(m_playSpeed, SIGNAL(mouseLeft()), this, SLOT(mouseLeftWidget()));
310

    
311
    layout->setSpacing(4);
312
    layout->addWidget(scroll, 0, 0, 1, 6);
313
    layout->addWidget(m_overview, 1, 1);
314
    layout->addWidget(m_fader, 1, 2);
315
    layout->addWidget(m_playSpeed, 1, 3);
316

    
317
    m_paneStack->setPropertyStackMinWidth
318
        (m_fader->width() + m_playSpeed->width() +
319
         layout->spacing() * 4);
320

    
321
    layout->setColumnStretch(1, 10);
322

    
323
    frame->setLayout(layout);
324

    
325
    findSegmentationTransforms();
326

    
327
    setupMenus();
328
    setupToolbars();
329
    setupHelpMenu();
330

    
331
    statusBar();
332

    
333
    newSession();
334
}
335

    
336
MainWindow::~MainWindow()
337
{
338
    delete m_keyReference;
339
    delete m_preferencesDialog;
340
    delete m_layerTreeView;
341
    Profiles::getInstance()->dump();
342
}
343

    
344
void
345
MainWindow::setupMenus()
346
{
347
    if (!m_mainMenusCreated) {
348
        m_rightButtonMenu = new QMenu();
349

    
350
        // No -- we don't want tear-off enabled on the right-button
351
        // menu.  If it is enabled, then simply right-clicking and
352
        // releasing will pop up the menu, activate the tear-off, and
353
        // leave the torn-off menu window in front of the main window.
354
        // That isn't desirable.  I'm not sure it ever would be, in a
355
        // context menu -- perhaps technically a Qt bug?
356
//        m_rightButtonMenu->setTearOffEnabled(true);
357
    }
358

    
359
    if (!m_mainMenusCreated) {
360
        CommandHistory::getInstance()->registerMenu(m_rightButtonMenu);
361
        m_rightButtonMenu->addSeparator();
362
    }
363

    
364
    setupFileMenu();
365
//    setupEditMenu();
366
    setupViewMenu();
367
    setupSegmentersMenu();
368

    
369
    m_mainMenusCreated = true;
370
}
371

    
372
void
373
MainWindow::setupFileMenu()
374
{
375
    if (m_mainMenusCreated) return;
376

    
377
    QMenu *menu = menuBar()->addMenu(tr("&File"));
378
    menu->setTearOffEnabled(true);
379
    QToolBar *toolbar = addToolBar(tr("File Toolbar"));
380

    
381
    m_keyReference->setCategory(tr("File and Session Management"));
382

    
383
    IconLoader il;
384

    
385
    QIcon icon = il.load("filenew");
386
    icon.addPixmap(il.loadPixmap("filenew-22"));
387
    QAction *action = new QAction(icon, tr("&Clear Session"), this);
388
    action->setShortcut(tr("Ctrl+N"));
389
    action->setStatusTip(tr("Abandon the current session and start a new one"));
390
    connect(action, SIGNAL(triggered()), this, SLOT(newSession()));
391
    m_keyReference->registerShortcut(action);
392
    menu->addAction(action);
393
    toolbar->addAction(action);
394

    
395
    icon = il.load("fileopen");
396
    icon.addPixmap(il.loadPixmap("fileopen-22"));
397
    action = new QAction(icon, tr("&Add File..."), this);
398
    action->setShortcut(tr("Ctrl+O"));
399
    action->setStatusTip(tr("Add a file"));
400
    connect(action, SIGNAL(triggered()), this, SLOT(openFile()));
401
    m_keyReference->registerShortcut(action);
402
    menu->addAction(action);
403
    toolbar->addAction(action);
404

    
405
    action = new QAction(tr("Add Lo&cation..."), this);
406
    action->setShortcut(tr("Ctrl+Shift+O"));
407
    action->setStatusTip(tr("Add a file from a remote URL"));
408
    connect(action, SIGNAL(triggered()), this, SLOT(openLocation()));
409
    m_keyReference->registerShortcut(action);
410
    menu->addAction(action);
411

    
412
    menu->addSeparator();
413

    
414
    m_recentFilesMenu = menu->addMenu(tr("&Recent Locations"));
415
    m_recentFilesMenu->setTearOffEnabled(true);
416
    setupRecentFilesMenu();
417
    connect(&m_recentFiles, SIGNAL(recentChanged()),
418
            this, SLOT(setupRecentFilesMenu()));
419
/*
420
    menu->addSeparator();
421
    action = new QAction(tr("&Preferences..."), this);
422
    action->setStatusTip(tr("Adjust the application preferences"));
423
    connect(action, SIGNAL(triggered()), this, SLOT(preferences()));
424
    menu->addAction(action);
425
*/
426
    menu->addSeparator();
427
    action = new QAction(il.load("exit"), tr("&Quit"), this);
428
    action->setShortcut(tr("Ctrl+Q"));
429
    action->setStatusTip(tr("Exit %1").arg(QApplication::applicationName()));
430
    connect(action, SIGNAL(triggered()), this, SLOT(close()));
431
    m_keyReference->registerShortcut(action);
432
    menu->addAction(action);
433
}
434

    
435
void
436
MainWindow::setupEditMenu()
437
{
438
    if (m_mainMenusCreated) return;
439

    
440
    QMenu *menu = menuBar()->addMenu(tr("&Edit"));
441
    menu->setTearOffEnabled(true);
442
    CommandHistory::getInstance()->registerMenu(menu);
443
}
444

    
445
void
446
MainWindow::setupViewMenu()
447
{
448
    if (m_mainMenusCreated) return;
449

    
450
    IconLoader il;
451

    
452
    QAction *action = 0;
453

    
454
    m_keyReference->setCategory(tr("Panning and Navigation"));
455

    
456
    QMenu *menu = menuBar()->addMenu(tr("&View"));
457
    menu->setTearOffEnabled(true);
458
    action = new QAction(tr("Scroll &Left"), this);
459
    action->setShortcut(tr("Left"));
460
    action->setStatusTip(tr("Scroll the current pane to the left"));
461
    connect(action, SIGNAL(triggered()), this, SLOT(scrollLeft()));
462
    connect(this, SIGNAL(canScroll(bool)), action, SLOT(setEnabled(bool)));
463
    m_keyReference->registerShortcut(action);
464
    menu->addAction(action);
465
        
466
    action = new QAction(tr("Scroll &Right"), this);
467
    action->setShortcut(tr("Right"));
468
    action->setStatusTip(tr("Scroll the current pane to the right"));
469
    connect(action, SIGNAL(triggered()), this, SLOT(scrollRight()));
470
    connect(this, SIGNAL(canScroll(bool)), action, SLOT(setEnabled(bool)));
471
    m_keyReference->registerShortcut(action);
472
    menu->addAction(action);
473
        
474
    action = new QAction(tr("&Jump Left"), this);
475
    action->setShortcut(tr("Ctrl+Left"));
476
    action->setStatusTip(tr("Scroll the current pane a big step to the left"));
477
    connect(action, SIGNAL(triggered()), this, SLOT(jumpLeft()));
478
    connect(this, SIGNAL(canScroll(bool)), action, SLOT(setEnabled(bool)));
479
    m_keyReference->registerShortcut(action);
480
    menu->addAction(action);
481
        
482
    action = new QAction(tr("J&ump Right"), this);
483
    action->setShortcut(tr("Ctrl+Right"));
484
    action->setStatusTip(tr("Scroll the current pane a big step to the right"));
485
    connect(action, SIGNAL(triggered()), this, SLOT(jumpRight()));
486
    connect(this, SIGNAL(canScroll(bool)), action, SLOT(setEnabled(bool)));
487
    m_keyReference->registerShortcut(action);
488
    menu->addAction(action);
489

    
490
    menu->addSeparator();
491

    
492
    m_keyReference->setCategory(tr("Zoom"));
493

    
494
    action = new QAction(il.load("zoom-in"),
495
                         tr("Zoom &In"), this);
496
    action->setShortcut(tr("Up"));
497
    action->setStatusTip(tr("Increase the zoom level"));
498
    connect(action, SIGNAL(triggered()), this, SLOT(zoomIn()));
499
    connect(this, SIGNAL(canZoom(bool)), action, SLOT(setEnabled(bool)));
500
    m_keyReference->registerShortcut(action);
501
    menu->addAction(action);
502
        
503
    action = new QAction(il.load("zoom-out"),
504
                         tr("Zoom &Out"), this);
505
    action->setShortcut(tr("Down"));
506
    action->setStatusTip(tr("Decrease the zoom level"));
507
    connect(action, SIGNAL(triggered()), this, SLOT(zoomOut()));
508
    connect(this, SIGNAL(canZoom(bool)), action, SLOT(setEnabled(bool)));
509
    m_keyReference->registerShortcut(action);
510
    menu->addAction(action);
511
        
512
    action = new QAction(tr("Restore &Default Zoom"), this);
513
    action->setStatusTip(tr("Restore the zoom level to the default"));
514
    connect(action, SIGNAL(triggered()), this, SLOT(zoomDefault()));
515
    connect(this, SIGNAL(canZoom(bool)), action, SLOT(setEnabled(bool)));
516
    menu->addAction(action);
517

    
518
    action = new QAction(il.load("zoom-fit"),
519
                         tr("Zoom to &Fit"), this);
520
    action->setShortcut(tr("F"));
521
    action->setStatusTip(tr("Zoom to show the whole file"));
522
    connect(action, SIGNAL(triggered()), this, SLOT(zoomToFit()));
523
    connect(this, SIGNAL(canZoom(bool)), action, SLOT(setEnabled(bool)));
524
    m_keyReference->registerShortcut(action);
525
    menu->addAction(action);
526

    
527
    menu->addSeparator();
528

    
529
    m_keyReference->setCategory(tr("Display Features"));
530

    
531
    QActionGroup *overlayGroup = new QActionGroup(this);
532
        
533
    action = new QAction(tr("Show &No Overlays"), this);
534
    action->setShortcut(tr("0"));
535
    action->setStatusTip(tr("Hide centre indicator, frame times, layer names and scale"));
536
    connect(action, SIGNAL(triggered()), this, SLOT(showNoOverlays()));
537
    action->setCheckable(true);
538
    action->setChecked(false);
539
    overlayGroup->addAction(action);
540
    m_keyReference->registerShortcut(action);
541
    menu->addAction(action);
542
        
543
    action = new QAction(tr("Show &Minimal Overlays"), this);
544
    action->setShortcut(tr("9"));
545
    action->setStatusTip(tr("Show centre indicator only"));
546
    connect(action, SIGNAL(triggered()), this, SLOT(showMinimalOverlays()));
547
    action->setCheckable(true);
548
    action->setChecked(false);
549
    overlayGroup->addAction(action);
550
    m_keyReference->registerShortcut(action);
551
    menu->addAction(action);
552
        
553
    action = new QAction(tr("Show &Standard Overlays"), this);
554
    action->setShortcut(tr("8"));
555
    action->setStatusTip(tr("Show centre indicator, frame times and scale"));
556
    connect(action, SIGNAL(triggered()), this, SLOT(showStandardOverlays()));
557
    action->setCheckable(true);
558
    action->setChecked(true);
559
    overlayGroup->addAction(action);
560
    m_keyReference->registerShortcut(action);
561
    menu->addAction(action);
562
        
563
    action = new QAction(tr("Show &All Overlays"), this);
564
    action->setShortcut(tr("7"));
565
    action->setStatusTip(tr("Show all texts and scale"));
566
    connect(action, SIGNAL(triggered()), this, SLOT(showAllOverlays()));
567
    action->setCheckable(true);
568
    action->setChecked(false);
569
    overlayGroup->addAction(action);
570
    m_keyReference->registerShortcut(action);
571
    menu->addAction(action);
572
        
573
    menu->addSeparator();
574

    
575
    action = new QAction(tr("Show &Zoom Wheels"), this);
576
    action->setShortcut(tr("Z"));
577
    action->setStatusTip(tr("Show thumbwheels for zooming horizontally and vertically"));
578
    connect(action, SIGNAL(triggered()), this, SLOT(toggleZoomWheels()));
579
    action->setCheckable(true);
580
    action->setChecked(m_viewManager->getZoomWheelsEnabled());
581
    m_keyReference->registerShortcut(action);
582
    menu->addAction(action);
583
        
584
    action = new QAction(tr("Show Property Bo&xes"), this);
585
    action->setShortcut(tr("X"));
586
    action->setStatusTip(tr("Show the layer property boxes at the side of the main window"));
587
    connect(action, SIGNAL(triggered()), this, SLOT(togglePropertyBoxes()));
588
    action->setCheckable(true);
589
    action->setChecked(false); //!!!
590
    m_keyReference->registerShortcut(action);
591
    menu->addAction(action);
592

    
593
    action = new QAction(tr("Show Status &Bar"), this);
594
    action->setStatusTip(tr("Show context help information in the status bar at the bottom of the window"));
595
    connect(action, SIGNAL(triggered()), this, SLOT(toggleStatusBar()));
596
    action->setCheckable(true);
597
    action->setChecked(true);
598
    menu->addAction(action);
599

    
600
    QSettings settings;
601
    settings.beginGroup("MainWindow");
602
    bool sb = settings.value("showstatusbar", true).toBool();
603
    if (!sb) {
604
        action->setChecked(false);
605
        statusBar()->hide();
606
    }
607
    settings.endGroup();
608

    
609
    menu->addSeparator();
610

    
611
    action = new QAction(tr("Show La&yer Hierarchy"), this);
612
    action->setShortcut(tr("H"));
613
    action->setStatusTip(tr("Open a window displaying the hierarchy of panes and layers in this session"));
614
    connect(action, SIGNAL(triggered()), this, SLOT(showLayerTree()));
615
    m_keyReference->registerShortcut(action);
616
    menu->addAction(action);
617
}
618

    
619
void
620
MainWindow::setupSegmentersMenu()
621
{
622
    if (m_segmentersMenu) {
623
        m_segmenterActions.clear();
624
        m_segmentersMenu->clear();
625
    } else {
626
        m_segmentersMenu = menuBar()->addMenu(tr("&Segmenters")); 
627
        m_segmentersMenu->setTearOffEnabled(true);
628
        m_segmentersMenu->setSeparatorsCollapsible(true);
629
    }
630

    
631
    std::set<QString> seenNames, duplicateNames, seenPluginNames, duplicatePluginNames;
632
    for (unsigned int i = 0; i < m_segmentationTransforms.size(); ++i) {
633
        QString name = m_segmentationTransforms[i].name;
634
        QString pluginName = name.section(": ", 0, 0);
635
        if (seenNames.find(name) != seenNames.end()) {
636
            duplicateNames.insert(name);
637
        } else {
638
            seenNames.insert(name);
639
        }
640
        if (seenPluginNames.find(pluginName) != seenPluginNames.end()) {
641
            duplicatePluginNames.insert(pluginName);
642
        } else {
643
            seenPluginNames.insert(pluginName);
644
        }
645
    }
646

    
647
    for (unsigned int i = 0; i < m_segmentationTransforms.size(); ++i) {
648
        
649
        QString name = m_segmentationTransforms[i].name;
650
        if (name == "") name = m_segmentationTransforms[i].identifier;
651

    
652
        QString maker = m_segmentationTransforms[i].maker;
653
        if (maker == "") maker = tr("Unknown");
654
        maker.replace(QRegExp(tr(" [\\(<].*$")), "");
655

    
656
        QString pluginName = name.section(": ", 0, 0);
657
        QString output = name.section(": ", 1);
658

    
659
        if (duplicateNames.find(pluginName) != duplicateNames.end()) {
660
            pluginName = QString("%1 <%2>")
661
                .arg(pluginName)
662
                .arg(m_segmentationTransforms[i].identifier.section(':', 1, 1));
663
            if (output == "") {
664
                name = pluginName;
665
            } else {
666
                name = QString("%1: %2")
667
                    .arg(pluginName)
668
                    .arg(output);
669
            }
670
        } else if (duplicatePluginNames.find(pluginName) ==
671
                   duplicatePluginNames.end()) {
672
            name = pluginName;
673
        }            
674

    
675
        QAction *action = new QAction(tr("%1...").arg(name), this);
676
        connect(action, SIGNAL(triggered()), this, SLOT(addSegmentation()));
677
        m_segmenterActions[action] = m_segmentationTransforms[i].identifier;
678
        connect(this, SIGNAL(canAddLayer(bool)), action, SLOT(setEnabled(bool)));
679
        action->setStatusTip(m_segmentationTransforms[i].longDescription);
680

    
681
        m_segmentersMenu->addAction(action);
682
    }
683
}
684

    
685
void
686
MainWindow::setupHelpMenu()
687
{
688
    QMenu *menu = menuBar()->addMenu(tr("&Help"));
689
    menu->setTearOffEnabled(true);
690
    
691
    m_keyReference->setCategory(tr("Help"));
692

    
693
    IconLoader il;
694

    
695
    QAction *action = new QAction(il.load("help"),
696
                                  tr("&Help Reference"), this); 
697
    action->setShortcut(tr("F1"));
698
    action->setStatusTip(tr("Open the reference manual")); 
699
    connect(action, SIGNAL(triggered()), this, SLOT(help()));
700
    m_keyReference->registerShortcut(action);
701
    menu->addAction(action);
702

    
703
    action = new QAction(tr("&Key and Mouse Reference"), this);
704
    action->setShortcut(tr("F2"));
705
    action->setStatusTip(tr("Open a window showing the keystrokes you can use"));
706
    connect(action, SIGNAL(triggered()), this, SLOT(keyReference()));
707
    m_keyReference->registerShortcut(action);
708
    menu->addAction(action);
709
    
710
    action = new QAction(tr("Sonic Visualiser on the &Web"), this); 
711
    action->setStatusTip(tr("Open the Sonic Visualiser website")); 
712
    connect(action, SIGNAL(triggered()), this, SLOT(website()));
713
    menu->addAction(action);
714
    
715
    action = new QAction(tr("&About Sonic Visualiser"), this); 
716
    action->setStatusTip(tr("Show information about Sonic Visualiser")); 
717
    connect(action, SIGNAL(triggered()), this, SLOT(about()));
718
    menu->addAction(action);
719
}
720

    
721
void
722
MainWindow::setupRecentFilesMenu()
723
{
724
    m_recentFilesMenu->clear();
725
    vector<QString> files = m_recentFiles.getRecent();
726
    for (size_t i = 0; i < files.size(); ++i) {
727
        QAction *action = new QAction(files[i], this);
728
        connect(action, SIGNAL(triggered()), this, SLOT(openRecentFile()));
729
        if (i == 0) {
730
            action->setShortcut(tr("Ctrl+R"));
731
            m_keyReference->registerShortcut
732
                (tr("Re-open"),
733
                 action->shortcut(),
734
                 tr("Re-open the current or most recently opened file"));
735
        }
736
        m_recentFilesMenu->addAction(action);
737
    }
738
}
739

    
740
void
741
MainWindow::setupToolbars()
742
{
743
    m_keyReference->setCategory(tr("Playback and Transport Controls"));
744

    
745
    IconLoader il;
746

    
747
    QMenu *menu = m_playbackMenu = menuBar()->addMenu(tr("Play&back"));
748
    menu->setTearOffEnabled(true);
749
    m_rightButtonMenu->addSeparator();
750
    m_rightButtonPlaybackMenu = m_rightButtonMenu->addMenu(tr("Playback"));
751

    
752
    QToolBar *toolbar = addToolBar(tr("Playback Toolbar"));
753

    
754
    QAction *rwdStartAction = toolbar->addAction(il.load("rewind-start"),
755
                                                 tr("Rewind to Start"));
756
    rwdStartAction->setShortcut(tr("Home"));
757
    rwdStartAction->setStatusTip(tr("Rewind to the start"));
758
    connect(rwdStartAction, SIGNAL(triggered()), this, SLOT(rewindStart()));
759
    connect(this, SIGNAL(canPlay(bool)), rwdStartAction, SLOT(setEnabled(bool)));
760

    
761
    QAction *m_rwdAction = toolbar->addAction(il.load("rewind"),
762
                                              tr("Rewind"));
763
    m_rwdAction->setShortcut(tr("PgUp"));
764
    m_rwdAction->setStatusTip(tr("Rewind to the previous time instant or time ruler notch"));
765
    connect(m_rwdAction, SIGNAL(triggered()), this, SLOT(rewind()));
766
    connect(this, SIGNAL(canRewind(bool)), m_rwdAction, SLOT(setEnabled(bool)));
767

    
768
    QAction *playAction = toolbar->addAction(il.load("playpause"),
769
                                             tr("Play / Pause"));
770
    playAction->setCheckable(true);
771
    playAction->setShortcut(tr("Space"));
772
    playAction->setStatusTip(tr("Start or stop playback from the current position"));
773
    connect(playAction, SIGNAL(triggered()), this, SLOT(play()));
774
    connect(m_playSource, SIGNAL(playStatusChanged(bool)),
775
            playAction, SLOT(setChecked(bool)));
776
    connect(this, SIGNAL(canPlay(bool)), playAction, SLOT(setEnabled(bool)));
777

    
778
    m_ffwdAction = toolbar->addAction(il.load("ffwd"),
779
                                              tr("Fast Forward"));
780
    m_ffwdAction->setShortcut(tr("PgDown"));
781
    m_ffwdAction->setStatusTip(tr("Fast-forward to the next time instant or time ruler notch"));
782
    connect(m_ffwdAction, SIGNAL(triggered()), this, SLOT(ffwd()));
783
    connect(this, SIGNAL(canFfwd(bool)), m_ffwdAction, SLOT(setEnabled(bool)));
784

    
785
    QAction *ffwdEndAction = toolbar->addAction(il.load("ffwd-end"),
786
                                                tr("Fast Forward to End"));
787
    ffwdEndAction->setShortcut(tr("End"));
788
    ffwdEndAction->setStatusTip(tr("Fast-forward to the end"));
789
    connect(ffwdEndAction, SIGNAL(triggered()), this, SLOT(ffwdEnd()));
790
    connect(this, SIGNAL(canPlay(bool)), ffwdEndAction, SLOT(setEnabled(bool)));
791
/*
792
    toolbar = addToolBar(tr("Play Mode Toolbar"));
793

794
    QAction *psAction = toolbar->addAction(il.load("playselection"),
795
                                           tr("Constrain Playback to Selection"));
796
    psAction->setCheckable(true);
797
    psAction->setChecked(m_viewManager->getPlaySelectionMode());
798
    psAction->setShortcut(tr("s"));
799
    psAction->setStatusTip(tr("Constrain playback to the selected regions"));
800
    connect(m_viewManager, SIGNAL(playSelectionModeChanged(bool)),
801
            psAction, SLOT(setChecked(bool)));
802
    connect(psAction, SIGNAL(triggered()), this, SLOT(playSelectionToggled()));
803
    connect(this, SIGNAL(canPlaySelection(bool)), psAction, SLOT(setEnabled(bool)));
804

805
    QAction *plAction = toolbar->addAction(il.load("playloop"),
806
                                           tr("Loop Playback"));
807
    plAction->setCheckable(true);
808
    plAction->setChecked(m_viewManager->getPlayLoopMode());
809
    plAction->setShortcut(tr("l"));
810
    plAction->setStatusTip(tr("Loop playback"));
811
    connect(m_viewManager, SIGNAL(playLoopModeChanged(bool)),
812
            plAction, SLOT(setChecked(bool)));
813
    connect(plAction, SIGNAL(triggered()), this, SLOT(playLoopToggled()));
814
    connect(this, SIGNAL(canPlay(bool)), plAction, SLOT(setEnabled(bool)));
815

816
    QAction *soAction = toolbar->addAction(il.load("solo"),
817
                                           tr("Solo Current Pane"));
818
    soAction->setCheckable(true);
819
    soAction->setChecked(m_viewManager->getPlaySoloMode());
820
    soAction->setShortcut(tr("o"));
821
    soAction->setStatusTip(tr("Solo the current pane during playback"));
822
    connect(m_viewManager, SIGNAL(playSoloModeChanged(bool)),
823
            soAction, SLOT(setChecked(bool)));
824
    connect(soAction, SIGNAL(triggered()), this, SLOT(playSoloToggled()));
825
    connect(this, SIGNAL(canPlay(bool)), soAction, SLOT(setEnabled(bool)));
826

827
    m_keyReference->registerShortcut(psAction);
828
    m_keyReference->registerShortcut(plAction);
829
    m_keyReference->registerShortcut(soAction);
830
*/
831
    m_keyReference->registerShortcut(playAction);
832
    m_keyReference->registerShortcut(m_rwdAction);
833
    m_keyReference->registerShortcut(m_ffwdAction);
834
    m_keyReference->registerShortcut(rwdStartAction);
835
    m_keyReference->registerShortcut(ffwdEndAction);
836

    
837
/*
838
    menu->addAction(psAction);
839
    menu->addAction(plAction);
840
    menu->addAction(soAction);
841
*/
842
    menu->addAction(playAction);
843
    menu->addSeparator();
844
    menu->addAction(m_rwdAction);
845
    menu->addAction(m_ffwdAction);
846
    menu->addSeparator();
847
    menu->addAction(rwdStartAction);
848
    menu->addAction(ffwdEndAction);
849
    menu->addSeparator();
850

    
851
    m_rightButtonPlaybackMenu->addAction(playAction);
852
/*
853
    m_rightButtonPlaybackMenu->addAction(psAction);
854
    m_rightButtonPlaybackMenu->addAction(plAction);
855
    m_rightButtonPlaybackMenu->addAction(soAction);
856
*/
857
    m_rightButtonPlaybackMenu->addSeparator();
858
    m_rightButtonPlaybackMenu->addAction(m_rwdAction);
859
    m_rightButtonPlaybackMenu->addAction(m_ffwdAction);
860
    m_rightButtonPlaybackMenu->addSeparator();
861
    m_rightButtonPlaybackMenu->addAction(rwdStartAction);
862
    m_rightButtonPlaybackMenu->addAction(ffwdEndAction);
863
    m_rightButtonPlaybackMenu->addSeparator();
864

    
865
    QAction *fastAction = menu->addAction(tr("Speed Up"));
866
    fastAction->setShortcut(tr("Ctrl+PgUp"));
867
    fastAction->setStatusTip(tr("Time-stretch playback to speed it up without changing pitch"));
868
    connect(fastAction, SIGNAL(triggered()), this, SLOT(speedUpPlayback()));
869
    connect(this, SIGNAL(canSpeedUpPlayback(bool)), fastAction, SLOT(setEnabled(bool)));
870
    
871
    QAction *slowAction = menu->addAction(tr("Slow Down"));
872
    slowAction->setShortcut(tr("Ctrl+PgDown"));
873
    slowAction->setStatusTip(tr("Time-stretch playback to slow it down without changing pitch"));
874
    connect(slowAction, SIGNAL(triggered()), this, SLOT(slowDownPlayback()));
875
    connect(this, SIGNAL(canSlowDownPlayback(bool)), slowAction, SLOT(setEnabled(bool)));
876

    
877
    QAction *normalAction = menu->addAction(tr("Restore Normal Speed"));
878
    normalAction->setShortcut(tr("Ctrl+Home"));
879
    normalAction->setStatusTip(tr("Restore non-time-stretched playback"));
880
    connect(normalAction, SIGNAL(triggered()), this, SLOT(restoreNormalPlayback()));
881
    connect(this, SIGNAL(canChangePlaybackSpeed(bool)), normalAction, SLOT(setEnabled(bool)));
882

    
883
    m_keyReference->registerShortcut(fastAction);
884
    m_keyReference->registerShortcut(slowAction);
885
    m_keyReference->registerShortcut(normalAction);
886

    
887
    m_rightButtonPlaybackMenu->addAction(fastAction);
888
    m_rightButtonPlaybackMenu->addAction(slowAction);
889
    m_rightButtonPlaybackMenu->addAction(normalAction);
890
/*
891
    toolbar = addToolBar(tr("Edit Toolbar"));
892
    CommandHistory::getInstance()->registerToolbar(toolbar);
893
*/
894

    
895
    Pane::registerShortcuts(*m_keyReference);
896
}
897

    
898
void
899
MainWindow::updateMenuStates()
900
{
901
    MainWindowBase::updateMenuStates();
902

    
903
    Pane *currentPane = 0;
904
    Layer *currentLayer = 0;
905

    
906
    if (m_paneStack) currentPane = m_paneStack->getCurrentPane();
907
    if (currentPane) currentLayer = currentPane->getSelectedLayer();
908

    
909
    bool haveCurrentPane =
910
        (currentPane != 0);
911
    bool haveCurrentLayer =
912
        (haveCurrentPane &&
913
         (currentLayer != 0));
914
    bool haveSelection = 
915
        (m_viewManager &&
916
         !m_viewManager->getSelections().empty());
917
    bool haveCurrentEditableLayer =
918
        (haveCurrentLayer &&
919
         currentLayer->isLayerEditable());
920
    bool haveCurrentTimeInstantsLayer = 
921
        (haveCurrentLayer &&
922
         dynamic_cast<TimeInstantLayer *>(currentLayer));
923
    bool haveCurrentTimeValueLayer = 
924
        (haveCurrentLayer &&
925
         dynamic_cast<TimeValueLayer *>(currentLayer));
926

    
927
    emit canChangePlaybackSpeed(true);
928
    int v = m_playSpeed->value();
929
    emit canSpeedUpPlayback(v < m_playSpeed->maximum());
930
    emit canSlowDownPlayback(v > m_playSpeed->minimum());
931

    
932
    if (m_ffwdAction && m_rwdAction) {
933
        if (haveCurrentTimeInstantsLayer) {
934
            m_ffwdAction->setText(tr("Fast Forward to Next Instant"));
935
            m_ffwdAction->setStatusTip(tr("Fast forward to the next time instant in the current layer"));
936
            m_rwdAction->setText(tr("Rewind to Previous Instant"));
937
            m_rwdAction->setStatusTip(tr("Rewind to the previous time instant in the current layer"));
938
        } else if (haveCurrentTimeValueLayer) {
939
            m_ffwdAction->setText(tr("Fast Forward to Next Point"));
940
            m_ffwdAction->setStatusTip(tr("Fast forward to the next point in the current layer"));
941
            m_rwdAction->setText(tr("Rewind to Previous Point"));
942
            m_rwdAction->setStatusTip(tr("Rewind to the previous point in the current layer"));
943
        } else {
944
            m_ffwdAction->setText(tr("Fast Forward"));
945
            m_ffwdAction->setStatusTip(tr("Fast forward"));
946
            m_rwdAction->setText(tr("Rewind"));
947
            m_rwdAction->setStatusTip(tr("Rewind"));
948
        }
949
    }
950
}
951

    
952
void
953
MainWindow::updateDescriptionLabel()
954
{
955
    if (!getMainModel()) {
956
        m_descriptionLabel->setText(tr("No audio file loaded."));
957
        return;
958
    }
959

    
960
    QString description;
961

    
962
    size_t ssr = getMainModel()->getSampleRate();
963
    size_t tsr = ssr;
964
    if (m_playSource) tsr = m_playSource->getTargetSampleRate();
965

    
966
    if (ssr != tsr) {
967
        description = tr("%1Hz (resampling to %2Hz)").arg(ssr).arg(tsr);
968
    } else {
969
        description = QString("%1Hz").arg(ssr);
970
    }
971

    
972
    description = QString("%1 - %2")
973
        .arg(RealTime::frame2RealTime(getMainModel()->getEndFrame(), ssr)
974
             .toText(false).c_str())
975
        .arg(description);
976

    
977
    m_descriptionLabel->setText(description);
978
}
979

    
980
void
981
MainWindow::documentModified()
982
{
983
    //!!!
984
    MainWindowBase::documentModified();
985
}
986

    
987
void
988
MainWindow::documentRestored()
989
{
990
    //!!!
991
    MainWindowBase::documentRestored();
992
}
993

    
994
void
995
MainWindow::newSession()
996
{
997
    if (!checkSaveModified()) return;
998

    
999
    closeSession();
1000
    createDocument();
1001
    m_document->setAutoAlignment(true);
1002

    
1003
    Pane *pane;
1004
    Layer *waveform;
1005

    
1006
    for (int i = 0; i < 2; ++i) {
1007
        pane = m_paneStack->addPane();
1008
        pane->setFollowGlobalPan(false);
1009
        connect(pane, SIGNAL(contextHelpChanged(const QString &)),
1010
                this, SLOT(contextHelpChanged(const QString &)));
1011
        waveform = m_document->createMainModelLayer(LayerFactory::Waveform);
1012
        m_document->addLayerToView(pane, waveform);
1013
        m_overview->registerView(pane);
1014
    }
1015

    
1016
    CommandHistory::getInstance()->clear();
1017
    CommandHistory::getInstance()->documentSaved();
1018
    documentRestored();
1019
    updateMenuStates();
1020
}
1021

    
1022
void
1023
MainWindow::closeSession()
1024
{
1025
    if (!checkSaveModified()) return;
1026

    
1027
    while (m_paneStack->getPaneCount() > 0) {
1028

    
1029
        Pane *pane = m_paneStack->getPane(m_paneStack->getPaneCount() - 1);
1030

    
1031
        while (pane->getLayerCount() > 0) {
1032
            m_document->removeLayerFromView
1033
                (pane, pane->getLayer(pane->getLayerCount() - 1));
1034
        }
1035

    
1036
        m_overview->unregisterView(pane);
1037
        m_paneStack->deletePane(pane);
1038
    }
1039

    
1040
    while (m_paneStack->getHiddenPaneCount() > 0) {
1041

    
1042
        Pane *pane = m_paneStack->getHiddenPane
1043
            (m_paneStack->getHiddenPaneCount() - 1);
1044

    
1045
        while (pane->getLayerCount() > 0) {
1046
            m_document->removeLayerFromView
1047
                (pane, pane->getLayer(pane->getLayerCount() - 1));
1048
        }
1049

    
1050
        m_overview->unregisterView(pane);
1051
        m_paneStack->deletePane(pane);
1052
    }
1053

    
1054
    delete m_document;
1055
    m_document = 0;
1056
    m_viewManager->clearSelections();
1057
    m_timeRulerLayer = 0; // document owned this
1058

    
1059
    m_sessionFile = "";
1060
    setWindowTitle(QApplication::applicationName());
1061

    
1062
    CommandHistory::getInstance()->clear();
1063
    CommandHistory::getInstance()->documentSaved();
1064
    documentRestored();
1065
}
1066

    
1067
void
1068
MainWindow::openFile()
1069
{
1070
    QString orig = m_audioFile;
1071
    if (orig == "") orig = ".";
1072
    else orig = QFileInfo(orig).absoluteDir().canonicalPath();
1073

    
1074
    QString path = getOpenFileName(FileFinder::AnyFile);
1075

    
1076
    if (path.isEmpty()) return;
1077

    
1078
    FileOpenStatus status = open(path, CreateAdditionalModel);
1079

    
1080
    if (status == FileOpenFailed) {
1081
        QMessageBox::critical(this, tr("Failed to open file"),
1082
                              tr("<b>File open failed</b><p>File \"%1\" could not be opened").arg(path));
1083
    } else if (status == FileOpenWrongMode) {
1084
        QMessageBox::critical(this, tr("Failed to open file"),
1085
                              tr("<b>Audio required</b><p>Please load at least one audio file before importing annotation data"));
1086
    } else {
1087
        configureNewPane(m_paneStack->getCurrentPane());
1088
    }
1089
}
1090

    
1091
void
1092
MainWindow::openLocation()
1093
{
1094
    QSettings settings;
1095
    settings.beginGroup("MainWindow");
1096
    QString lastLocation = settings.value("lastremote", "").toString();
1097

    
1098
    bool ok = false;
1099
    QString text = QInputDialog::getText
1100
        (this, tr("Open Location"),
1101
         tr("Please enter the URL of the location to open:"),
1102
         QLineEdit::Normal, lastLocation, &ok);
1103

    
1104
    if (!ok) return;
1105

    
1106
    settings.setValue("lastremote", text);
1107

    
1108
    if (text.isEmpty()) return;
1109

    
1110
    FileOpenStatus status = open(text, CreateAdditionalModel);
1111

    
1112
    if (status == FileOpenFailed) {
1113
        QMessageBox::critical(this, tr("Failed to open location"),
1114
                              tr("<b>Open failed</b><p>URL \"%1\" could not be opened").arg(text));
1115
    } else if (status == FileOpenWrongMode) {
1116
        QMessageBox::critical(this, tr("Failed to open location"),
1117
                              tr("<b>Audio required</b><p>Please load at least one audio file before importing annotation data"));
1118
    } else {
1119
        configureNewPane(m_paneStack->getCurrentPane());
1120
    }
1121
}
1122

    
1123
void
1124
MainWindow::openRecentFile()
1125
{
1126
    QObject *obj = sender();
1127
    QAction *action = dynamic_cast<QAction *>(obj);
1128
    
1129
    if (!action) {
1130
        std::cerr << "WARNING: MainWindow::openRecentFile: sender is not an action"
1131
                  << std::endl;
1132
        return;
1133
    }
1134

    
1135
    QString path = action->text();
1136
    if (path == "") return;
1137

    
1138
    FileOpenStatus status = open(path, CreateAdditionalModel);
1139

    
1140
    if (status == FileOpenFailed) {
1141
        QMessageBox::critical(this, tr("Failed to open location"),
1142
                              tr("<b>Open failed</b><p>File or URL \"%1\" could not be opened").arg(path));
1143
    } else if (status == FileOpenWrongMode) {
1144
        QMessageBox::critical(this, tr("Failed to open location"),
1145
                              tr("<b>Audio required</b><p>Please load at least one audio file before importing annotation data"));
1146
    } else {
1147
        configureNewPane(m_paneStack->getCurrentPane());
1148
    }
1149
}
1150

    
1151
bool
1152
MainWindow::selectExistingModeLayer(Pane *pane, QString name)
1153
{   
1154
    // return false if we do not need to go on and create a new layer
1155
    // -- either because a suitable hidden one has been dredged up, or
1156
    // because no layer that needed replacing was found in this pane
1157

    
1158
    if (!m_document) return false;
1159

    
1160
    bool required = false;
1161

    
1162
    for (int i = 0; i < pane->getLayerCount(); ++i) {
1163
        
1164
        Layer *layer = pane->getLayer(i);
1165
        if (!layer) continue;
1166
        
1167
        Model *lm = layer->getModel();
1168
        while (lm && lm->getSourceModel()) lm = lm->getSourceModel();
1169
        if (dynamic_cast<WaveFileModel *>(lm)) {
1170
            if (lm == m_document->getMainModel()) {
1171
                required = true;
1172
                break;
1173
            }
1174
        }
1175
    }
1176
    
1177
    if (!required) return false;
1178
        
1179
    for (int i = 0; i < pane->getLayerCount(); ++i) {
1180
        
1181
        Layer *layer = pane->getLayer(i);
1182
        if (!layer) continue;
1183
        
1184
        QString ln = layer->objectName();
1185
        if (ln != name) {
1186
            m_hiddenLayers[pane].insert(layer);
1187
            m_document->removeLayerFromView(pane, layer);
1188
            continue;
1189
        }
1190
    }
1191

    
1192
    LayerSet &ls = m_hiddenLayers[pane];
1193
    bool found = false;
1194
    for (LayerSet::iterator i = ls.begin(); i != ls.end(); ++i) {
1195
        if ((*i)->objectName() == name) {
1196
            m_document->addLayerToView(pane, *i);
1197
            ls.erase(i);
1198
            found = true;
1199
            break;
1200
        }
1201
    }
1202

    
1203
    return !found;
1204
}
1205

    
1206
void
1207
MainWindow::addSegmentation()
1208
{
1209
    QObject *s = sender();
1210
    QAction *action = dynamic_cast<QAction *>(s);
1211
    
1212
    if (!action) {
1213
        std::cerr << "WARNING: MainWindow::addSegmentation: sender is not an action"
1214
                  << std::endl;
1215
        return;
1216
    }
1217

    
1218
    TransformActionMap::iterator i = m_segmenterActions.find(action);
1219
    if (i == m_segmenterActions.end()) return;
1220

    
1221
    // We always ask for configuration, even if the plugin isn't
1222
    // supposed to be configurable, because we need to let the user
1223
    // change the execution context (block size etc).
1224

    
1225
    QString transformId = i->second;
1226

    
1227
    Transform transform = TransformFactory::getInstance()->
1228
        getDefaultTransformFor(transformId);
1229

    
1230
    Model *defaultInputModel = m_document->getMainModel();
1231
    std::vector<Model *> candidateInputModels;
1232
    candidateInputModels.push_back(defaultInputModel);
1233

    
1234
    ModelTransformer::Input input = ModelTransformerFactory::getInstance()->
1235
        getConfigurationForTransform
1236
        (transform,
1237
         candidateInputModels,
1238
         defaultInputModel,
1239
         m_playSource,
1240
         0,
1241
         0);
1242

    
1243
    if (!input.getModel()) return;
1244

    
1245
//    std::cerr << "MainWindow::addLayer: Input model is " << input.getModel() << " \"" << input.getModel()->objectName().toStdString() << "\"" << std::endl << "transform:" << std::endl << transform.toXmlString().toStdString() << std::endl;
1246

    
1247
    Layer *newLayer = m_document->createDerivedLayer(transform, input);
1248

    
1249
    if (newLayer) {
1250

    
1251
        AddPaneCommand *command = new AddPaneCommand(this);
1252
        CommandHistory::getInstance()->addCommand(command);
1253

    
1254
        Pane *pane = command->getPane();
1255
        if (!pane) return;
1256

    
1257
        // Set the source model to NULL to avoid us treating it as a
1258
        // different visualisation for the main model and replacing it
1259
        // when one of the visualisation mode toggles is activated
1260
        newLayer->getModel()->setSourceModel(0);
1261

    
1262
        m_document->addLayerToView(pane, newLayer);
1263
        m_document->setChannel(newLayer, input.getChannel());
1264
//        m_paneStack->setCurrentLayer(pane, newLayer);
1265
    }
1266

    
1267
    updateMenuStates();
1268
}
1269

    
1270
void
1271
MainWindow::findSegmentationTransforms()
1272
{
1273
    m_segmentationTransforms.clear();
1274
    TransformList all = 
1275
        TransformFactory::getInstance()->getAllTransformDescriptions();
1276
    for (TransformList::iterator i = all.begin(); i != all.end(); ++i) {
1277
        if (i->type != Transform::FeatureExtraction) continue;
1278
        Transform t;
1279
        t.setIdentifier(i->identifier);
1280
        QString pluginId = t.getPluginIdentifier();
1281
        QString outputId = t.getOutput();
1282
        PluginRDFDescription desc(pluginId);
1283
        if (!desc.haveDescription()) continue;
1284
        QString feature = desc.getOutputEventTypeURI(outputId);
1285
        if (feature == "") continue;
1286
        std::cerr << "Feature: " << feature.toStdString() << std::endl;
1287
        //!!! This is grotesque
1288
        if (feature.endsWith("Segment") || feature.endsWith("Change")) {
1289
            m_segmentationTransforms.push_back(*i);
1290
        }
1291
    }
1292
    std::cerr << "MainWindow::findSegmentationTransforms: Found "
1293
              << m_segmentationTransforms.size() << " transforms" << std::endl;
1294
}
1295

    
1296
void
1297
MainWindow::curveModeSelected()
1298
{
1299
    QString name = tr("Curve");
1300

    
1301
    for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
1302

    
1303
        Pane *pane = m_paneStack->getPane(i);
1304
        if (!pane) continue;
1305

    
1306
        if (!selectExistingModeLayer(pane, name)) continue;
1307
        Model *model = m_document->getMainModel();
1308
        if (!model) continue;
1309

    
1310
        TransformId id = "vamp:qm-vamp-plugins:qm-onsetdetector:detection_fn";
1311
        TransformFactory *tf = TransformFactory::getInstance();
1312

    
1313
        if (tf->haveTransform(id)) {
1314

    
1315
            Transform transform = tf->getDefaultTransformFor
1316
                (id, model->getSampleRate());
1317

    
1318
            transform.setStepSize(1024);
1319
            transform.setBlockSize(2048);
1320

    
1321
            ModelTransformer::Input input(model, -1);
1322

    
1323
//!!! no equivalent for this yet            context.updates = false;
1324

    
1325
            Layer *newLayer = m_document->createDerivedLayer
1326
                (transform, model);
1327

    
1328
            if (newLayer) {
1329
                newLayer->setObjectName(name);
1330
                m_document->addLayerToView(pane, newLayer);
1331
                m_paneStack->setCurrentLayer(pane, newLayer);
1332
            }
1333
            
1334
        } else {
1335
            std::cerr << "No QM onset detector plugin available" << std::endl;
1336
        }
1337
    }
1338

    
1339
    m_displayMode = CurveMode;
1340
}
1341

    
1342
void
1343
MainWindow::waveformModeSelected()
1344
{
1345
    QString name = tr("Waveform");
1346

    
1347
    for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
1348

    
1349
        Pane *pane = m_paneStack->getPane(i);
1350
        if (!pane) continue;
1351

    
1352
        if (!selectExistingModeLayer(pane, name)) continue;
1353
        Model *model = m_document->getMainModel();
1354
        if (!model) continue;
1355

    
1356
        Layer *newLayer = m_document->createLayer(LayerFactory::Waveform);
1357
        newLayer->setObjectName(name);
1358
        m_document->setModel(newLayer, model);
1359
        m_document->addLayerToView(pane, newLayer);
1360
        m_paneStack->setCurrentLayer(pane, newLayer);
1361
    }
1362

    
1363
    m_displayMode = WaveformMode;
1364
}
1365

    
1366
void
1367
MainWindow::spectrogramModeSelected()
1368
{
1369
    QString name = tr("Spectrogram");
1370

    
1371
    for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
1372

    
1373
        Pane *pane = m_paneStack->getPane(i);
1374
        if (!pane) continue;
1375

    
1376
        if (!selectExistingModeLayer(pane, name)) continue;
1377
        Model *model = m_document->getMainModel();
1378
        if (!model) continue;
1379

    
1380
        Layer *newLayer = m_document->createLayer(LayerFactory::Spectrogram);
1381
        newLayer->setObjectName(name);
1382
        m_document->setModel(newLayer, model);
1383
        m_document->addLayerToView(pane, newLayer);
1384
        m_paneStack->setCurrentLayer(pane, newLayer);
1385
    }
1386

    
1387
    m_displayMode = SpectrogramMode;
1388
}
1389

    
1390
void
1391
MainWindow::melodogramModeSelected()
1392
{
1393
    QString name = tr("Melodic Range Spectrogram");
1394

    
1395
    for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
1396

    
1397
        Pane *pane = m_paneStack->getPane(i);
1398
        if (!pane) continue;
1399

    
1400
        if (!selectExistingModeLayer(pane, name)) continue;
1401
        Model *model = m_document->getMainModel();
1402
        if (!model) continue;
1403

    
1404
        Layer *newLayer = m_document->createLayer
1405
            (LayerFactory::MelodicRangeSpectrogram);
1406
        newLayer->setObjectName(name);
1407
        m_document->setModel(newLayer, model);
1408
        m_document->addLayerToView(pane, newLayer);
1409
        m_paneStack->setCurrentLayer(pane, newLayer);
1410
    }
1411

    
1412
    m_displayMode = MelodogramMode;
1413
}
1414

    
1415
void
1416
MainWindow::reselectMode()
1417
{
1418
    switch (m_displayMode) {
1419
    case CurveMode: curveModeSelected(); break;
1420
    case WaveformMode: waveformModeSelected(); break;
1421
    case SpectrogramMode: spectrogramModeSelected(); break;
1422
    case MelodogramMode: melodogramModeSelected(); break;
1423
    }
1424
}
1425

    
1426
void
1427
MainWindow::paneAdded(Pane *pane)
1428
{
1429
    pane->setPlaybackFollow(PlaybackScrollContinuous);
1430
    m_paneStack->sizePanesEqually();
1431
    if (m_overview) m_overview->registerView(pane);
1432
}    
1433

    
1434
void
1435
MainWindow::paneHidden(Pane *pane)
1436
{
1437
    if (m_overview) m_overview->unregisterView(pane); 
1438
}    
1439

    
1440
void
1441
MainWindow::paneAboutToBeDeleted(Pane *pane)
1442
{
1443
    if (m_overview) m_overview->unregisterView(pane); 
1444
}    
1445

    
1446
void
1447
MainWindow::paneDropAccepted(Pane *pane, QStringList uriList)
1448
{
1449
//    if (pane) m_paneStack->setCurrentPane(pane);
1450

    
1451
    for (QStringList::iterator i = uriList.begin(); i != uriList.end(); ++i) {
1452

    
1453
        FileOpenStatus status = open(*i, CreateAdditionalModel);
1454

    
1455
        if (status == FileOpenFailed) {
1456
            QMessageBox::critical(this, tr("Failed to open dropped URL"),
1457
                                  tr("<b>Open failed</b><p>Dropped URL \"%1\" could not be opened").arg(*i));
1458
        } else if (status == FileOpenWrongMode) {
1459
            QMessageBox::critical(this, tr("Failed to open dropped URL"),
1460
                                  tr("<b>Audio required</b><p>Please load at least one audio file before importing annotation data"));
1461
        } else {
1462
            configureNewPane(m_paneStack->getCurrentPane());
1463
        }
1464
    }
1465
}
1466

    
1467
void
1468
MainWindow::paneDropAccepted(Pane *pane, QString text)
1469
{
1470
    if (pane) m_paneStack->setCurrentPane(pane);
1471

    
1472
    QUrl testUrl(text);
1473
    if (testUrl.scheme() == "file" || 
1474
        testUrl.scheme() == "http" || 
1475
        testUrl.scheme() == "ftp") {
1476
        QStringList list;
1477
        list.push_back(text);
1478
        paneDropAccepted(pane, list);
1479
        return;
1480
    }
1481

    
1482
    //!!! open as text -- but by importing as if a CSV, or just adding
1483
    //to a text layer?
1484
}
1485

    
1486
void
1487
MainWindow::configureNewPane(Pane *pane)
1488
{
1489
    std::cerr << "MainWindow::configureNewPane(" << pane << ")" << std::endl;
1490

    
1491
    if (!pane) return;
1492

    
1493
    Layer *waveformLayer = 0;
1494

    
1495
    for (int i = 0; i < pane->getLayerCount(); ++i) {
1496
        Layer *layer = pane->getLayer(i);
1497
        if (!layer) continue;
1498
        if (dynamic_cast<WaveformLayer *>(layer)) waveformLayer = layer;
1499
        if (dynamic_cast<TimeValueLayer *>(layer)) return;
1500
    }
1501
    if (!waveformLayer) return;
1502

    
1503
    waveformLayer->setObjectName(tr("Waveform"));
1504

    
1505
    zoomToFit();
1506
    reselectMode();
1507
}
1508

    
1509
void
1510
MainWindow::closeEvent(QCloseEvent *e)
1511
{
1512
//    std::cerr << "MainWindow::closeEvent" << std::endl;
1513

    
1514
    if (m_openingAudioFile) {
1515
//        std::cerr << "Busy - ignoring close event" << std::endl;
1516
        e->ignore();
1517
        return;
1518
    }
1519

    
1520
    if (!m_abandoning && !checkSaveModified()) {
1521
//        std::cerr << "Ignoring close event" << std::endl;
1522
        e->ignore();
1523
        return;
1524
    }
1525

    
1526
    QSettings settings;
1527
    settings.beginGroup("MainWindow");
1528
    settings.setValue("size", size());
1529
    settings.setValue("position", pos());
1530
    settings.endGroup();
1531

    
1532
    delete m_keyReference;
1533
    m_keyReference = 0;
1534

    
1535
    if (m_preferencesDialog &&
1536
        m_preferencesDialog->isVisible()) {
1537
        closeSession(); // otherwise we'll have to wait for prefs changes
1538
        m_preferencesDialog->applicationClosing(false);
1539
    }
1540

    
1541
    if (m_layerTreeView &&
1542
        m_layerTreeView->isVisible()) {
1543
        delete m_layerTreeView;
1544
    }
1545

    
1546
    closeSession();
1547

    
1548
    e->accept();
1549
    return;
1550
}
1551

    
1552
bool
1553
MainWindow::commitData(bool mayAskUser)
1554
{
1555
    if (mayAskUser) {
1556
        bool rv = checkSaveModified();
1557
        if (rv) {
1558
            if (m_preferencesDialog &&
1559
                m_preferencesDialog->isVisible()) {
1560
                m_preferencesDialog->applicationClosing(false);
1561
            }
1562
        }
1563
        return rv;
1564
    } else {
1565
        if (m_preferencesDialog &&
1566
            m_preferencesDialog->isVisible()) {
1567
            m_preferencesDialog->applicationClosing(true);
1568
        }
1569
        if (!m_documentModified) return true;
1570

    
1571
        // If we can't check with the user first, then we can't save
1572
        // to the original session file (even if we have it) -- have
1573
        // to use a temporary file
1574

    
1575
        QString svDirBase = ".sv1";
1576
        QString svDir = QDir::home().filePath(svDirBase);
1577

    
1578
        if (!QFileInfo(svDir).exists()) {
1579
            if (!QDir::home().mkdir(svDirBase)) return false;
1580
        } else {
1581
            if (!QFileInfo(svDir).isDir()) return false;
1582
        }
1583
        
1584
        // This name doesn't have to be unguessable
1585
#ifndef _WIN32
1586
        QString fname = QString("tmp-%1-%2.sv")
1587
            .arg(QDateTime::currentDateTime().toString("yyyyMMddhhmmsszzz"))
1588
            .arg(QProcess().pid());
1589
#else
1590
        QString fname = QString("tmp-%1.sv")
1591
            .arg(QDateTime::currentDateTime().toString("yyyyMMddhhmmsszzz"));
1592
#endif
1593
        QString fpath = QDir(svDir).filePath(fname);
1594
        if (saveSessionFile(fpath)) {
1595
            m_recentFiles.addFile(fpath);
1596
            return true;
1597
        } else {
1598
            return false;
1599
        }
1600
    }
1601
}
1602

    
1603
bool
1604
MainWindow::checkSaveModified()
1605
{
1606
    // Called before some destructive operation (e.g. new session,
1607
    // exit program).  Return true if we can safely proceed, false to
1608
    // cancel.
1609

    
1610
    if (!m_documentModified) return true;
1611

    
1612
    int button = 
1613
        QMessageBox::warning(this,
1614
                             tr("Session modified"),
1615
                             tr("The current session has been modified.\nDo you want to save it?"),
1616
                             QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
1617
                             QMessageBox::Yes);
1618

    
1619
    if (button == QMessageBox::Yes) {
1620
        saveSession();
1621
        if (m_documentModified) { // save failed -- don't proceed!
1622
            return false;
1623
        } else {
1624
            return true; // saved, so it's safe to continue now
1625
        }
1626
    } else if (button == QMessageBox::No) {
1627
        m_documentModified = false; // so we know to abandon it
1628
        return true;
1629
    }
1630

    
1631
    // else cancel
1632
    return false;
1633
}
1634

    
1635
void
1636
MainWindow::saveSession()
1637
{
1638
    if (m_sessionFile != "") {
1639
        if (!saveSessionFile(m_sessionFile)) {
1640
            QMessageBox::critical(this, tr("Failed to save file"),
1641
                                  tr("Session file \"%1\" could not be saved.").arg(m_sessionFile));
1642
        } else {
1643
            CommandHistory::getInstance()->documentSaved();
1644
            documentRestored();
1645
        }
1646
    } else {
1647
        saveSessionAs();
1648
    }
1649
}
1650

    
1651
void
1652
MainWindow::saveSessionAs()
1653
{
1654
    QString orig = m_audioFile;
1655
    if (orig == "") orig = ".";
1656
    else orig = QFileInfo(orig).absoluteDir().canonicalPath();
1657

    
1658
    QString path = getSaveFileName(FileFinder::SessionFile);
1659

    
1660
    if (path == "") return;
1661

    
1662
    if (!saveSessionFile(path)) {
1663
        QMessageBox::critical(this, tr("Failed to save file"),
1664
                              tr("Session file \"%1\" could not be saved.").arg(path));
1665
    } else {
1666
        setWindowTitle(tr("Vect: %1")
1667
                       .arg(QFileInfo(path).fileName()));
1668
        m_sessionFile = path;
1669
        CommandHistory::getInstance()->documentSaved();
1670
        documentRestored();
1671
        m_recentFiles.addFile(path);
1672
    }
1673
}
1674

    
1675
void
1676
MainWindow::preferenceChanged(PropertyContainer::PropertyName name)
1677
{
1678
    MainWindowBase::preferenceChanged(name);
1679

    
1680
    if (name == "Background Mode" && m_viewManager) {
1681
        if (m_viewManager->getGlobalDarkBackground()) {
1682
            m_panLayer->setBaseColour
1683
                (ColourDatabase::getInstance()->getColourIndex(tr("Bright Green")));
1684
        } else {
1685
            m_panLayer->setBaseColour
1686
                (ColourDatabase::getInstance()->getColourIndex(tr("Green")));
1687
        }      
1688
    }    
1689
}
1690

    
1691
void
1692
MainWindow::renameCurrentLayer()
1693
{
1694
    Pane *pane = m_paneStack->getCurrentPane();
1695
    if (pane) {
1696
        Layer *layer = pane->getSelectedLayer();
1697
        if (layer) {
1698
            bool ok = false;
1699
            QString newName = QInputDialog::getText
1700
                (this, tr("Rename Layer"),
1701
                 tr("New name for this layer:"),
1702
                 QLineEdit::Normal, layer->objectName(), &ok);
1703
            if (ok) {
1704
                layer->setObjectName(newName);
1705
            }
1706
        }
1707
    }
1708
}
1709

    
1710
void
1711
MainWindow::playSpeedChanged(int position)
1712
{
1713
    PlaySpeedRangeMapper mapper(0, 200);
1714

    
1715
    float percent = m_playSpeed->mappedValue();
1716
    float factor = mapper.getFactorForValue(percent);
1717

    
1718
    std::cerr << "speed = " << position << " percent = " << percent << " factor = " << factor << std::endl;
1719

    
1720
    bool something = (position != 100);
1721

    
1722
    int pc = lrintf(percent);
1723

    
1724
    if (!something) {
1725
        contextHelpChanged(tr("Playback speed: Normal"));
1726
    } else {
1727
        contextHelpChanged(tr("Playback speed: %1%2%")
1728
                           .arg(position > 100 ? "+" : "")
1729
                           .arg(pc));
1730
    }
1731

    
1732
    m_playSource->setTimeStretch(factor);
1733

    
1734
    updateMenuStates();
1735
}
1736

    
1737
void
1738
MainWindow::playSharpenToggled()
1739
{
1740
    QSettings settings;
1741
    settings.beginGroup("MainWindow");
1742
    settings.setValue("playsharpen", m_playSharpen->isChecked());
1743
    settings.endGroup();
1744

    
1745
    playSpeedChanged(m_playSpeed->value());
1746
}
1747

    
1748
void
1749
MainWindow::playMonoToggled()
1750
{
1751
    QSettings settings;
1752
    settings.beginGroup("MainWindow");
1753
    settings.setValue("playmono", m_playMono->isChecked());
1754
    settings.endGroup();
1755

    
1756
    playSpeedChanged(m_playSpeed->value());
1757
}    
1758

    
1759
void
1760
MainWindow::speedUpPlayback()
1761
{
1762
    int value = m_playSpeed->value();
1763
    value = value + m_playSpeed->pageStep();
1764
    if (value > m_playSpeed->maximum()) value = m_playSpeed->maximum();
1765
    m_playSpeed->setValue(value);
1766
}
1767

    
1768
void
1769
MainWindow::slowDownPlayback()
1770
{
1771
    int value = m_playSpeed->value();
1772
    value = value - m_playSpeed->pageStep();
1773
    if (value < m_playSpeed->minimum()) value = m_playSpeed->minimum();
1774
    m_playSpeed->setValue(value);
1775
}
1776

    
1777
void
1778
MainWindow::restoreNormalPlayback()
1779
{
1780
    m_playSpeed->setValue(m_playSpeed->defaultValue());
1781
}
1782

    
1783
void
1784
MainWindow::updateVisibleRangeDisplay(Pane *p) const
1785
{
1786
    if (!getMainModel() || !p) {
1787
        return;
1788
    }
1789

    
1790
    bool haveSelection = false;
1791
    size_t startFrame = 0, endFrame = 0;
1792

    
1793
    if (m_viewManager && m_viewManager->haveInProgressSelection()) {
1794

    
1795
        bool exclusive = false;
1796
        Selection s = m_viewManager->getInProgressSelection(exclusive);
1797

    
1798
        if (!s.isEmpty()) {
1799
            haveSelection = true;
1800
            startFrame = s.getStartFrame();
1801
            endFrame = s.getEndFrame();
1802
        }
1803
    }
1804

    
1805
    if (!haveSelection) {
1806
        startFrame = p->getFirstVisibleFrame();
1807
        endFrame = p->getLastVisibleFrame();
1808
    }
1809

    
1810
    RealTime start = RealTime::frame2RealTime
1811
        (startFrame, getMainModel()->getSampleRate());
1812

    
1813
    RealTime end = RealTime::frame2RealTime
1814
        (endFrame, getMainModel()->getSampleRate());
1815

    
1816
    RealTime duration = end - start;
1817

    
1818
    QString startStr, endStr, durationStr;
1819
    startStr = start.toText(true).c_str();
1820
    endStr = end.toText(true).c_str();
1821
    durationStr = duration.toText(true).c_str();
1822

    
1823
    if (haveSelection) {
1824
        m_myStatusMessage = tr("Selection: %1 to %2 (duration %3)")
1825
            .arg(startStr).arg(endStr).arg(durationStr);
1826
    } else {
1827
        m_myStatusMessage = tr("Visible: %1 to %2 (duration %3)")
1828
            .arg(startStr).arg(endStr).arg(durationStr);
1829
    }
1830

    
1831
    statusBar()->showMessage(m_myStatusMessage);
1832
}
1833

    
1834
void
1835
MainWindow::outputLevelsChanged(float left, float right)
1836
{
1837
    m_fader->setPeakLeft(left);
1838
    m_fader->setPeakRight(right);
1839
}
1840

    
1841
void
1842
MainWindow::sampleRateMismatch(size_t requested, size_t actual,
1843
                               bool willResample)
1844
{
1845
    if (!willResample) {
1846
        //!!! more helpful message needed
1847
        QMessageBox::information
1848
            (this, tr("Sample rate mismatch"),
1849
             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.")
1850
             .arg(requested).arg(actual));
1851
    }        
1852

    
1853
    updateDescriptionLabel();
1854
}
1855

    
1856
void
1857
MainWindow::audioOverloadPluginDisabled()
1858
{
1859
    QMessageBox::information
1860
        (this, tr("Audio processing overload"),
1861
         tr("<b>Overloaded</b><p>Audio effects plugin auditioning has been disabled due to a processing overload."));
1862
}
1863

    
1864
void
1865
MainWindow::audioTimeStretchMultiChannelDisabled()
1866
{
1867
    static bool shownOnce = false;
1868
    if (shownOnce) return;
1869
    QMessageBox::information
1870
        (this, tr("Audio processing overload"),
1871
         tr("<b>Overloaded</b><p>Audio playback speed processing has been reduced to a single channel, due to a processing overload."));
1872
    shownOnce = true;
1873
}
1874

    
1875
void
1876
MainWindow::layerRemoved(Layer *layer)
1877
{
1878
    MainWindowBase::layerRemoved(layer);
1879
}
1880

    
1881
void
1882
MainWindow::layerInAView(Layer *layer, bool inAView)
1883
{
1884
    MainWindowBase::layerInAView(layer, inAView);
1885
}
1886

    
1887
void
1888
MainWindow::modelAdded(Model *model)
1889
{
1890
    MainWindowBase::modelAdded(model);
1891
    DenseTimeValueModel *dtvm = dynamic_cast<DenseTimeValueModel *>(model);
1892
    if (dtvm) {
1893
        if (!model->isReady()) {
1894
            connect(dtvm, SIGNAL(ready()), this, SLOT(modelReady()));
1895
        } else {
1896
            StorageAdviser::Criteria criteria = StorageAdviser::NoCriteria;
1897
            if (dtvm == getMainModel()) {
1898
                criteria = StorageAdviser::SpeedCritical;
1899
            }
1900

    
1901
            FFTModel *fftmodel = new FFTModel
1902
                (dtvm,
1903
                 -1,
1904
                 HanningWindow,
1905
                 2048, 1024, 2048,
1906
                 false,
1907
                 criteria);
1908

    
1909
            m_fftModelMap[dtvm] = fftmodel;
1910
            fftmodel->resume();
1911
        }
1912
    }
1913
}
1914

    
1915
void
1916
MainWindow::modelReady()
1917
{
1918
    QObject *s = sender();
1919
    std::cerr << "MainWindow::modelReady(" << s << ")" << std::endl;
1920
    if (s) {
1921
        DenseTimeValueModel *dtvm = dynamic_cast<DenseTimeValueModel *>(s);
1922
        if (dtvm) {
1923
            StorageAdviser::Criteria criteria = StorageAdviser::NoCriteria;
1924
            if (dtvm == getMainModel()) {
1925
                criteria = StorageAdviser::SpeedCritical;
1926
            }
1927

    
1928
            FFTModel *fftmodel = new FFTModel
1929
                (dtvm,
1930
                 -1,
1931
                 HanningWindow,
1932
                 2048, 1024, 2048,
1933
                 false,
1934
                 criteria);
1935

    
1936
            m_fftModelMap[dtvm] = fftmodel;
1937
            fftmodel->resume();
1938
        } else {
1939
            std::cerr << "Not a DenseTimeValueModel!" << std::endl;
1940
        }
1941
    }
1942
}            
1943

    
1944
void
1945
MainWindow::modelAboutToBeDeleted(Model *model)
1946
{
1947
    if (m_fftModelMap.find(model) != m_fftModelMap.end()) {
1948
        delete m_fftModelMap[model];
1949
        m_fftModelMap.erase(model);
1950
    }
1951
    MainWindowBase::modelAboutToBeDeleted(model);
1952
}
1953

    
1954
void
1955
MainWindow::mainModelChanged(WaveFileModel *model)
1956
{
1957
    m_panLayer->setModel(model);
1958

    
1959
    MainWindowBase::mainModelChanged(model);
1960

    
1961
    if (m_playTarget) {
1962
        connect(m_fader, SIGNAL(valueChanged(float)),
1963
                m_playTarget, SLOT(setOutputGain(float)));
1964
    }
1965
}
1966

    
1967
void
1968
MainWindow::modelGenerationFailed(QString transformName, QString message)
1969
{
1970
    if (message != "") {
1971

    
1972
        QMessageBox::warning
1973
            (this,
1974
             tr("Failed to generate layer"),
1975
             tr("<b>Layer generation failed</b><p>Failed to generate derived layer.<p>The layer transform \"%1\" failed:<p>%2")
1976
             .arg(transformName).arg(message),
1977
             QMessageBox::Ok);
1978
    } else {
1979
        QMessageBox::warning
1980
            (this,
1981
             tr("Failed to generate layer"),
1982
             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.")
1983
             .arg(transformName),
1984
             QMessageBox::Ok);
1985
    }
1986
}
1987

    
1988
void
1989
MainWindow::modelGenerationWarning(QString transformName, QString message)
1990
{
1991
    QMessageBox::warning
1992
        (this, tr("Warning"), message, QMessageBox::Ok);
1993
}
1994

    
1995
void
1996
MainWindow::modelRegenerationFailed(QString layerName,
1997
                                    QString transformName, QString message)
1998
{
1999
    if (message != "") {
2000

    
2001
        QMessageBox::warning
2002
            (this,
2003
             tr("Failed to regenerate layer"),
2004
             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")
2005
             .arg(layerName).arg(transformName).arg(message),
2006
             QMessageBox::Ok);
2007
    } else {
2008
        QMessageBox::warning
2009
            (this,
2010
             tr("Failed to regenerate layer"),
2011
             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.")
2012
             .arg(layerName).arg(transformName),
2013
             QMessageBox::Ok);
2014
    }
2015
}
2016

    
2017
void
2018
MainWindow::modelRegenerationWarning(QString layerName,
2019
                                     QString transformName, QString message)
2020
{
2021
    QMessageBox::warning
2022
        (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);
2023
}
2024

    
2025
void
2026
MainWindow::alignmentFailed(QString transformName, QString message)
2027
{
2028
    QMessageBox::warning
2029
        (this,
2030
         tr("Failed to calculate alignment"),
2031
         tr("<b>Alignment calculation failed</b><p>Failed to calculate an audio alignment using transform \"%1\":<p>%2")
2032
         .arg(transformName).arg(message),
2033
         QMessageBox::Ok);
2034
}
2035

    
2036
void
2037
MainWindow::rightButtonMenuRequested(Pane *pane, QPoint position)
2038
{
2039
//    std::cerr << "MainWindow::rightButtonMenuRequested(" << pane << ", " << position.x() << ", " << position.y() << ")" << std::endl;
2040
    m_paneStack->setCurrentPane(pane);
2041
    m_rightButtonMenu->popup(position);
2042
}
2043

    
2044
void
2045
MainWindow::showLayerTree()
2046
{
2047
    if (!m_layerTreeView.isNull()) {
2048
        m_layerTreeView->show();
2049
        m_layerTreeView->raise();
2050
        return;
2051
    }
2052

    
2053
    //!!! should use an actual dialog class
2054
        
2055
    m_layerTreeView = new QTreeView();
2056
    LayerTreeModel *tree = new LayerTreeModel(m_paneStack);
2057
    m_layerTreeView->resize(500, 300); //!!!
2058
    m_layerTreeView->setModel(tree);
2059
    m_layerTreeView->expandAll();
2060
    m_layerTreeView->show();
2061
}
2062

    
2063
void
2064
MainWindow::handleOSCMessage(const OSCMessage &message)
2065
{
2066
    std::cerr << "MainWindow::handleOSCMessage: Not implemented" << std::endl;
2067
}
2068

    
2069
void
2070
MainWindow::preferences()
2071
{
2072
    if (!m_preferencesDialog.isNull()) {
2073
        m_preferencesDialog->show();
2074
        m_preferencesDialog->raise();
2075
        return;
2076
    }
2077

    
2078
    m_preferencesDialog = new PreferencesDialog(this);
2079

    
2080
    // DeleteOnClose is safe here, because m_preferencesDialog is a
2081
    // QPointer that will be zeroed when the dialog is deleted.  We
2082
    // use it in preference to leaving the dialog lying around because
2083
    // if you Cancel the dialog, it resets the preferences state
2084
    // without resetting its own widgets, so its state will be
2085
    // incorrect when next shown unless we construct it afresh
2086
    m_preferencesDialog->setAttribute(Qt::WA_DeleteOnClose);
2087

    
2088
    m_preferencesDialog->show();
2089
}
2090

    
2091
void
2092
MainWindow::mouseEnteredWidget()
2093
{
2094
    QWidget *w = dynamic_cast<QWidget *>(sender());
2095
    if (!w) return;
2096

    
2097
    if (w == m_fader) {
2098
        contextHelpChanged(tr("Adjust the master playback level"));
2099
    } else if (w == m_playSpeed) {
2100
        contextHelpChanged(tr("Adjust the master playback speed"));
2101
    } else if (w == m_playSharpen && w->isEnabled()) {
2102
        contextHelpChanged(tr("Toggle transient sharpening for playback time scaling"));
2103
    } else if (w == m_playMono && w->isEnabled()) {
2104
        contextHelpChanged(tr("Toggle mono mode for playback time scaling"));
2105
    }
2106
}
2107

    
2108
void
2109
MainWindow::mouseLeftWidget()
2110
{
2111
    contextHelpChanged("");
2112
}
2113

    
2114
void
2115
MainWindow::website()
2116
{
2117
    openHelpUrl(tr("http://www.sonicvisualiser.org/"));
2118
}
2119

    
2120
void
2121
MainWindow::help()
2122
{
2123
    openHelpUrl(tr("http://www.sonicvisualiser.org/doc/reference/1.0/en/"));
2124
}
2125

    
2126
void
2127
MainWindow::about()
2128
{
2129
    bool debug = false;
2130
    QString version = "(unknown version)";
2131

    
2132
#ifdef BUILD_DEBUG
2133
    debug = true;
2134
#endif
2135
#ifdef VECT_VERSION
2136
#ifdef SVNREV
2137
    version = tr("Release %1 : Revision %2").arg(VECT_VERSION).arg(SVNREV);
2138
#else
2139
    version = tr("Release %1").arg(VECT_VERSION);
2140
#endif
2141
#else
2142
#ifdef SVNREV
2143
    version = tr("Unreleased : Revision %1").arg(SVNREV);
2144
#endif
2145
#endif
2146

    
2147
    QString aboutText;
2148

    
2149
    aboutText += tr("<h3>About Sonic Visualiser</h3>");
2150
    aboutText += tr("<p>Sonic Visualiser is a program for viewing and exploring audio data for<br>semantic music analysis and annotation.</p>");
2151
    aboutText += tr("<p>%1 : %2 configuration</p>")
2152
        .arg(version)
2153
        .arg(debug ? tr("Debug") : tr("Release"));
2154

    
2155
#ifndef BUILD_STATIC
2156
    aboutText += tr("<br>Using Qt v%1 &copy; Trolltech AS").arg(QT_VERSION_STR);
2157
#else
2158
#ifdef QT_SHARED
2159
    aboutText += tr("<br>Using Qt v%1 &copy; Trolltech AS").arg(QT_VERSION_STR);
2160
#endif
2161
#endif
2162

    
2163
#ifdef BUILD_STATIC
2164
    aboutText += tr("<p>Statically linked");
2165
#ifndef QT_SHARED
2166
    aboutText += tr("<br>With Qt (v%1) &copy; Trolltech AS").arg(QT_VERSION_STR);
2167
#endif
2168
#ifdef HAVE_JACK
2169
#ifdef JACK_VERSION
2170
    aboutText += tr("<br>With JACK audio output (v%1) &copy; Paul Davis and Jack O'Quin").arg(JACK_VERSION);
2171
#else
2172
    aboutText += tr("<br>With JACK audio output &copy; Paul Davis and Jack O'Quin");
2173
#endif
2174
#endif
2175
#ifdef HAVE_PORTAUDIO
2176
    aboutText += tr("<br>With PortAudio audio output &copy; Ross Bencina and Phil Burk");
2177
#endif
2178
#ifdef HAVE_OGGZ
2179
#ifdef OGGZ_VERSION
2180
    aboutText += tr("<br>With Ogg file decoder (oggz v%1, fishsound v%2) &copy; CSIRO Australia").arg(OGGZ_VERSION).arg(FISHSOUND_VERSION);
2181
#else
2182
    aboutText += tr("<br>With Ogg file decoder &copy; CSIRO Australia");
2183
#endif
2184
#endif
2185
#ifdef HAVE_MAD
2186
#ifdef MAD_VERSION
2187
    aboutText += tr("<br>With MAD mp3 decoder (v%1) &copy; Underbit Technologies Inc").arg(MAD_VERSION);
2188
#else
2189
    aboutText += tr("<br>With MAD mp3 decoder &copy; Underbit Technologies Inc");
2190
#endif
2191
#endif
2192
#ifdef HAVE_SAMPLERATE
2193
#ifdef SAMPLERATE_VERSION
2194
    aboutText += tr("<br>With libsamplerate (v%1) &copy; Erik de Castro Lopo").arg(SAMPLERATE_VERSION);
2195
#else
2196
    aboutText += tr("<br>With libsamplerate &copy; Erik de Castro Lopo");
2197
#endif
2198
#endif
2199
#ifdef HAVE_SNDFILE
2200
#ifdef SNDFILE_VERSION
2201
    aboutText += tr("<br>With libsndfile (v%1) &copy; Erik de Castro Lopo").arg(SNDFILE_VERSION);
2202
#else
2203
    aboutText += tr("<br>With libsndfile &copy; Erik de Castro Lopo");
2204
#endif
2205
#endif
2206
#ifdef HAVE_FFTW3F
2207
#ifdef FFTW3_VERSION
2208
    aboutText += tr("<br>With FFTW3 (v%1) &copy; Matteo Frigo and MIT").arg(FFTW3_VERSION);
2209
#else
2210
    aboutText += tr("<br>With FFTW3 &copy; Matteo Frigo and MIT");
2211
#endif
2212
#endif
2213
#ifdef HAVE_VAMP
2214
    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);
2215
#endif
2216
    aboutText += tr("<br>With LADSPA plugin support (API v%1) &copy; Richard Furse, Paul Davis, Stefan Westerfeld").arg(LADSPA_VERSION);
2217
    aboutText += tr("<br>With DSSI plugin support (API v%1) &copy; Chris Cannam, Steve Harris, Sean Bolton").arg(DSSI_VERSION);
2218
#ifdef HAVE_LIBLO
2219
#ifdef LIBLO_VERSION
2220
    aboutText += tr("<br>With liblo Lite OSC library (v%1) &copy; Steve Harris").arg(LIBLO_VERSION);
2221
#else
2222
    aboutText += tr("<br>With liblo Lite OSC library &copy; Steve Harris").arg(LIBLO_VERSION);
2223
#endif
2224
    if (m_oscQueue && m_oscQueue->isOK()) {
2225
        aboutText += tr("<p>The OSC URL for this instance is: \"%1\"").arg(m_oscQueue->getOSCURL());
2226
    }
2227
#endif
2228
    aboutText += "</p>";
2229
#endif
2230

    
2231
    aboutText += 
2232
        "<p>Sonic Visualiser Copyright &copy; 2005 - 2007 Chris Cannam and<br>"
2233
        "Queen Mary, University of London.</p>"
2234
        "<p>This program is free software; you can redistribute it and/or<br>"
2235
        "modify it under the terms of the GNU General Public License as<br>"
2236
        "published by the Free Software Foundation; either version 2 of the<br>"
2237
        "License, or (at your option) any later version.<br>See the file "
2238
        "COPYING included with this distribution for more information.</p>";
2239
    
2240
    QMessageBox::about(this, tr("About Sonic Visualiser"), aboutText);
2241
}
2242

    
2243
void
2244
MainWindow::keyReference()
2245
{
2246
    m_keyReference->show();
2247
}
2248