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