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