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