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