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