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