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