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