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