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