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@659
|
2806 bool
|
Chris@721
|
2807 MainWindowBase::exportLayerTo(Layer *layer, View *fromView,
|
Chris@724
|
2808 MultiSelection *selectionsToWrite,
|
Chris@721
|
2809 QString path, QString &error)
|
Chris@659
|
2810 {
|
Chris@724
|
2811 //!!! should we pull out the whole export logic into another
|
Chris@724
|
2812 // class? then we can more reasonably query it for things like
|
Chris@724
|
2813 // "can we export this layer type to this file format? can we
|
Chris@724
|
2814 // export selections, or only the whole layer?"
|
Chris@724
|
2815
|
Chris@659
|
2816 if (QFileInfo(path).suffix() == "") path += ".svl";
|
Chris@659
|
2817
|
Chris@659
|
2818 QString suffix = QFileInfo(path).suffix().toLower();
|
Chris@659
|
2819
|
Chris@721
|
2820 auto model = ModelById::get(layer->getExportModel(fromView));
|
Chris@684
|
2821 if (!model) {
|
Chris@684
|
2822 error = tr("Internal error: unknown model");
|
Chris@684
|
2823 return false;
|
Chris@684
|
2824 }
|
Chris@659
|
2825
|
Chris@659
|
2826 if (suffix == "xml" || suffix == "svl") {
|
Chris@659
|
2827
|
Chris@659
|
2828 QFile file(path);
|
Chris@659
|
2829 if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
Chris@659
|
2830 error = tr("Failed to open file %1 for writing").arg(path);
|
Chris@659
|
2831 } else {
|
Chris@659
|
2832 QTextStream out(&file);
|
Chris@659
|
2833 out.setCodec(QTextCodec::codecForName("UTF-8"));
|
Chris@659
|
2834 out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
|
Chris@659
|
2835 << "<!DOCTYPE sonic-visualiser>\n"
|
Chris@659
|
2836 << "<sv>\n"
|
Chris@659
|
2837 << " <data>\n";
|
Chris@659
|
2838
|
Chris@659
|
2839 model->toXml(out, " ");
|
Chris@659
|
2840
|
Chris@659
|
2841 out << " </data>\n"
|
Chris@659
|
2842 << " <display>\n";
|
Chris@659
|
2843
|
Chris@659
|
2844 layer->toXml(out, " ");
|
Chris@659
|
2845
|
Chris@659
|
2846 out << " </display>\n"
|
Chris@659
|
2847 << "</sv>\n";
|
Chris@659
|
2848 }
|
Chris@659
|
2849
|
Chris@659
|
2850 } else if (suffix == "mid" || suffix == "midi") {
|
Chris@659
|
2851
|
Chris@684
|
2852 auto nm = ModelById::getAs<NoteModel>(layer->getModel());
|
Chris@659
|
2853
|
Chris@659
|
2854 if (!nm) {
|
Chris@659
|
2855 error = tr("Can't export non-note layers to MIDI");
|
Chris@724
|
2856 } else if (!selectionsToWrite) {
|
Chris@684
|
2857 MIDIFileWriter writer(path, nm.get(), nm->getSampleRate());
|
Chris@659
|
2858 writer.write();
|
Chris@659
|
2859 if (!writer.isOK()) {
|
Chris@659
|
2860 error = writer.getError();
|
Chris@659
|
2861 }
|
Chris@724
|
2862 } else {
|
Chris@724
|
2863 NoteModel temporary(nm->getSampleRate(),
|
Chris@724
|
2864 nm->getResolution(),
|
Chris@724
|
2865 nm->getValueMinimum(),
|
Chris@724
|
2866 nm->getValueMaximum(),
|
Chris@724
|
2867 false);
|
Chris@724
|
2868 temporary.setScaleUnits(nm->getScaleUnits());
|
Chris@724
|
2869 for (const auto &s: selectionsToWrite->getSelections()) {
|
Chris@724
|
2870 EventVector ev(nm->getEventsStartingWithin
|
Chris@724
|
2871 (s.getStartFrame(), s.getDuration()));
|
Chris@724
|
2872 for (const auto &e: ev) {
|
Chris@724
|
2873 temporary.add(e);
|
Chris@724
|
2874 }
|
Chris@724
|
2875 }
|
Chris@724
|
2876 MIDIFileWriter writer(path, &temporary, temporary.getSampleRate());
|
Chris@724
|
2877 writer.write();
|
Chris@724
|
2878 if (!writer.isOK()) {
|
Chris@724
|
2879 error = writer.getError();
|
Chris@724
|
2880 }
|
Chris@659
|
2881 }
|
Chris@659
|
2882
|
Chris@659
|
2883 } else if (suffix == "ttl" || suffix == "n3") {
|
Chris@659
|
2884
|
Chris@684
|
2885 if (!RDFExporter::canExportModel(model.get())) {
|
Chris@659
|
2886 error = tr("Sorry, cannot export this layer type to RDF (supported types are: region, note, text, time instants, time values)");
|
Chris@659
|
2887 } else {
|
Chris@684
|
2888 RDFExporter exporter(path, model.get());
|
Chris@659
|
2889 exporter.write();
|
Chris@659
|
2890 if (!exporter.isOK()) {
|
Chris@659
|
2891 error = exporter.getError();
|
Chris@659
|
2892 }
|
Chris@659
|
2893 }
|
Chris@659
|
2894
|
Chris@659
|
2895 } else {
|
Chris@659
|
2896
|
Chris@722
|
2897 ProgressDialog dialog {
|
Chris@722
|
2898 QObject::tr("Exporting layer..."), true, 500, this,
|
Chris@722
|
2899 Qt::ApplicationModal
|
Chris@722
|
2900 };
|
Chris@722
|
2901
|
Chris@722
|
2902 CSVFileWriter writer(path, model.get(), &dialog,
|
Chris@659
|
2903 ((suffix == "csv") ? "," : "\t"));
|
Chris@724
|
2904
|
Chris@724
|
2905 if (selectionsToWrite) {
|
Chris@724
|
2906 writer.writeSelection(*selectionsToWrite);
|
Chris@724
|
2907 } else {
|
Chris@724
|
2908 writer.write();
|
Chris@724
|
2909 }
|
Chris@659
|
2910
|
Chris@659
|
2911 if (!writer.isOK()) {
|
Chris@659
|
2912 error = writer.getError();
|
Chris@722
|
2913 if (error == "") {
|
Chris@722
|
2914 error = tr("Failed to export layer for an unknown reason");
|
Chris@722
|
2915 }
|
Chris@659
|
2916 }
|
Chris@659
|
2917 }
|
Chris@659
|
2918
|
Chris@659
|
2919 return (error == "");
|
Chris@659
|
2920 }
|
Chris@659
|
2921
|
Chris@45
|
2922 void
|
Chris@226
|
2923 MainWindowBase::toXml(QTextStream &out, bool asTemplate)
|
Chris@45
|
2924 {
|
Chris@45
|
2925 QString indent(" ");
|
Chris@45
|
2926
|
Chris@45
|
2927 out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
|
Chris@45
|
2928 out << "<!DOCTYPE sonic-visualiser>\n";
|
Chris@45
|
2929 out << "<sv>\n";
|
Chris@45
|
2930
|
Chris@226
|
2931 if (asTemplate) {
|
Chris@226
|
2932 m_document->toXmlAsTemplate(out, "", "");
|
Chris@226
|
2933 } else {
|
Chris@226
|
2934 m_document->toXml(out, "", "");
|
Chris@226
|
2935 }
|
Chris@45
|
2936
|
Chris@45
|
2937 out << "<display>\n";
|
Chris@45
|
2938
|
Chris@45
|
2939 out << QString(" <window width=\"%1\" height=\"%2\"/>\n")
|
Chris@595
|
2940 .arg(width()).arg(height());
|
Chris@45
|
2941
|
Chris@45
|
2942 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
|
Chris@45
|
2943
|
Chris@595
|
2944 Pane *pane = m_paneStack->getPane(i);
|
Chris@595
|
2945
|
Chris@595
|
2946 if (pane) {
|
Chris@45
|
2947 pane->toXml(out, indent);
|
Chris@595
|
2948 }
|
Chris@45
|
2949 }
|
Chris@45
|
2950
|
Chris@45
|
2951 out << "</display>\n";
|
Chris@45
|
2952
|
Chris@45
|
2953 m_viewManager->getSelection().toXml(out);
|
Chris@45
|
2954
|
Chris@45
|
2955 out << "</sv>\n";
|
Chris@45
|
2956 }
|
Chris@45
|
2957
|
Chris@45
|
2958 Pane *
|
Chris@45
|
2959 MainWindowBase::addPaneToStack()
|
Chris@45
|
2960 {
|
Chris@342
|
2961 cerr << "MainWindowBase::addPaneToStack()" << endl;
|
Chris@45
|
2962 AddPaneCommand *command = new AddPaneCommand(this);
|
Chris@45
|
2963 CommandHistory::getInstance()->addCommand(command);
|
Chris@57
|
2964 Pane *pane = command->getPane();
|
Chris@57
|
2965 return pane;
|
Chris@45
|
2966 }
|
Chris@45
|
2967
|
Chris@45
|
2968 void
|
Chris@45
|
2969 MainWindowBase::zoomIn()
|
Chris@45
|
2970 {
|
Chris@45
|
2971 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@45
|
2972 if (currentPane) currentPane->zoom(true);
|
Chris@45
|
2973 }
|
Chris@45
|
2974
|
Chris@45
|
2975 void
|
Chris@45
|
2976 MainWindowBase::zoomOut()
|
Chris@45
|
2977 {
|
Chris@45
|
2978 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@45
|
2979 if (currentPane) currentPane->zoom(false);
|
Chris@45
|
2980 }
|
Chris@45
|
2981
|
Chris@45
|
2982 void
|
Chris@45
|
2983 MainWindowBase::zoomToFit()
|
Chris@45
|
2984 {
|
Chris@45
|
2985 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@45
|
2986 if (!currentPane) return;
|
Chris@45
|
2987
|
Chris@684
|
2988 auto model = getMainModel();
|
Chris@45
|
2989 if (!model) return;
|
Chris@45
|
2990
|
Chris@434
|
2991 sv_frame_t start = model->getStartFrame();
|
Chris@434
|
2992 sv_frame_t end = model->getEndFrame();
|
Chris@60
|
2993 if (m_playSource) end = std::max(end, m_playSource->getPlayEndFrame());
|
Chris@366
|
2994 int pixels = currentPane->width();
|
Chris@366
|
2995
|
Chris@366
|
2996 int sw = currentPane->getVerticalScaleWidth();
|
Chris@45
|
2997 if (pixels > sw * 2) pixels -= sw * 2;
|
Chris@45
|
2998 else pixels = 1;
|
Chris@45
|
2999 if (pixels > 4) pixels -= 4;
|
Chris@45
|
3000
|
Chris@624
|
3001 ZoomLevel zoomLevel = ZoomLevel::fromRatio(pixels, end - start);
|
Chris@45
|
3002 currentPane->setZoomLevel(zoomLevel);
|
Chris@45
|
3003 currentPane->setCentreFrame((start + end) / 2);
|
Chris@45
|
3004 }
|
Chris@45
|
3005
|
Chris@45
|
3006 void
|
Chris@45
|
3007 MainWindowBase::zoomDefault()
|
Chris@45
|
3008 {
|
Chris@45
|
3009 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@302
|
3010 QSettings settings;
|
Chris@302
|
3011 settings.beginGroup("MainWindow");
|
Chris@302
|
3012 int zoom = settings.value("zoom-default", 1024).toInt();
|
Chris@302
|
3013 settings.endGroup();
|
Chris@624
|
3014 if (currentPane) {
|
Chris@624
|
3015 currentPane->setZoomLevel(ZoomLevel(ZoomLevel::FramesPerPixel, zoom));
|
Chris@624
|
3016 }
|
Chris@45
|
3017 }
|
Chris@45
|
3018
|
Chris@45
|
3019 void
|
Chris@45
|
3020 MainWindowBase::scrollLeft()
|
Chris@45
|
3021 {
|
Chris@45
|
3022 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@45
|
3023 if (currentPane) currentPane->scroll(false, false);
|
Chris@45
|
3024 }
|
Chris@45
|
3025
|
Chris@45
|
3026 void
|
Chris@45
|
3027 MainWindowBase::jumpLeft()
|
Chris@45
|
3028 {
|
Chris@45
|
3029 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@45
|
3030 if (currentPane) currentPane->scroll(false, true);
|
Chris@45
|
3031 }
|
Chris@45
|
3032
|
Chris@45
|
3033 void
|
Chris@162
|
3034 MainWindowBase::peekLeft()
|
Chris@162
|
3035 {
|
Chris@162
|
3036 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@162
|
3037 if (currentPane) currentPane->scroll(false, false, false);
|
Chris@162
|
3038 }
|
Chris@162
|
3039
|
Chris@162
|
3040 void
|
Chris@45
|
3041 MainWindowBase::scrollRight()
|
Chris@45
|
3042 {
|
Chris@45
|
3043 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@45
|
3044 if (currentPane) currentPane->scroll(true, false);
|
Chris@45
|
3045 }
|
Chris@45
|
3046
|
Chris@45
|
3047 void
|
Chris@45
|
3048 MainWindowBase::jumpRight()
|
Chris@45
|
3049 {
|
Chris@45
|
3050 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@45
|
3051 if (currentPane) currentPane->scroll(true, true);
|
Chris@45
|
3052 }
|
Chris@45
|
3053
|
Chris@45
|
3054 void
|
Chris@162
|
3055 MainWindowBase::peekRight()
|
Chris@162
|
3056 {
|
Chris@162
|
3057 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@162
|
3058 if (currentPane) currentPane->scroll(true, false, false);
|
Chris@162
|
3059 }
|
Chris@162
|
3060
|
Chris@162
|
3061 void
|
Chris@45
|
3062 MainWindowBase::showNoOverlays()
|
Chris@45
|
3063 {
|
Chris@45
|
3064 m_viewManager->setOverlayMode(ViewManager::NoOverlays);
|
Chris@45
|
3065 }
|
Chris@45
|
3066
|
Chris@45
|
3067 void
|
Chris@45
|
3068 MainWindowBase::showMinimalOverlays()
|
Chris@45
|
3069 {
|
Chris@335
|
3070 m_viewManager->setOverlayMode(ViewManager::StandardOverlays);
|
Chris@45
|
3071 }
|
Chris@45
|
3072
|
Chris@45
|
3073 void
|
Chris@45
|
3074 MainWindowBase::showAllOverlays()
|
Chris@45
|
3075 {
|
Chris@45
|
3076 m_viewManager->setOverlayMode(ViewManager::AllOverlays);
|
Chris@45
|
3077 }
|
Chris@45
|
3078
|
Chris@45
|
3079 void
|
Chris@577
|
3080 MainWindowBase::findTimeRulerLayer()
|
Chris@577
|
3081 {
|
Chris@577
|
3082 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
|
Chris@577
|
3083 Pane *pane = m_paneStack->getPane(i);
|
Chris@577
|
3084 if (!pane) continue;
|
Chris@577
|
3085 for (int j = 0; j < pane->getLayerCount(); ++j) {
|
Chris@577
|
3086 Layer *layer = pane->getLayer(j);
|
Chris@577
|
3087 if (!dynamic_cast<TimeRulerLayer *>(layer)) continue;
|
Chris@577
|
3088 m_timeRulerLayer = layer;
|
Chris@577
|
3089 return;
|
Chris@577
|
3090 }
|
Chris@577
|
3091 }
|
Chris@577
|
3092 if (m_timeRulerLayer) {
|
Chris@577
|
3093 SVCERR << "WARNING: Time ruler layer was not reset to 0 before session template loaded?" << endl;
|
Chris@577
|
3094 delete m_timeRulerLayer;
|
Chris@636
|
3095 m_timeRulerLayer = nullptr;
|
Chris@577
|
3096 }
|
Chris@577
|
3097 }
|
Chris@577
|
3098
|
Chris@577
|
3099 void
|
Chris@211
|
3100 MainWindowBase::toggleTimeRulers()
|
Chris@211
|
3101 {
|
Chris@211
|
3102 bool haveRulers = false;
|
Chris@211
|
3103 bool someHidden = false;
|
Chris@211
|
3104
|
Chris@211
|
3105 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
|
Chris@211
|
3106
|
Chris@211
|
3107 Pane *pane = m_paneStack->getPane(i);
|
Chris@211
|
3108 if (!pane) continue;
|
Chris@211
|
3109
|
Chris@211
|
3110 for (int j = 0; j < pane->getLayerCount(); ++j) {
|
Chris@211
|
3111
|
Chris@211
|
3112 Layer *layer = pane->getLayer(j);
|
Chris@211
|
3113 if (!dynamic_cast<TimeRulerLayer *>(layer)) continue;
|
Chris@211
|
3114
|
Chris@211
|
3115 haveRulers = true;
|
Chris@211
|
3116 if (layer->isLayerDormant(pane)) someHidden = true;
|
Chris@211
|
3117 }
|
Chris@211
|
3118 }
|
Chris@211
|
3119
|
Chris@211
|
3120 if (haveRulers) {
|
Chris@211
|
3121
|
Chris@211
|
3122 bool show = someHidden;
|
Chris@211
|
3123
|
Chris@211
|
3124 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
|
Chris@211
|
3125
|
Chris@211
|
3126 Pane *pane = m_paneStack->getPane(i);
|
Chris@211
|
3127 if (!pane) continue;
|
Chris@211
|
3128
|
Chris@211
|
3129 for (int j = 0; j < pane->getLayerCount(); ++j) {
|
Chris@211
|
3130
|
Chris@211
|
3131 Layer *layer = pane->getLayer(j);
|
Chris@211
|
3132 if (!dynamic_cast<TimeRulerLayer *>(layer)) continue;
|
Chris@211
|
3133
|
Chris@211
|
3134 layer->showLayer(pane, show);
|
Chris@211
|
3135 }
|
Chris@211
|
3136 }
|
Chris@211
|
3137 }
|
Chris@211
|
3138 }
|
Chris@211
|
3139
|
Chris@211
|
3140 void
|
Chris@45
|
3141 MainWindowBase::toggleZoomWheels()
|
Chris@45
|
3142 {
|
Chris@45
|
3143 if (m_viewManager->getZoomWheelsEnabled()) {
|
Chris@45
|
3144 m_viewManager->setZoomWheelsEnabled(false);
|
Chris@45
|
3145 } else {
|
Chris@45
|
3146 m_viewManager->setZoomWheelsEnabled(true);
|
Chris@45
|
3147 }
|
Chris@45
|
3148 }
|
Chris@45
|
3149
|
Chris@45
|
3150 void
|
Chris@45
|
3151 MainWindowBase::togglePropertyBoxes()
|
Chris@45
|
3152 {
|
Chris@712
|
3153 if (m_paneStack->getLayoutStyle() == PaneStack::HiddenPropertyStacksLayout) {
|
Chris@45
|
3154 if (Preferences::getInstance()->getPropertyBoxLayout() ==
|
Chris@45
|
3155 Preferences::VerticallyStacked) {
|
Chris@45
|
3156 m_paneStack->setLayoutStyle(PaneStack::PropertyStackPerPaneLayout);
|
Chris@45
|
3157 } else {
|
Chris@45
|
3158 m_paneStack->setLayoutStyle(PaneStack::SinglePropertyStackLayout);
|
Chris@45
|
3159 }
|
Chris@45
|
3160 } else {
|
Chris@712
|
3161 m_paneStack->setLayoutStyle(PaneStack::HiddenPropertyStacksLayout);
|
Chris@45
|
3162 }
|
Chris@45
|
3163 }
|
Chris@45
|
3164
|
Chris@378
|
3165 QLabel *
|
Chris@378
|
3166 MainWindowBase::getStatusLabel() const
|
Chris@378
|
3167 {
|
Chris@378
|
3168 if (!m_statusLabel) {
|
Chris@378
|
3169 m_statusLabel = new QLabel();
|
Chris@378
|
3170 statusBar()->addWidget(m_statusLabel, 1);
|
Chris@378
|
3171 }
|
Chris@379
|
3172
|
Chris@379
|
3173 QList<QFrame *> frames = statusBar()->findChildren<QFrame *>();
|
Chris@379
|
3174 foreach (QFrame *f, frames) {
|
Chris@379
|
3175 f->setFrameStyle(QFrame::NoFrame);
|
Chris@379
|
3176 }
|
Chris@379
|
3177
|
Chris@378
|
3178 return m_statusLabel;
|
Chris@378
|
3179 }
|
Chris@378
|
3180
|
Chris@45
|
3181 void
|
Chris@45
|
3182 MainWindowBase::toggleStatusBar()
|
Chris@45
|
3183 {
|
Chris@45
|
3184 QSettings settings;
|
Chris@45
|
3185 settings.beginGroup("MainWindow");
|
Chris@45
|
3186 bool sb = settings.value("showstatusbar", true).toBool();
|
Chris@45
|
3187
|
Chris@45
|
3188 if (sb) {
|
Chris@45
|
3189 statusBar()->hide();
|
Chris@45
|
3190 } else {
|
Chris@45
|
3191 statusBar()->show();
|
Chris@45
|
3192 }
|
Chris@45
|
3193
|
Chris@45
|
3194 settings.setValue("showstatusbar", !sb);
|
Chris@45
|
3195
|
Chris@45
|
3196 settings.endGroup();
|
Chris@45
|
3197 }
|
Chris@45
|
3198
|
Chris@45
|
3199 void
|
Chris@256
|
3200 MainWindowBase::toggleCentreLine()
|
Chris@256
|
3201 {
|
Chris@256
|
3202 if (m_viewManager->shouldShowCentreLine()) {
|
Chris@256
|
3203 m_viewManager->setShowCentreLine(false);
|
Chris@256
|
3204 } else {
|
Chris@256
|
3205 m_viewManager->setShowCentreLine(true);
|
Chris@256
|
3206 }
|
Chris@256
|
3207 }
|
Chris@256
|
3208
|
Chris@256
|
3209 void
|
Chris@45
|
3210 MainWindowBase::preferenceChanged(PropertyContainer::PropertyName name)
|
Chris@45
|
3211 {
|
Chris@45
|
3212 if (name == "Property Box Layout") {
|
Chris@712
|
3213 if (m_paneStack->getLayoutStyle() != PaneStack::HiddenPropertyStacksLayout) {
|
Chris@45
|
3214 if (Preferences::getInstance()->getPropertyBoxLayout() ==
|
Chris@45
|
3215 Preferences::VerticallyStacked) {
|
Chris@45
|
3216 m_paneStack->setLayoutStyle(PaneStack::PropertyStackPerPaneLayout);
|
Chris@45
|
3217 } else {
|
Chris@45
|
3218 m_paneStack->setLayoutStyle(PaneStack::SinglePropertyStackLayout);
|
Chris@45
|
3219 }
|
Chris@45
|
3220 }
|
Chris@45
|
3221 } else if (name == "Background Mode" && m_viewManager) {
|
Chris@45
|
3222 Preferences::BackgroundMode mode =
|
Chris@45
|
3223 Preferences::getInstance()->getBackgroundMode();
|
Chris@45
|
3224 if (mode == Preferences::BackgroundFromTheme) {
|
Chris@45
|
3225 m_viewManager->setGlobalDarkBackground(m_initialDarkBackground);
|
Chris@45
|
3226 } else if (mode == Preferences::DarkBackground) {
|
Chris@45
|
3227 m_viewManager->setGlobalDarkBackground(true);
|
Chris@45
|
3228 } else {
|
Chris@45
|
3229 m_viewManager->setGlobalDarkBackground(false);
|
Chris@45
|
3230 }
|
Chris@45
|
3231 }
|
Chris@45
|
3232 }
|
Chris@45
|
3233
|
Chris@45
|
3234 void
|
Chris@45
|
3235 MainWindowBase::play()
|
Chris@45
|
3236 {
|
Chris@516
|
3237 if ((m_recordTarget && m_recordTarget->isRecording()) ||
|
Chris@516
|
3238 (m_playSource && m_playSource->isPlaying())) {
|
Chris@45
|
3239 stop();
|
Chris@479
|
3240 QAction *action = qobject_cast<QAction *>(sender());
|
Chris@479
|
3241 if (action) action->setChecked(false);
|
Chris@45
|
3242 } else {
|
Chris@487
|
3243 if (m_audioIO) m_audioIO->resume();
|
Chris@509
|
3244 else if (m_playTarget) m_playTarget->resume();
|
Chris@45
|
3245 playbackFrameChanged(m_viewManager->getPlaybackFrame());
|
Chris@595
|
3246 m_playSource->play(m_viewManager->getPlaybackFrame());
|
Chris@45
|
3247 }
|
Chris@45
|
3248 }
|
Chris@45
|
3249
|
Chris@45
|
3250 void
|
Chris@477
|
3251 MainWindowBase::record()
|
Chris@477
|
3252 {
|
Chris@586
|
3253 QAction *action = qobject_cast<QAction *>(sender());
|
Chris@586
|
3254
|
Chris@714
|
3255 if (m_audioMode == AUDIO_NONE || m_audioMode == AUDIO_PLAYBACK_ONLY) {
|
Chris@586
|
3256 if (action) action->setChecked(false);
|
Chris@478
|
3257 return;
|
Chris@478
|
3258 }
|
Chris@478
|
3259
|
Chris@477
|
3260 if (!m_recordTarget) {
|
Chris@586
|
3261 if (action) action->setChecked(false);
|
Chris@477
|
3262 return;
|
Chris@477
|
3263 }
|
Chris@477
|
3264
|
Chris@714
|
3265 if (m_audioMode == AUDIO_PLAYBACK_NOW_RECORD_LATER) {
|
Chris@714
|
3266 SVDEBUG << "MainWindowBase::record: upgrading from "
|
Chris@714
|
3267 << "AUDIO_PLAYBACK_NOW_RECORD_LATER to "
|
Chris@714
|
3268 << "AUDIO_PLAYBACK_AND_RECORD" << endl;
|
Chris@714
|
3269 m_audioMode = AUDIO_PLAYBACK_AND_RECORD;
|
Chris@714
|
3270 deleteAudioIO();
|
Chris@714
|
3271 }
|
Chris@714
|
3272
|
Chris@478
|
3273 if (!m_audioIO) {
|
Chris@611
|
3274 SVDEBUG << "MainWindowBase::record: about to create audio IO" << endl;
|
Chris@478
|
3275 createAudioIO();
|
Chris@478
|
3276 }
|
Chris@492
|
3277
|
Chris@492
|
3278 if (!m_audioIO) {
|
Chris@586
|
3279 if (!m_playTarget) {
|
Chris@586
|
3280 // Don't need to report this, createAudioIO should have
|
Chris@586
|
3281 if (action) action->setChecked(false);
|
Chris@586
|
3282 return;
|
Chris@586
|
3283 } else {
|
Chris@586
|
3284 // Need to report this: if the play target exists instead
|
Chris@586
|
3285 // of the audio IO, then that means we failed to open a
|
Chris@586
|
3286 // capture device. The record control should be disabled
|
Chris@586
|
3287 // in that situation, so if it happens here, that must
|
Chris@586
|
3288 // mean this is the first time we ever tried to open the
|
Chris@586
|
3289 // audio device, hence the need to report the problem here
|
Chris@586
|
3290 QMessageBox::critical
|
Chris@586
|
3291 (this, tr("No record device available"),
|
Chris@586
|
3292 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
|
3293 if (action) action->setChecked(false);
|
Chris@586
|
3294 updateMenuStates();
|
Chris@586
|
3295 return;
|
Chris@586
|
3296 }
|
Chris@492
|
3297 }
|
Chris@478
|
3298
|
Chris@477
|
3299 if (m_recordTarget->isRecording()) {
|
Chris@492
|
3300 stop();
|
Chris@477
|
3301 return;
|
Chris@477
|
3302 }
|
Chris@490
|
3303
|
Chris@483
|
3304 if (m_audioRecordMode == RecordReplaceSession) {
|
Chris@490
|
3305 if (!checkSaveModified()) {
|
Chris@490
|
3306 if (action) action->setChecked(false);
|
Chris@490
|
3307 return;
|
Chris@490
|
3308 }
|
Chris@483
|
3309 }
|
Chris@487
|
3310
|
Chris@557
|
3311 if (m_viewManager) m_viewManager->setGlobalCentreFrame(0);
|
Chris@557
|
3312
|
Chris@611
|
3313 SVCERR << "MainWindowBase::record: about to resume" << endl;
|
Chris@492
|
3314 m_audioIO->resume();
|
Chris@509
|
3315
|
Chris@684
|
3316 WritableWaveFileModel *modelPtr = m_recordTarget->startRecording();
|
Chris@684
|
3317 if (!modelPtr) {
|
Chris@586
|
3318 SVCERR << "ERROR: MainWindowBase::record: Recording failed" << endl;
|
Chris@586
|
3319 QMessageBox::critical
|
Chris@586
|
3320 (this, tr("Recording failed"),
|
Chris@586
|
3321 tr("<b>Recording failed</b><p>Failed to switch to record mode (some internal problem?)</p>"));
|
Chris@490
|
3322 if (action) action->setChecked(false);
|
Chris@477
|
3323 return;
|
Chris@477
|
3324 }
|
Chris@477
|
3325
|
Chris@684
|
3326 if (!modelPtr->isOK()) {
|
Chris@611
|
3327 SVCERR << "MainWindowBase::record: Model not OK, stopping and suspending" << endl;
|
Chris@477
|
3328 m_recordTarget->stopRecording();
|
Chris@492
|
3329 m_audioIO->suspend();
|
Chris@586
|
3330 if (action) action->setChecked(false);
|
Chris@684
|
3331 delete modelPtr;
|
Chris@477
|
3332 return;
|
Chris@477
|
3333 }
|
Chris@611
|
3334
|
Chris@611
|
3335 SVCERR << "MainWindowBase::record: Model is OK, continuing..." << endl;
|
Chris@684
|
3336
|
Chris@684
|
3337 QString location = modelPtr->getLocation();
|
Chris@487
|
3338
|
Chris@687
|
3339 auto modelId = ModelById::add(std::shared_ptr<Model>(modelPtr));
|
Chris@483
|
3340
|
Chris@483
|
3341 if (m_audioRecordMode == RecordReplaceSession || !getMainModel()) {
|
Chris@478
|
3342
|
Chris@479
|
3343 //!!! duplication with openAudio here
|
Chris@479
|
3344
|
Chris@479
|
3345 QString templateName = getDefaultSessionTemplate();
|
Chris@479
|
3346 bool loadedTemplate = false;
|
Chris@479
|
3347
|
Chris@479
|
3348 if (templateName != "") {
|
Chris@479
|
3349 FileOpenStatus tplStatus = openSessionTemplate(templateName);
|
Chris@479
|
3350 if (tplStatus == FileOpenCancelled) {
|
Chris@611
|
3351 SVCERR << "MainWindowBase::record: Session template open cancelled, stopping and suspending" << endl;
|
Chris@490
|
3352 m_recordTarget->stopRecording();
|
Chris@492
|
3353 m_audioIO->suspend();
|
Chris@684
|
3354 ModelById::release(modelId);
|
Chris@479
|
3355 return;
|
Chris@479
|
3356 }
|
Chris@479
|
3357 if (tplStatus != FileOpenFailed) {
|
Chris@479
|
3358 loadedTemplate = true;
|
Chris@479
|
3359 }
|
Chris@479
|
3360 }
|
Chris@479
|
3361
|
Chris@479
|
3362 if (!loadedTemplate) {
|
Chris@479
|
3363 closeSession();
|
Chris@479
|
3364 createDocument();
|
Chris@479
|
3365 }
|
Chris@479
|
3366
|
Chris@684
|
3367 ModelId prevMain = getMainModelId();
|
Chris@684
|
3368 if (!prevMain.isNone()) {
|
Chris@479
|
3369 m_playSource->removeModel(prevMain);
|
Chris@479
|
3370 }
|
Chris@479
|
3371
|
Chris@684
|
3372 m_document->setMainModel(modelId);
|
Chris@478
|
3373 setupMenus();
|
Chris@577
|
3374 findTimeRulerLayer();
|
Chris@478
|
3375
|
Chris@684
|
3376 m_originalLocation = location;
|
Chris@669
|
3377
|
Chris@595
|
3378 if (loadedTemplate || (m_sessionFile == "")) {
|
Chris@595
|
3379 CommandHistory::getInstance()->clear();
|
Chris@595
|
3380 CommandHistory::getInstance()->documentSaved();
|
Chris@595
|
3381 }
|
Chris@479
|
3382
|
Chris@669
|
3383 m_documentModified = false;
|
Chris@669
|
3384 updateWindowTitle();
|
Chris@669
|
3385
|
Chris@478
|
3386 } else {
|
Chris@478
|
3387
|
Chris@478
|
3388 CommandHistory::getInstance()->startCompoundOperation
|
Chris@478
|
3389 (tr("Import Recorded Audio"), true);
|
Chris@478
|
3390
|
Chris@691
|
3391 m_document->addNonDerivedModel(modelId);
|
Chris@478
|
3392
|
Chris@478
|
3393 AddPaneCommand *command = new AddPaneCommand(this);
|
Chris@478
|
3394 CommandHistory::getInstance()->addCommand(command);
|
Chris@478
|
3395
|
Chris@478
|
3396 Pane *pane = command->getPane();
|
Chris@478
|
3397
|
Chris@478
|
3398 if (m_timeRulerLayer) {
|
Chris@478
|
3399 m_document->addLayerToView(pane, m_timeRulerLayer);
|
Chris@478
|
3400 }
|
Chris@478
|
3401
|
Chris@684
|
3402 Layer *newLayer = m_document->createImportedLayer(modelId);
|
Chris@478
|
3403
|
Chris@478
|
3404 if (newLayer) {
|
Chris@478
|
3405 m_document->addLayerToView(pane, newLayer);
|
Chris@478
|
3406 }
|
Chris@595
|
3407
|
Chris@478
|
3408 CommandHistory::getInstance()->endCompoundOperation();
|
Chris@477
|
3409 }
|
Chris@479
|
3410
|
Chris@479
|
3411 updateMenuStates();
|
Chris@684
|
3412 m_recentFiles.addFile(location);
|
Chris@479
|
3413 currentPaneChanged(m_paneStack->getCurrentPane());
|
Chris@611
|
3414
|
Chris@496
|
3415 emit audioFileLoaded();
|
Chris@477
|
3416 }
|
Chris@477
|
3417
|
Chris@477
|
3418 void
|
Chris@45
|
3419 MainWindowBase::ffwd()
|
Chris@45
|
3420 {
|
Chris@45
|
3421 if (!getMainModel()) return;
|
Chris@45
|
3422
|
Chris@708
|
3423 sv_frame_t playbackFrame = m_viewManager->getPlaybackFrame();
|
Chris@708
|
3424 sv_frame_t frame = playbackFrame + 1;
|
Chris@45
|
3425
|
Chris@85
|
3426 Pane *pane = m_paneStack->getCurrentPane();
|
Chris@45
|
3427 Layer *layer = getSnapLayer();
|
Chris@435
|
3428 sv_samplerate_t sr = getMainModel()->getSampleRate();
|
Chris@45
|
3429
|
Chris@708
|
3430 if (!pane || !layer) {
|
Chris@45
|
3431
|
Chris@45
|
3432 frame = RealTime::realTime2Frame
|
Chris@357
|
3433 (RealTime::frame2RealTime(frame, sr) + m_defaultFfwdRwdStep, sr);
|
Chris@435
|
3434 if (frame > getMainModel()->getEndFrame()) {
|
Chris@45
|
3435 frame = getMainModel()->getEndFrame();
|
Chris@45
|
3436 }
|
Chris@45
|
3437
|
Chris@45
|
3438 } else {
|
Chris@45
|
3439
|
Chris@708
|
3440 sv_frame_t pframe = pane->alignFromReference(frame);
|
Chris@366
|
3441 int resolution = 0;
|
Chris@708
|
3442 bool success = false;
|
Chris@708
|
3443
|
Chris@708
|
3444 while (layer->snapToFeatureFrame(pane, pframe, resolution,
|
Chris@715
|
3445 Layer::SnapRight, -1)) {
|
Chris@708
|
3446 if (pane->alignToReference(pframe) > playbackFrame) {
|
Chris@708
|
3447 success = true;
|
Chris@708
|
3448 break;
|
Chris@708
|
3449 } else {
|
Chris@708
|
3450 ++pframe;
|
Chris@708
|
3451 }
|
Chris@708
|
3452 }
|
Chris@708
|
3453
|
Chris@708
|
3454 if (success) {
|
Chris@708
|
3455 frame = pane->alignToReference(pframe);
|
Chris@85
|
3456 } else {
|
Chris@45
|
3457 frame = getMainModel()->getEndFrame();
|
Chris@45
|
3458 }
|
Chris@45
|
3459 }
|
Chris@45
|
3460
|
Chris@45
|
3461 if (frame < 0) frame = 0;
|
Chris@45
|
3462
|
Chris@45
|
3463 if (m_viewManager->getPlaySelectionMode()) {
|
Chris@435
|
3464 frame = m_viewManager->constrainFrameToSelection(frame);
|
Chris@45
|
3465 }
|
Chris@45
|
3466
|
Chris@45
|
3467 m_viewManager->setPlaybackFrame(frame);
|
Chris@166
|
3468
|
Chris@708
|
3469 if (frame >= getMainModel()->getEndFrame() &&
|
Chris@166
|
3470 m_playSource &&
|
Chris@166
|
3471 m_playSource->isPlaying() &&
|
Chris@166
|
3472 !m_viewManager->getPlayLoopMode()) {
|
Chris@166
|
3473 stop();
|
Chris@166
|
3474 }
|
Chris@45
|
3475 }
|
Chris@45
|
3476
|
Chris@45
|
3477 void
|
Chris@45
|
3478 MainWindowBase::ffwdEnd()
|
Chris@45
|
3479 {
|
Chris@45
|
3480 if (!getMainModel()) return;
|
Chris@45
|
3481
|
Chris@139
|
3482 if (m_playSource &&
|
Chris@139
|
3483 m_playSource->isPlaying() &&
|
Chris@139
|
3484 !m_viewManager->getPlayLoopMode()) {
|
Chris@139
|
3485 stop();
|
Chris@139
|
3486 }
|
Chris@139
|
3487
|
Chris@435
|
3488 sv_frame_t frame = getMainModel()->getEndFrame();
|
Chris@45
|
3489
|
Chris@45
|
3490 if (m_viewManager->getPlaySelectionMode()) {
|
Chris@45
|
3491 frame = m_viewManager->constrainFrameToSelection(frame);
|
Chris@45
|
3492 }
|
Chris@45
|
3493
|
Chris@45
|
3494 m_viewManager->setPlaybackFrame(frame);
|
Chris@45
|
3495 }
|
Chris@45
|
3496
|
Chris@45
|
3497 void
|
Chris@166
|
3498 MainWindowBase::ffwdSimilar()
|
Chris@166
|
3499 {
|
Chris@166
|
3500 if (!getMainModel()) return;
|
Chris@166
|
3501
|
Chris@166
|
3502 Layer *layer = getSnapLayer();
|
Chris@166
|
3503 if (!layer) { ffwd(); return; }
|
Chris@166
|
3504
|
Chris@166
|
3505 Pane *pane = m_paneStack->getCurrentPane();
|
Chris@435
|
3506 sv_frame_t frame = m_viewManager->getPlaybackFrame();
|
Chris@166
|
3507
|
Chris@366
|
3508 int resolution = 0;
|
Chris@166
|
3509 if (pane) frame = pane->alignFromReference(frame);
|
Chris@166
|
3510 if (layer->snapToSimilarFeature(m_paneStack->getCurrentPane(),
|
Chris@166
|
3511 frame, resolution, Layer::SnapRight)) {
|
Chris@166
|
3512 if (pane) frame = pane->alignToReference(frame);
|
Chris@166
|
3513 } else {
|
Chris@166
|
3514 frame = getMainModel()->getEndFrame();
|
Chris@166
|
3515 }
|
Chris@166
|
3516
|
Chris@166
|
3517 if (frame < 0) frame = 0;
|
Chris@166
|
3518
|
Chris@166
|
3519 if (m_viewManager->getPlaySelectionMode()) {
|
Chris@435
|
3520 frame = m_viewManager->constrainFrameToSelection(frame);
|
Chris@166
|
3521 }
|
Chris@166
|
3522
|
Chris@166
|
3523 m_viewManager->setPlaybackFrame(frame);
|
Chris@166
|
3524
|
Chris@435
|
3525 if (frame == getMainModel()->getEndFrame() &&
|
Chris@166
|
3526 m_playSource &&
|
Chris@166
|
3527 m_playSource->isPlaying() &&
|
Chris@166
|
3528 !m_viewManager->getPlayLoopMode()) {
|
Chris@166
|
3529 stop();
|
Chris@166
|
3530 }
|
Chris@166
|
3531 }
|
Chris@166
|
3532
|
Chris@166
|
3533 void
|
Chris@45
|
3534 MainWindowBase::rewind()
|
Chris@45
|
3535 {
|
Chris@45
|
3536 if (!getMainModel()) return;
|
Chris@45
|
3537
|
Chris@708
|
3538 sv_frame_t playbackFrame = m_viewManager->getPlaybackFrame();
|
Chris@708
|
3539 sv_frame_t frame = playbackFrame;
|
Chris@45
|
3540 if (frame > 0) --frame;
|
Chris@45
|
3541
|
Chris@85
|
3542 Pane *pane = m_paneStack->getCurrentPane();
|
Chris@45
|
3543 Layer *layer = getSnapLayer();
|
Chris@435
|
3544 sv_samplerate_t sr = getMainModel()->getSampleRate();
|
Chris@45
|
3545
|
Chris@45
|
3546 // when rewinding during playback, we want to allow a period
|
Chris@45
|
3547 // following a rewind target point at which the rewind will go to
|
Chris@45
|
3548 // the prior point instead of the immediately neighbouring one
|
Chris@45
|
3549 if (m_playSource && m_playSource->isPlaying()) {
|
Chris@45
|
3550 RealTime ct = RealTime::frame2RealTime(frame, sr);
|
Chris@357
|
3551 ct = ct - RealTime::fromSeconds(0.15);
|
Chris@45
|
3552 if (ct < RealTime::zeroTime) ct = RealTime::zeroTime;
|
Chris@45
|
3553 frame = RealTime::realTime2Frame(ct, sr);
|
Chris@45
|
3554 }
|
Chris@45
|
3555
|
Chris@708
|
3556 if (!pane || !layer) {
|
Chris@45
|
3557
|
Chris@45
|
3558 frame = RealTime::realTime2Frame
|
Chris@357
|
3559 (RealTime::frame2RealTime(frame, sr) - m_defaultFfwdRwdStep, sr);
|
Chris@435
|
3560 if (frame < getMainModel()->getStartFrame()) {
|
Chris@45
|
3561 frame = getMainModel()->getStartFrame();
|
Chris@45
|
3562 }
|
Chris@45
|
3563
|
Chris@45
|
3564 } else {
|
Chris@45
|
3565
|
Chris@708
|
3566 sv_frame_t pframe = pane->alignFromReference(frame);
|
Chris@366
|
3567 int resolution = 0;
|
Chris@708
|
3568 bool success = false;
|
Chris@708
|
3569
|
Chris@708
|
3570 while (layer->snapToFeatureFrame(pane, pframe, resolution,
|
Chris@715
|
3571 Layer::SnapLeft, -1)) {
|
Chris@708
|
3572 if (pane->alignToReference(pframe) < playbackFrame ||
|
Chris@708
|
3573 pframe <= 0) {
|
Chris@708
|
3574 success = true;
|
Chris@708
|
3575 break;
|
Chris@708
|
3576 } else {
|
Chris@708
|
3577 --pframe;
|
Chris@708
|
3578 }
|
Chris@708
|
3579 }
|
Chris@708
|
3580
|
Chris@708
|
3581 if (success) {
|
Chris@708
|
3582 frame = pane->alignToReference(pframe);
|
Chris@85
|
3583 } else {
|
Chris@45
|
3584 frame = getMainModel()->getStartFrame();
|
Chris@45
|
3585 }
|
Chris@45
|
3586 }
|
Chris@45
|
3587
|
Chris@45
|
3588 if (frame < 0) frame = 0;
|
Chris@45
|
3589
|
Chris@45
|
3590 if (m_viewManager->getPlaySelectionMode()) {
|
Chris@435
|
3591 frame = m_viewManager->constrainFrameToSelection(frame);
|
Chris@45
|
3592 }
|
Chris@45
|
3593
|
Chris@45
|
3594 m_viewManager->setPlaybackFrame(frame);
|
Chris@45
|
3595 }
|
Chris@45
|
3596
|
Chris@45
|
3597 void
|
Chris@45
|
3598 MainWindowBase::rewindStart()
|
Chris@45
|
3599 {
|
Chris@45
|
3600 if (!getMainModel()) return;
|
Chris@45
|
3601
|
Chris@435
|
3602 sv_frame_t frame = getMainModel()->getStartFrame();
|
Chris@45
|
3603
|
Chris@45
|
3604 if (m_viewManager->getPlaySelectionMode()) {
|
Chris@45
|
3605 frame = m_viewManager->constrainFrameToSelection(frame);
|
Chris@45
|
3606 }
|
Chris@45
|
3607
|
Chris@45
|
3608 m_viewManager->setPlaybackFrame(frame);
|
Chris@45
|
3609 }
|
Chris@45
|
3610
|
Chris@166
|
3611 void
|
Chris@166
|
3612 MainWindowBase::rewindSimilar()
|
Chris@166
|
3613 {
|
Chris@166
|
3614 if (!getMainModel()) return;
|
Chris@166
|
3615
|
Chris@166
|
3616 Layer *layer = getSnapLayer();
|
Chris@166
|
3617 if (!layer) { rewind(); return; }
|
Chris@166
|
3618
|
Chris@166
|
3619 Pane *pane = m_paneStack->getCurrentPane();
|
Chris@435
|
3620 sv_frame_t frame = m_viewManager->getPlaybackFrame();
|
Chris@166
|
3621
|
Chris@366
|
3622 int resolution = 0;
|
Chris@166
|
3623 if (pane) frame = pane->alignFromReference(frame);
|
Chris@166
|
3624 if (layer->snapToSimilarFeature(m_paneStack->getCurrentPane(),
|
Chris@166
|
3625 frame, resolution, Layer::SnapLeft)) {
|
Chris@166
|
3626 if (pane) frame = pane->alignToReference(frame);
|
Chris@166
|
3627 } else {
|
Chris@166
|
3628 frame = getMainModel()->getStartFrame();
|
Chris@166
|
3629 }
|
Chris@166
|
3630
|
Chris@166
|
3631 if (frame < 0) frame = 0;
|
Chris@166
|
3632
|
Chris@166
|
3633 if (m_viewManager->getPlaySelectionMode()) {
|
Chris@435
|
3634 frame = m_viewManager->constrainFrameToSelection(frame);
|
Chris@166
|
3635 }
|
Chris@166
|
3636
|
Chris@166
|
3637 m_viewManager->setPlaybackFrame(frame);
|
Chris@166
|
3638 }
|
Chris@166
|
3639
|
Chris@45
|
3640 Layer *
|
Chris@45
|
3641 MainWindowBase::getSnapLayer() const
|
Chris@45
|
3642 {
|
Chris@45
|
3643 Pane *pane = m_paneStack->getCurrentPane();
|
Chris@636
|
3644 if (!pane) return nullptr;
|
Chris@45
|
3645
|
Chris@45
|
3646 Layer *layer = pane->getSelectedLayer();
|
Chris@45
|
3647
|
Chris@45
|
3648 if (!dynamic_cast<TimeInstantLayer *>(layer) &&
|
Chris@45
|
3649 !dynamic_cast<TimeValueLayer *>(layer) &&
|
Chris@194
|
3650 !dynamic_cast<RegionLayer *>(layer) &&
|
Chris@45
|
3651 !dynamic_cast<TimeRulerLayer *>(layer)) {
|
Chris@45
|
3652
|
Chris@636
|
3653 layer = nullptr;
|
Chris@45
|
3654
|
Chris@45
|
3655 for (int i = pane->getLayerCount(); i > 0; --i) {
|
Chris@45
|
3656 Layer *l = pane->getLayer(i-1);
|
Chris@45
|
3657 if (dynamic_cast<TimeRulerLayer *>(l)) {
|
Chris@45
|
3658 layer = l;
|
Chris@45
|
3659 break;
|
Chris@45
|
3660 }
|
Chris@45
|
3661 }
|
Chris@45
|
3662 }
|
Chris@45
|
3663
|
Chris@45
|
3664 return layer;
|
Chris@45
|
3665 }
|
Chris@45
|
3666
|
Chris@45
|
3667 void
|
Chris@45
|
3668 MainWindowBase::stop()
|
Chris@45
|
3669 {
|
Chris@516
|
3670 if (m_recordTarget &&
|
Chris@516
|
3671 m_recordTarget->isRecording()) {
|
Chris@477
|
3672 m_recordTarget->stopRecording();
|
Chris@477
|
3673 }
|
Chris@516
|
3674
|
Chris@516
|
3675 if (!m_playSource) return;
|
Chris@516
|
3676
|
Chris@45
|
3677 m_playSource->stop();
|
Chris@45
|
3678
|
Chris@611
|
3679 SVCERR << "MainWindowBase::stop: suspending" << endl;
|
Chris@611
|
3680
|
Chris@487
|
3681 if (m_audioIO) m_audioIO->suspend();
|
Chris@509
|
3682 else if (m_playTarget) m_playTarget->suspend();
|
Chris@487
|
3683
|
Chris@45
|
3684 if (m_paneStack && m_paneStack->getCurrentPane()) {
|
Chris@45
|
3685 updateVisibleRangeDisplay(m_paneStack->getCurrentPane());
|
Chris@45
|
3686 } else {
|
Chris@45
|
3687 m_myStatusMessage = "";
|
Chris@378
|
3688 getStatusLabel()->setText("");
|
Chris@45
|
3689 }
|
Chris@45
|
3690 }
|
Chris@45
|
3691
|
Chris@45
|
3692 MainWindowBase::AddPaneCommand::AddPaneCommand(MainWindowBase *mw) :
|
Chris@45
|
3693 m_mw(mw),
|
Chris@636
|
3694 m_pane(nullptr),
|
Chris@636
|
3695 m_prevCurrentPane(nullptr),
|
Chris@45
|
3696 m_added(false)
|
Chris@45
|
3697 {
|
Chris@45
|
3698 }
|
Chris@45
|
3699
|
Chris@45
|
3700 MainWindowBase::AddPaneCommand::~AddPaneCommand()
|
Chris@45
|
3701 {
|
Chris@45
|
3702 if (m_pane && !m_added) {
|
Chris@595
|
3703 m_mw->m_paneStack->deletePane(m_pane);
|
Chris@45
|
3704 }
|
Chris@45
|
3705 }
|
Chris@45
|
3706
|
Chris@45
|
3707 QString
|
Chris@45
|
3708 MainWindowBase::AddPaneCommand::getName() const
|
Chris@45
|
3709 {
|
Chris@45
|
3710 return tr("Add Pane");
|
Chris@45
|
3711 }
|
Chris@45
|
3712
|
Chris@45
|
3713 void
|
Chris@45
|
3714 MainWindowBase::AddPaneCommand::execute()
|
Chris@45
|
3715 {
|
Chris@45
|
3716 if (!m_pane) {
|
Chris@595
|
3717 m_prevCurrentPane = m_mw->m_paneStack->getCurrentPane();
|
Chris@595
|
3718 m_pane = m_mw->m_paneStack->addPane();
|
Chris@45
|
3719
|
Chris@45
|
3720 connect(m_pane, SIGNAL(contextHelpChanged(const QString &)),
|
Chris@45
|
3721 m_mw, SLOT(contextHelpChanged(const QString &)));
|
Chris@45
|
3722 } else {
|
Chris@595
|
3723 m_mw->m_paneStack->showPane(m_pane);
|
Chris@45
|
3724 }
|
Chris@45
|
3725
|
Chris@45
|
3726 m_mw->m_paneStack->setCurrentPane(m_pane);
|
Chris@45
|
3727 m_added = true;
|
Chris@45
|
3728 }
|
Chris@45
|
3729
|
Chris@45
|
3730 void
|
Chris@45
|
3731 MainWindowBase::AddPaneCommand::unexecute()
|
Chris@45
|
3732 {
|
Chris@45
|
3733 m_mw->m_paneStack->hidePane(m_pane);
|
Chris@45
|
3734 m_mw->m_paneStack->setCurrentPane(m_prevCurrentPane);
|
Chris@45
|
3735 m_added = false;
|
Chris@45
|
3736 }
|
Chris@45
|
3737
|
Chris@45
|
3738 MainWindowBase::RemovePaneCommand::RemovePaneCommand(MainWindowBase *mw, Pane *pane) :
|
Chris@45
|
3739 m_mw(mw),
|
Chris@45
|
3740 m_pane(pane),
|
Chris@636
|
3741 m_prevCurrentPane(nullptr),
|
Chris@45
|
3742 m_added(true)
|
Chris@45
|
3743 {
|
Chris@45
|
3744 }
|
Chris@45
|
3745
|
Chris@45
|
3746 MainWindowBase::RemovePaneCommand::~RemovePaneCommand()
|
Chris@45
|
3747 {
|
Chris@45
|
3748 if (m_pane && !m_added) {
|
Chris@595
|
3749 m_mw->m_paneStack->deletePane(m_pane);
|
Chris@45
|
3750 }
|
Chris@45
|
3751 }
|
Chris@45
|
3752
|
Chris@45
|
3753 QString
|
Chris@45
|
3754 MainWindowBase::RemovePaneCommand::getName() const
|
Chris@45
|
3755 {
|
Chris@45
|
3756 return tr("Remove Pane");
|
Chris@45
|
3757 }
|
Chris@45
|
3758
|
Chris@45
|
3759 void
|
Chris@45
|
3760 MainWindowBase::RemovePaneCommand::execute()
|
Chris@45
|
3761 {
|
Chris@45
|
3762 m_prevCurrentPane = m_mw->m_paneStack->getCurrentPane();
|
Chris@45
|
3763 m_mw->m_paneStack->hidePane(m_pane);
|
Chris@45
|
3764 m_added = false;
|
Chris@45
|
3765 }
|
Chris@45
|
3766
|
Chris@45
|
3767 void
|
Chris@45
|
3768 MainWindowBase::RemovePaneCommand::unexecute()
|
Chris@45
|
3769 {
|
Chris@45
|
3770 m_mw->m_paneStack->showPane(m_pane);
|
Chris@45
|
3771 m_mw->m_paneStack->setCurrentPane(m_prevCurrentPane);
|
Chris@45
|
3772 m_added = true;
|
Chris@45
|
3773 }
|
Chris@45
|
3774
|
Chris@45
|
3775 void
|
Chris@45
|
3776 MainWindowBase::deleteCurrentPane()
|
Chris@45
|
3777 {
|
Chris@45
|
3778 CommandHistory::getInstance()->startCompoundOperation
|
Chris@595
|
3779 (tr("Delete Pane"), true);
|
Chris@45
|
3780
|
Chris@45
|
3781 Pane *pane = m_paneStack->getCurrentPane();
|
Chris@45
|
3782 if (pane) {
|
Chris@595
|
3783 while (pane->getLayerCount() > 0) {
|
Chris@595
|
3784 Layer *layer = pane->getLayer(0);
|
Chris@595
|
3785 if (layer) {
|
Chris@595
|
3786 m_document->removeLayerFromView(pane, layer);
|
Chris@595
|
3787 } else {
|
Chris@595
|
3788 break;
|
Chris@595
|
3789 }
|
Chris@595
|
3790 }
|
Chris@595
|
3791
|
Chris@595
|
3792 RemovePaneCommand *command = new RemovePaneCommand(this, pane);
|
Chris@595
|
3793 CommandHistory::getInstance()->addCommand(command);
|
Chris@45
|
3794 }
|
Chris@45
|
3795
|
Chris@45
|
3796 CommandHistory::getInstance()->endCompoundOperation();
|
Chris@45
|
3797
|
Chris@45
|
3798 updateMenuStates();
|
Chris@45
|
3799 }
|
Chris@45
|
3800
|
Chris@45
|
3801 void
|
Chris@45
|
3802 MainWindowBase::deleteCurrentLayer()
|
Chris@45
|
3803 {
|
Chris@45
|
3804 Pane *pane = m_paneStack->getCurrentPane();
|
Chris@45
|
3805 if (pane) {
|
Chris@595
|
3806 Layer *layer = pane->getSelectedLayer();
|
Chris@595
|
3807 if (layer) {
|
Chris@595
|
3808 m_document->removeLayerFromView(pane, layer);
|
Chris@595
|
3809 }
|
Chris@45
|
3810 }
|
Chris@45
|
3811 updateMenuStates();
|
Chris@45
|
3812 }
|
Chris@45
|
3813
|
Chris@45
|
3814 void
|
Chris@123
|
3815 MainWindowBase::editCurrentLayer()
|
Chris@123
|
3816 {
|
Chris@636
|
3817 Layer *layer = nullptr;
|
Chris@123
|
3818 Pane *pane = m_paneStack->getCurrentPane();
|
Chris@123
|
3819 if (pane) layer = pane->getSelectedLayer();
|
Chris@123
|
3820 if (!layer) return;
|
Chris@123
|
3821
|
Chris@684
|
3822 auto tabular = ModelById::getAs<TabularModel>(layer->getModel());
|
Chris@124
|
3823 if (!tabular) {
|
Chris@124
|
3824 //!!! how to prevent this function from being active if not
|
Chris@124
|
3825 //appropriate model type? or will we ultimately support
|
Chris@124
|
3826 //tabular display for all editable models?
|
Chris@233
|
3827 SVDEBUG << "NOTE: Not a tabular model" << endl;
|
Chris@124
|
3828 return;
|
Chris@124
|
3829 }
|
Chris@124
|
3830
|
Chris@123
|
3831 if (m_layerDataDialogMap.find(layer) != m_layerDataDialogMap.end()) {
|
Chris@126
|
3832 if (!m_layerDataDialogMap[layer].isNull()) {
|
Chris@126
|
3833 m_layerDataDialogMap[layer]->show();
|
Chris@126
|
3834 m_layerDataDialogMap[layer]->raise();
|
Chris@126
|
3835 return;
|
Chris@126
|
3836 }
|
Chris@123
|
3837 }
|
Chris@123
|
3838
|
Chris@125
|
3839 QString title = layer->getLayerPresentationName();
|
Chris@125
|
3840
|
Chris@684
|
3841 ModelDataTableDialog *dialog = new ModelDataTableDialog
|
Chris@684
|
3842 (layer->getModel(), title, this);
|
Chris@128
|
3843 dialog->setAttribute(Qt::WA_DeleteOnClose);
|
Chris@128
|
3844
|
Chris@128
|
3845 connectLayerEditDialog(dialog);
|
Chris@123
|
3846
|
Chris@128
|
3847 m_layerDataDialogMap[layer] = dialog;
|
Chris@128
|
3848 m_viewDataDialogMap[pane].insert(dialog);
|
Chris@128
|
3849
|
Chris@128
|
3850 dialog->show();
|
Chris@128
|
3851 }
|
Chris@128
|
3852
|
Chris@128
|
3853 void
|
Chris@128
|
3854 MainWindowBase::connectLayerEditDialog(ModelDataTableDialog *dialog)
|
Chris@128
|
3855 {
|
Chris@123
|
3856 connect(m_viewManager,
|
Chris@435
|
3857 SIGNAL(globalCentreFrameChanged(sv_frame_t)),
|
Chris@123
|
3858 dialog,
|
Chris@435
|
3859 SLOT(userScrolledToFrame(sv_frame_t)));
|
Chris@127
|
3860
|
Chris@127
|
3861 connect(m_viewManager,
|
Chris@435
|
3862 SIGNAL(playbackFrameChanged(sv_frame_t)),
|
Chris@127
|
3863 dialog,
|
Chris@435
|
3864 SLOT(playbackScrolledToFrame(sv_frame_t)));
|
Chris@127
|
3865
|
Chris@123
|
3866 connect(dialog,
|
Chris@435
|
3867 SIGNAL(scrollToFrame(sv_frame_t)),
|
Chris@123
|
3868 m_viewManager,
|
Chris@435
|
3869 SLOT(setGlobalCentreFrame(sv_frame_t)));
|
Chris@129
|
3870
|
Chris@129
|
3871 connect(dialog,
|
Chris@435
|
3872 SIGNAL(scrollToFrame(sv_frame_t)),
|
Chris@129
|
3873 m_viewManager,
|
Chris@435
|
3874 SLOT(setPlaybackFrame(sv_frame_t)));
|
Chris@128
|
3875 }
|
Chris@123
|
3876
|
Chris@123
|
3877 void
|
Chris@73
|
3878 MainWindowBase::previousPane()
|
Chris@73
|
3879 {
|
Chris@73
|
3880 if (!m_paneStack) return;
|
Chris@73
|
3881
|
Chris@73
|
3882 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@73
|
3883 if (!currentPane) return;
|
Chris@73
|
3884
|
Chris@73
|
3885 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
|
Chris@73
|
3886 if (m_paneStack->getPane(i) == currentPane) {
|
Chris@73
|
3887 if (i == 0) return;
|
Chris@73
|
3888 m_paneStack->setCurrentPane(m_paneStack->getPane(i-1));
|
Chris@73
|
3889 updateMenuStates();
|
Chris@73
|
3890 return;
|
Chris@73
|
3891 }
|
Chris@73
|
3892 }
|
Chris@73
|
3893 }
|
Chris@73
|
3894
|
Chris@73
|
3895 void
|
Chris@73
|
3896 MainWindowBase::nextPane()
|
Chris@73
|
3897 {
|
Chris@73
|
3898 if (!m_paneStack) return;
|
Chris@73
|
3899
|
Chris@73
|
3900 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@73
|
3901 if (!currentPane) return;
|
Chris@73
|
3902
|
Chris@73
|
3903 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
|
Chris@73
|
3904 if (m_paneStack->getPane(i) == currentPane) {
|
Chris@73
|
3905 if (i == m_paneStack->getPaneCount()-1) return;
|
Chris@73
|
3906 m_paneStack->setCurrentPane(m_paneStack->getPane(i+1));
|
Chris@73
|
3907 updateMenuStates();
|
Chris@73
|
3908 return;
|
Chris@73
|
3909 }
|
Chris@73
|
3910 }
|
Chris@73
|
3911 }
|
Chris@73
|
3912
|
Chris@73
|
3913 void
|
Chris@73
|
3914 MainWindowBase::previousLayer()
|
Chris@73
|
3915 {
|
Chris@73
|
3916 if (!m_paneStack) return;
|
Chris@73
|
3917
|
Chris@73
|
3918 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@73
|
3919 if (!currentPane) return;
|
Chris@73
|
3920
|
Chris@403
|
3921 int count = currentPane->getLayerCount();
|
Chris@403
|
3922 if (count == 0) return;
|
Chris@403
|
3923
|
Chris@73
|
3924 Layer *currentLayer = currentPane->getSelectedLayer();
|
Chris@403
|
3925
|
Chris@403
|
3926 if (!currentLayer) {
|
Chris@403
|
3927 // The pane itself is current
|
Chris@403
|
3928 m_paneStack->setCurrentLayer
|
Chris@403
|
3929 (currentPane, currentPane->getFixedOrderLayer(count-1));
|
Chris@403
|
3930 } else {
|
Chris@403
|
3931 for (int i = 0; i < count; ++i) {
|
Chris@403
|
3932 if (currentPane->getFixedOrderLayer(i) == currentLayer) {
|
Chris@403
|
3933 if (i == 0) {
|
Chris@403
|
3934 m_paneStack->setCurrentLayer
|
Chris@636
|
3935 (currentPane, nullptr); // pane
|
Chris@403
|
3936 } else {
|
Chris@403
|
3937 m_paneStack->setCurrentLayer
|
Chris@403
|
3938 (currentPane, currentPane->getFixedOrderLayer(i-1));
|
Chris@403
|
3939 }
|
Chris@403
|
3940 break;
|
Chris@403
|
3941 }
|
Chris@73
|
3942 }
|
Chris@73
|
3943 }
|
Chris@403
|
3944
|
Chris@403
|
3945 updateMenuStates();
|
Chris@73
|
3946 }
|
Chris@73
|
3947
|
Chris@73
|
3948 void
|
Chris@73
|
3949 MainWindowBase::nextLayer()
|
Chris@73
|
3950 {
|
Chris@73
|
3951 if (!m_paneStack) return;
|
Chris@73
|
3952
|
Chris@73
|
3953 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@73
|
3954 if (!currentPane) return;
|
Chris@73
|
3955
|
Chris@403
|
3956 int count = currentPane->getLayerCount();
|
Chris@403
|
3957 if (count == 0) return;
|
Chris@403
|
3958
|
Chris@73
|
3959 Layer *currentLayer = currentPane->getSelectedLayer();
|
Chris@403
|
3960
|
Chris@403
|
3961 if (!currentLayer) {
|
Chris@403
|
3962 // The pane itself is current
|
Chris@403
|
3963 m_paneStack->setCurrentLayer
|
Chris@403
|
3964 (currentPane, currentPane->getFixedOrderLayer(0));
|
Chris@403
|
3965 } else {
|
Chris@403
|
3966 for (int i = 0; i < count; ++i) {
|
Chris@403
|
3967 if (currentPane->getFixedOrderLayer(i) == currentLayer) {
|
Chris@403
|
3968 if (i == currentPane->getLayerCount()-1) {
|
Chris@403
|
3969 m_paneStack->setCurrentLayer
|
Chris@636
|
3970 (currentPane, nullptr); // pane
|
Chris@403
|
3971 } else {
|
Chris@403
|
3972 m_paneStack->setCurrentLayer
|
Chris@403
|
3973 (currentPane, currentPane->getFixedOrderLayer(i+1));
|
Chris@403
|
3974 }
|
Chris@403
|
3975 break;
|
Chris@403
|
3976 }
|
Chris@73
|
3977 }
|
Chris@73
|
3978 }
|
Chris@403
|
3979
|
Chris@403
|
3980 updateMenuStates();
|
Chris@73
|
3981 }
|
Chris@73
|
3982
|
Chris@73
|
3983 void
|
Chris@435
|
3984 MainWindowBase::playbackFrameChanged(sv_frame_t frame)
|
Chris@45
|
3985 {
|
Chris@45
|
3986 if (!(m_playSource && m_playSource->isPlaying()) || !getMainModel()) return;
|
Chris@45
|
3987
|
Chris@187
|
3988 updatePositionStatusDisplays();
|
Chris@187
|
3989
|
Chris@45
|
3990 RealTime now = RealTime::frame2RealTime
|
Chris@45
|
3991 (frame, getMainModel()->getSampleRate());
|
Chris@45
|
3992
|
Chris@45
|
3993 if (now.sec == m_lastPlayStatusSec) return;
|
Chris@45
|
3994
|
Chris@45
|
3995 RealTime then = RealTime::frame2RealTime
|
Chris@45
|
3996 (m_playSource->getPlayEndFrame(), getMainModel()->getSampleRate());
|
Chris@45
|
3997
|
Chris@45
|
3998 QString nowStr;
|
Chris@45
|
3999 QString thenStr;
|
Chris@45
|
4000 QString remainingStr;
|
Chris@45
|
4001
|
Chris@45
|
4002 if (then.sec > 10) {
|
Chris@45
|
4003 nowStr = now.toSecText().c_str();
|
Chris@45
|
4004 thenStr = then.toSecText().c_str();
|
Chris@45
|
4005 remainingStr = (then - now).toSecText().c_str();
|
Chris@45
|
4006 m_lastPlayStatusSec = now.sec;
|
Chris@45
|
4007 } else {
|
Chris@45
|
4008 nowStr = now.toText(true).c_str();
|
Chris@45
|
4009 thenStr = then.toText(true).c_str();
|
Chris@45
|
4010 remainingStr = (then - now).toText(true).c_str();
|
Chris@45
|
4011 }
|
Chris@45
|
4012
|
Chris@45
|
4013 m_myStatusMessage = tr("Playing: %1 of %2 (%3 remaining)")
|
Chris@45
|
4014 .arg(nowStr).arg(thenStr).arg(remainingStr);
|
Chris@45
|
4015
|
Chris@378
|
4016 getStatusLabel()->setText(m_myStatusMessage);
|
Chris@45
|
4017 }
|
Chris@45
|
4018
|
Chris@45
|
4019 void
|
Chris@486
|
4020 MainWindowBase::recordDurationChanged(sv_frame_t frame, sv_samplerate_t rate)
|
Chris@486
|
4021 {
|
Chris@486
|
4022 RealTime duration = RealTime::frame2RealTime(frame, rate);
|
Chris@486
|
4023 QString durStr = duration.toSecText().c_str();
|
Chris@486
|
4024
|
Chris@486
|
4025 m_myStatusMessage = tr("Recording: %1").arg(durStr);
|
Chris@486
|
4026
|
Chris@486
|
4027 getStatusLabel()->setText(m_myStatusMessage);
|
Chris@486
|
4028 }
|
Chris@486
|
4029
|
Chris@486
|
4030 void
|
Chris@435
|
4031 MainWindowBase::globalCentreFrameChanged(sv_frame_t )
|
Chris@45
|
4032 {
|
Chris@45
|
4033 if ((m_playSource && m_playSource->isPlaying()) || !getMainModel()) return;
|
Chris@636
|
4034 Pane *p = nullptr;
|
Chris@45
|
4035 if (!m_paneStack || !(p = m_paneStack->getCurrentPane())) return;
|
Chris@45
|
4036 if (!p->getFollowGlobalPan()) return;
|
Chris@45
|
4037 updateVisibleRangeDisplay(p);
|
Chris@45
|
4038 }
|
Chris@45
|
4039
|
Chris@45
|
4040 void
|
Chris@435
|
4041 MainWindowBase::viewCentreFrameChanged(View *v, sv_frame_t frame)
|
Chris@45
|
4042 {
|
Chris@233
|
4043 // SVDEBUG << "MainWindowBase::viewCentreFrameChanged(" << v << "," << frame << ")" << endl;
|
Chris@123
|
4044
|
Chris@123
|
4045 if (m_viewDataDialogMap.find(v) != m_viewDataDialogMap.end()) {
|
Chris@123
|
4046 for (DataDialogSet::iterator i = m_viewDataDialogMap[v].begin();
|
Chris@123
|
4047 i != m_viewDataDialogMap[v].end(); ++i) {
|
Chris@127
|
4048 (*i)->userScrolledToFrame(frame);
|
Chris@123
|
4049 }
|
Chris@123
|
4050 }
|
Chris@45
|
4051 if ((m_playSource && m_playSource->isPlaying()) || !getMainModel()) return;
|
Chris@636
|
4052 Pane *p = nullptr;
|
Chris@45
|
4053 if (!m_paneStack || !(p = m_paneStack->getCurrentPane())) return;
|
Chris@45
|
4054 if (v == p) updateVisibleRangeDisplay(p);
|
Chris@45
|
4055 }
|
Chris@45
|
4056
|
Chris@45
|
4057 void
|
Chris@624
|
4058 MainWindowBase::viewZoomLevelChanged(View *v, ZoomLevel, bool )
|
Chris@45
|
4059 {
|
Chris@45
|
4060 if ((m_playSource && m_playSource->isPlaying()) || !getMainModel()) return;
|
Chris@636
|
4061 Pane *p = nullptr;
|
Chris@45
|
4062 if (!m_paneStack || !(p = m_paneStack->getCurrentPane())) return;
|
Chris@45
|
4063 if (v == p) updateVisibleRangeDisplay(p);
|
Chris@45
|
4064 }
|
Chris@45
|
4065
|
Chris@45
|
4066 void
|
Chris@45
|
4067 MainWindowBase::layerAdded(Layer *)
|
Chris@45
|
4068 {
|
Chris@233
|
4069 // SVDEBUG << "MainWindowBase::layerAdded(" << layer << ")" << endl;
|
Chris@45
|
4070 updateMenuStates();
|
Chris@45
|
4071 }
|
Chris@45
|
4072
|
Chris@45
|
4073 void
|
Chris@45
|
4074 MainWindowBase::layerRemoved(Layer *)
|
Chris@45
|
4075 {
|
Chris@233
|
4076 // SVDEBUG << "MainWindowBase::layerRemoved(" << layer << ")" << endl;
|
Chris@45
|
4077 updateMenuStates();
|
Chris@45
|
4078 }
|
Chris@45
|
4079
|
Chris@45
|
4080 void
|
Chris@45
|
4081 MainWindowBase::layerAboutToBeDeleted(Layer *layer)
|
Chris@45
|
4082 {
|
Chris@233
|
4083 // SVDEBUG << "MainWindowBase::layerAboutToBeDeleted(" << layer << ")" << endl;
|
Chris@123
|
4084
|
Chris@128
|
4085 removeLayerEditDialog(layer);
|
Chris@123
|
4086
|
Chris@47
|
4087 if (m_timeRulerLayer && (layer == m_timeRulerLayer)) {
|
Chris@595
|
4088 // cerr << "(this is the time ruler layer)" << endl;
|
Chris@636
|
4089 m_timeRulerLayer = nullptr;
|
Chris@45
|
4090 }
|
Chris@45
|
4091 }
|
Chris@45
|
4092
|
Chris@45
|
4093 void
|
Chris@45
|
4094 MainWindowBase::layerInAView(Layer *layer, bool inAView)
|
Chris@45
|
4095 {
|
Chris@233
|
4096 // SVDEBUG << "MainWindowBase::layerInAView(" << layer << "," << inAView << ")" << endl;
|
Chris@128
|
4097
|
Chris@128
|
4098 if (!inAView) removeLayerEditDialog(layer);
|
Chris@45
|
4099
|
Chris@45
|
4100 // Check whether we need to add or remove model from play source
|
Chris@684
|
4101 ModelId modelId = layer->getModel();
|
Chris@684
|
4102 if (!modelId.isNone()) {
|
Chris@45
|
4103 if (inAView) {
|
Chris@684
|
4104 m_playSource->addModel(modelId);
|
Chris@45
|
4105 } else {
|
Chris@45
|
4106 bool found = false;
|
Chris@45
|
4107 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
|
Chris@45
|
4108 Pane *pane = m_paneStack->getPane(i);
|
Chris@45
|
4109 if (!pane) continue;
|
Chris@45
|
4110 for (int j = 0; j < pane->getLayerCount(); ++j) {
|
Chris@45
|
4111 Layer *pl = pane->getLayer(j);
|
Chris@183
|
4112 if (pl &&
|
Chris@183
|
4113 !dynamic_cast<TimeRulerLayer *>(pl) &&
|
Chris@684
|
4114 (pl->getModel() == modelId)) {
|
Chris@45
|
4115 found = true;
|
Chris@45
|
4116 break;
|
Chris@45
|
4117 }
|
Chris@45
|
4118 }
|
Chris@45
|
4119 if (found) break;
|
Chris@45
|
4120 }
|
Chris@173
|
4121 if (!found) {
|
Chris@684
|
4122 m_playSource->removeModel(modelId);
|
Chris@173
|
4123 }
|
Chris@45
|
4124 }
|
Chris@45
|
4125 }
|
Chris@45
|
4126
|
Chris@45
|
4127 updateMenuStates();
|
Chris@45
|
4128 }
|
Chris@45
|
4129
|
Chris@45
|
4130 void
|
Chris@128
|
4131 MainWindowBase::removeLayerEditDialog(Layer *layer)
|
Chris@128
|
4132 {
|
Chris@128
|
4133 if (m_layerDataDialogMap.find(layer) != m_layerDataDialogMap.end()) {
|
Chris@128
|
4134
|
Chris@128
|
4135 ModelDataTableDialog *dialog = m_layerDataDialogMap[layer];
|
Chris@128
|
4136
|
Chris@128
|
4137 for (ViewDataDialogMap::iterator vi = m_viewDataDialogMap.begin();
|
Chris@128
|
4138 vi != m_viewDataDialogMap.end(); ++vi) {
|
Chris@128
|
4139 vi->second.erase(dialog);
|
Chris@128
|
4140 }
|
Chris@128
|
4141
|
Chris@128
|
4142 m_layerDataDialogMap.erase(layer);
|
Chris@128
|
4143 delete dialog;
|
Chris@128
|
4144 }
|
Chris@128
|
4145 }
|
Chris@128
|
4146
|
Chris@128
|
4147 void
|
Chris@684
|
4148 MainWindowBase::modelAdded(ModelId model)
|
Chris@45
|
4149 {
|
Chris@233
|
4150 // SVDEBUG << "MainWindowBase::modelAdded(" << model << ")" << endl;
|
Chris@684
|
4151 std::cerr << "\nAdding model " << model << " to playsource " << std::endl;
|
Chris@45
|
4152 m_playSource->addModel(model);
|
Chris@45
|
4153 }
|
Chris@45
|
4154
|
Chris@45
|
4155 void
|
Chris@684
|
4156 MainWindowBase::mainModelChanged(ModelId modelId)
|
Chris@45
|
4157 {
|
Chris@233
|
4158 // SVDEBUG << "MainWindowBase::mainModelChanged(" << model << ")" << endl;
|
Chris@45
|
4159 updateDescriptionLabel();
|
Chris@684
|
4160 auto model = ModelById::getAs<WaveFileModel>(modelId);
|
Chris@45
|
4161 if (model) m_viewManager->setMainModelSampleRate(model->getSampleRate());
|
Chris@714
|
4162 if (model && !(m_playTarget || m_audioIO) && (m_audioMode != AUDIO_NONE)) {
|
Chris@475
|
4163 createAudioIO();
|
Chris@360
|
4164 }
|
Chris@45
|
4165 }
|
Chris@45
|
4166
|
Chris@45
|
4167 void
|
Chris@55
|
4168 MainWindowBase::paneDeleteButtonClicked(Pane *pane)
|
Chris@55
|
4169 {
|
Chris@55
|
4170 bool found = false;
|
Chris@55
|
4171 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
|
Chris@55
|
4172 if (m_paneStack->getPane(i) == pane) {
|
Chris@55
|
4173 found = true;
|
Chris@55
|
4174 break;
|
Chris@55
|
4175 }
|
Chris@55
|
4176 }
|
Chris@55
|
4177 if (!found) {
|
Chris@233
|
4178 SVDEBUG << "MainWindowBase::paneDeleteButtonClicked: Unknown pane "
|
Chris@229
|
4179 << pane << endl;
|
Chris@55
|
4180 return;
|
Chris@55
|
4181 }
|
Chris@55
|
4182
|
Chris@55
|
4183 CommandHistory::getInstance()->startCompoundOperation
|
Chris@595
|
4184 (tr("Delete Pane"), true);
|
Chris@55
|
4185
|
Chris@55
|
4186 while (pane->getLayerCount() > 0) {
|
Chris@637
|
4187 Layer *layer = pane->getLayer(pane->getLayerCount() - 1);
|
Chris@55
|
4188 if (layer) {
|
Chris@55
|
4189 m_document->removeLayerFromView(pane, layer);
|
Chris@55
|
4190 } else {
|
Chris@55
|
4191 break;
|
Chris@55
|
4192 }
|
Chris@55
|
4193 }
|
Chris@55
|
4194
|
Chris@55
|
4195 RemovePaneCommand *command = new RemovePaneCommand(this, pane);
|
Chris@55
|
4196 CommandHistory::getInstance()->addCommand(command);
|
Chris@55
|
4197
|
Chris@55
|
4198 CommandHistory::getInstance()->endCompoundOperation();
|
Chris@55
|
4199
|
Chris@55
|
4200 updateMenuStates();
|
Chris@55
|
4201 }
|
Chris@55
|
4202
|
Chris@55
|
4203 void
|
Chris@684
|
4204 MainWindowBase::alignmentComplete(ModelId alignmentModelId)
|
Chris@429
|
4205 {
|
Chris@684
|
4206 cerr << "MainWindowBase::alignmentComplete(" << alignmentModelId << ")" << endl;
|
Chris@429
|
4207 }
|
Chris@429
|
4208
|
Chris@429
|
4209 void
|
Chris@45
|
4210 MainWindowBase::pollOSC()
|
Chris@45
|
4211 {
|
Chris@45
|
4212 if (!m_oscQueue || m_oscQueue->isEmpty()) return;
|
Chris@725
|
4213
|
Chris@725
|
4214 while (!m_oscQueue->isEmpty()) {
|
Chris@725
|
4215
|
Chris@725
|
4216 if (m_openingAudioFile) {
|
Chris@725
|
4217 SVDEBUG << "MainWindowBase::pollOSC: "
|
Chris@725
|
4218 << "waiting for audio to finish loading"
|
Chris@725
|
4219 << endl;
|
Chris@725
|
4220 return;
|
Chris@725
|
4221 }
|
Chris@725
|
4222
|
Chris@725
|
4223 if (ModelTransformerFactory::getInstance()->haveRunningTransformers()) {
|
Chris@725
|
4224 SVDEBUG << "MainWindowBase::pollOSC: "
|
Chris@725
|
4225 << "waiting for running transforms to complete"
|
Chris@725
|
4226 << endl;
|
Chris@725
|
4227 return;
|
Chris@725
|
4228 }
|
Chris@725
|
4229
|
Chris@725
|
4230 SVDEBUG << "MainWindowBase::pollOSC: have "
|
Chris@725
|
4231 << m_oscQueue->getMessagesAvailable()
|
Chris@725
|
4232 << " messages" << endl;
|
Chris@725
|
4233
|
Chris@725
|
4234 OSCMessage message = m_oscQueue->readMessage();
|
Chris@725
|
4235
|
Chris@725
|
4236 if (message.getTarget() != 0) {
|
Chris@725
|
4237 SVCERR << "MainWindowBase::pollOSC: ignoring message with target "
|
Chris@725
|
4238 << message.getTarget() << " (we are target 0)" << endl;
|
Chris@725
|
4239 continue;
|
Chris@725
|
4240 }
|
Chris@725
|
4241
|
Chris@725
|
4242 handleOSCMessage(message);
|
Chris@725
|
4243
|
Chris@725
|
4244 disconnect(m_oscQueue, SIGNAL(messagesAvailable()),
|
Chris@725
|
4245 this, SLOT(pollOSC()));
|
Chris@725
|
4246
|
Chris@725
|
4247 QApplication::processEvents(QEventLoop::ExcludeUserInputEvents |
|
Chris@725
|
4248 QEventLoop::ExcludeSocketNotifiers);
|
Chris@725
|
4249
|
Chris@725
|
4250 connect(m_oscQueue, SIGNAL(messagesAvailable()),
|
Chris@725
|
4251 this, SLOT(pollOSC()));
|
Chris@45
|
4252 }
|
Chris@45
|
4253 }
|
Chris@45
|
4254
|
Chris@45
|
4255 void
|
Chris@45
|
4256 MainWindowBase::inProgressSelectionChanged()
|
Chris@45
|
4257 {
|
Chris@636
|
4258 Pane *currentPane = nullptr;
|
Chris@45
|
4259 if (m_paneStack) currentPane = m_paneStack->getCurrentPane();
|
justin@331
|
4260 if (currentPane) {
|
justin@331
|
4261 //cerr << "JTEST: mouse event on selection pane" << endl;
|
justin@331
|
4262 updateVisibleRangeDisplay(currentPane);
|
justin@331
|
4263 }
|
Chris@45
|
4264 }
|
Chris@45
|
4265
|
Chris@45
|
4266 void
|
Chris@45
|
4267 MainWindowBase::contextHelpChanged(const QString &s)
|
Chris@45
|
4268 {
|
Chris@378
|
4269 QLabel *lab = getStatusLabel();
|
Chris@375
|
4270
|
Chris@45
|
4271 if (s == "" && m_myStatusMessage != "") {
|
Chris@378
|
4272 if (lab->text() != m_myStatusMessage) {
|
Chris@378
|
4273 lab->setText(m_myStatusMessage);
|
Chris@375
|
4274 }
|
Chris@45
|
4275 return;
|
Chris@45
|
4276 }
|
Chris@375
|
4277
|
Chris@378
|
4278 lab->setText(s);
|
Chris@45
|
4279 }
|
Chris@45
|
4280
|
Chris@45
|
4281 void
|
Chris@45
|
4282 MainWindowBase::openHelpUrl(QString url)
|
Chris@45
|
4283 {
|
Chris@45
|
4284 // This method mostly lifted from Qt Assistant source code
|
Chris@45
|
4285
|
Chris@45
|
4286 QProcess *process = new QProcess(this);
|
Chris@45
|
4287 connect(process, SIGNAL(finished(int)), process, SLOT(deleteLater()));
|
Chris@45
|
4288
|
Chris@45
|
4289 QStringList args;
|
Chris@45
|
4290
|
Chris@45
|
4291 #ifdef Q_OS_MAC
|
Chris@45
|
4292 args.append(url);
|
Chris@45
|
4293 process->start("open", args);
|
Chris@45
|
4294 #else
|
Chris@45
|
4295 #ifdef Q_OS_WIN32
|
Chris@599
|
4296 std::string pfiles;
|
Chris@599
|
4297 (void)getEnvUtf8("ProgramFiles", pfiles);
|
Chris@599
|
4298 QString command =
|
Chris@599
|
4299 QString::fromStdString(pfiles) +
|
Chris@599
|
4300 QString("\\Internet Explorer\\IEXPLORE.EXE");
|
Chris@358
|
4301
|
Chris@358
|
4302 args.append(url);
|
Chris@358
|
4303 process->start(command, args);
|
Chris@45
|
4304 #else
|
Chris@45
|
4305 if (!qgetenv("KDE_FULL_SESSION").isEmpty()) {
|
Chris@45
|
4306 args.append("exec");
|
Chris@45
|
4307 args.append(url);
|
Chris@45
|
4308 process->start("kfmclient", args);
|
Chris@45
|
4309 } else if (!qgetenv("BROWSER").isEmpty()) {
|
Chris@45
|
4310 args.append(url);
|
Chris@45
|
4311 process->start(qgetenv("BROWSER"), args);
|
Chris@45
|
4312 } else {
|
Chris@45
|
4313 args.append(url);
|
Chris@45
|
4314 process->start("firefox", args);
|
Chris@45
|
4315 }
|
Chris@45
|
4316 #endif
|
Chris@45
|
4317 #endif
|
Chris@45
|
4318 }
|
Chris@45
|
4319
|
Chris@483
|
4320 void
|
Chris@483
|
4321 MainWindowBase::openLocalFolder(QString path)
|
Chris@483
|
4322 {
|
Chris@483
|
4323 QDir d(path);
|
Chris@483
|
4324 if (d.exists()) {
|
Chris@483
|
4325 QStringList args;
|
Chris@483
|
4326 QString path = d.canonicalPath();
|
Chris@483
|
4327 #if defined Q_OS_WIN32
|
Chris@483
|
4328 // Although the Win32 API is quite happy to have
|
Chris@483
|
4329 // forward slashes as directory separators, Windows
|
Chris@483
|
4330 // Explorer is not
|
Chris@483
|
4331 path = path.replace('/', '\\');
|
Chris@483
|
4332 args << path;
|
Chris@483
|
4333 QProcess::execute("c:/windows/explorer.exe", args);
|
Chris@483
|
4334 #else
|
Chris@483
|
4335 args << path;
|
Chris@605
|
4336 QProcess process;
|
Chris@605
|
4337 QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
|
Chris@606
|
4338 env.insert("LD_LIBRARY_PATH", "");
|
Chris@605
|
4339 process.setProcessEnvironment(env);
|
Chris@605
|
4340 process.start(
|
Chris@483
|
4341 #if defined Q_OS_MAC
|
Chris@483
|
4342 "/usr/bin/open",
|
Chris@483
|
4343 #else
|
Chris@483
|
4344 "/usr/bin/xdg-open",
|
Chris@483
|
4345 #endif
|
Chris@483
|
4346 args);
|
Chris@608
|
4347 process.waitForFinished();
|
Chris@483
|
4348 #endif
|
Chris@483
|
4349 }
|
Chris@483
|
4350 }
|
Chris@483
|
4351
|