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