Chris@45
|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */
|
Chris@45
|
2
|
Chris@45
|
3 /*
|
Chris@45
|
4 Sonic Visualiser
|
Chris@45
|
5 An audio file viewer and annotation editor.
|
Chris@45
|
6 Centre for Digital Music, Queen Mary, University of London.
|
Chris@45
|
7 This file copyright 2006-2007 Chris Cannam and QMUL.
|
Chris@45
|
8
|
Chris@45
|
9 This program is free software; you can redistribute it and/or
|
Chris@45
|
10 modify it under the terms of the GNU General Public License as
|
Chris@45
|
11 published by the Free Software Foundation; either version 2 of the
|
Chris@45
|
12 License, or (at your option) any later version. See the file
|
Chris@45
|
13 COPYING included with this distribution for more information.
|
Chris@45
|
14 */
|
Chris@45
|
15
|
Chris@45
|
16 #include "MainWindowBase.h"
|
Chris@46
|
17 #include "Document.h"
|
Chris@45
|
18
|
Chris@45
|
19 #include "view/Pane.h"
|
Chris@45
|
20 #include "view/PaneStack.h"
|
Chris@479
|
21 #include "data/model/ReadOnlyWaveFileModel.h"
|
Chris@477
|
22 #include "data/model/WritableWaveFileModel.h"
|
Chris@45
|
23 #include "data/model/SparseOneDimensionalModel.h"
|
Chris@45
|
24 #include "data/model/NoteModel.h"
|
matthiasm@267
|
25 #include "data/model/FlexiNoteModel.h"
|
Chris@45
|
26 #include "data/model/Labeller.h"
|
Chris@124
|
27 #include "data/model/TabularModel.h"
|
Chris@45
|
28 #include "view/ViewManager.h"
|
Chris@45
|
29
|
Chris@45
|
30 #include "layer/WaveformLayer.h"
|
Chris@45
|
31 #include "layer/TimeRulerLayer.h"
|
Chris@45
|
32 #include "layer/TimeInstantLayer.h"
|
Chris@45
|
33 #include "layer/TimeValueLayer.h"
|
Chris@45
|
34 #include "layer/Colour3DPlotLayer.h"
|
Chris@45
|
35 #include "layer/SliceLayer.h"
|
Chris@45
|
36 #include "layer/SliceableLayer.h"
|
Chris@45
|
37 #include "layer/ImageLayer.h"
|
Chris@184
|
38 #include "layer/NoteLayer.h"
|
matthiasm@267
|
39 #include "layer/FlexiNoteLayer.h"
|
Chris@184
|
40 #include "layer/RegionLayer.h"
|
Chris@45
|
41
|
Chris@45
|
42 #include "widgets/ListInputDialog.h"
|
Chris@105
|
43 #include "widgets/CommandHistory.h"
|
Chris@109
|
44 #include "widgets/ProgressDialog.h"
|
Chris@109
|
45 #include "widgets/MIDIFileImportDialog.h"
|
Chris@109
|
46 #include "widgets/CSVFormatDialog.h"
|
Chris@123
|
47 #include "widgets/ModelDataTableDialog.h"
|
Chris@341
|
48 #include "widgets/InteractiveFileFinder.h"
|
Chris@45
|
49
|
Chris@468
|
50 #include "audio/AudioCallbackPlaySource.h"
|
Chris@574
|
51 #include "audio/AudioCallbackRecordTarget.h"
|
Chris@468
|
52 #include "audio/PlaySpeedRangeMapper.h"
|
Chris@475
|
53
|
Chris@45
|
54 #include "data/fileio/DataFileReaderFactory.h"
|
Chris@45
|
55 #include "data/fileio/PlaylistFileReader.h"
|
Chris@45
|
56 #include "data/fileio/WavFileWriter.h"
|
Chris@45
|
57 #include "data/fileio/MIDIFileWriter.h"
|
Chris@45
|
58 #include "data/fileio/BZipFileDevice.h"
|
Chris@45
|
59 #include "data/fileio/FileSource.h"
|
Chris@152
|
60 #include "data/fileio/AudioFileReaderFactory.h"
|
Chris@134
|
61 #include "rdf/RDFImporter.h"
|
Chris@45
|
62
|
Chris@45
|
63 #include "base/RecentFiles.h"
|
Chris@45
|
64
|
Chris@45
|
65 #include "base/PlayParameterRepository.h"
|
Chris@45
|
66 #include "base/XmlExportable.h"
|
Chris@45
|
67 #include "base/Profiler.h"
|
Chris@45
|
68 #include "base/Preferences.h"
|
Chris@217
|
69 #include "base/TempWriteFile.h"
|
Chris@217
|
70 #include "base/Exceptions.h"
|
Chris@223
|
71 #include "base/ResourceFinder.h"
|
Chris@45
|
72
|
Chris@45
|
73 #include "data/osc/OSCQueue.h"
|
Chris@157
|
74 #include "data/midi/MIDIInput.h"
|
Chris@45
|
75
|
Chris@599
|
76 #include "system/System.h"
|
Chris@599
|
77
|
Chris@468
|
78 #include <bqaudioio/SystemPlaybackTarget.h>
|
Chris@475
|
79 #include <bqaudioio/SystemAudioIO.h>
|
Chris@468
|
80 #include <bqaudioio/AudioFactory.h>
|
Chris@551
|
81 #include <bqaudioio/ResamplerWrapper.h>
|
Chris@468
|
82
|
Chris@45
|
83 #include <QApplication>
|
Chris@45
|
84 #include <QMessageBox>
|
Chris@45
|
85 #include <QGridLayout>
|
Chris@45
|
86 #include <QLabel>
|
Chris@45
|
87 #include <QAction>
|
Chris@45
|
88 #include <QMenuBar>
|
Chris@45
|
89 #include <QToolBar>
|
Chris@45
|
90 #include <QInputDialog>
|
Chris@45
|
91 #include <QStatusBar>
|
Chris@45
|
92 #include <QTreeView>
|
Chris@45
|
93 #include <QFile>
|
Chris@45
|
94 #include <QFileInfo>
|
Chris@45
|
95 #include <QDir>
|
Chris@45
|
96 #include <QTextStream>
|
Chris@432
|
97 #include <QTextCodec>
|
Chris@45
|
98 #include <QProcess>
|
Chris@45
|
99 #include <QShortcut>
|
Chris@45
|
100 #include <QSettings>
|
Chris@45
|
101 #include <QDateTime>
|
Chris@45
|
102 #include <QProcess>
|
Chris@45
|
103 #include <QCheckBox>
|
Chris@45
|
104 #include <QRegExp>
|
Chris@45
|
105 #include <QScrollArea>
|
Chris@168
|
106 #include <QDesktopWidget>
|
Chris@354
|
107 #include <QSignalMapper>
|
Chris@45
|
108
|
Chris@45
|
109 #include <iostream>
|
Chris@45
|
110 #include <cstdio>
|
Chris@45
|
111 #include <errno.h>
|
Chris@45
|
112
|
Chris@45
|
113 using std::vector;
|
Chris@45
|
114 using std::map;
|
Chris@45
|
115 using std::set;
|
Chris@45
|
116
|
Chris@255
|
117 #ifdef Q_WS_X11
|
Chris@255
|
118 #define Window X11Window
|
Chris@255
|
119 #include <X11/Xlib.h>
|
Chris@255
|
120 #include <X11/Xutil.h>
|
Chris@255
|
121 #include <X11/Xatom.h>
|
Chris@255
|
122 #include <X11/SM/SMlib.h>
|
Chris@255
|
123
|
Chris@255
|
124 static int handle_x11_error(Display *dpy, XErrorEvent *err)
|
Chris@255
|
125 {
|
Chris@255
|
126 char errstr[256];
|
Chris@255
|
127 XGetErrorText(dpy, err->error_code, errstr, 256);
|
Chris@255
|
128 if (err->error_code != BadWindow) {
|
Chris@595
|
129 cerr << "Sonic Visualiser: X Error: "
|
Chris@595
|
130 << errstr << " " << int(err->error_code)
|
Chris@595
|
131 << "\nin major opcode: "
|
Chris@595
|
132 << int(err->request_code) << endl;
|
Chris@255
|
133 }
|
Chris@255
|
134 return 0;
|
Chris@255
|
135 }
|
Chris@255
|
136 #undef Window
|
Chris@255
|
137 #endif
|
Chris@45
|
138
|
Chris@475
|
139 MainWindowBase::MainWindowBase(SoundOptions options) :
|
Chris@636
|
140 m_document(nullptr),
|
Chris@636
|
141 m_paneStack(nullptr),
|
Chris@636
|
142 m_viewManager(nullptr),
|
Chris@636
|
143 m_timeRulerLayer(nullptr),
|
Chris@475
|
144 m_soundOptions(options),
|
Chris@636
|
145 m_playSource(nullptr),
|
Chris@636
|
146 m_recordTarget(nullptr),
|
Chris@636
|
147 m_resamplerWrapper(nullptr),
|
Chris@636
|
148 m_playTarget(nullptr),
|
Chris@636
|
149 m_audioIO(nullptr),
|
Chris@636
|
150 m_oscQueue(nullptr),
|
Chris@636
|
151 m_oscQueueStarter(nullptr),
|
Chris@636
|
152 m_midiInput(nullptr),
|
Chris@45
|
153 m_recentFiles("RecentFiles", 20),
|
Chris@54
|
154 m_recentTransforms("RecentTransforms", 20),
|
Chris@45
|
155 m_documentModified(false),
|
Chris@45
|
156 m_openingAudioFile(false),
|
Chris@45
|
157 m_abandoning(false),
|
Chris@636
|
158 m_labeller(nullptr),
|
Chris@357
|
159 m_lastPlayStatusSec(0),
|
Chris@357
|
160 m_initialDarkBackground(false),
|
Chris@378
|
161 m_defaultFfwdRwdStep(2, 0),
|
Chris@483
|
162 m_audioRecordMode(RecordCreateAdditionalModel),
|
Chris@636
|
163 m_statusLabel(nullptr),
|
Chris@426
|
164 m_iconsVisibleInMenus(true),
|
Chris@636
|
165 m_menuShortcutMapper(nullptr)
|
Chris@45
|
166 {
|
Chris@113
|
167 Profiler profiler("MainWindowBase::MainWindowBase");
|
Chris@113
|
168
|
Chris@591
|
169 SVDEBUG << "MainWindowBase::MainWindowBase" << endl;
|
Chris@591
|
170
|
Chris@475
|
171 if (options & WithAudioInput) {
|
Chris@475
|
172 if (!(options & WithAudioOutput)) {
|
Chris@591
|
173 SVCERR << "WARNING: MainWindowBase: WithAudioInput requires WithAudioOutput -- recording will not work" << endl;
|
Chris@475
|
174 }
|
Chris@475
|
175 }
|
Chris@475
|
176
|
Chris@438
|
177 qRegisterMetaType<sv_frame_t>("sv_frame_t");
|
Chris@438
|
178 qRegisterMetaType<sv_samplerate_t>("sv_samplerate_t");
|
Chris@438
|
179
|
Chris@255
|
180 #ifdef Q_WS_X11
|
Chris@255
|
181 XSetErrorHandler(handle_x11_error);
|
Chris@255
|
182 #endif
|
Chris@255
|
183
|
Chris@452
|
184 connect(this, SIGNAL(hideSplash()), this, SLOT(emitHideSplash()));
|
Chris@452
|
185
|
Chris@45
|
186 connect(CommandHistory::getInstance(), SIGNAL(commandExecuted()),
|
Chris@595
|
187 this, SLOT(documentModified()));
|
Chris@45
|
188 connect(CommandHistory::getInstance(), SIGNAL(documentRestored()),
|
Chris@595
|
189 this, SLOT(documentRestored()));
|
Chris@45
|
190
|
Chris@591
|
191 SVDEBUG << "MainWindowBase: Creating view manager" << endl;
|
Chris@591
|
192
|
Chris@45
|
193 m_viewManager = new ViewManager();
|
Chris@45
|
194 connect(m_viewManager, SIGNAL(selectionChanged()),
|
Chris@595
|
195 this, SLOT(updateMenuStates()));
|
Chris@45
|
196 connect(m_viewManager, SIGNAL(inProgressSelectionChanged()),
|
Chris@595
|
197 this, SLOT(inProgressSelectionChanged()));
|
Chris@45
|
198
|
Chris@591
|
199 SVDEBUG << "MainWindowBase: Calculating view font size" << endl;
|
Chris@591
|
200
|
Chris@105
|
201 // set a sensible default font size for views -- cannot do this
|
Chris@105
|
202 // in Preferences, which is in base and not supposed to use QtGui
|
Chris@436
|
203 int viewFontSize = int(QApplication::font().pointSize() * 0.9);
|
Chris@105
|
204 QSettings settings;
|
Chris@105
|
205 settings.beginGroup("Preferences");
|
Chris@105
|
206 viewFontSize = settings.value("view-font-size", viewFontSize).toInt();
|
Chris@105
|
207 settings.setValue("view-font-size", viewFontSize);
|
Chris@105
|
208 settings.endGroup();
|
Chris@105
|
209
|
Chris@591
|
210 SVDEBUG << "MainWindowBase: View font size is " << viewFontSize << endl;
|
Chris@591
|
211
|
Chris@511
|
212 #ifdef NOT_DEFINED // This no longer works correctly on any platform AFAICS
|
Chris@45
|
213 Preferences::BackgroundMode mode =
|
Chris@45
|
214 Preferences::getInstance()->getBackgroundMode();
|
Chris@45
|
215 m_initialDarkBackground = m_viewManager->getGlobalDarkBackground();
|
Chris@45
|
216 if (mode != Preferences::BackgroundFromTheme) {
|
Chris@45
|
217 m_viewManager->setGlobalDarkBackground
|
Chris@45
|
218 (mode == Preferences::DarkBackground);
|
Chris@45
|
219 }
|
Chris@511
|
220 #endif
|
Chris@45
|
221
|
Chris@636
|
222 m_paneStack = new PaneStack(nullptr, m_viewManager);
|
Chris@45
|
223 connect(m_paneStack, SIGNAL(currentPaneChanged(Pane *)),
|
Chris@595
|
224 this, SLOT(currentPaneChanged(Pane *)));
|
Chris@45
|
225 connect(m_paneStack, SIGNAL(currentLayerChanged(Pane *, Layer *)),
|
Chris@595
|
226 this, SLOT(currentLayerChanged(Pane *, Layer *)));
|
Chris@45
|
227 connect(m_paneStack, SIGNAL(rightButtonMenuRequested(Pane *, QPoint)),
|
Chris@45
|
228 this, SLOT(rightButtonMenuRequested(Pane *, QPoint)));
|
Chris@45
|
229 connect(m_paneStack, SIGNAL(contextHelpChanged(const QString &)),
|
Chris@45
|
230 this, SLOT(contextHelpChanged(const QString &)));
|
Chris@45
|
231 connect(m_paneStack, SIGNAL(paneAdded(Pane *)),
|
Chris@45
|
232 this, SLOT(paneAdded(Pane *)));
|
Chris@45
|
233 connect(m_paneStack, SIGNAL(paneHidden(Pane *)),
|
Chris@45
|
234 this, SLOT(paneHidden(Pane *)));
|
Chris@45
|
235 connect(m_paneStack, SIGNAL(paneAboutToBeDeleted(Pane *)),
|
Chris@45
|
236 this, SLOT(paneAboutToBeDeleted(Pane *)));
|
Chris@45
|
237 connect(m_paneStack, SIGNAL(dropAccepted(Pane *, QStringList)),
|
Chris@45
|
238 this, SLOT(paneDropAccepted(Pane *, QStringList)));
|
Chris@45
|
239 connect(m_paneStack, SIGNAL(dropAccepted(Pane *, QString)),
|
Chris@45
|
240 this, SLOT(paneDropAccepted(Pane *, QString)));
|
Chris@55
|
241 connect(m_paneStack, SIGNAL(paneDeleteButtonClicked(Pane *)),
|
Chris@55
|
242 this, SLOT(paneDeleteButtonClicked(Pane *)));
|
Chris@591
|
243
|
Chris@591
|
244 SVDEBUG << "MainWindowBase: Creating play source" << endl;
|
Chris@571
|
245
|
Chris@574
|
246 m_playSource = new AudioCallbackPlaySource
|
Chris@574
|
247 (m_viewManager, QApplication::applicationName());
|
Chris@574
|
248
|
Chris@475
|
249 if (m_soundOptions & WithAudioInput) {
|
Chris@591
|
250 SVDEBUG << "MainWindowBase: Creating record target" << endl;
|
Chris@574
|
251 m_recordTarget = new AudioCallbackRecordTarget
|
Chris@574
|
252 (m_viewManager, QApplication::applicationName());
|
Chris@572
|
253 connect(m_recordTarget,
|
Chris@572
|
254 SIGNAL(recordDurationChanged(sv_frame_t, sv_samplerate_t)),
|
Chris@572
|
255 this,
|
Chris@572
|
256 SLOT(recordDurationChanged(sv_frame_t, sv_samplerate_t)));
|
Chris@475
|
257 }
|
Chris@45
|
258
|
Chris@436
|
259 connect(m_playSource, SIGNAL(sampleRateMismatch(sv_samplerate_t, sv_samplerate_t, bool)),
|
Chris@591
|
260 this, SLOT(sampleRateMismatch(sv_samplerate_t, sv_samplerate_t, bool)));
|
Chris@570
|
261 connect(m_playSource, SIGNAL(channelCountIncreased(int)),
|
Chris@570
|
262 this, SLOT(audioChannelCountIncreased(int)));
|
Chris@45
|
263 connect(m_playSource, SIGNAL(audioOverloadPluginDisabled()),
|
Chris@45
|
264 this, SLOT(audioOverloadPluginDisabled()));
|
Chris@130
|
265 connect(m_playSource, SIGNAL(audioTimeStretchMultiChannelDisabled()),
|
Chris@130
|
266 this, SLOT(audioTimeStretchMultiChannelDisabled()));
|
Chris@45
|
267
|
Chris@574
|
268 connect(m_viewManager, SIGNAL(monitoringLevelsChanged(float, float)),
|
Chris@595
|
269 this, SLOT(monitoringLevelsChanged(float, float)));
|
Chris@45
|
270
|
Chris@435
|
271 connect(m_viewManager, SIGNAL(playbackFrameChanged(sv_frame_t)),
|
Chris@435
|
272 this, SLOT(playbackFrameChanged(sv_frame_t)));
|
Chris@435
|
273
|
Chris@435
|
274 connect(m_viewManager, SIGNAL(globalCentreFrameChanged(sv_frame_t)),
|
Chris@435
|
275 this, SLOT(globalCentreFrameChanged(sv_frame_t)));
|
Chris@435
|
276
|
Chris@435
|
277 connect(m_viewManager, SIGNAL(viewCentreFrameChanged(View *, sv_frame_t)),
|
Chris@435
|
278 this, SLOT(viewCentreFrameChanged(View *, sv_frame_t)));
|
Chris@366
|
279
|
Chris@366
|
280 connect(m_viewManager, SIGNAL(viewZoomLevelChanged(View *, int, bool)),
|
Chris@366
|
281 this, SLOT(viewZoomLevelChanged(View *, int, bool)));
|
Chris@45
|
282
|
Chris@45
|
283 connect(Preferences::getInstance(),
|
Chris@45
|
284 SIGNAL(propertyChanged(PropertyContainer::PropertyName)),
|
Chris@45
|
285 this,
|
Chris@45
|
286 SLOT(preferenceChanged(PropertyContainer::PropertyName)));
|
Chris@45
|
287
|
Chris@591
|
288 SVDEBUG << "MainWindowBase: Creating labeller" << endl;
|
Chris@591
|
289
|
Chris@45
|
290 Labeller::ValueType labellerType = Labeller::ValueFromTwoLevelCounter;
|
Chris@45
|
291 settings.beginGroup("MainWindow");
|
Chris@230
|
292
|
Chris@45
|
293 labellerType = (Labeller::ValueType)
|
Chris@45
|
294 settings.value("labellertype", (int)labellerType).toInt();
|
Chris@45
|
295 int cycle = settings.value("labellercycle", 4).toInt();
|
Chris@230
|
296
|
Chris@45
|
297 settings.endGroup();
|
Chris@45
|
298
|
Chris@45
|
299 m_labeller = new Labeller(labellerType);
|
Chris@45
|
300 m_labeller->setCounterCycleSize(cycle);
|
Chris@113
|
301
|
Chris@475
|
302 if (m_soundOptions & WithMIDIInput) {
|
Chris@591
|
303 SVDEBUG << "MainWindowBase: Creating MIDI input" << endl;
|
Chris@161
|
304 m_midiInput = new MIDIInput(QApplication::applicationName(), this);
|
Chris@161
|
305 }
|
Chris@452
|
306
|
Chris@452
|
307 QTimer::singleShot(1500, this, SIGNAL(hideSplash()));
|
Chris@591
|
308
|
Chris@591
|
309 SVDEBUG << "MainWindowBase: Constructor done" << endl;
|
Chris@45
|
310 }
|
Chris@45
|
311
|
Chris@45
|
312 MainWindowBase::~MainWindowBase()
|
Chris@45
|
313 {
|
Chris@233
|
314 SVDEBUG << "MainWindowBase::~MainWindowBase" << endl;
|
Chris@540
|
315
|
Chris@540
|
316 // We have to delete the breakfastquay::SystemPlaybackTarget or
|
Chris@540
|
317 // breakfastquay::SystemAudioIO object (whichever we have -- it
|
Chris@540
|
318 // depends on whether we handle recording or not) before we delete
|
Chris@540
|
319 // the ApplicationPlaybackSource and ApplicationRecordTarget that
|
Chris@540
|
320 // they refer to.
|
Chris@556
|
321
|
Chris@556
|
322 deleteAudioIO();
|
Chris@540
|
323
|
Chris@540
|
324 // Then delete the Application objects.
|
Chris@45
|
325 delete m_playSource;
|
Chris@475
|
326 delete m_recordTarget;
|
Chris@540
|
327
|
Chris@45
|
328 delete m_viewManager;
|
cannam@632
|
329 delete m_midiInput;
|
cannam@632
|
330
|
Chris@636
|
331 disconnect(m_oscQueueStarter, nullptr, nullptr, nullptr);
|
cannam@632
|
332 m_oscQueueStarter->wait(1000);
|
cannam@632
|
333 if (m_oscQueueStarter->isRunning()) {
|
cannam@632
|
334 m_oscQueueStarter->terminate();
|
cannam@632
|
335 m_oscQueueStarter->wait(1000);
|
cannam@632
|
336 }
|
cannam@632
|
337 delete m_oscQueueStarter;
|
Chris@45
|
338 delete m_oscQueue;
|
cannam@632
|
339
|
Chris@45
|
340 Profiles::getInstance()->dump();
|
Chris@45
|
341 }
|
Chris@45
|
342
|
Chris@113
|
343 void
|
Chris@452
|
344 MainWindowBase::emitHideSplash()
|
Chris@452
|
345 {
|
Chris@591
|
346 SVDEBUG << "MainWindowBase: Hiding splash screen" << endl;
|
Chris@452
|
347 emit hideSplash(this);
|
Chris@452
|
348 }
|
Chris@452
|
349
|
Chris@452
|
350 void
|
Chris@354
|
351 MainWindowBase::finaliseMenus()
|
Chris@354
|
352 {
|
Chris@591
|
353 SVDEBUG << "MainWindowBase::finaliseMenus called" << endl;
|
Chris@591
|
354
|
Chris@390
|
355 delete m_menuShortcutMapper;
|
Chris@636
|
356 m_menuShortcutMapper = nullptr;
|
Chris@390
|
357
|
Chris@391
|
358 foreach (QShortcut *sc, m_appShortcuts) {
|
Chris@391
|
359 delete sc;
|
Chris@391
|
360 }
|
Chris@391
|
361 m_appShortcuts.clear();
|
Chris@391
|
362
|
Chris@354
|
363 QMenuBar *mb = menuBar();
|
Chris@394
|
364
|
Chris@396
|
365 // This used to find all children of QMenu type, and call
|
Chris@396
|
366 // finaliseMenu on those. But it seems we are getting hold of some
|
Chris@396
|
367 // menus that way that are not actually active in the menu bar and
|
Chris@396
|
368 // are not returned in their parent menu's actions() list, and if
|
Chris@396
|
369 // we finalise those, we end up with duplicate shortcuts in the
|
Chris@396
|
370 // app shortcut mapper. So we should do this by descending the
|
Chris@396
|
371 // menu tree through only those menus accessible via actions()
|
Chris@396
|
372 // from their parents instead.
|
Chris@396
|
373
|
Chris@394
|
374 QList<QMenu *> menus = mb->findChildren<QMenu *>
|
Chris@394
|
375 (QString(), Qt::FindDirectChildrenOnly);
|
Chris@394
|
376
|
Chris@354
|
377 foreach (QMenu *menu, menus) {
|
Chris@354
|
378 if (menu) finaliseMenu(menu);
|
Chris@354
|
379 }
|
Chris@591
|
380
|
Chris@591
|
381 SVDEBUG << "MainWindowBase::finaliseMenus done" << endl;
|
Chris@354
|
382 }
|
Chris@354
|
383
|
Chris@354
|
384 void
|
Chris@426
|
385 MainWindowBase::finaliseMenu(QMenu *menu)
|
Chris@354
|
386 {
|
Chris@426
|
387 foreach (QAction *a, menu->actions()) {
|
Chris@426
|
388 a->setIconVisibleInMenu(m_iconsVisibleInMenus);
|
Chris@426
|
389 }
|
Chris@426
|
390
|
Chris@354
|
391 #ifdef Q_OS_MAC
|
Chris@354
|
392 // See https://bugreports.qt-project.org/browse/QTBUG-38256 and
|
Chris@354
|
393 // our issue #890 http://code.soundsoftware.ac.uk/issues/890 --
|
Chris@354
|
394 // single-key shortcuts that are associated only with a menu
|
Chris@384
|
395 // action (and not with a toolbar button) do not work with Qt 5.x
|
Chris@384
|
396 // under OS/X.
|
Chris@354
|
397 //
|
Chris@354
|
398 // Apparently Cocoa never handled them as a matter of course, but
|
Chris@354
|
399 // earlier versions of Qt picked them up as widget shortcuts and
|
Chris@354
|
400 // handled them anyway. That behaviour was removed to fix a crash
|
Chris@354
|
401 // when invoking a menu while its window was overridden by a modal
|
Chris@354
|
402 // dialog (https://bugreports.qt-project.org/browse/QTBUG-30657).
|
Chris@354
|
403 //
|
Chris@354
|
404 // This workaround restores the single-key shortcut behaviour by
|
Chris@384
|
405 // searching in menus for single-key shortcuts that are associated
|
Chris@384
|
406 // only with the menu and not with a toolbar button, and
|
Chris@384
|
407 // augmenting them with global application shortcuts that invoke
|
Chris@384
|
408 // the relevant actions, testing whether the actions are enabled
|
Chris@384
|
409 // on invocation.
|
Chris@354
|
410 //
|
Chris@384
|
411 // (Previously this acted on all single-key shortcuts in menus,
|
Chris@384
|
412 // and it removed the shortcut from the action when it created
|
Chris@384
|
413 // each new global one, in order to avoid an "ambiguous shortcut"
|
Chris@384
|
414 // error in the case where the action was also associated with a
|
Chris@384
|
415 // toolbar button. But that has the unwelcome side-effect of
|
Chris@384
|
416 // removing the shortcut hint from the menu entry. So now we leave
|
Chris@384
|
417 // the shortcut in the menu action as well as creating a global
|
Chris@384
|
418 // one, and we only act on shortcuts that have no toolbar button,
|
Chris@384
|
419 // i.e. that will not otherwise work. The downside is that if this
|
Chris@384
|
420 // bug is fixed in a future Qt release, we will start getting
|
Chris@384
|
421 // "ambiguous shortcut" errors from the menu entry actions and
|
Chris@384
|
422 // will need to update the code.)
|
Chris@354
|
423
|
Chris@443
|
424 // Update: The bug was fixed in Qt 5.4 for shortcuts with no
|
Chris@443
|
425 // modifier, and I believe it is fixed in Qt 5.5 for shortcuts
|
Chris@443
|
426 // with Shift modifiers. The below reflects that
|
Chris@443
|
427
|
Chris@443
|
428 #if (QT_VERSION < QT_VERSION_CHECK(5, 5, 0))
|
Chris@443
|
429
|
Chris@390
|
430 if (!m_menuShortcutMapper) {
|
Chris@390
|
431 m_menuShortcutMapper = new QSignalMapper(this);
|
Chris@392
|
432 connect(m_menuShortcutMapper, SIGNAL(mapped(QObject *)),
|
Chris@392
|
433 this, SLOT(menuActionMapperInvoked(QObject *)));
|
Chris@390
|
434 }
|
Chris@390
|
435
|
Chris@354
|
436 foreach (QAction *a, menu->actions()) {
|
Chris@394
|
437
|
Chris@394
|
438 if (a->isSeparator()) {
|
Chris@394
|
439 continue;
|
Chris@394
|
440 } else if (a->menu()) {
|
Chris@394
|
441 finaliseMenu(a->menu());
|
Chris@394
|
442 } else {
|
Chris@394
|
443
|
Chris@394
|
444 QWidgetList ww = a->associatedWidgets();
|
Chris@394
|
445 bool hasButton = false;
|
Chris@394
|
446 foreach (QWidget *w, ww) {
|
Chris@394
|
447 if (qobject_cast<QAbstractButton *>(w)) {
|
Chris@394
|
448 hasButton = true;
|
Chris@394
|
449 break;
|
Chris@394
|
450 }
|
Chris@394
|
451 }
|
Chris@394
|
452 if (hasButton) continue;
|
Chris@394
|
453 QKeySequence sc = a->shortcut();
|
Chris@399
|
454
|
Chris@399
|
455 // Note that the set of "single-key shortcuts" that aren't
|
Chris@399
|
456 // working and that we need to handle here includes those
|
Chris@399
|
457 // with the Shift modifier mask as well as those with no
|
Chris@399
|
458 // modifier at all
|
Chris@443
|
459 #if (QT_VERSION >= QT_VERSION_CHECK(5, 5, 0))
|
Chris@443
|
460 // Nothing needed
|
Chris@443
|
461 if (false) {
|
Chris@443
|
462 #elif (QT_VERSION >= QT_VERSION_CHECK(5, 4, 0))
|
Chris@443
|
463 if (sc.count() == 1 &&
|
Chris@443
|
464 (sc[0] & Qt::KeyboardModifierMask) == Qt::ShiftModifier) {
|
Chris@443
|
465 #else
|
Chris@399
|
466 if (sc.count() == 1 &&
|
Chris@399
|
467 ((sc[0] & Qt::KeyboardModifierMask) == Qt::NoModifier ||
|
Chris@399
|
468 (sc[0] & Qt::KeyboardModifierMask) == Qt::ShiftModifier)) {
|
Chris@443
|
469 #endif
|
Chris@394
|
470 QShortcut *newSc = new QShortcut(sc, a->parentWidget());
|
Chris@394
|
471 QObject::connect(newSc, SIGNAL(activated()),
|
Chris@394
|
472 m_menuShortcutMapper, SLOT(map()));
|
Chris@394
|
473 m_menuShortcutMapper->setMapping(newSc, a);
|
Chris@394
|
474 m_appShortcuts.push_back(newSc);
|
Chris@384
|
475 }
|
Chris@384
|
476 }
|
Chris@354
|
477 }
|
Chris@354
|
478 #endif
|
Chris@443
|
479 #endif
|
Chris@354
|
480 }
|
Chris@354
|
481
|
Chris@354
|
482 void
|
Chris@354
|
483 MainWindowBase::menuActionMapperInvoked(QObject *o)
|
Chris@354
|
484 {
|
Chris@354
|
485 QAction *a = qobject_cast<QAction *>(o);
|
Chris@354
|
486 if (a && a->isEnabled()) {
|
Chris@354
|
487 a->trigger();
|
Chris@354
|
488 }
|
Chris@354
|
489 }
|
Chris@354
|
490
|
Chris@354
|
491 void
|
Chris@168
|
492 MainWindowBase::resizeConstrained(QSize size)
|
Chris@168
|
493 {
|
Chris@168
|
494 QDesktopWidget *desktop = QApplication::desktop();
|
Chris@168
|
495 QRect available = desktop->availableGeometry();
|
Chris@168
|
496 QSize actual(std::min(size.width(), available.width()),
|
Chris@168
|
497 std::min(size.height(), available.height()));
|
Chris@168
|
498 resize(actual);
|
Chris@168
|
499 }
|
Chris@168
|
500
|
Chris@168
|
501 void
|
Chris@304
|
502 MainWindowBase::startOSCQueue()
|
Chris@304
|
503 {
|
Chris@304
|
504 m_oscQueueStarter = new OSCQueueStarter(this);
|
Chris@304
|
505 connect(m_oscQueueStarter, SIGNAL(finished()), this, SLOT(oscReady()));
|
Chris@304
|
506 m_oscQueueStarter->start();
|
Chris@304
|
507 }
|
Chris@304
|
508
|
Chris@304
|
509 void
|
Chris@113
|
510 MainWindowBase::oscReady()
|
Chris@113
|
511 {
|
Chris@113
|
512 if (m_oscQueue && m_oscQueue->isOK()) {
|
Chris@113
|
513 connect(m_oscQueue, SIGNAL(messagesAvailable()), this, SLOT(pollOSC()));
|
Chris@113
|
514 QTimer *oscTimer = new QTimer(this);
|
Chris@113
|
515 connect(oscTimer, SIGNAL(timeout()), this, SLOT(pollOSC()));
|
Chris@113
|
516 oscTimer->start(1000);
|
Chris@571
|
517 SVCERR << "Finished setting up OSC interface" << endl;
|
Chris@113
|
518 }
|
Chris@113
|
519 }
|
Chris@113
|
520
|
Chris@45
|
521 QString
|
Chris@45
|
522 MainWindowBase::getOpenFileName(FileFinder::FileType type)
|
Chris@45
|
523 {
|
Chris@45
|
524 FileFinder *ff = FileFinder::getInstance();
|
Chris@358
|
525
|
Chris@358
|
526 if (type == FileFinder::AnyFile) {
|
Chris@636
|
527 if (getMainModel() != nullptr &&
|
Chris@636
|
528 m_paneStack != nullptr &&
|
Chris@636
|
529 m_paneStack->getCurrentPane() != nullptr) { // can import a layer
|
Chris@45
|
530 return ff->getOpenFileName(FileFinder::AnyFile, m_sessionFile);
|
Chris@45
|
531 } else {
|
Chris@45
|
532 return ff->getOpenFileName(FileFinder::SessionOrAudioFile,
|
Chris@45
|
533 m_sessionFile);
|
Chris@45
|
534 }
|
Chris@358
|
535 }
|
Chris@358
|
536
|
Chris@358
|
537 QString lastPath = m_sessionFile;
|
Chris@358
|
538
|
Chris@358
|
539 if (type == FileFinder::AudioFile) {
|
Chris@358
|
540 lastPath = m_audioFile;
|
Chris@45
|
541 }
|
Chris@358
|
542
|
Chris@358
|
543 return ff->getOpenFileName(type, lastPath);
|
Chris@45
|
544 }
|
Chris@45
|
545
|
Chris@45
|
546 QString
|
Chris@45
|
547 MainWindowBase::getSaveFileName(FileFinder::FileType type)
|
Chris@45
|
548 {
|
Chris@358
|
549 QString lastPath = m_sessionFile;
|
Chris@358
|
550
|
Chris@358
|
551 if (type == FileFinder::AudioFile) {
|
Chris@358
|
552 lastPath = m_audioFile;
|
Chris@358
|
553 }
|
Chris@358
|
554
|
Chris@45
|
555 FileFinder *ff = FileFinder::getInstance();
|
Chris@358
|
556 return ff->getSaveFileName(type, lastPath);
|
Chris@45
|
557 }
|
Chris@45
|
558
|
Chris@45
|
559 void
|
Chris@45
|
560 MainWindowBase::registerLastOpenedFilePath(FileFinder::FileType type, QString path)
|
Chris@45
|
561 {
|
Chris@45
|
562 FileFinder *ff = FileFinder::getInstance();
|
Chris@45
|
563 ff->registerLastOpenedFilePath(type, path);
|
Chris@45
|
564 }
|
Chris@45
|
565
|
Chris@222
|
566 QString
|
Chris@222
|
567 MainWindowBase::getDefaultSessionTemplate() const
|
Chris@222
|
568 {
|
Chris@231
|
569 QSettings settings;
|
Chris@231
|
570 settings.beginGroup("MainWindow");
|
Chris@231
|
571 QString templateName = settings.value("sessiontemplate", "").toString();
|
Chris@231
|
572 if (templateName == "") templateName = "default";
|
Chris@231
|
573 return templateName;
|
Chris@222
|
574 }
|
Chris@222
|
575
|
Chris@222
|
576 void
|
Chris@251
|
577 MainWindowBase::setDefaultSessionTemplate(QString n)
|
Chris@251
|
578 {
|
Chris@251
|
579 QSettings settings;
|
Chris@251
|
580 settings.beginGroup("MainWindow");
|
Chris@251
|
581 settings.setValue("sessiontemplate", n);
|
Chris@251
|
582 }
|
Chris@251
|
583
|
Chris@251
|
584 void
|
Chris@45
|
585 MainWindowBase::updateMenuStates()
|
Chris@45
|
586 {
|
Chris@636
|
587 Pane *currentPane = nullptr;
|
Chris@636
|
588 Layer *currentLayer = nullptr;
|
Chris@45
|
589
|
Chris@45
|
590 if (m_paneStack) currentPane = m_paneStack->getCurrentPane();
|
Chris@45
|
591 if (currentPane) currentLayer = currentPane->getSelectedLayer();
|
Chris@45
|
592
|
Chris@73
|
593 bool havePrevPane = false, haveNextPane = false;
|
Chris@73
|
594 bool havePrevLayer = false, haveNextLayer = false;
|
Chris@73
|
595
|
Chris@73
|
596 if (currentPane) {
|
Chris@73
|
597 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
|
Chris@73
|
598 if (m_paneStack->getPane(i) == currentPane) {
|
Chris@73
|
599 if (i > 0) havePrevPane = true;
|
Chris@73
|
600 if (i < m_paneStack->getPaneCount()-1) haveNextPane = true;
|
Chris@73
|
601 break;
|
Chris@73
|
602 }
|
Chris@73
|
603 }
|
Chris@403
|
604 // the prev/next layer commands actually include the pane
|
Chris@403
|
605 // itself as one of the selectables -- so we always have a
|
Chris@403
|
606 // prev and next layer, as long as we have a pane with at
|
Chris@403
|
607 // least one layer in it
|
Chris@403
|
608 if (currentPane->getLayerCount() > 0) {
|
Chris@403
|
609 havePrevLayer = true;
|
Chris@403
|
610 haveNextLayer = true;
|
Chris@73
|
611 }
|
Chris@73
|
612 }
|
Chris@73
|
613
|
Chris@45
|
614 bool haveCurrentPane =
|
Chris@636
|
615 (currentPane != nullptr);
|
Chris@45
|
616 bool haveCurrentLayer =
|
Chris@45
|
617 (haveCurrentPane &&
|
Chris@636
|
618 (currentLayer != nullptr));
|
Chris@45
|
619 bool haveMainModel =
|
Chris@636
|
620 (getMainModel() != nullptr);
|
Chris@45
|
621 bool havePlayTarget =
|
Chris@636
|
622 (m_playTarget != nullptr || m_audioIO != nullptr);
|
Chris@45
|
623 bool haveSelection =
|
Chris@595
|
624 (m_viewManager &&
|
Chris@595
|
625 !m_viewManager->getSelections().empty());
|
Chris@45
|
626 bool haveCurrentEditableLayer =
|
Chris@595
|
627 (haveCurrentLayer &&
|
Chris@595
|
628 currentLayer->isLayerEditable());
|
Chris@45
|
629 bool haveCurrentTimeInstantsLayer =
|
Chris@595
|
630 (haveCurrentLayer &&
|
Chris@595
|
631 dynamic_cast<TimeInstantLayer *>(currentLayer));
|
Chris@184
|
632 bool haveCurrentDurationLayer =
|
Chris@595
|
633 (haveCurrentLayer &&
|
Chris@595
|
634 (dynamic_cast<NoteLayer *>(currentLayer) ||
|
Chris@595
|
635 dynamic_cast<FlexiNoteLayer *>(currentLayer) ||
|
Chris@184
|
636 dynamic_cast<RegionLayer *>(currentLayer)));
|
Chris@45
|
637 bool haveCurrentColour3DPlot =
|
Chris@45
|
638 (haveCurrentLayer &&
|
Chris@45
|
639 dynamic_cast<Colour3DPlotLayer *>(currentLayer));
|
Chris@45
|
640 bool haveClipboardContents =
|
Chris@45
|
641 (m_viewManager &&
|
Chris@45
|
642 !m_viewManager->getClipboard().empty());
|
Chris@146
|
643 bool haveTabularLayer =
|
Chris@146
|
644 (haveCurrentLayer &&
|
Chris@146
|
645 dynamic_cast<TabularModel *>(currentLayer->getModel()));
|
Chris@45
|
646
|
Chris@45
|
647 emit canAddPane(haveMainModel);
|
Chris@45
|
648 emit canDeleteCurrentPane(haveCurrentPane);
|
Chris@45
|
649 emit canZoom(haveMainModel && haveCurrentPane);
|
Chris@45
|
650 emit canScroll(haveMainModel && haveCurrentPane);
|
Chris@45
|
651 emit canAddLayer(haveMainModel && haveCurrentPane);
|
Chris@45
|
652 emit canImportMoreAudio(haveMainModel);
|
Chris@259
|
653 emit canReplaceMainAudio(haveMainModel);
|
Chris@45
|
654 emit canImportLayer(haveMainModel && haveCurrentPane);
|
Chris@45
|
655 emit canExportAudio(haveMainModel);
|
Chris@289
|
656 emit canChangeSessionTemplate(haveMainModel);
|
Chris@45
|
657 emit canExportLayer(haveMainModel &&
|
Chris@45
|
658 (haveCurrentEditableLayer || haveCurrentColour3DPlot));
|
Chris@45
|
659 emit canExportImage(haveMainModel && haveCurrentPane);
|
Chris@45
|
660 emit canDeleteCurrentLayer(haveCurrentLayer);
|
Chris@45
|
661 emit canRenameLayer(haveCurrentLayer);
|
Chris@45
|
662 emit canEditLayer(haveCurrentEditableLayer);
|
Chris@146
|
663 emit canEditLayerTabular(haveCurrentEditableLayer || haveTabularLayer);
|
Chris@45
|
664 emit canMeasureLayer(haveCurrentLayer);
|
Chris@45
|
665 emit canSelect(haveMainModel && haveCurrentPane);
|
Chris@188
|
666 emit canPlay(haveMainModel && havePlayTarget);
|
Chris@453
|
667 emit canFfwd(haveMainModel);
|
Chris@453
|
668 emit canRewind(haveMainModel);
|
Chris@87
|
669 emit canPaste(haveClipboardContents);
|
Chris@45
|
670 emit canInsertInstant(haveCurrentPane);
|
Chris@45
|
671 emit canInsertInstantsAtBoundaries(haveCurrentPane && haveSelection);
|
Chris@184
|
672 emit canInsertItemAtSelection(haveCurrentPane && haveSelection && haveCurrentDurationLayer);
|
Chris@45
|
673 emit canRenumberInstants(haveCurrentTimeInstantsLayer && haveSelection);
|
Chris@537
|
674 emit canSubdivideInstants(haveCurrentTimeInstantsLayer && haveSelection);
|
Chris@538
|
675 emit canWinnowInstants(haveCurrentTimeInstantsLayer && haveSelection);
|
Chris@45
|
676 emit canPlaySelection(haveMainModel && havePlayTarget && haveSelection);
|
Chris@45
|
677 emit canClearSelection(haveSelection);
|
Chris@45
|
678 emit canEditSelection(haveSelection && haveCurrentEditableLayer);
|
Chris@45
|
679 emit canSave(m_sessionFile != "" && m_documentModified);
|
Chris@601
|
680 emit canSaveAs(haveMainModel); // possibly used only in Tony, not SV
|
Chris@73
|
681 emit canSelectPreviousPane(havePrevPane);
|
Chris@73
|
682 emit canSelectNextPane(haveNextPane);
|
Chris@73
|
683 emit canSelectPreviousLayer(havePrevLayer);
|
Chris@73
|
684 emit canSelectNextLayer(haveNextLayer);
|
Chris@586
|
685
|
Chris@586
|
686 // This is quite subtle -- whereas we can play back only if a
|
Chris@586
|
687 // system play target or I/O exists, we can record even if no
|
Chris@586
|
688 // record source (i.e. audioIO) exists because we can record into
|
Chris@586
|
689 // an empty session before the audio device has been
|
Chris@586
|
690 // opened. However, if there is no record *target* then recording
|
Chris@586
|
691 // was actively disabled (flag not set in m_soundOptions). And if
|
Chris@586
|
692 // we have a play target instead of an audioIO, then we must have
|
Chris@586
|
693 // tried to open the device but failed to find any capture source.
|
Chris@586
|
694 bool recordDisabled = (m_recordTarget == nullptr);
|
Chris@586
|
695 bool recordDeviceFailed = (m_playTarget != nullptr && m_audioIO == nullptr);
|
Chris@586
|
696 emit canRecord(!recordDisabled && !recordDeviceFailed);
|
Chris@45
|
697 }
|
Chris@45
|
698
|
Chris@45
|
699 void
|
Chris@45
|
700 MainWindowBase::documentModified()
|
Chris@45
|
701 {
|
Chris@233
|
702 // SVDEBUG << "MainWindowBase::documentModified" << endl;
|
Chris@45
|
703
|
Chris@45
|
704 if (!m_documentModified) {
|
Chris@45
|
705 //!!! this in subclass implementation?
|
Chris@595
|
706 setWindowTitle(tr("%1 (modified)").arg(windowTitle()));
|
Chris@45
|
707 }
|
Chris@45
|
708
|
Chris@45
|
709 m_documentModified = true;
|
Chris@45
|
710 updateMenuStates();
|
Chris@45
|
711 }
|
Chris@45
|
712
|
Chris@45
|
713 void
|
Chris@45
|
714 MainWindowBase::documentRestored()
|
Chris@45
|
715 {
|
Chris@233
|
716 // SVDEBUG << "MainWindowBase::documentRestored" << endl;
|
Chris@45
|
717
|
Chris@45
|
718 if (m_documentModified) {
|
Chris@45
|
719 //!!! this in subclass implementation?
|
Chris@595
|
720 QString wt(windowTitle());
|
Chris@595
|
721 wt.replace(tr(" (modified)"), "");
|
Chris@595
|
722 setWindowTitle(wt);
|
Chris@45
|
723 }
|
Chris@45
|
724
|
Chris@45
|
725 m_documentModified = false;
|
Chris@45
|
726 updateMenuStates();
|
Chris@45
|
727 }
|
Chris@45
|
728
|
Chris@45
|
729 void
|
Chris@45
|
730 MainWindowBase::playLoopToggled()
|
Chris@45
|
731 {
|
Chris@45
|
732 QAction *action = dynamic_cast<QAction *>(sender());
|
Chris@45
|
733
|
Chris@45
|
734 if (action) {
|
Chris@595
|
735 m_viewManager->setPlayLoopMode(action->isChecked());
|
Chris@45
|
736 } else {
|
Chris@595
|
737 m_viewManager->setPlayLoopMode(!m_viewManager->getPlayLoopMode());
|
Chris@45
|
738 }
|
Chris@45
|
739 }
|
Chris@45
|
740
|
Chris@45
|
741 void
|
Chris@45
|
742 MainWindowBase::playSelectionToggled()
|
Chris@45
|
743 {
|
Chris@45
|
744 QAction *action = dynamic_cast<QAction *>(sender());
|
Chris@45
|
745
|
Chris@45
|
746 if (action) {
|
Chris@595
|
747 m_viewManager->setPlaySelectionMode(action->isChecked());
|
Chris@45
|
748 } else {
|
Chris@595
|
749 m_viewManager->setPlaySelectionMode(!m_viewManager->getPlaySelectionMode());
|
Chris@45
|
750 }
|
Chris@45
|
751 }
|
Chris@45
|
752
|
Chris@45
|
753 void
|
Chris@45
|
754 MainWindowBase::playSoloToggled()
|
Chris@45
|
755 {
|
Chris@45
|
756 QAction *action = dynamic_cast<QAction *>(sender());
|
Chris@45
|
757
|
Chris@45
|
758 if (action) {
|
Chris@595
|
759 m_viewManager->setPlaySoloMode(action->isChecked());
|
Chris@45
|
760 } else {
|
Chris@595
|
761 m_viewManager->setPlaySoloMode(!m_viewManager->getPlaySoloMode());
|
Chris@45
|
762 }
|
Chris@45
|
763
|
Chris@45
|
764 if (m_viewManager->getPlaySoloMode()) {
|
Chris@45
|
765 currentPaneChanged(m_paneStack->getCurrentPane());
|
Chris@45
|
766 } else {
|
Chris@636
|
767 m_viewManager->setPlaybackModel(nullptr);
|
Chris@45
|
768 if (m_playSource) {
|
Chris@45
|
769 m_playSource->clearSoloModelSet();
|
Chris@45
|
770 }
|
Chris@45
|
771 }
|
Chris@45
|
772 }
|
Chris@45
|
773
|
Chris@45
|
774 void
|
Chris@45
|
775 MainWindowBase::currentPaneChanged(Pane *p)
|
Chris@45
|
776 {
|
Chris@45
|
777 updateMenuStates();
|
Chris@45
|
778 updateVisibleRangeDisplay(p);
|
Chris@45
|
779
|
Chris@45
|
780 if (!p) return;
|
Chris@45
|
781
|
Chris@45
|
782 if (!(m_viewManager &&
|
Chris@45
|
783 m_playSource &&
|
Chris@45
|
784 m_viewManager->getPlaySoloMode())) {
|
Chris@636
|
785 if (m_viewManager) m_viewManager->setPlaybackModel(nullptr);
|
Chris@45
|
786 return;
|
Chris@45
|
787 }
|
Chris@45
|
788
|
Chris@45
|
789 Model *prevPlaybackModel = m_viewManager->getPlaybackModel();
|
Chris@60
|
790
|
Chris@93
|
791 // What we want here is not the currently playing frame (unless we
|
Chris@93
|
792 // are about to clear out the audio playback buffers -- which may
|
Chris@93
|
793 // or may not be possible, depending on the audio driver). What
|
Chris@93
|
794 // we want is the frame that was last committed to the soundcard
|
Chris@93
|
795 // buffers, as the audio driver will continue playing up to that
|
Chris@93
|
796 // frame before switching to whichever one we decide we want to
|
Chris@93
|
797 // switch to, regardless of our efforts.
|
Chris@93
|
798
|
Chris@435
|
799 sv_frame_t frame = m_playSource->getCurrentBufferedFrame();
|
Chris@93
|
800
|
Chris@388
|
801 cerr << "currentPaneChanged: current frame (in ref model) = " << frame << endl;
|
Chris@45
|
802
|
Chris@45
|
803 View::ModelSet soloModels = p->getModels();
|
Chris@45
|
804
|
Chris@57
|
805 View::ModelSet sources;
|
Chris@57
|
806 for (View::ModelSet::iterator mi = soloModels.begin();
|
Chris@57
|
807 mi != soloModels.end(); ++mi) {
|
Chris@190
|
808 // If a model in this pane is derived from something else,
|
Chris@190
|
809 // then we want to play that model as well -- if the model
|
Chris@190
|
810 // that's derived from it is not something that is itself
|
Chris@190
|
811 // individually playable (e.g. a waveform)
|
Chris@190
|
812 if (*mi &&
|
Chris@190
|
813 !dynamic_cast<RangeSummarisableTimeValueModel *>(*mi) &&
|
Chris@190
|
814 (*mi)->getSourceModel()) {
|
Chris@57
|
815 sources.insert((*mi)->getSourceModel());
|
Chris@57
|
816 }
|
Chris@57
|
817 }
|
Chris@57
|
818 for (View::ModelSet::iterator mi = sources.begin();
|
Chris@57
|
819 mi != sources.end(); ++mi) {
|
Chris@57
|
820 soloModels.insert(*mi);
|
Chris@57
|
821 }
|
Chris@57
|
822
|
Chris@60
|
823 //!!! Need an "atomic" way of telling the play source that the
|
Chris@60
|
824 //playback model has changed, and changing it on ViewManager --
|
Chris@60
|
825 //the play source should be making the setPlaybackModel call to
|
Chris@60
|
826 //ViewManager
|
Chris@60
|
827
|
Chris@45
|
828 for (View::ModelSet::iterator mi = soloModels.begin();
|
Chris@45
|
829 mi != soloModels.end(); ++mi) {
|
Chris@45
|
830 if (dynamic_cast<RangeSummarisableTimeValueModel *>(*mi)) {
|
Chris@45
|
831 m_viewManager->setPlaybackModel(*mi);
|
Chris@45
|
832 }
|
Chris@45
|
833 }
|
Chris@45
|
834
|
Chris@45
|
835 RangeSummarisableTimeValueModel *a =
|
Chris@45
|
836 dynamic_cast<RangeSummarisableTimeValueModel *>(prevPlaybackModel);
|
Chris@45
|
837 RangeSummarisableTimeValueModel *b =
|
Chris@45
|
838 dynamic_cast<RangeSummarisableTimeValueModel *>(m_viewManager->
|
Chris@45
|
839 getPlaybackModel());
|
Chris@45
|
840
|
Chris@45
|
841 m_playSource->setSoloModelSet(soloModels);
|
Chris@45
|
842
|
Chris@45
|
843 if (a && b && (a != b)) {
|
Chris@60
|
844 if (m_playSource->isPlaying()) m_playSource->play(frame);
|
Chris@45
|
845 }
|
Chris@45
|
846 }
|
Chris@45
|
847
|
Chris@45
|
848 void
|
Chris@45
|
849 MainWindowBase::currentLayerChanged(Pane *p, Layer *)
|
Chris@45
|
850 {
|
Chris@45
|
851 updateMenuStates();
|
Chris@45
|
852 updateVisibleRangeDisplay(p);
|
Chris@45
|
853 }
|
Chris@45
|
854
|
Chris@597
|
855 sv_frame_t
|
Chris@597
|
856 MainWindowBase::getModelsStartFrame() const
|
Chris@597
|
857 {
|
Chris@597
|
858 sv_frame_t startFrame = 0;
|
Chris@597
|
859 if (!m_paneStack) return startFrame;
|
Chris@597
|
860 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
|
Chris@597
|
861 sv_frame_t thisStart = m_paneStack->getPane(i)->getModelsStartFrame();
|
Chris@597
|
862 if (i == 0 || thisStart < startFrame) {
|
Chris@597
|
863 startFrame = thisStart;
|
Chris@597
|
864 }
|
Chris@597
|
865 }
|
Chris@597
|
866 return startFrame;
|
Chris@597
|
867 }
|
Chris@597
|
868
|
Chris@597
|
869 sv_frame_t
|
Chris@597
|
870 MainWindowBase::getModelsEndFrame() const
|
Chris@597
|
871 {
|
Chris@597
|
872 sv_frame_t endFrame = 0;
|
Chris@597
|
873 if (!m_paneStack) return endFrame;
|
Chris@597
|
874 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
|
Chris@597
|
875 sv_frame_t thisEnd = m_paneStack->getPane(i)->getModelsEndFrame();
|
Chris@597
|
876 if (i == 0 || thisEnd > endFrame) {
|
Chris@597
|
877 endFrame = thisEnd;
|
Chris@597
|
878 }
|
Chris@597
|
879 }
|
Chris@597
|
880 return endFrame;
|
Chris@597
|
881 }
|
Chris@597
|
882
|
Chris@45
|
883 void
|
Chris@45
|
884 MainWindowBase::selectAll()
|
Chris@45
|
885 {
|
Chris@45
|
886 if (!getMainModel()) return;
|
Chris@597
|
887 m_viewManager->setSelection(Selection(getModelsStartFrame(),
|
Chris@597
|
888 getModelsEndFrame()));
|
Chris@45
|
889 }
|
Chris@45
|
890
|
Chris@45
|
891 void
|
Chris@45
|
892 MainWindowBase::selectToStart()
|
Chris@45
|
893 {
|
Chris@45
|
894 if (!getMainModel()) return;
|
Chris@45
|
895 m_viewManager->setSelection(Selection(getMainModel()->getStartFrame(),
|
Chris@595
|
896 m_viewManager->getGlobalCentreFrame()));
|
Chris@45
|
897 }
|
Chris@45
|
898
|
Chris@45
|
899 void
|
Chris@45
|
900 MainWindowBase::selectToEnd()
|
Chris@45
|
901 {
|
Chris@45
|
902 if (!getMainModel()) return;
|
Chris@45
|
903 m_viewManager->setSelection(Selection(m_viewManager->getGlobalCentreFrame(),
|
Chris@595
|
904 getMainModel()->getEndFrame()));
|
Chris@45
|
905 }
|
Chris@45
|
906
|
Chris@45
|
907 void
|
Chris@45
|
908 MainWindowBase::selectVisible()
|
Chris@45
|
909 {
|
Chris@45
|
910 Model *model = getMainModel();
|
Chris@45
|
911 if (!model) return;
|
Chris@45
|
912
|
Chris@45
|
913 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@45
|
914 if (!currentPane) return;
|
Chris@45
|
915
|
Chris@435
|
916 sv_frame_t startFrame, endFrame;
|
Chris@45
|
917
|
Chris@45
|
918 if (currentPane->getStartFrame() < 0) startFrame = 0;
|
Chris@45
|
919 else startFrame = currentPane->getStartFrame();
|
Chris@45
|
920
|
Chris@45
|
921 if (currentPane->getEndFrame() > model->getEndFrame()) endFrame = model->getEndFrame();
|
Chris@45
|
922 else endFrame = currentPane->getEndFrame();
|
Chris@45
|
923
|
Chris@45
|
924 m_viewManager->setSelection(Selection(startFrame, endFrame));
|
Chris@45
|
925 }
|
Chris@45
|
926
|
Chris@45
|
927 void
|
Chris@45
|
928 MainWindowBase::clearSelection()
|
Chris@45
|
929 {
|
Chris@45
|
930 m_viewManager->clearSelections();
|
Chris@45
|
931 }
|
Chris@45
|
932
|
Chris@45
|
933 void
|
Chris@45
|
934 MainWindowBase::cut()
|
Chris@45
|
935 {
|
Chris@45
|
936 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@45
|
937 if (!currentPane) return;
|
Chris@45
|
938
|
Chris@45
|
939 Layer *layer = currentPane->getSelectedLayer();
|
Chris@45
|
940 if (!layer) return;
|
Chris@45
|
941
|
Chris@45
|
942 Clipboard &clipboard = m_viewManager->getClipboard();
|
Chris@45
|
943 clipboard.clear();
|
Chris@45
|
944
|
Chris@45
|
945 MultiSelection::SelectionList selections = m_viewManager->getSelections();
|
Chris@45
|
946
|
Chris@45
|
947 CommandHistory::getInstance()->startCompoundOperation(tr("Cut"), true);
|
Chris@45
|
948
|
Chris@45
|
949 for (MultiSelection::SelectionList::iterator i = selections.begin();
|
Chris@45
|
950 i != selections.end(); ++i) {
|
Chris@86
|
951 layer->copy(currentPane, *i, clipboard);
|
Chris@45
|
952 layer->deleteSelection(*i);
|
Chris@45
|
953 }
|
Chris@45
|
954
|
Chris@45
|
955 CommandHistory::getInstance()->endCompoundOperation();
|
Chris@45
|
956 }
|
Chris@45
|
957
|
Chris@45
|
958 void
|
Chris@45
|
959 MainWindowBase::copy()
|
Chris@45
|
960 {
|
Chris@45
|
961 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@45
|
962 if (!currentPane) return;
|
Chris@45
|
963
|
Chris@45
|
964 Layer *layer = currentPane->getSelectedLayer();
|
Chris@45
|
965 if (!layer) return;
|
Chris@45
|
966
|
Chris@45
|
967 Clipboard &clipboard = m_viewManager->getClipboard();
|
Chris@45
|
968 clipboard.clear();
|
Chris@45
|
969
|
Chris@45
|
970 MultiSelection::SelectionList selections = m_viewManager->getSelections();
|
Chris@45
|
971
|
Chris@45
|
972 for (MultiSelection::SelectionList::iterator i = selections.begin();
|
Chris@45
|
973 i != selections.end(); ++i) {
|
Chris@86
|
974 layer->copy(currentPane, *i, clipboard);
|
Chris@45
|
975 }
|
Chris@45
|
976 }
|
Chris@45
|
977
|
Chris@45
|
978 void
|
Chris@45
|
979 MainWindowBase::paste()
|
Chris@45
|
980 {
|
Chris@215
|
981 pasteRelative(0);
|
Chris@215
|
982 }
|
Chris@215
|
983
|
Chris@215
|
984 void
|
Chris@215
|
985 MainWindowBase::pasteAtPlaybackPosition()
|
Chris@215
|
986 {
|
Chris@435
|
987 sv_frame_t pos = getFrame();
|
Chris@215
|
988 Clipboard &clipboard = m_viewManager->getClipboard();
|
Chris@215
|
989 if (!clipboard.empty()) {
|
Chris@435
|
990 sv_frame_t firstEventFrame = clipboard.getPoints()[0].getFrame();
|
Chris@435
|
991 sv_frame_t offset = 0;
|
Chris@215
|
992 if (firstEventFrame < 0) {
|
Chris@366
|
993 offset = pos - firstEventFrame;
|
Chris@354
|
994 } else if (firstEventFrame < pos) {
|
Chris@366
|
995 offset = pos - firstEventFrame;
|
Chris@215
|
996 } else {
|
Chris@366
|
997 offset = -(firstEventFrame - pos);
|
Chris@215
|
998 }
|
Chris@215
|
999 pasteRelative(offset);
|
Chris@215
|
1000 }
|
Chris@215
|
1001 }
|
Chris@215
|
1002
|
Chris@215
|
1003 void
|
Chris@435
|
1004 MainWindowBase::pasteRelative(sv_frame_t offset)
|
Chris@215
|
1005 {
|
Chris@45
|
1006 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@45
|
1007 if (!currentPane) return;
|
Chris@45
|
1008
|
Chris@45
|
1009 Layer *layer = currentPane->getSelectedLayer();
|
Chris@45
|
1010
|
Chris@45
|
1011 Clipboard &clipboard = m_viewManager->getClipboard();
|
Chris@87
|
1012
|
Chris@98
|
1013 bool inCompound = false;
|
Chris@87
|
1014
|
Chris@87
|
1015 if (!layer || !layer->isLayerEditable()) {
|
Chris@87
|
1016
|
Chris@87
|
1017 CommandHistory::getInstance()->startCompoundOperation
|
Chris@87
|
1018 (tr("Paste"), true);
|
Chris@87
|
1019
|
Chris@87
|
1020 // no suitable current layer: create one of the most
|
Chris@87
|
1021 // appropriate sort
|
Chris@87
|
1022 LayerFactory::LayerType type =
|
Chris@87
|
1023 LayerFactory::getInstance()->getLayerTypeForClipboardContents(clipboard);
|
Chris@87
|
1024 layer = m_document->createEmptyLayer(type);
|
Chris@87
|
1025
|
Chris@87
|
1026 if (!layer) {
|
Chris@87
|
1027 CommandHistory::getInstance()->endCompoundOperation();
|
Chris@87
|
1028 return;
|
Chris@45
|
1029 }
|
Chris@87
|
1030
|
Chris@87
|
1031 m_document->addLayerToView(currentPane, layer);
|
Chris@87
|
1032 m_paneStack->setCurrentLayer(currentPane, layer);
|
Chris@87
|
1033
|
Chris@87
|
1034 inCompound = true;
|
Chris@45
|
1035 }
|
Chris@45
|
1036
|
Chris@215
|
1037 layer->paste(currentPane, clipboard, offset, true);
|
Chris@45
|
1038
|
Chris@87
|
1039 if (inCompound) CommandHistory::getInstance()->endCompoundOperation();
|
Chris@45
|
1040 }
|
Chris@45
|
1041
|
Chris@45
|
1042 void
|
Chris@45
|
1043 MainWindowBase::deleteSelected()
|
Chris@45
|
1044 {
|
Chris@45
|
1045 if (m_paneStack->getCurrentPane() &&
|
Chris@595
|
1046 m_paneStack->getCurrentPane()->getSelectedLayer()) {
|
Chris@45
|
1047
|
Chris@45
|
1048 Layer *layer = m_paneStack->getCurrentPane()->getSelectedLayer();
|
Chris@45
|
1049
|
Chris@409
|
1050 if (m_viewManager) {
|
Chris@409
|
1051
|
Chris@409
|
1052 if (m_viewManager->getToolMode() == ViewManager::MeasureMode) {
|
Chris@409
|
1053
|
Chris@409
|
1054 layer->deleteCurrentMeasureRect();
|
Chris@45
|
1055
|
Chris@409
|
1056 } else {
|
Chris@409
|
1057
|
Chris@409
|
1058 MultiSelection::SelectionList selections =
|
Chris@409
|
1059 m_viewManager->getSelections();
|
Chris@409
|
1060
|
Chris@409
|
1061 for (MultiSelection::SelectionList::iterator i = selections.begin();
|
Chris@409
|
1062 i != selections.end(); ++i) {
|
Chris@409
|
1063 layer->deleteSelection(*i);
|
Chris@409
|
1064 }
|
Chris@45
|
1065 }
|
Chris@595
|
1066 }
|
Chris@45
|
1067 }
|
Chris@45
|
1068 }
|
Chris@45
|
1069
|
Chris@161
|
1070 // FrameTimer method
|
Chris@161
|
1071
|
Chris@435
|
1072 sv_frame_t
|
Chris@161
|
1073 MainWindowBase::getFrame() const
|
Chris@161
|
1074 {
|
Chris@161
|
1075 if (m_playSource && m_playSource->isPlaying()) {
|
Chris@161
|
1076 return m_playSource->getCurrentPlayingFrame();
|
Chris@161
|
1077 } else {
|
Chris@161
|
1078 return m_viewManager->getPlaybackFrame();
|
Chris@161
|
1079 }
|
Chris@161
|
1080 }
|
Chris@161
|
1081
|
Chris@45
|
1082 void
|
Chris@45
|
1083 MainWindowBase::insertInstant()
|
Chris@45
|
1084 {
|
Chris@161
|
1085 insertInstantAt(getFrame());
|
Chris@45
|
1086 }
|
Chris@45
|
1087
|
Chris@45
|
1088 void
|
Chris@45
|
1089 MainWindowBase::insertInstantsAtBoundaries()
|
Chris@45
|
1090 {
|
Chris@45
|
1091 MultiSelection::SelectionList selections = m_viewManager->getSelections();
|
Chris@45
|
1092 for (MultiSelection::SelectionList::iterator i = selections.begin();
|
Chris@45
|
1093 i != selections.end(); ++i) {
|
Chris@435
|
1094 sv_frame_t start = i->getStartFrame();
|
Chris@435
|
1095 sv_frame_t end = i->getEndFrame();
|
Chris@45
|
1096 if (start != end) {
|
Chris@184
|
1097 insertInstantAt(start);
|
Chris@184
|
1098 insertInstantAt(end);
|
Chris@45
|
1099 }
|
Chris@45
|
1100 }
|
Chris@45
|
1101 }
|
Chris@45
|
1102
|
Chris@45
|
1103 void
|
Chris@435
|
1104 MainWindowBase::insertInstantAt(sv_frame_t frame)
|
Chris@45
|
1105 {
|
Chris@45
|
1106 Pane *pane = m_paneStack->getCurrentPane();
|
Chris@45
|
1107 if (!pane) {
|
Chris@45
|
1108 return;
|
Chris@45
|
1109 }
|
Chris@45
|
1110
|
Chris@74
|
1111 frame = pane->alignFromReference(frame);
|
Chris@74
|
1112
|
Chris@45
|
1113 Layer *layer = dynamic_cast<TimeInstantLayer *>
|
Chris@45
|
1114 (pane->getSelectedLayer());
|
Chris@45
|
1115
|
Chris@45
|
1116 if (!layer) {
|
Chris@45
|
1117 for (int i = pane->getLayerCount(); i > 0; --i) {
|
Chris@45
|
1118 layer = dynamic_cast<TimeInstantLayer *>(pane->getLayer(i - 1));
|
Chris@45
|
1119 if (layer) break;
|
Chris@45
|
1120 }
|
Chris@45
|
1121
|
Chris@45
|
1122 if (!layer) {
|
Chris@45
|
1123 CommandHistory::getInstance()->startCompoundOperation
|
Chris@45
|
1124 (tr("Add Point"), true);
|
Chris@45
|
1125 layer = m_document->createEmptyLayer(LayerFactory::TimeInstants);
|
Chris@45
|
1126 if (layer) {
|
Chris@45
|
1127 m_document->addLayerToView(pane, layer);
|
Chris@45
|
1128 m_paneStack->setCurrentLayer(pane, layer);
|
Chris@45
|
1129 }
|
Chris@45
|
1130 CommandHistory::getInstance()->endCompoundOperation();
|
Chris@45
|
1131 }
|
Chris@45
|
1132 }
|
Chris@45
|
1133
|
Chris@45
|
1134 if (layer) {
|
Chris@45
|
1135
|
Chris@45
|
1136 Model *model = layer->getModel();
|
Chris@45
|
1137 SparseOneDimensionalModel *sodm = dynamic_cast<SparseOneDimensionalModel *>
|
Chris@45
|
1138 (model);
|
Chris@45
|
1139
|
Chris@45
|
1140 if (sodm) {
|
Chris@45
|
1141 SparseOneDimensionalModel::Point point(frame, "");
|
Chris@45
|
1142
|
Chris@45
|
1143 SparseOneDimensionalModel::Point prevPoint(0);
|
Chris@45
|
1144 bool havePrevPoint = false;
|
Chris@45
|
1145
|
Chris@45
|
1146 SparseOneDimensionalModel::EditCommand *command =
|
Chris@45
|
1147 new SparseOneDimensionalModel::EditCommand(sodm, tr("Add Point"));
|
Chris@45
|
1148
|
Chris@409
|
1149 if (m_labeller) {
|
Chris@409
|
1150
|
Chris@409
|
1151 if (m_labeller->requiresPrevPoint()) {
|
Chris@409
|
1152
|
Chris@409
|
1153 SparseOneDimensionalModel::PointList prevPoints =
|
Chris@409
|
1154 sodm->getPreviousPoints(frame);
|
Chris@409
|
1155
|
Chris@409
|
1156 if (!prevPoints.empty()) {
|
Chris@409
|
1157 prevPoint = *prevPoints.begin();
|
Chris@409
|
1158 havePrevPoint = true;
|
Chris@409
|
1159 }
|
Chris@45
|
1160 }
|
Chris@45
|
1161
|
Chris@45
|
1162 m_labeller->setSampleRate(sodm->getSampleRate());
|
Chris@45
|
1163
|
Chris@352
|
1164 if (m_labeller->actingOnPrevPoint() && havePrevPoint) {
|
Chris@45
|
1165 command->deletePoint(prevPoint);
|
Chris@45
|
1166 }
|
Chris@45
|
1167
|
Chris@45
|
1168 m_labeller->label<SparseOneDimensionalModel::Point>
|
Chris@636
|
1169 (point, havePrevPoint ? &prevPoint : nullptr);
|
Chris@45
|
1170
|
Chris@352
|
1171 if (m_labeller->actingOnPrevPoint() && havePrevPoint) {
|
Chris@45
|
1172 command->addPoint(prevPoint);
|
Chris@45
|
1173 }
|
Chris@45
|
1174 }
|
Chris@45
|
1175
|
Chris@45
|
1176 command->addPoint(point);
|
Chris@45
|
1177
|
Chris@45
|
1178 command->setName(tr("Add Point at %1 s")
|
Chris@45
|
1179 .arg(RealTime::frame2RealTime
|
Chris@45
|
1180 (frame,
|
Chris@45
|
1181 sodm->getSampleRate())
|
Chris@45
|
1182 .toText(false).c_str()));
|
Chris@45
|
1183
|
Chris@108
|
1184 Command *c = command->finish();
|
Chris@108
|
1185 if (c) CommandHistory::getInstance()->addCommand(c, false);
|
Chris@45
|
1186 }
|
Chris@45
|
1187 }
|
Chris@45
|
1188 }
|
Chris@45
|
1189
|
Chris@45
|
1190 void
|
Chris@184
|
1191 MainWindowBase::insertItemAtSelection()
|
Chris@184
|
1192 {
|
Chris@184
|
1193 MultiSelection::SelectionList selections = m_viewManager->getSelections();
|
Chris@184
|
1194 for (MultiSelection::SelectionList::iterator i = selections.begin();
|
Chris@184
|
1195 i != selections.end(); ++i) {
|
Chris@435
|
1196 sv_frame_t start = i->getStartFrame();
|
Chris@435
|
1197 sv_frame_t end = i->getEndFrame();
|
Chris@184
|
1198 if (start < end) {
|
Chris@184
|
1199 insertItemAt(start, end - start);
|
Chris@184
|
1200 }
|
Chris@184
|
1201 }
|
Chris@184
|
1202 }
|
Chris@184
|
1203
|
Chris@184
|
1204 void
|
Chris@435
|
1205 MainWindowBase::insertItemAt(sv_frame_t frame, sv_frame_t duration)
|
Chris@184
|
1206 {
|
Chris@184
|
1207 Pane *pane = m_paneStack->getCurrentPane();
|
Chris@184
|
1208 if (!pane) {
|
Chris@184
|
1209 return;
|
Chris@184
|
1210 }
|
Chris@184
|
1211
|
Chris@184
|
1212 // ugh!
|
Chris@184
|
1213
|
Chris@435
|
1214 sv_frame_t alignedStart = pane->alignFromReference(frame);
|
Chris@435
|
1215 sv_frame_t alignedEnd = pane->alignFromReference(frame + duration);
|
Chris@184
|
1216 if (alignedStart >= alignedEnd) return;
|
Chris@435
|
1217 sv_frame_t alignedDuration = alignedEnd - alignedStart;
|
Chris@184
|
1218
|
Chris@636
|
1219 Command *c = nullptr;
|
Chris@184
|
1220
|
Chris@184
|
1221 QString name = tr("Add Item at %1 s")
|
Chris@184
|
1222 .arg(RealTime::frame2RealTime
|
Chris@184
|
1223 (alignedStart,
|
Chris@184
|
1224 getMainModel()->getSampleRate())
|
Chris@184
|
1225 .toText(false).c_str());
|
Chris@184
|
1226
|
Chris@184
|
1227 Layer *layer = pane->getSelectedLayer();
|
Chris@184
|
1228 if (!layer) return;
|
Chris@184
|
1229
|
Chris@184
|
1230 RegionModel *rm = dynamic_cast<RegionModel *>(layer->getModel());
|
Chris@184
|
1231 if (rm) {
|
Chris@184
|
1232 RegionModel::Point point(alignedStart,
|
Chris@185
|
1233 rm->getValueMaximum() + 1,
|
Chris@184
|
1234 alignedDuration,
|
Chris@184
|
1235 "");
|
Chris@184
|
1236 RegionModel::EditCommand *command =
|
Chris@184
|
1237 new RegionModel::EditCommand(rm, tr("Add Point"));
|
Chris@184
|
1238 command->addPoint(point);
|
Chris@184
|
1239 command->setName(name);
|
Chris@184
|
1240 c = command->finish();
|
Chris@184
|
1241 }
|
Chris@184
|
1242
|
Chris@184
|
1243 if (c) {
|
Chris@184
|
1244 CommandHistory::getInstance()->addCommand(c, false);
|
Chris@184
|
1245 return;
|
Chris@184
|
1246 }
|
Chris@184
|
1247
|
Chris@184
|
1248 NoteModel *nm = dynamic_cast<NoteModel *>(layer->getModel());
|
Chris@184
|
1249 if (nm) {
|
Chris@184
|
1250 NoteModel::Point point(alignedStart,
|
Chris@409
|
1251 nm->getValueMinimum(),
|
Chris@184
|
1252 alignedDuration,
|
Chris@184
|
1253 1.f,
|
Chris@184
|
1254 "");
|
Chris@184
|
1255 NoteModel::EditCommand *command =
|
Chris@184
|
1256 new NoteModel::EditCommand(nm, tr("Add Point"));
|
Chris@184
|
1257 command->addPoint(point);
|
Chris@184
|
1258 command->setName(name);
|
Chris@184
|
1259 c = command->finish();
|
Chris@184
|
1260 }
|
Chris@184
|
1261
|
Chris@184
|
1262 if (c) {
|
Chris@184
|
1263 CommandHistory::getInstance()->addCommand(c, false);
|
Chris@184
|
1264 return;
|
Chris@184
|
1265 }
|
matthiasm@267
|
1266
|
matthiasm@268
|
1267 FlexiNoteModel *fnm = dynamic_cast<FlexiNoteModel *>(layer->getModel());
|
matthiasm@268
|
1268 if (fnm) {
|
matthiasm@267
|
1269 FlexiNoteModel::Point point(alignedStart,
|
Chris@409
|
1270 fnm->getValueMinimum(),
|
Chris@409
|
1271 alignedDuration,
|
Chris@409
|
1272 1.f,
|
Chris@409
|
1273 "");
|
matthiasm@267
|
1274 FlexiNoteModel::EditCommand *command =
|
matthiasm@268
|
1275 new FlexiNoteModel::EditCommand(fnm, tr("Add Point"));
|
matthiasm@267
|
1276 command->addPoint(point);
|
matthiasm@267
|
1277 command->setName(name);
|
matthiasm@267
|
1278 c = command->finish();
|
matthiasm@267
|
1279 }
|
Chris@409
|
1280
|
matthiasm@267
|
1281 if (c) {
|
matthiasm@267
|
1282 CommandHistory::getInstance()->addCommand(c, false);
|
matthiasm@267
|
1283 return;
|
matthiasm@267
|
1284 }
|
Chris@184
|
1285 }
|
Chris@184
|
1286
|
Chris@184
|
1287 void
|
Chris@45
|
1288 MainWindowBase::renumberInstants()
|
Chris@45
|
1289 {
|
Chris@45
|
1290 Pane *pane = m_paneStack->getCurrentPane();
|
Chris@45
|
1291 if (!pane) return;
|
Chris@45
|
1292
|
Chris@45
|
1293 Layer *layer = dynamic_cast<TimeInstantLayer *>(pane->getSelectedLayer());
|
Chris@45
|
1294 if (!layer) return;
|
Chris@45
|
1295
|
Chris@45
|
1296 MultiSelection ms(m_viewManager->getSelection());
|
Chris@45
|
1297
|
Chris@45
|
1298 Model *model = layer->getModel();
|
Chris@45
|
1299 SparseOneDimensionalModel *sodm = dynamic_cast<SparseOneDimensionalModel *>
|
Chris@45
|
1300 (model);
|
Chris@45
|
1301 if (!sodm) return;
|
Chris@45
|
1302
|
Chris@45
|
1303 if (!m_labeller) return;
|
Chris@45
|
1304
|
Chris@45
|
1305 Labeller labeller(*m_labeller);
|
Chris@45
|
1306 labeller.setSampleRate(sodm->getSampleRate());
|
Chris@45
|
1307
|
Chris@537
|
1308 Command *c = labeller.labelAll<SparseOneDimensionalModel::Point>(*sodm, &ms);
|
Chris@537
|
1309 if (c) CommandHistory::getInstance()->addCommand(c, false);
|
Chris@537
|
1310 }
|
Chris@537
|
1311
|
Chris@537
|
1312 void
|
Chris@537
|
1313 MainWindowBase::subdivideInstantsBy(int n)
|
Chris@537
|
1314 {
|
Chris@537
|
1315 Pane *pane = m_paneStack->getCurrentPane();
|
Chris@537
|
1316 if (!pane) return;
|
Chris@537
|
1317
|
Chris@537
|
1318 Layer *layer = dynamic_cast<TimeInstantLayer *>(pane->getSelectedLayer());
|
Chris@537
|
1319 if (!layer) return;
|
Chris@537
|
1320
|
Chris@537
|
1321 MultiSelection ms(m_viewManager->getSelection());
|
Chris@537
|
1322
|
Chris@537
|
1323 Model *model = layer->getModel();
|
Chris@537
|
1324 SparseOneDimensionalModel *sodm =
|
Chris@537
|
1325 dynamic_cast<SparseOneDimensionalModel *>(model);
|
Chris@537
|
1326 if (!sodm) return;
|
Chris@537
|
1327
|
Chris@537
|
1328 if (!m_labeller) return;
|
Chris@537
|
1329
|
Chris@537
|
1330 Labeller labeller(*m_labeller);
|
Chris@537
|
1331 labeller.setSampleRate(sodm->getSampleRate());
|
Chris@537
|
1332
|
Chris@537
|
1333 Command *c = labeller.subdivide<SparseOneDimensionalModel::Point>
|
Chris@537
|
1334 (*sodm, &ms, n);
|
Chris@537
|
1335 if (c) CommandHistory::getInstance()->addCommand(c, false);
|
Chris@45
|
1336 }
|
Chris@45
|
1337
|
Chris@538
|
1338 void
|
Chris@538
|
1339 MainWindowBase::winnowInstantsBy(int n)
|
Chris@538
|
1340 {
|
Chris@538
|
1341 Pane *pane = m_paneStack->getCurrentPane();
|
Chris@538
|
1342 if (!pane) return;
|
Chris@538
|
1343
|
Chris@538
|
1344 Layer *layer = dynamic_cast<TimeInstantLayer *>(pane->getSelectedLayer());
|
Chris@538
|
1345 if (!layer) return;
|
Chris@538
|
1346
|
Chris@538
|
1347 MultiSelection ms(m_viewManager->getSelection());
|
Chris@538
|
1348
|
Chris@538
|
1349 Model *model = layer->getModel();
|
Chris@538
|
1350 SparseOneDimensionalModel *sodm =
|
Chris@538
|
1351 dynamic_cast<SparseOneDimensionalModel *>(model);
|
Chris@538
|
1352 if (!sodm) return;
|
Chris@538
|
1353
|
Chris@538
|
1354 if (!m_labeller) return;
|
Chris@538
|
1355
|
Chris@538
|
1356 Labeller labeller(*m_labeller);
|
Chris@538
|
1357 labeller.setSampleRate(sodm->getSampleRate());
|
Chris@538
|
1358
|
Chris@538
|
1359 Command *c = labeller.winnow<SparseOneDimensionalModel::Point>
|
Chris@538
|
1360 (*sodm, &ms, n);
|
Chris@538
|
1361 if (c) CommandHistory::getInstance()->addCommand(c, false);
|
Chris@538
|
1362 }
|
Chris@538
|
1363
|
Chris@45
|
1364 MainWindowBase::FileOpenStatus
|
Chris@373
|
1365 MainWindowBase::openPath(QString fileOrUrl, AudioFileOpenMode mode)
|
Chris@45
|
1366 {
|
Chris@134
|
1367 ProgressDialog dialog(tr("Opening file or URL..."), true, 2000, this);
|
Chris@134
|
1368 connect(&dialog, SIGNAL(showing()), this, SIGNAL(hideSplash()));
|
Chris@109
|
1369 return open(FileSource(fileOrUrl, &dialog), mode);
|
Chris@45
|
1370 }
|
Chris@45
|
1371
|
Chris@45
|
1372 MainWindowBase::FileOpenStatus
|
Chris@45
|
1373 MainWindowBase::open(FileSource source, AudioFileOpenMode mode)
|
Chris@45
|
1374 {
|
Chris@45
|
1375 FileOpenStatus status;
|
Chris@45
|
1376
|
Chris@45
|
1377 if (!source.isAvailable()) return FileOpenFailed;
|
Chris@45
|
1378 source.waitForData();
|
Chris@45
|
1379
|
Chris@636
|
1380 bool canImportLayer = (getMainModel() != nullptr &&
|
Chris@636
|
1381 m_paneStack != nullptr &&
|
Chris@636
|
1382 m_paneStack->getCurrentPane() != nullptr);
|
Chris@45
|
1383
|
Chris@152
|
1384 bool rdf = (source.getExtension().toLower() == "rdf" ||
|
Chris@152
|
1385 source.getExtension().toLower() == "n3" ||
|
Chris@152
|
1386 source.getExtension().toLower() == "ttl");
|
Chris@152
|
1387
|
Chris@152
|
1388 bool audio = AudioFileReaderFactory::getKnownExtensions().contains
|
Chris@152
|
1389 (source.getExtension().toLower());
|
Chris@145
|
1390
|
Chris@145
|
1391 bool rdfSession = false;
|
Chris@145
|
1392 if (rdf) {
|
Chris@145
|
1393 RDFImporter::RDFDocumentType rdfType =
|
Chris@145
|
1394 RDFImporter::identifyDocumentType
|
Chris@145
|
1395 (QUrl::fromLocalFile(source.getLocalFilename()).toString());
|
Chris@145
|
1396 if (rdfType == RDFImporter::AudioRefAndAnnotations ||
|
Chris@145
|
1397 rdfType == RDFImporter::AudioRef) {
|
Chris@145
|
1398 rdfSession = true;
|
Chris@145
|
1399 } else if (rdfType == RDFImporter::NotRDF) {
|
Chris@145
|
1400 rdf = false;
|
Chris@145
|
1401 }
|
Chris@145
|
1402 }
|
Chris@145
|
1403
|
Chris@579
|
1404 try {
|
Chris@579
|
1405 if (rdf) {
|
Chris@579
|
1406 if (rdfSession) {
|
Chris@579
|
1407 bool cancel = false;
|
Chris@579
|
1408 if (!canImportLayer || shouldCreateNewSessionForRDFAudio(&cancel)) {
|
Chris@579
|
1409 return openSession(source);
|
Chris@579
|
1410 } else if (cancel) {
|
Chris@579
|
1411 return FileOpenCancelled;
|
Chris@579
|
1412 } else {
|
Chris@579
|
1413 return openLayer(source);
|
Chris@579
|
1414 }
|
Chris@145
|
1415 } else {
|
Chris@579
|
1416 if ((status = openSession(source)) != FileOpenFailed) {
|
Chris@579
|
1417 return status;
|
Chris@579
|
1418 } else if (!canImportLayer) {
|
Chris@579
|
1419 return FileOpenWrongMode;
|
Chris@579
|
1420 } else if ((status = openLayer(source)) != FileOpenFailed) {
|
Chris@579
|
1421 return status;
|
Chris@579
|
1422 } else {
|
Chris@579
|
1423 return FileOpenFailed;
|
Chris@579
|
1424 }
|
Chris@145
|
1425 }
|
Chris@145
|
1426 }
|
Chris@579
|
1427
|
Chris@579
|
1428 if (audio && (status = openAudio(source, mode)) != FileOpenFailed) {
|
Chris@579
|
1429 return status;
|
Chris@579
|
1430 } else if ((status = openSession(source)) != FileOpenFailed) {
|
Chris@579
|
1431 return status;
|
Chris@579
|
1432 } else if ((status = openPlaylist(source, mode)) != FileOpenFailed) {
|
Chris@579
|
1433 return status;
|
Chris@579
|
1434 } else if (!canImportLayer) {
|
Chris@579
|
1435 return FileOpenWrongMode;
|
Chris@579
|
1436 } else if ((status = openImage(source)) != FileOpenFailed) {
|
Chris@579
|
1437 return status;
|
Chris@579
|
1438 } else if ((status = openLayer(source)) != FileOpenFailed) {
|
Chris@579
|
1439 return status;
|
Chris@579
|
1440 } else {
|
Chris@579
|
1441 return FileOpenFailed;
|
Chris@579
|
1442 }
|
Chris@579
|
1443 } catch (const InsufficientDiscSpace &e) {
|
Chris@579
|
1444 emit hideSplash();
|
Chris@579
|
1445 m_openingAudioFile = false;
|
Chris@594
|
1446 SVCERR << "MainWindowBase: Caught InsufficientDiscSpace in file open" << endl;
|
Chris@579
|
1447 QMessageBox::critical
|
Chris@579
|
1448 (this, tr("Not enough disc space"),
|
Chris@579
|
1449 tr("<b>Not enough disc space</b><p>There doesn't appear to be enough spare disc space to accommodate any necessary temporary files.</p><p>Please clear some space and try again.</p>").arg(e.what()));
|
Chris@579
|
1450 return FileOpenFailed;
|
Chris@592
|
1451 } catch (const std::bad_alloc &e) { // reader may have rethrown this after cleaning up
|
Chris@592
|
1452 emit hideSplash();
|
Chris@592
|
1453 m_openingAudioFile = false;
|
Chris@594
|
1454 SVCERR << "MainWindowBase: Caught bad_alloc in file open" << endl;
|
Chris@592
|
1455 QMessageBox::critical
|
Chris@592
|
1456 (this, tr("Not enough memory"),
|
Chris@592
|
1457 tr("<b>Not enough memory</b><p>There doesn't appear to be enough memory to accommodate any necessary temporary data.</p>"));
|
Chris@592
|
1458 return FileOpenFailed;
|
Chris@45
|
1459 }
|
Chris@45
|
1460 }
|
Chris@45
|
1461
|
Chris@45
|
1462 MainWindowBase::FileOpenStatus
|
Chris@626
|
1463 MainWindowBase::openAudio(FileSource source,
|
Chris@626
|
1464 AudioFileOpenMode mode,
|
Chris@227
|
1465 QString templateName)
|
Chris@45
|
1466 {
|
Chris@386
|
1467 SVDEBUG << "MainWindowBase::openAudio(" << source.getLocation() << ") with mode " << mode << " and template " << templateName << endl;
|
Chris@45
|
1468
|
Chris@222
|
1469 if (templateName == "") {
|
Chris@231
|
1470 templateName = getDefaultSessionTemplate();
|
Chris@577
|
1471 SVDEBUG << "(Default template is: \"" << templateName << "\")" << endl;
|
Chris@222
|
1472 }
|
Chris@220
|
1473
|
Chris@374
|
1474 // cerr << "template is: \"" << templateName << "\"" << endl;
|
Chris@223
|
1475
|
Chris@413
|
1476 if (!source.isAvailable()) {
|
Chris@413
|
1477 if (source.wasCancelled()) {
|
Chris@413
|
1478 return FileOpenCancelled;
|
Chris@413
|
1479 } else {
|
Chris@413
|
1480 return FileOpenFailed;
|
Chris@413
|
1481 }
|
Chris@413
|
1482 }
|
Chris@413
|
1483
|
Chris@45
|
1484 source.waitForData();
|
Chris@45
|
1485
|
Chris@45
|
1486 m_openingAudioFile = true;
|
Chris@45
|
1487
|
Chris@435
|
1488 sv_samplerate_t rate = 0;
|
Chris@45
|
1489
|
Chris@626
|
1490 SVDEBUG << "Checking whether to preserve incoming audio file's sample rate"
|
Chris@626
|
1491 << endl;
|
Chris@626
|
1492
|
Chris@360
|
1493 if (Preferences::getInstance()->getFixedSampleRate() != 0) {
|
Chris@360
|
1494 rate = Preferences::getInstance()->getFixedSampleRate();
|
Chris@626
|
1495 SVDEBUG << "No: preferences specify fixed rate of " << rate << endl;
|
Chris@360
|
1496 } else if (Preferences::getInstance()->getResampleOnLoad()) {
|
Chris@552
|
1497 if (getMainModel()) {
|
Chris@626
|
1498 if (mode == ReplaceSession || mode == ReplaceMainModel) {
|
Chris@626
|
1499 SVDEBUG << "Preferences specify resampling additional models to match main model, but we are opening this file to replace the main model according to the open mode: therefore..." << endl;
|
Chris@626
|
1500 } else {
|
Chris@626
|
1501 rate = getMainModel()->getSampleRate();
|
Chris@626
|
1502 SVDEBUG << "No: preferences specify resampling to match main model, whose rate is currently " << rate << endl;
|
Chris@626
|
1503 }
|
Chris@552
|
1504 }
|
Chris@45
|
1505 }
|
Chris@45
|
1506
|
Chris@626
|
1507 if (rate == 0) {
|
Chris@626
|
1508 SVDEBUG << "Yes, preserving incoming file rate" << endl;
|
Chris@626
|
1509 }
|
Chris@626
|
1510
|
Chris@479
|
1511 ReadOnlyWaveFileModel *newModel = new ReadOnlyWaveFileModel(source, rate);
|
Chris@45
|
1512
|
Chris@45
|
1513 if (!newModel->isOK()) {
|
Chris@595
|
1514 delete newModel;
|
Chris@45
|
1515 m_openingAudioFile = false;
|
Chris@413
|
1516 if (source.wasCancelled()) {
|
Chris@413
|
1517 return FileOpenCancelled;
|
Chris@413
|
1518 } else {
|
Chris@413
|
1519 return FileOpenFailed;
|
Chris@413
|
1520 }
|
Chris@45
|
1521 }
|
Chris@45
|
1522
|
Chris@604
|
1523 return addOpenedAudioModel(source, newModel, mode, templateName, true);
|
Chris@604
|
1524 }
|
Chris@604
|
1525
|
Chris@604
|
1526 MainWindowBase::FileOpenStatus
|
Chris@604
|
1527 MainWindowBase::addOpenedAudioModel(FileSource source,
|
Chris@604
|
1528 WaveFileModel *newModel,
|
Chris@604
|
1529 AudioFileOpenMode mode,
|
Chris@604
|
1530 QString templateName,
|
Chris@604
|
1531 bool registerSource)
|
Chris@604
|
1532 {
|
Chris@45
|
1533 if (mode == AskUser) {
|
Chris@45
|
1534 if (getMainModel()) {
|
Chris@45
|
1535
|
Chris@147
|
1536 QSettings settings;
|
Chris@147
|
1537 settings.beginGroup("MainWindow");
|
Chris@221
|
1538 int lastMode = settings.value("lastaudioopenmode", 0).toBool();
|
Chris@147
|
1539 settings.endGroup();
|
Chris@221
|
1540 int imode = 0;
|
Chris@45
|
1541
|
Chris@45
|
1542 QStringList items;
|
Chris@221
|
1543 items << tr("Close the current session and start a new one")
|
Chris@221
|
1544 << tr("Replace the main audio file in this session")
|
Chris@221
|
1545 << tr("Add the audio file to this session");
|
Chris@45
|
1546
|
Chris@45
|
1547 bool ok = false;
|
Chris@45
|
1548 QString item = ListInputDialog::getItem
|
Chris@45
|
1549 (this, tr("Select target for import"),
|
Chris@221
|
1550 tr("<b>Select a target for import</b><p>You already have an audio file loaded.<br>What would you like to do with the new audio file?"),
|
Chris@221
|
1551 items, lastMode, &ok);
|
Chris@45
|
1552
|
Chris@45
|
1553 if (!ok || item.isEmpty()) {
|
Chris@45
|
1554 delete newModel;
|
Chris@45
|
1555 m_openingAudioFile = false;
|
Chris@45
|
1556 return FileOpenCancelled;
|
Chris@45
|
1557 }
|
Chris@45
|
1558
|
Chris@221
|
1559 for (int i = 0; i < items.size(); ++i) {
|
Chris@221
|
1560 if (item == items[i]) imode = i;
|
Chris@221
|
1561 }
|
Chris@221
|
1562
|
Chris@147
|
1563 settings.beginGroup("MainWindow");
|
Chris@221
|
1564 settings.setValue("lastaudioopenmode", imode);
|
Chris@147
|
1565 settings.endGroup();
|
Chris@45
|
1566
|
Chris@221
|
1567 mode = (AudioFileOpenMode)imode;
|
Chris@45
|
1568
|
Chris@45
|
1569 } else {
|
Chris@221
|
1570 // no main model: make a new session
|
Chris@221
|
1571 mode = ReplaceSession;
|
Chris@45
|
1572 }
|
Chris@45
|
1573 }
|
Chris@45
|
1574
|
Chris@45
|
1575 if (mode == ReplaceCurrentPane) {
|
Chris@45
|
1576
|
Chris@45
|
1577 Pane *pane = m_paneStack->getCurrentPane();
|
Chris@45
|
1578 if (pane) {
|
Chris@45
|
1579 if (getMainModel()) {
|
Chris@45
|
1580 View::ModelSet models(pane->getModels());
|
Chris@45
|
1581 if (models.find(getMainModel()) != models.end()) {
|
Chris@221
|
1582 // Current pane contains main model: replace that
|
Chris@45
|
1583 mode = ReplaceMainModel;
|
Chris@45
|
1584 }
|
Chris@221
|
1585 // Otherwise the current pane has a non-default model,
|
Chris@221
|
1586 // which we will deal with later
|
Chris@45
|
1587 } else {
|
Chris@221
|
1588 // We have no main model, so start a new session with
|
Chris@221
|
1589 // optional template
|
Chris@221
|
1590 mode = ReplaceSession;
|
Chris@45
|
1591 }
|
Chris@45
|
1592 } else {
|
Chris@221
|
1593 // We seem to have no current pane! Oh well
|
Chris@45
|
1594 mode = CreateAdditionalModel;
|
Chris@45
|
1595 }
|
Chris@45
|
1596 }
|
Chris@45
|
1597
|
Chris@45
|
1598 if (mode == CreateAdditionalModel && !getMainModel()) {
|
Chris@386
|
1599 SVDEBUG << "Mode is CreateAdditionalModel but we have no main model, switching to ReplaceSession mode" << endl;
|
Chris@221
|
1600 mode = ReplaceSession;
|
Chris@221
|
1601 }
|
Chris@221
|
1602
|
Chris@221
|
1603 bool loadedTemplate = false;
|
Chris@221
|
1604
|
Chris@221
|
1605 if (mode == ReplaceSession) {
|
Chris@258
|
1606
|
Chris@258
|
1607 if (!checkSaveModified()) return FileOpenCancelled;
|
Chris@258
|
1608
|
Chris@386
|
1609 SVDEBUG << "SV looking for template " << templateName << endl;
|
Chris@230
|
1610 if (templateName != "") {
|
Chris@230
|
1611 FileOpenStatus tplStatus = openSessionTemplate(templateName);
|
Chris@258
|
1612 if (tplStatus == FileOpenCancelled) {
|
Chris@577
|
1613 SVDEBUG << "Template load cancelled" << endl;
|
Chris@258
|
1614 return FileOpenCancelled;
|
Chris@258
|
1615 }
|
Chris@230
|
1616 if (tplStatus != FileOpenFailed) {
|
Chris@577
|
1617 SVDEBUG << "Template load succeeded" << endl;
|
Chris@230
|
1618 loadedTemplate = true;
|
Chris@221
|
1619 }
|
Chris@221
|
1620 }
|
Chris@221
|
1621
|
Chris@221
|
1622 if (!loadedTemplate) {
|
Chris@386
|
1623 SVDEBUG << "No template found: closing session, creating new empty document" << endl;
|
Chris@221
|
1624 closeSession();
|
Chris@221
|
1625 createDocument();
|
Chris@221
|
1626 }
|
Chris@221
|
1627
|
Chris@386
|
1628 SVDEBUG << "Now switching to ReplaceMainModel mode" << endl;
|
Chris@45
|
1629 mode = ReplaceMainModel;
|
Chris@45
|
1630 }
|
Chris@45
|
1631
|
Chris@164
|
1632 emit activity(tr("Import audio file \"%1\"").arg(source.getLocation()));
|
Chris@164
|
1633
|
Chris@45
|
1634 if (mode == ReplaceMainModel) {
|
Chris@45
|
1635
|
Chris@45
|
1636 Model *prevMain = getMainModel();
|
Chris@45
|
1637 if (prevMain) {
|
Chris@45
|
1638 m_playSource->removeModel(prevMain);
|
Chris@108
|
1639 PlayParameterRepository::getInstance()->removePlayable(prevMain);
|
Chris@45
|
1640 }
|
Chris@108
|
1641 PlayParameterRepository::getInstance()->addPlayable(newModel);
|
Chris@45
|
1642
|
Chris@248
|
1643 SVDEBUG << "SV about to call setMainModel(" << newModel << "): prevMain is " << prevMain << endl;
|
Chris@248
|
1644
|
Chris@595
|
1645 m_document->setMainModel(newModel);
|
Chris@595
|
1646
|
Chris@595
|
1647 setupMenus();
|
Chris@595
|
1648
|
Chris@595
|
1649 if (loadedTemplate || (m_sessionFile == "")) {
|
Chris@45
|
1650 //!!! shouldn't be dealing directly with title from here -- call a method
|
Chris@595
|
1651 setWindowTitle(tr("%1: %2")
|
Chris@57
|
1652 .arg(QApplication::applicationName())
|
Chris@45
|
1653 .arg(source.getLocation()));
|
Chris@595
|
1654 CommandHistory::getInstance()->clear();
|
Chris@595
|
1655 CommandHistory::getInstance()->documentSaved();
|
Chris@595
|
1656 m_documentModified = false;
|
Chris@595
|
1657 } else {
|
Chris@595
|
1658 setWindowTitle(tr("%1: %2 [%3]")
|
Chris@57
|
1659 .arg(QApplication::applicationName())
|
Chris@595
|
1660 .arg(QFileInfo(m_sessionFile).fileName())
|
Chris@595
|
1661 .arg(source.getLocation()));
|
Chris@595
|
1662 if (m_documentModified) {
|
Chris@595
|
1663 m_documentModified = false;
|
Chris@595
|
1664 documentModified(); // so as to restore "(modified)" window title
|
Chris@595
|
1665 }
|
Chris@595
|
1666 }
|
Chris@45
|
1667
|
Chris@604
|
1668 if (!source.isRemote() && registerSource) {
|
Chris@604
|
1669 m_audioFile = source.getLocalFilename();
|
Chris@604
|
1670 }
|
Chris@45
|
1671
|
Chris@45
|
1672 } else if (mode == CreateAdditionalModel) {
|
Chris@45
|
1673
|
Chris@577
|
1674 SVCERR << "Mode is CreateAdditionalModel" << endl;
|
Chris@577
|
1675
|
Chris@595
|
1676 CommandHistory::getInstance()->startCompoundOperation
|
Chris@595
|
1677 (tr("Import \"%1\"").arg(source.getBasename()), true);
|
Chris@595
|
1678
|
Chris@595
|
1679 m_document->addImportedModel(newModel);
|
Chris@595
|
1680
|
Chris@595
|
1681 AddPaneCommand *command = new AddPaneCommand(this);
|
Chris@595
|
1682 CommandHistory::getInstance()->addCommand(command);
|
Chris@595
|
1683
|
Chris@595
|
1684 Pane *pane = command->getPane();
|
Chris@45
|
1685
|
Chris@47
|
1686 if (m_timeRulerLayer) {
|
Chris@577
|
1687 SVCERR << "Have time ruler, adding it" << endl;
|
Chris@47
|
1688 m_document->addLayerToView(pane, m_timeRulerLayer);
|
Chris@577
|
1689 } else {
|
Chris@577
|
1690 SVCERR << "Do not have time ruler" << endl;
|
Chris@47
|
1691 }
|
Chris@45
|
1692
|
Chris@595
|
1693 Layer *newLayer = m_document->createImportedLayer(newModel);
|
Chris@595
|
1694
|
Chris@595
|
1695 if (newLayer) {
|
Chris@595
|
1696 m_document->addLayerToView(pane, newLayer);
|
Chris@595
|
1697 }
|
Chris@595
|
1698
|
Chris@595
|
1699 CommandHistory::getInstance()->endCompoundOperation();
|
Chris@45
|
1700
|
Chris@45
|
1701 } else if (mode == ReplaceCurrentPane) {
|
Chris@45
|
1702
|
Chris@45
|
1703 // We know there is a current pane, otherwise we would have
|
Chris@45
|
1704 // reset the mode to CreateAdditionalModel above; and we know
|
Chris@45
|
1705 // the current pane does not contain the main model, otherwise
|
Chris@45
|
1706 // we would have reset it to ReplaceMainModel. But we don't
|
Chris@45
|
1707 // know whether the pane contains a waveform model at all.
|
Chris@45
|
1708
|
Chris@45
|
1709 Pane *pane = m_paneStack->getCurrentPane();
|
Chris@636
|
1710 Layer *replace = nullptr;
|
Chris@45
|
1711
|
Chris@45
|
1712 for (int i = 0; i < pane->getLayerCount(); ++i) {
|
Chris@45
|
1713 Layer *layer = pane->getLayer(i);
|
Chris@45
|
1714 if (dynamic_cast<WaveformLayer *>(layer)) {
|
Chris@45
|
1715 replace = layer;
|
Chris@45
|
1716 break;
|
Chris@45
|
1717 }
|
Chris@45
|
1718 }
|
Chris@45
|
1719
|
Chris@595
|
1720 CommandHistory::getInstance()->startCompoundOperation
|
Chris@595
|
1721 (tr("Import \"%1\"").arg(source.getBasename()), true);
|
Chris@595
|
1722
|
Chris@595
|
1723 m_document->addImportedModel(newModel);
|
Chris@45
|
1724
|
Chris@45
|
1725 if (replace) {
|
Chris@45
|
1726 m_document->removeLayerFromView(pane, replace);
|
Chris@45
|
1727 }
|
Chris@45
|
1728
|
Chris@595
|
1729 Layer *newLayer = m_document->createImportedLayer(newModel);
|
Chris@595
|
1730
|
Chris@595
|
1731 if (newLayer) {
|
Chris@595
|
1732 m_document->addLayerToView(pane, newLayer);
|
Chris@595
|
1733 }
|
Chris@595
|
1734
|
Chris@595
|
1735 CommandHistory::getInstance()->endCompoundOperation();
|
Chris@45
|
1736 }
|
Chris@45
|
1737
|
Chris@45
|
1738 updateMenuStates();
|
Chris@622
|
1739
|
Chris@622
|
1740 if (registerSource) {
|
Chris@622
|
1741 m_recentFiles.addFile(source.getLocation());
|
Chris@622
|
1742 }
|
Chris@604
|
1743 if (!source.isRemote() && registerSource) {
|
Chris@45
|
1744 // for file dialog
|
Chris@45
|
1745 registerLastOpenedFilePath(FileFinder::AudioFile,
|
Chris@45
|
1746 source.getLocalFilename());
|
Chris@45
|
1747 }
|
Chris@622
|
1748
|
Chris@45
|
1749 m_openingAudioFile = false;
|
Chris@45
|
1750
|
Chris@45
|
1751 currentPaneChanged(m_paneStack->getCurrentPane());
|
Chris@45
|
1752
|
Chris@342
|
1753 emit audioFileLoaded();
|
Chris@342
|
1754
|
Chris@45
|
1755 return FileOpenSucceeded;
|
Chris@45
|
1756 }
|
Chris@45
|
1757
|
Chris@45
|
1758 MainWindowBase::FileOpenStatus
|
Chris@45
|
1759 MainWindowBase::openPlaylist(FileSource source, AudioFileOpenMode mode)
|
Chris@45
|
1760 {
|
Chris@233
|
1761 SVDEBUG << "MainWindowBase::openPlaylist(" << source.getLocation() << ")" << endl;
|
Chris@135
|
1762
|
Chris@45
|
1763 std::set<QString> extensions;
|
Chris@45
|
1764 PlaylistFileReader::getSupportedExtensions(extensions);
|
Chris@152
|
1765 QString extension = source.getExtension().toLower();
|
Chris@45
|
1766 if (extensions.find(extension) == extensions.end()) return FileOpenFailed;
|
Chris@45
|
1767
|
Chris@45
|
1768 if (!source.isAvailable()) return FileOpenFailed;
|
Chris@45
|
1769 source.waitForData();
|
Chris@45
|
1770
|
Chris@45
|
1771 PlaylistFileReader reader(source.getLocalFilename());
|
Chris@45
|
1772 if (!reader.isOK()) return FileOpenFailed;
|
Chris@45
|
1773
|
Chris@45
|
1774 PlaylistFileReader::Playlist playlist = reader.load();
|
Chris@45
|
1775
|
Chris@45
|
1776 bool someSuccess = false;
|
Chris@45
|
1777
|
Chris@45
|
1778 for (PlaylistFileReader::Playlist::const_iterator i = playlist.begin();
|
Chris@45
|
1779 i != playlist.end(); ++i) {
|
Chris@45
|
1780
|
Chris@134
|
1781 ProgressDialog dialog(tr("Opening playlist..."), true, 2000, this);
|
Chris@134
|
1782 connect(&dialog, SIGNAL(showing()), this, SIGNAL(hideSplash()));
|
Chris@109
|
1783 FileOpenStatus status = openAudio(FileSource(*i, &dialog), mode);
|
Chris@45
|
1784
|
Chris@45
|
1785 if (status == FileOpenCancelled) {
|
Chris@45
|
1786 return FileOpenCancelled;
|
Chris@45
|
1787 }
|
Chris@45
|
1788
|
Chris@45
|
1789 if (status == FileOpenSucceeded) {
|
Chris@45
|
1790 someSuccess = true;
|
Chris@45
|
1791 mode = CreateAdditionalModel;
|
Chris@45
|
1792 }
|
Chris@45
|
1793 }
|
Chris@45
|
1794
|
Chris@45
|
1795 if (someSuccess) return FileOpenSucceeded;
|
Chris@45
|
1796 else return FileOpenFailed;
|
Chris@45
|
1797 }
|
Chris@45
|
1798
|
Chris@45
|
1799 MainWindowBase::FileOpenStatus
|
Chris@45
|
1800 MainWindowBase::openLayer(FileSource source)
|
Chris@45
|
1801 {
|
Chris@233
|
1802 SVDEBUG << "MainWindowBase::openLayer(" << source.getLocation() << ")" << endl;
|
Chris@135
|
1803
|
Chris@45
|
1804 Pane *pane = m_paneStack->getCurrentPane();
|
Chris@45
|
1805
|
Chris@45
|
1806 if (!pane) {
|
Chris@595
|
1807 // shouldn't happen, as the menu action should have been disabled
|
Chris@595
|
1808 cerr << "WARNING: MainWindowBase::openLayer: no current pane" << endl;
|
Chris@595
|
1809 return FileOpenWrongMode;
|
Chris@45
|
1810 }
|
Chris@45
|
1811
|
Chris@45
|
1812 if (!getMainModel()) {
|
Chris@595
|
1813 // shouldn't happen, as the menu action should have been disabled
|
Chris@595
|
1814 cerr << "WARNING: MainWindowBase::openLayer: No main model -- hence no default sample rate available" << endl;
|
Chris@595
|
1815 return FileOpenWrongMode;
|
Chris@45
|
1816 }
|
Chris@45
|
1817
|
Chris@45
|
1818 if (!source.isAvailable()) return FileOpenFailed;
|
Chris@45
|
1819 source.waitForData();
|
Chris@45
|
1820
|
Chris@45
|
1821 QString path = source.getLocalFilename();
|
Chris@45
|
1822
|
Chris@145
|
1823 RDFImporter::RDFDocumentType rdfType =
|
Chris@145
|
1824 RDFImporter::identifyDocumentType(QUrl::fromLocalFile(path).toString());
|
Chris@145
|
1825
|
Chris@293
|
1826 // cerr << "RDF type: (in layer) " << (int) rdfType << endl;
|
Chris@148
|
1827
|
Chris@145
|
1828 if (rdfType != RDFImporter::NotRDF) {
|
Chris@145
|
1829
|
Chris@145
|
1830 return openLayersFromRDF(source);
|
Chris@134
|
1831
|
Chris@152
|
1832 } else if (source.getExtension().toLower() == "svl" ||
|
Chris@152
|
1833 (source.getExtension().toLower() == "xml" &&
|
Chris@140
|
1834 (SVFileReader::identifyXmlFile(source.getLocalFilename())
|
Chris@140
|
1835 == SVFileReader::SVLayerFile))) {
|
Chris@45
|
1836
|
Chris@45
|
1837 PaneCallback callback(this);
|
Chris@45
|
1838 QFile file(path);
|
Chris@45
|
1839
|
Chris@45
|
1840 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
Chris@293
|
1841 cerr << "ERROR: MainWindowBase::openLayer("
|
Chris@294
|
1842 << source.getLocation()
|
Chris@293
|
1843 << "): Failed to open file for reading" << endl;
|
Chris@45
|
1844 return FileOpenFailed;
|
Chris@45
|
1845 }
|
Chris@45
|
1846
|
Chris@45
|
1847 SVFileReader reader(m_document, callback, source.getLocation());
|
Chris@79
|
1848 connect
|
Chris@79
|
1849 (&reader, SIGNAL(modelRegenerationFailed(QString, QString, QString)),
|
Chris@79
|
1850 this, SLOT(modelRegenerationFailed(QString, QString, QString)));
|
Chris@79
|
1851 connect
|
Chris@79
|
1852 (&reader, SIGNAL(modelRegenerationWarning(QString, QString, QString)),
|
Chris@79
|
1853 this, SLOT(modelRegenerationWarning(QString, QString, QString)));
|
Chris@45
|
1854 reader.setCurrentPane(pane);
|
Chris@45
|
1855
|
Chris@45
|
1856 QXmlInputSource inputSource(&file);
|
Chris@45
|
1857 reader.parse(inputSource);
|
Chris@45
|
1858
|
Chris@45
|
1859 if (!reader.isOK()) {
|
Chris@293
|
1860 cerr << "ERROR: MainWindowBase::openLayer("
|
Chris@294
|
1861 << source.getLocation()
|
Chris@45
|
1862 << "): Failed to read XML file: "
|
Chris@293
|
1863 << reader.getErrorString() << endl;
|
Chris@45
|
1864 return FileOpenFailed;
|
Chris@45
|
1865 }
|
Chris@45
|
1866
|
Chris@164
|
1867 emit activity(tr("Import layer XML file \"%1\"").arg(source.getLocation()));
|
Chris@164
|
1868
|
Chris@45
|
1869 m_recentFiles.addFile(source.getLocation());
|
Chris@45
|
1870
|
Chris@45
|
1871 if (!source.isRemote()) {
|
Chris@45
|
1872 registerLastOpenedFilePath(FileFinder::LayerFile, path); // for file dialog
|
Chris@45
|
1873 }
|
Chris@45
|
1874
|
Chris@75
|
1875 return FileOpenSucceeded;
|
Chris@75
|
1876
|
Chris@45
|
1877 } else {
|
Chris@45
|
1878
|
Chris@45
|
1879 try {
|
Chris@45
|
1880
|
Chris@109
|
1881 MIDIFileImportDialog midiDlg(this);
|
Chris@109
|
1882
|
Chris@109
|
1883 Model *model = DataFileReaderFactory::loadNonCSV
|
Chris@109
|
1884 (path, &midiDlg, getMainModel()->getSampleRate());
|
Chris@45
|
1885
|
Chris@109
|
1886 if (!model) {
|
Chris@196
|
1887 CSVFormat format(path);
|
Chris@196
|
1888 format.setSampleRate(getMainModel()->getSampleRate());
|
Chris@196
|
1889 CSVFormatDialog *dialog = new CSVFormatDialog(this, format);
|
Chris@109
|
1890 if (dialog->exec() == QDialog::Accepted) {
|
Chris@109
|
1891 model = DataFileReaderFactory::loadCSV
|
Chris@109
|
1892 (path, dialog->getFormat(),
|
Chris@109
|
1893 getMainModel()->getSampleRate());
|
Chris@109
|
1894 }
|
Chris@619
|
1895 delete dialog;
|
Chris@109
|
1896 }
|
Chris@109
|
1897
|
Chris@45
|
1898 if (model) {
|
Chris@45
|
1899
|
Chris@233
|
1900 SVDEBUG << "MainWindowBase::openLayer: Have model" << endl;
|
Chris@45
|
1901
|
Chris@164
|
1902 emit activity(tr("Import MIDI file \"%1\"").arg(source.getLocation()));
|
Chris@164
|
1903
|
Chris@45
|
1904 Layer *newLayer = m_document->createImportedLayer(model);
|
Chris@45
|
1905
|
Chris@45
|
1906 if (newLayer) {
|
Chris@45
|
1907
|
Chris@45
|
1908 m_document->addLayerToView(pane, newLayer);
|
Chris@88
|
1909 m_paneStack->setCurrentLayer(pane, newLayer);
|
Chris@88
|
1910
|
Chris@45
|
1911 m_recentFiles.addFile(source.getLocation());
|
Chris@45
|
1912
|
Chris@45
|
1913 if (!source.isRemote()) {
|
Chris@45
|
1914 registerLastOpenedFilePath
|
Chris@45
|
1915 (FileFinder::LayerFile,
|
Chris@45
|
1916 path); // for file dialog
|
Chris@45
|
1917 }
|
Chris@88
|
1918
|
Chris@45
|
1919 return FileOpenSucceeded;
|
Chris@45
|
1920 }
|
Chris@45
|
1921 }
|
Chris@45
|
1922 } catch (DataFileReaderFactory::Exception e) {
|
Chris@45
|
1923 if (e == DataFileReaderFactory::ImportCancelled) {
|
Chris@45
|
1924 return FileOpenCancelled;
|
Chris@45
|
1925 }
|
Chris@45
|
1926 }
|
Chris@45
|
1927 }
|
Chris@45
|
1928
|
Chris@45
|
1929 return FileOpenFailed;
|
Chris@45
|
1930 }
|
Chris@45
|
1931
|
Chris@45
|
1932 MainWindowBase::FileOpenStatus
|
Chris@45
|
1933 MainWindowBase::openImage(FileSource source)
|
Chris@45
|
1934 {
|
Chris@233
|
1935 SVDEBUG << "MainWindowBase::openImage(" << source.getLocation() << ")" << endl;
|
Chris@135
|
1936
|
Chris@45
|
1937 Pane *pane = m_paneStack->getCurrentPane();
|
Chris@45
|
1938
|
Chris@45
|
1939 if (!pane) {
|
Chris@595
|
1940 // shouldn't happen, as the menu action should have been disabled
|
Chris@595
|
1941 cerr << "WARNING: MainWindowBase::openImage: no current pane" << endl;
|
Chris@595
|
1942 return FileOpenWrongMode;
|
Chris@45
|
1943 }
|
Chris@45
|
1944
|
Chris@45
|
1945 if (!m_document->getMainModel()) {
|
Chris@45
|
1946 return FileOpenWrongMode;
|
Chris@45
|
1947 }
|
Chris@45
|
1948
|
Chris@45
|
1949 bool newLayer = false;
|
Chris@45
|
1950
|
Chris@45
|
1951 ImageLayer *il = dynamic_cast<ImageLayer *>(pane->getSelectedLayer());
|
Chris@45
|
1952 if (!il) {
|
Chris@45
|
1953 for (int i = pane->getLayerCount()-1; i >= 0; --i) {
|
Chris@45
|
1954 il = dynamic_cast<ImageLayer *>(pane->getLayer(i));
|
Chris@45
|
1955 if (il) break;
|
Chris@45
|
1956 }
|
Chris@45
|
1957 }
|
Chris@45
|
1958 if (!il) {
|
Chris@45
|
1959 il = dynamic_cast<ImageLayer *>
|
Chris@45
|
1960 (m_document->createEmptyLayer(LayerFactory::Image));
|
Chris@45
|
1961 if (!il) return FileOpenFailed;
|
Chris@45
|
1962 newLayer = true;
|
Chris@45
|
1963 }
|
Chris@45
|
1964
|
Chris@45
|
1965 // We don't put the image file in Recent Files
|
Chris@45
|
1966
|
Chris@293
|
1967 cerr << "openImage: trying location \"" << source.getLocation() << "\" in image layer" << endl;
|
Chris@45
|
1968
|
Chris@45
|
1969 if (!il->addImage(m_viewManager->getGlobalCentreFrame(), source.getLocation())) {
|
Chris@45
|
1970 if (newLayer) {
|
Chris@52
|
1971 m_document->deleteLayer(il); // also releases its model
|
Chris@45
|
1972 }
|
Chris@45
|
1973 return FileOpenFailed;
|
Chris@45
|
1974 } else {
|
Chris@45
|
1975 if (newLayer) {
|
Chris@45
|
1976 m_document->addLayerToView(pane, il);
|
Chris@45
|
1977 }
|
Chris@45
|
1978 m_paneStack->setCurrentLayer(pane, il);
|
Chris@45
|
1979 }
|
Chris@45
|
1980
|
Chris@45
|
1981 return FileOpenSucceeded;
|
Chris@45
|
1982 }
|
Chris@45
|
1983
|
Chris@45
|
1984 MainWindowBase::FileOpenStatus
|
Chris@427
|
1985 MainWindowBase::openDirOfAudio(QString dirPath)
|
Chris@427
|
1986 {
|
Chris@427
|
1987 QDir dir(dirPath);
|
Chris@427
|
1988 QStringList files = dir.entryList(QDir::Files | QDir::Readable);
|
Chris@427
|
1989 files.sort();
|
Chris@427
|
1990
|
Chris@427
|
1991 FileOpenStatus status = FileOpenFailed;
|
Chris@427
|
1992 bool first = true;
|
Chris@427
|
1993 bool cancelled = false;
|
Chris@427
|
1994
|
Chris@427
|
1995 foreach (QString file, files) {
|
Chris@427
|
1996
|
Chris@427
|
1997 FileSource source(dir.filePath(file));
|
Chris@427
|
1998 if (!source.isAvailable()) {
|
Chris@427
|
1999 continue;
|
Chris@427
|
2000 }
|
Chris@427
|
2001
|
Chris@427
|
2002 if (AudioFileReaderFactory::getKnownExtensions().contains
|
Chris@427
|
2003 (source.getExtension().toLower())) {
|
Chris@427
|
2004
|
Chris@427
|
2005 AudioFileOpenMode mode = CreateAdditionalModel;
|
Chris@427
|
2006 if (first) mode = ReplaceSession;
|
Chris@427
|
2007
|
Chris@427
|
2008 switch (openAudio(source, mode)) {
|
Chris@427
|
2009 case FileOpenSucceeded:
|
Chris@427
|
2010 status = FileOpenSucceeded;
|
Chris@427
|
2011 first = false;
|
Chris@427
|
2012 break;
|
Chris@427
|
2013 case FileOpenFailed:
|
Chris@427
|
2014 break;
|
Chris@427
|
2015 case FileOpenCancelled:
|
Chris@427
|
2016 cancelled = true;
|
Chris@427
|
2017 break;
|
Chris@427
|
2018 case FileOpenWrongMode:
|
Chris@427
|
2019 break;
|
Chris@427
|
2020 }
|
Chris@427
|
2021 }
|
Chris@427
|
2022
|
Chris@427
|
2023 if (cancelled) break;
|
Chris@427
|
2024 }
|
Chris@427
|
2025
|
Chris@427
|
2026 return status;
|
Chris@427
|
2027 }
|
Chris@427
|
2028
|
Chris@427
|
2029 MainWindowBase::FileOpenStatus
|
Chris@373
|
2030 MainWindowBase::openSessionPath(QString fileOrUrl)
|
Chris@45
|
2031 {
|
Chris@134
|
2032 ProgressDialog dialog(tr("Opening session..."), true, 2000, this);
|
Chris@134
|
2033 connect(&dialog, SIGNAL(showing()), this, SIGNAL(hideSplash()));
|
Chris@109
|
2034 return openSession(FileSource(fileOrUrl, &dialog));
|
Chris@45
|
2035 }
|
Chris@45
|
2036
|
Chris@45
|
2037 MainWindowBase::FileOpenStatus
|
Chris@45
|
2038 MainWindowBase::openSession(FileSource source)
|
Chris@45
|
2039 {
|
Chris@233
|
2040 SVDEBUG << "MainWindowBase::openSession(" << source.getLocation() << ")" << endl;
|
Chris@135
|
2041
|
Chris@45
|
2042 if (!source.isAvailable()) return FileOpenFailed;
|
Chris@145
|
2043 source.waitForData();
|
Chris@141
|
2044
|
Chris@341
|
2045 QString sessionExt =
|
Chris@341
|
2046 InteractiveFileFinder::getInstance()->getApplicationSessionExtension();
|
Chris@341
|
2047
|
Chris@341
|
2048 if (source.getExtension().toLower() != sessionExt) {
|
Chris@145
|
2049
|
Chris@145
|
2050 RDFImporter::RDFDocumentType rdfType =
|
Chris@145
|
2051 RDFImporter::identifyDocumentType
|
Chris@145
|
2052 (QUrl::fromLocalFile(source.getLocalFilename()).toString());
|
Chris@145
|
2053
|
Chris@293
|
2054 // cerr << "RDF type: " << (int)rdfType << endl;
|
Chris@148
|
2055
|
Chris@145
|
2056 if (rdfType == RDFImporter::AudioRefAndAnnotations ||
|
Chris@145
|
2057 rdfType == RDFImporter::AudioRef) {
|
Chris@145
|
2058 return openSessionFromRDF(source);
|
Chris@145
|
2059 } else if (rdfType != RDFImporter::NotRDF) {
|
Chris@145
|
2060 return FileOpenFailed;
|
Chris@145
|
2061 }
|
Chris@145
|
2062
|
Chris@152
|
2063 if (source.getExtension().toLower() == "xml") {
|
Chris@140
|
2064 if (SVFileReader::identifyXmlFile(source.getLocalFilename()) ==
|
Chris@140
|
2065 SVFileReader::SVSessionFile) {
|
Chris@293
|
2066 cerr << "This XML file looks like a session file, attempting to open it as a session" << endl;
|
Chris@140
|
2067 } else {
|
Chris@140
|
2068 return FileOpenFailed;
|
Chris@140
|
2069 }
|
Chris@140
|
2070 } else {
|
Chris@140
|
2071 return FileOpenFailed;
|
Chris@140
|
2072 }
|
Chris@140
|
2073 }
|
Chris@45
|
2074
|
Chris@636
|
2075 QXmlInputSource *inputSource = nullptr;
|
Chris@636
|
2076 BZipFileDevice *bzFile = nullptr;
|
Chris@636
|
2077 QFile *rawFile = nullptr;
|
Chris@140
|
2078
|
Chris@341
|
2079 if (source.getExtension().toLower() == sessionExt) {
|
Chris@140
|
2080 bzFile = new BZipFileDevice(source.getLocalFilename());
|
Chris@140
|
2081 if (!bzFile->open(QIODevice::ReadOnly)) {
|
Chris@140
|
2082 delete bzFile;
|
Chris@140
|
2083 return FileOpenFailed;
|
Chris@140
|
2084 }
|
Chris@140
|
2085 inputSource = new QXmlInputSource(bzFile);
|
Chris@140
|
2086 } else {
|
Chris@140
|
2087 rawFile = new QFile(source.getLocalFilename());
|
Chris@140
|
2088 inputSource = new QXmlInputSource(rawFile);
|
Chris@140
|
2089 }
|
Chris@140
|
2090
|
Chris@140
|
2091 if (!checkSaveModified()) {
|
Chris@140
|
2092 if (bzFile) bzFile->close();
|
Chris@140
|
2093 delete inputSource;
|
Chris@140
|
2094 delete bzFile;
|
Chris@140
|
2095 delete rawFile;
|
Chris@140
|
2096 return FileOpenCancelled;
|
Chris@140
|
2097 }
|
Chris@45
|
2098
|
Chris@45
|
2099 QString error;
|
Chris@45
|
2100 closeSession();
|
Chris@45
|
2101 createDocument();
|
Chris@45
|
2102
|
Chris@45
|
2103 PaneCallback callback(this);
|
Chris@45
|
2104 m_viewManager->clearSelections();
|
Chris@45
|
2105
|
Chris@45
|
2106 SVFileReader reader(m_document, callback, source.getLocation());
|
Chris@79
|
2107 connect
|
Chris@79
|
2108 (&reader, SIGNAL(modelRegenerationFailed(QString, QString, QString)),
|
Chris@79
|
2109 this, SLOT(modelRegenerationFailed(QString, QString, QString)));
|
Chris@79
|
2110 connect
|
Chris@79
|
2111 (&reader, SIGNAL(modelRegenerationWarning(QString, QString, QString)),
|
Chris@79
|
2112 this, SLOT(modelRegenerationWarning(QString, QString, QString)));
|
Chris@140
|
2113
|
Chris@140
|
2114 reader.parse(*inputSource);
|
Chris@45
|
2115
|
Chris@45
|
2116 if (!reader.isOK()) {
|
Chris@45
|
2117 error = tr("SV XML file read error:\n%1").arg(reader.getErrorString());
|
Chris@45
|
2118 }
|
Chris@45
|
2119
|
Chris@140
|
2120 if (bzFile) bzFile->close();
|
Chris@140
|
2121
|
Chris@140
|
2122 delete inputSource;
|
Chris@140
|
2123 delete bzFile;
|
Chris@140
|
2124 delete rawFile;
|
Chris@45
|
2125
|
Chris@45
|
2126 bool ok = (error == "");
|
Chris@45
|
2127
|
Chris@45
|
2128 if (ok) {
|
Chris@45
|
2129
|
Chris@164
|
2130 emit activity(tr("Import session file \"%1\"").arg(source.getLocation()));
|
Chris@164
|
2131
|
Chris@595
|
2132 setWindowTitle(tr("%1: %2")
|
Chris@57
|
2133 .arg(QApplication::applicationName())
|
Chris@595
|
2134 .arg(source.getLocation()));
|
Chris@595
|
2135
|
Chris@601
|
2136 if (!source.isRemote() && !m_document->isIncomplete()) {
|
Chris@601
|
2137 // Setting the session file path enables the Save (as
|
Chris@601
|
2138 // opposed to Save As...) option. We can't do this if we
|
Chris@601
|
2139 // don't have a local path to save to, but we also don't
|
Chris@601
|
2140 // want to do it if we failed to find an audio file or
|
Chris@601
|
2141 // similar on load, as the audio reference would then end
|
Chris@601
|
2142 // up being lost from any saved or auto-saved-on-exit copy
|
Chris@601
|
2143 m_sessionFile = source.getLocalFilename();
|
Chris@602
|
2144 } else {
|
Chris@602
|
2145 QMessageBox::warning
|
Chris@602
|
2146 (this,
|
Chris@602
|
2147 tr("Incomplete session loaded"),
|
Chris@603
|
2148 tr("Some of the audio content referred to by the original session file could not be loaded.\nIf you save this session, it will be saved without any reference to that audio, and information may be lost."),
|
Chris@602
|
2149 QMessageBox::Ok);
|
Chris@601
|
2150 }
|
Chris@595
|
2151
|
Chris@595
|
2152 setupMenus();
|
Chris@577
|
2153 findTimeRulerLayer();
|
Chris@45
|
2154
|
Chris@595
|
2155 CommandHistory::getInstance()->clear();
|
Chris@595
|
2156 CommandHistory::getInstance()->documentSaved();
|
Chris@595
|
2157 m_documentModified = false;
|
Chris@595
|
2158 updateMenuStates();
|
Chris@45
|
2159
|
Chris@227
|
2160 m_recentFiles.addFile(source.getLocation());
|
Chris@45
|
2161
|
Chris@45
|
2162 if (!source.isRemote()) {
|
Chris@45
|
2163 // for file dialog
|
Chris@45
|
2164 registerLastOpenedFilePath(FileFinder::SessionFile,
|
Chris@227
|
2165 source.getLocalFilename());
|
Chris@45
|
2166 }
|
Chris@45
|
2167
|
Chris@342
|
2168 emit sessionLoaded();
|
Chris@342
|
2169
|
Chris@45
|
2170 } else {
|
Chris@595
|
2171 setWindowTitle(QApplication::applicationName());
|
Chris@45
|
2172 }
|
Chris@45
|
2173
|
Chris@45
|
2174 return ok ? FileOpenSucceeded : FileOpenFailed;
|
Chris@45
|
2175 }
|
Chris@45
|
2176
|
Chris@141
|
2177 MainWindowBase::FileOpenStatus
|
Chris@230
|
2178 MainWindowBase::openSessionTemplate(QString templateName)
|
Chris@230
|
2179 {
|
Chris@230
|
2180 // Template in the user's template directory takes
|
Chris@230
|
2181 // priority over a bundled one; we don't unbundle, but
|
Chris@230
|
2182 // open directly from the bundled file (where applicable)
|
Chris@230
|
2183 ResourceFinder rf;
|
Chris@230
|
2184 QString tfile = rf.getResourcePath("templates", templateName + ".svt");
|
Chris@230
|
2185 if (tfile != "") {
|
Chris@294
|
2186 cerr << "SV loading template file " << tfile << endl;
|
Chris@230
|
2187 return openSessionTemplate(FileSource("file:" + tfile));
|
Chris@230
|
2188 } else {
|
Chris@230
|
2189 return FileOpenFailed;
|
Chris@230
|
2190 }
|
Chris@230
|
2191 }
|
Chris@230
|
2192
|
Chris@230
|
2193 MainWindowBase::FileOpenStatus
|
Chris@227
|
2194 MainWindowBase::openSessionTemplate(FileSource source)
|
Chris@227
|
2195 {
|
Chris@294
|
2196 cerr << "MainWindowBase::openSessionTemplate(" << source.getLocation() << ")" << endl;
|
Chris@227
|
2197
|
Chris@227
|
2198 if (!source.isAvailable()) return FileOpenFailed;
|
Chris@227
|
2199 source.waitForData();
|
Chris@227
|
2200
|
Chris@636
|
2201 QXmlInputSource *inputSource = nullptr;
|
Chris@636
|
2202 QFile *file = nullptr;
|
Chris@227
|
2203
|
Chris@227
|
2204 file = new QFile(source.getLocalFilename());
|
Chris@227
|
2205 inputSource = new QXmlInputSource(file);
|
Chris@227
|
2206
|
Chris@227
|
2207 if (!checkSaveModified()) {
|
Chris@227
|
2208 delete inputSource;
|
Chris@227
|
2209 delete file;
|
Chris@227
|
2210 return FileOpenCancelled;
|
Chris@227
|
2211 }
|
Chris@227
|
2212
|
Chris@227
|
2213 QString error;
|
Chris@227
|
2214 closeSession();
|
Chris@227
|
2215 createDocument();
|
Chris@227
|
2216
|
Chris@227
|
2217 PaneCallback callback(this);
|
Chris@227
|
2218 m_viewManager->clearSelections();
|
Chris@227
|
2219
|
Chris@227
|
2220 SVFileReader reader(m_document, callback, source.getLocation());
|
Chris@227
|
2221 connect
|
Chris@227
|
2222 (&reader, SIGNAL(modelRegenerationFailed(QString, QString, QString)),
|
Chris@227
|
2223 this, SLOT(modelRegenerationFailed(QString, QString, QString)));
|
Chris@227
|
2224 connect
|
Chris@227
|
2225 (&reader, SIGNAL(modelRegenerationWarning(QString, QString, QString)),
|
Chris@227
|
2226 this, SLOT(modelRegenerationWarning(QString, QString, QString)));
|
Chris@227
|
2227
|
Chris@227
|
2228 reader.parse(*inputSource);
|
Chris@227
|
2229
|
Chris@227
|
2230 if (!reader.isOK()) {
|
Chris@227
|
2231 error = tr("SV XML file read error:\n%1").arg(reader.getErrorString());
|
Chris@227
|
2232 }
|
Chris@227
|
2233
|
Chris@227
|
2234 delete inputSource;
|
Chris@227
|
2235 delete file;
|
Chris@227
|
2236
|
Chris@227
|
2237 bool ok = (error == "");
|
Chris@227
|
2238
|
Chris@227
|
2239 setWindowTitle(QApplication::applicationName());
|
Chris@227
|
2240
|
Chris@227
|
2241 if (ok) {
|
Chris@227
|
2242
|
Chris@227
|
2243 emit activity(tr("Open session template \"%1\"").arg(source.getLocation()));
|
Chris@227
|
2244
|
Chris@595
|
2245 setupMenus();
|
Chris@577
|
2246 findTimeRulerLayer();
|
Chris@227
|
2247
|
Chris@595
|
2248 CommandHistory::getInstance()->clear();
|
Chris@595
|
2249 CommandHistory::getInstance()->documentSaved();
|
Chris@595
|
2250 m_documentModified = false;
|
Chris@595
|
2251 updateMenuStates();
|
Chris@342
|
2252
|
Chris@342
|
2253 emit sessionLoaded();
|
Chris@227
|
2254 }
|
Chris@227
|
2255
|
Chris@227
|
2256 return ok ? FileOpenSucceeded : FileOpenFailed;
|
Chris@227
|
2257 }
|
Chris@227
|
2258
|
Chris@227
|
2259 MainWindowBase::FileOpenStatus
|
Chris@141
|
2260 MainWindowBase::openSessionFromRDF(FileSource source)
|
Chris@141
|
2261 {
|
Chris@233
|
2262 SVDEBUG << "MainWindowBase::openSessionFromRDF(" << source.getLocation() << ")" << endl;
|
Chris@141
|
2263
|
Chris@141
|
2264 if (!source.isAvailable()) return FileOpenFailed;
|
Chris@141
|
2265 source.waitForData();
|
Chris@141
|
2266
|
Chris@145
|
2267 if (!checkSaveModified()) {
|
Chris@145
|
2268 return FileOpenCancelled;
|
Chris@141
|
2269 }
|
Chris@143
|
2270
|
Chris@145
|
2271 closeSession();
|
Chris@145
|
2272 createDocument();
|
Chris@145
|
2273
|
Chris@145
|
2274 FileOpenStatus status = openLayersFromRDF(source);
|
Chris@141
|
2275
|
Chris@141
|
2276 setupMenus();
|
Chris@577
|
2277 findTimeRulerLayer();
|
Chris@141
|
2278
|
Chris@141
|
2279 setWindowTitle(tr("%1: %2")
|
Chris@141
|
2280 .arg(QApplication::applicationName())
|
Chris@141
|
2281 .arg(source.getLocation()));
|
Chris@141
|
2282 CommandHistory::getInstance()->clear();
|
Chris@141
|
2283 CommandHistory::getInstance()->documentSaved();
|
Chris@141
|
2284 m_documentModified = false;
|
Chris@145
|
2285
|
Chris@342
|
2286 emit sessionLoaded();
|
Chris@342
|
2287
|
Chris@145
|
2288 return status;
|
Chris@145
|
2289 }
|
Chris@145
|
2290
|
Chris@145
|
2291 MainWindowBase::FileOpenStatus
|
Chris@145
|
2292 MainWindowBase::openLayersFromRDF(FileSource source)
|
Chris@145
|
2293 {
|
Chris@435
|
2294 sv_samplerate_t rate = 0;
|
Chris@145
|
2295
|
Chris@233
|
2296 SVDEBUG << "MainWindowBase::openLayersFromRDF" << endl;
|
Chris@186
|
2297
|
Chris@145
|
2298 ProgressDialog dialog(tr("Importing from RDF..."), true, 2000, this);
|
Chris@145
|
2299 connect(&dialog, SIGNAL(showing()), this, SIGNAL(hideSplash()));
|
Chris@145
|
2300
|
Chris@145
|
2301 if (getMainModel()) {
|
Chris@145
|
2302 rate = getMainModel()->getSampleRate();
|
Chris@145
|
2303 } else if (Preferences::getInstance()->getResampleOnLoad()) {
|
Chris@552
|
2304 if (getMainModel()) {
|
Chris@552
|
2305 rate = getMainModel()->getSampleRate();
|
Chris@552
|
2306 }
|
Chris@145
|
2307 }
|
Chris@145
|
2308
|
Chris@145
|
2309 RDFImporter importer
|
Chris@145
|
2310 (QUrl::fromLocalFile(source.getLocalFilename()).toString(), rate);
|
Chris@145
|
2311
|
Chris@145
|
2312 if (!importer.isOK()) {
|
Chris@147
|
2313 if (importer.getErrorString() != "") {
|
Chris@147
|
2314 QMessageBox::critical
|
Chris@147
|
2315 (this, tr("Failed to import RDF"),
|
Chris@147
|
2316 tr("<b>Failed to import RDF</b><p>Importing data from RDF document at \"%1\" failed: %2</p>")
|
Chris@147
|
2317 .arg(source.getLocation()).arg(importer.getErrorString()));
|
Chris@147
|
2318 }
|
Chris@145
|
2319 return FileOpenFailed;
|
Chris@145
|
2320 }
|
Chris@145
|
2321
|
Chris@145
|
2322 std::vector<Model *> models = importer.getDataModels(&dialog);
|
Chris@145
|
2323
|
Chris@145
|
2324 dialog.setMessage(tr("Importing from RDF..."));
|
Chris@145
|
2325
|
Chris@145
|
2326 if (models.empty()) {
|
Chris@186
|
2327 QMessageBox::critical
|
Chris@186
|
2328 (this, tr("Failed to import RDF"),
|
Chris@186
|
2329 tr("<b>Failed to import RDF</b><p>No suitable data models found for import from RDF document at \"%1\"</p>").arg(source.getLocation()));
|
Chris@145
|
2330 return FileOpenFailed;
|
Chris@145
|
2331 }
|
Chris@145
|
2332
|
Chris@164
|
2333 emit activity(tr("Import RDF document \"%1\"").arg(source.getLocation()));
|
Chris@164
|
2334
|
Chris@145
|
2335 std::set<Model *> added;
|
Chris@145
|
2336
|
Chris@221
|
2337 for (int i = 0; i < (int)models.size(); ++i) {
|
Chris@145
|
2338
|
Chris@145
|
2339 Model *m = models[i];
|
Chris@145
|
2340 WaveFileModel *w = dynamic_cast<WaveFileModel *>(m);
|
Chris@145
|
2341
|
Chris@145
|
2342 if (w) {
|
Chris@145
|
2343
|
Chris@145
|
2344 Pane *pane = addPaneToStack();
|
Chris@636
|
2345 Layer *layer = nullptr;
|
Chris@145
|
2346
|
Chris@145
|
2347 if (m_timeRulerLayer) {
|
Chris@145
|
2348 m_document->addLayerToView(pane, m_timeRulerLayer);
|
Chris@145
|
2349 }
|
Chris@145
|
2350
|
Chris@145
|
2351 if (!getMainModel()) {
|
Chris@145
|
2352 m_document->setMainModel(w);
|
Chris@145
|
2353 layer = m_document->createMainModelLayer(LayerFactory::Waveform);
|
Chris@145
|
2354 } else {
|
Chris@145
|
2355 layer = m_document->createImportedLayer(w);
|
Chris@145
|
2356 }
|
Chris@145
|
2357
|
Chris@145
|
2358 m_document->addLayerToView(pane, layer);
|
Chris@145
|
2359
|
Chris@145
|
2360 added.insert(w);
|
Chris@145
|
2361
|
Chris@221
|
2362 for (int j = 0; j < (int)models.size(); ++j) {
|
Chris@145
|
2363
|
Chris@145
|
2364 Model *dm = models[j];
|
Chris@145
|
2365
|
Chris@145
|
2366 if (dm == m) continue;
|
Chris@145
|
2367 if (dm->getSourceModel() != m) continue;
|
Chris@145
|
2368
|
Chris@145
|
2369 layer = m_document->createImportedLayer(dm);
|
Chris@145
|
2370
|
Chris@145
|
2371 if (layer->isLayerOpaque() ||
|
Chris@145
|
2372 dynamic_cast<Colour3DPlotLayer *>(layer)) {
|
Chris@145
|
2373
|
Chris@156
|
2374 // these always go in a new pane, with nothing
|
Chris@156
|
2375 // else going in the same pane
|
Chris@156
|
2376
|
Chris@145
|
2377 Pane *singleLayerPane = addPaneToStack();
|
Chris@145
|
2378 if (m_timeRulerLayer) {
|
Chris@145
|
2379 m_document->addLayerToView(singleLayerPane, m_timeRulerLayer);
|
Chris@145
|
2380 }
|
Chris@145
|
2381 m_document->addLayerToView(singleLayerPane, layer);
|
Chris@145
|
2382
|
Chris@156
|
2383 } else if (layer->getLayerColourSignificance() ==
|
Chris@156
|
2384 Layer::ColourHasMeaningfulValue) {
|
Chris@156
|
2385
|
Chris@156
|
2386 // these can go in a pane with something else, but
|
Chris@156
|
2387 // only if none of the something elses also have
|
Chris@156
|
2388 // this quality
|
Chris@156
|
2389
|
Chris@156
|
2390 bool needNewPane = false;
|
Chris@156
|
2391 for (int i = 0; i < pane->getLayerCount(); ++i) {
|
Chris@156
|
2392 Layer *otherLayer = pane->getLayer(i);
|
Chris@156
|
2393 if (otherLayer &&
|
Chris@156
|
2394 (otherLayer->getLayerColourSignificance() ==
|
Chris@156
|
2395 Layer::ColourHasMeaningfulValue)) {
|
Chris@156
|
2396 needNewPane = true;
|
Chris@156
|
2397 break;
|
Chris@156
|
2398 }
|
Chris@156
|
2399 }
|
Chris@156
|
2400 if (needNewPane) {
|
Chris@156
|
2401 pane = addPaneToStack();
|
Chris@156
|
2402 }
|
Chris@156
|
2403
|
Chris@156
|
2404 m_document->addLayerToView(pane, layer);
|
Chris@156
|
2405
|
Chris@145
|
2406 } else {
|
Chris@145
|
2407
|
Chris@145
|
2408 if (pane->getLayerCount() > 4) {
|
Chris@145
|
2409 pane = addPaneToStack();
|
Chris@145
|
2410 }
|
Chris@145
|
2411
|
Chris@145
|
2412 m_document->addLayerToView(pane, layer);
|
Chris@145
|
2413 }
|
Chris@145
|
2414
|
Chris@145
|
2415 added.insert(dm);
|
Chris@145
|
2416 }
|
Chris@145
|
2417 }
|
Chris@145
|
2418 }
|
Chris@145
|
2419
|
Chris@221
|
2420 for (int i = 0; i < (int)models.size(); ++i) {
|
Chris@145
|
2421
|
Chris@145
|
2422 Model *m = models[i];
|
Chris@145
|
2423
|
Chris@145
|
2424 if (added.find(m) == added.end()) {
|
Chris@145
|
2425
|
Chris@145
|
2426 Layer *layer = m_document->createImportedLayer(m);
|
Chris@145
|
2427 if (!layer) return FileOpenFailed;
|
Chris@145
|
2428
|
Chris@145
|
2429 Pane *singleLayerPane = addPaneToStack();
|
Chris@145
|
2430 if (m_timeRulerLayer) {
|
Chris@145
|
2431 m_document->addLayerToView(singleLayerPane, m_timeRulerLayer);
|
Chris@145
|
2432 }
|
Chris@145
|
2433 m_document->addLayerToView(singleLayerPane, layer);
|
Chris@145
|
2434 }
|
Chris@145
|
2435 }
|
Chris@145
|
2436
|
Chris@145
|
2437 m_recentFiles.addFile(source.getLocation());
|
Chris@145
|
2438 return FileOpenSucceeded;
|
Chris@141
|
2439 }
|
Chris@141
|
2440
|
Chris@584
|
2441 class AudioLogCallback : public breakfastquay::AudioFactory::LogCallback
|
Chris@584
|
2442 {
|
Chris@584
|
2443 public:
|
Chris@584
|
2444 void log(std::string message) const override {
|
Chris@584
|
2445 SVDEBUG << message << endl;
|
Chris@584
|
2446 }
|
Chris@584
|
2447 };
|
Chris@584
|
2448
|
Chris@45
|
2449 void
|
Chris@475
|
2450 MainWindowBase::createAudioIO()
|
Chris@45
|
2451 {
|
Chris@475
|
2452 if (m_playTarget || m_audioIO) return;
|
Chris@475
|
2453
|
Chris@584
|
2454 static AudioLogCallback audioLogCallback;
|
Chris@584
|
2455 breakfastquay::AudioFactory::setLogCallback(&audioLogCallback);
|
Chris@584
|
2456
|
Chris@475
|
2457 if (!(m_soundOptions & WithAudioOutput)) return;
|
Chris@45
|
2458
|
Chris@126
|
2459 QSettings settings;
|
Chris@126
|
2460 settings.beginGroup("Preferences");
|
Chris@547
|
2461 QString implementation = settings.value
|
Chris@547
|
2462 ("audio-target", "").toString();
|
Chris@547
|
2463 QString suffix;
|
Chris@547
|
2464 if (implementation != "") suffix = "-" + implementation;
|
Chris@547
|
2465 QString recordDevice = settings.value
|
Chris@547
|
2466 ("audio-record-device" + suffix, "").toString();
|
Chris@547
|
2467 QString playbackDevice = settings.value
|
Chris@547
|
2468 ("audio-playback-device" + suffix, "").toString();
|
Chris@126
|
2469 settings.endGroup();
|
Chris@547
|
2470
|
Chris@547
|
2471 if (implementation == "auto") {
|
Chris@547
|
2472 implementation = "";
|
Chris@547
|
2473 }
|
Chris@468
|
2474
|
Chris@547
|
2475 breakfastquay::AudioFactory::Preference preference;
|
Chris@547
|
2476 preference.implementation = implementation.toStdString();
|
Chris@547
|
2477 preference.recordDevice = recordDevice.toStdString();
|
Chris@547
|
2478 preference.playbackDevice = playbackDevice.toStdString();
|
Chris@547
|
2479
|
Chris@547
|
2480 SVCERR << "createAudioIO: Preferred implementation = \""
|
Chris@547
|
2481 << preference.implementation << "\"" << endl;
|
Chris@547
|
2482 SVCERR << "createAudioIO: Preferred playback device = \""
|
Chris@547
|
2483 << preference.playbackDevice << "\"" << endl;
|
Chris@547
|
2484 SVCERR << "createAudioIO: Preferred record device = \""
|
Chris@547
|
2485 << preference.recordDevice << "\"" << endl;
|
Chris@475
|
2486
|
Chris@551
|
2487 if (!m_resamplerWrapper) {
|
Chris@551
|
2488 m_resamplerWrapper = new breakfastquay::ResamplerWrapper(m_playSource);
|
Chris@551
|
2489 m_playSource->setResamplerWrapper(m_resamplerWrapper);
|
Chris@551
|
2490 }
|
Chris@569
|
2491
|
Chris@569
|
2492 std::string errorString;
|
Chris@551
|
2493
|
Chris@475
|
2494 if (m_soundOptions & WithAudioInput) {
|
Chris@475
|
2495 m_audioIO = breakfastquay::AudioFactory::
|
Chris@569
|
2496 createCallbackIO(m_recordTarget, m_resamplerWrapper,
|
Chris@569
|
2497 preference, errorString);
|
Chris@525
|
2498 if (m_audioIO) {
|
Chris@611
|
2499 SVCERR << "MainWindowBase::createAudioIO: Suspending on creation" << endl;
|
Chris@525
|
2500 m_audioIO->suspend(); // start in suspended state
|
Chris@525
|
2501 m_playSource->setSystemPlaybackTarget(m_audioIO);
|
Chris@586
|
2502 } else {
|
Chris@586
|
2503 // Failed to create audio I/O; this may just mean there is
|
Chris@586
|
2504 // no record device, so fall through to see what happens
|
Chris@586
|
2505 // next. We only report complete failure if we end up with
|
Chris@586
|
2506 // neither m_audioIO nor m_playTarget.
|
Chris@525
|
2507 }
|
Chris@586
|
2508 }
|
Chris@586
|
2509
|
Chris@586
|
2510 if (!m_audioIO) {
|
Chris@475
|
2511 m_playTarget = breakfastquay::AudioFactory::
|
Chris@569
|
2512 createCallbackPlayTarget(m_resamplerWrapper,
|
Chris@569
|
2513 preference, errorString);
|
Chris@525
|
2514 if (m_playTarget) {
|
Chris@611
|
2515 SVCERR << "MainWindowBase::createAudioIO: Suspending on creation" << endl;
|
Chris@525
|
2516 m_playTarget->suspend(); // start in suspended state
|
Chris@525
|
2517 m_playSource->setSystemPlaybackTarget(m_playTarget);
|
Chris@525
|
2518 }
|
Chris@475
|
2519 }
|
Chris@475
|
2520
|
Chris@475
|
2521 if (!m_playTarget && !m_audioIO) {
|
Chris@104
|
2522 emit hideSplash();
|
Chris@569
|
2523 QString message;
|
Chris@569
|
2524 QString error = errorString.c_str();
|
Chris@569
|
2525 QString firstBit, secondBit;
|
Chris@547
|
2526 if (implementation == "") {
|
Chris@569
|
2527 if (error == "") {
|
Chris@569
|
2528 firstBit = tr("<b>No audio available</b><p>Could not open an audio device.</p>");
|
Chris@569
|
2529 } else {
|
Chris@569
|
2530 firstBit = tr("<b>No audio available</b><p>Could not open audio device: %1</p>").arg(error);
|
Chris@569
|
2531 }
|
Chris@569
|
2532 if (m_soundOptions & WithAudioInput) {
|
Chris@569
|
2533 secondBit = tr("<p>Automatic audio device detection failed. Audio playback and recording will not be available during this session.</p>");
|
Chris@569
|
2534 } else {
|
Chris@569
|
2535 secondBit = tr("<p>Automatic audio device detection failed. Audio playback will not be available during this session.</p>");
|
Chris@569
|
2536 }
|
Chris@126
|
2537 } else {
|
Chris@569
|
2538 QString driverName = breakfastquay::AudioFactory::
|
Chris@569
|
2539 getImplementationDescription(implementation.toStdString())
|
Chris@569
|
2540 .c_str();
|
Chris@569
|
2541 if (error == "") {
|
Chris@569
|
2542 firstBit = tr("<b>No audio available</b><p>Failed to open your preferred audio driver (\"%1\").</p>").arg(driverName);
|
Chris@569
|
2543 } else {
|
Chris@569
|
2544 firstBit = tr("<b>No audio available</b><p>Failed to open your preferred audio driver (\"%1\"): %2.</p>").arg(driverName).arg(error);
|
Chris@569
|
2545 }
|
Chris@569
|
2546 if (m_soundOptions & WithAudioInput) {
|
Chris@569
|
2547 secondBit = tr("<p>Audio playback and recording will not be available during this session.</p>");
|
Chris@569
|
2548 } else {
|
Chris@569
|
2549 secondBit = tr("<p>Audio playback will not be available during this session.</p>");
|
Chris@569
|
2550 }
|
Chris@126
|
2551 }
|
Chris@570
|
2552 SVDEBUG << "createAudioIO: ERROR: Failed to open audio device \""
|
Chris@570
|
2553 << implementation << "\": error is: " << error << endl;
|
Chris@569
|
2554 QMessageBox::warning(this, tr("Couldn't open audio device"),
|
Chris@569
|
2555 firstBit + secondBit, QMessageBox::Ok);
|
Chris@45
|
2556 }
|
Chris@45
|
2557 }
|
Chris@45
|
2558
|
Chris@556
|
2559 void
|
Chris@556
|
2560 MainWindowBase::deleteAudioIO()
|
Chris@556
|
2561 {
|
Chris@556
|
2562 // First prevent this trying to call target.
|
Chris@559
|
2563 if (m_playSource) {
|
Chris@636
|
2564 m_playSource->setSystemPlaybackTarget(nullptr);
|
Chris@636
|
2565 m_playSource->setResamplerWrapper(nullptr);
|
Chris@559
|
2566 }
|
Chris@556
|
2567
|
Chris@556
|
2568 // Then delete the breakfastquay::System object.
|
Chris@556
|
2569 // Only one of these two exists!
|
Chris@556
|
2570 delete m_audioIO;
|
Chris@556
|
2571 delete m_playTarget;
|
Chris@556
|
2572
|
Chris@559
|
2573 // And the breakfastquay resampler wrapper. We need to
|
Chris@559
|
2574 // delete/recreate this if the channel count changes, which is one
|
Chris@559
|
2575 // of the use cases for recreateAudioIO() calling this
|
Chris@559
|
2576 delete m_resamplerWrapper;
|
Chris@559
|
2577
|
Chris@636
|
2578 m_audioIO = nullptr;
|
Chris@636
|
2579 m_playTarget = nullptr;
|
Chris@636
|
2580 m_resamplerWrapper = nullptr;
|
Chris@556
|
2581 }
|
Chris@556
|
2582
|
Chris@556
|
2583 void
|
Chris@556
|
2584 MainWindowBase::recreateAudioIO()
|
Chris@556
|
2585 {
|
Chris@556
|
2586 deleteAudioIO();
|
Chris@556
|
2587 createAudioIO();
|
Chris@556
|
2588 }
|
Chris@556
|
2589
|
Chris@570
|
2590 void
|
Chris@570
|
2591 MainWindowBase::audioChannelCountIncreased(int)
|
Chris@570
|
2592 {
|
Chris@611
|
2593 SVCERR << "MainWindowBase::audioChannelCountIncreased" << endl;
|
Chris@570
|
2594 recreateAudioIO();
|
Chris@610
|
2595
|
Chris@610
|
2596 if (m_recordTarget &&
|
Chris@610
|
2597 m_recordTarget->isRecording() &&
|
Chris@610
|
2598 m_audioIO) {
|
Chris@610
|
2599 SVCERR << "MainWindowBase::audioChannelCountIncreased: we were recording already, so resuming IO now" << endl;
|
Chris@610
|
2600 m_audioIO->resume();
|
Chris@610
|
2601 }
|
Chris@570
|
2602 }
|
Chris@570
|
2603
|
Chris@45
|
2604 WaveFileModel *
|
Chris@45
|
2605 MainWindowBase::getMainModel()
|
Chris@45
|
2606 {
|
Chris@636
|
2607 if (!m_document) return nullptr;
|
Chris@45
|
2608 return m_document->getMainModel();
|
Chris@45
|
2609 }
|
Chris@45
|
2610
|
Chris@45
|
2611 const WaveFileModel *
|
Chris@45
|
2612 MainWindowBase::getMainModel() const
|
Chris@45
|
2613 {
|
Chris@636
|
2614 if (!m_document) return nullptr;
|
Chris@45
|
2615 return m_document->getMainModel();
|
Chris@45
|
2616 }
|
Chris@45
|
2617
|
Chris@45
|
2618 void
|
Chris@45
|
2619 MainWindowBase::createDocument()
|
Chris@45
|
2620 {
|
Chris@45
|
2621 m_document = new Document;
|
Chris@45
|
2622
|
Chris@45
|
2623 connect(m_document, SIGNAL(layerAdded(Layer *)),
|
Chris@595
|
2624 this, SLOT(layerAdded(Layer *)));
|
Chris@45
|
2625 connect(m_document, SIGNAL(layerRemoved(Layer *)),
|
Chris@595
|
2626 this, SLOT(layerRemoved(Layer *)));
|
Chris@45
|
2627 connect(m_document, SIGNAL(layerAboutToBeDeleted(Layer *)),
|
Chris@595
|
2628 this, SLOT(layerAboutToBeDeleted(Layer *)));
|
Chris@45
|
2629 connect(m_document, SIGNAL(layerInAView(Layer *, bool)),
|
Chris@595
|
2630 this, SLOT(layerInAView(Layer *, bool)));
|
Chris@45
|
2631
|
Chris@45
|
2632 connect(m_document, SIGNAL(modelAdded(Model *)),
|
Chris@595
|
2633 this, SLOT(modelAdded(Model *)));
|
Chris@45
|
2634 connect(m_document, SIGNAL(mainModelChanged(WaveFileModel *)),
|
Chris@595
|
2635 this, SLOT(mainModelChanged(WaveFileModel *)));
|
Chris@45
|
2636 connect(m_document, SIGNAL(modelAboutToBeDeleted(Model *)),
|
Chris@595
|
2637 this, SLOT(modelAboutToBeDeleted(Model *)));
|
Chris@45
|
2638
|
Chris@78
|
2639 connect(m_document, SIGNAL(modelGenerationFailed(QString, QString)),
|
Chris@78
|
2640 this, SLOT(modelGenerationFailed(QString, QString)));
|
Chris@78
|
2641 connect(m_document, SIGNAL(modelRegenerationWarning(QString, QString, QString)),
|
Chris@78
|
2642 this, SLOT(modelRegenerationWarning(QString, QString, QString)));
|
Chris@429
|
2643 connect(m_document, SIGNAL(alignmentComplete(AlignmentModel *)),
|
Chris@429
|
2644 this, SLOT(alignmentComplete(AlignmentModel *)));
|
Chris@423
|
2645 connect(m_document, SIGNAL(alignmentFailed(QString)),
|
Chris@423
|
2646 this, SLOT(alignmentFailed(QString)));
|
Chris@160
|
2647
|
Chris@160
|
2648 emit replacedDocument();
|
Chris@45
|
2649 }
|
Chris@45
|
2650
|
Chris@45
|
2651 bool
|
Chris@45
|
2652 MainWindowBase::saveSessionFile(QString path)
|
Chris@45
|
2653 {
|
Chris@217
|
2654 try {
|
Chris@217
|
2655
|
Chris@217
|
2656 TempWriteFile temp(path);
|
Chris@217
|
2657
|
Chris@217
|
2658 BZipFileDevice bzFile(temp.getTemporaryFilename());
|
Chris@217
|
2659 if (!bzFile.open(QIODevice::WriteOnly)) {
|
Chris@293
|
2660 cerr << "Failed to open session file \""
|
Chris@294
|
2661 << temp.getTemporaryFilename()
|
Chris@217
|
2662 << "\" for writing: "
|
Chris@293
|
2663 << bzFile.errorString() << endl;
|
Chris@217
|
2664 return false;
|
Chris@217
|
2665 }
|
Chris@217
|
2666
|
Chris@217
|
2667 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
|
Chris@217
|
2668
|
Chris@217
|
2669 QTextStream out(&bzFile);
|
Chris@432
|
2670 out.setCodec(QTextCodec::codecForName("UTF-8"));
|
Chris@226
|
2671 toXml(out, false);
|
Chris@217
|
2672 out.flush();
|
Chris@217
|
2673
|
Chris@217
|
2674 QApplication::restoreOverrideCursor();
|
Chris@217
|
2675
|
Chris@217
|
2676 if (!bzFile.isOK()) {
|
Chris@217
|
2677 QMessageBox::critical(this, tr("Failed to write file"),
|
Chris@217
|
2678 tr("<b>Save failed</b><p>Failed to write to file \"%1\": %2")
|
Chris@217
|
2679 .arg(path).arg(bzFile.errorString()));
|
Chris@217
|
2680 bzFile.close();
|
Chris@217
|
2681 return false;
|
Chris@217
|
2682 }
|
Chris@217
|
2683
|
Chris@217
|
2684 bzFile.close();
|
Chris@217
|
2685 temp.moveToTarget();
|
Chris@217
|
2686 return true;
|
Chris@217
|
2687
|
Chris@217
|
2688 } catch (FileOperationFailed &f) {
|
Chris@217
|
2689
|
Chris@217
|
2690 QMessageBox::critical(this, tr("Failed to write file"),
|
Chris@217
|
2691 tr("<b>Save failed</b><p>Failed to write to file \"%1\": %2")
|
Chris@217
|
2692 .arg(path).arg(f.what()));
|
Chris@45
|
2693 return false;
|
Chris@45
|
2694 }
|
Chris@45
|
2695 }
|
Chris@45
|
2696
|
Chris@224
|
2697 bool
|
Chris@224
|
2698 MainWindowBase::saveSessionTemplate(QString path)
|
Chris@224
|
2699 {
|
Chris@224
|
2700 try {
|
Chris@224
|
2701
|
Chris@224
|
2702 TempWriteFile temp(path);
|
Chris@224
|
2703
|
Chris@224
|
2704 QFile file(temp.getTemporaryFilename());
|
Chris@224
|
2705 if (!file.open(QIODevice::WriteOnly)) {
|
Chris@293
|
2706 cerr << "Failed to open session template file \""
|
Chris@294
|
2707 << temp.getTemporaryFilename()
|
Chris@224
|
2708 << "\" for writing: "
|
Chris@294
|
2709 << file.errorString() << endl;
|
Chris@224
|
2710 return false;
|
Chris@224
|
2711 }
|
Chris@224
|
2712
|
Chris@224
|
2713 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
|
Chris@224
|
2714
|
Chris@224
|
2715 QTextStream out(&file);
|
Chris@432
|
2716 out.setCodec(QTextCodec::codecForName("UTF-8"));
|
Chris@226
|
2717 toXml(out, true);
|
Chris@224
|
2718 out.flush();
|
Chris@224
|
2719
|
Chris@224
|
2720 QApplication::restoreOverrideCursor();
|
Chris@224
|
2721
|
Chris@224
|
2722 file.close();
|
Chris@224
|
2723 temp.moveToTarget();
|
Chris@224
|
2724 return true;
|
Chris@224
|
2725
|
Chris@224
|
2726 } catch (FileOperationFailed &f) {
|
Chris@224
|
2727
|
Chris@224
|
2728 QMessageBox::critical(this, tr("Failed to write file"),
|
Chris@224
|
2729 tr("<b>Save failed</b><p>Failed to write to file \"%1\": %2")
|
Chris@224
|
2730 .arg(path).arg(f.what()));
|
Chris@224
|
2731 return false;
|
Chris@224
|
2732 }
|
Chris@224
|
2733 }
|
Chris@224
|
2734
|
Chris@45
|
2735 void
|
Chris@226
|
2736 MainWindowBase::toXml(QTextStream &out, bool asTemplate)
|
Chris@45
|
2737 {
|
Chris@45
|
2738 QString indent(" ");
|
Chris@45
|
2739
|
Chris@45
|
2740 out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
|
Chris@45
|
2741 out << "<!DOCTYPE sonic-visualiser>\n";
|
Chris@45
|
2742 out << "<sv>\n";
|
Chris@45
|
2743
|
Chris@226
|
2744 if (asTemplate) {
|
Chris@226
|
2745 m_document->toXmlAsTemplate(out, "", "");
|
Chris@226
|
2746 } else {
|
Chris@226
|
2747 m_document->toXml(out, "", "");
|
Chris@226
|
2748 }
|
Chris@45
|
2749
|
Chris@45
|
2750 out << "<display>\n";
|
Chris@45
|
2751
|
Chris@45
|
2752 out << QString(" <window width=\"%1\" height=\"%2\"/>\n")
|
Chris@595
|
2753 .arg(width()).arg(height());
|
Chris@45
|
2754
|
Chris@45
|
2755 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
|
Chris@45
|
2756
|
Chris@595
|
2757 Pane *pane = m_paneStack->getPane(i);
|
Chris@595
|
2758
|
Chris@595
|
2759 if (pane) {
|
Chris@45
|
2760 pane->toXml(out, indent);
|
Chris@595
|
2761 }
|
Chris@45
|
2762 }
|
Chris@45
|
2763
|
Chris@45
|
2764 out << "</display>\n";
|
Chris@45
|
2765
|
Chris@45
|
2766 m_viewManager->getSelection().toXml(out);
|
Chris@45
|
2767
|
Chris@45
|
2768 out << "</sv>\n";
|
Chris@45
|
2769 }
|
Chris@45
|
2770
|
Chris@45
|
2771 Pane *
|
Chris@45
|
2772 MainWindowBase::addPaneToStack()
|
Chris@45
|
2773 {
|
Chris@342
|
2774 cerr << "MainWindowBase::addPaneToStack()" << endl;
|
Chris@45
|
2775 AddPaneCommand *command = new AddPaneCommand(this);
|
Chris@45
|
2776 CommandHistory::getInstance()->addCommand(command);
|
Chris@57
|
2777 Pane *pane = command->getPane();
|
Chris@57
|
2778 return pane;
|
Chris@45
|
2779 }
|
Chris@45
|
2780
|
Chris@45
|
2781 void
|
Chris@45
|
2782 MainWindowBase::zoomIn()
|
Chris@45
|
2783 {
|
Chris@45
|
2784 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@45
|
2785 if (currentPane) currentPane->zoom(true);
|
Chris@45
|
2786 }
|
Chris@45
|
2787
|
Chris@45
|
2788 void
|
Chris@45
|
2789 MainWindowBase::zoomOut()
|
Chris@45
|
2790 {
|
Chris@45
|
2791 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@45
|
2792 if (currentPane) currentPane->zoom(false);
|
Chris@45
|
2793 }
|
Chris@45
|
2794
|
Chris@45
|
2795 void
|
Chris@45
|
2796 MainWindowBase::zoomToFit()
|
Chris@45
|
2797 {
|
Chris@45
|
2798 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@45
|
2799 if (!currentPane) return;
|
Chris@45
|
2800
|
Chris@45
|
2801 Model *model = getMainModel();
|
Chris@45
|
2802 if (!model) return;
|
Chris@45
|
2803
|
Chris@434
|
2804 sv_frame_t start = model->getStartFrame();
|
Chris@434
|
2805 sv_frame_t end = model->getEndFrame();
|
Chris@60
|
2806 if (m_playSource) end = std::max(end, m_playSource->getPlayEndFrame());
|
Chris@366
|
2807 int pixels = currentPane->width();
|
Chris@366
|
2808
|
Chris@366
|
2809 int sw = currentPane->getVerticalScaleWidth();
|
Chris@45
|
2810 if (pixels > sw * 2) pixels -= sw * 2;
|
Chris@45
|
2811 else pixels = 1;
|
Chris@45
|
2812 if (pixels > 4) pixels -= 4;
|
Chris@45
|
2813
|
Chris@624
|
2814 ZoomLevel zoomLevel = ZoomLevel::fromRatio(pixels, end - start);
|
Chris@45
|
2815 currentPane->setZoomLevel(zoomLevel);
|
Chris@45
|
2816 currentPane->setCentreFrame((start + end) / 2);
|
Chris@45
|
2817 }
|
Chris@45
|
2818
|
Chris@45
|
2819 void
|
Chris@45
|
2820 MainWindowBase::zoomDefault()
|
Chris@45
|
2821 {
|
Chris@45
|
2822 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@302
|
2823 QSettings settings;
|
Chris@302
|
2824 settings.beginGroup("MainWindow");
|
Chris@302
|
2825 int zoom = settings.value("zoom-default", 1024).toInt();
|
Chris@302
|
2826 settings.endGroup();
|
Chris@624
|
2827 if (currentPane) {
|
Chris@624
|
2828 currentPane->setZoomLevel(ZoomLevel(ZoomLevel::FramesPerPixel, zoom));
|
Chris@624
|
2829 }
|
Chris@45
|
2830 }
|
Chris@45
|
2831
|
Chris@45
|
2832 void
|
Chris@45
|
2833 MainWindowBase::scrollLeft()
|
Chris@45
|
2834 {
|
Chris@45
|
2835 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@45
|
2836 if (currentPane) currentPane->scroll(false, false);
|
Chris@45
|
2837 }
|
Chris@45
|
2838
|
Chris@45
|
2839 void
|
Chris@45
|
2840 MainWindowBase::jumpLeft()
|
Chris@45
|
2841 {
|
Chris@45
|
2842 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@45
|
2843 if (currentPane) currentPane->scroll(false, true);
|
Chris@45
|
2844 }
|
Chris@45
|
2845
|
Chris@45
|
2846 void
|
Chris@162
|
2847 MainWindowBase::peekLeft()
|
Chris@162
|
2848 {
|
Chris@162
|
2849 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@162
|
2850 if (currentPane) currentPane->scroll(false, false, false);
|
Chris@162
|
2851 }
|
Chris@162
|
2852
|
Chris@162
|
2853 void
|
Chris@45
|
2854 MainWindowBase::scrollRight()
|
Chris@45
|
2855 {
|
Chris@45
|
2856 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@45
|
2857 if (currentPane) currentPane->scroll(true, false);
|
Chris@45
|
2858 }
|
Chris@45
|
2859
|
Chris@45
|
2860 void
|
Chris@45
|
2861 MainWindowBase::jumpRight()
|
Chris@45
|
2862 {
|
Chris@45
|
2863 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@45
|
2864 if (currentPane) currentPane->scroll(true, true);
|
Chris@45
|
2865 }
|
Chris@45
|
2866
|
Chris@45
|
2867 void
|
Chris@162
|
2868 MainWindowBase::peekRight()
|
Chris@162
|
2869 {
|
Chris@162
|
2870 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@162
|
2871 if (currentPane) currentPane->scroll(true, false, false);
|
Chris@162
|
2872 }
|
Chris@162
|
2873
|
Chris@162
|
2874 void
|
Chris@45
|
2875 MainWindowBase::showNoOverlays()
|
Chris@45
|
2876 {
|
Chris@45
|
2877 m_viewManager->setOverlayMode(ViewManager::NoOverlays);
|
Chris@45
|
2878 }
|
Chris@45
|
2879
|
Chris@45
|
2880 void
|
Chris@45
|
2881 MainWindowBase::showMinimalOverlays()
|
Chris@45
|
2882 {
|
Chris@335
|
2883 m_viewManager->setOverlayMode(ViewManager::StandardOverlays);
|
Chris@45
|
2884 }
|
Chris@45
|
2885
|
Chris@45
|
2886 void
|
Chris@45
|
2887 MainWindowBase::showAllOverlays()
|
Chris@45
|
2888 {
|
Chris@45
|
2889 m_viewManager->setOverlayMode(ViewManager::AllOverlays);
|
Chris@45
|
2890 }
|
Chris@45
|
2891
|
Chris@45
|
2892 void
|
Chris@577
|
2893 MainWindowBase::findTimeRulerLayer()
|
Chris@577
|
2894 {
|
Chris@577
|
2895 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
|
Chris@577
|
2896 Pane *pane = m_paneStack->getPane(i);
|
Chris@577
|
2897 if (!pane) continue;
|
Chris@577
|
2898 for (int j = 0; j < pane->getLayerCount(); ++j) {
|
Chris@577
|
2899 Layer *layer = pane->getLayer(j);
|
Chris@577
|
2900 if (!dynamic_cast<TimeRulerLayer *>(layer)) continue;
|
Chris@577
|
2901 m_timeRulerLayer = layer;
|
Chris@577
|
2902 return;
|
Chris@577
|
2903 }
|
Chris@577
|
2904 }
|
Chris@577
|
2905 if (m_timeRulerLayer) {
|
Chris@577
|
2906 SVCERR << "WARNING: Time ruler layer was not reset to 0 before session template loaded?" << endl;
|
Chris@577
|
2907 delete m_timeRulerLayer;
|
Chris@636
|
2908 m_timeRulerLayer = nullptr;
|
Chris@577
|
2909 }
|
Chris@577
|
2910 }
|
Chris@577
|
2911
|
Chris@577
|
2912 void
|
Chris@211
|
2913 MainWindowBase::toggleTimeRulers()
|
Chris@211
|
2914 {
|
Chris@211
|
2915 bool haveRulers = false;
|
Chris@211
|
2916 bool someHidden = false;
|
Chris@211
|
2917
|
Chris@211
|
2918 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
|
Chris@211
|
2919
|
Chris@211
|
2920 Pane *pane = m_paneStack->getPane(i);
|
Chris@211
|
2921 if (!pane) continue;
|
Chris@211
|
2922
|
Chris@211
|
2923 for (int j = 0; j < pane->getLayerCount(); ++j) {
|
Chris@211
|
2924
|
Chris@211
|
2925 Layer *layer = pane->getLayer(j);
|
Chris@211
|
2926 if (!dynamic_cast<TimeRulerLayer *>(layer)) continue;
|
Chris@211
|
2927
|
Chris@211
|
2928 haveRulers = true;
|
Chris@211
|
2929 if (layer->isLayerDormant(pane)) someHidden = true;
|
Chris@211
|
2930 }
|
Chris@211
|
2931 }
|
Chris@211
|
2932
|
Chris@211
|
2933 if (haveRulers) {
|
Chris@211
|
2934
|
Chris@211
|
2935 bool show = someHidden;
|
Chris@211
|
2936
|
Chris@211
|
2937 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
|
Chris@211
|
2938
|
Chris@211
|
2939 Pane *pane = m_paneStack->getPane(i);
|
Chris@211
|
2940 if (!pane) continue;
|
Chris@211
|
2941
|
Chris@211
|
2942 for (int j = 0; j < pane->getLayerCount(); ++j) {
|
Chris@211
|
2943
|
Chris@211
|
2944 Layer *layer = pane->getLayer(j);
|
Chris@211
|
2945 if (!dynamic_cast<TimeRulerLayer *>(layer)) continue;
|
Chris@211
|
2946
|
Chris@211
|
2947 layer->showLayer(pane, show);
|
Chris@211
|
2948 }
|
Chris@211
|
2949 }
|
Chris@211
|
2950 }
|
Chris@211
|
2951 }
|
Chris@211
|
2952
|
Chris@211
|
2953 void
|
Chris@45
|
2954 MainWindowBase::toggleZoomWheels()
|
Chris@45
|
2955 {
|
Chris@45
|
2956 if (m_viewManager->getZoomWheelsEnabled()) {
|
Chris@45
|
2957 m_viewManager->setZoomWheelsEnabled(false);
|
Chris@45
|
2958 } else {
|
Chris@45
|
2959 m_viewManager->setZoomWheelsEnabled(true);
|
Chris@45
|
2960 }
|
Chris@45
|
2961 }
|
Chris@45
|
2962
|
Chris@45
|
2963 void
|
Chris@45
|
2964 MainWindowBase::togglePropertyBoxes()
|
Chris@45
|
2965 {
|
Chris@45
|
2966 if (m_paneStack->getLayoutStyle() == PaneStack::NoPropertyStacks) {
|
Chris@45
|
2967 if (Preferences::getInstance()->getPropertyBoxLayout() ==
|
Chris@45
|
2968 Preferences::VerticallyStacked) {
|
Chris@45
|
2969 m_paneStack->setLayoutStyle(PaneStack::PropertyStackPerPaneLayout);
|
Chris@45
|
2970 } else {
|
Chris@45
|
2971 m_paneStack->setLayoutStyle(PaneStack::SinglePropertyStackLayout);
|
Chris@45
|
2972 }
|
Chris@45
|
2973 } else {
|
Chris@45
|
2974 m_paneStack->setLayoutStyle(PaneStack::NoPropertyStacks);
|
Chris@45
|
2975 }
|
Chris@45
|
2976 }
|
Chris@45
|
2977
|
Chris@378
|
2978 QLabel *
|
Chris@378
|
2979 MainWindowBase::getStatusLabel() const
|
Chris@378
|
2980 {
|
Chris@378
|
2981 if (!m_statusLabel) {
|
Chris@378
|
2982 m_statusLabel = new QLabel();
|
Chris@378
|
2983 statusBar()->addWidget(m_statusLabel, 1);
|
Chris@378
|
2984 }
|
Chris@379
|
2985
|
Chris@379
|
2986 QList<QFrame *> frames = statusBar()->findChildren<QFrame *>();
|
Chris@379
|
2987 foreach (QFrame *f, frames) {
|
Chris@379
|
2988 f->setFrameStyle(QFrame::NoFrame);
|
Chris@379
|
2989 }
|
Chris@379
|
2990
|
Chris@378
|
2991 return m_statusLabel;
|
Chris@378
|
2992 }
|
Chris@378
|
2993
|
Chris@45
|
2994 void
|
Chris@45
|
2995 MainWindowBase::toggleStatusBar()
|
Chris@45
|
2996 {
|
Chris@45
|
2997 QSettings settings;
|
Chris@45
|
2998 settings.beginGroup("MainWindow");
|
Chris@45
|
2999 bool sb = settings.value("showstatusbar", true).toBool();
|
Chris@45
|
3000
|
Chris@45
|
3001 if (sb) {
|
Chris@45
|
3002 statusBar()->hide();
|
Chris@45
|
3003 } else {
|
Chris@45
|
3004 statusBar()->show();
|
Chris@45
|
3005 }
|
Chris@45
|
3006
|
Chris@45
|
3007 settings.setValue("showstatusbar", !sb);
|
Chris@45
|
3008
|
Chris@45
|
3009 settings.endGroup();
|
Chris@45
|
3010 }
|
Chris@45
|
3011
|
Chris@45
|
3012 void
|
Chris@256
|
3013 MainWindowBase::toggleCentreLine()
|
Chris@256
|
3014 {
|
Chris@256
|
3015 if (m_viewManager->shouldShowCentreLine()) {
|
Chris@256
|
3016 m_viewManager->setShowCentreLine(false);
|
Chris@256
|
3017 } else {
|
Chris@256
|
3018 m_viewManager->setShowCentreLine(true);
|
Chris@256
|
3019 }
|
Chris@256
|
3020 }
|
Chris@256
|
3021
|
Chris@256
|
3022 void
|
Chris@45
|
3023 MainWindowBase::preferenceChanged(PropertyContainer::PropertyName name)
|
Chris@45
|
3024 {
|
Chris@45
|
3025 if (name == "Property Box Layout") {
|
Chris@45
|
3026 if (m_paneStack->getLayoutStyle() != PaneStack::NoPropertyStacks) {
|
Chris@45
|
3027 if (Preferences::getInstance()->getPropertyBoxLayout() ==
|
Chris@45
|
3028 Preferences::VerticallyStacked) {
|
Chris@45
|
3029 m_paneStack->setLayoutStyle(PaneStack::PropertyStackPerPaneLayout);
|
Chris@45
|
3030 } else {
|
Chris@45
|
3031 m_paneStack->setLayoutStyle(PaneStack::SinglePropertyStackLayout);
|
Chris@45
|
3032 }
|
Chris@45
|
3033 }
|
Chris@45
|
3034 } else if (name == "Background Mode" && m_viewManager) {
|
Chris@45
|
3035 Preferences::BackgroundMode mode =
|
Chris@45
|
3036 Preferences::getInstance()->getBackgroundMode();
|
Chris@45
|
3037 if (mode == Preferences::BackgroundFromTheme) {
|
Chris@45
|
3038 m_viewManager->setGlobalDarkBackground(m_initialDarkBackground);
|
Chris@45
|
3039 } else if (mode == Preferences::DarkBackground) {
|
Chris@45
|
3040 m_viewManager->setGlobalDarkBackground(true);
|
Chris@45
|
3041 } else {
|
Chris@45
|
3042 m_viewManager->setGlobalDarkBackground(false);
|
Chris@45
|
3043 }
|
Chris@45
|
3044 }
|
Chris@45
|
3045 }
|
Chris@45
|
3046
|
Chris@45
|
3047 void
|
Chris@45
|
3048 MainWindowBase::play()
|
Chris@45
|
3049 {
|
Chris@516
|
3050 if ((m_recordTarget && m_recordTarget->isRecording()) ||
|
Chris@516
|
3051 (m_playSource && m_playSource->isPlaying())) {
|
Chris@45
|
3052 stop();
|
Chris@479
|
3053 QAction *action = qobject_cast<QAction *>(sender());
|
Chris@479
|
3054 if (action) action->setChecked(false);
|
Chris@45
|
3055 } else {
|
Chris@487
|
3056 if (m_audioIO) m_audioIO->resume();
|
Chris@509
|
3057 else if (m_playTarget) m_playTarget->resume();
|
Chris@45
|
3058 playbackFrameChanged(m_viewManager->getPlaybackFrame());
|
Chris@595
|
3059 m_playSource->play(m_viewManager->getPlaybackFrame());
|
Chris@45
|
3060 }
|
Chris@45
|
3061 }
|
Chris@45
|
3062
|
Chris@45
|
3063 void
|
Chris@477
|
3064 MainWindowBase::record()
|
Chris@477
|
3065 {
|
Chris@586
|
3066 QAction *action = qobject_cast<QAction *>(sender());
|
Chris@586
|
3067
|
Chris@478
|
3068 if (!(m_soundOptions & WithAudioInput)) {
|
Chris@586
|
3069 if (action) action->setChecked(false);
|
Chris@478
|
3070 return;
|
Chris@478
|
3071 }
|
Chris@478
|
3072
|
Chris@477
|
3073 if (!m_recordTarget) {
|
Chris@586
|
3074 if (action) action->setChecked(false);
|
Chris@477
|
3075 return;
|
Chris@477
|
3076 }
|
Chris@477
|
3077
|
Chris@478
|
3078 if (!m_audioIO) {
|
Chris@611
|
3079 SVDEBUG << "MainWindowBase::record: about to create audio IO" << endl;
|
Chris@478
|
3080 createAudioIO();
|
Chris@478
|
3081 }
|
Chris@492
|
3082
|
Chris@492
|
3083 if (!m_audioIO) {
|
Chris@586
|
3084 if (!m_playTarget) {
|
Chris@586
|
3085 // Don't need to report this, createAudioIO should have
|
Chris@586
|
3086 if (action) action->setChecked(false);
|
Chris@586
|
3087 return;
|
Chris@586
|
3088 } else {
|
Chris@586
|
3089 // Need to report this: if the play target exists instead
|
Chris@586
|
3090 // of the audio IO, then that means we failed to open a
|
Chris@586
|
3091 // capture device. The record control should be disabled
|
Chris@586
|
3092 // in that situation, so if it happens here, that must
|
Chris@586
|
3093 // mean this is the first time we ever tried to open the
|
Chris@586
|
3094 // audio device, hence the need to report the problem here
|
Chris@586
|
3095 QMessageBox::critical
|
Chris@586
|
3096 (this, tr("No record device available"),
|
Chris@586
|
3097 tr("<b>No record device available</b><p>Failed to find or open an audio device for recording. Only playback will be available.</p>"));
|
Chris@586
|
3098 if (action) action->setChecked(false);
|
Chris@586
|
3099 updateMenuStates();
|
Chris@586
|
3100 return;
|
Chris@586
|
3101 }
|
Chris@492
|
3102 }
|
Chris@478
|
3103
|
Chris@477
|
3104 if (m_recordTarget->isRecording()) {
|
Chris@492
|
3105 stop();
|
Chris@477
|
3106 return;
|
Chris@477
|
3107 }
|
Chris@490
|
3108
|
Chris@483
|
3109 if (m_audioRecordMode == RecordReplaceSession) {
|
Chris@490
|
3110 if (!checkSaveModified()) {
|
Chris@490
|
3111 if (action) action->setChecked(false);
|
Chris@490
|
3112 return;
|
Chris@490
|
3113 }
|
Chris@483
|
3114 }
|
Chris@487
|
3115
|
Chris@557
|
3116 if (m_viewManager) m_viewManager->setGlobalCentreFrame(0);
|
Chris@557
|
3117
|
Chris@611
|
3118 SVCERR << "MainWindowBase::record: about to resume" << endl;
|
Chris@492
|
3119 m_audioIO->resume();
|
Chris@509
|
3120
|
Chris@477
|
3121 WritableWaveFileModel *model = m_recordTarget->startRecording();
|
Chris@477
|
3122 if (!model) {
|
Chris@586
|
3123 SVCERR << "ERROR: MainWindowBase::record: Recording failed" << endl;
|
Chris@586
|
3124 QMessageBox::critical
|
Chris@586
|
3125 (this, tr("Recording failed"),
|
Chris@586
|
3126 tr("<b>Recording failed</b><p>Failed to switch to record mode (some internal problem?)</p>"));
|
Chris@490
|
3127 if (action) action->setChecked(false);
|
Chris@477
|
3128 return;
|
Chris@477
|
3129 }
|
Chris@477
|
3130
|
Chris@477
|
3131 if (!model->isOK()) {
|
Chris@611
|
3132 SVCERR << "MainWindowBase::record: Model not OK, stopping and suspending" << endl;
|
Chris@477
|
3133 m_recordTarget->stopRecording();
|
Chris@492
|
3134 m_audioIO->suspend();
|
Chris@586
|
3135 if (action) action->setChecked(false);
|
Chris@477
|
3136 delete model;
|
Chris@477
|
3137 return;
|
Chris@477
|
3138 }
|
Chris@611
|
3139
|
Chris@611
|
3140 SVCERR << "MainWindowBase::record: Model is OK, continuing..." << endl;
|
Chris@487
|
3141
|
Chris@478
|
3142 PlayParameterRepository::getInstance()->addPlayable(model);
|
Chris@483
|
3143
|
Chris@483
|
3144 if (m_audioRecordMode == RecordReplaceSession || !getMainModel()) {
|
Chris@478
|
3145
|
Chris@479
|
3146 //!!! duplication with openAudio here
|
Chris@479
|
3147
|
Chris@479
|
3148 QString templateName = getDefaultSessionTemplate();
|
Chris@479
|
3149 bool loadedTemplate = false;
|
Chris@479
|
3150
|
Chris@479
|
3151 if (templateName != "") {
|
Chris@479
|
3152 FileOpenStatus tplStatus = openSessionTemplate(templateName);
|
Chris@479
|
3153 if (tplStatus == FileOpenCancelled) {
|
Chris@611
|
3154 SVCERR << "MainWindowBase::record: Session template open cancelled, stopping and suspending" << endl;
|
Chris@490
|
3155 m_recordTarget->stopRecording();
|
Chris@492
|
3156 m_audioIO->suspend();
|
Chris@490
|
3157 PlayParameterRepository::getInstance()->removePlayable(model);
|
Chris@479
|
3158 return;
|
Chris@479
|
3159 }
|
Chris@479
|
3160 if (tplStatus != FileOpenFailed) {
|
Chris@479
|
3161 loadedTemplate = true;
|
Chris@479
|
3162 }
|
Chris@479
|
3163 }
|
Chris@479
|
3164
|
Chris@479
|
3165 if (!loadedTemplate) {
|
Chris@479
|
3166 closeSession();
|
Chris@479
|
3167 createDocument();
|
Chris@479
|
3168 }
|
Chris@479
|
3169
|
Chris@479
|
3170 Model *prevMain = getMainModel();
|
Chris@479
|
3171 if (prevMain) {
|
Chris@479
|
3172 m_playSource->removeModel(prevMain);
|
Chris@479
|
3173 PlayParameterRepository::getInstance()->removePlayable(prevMain);
|
Chris@479
|
3174 }
|
Chris@479
|
3175
|
Chris@478
|
3176 m_document->setMainModel(model);
|
Chris@478
|
3177 setupMenus();
|
Chris@577
|
3178 findTimeRulerLayer();
|
Chris@478
|
3179
|
Chris@595
|
3180 if (loadedTemplate || (m_sessionFile == "")) {
|
Chris@479
|
3181 //!!! shouldn't be dealing directly with title from here -- call a method
|
Chris@595
|
3182 setWindowTitle(tr("%1: %2")
|
Chris@479
|
3183 .arg(QApplication::applicationName())
|
Chris@479
|
3184 .arg(model->getLocation()));
|
Chris@595
|
3185 CommandHistory::getInstance()->clear();
|
Chris@595
|
3186 CommandHistory::getInstance()->documentSaved();
|
Chris@595
|
3187 m_documentModified = false;
|
Chris@595
|
3188 } else {
|
Chris@595
|
3189 setWindowTitle(tr("%1: %2 [%3]")
|
Chris@479
|
3190 .arg(QApplication::applicationName())
|
Chris@595
|
3191 .arg(QFileInfo(m_sessionFile).fileName())
|
Chris@595
|
3192 .arg(model->getLocation()));
|
Chris@595
|
3193 if (m_documentModified) {
|
Chris@595
|
3194 m_documentModified = false;
|
Chris@595
|
3195 documentModified(); // so as to restore "(modified)" window title
|
Chris@595
|
3196 }
|
Chris@595
|
3197 }
|
Chris@479
|
3198
|
Chris@478
|
3199 } else {
|
Chris@478
|
3200
|
Chris@478
|
3201 CommandHistory::getInstance()->startCompoundOperation
|
Chris@478
|
3202 (tr("Import Recorded Audio"), true);
|
Chris@478
|
3203
|
Chris@478
|
3204 m_document->addImportedModel(model);
|
Chris@478
|
3205
|
Chris@478
|
3206 AddPaneCommand *command = new AddPaneCommand(this);
|
Chris@478
|
3207 CommandHistory::getInstance()->addCommand(command);
|
Chris@478
|
3208
|
Chris@478
|
3209 Pane *pane = command->getPane();
|
Chris@478
|
3210
|
Chris@478
|
3211 if (m_timeRulerLayer) {
|
Chris@478
|
3212 m_document->addLayerToView(pane, m_timeRulerLayer);
|
Chris@478
|
3213 }
|
Chris@478
|
3214
|
Chris@478
|
3215 Layer *newLayer = m_document->createImportedLayer(model);
|
Chris@478
|
3216
|
Chris@478
|
3217 if (newLayer) {
|
Chris@478
|
3218 m_document->addLayerToView(pane, newLayer);
|
Chris@478
|
3219 }
|
Chris@595
|
3220
|
Chris@478
|
3221 CommandHistory::getInstance()->endCompoundOperation();
|
Chris@477
|
3222 }
|
Chris@479
|
3223
|
Chris@479
|
3224 updateMenuStates();
|
Chris@479
|
3225 m_recentFiles.addFile(model->getLocation());
|
Chris@479
|
3226 currentPaneChanged(m_paneStack->getCurrentPane());
|
Chris@611
|
3227
|
Chris@496
|
3228 emit audioFileLoaded();
|
Chris@477
|
3229 }
|
Chris@477
|
3230
|
Chris@477
|
3231 void
|
Chris@45
|
3232 MainWindowBase::ffwd()
|
Chris@45
|
3233 {
|
Chris@45
|
3234 if (!getMainModel()) return;
|
Chris@45
|
3235
|
Chris@435
|
3236 sv_frame_t frame = m_viewManager->getPlaybackFrame();
|
Chris@45
|
3237 ++frame;
|
Chris@45
|
3238
|
Chris@85
|
3239 Pane *pane = m_paneStack->getCurrentPane();
|
Chris@45
|
3240 Layer *layer = getSnapLayer();
|
Chris@435
|
3241 sv_samplerate_t sr = getMainModel()->getSampleRate();
|
Chris@45
|
3242
|
Chris@45
|
3243 if (!layer) {
|
Chris@45
|
3244
|
Chris@45
|
3245 frame = RealTime::realTime2Frame
|
Chris@357
|
3246 (RealTime::frame2RealTime(frame, sr) + m_defaultFfwdRwdStep, sr);
|
Chris@435
|
3247 if (frame > getMainModel()->getEndFrame()) {
|
Chris@45
|
3248 frame = getMainModel()->getEndFrame();
|
Chris@45
|
3249 }
|
Chris@45
|
3250
|
Chris@45
|
3251 } else {
|
Chris@45
|
3252
|
Chris@366
|
3253 int resolution = 0;
|
Chris@166
|
3254 if (pane) frame = pane->alignFromReference(frame);
|
Chris@85
|
3255 if (layer->snapToFeatureFrame(m_paneStack->getCurrentPane(),
|
Chris@85
|
3256 frame, resolution, Layer::SnapRight)) {
|
Chris@85
|
3257 if (pane) frame = pane->alignToReference(frame);
|
Chris@85
|
3258 } else {
|
Chris@45
|
3259 frame = getMainModel()->getEndFrame();
|
Chris@45
|
3260 }
|
Chris@45
|
3261 }
|
Chris@45
|
3262
|
Chris@45
|
3263 if (frame < 0) frame = 0;
|
Chris@45
|
3264
|
Chris@45
|
3265 if (m_viewManager->getPlaySelectionMode()) {
|
Chris@435
|
3266 frame = m_viewManager->constrainFrameToSelection(frame);
|
Chris@45
|
3267 }
|
Chris@45
|
3268
|
Chris@45
|
3269 m_viewManager->setPlaybackFrame(frame);
|
Chris@166
|
3270
|
Chris@435
|
3271 if (frame == getMainModel()->getEndFrame() &&
|
Chris@166
|
3272 m_playSource &&
|
Chris@166
|
3273 m_playSource->isPlaying() &&
|
Chris@166
|
3274 !m_viewManager->getPlayLoopMode()) {
|
Chris@166
|
3275 stop();
|
Chris@166
|
3276 }
|
Chris@45
|
3277 }
|
Chris@45
|
3278
|
Chris@45
|
3279 void
|
Chris@45
|
3280 MainWindowBase::ffwdEnd()
|
Chris@45
|
3281 {
|
Chris@45
|
3282 if (!getMainModel()) return;
|
Chris@45
|
3283
|
Chris@139
|
3284 if (m_playSource &&
|
Chris@139
|
3285 m_playSource->isPlaying() &&
|
Chris@139
|
3286 !m_viewManager->getPlayLoopMode()) {
|
Chris@139
|
3287 stop();
|
Chris@139
|
3288 }
|
Chris@139
|
3289
|
Chris@435
|
3290 sv_frame_t frame = getMainModel()->getEndFrame();
|
Chris@45
|
3291
|
Chris@45
|
3292 if (m_viewManager->getPlaySelectionMode()) {
|
Chris@45
|
3293 frame = m_viewManager->constrainFrameToSelection(frame);
|
Chris@45
|
3294 }
|
Chris@45
|
3295
|
Chris@45
|
3296 m_viewManager->setPlaybackFrame(frame);
|
Chris@45
|
3297 }
|
Chris@45
|
3298
|
Chris@45
|
3299 void
|
Chris@166
|
3300 MainWindowBase::ffwdSimilar()
|
Chris@166
|
3301 {
|
Chris@166
|
3302 if (!getMainModel()) return;
|
Chris@166
|
3303
|
Chris@166
|
3304 Layer *layer = getSnapLayer();
|
Chris@166
|
3305 if (!layer) { ffwd(); return; }
|
Chris@166
|
3306
|
Chris@166
|
3307 Pane *pane = m_paneStack->getCurrentPane();
|
Chris@435
|
3308 sv_frame_t frame = m_viewManager->getPlaybackFrame();
|
Chris@166
|
3309
|
Chris@366
|
3310 int resolution = 0;
|
Chris@166
|
3311 if (pane) frame = pane->alignFromReference(frame);
|
Chris@166
|
3312 if (layer->snapToSimilarFeature(m_paneStack->getCurrentPane(),
|
Chris@166
|
3313 frame, resolution, Layer::SnapRight)) {
|
Chris@166
|
3314 if (pane) frame = pane->alignToReference(frame);
|
Chris@166
|
3315 } else {
|
Chris@166
|
3316 frame = getMainModel()->getEndFrame();
|
Chris@166
|
3317 }
|
Chris@166
|
3318
|
Chris@166
|
3319 if (frame < 0) frame = 0;
|
Chris@166
|
3320
|
Chris@166
|
3321 if (m_viewManager->getPlaySelectionMode()) {
|
Chris@435
|
3322 frame = m_viewManager->constrainFrameToSelection(frame);
|
Chris@166
|
3323 }
|
Chris@166
|
3324
|
Chris@166
|
3325 m_viewManager->setPlaybackFrame(frame);
|
Chris@166
|
3326
|
Chris@435
|
3327 if (frame == getMainModel()->getEndFrame() &&
|
Chris@166
|
3328 m_playSource &&
|
Chris@166
|
3329 m_playSource->isPlaying() &&
|
Chris@166
|
3330 !m_viewManager->getPlayLoopMode()) {
|
Chris@166
|
3331 stop();
|
Chris@166
|
3332 }
|
Chris@166
|
3333 }
|
Chris@166
|
3334
|
Chris@166
|
3335 void
|
Chris@45
|
3336 MainWindowBase::rewind()
|
Chris@45
|
3337 {
|
Chris@45
|
3338 if (!getMainModel()) return;
|
Chris@45
|
3339
|
Chris@435
|
3340 sv_frame_t frame = m_viewManager->getPlaybackFrame();
|
Chris@45
|
3341 if (frame > 0) --frame;
|
Chris@45
|
3342
|
Chris@85
|
3343 Pane *pane = m_paneStack->getCurrentPane();
|
Chris@45
|
3344 Layer *layer = getSnapLayer();
|
Chris@435
|
3345 sv_samplerate_t sr = getMainModel()->getSampleRate();
|
Chris@45
|
3346
|
Chris@45
|
3347 // when rewinding during playback, we want to allow a period
|
Chris@45
|
3348 // following a rewind target point at which the rewind will go to
|
Chris@45
|
3349 // the prior point instead of the immediately neighbouring one
|
Chris@45
|
3350 if (m_playSource && m_playSource->isPlaying()) {
|
Chris@45
|
3351 RealTime ct = RealTime::frame2RealTime(frame, sr);
|
Chris@357
|
3352 ct = ct - RealTime::fromSeconds(0.15);
|
Chris@45
|
3353 if (ct < RealTime::zeroTime) ct = RealTime::zeroTime;
|
Chris@45
|
3354 frame = RealTime::realTime2Frame(ct, sr);
|
Chris@45
|
3355 }
|
Chris@45
|
3356
|
Chris@45
|
3357 if (!layer) {
|
Chris@45
|
3358
|
Chris@45
|
3359 frame = RealTime::realTime2Frame
|
Chris@357
|
3360 (RealTime::frame2RealTime(frame, sr) - m_defaultFfwdRwdStep, sr);
|
Chris@435
|
3361 if (frame < getMainModel()->getStartFrame()) {
|
Chris@45
|
3362 frame = getMainModel()->getStartFrame();
|
Chris@45
|
3363 }
|
Chris@45
|
3364
|
Chris@45
|
3365 } else {
|
Chris@45
|
3366
|
Chris@366
|
3367 int resolution = 0;
|
Chris@166
|
3368 if (pane) frame = pane->alignFromReference(frame);
|
Chris@85
|
3369 if (layer->snapToFeatureFrame(m_paneStack->getCurrentPane(),
|
Chris@85
|
3370 frame, resolution, Layer::SnapLeft)) {
|
Chris@85
|
3371 if (pane) frame = pane->alignToReference(frame);
|
Chris@85
|
3372 } else {
|
Chris@45
|
3373 frame = getMainModel()->getStartFrame();
|
Chris@45
|
3374 }
|
Chris@45
|
3375 }
|
Chris@45
|
3376
|
Chris@45
|
3377 if (frame < 0) frame = 0;
|
Chris@45
|
3378
|
Chris@45
|
3379 if (m_viewManager->getPlaySelectionMode()) {
|
Chris@435
|
3380 frame = m_viewManager->constrainFrameToSelection(frame);
|
Chris@45
|
3381 }
|
Chris@45
|
3382
|
Chris@45
|
3383 m_viewManager->setPlaybackFrame(frame);
|
Chris@45
|
3384 }
|
Chris@45
|
3385
|
Chris@45
|
3386 void
|
Chris@45
|
3387 MainWindowBase::rewindStart()
|
Chris@45
|
3388 {
|
Chris@45
|
3389 if (!getMainModel()) return;
|
Chris@45
|
3390
|
Chris@435
|
3391 sv_frame_t frame = getMainModel()->getStartFrame();
|
Chris@45
|
3392
|
Chris@45
|
3393 if (m_viewManager->getPlaySelectionMode()) {
|
Chris@45
|
3394 frame = m_viewManager->constrainFrameToSelection(frame);
|
Chris@45
|
3395 }
|
Chris@45
|
3396
|
Chris@45
|
3397 m_viewManager->setPlaybackFrame(frame);
|
Chris@45
|
3398 }
|
Chris@45
|
3399
|
Chris@166
|
3400 void
|
Chris@166
|
3401 MainWindowBase::rewindSimilar()
|
Chris@166
|
3402 {
|
Chris@166
|
3403 if (!getMainModel()) return;
|
Chris@166
|
3404
|
Chris@166
|
3405 Layer *layer = getSnapLayer();
|
Chris@166
|
3406 if (!layer) { rewind(); return; }
|
Chris@166
|
3407
|
Chris@166
|
3408 Pane *pane = m_paneStack->getCurrentPane();
|
Chris@435
|
3409 sv_frame_t frame = m_viewManager->getPlaybackFrame();
|
Chris@166
|
3410
|
Chris@366
|
3411 int resolution = 0;
|
Chris@166
|
3412 if (pane) frame = pane->alignFromReference(frame);
|
Chris@166
|
3413 if (layer->snapToSimilarFeature(m_paneStack->getCurrentPane(),
|
Chris@166
|
3414 frame, resolution, Layer::SnapLeft)) {
|
Chris@166
|
3415 if (pane) frame = pane->alignToReference(frame);
|
Chris@166
|
3416 } else {
|
Chris@166
|
3417 frame = getMainModel()->getStartFrame();
|
Chris@166
|
3418 }
|
Chris@166
|
3419
|
Chris@166
|
3420 if (frame < 0) frame = 0;
|
Chris@166
|
3421
|
Chris@166
|
3422 if (m_viewManager->getPlaySelectionMode()) {
|
Chris@435
|
3423 frame = m_viewManager->constrainFrameToSelection(frame);
|
Chris@166
|
3424 }
|
Chris@166
|
3425
|
Chris@166
|
3426 m_viewManager->setPlaybackFrame(frame);
|
Chris@166
|
3427 }
|
Chris@166
|
3428
|
Chris@45
|
3429 Layer *
|
Chris@45
|
3430 MainWindowBase::getSnapLayer() const
|
Chris@45
|
3431 {
|
Chris@45
|
3432 Pane *pane = m_paneStack->getCurrentPane();
|
Chris@636
|
3433 if (!pane) return nullptr;
|
Chris@45
|
3434
|
Chris@45
|
3435 Layer *layer = pane->getSelectedLayer();
|
Chris@45
|
3436
|
Chris@45
|
3437 if (!dynamic_cast<TimeInstantLayer *>(layer) &&
|
Chris@45
|
3438 !dynamic_cast<TimeValueLayer *>(layer) &&
|
Chris@194
|
3439 !dynamic_cast<RegionLayer *>(layer) &&
|
Chris@45
|
3440 !dynamic_cast<TimeRulerLayer *>(layer)) {
|
Chris@45
|
3441
|
Chris@636
|
3442 layer = nullptr;
|
Chris@45
|
3443
|
Chris@45
|
3444 for (int i = pane->getLayerCount(); i > 0; --i) {
|
Chris@45
|
3445 Layer *l = pane->getLayer(i-1);
|
Chris@45
|
3446 if (dynamic_cast<TimeRulerLayer *>(l)) {
|
Chris@45
|
3447 layer = l;
|
Chris@45
|
3448 break;
|
Chris@45
|
3449 }
|
Chris@45
|
3450 }
|
Chris@45
|
3451 }
|
Chris@45
|
3452
|
Chris@45
|
3453 return layer;
|
Chris@45
|
3454 }
|
Chris@45
|
3455
|
Chris@45
|
3456 void
|
Chris@45
|
3457 MainWindowBase::stop()
|
Chris@45
|
3458 {
|
Chris@516
|
3459 if (m_recordTarget &&
|
Chris@516
|
3460 m_recordTarget->isRecording()) {
|
Chris@477
|
3461 m_recordTarget->stopRecording();
|
Chris@477
|
3462 }
|
Chris@516
|
3463
|
Chris@516
|
3464 if (!m_playSource) return;
|
Chris@516
|
3465
|
Chris@45
|
3466 m_playSource->stop();
|
Chris@45
|
3467
|
Chris@611
|
3468 SVCERR << "MainWindowBase::stop: suspending" << endl;
|
Chris@611
|
3469
|
Chris@487
|
3470 if (m_audioIO) m_audioIO->suspend();
|
Chris@509
|
3471 else if (m_playTarget) m_playTarget->suspend();
|
Chris@487
|
3472
|
Chris@45
|
3473 if (m_paneStack && m_paneStack->getCurrentPane()) {
|
Chris@45
|
3474 updateVisibleRangeDisplay(m_paneStack->getCurrentPane());
|
Chris@45
|
3475 } else {
|
Chris@45
|
3476 m_myStatusMessage = "";
|
Chris@378
|
3477 getStatusLabel()->setText("");
|
Chris@45
|
3478 }
|
Chris@45
|
3479 }
|
Chris@45
|
3480
|
Chris@45
|
3481 MainWindowBase::AddPaneCommand::AddPaneCommand(MainWindowBase *mw) :
|
Chris@45
|
3482 m_mw(mw),
|
Chris@636
|
3483 m_pane(nullptr),
|
Chris@636
|
3484 m_prevCurrentPane(nullptr),
|
Chris@45
|
3485 m_added(false)
|
Chris@45
|
3486 {
|
Chris@45
|
3487 }
|
Chris@45
|
3488
|
Chris@45
|
3489 MainWindowBase::AddPaneCommand::~AddPaneCommand()
|
Chris@45
|
3490 {
|
Chris@45
|
3491 if (m_pane && !m_added) {
|
Chris@595
|
3492 m_mw->m_paneStack->deletePane(m_pane);
|
Chris@45
|
3493 }
|
Chris@45
|
3494 }
|
Chris@45
|
3495
|
Chris@45
|
3496 QString
|
Chris@45
|
3497 MainWindowBase::AddPaneCommand::getName() const
|
Chris@45
|
3498 {
|
Chris@45
|
3499 return tr("Add Pane");
|
Chris@45
|
3500 }
|
Chris@45
|
3501
|
Chris@45
|
3502 void
|
Chris@45
|
3503 MainWindowBase::AddPaneCommand::execute()
|
Chris@45
|
3504 {
|
Chris@45
|
3505 if (!m_pane) {
|
Chris@595
|
3506 m_prevCurrentPane = m_mw->m_paneStack->getCurrentPane();
|
Chris@595
|
3507 m_pane = m_mw->m_paneStack->addPane();
|
Chris@45
|
3508
|
Chris@45
|
3509 connect(m_pane, SIGNAL(contextHelpChanged(const QString &)),
|
Chris@45
|
3510 m_mw, SLOT(contextHelpChanged(const QString &)));
|
Chris@45
|
3511 } else {
|
Chris@595
|
3512 m_mw->m_paneStack->showPane(m_pane);
|
Chris@45
|
3513 }
|
Chris@45
|
3514
|
Chris@45
|
3515 m_mw->m_paneStack->setCurrentPane(m_pane);
|
Chris@45
|
3516 m_added = true;
|
Chris@45
|
3517 }
|
Chris@45
|
3518
|
Chris@45
|
3519 void
|
Chris@45
|
3520 MainWindowBase::AddPaneCommand::unexecute()
|
Chris@45
|
3521 {
|
Chris@45
|
3522 m_mw->m_paneStack->hidePane(m_pane);
|
Chris@45
|
3523 m_mw->m_paneStack->setCurrentPane(m_prevCurrentPane);
|
Chris@45
|
3524 m_added = false;
|
Chris@45
|
3525 }
|
Chris@45
|
3526
|
Chris@45
|
3527 MainWindowBase::RemovePaneCommand::RemovePaneCommand(MainWindowBase *mw, Pane *pane) :
|
Chris@45
|
3528 m_mw(mw),
|
Chris@45
|
3529 m_pane(pane),
|
Chris@636
|
3530 m_prevCurrentPane(nullptr),
|
Chris@45
|
3531 m_added(true)
|
Chris@45
|
3532 {
|
Chris@45
|
3533 }
|
Chris@45
|
3534
|
Chris@45
|
3535 MainWindowBase::RemovePaneCommand::~RemovePaneCommand()
|
Chris@45
|
3536 {
|
Chris@45
|
3537 if (m_pane && !m_added) {
|
Chris@595
|
3538 m_mw->m_paneStack->deletePane(m_pane);
|
Chris@45
|
3539 }
|
Chris@45
|
3540 }
|
Chris@45
|
3541
|
Chris@45
|
3542 QString
|
Chris@45
|
3543 MainWindowBase::RemovePaneCommand::getName() const
|
Chris@45
|
3544 {
|
Chris@45
|
3545 return tr("Remove Pane");
|
Chris@45
|
3546 }
|
Chris@45
|
3547
|
Chris@45
|
3548 void
|
Chris@45
|
3549 MainWindowBase::RemovePaneCommand::execute()
|
Chris@45
|
3550 {
|
Chris@45
|
3551 m_prevCurrentPane = m_mw->m_paneStack->getCurrentPane();
|
Chris@45
|
3552 m_mw->m_paneStack->hidePane(m_pane);
|
Chris@45
|
3553 m_added = false;
|
Chris@45
|
3554 }
|
Chris@45
|
3555
|
Chris@45
|
3556 void
|
Chris@45
|
3557 MainWindowBase::RemovePaneCommand::unexecute()
|
Chris@45
|
3558 {
|
Chris@45
|
3559 m_mw->m_paneStack->showPane(m_pane);
|
Chris@45
|
3560 m_mw->m_paneStack->setCurrentPane(m_prevCurrentPane);
|
Chris@45
|
3561 m_added = true;
|
Chris@45
|
3562 }
|
Chris@45
|
3563
|
Chris@45
|
3564 void
|
Chris@45
|
3565 MainWindowBase::deleteCurrentPane()
|
Chris@45
|
3566 {
|
Chris@45
|
3567 CommandHistory::getInstance()->startCompoundOperation
|
Chris@595
|
3568 (tr("Delete Pane"), true);
|
Chris@45
|
3569
|
Chris@45
|
3570 Pane *pane = m_paneStack->getCurrentPane();
|
Chris@45
|
3571 if (pane) {
|
Chris@595
|
3572 while (pane->getLayerCount() > 0) {
|
Chris@595
|
3573 Layer *layer = pane->getLayer(0);
|
Chris@595
|
3574 if (layer) {
|
Chris@595
|
3575 m_document->removeLayerFromView(pane, layer);
|
Chris@595
|
3576 } else {
|
Chris@595
|
3577 break;
|
Chris@595
|
3578 }
|
Chris@595
|
3579 }
|
Chris@595
|
3580
|
Chris@595
|
3581 RemovePaneCommand *command = new RemovePaneCommand(this, pane);
|
Chris@595
|
3582 CommandHistory::getInstance()->addCommand(command);
|
Chris@45
|
3583 }
|
Chris@45
|
3584
|
Chris@45
|
3585 CommandHistory::getInstance()->endCompoundOperation();
|
Chris@45
|
3586
|
Chris@45
|
3587 updateMenuStates();
|
Chris@45
|
3588 }
|
Chris@45
|
3589
|
Chris@45
|
3590 void
|
Chris@45
|
3591 MainWindowBase::deleteCurrentLayer()
|
Chris@45
|
3592 {
|
Chris@45
|
3593 Pane *pane = m_paneStack->getCurrentPane();
|
Chris@45
|
3594 if (pane) {
|
Chris@595
|
3595 Layer *layer = pane->getSelectedLayer();
|
Chris@595
|
3596 if (layer) {
|
Chris@595
|
3597 m_document->removeLayerFromView(pane, layer);
|
Chris@595
|
3598 }
|
Chris@45
|
3599 }
|
Chris@45
|
3600 updateMenuStates();
|
Chris@45
|
3601 }
|
Chris@45
|
3602
|
Chris@45
|
3603 void
|
Chris@123
|
3604 MainWindowBase::editCurrentLayer()
|
Chris@123
|
3605 {
|
Chris@636
|
3606 Layer *layer = nullptr;
|
Chris@123
|
3607 Pane *pane = m_paneStack->getCurrentPane();
|
Chris@123
|
3608 if (pane) layer = pane->getSelectedLayer();
|
Chris@123
|
3609 if (!layer) return;
|
Chris@123
|
3610
|
Chris@123
|
3611 Model *model = layer->getModel();
|
Chris@123
|
3612 if (!model) return;
|
Chris@123
|
3613
|
Chris@124
|
3614 TabularModel *tabular = dynamic_cast<TabularModel *>(model);
|
Chris@124
|
3615 if (!tabular) {
|
Chris@124
|
3616 //!!! how to prevent this function from being active if not
|
Chris@124
|
3617 //appropriate model type? or will we ultimately support
|
Chris@124
|
3618 //tabular display for all editable models?
|
Chris@233
|
3619 SVDEBUG << "NOTE: Not a tabular model" << endl;
|
Chris@124
|
3620 return;
|
Chris@124
|
3621 }
|
Chris@124
|
3622
|
Chris@123
|
3623 if (m_layerDataDialogMap.find(layer) != m_layerDataDialogMap.end()) {
|
Chris@126
|
3624 if (!m_layerDataDialogMap[layer].isNull()) {
|
Chris@126
|
3625 m_layerDataDialogMap[layer]->show();
|
Chris@126
|
3626 m_layerDataDialogMap[layer]->raise();
|
Chris@126
|
3627 return;
|
Chris@126
|
3628 }
|
Chris@123
|
3629 }
|
Chris@123
|
3630
|
Chris@125
|
3631 QString title = layer->getLayerPresentationName();
|
Chris@125
|
3632
|
Chris@125
|
3633 ModelDataTableDialog *dialog = new ModelDataTableDialog(tabular, title, this);
|
Chris@128
|
3634 dialog->setAttribute(Qt::WA_DeleteOnClose);
|
Chris@128
|
3635
|
Chris@128
|
3636 connectLayerEditDialog(dialog);
|
Chris@123
|
3637
|
Chris@128
|
3638 m_layerDataDialogMap[layer] = dialog;
|
Chris@128
|
3639 m_viewDataDialogMap[pane].insert(dialog);
|
Chris@128
|
3640
|
Chris@128
|
3641 dialog->show();
|
Chris@128
|
3642 }
|
Chris@128
|
3643
|
Chris@128
|
3644 void
|
Chris@128
|
3645 MainWindowBase::connectLayerEditDialog(ModelDataTableDialog *dialog)
|
Chris@128
|
3646 {
|
Chris@123
|
3647 connect(m_viewManager,
|
Chris@435
|
3648 SIGNAL(globalCentreFrameChanged(sv_frame_t)),
|
Chris@123
|
3649 dialog,
|
Chris@435
|
3650 SLOT(userScrolledToFrame(sv_frame_t)));
|
Chris@127
|
3651
|
Chris@127
|
3652 connect(m_viewManager,
|
Chris@435
|
3653 SIGNAL(playbackFrameChanged(sv_frame_t)),
|
Chris@127
|
3654 dialog,
|
Chris@435
|
3655 SLOT(playbackScrolledToFrame(sv_frame_t)));
|
Chris@127
|
3656
|
Chris@123
|
3657 connect(dialog,
|
Chris@435
|
3658 SIGNAL(scrollToFrame(sv_frame_t)),
|
Chris@123
|
3659 m_viewManager,
|
Chris@435
|
3660 SLOT(setGlobalCentreFrame(sv_frame_t)));
|
Chris@129
|
3661
|
Chris@129
|
3662 connect(dialog,
|
Chris@435
|
3663 SIGNAL(scrollToFrame(sv_frame_t)),
|
Chris@129
|
3664 m_viewManager,
|
Chris@435
|
3665 SLOT(setPlaybackFrame(sv_frame_t)));
|
Chris@128
|
3666 }
|
Chris@123
|
3667
|
Chris@123
|
3668 void
|
Chris@73
|
3669 MainWindowBase::previousPane()
|
Chris@73
|
3670 {
|
Chris@73
|
3671 if (!m_paneStack) return;
|
Chris@73
|
3672
|
Chris@73
|
3673 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@73
|
3674 if (!currentPane) return;
|
Chris@73
|
3675
|
Chris@73
|
3676 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
|
Chris@73
|
3677 if (m_paneStack->getPane(i) == currentPane) {
|
Chris@73
|
3678 if (i == 0) return;
|
Chris@73
|
3679 m_paneStack->setCurrentPane(m_paneStack->getPane(i-1));
|
Chris@73
|
3680 updateMenuStates();
|
Chris@73
|
3681 return;
|
Chris@73
|
3682 }
|
Chris@73
|
3683 }
|
Chris@73
|
3684 }
|
Chris@73
|
3685
|
Chris@73
|
3686 void
|
Chris@73
|
3687 MainWindowBase::nextPane()
|
Chris@73
|
3688 {
|
Chris@73
|
3689 if (!m_paneStack) return;
|
Chris@73
|
3690
|
Chris@73
|
3691 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@73
|
3692 if (!currentPane) return;
|
Chris@73
|
3693
|
Chris@73
|
3694 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
|
Chris@73
|
3695 if (m_paneStack->getPane(i) == currentPane) {
|
Chris@73
|
3696 if (i == m_paneStack->getPaneCount()-1) return;
|
Chris@73
|
3697 m_paneStack->setCurrentPane(m_paneStack->getPane(i+1));
|
Chris@73
|
3698 updateMenuStates();
|
Chris@73
|
3699 return;
|
Chris@73
|
3700 }
|
Chris@73
|
3701 }
|
Chris@73
|
3702 }
|
Chris@73
|
3703
|
Chris@73
|
3704 void
|
Chris@73
|
3705 MainWindowBase::previousLayer()
|
Chris@73
|
3706 {
|
Chris@73
|
3707 if (!m_paneStack) return;
|
Chris@73
|
3708
|
Chris@73
|
3709 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@73
|
3710 if (!currentPane) return;
|
Chris@73
|
3711
|
Chris@403
|
3712 int count = currentPane->getLayerCount();
|
Chris@403
|
3713 if (count == 0) return;
|
Chris@403
|
3714
|
Chris@73
|
3715 Layer *currentLayer = currentPane->getSelectedLayer();
|
Chris@403
|
3716
|
Chris@403
|
3717 if (!currentLayer) {
|
Chris@403
|
3718 // The pane itself is current
|
Chris@403
|
3719 m_paneStack->setCurrentLayer
|
Chris@403
|
3720 (currentPane, currentPane->getFixedOrderLayer(count-1));
|
Chris@403
|
3721 } else {
|
Chris@403
|
3722 for (int i = 0; i < count; ++i) {
|
Chris@403
|
3723 if (currentPane->getFixedOrderLayer(i) == currentLayer) {
|
Chris@403
|
3724 if (i == 0) {
|
Chris@403
|
3725 m_paneStack->setCurrentLayer
|
Chris@636
|
3726 (currentPane, nullptr); // pane
|
Chris@403
|
3727 } else {
|
Chris@403
|
3728 m_paneStack->setCurrentLayer
|
Chris@403
|
3729 (currentPane, currentPane->getFixedOrderLayer(i-1));
|
Chris@403
|
3730 }
|
Chris@403
|
3731 break;
|
Chris@403
|
3732 }
|
Chris@73
|
3733 }
|
Chris@73
|
3734 }
|
Chris@403
|
3735
|
Chris@403
|
3736 updateMenuStates();
|
Chris@73
|
3737 }
|
Chris@73
|
3738
|
Chris@73
|
3739 void
|
Chris@73
|
3740 MainWindowBase::nextLayer()
|
Chris@73
|
3741 {
|
Chris@73
|
3742 if (!m_paneStack) return;
|
Chris@73
|
3743
|
Chris@73
|
3744 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@73
|
3745 if (!currentPane) return;
|
Chris@73
|
3746
|
Chris@403
|
3747 int count = currentPane->getLayerCount();
|
Chris@403
|
3748 if (count == 0) return;
|
Chris@403
|
3749
|
Chris@73
|
3750 Layer *currentLayer = currentPane->getSelectedLayer();
|
Chris@403
|
3751
|
Chris@403
|
3752 if (!currentLayer) {
|
Chris@403
|
3753 // The pane itself is current
|
Chris@403
|
3754 m_paneStack->setCurrentLayer
|
Chris@403
|
3755 (currentPane, currentPane->getFixedOrderLayer(0));
|
Chris@403
|
3756 } else {
|
Chris@403
|
3757 for (int i = 0; i < count; ++i) {
|
Chris@403
|
3758 if (currentPane->getFixedOrderLayer(i) == currentLayer) {
|
Chris@403
|
3759 if (i == currentPane->getLayerCount()-1) {
|
Chris@403
|
3760 m_paneStack->setCurrentLayer
|
Chris@636
|
3761 (currentPane, nullptr); // pane
|
Chris@403
|
3762 } else {
|
Chris@403
|
3763 m_paneStack->setCurrentLayer
|
Chris@403
|
3764 (currentPane, currentPane->getFixedOrderLayer(i+1));
|
Chris@403
|
3765 }
|
Chris@403
|
3766 break;
|
Chris@403
|
3767 }
|
Chris@73
|
3768 }
|
Chris@73
|
3769 }
|
Chris@403
|
3770
|
Chris@403
|
3771 updateMenuStates();
|
Chris@73
|
3772 }
|
Chris@73
|
3773
|
Chris@73
|
3774 void
|
Chris@435
|
3775 MainWindowBase::playbackFrameChanged(sv_frame_t frame)
|
Chris@45
|
3776 {
|
Chris@45
|
3777 if (!(m_playSource && m_playSource->isPlaying()) || !getMainModel()) return;
|
Chris@45
|
3778
|
Chris@187
|
3779 updatePositionStatusDisplays();
|
Chris@187
|
3780
|
Chris@45
|
3781 RealTime now = RealTime::frame2RealTime
|
Chris@45
|
3782 (frame, getMainModel()->getSampleRate());
|
Chris@45
|
3783
|
Chris@45
|
3784 if (now.sec == m_lastPlayStatusSec) return;
|
Chris@45
|
3785
|
Chris@45
|
3786 RealTime then = RealTime::frame2RealTime
|
Chris@45
|
3787 (m_playSource->getPlayEndFrame(), getMainModel()->getSampleRate());
|
Chris@45
|
3788
|
Chris@45
|
3789 QString nowStr;
|
Chris@45
|
3790 QString thenStr;
|
Chris@45
|
3791 QString remainingStr;
|
Chris@45
|
3792
|
Chris@45
|
3793 if (then.sec > 10) {
|
Chris@45
|
3794 nowStr = now.toSecText().c_str();
|
Chris@45
|
3795 thenStr = then.toSecText().c_str();
|
Chris@45
|
3796 remainingStr = (then - now).toSecText().c_str();
|
Chris@45
|
3797 m_lastPlayStatusSec = now.sec;
|
Chris@45
|
3798 } else {
|
Chris@45
|
3799 nowStr = now.toText(true).c_str();
|
Chris@45
|
3800 thenStr = then.toText(true).c_str();
|
Chris@45
|
3801 remainingStr = (then - now).toText(true).c_str();
|
Chris@45
|
3802 }
|
Chris@45
|
3803
|
Chris@45
|
3804 m_myStatusMessage = tr("Playing: %1 of %2 (%3 remaining)")
|
Chris@45
|
3805 .arg(nowStr).arg(thenStr).arg(remainingStr);
|
Chris@45
|
3806
|
Chris@378
|
3807 getStatusLabel()->setText(m_myStatusMessage);
|
Chris@45
|
3808 }
|
Chris@45
|
3809
|
Chris@45
|
3810 void
|
Chris@486
|
3811 MainWindowBase::recordDurationChanged(sv_frame_t frame, sv_samplerate_t rate)
|
Chris@486
|
3812 {
|
Chris@486
|
3813 RealTime duration = RealTime::frame2RealTime(frame, rate);
|
Chris@486
|
3814 QString durStr = duration.toSecText().c_str();
|
Chris@486
|
3815
|
Chris@486
|
3816 m_myStatusMessage = tr("Recording: %1").arg(durStr);
|
Chris@486
|
3817
|
Chris@486
|
3818 getStatusLabel()->setText(m_myStatusMessage);
|
Chris@486
|
3819 }
|
Chris@486
|
3820
|
Chris@486
|
3821 void
|
Chris@435
|
3822 MainWindowBase::globalCentreFrameChanged(sv_frame_t )
|
Chris@45
|
3823 {
|
Chris@45
|
3824 if ((m_playSource && m_playSource->isPlaying()) || !getMainModel()) return;
|
Chris@636
|
3825 Pane *p = nullptr;
|
Chris@45
|
3826 if (!m_paneStack || !(p = m_paneStack->getCurrentPane())) return;
|
Chris@45
|
3827 if (!p->getFollowGlobalPan()) return;
|
Chris@45
|
3828 updateVisibleRangeDisplay(p);
|
Chris@45
|
3829 }
|
Chris@45
|
3830
|
Chris@45
|
3831 void
|
Chris@435
|
3832 MainWindowBase::viewCentreFrameChanged(View *v, sv_frame_t frame)
|
Chris@45
|
3833 {
|
Chris@233
|
3834 // SVDEBUG << "MainWindowBase::viewCentreFrameChanged(" << v << "," << frame << ")" << endl;
|
Chris@123
|
3835
|
Chris@123
|
3836 if (m_viewDataDialogMap.find(v) != m_viewDataDialogMap.end()) {
|
Chris@123
|
3837 for (DataDialogSet::iterator i = m_viewDataDialogMap[v].begin();
|
Chris@123
|
3838 i != m_viewDataDialogMap[v].end(); ++i) {
|
Chris@127
|
3839 (*i)->userScrolledToFrame(frame);
|
Chris@123
|
3840 }
|
Chris@123
|
3841 }
|
Chris@45
|
3842 if ((m_playSource && m_playSource->isPlaying()) || !getMainModel()) return;
|
Chris@636
|
3843 Pane *p = nullptr;
|
Chris@45
|
3844 if (!m_paneStack || !(p = m_paneStack->getCurrentPane())) return;
|
Chris@45
|
3845 if (v == p) updateVisibleRangeDisplay(p);
|
Chris@45
|
3846 }
|
Chris@45
|
3847
|
Chris@45
|
3848 void
|
Chris@624
|
3849 MainWindowBase::viewZoomLevelChanged(View *v, ZoomLevel, bool )
|
Chris@45
|
3850 {
|
Chris@45
|
3851 if ((m_playSource && m_playSource->isPlaying()) || !getMainModel()) return;
|
Chris@636
|
3852 Pane *p = nullptr;
|
Chris@45
|
3853 if (!m_paneStack || !(p = m_paneStack->getCurrentPane())) return;
|
Chris@45
|
3854 if (v == p) updateVisibleRangeDisplay(p);
|
Chris@45
|
3855 }
|
Chris@45
|
3856
|
Chris@45
|
3857 void
|
Chris@45
|
3858 MainWindowBase::layerAdded(Layer *)
|
Chris@45
|
3859 {
|
Chris@233
|
3860 // SVDEBUG << "MainWindowBase::layerAdded(" << layer << ")" << endl;
|
Chris@45
|
3861 updateMenuStates();
|
Chris@45
|
3862 }
|
Chris@45
|
3863
|
Chris@45
|
3864 void
|
Chris@45
|
3865 MainWindowBase::layerRemoved(Layer *)
|
Chris@45
|
3866 {
|
Chris@233
|
3867 // SVDEBUG << "MainWindowBase::layerRemoved(" << layer << ")" << endl;
|
Chris@45
|
3868 updateMenuStates();
|
Chris@45
|
3869 }
|
Chris@45
|
3870
|
Chris@45
|
3871 void
|
Chris@45
|
3872 MainWindowBase::layerAboutToBeDeleted(Layer *layer)
|
Chris@45
|
3873 {
|
Chris@233
|
3874 // SVDEBUG << "MainWindowBase::layerAboutToBeDeleted(" << layer << ")" << endl;
|
Chris@123
|
3875
|
Chris@128
|
3876 removeLayerEditDialog(layer);
|
Chris@123
|
3877
|
Chris@47
|
3878 if (m_timeRulerLayer && (layer == m_timeRulerLayer)) {
|
Chris@595
|
3879 // cerr << "(this is the time ruler layer)" << endl;
|
Chris@636
|
3880 m_timeRulerLayer = nullptr;
|
Chris@45
|
3881 }
|
Chris@45
|
3882 }
|
Chris@45
|
3883
|
Chris@45
|
3884 void
|
Chris@45
|
3885 MainWindowBase::layerInAView(Layer *layer, bool inAView)
|
Chris@45
|
3886 {
|
Chris@233
|
3887 // SVDEBUG << "MainWindowBase::layerInAView(" << layer << "," << inAView << ")" << endl;
|
Chris@128
|
3888
|
Chris@128
|
3889 if (!inAView) removeLayerEditDialog(layer);
|
Chris@45
|
3890
|
Chris@45
|
3891 // Check whether we need to add or remove model from play source
|
Chris@45
|
3892 Model *model = layer->getModel();
|
Chris@45
|
3893 if (model) {
|
Chris@45
|
3894 if (inAView) {
|
Chris@45
|
3895 m_playSource->addModel(model);
|
Chris@45
|
3896 } else {
|
Chris@45
|
3897 bool found = false;
|
Chris@45
|
3898 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
|
Chris@45
|
3899 Pane *pane = m_paneStack->getPane(i);
|
Chris@45
|
3900 if (!pane) continue;
|
Chris@45
|
3901 for (int j = 0; j < pane->getLayerCount(); ++j) {
|
Chris@45
|
3902 Layer *pl = pane->getLayer(j);
|
Chris@183
|
3903 if (pl &&
|
Chris@183
|
3904 !dynamic_cast<TimeRulerLayer *>(pl) &&
|
Chris@183
|
3905 (pl->getModel() == model)) {
|
Chris@45
|
3906 found = true;
|
Chris@45
|
3907 break;
|
Chris@45
|
3908 }
|
Chris@45
|
3909 }
|
Chris@45
|
3910 if (found) break;
|
Chris@45
|
3911 }
|
Chris@173
|
3912 if (!found) {
|
Chris@173
|
3913 m_playSource->removeModel(model);
|
Chris@173
|
3914 }
|
Chris@45
|
3915 }
|
Chris@45
|
3916 }
|
Chris@45
|
3917
|
Chris@45
|
3918 updateMenuStates();
|
Chris@45
|
3919 }
|
Chris@45
|
3920
|
Chris@45
|
3921 void
|
Chris@128
|
3922 MainWindowBase::removeLayerEditDialog(Layer *layer)
|
Chris@128
|
3923 {
|
Chris@128
|
3924 if (m_layerDataDialogMap.find(layer) != m_layerDataDialogMap.end()) {
|
Chris@128
|
3925
|
Chris@128
|
3926 ModelDataTableDialog *dialog = m_layerDataDialogMap[layer];
|
Chris@128
|
3927
|
Chris@128
|
3928 for (ViewDataDialogMap::iterator vi = m_viewDataDialogMap.begin();
|
Chris@128
|
3929 vi != m_viewDataDialogMap.end(); ++vi) {
|
Chris@128
|
3930 vi->second.erase(dialog);
|
Chris@128
|
3931 }
|
Chris@128
|
3932
|
Chris@128
|
3933 m_layerDataDialogMap.erase(layer);
|
Chris@128
|
3934 delete dialog;
|
Chris@128
|
3935 }
|
Chris@128
|
3936 }
|
Chris@128
|
3937
|
Chris@128
|
3938 void
|
Chris@45
|
3939 MainWindowBase::modelAdded(Model *model)
|
Chris@45
|
3940 {
|
Chris@233
|
3941 // SVDEBUG << "MainWindowBase::modelAdded(" << model << ")" << endl;
|
Chris@595
|
3942 std::cerr << "\nAdding model " << model->getTypeName() << " to playsource " << std::endl;
|
Chris@45
|
3943 m_playSource->addModel(model);
|
Chris@45
|
3944 }
|
Chris@45
|
3945
|
Chris@45
|
3946 void
|
Chris@45
|
3947 MainWindowBase::mainModelChanged(WaveFileModel *model)
|
Chris@45
|
3948 {
|
Chris@233
|
3949 // SVDEBUG << "MainWindowBase::mainModelChanged(" << model << ")" << endl;
|
Chris@45
|
3950 updateDescriptionLabel();
|
Chris@45
|
3951 if (model) m_viewManager->setMainModelSampleRate(model->getSampleRate());
|
Chris@475
|
3952 if (model && !(m_playTarget || m_audioIO) &&
|
Chris@475
|
3953 (m_soundOptions & WithAudioOutput)) {
|
Chris@475
|
3954 createAudioIO();
|
Chris@360
|
3955 }
|
Chris@45
|
3956 }
|
Chris@45
|
3957
|
Chris@45
|
3958 void
|
Chris@45
|
3959 MainWindowBase::modelAboutToBeDeleted(Model *model)
|
Chris@45
|
3960 {
|
Chris@233
|
3961 // SVDEBUG << "MainWindowBase::modelAboutToBeDeleted(" << model << ")" << endl;
|
Chris@45
|
3962 if (model == m_viewManager->getPlaybackModel()) {
|
Chris@636
|
3963 m_viewManager->setPlaybackModel(nullptr);
|
Chris@45
|
3964 }
|
Chris@45
|
3965 m_playSource->removeModel(model);
|
Chris@45
|
3966 }
|
Chris@45
|
3967
|
Chris@45
|
3968 void
|
Chris@55
|
3969 MainWindowBase::paneDeleteButtonClicked(Pane *pane)
|
Chris@55
|
3970 {
|
Chris@55
|
3971 bool found = false;
|
Chris@55
|
3972 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
|
Chris@55
|
3973 if (m_paneStack->getPane(i) == pane) {
|
Chris@55
|
3974 found = true;
|
Chris@55
|
3975 break;
|
Chris@55
|
3976 }
|
Chris@55
|
3977 }
|
Chris@55
|
3978 if (!found) {
|
Chris@233
|
3979 SVDEBUG << "MainWindowBase::paneDeleteButtonClicked: Unknown pane "
|
Chris@229
|
3980 << pane << endl;
|
Chris@55
|
3981 return;
|
Chris@55
|
3982 }
|
Chris@55
|
3983
|
Chris@55
|
3984 CommandHistory::getInstance()->startCompoundOperation
|
Chris@595
|
3985 (tr("Delete Pane"), true);
|
Chris@55
|
3986
|
Chris@55
|
3987 while (pane->getLayerCount() > 0) {
|
Chris@55
|
3988 Layer *layer = pane->getLayer(0);
|
Chris@55
|
3989 if (layer) {
|
Chris@55
|
3990 m_document->removeLayerFromView(pane, layer);
|
Chris@55
|
3991 } else {
|
Chris@55
|
3992 break;
|
Chris@55
|
3993 }
|
Chris@55
|
3994 }
|
Chris@55
|
3995
|
Chris@55
|
3996 RemovePaneCommand *command = new RemovePaneCommand(this, pane);
|
Chris@55
|
3997 CommandHistory::getInstance()->addCommand(command);
|
Chris@55
|
3998
|
Chris@55
|
3999 CommandHistory::getInstance()->endCompoundOperation();
|
Chris@55
|
4000
|
Chris@55
|
4001 updateMenuStates();
|
Chris@55
|
4002 }
|
Chris@55
|
4003
|
Chris@55
|
4004 void
|
Chris@429
|
4005 MainWindowBase::alignmentComplete(AlignmentModel *model)
|
Chris@429
|
4006 {
|
Chris@429
|
4007 cerr << "MainWindowBase::alignmentComplete(" << model << ")" << endl;
|
Chris@429
|
4008 }
|
Chris@429
|
4009
|
Chris@429
|
4010 void
|
Chris@45
|
4011 MainWindowBase::pollOSC()
|
Chris@45
|
4012 {
|
Chris@45
|
4013 if (!m_oscQueue || m_oscQueue->isEmpty()) return;
|
Chris@233
|
4014 SVDEBUG << "MainWindowBase::pollOSC: have " << m_oscQueue->getMessagesAvailable() << " messages" << endl;
|
Chris@45
|
4015
|
Chris@45
|
4016 if (m_openingAudioFile) return;
|
Chris@45
|
4017
|
Chris@45
|
4018 OSCMessage message = m_oscQueue->readMessage();
|
Chris@45
|
4019
|
Chris@45
|
4020 if (message.getTarget() != 0) {
|
Chris@45
|
4021 return; //!!! for now -- this class is target 0, others not handled yet
|
Chris@45
|
4022 }
|
Chris@45
|
4023
|
Chris@45
|
4024 handleOSCMessage(message);
|
Chris@45
|
4025 }
|
Chris@45
|
4026
|
Chris@45
|
4027 void
|
Chris@45
|
4028 MainWindowBase::inProgressSelectionChanged()
|
Chris@45
|
4029 {
|
Chris@636
|
4030 Pane *currentPane = nullptr;
|
Chris@45
|
4031 if (m_paneStack) currentPane = m_paneStack->getCurrentPane();
|
justin@331
|
4032 if (currentPane) {
|
justin@331
|
4033 //cerr << "JTEST: mouse event on selection pane" << endl;
|
justin@331
|
4034 updateVisibleRangeDisplay(currentPane);
|
justin@331
|
4035 }
|
Chris@45
|
4036 }
|
Chris@45
|
4037
|
Chris@45
|
4038 void
|
Chris@45
|
4039 MainWindowBase::contextHelpChanged(const QString &s)
|
Chris@45
|
4040 {
|
Chris@378
|
4041 QLabel *lab = getStatusLabel();
|
Chris@375
|
4042
|
Chris@45
|
4043 if (s == "" && m_myStatusMessage != "") {
|
Chris@378
|
4044 if (lab->text() != m_myStatusMessage) {
|
Chris@378
|
4045 lab->setText(m_myStatusMessage);
|
Chris@375
|
4046 }
|
Chris@45
|
4047 return;
|
Chris@45
|
4048 }
|
Chris@375
|
4049
|
Chris@378
|
4050 lab->setText(s);
|
Chris@45
|
4051 }
|
Chris@45
|
4052
|
Chris@45
|
4053 void
|
Chris@45
|
4054 MainWindowBase::openHelpUrl(QString url)
|
Chris@45
|
4055 {
|
Chris@45
|
4056 // This method mostly lifted from Qt Assistant source code
|
Chris@45
|
4057
|
Chris@45
|
4058 QProcess *process = new QProcess(this);
|
Chris@45
|
4059 connect(process, SIGNAL(finished(int)), process, SLOT(deleteLater()));
|
Chris@45
|
4060
|
Chris@45
|
4061 QStringList args;
|
Chris@45
|
4062
|
Chris@45
|
4063 #ifdef Q_OS_MAC
|
Chris@45
|
4064 args.append(url);
|
Chris@45
|
4065 process->start("open", args);
|
Chris@45
|
4066 #else
|
Chris@45
|
4067 #ifdef Q_OS_WIN32
|
Chris@599
|
4068 std::string pfiles;
|
Chris@599
|
4069 (void)getEnvUtf8("ProgramFiles", pfiles);
|
Chris@599
|
4070 QString command =
|
Chris@599
|
4071 QString::fromStdString(pfiles) +
|
Chris@599
|
4072 QString("\\Internet Explorer\\IEXPLORE.EXE");
|
Chris@358
|
4073
|
Chris@358
|
4074 args.append(url);
|
Chris@358
|
4075 process->start(command, args);
|
Chris@45
|
4076 #else
|
Chris@45
|
4077 if (!qgetenv("KDE_FULL_SESSION").isEmpty()) {
|
Chris@45
|
4078 args.append("exec");
|
Chris@45
|
4079 args.append(url);
|
Chris@45
|
4080 process->start("kfmclient", args);
|
Chris@45
|
4081 } else if (!qgetenv("BROWSER").isEmpty()) {
|
Chris@45
|
4082 args.append(url);
|
Chris@45
|
4083 process->start(qgetenv("BROWSER"), args);
|
Chris@45
|
4084 } else {
|
Chris@45
|
4085 args.append(url);
|
Chris@45
|
4086 process->start("firefox", args);
|
Chris@45
|
4087 }
|
Chris@45
|
4088 #endif
|
Chris@45
|
4089 #endif
|
Chris@45
|
4090 }
|
Chris@45
|
4091
|
Chris@483
|
4092 void
|
Chris@483
|
4093 MainWindowBase::openLocalFolder(QString path)
|
Chris@483
|
4094 {
|
Chris@483
|
4095 QDir d(path);
|
Chris@483
|
4096 if (d.exists()) {
|
Chris@483
|
4097 QStringList args;
|
Chris@483
|
4098 QString path = d.canonicalPath();
|
Chris@483
|
4099 #if defined Q_OS_WIN32
|
Chris@483
|
4100 // Although the Win32 API is quite happy to have
|
Chris@483
|
4101 // forward slashes as directory separators, Windows
|
Chris@483
|
4102 // Explorer is not
|
Chris@483
|
4103 path = path.replace('/', '\\');
|
Chris@483
|
4104 args << path;
|
Chris@483
|
4105 QProcess::execute("c:/windows/explorer.exe", args);
|
Chris@483
|
4106 #else
|
Chris@483
|
4107 args << path;
|
Chris@605
|
4108 QProcess process;
|
Chris@605
|
4109 QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
|
Chris@606
|
4110 env.insert("LD_LIBRARY_PATH", "");
|
Chris@605
|
4111 process.setProcessEnvironment(env);
|
Chris@605
|
4112 process.start(
|
Chris@483
|
4113 #if defined Q_OS_MAC
|
Chris@483
|
4114 "/usr/bin/open",
|
Chris@483
|
4115 #else
|
Chris@483
|
4116 "/usr/bin/xdg-open",
|
Chris@483
|
4117 #endif
|
Chris@483
|
4118 args);
|
Chris@608
|
4119 process.waitForFinished();
|
Chris@483
|
4120 #endif
|
Chris@483
|
4121 }
|
Chris@483
|
4122 }
|
Chris@483
|
4123
|