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