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