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