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