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