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