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