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