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@649
|
1165 if (m_labeller->actingOnPrevEvent() && havePrevPoint) {
|
Chris@45
|
1166 command->deletePoint(prevPoint);
|
Chris@45
|
1167 }
|
Chris@649
|
1168 /*!!! to be updated after we have switched SODM to new API
|
Chris@45
|
1169 m_labeller->label<SparseOneDimensionalModel::Point>
|
Chris@636
|
1170 (point, havePrevPoint ? &prevPoint : nullptr);
|
Chris@649
|
1171 */
|
Chris@649
|
1172 if (m_labeller->actingOnPrevEvent() && 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@649
|
1285 /*!!! to be updated after SODM API update
|
Chris@537
|
1286 Command *c = labeller.labelAll<SparseOneDimensionalModel::Point>(*sodm, &ms);
|
Chris@537
|
1287 if (c) CommandHistory::getInstance()->addCommand(c, false);
|
Chris@649
|
1288 */
|
Chris@537
|
1289 }
|
Chris@537
|
1290
|
Chris@537
|
1291 void
|
Chris@537
|
1292 MainWindowBase::subdivideInstantsBy(int n)
|
Chris@537
|
1293 {
|
Chris@537
|
1294 Pane *pane = m_paneStack->getCurrentPane();
|
Chris@537
|
1295 if (!pane) return;
|
Chris@537
|
1296
|
Chris@537
|
1297 Layer *layer = dynamic_cast<TimeInstantLayer *>(pane->getSelectedLayer());
|
Chris@537
|
1298 if (!layer) return;
|
Chris@537
|
1299
|
Chris@537
|
1300 MultiSelection ms(m_viewManager->getSelection());
|
Chris@537
|
1301
|
Chris@537
|
1302 Model *model = layer->getModel();
|
Chris@537
|
1303 SparseOneDimensionalModel *sodm =
|
Chris@537
|
1304 dynamic_cast<SparseOneDimensionalModel *>(model);
|
Chris@537
|
1305 if (!sodm) return;
|
Chris@537
|
1306
|
Chris@537
|
1307 if (!m_labeller) return;
|
Chris@537
|
1308
|
Chris@537
|
1309 Labeller labeller(*m_labeller);
|
Chris@537
|
1310 labeller.setSampleRate(sodm->getSampleRate());
|
Chris@537
|
1311
|
Chris@649
|
1312 (void)n;
|
Chris@649
|
1313 /*!!! to be updated after SODM API update
|
Chris@537
|
1314 Command *c = labeller.subdivide<SparseOneDimensionalModel::Point>
|
Chris@537
|
1315 (*sodm, &ms, n);
|
Chris@537
|
1316 if (c) CommandHistory::getInstance()->addCommand(c, false);
|
Chris@649
|
1317 */
|
Chris@45
|
1318 }
|
Chris@45
|
1319
|
Chris@538
|
1320 void
|
Chris@538
|
1321 MainWindowBase::winnowInstantsBy(int n)
|
Chris@538
|
1322 {
|
Chris@538
|
1323 Pane *pane = m_paneStack->getCurrentPane();
|
Chris@538
|
1324 if (!pane) return;
|
Chris@538
|
1325
|
Chris@538
|
1326 Layer *layer = dynamic_cast<TimeInstantLayer *>(pane->getSelectedLayer());
|
Chris@538
|
1327 if (!layer) return;
|
Chris@538
|
1328
|
Chris@538
|
1329 MultiSelection ms(m_viewManager->getSelection());
|
Chris@538
|
1330
|
Chris@538
|
1331 Model *model = layer->getModel();
|
Chris@538
|
1332 SparseOneDimensionalModel *sodm =
|
Chris@538
|
1333 dynamic_cast<SparseOneDimensionalModel *>(model);
|
Chris@538
|
1334 if (!sodm) return;
|
Chris@538
|
1335
|
Chris@538
|
1336 if (!m_labeller) return;
|
Chris@538
|
1337
|
Chris@538
|
1338 Labeller labeller(*m_labeller);
|
Chris@538
|
1339 labeller.setSampleRate(sodm->getSampleRate());
|
Chris@538
|
1340
|
Chris@649
|
1341 (void)n;
|
Chris@649
|
1342 /*!!! to be updated after SODM API update
|
Chris@538
|
1343 Command *c = labeller.winnow<SparseOneDimensionalModel::Point>
|
Chris@538
|
1344 (*sodm, &ms, n);
|
Chris@538
|
1345 if (c) CommandHistory::getInstance()->addCommand(c, false);
|
Chris@649
|
1346 */
|
Chris@538
|
1347 }
|
Chris@538
|
1348
|
Chris@45
|
1349 MainWindowBase::FileOpenStatus
|
Chris@373
|
1350 MainWindowBase::openPath(QString fileOrUrl, AudioFileOpenMode mode)
|
Chris@45
|
1351 {
|
Chris@134
|
1352 ProgressDialog dialog(tr("Opening file or URL..."), true, 2000, this);
|
Chris@134
|
1353 connect(&dialog, SIGNAL(showing()), this, SIGNAL(hideSplash()));
|
Chris@109
|
1354 return open(FileSource(fileOrUrl, &dialog), mode);
|
Chris@45
|
1355 }
|
Chris@45
|
1356
|
Chris@45
|
1357 MainWindowBase::FileOpenStatus
|
Chris@45
|
1358 MainWindowBase::open(FileSource source, AudioFileOpenMode mode)
|
Chris@45
|
1359 {
|
Chris@45
|
1360 FileOpenStatus status;
|
Chris@45
|
1361
|
Chris@45
|
1362 if (!source.isAvailable()) return FileOpenFailed;
|
Chris@45
|
1363 source.waitForData();
|
Chris@45
|
1364
|
Chris@636
|
1365 bool canImportLayer = (getMainModel() != nullptr &&
|
Chris@636
|
1366 m_paneStack != nullptr &&
|
Chris@636
|
1367 m_paneStack->getCurrentPane() != nullptr);
|
Chris@45
|
1368
|
Chris@152
|
1369 bool rdf = (source.getExtension().toLower() == "rdf" ||
|
Chris@152
|
1370 source.getExtension().toLower() == "n3" ||
|
Chris@152
|
1371 source.getExtension().toLower() == "ttl");
|
Chris@152
|
1372
|
Chris@152
|
1373 bool audio = AudioFileReaderFactory::getKnownExtensions().contains
|
Chris@152
|
1374 (source.getExtension().toLower());
|
Chris@145
|
1375
|
Chris@145
|
1376 bool rdfSession = false;
|
Chris@145
|
1377 if (rdf) {
|
Chris@145
|
1378 RDFImporter::RDFDocumentType rdfType =
|
Chris@145
|
1379 RDFImporter::identifyDocumentType
|
Chris@145
|
1380 (QUrl::fromLocalFile(source.getLocalFilename()).toString());
|
Chris@145
|
1381 if (rdfType == RDFImporter::AudioRefAndAnnotations ||
|
Chris@145
|
1382 rdfType == RDFImporter::AudioRef) {
|
Chris@145
|
1383 rdfSession = true;
|
Chris@145
|
1384 } else if (rdfType == RDFImporter::NotRDF) {
|
Chris@145
|
1385 rdf = false;
|
Chris@145
|
1386 }
|
Chris@145
|
1387 }
|
Chris@145
|
1388
|
Chris@579
|
1389 try {
|
Chris@579
|
1390 if (rdf) {
|
Chris@579
|
1391 if (rdfSession) {
|
Chris@579
|
1392 bool cancel = false;
|
Chris@579
|
1393 if (!canImportLayer || shouldCreateNewSessionForRDFAudio(&cancel)) {
|
Chris@579
|
1394 return openSession(source);
|
Chris@579
|
1395 } else if (cancel) {
|
Chris@579
|
1396 return FileOpenCancelled;
|
Chris@579
|
1397 } else {
|
Chris@579
|
1398 return openLayer(source);
|
Chris@579
|
1399 }
|
Chris@145
|
1400 } else {
|
Chris@579
|
1401 if ((status = openSession(source)) != FileOpenFailed) {
|
Chris@579
|
1402 return status;
|
Chris@579
|
1403 } else if (!canImportLayer) {
|
Chris@579
|
1404 return FileOpenWrongMode;
|
Chris@579
|
1405 } else if ((status = openLayer(source)) != FileOpenFailed) {
|
Chris@579
|
1406 return status;
|
Chris@579
|
1407 } else {
|
Chris@579
|
1408 return FileOpenFailed;
|
Chris@579
|
1409 }
|
Chris@145
|
1410 }
|
Chris@145
|
1411 }
|
Chris@579
|
1412
|
Chris@579
|
1413 if (audio && (status = openAudio(source, mode)) != FileOpenFailed) {
|
Chris@579
|
1414 return status;
|
Chris@579
|
1415 } else if ((status = openSession(source)) != FileOpenFailed) {
|
Chris@579
|
1416 return status;
|
Chris@579
|
1417 } else if ((status = openPlaylist(source, mode)) != FileOpenFailed) {
|
Chris@579
|
1418 return status;
|
Chris@579
|
1419 } else if (!canImportLayer) {
|
Chris@579
|
1420 return FileOpenWrongMode;
|
Chris@579
|
1421 } else if ((status = openImage(source)) != FileOpenFailed) {
|
Chris@579
|
1422 return status;
|
Chris@579
|
1423 } else if ((status = openLayer(source)) != FileOpenFailed) {
|
Chris@579
|
1424 return status;
|
Chris@579
|
1425 } else {
|
Chris@579
|
1426 return FileOpenFailed;
|
Chris@579
|
1427 }
|
Chris@579
|
1428 } catch (const InsufficientDiscSpace &e) {
|
Chris@579
|
1429 emit hideSplash();
|
Chris@579
|
1430 m_openingAudioFile = false;
|
Chris@594
|
1431 SVCERR << "MainWindowBase: Caught InsufficientDiscSpace in file open" << endl;
|
Chris@579
|
1432 QMessageBox::critical
|
Chris@579
|
1433 (this, tr("Not enough disc space"),
|
Chris@579
|
1434 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
|
1435 return FileOpenFailed;
|
Chris@592
|
1436 } catch (const std::bad_alloc &e) { // reader may have rethrown this after cleaning up
|
Chris@592
|
1437 emit hideSplash();
|
Chris@592
|
1438 m_openingAudioFile = false;
|
Chris@594
|
1439 SVCERR << "MainWindowBase: Caught bad_alloc in file open" << endl;
|
Chris@592
|
1440 QMessageBox::critical
|
Chris@592
|
1441 (this, tr("Not enough memory"),
|
Chris@592
|
1442 tr("<b>Not enough memory</b><p>There doesn't appear to be enough memory to accommodate any necessary temporary data.</p>"));
|
Chris@592
|
1443 return FileOpenFailed;
|
Chris@45
|
1444 }
|
Chris@45
|
1445 }
|
Chris@45
|
1446
|
Chris@45
|
1447 MainWindowBase::FileOpenStatus
|
Chris@626
|
1448 MainWindowBase::openAudio(FileSource source,
|
Chris@626
|
1449 AudioFileOpenMode mode,
|
Chris@227
|
1450 QString templateName)
|
Chris@45
|
1451 {
|
Chris@386
|
1452 SVDEBUG << "MainWindowBase::openAudio(" << source.getLocation() << ") with mode " << mode << " and template " << templateName << endl;
|
Chris@45
|
1453
|
Chris@222
|
1454 if (templateName == "") {
|
Chris@231
|
1455 templateName = getDefaultSessionTemplate();
|
Chris@577
|
1456 SVDEBUG << "(Default template is: \"" << templateName << "\")" << endl;
|
Chris@222
|
1457 }
|
Chris@220
|
1458
|
Chris@374
|
1459 // cerr << "template is: \"" << templateName << "\"" << endl;
|
Chris@223
|
1460
|
Chris@413
|
1461 if (!source.isAvailable()) {
|
Chris@413
|
1462 if (source.wasCancelled()) {
|
Chris@413
|
1463 return FileOpenCancelled;
|
Chris@413
|
1464 } else {
|
Chris@413
|
1465 return FileOpenFailed;
|
Chris@413
|
1466 }
|
Chris@413
|
1467 }
|
Chris@413
|
1468
|
Chris@45
|
1469 source.waitForData();
|
Chris@45
|
1470
|
Chris@45
|
1471 m_openingAudioFile = true;
|
Chris@45
|
1472
|
Chris@435
|
1473 sv_samplerate_t rate = 0;
|
Chris@45
|
1474
|
Chris@626
|
1475 SVDEBUG << "Checking whether to preserve incoming audio file's sample rate"
|
Chris@626
|
1476 << endl;
|
Chris@626
|
1477
|
Chris@360
|
1478 if (Preferences::getInstance()->getFixedSampleRate() != 0) {
|
Chris@360
|
1479 rate = Preferences::getInstance()->getFixedSampleRate();
|
Chris@626
|
1480 SVDEBUG << "No: preferences specify fixed rate of " << rate << endl;
|
Chris@360
|
1481 } else if (Preferences::getInstance()->getResampleOnLoad()) {
|
Chris@552
|
1482 if (getMainModel()) {
|
Chris@626
|
1483 if (mode == ReplaceSession || mode == ReplaceMainModel) {
|
Chris@626
|
1484 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
|
1485 } else {
|
Chris@626
|
1486 rate = getMainModel()->getSampleRate();
|
Chris@626
|
1487 SVDEBUG << "No: preferences specify resampling to match main model, whose rate is currently " << rate << endl;
|
Chris@626
|
1488 }
|
Chris@552
|
1489 }
|
Chris@45
|
1490 }
|
Chris@45
|
1491
|
Chris@626
|
1492 if (rate == 0) {
|
Chris@626
|
1493 SVDEBUG << "Yes, preserving incoming file rate" << endl;
|
Chris@626
|
1494 }
|
Chris@626
|
1495
|
Chris@479
|
1496 ReadOnlyWaveFileModel *newModel = new ReadOnlyWaveFileModel(source, rate);
|
Chris@45
|
1497
|
Chris@45
|
1498 if (!newModel->isOK()) {
|
Chris@595
|
1499 delete newModel;
|
Chris@45
|
1500 m_openingAudioFile = false;
|
Chris@413
|
1501 if (source.wasCancelled()) {
|
Chris@413
|
1502 return FileOpenCancelled;
|
Chris@413
|
1503 } else {
|
Chris@413
|
1504 return FileOpenFailed;
|
Chris@413
|
1505 }
|
Chris@45
|
1506 }
|
Chris@45
|
1507
|
Chris@604
|
1508 return addOpenedAudioModel(source, newModel, mode, templateName, true);
|
Chris@604
|
1509 }
|
Chris@604
|
1510
|
Chris@604
|
1511 MainWindowBase::FileOpenStatus
|
Chris@604
|
1512 MainWindowBase::addOpenedAudioModel(FileSource source,
|
Chris@604
|
1513 WaveFileModel *newModel,
|
Chris@604
|
1514 AudioFileOpenMode mode,
|
Chris@604
|
1515 QString templateName,
|
Chris@604
|
1516 bool registerSource)
|
Chris@604
|
1517 {
|
Chris@45
|
1518 if (mode == AskUser) {
|
Chris@45
|
1519 if (getMainModel()) {
|
Chris@45
|
1520
|
Chris@147
|
1521 QSettings settings;
|
Chris@147
|
1522 settings.beginGroup("MainWindow");
|
Chris@221
|
1523 int lastMode = settings.value("lastaudioopenmode", 0).toBool();
|
Chris@147
|
1524 settings.endGroup();
|
Chris@221
|
1525 int imode = 0;
|
Chris@45
|
1526
|
Chris@45
|
1527 QStringList items;
|
Chris@221
|
1528 items << tr("Close the current session and start a new one")
|
Chris@221
|
1529 << tr("Replace the main audio file in this session")
|
Chris@221
|
1530 << tr("Add the audio file to this session");
|
Chris@45
|
1531
|
Chris@45
|
1532 bool ok = false;
|
Chris@45
|
1533 QString item = ListInputDialog::getItem
|
Chris@45
|
1534 (this, tr("Select target for import"),
|
Chris@221
|
1535 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
|
1536 items, lastMode, &ok);
|
Chris@45
|
1537
|
Chris@45
|
1538 if (!ok || item.isEmpty()) {
|
Chris@45
|
1539 delete newModel;
|
Chris@45
|
1540 m_openingAudioFile = false;
|
Chris@45
|
1541 return FileOpenCancelled;
|
Chris@45
|
1542 }
|
Chris@45
|
1543
|
Chris@221
|
1544 for (int i = 0; i < items.size(); ++i) {
|
Chris@221
|
1545 if (item == items[i]) imode = i;
|
Chris@221
|
1546 }
|
Chris@221
|
1547
|
Chris@147
|
1548 settings.beginGroup("MainWindow");
|
Chris@221
|
1549 settings.setValue("lastaudioopenmode", imode);
|
Chris@147
|
1550 settings.endGroup();
|
Chris@45
|
1551
|
Chris@221
|
1552 mode = (AudioFileOpenMode)imode;
|
Chris@45
|
1553
|
Chris@45
|
1554 } else {
|
Chris@221
|
1555 // no main model: make a new session
|
Chris@221
|
1556 mode = ReplaceSession;
|
Chris@45
|
1557 }
|
Chris@45
|
1558 }
|
Chris@45
|
1559
|
Chris@45
|
1560 if (mode == ReplaceCurrentPane) {
|
Chris@45
|
1561
|
Chris@45
|
1562 Pane *pane = m_paneStack->getCurrentPane();
|
Chris@45
|
1563 if (pane) {
|
Chris@45
|
1564 if (getMainModel()) {
|
Chris@45
|
1565 View::ModelSet models(pane->getModels());
|
Chris@45
|
1566 if (models.find(getMainModel()) != models.end()) {
|
Chris@221
|
1567 // Current pane contains main model: replace that
|
Chris@45
|
1568 mode = ReplaceMainModel;
|
Chris@45
|
1569 }
|
Chris@221
|
1570 // Otherwise the current pane has a non-default model,
|
Chris@221
|
1571 // which we will deal with later
|
Chris@45
|
1572 } else {
|
Chris@221
|
1573 // We have no main model, so start a new session with
|
Chris@221
|
1574 // optional template
|
Chris@221
|
1575 mode = ReplaceSession;
|
Chris@45
|
1576 }
|
Chris@45
|
1577 } else {
|
Chris@221
|
1578 // We seem to have no current pane! Oh well
|
Chris@45
|
1579 mode = CreateAdditionalModel;
|
Chris@45
|
1580 }
|
Chris@45
|
1581 }
|
Chris@45
|
1582
|
Chris@45
|
1583 if (mode == CreateAdditionalModel && !getMainModel()) {
|
Chris@386
|
1584 SVDEBUG << "Mode is CreateAdditionalModel but we have no main model, switching to ReplaceSession mode" << endl;
|
Chris@221
|
1585 mode = ReplaceSession;
|
Chris@221
|
1586 }
|
Chris@221
|
1587
|
Chris@221
|
1588 bool loadedTemplate = false;
|
Chris@221
|
1589
|
Chris@221
|
1590 if (mode == ReplaceSession) {
|
Chris@258
|
1591
|
Chris@258
|
1592 if (!checkSaveModified()) return FileOpenCancelled;
|
Chris@258
|
1593
|
Chris@386
|
1594 SVDEBUG << "SV looking for template " << templateName << endl;
|
Chris@230
|
1595 if (templateName != "") {
|
Chris@230
|
1596 FileOpenStatus tplStatus = openSessionTemplate(templateName);
|
Chris@258
|
1597 if (tplStatus == FileOpenCancelled) {
|
Chris@577
|
1598 SVDEBUG << "Template load cancelled" << endl;
|
Chris@258
|
1599 return FileOpenCancelled;
|
Chris@258
|
1600 }
|
Chris@230
|
1601 if (tplStatus != FileOpenFailed) {
|
Chris@577
|
1602 SVDEBUG << "Template load succeeded" << endl;
|
Chris@230
|
1603 loadedTemplate = true;
|
Chris@221
|
1604 }
|
Chris@221
|
1605 }
|
Chris@221
|
1606
|
Chris@221
|
1607 if (!loadedTemplate) {
|
Chris@386
|
1608 SVDEBUG << "No template found: closing session, creating new empty document" << endl;
|
Chris@221
|
1609 closeSession();
|
Chris@221
|
1610 createDocument();
|
Chris@221
|
1611 }
|
Chris@221
|
1612
|
Chris@386
|
1613 SVDEBUG << "Now switching to ReplaceMainModel mode" << endl;
|
Chris@45
|
1614 mode = ReplaceMainModel;
|
Chris@45
|
1615 }
|
Chris@45
|
1616
|
Chris@164
|
1617 emit activity(tr("Import audio file \"%1\"").arg(source.getLocation()));
|
Chris@164
|
1618
|
Chris@45
|
1619 if (mode == ReplaceMainModel) {
|
Chris@45
|
1620
|
Chris@45
|
1621 Model *prevMain = getMainModel();
|
Chris@45
|
1622 if (prevMain) {
|
Chris@45
|
1623 m_playSource->removeModel(prevMain);
|
Chris@108
|
1624 PlayParameterRepository::getInstance()->removePlayable(prevMain);
|
Chris@45
|
1625 }
|
Chris@108
|
1626 PlayParameterRepository::getInstance()->addPlayable(newModel);
|
Chris@45
|
1627
|
Chris@248
|
1628 SVDEBUG << "SV about to call setMainModel(" << newModel << "): prevMain is " << prevMain << endl;
|
Chris@248
|
1629
|
Chris@595
|
1630 m_document->setMainModel(newModel);
|
Chris@595
|
1631
|
Chris@595
|
1632 setupMenus();
|
Chris@595
|
1633
|
Chris@595
|
1634 if (loadedTemplate || (m_sessionFile == "")) {
|
Chris@45
|
1635 //!!! shouldn't be dealing directly with title from here -- call a method
|
Chris@595
|
1636 setWindowTitle(tr("%1: %2")
|
Chris@57
|
1637 .arg(QApplication::applicationName())
|
Chris@45
|
1638 .arg(source.getLocation()));
|
Chris@595
|
1639 CommandHistory::getInstance()->clear();
|
Chris@595
|
1640 CommandHistory::getInstance()->documentSaved();
|
Chris@595
|
1641 m_documentModified = false;
|
Chris@595
|
1642 } else {
|
Chris@595
|
1643 setWindowTitle(tr("%1: %2 [%3]")
|
Chris@57
|
1644 .arg(QApplication::applicationName())
|
Chris@595
|
1645 .arg(QFileInfo(m_sessionFile).fileName())
|
Chris@595
|
1646 .arg(source.getLocation()));
|
Chris@595
|
1647 if (m_documentModified) {
|
Chris@595
|
1648 m_documentModified = false;
|
Chris@595
|
1649 documentModified(); // so as to restore "(modified)" window title
|
Chris@595
|
1650 }
|
Chris@595
|
1651 }
|
Chris@45
|
1652
|
Chris@604
|
1653 if (!source.isRemote() && registerSource) {
|
Chris@604
|
1654 m_audioFile = source.getLocalFilename();
|
Chris@604
|
1655 }
|
Chris@45
|
1656
|
Chris@45
|
1657 } else if (mode == CreateAdditionalModel) {
|
Chris@45
|
1658
|
Chris@577
|
1659 SVCERR << "Mode is CreateAdditionalModel" << endl;
|
Chris@577
|
1660
|
Chris@595
|
1661 CommandHistory::getInstance()->startCompoundOperation
|
Chris@595
|
1662 (tr("Import \"%1\"").arg(source.getBasename()), true);
|
Chris@595
|
1663
|
Chris@595
|
1664 m_document->addImportedModel(newModel);
|
Chris@595
|
1665
|
Chris@595
|
1666 AddPaneCommand *command = new AddPaneCommand(this);
|
Chris@595
|
1667 CommandHistory::getInstance()->addCommand(command);
|
Chris@595
|
1668
|
Chris@595
|
1669 Pane *pane = command->getPane();
|
Chris@45
|
1670
|
Chris@47
|
1671 if (m_timeRulerLayer) {
|
Chris@577
|
1672 SVCERR << "Have time ruler, adding it" << endl;
|
Chris@47
|
1673 m_document->addLayerToView(pane, m_timeRulerLayer);
|
Chris@577
|
1674 } else {
|
Chris@577
|
1675 SVCERR << "Do not have time ruler" << endl;
|
Chris@47
|
1676 }
|
Chris@45
|
1677
|
Chris@595
|
1678 Layer *newLayer = m_document->createImportedLayer(newModel);
|
Chris@595
|
1679
|
Chris@595
|
1680 if (newLayer) {
|
Chris@595
|
1681 m_document->addLayerToView(pane, newLayer);
|
Chris@595
|
1682 }
|
Chris@595
|
1683
|
Chris@595
|
1684 CommandHistory::getInstance()->endCompoundOperation();
|
Chris@45
|
1685
|
Chris@45
|
1686 } else if (mode == ReplaceCurrentPane) {
|
Chris@45
|
1687
|
Chris@45
|
1688 // We know there is a current pane, otherwise we would have
|
Chris@45
|
1689 // reset the mode to CreateAdditionalModel above; and we know
|
Chris@45
|
1690 // the current pane does not contain the main model, otherwise
|
Chris@45
|
1691 // we would have reset it to ReplaceMainModel. But we don't
|
Chris@45
|
1692 // know whether the pane contains a waveform model at all.
|
Chris@45
|
1693
|
Chris@45
|
1694 Pane *pane = m_paneStack->getCurrentPane();
|
Chris@636
|
1695 Layer *replace = nullptr;
|
Chris@45
|
1696
|
Chris@45
|
1697 for (int i = 0; i < pane->getLayerCount(); ++i) {
|
Chris@45
|
1698 Layer *layer = pane->getLayer(i);
|
Chris@45
|
1699 if (dynamic_cast<WaveformLayer *>(layer)) {
|
Chris@45
|
1700 replace = layer;
|
Chris@45
|
1701 break;
|
Chris@45
|
1702 }
|
Chris@45
|
1703 }
|
Chris@45
|
1704
|
Chris@595
|
1705 CommandHistory::getInstance()->startCompoundOperation
|
Chris@595
|
1706 (tr("Import \"%1\"").arg(source.getBasename()), true);
|
Chris@595
|
1707
|
Chris@595
|
1708 m_document->addImportedModel(newModel);
|
Chris@45
|
1709
|
Chris@45
|
1710 if (replace) {
|
Chris@45
|
1711 m_document->removeLayerFromView(pane, replace);
|
Chris@45
|
1712 }
|
Chris@45
|
1713
|
Chris@595
|
1714 Layer *newLayer = m_document->createImportedLayer(newModel);
|
Chris@595
|
1715
|
Chris@595
|
1716 if (newLayer) {
|
Chris@595
|
1717 m_document->addLayerToView(pane, newLayer);
|
Chris@595
|
1718 }
|
Chris@595
|
1719
|
Chris@595
|
1720 CommandHistory::getInstance()->endCompoundOperation();
|
Chris@45
|
1721 }
|
Chris@45
|
1722
|
Chris@45
|
1723 updateMenuStates();
|
Chris@622
|
1724
|
Chris@622
|
1725 if (registerSource) {
|
Chris@622
|
1726 m_recentFiles.addFile(source.getLocation());
|
Chris@622
|
1727 }
|
Chris@604
|
1728 if (!source.isRemote() && registerSource) {
|
Chris@45
|
1729 // for file dialog
|
Chris@45
|
1730 registerLastOpenedFilePath(FileFinder::AudioFile,
|
Chris@45
|
1731 source.getLocalFilename());
|
Chris@45
|
1732 }
|
Chris@622
|
1733
|
Chris@45
|
1734 m_openingAudioFile = false;
|
Chris@45
|
1735
|
Chris@45
|
1736 currentPaneChanged(m_paneStack->getCurrentPane());
|
Chris@45
|
1737
|
Chris@342
|
1738 emit audioFileLoaded();
|
Chris@342
|
1739
|
Chris@45
|
1740 return FileOpenSucceeded;
|
Chris@45
|
1741 }
|
Chris@45
|
1742
|
Chris@45
|
1743 MainWindowBase::FileOpenStatus
|
Chris@45
|
1744 MainWindowBase::openPlaylist(FileSource source, AudioFileOpenMode mode)
|
Chris@45
|
1745 {
|
Chris@233
|
1746 SVDEBUG << "MainWindowBase::openPlaylist(" << source.getLocation() << ")" << endl;
|
Chris@135
|
1747
|
Chris@45
|
1748 std::set<QString> extensions;
|
Chris@45
|
1749 PlaylistFileReader::getSupportedExtensions(extensions);
|
Chris@152
|
1750 QString extension = source.getExtension().toLower();
|
Chris@45
|
1751 if (extensions.find(extension) == extensions.end()) return FileOpenFailed;
|
Chris@45
|
1752
|
Chris@45
|
1753 if (!source.isAvailable()) return FileOpenFailed;
|
Chris@45
|
1754 source.waitForData();
|
Chris@45
|
1755
|
Chris@45
|
1756 PlaylistFileReader reader(source.getLocalFilename());
|
Chris@45
|
1757 if (!reader.isOK()) return FileOpenFailed;
|
Chris@45
|
1758
|
Chris@45
|
1759 PlaylistFileReader::Playlist playlist = reader.load();
|
Chris@45
|
1760
|
Chris@45
|
1761 bool someSuccess = false;
|
Chris@45
|
1762
|
Chris@45
|
1763 for (PlaylistFileReader::Playlist::const_iterator i = playlist.begin();
|
Chris@45
|
1764 i != playlist.end(); ++i) {
|
Chris@45
|
1765
|
Chris@134
|
1766 ProgressDialog dialog(tr("Opening playlist..."), true, 2000, this);
|
Chris@134
|
1767 connect(&dialog, SIGNAL(showing()), this, SIGNAL(hideSplash()));
|
Chris@109
|
1768 FileOpenStatus status = openAudio(FileSource(*i, &dialog), mode);
|
Chris@45
|
1769
|
Chris@45
|
1770 if (status == FileOpenCancelled) {
|
Chris@45
|
1771 return FileOpenCancelled;
|
Chris@45
|
1772 }
|
Chris@45
|
1773
|
Chris@45
|
1774 if (status == FileOpenSucceeded) {
|
Chris@45
|
1775 someSuccess = true;
|
Chris@45
|
1776 mode = CreateAdditionalModel;
|
Chris@45
|
1777 }
|
Chris@45
|
1778 }
|
Chris@45
|
1779
|
Chris@45
|
1780 if (someSuccess) return FileOpenSucceeded;
|
Chris@45
|
1781 else return FileOpenFailed;
|
Chris@45
|
1782 }
|
Chris@45
|
1783
|
Chris@45
|
1784 MainWindowBase::FileOpenStatus
|
Chris@45
|
1785 MainWindowBase::openLayer(FileSource source)
|
Chris@45
|
1786 {
|
Chris@233
|
1787 SVDEBUG << "MainWindowBase::openLayer(" << source.getLocation() << ")" << endl;
|
Chris@135
|
1788
|
Chris@45
|
1789 Pane *pane = m_paneStack->getCurrentPane();
|
Chris@45
|
1790
|
Chris@45
|
1791 if (!pane) {
|
Chris@595
|
1792 // shouldn't happen, as the menu action should have been disabled
|
Chris@595
|
1793 cerr << "WARNING: MainWindowBase::openLayer: no current pane" << endl;
|
Chris@595
|
1794 return FileOpenWrongMode;
|
Chris@45
|
1795 }
|
Chris@45
|
1796
|
Chris@45
|
1797 if (!getMainModel()) {
|
Chris@595
|
1798 // shouldn't happen, as the menu action should have been disabled
|
Chris@595
|
1799 cerr << "WARNING: MainWindowBase::openLayer: No main model -- hence no default sample rate available" << endl;
|
Chris@595
|
1800 return FileOpenWrongMode;
|
Chris@45
|
1801 }
|
Chris@45
|
1802
|
Chris@45
|
1803 if (!source.isAvailable()) return FileOpenFailed;
|
Chris@45
|
1804 source.waitForData();
|
Chris@45
|
1805
|
Chris@45
|
1806 QString path = source.getLocalFilename();
|
Chris@45
|
1807
|
Chris@145
|
1808 RDFImporter::RDFDocumentType rdfType =
|
Chris@145
|
1809 RDFImporter::identifyDocumentType(QUrl::fromLocalFile(path).toString());
|
Chris@145
|
1810
|
Chris@293
|
1811 // cerr << "RDF type: (in layer) " << (int) rdfType << endl;
|
Chris@148
|
1812
|
Chris@145
|
1813 if (rdfType != RDFImporter::NotRDF) {
|
Chris@145
|
1814
|
Chris@145
|
1815 return openLayersFromRDF(source);
|
Chris@134
|
1816
|
Chris@152
|
1817 } else if (source.getExtension().toLower() == "svl" ||
|
Chris@152
|
1818 (source.getExtension().toLower() == "xml" &&
|
Chris@140
|
1819 (SVFileReader::identifyXmlFile(source.getLocalFilename())
|
Chris@140
|
1820 == SVFileReader::SVLayerFile))) {
|
Chris@45
|
1821
|
Chris@45
|
1822 PaneCallback callback(this);
|
Chris@45
|
1823 QFile file(path);
|
Chris@45
|
1824
|
Chris@45
|
1825 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
Chris@293
|
1826 cerr << "ERROR: MainWindowBase::openLayer("
|
Chris@294
|
1827 << source.getLocation()
|
Chris@293
|
1828 << "): Failed to open file for reading" << endl;
|
Chris@45
|
1829 return FileOpenFailed;
|
Chris@45
|
1830 }
|
Chris@45
|
1831
|
Chris@45
|
1832 SVFileReader reader(m_document, callback, source.getLocation());
|
Chris@79
|
1833 connect
|
Chris@79
|
1834 (&reader, SIGNAL(modelRegenerationFailed(QString, QString, QString)),
|
Chris@79
|
1835 this, SLOT(modelRegenerationFailed(QString, QString, QString)));
|
Chris@79
|
1836 connect
|
Chris@79
|
1837 (&reader, SIGNAL(modelRegenerationWarning(QString, QString, QString)),
|
Chris@79
|
1838 this, SLOT(modelRegenerationWarning(QString, QString, QString)));
|
Chris@45
|
1839 reader.setCurrentPane(pane);
|
Chris@45
|
1840
|
Chris@45
|
1841 QXmlInputSource inputSource(&file);
|
Chris@45
|
1842 reader.parse(inputSource);
|
Chris@45
|
1843
|
Chris@45
|
1844 if (!reader.isOK()) {
|
Chris@293
|
1845 cerr << "ERROR: MainWindowBase::openLayer("
|
Chris@294
|
1846 << source.getLocation()
|
Chris@45
|
1847 << "): Failed to read XML file: "
|
Chris@293
|
1848 << reader.getErrorString() << endl;
|
Chris@45
|
1849 return FileOpenFailed;
|
Chris@45
|
1850 }
|
Chris@45
|
1851
|
Chris@164
|
1852 emit activity(tr("Import layer XML file \"%1\"").arg(source.getLocation()));
|
Chris@164
|
1853
|
Chris@45
|
1854 m_recentFiles.addFile(source.getLocation());
|
Chris@45
|
1855
|
Chris@45
|
1856 if (!source.isRemote()) {
|
Chris@45
|
1857 registerLastOpenedFilePath(FileFinder::LayerFile, path); // for file dialog
|
Chris@45
|
1858 }
|
Chris@45
|
1859
|
Chris@75
|
1860 return FileOpenSucceeded;
|
Chris@75
|
1861
|
Chris@45
|
1862 } else {
|
Chris@45
|
1863
|
Chris@45
|
1864 try {
|
Chris@45
|
1865
|
Chris@109
|
1866 MIDIFileImportDialog midiDlg(this);
|
Chris@109
|
1867
|
Chris@109
|
1868 Model *model = DataFileReaderFactory::loadNonCSV
|
Chris@109
|
1869 (path, &midiDlg, getMainModel()->getSampleRate());
|
Chris@45
|
1870
|
Chris@109
|
1871 if (!model) {
|
Chris@643
|
1872 CSVFormatDialog *dialog =
|
Chris@643
|
1873 new CSVFormatDialog(this,
|
Chris@643
|
1874 path,
|
Chris@643
|
1875 getMainModel()->getSampleRate(),
|
Chris@643
|
1876 5);
|
Chris@109
|
1877 if (dialog->exec() == QDialog::Accepted) {
|
Chris@109
|
1878 model = DataFileReaderFactory::loadCSV
|
Chris@109
|
1879 (path, dialog->getFormat(),
|
Chris@109
|
1880 getMainModel()->getSampleRate());
|
Chris@109
|
1881 }
|
Chris@619
|
1882 delete dialog;
|
Chris@109
|
1883 }
|
Chris@109
|
1884
|
Chris@45
|
1885 if (model) {
|
Chris@45
|
1886
|
Chris@233
|
1887 SVDEBUG << "MainWindowBase::openLayer: Have model" << endl;
|
Chris@45
|
1888
|
Chris@164
|
1889 emit activity(tr("Import MIDI file \"%1\"").arg(source.getLocation()));
|
Chris@164
|
1890
|
Chris@45
|
1891 Layer *newLayer = m_document->createImportedLayer(model);
|
Chris@45
|
1892
|
Chris@45
|
1893 if (newLayer) {
|
Chris@45
|
1894
|
Chris@45
|
1895 m_document->addLayerToView(pane, newLayer);
|
Chris@88
|
1896 m_paneStack->setCurrentLayer(pane, newLayer);
|
Chris@88
|
1897
|
Chris@45
|
1898 m_recentFiles.addFile(source.getLocation());
|
Chris@45
|
1899
|
Chris@45
|
1900 if (!source.isRemote()) {
|
Chris@45
|
1901 registerLastOpenedFilePath
|
Chris@45
|
1902 (FileFinder::LayerFile,
|
Chris@45
|
1903 path); // for file dialog
|
Chris@45
|
1904 }
|
Chris@88
|
1905
|
Chris@45
|
1906 return FileOpenSucceeded;
|
Chris@45
|
1907 }
|
Chris@45
|
1908 }
|
Chris@45
|
1909 } catch (DataFileReaderFactory::Exception e) {
|
Chris@45
|
1910 if (e == DataFileReaderFactory::ImportCancelled) {
|
Chris@45
|
1911 return FileOpenCancelled;
|
Chris@45
|
1912 }
|
Chris@45
|
1913 }
|
Chris@45
|
1914 }
|
Chris@45
|
1915
|
Chris@45
|
1916 return FileOpenFailed;
|
Chris@45
|
1917 }
|
Chris@45
|
1918
|
Chris@45
|
1919 MainWindowBase::FileOpenStatus
|
Chris@45
|
1920 MainWindowBase::openImage(FileSource source)
|
Chris@45
|
1921 {
|
Chris@233
|
1922 SVDEBUG << "MainWindowBase::openImage(" << source.getLocation() << ")" << endl;
|
Chris@135
|
1923
|
Chris@45
|
1924 Pane *pane = m_paneStack->getCurrentPane();
|
Chris@45
|
1925
|
Chris@45
|
1926 if (!pane) {
|
Chris@595
|
1927 // shouldn't happen, as the menu action should have been disabled
|
Chris@595
|
1928 cerr << "WARNING: MainWindowBase::openImage: no current pane" << endl;
|
Chris@595
|
1929 return FileOpenWrongMode;
|
Chris@45
|
1930 }
|
Chris@45
|
1931
|
Chris@45
|
1932 if (!m_document->getMainModel()) {
|
Chris@45
|
1933 return FileOpenWrongMode;
|
Chris@45
|
1934 }
|
Chris@45
|
1935
|
Chris@45
|
1936 bool newLayer = false;
|
Chris@45
|
1937
|
Chris@45
|
1938 ImageLayer *il = dynamic_cast<ImageLayer *>(pane->getSelectedLayer());
|
Chris@45
|
1939 if (!il) {
|
Chris@45
|
1940 for (int i = pane->getLayerCount()-1; i >= 0; --i) {
|
Chris@45
|
1941 il = dynamic_cast<ImageLayer *>(pane->getLayer(i));
|
Chris@45
|
1942 if (il) break;
|
Chris@45
|
1943 }
|
Chris@45
|
1944 }
|
Chris@45
|
1945 if (!il) {
|
Chris@45
|
1946 il = dynamic_cast<ImageLayer *>
|
Chris@45
|
1947 (m_document->createEmptyLayer(LayerFactory::Image));
|
Chris@45
|
1948 if (!il) return FileOpenFailed;
|
Chris@45
|
1949 newLayer = true;
|
Chris@45
|
1950 }
|
Chris@45
|
1951
|
Chris@45
|
1952 // We don't put the image file in Recent Files
|
Chris@45
|
1953
|
Chris@293
|
1954 cerr << "openImage: trying location \"" << source.getLocation() << "\" in image layer" << endl;
|
Chris@45
|
1955
|
Chris@45
|
1956 if (!il->addImage(m_viewManager->getGlobalCentreFrame(), source.getLocation())) {
|
Chris@45
|
1957 if (newLayer) {
|
Chris@52
|
1958 m_document->deleteLayer(il); // also releases its model
|
Chris@45
|
1959 }
|
Chris@45
|
1960 return FileOpenFailed;
|
Chris@45
|
1961 } else {
|
Chris@45
|
1962 if (newLayer) {
|
Chris@45
|
1963 m_document->addLayerToView(pane, il);
|
Chris@45
|
1964 }
|
Chris@45
|
1965 m_paneStack->setCurrentLayer(pane, il);
|
Chris@45
|
1966 }
|
Chris@45
|
1967
|
Chris@45
|
1968 return FileOpenSucceeded;
|
Chris@45
|
1969 }
|
Chris@45
|
1970
|
Chris@45
|
1971 MainWindowBase::FileOpenStatus
|
Chris@427
|
1972 MainWindowBase::openDirOfAudio(QString dirPath)
|
Chris@427
|
1973 {
|
Chris@427
|
1974 QDir dir(dirPath);
|
Chris@427
|
1975 QStringList files = dir.entryList(QDir::Files | QDir::Readable);
|
Chris@427
|
1976 files.sort();
|
Chris@427
|
1977
|
Chris@427
|
1978 FileOpenStatus status = FileOpenFailed;
|
Chris@427
|
1979 bool first = true;
|
Chris@427
|
1980 bool cancelled = false;
|
Chris@427
|
1981
|
Chris@427
|
1982 foreach (QString file, files) {
|
Chris@427
|
1983
|
Chris@427
|
1984 FileSource source(dir.filePath(file));
|
Chris@427
|
1985 if (!source.isAvailable()) {
|
Chris@427
|
1986 continue;
|
Chris@427
|
1987 }
|
Chris@427
|
1988
|
Chris@427
|
1989 if (AudioFileReaderFactory::getKnownExtensions().contains
|
Chris@427
|
1990 (source.getExtension().toLower())) {
|
Chris@427
|
1991
|
Chris@427
|
1992 AudioFileOpenMode mode = CreateAdditionalModel;
|
Chris@427
|
1993 if (first) mode = ReplaceSession;
|
Chris@427
|
1994
|
Chris@427
|
1995 switch (openAudio(source, mode)) {
|
Chris@427
|
1996 case FileOpenSucceeded:
|
Chris@427
|
1997 status = FileOpenSucceeded;
|
Chris@427
|
1998 first = false;
|
Chris@427
|
1999 break;
|
Chris@427
|
2000 case FileOpenFailed:
|
Chris@427
|
2001 break;
|
Chris@427
|
2002 case FileOpenCancelled:
|
Chris@427
|
2003 cancelled = true;
|
Chris@427
|
2004 break;
|
Chris@427
|
2005 case FileOpenWrongMode:
|
Chris@427
|
2006 break;
|
Chris@427
|
2007 }
|
Chris@427
|
2008 }
|
Chris@427
|
2009
|
Chris@427
|
2010 if (cancelled) break;
|
Chris@427
|
2011 }
|
Chris@427
|
2012
|
Chris@427
|
2013 return status;
|
Chris@427
|
2014 }
|
Chris@427
|
2015
|
Chris@427
|
2016 MainWindowBase::FileOpenStatus
|
Chris@373
|
2017 MainWindowBase::openSessionPath(QString fileOrUrl)
|
Chris@45
|
2018 {
|
Chris@134
|
2019 ProgressDialog dialog(tr("Opening session..."), true, 2000, this);
|
Chris@134
|
2020 connect(&dialog, SIGNAL(showing()), this, SIGNAL(hideSplash()));
|
Chris@109
|
2021 return openSession(FileSource(fileOrUrl, &dialog));
|
Chris@45
|
2022 }
|
Chris@45
|
2023
|
Chris@45
|
2024 MainWindowBase::FileOpenStatus
|
Chris@45
|
2025 MainWindowBase::openSession(FileSource source)
|
Chris@45
|
2026 {
|
Chris@233
|
2027 SVDEBUG << "MainWindowBase::openSession(" << source.getLocation() << ")" << endl;
|
Chris@135
|
2028
|
Chris@45
|
2029 if (!source.isAvailable()) return FileOpenFailed;
|
Chris@145
|
2030 source.waitForData();
|
Chris@141
|
2031
|
Chris@341
|
2032 QString sessionExt =
|
Chris@341
|
2033 InteractiveFileFinder::getInstance()->getApplicationSessionExtension();
|
Chris@341
|
2034
|
Chris@341
|
2035 if (source.getExtension().toLower() != sessionExt) {
|
Chris@145
|
2036
|
Chris@145
|
2037 RDFImporter::RDFDocumentType rdfType =
|
Chris@145
|
2038 RDFImporter::identifyDocumentType
|
Chris@145
|
2039 (QUrl::fromLocalFile(source.getLocalFilename()).toString());
|
Chris@145
|
2040
|
Chris@293
|
2041 // cerr << "RDF type: " << (int)rdfType << endl;
|
Chris@148
|
2042
|
Chris@145
|
2043 if (rdfType == RDFImporter::AudioRefAndAnnotations ||
|
Chris@145
|
2044 rdfType == RDFImporter::AudioRef) {
|
Chris@145
|
2045 return openSessionFromRDF(source);
|
Chris@145
|
2046 } else if (rdfType != RDFImporter::NotRDF) {
|
Chris@145
|
2047 return FileOpenFailed;
|
Chris@145
|
2048 }
|
Chris@145
|
2049
|
Chris@152
|
2050 if (source.getExtension().toLower() == "xml") {
|
Chris@140
|
2051 if (SVFileReader::identifyXmlFile(source.getLocalFilename()) ==
|
Chris@140
|
2052 SVFileReader::SVSessionFile) {
|
Chris@293
|
2053 cerr << "This XML file looks like a session file, attempting to open it as a session" << endl;
|
Chris@140
|
2054 } else {
|
Chris@140
|
2055 return FileOpenFailed;
|
Chris@140
|
2056 }
|
Chris@140
|
2057 } else {
|
Chris@140
|
2058 return FileOpenFailed;
|
Chris@140
|
2059 }
|
Chris@140
|
2060 }
|
Chris@45
|
2061
|
Chris@636
|
2062 QXmlInputSource *inputSource = nullptr;
|
Chris@636
|
2063 BZipFileDevice *bzFile = nullptr;
|
Chris@636
|
2064 QFile *rawFile = nullptr;
|
Chris@140
|
2065
|
Chris@341
|
2066 if (source.getExtension().toLower() == sessionExt) {
|
Chris@140
|
2067 bzFile = new BZipFileDevice(source.getLocalFilename());
|
Chris@140
|
2068 if (!bzFile->open(QIODevice::ReadOnly)) {
|
Chris@140
|
2069 delete bzFile;
|
Chris@140
|
2070 return FileOpenFailed;
|
Chris@140
|
2071 }
|
Chris@140
|
2072 inputSource = new QXmlInputSource(bzFile);
|
Chris@140
|
2073 } else {
|
Chris@140
|
2074 rawFile = new QFile(source.getLocalFilename());
|
Chris@140
|
2075 inputSource = new QXmlInputSource(rawFile);
|
Chris@140
|
2076 }
|
Chris@140
|
2077
|
Chris@140
|
2078 if (!checkSaveModified()) {
|
Chris@140
|
2079 if (bzFile) bzFile->close();
|
Chris@140
|
2080 delete inputSource;
|
Chris@140
|
2081 delete bzFile;
|
Chris@140
|
2082 delete rawFile;
|
Chris@140
|
2083 return FileOpenCancelled;
|
Chris@140
|
2084 }
|
Chris@45
|
2085
|
Chris@45
|
2086 QString error;
|
Chris@45
|
2087 closeSession();
|
Chris@45
|
2088 createDocument();
|
Chris@45
|
2089
|
Chris@45
|
2090 PaneCallback callback(this);
|
Chris@45
|
2091 m_viewManager->clearSelections();
|
Chris@45
|
2092
|
Chris@45
|
2093 SVFileReader reader(m_document, callback, source.getLocation());
|
Chris@79
|
2094 connect
|
Chris@79
|
2095 (&reader, SIGNAL(modelRegenerationFailed(QString, QString, QString)),
|
Chris@79
|
2096 this, SLOT(modelRegenerationFailed(QString, QString, QString)));
|
Chris@79
|
2097 connect
|
Chris@79
|
2098 (&reader, SIGNAL(modelRegenerationWarning(QString, QString, QString)),
|
Chris@79
|
2099 this, SLOT(modelRegenerationWarning(QString, QString, QString)));
|
Chris@140
|
2100
|
Chris@140
|
2101 reader.parse(*inputSource);
|
Chris@45
|
2102
|
Chris@45
|
2103 if (!reader.isOK()) {
|
Chris@45
|
2104 error = tr("SV XML file read error:\n%1").arg(reader.getErrorString());
|
Chris@45
|
2105 }
|
Chris@45
|
2106
|
Chris@140
|
2107 if (bzFile) bzFile->close();
|
Chris@140
|
2108
|
Chris@140
|
2109 delete inputSource;
|
Chris@140
|
2110 delete bzFile;
|
Chris@140
|
2111 delete rawFile;
|
Chris@45
|
2112
|
Chris@45
|
2113 bool ok = (error == "");
|
Chris@45
|
2114
|
Chris@45
|
2115 if (ok) {
|
Chris@45
|
2116
|
Chris@164
|
2117 emit activity(tr("Import session file \"%1\"").arg(source.getLocation()));
|
Chris@164
|
2118
|
Chris@595
|
2119 setWindowTitle(tr("%1: %2")
|
Chris@57
|
2120 .arg(QApplication::applicationName())
|
Chris@595
|
2121 .arg(source.getLocation()));
|
Chris@595
|
2122
|
Chris@601
|
2123 if (!source.isRemote() && !m_document->isIncomplete()) {
|
Chris@601
|
2124 // Setting the session file path enables the Save (as
|
Chris@601
|
2125 // opposed to Save As...) option. We can't do this if we
|
Chris@601
|
2126 // don't have a local path to save to, but we also don't
|
Chris@601
|
2127 // want to do it if we failed to find an audio file or
|
Chris@601
|
2128 // similar on load, as the audio reference would then end
|
Chris@601
|
2129 // up being lost from any saved or auto-saved-on-exit copy
|
Chris@601
|
2130 m_sessionFile = source.getLocalFilename();
|
Chris@602
|
2131 } else {
|
Chris@602
|
2132 QMessageBox::warning
|
Chris@602
|
2133 (this,
|
Chris@602
|
2134 tr("Incomplete session loaded"),
|
Chris@603
|
2135 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
|
2136 QMessageBox::Ok);
|
Chris@601
|
2137 }
|
Chris@595
|
2138
|
Chris@595
|
2139 setupMenus();
|
Chris@577
|
2140 findTimeRulerLayer();
|
Chris@45
|
2141
|
Chris@595
|
2142 CommandHistory::getInstance()->clear();
|
Chris@595
|
2143 CommandHistory::getInstance()->documentSaved();
|
Chris@595
|
2144 m_documentModified = false;
|
Chris@595
|
2145 updateMenuStates();
|
Chris@45
|
2146
|
Chris@227
|
2147 m_recentFiles.addFile(source.getLocation());
|
Chris@45
|
2148
|
Chris@45
|
2149 if (!source.isRemote()) {
|
Chris@45
|
2150 // for file dialog
|
Chris@45
|
2151 registerLastOpenedFilePath(FileFinder::SessionFile,
|
Chris@227
|
2152 source.getLocalFilename());
|
Chris@45
|
2153 }
|
Chris@45
|
2154
|
Chris@342
|
2155 emit sessionLoaded();
|
Chris@342
|
2156
|
Chris@45
|
2157 } else {
|
Chris@595
|
2158 setWindowTitle(QApplication::applicationName());
|
Chris@45
|
2159 }
|
Chris@45
|
2160
|
Chris@45
|
2161 return ok ? FileOpenSucceeded : FileOpenFailed;
|
Chris@45
|
2162 }
|
Chris@45
|
2163
|
Chris@141
|
2164 MainWindowBase::FileOpenStatus
|
Chris@230
|
2165 MainWindowBase::openSessionTemplate(QString templateName)
|
Chris@230
|
2166 {
|
Chris@230
|
2167 // Template in the user's template directory takes
|
Chris@230
|
2168 // priority over a bundled one; we don't unbundle, but
|
Chris@230
|
2169 // open directly from the bundled file (where applicable)
|
Chris@230
|
2170 ResourceFinder rf;
|
Chris@230
|
2171 QString tfile = rf.getResourcePath("templates", templateName + ".svt");
|
Chris@230
|
2172 if (tfile != "") {
|
Chris@294
|
2173 cerr << "SV loading template file " << tfile << endl;
|
Chris@230
|
2174 return openSessionTemplate(FileSource("file:" + tfile));
|
Chris@230
|
2175 } else {
|
Chris@230
|
2176 return FileOpenFailed;
|
Chris@230
|
2177 }
|
Chris@230
|
2178 }
|
Chris@230
|
2179
|
Chris@230
|
2180 MainWindowBase::FileOpenStatus
|
Chris@227
|
2181 MainWindowBase::openSessionTemplate(FileSource source)
|
Chris@227
|
2182 {
|
Chris@294
|
2183 cerr << "MainWindowBase::openSessionTemplate(" << source.getLocation() << ")" << endl;
|
Chris@227
|
2184
|
Chris@227
|
2185 if (!source.isAvailable()) return FileOpenFailed;
|
Chris@227
|
2186 source.waitForData();
|
Chris@227
|
2187
|
Chris@636
|
2188 QXmlInputSource *inputSource = nullptr;
|
Chris@636
|
2189 QFile *file = nullptr;
|
Chris@227
|
2190
|
Chris@227
|
2191 file = new QFile(source.getLocalFilename());
|
Chris@227
|
2192 inputSource = new QXmlInputSource(file);
|
Chris@227
|
2193
|
Chris@227
|
2194 if (!checkSaveModified()) {
|
Chris@227
|
2195 delete inputSource;
|
Chris@227
|
2196 delete file;
|
Chris@227
|
2197 return FileOpenCancelled;
|
Chris@227
|
2198 }
|
Chris@227
|
2199
|
Chris@227
|
2200 QString error;
|
Chris@227
|
2201 closeSession();
|
Chris@227
|
2202 createDocument();
|
Chris@227
|
2203
|
Chris@227
|
2204 PaneCallback callback(this);
|
Chris@227
|
2205 m_viewManager->clearSelections();
|
Chris@227
|
2206
|
Chris@227
|
2207 SVFileReader reader(m_document, callback, source.getLocation());
|
Chris@227
|
2208 connect
|
Chris@227
|
2209 (&reader, SIGNAL(modelRegenerationFailed(QString, QString, QString)),
|
Chris@227
|
2210 this, SLOT(modelRegenerationFailed(QString, QString, QString)));
|
Chris@227
|
2211 connect
|
Chris@227
|
2212 (&reader, SIGNAL(modelRegenerationWarning(QString, QString, QString)),
|
Chris@227
|
2213 this, SLOT(modelRegenerationWarning(QString, QString, QString)));
|
Chris@227
|
2214
|
Chris@227
|
2215 reader.parse(*inputSource);
|
Chris@227
|
2216
|
Chris@227
|
2217 if (!reader.isOK()) {
|
Chris@227
|
2218 error = tr("SV XML file read error:\n%1").arg(reader.getErrorString());
|
Chris@227
|
2219 }
|
Chris@227
|
2220
|
Chris@227
|
2221 delete inputSource;
|
Chris@227
|
2222 delete file;
|
Chris@227
|
2223
|
Chris@227
|
2224 bool ok = (error == "");
|
Chris@227
|
2225
|
Chris@227
|
2226 setWindowTitle(QApplication::applicationName());
|
Chris@227
|
2227
|
Chris@227
|
2228 if (ok) {
|
Chris@227
|
2229
|
Chris@227
|
2230 emit activity(tr("Open session template \"%1\"").arg(source.getLocation()));
|
Chris@227
|
2231
|
Chris@595
|
2232 setupMenus();
|
Chris@577
|
2233 findTimeRulerLayer();
|
Chris@227
|
2234
|
Chris@595
|
2235 CommandHistory::getInstance()->clear();
|
Chris@595
|
2236 CommandHistory::getInstance()->documentSaved();
|
Chris@595
|
2237 m_documentModified = false;
|
Chris@595
|
2238 updateMenuStates();
|
Chris@342
|
2239
|
Chris@342
|
2240 emit sessionLoaded();
|
Chris@227
|
2241 }
|
Chris@227
|
2242
|
Chris@227
|
2243 return ok ? FileOpenSucceeded : FileOpenFailed;
|
Chris@227
|
2244 }
|
Chris@227
|
2245
|
Chris@227
|
2246 MainWindowBase::FileOpenStatus
|
Chris@141
|
2247 MainWindowBase::openSessionFromRDF(FileSource source)
|
Chris@141
|
2248 {
|
Chris@233
|
2249 SVDEBUG << "MainWindowBase::openSessionFromRDF(" << source.getLocation() << ")" << endl;
|
Chris@141
|
2250
|
Chris@141
|
2251 if (!source.isAvailable()) return FileOpenFailed;
|
Chris@141
|
2252 source.waitForData();
|
Chris@141
|
2253
|
Chris@145
|
2254 if (!checkSaveModified()) {
|
Chris@145
|
2255 return FileOpenCancelled;
|
Chris@141
|
2256 }
|
Chris@143
|
2257
|
Chris@145
|
2258 closeSession();
|
Chris@145
|
2259 createDocument();
|
Chris@145
|
2260
|
Chris@145
|
2261 FileOpenStatus status = openLayersFromRDF(source);
|
Chris@141
|
2262
|
Chris@141
|
2263 setupMenus();
|
Chris@577
|
2264 findTimeRulerLayer();
|
Chris@141
|
2265
|
Chris@141
|
2266 setWindowTitle(tr("%1: %2")
|
Chris@141
|
2267 .arg(QApplication::applicationName())
|
Chris@141
|
2268 .arg(source.getLocation()));
|
Chris@141
|
2269 CommandHistory::getInstance()->clear();
|
Chris@141
|
2270 CommandHistory::getInstance()->documentSaved();
|
Chris@141
|
2271 m_documentModified = false;
|
Chris@145
|
2272
|
Chris@342
|
2273 emit sessionLoaded();
|
Chris@342
|
2274
|
Chris@145
|
2275 return status;
|
Chris@145
|
2276 }
|
Chris@145
|
2277
|
Chris@145
|
2278 MainWindowBase::FileOpenStatus
|
Chris@145
|
2279 MainWindowBase::openLayersFromRDF(FileSource source)
|
Chris@145
|
2280 {
|
Chris@435
|
2281 sv_samplerate_t rate = 0;
|
Chris@145
|
2282
|
Chris@233
|
2283 SVDEBUG << "MainWindowBase::openLayersFromRDF" << endl;
|
Chris@186
|
2284
|
Chris@145
|
2285 ProgressDialog dialog(tr("Importing from RDF..."), true, 2000, this);
|
Chris@145
|
2286 connect(&dialog, SIGNAL(showing()), this, SIGNAL(hideSplash()));
|
Chris@145
|
2287
|
Chris@145
|
2288 if (getMainModel()) {
|
Chris@145
|
2289 rate = getMainModel()->getSampleRate();
|
Chris@145
|
2290 } else if (Preferences::getInstance()->getResampleOnLoad()) {
|
Chris@552
|
2291 if (getMainModel()) {
|
Chris@552
|
2292 rate = getMainModel()->getSampleRate();
|
Chris@552
|
2293 }
|
Chris@145
|
2294 }
|
Chris@145
|
2295
|
Chris@145
|
2296 RDFImporter importer
|
Chris@145
|
2297 (QUrl::fromLocalFile(source.getLocalFilename()).toString(), rate);
|
Chris@145
|
2298
|
Chris@145
|
2299 if (!importer.isOK()) {
|
Chris@147
|
2300 if (importer.getErrorString() != "") {
|
Chris@147
|
2301 QMessageBox::critical
|
Chris@147
|
2302 (this, tr("Failed to import RDF"),
|
Chris@147
|
2303 tr("<b>Failed to import RDF</b><p>Importing data from RDF document at \"%1\" failed: %2</p>")
|
Chris@147
|
2304 .arg(source.getLocation()).arg(importer.getErrorString()));
|
Chris@147
|
2305 }
|
Chris@145
|
2306 return FileOpenFailed;
|
Chris@145
|
2307 }
|
Chris@145
|
2308
|
Chris@145
|
2309 std::vector<Model *> models = importer.getDataModels(&dialog);
|
Chris@145
|
2310
|
Chris@145
|
2311 dialog.setMessage(tr("Importing from RDF..."));
|
Chris@145
|
2312
|
Chris@145
|
2313 if (models.empty()) {
|
Chris@186
|
2314 QMessageBox::critical
|
Chris@186
|
2315 (this, tr("Failed to import RDF"),
|
Chris@186
|
2316 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
|
2317 return FileOpenFailed;
|
Chris@145
|
2318 }
|
Chris@145
|
2319
|
Chris@164
|
2320 emit activity(tr("Import RDF document \"%1\"").arg(source.getLocation()));
|
Chris@164
|
2321
|
Chris@145
|
2322 std::set<Model *> added;
|
Chris@145
|
2323
|
Chris@221
|
2324 for (int i = 0; i < (int)models.size(); ++i) {
|
Chris@145
|
2325
|
Chris@145
|
2326 Model *m = models[i];
|
Chris@145
|
2327 WaveFileModel *w = dynamic_cast<WaveFileModel *>(m);
|
Chris@145
|
2328
|
Chris@145
|
2329 if (w) {
|
Chris@145
|
2330
|
Chris@145
|
2331 Pane *pane = addPaneToStack();
|
Chris@636
|
2332 Layer *layer = nullptr;
|
Chris@145
|
2333
|
Chris@145
|
2334 if (m_timeRulerLayer) {
|
Chris@145
|
2335 m_document->addLayerToView(pane, m_timeRulerLayer);
|
Chris@145
|
2336 }
|
Chris@145
|
2337
|
Chris@145
|
2338 if (!getMainModel()) {
|
Chris@145
|
2339 m_document->setMainModel(w);
|
Chris@145
|
2340 layer = m_document->createMainModelLayer(LayerFactory::Waveform);
|
Chris@145
|
2341 } else {
|
Chris@145
|
2342 layer = m_document->createImportedLayer(w);
|
Chris@145
|
2343 }
|
Chris@145
|
2344
|
Chris@145
|
2345 m_document->addLayerToView(pane, layer);
|
Chris@145
|
2346
|
Chris@145
|
2347 added.insert(w);
|
Chris@145
|
2348
|
Chris@221
|
2349 for (int j = 0; j < (int)models.size(); ++j) {
|
Chris@145
|
2350
|
Chris@145
|
2351 Model *dm = models[j];
|
Chris@145
|
2352
|
Chris@145
|
2353 if (dm == m) continue;
|
Chris@145
|
2354 if (dm->getSourceModel() != m) continue;
|
Chris@145
|
2355
|
Chris@145
|
2356 layer = m_document->createImportedLayer(dm);
|
Chris@145
|
2357
|
Chris@145
|
2358 if (layer->isLayerOpaque() ||
|
Chris@145
|
2359 dynamic_cast<Colour3DPlotLayer *>(layer)) {
|
Chris@145
|
2360
|
Chris@156
|
2361 // these always go in a new pane, with nothing
|
Chris@156
|
2362 // else going in the same pane
|
Chris@156
|
2363
|
Chris@145
|
2364 Pane *singleLayerPane = addPaneToStack();
|
Chris@145
|
2365 if (m_timeRulerLayer) {
|
Chris@145
|
2366 m_document->addLayerToView(singleLayerPane, m_timeRulerLayer);
|
Chris@145
|
2367 }
|
Chris@145
|
2368 m_document->addLayerToView(singleLayerPane, layer);
|
Chris@145
|
2369
|
Chris@156
|
2370 } else if (layer->getLayerColourSignificance() ==
|
Chris@156
|
2371 Layer::ColourHasMeaningfulValue) {
|
Chris@156
|
2372
|
Chris@156
|
2373 // these can go in a pane with something else, but
|
Chris@156
|
2374 // only if none of the something elses also have
|
Chris@156
|
2375 // this quality
|
Chris@156
|
2376
|
Chris@156
|
2377 bool needNewPane = false;
|
Chris@156
|
2378 for (int i = 0; i < pane->getLayerCount(); ++i) {
|
Chris@156
|
2379 Layer *otherLayer = pane->getLayer(i);
|
Chris@156
|
2380 if (otherLayer &&
|
Chris@156
|
2381 (otherLayer->getLayerColourSignificance() ==
|
Chris@156
|
2382 Layer::ColourHasMeaningfulValue)) {
|
Chris@156
|
2383 needNewPane = true;
|
Chris@156
|
2384 break;
|
Chris@156
|
2385 }
|
Chris@156
|
2386 }
|
Chris@156
|
2387 if (needNewPane) {
|
Chris@156
|
2388 pane = addPaneToStack();
|
Chris@156
|
2389 }
|
Chris@156
|
2390
|
Chris@156
|
2391 m_document->addLayerToView(pane, layer);
|
Chris@156
|
2392
|
Chris@145
|
2393 } else {
|
Chris@145
|
2394
|
Chris@145
|
2395 if (pane->getLayerCount() > 4) {
|
Chris@145
|
2396 pane = addPaneToStack();
|
Chris@145
|
2397 }
|
Chris@145
|
2398
|
Chris@145
|
2399 m_document->addLayerToView(pane, layer);
|
Chris@145
|
2400 }
|
Chris@145
|
2401
|
Chris@145
|
2402 added.insert(dm);
|
Chris@145
|
2403 }
|
Chris@145
|
2404 }
|
Chris@145
|
2405 }
|
Chris@145
|
2406
|
Chris@221
|
2407 for (int i = 0; i < (int)models.size(); ++i) {
|
Chris@145
|
2408
|
Chris@145
|
2409 Model *m = models[i];
|
Chris@145
|
2410
|
Chris@145
|
2411 if (added.find(m) == added.end()) {
|
Chris@145
|
2412
|
Chris@145
|
2413 Layer *layer = m_document->createImportedLayer(m);
|
Chris@145
|
2414 if (!layer) return FileOpenFailed;
|
Chris@145
|
2415
|
Chris@145
|
2416 Pane *singleLayerPane = addPaneToStack();
|
Chris@145
|
2417 if (m_timeRulerLayer) {
|
Chris@145
|
2418 m_document->addLayerToView(singleLayerPane, m_timeRulerLayer);
|
Chris@145
|
2419 }
|
Chris@145
|
2420 m_document->addLayerToView(singleLayerPane, layer);
|
Chris@145
|
2421 }
|
Chris@145
|
2422 }
|
Chris@145
|
2423
|
Chris@145
|
2424 m_recentFiles.addFile(source.getLocation());
|
Chris@145
|
2425 return FileOpenSucceeded;
|
Chris@141
|
2426 }
|
Chris@141
|
2427
|
Chris@584
|
2428 class AudioLogCallback : public breakfastquay::AudioFactory::LogCallback
|
Chris@584
|
2429 {
|
Chris@584
|
2430 public:
|
Chris@584
|
2431 void log(std::string message) const override {
|
Chris@584
|
2432 SVDEBUG << message << endl;
|
Chris@584
|
2433 }
|
Chris@584
|
2434 };
|
Chris@584
|
2435
|
Chris@45
|
2436 void
|
Chris@475
|
2437 MainWindowBase::createAudioIO()
|
Chris@45
|
2438 {
|
Chris@475
|
2439 if (m_playTarget || m_audioIO) return;
|
Chris@475
|
2440
|
Chris@584
|
2441 static AudioLogCallback audioLogCallback;
|
Chris@584
|
2442 breakfastquay::AudioFactory::setLogCallback(&audioLogCallback);
|
Chris@584
|
2443
|
Chris@475
|
2444 if (!(m_soundOptions & WithAudioOutput)) return;
|
Chris@45
|
2445
|
Chris@126
|
2446 QSettings settings;
|
Chris@126
|
2447 settings.beginGroup("Preferences");
|
Chris@547
|
2448 QString implementation = settings.value
|
Chris@547
|
2449 ("audio-target", "").toString();
|
Chris@547
|
2450 QString suffix;
|
Chris@547
|
2451 if (implementation != "") suffix = "-" + implementation;
|
Chris@547
|
2452 QString recordDevice = settings.value
|
Chris@547
|
2453 ("audio-record-device" + suffix, "").toString();
|
Chris@547
|
2454 QString playbackDevice = settings.value
|
Chris@547
|
2455 ("audio-playback-device" + suffix, "").toString();
|
Chris@126
|
2456 settings.endGroup();
|
Chris@547
|
2457
|
Chris@547
|
2458 if (implementation == "auto") {
|
Chris@547
|
2459 implementation = "";
|
Chris@547
|
2460 }
|
Chris@468
|
2461
|
Chris@547
|
2462 breakfastquay::AudioFactory::Preference preference;
|
Chris@547
|
2463 preference.implementation = implementation.toStdString();
|
Chris@547
|
2464 preference.recordDevice = recordDevice.toStdString();
|
Chris@547
|
2465 preference.playbackDevice = playbackDevice.toStdString();
|
Chris@547
|
2466
|
Chris@547
|
2467 SVCERR << "createAudioIO: Preferred implementation = \""
|
Chris@547
|
2468 << preference.implementation << "\"" << endl;
|
Chris@547
|
2469 SVCERR << "createAudioIO: Preferred playback device = \""
|
Chris@547
|
2470 << preference.playbackDevice << "\"" << endl;
|
Chris@547
|
2471 SVCERR << "createAudioIO: Preferred record device = \""
|
Chris@547
|
2472 << preference.recordDevice << "\"" << endl;
|
Chris@475
|
2473
|
Chris@551
|
2474 if (!m_resamplerWrapper) {
|
Chris@551
|
2475 m_resamplerWrapper = new breakfastquay::ResamplerWrapper(m_playSource);
|
Chris@551
|
2476 m_playSource->setResamplerWrapper(m_resamplerWrapper);
|
Chris@551
|
2477 }
|
Chris@569
|
2478
|
Chris@569
|
2479 std::string errorString;
|
Chris@551
|
2480
|
Chris@475
|
2481 if (m_soundOptions & WithAudioInput) {
|
Chris@475
|
2482 m_audioIO = breakfastquay::AudioFactory::
|
Chris@569
|
2483 createCallbackIO(m_recordTarget, m_resamplerWrapper,
|
Chris@569
|
2484 preference, errorString);
|
Chris@525
|
2485 if (m_audioIO) {
|
Chris@611
|
2486 SVCERR << "MainWindowBase::createAudioIO: Suspending on creation" << endl;
|
Chris@525
|
2487 m_audioIO->suspend(); // start in suspended state
|
Chris@525
|
2488 m_playSource->setSystemPlaybackTarget(m_audioIO);
|
Chris@586
|
2489 } else {
|
Chris@586
|
2490 // Failed to create audio I/O; this may just mean there is
|
Chris@586
|
2491 // no record device, so fall through to see what happens
|
Chris@586
|
2492 // next. We only report complete failure if we end up with
|
Chris@586
|
2493 // neither m_audioIO nor m_playTarget.
|
Chris@525
|
2494 }
|
Chris@586
|
2495 }
|
Chris@586
|
2496
|
Chris@586
|
2497 if (!m_audioIO) {
|
Chris@475
|
2498 m_playTarget = breakfastquay::AudioFactory::
|
Chris@569
|
2499 createCallbackPlayTarget(m_resamplerWrapper,
|
Chris@569
|
2500 preference, errorString);
|
Chris@525
|
2501 if (m_playTarget) {
|
Chris@611
|
2502 SVCERR << "MainWindowBase::createAudioIO: Suspending on creation" << endl;
|
Chris@525
|
2503 m_playTarget->suspend(); // start in suspended state
|
Chris@525
|
2504 m_playSource->setSystemPlaybackTarget(m_playTarget);
|
Chris@525
|
2505 }
|
Chris@475
|
2506 }
|
Chris@475
|
2507
|
Chris@475
|
2508 if (!m_playTarget && !m_audioIO) {
|
Chris@104
|
2509 emit hideSplash();
|
Chris@569
|
2510 QString message;
|
Chris@569
|
2511 QString error = errorString.c_str();
|
Chris@569
|
2512 QString firstBit, secondBit;
|
Chris@547
|
2513 if (implementation == "") {
|
Chris@569
|
2514 if (error == "") {
|
Chris@569
|
2515 firstBit = tr("<b>No audio available</b><p>Could not open an audio device.</p>");
|
Chris@569
|
2516 } else {
|
Chris@569
|
2517 firstBit = tr("<b>No audio available</b><p>Could not open audio device: %1</p>").arg(error);
|
Chris@569
|
2518 }
|
Chris@569
|
2519 if (m_soundOptions & WithAudioInput) {
|
Chris@569
|
2520 secondBit = tr("<p>Automatic audio device detection failed. Audio playback and recording will not be available during this session.</p>");
|
Chris@569
|
2521 } else {
|
Chris@569
|
2522 secondBit = tr("<p>Automatic audio device detection failed. Audio playback will not be available during this session.</p>");
|
Chris@569
|
2523 }
|
Chris@126
|
2524 } else {
|
Chris@569
|
2525 QString driverName = breakfastquay::AudioFactory::
|
Chris@569
|
2526 getImplementationDescription(implementation.toStdString())
|
Chris@569
|
2527 .c_str();
|
Chris@569
|
2528 if (error == "") {
|
Chris@569
|
2529 firstBit = tr("<b>No audio available</b><p>Failed to open your preferred audio driver (\"%1\").</p>").arg(driverName);
|
Chris@569
|
2530 } else {
|
Chris@569
|
2531 firstBit = tr("<b>No audio available</b><p>Failed to open your preferred audio driver (\"%1\"): %2.</p>").arg(driverName).arg(error);
|
Chris@569
|
2532 }
|
Chris@569
|
2533 if (m_soundOptions & WithAudioInput) {
|
Chris@569
|
2534 secondBit = tr("<p>Audio playback and recording will not be available during this session.</p>");
|
Chris@569
|
2535 } else {
|
Chris@569
|
2536 secondBit = tr("<p>Audio playback will not be available during this session.</p>");
|
Chris@569
|
2537 }
|
Chris@126
|
2538 }
|
Chris@570
|
2539 SVDEBUG << "createAudioIO: ERROR: Failed to open audio device \""
|
Chris@570
|
2540 << implementation << "\": error is: " << error << endl;
|
Chris@569
|
2541 QMessageBox::warning(this, tr("Couldn't open audio device"),
|
Chris@569
|
2542 firstBit + secondBit, QMessageBox::Ok);
|
Chris@45
|
2543 }
|
Chris@45
|
2544 }
|
Chris@45
|
2545
|
Chris@556
|
2546 void
|
Chris@556
|
2547 MainWindowBase::deleteAudioIO()
|
Chris@556
|
2548 {
|
Chris@556
|
2549 // First prevent this trying to call target.
|
Chris@559
|
2550 if (m_playSource) {
|
Chris@636
|
2551 m_playSource->setSystemPlaybackTarget(nullptr);
|
Chris@636
|
2552 m_playSource->setResamplerWrapper(nullptr);
|
Chris@559
|
2553 }
|
Chris@556
|
2554
|
Chris@556
|
2555 // Then delete the breakfastquay::System object.
|
Chris@556
|
2556 // Only one of these two exists!
|
Chris@556
|
2557 delete m_audioIO;
|
Chris@556
|
2558 delete m_playTarget;
|
Chris@556
|
2559
|
Chris@559
|
2560 // And the breakfastquay resampler wrapper. We need to
|
Chris@559
|
2561 // delete/recreate this if the channel count changes, which is one
|
Chris@559
|
2562 // of the use cases for recreateAudioIO() calling this
|
Chris@559
|
2563 delete m_resamplerWrapper;
|
Chris@559
|
2564
|
Chris@636
|
2565 m_audioIO = nullptr;
|
Chris@636
|
2566 m_playTarget = nullptr;
|
Chris@636
|
2567 m_resamplerWrapper = nullptr;
|
Chris@556
|
2568 }
|
Chris@556
|
2569
|
Chris@556
|
2570 void
|
Chris@556
|
2571 MainWindowBase::recreateAudioIO()
|
Chris@556
|
2572 {
|
Chris@556
|
2573 deleteAudioIO();
|
Chris@556
|
2574 createAudioIO();
|
Chris@556
|
2575 }
|
Chris@556
|
2576
|
Chris@570
|
2577 void
|
Chris@570
|
2578 MainWindowBase::audioChannelCountIncreased(int)
|
Chris@570
|
2579 {
|
Chris@611
|
2580 SVCERR << "MainWindowBase::audioChannelCountIncreased" << endl;
|
Chris@570
|
2581 recreateAudioIO();
|
Chris@610
|
2582
|
Chris@610
|
2583 if (m_recordTarget &&
|
Chris@610
|
2584 m_recordTarget->isRecording() &&
|
Chris@610
|
2585 m_audioIO) {
|
Chris@610
|
2586 SVCERR << "MainWindowBase::audioChannelCountIncreased: we were recording already, so resuming IO now" << endl;
|
Chris@610
|
2587 m_audioIO->resume();
|
Chris@610
|
2588 }
|
Chris@570
|
2589 }
|
Chris@570
|
2590
|
Chris@45
|
2591 WaveFileModel *
|
Chris@45
|
2592 MainWindowBase::getMainModel()
|
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 const WaveFileModel *
|
Chris@45
|
2599 MainWindowBase::getMainModel() const
|
Chris@45
|
2600 {
|
Chris@636
|
2601 if (!m_document) return nullptr;
|
Chris@45
|
2602 return m_document->getMainModel();
|
Chris@45
|
2603 }
|
Chris@45
|
2604
|
Chris@45
|
2605 void
|
Chris@45
|
2606 MainWindowBase::createDocument()
|
Chris@45
|
2607 {
|
Chris@45
|
2608 m_document = new Document;
|
Chris@45
|
2609
|
Chris@45
|
2610 connect(m_document, SIGNAL(layerAdded(Layer *)),
|
Chris@595
|
2611 this, SLOT(layerAdded(Layer *)));
|
Chris@45
|
2612 connect(m_document, SIGNAL(layerRemoved(Layer *)),
|
Chris@595
|
2613 this, SLOT(layerRemoved(Layer *)));
|
Chris@45
|
2614 connect(m_document, SIGNAL(layerAboutToBeDeleted(Layer *)),
|
Chris@595
|
2615 this, SLOT(layerAboutToBeDeleted(Layer *)));
|
Chris@45
|
2616 connect(m_document, SIGNAL(layerInAView(Layer *, bool)),
|
Chris@595
|
2617 this, SLOT(layerInAView(Layer *, bool)));
|
Chris@45
|
2618
|
Chris@45
|
2619 connect(m_document, SIGNAL(modelAdded(Model *)),
|
Chris@595
|
2620 this, SLOT(modelAdded(Model *)));
|
Chris@45
|
2621 connect(m_document, SIGNAL(mainModelChanged(WaveFileModel *)),
|
Chris@595
|
2622 this, SLOT(mainModelChanged(WaveFileModel *)));
|
Chris@45
|
2623 connect(m_document, SIGNAL(modelAboutToBeDeleted(Model *)),
|
Chris@595
|
2624 this, SLOT(modelAboutToBeDeleted(Model *)));
|
Chris@45
|
2625
|
Chris@78
|
2626 connect(m_document, SIGNAL(modelGenerationFailed(QString, QString)),
|
Chris@78
|
2627 this, SLOT(modelGenerationFailed(QString, QString)));
|
Chris@78
|
2628 connect(m_document, SIGNAL(modelRegenerationWarning(QString, QString, QString)),
|
Chris@78
|
2629 this, SLOT(modelRegenerationWarning(QString, QString, QString)));
|
Chris@429
|
2630 connect(m_document, SIGNAL(alignmentComplete(AlignmentModel *)),
|
Chris@429
|
2631 this, SLOT(alignmentComplete(AlignmentModel *)));
|
Chris@423
|
2632 connect(m_document, SIGNAL(alignmentFailed(QString)),
|
Chris@423
|
2633 this, SLOT(alignmentFailed(QString)));
|
Chris@160
|
2634
|
Chris@160
|
2635 emit replacedDocument();
|
Chris@45
|
2636 }
|
Chris@45
|
2637
|
Chris@45
|
2638 bool
|
Chris@45
|
2639 MainWindowBase::saveSessionFile(QString path)
|
Chris@45
|
2640 {
|
Chris@217
|
2641 try {
|
Chris@217
|
2642
|
Chris@217
|
2643 TempWriteFile temp(path);
|
Chris@217
|
2644
|
Chris@217
|
2645 BZipFileDevice bzFile(temp.getTemporaryFilename());
|
Chris@217
|
2646 if (!bzFile.open(QIODevice::WriteOnly)) {
|
Chris@293
|
2647 cerr << "Failed to open session file \""
|
Chris@294
|
2648 << temp.getTemporaryFilename()
|
Chris@217
|
2649 << "\" for writing: "
|
Chris@293
|
2650 << bzFile.errorString() << endl;
|
Chris@217
|
2651 return false;
|
Chris@217
|
2652 }
|
Chris@217
|
2653
|
Chris@217
|
2654 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
|
Chris@217
|
2655
|
Chris@217
|
2656 QTextStream out(&bzFile);
|
Chris@432
|
2657 out.setCodec(QTextCodec::codecForName("UTF-8"));
|
Chris@226
|
2658 toXml(out, false);
|
Chris@217
|
2659 out.flush();
|
Chris@217
|
2660
|
Chris@217
|
2661 QApplication::restoreOverrideCursor();
|
Chris@217
|
2662
|
Chris@217
|
2663 if (!bzFile.isOK()) {
|
Chris@217
|
2664 QMessageBox::critical(this, tr("Failed to write file"),
|
Chris@217
|
2665 tr("<b>Save failed</b><p>Failed to write to file \"%1\": %2")
|
Chris@217
|
2666 .arg(path).arg(bzFile.errorString()));
|
Chris@217
|
2667 bzFile.close();
|
Chris@217
|
2668 return false;
|
Chris@217
|
2669 }
|
Chris@217
|
2670
|
Chris@217
|
2671 bzFile.close();
|
Chris@217
|
2672 temp.moveToTarget();
|
Chris@217
|
2673 return true;
|
Chris@217
|
2674
|
Chris@217
|
2675 } catch (FileOperationFailed &f) {
|
Chris@217
|
2676
|
Chris@217
|
2677 QMessageBox::critical(this, tr("Failed to write file"),
|
Chris@217
|
2678 tr("<b>Save failed</b><p>Failed to write to file \"%1\": %2")
|
Chris@217
|
2679 .arg(path).arg(f.what()));
|
Chris@45
|
2680 return false;
|
Chris@45
|
2681 }
|
Chris@45
|
2682 }
|
Chris@45
|
2683
|
Chris@224
|
2684 bool
|
Chris@224
|
2685 MainWindowBase::saveSessionTemplate(QString path)
|
Chris@224
|
2686 {
|
Chris@224
|
2687 try {
|
Chris@224
|
2688
|
Chris@224
|
2689 TempWriteFile temp(path);
|
Chris@224
|
2690
|
Chris@224
|
2691 QFile file(temp.getTemporaryFilename());
|
Chris@224
|
2692 if (!file.open(QIODevice::WriteOnly)) {
|
Chris@293
|
2693 cerr << "Failed to open session template file \""
|
Chris@294
|
2694 << temp.getTemporaryFilename()
|
Chris@224
|
2695 << "\" for writing: "
|
Chris@294
|
2696 << file.errorString() << endl;
|
Chris@224
|
2697 return false;
|
Chris@224
|
2698 }
|
Chris@224
|
2699
|
Chris@224
|
2700 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
|
Chris@224
|
2701
|
Chris@224
|
2702 QTextStream out(&file);
|
Chris@432
|
2703 out.setCodec(QTextCodec::codecForName("UTF-8"));
|
Chris@226
|
2704 toXml(out, true);
|
Chris@224
|
2705 out.flush();
|
Chris@224
|
2706
|
Chris@224
|
2707 QApplication::restoreOverrideCursor();
|
Chris@224
|
2708
|
Chris@224
|
2709 file.close();
|
Chris@224
|
2710 temp.moveToTarget();
|
Chris@224
|
2711 return true;
|
Chris@224
|
2712
|
Chris@224
|
2713 } catch (FileOperationFailed &f) {
|
Chris@224
|
2714
|
Chris@224
|
2715 QMessageBox::critical(this, tr("Failed to write file"),
|
Chris@224
|
2716 tr("<b>Save failed</b><p>Failed to write to file \"%1\": %2")
|
Chris@224
|
2717 .arg(path).arg(f.what()));
|
Chris@224
|
2718 return false;
|
Chris@224
|
2719 }
|
Chris@224
|
2720 }
|
Chris@224
|
2721
|
Chris@45
|
2722 void
|
Chris@226
|
2723 MainWindowBase::toXml(QTextStream &out, bool asTemplate)
|
Chris@45
|
2724 {
|
Chris@45
|
2725 QString indent(" ");
|
Chris@45
|
2726
|
Chris@45
|
2727 out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
|
Chris@45
|
2728 out << "<!DOCTYPE sonic-visualiser>\n";
|
Chris@45
|
2729 out << "<sv>\n";
|
Chris@45
|
2730
|
Chris@226
|
2731 if (asTemplate) {
|
Chris@226
|
2732 m_document->toXmlAsTemplate(out, "", "");
|
Chris@226
|
2733 } else {
|
Chris@226
|
2734 m_document->toXml(out, "", "");
|
Chris@226
|
2735 }
|
Chris@45
|
2736
|
Chris@45
|
2737 out << "<display>\n";
|
Chris@45
|
2738
|
Chris@45
|
2739 out << QString(" <window width=\"%1\" height=\"%2\"/>\n")
|
Chris@595
|
2740 .arg(width()).arg(height());
|
Chris@45
|
2741
|
Chris@45
|
2742 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
|
Chris@45
|
2743
|
Chris@595
|
2744 Pane *pane = m_paneStack->getPane(i);
|
Chris@595
|
2745
|
Chris@595
|
2746 if (pane) {
|
Chris@45
|
2747 pane->toXml(out, indent);
|
Chris@595
|
2748 }
|
Chris@45
|
2749 }
|
Chris@45
|
2750
|
Chris@45
|
2751 out << "</display>\n";
|
Chris@45
|
2752
|
Chris@45
|
2753 m_viewManager->getSelection().toXml(out);
|
Chris@45
|
2754
|
Chris@45
|
2755 out << "</sv>\n";
|
Chris@45
|
2756 }
|
Chris@45
|
2757
|
Chris@45
|
2758 Pane *
|
Chris@45
|
2759 MainWindowBase::addPaneToStack()
|
Chris@45
|
2760 {
|
Chris@342
|
2761 cerr << "MainWindowBase::addPaneToStack()" << endl;
|
Chris@45
|
2762 AddPaneCommand *command = new AddPaneCommand(this);
|
Chris@45
|
2763 CommandHistory::getInstance()->addCommand(command);
|
Chris@57
|
2764 Pane *pane = command->getPane();
|
Chris@57
|
2765 return pane;
|
Chris@45
|
2766 }
|
Chris@45
|
2767
|
Chris@45
|
2768 void
|
Chris@45
|
2769 MainWindowBase::zoomIn()
|
Chris@45
|
2770 {
|
Chris@45
|
2771 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@45
|
2772 if (currentPane) currentPane->zoom(true);
|
Chris@45
|
2773 }
|
Chris@45
|
2774
|
Chris@45
|
2775 void
|
Chris@45
|
2776 MainWindowBase::zoomOut()
|
Chris@45
|
2777 {
|
Chris@45
|
2778 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@45
|
2779 if (currentPane) currentPane->zoom(false);
|
Chris@45
|
2780 }
|
Chris@45
|
2781
|
Chris@45
|
2782 void
|
Chris@45
|
2783 MainWindowBase::zoomToFit()
|
Chris@45
|
2784 {
|
Chris@45
|
2785 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@45
|
2786 if (!currentPane) return;
|
Chris@45
|
2787
|
Chris@45
|
2788 Model *model = getMainModel();
|
Chris@45
|
2789 if (!model) return;
|
Chris@45
|
2790
|
Chris@434
|
2791 sv_frame_t start = model->getStartFrame();
|
Chris@434
|
2792 sv_frame_t end = model->getEndFrame();
|
Chris@60
|
2793 if (m_playSource) end = std::max(end, m_playSource->getPlayEndFrame());
|
Chris@366
|
2794 int pixels = currentPane->width();
|
Chris@366
|
2795
|
Chris@366
|
2796 int sw = currentPane->getVerticalScaleWidth();
|
Chris@45
|
2797 if (pixels > sw * 2) pixels -= sw * 2;
|
Chris@45
|
2798 else pixels = 1;
|
Chris@45
|
2799 if (pixels > 4) pixels -= 4;
|
Chris@45
|
2800
|
Chris@624
|
2801 ZoomLevel zoomLevel = ZoomLevel::fromRatio(pixels, end - start);
|
Chris@45
|
2802 currentPane->setZoomLevel(zoomLevel);
|
Chris@45
|
2803 currentPane->setCentreFrame((start + end) / 2);
|
Chris@45
|
2804 }
|
Chris@45
|
2805
|
Chris@45
|
2806 void
|
Chris@45
|
2807 MainWindowBase::zoomDefault()
|
Chris@45
|
2808 {
|
Chris@45
|
2809 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@302
|
2810 QSettings settings;
|
Chris@302
|
2811 settings.beginGroup("MainWindow");
|
Chris@302
|
2812 int zoom = settings.value("zoom-default", 1024).toInt();
|
Chris@302
|
2813 settings.endGroup();
|
Chris@624
|
2814 if (currentPane) {
|
Chris@624
|
2815 currentPane->setZoomLevel(ZoomLevel(ZoomLevel::FramesPerPixel, zoom));
|
Chris@624
|
2816 }
|
Chris@45
|
2817 }
|
Chris@45
|
2818
|
Chris@45
|
2819 void
|
Chris@45
|
2820 MainWindowBase::scrollLeft()
|
Chris@45
|
2821 {
|
Chris@45
|
2822 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@45
|
2823 if (currentPane) currentPane->scroll(false, false);
|
Chris@45
|
2824 }
|
Chris@45
|
2825
|
Chris@45
|
2826 void
|
Chris@45
|
2827 MainWindowBase::jumpLeft()
|
Chris@45
|
2828 {
|
Chris@45
|
2829 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@45
|
2830 if (currentPane) currentPane->scroll(false, true);
|
Chris@45
|
2831 }
|
Chris@45
|
2832
|
Chris@45
|
2833 void
|
Chris@162
|
2834 MainWindowBase::peekLeft()
|
Chris@162
|
2835 {
|
Chris@162
|
2836 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@162
|
2837 if (currentPane) currentPane->scroll(false, false, false);
|
Chris@162
|
2838 }
|
Chris@162
|
2839
|
Chris@162
|
2840 void
|
Chris@45
|
2841 MainWindowBase::scrollRight()
|
Chris@45
|
2842 {
|
Chris@45
|
2843 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@45
|
2844 if (currentPane) currentPane->scroll(true, false);
|
Chris@45
|
2845 }
|
Chris@45
|
2846
|
Chris@45
|
2847 void
|
Chris@45
|
2848 MainWindowBase::jumpRight()
|
Chris@45
|
2849 {
|
Chris@45
|
2850 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@45
|
2851 if (currentPane) currentPane->scroll(true, true);
|
Chris@45
|
2852 }
|
Chris@45
|
2853
|
Chris@45
|
2854 void
|
Chris@162
|
2855 MainWindowBase::peekRight()
|
Chris@162
|
2856 {
|
Chris@162
|
2857 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@162
|
2858 if (currentPane) currentPane->scroll(true, false, false);
|
Chris@162
|
2859 }
|
Chris@162
|
2860
|
Chris@162
|
2861 void
|
Chris@45
|
2862 MainWindowBase::showNoOverlays()
|
Chris@45
|
2863 {
|
Chris@45
|
2864 m_viewManager->setOverlayMode(ViewManager::NoOverlays);
|
Chris@45
|
2865 }
|
Chris@45
|
2866
|
Chris@45
|
2867 void
|
Chris@45
|
2868 MainWindowBase::showMinimalOverlays()
|
Chris@45
|
2869 {
|
Chris@335
|
2870 m_viewManager->setOverlayMode(ViewManager::StandardOverlays);
|
Chris@45
|
2871 }
|
Chris@45
|
2872
|
Chris@45
|
2873 void
|
Chris@45
|
2874 MainWindowBase::showAllOverlays()
|
Chris@45
|
2875 {
|
Chris@45
|
2876 m_viewManager->setOverlayMode(ViewManager::AllOverlays);
|
Chris@45
|
2877 }
|
Chris@45
|
2878
|
Chris@45
|
2879 void
|
Chris@577
|
2880 MainWindowBase::findTimeRulerLayer()
|
Chris@577
|
2881 {
|
Chris@577
|
2882 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
|
Chris@577
|
2883 Pane *pane = m_paneStack->getPane(i);
|
Chris@577
|
2884 if (!pane) continue;
|
Chris@577
|
2885 for (int j = 0; j < pane->getLayerCount(); ++j) {
|
Chris@577
|
2886 Layer *layer = pane->getLayer(j);
|
Chris@577
|
2887 if (!dynamic_cast<TimeRulerLayer *>(layer)) continue;
|
Chris@577
|
2888 m_timeRulerLayer = layer;
|
Chris@577
|
2889 return;
|
Chris@577
|
2890 }
|
Chris@577
|
2891 }
|
Chris@577
|
2892 if (m_timeRulerLayer) {
|
Chris@577
|
2893 SVCERR << "WARNING: Time ruler layer was not reset to 0 before session template loaded?" << endl;
|
Chris@577
|
2894 delete m_timeRulerLayer;
|
Chris@636
|
2895 m_timeRulerLayer = nullptr;
|
Chris@577
|
2896 }
|
Chris@577
|
2897 }
|
Chris@577
|
2898
|
Chris@577
|
2899 void
|
Chris@211
|
2900 MainWindowBase::toggleTimeRulers()
|
Chris@211
|
2901 {
|
Chris@211
|
2902 bool haveRulers = false;
|
Chris@211
|
2903 bool someHidden = false;
|
Chris@211
|
2904
|
Chris@211
|
2905 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
|
Chris@211
|
2906
|
Chris@211
|
2907 Pane *pane = m_paneStack->getPane(i);
|
Chris@211
|
2908 if (!pane) continue;
|
Chris@211
|
2909
|
Chris@211
|
2910 for (int j = 0; j < pane->getLayerCount(); ++j) {
|
Chris@211
|
2911
|
Chris@211
|
2912 Layer *layer = pane->getLayer(j);
|
Chris@211
|
2913 if (!dynamic_cast<TimeRulerLayer *>(layer)) continue;
|
Chris@211
|
2914
|
Chris@211
|
2915 haveRulers = true;
|
Chris@211
|
2916 if (layer->isLayerDormant(pane)) someHidden = true;
|
Chris@211
|
2917 }
|
Chris@211
|
2918 }
|
Chris@211
|
2919
|
Chris@211
|
2920 if (haveRulers) {
|
Chris@211
|
2921
|
Chris@211
|
2922 bool show = someHidden;
|
Chris@211
|
2923
|
Chris@211
|
2924 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
|
Chris@211
|
2925
|
Chris@211
|
2926 Pane *pane = m_paneStack->getPane(i);
|
Chris@211
|
2927 if (!pane) continue;
|
Chris@211
|
2928
|
Chris@211
|
2929 for (int j = 0; j < pane->getLayerCount(); ++j) {
|
Chris@211
|
2930
|
Chris@211
|
2931 Layer *layer = pane->getLayer(j);
|
Chris@211
|
2932 if (!dynamic_cast<TimeRulerLayer *>(layer)) continue;
|
Chris@211
|
2933
|
Chris@211
|
2934 layer->showLayer(pane, show);
|
Chris@211
|
2935 }
|
Chris@211
|
2936 }
|
Chris@211
|
2937 }
|
Chris@211
|
2938 }
|
Chris@211
|
2939
|
Chris@211
|
2940 void
|
Chris@45
|
2941 MainWindowBase::toggleZoomWheels()
|
Chris@45
|
2942 {
|
Chris@45
|
2943 if (m_viewManager->getZoomWheelsEnabled()) {
|
Chris@45
|
2944 m_viewManager->setZoomWheelsEnabled(false);
|
Chris@45
|
2945 } else {
|
Chris@45
|
2946 m_viewManager->setZoomWheelsEnabled(true);
|
Chris@45
|
2947 }
|
Chris@45
|
2948 }
|
Chris@45
|
2949
|
Chris@45
|
2950 void
|
Chris@45
|
2951 MainWindowBase::togglePropertyBoxes()
|
Chris@45
|
2952 {
|
Chris@45
|
2953 if (m_paneStack->getLayoutStyle() == PaneStack::NoPropertyStacks) {
|
Chris@45
|
2954 if (Preferences::getInstance()->getPropertyBoxLayout() ==
|
Chris@45
|
2955 Preferences::VerticallyStacked) {
|
Chris@45
|
2956 m_paneStack->setLayoutStyle(PaneStack::PropertyStackPerPaneLayout);
|
Chris@45
|
2957 } else {
|
Chris@45
|
2958 m_paneStack->setLayoutStyle(PaneStack::SinglePropertyStackLayout);
|
Chris@45
|
2959 }
|
Chris@45
|
2960 } else {
|
Chris@45
|
2961 m_paneStack->setLayoutStyle(PaneStack::NoPropertyStacks);
|
Chris@45
|
2962 }
|
Chris@45
|
2963 }
|
Chris@45
|
2964
|
Chris@378
|
2965 QLabel *
|
Chris@378
|
2966 MainWindowBase::getStatusLabel() const
|
Chris@378
|
2967 {
|
Chris@378
|
2968 if (!m_statusLabel) {
|
Chris@378
|
2969 m_statusLabel = new QLabel();
|
Chris@378
|
2970 statusBar()->addWidget(m_statusLabel, 1);
|
Chris@378
|
2971 }
|
Chris@379
|
2972
|
Chris@379
|
2973 QList<QFrame *> frames = statusBar()->findChildren<QFrame *>();
|
Chris@379
|
2974 foreach (QFrame *f, frames) {
|
Chris@379
|
2975 f->setFrameStyle(QFrame::NoFrame);
|
Chris@379
|
2976 }
|
Chris@379
|
2977
|
Chris@378
|
2978 return m_statusLabel;
|
Chris@378
|
2979 }
|
Chris@378
|
2980
|
Chris@45
|
2981 void
|
Chris@45
|
2982 MainWindowBase::toggleStatusBar()
|
Chris@45
|
2983 {
|
Chris@45
|
2984 QSettings settings;
|
Chris@45
|
2985 settings.beginGroup("MainWindow");
|
Chris@45
|
2986 bool sb = settings.value("showstatusbar", true).toBool();
|
Chris@45
|
2987
|
Chris@45
|
2988 if (sb) {
|
Chris@45
|
2989 statusBar()->hide();
|
Chris@45
|
2990 } else {
|
Chris@45
|
2991 statusBar()->show();
|
Chris@45
|
2992 }
|
Chris@45
|
2993
|
Chris@45
|
2994 settings.setValue("showstatusbar", !sb);
|
Chris@45
|
2995
|
Chris@45
|
2996 settings.endGroup();
|
Chris@45
|
2997 }
|
Chris@45
|
2998
|
Chris@45
|
2999 void
|
Chris@256
|
3000 MainWindowBase::toggleCentreLine()
|
Chris@256
|
3001 {
|
Chris@256
|
3002 if (m_viewManager->shouldShowCentreLine()) {
|
Chris@256
|
3003 m_viewManager->setShowCentreLine(false);
|
Chris@256
|
3004 } else {
|
Chris@256
|
3005 m_viewManager->setShowCentreLine(true);
|
Chris@256
|
3006 }
|
Chris@256
|
3007 }
|
Chris@256
|
3008
|
Chris@256
|
3009 void
|
Chris@45
|
3010 MainWindowBase::preferenceChanged(PropertyContainer::PropertyName name)
|
Chris@45
|
3011 {
|
Chris@45
|
3012 if (name == "Property Box Layout") {
|
Chris@45
|
3013 if (m_paneStack->getLayoutStyle() != PaneStack::NoPropertyStacks) {
|
Chris@45
|
3014 if (Preferences::getInstance()->getPropertyBoxLayout() ==
|
Chris@45
|
3015 Preferences::VerticallyStacked) {
|
Chris@45
|
3016 m_paneStack->setLayoutStyle(PaneStack::PropertyStackPerPaneLayout);
|
Chris@45
|
3017 } else {
|
Chris@45
|
3018 m_paneStack->setLayoutStyle(PaneStack::SinglePropertyStackLayout);
|
Chris@45
|
3019 }
|
Chris@45
|
3020 }
|
Chris@45
|
3021 } else if (name == "Background Mode" && m_viewManager) {
|
Chris@45
|
3022 Preferences::BackgroundMode mode =
|
Chris@45
|
3023 Preferences::getInstance()->getBackgroundMode();
|
Chris@45
|
3024 if (mode == Preferences::BackgroundFromTheme) {
|
Chris@45
|
3025 m_viewManager->setGlobalDarkBackground(m_initialDarkBackground);
|
Chris@45
|
3026 } else if (mode == Preferences::DarkBackground) {
|
Chris@45
|
3027 m_viewManager->setGlobalDarkBackground(true);
|
Chris@45
|
3028 } else {
|
Chris@45
|
3029 m_viewManager->setGlobalDarkBackground(false);
|
Chris@45
|
3030 }
|
Chris@45
|
3031 }
|
Chris@45
|
3032 }
|
Chris@45
|
3033
|
Chris@45
|
3034 void
|
Chris@45
|
3035 MainWindowBase::play()
|
Chris@45
|
3036 {
|
Chris@516
|
3037 if ((m_recordTarget && m_recordTarget->isRecording()) ||
|
Chris@516
|
3038 (m_playSource && m_playSource->isPlaying())) {
|
Chris@45
|
3039 stop();
|
Chris@479
|
3040 QAction *action = qobject_cast<QAction *>(sender());
|
Chris@479
|
3041 if (action) action->setChecked(false);
|
Chris@45
|
3042 } else {
|
Chris@487
|
3043 if (m_audioIO) m_audioIO->resume();
|
Chris@509
|
3044 else if (m_playTarget) m_playTarget->resume();
|
Chris@45
|
3045 playbackFrameChanged(m_viewManager->getPlaybackFrame());
|
Chris@595
|
3046 m_playSource->play(m_viewManager->getPlaybackFrame());
|
Chris@45
|
3047 }
|
Chris@45
|
3048 }
|
Chris@45
|
3049
|
Chris@45
|
3050 void
|
Chris@477
|
3051 MainWindowBase::record()
|
Chris@477
|
3052 {
|
Chris@586
|
3053 QAction *action = qobject_cast<QAction *>(sender());
|
Chris@586
|
3054
|
Chris@478
|
3055 if (!(m_soundOptions & WithAudioInput)) {
|
Chris@586
|
3056 if (action) action->setChecked(false);
|
Chris@478
|
3057 return;
|
Chris@478
|
3058 }
|
Chris@478
|
3059
|
Chris@477
|
3060 if (!m_recordTarget) {
|
Chris@586
|
3061 if (action) action->setChecked(false);
|
Chris@477
|
3062 return;
|
Chris@477
|
3063 }
|
Chris@477
|
3064
|
Chris@478
|
3065 if (!m_audioIO) {
|
Chris@611
|
3066 SVDEBUG << "MainWindowBase::record: about to create audio IO" << endl;
|
Chris@478
|
3067 createAudioIO();
|
Chris@478
|
3068 }
|
Chris@492
|
3069
|
Chris@492
|
3070 if (!m_audioIO) {
|
Chris@586
|
3071 if (!m_playTarget) {
|
Chris@586
|
3072 // Don't need to report this, createAudioIO should have
|
Chris@586
|
3073 if (action) action->setChecked(false);
|
Chris@586
|
3074 return;
|
Chris@586
|
3075 } else {
|
Chris@586
|
3076 // Need to report this: if the play target exists instead
|
Chris@586
|
3077 // of the audio IO, then that means we failed to open a
|
Chris@586
|
3078 // capture device. The record control should be disabled
|
Chris@586
|
3079 // in that situation, so if it happens here, that must
|
Chris@586
|
3080 // mean this is the first time we ever tried to open the
|
Chris@586
|
3081 // audio device, hence the need to report the problem here
|
Chris@586
|
3082 QMessageBox::critical
|
Chris@586
|
3083 (this, tr("No record device available"),
|
Chris@586
|
3084 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
|
3085 if (action) action->setChecked(false);
|
Chris@586
|
3086 updateMenuStates();
|
Chris@586
|
3087 return;
|
Chris@586
|
3088 }
|
Chris@492
|
3089 }
|
Chris@478
|
3090
|
Chris@477
|
3091 if (m_recordTarget->isRecording()) {
|
Chris@492
|
3092 stop();
|
Chris@477
|
3093 return;
|
Chris@477
|
3094 }
|
Chris@490
|
3095
|
Chris@483
|
3096 if (m_audioRecordMode == RecordReplaceSession) {
|
Chris@490
|
3097 if (!checkSaveModified()) {
|
Chris@490
|
3098 if (action) action->setChecked(false);
|
Chris@490
|
3099 return;
|
Chris@490
|
3100 }
|
Chris@483
|
3101 }
|
Chris@487
|
3102
|
Chris@557
|
3103 if (m_viewManager) m_viewManager->setGlobalCentreFrame(0);
|
Chris@557
|
3104
|
Chris@611
|
3105 SVCERR << "MainWindowBase::record: about to resume" << endl;
|
Chris@492
|
3106 m_audioIO->resume();
|
Chris@509
|
3107
|
Chris@477
|
3108 WritableWaveFileModel *model = m_recordTarget->startRecording();
|
Chris@477
|
3109 if (!model) {
|
Chris@586
|
3110 SVCERR << "ERROR: MainWindowBase::record: Recording failed" << endl;
|
Chris@586
|
3111 QMessageBox::critical
|
Chris@586
|
3112 (this, tr("Recording failed"),
|
Chris@586
|
3113 tr("<b>Recording failed</b><p>Failed to switch to record mode (some internal problem?)</p>"));
|
Chris@490
|
3114 if (action) action->setChecked(false);
|
Chris@477
|
3115 return;
|
Chris@477
|
3116 }
|
Chris@477
|
3117
|
Chris@477
|
3118 if (!model->isOK()) {
|
Chris@611
|
3119 SVCERR << "MainWindowBase::record: Model not OK, stopping and suspending" << endl;
|
Chris@477
|
3120 m_recordTarget->stopRecording();
|
Chris@492
|
3121 m_audioIO->suspend();
|
Chris@586
|
3122 if (action) action->setChecked(false);
|
Chris@477
|
3123 delete model;
|
Chris@477
|
3124 return;
|
Chris@477
|
3125 }
|
Chris@611
|
3126
|
Chris@611
|
3127 SVCERR << "MainWindowBase::record: Model is OK, continuing..." << endl;
|
Chris@487
|
3128
|
Chris@478
|
3129 PlayParameterRepository::getInstance()->addPlayable(model);
|
Chris@483
|
3130
|
Chris@483
|
3131 if (m_audioRecordMode == RecordReplaceSession || !getMainModel()) {
|
Chris@478
|
3132
|
Chris@479
|
3133 //!!! duplication with openAudio here
|
Chris@479
|
3134
|
Chris@479
|
3135 QString templateName = getDefaultSessionTemplate();
|
Chris@479
|
3136 bool loadedTemplate = false;
|
Chris@479
|
3137
|
Chris@479
|
3138 if (templateName != "") {
|
Chris@479
|
3139 FileOpenStatus tplStatus = openSessionTemplate(templateName);
|
Chris@479
|
3140 if (tplStatus == FileOpenCancelled) {
|
Chris@611
|
3141 SVCERR << "MainWindowBase::record: Session template open cancelled, stopping and suspending" << endl;
|
Chris@490
|
3142 m_recordTarget->stopRecording();
|
Chris@492
|
3143 m_audioIO->suspend();
|
Chris@490
|
3144 PlayParameterRepository::getInstance()->removePlayable(model);
|
Chris@479
|
3145 return;
|
Chris@479
|
3146 }
|
Chris@479
|
3147 if (tplStatus != FileOpenFailed) {
|
Chris@479
|
3148 loadedTemplate = true;
|
Chris@479
|
3149 }
|
Chris@479
|
3150 }
|
Chris@479
|
3151
|
Chris@479
|
3152 if (!loadedTemplate) {
|
Chris@479
|
3153 closeSession();
|
Chris@479
|
3154 createDocument();
|
Chris@479
|
3155 }
|
Chris@479
|
3156
|
Chris@479
|
3157 Model *prevMain = getMainModel();
|
Chris@479
|
3158 if (prevMain) {
|
Chris@479
|
3159 m_playSource->removeModel(prevMain);
|
Chris@479
|
3160 PlayParameterRepository::getInstance()->removePlayable(prevMain);
|
Chris@479
|
3161 }
|
Chris@479
|
3162
|
Chris@478
|
3163 m_document->setMainModel(model);
|
Chris@478
|
3164 setupMenus();
|
Chris@577
|
3165 findTimeRulerLayer();
|
Chris@478
|
3166
|
Chris@595
|
3167 if (loadedTemplate || (m_sessionFile == "")) {
|
Chris@479
|
3168 //!!! shouldn't be dealing directly with title from here -- call a method
|
Chris@595
|
3169 setWindowTitle(tr("%1: %2")
|
Chris@479
|
3170 .arg(QApplication::applicationName())
|
Chris@479
|
3171 .arg(model->getLocation()));
|
Chris@595
|
3172 CommandHistory::getInstance()->clear();
|
Chris@595
|
3173 CommandHistory::getInstance()->documentSaved();
|
Chris@595
|
3174 m_documentModified = false;
|
Chris@595
|
3175 } else {
|
Chris@595
|
3176 setWindowTitle(tr("%1: %2 [%3]")
|
Chris@479
|
3177 .arg(QApplication::applicationName())
|
Chris@595
|
3178 .arg(QFileInfo(m_sessionFile).fileName())
|
Chris@595
|
3179 .arg(model->getLocation()));
|
Chris@595
|
3180 if (m_documentModified) {
|
Chris@595
|
3181 m_documentModified = false;
|
Chris@595
|
3182 documentModified(); // so as to restore "(modified)" window title
|
Chris@595
|
3183 }
|
Chris@595
|
3184 }
|
Chris@479
|
3185
|
Chris@478
|
3186 } else {
|
Chris@478
|
3187
|
Chris@478
|
3188 CommandHistory::getInstance()->startCompoundOperation
|
Chris@478
|
3189 (tr("Import Recorded Audio"), true);
|
Chris@478
|
3190
|
Chris@478
|
3191 m_document->addImportedModel(model);
|
Chris@478
|
3192
|
Chris@478
|
3193 AddPaneCommand *command = new AddPaneCommand(this);
|
Chris@478
|
3194 CommandHistory::getInstance()->addCommand(command);
|
Chris@478
|
3195
|
Chris@478
|
3196 Pane *pane = command->getPane();
|
Chris@478
|
3197
|
Chris@478
|
3198 if (m_timeRulerLayer) {
|
Chris@478
|
3199 m_document->addLayerToView(pane, m_timeRulerLayer);
|
Chris@478
|
3200 }
|
Chris@478
|
3201
|
Chris@478
|
3202 Layer *newLayer = m_document->createImportedLayer(model);
|
Chris@478
|
3203
|
Chris@478
|
3204 if (newLayer) {
|
Chris@478
|
3205 m_document->addLayerToView(pane, newLayer);
|
Chris@478
|
3206 }
|
Chris@595
|
3207
|
Chris@478
|
3208 CommandHistory::getInstance()->endCompoundOperation();
|
Chris@477
|
3209 }
|
Chris@479
|
3210
|
Chris@479
|
3211 updateMenuStates();
|
Chris@479
|
3212 m_recentFiles.addFile(model->getLocation());
|
Chris@479
|
3213 currentPaneChanged(m_paneStack->getCurrentPane());
|
Chris@611
|
3214
|
Chris@496
|
3215 emit audioFileLoaded();
|
Chris@477
|
3216 }
|
Chris@477
|
3217
|
Chris@477
|
3218 void
|
Chris@45
|
3219 MainWindowBase::ffwd()
|
Chris@45
|
3220 {
|
Chris@45
|
3221 if (!getMainModel()) return;
|
Chris@45
|
3222
|
Chris@435
|
3223 sv_frame_t frame = m_viewManager->getPlaybackFrame();
|
Chris@45
|
3224 ++frame;
|
Chris@45
|
3225
|
Chris@85
|
3226 Pane *pane = m_paneStack->getCurrentPane();
|
Chris@45
|
3227 Layer *layer = getSnapLayer();
|
Chris@435
|
3228 sv_samplerate_t sr = getMainModel()->getSampleRate();
|
Chris@45
|
3229
|
Chris@45
|
3230 if (!layer) {
|
Chris@45
|
3231
|
Chris@45
|
3232 frame = RealTime::realTime2Frame
|
Chris@357
|
3233 (RealTime::frame2RealTime(frame, sr) + m_defaultFfwdRwdStep, sr);
|
Chris@435
|
3234 if (frame > getMainModel()->getEndFrame()) {
|
Chris@45
|
3235 frame = getMainModel()->getEndFrame();
|
Chris@45
|
3236 }
|
Chris@45
|
3237
|
Chris@45
|
3238 } else {
|
Chris@45
|
3239
|
Chris@366
|
3240 int resolution = 0;
|
Chris@166
|
3241 if (pane) frame = pane->alignFromReference(frame);
|
Chris@85
|
3242 if (layer->snapToFeatureFrame(m_paneStack->getCurrentPane(),
|
Chris@85
|
3243 frame, resolution, Layer::SnapRight)) {
|
Chris@85
|
3244 if (pane) frame = pane->alignToReference(frame);
|
Chris@85
|
3245 } else {
|
Chris@45
|
3246 frame = getMainModel()->getEndFrame();
|
Chris@45
|
3247 }
|
Chris@45
|
3248 }
|
Chris@45
|
3249
|
Chris@45
|
3250 if (frame < 0) frame = 0;
|
Chris@45
|
3251
|
Chris@45
|
3252 if (m_viewManager->getPlaySelectionMode()) {
|
Chris@435
|
3253 frame = m_viewManager->constrainFrameToSelection(frame);
|
Chris@45
|
3254 }
|
Chris@45
|
3255
|
Chris@45
|
3256 m_viewManager->setPlaybackFrame(frame);
|
Chris@166
|
3257
|
Chris@435
|
3258 if (frame == getMainModel()->getEndFrame() &&
|
Chris@166
|
3259 m_playSource &&
|
Chris@166
|
3260 m_playSource->isPlaying() &&
|
Chris@166
|
3261 !m_viewManager->getPlayLoopMode()) {
|
Chris@166
|
3262 stop();
|
Chris@166
|
3263 }
|
Chris@45
|
3264 }
|
Chris@45
|
3265
|
Chris@45
|
3266 void
|
Chris@45
|
3267 MainWindowBase::ffwdEnd()
|
Chris@45
|
3268 {
|
Chris@45
|
3269 if (!getMainModel()) return;
|
Chris@45
|
3270
|
Chris@139
|
3271 if (m_playSource &&
|
Chris@139
|
3272 m_playSource->isPlaying() &&
|
Chris@139
|
3273 !m_viewManager->getPlayLoopMode()) {
|
Chris@139
|
3274 stop();
|
Chris@139
|
3275 }
|
Chris@139
|
3276
|
Chris@435
|
3277 sv_frame_t frame = getMainModel()->getEndFrame();
|
Chris@45
|
3278
|
Chris@45
|
3279 if (m_viewManager->getPlaySelectionMode()) {
|
Chris@45
|
3280 frame = m_viewManager->constrainFrameToSelection(frame);
|
Chris@45
|
3281 }
|
Chris@45
|
3282
|
Chris@45
|
3283 m_viewManager->setPlaybackFrame(frame);
|
Chris@45
|
3284 }
|
Chris@45
|
3285
|
Chris@45
|
3286 void
|
Chris@166
|
3287 MainWindowBase::ffwdSimilar()
|
Chris@166
|
3288 {
|
Chris@166
|
3289 if (!getMainModel()) return;
|
Chris@166
|
3290
|
Chris@166
|
3291 Layer *layer = getSnapLayer();
|
Chris@166
|
3292 if (!layer) { ffwd(); return; }
|
Chris@166
|
3293
|
Chris@166
|
3294 Pane *pane = m_paneStack->getCurrentPane();
|
Chris@435
|
3295 sv_frame_t frame = m_viewManager->getPlaybackFrame();
|
Chris@166
|
3296
|
Chris@366
|
3297 int resolution = 0;
|
Chris@166
|
3298 if (pane) frame = pane->alignFromReference(frame);
|
Chris@166
|
3299 if (layer->snapToSimilarFeature(m_paneStack->getCurrentPane(),
|
Chris@166
|
3300 frame, resolution, Layer::SnapRight)) {
|
Chris@166
|
3301 if (pane) frame = pane->alignToReference(frame);
|
Chris@166
|
3302 } else {
|
Chris@166
|
3303 frame = getMainModel()->getEndFrame();
|
Chris@166
|
3304 }
|
Chris@166
|
3305
|
Chris@166
|
3306 if (frame < 0) frame = 0;
|
Chris@166
|
3307
|
Chris@166
|
3308 if (m_viewManager->getPlaySelectionMode()) {
|
Chris@435
|
3309 frame = m_viewManager->constrainFrameToSelection(frame);
|
Chris@166
|
3310 }
|
Chris@166
|
3311
|
Chris@166
|
3312 m_viewManager->setPlaybackFrame(frame);
|
Chris@166
|
3313
|
Chris@435
|
3314 if (frame == getMainModel()->getEndFrame() &&
|
Chris@166
|
3315 m_playSource &&
|
Chris@166
|
3316 m_playSource->isPlaying() &&
|
Chris@166
|
3317 !m_viewManager->getPlayLoopMode()) {
|
Chris@166
|
3318 stop();
|
Chris@166
|
3319 }
|
Chris@166
|
3320 }
|
Chris@166
|
3321
|
Chris@166
|
3322 void
|
Chris@45
|
3323 MainWindowBase::rewind()
|
Chris@45
|
3324 {
|
Chris@45
|
3325 if (!getMainModel()) return;
|
Chris@45
|
3326
|
Chris@435
|
3327 sv_frame_t frame = m_viewManager->getPlaybackFrame();
|
Chris@45
|
3328 if (frame > 0) --frame;
|
Chris@45
|
3329
|
Chris@85
|
3330 Pane *pane = m_paneStack->getCurrentPane();
|
Chris@45
|
3331 Layer *layer = getSnapLayer();
|
Chris@435
|
3332 sv_samplerate_t sr = getMainModel()->getSampleRate();
|
Chris@45
|
3333
|
Chris@45
|
3334 // when rewinding during playback, we want to allow a period
|
Chris@45
|
3335 // following a rewind target point at which the rewind will go to
|
Chris@45
|
3336 // the prior point instead of the immediately neighbouring one
|
Chris@45
|
3337 if (m_playSource && m_playSource->isPlaying()) {
|
Chris@45
|
3338 RealTime ct = RealTime::frame2RealTime(frame, sr);
|
Chris@357
|
3339 ct = ct - RealTime::fromSeconds(0.15);
|
Chris@45
|
3340 if (ct < RealTime::zeroTime) ct = RealTime::zeroTime;
|
Chris@45
|
3341 frame = RealTime::realTime2Frame(ct, sr);
|
Chris@45
|
3342 }
|
Chris@45
|
3343
|
Chris@45
|
3344 if (!layer) {
|
Chris@45
|
3345
|
Chris@45
|
3346 frame = RealTime::realTime2Frame
|
Chris@357
|
3347 (RealTime::frame2RealTime(frame, sr) - m_defaultFfwdRwdStep, sr);
|
Chris@435
|
3348 if (frame < getMainModel()->getStartFrame()) {
|
Chris@45
|
3349 frame = getMainModel()->getStartFrame();
|
Chris@45
|
3350 }
|
Chris@45
|
3351
|
Chris@45
|
3352 } else {
|
Chris@45
|
3353
|
Chris@366
|
3354 int resolution = 0;
|
Chris@166
|
3355 if (pane) frame = pane->alignFromReference(frame);
|
Chris@85
|
3356 if (layer->snapToFeatureFrame(m_paneStack->getCurrentPane(),
|
Chris@85
|
3357 frame, resolution, Layer::SnapLeft)) {
|
Chris@85
|
3358 if (pane) frame = pane->alignToReference(frame);
|
Chris@85
|
3359 } else {
|
Chris@45
|
3360 frame = getMainModel()->getStartFrame();
|
Chris@45
|
3361 }
|
Chris@45
|
3362 }
|
Chris@45
|
3363
|
Chris@45
|
3364 if (frame < 0) frame = 0;
|
Chris@45
|
3365
|
Chris@45
|
3366 if (m_viewManager->getPlaySelectionMode()) {
|
Chris@435
|
3367 frame = m_viewManager->constrainFrameToSelection(frame);
|
Chris@45
|
3368 }
|
Chris@45
|
3369
|
Chris@45
|
3370 m_viewManager->setPlaybackFrame(frame);
|
Chris@45
|
3371 }
|
Chris@45
|
3372
|
Chris@45
|
3373 void
|
Chris@45
|
3374 MainWindowBase::rewindStart()
|
Chris@45
|
3375 {
|
Chris@45
|
3376 if (!getMainModel()) return;
|
Chris@45
|
3377
|
Chris@435
|
3378 sv_frame_t frame = getMainModel()->getStartFrame();
|
Chris@45
|
3379
|
Chris@45
|
3380 if (m_viewManager->getPlaySelectionMode()) {
|
Chris@45
|
3381 frame = m_viewManager->constrainFrameToSelection(frame);
|
Chris@45
|
3382 }
|
Chris@45
|
3383
|
Chris@45
|
3384 m_viewManager->setPlaybackFrame(frame);
|
Chris@45
|
3385 }
|
Chris@45
|
3386
|
Chris@166
|
3387 void
|
Chris@166
|
3388 MainWindowBase::rewindSimilar()
|
Chris@166
|
3389 {
|
Chris@166
|
3390 if (!getMainModel()) return;
|
Chris@166
|
3391
|
Chris@166
|
3392 Layer *layer = getSnapLayer();
|
Chris@166
|
3393 if (!layer) { rewind(); return; }
|
Chris@166
|
3394
|
Chris@166
|
3395 Pane *pane = m_paneStack->getCurrentPane();
|
Chris@435
|
3396 sv_frame_t frame = m_viewManager->getPlaybackFrame();
|
Chris@166
|
3397
|
Chris@366
|
3398 int resolution = 0;
|
Chris@166
|
3399 if (pane) frame = pane->alignFromReference(frame);
|
Chris@166
|
3400 if (layer->snapToSimilarFeature(m_paneStack->getCurrentPane(),
|
Chris@166
|
3401 frame, resolution, Layer::SnapLeft)) {
|
Chris@166
|
3402 if (pane) frame = pane->alignToReference(frame);
|
Chris@166
|
3403 } else {
|
Chris@166
|
3404 frame = getMainModel()->getStartFrame();
|
Chris@166
|
3405 }
|
Chris@166
|
3406
|
Chris@166
|
3407 if (frame < 0) frame = 0;
|
Chris@166
|
3408
|
Chris@166
|
3409 if (m_viewManager->getPlaySelectionMode()) {
|
Chris@435
|
3410 frame = m_viewManager->constrainFrameToSelection(frame);
|
Chris@166
|
3411 }
|
Chris@166
|
3412
|
Chris@166
|
3413 m_viewManager->setPlaybackFrame(frame);
|
Chris@166
|
3414 }
|
Chris@166
|
3415
|
Chris@45
|
3416 Layer *
|
Chris@45
|
3417 MainWindowBase::getSnapLayer() const
|
Chris@45
|
3418 {
|
Chris@45
|
3419 Pane *pane = m_paneStack->getCurrentPane();
|
Chris@636
|
3420 if (!pane) return nullptr;
|
Chris@45
|
3421
|
Chris@45
|
3422 Layer *layer = pane->getSelectedLayer();
|
Chris@45
|
3423
|
Chris@45
|
3424 if (!dynamic_cast<TimeInstantLayer *>(layer) &&
|
Chris@45
|
3425 !dynamic_cast<TimeValueLayer *>(layer) &&
|
Chris@194
|
3426 !dynamic_cast<RegionLayer *>(layer) &&
|
Chris@45
|
3427 !dynamic_cast<TimeRulerLayer *>(layer)) {
|
Chris@45
|
3428
|
Chris@636
|
3429 layer = nullptr;
|
Chris@45
|
3430
|
Chris@45
|
3431 for (int i = pane->getLayerCount(); i > 0; --i) {
|
Chris@45
|
3432 Layer *l = pane->getLayer(i-1);
|
Chris@45
|
3433 if (dynamic_cast<TimeRulerLayer *>(l)) {
|
Chris@45
|
3434 layer = l;
|
Chris@45
|
3435 break;
|
Chris@45
|
3436 }
|
Chris@45
|
3437 }
|
Chris@45
|
3438 }
|
Chris@45
|
3439
|
Chris@45
|
3440 return layer;
|
Chris@45
|
3441 }
|
Chris@45
|
3442
|
Chris@45
|
3443 void
|
Chris@45
|
3444 MainWindowBase::stop()
|
Chris@45
|
3445 {
|
Chris@516
|
3446 if (m_recordTarget &&
|
Chris@516
|
3447 m_recordTarget->isRecording()) {
|
Chris@477
|
3448 m_recordTarget->stopRecording();
|
Chris@477
|
3449 }
|
Chris@516
|
3450
|
Chris@516
|
3451 if (!m_playSource) return;
|
Chris@516
|
3452
|
Chris@45
|
3453 m_playSource->stop();
|
Chris@45
|
3454
|
Chris@611
|
3455 SVCERR << "MainWindowBase::stop: suspending" << endl;
|
Chris@611
|
3456
|
Chris@487
|
3457 if (m_audioIO) m_audioIO->suspend();
|
Chris@509
|
3458 else if (m_playTarget) m_playTarget->suspend();
|
Chris@487
|
3459
|
Chris@45
|
3460 if (m_paneStack && m_paneStack->getCurrentPane()) {
|
Chris@45
|
3461 updateVisibleRangeDisplay(m_paneStack->getCurrentPane());
|
Chris@45
|
3462 } else {
|
Chris@45
|
3463 m_myStatusMessage = "";
|
Chris@378
|
3464 getStatusLabel()->setText("");
|
Chris@45
|
3465 }
|
Chris@45
|
3466 }
|
Chris@45
|
3467
|
Chris@45
|
3468 MainWindowBase::AddPaneCommand::AddPaneCommand(MainWindowBase *mw) :
|
Chris@45
|
3469 m_mw(mw),
|
Chris@636
|
3470 m_pane(nullptr),
|
Chris@636
|
3471 m_prevCurrentPane(nullptr),
|
Chris@45
|
3472 m_added(false)
|
Chris@45
|
3473 {
|
Chris@45
|
3474 }
|
Chris@45
|
3475
|
Chris@45
|
3476 MainWindowBase::AddPaneCommand::~AddPaneCommand()
|
Chris@45
|
3477 {
|
Chris@45
|
3478 if (m_pane && !m_added) {
|
Chris@595
|
3479 m_mw->m_paneStack->deletePane(m_pane);
|
Chris@45
|
3480 }
|
Chris@45
|
3481 }
|
Chris@45
|
3482
|
Chris@45
|
3483 QString
|
Chris@45
|
3484 MainWindowBase::AddPaneCommand::getName() const
|
Chris@45
|
3485 {
|
Chris@45
|
3486 return tr("Add Pane");
|
Chris@45
|
3487 }
|
Chris@45
|
3488
|
Chris@45
|
3489 void
|
Chris@45
|
3490 MainWindowBase::AddPaneCommand::execute()
|
Chris@45
|
3491 {
|
Chris@45
|
3492 if (!m_pane) {
|
Chris@595
|
3493 m_prevCurrentPane = m_mw->m_paneStack->getCurrentPane();
|
Chris@595
|
3494 m_pane = m_mw->m_paneStack->addPane();
|
Chris@45
|
3495
|
Chris@45
|
3496 connect(m_pane, SIGNAL(contextHelpChanged(const QString &)),
|
Chris@45
|
3497 m_mw, SLOT(contextHelpChanged(const QString &)));
|
Chris@45
|
3498 } else {
|
Chris@595
|
3499 m_mw->m_paneStack->showPane(m_pane);
|
Chris@45
|
3500 }
|
Chris@45
|
3501
|
Chris@45
|
3502 m_mw->m_paneStack->setCurrentPane(m_pane);
|
Chris@45
|
3503 m_added = true;
|
Chris@45
|
3504 }
|
Chris@45
|
3505
|
Chris@45
|
3506 void
|
Chris@45
|
3507 MainWindowBase::AddPaneCommand::unexecute()
|
Chris@45
|
3508 {
|
Chris@45
|
3509 m_mw->m_paneStack->hidePane(m_pane);
|
Chris@45
|
3510 m_mw->m_paneStack->setCurrentPane(m_prevCurrentPane);
|
Chris@45
|
3511 m_added = false;
|
Chris@45
|
3512 }
|
Chris@45
|
3513
|
Chris@45
|
3514 MainWindowBase::RemovePaneCommand::RemovePaneCommand(MainWindowBase *mw, Pane *pane) :
|
Chris@45
|
3515 m_mw(mw),
|
Chris@45
|
3516 m_pane(pane),
|
Chris@636
|
3517 m_prevCurrentPane(nullptr),
|
Chris@45
|
3518 m_added(true)
|
Chris@45
|
3519 {
|
Chris@45
|
3520 }
|
Chris@45
|
3521
|
Chris@45
|
3522 MainWindowBase::RemovePaneCommand::~RemovePaneCommand()
|
Chris@45
|
3523 {
|
Chris@45
|
3524 if (m_pane && !m_added) {
|
Chris@595
|
3525 m_mw->m_paneStack->deletePane(m_pane);
|
Chris@45
|
3526 }
|
Chris@45
|
3527 }
|
Chris@45
|
3528
|
Chris@45
|
3529 QString
|
Chris@45
|
3530 MainWindowBase::RemovePaneCommand::getName() const
|
Chris@45
|
3531 {
|
Chris@45
|
3532 return tr("Remove Pane");
|
Chris@45
|
3533 }
|
Chris@45
|
3534
|
Chris@45
|
3535 void
|
Chris@45
|
3536 MainWindowBase::RemovePaneCommand::execute()
|
Chris@45
|
3537 {
|
Chris@45
|
3538 m_prevCurrentPane = m_mw->m_paneStack->getCurrentPane();
|
Chris@45
|
3539 m_mw->m_paneStack->hidePane(m_pane);
|
Chris@45
|
3540 m_added = false;
|
Chris@45
|
3541 }
|
Chris@45
|
3542
|
Chris@45
|
3543 void
|
Chris@45
|
3544 MainWindowBase::RemovePaneCommand::unexecute()
|
Chris@45
|
3545 {
|
Chris@45
|
3546 m_mw->m_paneStack->showPane(m_pane);
|
Chris@45
|
3547 m_mw->m_paneStack->setCurrentPane(m_prevCurrentPane);
|
Chris@45
|
3548 m_added = true;
|
Chris@45
|
3549 }
|
Chris@45
|
3550
|
Chris@45
|
3551 void
|
Chris@45
|
3552 MainWindowBase::deleteCurrentPane()
|
Chris@45
|
3553 {
|
Chris@45
|
3554 CommandHistory::getInstance()->startCompoundOperation
|
Chris@595
|
3555 (tr("Delete Pane"), true);
|
Chris@45
|
3556
|
Chris@45
|
3557 Pane *pane = m_paneStack->getCurrentPane();
|
Chris@45
|
3558 if (pane) {
|
Chris@595
|
3559 while (pane->getLayerCount() > 0) {
|
Chris@595
|
3560 Layer *layer = pane->getLayer(0);
|
Chris@595
|
3561 if (layer) {
|
Chris@595
|
3562 m_document->removeLayerFromView(pane, layer);
|
Chris@595
|
3563 } else {
|
Chris@595
|
3564 break;
|
Chris@595
|
3565 }
|
Chris@595
|
3566 }
|
Chris@595
|
3567
|
Chris@595
|
3568 RemovePaneCommand *command = new RemovePaneCommand(this, pane);
|
Chris@595
|
3569 CommandHistory::getInstance()->addCommand(command);
|
Chris@45
|
3570 }
|
Chris@45
|
3571
|
Chris@45
|
3572 CommandHistory::getInstance()->endCompoundOperation();
|
Chris@45
|
3573
|
Chris@45
|
3574 updateMenuStates();
|
Chris@45
|
3575 }
|
Chris@45
|
3576
|
Chris@45
|
3577 void
|
Chris@45
|
3578 MainWindowBase::deleteCurrentLayer()
|
Chris@45
|
3579 {
|
Chris@45
|
3580 Pane *pane = m_paneStack->getCurrentPane();
|
Chris@45
|
3581 if (pane) {
|
Chris@595
|
3582 Layer *layer = pane->getSelectedLayer();
|
Chris@595
|
3583 if (layer) {
|
Chris@595
|
3584 m_document->removeLayerFromView(pane, layer);
|
Chris@595
|
3585 }
|
Chris@45
|
3586 }
|
Chris@45
|
3587 updateMenuStates();
|
Chris@45
|
3588 }
|
Chris@45
|
3589
|
Chris@45
|
3590 void
|
Chris@123
|
3591 MainWindowBase::editCurrentLayer()
|
Chris@123
|
3592 {
|
Chris@636
|
3593 Layer *layer = nullptr;
|
Chris@123
|
3594 Pane *pane = m_paneStack->getCurrentPane();
|
Chris@123
|
3595 if (pane) layer = pane->getSelectedLayer();
|
Chris@123
|
3596 if (!layer) return;
|
Chris@123
|
3597
|
Chris@123
|
3598 Model *model = layer->getModel();
|
Chris@123
|
3599 if (!model) return;
|
Chris@123
|
3600
|
Chris@124
|
3601 TabularModel *tabular = dynamic_cast<TabularModel *>(model);
|
Chris@124
|
3602 if (!tabular) {
|
Chris@124
|
3603 //!!! how to prevent this function from being active if not
|
Chris@124
|
3604 //appropriate model type? or will we ultimately support
|
Chris@124
|
3605 //tabular display for all editable models?
|
Chris@233
|
3606 SVDEBUG << "NOTE: Not a tabular model" << endl;
|
Chris@124
|
3607 return;
|
Chris@124
|
3608 }
|
Chris@124
|
3609
|
Chris@123
|
3610 if (m_layerDataDialogMap.find(layer) != m_layerDataDialogMap.end()) {
|
Chris@126
|
3611 if (!m_layerDataDialogMap[layer].isNull()) {
|
Chris@126
|
3612 m_layerDataDialogMap[layer]->show();
|
Chris@126
|
3613 m_layerDataDialogMap[layer]->raise();
|
Chris@126
|
3614 return;
|
Chris@126
|
3615 }
|
Chris@123
|
3616 }
|
Chris@123
|
3617
|
Chris@125
|
3618 QString title = layer->getLayerPresentationName();
|
Chris@125
|
3619
|
Chris@125
|
3620 ModelDataTableDialog *dialog = new ModelDataTableDialog(tabular, title, this);
|
Chris@128
|
3621 dialog->setAttribute(Qt::WA_DeleteOnClose);
|
Chris@128
|
3622
|
Chris@128
|
3623 connectLayerEditDialog(dialog);
|
Chris@123
|
3624
|
Chris@128
|
3625 m_layerDataDialogMap[layer] = dialog;
|
Chris@128
|
3626 m_viewDataDialogMap[pane].insert(dialog);
|
Chris@128
|
3627
|
Chris@128
|
3628 dialog->show();
|
Chris@128
|
3629 }
|
Chris@128
|
3630
|
Chris@128
|
3631 void
|
Chris@128
|
3632 MainWindowBase::connectLayerEditDialog(ModelDataTableDialog *dialog)
|
Chris@128
|
3633 {
|
Chris@123
|
3634 connect(m_viewManager,
|
Chris@435
|
3635 SIGNAL(globalCentreFrameChanged(sv_frame_t)),
|
Chris@123
|
3636 dialog,
|
Chris@435
|
3637 SLOT(userScrolledToFrame(sv_frame_t)));
|
Chris@127
|
3638
|
Chris@127
|
3639 connect(m_viewManager,
|
Chris@435
|
3640 SIGNAL(playbackFrameChanged(sv_frame_t)),
|
Chris@127
|
3641 dialog,
|
Chris@435
|
3642 SLOT(playbackScrolledToFrame(sv_frame_t)));
|
Chris@127
|
3643
|
Chris@123
|
3644 connect(dialog,
|
Chris@435
|
3645 SIGNAL(scrollToFrame(sv_frame_t)),
|
Chris@123
|
3646 m_viewManager,
|
Chris@435
|
3647 SLOT(setGlobalCentreFrame(sv_frame_t)));
|
Chris@129
|
3648
|
Chris@129
|
3649 connect(dialog,
|
Chris@435
|
3650 SIGNAL(scrollToFrame(sv_frame_t)),
|
Chris@129
|
3651 m_viewManager,
|
Chris@435
|
3652 SLOT(setPlaybackFrame(sv_frame_t)));
|
Chris@128
|
3653 }
|
Chris@123
|
3654
|
Chris@123
|
3655 void
|
Chris@73
|
3656 MainWindowBase::previousPane()
|
Chris@73
|
3657 {
|
Chris@73
|
3658 if (!m_paneStack) return;
|
Chris@73
|
3659
|
Chris@73
|
3660 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@73
|
3661 if (!currentPane) return;
|
Chris@73
|
3662
|
Chris@73
|
3663 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
|
Chris@73
|
3664 if (m_paneStack->getPane(i) == currentPane) {
|
Chris@73
|
3665 if (i == 0) return;
|
Chris@73
|
3666 m_paneStack->setCurrentPane(m_paneStack->getPane(i-1));
|
Chris@73
|
3667 updateMenuStates();
|
Chris@73
|
3668 return;
|
Chris@73
|
3669 }
|
Chris@73
|
3670 }
|
Chris@73
|
3671 }
|
Chris@73
|
3672
|
Chris@73
|
3673 void
|
Chris@73
|
3674 MainWindowBase::nextPane()
|
Chris@73
|
3675 {
|
Chris@73
|
3676 if (!m_paneStack) return;
|
Chris@73
|
3677
|
Chris@73
|
3678 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@73
|
3679 if (!currentPane) return;
|
Chris@73
|
3680
|
Chris@73
|
3681 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
|
Chris@73
|
3682 if (m_paneStack->getPane(i) == currentPane) {
|
Chris@73
|
3683 if (i == m_paneStack->getPaneCount()-1) return;
|
Chris@73
|
3684 m_paneStack->setCurrentPane(m_paneStack->getPane(i+1));
|
Chris@73
|
3685 updateMenuStates();
|
Chris@73
|
3686 return;
|
Chris@73
|
3687 }
|
Chris@73
|
3688 }
|
Chris@73
|
3689 }
|
Chris@73
|
3690
|
Chris@73
|
3691 void
|
Chris@73
|
3692 MainWindowBase::previousLayer()
|
Chris@73
|
3693 {
|
Chris@73
|
3694 if (!m_paneStack) return;
|
Chris@73
|
3695
|
Chris@73
|
3696 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@73
|
3697 if (!currentPane) return;
|
Chris@73
|
3698
|
Chris@403
|
3699 int count = currentPane->getLayerCount();
|
Chris@403
|
3700 if (count == 0) return;
|
Chris@403
|
3701
|
Chris@73
|
3702 Layer *currentLayer = currentPane->getSelectedLayer();
|
Chris@403
|
3703
|
Chris@403
|
3704 if (!currentLayer) {
|
Chris@403
|
3705 // The pane itself is current
|
Chris@403
|
3706 m_paneStack->setCurrentLayer
|
Chris@403
|
3707 (currentPane, currentPane->getFixedOrderLayer(count-1));
|
Chris@403
|
3708 } else {
|
Chris@403
|
3709 for (int i = 0; i < count; ++i) {
|
Chris@403
|
3710 if (currentPane->getFixedOrderLayer(i) == currentLayer) {
|
Chris@403
|
3711 if (i == 0) {
|
Chris@403
|
3712 m_paneStack->setCurrentLayer
|
Chris@636
|
3713 (currentPane, nullptr); // pane
|
Chris@403
|
3714 } else {
|
Chris@403
|
3715 m_paneStack->setCurrentLayer
|
Chris@403
|
3716 (currentPane, currentPane->getFixedOrderLayer(i-1));
|
Chris@403
|
3717 }
|
Chris@403
|
3718 break;
|
Chris@403
|
3719 }
|
Chris@73
|
3720 }
|
Chris@73
|
3721 }
|
Chris@403
|
3722
|
Chris@403
|
3723 updateMenuStates();
|
Chris@73
|
3724 }
|
Chris@73
|
3725
|
Chris@73
|
3726 void
|
Chris@73
|
3727 MainWindowBase::nextLayer()
|
Chris@73
|
3728 {
|
Chris@73
|
3729 if (!m_paneStack) return;
|
Chris@73
|
3730
|
Chris@73
|
3731 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@73
|
3732 if (!currentPane) return;
|
Chris@73
|
3733
|
Chris@403
|
3734 int count = currentPane->getLayerCount();
|
Chris@403
|
3735 if (count == 0) return;
|
Chris@403
|
3736
|
Chris@73
|
3737 Layer *currentLayer = currentPane->getSelectedLayer();
|
Chris@403
|
3738
|
Chris@403
|
3739 if (!currentLayer) {
|
Chris@403
|
3740 // The pane itself is current
|
Chris@403
|
3741 m_paneStack->setCurrentLayer
|
Chris@403
|
3742 (currentPane, currentPane->getFixedOrderLayer(0));
|
Chris@403
|
3743 } else {
|
Chris@403
|
3744 for (int i = 0; i < count; ++i) {
|
Chris@403
|
3745 if (currentPane->getFixedOrderLayer(i) == currentLayer) {
|
Chris@403
|
3746 if (i == currentPane->getLayerCount()-1) {
|
Chris@403
|
3747 m_paneStack->setCurrentLayer
|
Chris@636
|
3748 (currentPane, nullptr); // pane
|
Chris@403
|
3749 } else {
|
Chris@403
|
3750 m_paneStack->setCurrentLayer
|
Chris@403
|
3751 (currentPane, currentPane->getFixedOrderLayer(i+1));
|
Chris@403
|
3752 }
|
Chris@403
|
3753 break;
|
Chris@403
|
3754 }
|
Chris@73
|
3755 }
|
Chris@73
|
3756 }
|
Chris@403
|
3757
|
Chris@403
|
3758 updateMenuStates();
|
Chris@73
|
3759 }
|
Chris@73
|
3760
|
Chris@73
|
3761 void
|
Chris@435
|
3762 MainWindowBase::playbackFrameChanged(sv_frame_t frame)
|
Chris@45
|
3763 {
|
Chris@45
|
3764 if (!(m_playSource && m_playSource->isPlaying()) || !getMainModel()) return;
|
Chris@45
|
3765
|
Chris@187
|
3766 updatePositionStatusDisplays();
|
Chris@187
|
3767
|
Chris@45
|
3768 RealTime now = RealTime::frame2RealTime
|
Chris@45
|
3769 (frame, getMainModel()->getSampleRate());
|
Chris@45
|
3770
|
Chris@45
|
3771 if (now.sec == m_lastPlayStatusSec) return;
|
Chris@45
|
3772
|
Chris@45
|
3773 RealTime then = RealTime::frame2RealTime
|
Chris@45
|
3774 (m_playSource->getPlayEndFrame(), getMainModel()->getSampleRate());
|
Chris@45
|
3775
|
Chris@45
|
3776 QString nowStr;
|
Chris@45
|
3777 QString thenStr;
|
Chris@45
|
3778 QString remainingStr;
|
Chris@45
|
3779
|
Chris@45
|
3780 if (then.sec > 10) {
|
Chris@45
|
3781 nowStr = now.toSecText().c_str();
|
Chris@45
|
3782 thenStr = then.toSecText().c_str();
|
Chris@45
|
3783 remainingStr = (then - now).toSecText().c_str();
|
Chris@45
|
3784 m_lastPlayStatusSec = now.sec;
|
Chris@45
|
3785 } else {
|
Chris@45
|
3786 nowStr = now.toText(true).c_str();
|
Chris@45
|
3787 thenStr = then.toText(true).c_str();
|
Chris@45
|
3788 remainingStr = (then - now).toText(true).c_str();
|
Chris@45
|
3789 }
|
Chris@45
|
3790
|
Chris@45
|
3791 m_myStatusMessage = tr("Playing: %1 of %2 (%3 remaining)")
|
Chris@45
|
3792 .arg(nowStr).arg(thenStr).arg(remainingStr);
|
Chris@45
|
3793
|
Chris@378
|
3794 getStatusLabel()->setText(m_myStatusMessage);
|
Chris@45
|
3795 }
|
Chris@45
|
3796
|
Chris@45
|
3797 void
|
Chris@486
|
3798 MainWindowBase::recordDurationChanged(sv_frame_t frame, sv_samplerate_t rate)
|
Chris@486
|
3799 {
|
Chris@486
|
3800 RealTime duration = RealTime::frame2RealTime(frame, rate);
|
Chris@486
|
3801 QString durStr = duration.toSecText().c_str();
|
Chris@486
|
3802
|
Chris@486
|
3803 m_myStatusMessage = tr("Recording: %1").arg(durStr);
|
Chris@486
|
3804
|
Chris@486
|
3805 getStatusLabel()->setText(m_myStatusMessage);
|
Chris@486
|
3806 }
|
Chris@486
|
3807
|
Chris@486
|
3808 void
|
Chris@435
|
3809 MainWindowBase::globalCentreFrameChanged(sv_frame_t )
|
Chris@45
|
3810 {
|
Chris@45
|
3811 if ((m_playSource && m_playSource->isPlaying()) || !getMainModel()) return;
|
Chris@636
|
3812 Pane *p = nullptr;
|
Chris@45
|
3813 if (!m_paneStack || !(p = m_paneStack->getCurrentPane())) return;
|
Chris@45
|
3814 if (!p->getFollowGlobalPan()) return;
|
Chris@45
|
3815 updateVisibleRangeDisplay(p);
|
Chris@45
|
3816 }
|
Chris@45
|
3817
|
Chris@45
|
3818 void
|
Chris@435
|
3819 MainWindowBase::viewCentreFrameChanged(View *v, sv_frame_t frame)
|
Chris@45
|
3820 {
|
Chris@233
|
3821 // SVDEBUG << "MainWindowBase::viewCentreFrameChanged(" << v << "," << frame << ")" << endl;
|
Chris@123
|
3822
|
Chris@123
|
3823 if (m_viewDataDialogMap.find(v) != m_viewDataDialogMap.end()) {
|
Chris@123
|
3824 for (DataDialogSet::iterator i = m_viewDataDialogMap[v].begin();
|
Chris@123
|
3825 i != m_viewDataDialogMap[v].end(); ++i) {
|
Chris@127
|
3826 (*i)->userScrolledToFrame(frame);
|
Chris@123
|
3827 }
|
Chris@123
|
3828 }
|
Chris@45
|
3829 if ((m_playSource && m_playSource->isPlaying()) || !getMainModel()) return;
|
Chris@636
|
3830 Pane *p = nullptr;
|
Chris@45
|
3831 if (!m_paneStack || !(p = m_paneStack->getCurrentPane())) return;
|
Chris@45
|
3832 if (v == p) updateVisibleRangeDisplay(p);
|
Chris@45
|
3833 }
|
Chris@45
|
3834
|
Chris@45
|
3835 void
|
Chris@624
|
3836 MainWindowBase::viewZoomLevelChanged(View *v, ZoomLevel, bool )
|
Chris@45
|
3837 {
|
Chris@45
|
3838 if ((m_playSource && m_playSource->isPlaying()) || !getMainModel()) return;
|
Chris@636
|
3839 Pane *p = nullptr;
|
Chris@45
|
3840 if (!m_paneStack || !(p = m_paneStack->getCurrentPane())) return;
|
Chris@45
|
3841 if (v == p) updateVisibleRangeDisplay(p);
|
Chris@45
|
3842 }
|
Chris@45
|
3843
|
Chris@45
|
3844 void
|
Chris@45
|
3845 MainWindowBase::layerAdded(Layer *)
|
Chris@45
|
3846 {
|
Chris@233
|
3847 // SVDEBUG << "MainWindowBase::layerAdded(" << layer << ")" << endl;
|
Chris@45
|
3848 updateMenuStates();
|
Chris@45
|
3849 }
|
Chris@45
|
3850
|
Chris@45
|
3851 void
|
Chris@45
|
3852 MainWindowBase::layerRemoved(Layer *)
|
Chris@45
|
3853 {
|
Chris@233
|
3854 // SVDEBUG << "MainWindowBase::layerRemoved(" << layer << ")" << endl;
|
Chris@45
|
3855 updateMenuStates();
|
Chris@45
|
3856 }
|
Chris@45
|
3857
|
Chris@45
|
3858 void
|
Chris@45
|
3859 MainWindowBase::layerAboutToBeDeleted(Layer *layer)
|
Chris@45
|
3860 {
|
Chris@233
|
3861 // SVDEBUG << "MainWindowBase::layerAboutToBeDeleted(" << layer << ")" << endl;
|
Chris@123
|
3862
|
Chris@128
|
3863 removeLayerEditDialog(layer);
|
Chris@123
|
3864
|
Chris@47
|
3865 if (m_timeRulerLayer && (layer == m_timeRulerLayer)) {
|
Chris@595
|
3866 // cerr << "(this is the time ruler layer)" << endl;
|
Chris@636
|
3867 m_timeRulerLayer = nullptr;
|
Chris@45
|
3868 }
|
Chris@45
|
3869 }
|
Chris@45
|
3870
|
Chris@45
|
3871 void
|
Chris@45
|
3872 MainWindowBase::layerInAView(Layer *layer, bool inAView)
|
Chris@45
|
3873 {
|
Chris@233
|
3874 // SVDEBUG << "MainWindowBase::layerInAView(" << layer << "," << inAView << ")" << endl;
|
Chris@128
|
3875
|
Chris@128
|
3876 if (!inAView) removeLayerEditDialog(layer);
|
Chris@45
|
3877
|
Chris@45
|
3878 // Check whether we need to add or remove model from play source
|
Chris@45
|
3879 Model *model = layer->getModel();
|
Chris@45
|
3880 if (model) {
|
Chris@45
|
3881 if (inAView) {
|
Chris@45
|
3882 m_playSource->addModel(model);
|
Chris@45
|
3883 } else {
|
Chris@45
|
3884 bool found = false;
|
Chris@45
|
3885 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
|
Chris@45
|
3886 Pane *pane = m_paneStack->getPane(i);
|
Chris@45
|
3887 if (!pane) continue;
|
Chris@45
|
3888 for (int j = 0; j < pane->getLayerCount(); ++j) {
|
Chris@45
|
3889 Layer *pl = pane->getLayer(j);
|
Chris@183
|
3890 if (pl &&
|
Chris@183
|
3891 !dynamic_cast<TimeRulerLayer *>(pl) &&
|
Chris@183
|
3892 (pl->getModel() == model)) {
|
Chris@45
|
3893 found = true;
|
Chris@45
|
3894 break;
|
Chris@45
|
3895 }
|
Chris@45
|
3896 }
|
Chris@45
|
3897 if (found) break;
|
Chris@45
|
3898 }
|
Chris@173
|
3899 if (!found) {
|
Chris@173
|
3900 m_playSource->removeModel(model);
|
Chris@173
|
3901 }
|
Chris@45
|
3902 }
|
Chris@45
|
3903 }
|
Chris@45
|
3904
|
Chris@45
|
3905 updateMenuStates();
|
Chris@45
|
3906 }
|
Chris@45
|
3907
|
Chris@45
|
3908 void
|
Chris@128
|
3909 MainWindowBase::removeLayerEditDialog(Layer *layer)
|
Chris@128
|
3910 {
|
Chris@128
|
3911 if (m_layerDataDialogMap.find(layer) != m_layerDataDialogMap.end()) {
|
Chris@128
|
3912
|
Chris@128
|
3913 ModelDataTableDialog *dialog = m_layerDataDialogMap[layer];
|
Chris@128
|
3914
|
Chris@128
|
3915 for (ViewDataDialogMap::iterator vi = m_viewDataDialogMap.begin();
|
Chris@128
|
3916 vi != m_viewDataDialogMap.end(); ++vi) {
|
Chris@128
|
3917 vi->second.erase(dialog);
|
Chris@128
|
3918 }
|
Chris@128
|
3919
|
Chris@128
|
3920 m_layerDataDialogMap.erase(layer);
|
Chris@128
|
3921 delete dialog;
|
Chris@128
|
3922 }
|
Chris@128
|
3923 }
|
Chris@128
|
3924
|
Chris@128
|
3925 void
|
Chris@45
|
3926 MainWindowBase::modelAdded(Model *model)
|
Chris@45
|
3927 {
|
Chris@233
|
3928 // SVDEBUG << "MainWindowBase::modelAdded(" << model << ")" << endl;
|
Chris@595
|
3929 std::cerr << "\nAdding model " << model->getTypeName() << " to playsource " << std::endl;
|
Chris@45
|
3930 m_playSource->addModel(model);
|
Chris@45
|
3931 }
|
Chris@45
|
3932
|
Chris@45
|
3933 void
|
Chris@45
|
3934 MainWindowBase::mainModelChanged(WaveFileModel *model)
|
Chris@45
|
3935 {
|
Chris@233
|
3936 // SVDEBUG << "MainWindowBase::mainModelChanged(" << model << ")" << endl;
|
Chris@45
|
3937 updateDescriptionLabel();
|
Chris@45
|
3938 if (model) m_viewManager->setMainModelSampleRate(model->getSampleRate());
|
Chris@475
|
3939 if (model && !(m_playTarget || m_audioIO) &&
|
Chris@475
|
3940 (m_soundOptions & WithAudioOutput)) {
|
Chris@475
|
3941 createAudioIO();
|
Chris@360
|
3942 }
|
Chris@45
|
3943 }
|
Chris@45
|
3944
|
Chris@45
|
3945 void
|
Chris@45
|
3946 MainWindowBase::modelAboutToBeDeleted(Model *model)
|
Chris@45
|
3947 {
|
Chris@233
|
3948 // SVDEBUG << "MainWindowBase::modelAboutToBeDeleted(" << model << ")" << endl;
|
Chris@45
|
3949 if (model == m_viewManager->getPlaybackModel()) {
|
Chris@636
|
3950 m_viewManager->setPlaybackModel(nullptr);
|
Chris@45
|
3951 }
|
Chris@45
|
3952 m_playSource->removeModel(model);
|
Chris@45
|
3953 }
|
Chris@45
|
3954
|
Chris@45
|
3955 void
|
Chris@55
|
3956 MainWindowBase::paneDeleteButtonClicked(Pane *pane)
|
Chris@55
|
3957 {
|
Chris@55
|
3958 bool found = false;
|
Chris@55
|
3959 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
|
Chris@55
|
3960 if (m_paneStack->getPane(i) == pane) {
|
Chris@55
|
3961 found = true;
|
Chris@55
|
3962 break;
|
Chris@55
|
3963 }
|
Chris@55
|
3964 }
|
Chris@55
|
3965 if (!found) {
|
Chris@233
|
3966 SVDEBUG << "MainWindowBase::paneDeleteButtonClicked: Unknown pane "
|
Chris@229
|
3967 << pane << endl;
|
Chris@55
|
3968 return;
|
Chris@55
|
3969 }
|
Chris@55
|
3970
|
Chris@55
|
3971 CommandHistory::getInstance()->startCompoundOperation
|
Chris@595
|
3972 (tr("Delete Pane"), true);
|
Chris@55
|
3973
|
Chris@55
|
3974 while (pane->getLayerCount() > 0) {
|
Chris@637
|
3975 Layer *layer = pane->getLayer(pane->getLayerCount() - 1);
|
Chris@55
|
3976 if (layer) {
|
Chris@55
|
3977 m_document->removeLayerFromView(pane, layer);
|
Chris@55
|
3978 } else {
|
Chris@55
|
3979 break;
|
Chris@55
|
3980 }
|
Chris@55
|
3981 }
|
Chris@55
|
3982
|
Chris@55
|
3983 RemovePaneCommand *command = new RemovePaneCommand(this, pane);
|
Chris@55
|
3984 CommandHistory::getInstance()->addCommand(command);
|
Chris@55
|
3985
|
Chris@55
|
3986 CommandHistory::getInstance()->endCompoundOperation();
|
Chris@55
|
3987
|
Chris@55
|
3988 updateMenuStates();
|
Chris@55
|
3989 }
|
Chris@55
|
3990
|
Chris@55
|
3991 void
|
Chris@429
|
3992 MainWindowBase::alignmentComplete(AlignmentModel *model)
|
Chris@429
|
3993 {
|
Chris@429
|
3994 cerr << "MainWindowBase::alignmentComplete(" << model << ")" << endl;
|
Chris@429
|
3995 }
|
Chris@429
|
3996
|
Chris@429
|
3997 void
|
Chris@45
|
3998 MainWindowBase::pollOSC()
|
Chris@45
|
3999 {
|
Chris@45
|
4000 if (!m_oscQueue || m_oscQueue->isEmpty()) return;
|
Chris@233
|
4001 SVDEBUG << "MainWindowBase::pollOSC: have " << m_oscQueue->getMessagesAvailable() << " messages" << endl;
|
Chris@45
|
4002
|
Chris@45
|
4003 if (m_openingAudioFile) return;
|
Chris@45
|
4004
|
Chris@45
|
4005 OSCMessage message = m_oscQueue->readMessage();
|
Chris@45
|
4006
|
Chris@45
|
4007 if (message.getTarget() != 0) {
|
Chris@45
|
4008 return; //!!! for now -- this class is target 0, others not handled yet
|
Chris@45
|
4009 }
|
Chris@45
|
4010
|
Chris@45
|
4011 handleOSCMessage(message);
|
Chris@45
|
4012 }
|
Chris@45
|
4013
|
Chris@45
|
4014 void
|
Chris@45
|
4015 MainWindowBase::inProgressSelectionChanged()
|
Chris@45
|
4016 {
|
Chris@636
|
4017 Pane *currentPane = nullptr;
|
Chris@45
|
4018 if (m_paneStack) currentPane = m_paneStack->getCurrentPane();
|
justin@331
|
4019 if (currentPane) {
|
justin@331
|
4020 //cerr << "JTEST: mouse event on selection pane" << endl;
|
justin@331
|
4021 updateVisibleRangeDisplay(currentPane);
|
justin@331
|
4022 }
|
Chris@45
|
4023 }
|
Chris@45
|
4024
|
Chris@45
|
4025 void
|
Chris@45
|
4026 MainWindowBase::contextHelpChanged(const QString &s)
|
Chris@45
|
4027 {
|
Chris@378
|
4028 QLabel *lab = getStatusLabel();
|
Chris@375
|
4029
|
Chris@45
|
4030 if (s == "" && m_myStatusMessage != "") {
|
Chris@378
|
4031 if (lab->text() != m_myStatusMessage) {
|
Chris@378
|
4032 lab->setText(m_myStatusMessage);
|
Chris@375
|
4033 }
|
Chris@45
|
4034 return;
|
Chris@45
|
4035 }
|
Chris@375
|
4036
|
Chris@378
|
4037 lab->setText(s);
|
Chris@45
|
4038 }
|
Chris@45
|
4039
|
Chris@45
|
4040 void
|
Chris@45
|
4041 MainWindowBase::openHelpUrl(QString url)
|
Chris@45
|
4042 {
|
Chris@45
|
4043 // This method mostly lifted from Qt Assistant source code
|
Chris@45
|
4044
|
Chris@45
|
4045 QProcess *process = new QProcess(this);
|
Chris@45
|
4046 connect(process, SIGNAL(finished(int)), process, SLOT(deleteLater()));
|
Chris@45
|
4047
|
Chris@45
|
4048 QStringList args;
|
Chris@45
|
4049
|
Chris@45
|
4050 #ifdef Q_OS_MAC
|
Chris@45
|
4051 args.append(url);
|
Chris@45
|
4052 process->start("open", args);
|
Chris@45
|
4053 #else
|
Chris@45
|
4054 #ifdef Q_OS_WIN32
|
Chris@599
|
4055 std::string pfiles;
|
Chris@599
|
4056 (void)getEnvUtf8("ProgramFiles", pfiles);
|
Chris@599
|
4057 QString command =
|
Chris@599
|
4058 QString::fromStdString(pfiles) +
|
Chris@599
|
4059 QString("\\Internet Explorer\\IEXPLORE.EXE");
|
Chris@358
|
4060
|
Chris@358
|
4061 args.append(url);
|
Chris@358
|
4062 process->start(command, args);
|
Chris@45
|
4063 #else
|
Chris@45
|
4064 if (!qgetenv("KDE_FULL_SESSION").isEmpty()) {
|
Chris@45
|
4065 args.append("exec");
|
Chris@45
|
4066 args.append(url);
|
Chris@45
|
4067 process->start("kfmclient", args);
|
Chris@45
|
4068 } else if (!qgetenv("BROWSER").isEmpty()) {
|
Chris@45
|
4069 args.append(url);
|
Chris@45
|
4070 process->start(qgetenv("BROWSER"), args);
|
Chris@45
|
4071 } else {
|
Chris@45
|
4072 args.append(url);
|
Chris@45
|
4073 process->start("firefox", args);
|
Chris@45
|
4074 }
|
Chris@45
|
4075 #endif
|
Chris@45
|
4076 #endif
|
Chris@45
|
4077 }
|
Chris@45
|
4078
|
Chris@483
|
4079 void
|
Chris@483
|
4080 MainWindowBase::openLocalFolder(QString path)
|
Chris@483
|
4081 {
|
Chris@483
|
4082 QDir d(path);
|
Chris@483
|
4083 if (d.exists()) {
|
Chris@483
|
4084 QStringList args;
|
Chris@483
|
4085 QString path = d.canonicalPath();
|
Chris@483
|
4086 #if defined Q_OS_WIN32
|
Chris@483
|
4087 // Although the Win32 API is quite happy to have
|
Chris@483
|
4088 // forward slashes as directory separators, Windows
|
Chris@483
|
4089 // Explorer is not
|
Chris@483
|
4090 path = path.replace('/', '\\');
|
Chris@483
|
4091 args << path;
|
Chris@483
|
4092 QProcess::execute("c:/windows/explorer.exe", args);
|
Chris@483
|
4093 #else
|
Chris@483
|
4094 args << path;
|
Chris@605
|
4095 QProcess process;
|
Chris@605
|
4096 QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
|
Chris@606
|
4097 env.insert("LD_LIBRARY_PATH", "");
|
Chris@605
|
4098 process.setProcessEnvironment(env);
|
Chris@605
|
4099 process.start(
|
Chris@483
|
4100 #if defined Q_OS_MAC
|
Chris@483
|
4101 "/usr/bin/open",
|
Chris@483
|
4102 #else
|
Chris@483
|
4103 "/usr/bin/xdg-open",
|
Chris@483
|
4104 #endif
|
Chris@483
|
4105 args);
|
Chris@608
|
4106 process.waitForFinished();
|
Chris@483
|
4107 #endif
|
Chris@483
|
4108 }
|
Chris@483
|
4109 }
|
Chris@483
|
4110
|