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 @ 4:a88c2a0279ba

History | View | Annotate | Download (71.5 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
        // Set the source model to NULL to avoid us treating it as a
1252
        // different visualisation for the main model and replacing it
1253
        // when one of the visualisation mode toggles is activated
1254
        newLayer->getModel()->setSourceModel(0);
1255
        m_document->setChannel(newLayer, input.getChannel());
1256

    
1257
        Pane *pane = m_paneStack->insertPane(0);
1258
        m_document->addLayerToView(pane, newLayer);
1259

    
1260
        pane = m_paneStack->insertPane(m_paneStack->getPaneCount());
1261
        m_document->addLayerToView(pane, newLayer);
1262
    }
1263

    
1264
    updateMenuStates();
1265
}
1266

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

    
1293
void
1294
MainWindow::curveModeSelected()
1295
{
1296
    QString name = tr("Curve");
1297

    
1298
    for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
1299

    
1300
        Pane *pane = m_paneStack->getPane(i);
1301
        if (!pane) continue;
1302

    
1303
        if (!selectExistingModeLayer(pane, name)) continue;
1304
        Model *model = m_document->getMainModel();
1305
        if (!model) continue;
1306

    
1307
        TransformId id = "vamp:qm-vamp-plugins:qm-onsetdetector:detection_fn";
1308
        TransformFactory *tf = TransformFactory::getInstance();
1309

    
1310
        if (tf->haveTransform(id)) {
1311

    
1312
            Transform transform = tf->getDefaultTransformFor
1313
                (id, model->getSampleRate());
1314

    
1315
            transform.setStepSize(1024);
1316
            transform.setBlockSize(2048);
1317

    
1318
            ModelTransformer::Input input(model, -1);
1319

    
1320
//!!! no equivalent for this yet            context.updates = false;
1321

    
1322
            Layer *newLayer = m_document->createDerivedLayer
1323
                (transform, model);
1324

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

    
1336
    m_displayMode = CurveMode;
1337
}
1338

    
1339
void
1340
MainWindow::waveformModeSelected()
1341
{
1342
    QString name = tr("Waveform");
1343

    
1344
    for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
1345

    
1346
        Pane *pane = m_paneStack->getPane(i);
1347
        if (!pane) continue;
1348

    
1349
        if (!selectExistingModeLayer(pane, name)) continue;
1350
        Model *model = m_document->getMainModel();
1351
        if (!model) continue;
1352

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

    
1360
    m_displayMode = WaveformMode;
1361
}
1362

    
1363
void
1364
MainWindow::spectrogramModeSelected()
1365
{
1366
    QString name = tr("Spectrogram");
1367

    
1368
    for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
1369

    
1370
        Pane *pane = m_paneStack->getPane(i);
1371
        if (!pane) continue;
1372

    
1373
        if (!selectExistingModeLayer(pane, name)) continue;
1374
        Model *model = m_document->getMainModel();
1375
        if (!model) continue;
1376

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

    
1384
    m_displayMode = SpectrogramMode;
1385
}
1386

    
1387
void
1388
MainWindow::melodogramModeSelected()
1389
{
1390
    QString name = tr("Melodic Range Spectrogram");
1391

    
1392
    for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
1393

    
1394
        Pane *pane = m_paneStack->getPane(i);
1395
        if (!pane) continue;
1396

    
1397
        if (!selectExistingModeLayer(pane, name)) continue;
1398
        Model *model = m_document->getMainModel();
1399
        if (!model) continue;
1400

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

    
1409
    m_displayMode = MelodogramMode;
1410
}
1411

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

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

    
1431
void
1432
MainWindow::paneHidden(Pane *pane)
1433
{
1434
    if (m_overview) m_overview->unregisterView(pane); 
1435
}    
1436

    
1437
void
1438
MainWindow::paneAboutToBeDeleted(Pane *pane)
1439
{
1440
    if (m_overview) m_overview->unregisterView(pane); 
1441
}    
1442

    
1443
void
1444
MainWindow::paneDropAccepted(Pane *pane, QStringList uriList)
1445
{
1446
//    if (pane) m_paneStack->setCurrentPane(pane);
1447

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

    
1450
        FileOpenStatus status = open(*i, CreateAdditionalModel);
1451

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

    
1464
void
1465
MainWindow::paneDropAccepted(Pane *pane, QString text)
1466
{
1467
    if (pane) m_paneStack->setCurrentPane(pane);
1468

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

    
1479
    //!!! open as text -- but by importing as if a CSV, or just adding
1480
    //to a text layer?
1481
}
1482

    
1483
void
1484
MainWindow::configureNewPane(Pane *pane)
1485
{
1486
    std::cerr << "MainWindow::configureNewPane(" << pane << ")" << std::endl;
1487

    
1488
    if (!pane) return;
1489

    
1490
    Layer *waveformLayer = 0;
1491

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

    
1500
    waveformLayer->setObjectName(tr("Waveform"));
1501

    
1502
    zoomToFit();
1503
    reselectMode();
1504
}
1505

    
1506
void
1507
MainWindow::closeEvent(QCloseEvent *e)
1508
{
1509
//    std::cerr << "MainWindow::closeEvent" << std::endl;
1510

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

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

    
1523
    QSettings settings;
1524
    settings.beginGroup("MainWindow");
1525
    settings.setValue("size", size());
1526
    settings.setValue("position", pos());
1527
    settings.endGroup();
1528

    
1529
    delete m_keyReference;
1530
    m_keyReference = 0;
1531

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

    
1538
    if (m_layerTreeView &&
1539
        m_layerTreeView->isVisible()) {
1540
        delete m_layerTreeView;
1541
    }
1542

    
1543
    closeSession();
1544

    
1545
    e->accept();
1546
    return;
1547
}
1548

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

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

    
1572
        QString svDirBase = ".sv1";
1573
        QString svDir = QDir::home().filePath(svDirBase);
1574

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

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

    
1607
    if (!m_documentModified) return true;
1608

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

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

    
1628
    // else cancel
1629
    return false;
1630
}
1631

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

    
1648
void
1649
MainWindow::saveSessionAs()
1650
{
1651
    QString orig = m_audioFile;
1652
    if (orig == "") orig = ".";
1653
    else orig = QFileInfo(orig).absoluteDir().canonicalPath();
1654

    
1655
    QString path = getSaveFileName(FileFinder::SessionFile);
1656

    
1657
    if (path == "") return;
1658

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

    
1672
void
1673
MainWindow::preferenceChanged(PropertyContainer::PropertyName name)
1674
{
1675
    MainWindowBase::preferenceChanged(name);
1676

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

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

    
1707
void
1708
MainWindow::playSpeedChanged(int position)
1709
{
1710
    PlaySpeedRangeMapper mapper(0, 200);
1711

    
1712
    float percent = m_playSpeed->mappedValue();
1713
    float factor = mapper.getFactorForValue(percent);
1714

    
1715
    std::cerr << "speed = " << position << " percent = " << percent << " factor = " << factor << std::endl;
1716

    
1717
    bool something = (position != 100);
1718

    
1719
    int pc = lrintf(percent);
1720

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

    
1729
    m_playSource->setTimeStretch(factor);
1730

    
1731
    updateMenuStates();
1732
}
1733

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

    
1742
    playSpeedChanged(m_playSpeed->value());
1743
}
1744

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

    
1753
    playSpeedChanged(m_playSpeed->value());
1754
}    
1755

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

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

    
1774
void
1775
MainWindow::restoreNormalPlayback()
1776
{
1777
    m_playSpeed->setValue(m_playSpeed->defaultValue());
1778
}
1779

    
1780
void
1781
MainWindow::updateVisibleRangeDisplay(Pane *p) const
1782
{
1783
    if (!getMainModel() || !p) {
1784
        return;
1785
    }
1786

    
1787
    bool haveSelection = false;
1788
    size_t startFrame = 0, endFrame = 0;
1789

    
1790
    if (m_viewManager && m_viewManager->haveInProgressSelection()) {
1791

    
1792
        bool exclusive = false;
1793
        Selection s = m_viewManager->getInProgressSelection(exclusive);
1794

    
1795
        if (!s.isEmpty()) {
1796
            haveSelection = true;
1797
            startFrame = s.getStartFrame();
1798
            endFrame = s.getEndFrame();
1799
        }
1800
    }
1801

    
1802
    if (!haveSelection) {
1803
        startFrame = p->getFirstVisibleFrame();
1804
        endFrame = p->getLastVisibleFrame();
1805
    }
1806

    
1807
    RealTime start = RealTime::frame2RealTime
1808
        (startFrame, getMainModel()->getSampleRate());
1809

    
1810
    RealTime end = RealTime::frame2RealTime
1811
        (endFrame, getMainModel()->getSampleRate());
1812

    
1813
    RealTime duration = end - start;
1814

    
1815
    QString startStr, endStr, durationStr;
1816
    startStr = start.toText(true).c_str();
1817
    endStr = end.toText(true).c_str();
1818
    durationStr = duration.toText(true).c_str();
1819

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

    
1828
    statusBar()->showMessage(m_myStatusMessage);
1829
}
1830

    
1831
void
1832
MainWindow::outputLevelsChanged(float left, float right)
1833
{
1834
    m_fader->setPeakLeft(left);
1835
    m_fader->setPeakRight(right);
1836
}
1837

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

    
1850
    updateDescriptionLabel();
1851
}
1852

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

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

    
1872
void
1873
MainWindow::layerRemoved(Layer *layer)
1874
{
1875
    MainWindowBase::layerRemoved(layer);
1876
}
1877

    
1878
void
1879
MainWindow::layerInAView(Layer *layer, bool inAView)
1880
{
1881
    MainWindowBase::layerInAView(layer, inAView);
1882
}
1883

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

    
1898
            FFTModel *fftmodel = new FFTModel
1899
                (dtvm,
1900
                 -1,
1901
                 HanningWindow,
1902
                 2048, 1024, 2048,
1903
                 false,
1904
                 criteria);
1905

    
1906
            m_fftModelMap[dtvm] = fftmodel;
1907
            fftmodel->resume();
1908
        }
1909
    }
1910
}
1911

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

    
1925
            FFTModel *fftmodel = new FFTModel
1926
                (dtvm,
1927
                 -1,
1928
                 HanningWindow,
1929
                 2048, 1024, 2048,
1930
                 false,
1931
                 criteria);
1932

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

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

    
1951
void
1952
MainWindow::mainModelChanged(WaveFileModel *model)
1953
{
1954
    m_panLayer->setModel(model);
1955

    
1956
    MainWindowBase::mainModelChanged(model);
1957

    
1958
    if (m_playTarget) {
1959
        connect(m_fader, SIGNAL(valueChanged(float)),
1960
                m_playTarget, SLOT(setOutputGain(float)));
1961
    }
1962
}
1963

    
1964
void
1965
MainWindow::modelGenerationFailed(QString transformName, QString message)
1966
{
1967
    if (message != "") {
1968

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

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

    
1992
void
1993
MainWindow::modelRegenerationFailed(QString layerName,
1994
                                    QString transformName, QString message)
1995
{
1996
    if (message != "") {
1997

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

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

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

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

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

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

    
2060
void
2061
MainWindow::handleOSCMessage(const OSCMessage &message)
2062
{
2063
    std::cerr << "MainWindow::handleOSCMessage: Not implemented" << std::endl;
2064
}
2065

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

    
2075
    m_preferencesDialog = new PreferencesDialog(this);
2076

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

    
2085
    m_preferencesDialog->show();
2086
}
2087

    
2088
void
2089
MainWindow::mouseEnteredWidget()
2090
{
2091
    QWidget *w = dynamic_cast<QWidget *>(sender());
2092
    if (!w) return;
2093

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

    
2105
void
2106
MainWindow::mouseLeftWidget()
2107
{
2108
    contextHelpChanged("");
2109
}
2110

    
2111
void
2112
MainWindow::website()
2113
{
2114
    openHelpUrl(tr("http://www.sonicvisualiser.org/"));
2115
}
2116

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

    
2123
void
2124
MainWindow::about()
2125
{
2126
    bool debug = false;
2127
    QString version = "(unknown version)";
2128

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

    
2144
    QString aboutText;
2145

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

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

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

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

    
2240
void
2241
MainWindow::keyReference()
2242
{
2243
    m_keyReference->show();
2244
}
2245