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