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@721
|
2800 MainWindowBase::exportLayerTo(Layer *layer, View *fromView,
|
Chris@721
|
2801 QString path, QString &error)
|
Chris@659
|
2802 {
|
Chris@659
|
2803 if (QFileInfo(path).suffix() == "") path += ".svl";
|
Chris@659
|
2804
|
Chris@659
|
2805 QString suffix = QFileInfo(path).suffix().toLower();
|
Chris@659
|
2806
|
Chris@721
|
2807 auto model = ModelById::get(layer->getExportModel(fromView));
|
Chris@684
|
2808 if (!model) {
|
Chris@684
|
2809 error = tr("Internal error: unknown model");
|
Chris@684
|
2810 return false;
|
Chris@684
|
2811 }
|
Chris@659
|
2812
|
Chris@659
|
2813 if (suffix == "xml" || suffix == "svl") {
|
Chris@659
|
2814
|
Chris@659
|
2815 QFile file(path);
|
Chris@659
|
2816 if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
Chris@659
|
2817 error = tr("Failed to open file %1 for writing").arg(path);
|
Chris@659
|
2818 } else {
|
Chris@659
|
2819 QTextStream out(&file);
|
Chris@659
|
2820 out.setCodec(QTextCodec::codecForName("UTF-8"));
|
Chris@659
|
2821 out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
|
Chris@659
|
2822 << "<!DOCTYPE sonic-visualiser>\n"
|
Chris@659
|
2823 << "<sv>\n"
|
Chris@659
|
2824 << " <data>\n";
|
Chris@659
|
2825
|
Chris@659
|
2826 model->toXml(out, " ");
|
Chris@659
|
2827
|
Chris@659
|
2828 out << " </data>\n"
|
Chris@659
|
2829 << " <display>\n";
|
Chris@659
|
2830
|
Chris@659
|
2831 layer->toXml(out, " ");
|
Chris@659
|
2832
|
Chris@659
|
2833 out << " </display>\n"
|
Chris@659
|
2834 << "</sv>\n";
|
Chris@659
|
2835 }
|
Chris@659
|
2836
|
Chris@659
|
2837 } else if (suffix == "mid" || suffix == "midi") {
|
Chris@659
|
2838
|
Chris@684
|
2839 auto nm = ModelById::getAs<NoteModel>(layer->getModel());
|
Chris@659
|
2840
|
Chris@659
|
2841 if (!nm) {
|
Chris@659
|
2842 error = tr("Can't export non-note layers to MIDI");
|
Chris@659
|
2843 } else {
|
Chris@684
|
2844 MIDIFileWriter writer(path, nm.get(), nm->getSampleRate());
|
Chris@659
|
2845 writer.write();
|
Chris@659
|
2846 if (!writer.isOK()) {
|
Chris@659
|
2847 error = writer.getError();
|
Chris@659
|
2848 }
|
Chris@659
|
2849 }
|
Chris@659
|
2850
|
Chris@659
|
2851 } else if (suffix == "ttl" || suffix == "n3") {
|
Chris@659
|
2852
|
Chris@684
|
2853 if (!RDFExporter::canExportModel(model.get())) {
|
Chris@659
|
2854 error = tr("Sorry, cannot export this layer type to RDF (supported types are: region, note, text, time instants, time values)");
|
Chris@659
|
2855 } else {
|
Chris@684
|
2856 RDFExporter exporter(path, model.get());
|
Chris@659
|
2857 exporter.write();
|
Chris@659
|
2858 if (!exporter.isOK()) {
|
Chris@659
|
2859 error = exporter.getError();
|
Chris@659
|
2860 }
|
Chris@659
|
2861 }
|
Chris@659
|
2862
|
Chris@659
|
2863 } else {
|
Chris@659
|
2864
|
Chris@684
|
2865 CSVFileWriter writer(path, model.get(),
|
Chris@659
|
2866 ((suffix == "csv") ? "," : "\t"));
|
Chris@659
|
2867 writer.write();
|
Chris@659
|
2868
|
Chris@659
|
2869 if (!writer.isOK()) {
|
Chris@659
|
2870 error = writer.getError();
|
Chris@659
|
2871 }
|
Chris@659
|
2872 }
|
Chris@659
|
2873
|
Chris@659
|
2874 return (error == "");
|
Chris@659
|
2875 }
|
Chris@659
|
2876
|
Chris@45
|
2877 void
|
Chris@226
|
2878 MainWindowBase::toXml(QTextStream &out, bool asTemplate)
|
Chris@45
|
2879 {
|
Chris@45
|
2880 QString indent(" ");
|
Chris@45
|
2881
|
Chris@45
|
2882 out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
|
Chris@45
|
2883 out << "<!DOCTYPE sonic-visualiser>\n";
|
Chris@45
|
2884 out << "<sv>\n";
|
Chris@45
|
2885
|
Chris@226
|
2886 if (asTemplate) {
|
Chris@226
|
2887 m_document->toXmlAsTemplate(out, "", "");
|
Chris@226
|
2888 } else {
|
Chris@226
|
2889 m_document->toXml(out, "", "");
|
Chris@226
|
2890 }
|
Chris@45
|
2891
|
Chris@45
|
2892 out << "<display>\n";
|
Chris@45
|
2893
|
Chris@45
|
2894 out << QString(" <window width=\"%1\" height=\"%2\"/>\n")
|
Chris@595
|
2895 .arg(width()).arg(height());
|
Chris@45
|
2896
|
Chris@45
|
2897 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
|
Chris@45
|
2898
|
Chris@595
|
2899 Pane *pane = m_paneStack->getPane(i);
|
Chris@595
|
2900
|
Chris@595
|
2901 if (pane) {
|
Chris@45
|
2902 pane->toXml(out, indent);
|
Chris@595
|
2903 }
|
Chris@45
|
2904 }
|
Chris@45
|
2905
|
Chris@45
|
2906 out << "</display>\n";
|
Chris@45
|
2907
|
Chris@45
|
2908 m_viewManager->getSelection().toXml(out);
|
Chris@45
|
2909
|
Chris@45
|
2910 out << "</sv>\n";
|
Chris@45
|
2911 }
|
Chris@45
|
2912
|
Chris@45
|
2913 Pane *
|
Chris@45
|
2914 MainWindowBase::addPaneToStack()
|
Chris@45
|
2915 {
|
Chris@342
|
2916 cerr << "MainWindowBase::addPaneToStack()" << endl;
|
Chris@45
|
2917 AddPaneCommand *command = new AddPaneCommand(this);
|
Chris@45
|
2918 CommandHistory::getInstance()->addCommand(command);
|
Chris@57
|
2919 Pane *pane = command->getPane();
|
Chris@57
|
2920 return pane;
|
Chris@45
|
2921 }
|
Chris@45
|
2922
|
Chris@45
|
2923 void
|
Chris@45
|
2924 MainWindowBase::zoomIn()
|
Chris@45
|
2925 {
|
Chris@45
|
2926 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@45
|
2927 if (currentPane) currentPane->zoom(true);
|
Chris@45
|
2928 }
|
Chris@45
|
2929
|
Chris@45
|
2930 void
|
Chris@45
|
2931 MainWindowBase::zoomOut()
|
Chris@45
|
2932 {
|
Chris@45
|
2933 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@45
|
2934 if (currentPane) currentPane->zoom(false);
|
Chris@45
|
2935 }
|
Chris@45
|
2936
|
Chris@45
|
2937 void
|
Chris@45
|
2938 MainWindowBase::zoomToFit()
|
Chris@45
|
2939 {
|
Chris@45
|
2940 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@45
|
2941 if (!currentPane) return;
|
Chris@45
|
2942
|
Chris@684
|
2943 auto model = getMainModel();
|
Chris@45
|
2944 if (!model) return;
|
Chris@45
|
2945
|
Chris@434
|
2946 sv_frame_t start = model->getStartFrame();
|
Chris@434
|
2947 sv_frame_t end = model->getEndFrame();
|
Chris@60
|
2948 if (m_playSource) end = std::max(end, m_playSource->getPlayEndFrame());
|
Chris@366
|
2949 int pixels = currentPane->width();
|
Chris@366
|
2950
|
Chris@366
|
2951 int sw = currentPane->getVerticalScaleWidth();
|
Chris@45
|
2952 if (pixels > sw * 2) pixels -= sw * 2;
|
Chris@45
|
2953 else pixels = 1;
|
Chris@45
|
2954 if (pixels > 4) pixels -= 4;
|
Chris@45
|
2955
|
Chris@624
|
2956 ZoomLevel zoomLevel = ZoomLevel::fromRatio(pixels, end - start);
|
Chris@45
|
2957 currentPane->setZoomLevel(zoomLevel);
|
Chris@45
|
2958 currentPane->setCentreFrame((start + end) / 2);
|
Chris@45
|
2959 }
|
Chris@45
|
2960
|
Chris@45
|
2961 void
|
Chris@45
|
2962 MainWindowBase::zoomDefault()
|
Chris@45
|
2963 {
|
Chris@45
|
2964 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@302
|
2965 QSettings settings;
|
Chris@302
|
2966 settings.beginGroup("MainWindow");
|
Chris@302
|
2967 int zoom = settings.value("zoom-default", 1024).toInt();
|
Chris@302
|
2968 settings.endGroup();
|
Chris@624
|
2969 if (currentPane) {
|
Chris@624
|
2970 currentPane->setZoomLevel(ZoomLevel(ZoomLevel::FramesPerPixel, zoom));
|
Chris@624
|
2971 }
|
Chris@45
|
2972 }
|
Chris@45
|
2973
|
Chris@45
|
2974 void
|
Chris@45
|
2975 MainWindowBase::scrollLeft()
|
Chris@45
|
2976 {
|
Chris@45
|
2977 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@45
|
2978 if (currentPane) currentPane->scroll(false, false);
|
Chris@45
|
2979 }
|
Chris@45
|
2980
|
Chris@45
|
2981 void
|
Chris@45
|
2982 MainWindowBase::jumpLeft()
|
Chris@45
|
2983 {
|
Chris@45
|
2984 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@45
|
2985 if (currentPane) currentPane->scroll(false, true);
|
Chris@45
|
2986 }
|
Chris@45
|
2987
|
Chris@45
|
2988 void
|
Chris@162
|
2989 MainWindowBase::peekLeft()
|
Chris@162
|
2990 {
|
Chris@162
|
2991 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@162
|
2992 if (currentPane) currentPane->scroll(false, false, false);
|
Chris@162
|
2993 }
|
Chris@162
|
2994
|
Chris@162
|
2995 void
|
Chris@45
|
2996 MainWindowBase::scrollRight()
|
Chris@45
|
2997 {
|
Chris@45
|
2998 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@45
|
2999 if (currentPane) currentPane->scroll(true, false);
|
Chris@45
|
3000 }
|
Chris@45
|
3001
|
Chris@45
|
3002 void
|
Chris@45
|
3003 MainWindowBase::jumpRight()
|
Chris@45
|
3004 {
|
Chris@45
|
3005 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@45
|
3006 if (currentPane) currentPane->scroll(true, true);
|
Chris@45
|
3007 }
|
Chris@45
|
3008
|
Chris@45
|
3009 void
|
Chris@162
|
3010 MainWindowBase::peekRight()
|
Chris@162
|
3011 {
|
Chris@162
|
3012 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@162
|
3013 if (currentPane) currentPane->scroll(true, false, false);
|
Chris@162
|
3014 }
|
Chris@162
|
3015
|
Chris@162
|
3016 void
|
Chris@45
|
3017 MainWindowBase::showNoOverlays()
|
Chris@45
|
3018 {
|
Chris@45
|
3019 m_viewManager->setOverlayMode(ViewManager::NoOverlays);
|
Chris@45
|
3020 }
|
Chris@45
|
3021
|
Chris@45
|
3022 void
|
Chris@45
|
3023 MainWindowBase::showMinimalOverlays()
|
Chris@45
|
3024 {
|
Chris@335
|
3025 m_viewManager->setOverlayMode(ViewManager::StandardOverlays);
|
Chris@45
|
3026 }
|
Chris@45
|
3027
|
Chris@45
|
3028 void
|
Chris@45
|
3029 MainWindowBase::showAllOverlays()
|
Chris@45
|
3030 {
|
Chris@45
|
3031 m_viewManager->setOverlayMode(ViewManager::AllOverlays);
|
Chris@45
|
3032 }
|
Chris@45
|
3033
|
Chris@45
|
3034 void
|
Chris@577
|
3035 MainWindowBase::findTimeRulerLayer()
|
Chris@577
|
3036 {
|
Chris@577
|
3037 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
|
Chris@577
|
3038 Pane *pane = m_paneStack->getPane(i);
|
Chris@577
|
3039 if (!pane) continue;
|
Chris@577
|
3040 for (int j = 0; j < pane->getLayerCount(); ++j) {
|
Chris@577
|
3041 Layer *layer = pane->getLayer(j);
|
Chris@577
|
3042 if (!dynamic_cast<TimeRulerLayer *>(layer)) continue;
|
Chris@577
|
3043 m_timeRulerLayer = layer;
|
Chris@577
|
3044 return;
|
Chris@577
|
3045 }
|
Chris@577
|
3046 }
|
Chris@577
|
3047 if (m_timeRulerLayer) {
|
Chris@577
|
3048 SVCERR << "WARNING: Time ruler layer was not reset to 0 before session template loaded?" << endl;
|
Chris@577
|
3049 delete m_timeRulerLayer;
|
Chris@636
|
3050 m_timeRulerLayer = nullptr;
|
Chris@577
|
3051 }
|
Chris@577
|
3052 }
|
Chris@577
|
3053
|
Chris@577
|
3054 void
|
Chris@211
|
3055 MainWindowBase::toggleTimeRulers()
|
Chris@211
|
3056 {
|
Chris@211
|
3057 bool haveRulers = false;
|
Chris@211
|
3058 bool someHidden = false;
|
Chris@211
|
3059
|
Chris@211
|
3060 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
|
Chris@211
|
3061
|
Chris@211
|
3062 Pane *pane = m_paneStack->getPane(i);
|
Chris@211
|
3063 if (!pane) continue;
|
Chris@211
|
3064
|
Chris@211
|
3065 for (int j = 0; j < pane->getLayerCount(); ++j) {
|
Chris@211
|
3066
|
Chris@211
|
3067 Layer *layer = pane->getLayer(j);
|
Chris@211
|
3068 if (!dynamic_cast<TimeRulerLayer *>(layer)) continue;
|
Chris@211
|
3069
|
Chris@211
|
3070 haveRulers = true;
|
Chris@211
|
3071 if (layer->isLayerDormant(pane)) someHidden = true;
|
Chris@211
|
3072 }
|
Chris@211
|
3073 }
|
Chris@211
|
3074
|
Chris@211
|
3075 if (haveRulers) {
|
Chris@211
|
3076
|
Chris@211
|
3077 bool show = someHidden;
|
Chris@211
|
3078
|
Chris@211
|
3079 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
|
Chris@211
|
3080
|
Chris@211
|
3081 Pane *pane = m_paneStack->getPane(i);
|
Chris@211
|
3082 if (!pane) continue;
|
Chris@211
|
3083
|
Chris@211
|
3084 for (int j = 0; j < pane->getLayerCount(); ++j) {
|
Chris@211
|
3085
|
Chris@211
|
3086 Layer *layer = pane->getLayer(j);
|
Chris@211
|
3087 if (!dynamic_cast<TimeRulerLayer *>(layer)) continue;
|
Chris@211
|
3088
|
Chris@211
|
3089 layer->showLayer(pane, show);
|
Chris@211
|
3090 }
|
Chris@211
|
3091 }
|
Chris@211
|
3092 }
|
Chris@211
|
3093 }
|
Chris@211
|
3094
|
Chris@211
|
3095 void
|
Chris@45
|
3096 MainWindowBase::toggleZoomWheels()
|
Chris@45
|
3097 {
|
Chris@45
|
3098 if (m_viewManager->getZoomWheelsEnabled()) {
|
Chris@45
|
3099 m_viewManager->setZoomWheelsEnabled(false);
|
Chris@45
|
3100 } else {
|
Chris@45
|
3101 m_viewManager->setZoomWheelsEnabled(true);
|
Chris@45
|
3102 }
|
Chris@45
|
3103 }
|
Chris@45
|
3104
|
Chris@45
|
3105 void
|
Chris@45
|
3106 MainWindowBase::togglePropertyBoxes()
|
Chris@45
|
3107 {
|
Chris@712
|
3108 if (m_paneStack->getLayoutStyle() == PaneStack::HiddenPropertyStacksLayout) {
|
Chris@45
|
3109 if (Preferences::getInstance()->getPropertyBoxLayout() ==
|
Chris@45
|
3110 Preferences::VerticallyStacked) {
|
Chris@45
|
3111 m_paneStack->setLayoutStyle(PaneStack::PropertyStackPerPaneLayout);
|
Chris@45
|
3112 } else {
|
Chris@45
|
3113 m_paneStack->setLayoutStyle(PaneStack::SinglePropertyStackLayout);
|
Chris@45
|
3114 }
|
Chris@45
|
3115 } else {
|
Chris@712
|
3116 m_paneStack->setLayoutStyle(PaneStack::HiddenPropertyStacksLayout);
|
Chris@45
|
3117 }
|
Chris@45
|
3118 }
|
Chris@45
|
3119
|
Chris@378
|
3120 QLabel *
|
Chris@378
|
3121 MainWindowBase::getStatusLabel() const
|
Chris@378
|
3122 {
|
Chris@378
|
3123 if (!m_statusLabel) {
|
Chris@378
|
3124 m_statusLabel = new QLabel();
|
Chris@378
|
3125 statusBar()->addWidget(m_statusLabel, 1);
|
Chris@378
|
3126 }
|
Chris@379
|
3127
|
Chris@379
|
3128 QList<QFrame *> frames = statusBar()->findChildren<QFrame *>();
|
Chris@379
|
3129 foreach (QFrame *f, frames) {
|
Chris@379
|
3130 f->setFrameStyle(QFrame::NoFrame);
|
Chris@379
|
3131 }
|
Chris@379
|
3132
|
Chris@378
|
3133 return m_statusLabel;
|
Chris@378
|
3134 }
|
Chris@378
|
3135
|
Chris@45
|
3136 void
|
Chris@45
|
3137 MainWindowBase::toggleStatusBar()
|
Chris@45
|
3138 {
|
Chris@45
|
3139 QSettings settings;
|
Chris@45
|
3140 settings.beginGroup("MainWindow");
|
Chris@45
|
3141 bool sb = settings.value("showstatusbar", true).toBool();
|
Chris@45
|
3142
|
Chris@45
|
3143 if (sb) {
|
Chris@45
|
3144 statusBar()->hide();
|
Chris@45
|
3145 } else {
|
Chris@45
|
3146 statusBar()->show();
|
Chris@45
|
3147 }
|
Chris@45
|
3148
|
Chris@45
|
3149 settings.setValue("showstatusbar", !sb);
|
Chris@45
|
3150
|
Chris@45
|
3151 settings.endGroup();
|
Chris@45
|
3152 }
|
Chris@45
|
3153
|
Chris@45
|
3154 void
|
Chris@256
|
3155 MainWindowBase::toggleCentreLine()
|
Chris@256
|
3156 {
|
Chris@256
|
3157 if (m_viewManager->shouldShowCentreLine()) {
|
Chris@256
|
3158 m_viewManager->setShowCentreLine(false);
|
Chris@256
|
3159 } else {
|
Chris@256
|
3160 m_viewManager->setShowCentreLine(true);
|
Chris@256
|
3161 }
|
Chris@256
|
3162 }
|
Chris@256
|
3163
|
Chris@256
|
3164 void
|
Chris@45
|
3165 MainWindowBase::preferenceChanged(PropertyContainer::PropertyName name)
|
Chris@45
|
3166 {
|
Chris@45
|
3167 if (name == "Property Box Layout") {
|
Chris@712
|
3168 if (m_paneStack->getLayoutStyle() != PaneStack::HiddenPropertyStacksLayout) {
|
Chris@45
|
3169 if (Preferences::getInstance()->getPropertyBoxLayout() ==
|
Chris@45
|
3170 Preferences::VerticallyStacked) {
|
Chris@45
|
3171 m_paneStack->setLayoutStyle(PaneStack::PropertyStackPerPaneLayout);
|
Chris@45
|
3172 } else {
|
Chris@45
|
3173 m_paneStack->setLayoutStyle(PaneStack::SinglePropertyStackLayout);
|
Chris@45
|
3174 }
|
Chris@45
|
3175 }
|
Chris@45
|
3176 } else if (name == "Background Mode" && m_viewManager) {
|
Chris@45
|
3177 Preferences::BackgroundMode mode =
|
Chris@45
|
3178 Preferences::getInstance()->getBackgroundMode();
|
Chris@45
|
3179 if (mode == Preferences::BackgroundFromTheme) {
|
Chris@45
|
3180 m_viewManager->setGlobalDarkBackground(m_initialDarkBackground);
|
Chris@45
|
3181 } else if (mode == Preferences::DarkBackground) {
|
Chris@45
|
3182 m_viewManager->setGlobalDarkBackground(true);
|
Chris@45
|
3183 } else {
|
Chris@45
|
3184 m_viewManager->setGlobalDarkBackground(false);
|
Chris@45
|
3185 }
|
Chris@45
|
3186 }
|
Chris@45
|
3187 }
|
Chris@45
|
3188
|
Chris@45
|
3189 void
|
Chris@45
|
3190 MainWindowBase::play()
|
Chris@45
|
3191 {
|
Chris@516
|
3192 if ((m_recordTarget && m_recordTarget->isRecording()) ||
|
Chris@516
|
3193 (m_playSource && m_playSource->isPlaying())) {
|
Chris@45
|
3194 stop();
|
Chris@479
|
3195 QAction *action = qobject_cast<QAction *>(sender());
|
Chris@479
|
3196 if (action) action->setChecked(false);
|
Chris@45
|
3197 } else {
|
Chris@487
|
3198 if (m_audioIO) m_audioIO->resume();
|
Chris@509
|
3199 else if (m_playTarget) m_playTarget->resume();
|
Chris@45
|
3200 playbackFrameChanged(m_viewManager->getPlaybackFrame());
|
Chris@595
|
3201 m_playSource->play(m_viewManager->getPlaybackFrame());
|
Chris@45
|
3202 }
|
Chris@45
|
3203 }
|
Chris@45
|
3204
|
Chris@45
|
3205 void
|
Chris@477
|
3206 MainWindowBase::record()
|
Chris@477
|
3207 {
|
Chris@586
|
3208 QAction *action = qobject_cast<QAction *>(sender());
|
Chris@586
|
3209
|
Chris@714
|
3210 if (m_audioMode == AUDIO_NONE || m_audioMode == AUDIO_PLAYBACK_ONLY) {
|
Chris@586
|
3211 if (action) action->setChecked(false);
|
Chris@478
|
3212 return;
|
Chris@478
|
3213 }
|
Chris@478
|
3214
|
Chris@477
|
3215 if (!m_recordTarget) {
|
Chris@586
|
3216 if (action) action->setChecked(false);
|
Chris@477
|
3217 return;
|
Chris@477
|
3218 }
|
Chris@477
|
3219
|
Chris@714
|
3220 if (m_audioMode == AUDIO_PLAYBACK_NOW_RECORD_LATER) {
|
Chris@714
|
3221 SVDEBUG << "MainWindowBase::record: upgrading from "
|
Chris@714
|
3222 << "AUDIO_PLAYBACK_NOW_RECORD_LATER to "
|
Chris@714
|
3223 << "AUDIO_PLAYBACK_AND_RECORD" << endl;
|
Chris@714
|
3224 m_audioMode = AUDIO_PLAYBACK_AND_RECORD;
|
Chris@714
|
3225 deleteAudioIO();
|
Chris@714
|
3226 }
|
Chris@714
|
3227
|
Chris@478
|
3228 if (!m_audioIO) {
|
Chris@611
|
3229 SVDEBUG << "MainWindowBase::record: about to create audio IO" << endl;
|
Chris@478
|
3230 createAudioIO();
|
Chris@478
|
3231 }
|
Chris@492
|
3232
|
Chris@492
|
3233 if (!m_audioIO) {
|
Chris@586
|
3234 if (!m_playTarget) {
|
Chris@586
|
3235 // Don't need to report this, createAudioIO should have
|
Chris@586
|
3236 if (action) action->setChecked(false);
|
Chris@586
|
3237 return;
|
Chris@586
|
3238 } else {
|
Chris@586
|
3239 // Need to report this: if the play target exists instead
|
Chris@586
|
3240 // of the audio IO, then that means we failed to open a
|
Chris@586
|
3241 // capture device. The record control should be disabled
|
Chris@586
|
3242 // in that situation, so if it happens here, that must
|
Chris@586
|
3243 // mean this is the first time we ever tried to open the
|
Chris@586
|
3244 // audio device, hence the need to report the problem here
|
Chris@586
|
3245 QMessageBox::critical
|
Chris@586
|
3246 (this, tr("No record device available"),
|
Chris@586
|
3247 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
|
3248 if (action) action->setChecked(false);
|
Chris@586
|
3249 updateMenuStates();
|
Chris@586
|
3250 return;
|
Chris@586
|
3251 }
|
Chris@492
|
3252 }
|
Chris@478
|
3253
|
Chris@477
|
3254 if (m_recordTarget->isRecording()) {
|
Chris@492
|
3255 stop();
|
Chris@477
|
3256 return;
|
Chris@477
|
3257 }
|
Chris@490
|
3258
|
Chris@483
|
3259 if (m_audioRecordMode == RecordReplaceSession) {
|
Chris@490
|
3260 if (!checkSaveModified()) {
|
Chris@490
|
3261 if (action) action->setChecked(false);
|
Chris@490
|
3262 return;
|
Chris@490
|
3263 }
|
Chris@483
|
3264 }
|
Chris@487
|
3265
|
Chris@557
|
3266 if (m_viewManager) m_viewManager->setGlobalCentreFrame(0);
|
Chris@557
|
3267
|
Chris@611
|
3268 SVCERR << "MainWindowBase::record: about to resume" << endl;
|
Chris@492
|
3269 m_audioIO->resume();
|
Chris@509
|
3270
|
Chris@684
|
3271 WritableWaveFileModel *modelPtr = m_recordTarget->startRecording();
|
Chris@684
|
3272 if (!modelPtr) {
|
Chris@586
|
3273 SVCERR << "ERROR: MainWindowBase::record: Recording failed" << endl;
|
Chris@586
|
3274 QMessageBox::critical
|
Chris@586
|
3275 (this, tr("Recording failed"),
|
Chris@586
|
3276 tr("<b>Recording failed</b><p>Failed to switch to record mode (some internal problem?)</p>"));
|
Chris@490
|
3277 if (action) action->setChecked(false);
|
Chris@477
|
3278 return;
|
Chris@477
|
3279 }
|
Chris@477
|
3280
|
Chris@684
|
3281 if (!modelPtr->isOK()) {
|
Chris@611
|
3282 SVCERR << "MainWindowBase::record: Model not OK, stopping and suspending" << endl;
|
Chris@477
|
3283 m_recordTarget->stopRecording();
|
Chris@492
|
3284 m_audioIO->suspend();
|
Chris@586
|
3285 if (action) action->setChecked(false);
|
Chris@684
|
3286 delete modelPtr;
|
Chris@477
|
3287 return;
|
Chris@477
|
3288 }
|
Chris@611
|
3289
|
Chris@611
|
3290 SVCERR << "MainWindowBase::record: Model is OK, continuing..." << endl;
|
Chris@684
|
3291
|
Chris@684
|
3292 QString location = modelPtr->getLocation();
|
Chris@487
|
3293
|
Chris@687
|
3294 auto modelId = ModelById::add(std::shared_ptr<Model>(modelPtr));
|
Chris@483
|
3295
|
Chris@483
|
3296 if (m_audioRecordMode == RecordReplaceSession || !getMainModel()) {
|
Chris@478
|
3297
|
Chris@479
|
3298 //!!! duplication with openAudio here
|
Chris@479
|
3299
|
Chris@479
|
3300 QString templateName = getDefaultSessionTemplate();
|
Chris@479
|
3301 bool loadedTemplate = false;
|
Chris@479
|
3302
|
Chris@479
|
3303 if (templateName != "") {
|
Chris@479
|
3304 FileOpenStatus tplStatus = openSessionTemplate(templateName);
|
Chris@479
|
3305 if (tplStatus == FileOpenCancelled) {
|
Chris@611
|
3306 SVCERR << "MainWindowBase::record: Session template open cancelled, stopping and suspending" << endl;
|
Chris@490
|
3307 m_recordTarget->stopRecording();
|
Chris@492
|
3308 m_audioIO->suspend();
|
Chris@684
|
3309 ModelById::release(modelId);
|
Chris@479
|
3310 return;
|
Chris@479
|
3311 }
|
Chris@479
|
3312 if (tplStatus != FileOpenFailed) {
|
Chris@479
|
3313 loadedTemplate = true;
|
Chris@479
|
3314 }
|
Chris@479
|
3315 }
|
Chris@479
|
3316
|
Chris@479
|
3317 if (!loadedTemplate) {
|
Chris@479
|
3318 closeSession();
|
Chris@479
|
3319 createDocument();
|
Chris@479
|
3320 }
|
Chris@479
|
3321
|
Chris@684
|
3322 ModelId prevMain = getMainModelId();
|
Chris@684
|
3323 if (!prevMain.isNone()) {
|
Chris@479
|
3324 m_playSource->removeModel(prevMain);
|
Chris@479
|
3325 }
|
Chris@479
|
3326
|
Chris@684
|
3327 m_document->setMainModel(modelId);
|
Chris@478
|
3328 setupMenus();
|
Chris@577
|
3329 findTimeRulerLayer();
|
Chris@478
|
3330
|
Chris@684
|
3331 m_originalLocation = location;
|
Chris@669
|
3332
|
Chris@595
|
3333 if (loadedTemplate || (m_sessionFile == "")) {
|
Chris@595
|
3334 CommandHistory::getInstance()->clear();
|
Chris@595
|
3335 CommandHistory::getInstance()->documentSaved();
|
Chris@595
|
3336 }
|
Chris@479
|
3337
|
Chris@669
|
3338 m_documentModified = false;
|
Chris@669
|
3339 updateWindowTitle();
|
Chris@669
|
3340
|
Chris@478
|
3341 } else {
|
Chris@478
|
3342
|
Chris@478
|
3343 CommandHistory::getInstance()->startCompoundOperation
|
Chris@478
|
3344 (tr("Import Recorded Audio"), true);
|
Chris@478
|
3345
|
Chris@691
|
3346 m_document->addNonDerivedModel(modelId);
|
Chris@478
|
3347
|
Chris@478
|
3348 AddPaneCommand *command = new AddPaneCommand(this);
|
Chris@478
|
3349 CommandHistory::getInstance()->addCommand(command);
|
Chris@478
|
3350
|
Chris@478
|
3351 Pane *pane = command->getPane();
|
Chris@478
|
3352
|
Chris@478
|
3353 if (m_timeRulerLayer) {
|
Chris@478
|
3354 m_document->addLayerToView(pane, m_timeRulerLayer);
|
Chris@478
|
3355 }
|
Chris@478
|
3356
|
Chris@684
|
3357 Layer *newLayer = m_document->createImportedLayer(modelId);
|
Chris@478
|
3358
|
Chris@478
|
3359 if (newLayer) {
|
Chris@478
|
3360 m_document->addLayerToView(pane, newLayer);
|
Chris@478
|
3361 }
|
Chris@595
|
3362
|
Chris@478
|
3363 CommandHistory::getInstance()->endCompoundOperation();
|
Chris@477
|
3364 }
|
Chris@479
|
3365
|
Chris@479
|
3366 updateMenuStates();
|
Chris@684
|
3367 m_recentFiles.addFile(location);
|
Chris@479
|
3368 currentPaneChanged(m_paneStack->getCurrentPane());
|
Chris@611
|
3369
|
Chris@496
|
3370 emit audioFileLoaded();
|
Chris@477
|
3371 }
|
Chris@477
|
3372
|
Chris@477
|
3373 void
|
Chris@45
|
3374 MainWindowBase::ffwd()
|
Chris@45
|
3375 {
|
Chris@45
|
3376 if (!getMainModel()) return;
|
Chris@45
|
3377
|
Chris@708
|
3378 sv_frame_t playbackFrame = m_viewManager->getPlaybackFrame();
|
Chris@708
|
3379 sv_frame_t frame = playbackFrame + 1;
|
Chris@45
|
3380
|
Chris@85
|
3381 Pane *pane = m_paneStack->getCurrentPane();
|
Chris@45
|
3382 Layer *layer = getSnapLayer();
|
Chris@435
|
3383 sv_samplerate_t sr = getMainModel()->getSampleRate();
|
Chris@45
|
3384
|
Chris@708
|
3385 if (!pane || !layer) {
|
Chris@45
|
3386
|
Chris@45
|
3387 frame = RealTime::realTime2Frame
|
Chris@357
|
3388 (RealTime::frame2RealTime(frame, sr) + m_defaultFfwdRwdStep, sr);
|
Chris@435
|
3389 if (frame > getMainModel()->getEndFrame()) {
|
Chris@45
|
3390 frame = getMainModel()->getEndFrame();
|
Chris@45
|
3391 }
|
Chris@45
|
3392
|
Chris@45
|
3393 } else {
|
Chris@45
|
3394
|
Chris@708
|
3395 sv_frame_t pframe = pane->alignFromReference(frame);
|
Chris@366
|
3396 int resolution = 0;
|
Chris@708
|
3397 bool success = false;
|
Chris@708
|
3398
|
Chris@708
|
3399 while (layer->snapToFeatureFrame(pane, pframe, resolution,
|
Chris@715
|
3400 Layer::SnapRight, -1)) {
|
Chris@708
|
3401 if (pane->alignToReference(pframe) > playbackFrame) {
|
Chris@708
|
3402 success = true;
|
Chris@708
|
3403 break;
|
Chris@708
|
3404 } else {
|
Chris@708
|
3405 ++pframe;
|
Chris@708
|
3406 }
|
Chris@708
|
3407 }
|
Chris@708
|
3408
|
Chris@708
|
3409 if (success) {
|
Chris@708
|
3410 frame = pane->alignToReference(pframe);
|
Chris@85
|
3411 } else {
|
Chris@45
|
3412 frame = getMainModel()->getEndFrame();
|
Chris@45
|
3413 }
|
Chris@45
|
3414 }
|
Chris@45
|
3415
|
Chris@45
|
3416 if (frame < 0) frame = 0;
|
Chris@45
|
3417
|
Chris@45
|
3418 if (m_viewManager->getPlaySelectionMode()) {
|
Chris@435
|
3419 frame = m_viewManager->constrainFrameToSelection(frame);
|
Chris@45
|
3420 }
|
Chris@45
|
3421
|
Chris@45
|
3422 m_viewManager->setPlaybackFrame(frame);
|
Chris@166
|
3423
|
Chris@708
|
3424 if (frame >= getMainModel()->getEndFrame() &&
|
Chris@166
|
3425 m_playSource &&
|
Chris@166
|
3426 m_playSource->isPlaying() &&
|
Chris@166
|
3427 !m_viewManager->getPlayLoopMode()) {
|
Chris@166
|
3428 stop();
|
Chris@166
|
3429 }
|
Chris@45
|
3430 }
|
Chris@45
|
3431
|
Chris@45
|
3432 void
|
Chris@45
|
3433 MainWindowBase::ffwdEnd()
|
Chris@45
|
3434 {
|
Chris@45
|
3435 if (!getMainModel()) return;
|
Chris@45
|
3436
|
Chris@139
|
3437 if (m_playSource &&
|
Chris@139
|
3438 m_playSource->isPlaying() &&
|
Chris@139
|
3439 !m_viewManager->getPlayLoopMode()) {
|
Chris@139
|
3440 stop();
|
Chris@139
|
3441 }
|
Chris@139
|
3442
|
Chris@435
|
3443 sv_frame_t frame = getMainModel()->getEndFrame();
|
Chris@45
|
3444
|
Chris@45
|
3445 if (m_viewManager->getPlaySelectionMode()) {
|
Chris@45
|
3446 frame = m_viewManager->constrainFrameToSelection(frame);
|
Chris@45
|
3447 }
|
Chris@45
|
3448
|
Chris@45
|
3449 m_viewManager->setPlaybackFrame(frame);
|
Chris@45
|
3450 }
|
Chris@45
|
3451
|
Chris@45
|
3452 void
|
Chris@166
|
3453 MainWindowBase::ffwdSimilar()
|
Chris@166
|
3454 {
|
Chris@166
|
3455 if (!getMainModel()) return;
|
Chris@166
|
3456
|
Chris@166
|
3457 Layer *layer = getSnapLayer();
|
Chris@166
|
3458 if (!layer) { ffwd(); return; }
|
Chris@166
|
3459
|
Chris@166
|
3460 Pane *pane = m_paneStack->getCurrentPane();
|
Chris@435
|
3461 sv_frame_t frame = m_viewManager->getPlaybackFrame();
|
Chris@166
|
3462
|
Chris@366
|
3463 int resolution = 0;
|
Chris@166
|
3464 if (pane) frame = pane->alignFromReference(frame);
|
Chris@166
|
3465 if (layer->snapToSimilarFeature(m_paneStack->getCurrentPane(),
|
Chris@166
|
3466 frame, resolution, Layer::SnapRight)) {
|
Chris@166
|
3467 if (pane) frame = pane->alignToReference(frame);
|
Chris@166
|
3468 } else {
|
Chris@166
|
3469 frame = getMainModel()->getEndFrame();
|
Chris@166
|
3470 }
|
Chris@166
|
3471
|
Chris@166
|
3472 if (frame < 0) frame = 0;
|
Chris@166
|
3473
|
Chris@166
|
3474 if (m_viewManager->getPlaySelectionMode()) {
|
Chris@435
|
3475 frame = m_viewManager->constrainFrameToSelection(frame);
|
Chris@166
|
3476 }
|
Chris@166
|
3477
|
Chris@166
|
3478 m_viewManager->setPlaybackFrame(frame);
|
Chris@166
|
3479
|
Chris@435
|
3480 if (frame == getMainModel()->getEndFrame() &&
|
Chris@166
|
3481 m_playSource &&
|
Chris@166
|
3482 m_playSource->isPlaying() &&
|
Chris@166
|
3483 !m_viewManager->getPlayLoopMode()) {
|
Chris@166
|
3484 stop();
|
Chris@166
|
3485 }
|
Chris@166
|
3486 }
|
Chris@166
|
3487
|
Chris@166
|
3488 void
|
Chris@45
|
3489 MainWindowBase::rewind()
|
Chris@45
|
3490 {
|
Chris@45
|
3491 if (!getMainModel()) return;
|
Chris@45
|
3492
|
Chris@708
|
3493 sv_frame_t playbackFrame = m_viewManager->getPlaybackFrame();
|
Chris@708
|
3494 sv_frame_t frame = playbackFrame;
|
Chris@45
|
3495 if (frame > 0) --frame;
|
Chris@45
|
3496
|
Chris@85
|
3497 Pane *pane = m_paneStack->getCurrentPane();
|
Chris@45
|
3498 Layer *layer = getSnapLayer();
|
Chris@435
|
3499 sv_samplerate_t sr = getMainModel()->getSampleRate();
|
Chris@45
|
3500
|
Chris@45
|
3501 // when rewinding during playback, we want to allow a period
|
Chris@45
|
3502 // following a rewind target point at which the rewind will go to
|
Chris@45
|
3503 // the prior point instead of the immediately neighbouring one
|
Chris@45
|
3504 if (m_playSource && m_playSource->isPlaying()) {
|
Chris@45
|
3505 RealTime ct = RealTime::frame2RealTime(frame, sr);
|
Chris@357
|
3506 ct = ct - RealTime::fromSeconds(0.15);
|
Chris@45
|
3507 if (ct < RealTime::zeroTime) ct = RealTime::zeroTime;
|
Chris@45
|
3508 frame = RealTime::realTime2Frame(ct, sr);
|
Chris@45
|
3509 }
|
Chris@45
|
3510
|
Chris@708
|
3511 if (!pane || !layer) {
|
Chris@45
|
3512
|
Chris@45
|
3513 frame = RealTime::realTime2Frame
|
Chris@357
|
3514 (RealTime::frame2RealTime(frame, sr) - m_defaultFfwdRwdStep, sr);
|
Chris@435
|
3515 if (frame < getMainModel()->getStartFrame()) {
|
Chris@45
|
3516 frame = getMainModel()->getStartFrame();
|
Chris@45
|
3517 }
|
Chris@45
|
3518
|
Chris@45
|
3519 } else {
|
Chris@45
|
3520
|
Chris@708
|
3521 sv_frame_t pframe = pane->alignFromReference(frame);
|
Chris@366
|
3522 int resolution = 0;
|
Chris@708
|
3523 bool success = false;
|
Chris@708
|
3524
|
Chris@708
|
3525 while (layer->snapToFeatureFrame(pane, pframe, resolution,
|
Chris@715
|
3526 Layer::SnapLeft, -1)) {
|
Chris@708
|
3527 if (pane->alignToReference(pframe) < playbackFrame ||
|
Chris@708
|
3528 pframe <= 0) {
|
Chris@708
|
3529 success = true;
|
Chris@708
|
3530 break;
|
Chris@708
|
3531 } else {
|
Chris@708
|
3532 --pframe;
|
Chris@708
|
3533 }
|
Chris@708
|
3534 }
|
Chris@708
|
3535
|
Chris@708
|
3536 if (success) {
|
Chris@708
|
3537 frame = pane->alignToReference(pframe);
|
Chris@85
|
3538 } else {
|
Chris@45
|
3539 frame = getMainModel()->getStartFrame();
|
Chris@45
|
3540 }
|
Chris@45
|
3541 }
|
Chris@45
|
3542
|
Chris@45
|
3543 if (frame < 0) frame = 0;
|
Chris@45
|
3544
|
Chris@45
|
3545 if (m_viewManager->getPlaySelectionMode()) {
|
Chris@435
|
3546 frame = m_viewManager->constrainFrameToSelection(frame);
|
Chris@45
|
3547 }
|
Chris@45
|
3548
|
Chris@45
|
3549 m_viewManager->setPlaybackFrame(frame);
|
Chris@45
|
3550 }
|
Chris@45
|
3551
|
Chris@45
|
3552 void
|
Chris@45
|
3553 MainWindowBase::rewindStart()
|
Chris@45
|
3554 {
|
Chris@45
|
3555 if (!getMainModel()) return;
|
Chris@45
|
3556
|
Chris@435
|
3557 sv_frame_t frame = getMainModel()->getStartFrame();
|
Chris@45
|
3558
|
Chris@45
|
3559 if (m_viewManager->getPlaySelectionMode()) {
|
Chris@45
|
3560 frame = m_viewManager->constrainFrameToSelection(frame);
|
Chris@45
|
3561 }
|
Chris@45
|
3562
|
Chris@45
|
3563 m_viewManager->setPlaybackFrame(frame);
|
Chris@45
|
3564 }
|
Chris@45
|
3565
|
Chris@166
|
3566 void
|
Chris@166
|
3567 MainWindowBase::rewindSimilar()
|
Chris@166
|
3568 {
|
Chris@166
|
3569 if (!getMainModel()) return;
|
Chris@166
|
3570
|
Chris@166
|
3571 Layer *layer = getSnapLayer();
|
Chris@166
|
3572 if (!layer) { rewind(); return; }
|
Chris@166
|
3573
|
Chris@166
|
3574 Pane *pane = m_paneStack->getCurrentPane();
|
Chris@435
|
3575 sv_frame_t frame = m_viewManager->getPlaybackFrame();
|
Chris@166
|
3576
|
Chris@366
|
3577 int resolution = 0;
|
Chris@166
|
3578 if (pane) frame = pane->alignFromReference(frame);
|
Chris@166
|
3579 if (layer->snapToSimilarFeature(m_paneStack->getCurrentPane(),
|
Chris@166
|
3580 frame, resolution, Layer::SnapLeft)) {
|
Chris@166
|
3581 if (pane) frame = pane->alignToReference(frame);
|
Chris@166
|
3582 } else {
|
Chris@166
|
3583 frame = getMainModel()->getStartFrame();
|
Chris@166
|
3584 }
|
Chris@166
|
3585
|
Chris@166
|
3586 if (frame < 0) frame = 0;
|
Chris@166
|
3587
|
Chris@166
|
3588 if (m_viewManager->getPlaySelectionMode()) {
|
Chris@435
|
3589 frame = m_viewManager->constrainFrameToSelection(frame);
|
Chris@166
|
3590 }
|
Chris@166
|
3591
|
Chris@166
|
3592 m_viewManager->setPlaybackFrame(frame);
|
Chris@166
|
3593 }
|
Chris@166
|
3594
|
Chris@45
|
3595 Layer *
|
Chris@45
|
3596 MainWindowBase::getSnapLayer() const
|
Chris@45
|
3597 {
|
Chris@45
|
3598 Pane *pane = m_paneStack->getCurrentPane();
|
Chris@636
|
3599 if (!pane) return nullptr;
|
Chris@45
|
3600
|
Chris@45
|
3601 Layer *layer = pane->getSelectedLayer();
|
Chris@45
|
3602
|
Chris@45
|
3603 if (!dynamic_cast<TimeInstantLayer *>(layer) &&
|
Chris@45
|
3604 !dynamic_cast<TimeValueLayer *>(layer) &&
|
Chris@194
|
3605 !dynamic_cast<RegionLayer *>(layer) &&
|
Chris@45
|
3606 !dynamic_cast<TimeRulerLayer *>(layer)) {
|
Chris@45
|
3607
|
Chris@636
|
3608 layer = nullptr;
|
Chris@45
|
3609
|
Chris@45
|
3610 for (int i = pane->getLayerCount(); i > 0; --i) {
|
Chris@45
|
3611 Layer *l = pane->getLayer(i-1);
|
Chris@45
|
3612 if (dynamic_cast<TimeRulerLayer *>(l)) {
|
Chris@45
|
3613 layer = l;
|
Chris@45
|
3614 break;
|
Chris@45
|
3615 }
|
Chris@45
|
3616 }
|
Chris@45
|
3617 }
|
Chris@45
|
3618
|
Chris@45
|
3619 return layer;
|
Chris@45
|
3620 }
|
Chris@45
|
3621
|
Chris@45
|
3622 void
|
Chris@45
|
3623 MainWindowBase::stop()
|
Chris@45
|
3624 {
|
Chris@516
|
3625 if (m_recordTarget &&
|
Chris@516
|
3626 m_recordTarget->isRecording()) {
|
Chris@477
|
3627 m_recordTarget->stopRecording();
|
Chris@477
|
3628 }
|
Chris@516
|
3629
|
Chris@516
|
3630 if (!m_playSource) return;
|
Chris@516
|
3631
|
Chris@45
|
3632 m_playSource->stop();
|
Chris@45
|
3633
|
Chris@611
|
3634 SVCERR << "MainWindowBase::stop: suspending" << endl;
|
Chris@611
|
3635
|
Chris@487
|
3636 if (m_audioIO) m_audioIO->suspend();
|
Chris@509
|
3637 else if (m_playTarget) m_playTarget->suspend();
|
Chris@487
|
3638
|
Chris@45
|
3639 if (m_paneStack && m_paneStack->getCurrentPane()) {
|
Chris@45
|
3640 updateVisibleRangeDisplay(m_paneStack->getCurrentPane());
|
Chris@45
|
3641 } else {
|
Chris@45
|
3642 m_myStatusMessage = "";
|
Chris@378
|
3643 getStatusLabel()->setText("");
|
Chris@45
|
3644 }
|
Chris@45
|
3645 }
|
Chris@45
|
3646
|
Chris@45
|
3647 MainWindowBase::AddPaneCommand::AddPaneCommand(MainWindowBase *mw) :
|
Chris@45
|
3648 m_mw(mw),
|
Chris@636
|
3649 m_pane(nullptr),
|
Chris@636
|
3650 m_prevCurrentPane(nullptr),
|
Chris@45
|
3651 m_added(false)
|
Chris@45
|
3652 {
|
Chris@45
|
3653 }
|
Chris@45
|
3654
|
Chris@45
|
3655 MainWindowBase::AddPaneCommand::~AddPaneCommand()
|
Chris@45
|
3656 {
|
Chris@45
|
3657 if (m_pane && !m_added) {
|
Chris@595
|
3658 m_mw->m_paneStack->deletePane(m_pane);
|
Chris@45
|
3659 }
|
Chris@45
|
3660 }
|
Chris@45
|
3661
|
Chris@45
|
3662 QString
|
Chris@45
|
3663 MainWindowBase::AddPaneCommand::getName() const
|
Chris@45
|
3664 {
|
Chris@45
|
3665 return tr("Add Pane");
|
Chris@45
|
3666 }
|
Chris@45
|
3667
|
Chris@45
|
3668 void
|
Chris@45
|
3669 MainWindowBase::AddPaneCommand::execute()
|
Chris@45
|
3670 {
|
Chris@45
|
3671 if (!m_pane) {
|
Chris@595
|
3672 m_prevCurrentPane = m_mw->m_paneStack->getCurrentPane();
|
Chris@595
|
3673 m_pane = m_mw->m_paneStack->addPane();
|
Chris@45
|
3674
|
Chris@45
|
3675 connect(m_pane, SIGNAL(contextHelpChanged(const QString &)),
|
Chris@45
|
3676 m_mw, SLOT(contextHelpChanged(const QString &)));
|
Chris@45
|
3677 } else {
|
Chris@595
|
3678 m_mw->m_paneStack->showPane(m_pane);
|
Chris@45
|
3679 }
|
Chris@45
|
3680
|
Chris@45
|
3681 m_mw->m_paneStack->setCurrentPane(m_pane);
|
Chris@45
|
3682 m_added = true;
|
Chris@45
|
3683 }
|
Chris@45
|
3684
|
Chris@45
|
3685 void
|
Chris@45
|
3686 MainWindowBase::AddPaneCommand::unexecute()
|
Chris@45
|
3687 {
|
Chris@45
|
3688 m_mw->m_paneStack->hidePane(m_pane);
|
Chris@45
|
3689 m_mw->m_paneStack->setCurrentPane(m_prevCurrentPane);
|
Chris@45
|
3690 m_added = false;
|
Chris@45
|
3691 }
|
Chris@45
|
3692
|
Chris@45
|
3693 MainWindowBase::RemovePaneCommand::RemovePaneCommand(MainWindowBase *mw, Pane *pane) :
|
Chris@45
|
3694 m_mw(mw),
|
Chris@45
|
3695 m_pane(pane),
|
Chris@636
|
3696 m_prevCurrentPane(nullptr),
|
Chris@45
|
3697 m_added(true)
|
Chris@45
|
3698 {
|
Chris@45
|
3699 }
|
Chris@45
|
3700
|
Chris@45
|
3701 MainWindowBase::RemovePaneCommand::~RemovePaneCommand()
|
Chris@45
|
3702 {
|
Chris@45
|
3703 if (m_pane && !m_added) {
|
Chris@595
|
3704 m_mw->m_paneStack->deletePane(m_pane);
|
Chris@45
|
3705 }
|
Chris@45
|
3706 }
|
Chris@45
|
3707
|
Chris@45
|
3708 QString
|
Chris@45
|
3709 MainWindowBase::RemovePaneCommand::getName() const
|
Chris@45
|
3710 {
|
Chris@45
|
3711 return tr("Remove Pane");
|
Chris@45
|
3712 }
|
Chris@45
|
3713
|
Chris@45
|
3714 void
|
Chris@45
|
3715 MainWindowBase::RemovePaneCommand::execute()
|
Chris@45
|
3716 {
|
Chris@45
|
3717 m_prevCurrentPane = m_mw->m_paneStack->getCurrentPane();
|
Chris@45
|
3718 m_mw->m_paneStack->hidePane(m_pane);
|
Chris@45
|
3719 m_added = false;
|
Chris@45
|
3720 }
|
Chris@45
|
3721
|
Chris@45
|
3722 void
|
Chris@45
|
3723 MainWindowBase::RemovePaneCommand::unexecute()
|
Chris@45
|
3724 {
|
Chris@45
|
3725 m_mw->m_paneStack->showPane(m_pane);
|
Chris@45
|
3726 m_mw->m_paneStack->setCurrentPane(m_prevCurrentPane);
|
Chris@45
|
3727 m_added = true;
|
Chris@45
|
3728 }
|
Chris@45
|
3729
|
Chris@45
|
3730 void
|
Chris@45
|
3731 MainWindowBase::deleteCurrentPane()
|
Chris@45
|
3732 {
|
Chris@45
|
3733 CommandHistory::getInstance()->startCompoundOperation
|
Chris@595
|
3734 (tr("Delete Pane"), true);
|
Chris@45
|
3735
|
Chris@45
|
3736 Pane *pane = m_paneStack->getCurrentPane();
|
Chris@45
|
3737 if (pane) {
|
Chris@595
|
3738 while (pane->getLayerCount() > 0) {
|
Chris@595
|
3739 Layer *layer = pane->getLayer(0);
|
Chris@595
|
3740 if (layer) {
|
Chris@595
|
3741 m_document->removeLayerFromView(pane, layer);
|
Chris@595
|
3742 } else {
|
Chris@595
|
3743 break;
|
Chris@595
|
3744 }
|
Chris@595
|
3745 }
|
Chris@595
|
3746
|
Chris@595
|
3747 RemovePaneCommand *command = new RemovePaneCommand(this, pane);
|
Chris@595
|
3748 CommandHistory::getInstance()->addCommand(command);
|
Chris@45
|
3749 }
|
Chris@45
|
3750
|
Chris@45
|
3751 CommandHistory::getInstance()->endCompoundOperation();
|
Chris@45
|
3752
|
Chris@45
|
3753 updateMenuStates();
|
Chris@45
|
3754 }
|
Chris@45
|
3755
|
Chris@45
|
3756 void
|
Chris@45
|
3757 MainWindowBase::deleteCurrentLayer()
|
Chris@45
|
3758 {
|
Chris@45
|
3759 Pane *pane = m_paneStack->getCurrentPane();
|
Chris@45
|
3760 if (pane) {
|
Chris@595
|
3761 Layer *layer = pane->getSelectedLayer();
|
Chris@595
|
3762 if (layer) {
|
Chris@595
|
3763 m_document->removeLayerFromView(pane, layer);
|
Chris@595
|
3764 }
|
Chris@45
|
3765 }
|
Chris@45
|
3766 updateMenuStates();
|
Chris@45
|
3767 }
|
Chris@45
|
3768
|
Chris@45
|
3769 void
|
Chris@123
|
3770 MainWindowBase::editCurrentLayer()
|
Chris@123
|
3771 {
|
Chris@636
|
3772 Layer *layer = nullptr;
|
Chris@123
|
3773 Pane *pane = m_paneStack->getCurrentPane();
|
Chris@123
|
3774 if (pane) layer = pane->getSelectedLayer();
|
Chris@123
|
3775 if (!layer) return;
|
Chris@123
|
3776
|
Chris@684
|
3777 auto tabular = ModelById::getAs<TabularModel>(layer->getModel());
|
Chris@124
|
3778 if (!tabular) {
|
Chris@124
|
3779 //!!! how to prevent this function from being active if not
|
Chris@124
|
3780 //appropriate model type? or will we ultimately support
|
Chris@124
|
3781 //tabular display for all editable models?
|
Chris@233
|
3782 SVDEBUG << "NOTE: Not a tabular model" << endl;
|
Chris@124
|
3783 return;
|
Chris@124
|
3784 }
|
Chris@124
|
3785
|
Chris@123
|
3786 if (m_layerDataDialogMap.find(layer) != m_layerDataDialogMap.end()) {
|
Chris@126
|
3787 if (!m_layerDataDialogMap[layer].isNull()) {
|
Chris@126
|
3788 m_layerDataDialogMap[layer]->show();
|
Chris@126
|
3789 m_layerDataDialogMap[layer]->raise();
|
Chris@126
|
3790 return;
|
Chris@126
|
3791 }
|
Chris@123
|
3792 }
|
Chris@123
|
3793
|
Chris@125
|
3794 QString title = layer->getLayerPresentationName();
|
Chris@125
|
3795
|
Chris@684
|
3796 ModelDataTableDialog *dialog = new ModelDataTableDialog
|
Chris@684
|
3797 (layer->getModel(), title, this);
|
Chris@128
|
3798 dialog->setAttribute(Qt::WA_DeleteOnClose);
|
Chris@128
|
3799
|
Chris@128
|
3800 connectLayerEditDialog(dialog);
|
Chris@123
|
3801
|
Chris@128
|
3802 m_layerDataDialogMap[layer] = dialog;
|
Chris@128
|
3803 m_viewDataDialogMap[pane].insert(dialog);
|
Chris@128
|
3804
|
Chris@128
|
3805 dialog->show();
|
Chris@128
|
3806 }
|
Chris@128
|
3807
|
Chris@128
|
3808 void
|
Chris@128
|
3809 MainWindowBase::connectLayerEditDialog(ModelDataTableDialog *dialog)
|
Chris@128
|
3810 {
|
Chris@123
|
3811 connect(m_viewManager,
|
Chris@435
|
3812 SIGNAL(globalCentreFrameChanged(sv_frame_t)),
|
Chris@123
|
3813 dialog,
|
Chris@435
|
3814 SLOT(userScrolledToFrame(sv_frame_t)));
|
Chris@127
|
3815
|
Chris@127
|
3816 connect(m_viewManager,
|
Chris@435
|
3817 SIGNAL(playbackFrameChanged(sv_frame_t)),
|
Chris@127
|
3818 dialog,
|
Chris@435
|
3819 SLOT(playbackScrolledToFrame(sv_frame_t)));
|
Chris@127
|
3820
|
Chris@123
|
3821 connect(dialog,
|
Chris@435
|
3822 SIGNAL(scrollToFrame(sv_frame_t)),
|
Chris@123
|
3823 m_viewManager,
|
Chris@435
|
3824 SLOT(setGlobalCentreFrame(sv_frame_t)));
|
Chris@129
|
3825
|
Chris@129
|
3826 connect(dialog,
|
Chris@435
|
3827 SIGNAL(scrollToFrame(sv_frame_t)),
|
Chris@129
|
3828 m_viewManager,
|
Chris@435
|
3829 SLOT(setPlaybackFrame(sv_frame_t)));
|
Chris@128
|
3830 }
|
Chris@123
|
3831
|
Chris@123
|
3832 void
|
Chris@73
|
3833 MainWindowBase::previousPane()
|
Chris@73
|
3834 {
|
Chris@73
|
3835 if (!m_paneStack) return;
|
Chris@73
|
3836
|
Chris@73
|
3837 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@73
|
3838 if (!currentPane) return;
|
Chris@73
|
3839
|
Chris@73
|
3840 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
|
Chris@73
|
3841 if (m_paneStack->getPane(i) == currentPane) {
|
Chris@73
|
3842 if (i == 0) return;
|
Chris@73
|
3843 m_paneStack->setCurrentPane(m_paneStack->getPane(i-1));
|
Chris@73
|
3844 updateMenuStates();
|
Chris@73
|
3845 return;
|
Chris@73
|
3846 }
|
Chris@73
|
3847 }
|
Chris@73
|
3848 }
|
Chris@73
|
3849
|
Chris@73
|
3850 void
|
Chris@73
|
3851 MainWindowBase::nextPane()
|
Chris@73
|
3852 {
|
Chris@73
|
3853 if (!m_paneStack) return;
|
Chris@73
|
3854
|
Chris@73
|
3855 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@73
|
3856 if (!currentPane) return;
|
Chris@73
|
3857
|
Chris@73
|
3858 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
|
Chris@73
|
3859 if (m_paneStack->getPane(i) == currentPane) {
|
Chris@73
|
3860 if (i == m_paneStack->getPaneCount()-1) return;
|
Chris@73
|
3861 m_paneStack->setCurrentPane(m_paneStack->getPane(i+1));
|
Chris@73
|
3862 updateMenuStates();
|
Chris@73
|
3863 return;
|
Chris@73
|
3864 }
|
Chris@73
|
3865 }
|
Chris@73
|
3866 }
|
Chris@73
|
3867
|
Chris@73
|
3868 void
|
Chris@73
|
3869 MainWindowBase::previousLayer()
|
Chris@73
|
3870 {
|
Chris@73
|
3871 if (!m_paneStack) return;
|
Chris@73
|
3872
|
Chris@73
|
3873 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@73
|
3874 if (!currentPane) return;
|
Chris@73
|
3875
|
Chris@403
|
3876 int count = currentPane->getLayerCount();
|
Chris@403
|
3877 if (count == 0) return;
|
Chris@403
|
3878
|
Chris@73
|
3879 Layer *currentLayer = currentPane->getSelectedLayer();
|
Chris@403
|
3880
|
Chris@403
|
3881 if (!currentLayer) {
|
Chris@403
|
3882 // The pane itself is current
|
Chris@403
|
3883 m_paneStack->setCurrentLayer
|
Chris@403
|
3884 (currentPane, currentPane->getFixedOrderLayer(count-1));
|
Chris@403
|
3885 } else {
|
Chris@403
|
3886 for (int i = 0; i < count; ++i) {
|
Chris@403
|
3887 if (currentPane->getFixedOrderLayer(i) == currentLayer) {
|
Chris@403
|
3888 if (i == 0) {
|
Chris@403
|
3889 m_paneStack->setCurrentLayer
|
Chris@636
|
3890 (currentPane, nullptr); // pane
|
Chris@403
|
3891 } else {
|
Chris@403
|
3892 m_paneStack->setCurrentLayer
|
Chris@403
|
3893 (currentPane, currentPane->getFixedOrderLayer(i-1));
|
Chris@403
|
3894 }
|
Chris@403
|
3895 break;
|
Chris@403
|
3896 }
|
Chris@73
|
3897 }
|
Chris@73
|
3898 }
|
Chris@403
|
3899
|
Chris@403
|
3900 updateMenuStates();
|
Chris@73
|
3901 }
|
Chris@73
|
3902
|
Chris@73
|
3903 void
|
Chris@73
|
3904 MainWindowBase::nextLayer()
|
Chris@73
|
3905 {
|
Chris@73
|
3906 if (!m_paneStack) return;
|
Chris@73
|
3907
|
Chris@73
|
3908 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@73
|
3909 if (!currentPane) return;
|
Chris@73
|
3910
|
Chris@403
|
3911 int count = currentPane->getLayerCount();
|
Chris@403
|
3912 if (count == 0) return;
|
Chris@403
|
3913
|
Chris@73
|
3914 Layer *currentLayer = currentPane->getSelectedLayer();
|
Chris@403
|
3915
|
Chris@403
|
3916 if (!currentLayer) {
|
Chris@403
|
3917 // The pane itself is current
|
Chris@403
|
3918 m_paneStack->setCurrentLayer
|
Chris@403
|
3919 (currentPane, currentPane->getFixedOrderLayer(0));
|
Chris@403
|
3920 } else {
|
Chris@403
|
3921 for (int i = 0; i < count; ++i) {
|
Chris@403
|
3922 if (currentPane->getFixedOrderLayer(i) == currentLayer) {
|
Chris@403
|
3923 if (i == currentPane->getLayerCount()-1) {
|
Chris@403
|
3924 m_paneStack->setCurrentLayer
|
Chris@636
|
3925 (currentPane, nullptr); // pane
|
Chris@403
|
3926 } else {
|
Chris@403
|
3927 m_paneStack->setCurrentLayer
|
Chris@403
|
3928 (currentPane, currentPane->getFixedOrderLayer(i+1));
|
Chris@403
|
3929 }
|
Chris@403
|
3930 break;
|
Chris@403
|
3931 }
|
Chris@73
|
3932 }
|
Chris@73
|
3933 }
|
Chris@403
|
3934
|
Chris@403
|
3935 updateMenuStates();
|
Chris@73
|
3936 }
|
Chris@73
|
3937
|
Chris@73
|
3938 void
|
Chris@435
|
3939 MainWindowBase::playbackFrameChanged(sv_frame_t frame)
|
Chris@45
|
3940 {
|
Chris@45
|
3941 if (!(m_playSource && m_playSource->isPlaying()) || !getMainModel()) return;
|
Chris@45
|
3942
|
Chris@187
|
3943 updatePositionStatusDisplays();
|
Chris@187
|
3944
|
Chris@45
|
3945 RealTime now = RealTime::frame2RealTime
|
Chris@45
|
3946 (frame, getMainModel()->getSampleRate());
|
Chris@45
|
3947
|
Chris@45
|
3948 if (now.sec == m_lastPlayStatusSec) return;
|
Chris@45
|
3949
|
Chris@45
|
3950 RealTime then = RealTime::frame2RealTime
|
Chris@45
|
3951 (m_playSource->getPlayEndFrame(), getMainModel()->getSampleRate());
|
Chris@45
|
3952
|
Chris@45
|
3953 QString nowStr;
|
Chris@45
|
3954 QString thenStr;
|
Chris@45
|
3955 QString remainingStr;
|
Chris@45
|
3956
|
Chris@45
|
3957 if (then.sec > 10) {
|
Chris@45
|
3958 nowStr = now.toSecText().c_str();
|
Chris@45
|
3959 thenStr = then.toSecText().c_str();
|
Chris@45
|
3960 remainingStr = (then - now).toSecText().c_str();
|
Chris@45
|
3961 m_lastPlayStatusSec = now.sec;
|
Chris@45
|
3962 } else {
|
Chris@45
|
3963 nowStr = now.toText(true).c_str();
|
Chris@45
|
3964 thenStr = then.toText(true).c_str();
|
Chris@45
|
3965 remainingStr = (then - now).toText(true).c_str();
|
Chris@45
|
3966 }
|
Chris@45
|
3967
|
Chris@45
|
3968 m_myStatusMessage = tr("Playing: %1 of %2 (%3 remaining)")
|
Chris@45
|
3969 .arg(nowStr).arg(thenStr).arg(remainingStr);
|
Chris@45
|
3970
|
Chris@378
|
3971 getStatusLabel()->setText(m_myStatusMessage);
|
Chris@45
|
3972 }
|
Chris@45
|
3973
|
Chris@45
|
3974 void
|
Chris@486
|
3975 MainWindowBase::recordDurationChanged(sv_frame_t frame, sv_samplerate_t rate)
|
Chris@486
|
3976 {
|
Chris@486
|
3977 RealTime duration = RealTime::frame2RealTime(frame, rate);
|
Chris@486
|
3978 QString durStr = duration.toSecText().c_str();
|
Chris@486
|
3979
|
Chris@486
|
3980 m_myStatusMessage = tr("Recording: %1").arg(durStr);
|
Chris@486
|
3981
|
Chris@486
|
3982 getStatusLabel()->setText(m_myStatusMessage);
|
Chris@486
|
3983 }
|
Chris@486
|
3984
|
Chris@486
|
3985 void
|
Chris@435
|
3986 MainWindowBase::globalCentreFrameChanged(sv_frame_t )
|
Chris@45
|
3987 {
|
Chris@45
|
3988 if ((m_playSource && m_playSource->isPlaying()) || !getMainModel()) return;
|
Chris@636
|
3989 Pane *p = nullptr;
|
Chris@45
|
3990 if (!m_paneStack || !(p = m_paneStack->getCurrentPane())) return;
|
Chris@45
|
3991 if (!p->getFollowGlobalPan()) return;
|
Chris@45
|
3992 updateVisibleRangeDisplay(p);
|
Chris@45
|
3993 }
|
Chris@45
|
3994
|
Chris@45
|
3995 void
|
Chris@435
|
3996 MainWindowBase::viewCentreFrameChanged(View *v, sv_frame_t frame)
|
Chris@45
|
3997 {
|
Chris@233
|
3998 // SVDEBUG << "MainWindowBase::viewCentreFrameChanged(" << v << "," << frame << ")" << endl;
|
Chris@123
|
3999
|
Chris@123
|
4000 if (m_viewDataDialogMap.find(v) != m_viewDataDialogMap.end()) {
|
Chris@123
|
4001 for (DataDialogSet::iterator i = m_viewDataDialogMap[v].begin();
|
Chris@123
|
4002 i != m_viewDataDialogMap[v].end(); ++i) {
|
Chris@127
|
4003 (*i)->userScrolledToFrame(frame);
|
Chris@123
|
4004 }
|
Chris@123
|
4005 }
|
Chris@45
|
4006 if ((m_playSource && m_playSource->isPlaying()) || !getMainModel()) return;
|
Chris@636
|
4007 Pane *p = nullptr;
|
Chris@45
|
4008 if (!m_paneStack || !(p = m_paneStack->getCurrentPane())) return;
|
Chris@45
|
4009 if (v == p) updateVisibleRangeDisplay(p);
|
Chris@45
|
4010 }
|
Chris@45
|
4011
|
Chris@45
|
4012 void
|
Chris@624
|
4013 MainWindowBase::viewZoomLevelChanged(View *v, ZoomLevel, bool )
|
Chris@45
|
4014 {
|
Chris@45
|
4015 if ((m_playSource && m_playSource->isPlaying()) || !getMainModel()) return;
|
Chris@636
|
4016 Pane *p = nullptr;
|
Chris@45
|
4017 if (!m_paneStack || !(p = m_paneStack->getCurrentPane())) return;
|
Chris@45
|
4018 if (v == p) updateVisibleRangeDisplay(p);
|
Chris@45
|
4019 }
|
Chris@45
|
4020
|
Chris@45
|
4021 void
|
Chris@45
|
4022 MainWindowBase::layerAdded(Layer *)
|
Chris@45
|
4023 {
|
Chris@233
|
4024 // SVDEBUG << "MainWindowBase::layerAdded(" << layer << ")" << endl;
|
Chris@45
|
4025 updateMenuStates();
|
Chris@45
|
4026 }
|
Chris@45
|
4027
|
Chris@45
|
4028 void
|
Chris@45
|
4029 MainWindowBase::layerRemoved(Layer *)
|
Chris@45
|
4030 {
|
Chris@233
|
4031 // SVDEBUG << "MainWindowBase::layerRemoved(" << layer << ")" << endl;
|
Chris@45
|
4032 updateMenuStates();
|
Chris@45
|
4033 }
|
Chris@45
|
4034
|
Chris@45
|
4035 void
|
Chris@45
|
4036 MainWindowBase::layerAboutToBeDeleted(Layer *layer)
|
Chris@45
|
4037 {
|
Chris@233
|
4038 // SVDEBUG << "MainWindowBase::layerAboutToBeDeleted(" << layer << ")" << endl;
|
Chris@123
|
4039
|
Chris@128
|
4040 removeLayerEditDialog(layer);
|
Chris@123
|
4041
|
Chris@47
|
4042 if (m_timeRulerLayer && (layer == m_timeRulerLayer)) {
|
Chris@595
|
4043 // cerr << "(this is the time ruler layer)" << endl;
|
Chris@636
|
4044 m_timeRulerLayer = nullptr;
|
Chris@45
|
4045 }
|
Chris@45
|
4046 }
|
Chris@45
|
4047
|
Chris@45
|
4048 void
|
Chris@45
|
4049 MainWindowBase::layerInAView(Layer *layer, bool inAView)
|
Chris@45
|
4050 {
|
Chris@233
|
4051 // SVDEBUG << "MainWindowBase::layerInAView(" << layer << "," << inAView << ")" << endl;
|
Chris@128
|
4052
|
Chris@128
|
4053 if (!inAView) removeLayerEditDialog(layer);
|
Chris@45
|
4054
|
Chris@45
|
4055 // Check whether we need to add or remove model from play source
|
Chris@684
|
4056 ModelId modelId = layer->getModel();
|
Chris@684
|
4057 if (!modelId.isNone()) {
|
Chris@45
|
4058 if (inAView) {
|
Chris@684
|
4059 m_playSource->addModel(modelId);
|
Chris@45
|
4060 } else {
|
Chris@45
|
4061 bool found = false;
|
Chris@45
|
4062 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
|
Chris@45
|
4063 Pane *pane = m_paneStack->getPane(i);
|
Chris@45
|
4064 if (!pane) continue;
|
Chris@45
|
4065 for (int j = 0; j < pane->getLayerCount(); ++j) {
|
Chris@45
|
4066 Layer *pl = pane->getLayer(j);
|
Chris@183
|
4067 if (pl &&
|
Chris@183
|
4068 !dynamic_cast<TimeRulerLayer *>(pl) &&
|
Chris@684
|
4069 (pl->getModel() == modelId)) {
|
Chris@45
|
4070 found = true;
|
Chris@45
|
4071 break;
|
Chris@45
|
4072 }
|
Chris@45
|
4073 }
|
Chris@45
|
4074 if (found) break;
|
Chris@45
|
4075 }
|
Chris@173
|
4076 if (!found) {
|
Chris@684
|
4077 m_playSource->removeModel(modelId);
|
Chris@173
|
4078 }
|
Chris@45
|
4079 }
|
Chris@45
|
4080 }
|
Chris@45
|
4081
|
Chris@45
|
4082 updateMenuStates();
|
Chris@45
|
4083 }
|
Chris@45
|
4084
|
Chris@45
|
4085 void
|
Chris@128
|
4086 MainWindowBase::removeLayerEditDialog(Layer *layer)
|
Chris@128
|
4087 {
|
Chris@128
|
4088 if (m_layerDataDialogMap.find(layer) != m_layerDataDialogMap.end()) {
|
Chris@128
|
4089
|
Chris@128
|
4090 ModelDataTableDialog *dialog = m_layerDataDialogMap[layer];
|
Chris@128
|
4091
|
Chris@128
|
4092 for (ViewDataDialogMap::iterator vi = m_viewDataDialogMap.begin();
|
Chris@128
|
4093 vi != m_viewDataDialogMap.end(); ++vi) {
|
Chris@128
|
4094 vi->second.erase(dialog);
|
Chris@128
|
4095 }
|
Chris@128
|
4096
|
Chris@128
|
4097 m_layerDataDialogMap.erase(layer);
|
Chris@128
|
4098 delete dialog;
|
Chris@128
|
4099 }
|
Chris@128
|
4100 }
|
Chris@128
|
4101
|
Chris@128
|
4102 void
|
Chris@684
|
4103 MainWindowBase::modelAdded(ModelId model)
|
Chris@45
|
4104 {
|
Chris@233
|
4105 // SVDEBUG << "MainWindowBase::modelAdded(" << model << ")" << endl;
|
Chris@684
|
4106 std::cerr << "\nAdding model " << model << " to playsource " << std::endl;
|
Chris@45
|
4107 m_playSource->addModel(model);
|
Chris@45
|
4108 }
|
Chris@45
|
4109
|
Chris@45
|
4110 void
|
Chris@684
|
4111 MainWindowBase::mainModelChanged(ModelId modelId)
|
Chris@45
|
4112 {
|
Chris@233
|
4113 // SVDEBUG << "MainWindowBase::mainModelChanged(" << model << ")" << endl;
|
Chris@45
|
4114 updateDescriptionLabel();
|
Chris@684
|
4115 auto model = ModelById::getAs<WaveFileModel>(modelId);
|
Chris@45
|
4116 if (model) m_viewManager->setMainModelSampleRate(model->getSampleRate());
|
Chris@714
|
4117 if (model && !(m_playTarget || m_audioIO) && (m_audioMode != AUDIO_NONE)) {
|
Chris@475
|
4118 createAudioIO();
|
Chris@360
|
4119 }
|
Chris@45
|
4120 }
|
Chris@45
|
4121
|
Chris@45
|
4122 void
|
Chris@55
|
4123 MainWindowBase::paneDeleteButtonClicked(Pane *pane)
|
Chris@55
|
4124 {
|
Chris@55
|
4125 bool found = false;
|
Chris@55
|
4126 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
|
Chris@55
|
4127 if (m_paneStack->getPane(i) == pane) {
|
Chris@55
|
4128 found = true;
|
Chris@55
|
4129 break;
|
Chris@55
|
4130 }
|
Chris@55
|
4131 }
|
Chris@55
|
4132 if (!found) {
|
Chris@233
|
4133 SVDEBUG << "MainWindowBase::paneDeleteButtonClicked: Unknown pane "
|
Chris@229
|
4134 << pane << endl;
|
Chris@55
|
4135 return;
|
Chris@55
|
4136 }
|
Chris@55
|
4137
|
Chris@55
|
4138 CommandHistory::getInstance()->startCompoundOperation
|
Chris@595
|
4139 (tr("Delete Pane"), true);
|
Chris@55
|
4140
|
Chris@55
|
4141 while (pane->getLayerCount() > 0) {
|
Chris@637
|
4142 Layer *layer = pane->getLayer(pane->getLayerCount() - 1);
|
Chris@55
|
4143 if (layer) {
|
Chris@55
|
4144 m_document->removeLayerFromView(pane, layer);
|
Chris@55
|
4145 } else {
|
Chris@55
|
4146 break;
|
Chris@55
|
4147 }
|
Chris@55
|
4148 }
|
Chris@55
|
4149
|
Chris@55
|
4150 RemovePaneCommand *command = new RemovePaneCommand(this, pane);
|
Chris@55
|
4151 CommandHistory::getInstance()->addCommand(command);
|
Chris@55
|
4152
|
Chris@55
|
4153 CommandHistory::getInstance()->endCompoundOperation();
|
Chris@55
|
4154
|
Chris@55
|
4155 updateMenuStates();
|
Chris@55
|
4156 }
|
Chris@55
|
4157
|
Chris@55
|
4158 void
|
Chris@684
|
4159 MainWindowBase::alignmentComplete(ModelId alignmentModelId)
|
Chris@429
|
4160 {
|
Chris@684
|
4161 cerr << "MainWindowBase::alignmentComplete(" << alignmentModelId << ")" << endl;
|
Chris@429
|
4162 }
|
Chris@429
|
4163
|
Chris@429
|
4164 void
|
Chris@45
|
4165 MainWindowBase::pollOSC()
|
Chris@45
|
4166 {
|
Chris@45
|
4167 if (!m_oscQueue || m_oscQueue->isEmpty()) return;
|
Chris@233
|
4168 SVDEBUG << "MainWindowBase::pollOSC: have " << m_oscQueue->getMessagesAvailable() << " messages" << endl;
|
Chris@45
|
4169
|
Chris@45
|
4170 if (m_openingAudioFile) return;
|
Chris@45
|
4171
|
Chris@45
|
4172 OSCMessage message = m_oscQueue->readMessage();
|
Chris@45
|
4173
|
Chris@45
|
4174 if (message.getTarget() != 0) {
|
Chris@45
|
4175 return; //!!! for now -- this class is target 0, others not handled yet
|
Chris@45
|
4176 }
|
Chris@45
|
4177
|
Chris@45
|
4178 handleOSCMessage(message);
|
Chris@45
|
4179 }
|
Chris@45
|
4180
|
Chris@45
|
4181 void
|
Chris@45
|
4182 MainWindowBase::inProgressSelectionChanged()
|
Chris@45
|
4183 {
|
Chris@636
|
4184 Pane *currentPane = nullptr;
|
Chris@45
|
4185 if (m_paneStack) currentPane = m_paneStack->getCurrentPane();
|
justin@331
|
4186 if (currentPane) {
|
justin@331
|
4187 //cerr << "JTEST: mouse event on selection pane" << endl;
|
justin@331
|
4188 updateVisibleRangeDisplay(currentPane);
|
justin@331
|
4189 }
|
Chris@45
|
4190 }
|
Chris@45
|
4191
|
Chris@45
|
4192 void
|
Chris@45
|
4193 MainWindowBase::contextHelpChanged(const QString &s)
|
Chris@45
|
4194 {
|
Chris@378
|
4195 QLabel *lab = getStatusLabel();
|
Chris@375
|
4196
|
Chris@45
|
4197 if (s == "" && m_myStatusMessage != "") {
|
Chris@378
|
4198 if (lab->text() != m_myStatusMessage) {
|
Chris@378
|
4199 lab->setText(m_myStatusMessage);
|
Chris@375
|
4200 }
|
Chris@45
|
4201 return;
|
Chris@45
|
4202 }
|
Chris@375
|
4203
|
Chris@378
|
4204 lab->setText(s);
|
Chris@45
|
4205 }
|
Chris@45
|
4206
|
Chris@45
|
4207 void
|
Chris@45
|
4208 MainWindowBase::openHelpUrl(QString url)
|
Chris@45
|
4209 {
|
Chris@45
|
4210 // This method mostly lifted from Qt Assistant source code
|
Chris@45
|
4211
|
Chris@45
|
4212 QProcess *process = new QProcess(this);
|
Chris@45
|
4213 connect(process, SIGNAL(finished(int)), process, SLOT(deleteLater()));
|
Chris@45
|
4214
|
Chris@45
|
4215 QStringList args;
|
Chris@45
|
4216
|
Chris@45
|
4217 #ifdef Q_OS_MAC
|
Chris@45
|
4218 args.append(url);
|
Chris@45
|
4219 process->start("open", args);
|
Chris@45
|
4220 #else
|
Chris@45
|
4221 #ifdef Q_OS_WIN32
|
Chris@599
|
4222 std::string pfiles;
|
Chris@599
|
4223 (void)getEnvUtf8("ProgramFiles", pfiles);
|
Chris@599
|
4224 QString command =
|
Chris@599
|
4225 QString::fromStdString(pfiles) +
|
Chris@599
|
4226 QString("\\Internet Explorer\\IEXPLORE.EXE");
|
Chris@358
|
4227
|
Chris@358
|
4228 args.append(url);
|
Chris@358
|
4229 process->start(command, args);
|
Chris@45
|
4230 #else
|
Chris@45
|
4231 if (!qgetenv("KDE_FULL_SESSION").isEmpty()) {
|
Chris@45
|
4232 args.append("exec");
|
Chris@45
|
4233 args.append(url);
|
Chris@45
|
4234 process->start("kfmclient", args);
|
Chris@45
|
4235 } else if (!qgetenv("BROWSER").isEmpty()) {
|
Chris@45
|
4236 args.append(url);
|
Chris@45
|
4237 process->start(qgetenv("BROWSER"), args);
|
Chris@45
|
4238 } else {
|
Chris@45
|
4239 args.append(url);
|
Chris@45
|
4240 process->start("firefox", args);
|
Chris@45
|
4241 }
|
Chris@45
|
4242 #endif
|
Chris@45
|
4243 #endif
|
Chris@45
|
4244 }
|
Chris@45
|
4245
|
Chris@483
|
4246 void
|
Chris@483
|
4247 MainWindowBase::openLocalFolder(QString path)
|
Chris@483
|
4248 {
|
Chris@483
|
4249 QDir d(path);
|
Chris@483
|
4250 if (d.exists()) {
|
Chris@483
|
4251 QStringList args;
|
Chris@483
|
4252 QString path = d.canonicalPath();
|
Chris@483
|
4253 #if defined Q_OS_WIN32
|
Chris@483
|
4254 // Although the Win32 API is quite happy to have
|
Chris@483
|
4255 // forward slashes as directory separators, Windows
|
Chris@483
|
4256 // Explorer is not
|
Chris@483
|
4257 path = path.replace('/', '\\');
|
Chris@483
|
4258 args << path;
|
Chris@483
|
4259 QProcess::execute("c:/windows/explorer.exe", args);
|
Chris@483
|
4260 #else
|
Chris@483
|
4261 args << path;
|
Chris@605
|
4262 QProcess process;
|
Chris@605
|
4263 QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
|
Chris@606
|
4264 env.insert("LD_LIBRARY_PATH", "");
|
Chris@605
|
4265 process.setProcessEnvironment(env);
|
Chris@605
|
4266 process.start(
|
Chris@483
|
4267 #if defined Q_OS_MAC
|
Chris@483
|
4268 "/usr/bin/open",
|
Chris@483
|
4269 #else
|
Chris@483
|
4270 "/usr/bin/xdg-open",
|
Chris@483
|
4271 #endif
|
Chris@483
|
4272 args);
|
Chris@608
|
4273 process.waitForFinished();
|
Chris@483
|
4274 #endif
|
Chris@483
|
4275 }
|
Chris@483
|
4276 }
|
Chris@483
|
4277
|