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