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