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