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 @ 1:f78d846bf880

History | View | Annotate | Download (65.2 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

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

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

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

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

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

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

    
116

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

    
135
    StorageAdviser::setFixedRecommendation
136
        (StorageAdviser::Recommendation(StorageAdviser::UseDisc |
137
                                        StorageAdviser::ConserveSpace));
138

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

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

    
158
    Preferences::getInstance()->setResampleOnLoad(true);
159
    Preferences::getInstance()->setSpectrogramSmoothing
160
        (Preferences::SpectrogramInterpolated);
161

    
162
    QSettings settings;
163

    
164
    settings.beginGroup("LayerDefaults");
165

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

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

    
176
    settings.setValue("spectrogram",
177
                      QString("<layer channel=\"-1\" windowSize=\"2048\" windowHopLevel=\"2\"/>"));
178

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

    
182
    settings.endGroup();
183

    
184
    settings.beginGroup("MainWindow");
185
    settings.setValue("showstatusbar", false);
186
    settings.endGroup();
187

    
188
    m_viewManager->setAlignMode(true);
189
    m_viewManager->setPlaySoloMode(true);
190
    m_viewManager->setToolMode(ViewManager::NavigateMode);
191
    m_viewManager->setZoomWheelsEnabled(false);
192
    m_viewManager->setIlluminateLocalFeatures(false);
193
    m_viewManager->setShowWorkTitle(true);
194

    
195
#ifndef __APPLE__
196
    m_viewManager->setGlobalDarkBackground(true);
197
#endif
198
    
199
    QFrame *frame = new QFrame;
200
    setCentralWidget(frame);
201

    
202
    QGridLayout *layout = new QGridLayout;
203
    
204
    m_descriptionLabel = new QLabel;
205

    
206
    QScrollArea *scroll = new QScrollArea(frame);
207
    scroll->setWidgetResizable(true);
208
    scroll->setHorizontalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
209
    scroll->setFrameShape(QFrame::NoFrame);
210

    
211
    m_paneStack->setLayoutStyle(PaneStack::NoPropertyStacks);
212
    scroll->setWidget(m_paneStack);
213
    
214
    QButtonGroup *bg = new QButtonGroup;
215
    IconLoader il;
216

    
217
    QFrame *buttonFrame = new QFrame;
218
    QHBoxLayout *buttonLayout = new QHBoxLayout;
219
    buttonLayout->setSpacing(0);
220
    buttonLayout->setMargin(0);
221
    buttonFrame->setLayout(buttonLayout);
222

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

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

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

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

    
259
    layout->addWidget(buttonFrame, 1, 0);
260

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

    
274
    m_panLayer = new WaveformLayer;
275
    m_panLayer->setChannelMode(WaveformLayer::MergeChannels);
276
    m_panLayer->setAggressiveCacheing(true);
277
    m_overview->addLayer(m_panLayer);
278

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

    
287
    m_fader = new Fader(frame, false);
288
    connect(m_fader, SIGNAL(mouseEntered()), this, SLOT(mouseEnteredWidget()));
289
    connect(m_fader, SIGNAL(mouseLeft()), this, SLOT(mouseLeftWidget()));
290

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

    
308
    layout->setSpacing(4);
309
    layout->addWidget(scroll, 0, 0, 1, 6);
310
    layout->addWidget(m_overview, 1, 1);
311
    layout->addWidget(m_fader, 1, 2);
312
    layout->addWidget(m_playSpeed, 1, 3);
313

    
314
    m_paneStack->setPropertyStackMinWidth
315
        (m_fader->width() + m_playSpeed->width() +
316
         layout->spacing() * 4);
317

    
318
    layout->setColumnStretch(1, 10);
319

    
320
    frame->setLayout(layout);
321

    
322
    setupMenus();
323
    setupToolbars();
324
    setupHelpMenu();
325

    
326
    statusBar();
327

    
328
    newSession();
329
}
330

    
331
MainWindow::~MainWindow()
332
{
333
    delete m_keyReference;
334
    delete m_preferencesDialog;
335
    delete m_layerTreeView;
336
    Profiles::getInstance()->dump();
337
}
338

    
339
void
340
MainWindow::setupMenus()
341
{
342
    if (!m_mainMenusCreated) {
343
        m_rightButtonMenu = new QMenu();
344

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

    
354
    if (!m_mainMenusCreated) {
355
        CommandHistory::getInstance()->registerMenu(m_rightButtonMenu);
356
        m_rightButtonMenu->addSeparator();
357
    }
358

    
359
    setupFileMenu();
360
//    setupEditMenu();
361
    setupViewMenu();
362

    
363
    m_mainMenusCreated = true;
364
}
365

    
366
void
367
MainWindow::setupFileMenu()
368
{
369
    if (m_mainMenusCreated) return;
370

    
371
    QMenu *menu = menuBar()->addMenu(tr("&File"));
372
    menu->setTearOffEnabled(true);
373
    QToolBar *toolbar = addToolBar(tr("File Toolbar"));
374

    
375
    m_keyReference->setCategory(tr("File and Session Management"));
376

    
377
    IconLoader il;
378

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

    
389
    icon = il.load("fileopen");
390
    icon.addPixmap(il.loadPixmap("fileopen-22"));
391
    action = new QAction(icon, tr("&Add File..."), this);
392
    action->setShortcut(tr("Ctrl+O"));
393
    action->setStatusTip(tr("Add a file"));
394
    connect(action, SIGNAL(triggered()), this, SLOT(openFile()));
395
    m_keyReference->registerShortcut(action);
396
    menu->addAction(action);
397
    toolbar->addAction(action);
398

    
399
    action = new QAction(tr("Add Lo&cation..."), this);
400
    action->setShortcut(tr("Ctrl+Shift+O"));
401
    action->setStatusTip(tr("Add a file from a remote URL"));
402
    connect(action, SIGNAL(triggered()), this, SLOT(openLocation()));
403
    m_keyReference->registerShortcut(action);
404
    menu->addAction(action);
405

    
406
    menu->addSeparator();
407

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

    
429
void
430
MainWindow::setupEditMenu()
431
{
432
    if (m_mainMenusCreated) return;
433

    
434
    QMenu *menu = menuBar()->addMenu(tr("&Edit"));
435
    menu->setTearOffEnabled(true);
436
    CommandHistory::getInstance()->registerMenu(menu);
437
}
438

    
439
void
440
MainWindow::setupViewMenu()
441
{
442
    if (m_mainMenusCreated) return;
443

    
444
    IconLoader il;
445

    
446
    QAction *action = 0;
447

    
448
    m_keyReference->setCategory(tr("Panning and Navigation"));
449

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

    
484
    menu->addSeparator();
485

    
486
    m_keyReference->setCategory(tr("Zoom"));
487

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

    
512
    action = new QAction(il.load("zoom-fit"),
513
                         tr("Zoom to &Fit"), this);
514
    action->setShortcut(tr("F"));
515
    action->setStatusTip(tr("Zoom to show the whole file"));
516
    connect(action, SIGNAL(triggered()), this, SLOT(zoomToFit()));
517
    connect(this, SIGNAL(canZoom(bool)), action, SLOT(setEnabled(bool)));
518
    m_keyReference->registerShortcut(action);
519
    menu->addAction(action);
520

    
521
    menu->addSeparator();
522

    
523
    m_keyReference->setCategory(tr("Display Features"));
524

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

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

    
587
    action = new QAction(tr("Show Status &Bar"), this);
588
    action->setStatusTip(tr("Show context help information in the status bar at the bottom of the window"));
589
    connect(action, SIGNAL(triggered()), this, SLOT(toggleStatusBar()));
590
    action->setCheckable(true);
591
    action->setChecked(true);
592
    menu->addAction(action);
593

    
594
    QSettings settings;
595
    settings.beginGroup("MainWindow");
596
    bool sb = settings.value("showstatusbar", true).toBool();
597
    if (!sb) {
598
        action->setChecked(false);
599
        statusBar()->hide();
600
    }
601
    settings.endGroup();
602

    
603
    menu->addSeparator();
604

    
605
    action = new QAction(tr("Show La&yer Hierarchy"), this);
606
    action->setShortcut(tr("H"));
607
    action->setStatusTip(tr("Open a window displaying the hierarchy of panes and layers in this session"));
608
    connect(action, SIGNAL(triggered()), this, SLOT(showLayerTree()));
609
    m_keyReference->registerShortcut(action);
610
    menu->addAction(action);
611
}
612

    
613
void
614
MainWindow::setupHelpMenu()
615
{
616
    QMenu *menu = menuBar()->addMenu(tr("&Help"));
617
    menu->setTearOffEnabled(true);
618
    
619
    m_keyReference->setCategory(tr("Help"));
620

    
621
    IconLoader il;
622

    
623
    QAction *action = new QAction(il.load("help"),
624
                                  tr("&Help Reference"), this); 
625
    action->setShortcut(tr("F1"));
626
    action->setStatusTip(tr("Open the reference manual")); 
627
    connect(action, SIGNAL(triggered()), this, SLOT(help()));
628
    m_keyReference->registerShortcut(action);
629
    menu->addAction(action);
630

    
631
    action = new QAction(tr("&Key and Mouse Reference"), this);
632
    action->setShortcut(tr("F2"));
633
    action->setStatusTip(tr("Open a window showing the keystrokes you can use"));
634
    connect(action, SIGNAL(triggered()), this, SLOT(keyReference()));
635
    m_keyReference->registerShortcut(action);
636
    menu->addAction(action);
637
    
638
    action = new QAction(tr("Sonic Visualiser on the &Web"), this); 
639
    action->setStatusTip(tr("Open the Sonic Visualiser website")); 
640
    connect(action, SIGNAL(triggered()), this, SLOT(website()));
641
    menu->addAction(action);
642
    
643
    action = new QAction(tr("&About Sonic Visualiser"), this); 
644
    action->setStatusTip(tr("Show information about Sonic Visualiser")); 
645
    connect(action, SIGNAL(triggered()), this, SLOT(about()));
646
    menu->addAction(action);
647
}
648

    
649
void
650
MainWindow::setupRecentFilesMenu()
651
{
652
    m_recentFilesMenu->clear();
653
    vector<QString> files = m_recentFiles.getRecent();
654
    for (size_t i = 0; i < files.size(); ++i) {
655
        QAction *action = new QAction(files[i], this);
656
        connect(action, SIGNAL(triggered()), this, SLOT(openRecentFile()));
657
        if (i == 0) {
658
            action->setShortcut(tr("Ctrl+R"));
659
            m_keyReference->registerShortcut
660
                (tr("Re-open"),
661
                 action->shortcut(),
662
                 tr("Re-open the current or most recently opened file"));
663
        }
664
        m_recentFilesMenu->addAction(action);
665
    }
666
}
667

    
668
void
669
MainWindow::setupToolbars()
670
{
671
    m_keyReference->setCategory(tr("Playback and Transport Controls"));
672

    
673
    IconLoader il;
674

    
675
    QMenu *menu = m_playbackMenu = menuBar()->addMenu(tr("Play&back"));
676
    menu->setTearOffEnabled(true);
677
    m_rightButtonMenu->addSeparator();
678
    m_rightButtonPlaybackMenu = m_rightButtonMenu->addMenu(tr("Playback"));
679

    
680
    QToolBar *toolbar = addToolBar(tr("Playback Toolbar"));
681

    
682
    QAction *rwdStartAction = toolbar->addAction(il.load("rewind-start"),
683
                                                 tr("Rewind to Start"));
684
    rwdStartAction->setShortcut(tr("Home"));
685
    rwdStartAction->setStatusTip(tr("Rewind to the start"));
686
    connect(rwdStartAction, SIGNAL(triggered()), this, SLOT(rewindStart()));
687
    connect(this, SIGNAL(canPlay(bool)), rwdStartAction, SLOT(setEnabled(bool)));
688

    
689
    QAction *m_rwdAction = toolbar->addAction(il.load("rewind"),
690
                                              tr("Rewind"));
691
    m_rwdAction->setShortcut(tr("PgUp"));
692
    m_rwdAction->setStatusTip(tr("Rewind to the previous time instant or time ruler notch"));
693
    connect(m_rwdAction, SIGNAL(triggered()), this, SLOT(rewind()));
694
    connect(this, SIGNAL(canRewind(bool)), m_rwdAction, SLOT(setEnabled(bool)));
695

    
696
    QAction *playAction = toolbar->addAction(il.load("playpause"),
697
                                             tr("Play / Pause"));
698
    playAction->setCheckable(true);
699
    playAction->setShortcut(tr("Space"));
700
    playAction->setStatusTip(tr("Start or stop playback from the current position"));
701
    connect(playAction, SIGNAL(triggered()), this, SLOT(play()));
702
    connect(m_playSource, SIGNAL(playStatusChanged(bool)),
703
            playAction, SLOT(setChecked(bool)));
704
    connect(this, SIGNAL(canPlay(bool)), playAction, SLOT(setEnabled(bool)));
705

    
706
    m_ffwdAction = toolbar->addAction(il.load("ffwd"),
707
                                              tr("Fast Forward"));
708
    m_ffwdAction->setShortcut(tr("PgDown"));
709
    m_ffwdAction->setStatusTip(tr("Fast-forward to the next time instant or time ruler notch"));
710
    connect(m_ffwdAction, SIGNAL(triggered()), this, SLOT(ffwd()));
711
    connect(this, SIGNAL(canFfwd(bool)), m_ffwdAction, SLOT(setEnabled(bool)));
712

    
713
    QAction *ffwdEndAction = toolbar->addAction(il.load("ffwd-end"),
714
                                                tr("Fast Forward to End"));
715
    ffwdEndAction->setShortcut(tr("End"));
716
    ffwdEndAction->setStatusTip(tr("Fast-forward to the end"));
717
    connect(ffwdEndAction, SIGNAL(triggered()), this, SLOT(ffwdEnd()));
718
    connect(this, SIGNAL(canPlay(bool)), ffwdEndAction, SLOT(setEnabled(bool)));
719
/*
720
    toolbar = addToolBar(tr("Play Mode Toolbar"));
721

722
    QAction *psAction = toolbar->addAction(il.load("playselection"),
723
                                           tr("Constrain Playback to Selection"));
724
    psAction->setCheckable(true);
725
    psAction->setChecked(m_viewManager->getPlaySelectionMode());
726
    psAction->setShortcut(tr("s"));
727
    psAction->setStatusTip(tr("Constrain playback to the selected regions"));
728
    connect(m_viewManager, SIGNAL(playSelectionModeChanged(bool)),
729
            psAction, SLOT(setChecked(bool)));
730
    connect(psAction, SIGNAL(triggered()), this, SLOT(playSelectionToggled()));
731
    connect(this, SIGNAL(canPlaySelection(bool)), psAction, SLOT(setEnabled(bool)));
732

733
    QAction *plAction = toolbar->addAction(il.load("playloop"),
734
                                           tr("Loop Playback"));
735
    plAction->setCheckable(true);
736
    plAction->setChecked(m_viewManager->getPlayLoopMode());
737
    plAction->setShortcut(tr("l"));
738
    plAction->setStatusTip(tr("Loop playback"));
739
    connect(m_viewManager, SIGNAL(playLoopModeChanged(bool)),
740
            plAction, SLOT(setChecked(bool)));
741
    connect(plAction, SIGNAL(triggered()), this, SLOT(playLoopToggled()));
742
    connect(this, SIGNAL(canPlay(bool)), plAction, SLOT(setEnabled(bool)));
743

744
    QAction *soAction = toolbar->addAction(il.load("solo"),
745
                                           tr("Solo Current Pane"));
746
    soAction->setCheckable(true);
747
    soAction->setChecked(m_viewManager->getPlaySoloMode());
748
    soAction->setShortcut(tr("o"));
749
    soAction->setStatusTip(tr("Solo the current pane during playback"));
750
    connect(m_viewManager, SIGNAL(playSoloModeChanged(bool)),
751
            soAction, SLOT(setChecked(bool)));
752
    connect(soAction, SIGNAL(triggered()), this, SLOT(playSoloToggled()));
753
    connect(this, SIGNAL(canPlay(bool)), soAction, SLOT(setEnabled(bool)));
754

755
    m_keyReference->registerShortcut(psAction);
756
    m_keyReference->registerShortcut(plAction);
757
    m_keyReference->registerShortcut(soAction);
758
*/
759
    m_keyReference->registerShortcut(playAction);
760
    m_keyReference->registerShortcut(m_rwdAction);
761
    m_keyReference->registerShortcut(m_ffwdAction);
762
    m_keyReference->registerShortcut(rwdStartAction);
763
    m_keyReference->registerShortcut(ffwdEndAction);
764

    
765
/*
766
    menu->addAction(psAction);
767
    menu->addAction(plAction);
768
    menu->addAction(soAction);
769
*/
770
    menu->addAction(playAction);
771
    menu->addSeparator();
772
    menu->addAction(m_rwdAction);
773
    menu->addAction(m_ffwdAction);
774
    menu->addSeparator();
775
    menu->addAction(rwdStartAction);
776
    menu->addAction(ffwdEndAction);
777
    menu->addSeparator();
778

    
779
    m_rightButtonPlaybackMenu->addAction(playAction);
780
/*
781
    m_rightButtonPlaybackMenu->addAction(psAction);
782
    m_rightButtonPlaybackMenu->addAction(plAction);
783
    m_rightButtonPlaybackMenu->addAction(soAction);
784
*/
785
    m_rightButtonPlaybackMenu->addSeparator();
786
    m_rightButtonPlaybackMenu->addAction(m_rwdAction);
787
    m_rightButtonPlaybackMenu->addAction(m_ffwdAction);
788
    m_rightButtonPlaybackMenu->addSeparator();
789
    m_rightButtonPlaybackMenu->addAction(rwdStartAction);
790
    m_rightButtonPlaybackMenu->addAction(ffwdEndAction);
791
    m_rightButtonPlaybackMenu->addSeparator();
792

    
793
    QAction *fastAction = menu->addAction(tr("Speed Up"));
794
    fastAction->setShortcut(tr("Ctrl+PgUp"));
795
    fastAction->setStatusTip(tr("Time-stretch playback to speed it up without changing pitch"));
796
    connect(fastAction, SIGNAL(triggered()), this, SLOT(speedUpPlayback()));
797
    connect(this, SIGNAL(canSpeedUpPlayback(bool)), fastAction, SLOT(setEnabled(bool)));
798
    
799
    QAction *slowAction = menu->addAction(tr("Slow Down"));
800
    slowAction->setShortcut(tr("Ctrl+PgDown"));
801
    slowAction->setStatusTip(tr("Time-stretch playback to slow it down without changing pitch"));
802
    connect(slowAction, SIGNAL(triggered()), this, SLOT(slowDownPlayback()));
803
    connect(this, SIGNAL(canSlowDownPlayback(bool)), slowAction, SLOT(setEnabled(bool)));
804

    
805
    QAction *normalAction = menu->addAction(tr("Restore Normal Speed"));
806
    normalAction->setShortcut(tr("Ctrl+Home"));
807
    normalAction->setStatusTip(tr("Restore non-time-stretched playback"));
808
    connect(normalAction, SIGNAL(triggered()), this, SLOT(restoreNormalPlayback()));
809
    connect(this, SIGNAL(canChangePlaybackSpeed(bool)), normalAction, SLOT(setEnabled(bool)));
810

    
811
    m_keyReference->registerShortcut(fastAction);
812
    m_keyReference->registerShortcut(slowAction);
813
    m_keyReference->registerShortcut(normalAction);
814

    
815
    m_rightButtonPlaybackMenu->addAction(fastAction);
816
    m_rightButtonPlaybackMenu->addAction(slowAction);
817
    m_rightButtonPlaybackMenu->addAction(normalAction);
818
/*
819
    toolbar = addToolBar(tr("Edit Toolbar"));
820
    CommandHistory::getInstance()->registerToolbar(toolbar);
821
*/
822

    
823
    Pane::registerShortcuts(*m_keyReference);
824
}
825

    
826
void
827
MainWindow::updateMenuStates()
828
{
829
    MainWindowBase::updateMenuStates();
830

    
831
    Pane *currentPane = 0;
832
    Layer *currentLayer = 0;
833

    
834
    if (m_paneStack) currentPane = m_paneStack->getCurrentPane();
835
    if (currentPane) currentLayer = currentPane->getSelectedLayer();
836

    
837
    bool haveCurrentPane =
838
        (currentPane != 0);
839
    bool haveCurrentLayer =
840
        (haveCurrentPane &&
841
         (currentLayer != 0));
842
    bool haveSelection = 
843
        (m_viewManager &&
844
         !m_viewManager->getSelections().empty());
845
    bool haveCurrentEditableLayer =
846
        (haveCurrentLayer &&
847
         currentLayer->isLayerEditable());
848
    bool haveCurrentTimeInstantsLayer = 
849
        (haveCurrentLayer &&
850
         dynamic_cast<TimeInstantLayer *>(currentLayer));
851
    bool haveCurrentTimeValueLayer = 
852
        (haveCurrentLayer &&
853
         dynamic_cast<TimeValueLayer *>(currentLayer));
854

    
855
    emit canChangePlaybackSpeed(true);
856
    int v = m_playSpeed->value();
857
    emit canSpeedUpPlayback(v < m_playSpeed->maximum());
858
    emit canSlowDownPlayback(v > m_playSpeed->minimum());
859

    
860
    if (m_ffwdAction && m_rwdAction) {
861
        if (haveCurrentTimeInstantsLayer) {
862
            m_ffwdAction->setText(tr("Fast Forward to Next Instant"));
863
            m_ffwdAction->setStatusTip(tr("Fast forward to the next time instant in the current layer"));
864
            m_rwdAction->setText(tr("Rewind to Previous Instant"));
865
            m_rwdAction->setStatusTip(tr("Rewind to the previous time instant in the current layer"));
866
        } else if (haveCurrentTimeValueLayer) {
867
            m_ffwdAction->setText(tr("Fast Forward to Next Point"));
868
            m_ffwdAction->setStatusTip(tr("Fast forward to the next point in the current layer"));
869
            m_rwdAction->setText(tr("Rewind to Previous Point"));
870
            m_rwdAction->setStatusTip(tr("Rewind to the previous point in the current layer"));
871
        } else {
872
            m_ffwdAction->setText(tr("Fast Forward"));
873
            m_ffwdAction->setStatusTip(tr("Fast forward"));
874
            m_rwdAction->setText(tr("Rewind"));
875
            m_rwdAction->setStatusTip(tr("Rewind"));
876
        }
877
    }
878
}
879

    
880
void
881
MainWindow::updateDescriptionLabel()
882
{
883
    if (!getMainModel()) {
884
        m_descriptionLabel->setText(tr("No audio file loaded."));
885
        return;
886
    }
887

    
888
    QString description;
889

    
890
    size_t ssr = getMainModel()->getSampleRate();
891
    size_t tsr = ssr;
892
    if (m_playSource) tsr = m_playSource->getTargetSampleRate();
893

    
894
    if (ssr != tsr) {
895
        description = tr("%1Hz (resampling to %2Hz)").arg(ssr).arg(tsr);
896
    } else {
897
        description = QString("%1Hz").arg(ssr);
898
    }
899

    
900
    description = QString("%1 - %2")
901
        .arg(RealTime::frame2RealTime(getMainModel()->getEndFrame(), ssr)
902
             .toText(false).c_str())
903
        .arg(description);
904

    
905
    m_descriptionLabel->setText(description);
906
}
907

    
908
void
909
MainWindow::documentModified()
910
{
911
    //!!!
912
    MainWindowBase::documentModified();
913
}
914

    
915
void
916
MainWindow::documentRestored()
917
{
918
    //!!!
919
    MainWindowBase::documentRestored();
920
}
921

    
922
void
923
MainWindow::newSession()
924
{
925
    if (!checkSaveModified()) return;
926

    
927
    closeSession();
928
    createDocument();
929
    m_document->setAutoAlignment(true);
930

    
931
    Pane *pane = m_paneStack->addPane();
932

    
933
    connect(pane, SIGNAL(contextHelpChanged(const QString &)),
934
            this, SLOT(contextHelpChanged(const QString &)));
935

    
936
    Layer *waveform = m_document->createMainModelLayer(LayerFactory::Waveform);
937
    m_document->addLayerToView(pane, waveform);
938

    
939
    m_overview->registerView(pane);
940

    
941
    CommandHistory::getInstance()->clear();
942
    CommandHistory::getInstance()->documentSaved();
943
    documentRestored();
944
    updateMenuStates();
945
}
946

    
947
void
948
MainWindow::closeSession()
949
{
950
    if (!checkSaveModified()) return;
951

    
952
    while (m_paneStack->getPaneCount() > 0) {
953

    
954
        Pane *pane = m_paneStack->getPane(m_paneStack->getPaneCount() - 1);
955

    
956
        while (pane->getLayerCount() > 0) {
957
            m_document->removeLayerFromView
958
                (pane, pane->getLayer(pane->getLayerCount() - 1));
959
        }
960

    
961
        m_overview->unregisterView(pane);
962
        m_paneStack->deletePane(pane);
963
    }
964

    
965
    while (m_paneStack->getHiddenPaneCount() > 0) {
966

    
967
        Pane *pane = m_paneStack->getHiddenPane
968
            (m_paneStack->getHiddenPaneCount() - 1);
969

    
970
        while (pane->getLayerCount() > 0) {
971
            m_document->removeLayerFromView
972
                (pane, pane->getLayer(pane->getLayerCount() - 1));
973
        }
974

    
975
        m_overview->unregisterView(pane);
976
        m_paneStack->deletePane(pane);
977
    }
978

    
979
    delete m_document;
980
    m_document = 0;
981
    m_viewManager->clearSelections();
982
    m_timeRulerLayer = 0; // document owned this
983

    
984
    m_sessionFile = "";
985
    setWindowTitle(QApplication::applicationName());
986

    
987
    CommandHistory::getInstance()->clear();
988
    CommandHistory::getInstance()->documentSaved();
989
    documentRestored();
990
}
991

    
992
void
993
MainWindow::openFile()
994
{
995
    QString orig = m_audioFile;
996
    if (orig == "") orig = ".";
997
    else orig = QFileInfo(orig).absoluteDir().canonicalPath();
998

    
999
    QString path = getOpenFileName(FileFinder::AnyFile);
1000

    
1001
    if (path.isEmpty()) return;
1002

    
1003
    FileOpenStatus status = open(path, CreateAdditionalModel);
1004

    
1005
    if (status == FileOpenFailed) {
1006
        QMessageBox::critical(this, tr("Failed to open file"),
1007
                              tr("<b>File open failed</b><p>File \"%1\" could not be opened").arg(path));
1008
    } else if (status == FileOpenWrongMode) {
1009
        QMessageBox::critical(this, tr("Failed to open file"),
1010
                              tr("<b>Audio required</b><p>Please load at least one audio file before importing annotation data"));
1011
    } else {
1012
        configureNewPane(m_paneStack->getCurrentPane());
1013
    }
1014
}
1015

    
1016
void
1017
MainWindow::openLocation()
1018
{
1019
    QSettings settings;
1020
    settings.beginGroup("MainWindow");
1021
    QString lastLocation = settings.value("lastremote", "").toString();
1022

    
1023
    bool ok = false;
1024
    QString text = QInputDialog::getText
1025
        (this, tr("Open Location"),
1026
         tr("Please enter the URL of the location to open:"),
1027
         QLineEdit::Normal, lastLocation, &ok);
1028

    
1029
    if (!ok) return;
1030

    
1031
    settings.setValue("lastremote", text);
1032

    
1033
    if (text.isEmpty()) return;
1034

    
1035
    FileOpenStatus status = open(text, CreateAdditionalModel);
1036

    
1037
    if (status == FileOpenFailed) {
1038
        QMessageBox::critical(this, tr("Failed to open location"),
1039
                              tr("<b>Open failed</b><p>URL \"%1\" could not be opened").arg(text));
1040
    } else if (status == FileOpenWrongMode) {
1041
        QMessageBox::critical(this, tr("Failed to open location"),
1042
                              tr("<b>Audio required</b><p>Please load at least one audio file before importing annotation data"));
1043
    } else {
1044
        configureNewPane(m_paneStack->getCurrentPane());
1045
    }
1046
}
1047

    
1048
void
1049
MainWindow::openRecentFile()
1050
{
1051
    QObject *obj = sender();
1052
    QAction *action = dynamic_cast<QAction *>(obj);
1053
    
1054
    if (!action) {
1055
        std::cerr << "WARNING: MainWindow::openRecentFile: sender is not an action"
1056
                  << std::endl;
1057
        return;
1058
    }
1059

    
1060
    QString path = action->text();
1061
    if (path == "") return;
1062

    
1063
    FileOpenStatus status = open(path, CreateAdditionalModel);
1064

    
1065
    if (status == FileOpenFailed) {
1066
        QMessageBox::critical(this, tr("Failed to open location"),
1067
                              tr("<b>Open failed</b><p>File or URL \"%1\" could not be opened").arg(path));
1068
    } else if (status == FileOpenWrongMode) {
1069
        QMessageBox::critical(this, tr("Failed to open location"),
1070
                              tr("<b>Audio required</b><p>Please load at least one audio file before importing annotation data"));
1071
    } else {
1072
        configureNewPane(m_paneStack->getCurrentPane());
1073
    }
1074
}
1075

    
1076
Model *
1077
MainWindow::selectExistingModeLayer(Pane *pane, QString name)
1078
{   
1079
    Model *model = 0;
1080

    
1081
    bool have = false;
1082

    
1083
    for (int i = 0; i < pane->getLayerCount(); ++i) {
1084
        
1085
        Layer *layer = pane->getLayer(i);
1086
        if (!layer) continue;
1087
        
1088
        Model *lm = layer->getModel();
1089
        while (lm && lm->getSourceModel()) lm = lm->getSourceModel();
1090
        if (dynamic_cast<WaveFileModel *>(lm)) model = lm;
1091
        
1092
        QString ln = layer->objectName();
1093
        if (ln != name) {
1094
            m_hiddenLayers[pane].insert(layer);
1095
            m_document->removeLayerFromView(pane, layer);
1096
            continue;
1097
        }
1098
        
1099
        have = true;
1100
    }
1101
    
1102
    if (have) return 0;
1103

    
1104
    LayerSet &ls = m_hiddenLayers[pane];
1105
    bool found = false;
1106
    for (LayerSet::iterator i = ls.begin(); i != ls.end(); ++i) {
1107
        if ((*i)->objectName() == name) {
1108
            m_document->addLayerToView(pane, *i);
1109
            ls.erase(i);
1110
            found = true;
1111
            break;
1112
        }
1113
    }
1114

    
1115
    if (found) return 0;
1116

    
1117
    return model;
1118
}
1119

    
1120
void
1121
MainWindow::curveModeSelected()
1122
{
1123
    QString name = tr("Curve");
1124

    
1125
    for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
1126

    
1127
        Pane *pane = m_paneStack->getPane(i);
1128
        if (!pane) continue;
1129

    
1130
        Model *model = selectExistingModeLayer(pane, name);
1131
        if (!model) continue;
1132

    
1133
        TransformId id = "vamp:qm-vamp-plugins:qm-onsetdetector:detection_fn";
1134
        TransformFactory *tf = TransformFactory::getInstance();
1135

    
1136
        if (tf->haveTransform(id)) {
1137

    
1138
            Transform transform = tf->getDefaultTransformFor
1139
                (id, model->getSampleRate());
1140

    
1141
            transform.setStepSize(1024);
1142
            transform.setBlockSize(2048);
1143

    
1144
            ModelTransformer::Input input(model, -1);
1145

    
1146
//!!! no equivalent for this yet            context.updates = false;
1147

    
1148
            Layer *newLayer = m_document->createDerivedLayer(transform, model);
1149

    
1150
            if (newLayer) {
1151
                newLayer->setObjectName(name);
1152
                m_document->addLayerToView(pane, newLayer);
1153
                m_paneStack->setCurrentLayer(pane, newLayer);
1154
            }
1155
            
1156
        } else {
1157
            std::cerr << "No Aubio onset detector plugin available" << std::endl;
1158
        }
1159
    }
1160

    
1161
    m_displayMode = CurveMode;
1162
}
1163

    
1164
void
1165
MainWindow::waveformModeSelected()
1166
{
1167
    QString name = tr("Waveform");
1168

    
1169
    for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
1170

    
1171
        Pane *pane = m_paneStack->getPane(i);
1172
        if (!pane) continue;
1173

    
1174
        Model *model = selectExistingModeLayer(pane, name);
1175
        if (!model) continue;
1176

    
1177
        Layer *newLayer = m_document->createLayer(LayerFactory::Waveform);
1178
        newLayer->setObjectName(name);
1179
        m_document->setModel(newLayer, model);
1180
        m_document->addLayerToView(pane, newLayer);
1181
        m_paneStack->setCurrentLayer(pane, newLayer);
1182
    }
1183

    
1184
    m_displayMode = WaveformMode;
1185
}
1186

    
1187
void
1188
MainWindow::spectrogramModeSelected()
1189
{
1190
    QString name = tr("Spectrogram");
1191

    
1192
    for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
1193

    
1194
        Pane *pane = m_paneStack->getPane(i);
1195
        if (!pane) continue;
1196

    
1197
        Model *model = selectExistingModeLayer(pane, name);
1198
        if (!model) continue;
1199

    
1200
        Layer *newLayer = m_document->createLayer(LayerFactory::Spectrogram);
1201
        newLayer->setObjectName(name);
1202
        m_document->setModel(newLayer, model);
1203
        m_document->addLayerToView(pane, newLayer);
1204
        m_paneStack->setCurrentLayer(pane, newLayer);
1205
    }
1206

    
1207
    m_displayMode = SpectrogramMode;
1208
}
1209

    
1210
void
1211
MainWindow::melodogramModeSelected()
1212
{
1213
    QString name = tr("Melodic Range Spectrogram");
1214

    
1215
    for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
1216

    
1217
        Pane *pane = m_paneStack->getPane(i);
1218
        if (!pane) continue;
1219

    
1220
        Model *model = selectExistingModeLayer(pane, name);
1221
        if (!model) continue;
1222

    
1223
        Layer *newLayer = m_document->createLayer
1224
            (LayerFactory::MelodicRangeSpectrogram);
1225
        newLayer->setObjectName(name);
1226
        m_document->setModel(newLayer, model);
1227
        m_document->addLayerToView(pane, newLayer);
1228
        m_paneStack->setCurrentLayer(pane, newLayer);
1229
    }
1230

    
1231
    m_displayMode = MelodogramMode;
1232
}
1233

    
1234
void
1235
MainWindow::reselectMode()
1236
{
1237
    switch (m_displayMode) {
1238
    case CurveMode: curveModeSelected(); break;
1239
    case WaveformMode: waveformModeSelected(); break;
1240
    case SpectrogramMode: spectrogramModeSelected(); break;
1241
    case MelodogramMode: melodogramModeSelected(); break;
1242
    }
1243
}
1244

    
1245
void
1246
MainWindow::paneAdded(Pane *pane)
1247
{
1248
    pane->setPlaybackFollow(PlaybackScrollContinuous);
1249
    m_paneStack->sizePanesEqually();
1250
    if (m_overview) m_overview->registerView(pane);
1251
}    
1252

    
1253
void
1254
MainWindow::paneHidden(Pane *pane)
1255
{
1256
    if (m_overview) m_overview->unregisterView(pane); 
1257
}    
1258

    
1259
void
1260
MainWindow::paneAboutToBeDeleted(Pane *pane)
1261
{
1262
    if (m_overview) m_overview->unregisterView(pane); 
1263
}    
1264

    
1265
void
1266
MainWindow::paneDropAccepted(Pane *pane, QStringList uriList)
1267
{
1268
//    if (pane) m_paneStack->setCurrentPane(pane);
1269

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

    
1272
        FileOpenStatus status = open(*i, CreateAdditionalModel);
1273

    
1274
        if (status == FileOpenFailed) {
1275
            QMessageBox::critical(this, tr("Failed to open dropped URL"),
1276
                                  tr("<b>Open failed</b><p>Dropped URL \"%1\" could not be opened").arg(*i));
1277
        } else if (status == FileOpenWrongMode) {
1278
            QMessageBox::critical(this, tr("Failed to open dropped URL"),
1279
                                  tr("<b>Audio required</b><p>Please load at least one audio file before importing annotation data"));
1280
        } else {
1281
            configureNewPane(m_paneStack->getCurrentPane());
1282
        }
1283
    }
1284
}
1285

    
1286
void
1287
MainWindow::paneDropAccepted(Pane *pane, QString text)
1288
{
1289
    if (pane) m_paneStack->setCurrentPane(pane);
1290

    
1291
    QUrl testUrl(text);
1292
    if (testUrl.scheme() == "file" || 
1293
        testUrl.scheme() == "http" || 
1294
        testUrl.scheme() == "ftp") {
1295
        QStringList list;
1296
        list.push_back(text);
1297
        paneDropAccepted(pane, list);
1298
        return;
1299
    }
1300

    
1301
    //!!! open as text -- but by importing as if a CSV, or just adding
1302
    //to a text layer?
1303
}
1304

    
1305
void
1306
MainWindow::configureNewPane(Pane *pane)
1307
{
1308
    std::cerr << "MainWindow::configureNewPane(" << pane << ")" << std::endl;
1309

    
1310
    if (!pane) return;
1311

    
1312
    Layer *waveformLayer = 0;
1313

    
1314
    for (int i = 0; i < pane->getLayerCount(); ++i) {
1315
        Layer *layer = pane->getLayer(i);
1316
        if (!layer) continue;
1317
        if (dynamic_cast<WaveformLayer *>(layer)) waveformLayer = layer;
1318
        if (dynamic_cast<TimeValueLayer *>(layer)) return;
1319
    }
1320
    if (!waveformLayer) return;
1321

    
1322
    waveformLayer->setObjectName(tr("Waveform"));
1323

    
1324
    zoomToFit();
1325
    reselectMode();
1326
}
1327

    
1328
void
1329
MainWindow::closeEvent(QCloseEvent *e)
1330
{
1331
//    std::cerr << "MainWindow::closeEvent" << std::endl;
1332

    
1333
    if (m_openingAudioFile) {
1334
//        std::cerr << "Busy - ignoring close event" << std::endl;
1335
        e->ignore();
1336
        return;
1337
    }
1338

    
1339
    if (!m_abandoning && !checkSaveModified()) {
1340
//        std::cerr << "Ignoring close event" << std::endl;
1341
        e->ignore();
1342
        return;
1343
    }
1344

    
1345
    QSettings settings;
1346
    settings.beginGroup("MainWindow");
1347
    settings.setValue("size", size());
1348
    settings.setValue("position", pos());
1349
    settings.endGroup();
1350

    
1351
    delete m_keyReference;
1352
    m_keyReference = 0;
1353

    
1354
    if (m_preferencesDialog &&
1355
        m_preferencesDialog->isVisible()) {
1356
        closeSession(); // otherwise we'll have to wait for prefs changes
1357
        m_preferencesDialog->applicationClosing(false);
1358
    }
1359

    
1360
    if (m_layerTreeView &&
1361
        m_layerTreeView->isVisible()) {
1362
        delete m_layerTreeView;
1363
    }
1364

    
1365
    closeSession();
1366

    
1367
    e->accept();
1368
    return;
1369
}
1370

    
1371
bool
1372
MainWindow::commitData(bool mayAskUser)
1373
{
1374
    if (mayAskUser) {
1375
        bool rv = checkSaveModified();
1376
        if (rv) {
1377
            if (m_preferencesDialog &&
1378
                m_preferencesDialog->isVisible()) {
1379
                m_preferencesDialog->applicationClosing(false);
1380
            }
1381
        }
1382
        return rv;
1383
    } else {
1384
        if (m_preferencesDialog &&
1385
            m_preferencesDialog->isVisible()) {
1386
            m_preferencesDialog->applicationClosing(true);
1387
        }
1388
        if (!m_documentModified) return true;
1389

    
1390
        // If we can't check with the user first, then we can't save
1391
        // to the original session file (even if we have it) -- have
1392
        // to use a temporary file
1393

    
1394
        QString svDirBase = ".sv1";
1395
        QString svDir = QDir::home().filePath(svDirBase);
1396

    
1397
        if (!QFileInfo(svDir).exists()) {
1398
            if (!QDir::home().mkdir(svDirBase)) return false;
1399
        } else {
1400
            if (!QFileInfo(svDir).isDir()) return false;
1401
        }
1402
        
1403
        // This name doesn't have to be unguessable
1404
#ifndef _WIN32
1405
        QString fname = QString("tmp-%1-%2.sv")
1406
            .arg(QDateTime::currentDateTime().toString("yyyyMMddhhmmsszzz"))
1407
            .arg(QProcess().pid());
1408
#else
1409
        QString fname = QString("tmp-%1.sv")
1410
            .arg(QDateTime::currentDateTime().toString("yyyyMMddhhmmsszzz"));
1411
#endif
1412
        QString fpath = QDir(svDir).filePath(fname);
1413
        if (saveSessionFile(fpath)) {
1414
            m_recentFiles.addFile(fpath);
1415
            return true;
1416
        } else {
1417
            return false;
1418
        }
1419
    }
1420
}
1421

    
1422
bool
1423
MainWindow::checkSaveModified()
1424
{
1425
    // Called before some destructive operation (e.g. new session,
1426
    // exit program).  Return true if we can safely proceed, false to
1427
    // cancel.
1428

    
1429
    if (!m_documentModified) return true;
1430

    
1431
    int button = 
1432
        QMessageBox::warning(this,
1433
                             tr("Session modified"),
1434
                             tr("The current session has been modified.\nDo you want to save it?"),
1435
                             QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel,
1436
                             QMessageBox::Yes);
1437

    
1438
    if (button == QMessageBox::Yes) {
1439
        saveSession();
1440
        if (m_documentModified) { // save failed -- don't proceed!
1441
            return false;
1442
        } else {
1443
            return true; // saved, so it's safe to continue now
1444
        }
1445
    } else if (button == QMessageBox::No) {
1446
        m_documentModified = false; // so we know to abandon it
1447
        return true;
1448
    }
1449

    
1450
    // else cancel
1451
    return false;
1452
}
1453

    
1454
void
1455
MainWindow::saveSession()
1456
{
1457
    if (m_sessionFile != "") {
1458
        if (!saveSessionFile(m_sessionFile)) {
1459
            QMessageBox::critical(this, tr("Failed to save file"),
1460
                                  tr("Session file \"%1\" could not be saved.").arg(m_sessionFile));
1461
        } else {
1462
            CommandHistory::getInstance()->documentSaved();
1463
            documentRestored();
1464
        }
1465
    } else {
1466
        saveSessionAs();
1467
    }
1468
}
1469

    
1470
void
1471
MainWindow::saveSessionAs()
1472
{
1473
    QString orig = m_audioFile;
1474
    if (orig == "") orig = ".";
1475
    else orig = QFileInfo(orig).absoluteDir().canonicalPath();
1476

    
1477
    QString path = getSaveFileName(FileFinder::SessionFile);
1478

    
1479
    if (path == "") return;
1480

    
1481
    if (!saveSessionFile(path)) {
1482
        QMessageBox::critical(this, tr("Failed to save file"),
1483
                              tr("Session file \"%1\" could not be saved.").arg(path));
1484
    } else {
1485
        setWindowTitle(tr("Vect: %1")
1486
                       .arg(QFileInfo(path).fileName()));
1487
        m_sessionFile = path;
1488
        CommandHistory::getInstance()->documentSaved();
1489
        documentRestored();
1490
        m_recentFiles.addFile(path);
1491
    }
1492
}
1493

    
1494
void
1495
MainWindow::preferenceChanged(PropertyContainer::PropertyName name)
1496
{
1497
    MainWindowBase::preferenceChanged(name);
1498

    
1499
    if (name == "Background Mode" && m_viewManager) {
1500
        if (m_viewManager->getGlobalDarkBackground()) {
1501
            m_panLayer->setBaseColour
1502
                (ColourDatabase::getInstance()->getColourIndex(tr("Bright Green")));
1503
        } else {
1504
            m_panLayer->setBaseColour
1505
                (ColourDatabase::getInstance()->getColourIndex(tr("Green")));
1506
        }      
1507
    }    
1508
}
1509

    
1510
void
1511
MainWindow::renameCurrentLayer()
1512
{
1513
    Pane *pane = m_paneStack->getCurrentPane();
1514
    if (pane) {
1515
        Layer *layer = pane->getSelectedLayer();
1516
        if (layer) {
1517
            bool ok = false;
1518
            QString newName = QInputDialog::getText
1519
                (this, tr("Rename Layer"),
1520
                 tr("New name for this layer:"),
1521
                 QLineEdit::Normal, layer->objectName(), &ok);
1522
            if (ok) {
1523
                layer->setObjectName(newName);
1524
            }
1525
        }
1526
    }
1527
}
1528

    
1529
void
1530
MainWindow::playSpeedChanged(int position)
1531
{
1532
    PlaySpeedRangeMapper mapper(0, 200);
1533

    
1534
    float percent = m_playSpeed->mappedValue();
1535
    float factor = mapper.getFactorForValue(percent);
1536

    
1537
    std::cerr << "speed = " << position << " percent = " << percent << " factor = " << factor << std::endl;
1538

    
1539
    bool something = (position != 100);
1540

    
1541
    int pc = lrintf(percent);
1542

    
1543
    if (!something) {
1544
        contextHelpChanged(tr("Playback speed: Normal"));
1545
    } else {
1546
        contextHelpChanged(tr("Playback speed: %1%2%")
1547
                           .arg(position > 100 ? "+" : "")
1548
                           .arg(pc));
1549
    }
1550

    
1551
    m_playSource->setTimeStretch(factor);
1552

    
1553
    updateMenuStates();
1554
}
1555

    
1556
void
1557
MainWindow::playSharpenToggled()
1558
{
1559
    QSettings settings;
1560
    settings.beginGroup("MainWindow");
1561
    settings.setValue("playsharpen", m_playSharpen->isChecked());
1562
    settings.endGroup();
1563

    
1564
    playSpeedChanged(m_playSpeed->value());
1565
}
1566

    
1567
void
1568
MainWindow::playMonoToggled()
1569
{
1570
    QSettings settings;
1571
    settings.beginGroup("MainWindow");
1572
    settings.setValue("playmono", m_playMono->isChecked());
1573
    settings.endGroup();
1574

    
1575
    playSpeedChanged(m_playSpeed->value());
1576
}    
1577

    
1578
void
1579
MainWindow::speedUpPlayback()
1580
{
1581
    int value = m_playSpeed->value();
1582
    value = value + m_playSpeed->pageStep();
1583
    if (value > m_playSpeed->maximum()) value = m_playSpeed->maximum();
1584
    m_playSpeed->setValue(value);
1585
}
1586

    
1587
void
1588
MainWindow::slowDownPlayback()
1589
{
1590
    int value = m_playSpeed->value();
1591
    value = value - m_playSpeed->pageStep();
1592
    if (value < m_playSpeed->minimum()) value = m_playSpeed->minimum();
1593
    m_playSpeed->setValue(value);
1594
}
1595

    
1596
void
1597
MainWindow::restoreNormalPlayback()
1598
{
1599
    m_playSpeed->setValue(m_playSpeed->defaultValue());
1600
}
1601

    
1602
void
1603
MainWindow::updateVisibleRangeDisplay(Pane *p) const
1604
{
1605
    if (!getMainModel() || !p) {
1606
        return;
1607
    }
1608

    
1609
    bool haveSelection = false;
1610
    size_t startFrame = 0, endFrame = 0;
1611

    
1612
    if (m_viewManager && m_viewManager->haveInProgressSelection()) {
1613

    
1614
        bool exclusive = false;
1615
        Selection s = m_viewManager->getInProgressSelection(exclusive);
1616

    
1617
        if (!s.isEmpty()) {
1618
            haveSelection = true;
1619
            startFrame = s.getStartFrame();
1620
            endFrame = s.getEndFrame();
1621
        }
1622
    }
1623

    
1624
    if (!haveSelection) {
1625
        startFrame = p->getFirstVisibleFrame();
1626
        endFrame = p->getLastVisibleFrame();
1627
    }
1628

    
1629
    RealTime start = RealTime::frame2RealTime
1630
        (startFrame, getMainModel()->getSampleRate());
1631

    
1632
    RealTime end = RealTime::frame2RealTime
1633
        (endFrame, getMainModel()->getSampleRate());
1634

    
1635
    RealTime duration = end - start;
1636

    
1637
    QString startStr, endStr, durationStr;
1638
    startStr = start.toText(true).c_str();
1639
    endStr = end.toText(true).c_str();
1640
    durationStr = duration.toText(true).c_str();
1641

    
1642
    if (haveSelection) {
1643
        m_myStatusMessage = tr("Selection: %1 to %2 (duration %3)")
1644
            .arg(startStr).arg(endStr).arg(durationStr);
1645
    } else {
1646
        m_myStatusMessage = tr("Visible: %1 to %2 (duration %3)")
1647
            .arg(startStr).arg(endStr).arg(durationStr);
1648
    }
1649

    
1650
    statusBar()->showMessage(m_myStatusMessage);
1651
}
1652

    
1653
void
1654
MainWindow::outputLevelsChanged(float left, float right)
1655
{
1656
    m_fader->setPeakLeft(left);
1657
    m_fader->setPeakRight(right);
1658
}
1659

    
1660
void
1661
MainWindow::sampleRateMismatch(size_t requested, size_t actual,
1662
                               bool willResample)
1663
{
1664
    if (!willResample) {
1665
        //!!! more helpful message needed
1666
        QMessageBox::information
1667
            (this, tr("Sample rate mismatch"),
1668
             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.")
1669
             .arg(requested).arg(actual));
1670
    }        
1671

    
1672
    updateDescriptionLabel();
1673
}
1674

    
1675
void
1676
MainWindow::audioOverloadPluginDisabled()
1677
{
1678
    QMessageBox::information
1679
        (this, tr("Audio processing overload"),
1680
         tr("<b>Overloaded</b><p>Audio effects plugin auditioning has been disabled due to a processing overload."));
1681
}
1682

    
1683
void
1684
MainWindow::audioTimeStretchMultiChannelDisabled()
1685
{
1686
    static bool shownOnce = false;
1687
    if (shownOnce) return;
1688
    QMessageBox::information
1689
        (this, tr("Audio processing overload"),
1690
         tr("<b>Overloaded</b><p>Audio playback speed processing has been reduced to a single channel, due to a processing overload."));
1691
    shownOnce = true;
1692
}
1693

    
1694
void
1695
MainWindow::layerRemoved(Layer *layer)
1696
{
1697
    MainWindowBase::layerRemoved(layer);
1698
}
1699

    
1700
void
1701
MainWindow::layerInAView(Layer *layer, bool inAView)
1702
{
1703
    MainWindowBase::layerInAView(layer, inAView);
1704
}
1705

    
1706
void
1707
MainWindow::modelAdded(Model *model)
1708
{
1709
    MainWindowBase::modelAdded(model);
1710
    DenseTimeValueModel *dtvm = dynamic_cast<DenseTimeValueModel *>(model);
1711
    if (dtvm) {
1712
        if (!model->isReady()) {
1713
            connect(dtvm, SIGNAL(ready()), this, SLOT(modelReady()));
1714
        } else {
1715
            StorageAdviser::Criteria criteria = StorageAdviser::NoCriteria;
1716
            if (dtvm == getMainModel()) {
1717
                criteria = StorageAdviser::SpeedCritical;
1718
            }
1719

    
1720
            FFTModel *fftmodel = new FFTModel
1721
                (dtvm,
1722
                 -1,
1723
                 HanningWindow,
1724
                 2048, 1024, 2048,
1725
                 false,
1726
                 criteria);
1727

    
1728
            m_fftModelMap[dtvm] = fftmodel;
1729
            fftmodel->resume();
1730
        }
1731
    }
1732
}
1733

    
1734
void
1735
MainWindow::modelReady()
1736
{
1737
    QObject *s = sender();
1738
    std::cerr << "MainWindow::modelReady(" << s << ")" << std::endl;
1739
    if (s) {
1740
        DenseTimeValueModel *dtvm = dynamic_cast<DenseTimeValueModel *>(s);
1741
        if (dtvm) {
1742
            StorageAdviser::Criteria criteria = StorageAdviser::NoCriteria;
1743
            if (dtvm == getMainModel()) {
1744
                criteria = StorageAdviser::SpeedCritical;
1745
            }
1746

    
1747
            FFTModel *fftmodel = new FFTModel
1748
                (dtvm,
1749
                 -1,
1750
                 HanningWindow,
1751
                 2048, 1024, 2048,
1752
                 false,
1753
                 criteria);
1754

    
1755
            m_fftModelMap[dtvm] = fftmodel;
1756
            fftmodel->resume();
1757
        } else {
1758
            std::cerr << "Not a DenseTimeValueModel!" << std::endl;
1759
        }
1760
    }
1761
}            
1762

    
1763
void
1764
MainWindow::modelAboutToBeDeleted(Model *model)
1765
{
1766
    if (m_fftModelMap.find(model) != m_fftModelMap.end()) {
1767
        delete m_fftModelMap[model];
1768
        m_fftModelMap.erase(model);
1769
    }
1770
    MainWindowBase::modelAboutToBeDeleted(model);
1771
}
1772

    
1773
void
1774
MainWindow::mainModelChanged(WaveFileModel *model)
1775
{
1776
    m_panLayer->setModel(model);
1777

    
1778
    MainWindowBase::mainModelChanged(model);
1779

    
1780
    if (m_playTarget) {
1781
        connect(m_fader, SIGNAL(valueChanged(float)),
1782
                m_playTarget, SLOT(setOutputGain(float)));
1783
    }
1784
}
1785

    
1786
void
1787
MainWindow::modelGenerationFailed(QString transformName, QString message)
1788
{
1789
    if (message != "") {
1790

    
1791
        QMessageBox::warning
1792
            (this,
1793
             tr("Failed to generate layer"),
1794
             tr("<b>Layer generation failed</b><p>Failed to generate derived layer.<p>The layer transform \"%1\" failed:<p>%2")
1795
             .arg(transformName).arg(message),
1796
             QMessageBox::Ok);
1797
    } else {
1798
        QMessageBox::warning
1799
            (this,
1800
             tr("Failed to generate layer"),
1801
             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.")
1802
             .arg(transformName),
1803
             QMessageBox::Ok);
1804
    }
1805
}
1806

    
1807
void
1808
MainWindow::modelGenerationWarning(QString transformName, QString message)
1809
{
1810
    QMessageBox::warning
1811
        (this, tr("Warning"), message, QMessageBox::Ok);
1812
}
1813

    
1814
void
1815
MainWindow::modelRegenerationFailed(QString layerName,
1816
                                    QString transformName, QString message)
1817
{
1818
    if (message != "") {
1819

    
1820
        QMessageBox::warning
1821
            (this,
1822
             tr("Failed to regenerate layer"),
1823
             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")
1824
             .arg(layerName).arg(transformName).arg(message),
1825
             QMessageBox::Ok);
1826
    } else {
1827
        QMessageBox::warning
1828
            (this,
1829
             tr("Failed to regenerate layer"),
1830
             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.")
1831
             .arg(layerName).arg(transformName),
1832
             QMessageBox::Ok);
1833
    }
1834
}
1835

    
1836
void
1837
MainWindow::modelRegenerationWarning(QString layerName,
1838
                                     QString transformName, QString message)
1839
{
1840
    QMessageBox::warning
1841
        (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);
1842
}
1843

    
1844
void
1845
MainWindow::alignmentFailed(QString transformName, QString message)
1846
{
1847
    QMessageBox::warning
1848
        (this,
1849
         tr("Failed to calculate alignment"),
1850
         tr("<b>Alignment calculation failed</b><p>Failed to calculate an audio alignment using transform \"%1\":<p>%2")
1851
         .arg(transformName).arg(message),
1852
         QMessageBox::Ok);
1853
}
1854

    
1855
void
1856
MainWindow::rightButtonMenuRequested(Pane *pane, QPoint position)
1857
{
1858
//    std::cerr << "MainWindow::rightButtonMenuRequested(" << pane << ", " << position.x() << ", " << position.y() << ")" << std::endl;
1859
    m_paneStack->setCurrentPane(pane);
1860
    m_rightButtonMenu->popup(position);
1861
}
1862

    
1863
void
1864
MainWindow::showLayerTree()
1865
{
1866
    if (!m_layerTreeView.isNull()) {
1867
        m_layerTreeView->show();
1868
        m_layerTreeView->raise();
1869
        return;
1870
    }
1871

    
1872
    //!!! should use an actual dialog class
1873
        
1874
    m_layerTreeView = new QTreeView();
1875
    LayerTreeModel *tree = new LayerTreeModel(m_paneStack);
1876
    m_layerTreeView->resize(500, 300); //!!!
1877
    m_layerTreeView->setModel(tree);
1878
    m_layerTreeView->expandAll();
1879
    m_layerTreeView->show();
1880
}
1881

    
1882
void
1883
MainWindow::handleOSCMessage(const OSCMessage &message)
1884
{
1885
    std::cerr << "MainWindow::handleOSCMessage: Not implemented" << std::endl;
1886
}
1887

    
1888
void
1889
MainWindow::preferences()
1890
{
1891
    if (!m_preferencesDialog.isNull()) {
1892
        m_preferencesDialog->show();
1893
        m_preferencesDialog->raise();
1894
        return;
1895
    }
1896

    
1897
    m_preferencesDialog = new PreferencesDialog(this);
1898

    
1899
    // DeleteOnClose is safe here, because m_preferencesDialog is a
1900
    // QPointer that will be zeroed when the dialog is deleted.  We
1901
    // use it in preference to leaving the dialog lying around because
1902
    // if you Cancel the dialog, it resets the preferences state
1903
    // without resetting its own widgets, so its state will be
1904
    // incorrect when next shown unless we construct it afresh
1905
    m_preferencesDialog->setAttribute(Qt::WA_DeleteOnClose);
1906

    
1907
    m_preferencesDialog->show();
1908
}
1909

    
1910
void
1911
MainWindow::mouseEnteredWidget()
1912
{
1913
    QWidget *w = dynamic_cast<QWidget *>(sender());
1914
    if (!w) return;
1915

    
1916
    if (w == m_fader) {
1917
        contextHelpChanged(tr("Adjust the master playback level"));
1918
    } else if (w == m_playSpeed) {
1919
        contextHelpChanged(tr("Adjust the master playback speed"));
1920
    } else if (w == m_playSharpen && w->isEnabled()) {
1921
        contextHelpChanged(tr("Toggle transient sharpening for playback time scaling"));
1922
    } else if (w == m_playMono && w->isEnabled()) {
1923
        contextHelpChanged(tr("Toggle mono mode for playback time scaling"));
1924
    }
1925
}
1926

    
1927
void
1928
MainWindow::mouseLeftWidget()
1929
{
1930
    contextHelpChanged("");
1931
}
1932

    
1933
void
1934
MainWindow::website()
1935
{
1936
    openHelpUrl(tr("http://www.sonicvisualiser.org/"));
1937
}
1938

    
1939
void
1940
MainWindow::help()
1941
{
1942
    openHelpUrl(tr("http://www.sonicvisualiser.org/doc/reference/1.0/en/"));
1943
}
1944

    
1945
void
1946
MainWindow::about()
1947
{
1948
    bool debug = false;
1949
    QString version = "(unknown version)";
1950

    
1951
#ifdef BUILD_DEBUG
1952
    debug = true;
1953
#endif
1954
#ifdef VECT_VERSION
1955
#ifdef SVNREV
1956
    version = tr("Release %1 : Revision %2").arg(VECT_VERSION).arg(SVNREV);
1957
#else
1958
    version = tr("Release %1").arg(VECT_VERSION);
1959
#endif
1960
#else
1961
#ifdef SVNREV
1962
    version = tr("Unreleased : Revision %1").arg(SVNREV);
1963
#endif
1964
#endif
1965

    
1966
    QString aboutText;
1967

    
1968
    aboutText += tr("<h3>About Sonic Visualiser</h3>");
1969
    aboutText += tr("<p>Sonic Visualiser is a program for viewing and exploring audio data for<br>semantic music analysis and annotation.</p>");
1970
    aboutText += tr("<p>%1 : %2 configuration</p>")
1971
        .arg(version)
1972
        .arg(debug ? tr("Debug") : tr("Release"));
1973

    
1974
#ifndef BUILD_STATIC
1975
    aboutText += tr("<br>Using Qt v%1 &copy; Trolltech AS").arg(QT_VERSION_STR);
1976
#else
1977
#ifdef QT_SHARED
1978
    aboutText += tr("<br>Using Qt v%1 &copy; Trolltech AS").arg(QT_VERSION_STR);
1979
#endif
1980
#endif
1981

    
1982
#ifdef BUILD_STATIC
1983
    aboutText += tr("<p>Statically linked");
1984
#ifndef QT_SHARED
1985
    aboutText += tr("<br>With Qt (v%1) &copy; Trolltech AS").arg(QT_VERSION_STR);
1986
#endif
1987
#ifdef HAVE_JACK
1988
#ifdef JACK_VERSION
1989
    aboutText += tr("<br>With JACK audio output (v%1) &copy; Paul Davis and Jack O'Quin").arg(JACK_VERSION);
1990
#else
1991
    aboutText += tr("<br>With JACK audio output &copy; Paul Davis and Jack O'Quin");
1992
#endif
1993
#endif
1994
#ifdef HAVE_PORTAUDIO
1995
    aboutText += tr("<br>With PortAudio audio output &copy; Ross Bencina and Phil Burk");
1996
#endif
1997
#ifdef HAVE_OGGZ
1998
#ifdef OGGZ_VERSION
1999
    aboutText += tr("<br>With Ogg file decoder (oggz v%1, fishsound v%2) &copy; CSIRO Australia").arg(OGGZ_VERSION).arg(FISHSOUND_VERSION);
2000
#else
2001
    aboutText += tr("<br>With Ogg file decoder &copy; CSIRO Australia");
2002
#endif
2003
#endif
2004
#ifdef HAVE_MAD
2005
#ifdef MAD_VERSION
2006
    aboutText += tr("<br>With MAD mp3 decoder (v%1) &copy; Underbit Technologies Inc").arg(MAD_VERSION);
2007
#else
2008
    aboutText += tr("<br>With MAD mp3 decoder &copy; Underbit Technologies Inc");
2009
#endif
2010
#endif
2011
#ifdef HAVE_SAMPLERATE
2012
#ifdef SAMPLERATE_VERSION
2013
    aboutText += tr("<br>With libsamplerate (v%1) &copy; Erik de Castro Lopo").arg(SAMPLERATE_VERSION);
2014
#else
2015
    aboutText += tr("<br>With libsamplerate &copy; Erik de Castro Lopo");
2016
#endif
2017
#endif
2018
#ifdef HAVE_SNDFILE
2019
#ifdef SNDFILE_VERSION
2020
    aboutText += tr("<br>With libsndfile (v%1) &copy; Erik de Castro Lopo").arg(SNDFILE_VERSION);
2021
#else
2022
    aboutText += tr("<br>With libsndfile &copy; Erik de Castro Lopo");
2023
#endif
2024
#endif
2025
#ifdef HAVE_FFTW3F
2026
#ifdef FFTW3_VERSION
2027
    aboutText += tr("<br>With FFTW3 (v%1) &copy; Matteo Frigo and MIT").arg(FFTW3_VERSION);
2028
#else
2029
    aboutText += tr("<br>With FFTW3 &copy; Matteo Frigo and MIT");
2030
#endif
2031
#endif
2032
#ifdef HAVE_VAMP
2033
    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);
2034
#endif
2035
    aboutText += tr("<br>With LADSPA plugin support (API v%1) &copy; Richard Furse, Paul Davis, Stefan Westerfeld").arg(LADSPA_VERSION);
2036
    aboutText += tr("<br>With DSSI plugin support (API v%1) &copy; Chris Cannam, Steve Harris, Sean Bolton").arg(DSSI_VERSION);
2037
#ifdef HAVE_LIBLO
2038
#ifdef LIBLO_VERSION
2039
    aboutText += tr("<br>With liblo Lite OSC library (v%1) &copy; Steve Harris").arg(LIBLO_VERSION);
2040
#else
2041
    aboutText += tr("<br>With liblo Lite OSC library &copy; Steve Harris").arg(LIBLO_VERSION);
2042
#endif
2043
    if (m_oscQueue && m_oscQueue->isOK()) {
2044
        aboutText += tr("<p>The OSC URL for this instance is: \"%1\"").arg(m_oscQueue->getOSCURL());
2045
    }
2046
#endif
2047
    aboutText += "</p>";
2048
#endif
2049

    
2050
    aboutText += 
2051
        "<p>Sonic Visualiser Copyright &copy; 2005 - 2007 Chris Cannam and<br>"
2052
        "Queen Mary, University of London.</p>"
2053
        "<p>This program is free software; you can redistribute it and/or<br>"
2054
        "modify it under the terms of the GNU General Public License as<br>"
2055
        "published by the Free Software Foundation; either version 2 of the<br>"
2056
        "License, or (at your option) any later version.<br>See the file "
2057
        "COPYING included with this distribution for more information.</p>";
2058
    
2059
    QMessageBox::about(this, tr("About Sonic Visualiser"), aboutText);
2060
}
2061

    
2062
void
2063
MainWindow::keyReference()
2064
{
2065
    m_keyReference->show();
2066
}
2067