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