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@694
|
1355
|
Chris@694
|
1356 ModelId modelId = layer->getModel();
|
Chris@45
|
1357
|
Chris@694
|
1358 auto sodm = ModelById::getAs<SparseOneDimensionalModel>(modelId);
|
Chris@45
|
1359 if (!sodm) return;
|
Chris@45
|
1360
|
Chris@45
|
1361 if (!m_labeller) return;
|
Chris@45
|
1362
|
Chris@45
|
1363 Labeller labeller(*m_labeller);
|
Chris@45
|
1364 labeller.setSampleRate(sodm->getSampleRate());
|
Chris@684
|
1365
|
Chris@694
|
1366 Command *c = labeller.labelAll(modelId.untyped, &ms, sodm->getAllEvents());
|
Chris@537
|
1367 if (c) CommandHistory::getInstance()->addCommand(c, false);
|
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@694
|
1380
|
Chris@694
|
1381 ModelId modelId = layer->getModel();
|
Chris@537
|
1382
|
Chris@694
|
1383 auto sodm = ModelById::getAs<SparseOneDimensionalModel>(modelId);
|
Chris@537
|
1384 if (!sodm) return;
|
Chris@537
|
1385
|
Chris@537
|
1386 if (!m_labeller) return;
|
Chris@537
|
1387
|
Chris@537
|
1388 Labeller labeller(*m_labeller);
|
Chris@537
|
1389 labeller.setSampleRate(sodm->getSampleRate());
|
Chris@537
|
1390
|
Chris@694
|
1391 Command *c = labeller.subdivide(modelId.untyped, &ms, sodm->getAllEvents(), n);
|
Chris@537
|
1392 if (c) CommandHistory::getInstance()->addCommand(c, false);
|
Chris@45
|
1393 }
|
Chris@45
|
1394
|
Chris@538
|
1395 void
|
Chris@538
|
1396 MainWindowBase::winnowInstantsBy(int n)
|
Chris@538
|
1397 {
|
Chris@538
|
1398 Pane *pane = m_paneStack->getCurrentPane();
|
Chris@538
|
1399 if (!pane) return;
|
Chris@538
|
1400
|
Chris@538
|
1401 Layer *layer = dynamic_cast<TimeInstantLayer *>(pane->getSelectedLayer());
|
Chris@538
|
1402 if (!layer) return;
|
Chris@538
|
1403
|
Chris@538
|
1404 MultiSelection ms(m_viewManager->getSelection());
|
Chris@694
|
1405
|
Chris@694
|
1406 ModelId modelId = layer->getModel();
|
Chris@538
|
1407
|
Chris@694
|
1408 auto sodm = ModelById::getAs<SparseOneDimensionalModel>(modelId);
|
Chris@538
|
1409 if (!sodm) return;
|
Chris@538
|
1410
|
Chris@538
|
1411 if (!m_labeller) return;
|
Chris@538
|
1412
|
Chris@538
|
1413 Labeller labeller(*m_labeller);
|
Chris@538
|
1414 labeller.setSampleRate(sodm->getSampleRate());
|
Chris@538
|
1415
|
Chris@694
|
1416 Command *c = labeller.winnow(modelId.untyped, &ms, sodm->getAllEvents(), n);
|
Chris@538
|
1417 if (c) CommandHistory::getInstance()->addCommand(c, false);
|
Chris@538
|
1418 }
|
Chris@538
|
1419
|
Chris@45
|
1420 MainWindowBase::FileOpenStatus
|
Chris@373
|
1421 MainWindowBase::openPath(QString fileOrUrl, AudioFileOpenMode mode)
|
Chris@45
|
1422 {
|
Chris@134
|
1423 ProgressDialog dialog(tr("Opening file or URL..."), true, 2000, this);
|
Chris@134
|
1424 connect(&dialog, SIGNAL(showing()), this, SIGNAL(hideSplash()));
|
Chris@109
|
1425 return open(FileSource(fileOrUrl, &dialog), mode);
|
Chris@45
|
1426 }
|
Chris@45
|
1427
|
Chris@45
|
1428 MainWindowBase::FileOpenStatus
|
Chris@45
|
1429 MainWindowBase::open(FileSource source, AudioFileOpenMode mode)
|
Chris@45
|
1430 {
|
Chris@45
|
1431 FileOpenStatus status;
|
Chris@45
|
1432
|
Chris@45
|
1433 if (!source.isAvailable()) return FileOpenFailed;
|
Chris@45
|
1434 source.waitForData();
|
Chris@45
|
1435
|
Chris@636
|
1436 bool canImportLayer = (getMainModel() != nullptr &&
|
Chris@636
|
1437 m_paneStack != nullptr &&
|
Chris@636
|
1438 m_paneStack->getCurrentPane() != nullptr);
|
Chris@45
|
1439
|
Chris@152
|
1440 bool rdf = (source.getExtension().toLower() == "rdf" ||
|
Chris@152
|
1441 source.getExtension().toLower() == "n3" ||
|
Chris@152
|
1442 source.getExtension().toLower() == "ttl");
|
Chris@152
|
1443
|
Chris@152
|
1444 bool audio = AudioFileReaderFactory::getKnownExtensions().contains
|
Chris@152
|
1445 (source.getExtension().toLower());
|
Chris@145
|
1446
|
Chris@145
|
1447 bool rdfSession = false;
|
Chris@145
|
1448 if (rdf) {
|
Chris@145
|
1449 RDFImporter::RDFDocumentType rdfType =
|
Chris@145
|
1450 RDFImporter::identifyDocumentType
|
Chris@145
|
1451 (QUrl::fromLocalFile(source.getLocalFilename()).toString());
|
Chris@145
|
1452 if (rdfType == RDFImporter::AudioRefAndAnnotations ||
|
Chris@145
|
1453 rdfType == RDFImporter::AudioRef) {
|
Chris@145
|
1454 rdfSession = true;
|
Chris@145
|
1455 } else if (rdfType == RDFImporter::NotRDF) {
|
Chris@145
|
1456 rdf = false;
|
Chris@145
|
1457 }
|
Chris@145
|
1458 }
|
Chris@145
|
1459
|
Chris@579
|
1460 try {
|
Chris@579
|
1461 if (rdf) {
|
Chris@579
|
1462 if (rdfSession) {
|
Chris@579
|
1463 bool cancel = false;
|
Chris@579
|
1464 if (!canImportLayer || shouldCreateNewSessionForRDFAudio(&cancel)) {
|
Chris@579
|
1465 return openSession(source);
|
Chris@579
|
1466 } else if (cancel) {
|
Chris@579
|
1467 return FileOpenCancelled;
|
Chris@579
|
1468 } else {
|
Chris@579
|
1469 return openLayer(source);
|
Chris@579
|
1470 }
|
Chris@145
|
1471 } else {
|
Chris@579
|
1472 if ((status = openSession(source)) != FileOpenFailed) {
|
Chris@579
|
1473 return status;
|
Chris@579
|
1474 } else if (!canImportLayer) {
|
Chris@579
|
1475 return FileOpenWrongMode;
|
Chris@579
|
1476 } else if ((status = openLayer(source)) != FileOpenFailed) {
|
Chris@579
|
1477 return status;
|
Chris@579
|
1478 } else {
|
Chris@579
|
1479 return FileOpenFailed;
|
Chris@579
|
1480 }
|
Chris@145
|
1481 }
|
Chris@145
|
1482 }
|
Chris@579
|
1483
|
Chris@579
|
1484 if (audio && (status = openAudio(source, mode)) != FileOpenFailed) {
|
Chris@579
|
1485 return status;
|
Chris@579
|
1486 } else if ((status = openSession(source)) != FileOpenFailed) {
|
Chris@579
|
1487 return status;
|
Chris@579
|
1488 } else if ((status = openPlaylist(source, mode)) != FileOpenFailed) {
|
Chris@579
|
1489 return status;
|
Chris@579
|
1490 } else if (!canImportLayer) {
|
Chris@579
|
1491 return FileOpenWrongMode;
|
Chris@579
|
1492 } else if ((status = openImage(source)) != FileOpenFailed) {
|
Chris@579
|
1493 return status;
|
Chris@579
|
1494 } else if ((status = openLayer(source)) != FileOpenFailed) {
|
Chris@579
|
1495 return status;
|
Chris@579
|
1496 } else {
|
Chris@579
|
1497 return FileOpenFailed;
|
Chris@579
|
1498 }
|
Chris@579
|
1499 } catch (const InsufficientDiscSpace &e) {
|
Chris@579
|
1500 emit hideSplash();
|
Chris@579
|
1501 m_openingAudioFile = false;
|
Chris@594
|
1502 SVCERR << "MainWindowBase: Caught InsufficientDiscSpace in file open" << endl;
|
Chris@579
|
1503 QMessageBox::critical
|
Chris@579
|
1504 (this, tr("Not enough disc space"),
|
Chris@579
|
1505 tr("<b>Not enough disc space</b><p>There doesn't appear to be enough spare disc space to accommodate any necessary temporary files.</p><p>Please clear some space and try again.</p>").arg(e.what()));
|
Chris@579
|
1506 return FileOpenFailed;
|
Chris@592
|
1507 } catch (const std::bad_alloc &e) { // reader may have rethrown this after cleaning up
|
Chris@592
|
1508 emit hideSplash();
|
Chris@592
|
1509 m_openingAudioFile = false;
|
Chris@594
|
1510 SVCERR << "MainWindowBase: Caught bad_alloc in file open" << endl;
|
Chris@592
|
1511 QMessageBox::critical
|
Chris@592
|
1512 (this, tr("Not enough memory"),
|
Chris@592
|
1513 tr("<b>Not enough memory</b><p>There doesn't appear to be enough memory to accommodate any necessary temporary data.</p>"));
|
Chris@592
|
1514 return FileOpenFailed;
|
Chris@45
|
1515 }
|
Chris@45
|
1516 }
|
Chris@45
|
1517
|
Chris@45
|
1518 MainWindowBase::FileOpenStatus
|
Chris@626
|
1519 MainWindowBase::openAudio(FileSource source,
|
Chris@626
|
1520 AudioFileOpenMode mode,
|
Chris@227
|
1521 QString templateName)
|
Chris@45
|
1522 {
|
Chris@386
|
1523 SVDEBUG << "MainWindowBase::openAudio(" << source.getLocation() << ") with mode " << mode << " and template " << templateName << endl;
|
Chris@45
|
1524
|
Chris@222
|
1525 if (templateName == "") {
|
Chris@231
|
1526 templateName = getDefaultSessionTemplate();
|
Chris@577
|
1527 SVDEBUG << "(Default template is: \"" << templateName << "\")" << endl;
|
Chris@222
|
1528 }
|
Chris@220
|
1529
|
Chris@374
|
1530 // cerr << "template is: \"" << templateName << "\"" << endl;
|
Chris@223
|
1531
|
Chris@413
|
1532 if (!source.isAvailable()) {
|
Chris@413
|
1533 if (source.wasCancelled()) {
|
Chris@413
|
1534 return FileOpenCancelled;
|
Chris@413
|
1535 } else {
|
Chris@413
|
1536 return FileOpenFailed;
|
Chris@413
|
1537 }
|
Chris@413
|
1538 }
|
Chris@413
|
1539
|
Chris@45
|
1540 source.waitForData();
|
Chris@45
|
1541
|
Chris@45
|
1542 m_openingAudioFile = true;
|
Chris@45
|
1543
|
Chris@435
|
1544 sv_samplerate_t rate = 0;
|
Chris@45
|
1545
|
Chris@626
|
1546 SVDEBUG << "Checking whether to preserve incoming audio file's sample rate"
|
Chris@626
|
1547 << endl;
|
Chris@626
|
1548
|
Chris@360
|
1549 if (Preferences::getInstance()->getFixedSampleRate() != 0) {
|
Chris@360
|
1550 rate = Preferences::getInstance()->getFixedSampleRate();
|
Chris@626
|
1551 SVDEBUG << "No: preferences specify fixed rate of " << rate << endl;
|
Chris@360
|
1552 } else if (Preferences::getInstance()->getResampleOnLoad()) {
|
Chris@552
|
1553 if (getMainModel()) {
|
Chris@626
|
1554 if (mode == ReplaceSession || mode == ReplaceMainModel) {
|
Chris@626
|
1555 SVDEBUG << "Preferences specify resampling additional models to match main model, but we are opening this file to replace the main model according to the open mode: therefore..." << endl;
|
Chris@626
|
1556 } else {
|
Chris@626
|
1557 rate = getMainModel()->getSampleRate();
|
Chris@626
|
1558 SVDEBUG << "No: preferences specify resampling to match main model, whose rate is currently " << rate << endl;
|
Chris@626
|
1559 }
|
Chris@552
|
1560 }
|
Chris@45
|
1561 }
|
Chris@45
|
1562
|
Chris@626
|
1563 if (rate == 0) {
|
Chris@626
|
1564 SVDEBUG << "Yes, preserving incoming file rate" << endl;
|
Chris@626
|
1565 }
|
Chris@626
|
1566
|
Chris@684
|
1567 auto newModel = std::make_shared<ReadOnlyWaveFileModel>(source, rate);
|
Chris@45
|
1568 if (!newModel->isOK()) {
|
Chris@45
|
1569 m_openingAudioFile = false;
|
Chris@413
|
1570 if (source.wasCancelled()) {
|
Chris@413
|
1571 return FileOpenCancelled;
|
Chris@413
|
1572 } else {
|
Chris@413
|
1573 return FileOpenFailed;
|
Chris@413
|
1574 }
|
Chris@45
|
1575 }
|
Chris@45
|
1576
|
Chris@687
|
1577 auto newModelId = ModelById::add(newModel);
|
Chris@687
|
1578 return addOpenedAudioModel(source, newModelId, mode, templateName, true);
|
Chris@604
|
1579 }
|
Chris@604
|
1580
|
Chris@604
|
1581 MainWindowBase::FileOpenStatus
|
Chris@604
|
1582 MainWindowBase::addOpenedAudioModel(FileSource source,
|
Chris@684
|
1583 ModelId newModel,
|
Chris@604
|
1584 AudioFileOpenMode mode,
|
Chris@604
|
1585 QString templateName,
|
Chris@604
|
1586 bool registerSource)
|
Chris@604
|
1587 {
|
Chris@45
|
1588 if (mode == AskUser) {
|
Chris@45
|
1589 if (getMainModel()) {
|
Chris@45
|
1590
|
Chris@147
|
1591 QSettings settings;
|
Chris@147
|
1592 settings.beginGroup("MainWindow");
|
Chris@221
|
1593 int lastMode = settings.value("lastaudioopenmode", 0).toBool();
|
Chris@147
|
1594 settings.endGroup();
|
Chris@221
|
1595 int imode = 0;
|
Chris@45
|
1596
|
Chris@45
|
1597 QStringList items;
|
Chris@221
|
1598 items << tr("Close the current session and start a new one")
|
Chris@221
|
1599 << tr("Replace the main audio file in this session")
|
Chris@221
|
1600 << tr("Add the audio file to this session");
|
Chris@45
|
1601
|
Chris@45
|
1602 bool ok = false;
|
Chris@45
|
1603 QString item = ListInputDialog::getItem
|
Chris@45
|
1604 (this, tr("Select target for import"),
|
Chris@221
|
1605 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
|
1606 items, lastMode, &ok);
|
Chris@45
|
1607
|
Chris@45
|
1608 if (!ok || item.isEmpty()) {
|
Chris@684
|
1609 ModelById::release(newModel);
|
Chris@45
|
1610 m_openingAudioFile = false;
|
Chris@45
|
1611 return FileOpenCancelled;
|
Chris@45
|
1612 }
|
Chris@45
|
1613
|
Chris@221
|
1614 for (int i = 0; i < items.size(); ++i) {
|
Chris@221
|
1615 if (item == items[i]) imode = i;
|
Chris@221
|
1616 }
|
Chris@221
|
1617
|
Chris@147
|
1618 settings.beginGroup("MainWindow");
|
Chris@221
|
1619 settings.setValue("lastaudioopenmode", imode);
|
Chris@147
|
1620 settings.endGroup();
|
Chris@45
|
1621
|
Chris@221
|
1622 mode = (AudioFileOpenMode)imode;
|
Chris@45
|
1623
|
Chris@45
|
1624 } else {
|
Chris@221
|
1625 // no main model: make a new session
|
Chris@221
|
1626 mode = ReplaceSession;
|
Chris@45
|
1627 }
|
Chris@45
|
1628 }
|
Chris@45
|
1629
|
Chris@45
|
1630 if (mode == ReplaceCurrentPane) {
|
Chris@45
|
1631
|
Chris@45
|
1632 Pane *pane = m_paneStack->getCurrentPane();
|
Chris@45
|
1633 if (pane) {
|
Chris@45
|
1634 if (getMainModel()) {
|
Chris@45
|
1635 View::ModelSet models(pane->getModels());
|
Chris@684
|
1636 if (models.find(getMainModelId()) != models.end()) {
|
Chris@221
|
1637 // Current pane contains main model: replace that
|
Chris@45
|
1638 mode = ReplaceMainModel;
|
Chris@45
|
1639 }
|
Chris@221
|
1640 // Otherwise the current pane has a non-default model,
|
Chris@221
|
1641 // which we will deal with later
|
Chris@45
|
1642 } else {
|
Chris@221
|
1643 // We have no main model, so start a new session with
|
Chris@221
|
1644 // optional template
|
Chris@221
|
1645 mode = ReplaceSession;
|
Chris@45
|
1646 }
|
Chris@45
|
1647 } else {
|
Chris@221
|
1648 // We seem to have no current pane! Oh well
|
Chris@45
|
1649 mode = CreateAdditionalModel;
|
Chris@45
|
1650 }
|
Chris@45
|
1651 }
|
Chris@45
|
1652
|
Chris@684
|
1653 if (mode == CreateAdditionalModel && getMainModelId().isNone()) {
|
Chris@386
|
1654 SVDEBUG << "Mode is CreateAdditionalModel but we have no main model, switching to ReplaceSession mode" << endl;
|
Chris@221
|
1655 mode = ReplaceSession;
|
Chris@221
|
1656 }
|
Chris@221
|
1657
|
Chris@221
|
1658 bool loadedTemplate = false;
|
Chris@221
|
1659
|
Chris@221
|
1660 if (mode == ReplaceSession) {
|
Chris@258
|
1661
|
Chris@258
|
1662 if (!checkSaveModified()) return FileOpenCancelled;
|
Chris@258
|
1663
|
Chris@386
|
1664 SVDEBUG << "SV looking for template " << templateName << endl;
|
Chris@230
|
1665 if (templateName != "") {
|
Chris@230
|
1666 FileOpenStatus tplStatus = openSessionTemplate(templateName);
|
Chris@258
|
1667 if (tplStatus == FileOpenCancelled) {
|
Chris@577
|
1668 SVDEBUG << "Template load cancelled" << endl;
|
Chris@258
|
1669 return FileOpenCancelled;
|
Chris@258
|
1670 }
|
Chris@230
|
1671 if (tplStatus != FileOpenFailed) {
|
Chris@577
|
1672 SVDEBUG << "Template load succeeded" << endl;
|
Chris@230
|
1673 loadedTemplate = true;
|
Chris@221
|
1674 }
|
Chris@221
|
1675 }
|
Chris@221
|
1676
|
Chris@221
|
1677 if (!loadedTemplate) {
|
Chris@386
|
1678 SVDEBUG << "No template found: closing session, creating new empty document" << endl;
|
Chris@221
|
1679 closeSession();
|
Chris@221
|
1680 createDocument();
|
Chris@221
|
1681 }
|
Chris@221
|
1682
|
Chris@386
|
1683 SVDEBUG << "Now switching to ReplaceMainModel mode" << endl;
|
Chris@45
|
1684 mode = ReplaceMainModel;
|
Chris@45
|
1685 }
|
Chris@45
|
1686
|
Chris@164
|
1687 emit activity(tr("Import audio file \"%1\"").arg(source.getLocation()));
|
Chris@669
|
1688
|
Chris@45
|
1689 if (mode == ReplaceMainModel) {
|
Chris@45
|
1690
|
Chris@684
|
1691 ModelId prevMain = getMainModelId();
|
Chris@684
|
1692 if (!prevMain.isNone()) {
|
Chris@45
|
1693 m_playSource->removeModel(prevMain);
|
Chris@45
|
1694 }
|
Chris@45
|
1695
|
Chris@248
|
1696 SVDEBUG << "SV about to call setMainModel(" << newModel << "): prevMain is " << prevMain << endl;
|
Chris@248
|
1697
|
Chris@595
|
1698 m_document->setMainModel(newModel);
|
Chris@595
|
1699
|
Chris@595
|
1700 setupMenus();
|
Chris@595
|
1701
|
Chris@669
|
1702 m_originalLocation = source.getLocation();
|
Chris@669
|
1703
|
Chris@595
|
1704 if (loadedTemplate || (m_sessionFile == "")) {
|
Chris@595
|
1705 CommandHistory::getInstance()->clear();
|
Chris@595
|
1706 CommandHistory::getInstance()->documentSaved();
|
Chris@595
|
1707 m_documentModified = false;
|
Chris@595
|
1708 } else {
|
Chris@595
|
1709 if (m_documentModified) {
|
Chris@595
|
1710 m_documentModified = false;
|
Chris@595
|
1711 }
|
Chris@595
|
1712 }
|
Chris@45
|
1713
|
Chris@604
|
1714 if (!source.isRemote() && registerSource) {
|
Chris@604
|
1715 m_audioFile = source.getLocalFilename();
|
Chris@604
|
1716 }
|
Chris@45
|
1717
|
Chris@669
|
1718 updateWindowTitle();
|
Chris@669
|
1719
|
Chris@45
|
1720 } else if (mode == CreateAdditionalModel) {
|
Chris@45
|
1721
|
Chris@577
|
1722 SVCERR << "Mode is CreateAdditionalModel" << endl;
|
Chris@577
|
1723
|
Chris@595
|
1724 CommandHistory::getInstance()->startCompoundOperation
|
Chris@595
|
1725 (tr("Import \"%1\"").arg(source.getBasename()), true);
|
Chris@595
|
1726
|
Chris@691
|
1727 m_document->addNonDerivedModel(newModel);
|
Chris@595
|
1728
|
Chris@595
|
1729 AddPaneCommand *command = new AddPaneCommand(this);
|
Chris@595
|
1730 CommandHistory::getInstance()->addCommand(command);
|
Chris@595
|
1731
|
Chris@595
|
1732 Pane *pane = command->getPane();
|
Chris@45
|
1733
|
Chris@47
|
1734 if (m_timeRulerLayer) {
|
Chris@577
|
1735 SVCERR << "Have time ruler, adding it" << endl;
|
Chris@47
|
1736 m_document->addLayerToView(pane, m_timeRulerLayer);
|
Chris@577
|
1737 } else {
|
Chris@577
|
1738 SVCERR << "Do not have time ruler" << endl;
|
Chris@47
|
1739 }
|
Chris@45
|
1740
|
Chris@595
|
1741 Layer *newLayer = m_document->createImportedLayer(newModel);
|
Chris@595
|
1742
|
Chris@595
|
1743 if (newLayer) {
|
Chris@595
|
1744 m_document->addLayerToView(pane, newLayer);
|
Chris@595
|
1745 }
|
Chris@595
|
1746
|
Chris@595
|
1747 CommandHistory::getInstance()->endCompoundOperation();
|
Chris@45
|
1748
|
Chris@45
|
1749 } else if (mode == ReplaceCurrentPane) {
|
Chris@45
|
1750
|
Chris@45
|
1751 // We know there is a current pane, otherwise we would have
|
Chris@45
|
1752 // reset the mode to CreateAdditionalModel above; and we know
|
Chris@45
|
1753 // the current pane does not contain the main model, otherwise
|
Chris@45
|
1754 // we would have reset it to ReplaceMainModel. But we don't
|
Chris@45
|
1755 // know whether the pane contains a waveform model at all.
|
Chris@45
|
1756
|
Chris@45
|
1757 Pane *pane = m_paneStack->getCurrentPane();
|
Chris@636
|
1758 Layer *replace = nullptr;
|
Chris@45
|
1759
|
Chris@45
|
1760 for (int i = 0; i < pane->getLayerCount(); ++i) {
|
Chris@45
|
1761 Layer *layer = pane->getLayer(i);
|
Chris@45
|
1762 if (dynamic_cast<WaveformLayer *>(layer)) {
|
Chris@45
|
1763 replace = layer;
|
Chris@45
|
1764 break;
|
Chris@45
|
1765 }
|
Chris@45
|
1766 }
|
Chris@45
|
1767
|
Chris@595
|
1768 CommandHistory::getInstance()->startCompoundOperation
|
Chris@595
|
1769 (tr("Import \"%1\"").arg(source.getBasename()), true);
|
Chris@595
|
1770
|
Chris@691
|
1771 m_document->addNonDerivedModel(newModel);
|
Chris@45
|
1772
|
Chris@45
|
1773 if (replace) {
|
Chris@45
|
1774 m_document->removeLayerFromView(pane, replace);
|
Chris@45
|
1775 }
|
Chris@45
|
1776
|
Chris@595
|
1777 Layer *newLayer = m_document->createImportedLayer(newModel);
|
Chris@595
|
1778
|
Chris@595
|
1779 if (newLayer) {
|
Chris@595
|
1780 m_document->addLayerToView(pane, newLayer);
|
Chris@595
|
1781 }
|
Chris@595
|
1782
|
Chris@595
|
1783 CommandHistory::getInstance()->endCompoundOperation();
|
Chris@45
|
1784 }
|
Chris@45
|
1785
|
Chris@45
|
1786 updateMenuStates();
|
Chris@622
|
1787
|
Chris@622
|
1788 if (registerSource) {
|
Chris@622
|
1789 m_recentFiles.addFile(source.getLocation());
|
Chris@622
|
1790 }
|
Chris@604
|
1791 if (!source.isRemote() && registerSource) {
|
Chris@45
|
1792 // for file dialog
|
Chris@45
|
1793 registerLastOpenedFilePath(FileFinder::AudioFile,
|
Chris@45
|
1794 source.getLocalFilename());
|
Chris@45
|
1795 }
|
Chris@622
|
1796
|
Chris@45
|
1797 m_openingAudioFile = false;
|
Chris@45
|
1798
|
Chris@45
|
1799 currentPaneChanged(m_paneStack->getCurrentPane());
|
Chris@45
|
1800
|
Chris@342
|
1801 emit audioFileLoaded();
|
Chris@342
|
1802
|
Chris@45
|
1803 return FileOpenSucceeded;
|
Chris@45
|
1804 }
|
Chris@45
|
1805
|
Chris@45
|
1806 MainWindowBase::FileOpenStatus
|
Chris@45
|
1807 MainWindowBase::openPlaylist(FileSource source, AudioFileOpenMode mode)
|
Chris@45
|
1808 {
|
Chris@233
|
1809 SVDEBUG << "MainWindowBase::openPlaylist(" << source.getLocation() << ")" << endl;
|
Chris@135
|
1810
|
Chris@45
|
1811 std::set<QString> extensions;
|
Chris@45
|
1812 PlaylistFileReader::getSupportedExtensions(extensions);
|
Chris@152
|
1813 QString extension = source.getExtension().toLower();
|
Chris@45
|
1814 if (extensions.find(extension) == extensions.end()) return FileOpenFailed;
|
Chris@45
|
1815
|
Chris@45
|
1816 if (!source.isAvailable()) return FileOpenFailed;
|
Chris@45
|
1817 source.waitForData();
|
Chris@45
|
1818
|
Chris@45
|
1819 PlaylistFileReader reader(source.getLocalFilename());
|
Chris@45
|
1820 if (!reader.isOK()) return FileOpenFailed;
|
Chris@45
|
1821
|
Chris@45
|
1822 PlaylistFileReader::Playlist playlist = reader.load();
|
Chris@45
|
1823
|
Chris@45
|
1824 bool someSuccess = false;
|
Chris@45
|
1825
|
Chris@45
|
1826 for (PlaylistFileReader::Playlist::const_iterator i = playlist.begin();
|
Chris@45
|
1827 i != playlist.end(); ++i) {
|
Chris@45
|
1828
|
Chris@134
|
1829 ProgressDialog dialog(tr("Opening playlist..."), true, 2000, this);
|
Chris@134
|
1830 connect(&dialog, SIGNAL(showing()), this, SIGNAL(hideSplash()));
|
Chris@109
|
1831 FileOpenStatus status = openAudio(FileSource(*i, &dialog), mode);
|
Chris@45
|
1832
|
Chris@45
|
1833 if (status == FileOpenCancelled) {
|
Chris@45
|
1834 return FileOpenCancelled;
|
Chris@45
|
1835 }
|
Chris@45
|
1836
|
Chris@45
|
1837 if (status == FileOpenSucceeded) {
|
Chris@45
|
1838 someSuccess = true;
|
Chris@45
|
1839 mode = CreateAdditionalModel;
|
Chris@45
|
1840 }
|
Chris@45
|
1841 }
|
Chris@45
|
1842
|
Chris@45
|
1843 if (someSuccess) return FileOpenSucceeded;
|
Chris@45
|
1844 else return FileOpenFailed;
|
Chris@45
|
1845 }
|
Chris@45
|
1846
|
Chris@45
|
1847 MainWindowBase::FileOpenStatus
|
Chris@45
|
1848 MainWindowBase::openLayer(FileSource source)
|
Chris@45
|
1849 {
|
Chris@233
|
1850 SVDEBUG << "MainWindowBase::openLayer(" << source.getLocation() << ")" << endl;
|
Chris@135
|
1851
|
Chris@45
|
1852 Pane *pane = m_paneStack->getCurrentPane();
|
Chris@45
|
1853
|
Chris@45
|
1854 if (!pane) {
|
Chris@595
|
1855 // shouldn't happen, as the menu action should have been disabled
|
Chris@595
|
1856 cerr << "WARNING: MainWindowBase::openLayer: no current pane" << endl;
|
Chris@595
|
1857 return FileOpenWrongMode;
|
Chris@45
|
1858 }
|
Chris@45
|
1859
|
Chris@45
|
1860 if (!getMainModel()) {
|
Chris@595
|
1861 // shouldn't happen, as the menu action should have been disabled
|
Chris@595
|
1862 cerr << "WARNING: MainWindowBase::openLayer: No main model -- hence no default sample rate available" << endl;
|
Chris@595
|
1863 return FileOpenWrongMode;
|
Chris@45
|
1864 }
|
Chris@45
|
1865
|
Chris@45
|
1866 if (!source.isAvailable()) return FileOpenFailed;
|
Chris@45
|
1867 source.waitForData();
|
Chris@45
|
1868
|
Chris@45
|
1869 QString path = source.getLocalFilename();
|
Chris@45
|
1870
|
Chris@145
|
1871 RDFImporter::RDFDocumentType rdfType =
|
Chris@145
|
1872 RDFImporter::identifyDocumentType(QUrl::fromLocalFile(path).toString());
|
Chris@145
|
1873
|
Chris@293
|
1874 // cerr << "RDF type: (in layer) " << (int) rdfType << endl;
|
Chris@148
|
1875
|
Chris@145
|
1876 if (rdfType != RDFImporter::NotRDF) {
|
Chris@145
|
1877
|
Chris@145
|
1878 return openLayersFromRDF(source);
|
Chris@134
|
1879
|
Chris@152
|
1880 } else if (source.getExtension().toLower() == "svl" ||
|
Chris@152
|
1881 (source.getExtension().toLower() == "xml" &&
|
Chris@140
|
1882 (SVFileReader::identifyXmlFile(source.getLocalFilename())
|
Chris@140
|
1883 == SVFileReader::SVLayerFile))) {
|
Chris@45
|
1884
|
Chris@45
|
1885 PaneCallback callback(this);
|
Chris@45
|
1886 QFile file(path);
|
Chris@45
|
1887
|
Chris@45
|
1888 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) {
|
Chris@293
|
1889 cerr << "ERROR: MainWindowBase::openLayer("
|
Chris@294
|
1890 << source.getLocation()
|
Chris@293
|
1891 << "): Failed to open file for reading" << endl;
|
Chris@45
|
1892 return FileOpenFailed;
|
Chris@45
|
1893 }
|
Chris@45
|
1894
|
Chris@45
|
1895 SVFileReader reader(m_document, callback, source.getLocation());
|
Chris@79
|
1896 connect
|
Chris@79
|
1897 (&reader, SIGNAL(modelRegenerationFailed(QString, QString, QString)),
|
Chris@79
|
1898 this, SLOT(modelRegenerationFailed(QString, QString, QString)));
|
Chris@79
|
1899 connect
|
Chris@79
|
1900 (&reader, SIGNAL(modelRegenerationWarning(QString, QString, QString)),
|
Chris@79
|
1901 this, SLOT(modelRegenerationWarning(QString, QString, QString)));
|
Chris@45
|
1902 reader.setCurrentPane(pane);
|
Chris@45
|
1903
|
Chris@45
|
1904 QXmlInputSource inputSource(&file);
|
Chris@45
|
1905 reader.parse(inputSource);
|
Chris@45
|
1906
|
Chris@45
|
1907 if (!reader.isOK()) {
|
Chris@293
|
1908 cerr << "ERROR: MainWindowBase::openLayer("
|
Chris@294
|
1909 << source.getLocation()
|
Chris@45
|
1910 << "): Failed to read XML file: "
|
Chris@293
|
1911 << reader.getErrorString() << endl;
|
Chris@45
|
1912 return FileOpenFailed;
|
Chris@45
|
1913 }
|
Chris@45
|
1914
|
Chris@164
|
1915 emit activity(tr("Import layer XML file \"%1\"").arg(source.getLocation()));
|
Chris@164
|
1916
|
Chris@45
|
1917 m_recentFiles.addFile(source.getLocation());
|
Chris@45
|
1918
|
Chris@45
|
1919 if (!source.isRemote()) {
|
Chris@45
|
1920 registerLastOpenedFilePath(FileFinder::LayerFile, path); // for file dialog
|
Chris@45
|
1921 }
|
Chris@45
|
1922
|
Chris@75
|
1923 return FileOpenSucceeded;
|
Chris@75
|
1924
|
Chris@45
|
1925 } else {
|
Chris@45
|
1926
|
Chris@45
|
1927 try {
|
Chris@45
|
1928
|
Chris@109
|
1929 MIDIFileImportDialog midiDlg(this);
|
Chris@109
|
1930
|
Chris@684
|
1931 Model *newModelPtr = DataFileReaderFactory::loadNonCSV
|
Chris@109
|
1932 (path, &midiDlg, getMainModel()->getSampleRate());
|
Chris@45
|
1933
|
Chris@684
|
1934 if (!newModelPtr) {
|
Chris@643
|
1935 CSVFormatDialog *dialog =
|
Chris@643
|
1936 new CSVFormatDialog(this,
|
Chris@643
|
1937 path,
|
Chris@643
|
1938 getMainModel()->getSampleRate(),
|
Chris@643
|
1939 5);
|
Chris@109
|
1940 if (dialog->exec() == QDialog::Accepted) {
|
Chris@684
|
1941 newModelPtr = DataFileReaderFactory::loadCSV
|
Chris@109
|
1942 (path, dialog->getFormat(),
|
Chris@109
|
1943 getMainModel()->getSampleRate());
|
Chris@109
|
1944 }
|
Chris@619
|
1945 delete dialog;
|
Chris@109
|
1946 }
|
Chris@109
|
1947
|
Chris@684
|
1948 if (newModelPtr) {
|
Chris@45
|
1949
|
Chris@233
|
1950 SVDEBUG << "MainWindowBase::openLayer: Have model" << endl;
|
Chris@45
|
1951
|
Chris@164
|
1952 emit activity(tr("Import MIDI file \"%1\"").arg(source.getLocation()));
|
Chris@164
|
1953
|
Chris@687
|
1954 ModelId modelId =
|
Chris@687
|
1955 ModelById::add(std::shared_ptr<Model>(newModelPtr));
|
Chris@684
|
1956
|
Chris@684
|
1957 Layer *newLayer = m_document->createImportedLayer(modelId);
|
Chris@45
|
1958
|
Chris@45
|
1959 if (newLayer) {
|
Chris@45
|
1960
|
Chris@45
|
1961 m_document->addLayerToView(pane, newLayer);
|
Chris@88
|
1962 m_paneStack->setCurrentLayer(pane, newLayer);
|
Chris@88
|
1963
|
Chris@45
|
1964 m_recentFiles.addFile(source.getLocation());
|
Chris@45
|
1965
|
Chris@45
|
1966 if (!source.isRemote()) {
|
Chris@45
|
1967 registerLastOpenedFilePath
|
Chris@45
|
1968 (FileFinder::LayerFile,
|
Chris@45
|
1969 path); // for file dialog
|
Chris@45
|
1970 }
|
Chris@88
|
1971
|
Chris@45
|
1972 return FileOpenSucceeded;
|
Chris@45
|
1973 }
|
Chris@45
|
1974 }
|
Chris@45
|
1975 } catch (DataFileReaderFactory::Exception e) {
|
Chris@45
|
1976 if (e == DataFileReaderFactory::ImportCancelled) {
|
Chris@45
|
1977 return FileOpenCancelled;
|
Chris@45
|
1978 }
|
Chris@45
|
1979 }
|
Chris@45
|
1980 }
|
Chris@45
|
1981
|
Chris@45
|
1982 return FileOpenFailed;
|
Chris@45
|
1983 }
|
Chris@45
|
1984
|
Chris@45
|
1985 MainWindowBase::FileOpenStatus
|
Chris@45
|
1986 MainWindowBase::openImage(FileSource source)
|
Chris@45
|
1987 {
|
Chris@233
|
1988 SVDEBUG << "MainWindowBase::openImage(" << source.getLocation() << ")" << endl;
|
Chris@135
|
1989
|
Chris@45
|
1990 Pane *pane = m_paneStack->getCurrentPane();
|
Chris@45
|
1991
|
Chris@45
|
1992 if (!pane) {
|
Chris@595
|
1993 // shouldn't happen, as the menu action should have been disabled
|
Chris@595
|
1994 cerr << "WARNING: MainWindowBase::openImage: no current pane" << endl;
|
Chris@595
|
1995 return FileOpenWrongMode;
|
Chris@45
|
1996 }
|
Chris@45
|
1997
|
Chris@684
|
1998 if (!getMainModel()) {
|
Chris@45
|
1999 return FileOpenWrongMode;
|
Chris@45
|
2000 }
|
Chris@45
|
2001
|
Chris@45
|
2002 bool newLayer = false;
|
Chris@45
|
2003
|
Chris@45
|
2004 ImageLayer *il = dynamic_cast<ImageLayer *>(pane->getSelectedLayer());
|
Chris@45
|
2005 if (!il) {
|
Chris@45
|
2006 for (int i = pane->getLayerCount()-1; i >= 0; --i) {
|
Chris@45
|
2007 il = dynamic_cast<ImageLayer *>(pane->getLayer(i));
|
Chris@45
|
2008 if (il) break;
|
Chris@45
|
2009 }
|
Chris@45
|
2010 }
|
Chris@45
|
2011 if (!il) {
|
Chris@45
|
2012 il = dynamic_cast<ImageLayer *>
|
Chris@45
|
2013 (m_document->createEmptyLayer(LayerFactory::Image));
|
Chris@45
|
2014 if (!il) return FileOpenFailed;
|
Chris@45
|
2015 newLayer = true;
|
Chris@45
|
2016 }
|
Chris@45
|
2017
|
Chris@45
|
2018 // We don't put the image file in Recent Files
|
Chris@45
|
2019
|
Chris@293
|
2020 cerr << "openImage: trying location \"" << source.getLocation() << "\" in image layer" << endl;
|
Chris@45
|
2021
|
Chris@45
|
2022 if (!il->addImage(m_viewManager->getGlobalCentreFrame(), source.getLocation())) {
|
Chris@45
|
2023 if (newLayer) {
|
Chris@52
|
2024 m_document->deleteLayer(il); // also releases its model
|
Chris@45
|
2025 }
|
Chris@45
|
2026 return FileOpenFailed;
|
Chris@45
|
2027 } else {
|
Chris@45
|
2028 if (newLayer) {
|
Chris@45
|
2029 m_document->addLayerToView(pane, il);
|
Chris@45
|
2030 }
|
Chris@45
|
2031 m_paneStack->setCurrentLayer(pane, il);
|
Chris@45
|
2032 }
|
Chris@45
|
2033
|
Chris@45
|
2034 return FileOpenSucceeded;
|
Chris@45
|
2035 }
|
Chris@45
|
2036
|
Chris@45
|
2037 MainWindowBase::FileOpenStatus
|
Chris@427
|
2038 MainWindowBase::openDirOfAudio(QString dirPath)
|
Chris@427
|
2039 {
|
Chris@427
|
2040 QDir dir(dirPath);
|
Chris@427
|
2041 QStringList files = dir.entryList(QDir::Files | QDir::Readable);
|
Chris@427
|
2042 files.sort();
|
Chris@427
|
2043
|
Chris@427
|
2044 FileOpenStatus status = FileOpenFailed;
|
Chris@427
|
2045 bool first = true;
|
Chris@427
|
2046 bool cancelled = false;
|
Chris@427
|
2047
|
Chris@427
|
2048 foreach (QString file, files) {
|
Chris@427
|
2049
|
Chris@427
|
2050 FileSource source(dir.filePath(file));
|
Chris@427
|
2051 if (!source.isAvailable()) {
|
Chris@427
|
2052 continue;
|
Chris@427
|
2053 }
|
Chris@427
|
2054
|
Chris@427
|
2055 if (AudioFileReaderFactory::getKnownExtensions().contains
|
Chris@427
|
2056 (source.getExtension().toLower())) {
|
Chris@427
|
2057
|
Chris@427
|
2058 AudioFileOpenMode mode = CreateAdditionalModel;
|
Chris@427
|
2059 if (first) mode = ReplaceSession;
|
Chris@427
|
2060
|
Chris@427
|
2061 switch (openAudio(source, mode)) {
|
Chris@427
|
2062 case FileOpenSucceeded:
|
Chris@427
|
2063 status = FileOpenSucceeded;
|
Chris@427
|
2064 first = false;
|
Chris@427
|
2065 break;
|
Chris@427
|
2066 case FileOpenFailed:
|
Chris@427
|
2067 break;
|
Chris@427
|
2068 case FileOpenCancelled:
|
Chris@427
|
2069 cancelled = true;
|
Chris@427
|
2070 break;
|
Chris@427
|
2071 case FileOpenWrongMode:
|
Chris@427
|
2072 break;
|
Chris@427
|
2073 }
|
Chris@427
|
2074 }
|
Chris@427
|
2075
|
Chris@427
|
2076 if (cancelled) break;
|
Chris@427
|
2077 }
|
Chris@427
|
2078
|
Chris@427
|
2079 return status;
|
Chris@427
|
2080 }
|
Chris@427
|
2081
|
Chris@427
|
2082 MainWindowBase::FileOpenStatus
|
Chris@373
|
2083 MainWindowBase::openSessionPath(QString fileOrUrl)
|
Chris@45
|
2084 {
|
Chris@134
|
2085 ProgressDialog dialog(tr("Opening session..."), true, 2000, this);
|
Chris@134
|
2086 connect(&dialog, SIGNAL(showing()), this, SIGNAL(hideSplash()));
|
Chris@109
|
2087 return openSession(FileSource(fileOrUrl, &dialog));
|
Chris@45
|
2088 }
|
Chris@45
|
2089
|
Chris@45
|
2090 MainWindowBase::FileOpenStatus
|
Chris@45
|
2091 MainWindowBase::openSession(FileSource source)
|
Chris@45
|
2092 {
|
Chris@233
|
2093 SVDEBUG << "MainWindowBase::openSession(" << source.getLocation() << ")" << endl;
|
Chris@135
|
2094
|
Chris@45
|
2095 if (!source.isAvailable()) return FileOpenFailed;
|
Chris@145
|
2096 source.waitForData();
|
Chris@141
|
2097
|
Chris@341
|
2098 QString sessionExt =
|
Chris@341
|
2099 InteractiveFileFinder::getInstance()->getApplicationSessionExtension();
|
Chris@341
|
2100
|
Chris@341
|
2101 if (source.getExtension().toLower() != sessionExt) {
|
Chris@145
|
2102
|
Chris@145
|
2103 RDFImporter::RDFDocumentType rdfType =
|
Chris@145
|
2104 RDFImporter::identifyDocumentType
|
Chris@145
|
2105 (QUrl::fromLocalFile(source.getLocalFilename()).toString());
|
Chris@145
|
2106
|
Chris@293
|
2107 // cerr << "RDF type: " << (int)rdfType << endl;
|
Chris@148
|
2108
|
Chris@145
|
2109 if (rdfType == RDFImporter::AudioRefAndAnnotations ||
|
Chris@145
|
2110 rdfType == RDFImporter::AudioRef) {
|
Chris@145
|
2111 return openSessionFromRDF(source);
|
Chris@145
|
2112 } else if (rdfType != RDFImporter::NotRDF) {
|
Chris@145
|
2113 return FileOpenFailed;
|
Chris@145
|
2114 }
|
Chris@145
|
2115
|
Chris@152
|
2116 if (source.getExtension().toLower() == "xml") {
|
Chris@140
|
2117 if (SVFileReader::identifyXmlFile(source.getLocalFilename()) ==
|
Chris@140
|
2118 SVFileReader::SVSessionFile) {
|
Chris@293
|
2119 cerr << "This XML file looks like a session file, attempting to open it as a session" << endl;
|
Chris@140
|
2120 } else {
|
Chris@140
|
2121 return FileOpenFailed;
|
Chris@140
|
2122 }
|
Chris@140
|
2123 } else {
|
Chris@140
|
2124 return FileOpenFailed;
|
Chris@140
|
2125 }
|
Chris@140
|
2126 }
|
Chris@45
|
2127
|
Chris@636
|
2128 QXmlInputSource *inputSource = nullptr;
|
Chris@636
|
2129 BZipFileDevice *bzFile = nullptr;
|
Chris@636
|
2130 QFile *rawFile = nullptr;
|
Chris@140
|
2131
|
Chris@341
|
2132 if (source.getExtension().toLower() == sessionExt) {
|
Chris@140
|
2133 bzFile = new BZipFileDevice(source.getLocalFilename());
|
Chris@140
|
2134 if (!bzFile->open(QIODevice::ReadOnly)) {
|
Chris@140
|
2135 delete bzFile;
|
Chris@140
|
2136 return FileOpenFailed;
|
Chris@140
|
2137 }
|
Chris@140
|
2138 inputSource = new QXmlInputSource(bzFile);
|
Chris@140
|
2139 } else {
|
Chris@140
|
2140 rawFile = new QFile(source.getLocalFilename());
|
Chris@140
|
2141 inputSource = new QXmlInputSource(rawFile);
|
Chris@140
|
2142 }
|
Chris@140
|
2143
|
Chris@140
|
2144 if (!checkSaveModified()) {
|
Chris@140
|
2145 if (bzFile) bzFile->close();
|
Chris@140
|
2146 delete inputSource;
|
Chris@140
|
2147 delete bzFile;
|
Chris@140
|
2148 delete rawFile;
|
Chris@140
|
2149 return FileOpenCancelled;
|
Chris@140
|
2150 }
|
Chris@45
|
2151
|
Chris@45
|
2152 QString error;
|
Chris@45
|
2153 closeSession();
|
Chris@45
|
2154 createDocument();
|
Chris@45
|
2155
|
Chris@45
|
2156 PaneCallback callback(this);
|
Chris@45
|
2157 m_viewManager->clearSelections();
|
Chris@45
|
2158
|
Chris@45
|
2159 SVFileReader reader(m_document, callback, source.getLocation());
|
Chris@79
|
2160 connect
|
Chris@79
|
2161 (&reader, SIGNAL(modelRegenerationFailed(QString, QString, QString)),
|
Chris@79
|
2162 this, SLOT(modelRegenerationFailed(QString, QString, QString)));
|
Chris@79
|
2163 connect
|
Chris@79
|
2164 (&reader, SIGNAL(modelRegenerationWarning(QString, QString, QString)),
|
Chris@79
|
2165 this, SLOT(modelRegenerationWarning(QString, QString, QString)));
|
Chris@140
|
2166
|
Chris@140
|
2167 reader.parse(*inputSource);
|
Chris@45
|
2168
|
Chris@45
|
2169 if (!reader.isOK()) {
|
Chris@45
|
2170 error = tr("SV XML file read error:\n%1").arg(reader.getErrorString());
|
Chris@45
|
2171 }
|
Chris@45
|
2172
|
Chris@140
|
2173 if (bzFile) bzFile->close();
|
Chris@140
|
2174
|
Chris@140
|
2175 delete inputSource;
|
Chris@140
|
2176 delete bzFile;
|
Chris@140
|
2177 delete rawFile;
|
Chris@45
|
2178
|
Chris@45
|
2179 bool ok = (error == "");
|
Chris@45
|
2180
|
Chris@45
|
2181 if (ok) {
|
Chris@45
|
2182
|
Chris@164
|
2183 emit activity(tr("Import session file \"%1\"").arg(source.getLocation()));
|
Chris@164
|
2184
|
Chris@601
|
2185 if (!source.isRemote() && !m_document->isIncomplete()) {
|
Chris@601
|
2186 // Setting the session file path enables the Save (as
|
Chris@601
|
2187 // opposed to Save As...) option. We can't do this if we
|
Chris@601
|
2188 // don't have a local path to save to, but we also don't
|
Chris@601
|
2189 // want to do it if we failed to find an audio file or
|
Chris@601
|
2190 // similar on load, as the audio reference would then end
|
Chris@601
|
2191 // up being lost from any saved or auto-saved-on-exit copy
|
Chris@601
|
2192 m_sessionFile = source.getLocalFilename();
|
Chris@602
|
2193 } else {
|
Chris@602
|
2194 QMessageBox::warning
|
Chris@602
|
2195 (this,
|
Chris@602
|
2196 tr("Incomplete session loaded"),
|
Chris@603
|
2197 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
|
2198 QMessageBox::Ok);
|
Chris@601
|
2199 }
|
Chris@595
|
2200
|
Chris@669
|
2201 updateWindowTitle();
|
Chris@595
|
2202 setupMenus();
|
Chris@577
|
2203 findTimeRulerLayer();
|
Chris@45
|
2204
|
Chris@595
|
2205 CommandHistory::getInstance()->clear();
|
Chris@595
|
2206 CommandHistory::getInstance()->documentSaved();
|
Chris@595
|
2207 m_documentModified = false;
|
Chris@595
|
2208 updateMenuStates();
|
Chris@45
|
2209
|
Chris@227
|
2210 m_recentFiles.addFile(source.getLocation());
|
Chris@45
|
2211
|
Chris@45
|
2212 if (!source.isRemote()) {
|
Chris@45
|
2213 // for file dialog
|
Chris@45
|
2214 registerLastOpenedFilePath(FileFinder::SessionFile,
|
Chris@227
|
2215 source.getLocalFilename());
|
Chris@45
|
2216 }
|
Chris@45
|
2217
|
Chris@669
|
2218 m_originalLocation = source.getLocation();
|
Chris@669
|
2219
|
Chris@342
|
2220 emit sessionLoaded();
|
Chris@342
|
2221
|
Chris@669
|
2222 updateWindowTitle();
|
Chris@45
|
2223 }
|
Chris@669
|
2224
|
Chris@45
|
2225 return ok ? FileOpenSucceeded : FileOpenFailed;
|
Chris@45
|
2226 }
|
Chris@45
|
2227
|
Chris@141
|
2228 MainWindowBase::FileOpenStatus
|
Chris@230
|
2229 MainWindowBase::openSessionTemplate(QString templateName)
|
Chris@230
|
2230 {
|
Chris@230
|
2231 // Template in the user's template directory takes
|
Chris@230
|
2232 // priority over a bundled one; we don't unbundle, but
|
Chris@230
|
2233 // open directly from the bundled file (where applicable)
|
Chris@230
|
2234 ResourceFinder rf;
|
Chris@230
|
2235 QString tfile = rf.getResourcePath("templates", templateName + ".svt");
|
Chris@230
|
2236 if (tfile != "") {
|
Chris@294
|
2237 cerr << "SV loading template file " << tfile << endl;
|
Chris@230
|
2238 return openSessionTemplate(FileSource("file:" + tfile));
|
Chris@230
|
2239 } else {
|
Chris@230
|
2240 return FileOpenFailed;
|
Chris@230
|
2241 }
|
Chris@230
|
2242 }
|
Chris@230
|
2243
|
Chris@230
|
2244 MainWindowBase::FileOpenStatus
|
Chris@227
|
2245 MainWindowBase::openSessionTemplate(FileSource source)
|
Chris@227
|
2246 {
|
Chris@294
|
2247 cerr << "MainWindowBase::openSessionTemplate(" << source.getLocation() << ")" << endl;
|
Chris@227
|
2248
|
Chris@227
|
2249 if (!source.isAvailable()) return FileOpenFailed;
|
Chris@227
|
2250 source.waitForData();
|
Chris@227
|
2251
|
Chris@636
|
2252 QXmlInputSource *inputSource = nullptr;
|
Chris@636
|
2253 QFile *file = nullptr;
|
Chris@227
|
2254
|
Chris@227
|
2255 file = new QFile(source.getLocalFilename());
|
Chris@227
|
2256 inputSource = new QXmlInputSource(file);
|
Chris@227
|
2257
|
Chris@227
|
2258 if (!checkSaveModified()) {
|
Chris@227
|
2259 delete inputSource;
|
Chris@227
|
2260 delete file;
|
Chris@227
|
2261 return FileOpenCancelled;
|
Chris@227
|
2262 }
|
Chris@227
|
2263
|
Chris@227
|
2264 QString error;
|
Chris@227
|
2265 closeSession();
|
Chris@227
|
2266 createDocument();
|
Chris@227
|
2267
|
Chris@227
|
2268 PaneCallback callback(this);
|
Chris@227
|
2269 m_viewManager->clearSelections();
|
Chris@227
|
2270
|
Chris@227
|
2271 SVFileReader reader(m_document, callback, source.getLocation());
|
Chris@227
|
2272 connect
|
Chris@227
|
2273 (&reader, SIGNAL(modelRegenerationFailed(QString, QString, QString)),
|
Chris@227
|
2274 this, SLOT(modelRegenerationFailed(QString, QString, QString)));
|
Chris@227
|
2275 connect
|
Chris@227
|
2276 (&reader, SIGNAL(modelRegenerationWarning(QString, QString, QString)),
|
Chris@227
|
2277 this, SLOT(modelRegenerationWarning(QString, QString, QString)));
|
Chris@227
|
2278
|
Chris@227
|
2279 reader.parse(*inputSource);
|
Chris@227
|
2280
|
Chris@227
|
2281 if (!reader.isOK()) {
|
Chris@227
|
2282 error = tr("SV XML file read error:\n%1").arg(reader.getErrorString());
|
Chris@227
|
2283 }
|
Chris@227
|
2284
|
Chris@227
|
2285 delete inputSource;
|
Chris@227
|
2286 delete file;
|
Chris@227
|
2287
|
Chris@227
|
2288 bool ok = (error == "");
|
Chris@227
|
2289
|
Chris@227
|
2290 if (ok) {
|
Chris@227
|
2291
|
Chris@227
|
2292 emit activity(tr("Open session template \"%1\"").arg(source.getLocation()));
|
Chris@227
|
2293
|
Chris@595
|
2294 setupMenus();
|
Chris@577
|
2295 findTimeRulerLayer();
|
Chris@227
|
2296
|
Chris@595
|
2297 CommandHistory::getInstance()->clear();
|
Chris@595
|
2298 CommandHistory::getInstance()->documentSaved();
|
Chris@595
|
2299 m_documentModified = false;
|
Chris@595
|
2300 updateMenuStates();
|
Chris@342
|
2301
|
Chris@342
|
2302 emit sessionLoaded();
|
Chris@227
|
2303 }
|
Chris@227
|
2304
|
Chris@669
|
2305 updateWindowTitle();
|
Chris@669
|
2306
|
Chris@227
|
2307 return ok ? FileOpenSucceeded : FileOpenFailed;
|
Chris@227
|
2308 }
|
Chris@227
|
2309
|
Chris@227
|
2310 MainWindowBase::FileOpenStatus
|
Chris@141
|
2311 MainWindowBase::openSessionFromRDF(FileSource source)
|
Chris@141
|
2312 {
|
Chris@233
|
2313 SVDEBUG << "MainWindowBase::openSessionFromRDF(" << source.getLocation() << ")" << endl;
|
Chris@141
|
2314
|
Chris@141
|
2315 if (!source.isAvailable()) return FileOpenFailed;
|
Chris@141
|
2316 source.waitForData();
|
Chris@141
|
2317
|
Chris@145
|
2318 if (!checkSaveModified()) {
|
Chris@145
|
2319 return FileOpenCancelled;
|
Chris@141
|
2320 }
|
Chris@143
|
2321
|
Chris@145
|
2322 closeSession();
|
Chris@145
|
2323 createDocument();
|
Chris@145
|
2324
|
Chris@145
|
2325 FileOpenStatus status = openLayersFromRDF(source);
|
Chris@141
|
2326
|
Chris@141
|
2327 setupMenus();
|
Chris@577
|
2328 findTimeRulerLayer();
|
Chris@669
|
2329
|
Chris@141
|
2330 CommandHistory::getInstance()->clear();
|
Chris@141
|
2331 CommandHistory::getInstance()->documentSaved();
|
Chris@141
|
2332 m_documentModified = false;
|
Chris@669
|
2333 updateWindowTitle();
|
Chris@145
|
2334
|
Chris@342
|
2335 emit sessionLoaded();
|
Chris@342
|
2336
|
Chris@145
|
2337 return status;
|
Chris@145
|
2338 }
|
Chris@145
|
2339
|
Chris@145
|
2340 MainWindowBase::FileOpenStatus
|
Chris@145
|
2341 MainWindowBase::openLayersFromRDF(FileSource source)
|
Chris@145
|
2342 {
|
Chris@435
|
2343 sv_samplerate_t rate = 0;
|
Chris@145
|
2344
|
Chris@233
|
2345 SVDEBUG << "MainWindowBase::openLayersFromRDF" << endl;
|
Chris@186
|
2346
|
Chris@145
|
2347 ProgressDialog dialog(tr("Importing from RDF..."), true, 2000, this);
|
Chris@145
|
2348 connect(&dialog, SIGNAL(showing()), this, SIGNAL(hideSplash()));
|
Chris@145
|
2349
|
Chris@145
|
2350 if (getMainModel()) {
|
Chris@145
|
2351 rate = getMainModel()->getSampleRate();
|
Chris@145
|
2352 } else if (Preferences::getInstance()->getResampleOnLoad()) {
|
Chris@552
|
2353 if (getMainModel()) {
|
Chris@552
|
2354 rate = getMainModel()->getSampleRate();
|
Chris@552
|
2355 }
|
Chris@145
|
2356 }
|
Chris@145
|
2357
|
Chris@145
|
2358 RDFImporter importer
|
Chris@145
|
2359 (QUrl::fromLocalFile(source.getLocalFilename()).toString(), rate);
|
Chris@145
|
2360
|
Chris@145
|
2361 if (!importer.isOK()) {
|
Chris@147
|
2362 if (importer.getErrorString() != "") {
|
Chris@147
|
2363 QMessageBox::critical
|
Chris@147
|
2364 (this, tr("Failed to import RDF"),
|
Chris@147
|
2365 tr("<b>Failed to import RDF</b><p>Importing data from RDF document at \"%1\" failed: %2</p>")
|
Chris@147
|
2366 .arg(source.getLocation()).arg(importer.getErrorString()));
|
Chris@147
|
2367 }
|
Chris@145
|
2368 return FileOpenFailed;
|
Chris@145
|
2369 }
|
Chris@145
|
2370
|
Chris@687
|
2371 std::vector<ModelId> modelIds = importer.getDataModels(&dialog);
|
Chris@145
|
2372
|
Chris@145
|
2373 dialog.setMessage(tr("Importing from RDF..."));
|
Chris@145
|
2374
|
Chris@687
|
2375 if (modelIds.empty()) {
|
Chris@186
|
2376 QMessageBox::critical
|
Chris@186
|
2377 (this, tr("Failed to import RDF"),
|
Chris@186
|
2378 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
|
2379 return FileOpenFailed;
|
Chris@145
|
2380 }
|
Chris@145
|
2381
|
Chris@164
|
2382 emit activity(tr("Import RDF document \"%1\"").arg(source.getLocation()));
|
Chris@684
|
2383
|
Chris@684
|
2384 std::set<ModelId> added;
|
Chris@684
|
2385
|
Chris@684
|
2386 for (auto modelId: modelIds) {
|
Chris@684
|
2387
|
Chris@684
|
2388 if (ModelById::isa<WaveFileModel>(modelId)) {
|
Chris@145
|
2389
|
Chris@145
|
2390 Pane *pane = addPaneToStack();
|
Chris@636
|
2391 Layer *layer = nullptr;
|
Chris@145
|
2392
|
Chris@145
|
2393 if (m_timeRulerLayer) {
|
Chris@145
|
2394 m_document->addLayerToView(pane, m_timeRulerLayer);
|
Chris@145
|
2395 }
|
Chris@145
|
2396
|
Chris@145
|
2397 if (!getMainModel()) {
|
Chris@684
|
2398 m_document->setMainModel(modelId);
|
Chris@145
|
2399 layer = m_document->createMainModelLayer(LayerFactory::Waveform);
|
Chris@145
|
2400 } else {
|
Chris@684
|
2401 layer = m_document->createImportedLayer(modelId);
|
Chris@145
|
2402 }
|
Chris@145
|
2403
|
Chris@145
|
2404 m_document->addLayerToView(pane, layer);
|
Chris@145
|
2405
|
Chris@684
|
2406 added.insert(modelId);
|
Chris@684
|
2407
|
Chris@684
|
2408 for (auto otherId: modelIds) {
|
Chris@684
|
2409
|
Chris@684
|
2410 if (otherId == modelId) continue;
|
Chris@684
|
2411
|
Chris@684
|
2412 bool isDependent = false;
|
Chris@684
|
2413 if (auto dm = ModelById::get(otherId)) {
|
Chris@684
|
2414 if (dm->getSourceModel() == modelId) {
|
Chris@684
|
2415 isDependent = true;
|
Chris@684
|
2416 }
|
Chris@684
|
2417 }
|
Chris@684
|
2418 if (!isDependent) continue;
|
Chris@684
|
2419
|
Chris@684
|
2420 layer = m_document->createImportedLayer(otherId);
|
Chris@145
|
2421
|
Chris@145
|
2422 if (layer->isLayerOpaque() ||
|
Chris@145
|
2423 dynamic_cast<Colour3DPlotLayer *>(layer)) {
|
Chris@145
|
2424
|
Chris@156
|
2425 // these always go in a new pane, with nothing
|
Chris@156
|
2426 // else going in the same pane
|
Chris@156
|
2427
|
Chris@145
|
2428 Pane *singleLayerPane = addPaneToStack();
|
Chris@145
|
2429 if (m_timeRulerLayer) {
|
Chris@145
|
2430 m_document->addLayerToView(singleLayerPane, m_timeRulerLayer);
|
Chris@145
|
2431 }
|
Chris@145
|
2432 m_document->addLayerToView(singleLayerPane, layer);
|
Chris@145
|
2433
|
Chris@156
|
2434 } else if (layer->getLayerColourSignificance() ==
|
Chris@156
|
2435 Layer::ColourHasMeaningfulValue) {
|
Chris@156
|
2436
|
Chris@156
|
2437 // these can go in a pane with something else, but
|
Chris@156
|
2438 // only if none of the something elses also have
|
Chris@156
|
2439 // this quality
|
Chris@156
|
2440
|
Chris@156
|
2441 bool needNewPane = false;
|
Chris@156
|
2442 for (int i = 0; i < pane->getLayerCount(); ++i) {
|
Chris@156
|
2443 Layer *otherLayer = pane->getLayer(i);
|
Chris@156
|
2444 if (otherLayer &&
|
Chris@156
|
2445 (otherLayer->getLayerColourSignificance() ==
|
Chris@156
|
2446 Layer::ColourHasMeaningfulValue)) {
|
Chris@156
|
2447 needNewPane = true;
|
Chris@156
|
2448 break;
|
Chris@156
|
2449 }
|
Chris@156
|
2450 }
|
Chris@156
|
2451 if (needNewPane) {
|
Chris@156
|
2452 pane = addPaneToStack();
|
Chris@156
|
2453 }
|
Chris@156
|
2454
|
Chris@156
|
2455 m_document->addLayerToView(pane, layer);
|
Chris@156
|
2456
|
Chris@145
|
2457 } else {
|
Chris@145
|
2458
|
Chris@145
|
2459 if (pane->getLayerCount() > 4) {
|
Chris@145
|
2460 pane = addPaneToStack();
|
Chris@145
|
2461 }
|
Chris@145
|
2462
|
Chris@145
|
2463 m_document->addLayerToView(pane, layer);
|
Chris@145
|
2464 }
|
Chris@145
|
2465
|
Chris@684
|
2466 added.insert(otherId);
|
Chris@145
|
2467 }
|
Chris@145
|
2468 }
|
Chris@145
|
2469 }
|
Chris@145
|
2470
|
Chris@684
|
2471 for (auto modelId : modelIds) {
|
Chris@684
|
2472
|
Chris@684
|
2473 if (added.find(modelId) == added.end()) {
|
Chris@145
|
2474
|
Chris@684
|
2475 Layer *layer = m_document->createImportedLayer(modelId);
|
Chris@145
|
2476 if (!layer) return FileOpenFailed;
|
Chris@145
|
2477
|
Chris@145
|
2478 Pane *singleLayerPane = addPaneToStack();
|
Chris@145
|
2479 if (m_timeRulerLayer) {
|
Chris@145
|
2480 m_document->addLayerToView(singleLayerPane, m_timeRulerLayer);
|
Chris@145
|
2481 }
|
Chris@145
|
2482 m_document->addLayerToView(singleLayerPane, layer);
|
Chris@145
|
2483 }
|
Chris@145
|
2484 }
|
Chris@145
|
2485
|
Chris@145
|
2486 m_recentFiles.addFile(source.getLocation());
|
Chris@145
|
2487 return FileOpenSucceeded;
|
Chris@141
|
2488 }
|
Chris@141
|
2489
|
Chris@584
|
2490 class AudioLogCallback : public breakfastquay::AudioFactory::LogCallback
|
Chris@584
|
2491 {
|
Chris@584
|
2492 public:
|
Chris@584
|
2493 void log(std::string message) const override {
|
Chris@584
|
2494 SVDEBUG << message << endl;
|
Chris@584
|
2495 }
|
Chris@584
|
2496 };
|
Chris@584
|
2497
|
Chris@45
|
2498 void
|
Chris@475
|
2499 MainWindowBase::createAudioIO()
|
Chris@45
|
2500 {
|
Chris@475
|
2501 if (m_playTarget || m_audioIO) return;
|
Chris@475
|
2502
|
Chris@584
|
2503 static AudioLogCallback audioLogCallback;
|
Chris@584
|
2504 breakfastquay::AudioFactory::setLogCallback(&audioLogCallback);
|
Chris@584
|
2505
|
Chris@475
|
2506 if (!(m_soundOptions & WithAudioOutput)) return;
|
Chris@45
|
2507
|
Chris@126
|
2508 QSettings settings;
|
Chris@126
|
2509 settings.beginGroup("Preferences");
|
Chris@547
|
2510 QString implementation = settings.value
|
Chris@547
|
2511 ("audio-target", "").toString();
|
Chris@547
|
2512 QString suffix;
|
Chris@547
|
2513 if (implementation != "") suffix = "-" + implementation;
|
Chris@547
|
2514 QString recordDevice = settings.value
|
Chris@547
|
2515 ("audio-record-device" + suffix, "").toString();
|
Chris@547
|
2516 QString playbackDevice = settings.value
|
Chris@547
|
2517 ("audio-playback-device" + suffix, "").toString();
|
Chris@126
|
2518 settings.endGroup();
|
Chris@547
|
2519
|
Chris@547
|
2520 if (implementation == "auto") {
|
Chris@547
|
2521 implementation = "";
|
Chris@547
|
2522 }
|
Chris@468
|
2523
|
Chris@547
|
2524 breakfastquay::AudioFactory::Preference preference;
|
Chris@547
|
2525 preference.implementation = implementation.toStdString();
|
Chris@547
|
2526 preference.recordDevice = recordDevice.toStdString();
|
Chris@547
|
2527 preference.playbackDevice = playbackDevice.toStdString();
|
Chris@547
|
2528
|
Chris@547
|
2529 SVCERR << "createAudioIO: Preferred implementation = \""
|
Chris@547
|
2530 << preference.implementation << "\"" << endl;
|
Chris@547
|
2531 SVCERR << "createAudioIO: Preferred playback device = \""
|
Chris@547
|
2532 << preference.playbackDevice << "\"" << endl;
|
Chris@547
|
2533 SVCERR << "createAudioIO: Preferred record device = \""
|
Chris@547
|
2534 << preference.recordDevice << "\"" << endl;
|
Chris@475
|
2535
|
Chris@551
|
2536 if (!m_resamplerWrapper) {
|
Chris@551
|
2537 m_resamplerWrapper = new breakfastquay::ResamplerWrapper(m_playSource);
|
Chris@551
|
2538 m_playSource->setResamplerWrapper(m_resamplerWrapper);
|
Chris@551
|
2539 }
|
Chris@569
|
2540
|
Chris@569
|
2541 std::string errorString;
|
Chris@551
|
2542
|
Chris@475
|
2543 if (m_soundOptions & WithAudioInput) {
|
Chris@475
|
2544 m_audioIO = breakfastquay::AudioFactory::
|
Chris@569
|
2545 createCallbackIO(m_recordTarget, m_resamplerWrapper,
|
Chris@569
|
2546 preference, errorString);
|
Chris@525
|
2547 if (m_audioIO) {
|
Chris@611
|
2548 SVCERR << "MainWindowBase::createAudioIO: Suspending on creation" << endl;
|
Chris@525
|
2549 m_audioIO->suspend(); // start in suspended state
|
Chris@525
|
2550 m_playSource->setSystemPlaybackTarget(m_audioIO);
|
Chris@586
|
2551 } else {
|
Chris@586
|
2552 // Failed to create audio I/O; this may just mean there is
|
Chris@586
|
2553 // no record device, so fall through to see what happens
|
Chris@586
|
2554 // next. We only report complete failure if we end up with
|
Chris@586
|
2555 // neither m_audioIO nor m_playTarget.
|
Chris@525
|
2556 }
|
Chris@586
|
2557 }
|
Chris@586
|
2558
|
Chris@586
|
2559 if (!m_audioIO) {
|
Chris@475
|
2560 m_playTarget = breakfastquay::AudioFactory::
|
Chris@569
|
2561 createCallbackPlayTarget(m_resamplerWrapper,
|
Chris@569
|
2562 preference, errorString);
|
Chris@525
|
2563 if (m_playTarget) {
|
Chris@611
|
2564 SVCERR << "MainWindowBase::createAudioIO: Suspending on creation" << endl;
|
Chris@525
|
2565 m_playTarget->suspend(); // start in suspended state
|
Chris@525
|
2566 m_playSource->setSystemPlaybackTarget(m_playTarget);
|
Chris@525
|
2567 }
|
Chris@475
|
2568 }
|
Chris@475
|
2569
|
Chris@475
|
2570 if (!m_playTarget && !m_audioIO) {
|
Chris@104
|
2571 emit hideSplash();
|
Chris@569
|
2572 QString message;
|
Chris@569
|
2573 QString error = errorString.c_str();
|
Chris@569
|
2574 QString firstBit, secondBit;
|
Chris@547
|
2575 if (implementation == "") {
|
Chris@569
|
2576 if (error == "") {
|
Chris@569
|
2577 firstBit = tr("<b>No audio available</b><p>Could not open an audio device.</p>");
|
Chris@569
|
2578 } else {
|
Chris@569
|
2579 firstBit = tr("<b>No audio available</b><p>Could not open audio device: %1</p>").arg(error);
|
Chris@569
|
2580 }
|
Chris@569
|
2581 if (m_soundOptions & WithAudioInput) {
|
Chris@569
|
2582 secondBit = tr("<p>Automatic audio device detection failed. Audio playback and recording will not be available during this session.</p>");
|
Chris@569
|
2583 } else {
|
Chris@569
|
2584 secondBit = tr("<p>Automatic audio device detection failed. Audio playback will not be available during this session.</p>");
|
Chris@569
|
2585 }
|
Chris@126
|
2586 } else {
|
Chris@569
|
2587 QString driverName = breakfastquay::AudioFactory::
|
Chris@569
|
2588 getImplementationDescription(implementation.toStdString())
|
Chris@569
|
2589 .c_str();
|
Chris@569
|
2590 if (error == "") {
|
Chris@569
|
2591 firstBit = tr("<b>No audio available</b><p>Failed to open your preferred audio driver (\"%1\").</p>").arg(driverName);
|
Chris@569
|
2592 } else {
|
Chris@569
|
2593 firstBit = tr("<b>No audio available</b><p>Failed to open your preferred audio driver (\"%1\"): %2.</p>").arg(driverName).arg(error);
|
Chris@569
|
2594 }
|
Chris@569
|
2595 if (m_soundOptions & WithAudioInput) {
|
Chris@569
|
2596 secondBit = tr("<p>Audio playback and recording will not be available during this session.</p>");
|
Chris@569
|
2597 } else {
|
Chris@569
|
2598 secondBit = tr("<p>Audio playback will not be available during this session.</p>");
|
Chris@569
|
2599 }
|
Chris@126
|
2600 }
|
Chris@570
|
2601 SVDEBUG << "createAudioIO: ERROR: Failed to open audio device \""
|
Chris@570
|
2602 << implementation << "\": error is: " << error << endl;
|
Chris@569
|
2603 QMessageBox::warning(this, tr("Couldn't open audio device"),
|
Chris@569
|
2604 firstBit + secondBit, QMessageBox::Ok);
|
Chris@45
|
2605 }
|
Chris@45
|
2606 }
|
Chris@45
|
2607
|
Chris@556
|
2608 void
|
Chris@556
|
2609 MainWindowBase::deleteAudioIO()
|
Chris@556
|
2610 {
|
Chris@556
|
2611 // First prevent this trying to call target.
|
Chris@559
|
2612 if (m_playSource) {
|
Chris@636
|
2613 m_playSource->setSystemPlaybackTarget(nullptr);
|
Chris@636
|
2614 m_playSource->setResamplerWrapper(nullptr);
|
Chris@559
|
2615 }
|
Chris@556
|
2616
|
Chris@556
|
2617 // Then delete the breakfastquay::System object.
|
Chris@556
|
2618 // Only one of these two exists!
|
Chris@556
|
2619 delete m_audioIO;
|
Chris@556
|
2620 delete m_playTarget;
|
Chris@556
|
2621
|
Chris@559
|
2622 // And the breakfastquay resampler wrapper. We need to
|
Chris@559
|
2623 // delete/recreate this if the channel count changes, which is one
|
Chris@559
|
2624 // of the use cases for recreateAudioIO() calling this
|
Chris@559
|
2625 delete m_resamplerWrapper;
|
Chris@559
|
2626
|
Chris@636
|
2627 m_audioIO = nullptr;
|
Chris@636
|
2628 m_playTarget = nullptr;
|
Chris@636
|
2629 m_resamplerWrapper = nullptr;
|
Chris@556
|
2630 }
|
Chris@556
|
2631
|
Chris@556
|
2632 void
|
Chris@556
|
2633 MainWindowBase::recreateAudioIO()
|
Chris@556
|
2634 {
|
Chris@556
|
2635 deleteAudioIO();
|
Chris@556
|
2636 createAudioIO();
|
Chris@556
|
2637 }
|
Chris@556
|
2638
|
Chris@570
|
2639 void
|
Chris@570
|
2640 MainWindowBase::audioChannelCountIncreased(int)
|
Chris@570
|
2641 {
|
Chris@611
|
2642 SVCERR << "MainWindowBase::audioChannelCountIncreased" << endl;
|
Chris@570
|
2643 recreateAudioIO();
|
Chris@610
|
2644
|
Chris@610
|
2645 if (m_recordTarget &&
|
Chris@610
|
2646 m_recordTarget->isRecording() &&
|
Chris@610
|
2647 m_audioIO) {
|
Chris@610
|
2648 SVCERR << "MainWindowBase::audioChannelCountIncreased: we were recording already, so resuming IO now" << endl;
|
Chris@610
|
2649 m_audioIO->resume();
|
Chris@610
|
2650 }
|
Chris@570
|
2651 }
|
Chris@570
|
2652
|
Chris@684
|
2653 ModelId
|
Chris@685
|
2654 MainWindowBase::getMainModelId() const
|
Chris@684
|
2655 {
|
Chris@684
|
2656 if (!m_document) return {};
|
Chris@684
|
2657 return m_document->getMainModel();
|
Chris@684
|
2658 }
|
Chris@684
|
2659
|
Chris@684
|
2660 std::shared_ptr<WaveFileModel>
|
Chris@685
|
2661 MainWindowBase::getMainModel() const
|
Chris@45
|
2662 {
|
Chris@684
|
2663 return ModelById::getAs<WaveFileModel>(getMainModelId());
|
Chris@45
|
2664 }
|
Chris@45
|
2665
|
Chris@45
|
2666 void
|
Chris@45
|
2667 MainWindowBase::createDocument()
|
Chris@45
|
2668 {
|
Chris@45
|
2669 m_document = new Document;
|
Chris@45
|
2670
|
Chris@45
|
2671 connect(m_document, SIGNAL(layerAdded(Layer *)),
|
Chris@595
|
2672 this, SLOT(layerAdded(Layer *)));
|
Chris@45
|
2673 connect(m_document, SIGNAL(layerRemoved(Layer *)),
|
Chris@595
|
2674 this, SLOT(layerRemoved(Layer *)));
|
Chris@45
|
2675 connect(m_document, SIGNAL(layerAboutToBeDeleted(Layer *)),
|
Chris@595
|
2676 this, SLOT(layerAboutToBeDeleted(Layer *)));
|
Chris@45
|
2677 connect(m_document, SIGNAL(layerInAView(Layer *, bool)),
|
Chris@595
|
2678 this, SLOT(layerInAView(Layer *, bool)));
|
Chris@45
|
2679
|
Chris@684
|
2680 connect(m_document, SIGNAL(modelAdded(ModelId )),
|
Chris@684
|
2681 this, SLOT(modelAdded(ModelId )));
|
Chris@687
|
2682 connect(m_document, SIGNAL(mainModelChanged(ModelId)),
|
Chris@687
|
2683 this, SLOT(mainModelChanged(ModelId)));
|
Chris@45
|
2684
|
Chris@78
|
2685 connect(m_document, SIGNAL(modelGenerationFailed(QString, QString)),
|
Chris@78
|
2686 this, SLOT(modelGenerationFailed(QString, QString)));
|
Chris@78
|
2687 connect(m_document, SIGNAL(modelRegenerationWarning(QString, QString, QString)),
|
Chris@78
|
2688 this, SLOT(modelRegenerationWarning(QString, QString, QString)));
|
Chris@687
|
2689 connect(m_document, SIGNAL(alignmentComplete(ModelId)),
|
Chris@687
|
2690 this, SLOT(alignmentComplete(ModelId)));
|
Chris@423
|
2691 connect(m_document, SIGNAL(alignmentFailed(QString)),
|
Chris@423
|
2692 this, SLOT(alignmentFailed(QString)));
|
Chris@160
|
2693
|
Chris@667
|
2694 m_document->setAutoAlignment(m_viewManager->getAlignMode());
|
Chris@667
|
2695
|
Chris@160
|
2696 emit replacedDocument();
|
Chris@45
|
2697 }
|
Chris@45
|
2698
|
Chris@45
|
2699 bool
|
Chris@45
|
2700 MainWindowBase::saveSessionFile(QString path)
|
Chris@45
|
2701 {
|
Chris@217
|
2702 try {
|
Chris@217
|
2703
|
Chris@217
|
2704 TempWriteFile temp(path);
|
Chris@217
|
2705
|
Chris@217
|
2706 BZipFileDevice bzFile(temp.getTemporaryFilename());
|
Chris@217
|
2707 if (!bzFile.open(QIODevice::WriteOnly)) {
|
Chris@293
|
2708 cerr << "Failed to open session file \""
|
Chris@294
|
2709 << temp.getTemporaryFilename()
|
Chris@217
|
2710 << "\" for writing: "
|
Chris@293
|
2711 << bzFile.errorString() << endl;
|
Chris@217
|
2712 return false;
|
Chris@217
|
2713 }
|
Chris@217
|
2714
|
Chris@217
|
2715 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
|
Chris@217
|
2716
|
Chris@217
|
2717 QTextStream out(&bzFile);
|
Chris@432
|
2718 out.setCodec(QTextCodec::codecForName("UTF-8"));
|
Chris@226
|
2719 toXml(out, false);
|
Chris@217
|
2720 out.flush();
|
Chris@217
|
2721
|
Chris@217
|
2722 QApplication::restoreOverrideCursor();
|
Chris@217
|
2723
|
Chris@217
|
2724 if (!bzFile.isOK()) {
|
Chris@217
|
2725 QMessageBox::critical(this, tr("Failed to write file"),
|
Chris@217
|
2726 tr("<b>Save failed</b><p>Failed to write to file \"%1\": %2")
|
Chris@217
|
2727 .arg(path).arg(bzFile.errorString()));
|
Chris@217
|
2728 bzFile.close();
|
Chris@217
|
2729 return false;
|
Chris@217
|
2730 }
|
Chris@217
|
2731
|
Chris@217
|
2732 bzFile.close();
|
Chris@217
|
2733 temp.moveToTarget();
|
Chris@217
|
2734 return true;
|
Chris@217
|
2735
|
Chris@217
|
2736 } catch (FileOperationFailed &f) {
|
Chris@217
|
2737
|
Chris@217
|
2738 QMessageBox::critical(this, tr("Failed to write file"),
|
Chris@217
|
2739 tr("<b>Save failed</b><p>Failed to write to file \"%1\": %2")
|
Chris@217
|
2740 .arg(path).arg(f.what()));
|
Chris@45
|
2741 return false;
|
Chris@45
|
2742 }
|
Chris@45
|
2743 }
|
Chris@45
|
2744
|
Chris@224
|
2745 bool
|
Chris@224
|
2746 MainWindowBase::saveSessionTemplate(QString path)
|
Chris@224
|
2747 {
|
Chris@224
|
2748 try {
|
Chris@224
|
2749
|
Chris@224
|
2750 TempWriteFile temp(path);
|
Chris@224
|
2751
|
Chris@224
|
2752 QFile file(temp.getTemporaryFilename());
|
Chris@224
|
2753 if (!file.open(QIODevice::WriteOnly)) {
|
Chris@293
|
2754 cerr << "Failed to open session template file \""
|
Chris@294
|
2755 << temp.getTemporaryFilename()
|
Chris@224
|
2756 << "\" for writing: "
|
Chris@294
|
2757 << file.errorString() << endl;
|
Chris@224
|
2758 return false;
|
Chris@224
|
2759 }
|
Chris@224
|
2760
|
Chris@224
|
2761 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
|
Chris@224
|
2762
|
Chris@224
|
2763 QTextStream out(&file);
|
Chris@432
|
2764 out.setCodec(QTextCodec::codecForName("UTF-8"));
|
Chris@226
|
2765 toXml(out, true);
|
Chris@224
|
2766 out.flush();
|
Chris@224
|
2767
|
Chris@224
|
2768 QApplication::restoreOverrideCursor();
|
Chris@224
|
2769
|
Chris@224
|
2770 file.close();
|
Chris@224
|
2771 temp.moveToTarget();
|
Chris@224
|
2772 return true;
|
Chris@224
|
2773
|
Chris@224
|
2774 } catch (FileOperationFailed &f) {
|
Chris@224
|
2775
|
Chris@224
|
2776 QMessageBox::critical(this, tr("Failed to write file"),
|
Chris@224
|
2777 tr("<b>Save failed</b><p>Failed to write to file \"%1\": %2")
|
Chris@224
|
2778 .arg(path).arg(f.what()));
|
Chris@224
|
2779 return false;
|
Chris@224
|
2780 }
|
Chris@224
|
2781 }
|
Chris@224
|
2782
|
Chris@659
|
2783 bool
|
Chris@659
|
2784 MainWindowBase::exportLayerTo(Layer *layer, QString path, QString &error)
|
Chris@659
|
2785 {
|
Chris@659
|
2786 if (QFileInfo(path).suffix() == "") path += ".svl";
|
Chris@659
|
2787
|
Chris@659
|
2788 QString suffix = QFileInfo(path).suffix().toLower();
|
Chris@659
|
2789
|
Chris@684
|
2790 auto model = ModelById::get(layer->getModel());
|
Chris@684
|
2791 if (!model) {
|
Chris@684
|
2792 error = tr("Internal error: unknown model");
|
Chris@684
|
2793 return false;
|
Chris@684
|
2794 }
|
Chris@659
|
2795
|
Chris@659
|
2796 if (suffix == "xml" || suffix == "svl") {
|
Chris@659
|
2797
|
Chris@659
|
2798 QFile file(path);
|
Chris@659
|
2799 if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
Chris@659
|
2800 error = tr("Failed to open file %1 for writing").arg(path);
|
Chris@659
|
2801 } else {
|
Chris@659
|
2802 QTextStream out(&file);
|
Chris@659
|
2803 out.setCodec(QTextCodec::codecForName("UTF-8"));
|
Chris@659
|
2804 out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
|
Chris@659
|
2805 << "<!DOCTYPE sonic-visualiser>\n"
|
Chris@659
|
2806 << "<sv>\n"
|
Chris@659
|
2807 << " <data>\n";
|
Chris@659
|
2808
|
Chris@659
|
2809 model->toXml(out, " ");
|
Chris@659
|
2810
|
Chris@659
|
2811 out << " </data>\n"
|
Chris@659
|
2812 << " <display>\n";
|
Chris@659
|
2813
|
Chris@659
|
2814 layer->toXml(out, " ");
|
Chris@659
|
2815
|
Chris@659
|
2816 out << " </display>\n"
|
Chris@659
|
2817 << "</sv>\n";
|
Chris@659
|
2818 }
|
Chris@659
|
2819
|
Chris@659
|
2820 } else if (suffix == "mid" || suffix == "midi") {
|
Chris@659
|
2821
|
Chris@684
|
2822 auto nm = ModelById::getAs<NoteModel>(layer->getModel());
|
Chris@659
|
2823
|
Chris@659
|
2824 if (!nm) {
|
Chris@659
|
2825 error = tr("Can't export non-note layers to MIDI");
|
Chris@659
|
2826 } else {
|
Chris@684
|
2827 MIDIFileWriter writer(path, nm.get(), nm->getSampleRate());
|
Chris@659
|
2828 writer.write();
|
Chris@659
|
2829 if (!writer.isOK()) {
|
Chris@659
|
2830 error = writer.getError();
|
Chris@659
|
2831 }
|
Chris@659
|
2832 }
|
Chris@659
|
2833
|
Chris@659
|
2834 } else if (suffix == "ttl" || suffix == "n3") {
|
Chris@659
|
2835
|
Chris@684
|
2836 if (!RDFExporter::canExportModel(model.get())) {
|
Chris@659
|
2837 error = tr("Sorry, cannot export this layer type to RDF (supported types are: region, note, text, time instants, time values)");
|
Chris@659
|
2838 } else {
|
Chris@684
|
2839 RDFExporter exporter(path, model.get());
|
Chris@659
|
2840 exporter.write();
|
Chris@659
|
2841 if (!exporter.isOK()) {
|
Chris@659
|
2842 error = exporter.getError();
|
Chris@659
|
2843 }
|
Chris@659
|
2844 }
|
Chris@659
|
2845
|
Chris@659
|
2846 } else {
|
Chris@659
|
2847
|
Chris@684
|
2848 CSVFileWriter writer(path, model.get(),
|
Chris@659
|
2849 ((suffix == "csv") ? "," : "\t"));
|
Chris@659
|
2850 writer.write();
|
Chris@659
|
2851
|
Chris@659
|
2852 if (!writer.isOK()) {
|
Chris@659
|
2853 error = writer.getError();
|
Chris@659
|
2854 }
|
Chris@659
|
2855 }
|
Chris@659
|
2856
|
Chris@659
|
2857 return (error == "");
|
Chris@659
|
2858 }
|
Chris@659
|
2859
|
Chris@45
|
2860 void
|
Chris@226
|
2861 MainWindowBase::toXml(QTextStream &out, bool asTemplate)
|
Chris@45
|
2862 {
|
Chris@45
|
2863 QString indent(" ");
|
Chris@45
|
2864
|
Chris@45
|
2865 out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
|
Chris@45
|
2866 out << "<!DOCTYPE sonic-visualiser>\n";
|
Chris@45
|
2867 out << "<sv>\n";
|
Chris@45
|
2868
|
Chris@226
|
2869 if (asTemplate) {
|
Chris@226
|
2870 m_document->toXmlAsTemplate(out, "", "");
|
Chris@226
|
2871 } else {
|
Chris@226
|
2872 m_document->toXml(out, "", "");
|
Chris@226
|
2873 }
|
Chris@45
|
2874
|
Chris@45
|
2875 out << "<display>\n";
|
Chris@45
|
2876
|
Chris@45
|
2877 out << QString(" <window width=\"%1\" height=\"%2\"/>\n")
|
Chris@595
|
2878 .arg(width()).arg(height());
|
Chris@45
|
2879
|
Chris@45
|
2880 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
|
Chris@45
|
2881
|
Chris@595
|
2882 Pane *pane = m_paneStack->getPane(i);
|
Chris@595
|
2883
|
Chris@595
|
2884 if (pane) {
|
Chris@45
|
2885 pane->toXml(out, indent);
|
Chris@595
|
2886 }
|
Chris@45
|
2887 }
|
Chris@45
|
2888
|
Chris@45
|
2889 out << "</display>\n";
|
Chris@45
|
2890
|
Chris@45
|
2891 m_viewManager->getSelection().toXml(out);
|
Chris@45
|
2892
|
Chris@45
|
2893 out << "</sv>\n";
|
Chris@45
|
2894 }
|
Chris@45
|
2895
|
Chris@45
|
2896 Pane *
|
Chris@45
|
2897 MainWindowBase::addPaneToStack()
|
Chris@45
|
2898 {
|
Chris@342
|
2899 cerr << "MainWindowBase::addPaneToStack()" << endl;
|
Chris@45
|
2900 AddPaneCommand *command = new AddPaneCommand(this);
|
Chris@45
|
2901 CommandHistory::getInstance()->addCommand(command);
|
Chris@57
|
2902 Pane *pane = command->getPane();
|
Chris@57
|
2903 return pane;
|
Chris@45
|
2904 }
|
Chris@45
|
2905
|
Chris@45
|
2906 void
|
Chris@45
|
2907 MainWindowBase::zoomIn()
|
Chris@45
|
2908 {
|
Chris@45
|
2909 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@45
|
2910 if (currentPane) currentPane->zoom(true);
|
Chris@45
|
2911 }
|
Chris@45
|
2912
|
Chris@45
|
2913 void
|
Chris@45
|
2914 MainWindowBase::zoomOut()
|
Chris@45
|
2915 {
|
Chris@45
|
2916 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@45
|
2917 if (currentPane) currentPane->zoom(false);
|
Chris@45
|
2918 }
|
Chris@45
|
2919
|
Chris@45
|
2920 void
|
Chris@45
|
2921 MainWindowBase::zoomToFit()
|
Chris@45
|
2922 {
|
Chris@45
|
2923 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@45
|
2924 if (!currentPane) return;
|
Chris@45
|
2925
|
Chris@684
|
2926 auto model = getMainModel();
|
Chris@45
|
2927 if (!model) return;
|
Chris@45
|
2928
|
Chris@434
|
2929 sv_frame_t start = model->getStartFrame();
|
Chris@434
|
2930 sv_frame_t end = model->getEndFrame();
|
Chris@60
|
2931 if (m_playSource) end = std::max(end, m_playSource->getPlayEndFrame());
|
Chris@366
|
2932 int pixels = currentPane->width();
|
Chris@366
|
2933
|
Chris@366
|
2934 int sw = currentPane->getVerticalScaleWidth();
|
Chris@45
|
2935 if (pixels > sw * 2) pixels -= sw * 2;
|
Chris@45
|
2936 else pixels = 1;
|
Chris@45
|
2937 if (pixels > 4) pixels -= 4;
|
Chris@45
|
2938
|
Chris@624
|
2939 ZoomLevel zoomLevel = ZoomLevel::fromRatio(pixels, end - start);
|
Chris@45
|
2940 currentPane->setZoomLevel(zoomLevel);
|
Chris@45
|
2941 currentPane->setCentreFrame((start + end) / 2);
|
Chris@45
|
2942 }
|
Chris@45
|
2943
|
Chris@45
|
2944 void
|
Chris@45
|
2945 MainWindowBase::zoomDefault()
|
Chris@45
|
2946 {
|
Chris@45
|
2947 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@302
|
2948 QSettings settings;
|
Chris@302
|
2949 settings.beginGroup("MainWindow");
|
Chris@302
|
2950 int zoom = settings.value("zoom-default", 1024).toInt();
|
Chris@302
|
2951 settings.endGroup();
|
Chris@624
|
2952 if (currentPane) {
|
Chris@624
|
2953 currentPane->setZoomLevel(ZoomLevel(ZoomLevel::FramesPerPixel, zoom));
|
Chris@624
|
2954 }
|
Chris@45
|
2955 }
|
Chris@45
|
2956
|
Chris@45
|
2957 void
|
Chris@45
|
2958 MainWindowBase::scrollLeft()
|
Chris@45
|
2959 {
|
Chris@45
|
2960 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@45
|
2961 if (currentPane) currentPane->scroll(false, false);
|
Chris@45
|
2962 }
|
Chris@45
|
2963
|
Chris@45
|
2964 void
|
Chris@45
|
2965 MainWindowBase::jumpLeft()
|
Chris@45
|
2966 {
|
Chris@45
|
2967 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@45
|
2968 if (currentPane) currentPane->scroll(false, true);
|
Chris@45
|
2969 }
|
Chris@45
|
2970
|
Chris@45
|
2971 void
|
Chris@162
|
2972 MainWindowBase::peekLeft()
|
Chris@162
|
2973 {
|
Chris@162
|
2974 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@162
|
2975 if (currentPane) currentPane->scroll(false, false, false);
|
Chris@162
|
2976 }
|
Chris@162
|
2977
|
Chris@162
|
2978 void
|
Chris@45
|
2979 MainWindowBase::scrollRight()
|
Chris@45
|
2980 {
|
Chris@45
|
2981 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@45
|
2982 if (currentPane) currentPane->scroll(true, false);
|
Chris@45
|
2983 }
|
Chris@45
|
2984
|
Chris@45
|
2985 void
|
Chris@45
|
2986 MainWindowBase::jumpRight()
|
Chris@45
|
2987 {
|
Chris@45
|
2988 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@45
|
2989 if (currentPane) currentPane->scroll(true, true);
|
Chris@45
|
2990 }
|
Chris@45
|
2991
|
Chris@45
|
2992 void
|
Chris@162
|
2993 MainWindowBase::peekRight()
|
Chris@162
|
2994 {
|
Chris@162
|
2995 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@162
|
2996 if (currentPane) currentPane->scroll(true, false, false);
|
Chris@162
|
2997 }
|
Chris@162
|
2998
|
Chris@162
|
2999 void
|
Chris@45
|
3000 MainWindowBase::showNoOverlays()
|
Chris@45
|
3001 {
|
Chris@45
|
3002 m_viewManager->setOverlayMode(ViewManager::NoOverlays);
|
Chris@45
|
3003 }
|
Chris@45
|
3004
|
Chris@45
|
3005 void
|
Chris@45
|
3006 MainWindowBase::showMinimalOverlays()
|
Chris@45
|
3007 {
|
Chris@335
|
3008 m_viewManager->setOverlayMode(ViewManager::StandardOverlays);
|
Chris@45
|
3009 }
|
Chris@45
|
3010
|
Chris@45
|
3011 void
|
Chris@45
|
3012 MainWindowBase::showAllOverlays()
|
Chris@45
|
3013 {
|
Chris@45
|
3014 m_viewManager->setOverlayMode(ViewManager::AllOverlays);
|
Chris@45
|
3015 }
|
Chris@45
|
3016
|
Chris@45
|
3017 void
|
Chris@577
|
3018 MainWindowBase::findTimeRulerLayer()
|
Chris@577
|
3019 {
|
Chris@577
|
3020 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
|
Chris@577
|
3021 Pane *pane = m_paneStack->getPane(i);
|
Chris@577
|
3022 if (!pane) continue;
|
Chris@577
|
3023 for (int j = 0; j < pane->getLayerCount(); ++j) {
|
Chris@577
|
3024 Layer *layer = pane->getLayer(j);
|
Chris@577
|
3025 if (!dynamic_cast<TimeRulerLayer *>(layer)) continue;
|
Chris@577
|
3026 m_timeRulerLayer = layer;
|
Chris@577
|
3027 return;
|
Chris@577
|
3028 }
|
Chris@577
|
3029 }
|
Chris@577
|
3030 if (m_timeRulerLayer) {
|
Chris@577
|
3031 SVCERR << "WARNING: Time ruler layer was not reset to 0 before session template loaded?" << endl;
|
Chris@577
|
3032 delete m_timeRulerLayer;
|
Chris@636
|
3033 m_timeRulerLayer = nullptr;
|
Chris@577
|
3034 }
|
Chris@577
|
3035 }
|
Chris@577
|
3036
|
Chris@577
|
3037 void
|
Chris@211
|
3038 MainWindowBase::toggleTimeRulers()
|
Chris@211
|
3039 {
|
Chris@211
|
3040 bool haveRulers = false;
|
Chris@211
|
3041 bool someHidden = false;
|
Chris@211
|
3042
|
Chris@211
|
3043 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
|
Chris@211
|
3044
|
Chris@211
|
3045 Pane *pane = m_paneStack->getPane(i);
|
Chris@211
|
3046 if (!pane) continue;
|
Chris@211
|
3047
|
Chris@211
|
3048 for (int j = 0; j < pane->getLayerCount(); ++j) {
|
Chris@211
|
3049
|
Chris@211
|
3050 Layer *layer = pane->getLayer(j);
|
Chris@211
|
3051 if (!dynamic_cast<TimeRulerLayer *>(layer)) continue;
|
Chris@211
|
3052
|
Chris@211
|
3053 haveRulers = true;
|
Chris@211
|
3054 if (layer->isLayerDormant(pane)) someHidden = true;
|
Chris@211
|
3055 }
|
Chris@211
|
3056 }
|
Chris@211
|
3057
|
Chris@211
|
3058 if (haveRulers) {
|
Chris@211
|
3059
|
Chris@211
|
3060 bool show = someHidden;
|
Chris@211
|
3061
|
Chris@211
|
3062 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
|
Chris@211
|
3063
|
Chris@211
|
3064 Pane *pane = m_paneStack->getPane(i);
|
Chris@211
|
3065 if (!pane) continue;
|
Chris@211
|
3066
|
Chris@211
|
3067 for (int j = 0; j < pane->getLayerCount(); ++j) {
|
Chris@211
|
3068
|
Chris@211
|
3069 Layer *layer = pane->getLayer(j);
|
Chris@211
|
3070 if (!dynamic_cast<TimeRulerLayer *>(layer)) continue;
|
Chris@211
|
3071
|
Chris@211
|
3072 layer->showLayer(pane, show);
|
Chris@211
|
3073 }
|
Chris@211
|
3074 }
|
Chris@211
|
3075 }
|
Chris@211
|
3076 }
|
Chris@211
|
3077
|
Chris@211
|
3078 void
|
Chris@45
|
3079 MainWindowBase::toggleZoomWheels()
|
Chris@45
|
3080 {
|
Chris@45
|
3081 if (m_viewManager->getZoomWheelsEnabled()) {
|
Chris@45
|
3082 m_viewManager->setZoomWheelsEnabled(false);
|
Chris@45
|
3083 } else {
|
Chris@45
|
3084 m_viewManager->setZoomWheelsEnabled(true);
|
Chris@45
|
3085 }
|
Chris@45
|
3086 }
|
Chris@45
|
3087
|
Chris@45
|
3088 void
|
Chris@45
|
3089 MainWindowBase::togglePropertyBoxes()
|
Chris@45
|
3090 {
|
Chris@45
|
3091 if (m_paneStack->getLayoutStyle() == PaneStack::NoPropertyStacks) {
|
Chris@45
|
3092 if (Preferences::getInstance()->getPropertyBoxLayout() ==
|
Chris@45
|
3093 Preferences::VerticallyStacked) {
|
Chris@45
|
3094 m_paneStack->setLayoutStyle(PaneStack::PropertyStackPerPaneLayout);
|
Chris@45
|
3095 } else {
|
Chris@45
|
3096 m_paneStack->setLayoutStyle(PaneStack::SinglePropertyStackLayout);
|
Chris@45
|
3097 }
|
Chris@45
|
3098 } else {
|
Chris@45
|
3099 m_paneStack->setLayoutStyle(PaneStack::NoPropertyStacks);
|
Chris@45
|
3100 }
|
Chris@45
|
3101 }
|
Chris@45
|
3102
|
Chris@378
|
3103 QLabel *
|
Chris@378
|
3104 MainWindowBase::getStatusLabel() const
|
Chris@378
|
3105 {
|
Chris@378
|
3106 if (!m_statusLabel) {
|
Chris@378
|
3107 m_statusLabel = new QLabel();
|
Chris@378
|
3108 statusBar()->addWidget(m_statusLabel, 1);
|
Chris@378
|
3109 }
|
Chris@379
|
3110
|
Chris@379
|
3111 QList<QFrame *> frames = statusBar()->findChildren<QFrame *>();
|
Chris@379
|
3112 foreach (QFrame *f, frames) {
|
Chris@379
|
3113 f->setFrameStyle(QFrame::NoFrame);
|
Chris@379
|
3114 }
|
Chris@379
|
3115
|
Chris@378
|
3116 return m_statusLabel;
|
Chris@378
|
3117 }
|
Chris@378
|
3118
|
Chris@45
|
3119 void
|
Chris@45
|
3120 MainWindowBase::toggleStatusBar()
|
Chris@45
|
3121 {
|
Chris@45
|
3122 QSettings settings;
|
Chris@45
|
3123 settings.beginGroup("MainWindow");
|
Chris@45
|
3124 bool sb = settings.value("showstatusbar", true).toBool();
|
Chris@45
|
3125
|
Chris@45
|
3126 if (sb) {
|
Chris@45
|
3127 statusBar()->hide();
|
Chris@45
|
3128 } else {
|
Chris@45
|
3129 statusBar()->show();
|
Chris@45
|
3130 }
|
Chris@45
|
3131
|
Chris@45
|
3132 settings.setValue("showstatusbar", !sb);
|
Chris@45
|
3133
|
Chris@45
|
3134 settings.endGroup();
|
Chris@45
|
3135 }
|
Chris@45
|
3136
|
Chris@45
|
3137 void
|
Chris@256
|
3138 MainWindowBase::toggleCentreLine()
|
Chris@256
|
3139 {
|
Chris@256
|
3140 if (m_viewManager->shouldShowCentreLine()) {
|
Chris@256
|
3141 m_viewManager->setShowCentreLine(false);
|
Chris@256
|
3142 } else {
|
Chris@256
|
3143 m_viewManager->setShowCentreLine(true);
|
Chris@256
|
3144 }
|
Chris@256
|
3145 }
|
Chris@256
|
3146
|
Chris@256
|
3147 void
|
Chris@45
|
3148 MainWindowBase::preferenceChanged(PropertyContainer::PropertyName name)
|
Chris@45
|
3149 {
|
Chris@45
|
3150 if (name == "Property Box Layout") {
|
Chris@45
|
3151 if (m_paneStack->getLayoutStyle() != PaneStack::NoPropertyStacks) {
|
Chris@45
|
3152 if (Preferences::getInstance()->getPropertyBoxLayout() ==
|
Chris@45
|
3153 Preferences::VerticallyStacked) {
|
Chris@45
|
3154 m_paneStack->setLayoutStyle(PaneStack::PropertyStackPerPaneLayout);
|
Chris@45
|
3155 } else {
|
Chris@45
|
3156 m_paneStack->setLayoutStyle(PaneStack::SinglePropertyStackLayout);
|
Chris@45
|
3157 }
|
Chris@45
|
3158 }
|
Chris@45
|
3159 } else if (name == "Background Mode" && m_viewManager) {
|
Chris@45
|
3160 Preferences::BackgroundMode mode =
|
Chris@45
|
3161 Preferences::getInstance()->getBackgroundMode();
|
Chris@45
|
3162 if (mode == Preferences::BackgroundFromTheme) {
|
Chris@45
|
3163 m_viewManager->setGlobalDarkBackground(m_initialDarkBackground);
|
Chris@45
|
3164 } else if (mode == Preferences::DarkBackground) {
|
Chris@45
|
3165 m_viewManager->setGlobalDarkBackground(true);
|
Chris@45
|
3166 } else {
|
Chris@45
|
3167 m_viewManager->setGlobalDarkBackground(false);
|
Chris@45
|
3168 }
|
Chris@45
|
3169 }
|
Chris@45
|
3170 }
|
Chris@45
|
3171
|
Chris@45
|
3172 void
|
Chris@45
|
3173 MainWindowBase::play()
|
Chris@45
|
3174 {
|
Chris@516
|
3175 if ((m_recordTarget && m_recordTarget->isRecording()) ||
|
Chris@516
|
3176 (m_playSource && m_playSource->isPlaying())) {
|
Chris@45
|
3177 stop();
|
Chris@479
|
3178 QAction *action = qobject_cast<QAction *>(sender());
|
Chris@479
|
3179 if (action) action->setChecked(false);
|
Chris@45
|
3180 } else {
|
Chris@487
|
3181 if (m_audioIO) m_audioIO->resume();
|
Chris@509
|
3182 else if (m_playTarget) m_playTarget->resume();
|
Chris@45
|
3183 playbackFrameChanged(m_viewManager->getPlaybackFrame());
|
Chris@595
|
3184 m_playSource->play(m_viewManager->getPlaybackFrame());
|
Chris@45
|
3185 }
|
Chris@45
|
3186 }
|
Chris@45
|
3187
|
Chris@45
|
3188 void
|
Chris@477
|
3189 MainWindowBase::record()
|
Chris@477
|
3190 {
|
Chris@586
|
3191 QAction *action = qobject_cast<QAction *>(sender());
|
Chris@586
|
3192
|
Chris@478
|
3193 if (!(m_soundOptions & WithAudioInput)) {
|
Chris@586
|
3194 if (action) action->setChecked(false);
|
Chris@478
|
3195 return;
|
Chris@478
|
3196 }
|
Chris@478
|
3197
|
Chris@477
|
3198 if (!m_recordTarget) {
|
Chris@586
|
3199 if (action) action->setChecked(false);
|
Chris@477
|
3200 return;
|
Chris@477
|
3201 }
|
Chris@477
|
3202
|
Chris@478
|
3203 if (!m_audioIO) {
|
Chris@611
|
3204 SVDEBUG << "MainWindowBase::record: about to create audio IO" << endl;
|
Chris@478
|
3205 createAudioIO();
|
Chris@478
|
3206 }
|
Chris@492
|
3207
|
Chris@492
|
3208 if (!m_audioIO) {
|
Chris@586
|
3209 if (!m_playTarget) {
|
Chris@586
|
3210 // Don't need to report this, createAudioIO should have
|
Chris@586
|
3211 if (action) action->setChecked(false);
|
Chris@586
|
3212 return;
|
Chris@586
|
3213 } else {
|
Chris@586
|
3214 // Need to report this: if the play target exists instead
|
Chris@586
|
3215 // of the audio IO, then that means we failed to open a
|
Chris@586
|
3216 // capture device. The record control should be disabled
|
Chris@586
|
3217 // in that situation, so if it happens here, that must
|
Chris@586
|
3218 // mean this is the first time we ever tried to open the
|
Chris@586
|
3219 // audio device, hence the need to report the problem here
|
Chris@586
|
3220 QMessageBox::critical
|
Chris@586
|
3221 (this, tr("No record device available"),
|
Chris@586
|
3222 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
|
3223 if (action) action->setChecked(false);
|
Chris@586
|
3224 updateMenuStates();
|
Chris@586
|
3225 return;
|
Chris@586
|
3226 }
|
Chris@492
|
3227 }
|
Chris@478
|
3228
|
Chris@477
|
3229 if (m_recordTarget->isRecording()) {
|
Chris@492
|
3230 stop();
|
Chris@477
|
3231 return;
|
Chris@477
|
3232 }
|
Chris@490
|
3233
|
Chris@483
|
3234 if (m_audioRecordMode == RecordReplaceSession) {
|
Chris@490
|
3235 if (!checkSaveModified()) {
|
Chris@490
|
3236 if (action) action->setChecked(false);
|
Chris@490
|
3237 return;
|
Chris@490
|
3238 }
|
Chris@483
|
3239 }
|
Chris@487
|
3240
|
Chris@557
|
3241 if (m_viewManager) m_viewManager->setGlobalCentreFrame(0);
|
Chris@557
|
3242
|
Chris@611
|
3243 SVCERR << "MainWindowBase::record: about to resume" << endl;
|
Chris@492
|
3244 m_audioIO->resume();
|
Chris@509
|
3245
|
Chris@684
|
3246 WritableWaveFileModel *modelPtr = m_recordTarget->startRecording();
|
Chris@684
|
3247 if (!modelPtr) {
|
Chris@586
|
3248 SVCERR << "ERROR: MainWindowBase::record: Recording failed" << endl;
|
Chris@586
|
3249 QMessageBox::critical
|
Chris@586
|
3250 (this, tr("Recording failed"),
|
Chris@586
|
3251 tr("<b>Recording failed</b><p>Failed to switch to record mode (some internal problem?)</p>"));
|
Chris@490
|
3252 if (action) action->setChecked(false);
|
Chris@477
|
3253 return;
|
Chris@477
|
3254 }
|
Chris@477
|
3255
|
Chris@684
|
3256 if (!modelPtr->isOK()) {
|
Chris@611
|
3257 SVCERR << "MainWindowBase::record: Model not OK, stopping and suspending" << endl;
|
Chris@477
|
3258 m_recordTarget->stopRecording();
|
Chris@492
|
3259 m_audioIO->suspend();
|
Chris@586
|
3260 if (action) action->setChecked(false);
|
Chris@684
|
3261 delete modelPtr;
|
Chris@477
|
3262 return;
|
Chris@477
|
3263 }
|
Chris@611
|
3264
|
Chris@611
|
3265 SVCERR << "MainWindowBase::record: Model is OK, continuing..." << endl;
|
Chris@684
|
3266
|
Chris@684
|
3267 QString location = modelPtr->getLocation();
|
Chris@487
|
3268
|
Chris@687
|
3269 auto modelId = ModelById::add(std::shared_ptr<Model>(modelPtr));
|
Chris@483
|
3270
|
Chris@483
|
3271 if (m_audioRecordMode == RecordReplaceSession || !getMainModel()) {
|
Chris@478
|
3272
|
Chris@479
|
3273 //!!! duplication with openAudio here
|
Chris@479
|
3274
|
Chris@479
|
3275 QString templateName = getDefaultSessionTemplate();
|
Chris@479
|
3276 bool loadedTemplate = false;
|
Chris@479
|
3277
|
Chris@479
|
3278 if (templateName != "") {
|
Chris@479
|
3279 FileOpenStatus tplStatus = openSessionTemplate(templateName);
|
Chris@479
|
3280 if (tplStatus == FileOpenCancelled) {
|
Chris@611
|
3281 SVCERR << "MainWindowBase::record: Session template open cancelled, stopping and suspending" << endl;
|
Chris@490
|
3282 m_recordTarget->stopRecording();
|
Chris@492
|
3283 m_audioIO->suspend();
|
Chris@684
|
3284 ModelById::release(modelId);
|
Chris@479
|
3285 return;
|
Chris@479
|
3286 }
|
Chris@479
|
3287 if (tplStatus != FileOpenFailed) {
|
Chris@479
|
3288 loadedTemplate = true;
|
Chris@479
|
3289 }
|
Chris@479
|
3290 }
|
Chris@479
|
3291
|
Chris@479
|
3292 if (!loadedTemplate) {
|
Chris@479
|
3293 closeSession();
|
Chris@479
|
3294 createDocument();
|
Chris@479
|
3295 }
|
Chris@479
|
3296
|
Chris@684
|
3297 ModelId prevMain = getMainModelId();
|
Chris@684
|
3298 if (!prevMain.isNone()) {
|
Chris@479
|
3299 m_playSource->removeModel(prevMain);
|
Chris@479
|
3300 }
|
Chris@479
|
3301
|
Chris@684
|
3302 m_document->setMainModel(modelId);
|
Chris@478
|
3303 setupMenus();
|
Chris@577
|
3304 findTimeRulerLayer();
|
Chris@478
|
3305
|
Chris@684
|
3306 m_originalLocation = location;
|
Chris@669
|
3307
|
Chris@595
|
3308 if (loadedTemplate || (m_sessionFile == "")) {
|
Chris@595
|
3309 CommandHistory::getInstance()->clear();
|
Chris@595
|
3310 CommandHistory::getInstance()->documentSaved();
|
Chris@595
|
3311 }
|
Chris@479
|
3312
|
Chris@669
|
3313 m_documentModified = false;
|
Chris@669
|
3314 updateWindowTitle();
|
Chris@669
|
3315
|
Chris@478
|
3316 } else {
|
Chris@478
|
3317
|
Chris@478
|
3318 CommandHistory::getInstance()->startCompoundOperation
|
Chris@478
|
3319 (tr("Import Recorded Audio"), true);
|
Chris@478
|
3320
|
Chris@691
|
3321 m_document->addNonDerivedModel(modelId);
|
Chris@478
|
3322
|
Chris@478
|
3323 AddPaneCommand *command = new AddPaneCommand(this);
|
Chris@478
|
3324 CommandHistory::getInstance()->addCommand(command);
|
Chris@478
|
3325
|
Chris@478
|
3326 Pane *pane = command->getPane();
|
Chris@478
|
3327
|
Chris@478
|
3328 if (m_timeRulerLayer) {
|
Chris@478
|
3329 m_document->addLayerToView(pane, m_timeRulerLayer);
|
Chris@478
|
3330 }
|
Chris@478
|
3331
|
Chris@684
|
3332 Layer *newLayer = m_document->createImportedLayer(modelId);
|
Chris@478
|
3333
|
Chris@478
|
3334 if (newLayer) {
|
Chris@478
|
3335 m_document->addLayerToView(pane, newLayer);
|
Chris@478
|
3336 }
|
Chris@595
|
3337
|
Chris@478
|
3338 CommandHistory::getInstance()->endCompoundOperation();
|
Chris@477
|
3339 }
|
Chris@479
|
3340
|
Chris@479
|
3341 updateMenuStates();
|
Chris@684
|
3342 m_recentFiles.addFile(location);
|
Chris@479
|
3343 currentPaneChanged(m_paneStack->getCurrentPane());
|
Chris@611
|
3344
|
Chris@496
|
3345 emit audioFileLoaded();
|
Chris@477
|
3346 }
|
Chris@477
|
3347
|
Chris@477
|
3348 void
|
Chris@45
|
3349 MainWindowBase::ffwd()
|
Chris@45
|
3350 {
|
Chris@45
|
3351 if (!getMainModel()) return;
|
Chris@45
|
3352
|
Chris@708
|
3353 sv_frame_t playbackFrame = m_viewManager->getPlaybackFrame();
|
Chris@708
|
3354 sv_frame_t frame = playbackFrame + 1;
|
Chris@45
|
3355
|
Chris@85
|
3356 Pane *pane = m_paneStack->getCurrentPane();
|
Chris@45
|
3357 Layer *layer = getSnapLayer();
|
Chris@435
|
3358 sv_samplerate_t sr = getMainModel()->getSampleRate();
|
Chris@45
|
3359
|
Chris@708
|
3360 if (!pane || !layer) {
|
Chris@45
|
3361
|
Chris@45
|
3362 frame = RealTime::realTime2Frame
|
Chris@357
|
3363 (RealTime::frame2RealTime(frame, sr) + m_defaultFfwdRwdStep, sr);
|
Chris@435
|
3364 if (frame > getMainModel()->getEndFrame()) {
|
Chris@45
|
3365 frame = getMainModel()->getEndFrame();
|
Chris@45
|
3366 }
|
Chris@45
|
3367
|
Chris@45
|
3368 } else {
|
Chris@45
|
3369
|
Chris@708
|
3370 sv_frame_t pframe = pane->alignFromReference(frame);
|
Chris@366
|
3371 int resolution = 0;
|
Chris@708
|
3372 bool success = false;
|
Chris@708
|
3373
|
Chris@708
|
3374 while (layer->snapToFeatureFrame(pane, pframe, resolution,
|
Chris@708
|
3375 Layer::SnapRight)) {
|
Chris@708
|
3376 if (pane->alignToReference(pframe) > playbackFrame) {
|
Chris@708
|
3377 success = true;
|
Chris@708
|
3378 break;
|
Chris@708
|
3379 } else {
|
Chris@708
|
3380 ++pframe;
|
Chris@708
|
3381 }
|
Chris@708
|
3382 }
|
Chris@708
|
3383
|
Chris@708
|
3384 if (success) {
|
Chris@708
|
3385 frame = pane->alignToReference(pframe);
|
Chris@85
|
3386 } else {
|
Chris@45
|
3387 frame = getMainModel()->getEndFrame();
|
Chris@45
|
3388 }
|
Chris@45
|
3389 }
|
Chris@45
|
3390
|
Chris@45
|
3391 if (frame < 0) frame = 0;
|
Chris@45
|
3392
|
Chris@45
|
3393 if (m_viewManager->getPlaySelectionMode()) {
|
Chris@435
|
3394 frame = m_viewManager->constrainFrameToSelection(frame);
|
Chris@45
|
3395 }
|
Chris@45
|
3396
|
Chris@45
|
3397 m_viewManager->setPlaybackFrame(frame);
|
Chris@166
|
3398
|
Chris@708
|
3399 if (frame >= getMainModel()->getEndFrame() &&
|
Chris@166
|
3400 m_playSource &&
|
Chris@166
|
3401 m_playSource->isPlaying() &&
|
Chris@166
|
3402 !m_viewManager->getPlayLoopMode()) {
|
Chris@166
|
3403 stop();
|
Chris@166
|
3404 }
|
Chris@45
|
3405 }
|
Chris@45
|
3406
|
Chris@45
|
3407 void
|
Chris@45
|
3408 MainWindowBase::ffwdEnd()
|
Chris@45
|
3409 {
|
Chris@45
|
3410 if (!getMainModel()) return;
|
Chris@45
|
3411
|
Chris@139
|
3412 if (m_playSource &&
|
Chris@139
|
3413 m_playSource->isPlaying() &&
|
Chris@139
|
3414 !m_viewManager->getPlayLoopMode()) {
|
Chris@139
|
3415 stop();
|
Chris@139
|
3416 }
|
Chris@139
|
3417
|
Chris@435
|
3418 sv_frame_t frame = getMainModel()->getEndFrame();
|
Chris@45
|
3419
|
Chris@45
|
3420 if (m_viewManager->getPlaySelectionMode()) {
|
Chris@45
|
3421 frame = m_viewManager->constrainFrameToSelection(frame);
|
Chris@45
|
3422 }
|
Chris@45
|
3423
|
Chris@45
|
3424 m_viewManager->setPlaybackFrame(frame);
|
Chris@45
|
3425 }
|
Chris@45
|
3426
|
Chris@45
|
3427 void
|
Chris@166
|
3428 MainWindowBase::ffwdSimilar()
|
Chris@166
|
3429 {
|
Chris@166
|
3430 if (!getMainModel()) return;
|
Chris@166
|
3431
|
Chris@166
|
3432 Layer *layer = getSnapLayer();
|
Chris@166
|
3433 if (!layer) { ffwd(); return; }
|
Chris@166
|
3434
|
Chris@166
|
3435 Pane *pane = m_paneStack->getCurrentPane();
|
Chris@435
|
3436 sv_frame_t frame = m_viewManager->getPlaybackFrame();
|
Chris@166
|
3437
|
Chris@366
|
3438 int resolution = 0;
|
Chris@166
|
3439 if (pane) frame = pane->alignFromReference(frame);
|
Chris@166
|
3440 if (layer->snapToSimilarFeature(m_paneStack->getCurrentPane(),
|
Chris@166
|
3441 frame, resolution, Layer::SnapRight)) {
|
Chris@166
|
3442 if (pane) frame = pane->alignToReference(frame);
|
Chris@166
|
3443 } else {
|
Chris@166
|
3444 frame = getMainModel()->getEndFrame();
|
Chris@166
|
3445 }
|
Chris@166
|
3446
|
Chris@166
|
3447 if (frame < 0) frame = 0;
|
Chris@166
|
3448
|
Chris@166
|
3449 if (m_viewManager->getPlaySelectionMode()) {
|
Chris@435
|
3450 frame = m_viewManager->constrainFrameToSelection(frame);
|
Chris@166
|
3451 }
|
Chris@166
|
3452
|
Chris@166
|
3453 m_viewManager->setPlaybackFrame(frame);
|
Chris@166
|
3454
|
Chris@435
|
3455 if (frame == getMainModel()->getEndFrame() &&
|
Chris@166
|
3456 m_playSource &&
|
Chris@166
|
3457 m_playSource->isPlaying() &&
|
Chris@166
|
3458 !m_viewManager->getPlayLoopMode()) {
|
Chris@166
|
3459 stop();
|
Chris@166
|
3460 }
|
Chris@166
|
3461 }
|
Chris@166
|
3462
|
Chris@166
|
3463 void
|
Chris@45
|
3464 MainWindowBase::rewind()
|
Chris@45
|
3465 {
|
Chris@45
|
3466 if (!getMainModel()) return;
|
Chris@45
|
3467
|
Chris@708
|
3468 sv_frame_t playbackFrame = m_viewManager->getPlaybackFrame();
|
Chris@708
|
3469 sv_frame_t frame = playbackFrame;
|
Chris@45
|
3470 if (frame > 0) --frame;
|
Chris@45
|
3471
|
Chris@85
|
3472 Pane *pane = m_paneStack->getCurrentPane();
|
Chris@45
|
3473 Layer *layer = getSnapLayer();
|
Chris@435
|
3474 sv_samplerate_t sr = getMainModel()->getSampleRate();
|
Chris@45
|
3475
|
Chris@45
|
3476 // when rewinding during playback, we want to allow a period
|
Chris@45
|
3477 // following a rewind target point at which the rewind will go to
|
Chris@45
|
3478 // the prior point instead of the immediately neighbouring one
|
Chris@45
|
3479 if (m_playSource && m_playSource->isPlaying()) {
|
Chris@45
|
3480 RealTime ct = RealTime::frame2RealTime(frame, sr);
|
Chris@357
|
3481 ct = ct - RealTime::fromSeconds(0.15);
|
Chris@45
|
3482 if (ct < RealTime::zeroTime) ct = RealTime::zeroTime;
|
Chris@45
|
3483 frame = RealTime::realTime2Frame(ct, sr);
|
Chris@45
|
3484 }
|
Chris@45
|
3485
|
Chris@708
|
3486 if (!pane || !layer) {
|
Chris@45
|
3487
|
Chris@45
|
3488 frame = RealTime::realTime2Frame
|
Chris@357
|
3489 (RealTime::frame2RealTime(frame, sr) - m_defaultFfwdRwdStep, sr);
|
Chris@435
|
3490 if (frame < getMainModel()->getStartFrame()) {
|
Chris@45
|
3491 frame = getMainModel()->getStartFrame();
|
Chris@45
|
3492 }
|
Chris@45
|
3493
|
Chris@45
|
3494 } else {
|
Chris@45
|
3495
|
Chris@708
|
3496 sv_frame_t pframe = pane->alignFromReference(frame);
|
Chris@366
|
3497 int resolution = 0;
|
Chris@708
|
3498 bool success = false;
|
Chris@708
|
3499
|
Chris@708
|
3500 while (layer->snapToFeatureFrame(pane, pframe, resolution,
|
Chris@708
|
3501 Layer::SnapLeft)) {
|
Chris@708
|
3502 if (pane->alignToReference(pframe) < playbackFrame ||
|
Chris@708
|
3503 pframe <= 0) {
|
Chris@708
|
3504 success = true;
|
Chris@708
|
3505 break;
|
Chris@708
|
3506 } else {
|
Chris@708
|
3507 --pframe;
|
Chris@708
|
3508 }
|
Chris@708
|
3509 }
|
Chris@708
|
3510
|
Chris@708
|
3511 if (success) {
|
Chris@708
|
3512 frame = pane->alignToReference(pframe);
|
Chris@85
|
3513 } else {
|
Chris@45
|
3514 frame = getMainModel()->getStartFrame();
|
Chris@45
|
3515 }
|
Chris@45
|
3516 }
|
Chris@45
|
3517
|
Chris@45
|
3518 if (frame < 0) frame = 0;
|
Chris@45
|
3519
|
Chris@45
|
3520 if (m_viewManager->getPlaySelectionMode()) {
|
Chris@435
|
3521 frame = m_viewManager->constrainFrameToSelection(frame);
|
Chris@45
|
3522 }
|
Chris@45
|
3523
|
Chris@45
|
3524 m_viewManager->setPlaybackFrame(frame);
|
Chris@45
|
3525 }
|
Chris@45
|
3526
|
Chris@45
|
3527 void
|
Chris@45
|
3528 MainWindowBase::rewindStart()
|
Chris@45
|
3529 {
|
Chris@45
|
3530 if (!getMainModel()) return;
|
Chris@45
|
3531
|
Chris@435
|
3532 sv_frame_t frame = getMainModel()->getStartFrame();
|
Chris@45
|
3533
|
Chris@45
|
3534 if (m_viewManager->getPlaySelectionMode()) {
|
Chris@45
|
3535 frame = m_viewManager->constrainFrameToSelection(frame);
|
Chris@45
|
3536 }
|
Chris@45
|
3537
|
Chris@45
|
3538 m_viewManager->setPlaybackFrame(frame);
|
Chris@45
|
3539 }
|
Chris@45
|
3540
|
Chris@166
|
3541 void
|
Chris@166
|
3542 MainWindowBase::rewindSimilar()
|
Chris@166
|
3543 {
|
Chris@166
|
3544 if (!getMainModel()) return;
|
Chris@166
|
3545
|
Chris@166
|
3546 Layer *layer = getSnapLayer();
|
Chris@166
|
3547 if (!layer) { rewind(); return; }
|
Chris@166
|
3548
|
Chris@166
|
3549 Pane *pane = m_paneStack->getCurrentPane();
|
Chris@435
|
3550 sv_frame_t frame = m_viewManager->getPlaybackFrame();
|
Chris@166
|
3551
|
Chris@366
|
3552 int resolution = 0;
|
Chris@166
|
3553 if (pane) frame = pane->alignFromReference(frame);
|
Chris@166
|
3554 if (layer->snapToSimilarFeature(m_paneStack->getCurrentPane(),
|
Chris@166
|
3555 frame, resolution, Layer::SnapLeft)) {
|
Chris@166
|
3556 if (pane) frame = pane->alignToReference(frame);
|
Chris@166
|
3557 } else {
|
Chris@166
|
3558 frame = getMainModel()->getStartFrame();
|
Chris@166
|
3559 }
|
Chris@166
|
3560
|
Chris@166
|
3561 if (frame < 0) frame = 0;
|
Chris@166
|
3562
|
Chris@166
|
3563 if (m_viewManager->getPlaySelectionMode()) {
|
Chris@435
|
3564 frame = m_viewManager->constrainFrameToSelection(frame);
|
Chris@166
|
3565 }
|
Chris@166
|
3566
|
Chris@166
|
3567 m_viewManager->setPlaybackFrame(frame);
|
Chris@166
|
3568 }
|
Chris@166
|
3569
|
Chris@45
|
3570 Layer *
|
Chris@45
|
3571 MainWindowBase::getSnapLayer() const
|
Chris@45
|
3572 {
|
Chris@45
|
3573 Pane *pane = m_paneStack->getCurrentPane();
|
Chris@636
|
3574 if (!pane) return nullptr;
|
Chris@45
|
3575
|
Chris@45
|
3576 Layer *layer = pane->getSelectedLayer();
|
Chris@45
|
3577
|
Chris@45
|
3578 if (!dynamic_cast<TimeInstantLayer *>(layer) &&
|
Chris@45
|
3579 !dynamic_cast<TimeValueLayer *>(layer) &&
|
Chris@194
|
3580 !dynamic_cast<RegionLayer *>(layer) &&
|
Chris@45
|
3581 !dynamic_cast<TimeRulerLayer *>(layer)) {
|
Chris@45
|
3582
|
Chris@636
|
3583 layer = nullptr;
|
Chris@45
|
3584
|
Chris@45
|
3585 for (int i = pane->getLayerCount(); i > 0; --i) {
|
Chris@45
|
3586 Layer *l = pane->getLayer(i-1);
|
Chris@45
|
3587 if (dynamic_cast<TimeRulerLayer *>(l)) {
|
Chris@45
|
3588 layer = l;
|
Chris@45
|
3589 break;
|
Chris@45
|
3590 }
|
Chris@45
|
3591 }
|
Chris@45
|
3592 }
|
Chris@45
|
3593
|
Chris@45
|
3594 return layer;
|
Chris@45
|
3595 }
|
Chris@45
|
3596
|
Chris@45
|
3597 void
|
Chris@45
|
3598 MainWindowBase::stop()
|
Chris@45
|
3599 {
|
Chris@516
|
3600 if (m_recordTarget &&
|
Chris@516
|
3601 m_recordTarget->isRecording()) {
|
Chris@477
|
3602 m_recordTarget->stopRecording();
|
Chris@477
|
3603 }
|
Chris@516
|
3604
|
Chris@516
|
3605 if (!m_playSource) return;
|
Chris@516
|
3606
|
Chris@45
|
3607 m_playSource->stop();
|
Chris@45
|
3608
|
Chris@611
|
3609 SVCERR << "MainWindowBase::stop: suspending" << endl;
|
Chris@611
|
3610
|
Chris@487
|
3611 if (m_audioIO) m_audioIO->suspend();
|
Chris@509
|
3612 else if (m_playTarget) m_playTarget->suspend();
|
Chris@487
|
3613
|
Chris@45
|
3614 if (m_paneStack && m_paneStack->getCurrentPane()) {
|
Chris@45
|
3615 updateVisibleRangeDisplay(m_paneStack->getCurrentPane());
|
Chris@45
|
3616 } else {
|
Chris@45
|
3617 m_myStatusMessage = "";
|
Chris@378
|
3618 getStatusLabel()->setText("");
|
Chris@45
|
3619 }
|
Chris@45
|
3620 }
|
Chris@45
|
3621
|
Chris@45
|
3622 MainWindowBase::AddPaneCommand::AddPaneCommand(MainWindowBase *mw) :
|
Chris@45
|
3623 m_mw(mw),
|
Chris@636
|
3624 m_pane(nullptr),
|
Chris@636
|
3625 m_prevCurrentPane(nullptr),
|
Chris@45
|
3626 m_added(false)
|
Chris@45
|
3627 {
|
Chris@45
|
3628 }
|
Chris@45
|
3629
|
Chris@45
|
3630 MainWindowBase::AddPaneCommand::~AddPaneCommand()
|
Chris@45
|
3631 {
|
Chris@45
|
3632 if (m_pane && !m_added) {
|
Chris@595
|
3633 m_mw->m_paneStack->deletePane(m_pane);
|
Chris@45
|
3634 }
|
Chris@45
|
3635 }
|
Chris@45
|
3636
|
Chris@45
|
3637 QString
|
Chris@45
|
3638 MainWindowBase::AddPaneCommand::getName() const
|
Chris@45
|
3639 {
|
Chris@45
|
3640 return tr("Add Pane");
|
Chris@45
|
3641 }
|
Chris@45
|
3642
|
Chris@45
|
3643 void
|
Chris@45
|
3644 MainWindowBase::AddPaneCommand::execute()
|
Chris@45
|
3645 {
|
Chris@45
|
3646 if (!m_pane) {
|
Chris@595
|
3647 m_prevCurrentPane = m_mw->m_paneStack->getCurrentPane();
|
Chris@595
|
3648 m_pane = m_mw->m_paneStack->addPane();
|
Chris@45
|
3649
|
Chris@45
|
3650 connect(m_pane, SIGNAL(contextHelpChanged(const QString &)),
|
Chris@45
|
3651 m_mw, SLOT(contextHelpChanged(const QString &)));
|
Chris@45
|
3652 } else {
|
Chris@595
|
3653 m_mw->m_paneStack->showPane(m_pane);
|
Chris@45
|
3654 }
|
Chris@45
|
3655
|
Chris@45
|
3656 m_mw->m_paneStack->setCurrentPane(m_pane);
|
Chris@45
|
3657 m_added = true;
|
Chris@45
|
3658 }
|
Chris@45
|
3659
|
Chris@45
|
3660 void
|
Chris@45
|
3661 MainWindowBase::AddPaneCommand::unexecute()
|
Chris@45
|
3662 {
|
Chris@45
|
3663 m_mw->m_paneStack->hidePane(m_pane);
|
Chris@45
|
3664 m_mw->m_paneStack->setCurrentPane(m_prevCurrentPane);
|
Chris@45
|
3665 m_added = false;
|
Chris@45
|
3666 }
|
Chris@45
|
3667
|
Chris@45
|
3668 MainWindowBase::RemovePaneCommand::RemovePaneCommand(MainWindowBase *mw, Pane *pane) :
|
Chris@45
|
3669 m_mw(mw),
|
Chris@45
|
3670 m_pane(pane),
|
Chris@636
|
3671 m_prevCurrentPane(nullptr),
|
Chris@45
|
3672 m_added(true)
|
Chris@45
|
3673 {
|
Chris@45
|
3674 }
|
Chris@45
|
3675
|
Chris@45
|
3676 MainWindowBase::RemovePaneCommand::~RemovePaneCommand()
|
Chris@45
|
3677 {
|
Chris@45
|
3678 if (m_pane && !m_added) {
|
Chris@595
|
3679 m_mw->m_paneStack->deletePane(m_pane);
|
Chris@45
|
3680 }
|
Chris@45
|
3681 }
|
Chris@45
|
3682
|
Chris@45
|
3683 QString
|
Chris@45
|
3684 MainWindowBase::RemovePaneCommand::getName() const
|
Chris@45
|
3685 {
|
Chris@45
|
3686 return tr("Remove Pane");
|
Chris@45
|
3687 }
|
Chris@45
|
3688
|
Chris@45
|
3689 void
|
Chris@45
|
3690 MainWindowBase::RemovePaneCommand::execute()
|
Chris@45
|
3691 {
|
Chris@45
|
3692 m_prevCurrentPane = m_mw->m_paneStack->getCurrentPane();
|
Chris@45
|
3693 m_mw->m_paneStack->hidePane(m_pane);
|
Chris@45
|
3694 m_added = false;
|
Chris@45
|
3695 }
|
Chris@45
|
3696
|
Chris@45
|
3697 void
|
Chris@45
|
3698 MainWindowBase::RemovePaneCommand::unexecute()
|
Chris@45
|
3699 {
|
Chris@45
|
3700 m_mw->m_paneStack->showPane(m_pane);
|
Chris@45
|
3701 m_mw->m_paneStack->setCurrentPane(m_prevCurrentPane);
|
Chris@45
|
3702 m_added = true;
|
Chris@45
|
3703 }
|
Chris@45
|
3704
|
Chris@45
|
3705 void
|
Chris@45
|
3706 MainWindowBase::deleteCurrentPane()
|
Chris@45
|
3707 {
|
Chris@45
|
3708 CommandHistory::getInstance()->startCompoundOperation
|
Chris@595
|
3709 (tr("Delete Pane"), true);
|
Chris@45
|
3710
|
Chris@45
|
3711 Pane *pane = m_paneStack->getCurrentPane();
|
Chris@45
|
3712 if (pane) {
|
Chris@595
|
3713 while (pane->getLayerCount() > 0) {
|
Chris@595
|
3714 Layer *layer = pane->getLayer(0);
|
Chris@595
|
3715 if (layer) {
|
Chris@595
|
3716 m_document->removeLayerFromView(pane, layer);
|
Chris@595
|
3717 } else {
|
Chris@595
|
3718 break;
|
Chris@595
|
3719 }
|
Chris@595
|
3720 }
|
Chris@595
|
3721
|
Chris@595
|
3722 RemovePaneCommand *command = new RemovePaneCommand(this, pane);
|
Chris@595
|
3723 CommandHistory::getInstance()->addCommand(command);
|
Chris@45
|
3724 }
|
Chris@45
|
3725
|
Chris@45
|
3726 CommandHistory::getInstance()->endCompoundOperation();
|
Chris@45
|
3727
|
Chris@45
|
3728 updateMenuStates();
|
Chris@45
|
3729 }
|
Chris@45
|
3730
|
Chris@45
|
3731 void
|
Chris@45
|
3732 MainWindowBase::deleteCurrentLayer()
|
Chris@45
|
3733 {
|
Chris@45
|
3734 Pane *pane = m_paneStack->getCurrentPane();
|
Chris@45
|
3735 if (pane) {
|
Chris@595
|
3736 Layer *layer = pane->getSelectedLayer();
|
Chris@595
|
3737 if (layer) {
|
Chris@595
|
3738 m_document->removeLayerFromView(pane, layer);
|
Chris@595
|
3739 }
|
Chris@45
|
3740 }
|
Chris@45
|
3741 updateMenuStates();
|
Chris@45
|
3742 }
|
Chris@45
|
3743
|
Chris@45
|
3744 void
|
Chris@123
|
3745 MainWindowBase::editCurrentLayer()
|
Chris@123
|
3746 {
|
Chris@636
|
3747 Layer *layer = nullptr;
|
Chris@123
|
3748 Pane *pane = m_paneStack->getCurrentPane();
|
Chris@123
|
3749 if (pane) layer = pane->getSelectedLayer();
|
Chris@123
|
3750 if (!layer) return;
|
Chris@123
|
3751
|
Chris@684
|
3752 auto tabular = ModelById::getAs<TabularModel>(layer->getModel());
|
Chris@124
|
3753 if (!tabular) {
|
Chris@124
|
3754 //!!! how to prevent this function from being active if not
|
Chris@124
|
3755 //appropriate model type? or will we ultimately support
|
Chris@124
|
3756 //tabular display for all editable models?
|
Chris@233
|
3757 SVDEBUG << "NOTE: Not a tabular model" << endl;
|
Chris@124
|
3758 return;
|
Chris@124
|
3759 }
|
Chris@124
|
3760
|
Chris@123
|
3761 if (m_layerDataDialogMap.find(layer) != m_layerDataDialogMap.end()) {
|
Chris@126
|
3762 if (!m_layerDataDialogMap[layer].isNull()) {
|
Chris@126
|
3763 m_layerDataDialogMap[layer]->show();
|
Chris@126
|
3764 m_layerDataDialogMap[layer]->raise();
|
Chris@126
|
3765 return;
|
Chris@126
|
3766 }
|
Chris@123
|
3767 }
|
Chris@123
|
3768
|
Chris@125
|
3769 QString title = layer->getLayerPresentationName();
|
Chris@125
|
3770
|
Chris@684
|
3771 ModelDataTableDialog *dialog = new ModelDataTableDialog
|
Chris@684
|
3772 (layer->getModel(), title, this);
|
Chris@128
|
3773 dialog->setAttribute(Qt::WA_DeleteOnClose);
|
Chris@128
|
3774
|
Chris@128
|
3775 connectLayerEditDialog(dialog);
|
Chris@123
|
3776
|
Chris@128
|
3777 m_layerDataDialogMap[layer] = dialog;
|
Chris@128
|
3778 m_viewDataDialogMap[pane].insert(dialog);
|
Chris@128
|
3779
|
Chris@128
|
3780 dialog->show();
|
Chris@128
|
3781 }
|
Chris@128
|
3782
|
Chris@128
|
3783 void
|
Chris@128
|
3784 MainWindowBase::connectLayerEditDialog(ModelDataTableDialog *dialog)
|
Chris@128
|
3785 {
|
Chris@123
|
3786 connect(m_viewManager,
|
Chris@435
|
3787 SIGNAL(globalCentreFrameChanged(sv_frame_t)),
|
Chris@123
|
3788 dialog,
|
Chris@435
|
3789 SLOT(userScrolledToFrame(sv_frame_t)));
|
Chris@127
|
3790
|
Chris@127
|
3791 connect(m_viewManager,
|
Chris@435
|
3792 SIGNAL(playbackFrameChanged(sv_frame_t)),
|
Chris@127
|
3793 dialog,
|
Chris@435
|
3794 SLOT(playbackScrolledToFrame(sv_frame_t)));
|
Chris@127
|
3795
|
Chris@123
|
3796 connect(dialog,
|
Chris@435
|
3797 SIGNAL(scrollToFrame(sv_frame_t)),
|
Chris@123
|
3798 m_viewManager,
|
Chris@435
|
3799 SLOT(setGlobalCentreFrame(sv_frame_t)));
|
Chris@129
|
3800
|
Chris@129
|
3801 connect(dialog,
|
Chris@435
|
3802 SIGNAL(scrollToFrame(sv_frame_t)),
|
Chris@129
|
3803 m_viewManager,
|
Chris@435
|
3804 SLOT(setPlaybackFrame(sv_frame_t)));
|
Chris@128
|
3805 }
|
Chris@123
|
3806
|
Chris@123
|
3807 void
|
Chris@73
|
3808 MainWindowBase::previousPane()
|
Chris@73
|
3809 {
|
Chris@73
|
3810 if (!m_paneStack) return;
|
Chris@73
|
3811
|
Chris@73
|
3812 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@73
|
3813 if (!currentPane) return;
|
Chris@73
|
3814
|
Chris@73
|
3815 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
|
Chris@73
|
3816 if (m_paneStack->getPane(i) == currentPane) {
|
Chris@73
|
3817 if (i == 0) return;
|
Chris@73
|
3818 m_paneStack->setCurrentPane(m_paneStack->getPane(i-1));
|
Chris@73
|
3819 updateMenuStates();
|
Chris@73
|
3820 return;
|
Chris@73
|
3821 }
|
Chris@73
|
3822 }
|
Chris@73
|
3823 }
|
Chris@73
|
3824
|
Chris@73
|
3825 void
|
Chris@73
|
3826 MainWindowBase::nextPane()
|
Chris@73
|
3827 {
|
Chris@73
|
3828 if (!m_paneStack) return;
|
Chris@73
|
3829
|
Chris@73
|
3830 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@73
|
3831 if (!currentPane) return;
|
Chris@73
|
3832
|
Chris@73
|
3833 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
|
Chris@73
|
3834 if (m_paneStack->getPane(i) == currentPane) {
|
Chris@73
|
3835 if (i == m_paneStack->getPaneCount()-1) return;
|
Chris@73
|
3836 m_paneStack->setCurrentPane(m_paneStack->getPane(i+1));
|
Chris@73
|
3837 updateMenuStates();
|
Chris@73
|
3838 return;
|
Chris@73
|
3839 }
|
Chris@73
|
3840 }
|
Chris@73
|
3841 }
|
Chris@73
|
3842
|
Chris@73
|
3843 void
|
Chris@73
|
3844 MainWindowBase::previousLayer()
|
Chris@73
|
3845 {
|
Chris@73
|
3846 if (!m_paneStack) return;
|
Chris@73
|
3847
|
Chris@73
|
3848 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@73
|
3849 if (!currentPane) return;
|
Chris@73
|
3850
|
Chris@403
|
3851 int count = currentPane->getLayerCount();
|
Chris@403
|
3852 if (count == 0) return;
|
Chris@403
|
3853
|
Chris@73
|
3854 Layer *currentLayer = currentPane->getSelectedLayer();
|
Chris@403
|
3855
|
Chris@403
|
3856 if (!currentLayer) {
|
Chris@403
|
3857 // The pane itself is current
|
Chris@403
|
3858 m_paneStack->setCurrentLayer
|
Chris@403
|
3859 (currentPane, currentPane->getFixedOrderLayer(count-1));
|
Chris@403
|
3860 } else {
|
Chris@403
|
3861 for (int i = 0; i < count; ++i) {
|
Chris@403
|
3862 if (currentPane->getFixedOrderLayer(i) == currentLayer) {
|
Chris@403
|
3863 if (i == 0) {
|
Chris@403
|
3864 m_paneStack->setCurrentLayer
|
Chris@636
|
3865 (currentPane, nullptr); // pane
|
Chris@403
|
3866 } else {
|
Chris@403
|
3867 m_paneStack->setCurrentLayer
|
Chris@403
|
3868 (currentPane, currentPane->getFixedOrderLayer(i-1));
|
Chris@403
|
3869 }
|
Chris@403
|
3870 break;
|
Chris@403
|
3871 }
|
Chris@73
|
3872 }
|
Chris@73
|
3873 }
|
Chris@403
|
3874
|
Chris@403
|
3875 updateMenuStates();
|
Chris@73
|
3876 }
|
Chris@73
|
3877
|
Chris@73
|
3878 void
|
Chris@73
|
3879 MainWindowBase::nextLayer()
|
Chris@73
|
3880 {
|
Chris@73
|
3881 if (!m_paneStack) return;
|
Chris@73
|
3882
|
Chris@73
|
3883 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@73
|
3884 if (!currentPane) return;
|
Chris@73
|
3885
|
Chris@403
|
3886 int count = currentPane->getLayerCount();
|
Chris@403
|
3887 if (count == 0) return;
|
Chris@403
|
3888
|
Chris@73
|
3889 Layer *currentLayer = currentPane->getSelectedLayer();
|
Chris@403
|
3890
|
Chris@403
|
3891 if (!currentLayer) {
|
Chris@403
|
3892 // The pane itself is current
|
Chris@403
|
3893 m_paneStack->setCurrentLayer
|
Chris@403
|
3894 (currentPane, currentPane->getFixedOrderLayer(0));
|
Chris@403
|
3895 } else {
|
Chris@403
|
3896 for (int i = 0; i < count; ++i) {
|
Chris@403
|
3897 if (currentPane->getFixedOrderLayer(i) == currentLayer) {
|
Chris@403
|
3898 if (i == currentPane->getLayerCount()-1) {
|
Chris@403
|
3899 m_paneStack->setCurrentLayer
|
Chris@636
|
3900 (currentPane, nullptr); // pane
|
Chris@403
|
3901 } else {
|
Chris@403
|
3902 m_paneStack->setCurrentLayer
|
Chris@403
|
3903 (currentPane, currentPane->getFixedOrderLayer(i+1));
|
Chris@403
|
3904 }
|
Chris@403
|
3905 break;
|
Chris@403
|
3906 }
|
Chris@73
|
3907 }
|
Chris@73
|
3908 }
|
Chris@403
|
3909
|
Chris@403
|
3910 updateMenuStates();
|
Chris@73
|
3911 }
|
Chris@73
|
3912
|
Chris@73
|
3913 void
|
Chris@435
|
3914 MainWindowBase::playbackFrameChanged(sv_frame_t frame)
|
Chris@45
|
3915 {
|
Chris@45
|
3916 if (!(m_playSource && m_playSource->isPlaying()) || !getMainModel()) return;
|
Chris@45
|
3917
|
Chris@187
|
3918 updatePositionStatusDisplays();
|
Chris@187
|
3919
|
Chris@45
|
3920 RealTime now = RealTime::frame2RealTime
|
Chris@45
|
3921 (frame, getMainModel()->getSampleRate());
|
Chris@45
|
3922
|
Chris@45
|
3923 if (now.sec == m_lastPlayStatusSec) return;
|
Chris@45
|
3924
|
Chris@45
|
3925 RealTime then = RealTime::frame2RealTime
|
Chris@45
|
3926 (m_playSource->getPlayEndFrame(), getMainModel()->getSampleRate());
|
Chris@45
|
3927
|
Chris@45
|
3928 QString nowStr;
|
Chris@45
|
3929 QString thenStr;
|
Chris@45
|
3930 QString remainingStr;
|
Chris@45
|
3931
|
Chris@45
|
3932 if (then.sec > 10) {
|
Chris@45
|
3933 nowStr = now.toSecText().c_str();
|
Chris@45
|
3934 thenStr = then.toSecText().c_str();
|
Chris@45
|
3935 remainingStr = (then - now).toSecText().c_str();
|
Chris@45
|
3936 m_lastPlayStatusSec = now.sec;
|
Chris@45
|
3937 } else {
|
Chris@45
|
3938 nowStr = now.toText(true).c_str();
|
Chris@45
|
3939 thenStr = then.toText(true).c_str();
|
Chris@45
|
3940 remainingStr = (then - now).toText(true).c_str();
|
Chris@45
|
3941 }
|
Chris@45
|
3942
|
Chris@45
|
3943 m_myStatusMessage = tr("Playing: %1 of %2 (%3 remaining)")
|
Chris@45
|
3944 .arg(nowStr).arg(thenStr).arg(remainingStr);
|
Chris@45
|
3945
|
Chris@378
|
3946 getStatusLabel()->setText(m_myStatusMessage);
|
Chris@45
|
3947 }
|
Chris@45
|
3948
|
Chris@45
|
3949 void
|
Chris@486
|
3950 MainWindowBase::recordDurationChanged(sv_frame_t frame, sv_samplerate_t rate)
|
Chris@486
|
3951 {
|
Chris@486
|
3952 RealTime duration = RealTime::frame2RealTime(frame, rate);
|
Chris@486
|
3953 QString durStr = duration.toSecText().c_str();
|
Chris@486
|
3954
|
Chris@486
|
3955 m_myStatusMessage = tr("Recording: %1").arg(durStr);
|
Chris@486
|
3956
|
Chris@486
|
3957 getStatusLabel()->setText(m_myStatusMessage);
|
Chris@486
|
3958 }
|
Chris@486
|
3959
|
Chris@486
|
3960 void
|
Chris@435
|
3961 MainWindowBase::globalCentreFrameChanged(sv_frame_t )
|
Chris@45
|
3962 {
|
Chris@45
|
3963 if ((m_playSource && m_playSource->isPlaying()) || !getMainModel()) return;
|
Chris@636
|
3964 Pane *p = nullptr;
|
Chris@45
|
3965 if (!m_paneStack || !(p = m_paneStack->getCurrentPane())) return;
|
Chris@45
|
3966 if (!p->getFollowGlobalPan()) return;
|
Chris@45
|
3967 updateVisibleRangeDisplay(p);
|
Chris@45
|
3968 }
|
Chris@45
|
3969
|
Chris@45
|
3970 void
|
Chris@435
|
3971 MainWindowBase::viewCentreFrameChanged(View *v, sv_frame_t frame)
|
Chris@45
|
3972 {
|
Chris@233
|
3973 // SVDEBUG << "MainWindowBase::viewCentreFrameChanged(" << v << "," << frame << ")" << endl;
|
Chris@123
|
3974
|
Chris@123
|
3975 if (m_viewDataDialogMap.find(v) != m_viewDataDialogMap.end()) {
|
Chris@123
|
3976 for (DataDialogSet::iterator i = m_viewDataDialogMap[v].begin();
|
Chris@123
|
3977 i != m_viewDataDialogMap[v].end(); ++i) {
|
Chris@127
|
3978 (*i)->userScrolledToFrame(frame);
|
Chris@123
|
3979 }
|
Chris@123
|
3980 }
|
Chris@45
|
3981 if ((m_playSource && m_playSource->isPlaying()) || !getMainModel()) return;
|
Chris@636
|
3982 Pane *p = nullptr;
|
Chris@45
|
3983 if (!m_paneStack || !(p = m_paneStack->getCurrentPane())) return;
|
Chris@45
|
3984 if (v == p) updateVisibleRangeDisplay(p);
|
Chris@45
|
3985 }
|
Chris@45
|
3986
|
Chris@45
|
3987 void
|
Chris@624
|
3988 MainWindowBase::viewZoomLevelChanged(View *v, ZoomLevel, bool )
|
Chris@45
|
3989 {
|
Chris@45
|
3990 if ((m_playSource && m_playSource->isPlaying()) || !getMainModel()) return;
|
Chris@636
|
3991 Pane *p = nullptr;
|
Chris@45
|
3992 if (!m_paneStack || !(p = m_paneStack->getCurrentPane())) return;
|
Chris@45
|
3993 if (v == p) updateVisibleRangeDisplay(p);
|
Chris@45
|
3994 }
|
Chris@45
|
3995
|
Chris@45
|
3996 void
|
Chris@45
|
3997 MainWindowBase::layerAdded(Layer *)
|
Chris@45
|
3998 {
|
Chris@233
|
3999 // SVDEBUG << "MainWindowBase::layerAdded(" << layer << ")" << endl;
|
Chris@45
|
4000 updateMenuStates();
|
Chris@45
|
4001 }
|
Chris@45
|
4002
|
Chris@45
|
4003 void
|
Chris@45
|
4004 MainWindowBase::layerRemoved(Layer *)
|
Chris@45
|
4005 {
|
Chris@233
|
4006 // SVDEBUG << "MainWindowBase::layerRemoved(" << layer << ")" << endl;
|
Chris@45
|
4007 updateMenuStates();
|
Chris@45
|
4008 }
|
Chris@45
|
4009
|
Chris@45
|
4010 void
|
Chris@45
|
4011 MainWindowBase::layerAboutToBeDeleted(Layer *layer)
|
Chris@45
|
4012 {
|
Chris@233
|
4013 // SVDEBUG << "MainWindowBase::layerAboutToBeDeleted(" << layer << ")" << endl;
|
Chris@123
|
4014
|
Chris@128
|
4015 removeLayerEditDialog(layer);
|
Chris@123
|
4016
|
Chris@47
|
4017 if (m_timeRulerLayer && (layer == m_timeRulerLayer)) {
|
Chris@595
|
4018 // cerr << "(this is the time ruler layer)" << endl;
|
Chris@636
|
4019 m_timeRulerLayer = nullptr;
|
Chris@45
|
4020 }
|
Chris@45
|
4021 }
|
Chris@45
|
4022
|
Chris@45
|
4023 void
|
Chris@45
|
4024 MainWindowBase::layerInAView(Layer *layer, bool inAView)
|
Chris@45
|
4025 {
|
Chris@233
|
4026 // SVDEBUG << "MainWindowBase::layerInAView(" << layer << "," << inAView << ")" << endl;
|
Chris@128
|
4027
|
Chris@128
|
4028 if (!inAView) removeLayerEditDialog(layer);
|
Chris@45
|
4029
|
Chris@45
|
4030 // Check whether we need to add or remove model from play source
|
Chris@684
|
4031 ModelId modelId = layer->getModel();
|
Chris@684
|
4032 if (!modelId.isNone()) {
|
Chris@45
|
4033 if (inAView) {
|
Chris@684
|
4034 m_playSource->addModel(modelId);
|
Chris@45
|
4035 } else {
|
Chris@45
|
4036 bool found = false;
|
Chris@45
|
4037 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
|
Chris@45
|
4038 Pane *pane = m_paneStack->getPane(i);
|
Chris@45
|
4039 if (!pane) continue;
|
Chris@45
|
4040 for (int j = 0; j < pane->getLayerCount(); ++j) {
|
Chris@45
|
4041 Layer *pl = pane->getLayer(j);
|
Chris@183
|
4042 if (pl &&
|
Chris@183
|
4043 !dynamic_cast<TimeRulerLayer *>(pl) &&
|
Chris@684
|
4044 (pl->getModel() == modelId)) {
|
Chris@45
|
4045 found = true;
|
Chris@45
|
4046 break;
|
Chris@45
|
4047 }
|
Chris@45
|
4048 }
|
Chris@45
|
4049 if (found) break;
|
Chris@45
|
4050 }
|
Chris@173
|
4051 if (!found) {
|
Chris@684
|
4052 m_playSource->removeModel(modelId);
|
Chris@173
|
4053 }
|
Chris@45
|
4054 }
|
Chris@45
|
4055 }
|
Chris@45
|
4056
|
Chris@45
|
4057 updateMenuStates();
|
Chris@45
|
4058 }
|
Chris@45
|
4059
|
Chris@45
|
4060 void
|
Chris@128
|
4061 MainWindowBase::removeLayerEditDialog(Layer *layer)
|
Chris@128
|
4062 {
|
Chris@128
|
4063 if (m_layerDataDialogMap.find(layer) != m_layerDataDialogMap.end()) {
|
Chris@128
|
4064
|
Chris@128
|
4065 ModelDataTableDialog *dialog = m_layerDataDialogMap[layer];
|
Chris@128
|
4066
|
Chris@128
|
4067 for (ViewDataDialogMap::iterator vi = m_viewDataDialogMap.begin();
|
Chris@128
|
4068 vi != m_viewDataDialogMap.end(); ++vi) {
|
Chris@128
|
4069 vi->second.erase(dialog);
|
Chris@128
|
4070 }
|
Chris@128
|
4071
|
Chris@128
|
4072 m_layerDataDialogMap.erase(layer);
|
Chris@128
|
4073 delete dialog;
|
Chris@128
|
4074 }
|
Chris@128
|
4075 }
|
Chris@128
|
4076
|
Chris@128
|
4077 void
|
Chris@684
|
4078 MainWindowBase::modelAdded(ModelId model)
|
Chris@45
|
4079 {
|
Chris@233
|
4080 // SVDEBUG << "MainWindowBase::modelAdded(" << model << ")" << endl;
|
Chris@684
|
4081 std::cerr << "\nAdding model " << model << " to playsource " << std::endl;
|
Chris@45
|
4082 m_playSource->addModel(model);
|
Chris@45
|
4083 }
|
Chris@45
|
4084
|
Chris@45
|
4085 void
|
Chris@684
|
4086 MainWindowBase::mainModelChanged(ModelId modelId)
|
Chris@45
|
4087 {
|
Chris@233
|
4088 // SVDEBUG << "MainWindowBase::mainModelChanged(" << model << ")" << endl;
|
Chris@45
|
4089 updateDescriptionLabel();
|
Chris@684
|
4090 auto model = ModelById::getAs<WaveFileModel>(modelId);
|
Chris@45
|
4091 if (model) m_viewManager->setMainModelSampleRate(model->getSampleRate());
|
Chris@475
|
4092 if (model && !(m_playTarget || m_audioIO) &&
|
Chris@475
|
4093 (m_soundOptions & WithAudioOutput)) {
|
Chris@475
|
4094 createAudioIO();
|
Chris@360
|
4095 }
|
Chris@45
|
4096 }
|
Chris@45
|
4097
|
Chris@45
|
4098 void
|
Chris@55
|
4099 MainWindowBase::paneDeleteButtonClicked(Pane *pane)
|
Chris@55
|
4100 {
|
Chris@55
|
4101 bool found = false;
|
Chris@55
|
4102 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
|
Chris@55
|
4103 if (m_paneStack->getPane(i) == pane) {
|
Chris@55
|
4104 found = true;
|
Chris@55
|
4105 break;
|
Chris@55
|
4106 }
|
Chris@55
|
4107 }
|
Chris@55
|
4108 if (!found) {
|
Chris@233
|
4109 SVDEBUG << "MainWindowBase::paneDeleteButtonClicked: Unknown pane "
|
Chris@229
|
4110 << pane << endl;
|
Chris@55
|
4111 return;
|
Chris@55
|
4112 }
|
Chris@55
|
4113
|
Chris@55
|
4114 CommandHistory::getInstance()->startCompoundOperation
|
Chris@595
|
4115 (tr("Delete Pane"), true);
|
Chris@55
|
4116
|
Chris@55
|
4117 while (pane->getLayerCount() > 0) {
|
Chris@637
|
4118 Layer *layer = pane->getLayer(pane->getLayerCount() - 1);
|
Chris@55
|
4119 if (layer) {
|
Chris@55
|
4120 m_document->removeLayerFromView(pane, layer);
|
Chris@55
|
4121 } else {
|
Chris@55
|
4122 break;
|
Chris@55
|
4123 }
|
Chris@55
|
4124 }
|
Chris@55
|
4125
|
Chris@55
|
4126 RemovePaneCommand *command = new RemovePaneCommand(this, pane);
|
Chris@55
|
4127 CommandHistory::getInstance()->addCommand(command);
|
Chris@55
|
4128
|
Chris@55
|
4129 CommandHistory::getInstance()->endCompoundOperation();
|
Chris@55
|
4130
|
Chris@55
|
4131 updateMenuStates();
|
Chris@55
|
4132 }
|
Chris@55
|
4133
|
Chris@55
|
4134 void
|
Chris@684
|
4135 MainWindowBase::alignmentComplete(ModelId alignmentModelId)
|
Chris@429
|
4136 {
|
Chris@684
|
4137 cerr << "MainWindowBase::alignmentComplete(" << alignmentModelId << ")" << endl;
|
Chris@429
|
4138 }
|
Chris@429
|
4139
|
Chris@429
|
4140 void
|
Chris@45
|
4141 MainWindowBase::pollOSC()
|
Chris@45
|
4142 {
|
Chris@45
|
4143 if (!m_oscQueue || m_oscQueue->isEmpty()) return;
|
Chris@233
|
4144 SVDEBUG << "MainWindowBase::pollOSC: have " << m_oscQueue->getMessagesAvailable() << " messages" << endl;
|
Chris@45
|
4145
|
Chris@45
|
4146 if (m_openingAudioFile) return;
|
Chris@45
|
4147
|
Chris@45
|
4148 OSCMessage message = m_oscQueue->readMessage();
|
Chris@45
|
4149
|
Chris@45
|
4150 if (message.getTarget() != 0) {
|
Chris@45
|
4151 return; //!!! for now -- this class is target 0, others not handled yet
|
Chris@45
|
4152 }
|
Chris@45
|
4153
|
Chris@45
|
4154 handleOSCMessage(message);
|
Chris@45
|
4155 }
|
Chris@45
|
4156
|
Chris@45
|
4157 void
|
Chris@45
|
4158 MainWindowBase::inProgressSelectionChanged()
|
Chris@45
|
4159 {
|
Chris@636
|
4160 Pane *currentPane = nullptr;
|
Chris@45
|
4161 if (m_paneStack) currentPane = m_paneStack->getCurrentPane();
|
justin@331
|
4162 if (currentPane) {
|
justin@331
|
4163 //cerr << "JTEST: mouse event on selection pane" << endl;
|
justin@331
|
4164 updateVisibleRangeDisplay(currentPane);
|
justin@331
|
4165 }
|
Chris@45
|
4166 }
|
Chris@45
|
4167
|
Chris@45
|
4168 void
|
Chris@45
|
4169 MainWindowBase::contextHelpChanged(const QString &s)
|
Chris@45
|
4170 {
|
Chris@378
|
4171 QLabel *lab = getStatusLabel();
|
Chris@375
|
4172
|
Chris@45
|
4173 if (s == "" && m_myStatusMessage != "") {
|
Chris@378
|
4174 if (lab->text() != m_myStatusMessage) {
|
Chris@378
|
4175 lab->setText(m_myStatusMessage);
|
Chris@375
|
4176 }
|
Chris@45
|
4177 return;
|
Chris@45
|
4178 }
|
Chris@375
|
4179
|
Chris@378
|
4180 lab->setText(s);
|
Chris@45
|
4181 }
|
Chris@45
|
4182
|
Chris@45
|
4183 void
|
Chris@45
|
4184 MainWindowBase::openHelpUrl(QString url)
|
Chris@45
|
4185 {
|
Chris@45
|
4186 // This method mostly lifted from Qt Assistant source code
|
Chris@45
|
4187
|
Chris@45
|
4188 QProcess *process = new QProcess(this);
|
Chris@45
|
4189 connect(process, SIGNAL(finished(int)), process, SLOT(deleteLater()));
|
Chris@45
|
4190
|
Chris@45
|
4191 QStringList args;
|
Chris@45
|
4192
|
Chris@45
|
4193 #ifdef Q_OS_MAC
|
Chris@45
|
4194 args.append(url);
|
Chris@45
|
4195 process->start("open", args);
|
Chris@45
|
4196 #else
|
Chris@45
|
4197 #ifdef Q_OS_WIN32
|
Chris@599
|
4198 std::string pfiles;
|
Chris@599
|
4199 (void)getEnvUtf8("ProgramFiles", pfiles);
|
Chris@599
|
4200 QString command =
|
Chris@599
|
4201 QString::fromStdString(pfiles) +
|
Chris@599
|
4202 QString("\\Internet Explorer\\IEXPLORE.EXE");
|
Chris@358
|
4203
|
Chris@358
|
4204 args.append(url);
|
Chris@358
|
4205 process->start(command, args);
|
Chris@45
|
4206 #else
|
Chris@45
|
4207 if (!qgetenv("KDE_FULL_SESSION").isEmpty()) {
|
Chris@45
|
4208 args.append("exec");
|
Chris@45
|
4209 args.append(url);
|
Chris@45
|
4210 process->start("kfmclient", args);
|
Chris@45
|
4211 } else if (!qgetenv("BROWSER").isEmpty()) {
|
Chris@45
|
4212 args.append(url);
|
Chris@45
|
4213 process->start(qgetenv("BROWSER"), args);
|
Chris@45
|
4214 } else {
|
Chris@45
|
4215 args.append(url);
|
Chris@45
|
4216 process->start("firefox", args);
|
Chris@45
|
4217 }
|
Chris@45
|
4218 #endif
|
Chris@45
|
4219 #endif
|
Chris@45
|
4220 }
|
Chris@45
|
4221
|
Chris@483
|
4222 void
|
Chris@483
|
4223 MainWindowBase::openLocalFolder(QString path)
|
Chris@483
|
4224 {
|
Chris@483
|
4225 QDir d(path);
|
Chris@483
|
4226 if (d.exists()) {
|
Chris@483
|
4227 QStringList args;
|
Chris@483
|
4228 QString path = d.canonicalPath();
|
Chris@483
|
4229 #if defined Q_OS_WIN32
|
Chris@483
|
4230 // Although the Win32 API is quite happy to have
|
Chris@483
|
4231 // forward slashes as directory separators, Windows
|
Chris@483
|
4232 // Explorer is not
|
Chris@483
|
4233 path = path.replace('/', '\\');
|
Chris@483
|
4234 args << path;
|
Chris@483
|
4235 QProcess::execute("c:/windows/explorer.exe", args);
|
Chris@483
|
4236 #else
|
Chris@483
|
4237 args << path;
|
Chris@605
|
4238 QProcess process;
|
Chris@605
|
4239 QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
|
Chris@606
|
4240 env.insert("LD_LIBRARY_PATH", "");
|
Chris@605
|
4241 process.setProcessEnvironment(env);
|
Chris@605
|
4242 process.start(
|
Chris@483
|
4243 #if defined Q_OS_MAC
|
Chris@483
|
4244 "/usr/bin/open",
|
Chris@483
|
4245 #else
|
Chris@483
|
4246 "/usr/bin/xdg-open",
|
Chris@483
|
4247 #endif
|
Chris@483
|
4248 args);
|
Chris@608
|
4249 process.waitForFinished();
|
Chris@483
|
4250 #endif
|
Chris@483
|
4251 }
|
Chris@483
|
4252 }
|
Chris@483
|
4253
|