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