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