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