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