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@667
|
2683 m_document->setAutoAlignment(m_viewManager->getAlignMode());
|
Chris@667
|
2684
|
Chris@160
|
2685 emit replacedDocument();
|
Chris@45
|
2686 }
|
Chris@45
|
2687
|
Chris@45
|
2688 bool
|
Chris@45
|
2689 MainWindowBase::saveSessionFile(QString path)
|
Chris@45
|
2690 {
|
Chris@217
|
2691 try {
|
Chris@217
|
2692
|
Chris@217
|
2693 TempWriteFile temp(path);
|
Chris@217
|
2694
|
Chris@217
|
2695 BZipFileDevice bzFile(temp.getTemporaryFilename());
|
Chris@217
|
2696 if (!bzFile.open(QIODevice::WriteOnly)) {
|
Chris@293
|
2697 cerr << "Failed to open session file \""
|
Chris@294
|
2698 << temp.getTemporaryFilename()
|
Chris@217
|
2699 << "\" for writing: "
|
Chris@293
|
2700 << bzFile.errorString() << endl;
|
Chris@217
|
2701 return false;
|
Chris@217
|
2702 }
|
Chris@217
|
2703
|
Chris@217
|
2704 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
|
Chris@217
|
2705
|
Chris@217
|
2706 QTextStream out(&bzFile);
|
Chris@432
|
2707 out.setCodec(QTextCodec::codecForName("UTF-8"));
|
Chris@226
|
2708 toXml(out, false);
|
Chris@217
|
2709 out.flush();
|
Chris@217
|
2710
|
Chris@217
|
2711 QApplication::restoreOverrideCursor();
|
Chris@217
|
2712
|
Chris@217
|
2713 if (!bzFile.isOK()) {
|
Chris@217
|
2714 QMessageBox::critical(this, tr("Failed to write file"),
|
Chris@217
|
2715 tr("<b>Save failed</b><p>Failed to write to file \"%1\": %2")
|
Chris@217
|
2716 .arg(path).arg(bzFile.errorString()));
|
Chris@217
|
2717 bzFile.close();
|
Chris@217
|
2718 return false;
|
Chris@217
|
2719 }
|
Chris@217
|
2720
|
Chris@217
|
2721 bzFile.close();
|
Chris@217
|
2722 temp.moveToTarget();
|
Chris@217
|
2723 return true;
|
Chris@217
|
2724
|
Chris@217
|
2725 } catch (FileOperationFailed &f) {
|
Chris@217
|
2726
|
Chris@217
|
2727 QMessageBox::critical(this, tr("Failed to write file"),
|
Chris@217
|
2728 tr("<b>Save failed</b><p>Failed to write to file \"%1\": %2")
|
Chris@217
|
2729 .arg(path).arg(f.what()));
|
Chris@45
|
2730 return false;
|
Chris@45
|
2731 }
|
Chris@45
|
2732 }
|
Chris@45
|
2733
|
Chris@224
|
2734 bool
|
Chris@224
|
2735 MainWindowBase::saveSessionTemplate(QString path)
|
Chris@224
|
2736 {
|
Chris@224
|
2737 try {
|
Chris@224
|
2738
|
Chris@224
|
2739 TempWriteFile temp(path);
|
Chris@224
|
2740
|
Chris@224
|
2741 QFile file(temp.getTemporaryFilename());
|
Chris@224
|
2742 if (!file.open(QIODevice::WriteOnly)) {
|
Chris@293
|
2743 cerr << "Failed to open session template file \""
|
Chris@294
|
2744 << temp.getTemporaryFilename()
|
Chris@224
|
2745 << "\" for writing: "
|
Chris@294
|
2746 << file.errorString() << endl;
|
Chris@224
|
2747 return false;
|
Chris@224
|
2748 }
|
Chris@224
|
2749
|
Chris@224
|
2750 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor));
|
Chris@224
|
2751
|
Chris@224
|
2752 QTextStream out(&file);
|
Chris@432
|
2753 out.setCodec(QTextCodec::codecForName("UTF-8"));
|
Chris@226
|
2754 toXml(out, true);
|
Chris@224
|
2755 out.flush();
|
Chris@224
|
2756
|
Chris@224
|
2757 QApplication::restoreOverrideCursor();
|
Chris@224
|
2758
|
Chris@224
|
2759 file.close();
|
Chris@224
|
2760 temp.moveToTarget();
|
Chris@224
|
2761 return true;
|
Chris@224
|
2762
|
Chris@224
|
2763 } catch (FileOperationFailed &f) {
|
Chris@224
|
2764
|
Chris@224
|
2765 QMessageBox::critical(this, tr("Failed to write file"),
|
Chris@224
|
2766 tr("<b>Save failed</b><p>Failed to write to file \"%1\": %2")
|
Chris@224
|
2767 .arg(path).arg(f.what()));
|
Chris@224
|
2768 return false;
|
Chris@224
|
2769 }
|
Chris@224
|
2770 }
|
Chris@224
|
2771
|
Chris@659
|
2772 bool
|
Chris@659
|
2773 MainWindowBase::exportLayerTo(Layer *layer, QString path, QString &error)
|
Chris@659
|
2774 {
|
Chris@659
|
2775 if (QFileInfo(path).suffix() == "") path += ".svl";
|
Chris@659
|
2776
|
Chris@659
|
2777 QString suffix = QFileInfo(path).suffix().toLower();
|
Chris@659
|
2778
|
Chris@659
|
2779 Model *model = layer->getModel();
|
Chris@659
|
2780
|
Chris@659
|
2781 if (suffix == "xml" || suffix == "svl") {
|
Chris@659
|
2782
|
Chris@659
|
2783 QFile file(path);
|
Chris@659
|
2784 if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) {
|
Chris@659
|
2785 error = tr("Failed to open file %1 for writing").arg(path);
|
Chris@659
|
2786 } else {
|
Chris@659
|
2787 QTextStream out(&file);
|
Chris@659
|
2788 out.setCodec(QTextCodec::codecForName("UTF-8"));
|
Chris@659
|
2789 out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
|
Chris@659
|
2790 << "<!DOCTYPE sonic-visualiser>\n"
|
Chris@659
|
2791 << "<sv>\n"
|
Chris@659
|
2792 << " <data>\n";
|
Chris@659
|
2793
|
Chris@659
|
2794 model->toXml(out, " ");
|
Chris@659
|
2795
|
Chris@659
|
2796 out << " </data>\n"
|
Chris@659
|
2797 << " <display>\n";
|
Chris@659
|
2798
|
Chris@659
|
2799 layer->toXml(out, " ");
|
Chris@659
|
2800
|
Chris@659
|
2801 out << " </display>\n"
|
Chris@659
|
2802 << "</sv>\n";
|
Chris@659
|
2803 }
|
Chris@659
|
2804
|
Chris@659
|
2805 } else if (suffix == "mid" || suffix == "midi") {
|
Chris@659
|
2806
|
Chris@659
|
2807 NoteModel *nm = dynamic_cast<NoteModel *>(model);
|
Chris@659
|
2808
|
Chris@659
|
2809 if (!nm) {
|
Chris@659
|
2810 error = tr("Can't export non-note layers to MIDI");
|
Chris@659
|
2811 } else {
|
Chris@659
|
2812 MIDIFileWriter writer(path, nm, nm->getSampleRate());
|
Chris@659
|
2813 writer.write();
|
Chris@659
|
2814 if (!writer.isOK()) {
|
Chris@659
|
2815 error = writer.getError();
|
Chris@659
|
2816 }
|
Chris@659
|
2817 }
|
Chris@659
|
2818
|
Chris@659
|
2819 } else if (suffix == "ttl" || suffix == "n3") {
|
Chris@659
|
2820
|
Chris@659
|
2821 if (!RDFExporter::canExportModel(model)) {
|
Chris@659
|
2822 error = tr("Sorry, cannot export this layer type to RDF (supported types are: region, note, text, time instants, time values)");
|
Chris@659
|
2823 } else {
|
Chris@659
|
2824 RDFExporter exporter(path, model);
|
Chris@659
|
2825 exporter.write();
|
Chris@659
|
2826 if (!exporter.isOK()) {
|
Chris@659
|
2827 error = exporter.getError();
|
Chris@659
|
2828 }
|
Chris@659
|
2829 }
|
Chris@659
|
2830
|
Chris@659
|
2831 } else {
|
Chris@659
|
2832
|
Chris@659
|
2833 CSVFileWriter writer(path, model,
|
Chris@659
|
2834 ((suffix == "csv") ? "," : "\t"));
|
Chris@659
|
2835 writer.write();
|
Chris@659
|
2836
|
Chris@659
|
2837 if (!writer.isOK()) {
|
Chris@659
|
2838 error = writer.getError();
|
Chris@659
|
2839 }
|
Chris@659
|
2840 }
|
Chris@659
|
2841
|
Chris@659
|
2842 return (error == "");
|
Chris@659
|
2843 }
|
Chris@659
|
2844
|
Chris@45
|
2845 void
|
Chris@226
|
2846 MainWindowBase::toXml(QTextStream &out, bool asTemplate)
|
Chris@45
|
2847 {
|
Chris@45
|
2848 QString indent(" ");
|
Chris@45
|
2849
|
Chris@45
|
2850 out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n";
|
Chris@45
|
2851 out << "<!DOCTYPE sonic-visualiser>\n";
|
Chris@45
|
2852 out << "<sv>\n";
|
Chris@45
|
2853
|
Chris@226
|
2854 if (asTemplate) {
|
Chris@226
|
2855 m_document->toXmlAsTemplate(out, "", "");
|
Chris@226
|
2856 } else {
|
Chris@226
|
2857 m_document->toXml(out, "", "");
|
Chris@226
|
2858 }
|
Chris@45
|
2859
|
Chris@45
|
2860 out << "<display>\n";
|
Chris@45
|
2861
|
Chris@45
|
2862 out << QString(" <window width=\"%1\" height=\"%2\"/>\n")
|
Chris@595
|
2863 .arg(width()).arg(height());
|
Chris@45
|
2864
|
Chris@45
|
2865 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
|
Chris@45
|
2866
|
Chris@595
|
2867 Pane *pane = m_paneStack->getPane(i);
|
Chris@595
|
2868
|
Chris@595
|
2869 if (pane) {
|
Chris@45
|
2870 pane->toXml(out, indent);
|
Chris@595
|
2871 }
|
Chris@45
|
2872 }
|
Chris@45
|
2873
|
Chris@45
|
2874 out << "</display>\n";
|
Chris@45
|
2875
|
Chris@45
|
2876 m_viewManager->getSelection().toXml(out);
|
Chris@45
|
2877
|
Chris@45
|
2878 out << "</sv>\n";
|
Chris@45
|
2879 }
|
Chris@45
|
2880
|
Chris@45
|
2881 Pane *
|
Chris@45
|
2882 MainWindowBase::addPaneToStack()
|
Chris@45
|
2883 {
|
Chris@342
|
2884 cerr << "MainWindowBase::addPaneToStack()" << endl;
|
Chris@45
|
2885 AddPaneCommand *command = new AddPaneCommand(this);
|
Chris@45
|
2886 CommandHistory::getInstance()->addCommand(command);
|
Chris@57
|
2887 Pane *pane = command->getPane();
|
Chris@57
|
2888 return pane;
|
Chris@45
|
2889 }
|
Chris@45
|
2890
|
Chris@45
|
2891 void
|
Chris@45
|
2892 MainWindowBase::zoomIn()
|
Chris@45
|
2893 {
|
Chris@45
|
2894 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@45
|
2895 if (currentPane) currentPane->zoom(true);
|
Chris@45
|
2896 }
|
Chris@45
|
2897
|
Chris@45
|
2898 void
|
Chris@45
|
2899 MainWindowBase::zoomOut()
|
Chris@45
|
2900 {
|
Chris@45
|
2901 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@45
|
2902 if (currentPane) currentPane->zoom(false);
|
Chris@45
|
2903 }
|
Chris@45
|
2904
|
Chris@45
|
2905 void
|
Chris@45
|
2906 MainWindowBase::zoomToFit()
|
Chris@45
|
2907 {
|
Chris@45
|
2908 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@45
|
2909 if (!currentPane) return;
|
Chris@45
|
2910
|
Chris@45
|
2911 Model *model = getMainModel();
|
Chris@45
|
2912 if (!model) return;
|
Chris@45
|
2913
|
Chris@434
|
2914 sv_frame_t start = model->getStartFrame();
|
Chris@434
|
2915 sv_frame_t end = model->getEndFrame();
|
Chris@60
|
2916 if (m_playSource) end = std::max(end, m_playSource->getPlayEndFrame());
|
Chris@366
|
2917 int pixels = currentPane->width();
|
Chris@366
|
2918
|
Chris@366
|
2919 int sw = currentPane->getVerticalScaleWidth();
|
Chris@45
|
2920 if (pixels > sw * 2) pixels -= sw * 2;
|
Chris@45
|
2921 else pixels = 1;
|
Chris@45
|
2922 if (pixels > 4) pixels -= 4;
|
Chris@45
|
2923
|
Chris@624
|
2924 ZoomLevel zoomLevel = ZoomLevel::fromRatio(pixels, end - start);
|
Chris@45
|
2925 currentPane->setZoomLevel(zoomLevel);
|
Chris@45
|
2926 currentPane->setCentreFrame((start + end) / 2);
|
Chris@45
|
2927 }
|
Chris@45
|
2928
|
Chris@45
|
2929 void
|
Chris@45
|
2930 MainWindowBase::zoomDefault()
|
Chris@45
|
2931 {
|
Chris@45
|
2932 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@302
|
2933 QSettings settings;
|
Chris@302
|
2934 settings.beginGroup("MainWindow");
|
Chris@302
|
2935 int zoom = settings.value("zoom-default", 1024).toInt();
|
Chris@302
|
2936 settings.endGroup();
|
Chris@624
|
2937 if (currentPane) {
|
Chris@624
|
2938 currentPane->setZoomLevel(ZoomLevel(ZoomLevel::FramesPerPixel, zoom));
|
Chris@624
|
2939 }
|
Chris@45
|
2940 }
|
Chris@45
|
2941
|
Chris@45
|
2942 void
|
Chris@45
|
2943 MainWindowBase::scrollLeft()
|
Chris@45
|
2944 {
|
Chris@45
|
2945 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@45
|
2946 if (currentPane) currentPane->scroll(false, false);
|
Chris@45
|
2947 }
|
Chris@45
|
2948
|
Chris@45
|
2949 void
|
Chris@45
|
2950 MainWindowBase::jumpLeft()
|
Chris@45
|
2951 {
|
Chris@45
|
2952 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@45
|
2953 if (currentPane) currentPane->scroll(false, true);
|
Chris@45
|
2954 }
|
Chris@45
|
2955
|
Chris@45
|
2956 void
|
Chris@162
|
2957 MainWindowBase::peekLeft()
|
Chris@162
|
2958 {
|
Chris@162
|
2959 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@162
|
2960 if (currentPane) currentPane->scroll(false, false, false);
|
Chris@162
|
2961 }
|
Chris@162
|
2962
|
Chris@162
|
2963 void
|
Chris@45
|
2964 MainWindowBase::scrollRight()
|
Chris@45
|
2965 {
|
Chris@45
|
2966 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@45
|
2967 if (currentPane) currentPane->scroll(true, false);
|
Chris@45
|
2968 }
|
Chris@45
|
2969
|
Chris@45
|
2970 void
|
Chris@45
|
2971 MainWindowBase::jumpRight()
|
Chris@45
|
2972 {
|
Chris@45
|
2973 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@45
|
2974 if (currentPane) currentPane->scroll(true, true);
|
Chris@45
|
2975 }
|
Chris@45
|
2976
|
Chris@45
|
2977 void
|
Chris@162
|
2978 MainWindowBase::peekRight()
|
Chris@162
|
2979 {
|
Chris@162
|
2980 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@162
|
2981 if (currentPane) currentPane->scroll(true, false, false);
|
Chris@162
|
2982 }
|
Chris@162
|
2983
|
Chris@162
|
2984 void
|
Chris@45
|
2985 MainWindowBase::showNoOverlays()
|
Chris@45
|
2986 {
|
Chris@45
|
2987 m_viewManager->setOverlayMode(ViewManager::NoOverlays);
|
Chris@45
|
2988 }
|
Chris@45
|
2989
|
Chris@45
|
2990 void
|
Chris@45
|
2991 MainWindowBase::showMinimalOverlays()
|
Chris@45
|
2992 {
|
Chris@335
|
2993 m_viewManager->setOverlayMode(ViewManager::StandardOverlays);
|
Chris@45
|
2994 }
|
Chris@45
|
2995
|
Chris@45
|
2996 void
|
Chris@45
|
2997 MainWindowBase::showAllOverlays()
|
Chris@45
|
2998 {
|
Chris@45
|
2999 m_viewManager->setOverlayMode(ViewManager::AllOverlays);
|
Chris@45
|
3000 }
|
Chris@45
|
3001
|
Chris@45
|
3002 void
|
Chris@577
|
3003 MainWindowBase::findTimeRulerLayer()
|
Chris@577
|
3004 {
|
Chris@577
|
3005 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
|
Chris@577
|
3006 Pane *pane = m_paneStack->getPane(i);
|
Chris@577
|
3007 if (!pane) continue;
|
Chris@577
|
3008 for (int j = 0; j < pane->getLayerCount(); ++j) {
|
Chris@577
|
3009 Layer *layer = pane->getLayer(j);
|
Chris@577
|
3010 if (!dynamic_cast<TimeRulerLayer *>(layer)) continue;
|
Chris@577
|
3011 m_timeRulerLayer = layer;
|
Chris@577
|
3012 return;
|
Chris@577
|
3013 }
|
Chris@577
|
3014 }
|
Chris@577
|
3015 if (m_timeRulerLayer) {
|
Chris@577
|
3016 SVCERR << "WARNING: Time ruler layer was not reset to 0 before session template loaded?" << endl;
|
Chris@577
|
3017 delete m_timeRulerLayer;
|
Chris@636
|
3018 m_timeRulerLayer = nullptr;
|
Chris@577
|
3019 }
|
Chris@577
|
3020 }
|
Chris@577
|
3021
|
Chris@577
|
3022 void
|
Chris@211
|
3023 MainWindowBase::toggleTimeRulers()
|
Chris@211
|
3024 {
|
Chris@211
|
3025 bool haveRulers = false;
|
Chris@211
|
3026 bool someHidden = false;
|
Chris@211
|
3027
|
Chris@211
|
3028 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
|
Chris@211
|
3029
|
Chris@211
|
3030 Pane *pane = m_paneStack->getPane(i);
|
Chris@211
|
3031 if (!pane) continue;
|
Chris@211
|
3032
|
Chris@211
|
3033 for (int j = 0; j < pane->getLayerCount(); ++j) {
|
Chris@211
|
3034
|
Chris@211
|
3035 Layer *layer = pane->getLayer(j);
|
Chris@211
|
3036 if (!dynamic_cast<TimeRulerLayer *>(layer)) continue;
|
Chris@211
|
3037
|
Chris@211
|
3038 haveRulers = true;
|
Chris@211
|
3039 if (layer->isLayerDormant(pane)) someHidden = true;
|
Chris@211
|
3040 }
|
Chris@211
|
3041 }
|
Chris@211
|
3042
|
Chris@211
|
3043 if (haveRulers) {
|
Chris@211
|
3044
|
Chris@211
|
3045 bool show = someHidden;
|
Chris@211
|
3046
|
Chris@211
|
3047 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
|
Chris@211
|
3048
|
Chris@211
|
3049 Pane *pane = m_paneStack->getPane(i);
|
Chris@211
|
3050 if (!pane) continue;
|
Chris@211
|
3051
|
Chris@211
|
3052 for (int j = 0; j < pane->getLayerCount(); ++j) {
|
Chris@211
|
3053
|
Chris@211
|
3054 Layer *layer = pane->getLayer(j);
|
Chris@211
|
3055 if (!dynamic_cast<TimeRulerLayer *>(layer)) continue;
|
Chris@211
|
3056
|
Chris@211
|
3057 layer->showLayer(pane, show);
|
Chris@211
|
3058 }
|
Chris@211
|
3059 }
|
Chris@211
|
3060 }
|
Chris@211
|
3061 }
|
Chris@211
|
3062
|
Chris@211
|
3063 void
|
Chris@45
|
3064 MainWindowBase::toggleZoomWheels()
|
Chris@45
|
3065 {
|
Chris@45
|
3066 if (m_viewManager->getZoomWheelsEnabled()) {
|
Chris@45
|
3067 m_viewManager->setZoomWheelsEnabled(false);
|
Chris@45
|
3068 } else {
|
Chris@45
|
3069 m_viewManager->setZoomWheelsEnabled(true);
|
Chris@45
|
3070 }
|
Chris@45
|
3071 }
|
Chris@45
|
3072
|
Chris@45
|
3073 void
|
Chris@45
|
3074 MainWindowBase::togglePropertyBoxes()
|
Chris@45
|
3075 {
|
Chris@45
|
3076 if (m_paneStack->getLayoutStyle() == PaneStack::NoPropertyStacks) {
|
Chris@45
|
3077 if (Preferences::getInstance()->getPropertyBoxLayout() ==
|
Chris@45
|
3078 Preferences::VerticallyStacked) {
|
Chris@45
|
3079 m_paneStack->setLayoutStyle(PaneStack::PropertyStackPerPaneLayout);
|
Chris@45
|
3080 } else {
|
Chris@45
|
3081 m_paneStack->setLayoutStyle(PaneStack::SinglePropertyStackLayout);
|
Chris@45
|
3082 }
|
Chris@45
|
3083 } else {
|
Chris@45
|
3084 m_paneStack->setLayoutStyle(PaneStack::NoPropertyStacks);
|
Chris@45
|
3085 }
|
Chris@45
|
3086 }
|
Chris@45
|
3087
|
Chris@378
|
3088 QLabel *
|
Chris@378
|
3089 MainWindowBase::getStatusLabel() const
|
Chris@378
|
3090 {
|
Chris@378
|
3091 if (!m_statusLabel) {
|
Chris@378
|
3092 m_statusLabel = new QLabel();
|
Chris@378
|
3093 statusBar()->addWidget(m_statusLabel, 1);
|
Chris@378
|
3094 }
|
Chris@379
|
3095
|
Chris@379
|
3096 QList<QFrame *> frames = statusBar()->findChildren<QFrame *>();
|
Chris@379
|
3097 foreach (QFrame *f, frames) {
|
Chris@379
|
3098 f->setFrameStyle(QFrame::NoFrame);
|
Chris@379
|
3099 }
|
Chris@379
|
3100
|
Chris@378
|
3101 return m_statusLabel;
|
Chris@378
|
3102 }
|
Chris@378
|
3103
|
Chris@45
|
3104 void
|
Chris@45
|
3105 MainWindowBase::toggleStatusBar()
|
Chris@45
|
3106 {
|
Chris@45
|
3107 QSettings settings;
|
Chris@45
|
3108 settings.beginGroup("MainWindow");
|
Chris@45
|
3109 bool sb = settings.value("showstatusbar", true).toBool();
|
Chris@45
|
3110
|
Chris@45
|
3111 if (sb) {
|
Chris@45
|
3112 statusBar()->hide();
|
Chris@45
|
3113 } else {
|
Chris@45
|
3114 statusBar()->show();
|
Chris@45
|
3115 }
|
Chris@45
|
3116
|
Chris@45
|
3117 settings.setValue("showstatusbar", !sb);
|
Chris@45
|
3118
|
Chris@45
|
3119 settings.endGroup();
|
Chris@45
|
3120 }
|
Chris@45
|
3121
|
Chris@45
|
3122 void
|
Chris@256
|
3123 MainWindowBase::toggleCentreLine()
|
Chris@256
|
3124 {
|
Chris@256
|
3125 if (m_viewManager->shouldShowCentreLine()) {
|
Chris@256
|
3126 m_viewManager->setShowCentreLine(false);
|
Chris@256
|
3127 } else {
|
Chris@256
|
3128 m_viewManager->setShowCentreLine(true);
|
Chris@256
|
3129 }
|
Chris@256
|
3130 }
|
Chris@256
|
3131
|
Chris@256
|
3132 void
|
Chris@45
|
3133 MainWindowBase::preferenceChanged(PropertyContainer::PropertyName name)
|
Chris@45
|
3134 {
|
Chris@45
|
3135 if (name == "Property Box Layout") {
|
Chris@45
|
3136 if (m_paneStack->getLayoutStyle() != PaneStack::NoPropertyStacks) {
|
Chris@45
|
3137 if (Preferences::getInstance()->getPropertyBoxLayout() ==
|
Chris@45
|
3138 Preferences::VerticallyStacked) {
|
Chris@45
|
3139 m_paneStack->setLayoutStyle(PaneStack::PropertyStackPerPaneLayout);
|
Chris@45
|
3140 } else {
|
Chris@45
|
3141 m_paneStack->setLayoutStyle(PaneStack::SinglePropertyStackLayout);
|
Chris@45
|
3142 }
|
Chris@45
|
3143 }
|
Chris@45
|
3144 } else if (name == "Background Mode" && m_viewManager) {
|
Chris@45
|
3145 Preferences::BackgroundMode mode =
|
Chris@45
|
3146 Preferences::getInstance()->getBackgroundMode();
|
Chris@45
|
3147 if (mode == Preferences::BackgroundFromTheme) {
|
Chris@45
|
3148 m_viewManager->setGlobalDarkBackground(m_initialDarkBackground);
|
Chris@45
|
3149 } else if (mode == Preferences::DarkBackground) {
|
Chris@45
|
3150 m_viewManager->setGlobalDarkBackground(true);
|
Chris@45
|
3151 } else {
|
Chris@45
|
3152 m_viewManager->setGlobalDarkBackground(false);
|
Chris@45
|
3153 }
|
Chris@45
|
3154 }
|
Chris@45
|
3155 }
|
Chris@45
|
3156
|
Chris@45
|
3157 void
|
Chris@45
|
3158 MainWindowBase::play()
|
Chris@45
|
3159 {
|
Chris@516
|
3160 if ((m_recordTarget && m_recordTarget->isRecording()) ||
|
Chris@516
|
3161 (m_playSource && m_playSource->isPlaying())) {
|
Chris@45
|
3162 stop();
|
Chris@479
|
3163 QAction *action = qobject_cast<QAction *>(sender());
|
Chris@479
|
3164 if (action) action->setChecked(false);
|
Chris@45
|
3165 } else {
|
Chris@487
|
3166 if (m_audioIO) m_audioIO->resume();
|
Chris@509
|
3167 else if (m_playTarget) m_playTarget->resume();
|
Chris@45
|
3168 playbackFrameChanged(m_viewManager->getPlaybackFrame());
|
Chris@595
|
3169 m_playSource->play(m_viewManager->getPlaybackFrame());
|
Chris@45
|
3170 }
|
Chris@45
|
3171 }
|
Chris@45
|
3172
|
Chris@45
|
3173 void
|
Chris@477
|
3174 MainWindowBase::record()
|
Chris@477
|
3175 {
|
Chris@586
|
3176 QAction *action = qobject_cast<QAction *>(sender());
|
Chris@586
|
3177
|
Chris@478
|
3178 if (!(m_soundOptions & WithAudioInput)) {
|
Chris@586
|
3179 if (action) action->setChecked(false);
|
Chris@478
|
3180 return;
|
Chris@478
|
3181 }
|
Chris@478
|
3182
|
Chris@477
|
3183 if (!m_recordTarget) {
|
Chris@586
|
3184 if (action) action->setChecked(false);
|
Chris@477
|
3185 return;
|
Chris@477
|
3186 }
|
Chris@477
|
3187
|
Chris@478
|
3188 if (!m_audioIO) {
|
Chris@611
|
3189 SVDEBUG << "MainWindowBase::record: about to create audio IO" << endl;
|
Chris@478
|
3190 createAudioIO();
|
Chris@478
|
3191 }
|
Chris@492
|
3192
|
Chris@492
|
3193 if (!m_audioIO) {
|
Chris@586
|
3194 if (!m_playTarget) {
|
Chris@586
|
3195 // Don't need to report this, createAudioIO should have
|
Chris@586
|
3196 if (action) action->setChecked(false);
|
Chris@586
|
3197 return;
|
Chris@586
|
3198 } else {
|
Chris@586
|
3199 // Need to report this: if the play target exists instead
|
Chris@586
|
3200 // of the audio IO, then that means we failed to open a
|
Chris@586
|
3201 // capture device. The record control should be disabled
|
Chris@586
|
3202 // in that situation, so if it happens here, that must
|
Chris@586
|
3203 // mean this is the first time we ever tried to open the
|
Chris@586
|
3204 // audio device, hence the need to report the problem here
|
Chris@586
|
3205 QMessageBox::critical
|
Chris@586
|
3206 (this, tr("No record device available"),
|
Chris@586
|
3207 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
|
3208 if (action) action->setChecked(false);
|
Chris@586
|
3209 updateMenuStates();
|
Chris@586
|
3210 return;
|
Chris@586
|
3211 }
|
Chris@492
|
3212 }
|
Chris@478
|
3213
|
Chris@477
|
3214 if (m_recordTarget->isRecording()) {
|
Chris@492
|
3215 stop();
|
Chris@477
|
3216 return;
|
Chris@477
|
3217 }
|
Chris@490
|
3218
|
Chris@483
|
3219 if (m_audioRecordMode == RecordReplaceSession) {
|
Chris@490
|
3220 if (!checkSaveModified()) {
|
Chris@490
|
3221 if (action) action->setChecked(false);
|
Chris@490
|
3222 return;
|
Chris@490
|
3223 }
|
Chris@483
|
3224 }
|
Chris@487
|
3225
|
Chris@557
|
3226 if (m_viewManager) m_viewManager->setGlobalCentreFrame(0);
|
Chris@557
|
3227
|
Chris@611
|
3228 SVCERR << "MainWindowBase::record: about to resume" << endl;
|
Chris@492
|
3229 m_audioIO->resume();
|
Chris@509
|
3230
|
Chris@477
|
3231 WritableWaveFileModel *model = m_recordTarget->startRecording();
|
Chris@477
|
3232 if (!model) {
|
Chris@586
|
3233 SVCERR << "ERROR: MainWindowBase::record: Recording failed" << endl;
|
Chris@586
|
3234 QMessageBox::critical
|
Chris@586
|
3235 (this, tr("Recording failed"),
|
Chris@586
|
3236 tr("<b>Recording failed</b><p>Failed to switch to record mode (some internal problem?)</p>"));
|
Chris@490
|
3237 if (action) action->setChecked(false);
|
Chris@477
|
3238 return;
|
Chris@477
|
3239 }
|
Chris@477
|
3240
|
Chris@477
|
3241 if (!model->isOK()) {
|
Chris@611
|
3242 SVCERR << "MainWindowBase::record: Model not OK, stopping and suspending" << endl;
|
Chris@477
|
3243 m_recordTarget->stopRecording();
|
Chris@492
|
3244 m_audioIO->suspend();
|
Chris@586
|
3245 if (action) action->setChecked(false);
|
Chris@477
|
3246 delete model;
|
Chris@477
|
3247 return;
|
Chris@477
|
3248 }
|
Chris@611
|
3249
|
Chris@611
|
3250 SVCERR << "MainWindowBase::record: Model is OK, continuing..." << endl;
|
Chris@487
|
3251
|
Chris@478
|
3252 PlayParameterRepository::getInstance()->addPlayable(model);
|
Chris@483
|
3253
|
Chris@483
|
3254 if (m_audioRecordMode == RecordReplaceSession || !getMainModel()) {
|
Chris@478
|
3255
|
Chris@479
|
3256 //!!! duplication with openAudio here
|
Chris@479
|
3257
|
Chris@479
|
3258 QString templateName = getDefaultSessionTemplate();
|
Chris@479
|
3259 bool loadedTemplate = false;
|
Chris@479
|
3260
|
Chris@479
|
3261 if (templateName != "") {
|
Chris@479
|
3262 FileOpenStatus tplStatus = openSessionTemplate(templateName);
|
Chris@479
|
3263 if (tplStatus == FileOpenCancelled) {
|
Chris@611
|
3264 SVCERR << "MainWindowBase::record: Session template open cancelled, stopping and suspending" << endl;
|
Chris@490
|
3265 m_recordTarget->stopRecording();
|
Chris@492
|
3266 m_audioIO->suspend();
|
Chris@490
|
3267 PlayParameterRepository::getInstance()->removePlayable(model);
|
Chris@479
|
3268 return;
|
Chris@479
|
3269 }
|
Chris@479
|
3270 if (tplStatus != FileOpenFailed) {
|
Chris@479
|
3271 loadedTemplate = true;
|
Chris@479
|
3272 }
|
Chris@479
|
3273 }
|
Chris@479
|
3274
|
Chris@479
|
3275 if (!loadedTemplate) {
|
Chris@479
|
3276 closeSession();
|
Chris@479
|
3277 createDocument();
|
Chris@479
|
3278 }
|
Chris@479
|
3279
|
Chris@479
|
3280 Model *prevMain = getMainModel();
|
Chris@479
|
3281 if (prevMain) {
|
Chris@479
|
3282 m_playSource->removeModel(prevMain);
|
Chris@479
|
3283 PlayParameterRepository::getInstance()->removePlayable(prevMain);
|
Chris@479
|
3284 }
|
Chris@479
|
3285
|
Chris@478
|
3286 m_document->setMainModel(model);
|
Chris@478
|
3287 setupMenus();
|
Chris@577
|
3288 findTimeRulerLayer();
|
Chris@478
|
3289
|
Chris@595
|
3290 if (loadedTemplate || (m_sessionFile == "")) {
|
Chris@479
|
3291 //!!! shouldn't be dealing directly with title from here -- call a method
|
Chris@595
|
3292 setWindowTitle(tr("%1: %2")
|
Chris@479
|
3293 .arg(QApplication::applicationName())
|
Chris@479
|
3294 .arg(model->getLocation()));
|
Chris@595
|
3295 CommandHistory::getInstance()->clear();
|
Chris@595
|
3296 CommandHistory::getInstance()->documentSaved();
|
Chris@595
|
3297 m_documentModified = false;
|
Chris@595
|
3298 } else {
|
Chris@595
|
3299 setWindowTitle(tr("%1: %2 [%3]")
|
Chris@479
|
3300 .arg(QApplication::applicationName())
|
Chris@595
|
3301 .arg(QFileInfo(m_sessionFile).fileName())
|
Chris@595
|
3302 .arg(model->getLocation()));
|
Chris@595
|
3303 if (m_documentModified) {
|
Chris@595
|
3304 m_documentModified = false;
|
Chris@595
|
3305 documentModified(); // so as to restore "(modified)" window title
|
Chris@595
|
3306 }
|
Chris@595
|
3307 }
|
Chris@479
|
3308
|
Chris@478
|
3309 } else {
|
Chris@478
|
3310
|
Chris@478
|
3311 CommandHistory::getInstance()->startCompoundOperation
|
Chris@478
|
3312 (tr("Import Recorded Audio"), true);
|
Chris@478
|
3313
|
Chris@478
|
3314 m_document->addImportedModel(model);
|
Chris@478
|
3315
|
Chris@478
|
3316 AddPaneCommand *command = new AddPaneCommand(this);
|
Chris@478
|
3317 CommandHistory::getInstance()->addCommand(command);
|
Chris@478
|
3318
|
Chris@478
|
3319 Pane *pane = command->getPane();
|
Chris@478
|
3320
|
Chris@478
|
3321 if (m_timeRulerLayer) {
|
Chris@478
|
3322 m_document->addLayerToView(pane, m_timeRulerLayer);
|
Chris@478
|
3323 }
|
Chris@478
|
3324
|
Chris@478
|
3325 Layer *newLayer = m_document->createImportedLayer(model);
|
Chris@478
|
3326
|
Chris@478
|
3327 if (newLayer) {
|
Chris@478
|
3328 m_document->addLayerToView(pane, newLayer);
|
Chris@478
|
3329 }
|
Chris@595
|
3330
|
Chris@478
|
3331 CommandHistory::getInstance()->endCompoundOperation();
|
Chris@477
|
3332 }
|
Chris@479
|
3333
|
Chris@479
|
3334 updateMenuStates();
|
Chris@479
|
3335 m_recentFiles.addFile(model->getLocation());
|
Chris@479
|
3336 currentPaneChanged(m_paneStack->getCurrentPane());
|
Chris@611
|
3337
|
Chris@496
|
3338 emit audioFileLoaded();
|
Chris@477
|
3339 }
|
Chris@477
|
3340
|
Chris@477
|
3341 void
|
Chris@45
|
3342 MainWindowBase::ffwd()
|
Chris@45
|
3343 {
|
Chris@45
|
3344 if (!getMainModel()) return;
|
Chris@45
|
3345
|
Chris@435
|
3346 sv_frame_t frame = m_viewManager->getPlaybackFrame();
|
Chris@45
|
3347 ++frame;
|
Chris@45
|
3348
|
Chris@85
|
3349 Pane *pane = m_paneStack->getCurrentPane();
|
Chris@45
|
3350 Layer *layer = getSnapLayer();
|
Chris@435
|
3351 sv_samplerate_t sr = getMainModel()->getSampleRate();
|
Chris@45
|
3352
|
Chris@45
|
3353 if (!layer) {
|
Chris@45
|
3354
|
Chris@45
|
3355 frame = RealTime::realTime2Frame
|
Chris@357
|
3356 (RealTime::frame2RealTime(frame, sr) + m_defaultFfwdRwdStep, sr);
|
Chris@435
|
3357 if (frame > getMainModel()->getEndFrame()) {
|
Chris@45
|
3358 frame = getMainModel()->getEndFrame();
|
Chris@45
|
3359 }
|
Chris@45
|
3360
|
Chris@45
|
3361 } else {
|
Chris@45
|
3362
|
Chris@366
|
3363 int resolution = 0;
|
Chris@166
|
3364 if (pane) frame = pane->alignFromReference(frame);
|
Chris@85
|
3365 if (layer->snapToFeatureFrame(m_paneStack->getCurrentPane(),
|
Chris@85
|
3366 frame, resolution, Layer::SnapRight)) {
|
Chris@85
|
3367 if (pane) frame = pane->alignToReference(frame);
|
Chris@85
|
3368 } else {
|
Chris@45
|
3369 frame = getMainModel()->getEndFrame();
|
Chris@45
|
3370 }
|
Chris@45
|
3371 }
|
Chris@45
|
3372
|
Chris@45
|
3373 if (frame < 0) frame = 0;
|
Chris@45
|
3374
|
Chris@45
|
3375 if (m_viewManager->getPlaySelectionMode()) {
|
Chris@435
|
3376 frame = m_viewManager->constrainFrameToSelection(frame);
|
Chris@45
|
3377 }
|
Chris@45
|
3378
|
Chris@45
|
3379 m_viewManager->setPlaybackFrame(frame);
|
Chris@166
|
3380
|
Chris@435
|
3381 if (frame == getMainModel()->getEndFrame() &&
|
Chris@166
|
3382 m_playSource &&
|
Chris@166
|
3383 m_playSource->isPlaying() &&
|
Chris@166
|
3384 !m_viewManager->getPlayLoopMode()) {
|
Chris@166
|
3385 stop();
|
Chris@166
|
3386 }
|
Chris@45
|
3387 }
|
Chris@45
|
3388
|
Chris@45
|
3389 void
|
Chris@45
|
3390 MainWindowBase::ffwdEnd()
|
Chris@45
|
3391 {
|
Chris@45
|
3392 if (!getMainModel()) return;
|
Chris@45
|
3393
|
Chris@139
|
3394 if (m_playSource &&
|
Chris@139
|
3395 m_playSource->isPlaying() &&
|
Chris@139
|
3396 !m_viewManager->getPlayLoopMode()) {
|
Chris@139
|
3397 stop();
|
Chris@139
|
3398 }
|
Chris@139
|
3399
|
Chris@435
|
3400 sv_frame_t frame = getMainModel()->getEndFrame();
|
Chris@45
|
3401
|
Chris@45
|
3402 if (m_viewManager->getPlaySelectionMode()) {
|
Chris@45
|
3403 frame = m_viewManager->constrainFrameToSelection(frame);
|
Chris@45
|
3404 }
|
Chris@45
|
3405
|
Chris@45
|
3406 m_viewManager->setPlaybackFrame(frame);
|
Chris@45
|
3407 }
|
Chris@45
|
3408
|
Chris@45
|
3409 void
|
Chris@166
|
3410 MainWindowBase::ffwdSimilar()
|
Chris@166
|
3411 {
|
Chris@166
|
3412 if (!getMainModel()) return;
|
Chris@166
|
3413
|
Chris@166
|
3414 Layer *layer = getSnapLayer();
|
Chris@166
|
3415 if (!layer) { ffwd(); return; }
|
Chris@166
|
3416
|
Chris@166
|
3417 Pane *pane = m_paneStack->getCurrentPane();
|
Chris@435
|
3418 sv_frame_t frame = m_viewManager->getPlaybackFrame();
|
Chris@166
|
3419
|
Chris@366
|
3420 int resolution = 0;
|
Chris@166
|
3421 if (pane) frame = pane->alignFromReference(frame);
|
Chris@166
|
3422 if (layer->snapToSimilarFeature(m_paneStack->getCurrentPane(),
|
Chris@166
|
3423 frame, resolution, Layer::SnapRight)) {
|
Chris@166
|
3424 if (pane) frame = pane->alignToReference(frame);
|
Chris@166
|
3425 } else {
|
Chris@166
|
3426 frame = getMainModel()->getEndFrame();
|
Chris@166
|
3427 }
|
Chris@166
|
3428
|
Chris@166
|
3429 if (frame < 0) frame = 0;
|
Chris@166
|
3430
|
Chris@166
|
3431 if (m_viewManager->getPlaySelectionMode()) {
|
Chris@435
|
3432 frame = m_viewManager->constrainFrameToSelection(frame);
|
Chris@166
|
3433 }
|
Chris@166
|
3434
|
Chris@166
|
3435 m_viewManager->setPlaybackFrame(frame);
|
Chris@166
|
3436
|
Chris@435
|
3437 if (frame == getMainModel()->getEndFrame() &&
|
Chris@166
|
3438 m_playSource &&
|
Chris@166
|
3439 m_playSource->isPlaying() &&
|
Chris@166
|
3440 !m_viewManager->getPlayLoopMode()) {
|
Chris@166
|
3441 stop();
|
Chris@166
|
3442 }
|
Chris@166
|
3443 }
|
Chris@166
|
3444
|
Chris@166
|
3445 void
|
Chris@45
|
3446 MainWindowBase::rewind()
|
Chris@45
|
3447 {
|
Chris@45
|
3448 if (!getMainModel()) return;
|
Chris@45
|
3449
|
Chris@435
|
3450 sv_frame_t frame = m_viewManager->getPlaybackFrame();
|
Chris@45
|
3451 if (frame > 0) --frame;
|
Chris@45
|
3452
|
Chris@85
|
3453 Pane *pane = m_paneStack->getCurrentPane();
|
Chris@45
|
3454 Layer *layer = getSnapLayer();
|
Chris@435
|
3455 sv_samplerate_t sr = getMainModel()->getSampleRate();
|
Chris@45
|
3456
|
Chris@45
|
3457 // when rewinding during playback, we want to allow a period
|
Chris@45
|
3458 // following a rewind target point at which the rewind will go to
|
Chris@45
|
3459 // the prior point instead of the immediately neighbouring one
|
Chris@45
|
3460 if (m_playSource && m_playSource->isPlaying()) {
|
Chris@45
|
3461 RealTime ct = RealTime::frame2RealTime(frame, sr);
|
Chris@357
|
3462 ct = ct - RealTime::fromSeconds(0.15);
|
Chris@45
|
3463 if (ct < RealTime::zeroTime) ct = RealTime::zeroTime;
|
Chris@45
|
3464 frame = RealTime::realTime2Frame(ct, sr);
|
Chris@45
|
3465 }
|
Chris@45
|
3466
|
Chris@45
|
3467 if (!layer) {
|
Chris@45
|
3468
|
Chris@45
|
3469 frame = RealTime::realTime2Frame
|
Chris@357
|
3470 (RealTime::frame2RealTime(frame, sr) - m_defaultFfwdRwdStep, sr);
|
Chris@435
|
3471 if (frame < getMainModel()->getStartFrame()) {
|
Chris@45
|
3472 frame = getMainModel()->getStartFrame();
|
Chris@45
|
3473 }
|
Chris@45
|
3474
|
Chris@45
|
3475 } else {
|
Chris@45
|
3476
|
Chris@366
|
3477 int resolution = 0;
|
Chris@166
|
3478 if (pane) frame = pane->alignFromReference(frame);
|
Chris@85
|
3479 if (layer->snapToFeatureFrame(m_paneStack->getCurrentPane(),
|
Chris@85
|
3480 frame, resolution, Layer::SnapLeft)) {
|
Chris@85
|
3481 if (pane) frame = pane->alignToReference(frame);
|
Chris@85
|
3482 } else {
|
Chris@45
|
3483 frame = getMainModel()->getStartFrame();
|
Chris@45
|
3484 }
|
Chris@45
|
3485 }
|
Chris@45
|
3486
|
Chris@45
|
3487 if (frame < 0) frame = 0;
|
Chris@45
|
3488
|
Chris@45
|
3489 if (m_viewManager->getPlaySelectionMode()) {
|
Chris@435
|
3490 frame = m_viewManager->constrainFrameToSelection(frame);
|
Chris@45
|
3491 }
|
Chris@45
|
3492
|
Chris@45
|
3493 m_viewManager->setPlaybackFrame(frame);
|
Chris@45
|
3494 }
|
Chris@45
|
3495
|
Chris@45
|
3496 void
|
Chris@45
|
3497 MainWindowBase::rewindStart()
|
Chris@45
|
3498 {
|
Chris@45
|
3499 if (!getMainModel()) return;
|
Chris@45
|
3500
|
Chris@435
|
3501 sv_frame_t frame = getMainModel()->getStartFrame();
|
Chris@45
|
3502
|
Chris@45
|
3503 if (m_viewManager->getPlaySelectionMode()) {
|
Chris@45
|
3504 frame = m_viewManager->constrainFrameToSelection(frame);
|
Chris@45
|
3505 }
|
Chris@45
|
3506
|
Chris@45
|
3507 m_viewManager->setPlaybackFrame(frame);
|
Chris@45
|
3508 }
|
Chris@45
|
3509
|
Chris@166
|
3510 void
|
Chris@166
|
3511 MainWindowBase::rewindSimilar()
|
Chris@166
|
3512 {
|
Chris@166
|
3513 if (!getMainModel()) return;
|
Chris@166
|
3514
|
Chris@166
|
3515 Layer *layer = getSnapLayer();
|
Chris@166
|
3516 if (!layer) { rewind(); return; }
|
Chris@166
|
3517
|
Chris@166
|
3518 Pane *pane = m_paneStack->getCurrentPane();
|
Chris@435
|
3519 sv_frame_t frame = m_viewManager->getPlaybackFrame();
|
Chris@166
|
3520
|
Chris@366
|
3521 int resolution = 0;
|
Chris@166
|
3522 if (pane) frame = pane->alignFromReference(frame);
|
Chris@166
|
3523 if (layer->snapToSimilarFeature(m_paneStack->getCurrentPane(),
|
Chris@166
|
3524 frame, resolution, Layer::SnapLeft)) {
|
Chris@166
|
3525 if (pane) frame = pane->alignToReference(frame);
|
Chris@166
|
3526 } else {
|
Chris@166
|
3527 frame = getMainModel()->getStartFrame();
|
Chris@166
|
3528 }
|
Chris@166
|
3529
|
Chris@166
|
3530 if (frame < 0) frame = 0;
|
Chris@166
|
3531
|
Chris@166
|
3532 if (m_viewManager->getPlaySelectionMode()) {
|
Chris@435
|
3533 frame = m_viewManager->constrainFrameToSelection(frame);
|
Chris@166
|
3534 }
|
Chris@166
|
3535
|
Chris@166
|
3536 m_viewManager->setPlaybackFrame(frame);
|
Chris@166
|
3537 }
|
Chris@166
|
3538
|
Chris@45
|
3539 Layer *
|
Chris@45
|
3540 MainWindowBase::getSnapLayer() const
|
Chris@45
|
3541 {
|
Chris@45
|
3542 Pane *pane = m_paneStack->getCurrentPane();
|
Chris@636
|
3543 if (!pane) return nullptr;
|
Chris@45
|
3544
|
Chris@45
|
3545 Layer *layer = pane->getSelectedLayer();
|
Chris@45
|
3546
|
Chris@45
|
3547 if (!dynamic_cast<TimeInstantLayer *>(layer) &&
|
Chris@45
|
3548 !dynamic_cast<TimeValueLayer *>(layer) &&
|
Chris@194
|
3549 !dynamic_cast<RegionLayer *>(layer) &&
|
Chris@45
|
3550 !dynamic_cast<TimeRulerLayer *>(layer)) {
|
Chris@45
|
3551
|
Chris@636
|
3552 layer = nullptr;
|
Chris@45
|
3553
|
Chris@45
|
3554 for (int i = pane->getLayerCount(); i > 0; --i) {
|
Chris@45
|
3555 Layer *l = pane->getLayer(i-1);
|
Chris@45
|
3556 if (dynamic_cast<TimeRulerLayer *>(l)) {
|
Chris@45
|
3557 layer = l;
|
Chris@45
|
3558 break;
|
Chris@45
|
3559 }
|
Chris@45
|
3560 }
|
Chris@45
|
3561 }
|
Chris@45
|
3562
|
Chris@45
|
3563 return layer;
|
Chris@45
|
3564 }
|
Chris@45
|
3565
|
Chris@45
|
3566 void
|
Chris@45
|
3567 MainWindowBase::stop()
|
Chris@45
|
3568 {
|
Chris@516
|
3569 if (m_recordTarget &&
|
Chris@516
|
3570 m_recordTarget->isRecording()) {
|
Chris@477
|
3571 m_recordTarget->stopRecording();
|
Chris@477
|
3572 }
|
Chris@516
|
3573
|
Chris@516
|
3574 if (!m_playSource) return;
|
Chris@516
|
3575
|
Chris@45
|
3576 m_playSource->stop();
|
Chris@45
|
3577
|
Chris@611
|
3578 SVCERR << "MainWindowBase::stop: suspending" << endl;
|
Chris@611
|
3579
|
Chris@487
|
3580 if (m_audioIO) m_audioIO->suspend();
|
Chris@509
|
3581 else if (m_playTarget) m_playTarget->suspend();
|
Chris@487
|
3582
|
Chris@45
|
3583 if (m_paneStack && m_paneStack->getCurrentPane()) {
|
Chris@45
|
3584 updateVisibleRangeDisplay(m_paneStack->getCurrentPane());
|
Chris@45
|
3585 } else {
|
Chris@45
|
3586 m_myStatusMessage = "";
|
Chris@378
|
3587 getStatusLabel()->setText("");
|
Chris@45
|
3588 }
|
Chris@45
|
3589 }
|
Chris@45
|
3590
|
Chris@45
|
3591 MainWindowBase::AddPaneCommand::AddPaneCommand(MainWindowBase *mw) :
|
Chris@45
|
3592 m_mw(mw),
|
Chris@636
|
3593 m_pane(nullptr),
|
Chris@636
|
3594 m_prevCurrentPane(nullptr),
|
Chris@45
|
3595 m_added(false)
|
Chris@45
|
3596 {
|
Chris@45
|
3597 }
|
Chris@45
|
3598
|
Chris@45
|
3599 MainWindowBase::AddPaneCommand::~AddPaneCommand()
|
Chris@45
|
3600 {
|
Chris@45
|
3601 if (m_pane && !m_added) {
|
Chris@595
|
3602 m_mw->m_paneStack->deletePane(m_pane);
|
Chris@45
|
3603 }
|
Chris@45
|
3604 }
|
Chris@45
|
3605
|
Chris@45
|
3606 QString
|
Chris@45
|
3607 MainWindowBase::AddPaneCommand::getName() const
|
Chris@45
|
3608 {
|
Chris@45
|
3609 return tr("Add Pane");
|
Chris@45
|
3610 }
|
Chris@45
|
3611
|
Chris@45
|
3612 void
|
Chris@45
|
3613 MainWindowBase::AddPaneCommand::execute()
|
Chris@45
|
3614 {
|
Chris@45
|
3615 if (!m_pane) {
|
Chris@595
|
3616 m_prevCurrentPane = m_mw->m_paneStack->getCurrentPane();
|
Chris@595
|
3617 m_pane = m_mw->m_paneStack->addPane();
|
Chris@45
|
3618
|
Chris@45
|
3619 connect(m_pane, SIGNAL(contextHelpChanged(const QString &)),
|
Chris@45
|
3620 m_mw, SLOT(contextHelpChanged(const QString &)));
|
Chris@45
|
3621 } else {
|
Chris@595
|
3622 m_mw->m_paneStack->showPane(m_pane);
|
Chris@45
|
3623 }
|
Chris@45
|
3624
|
Chris@45
|
3625 m_mw->m_paneStack->setCurrentPane(m_pane);
|
Chris@45
|
3626 m_added = true;
|
Chris@45
|
3627 }
|
Chris@45
|
3628
|
Chris@45
|
3629 void
|
Chris@45
|
3630 MainWindowBase::AddPaneCommand::unexecute()
|
Chris@45
|
3631 {
|
Chris@45
|
3632 m_mw->m_paneStack->hidePane(m_pane);
|
Chris@45
|
3633 m_mw->m_paneStack->setCurrentPane(m_prevCurrentPane);
|
Chris@45
|
3634 m_added = false;
|
Chris@45
|
3635 }
|
Chris@45
|
3636
|
Chris@45
|
3637 MainWindowBase::RemovePaneCommand::RemovePaneCommand(MainWindowBase *mw, Pane *pane) :
|
Chris@45
|
3638 m_mw(mw),
|
Chris@45
|
3639 m_pane(pane),
|
Chris@636
|
3640 m_prevCurrentPane(nullptr),
|
Chris@45
|
3641 m_added(true)
|
Chris@45
|
3642 {
|
Chris@45
|
3643 }
|
Chris@45
|
3644
|
Chris@45
|
3645 MainWindowBase::RemovePaneCommand::~RemovePaneCommand()
|
Chris@45
|
3646 {
|
Chris@45
|
3647 if (m_pane && !m_added) {
|
Chris@595
|
3648 m_mw->m_paneStack->deletePane(m_pane);
|
Chris@45
|
3649 }
|
Chris@45
|
3650 }
|
Chris@45
|
3651
|
Chris@45
|
3652 QString
|
Chris@45
|
3653 MainWindowBase::RemovePaneCommand::getName() const
|
Chris@45
|
3654 {
|
Chris@45
|
3655 return tr("Remove Pane");
|
Chris@45
|
3656 }
|
Chris@45
|
3657
|
Chris@45
|
3658 void
|
Chris@45
|
3659 MainWindowBase::RemovePaneCommand::execute()
|
Chris@45
|
3660 {
|
Chris@45
|
3661 m_prevCurrentPane = m_mw->m_paneStack->getCurrentPane();
|
Chris@45
|
3662 m_mw->m_paneStack->hidePane(m_pane);
|
Chris@45
|
3663 m_added = false;
|
Chris@45
|
3664 }
|
Chris@45
|
3665
|
Chris@45
|
3666 void
|
Chris@45
|
3667 MainWindowBase::RemovePaneCommand::unexecute()
|
Chris@45
|
3668 {
|
Chris@45
|
3669 m_mw->m_paneStack->showPane(m_pane);
|
Chris@45
|
3670 m_mw->m_paneStack->setCurrentPane(m_prevCurrentPane);
|
Chris@45
|
3671 m_added = true;
|
Chris@45
|
3672 }
|
Chris@45
|
3673
|
Chris@45
|
3674 void
|
Chris@45
|
3675 MainWindowBase::deleteCurrentPane()
|
Chris@45
|
3676 {
|
Chris@45
|
3677 CommandHistory::getInstance()->startCompoundOperation
|
Chris@595
|
3678 (tr("Delete Pane"), true);
|
Chris@45
|
3679
|
Chris@45
|
3680 Pane *pane = m_paneStack->getCurrentPane();
|
Chris@45
|
3681 if (pane) {
|
Chris@595
|
3682 while (pane->getLayerCount() > 0) {
|
Chris@595
|
3683 Layer *layer = pane->getLayer(0);
|
Chris@595
|
3684 if (layer) {
|
Chris@595
|
3685 m_document->removeLayerFromView(pane, layer);
|
Chris@595
|
3686 } else {
|
Chris@595
|
3687 break;
|
Chris@595
|
3688 }
|
Chris@595
|
3689 }
|
Chris@595
|
3690
|
Chris@595
|
3691 RemovePaneCommand *command = new RemovePaneCommand(this, pane);
|
Chris@595
|
3692 CommandHistory::getInstance()->addCommand(command);
|
Chris@45
|
3693 }
|
Chris@45
|
3694
|
Chris@45
|
3695 CommandHistory::getInstance()->endCompoundOperation();
|
Chris@45
|
3696
|
Chris@45
|
3697 updateMenuStates();
|
Chris@45
|
3698 }
|
Chris@45
|
3699
|
Chris@45
|
3700 void
|
Chris@45
|
3701 MainWindowBase::deleteCurrentLayer()
|
Chris@45
|
3702 {
|
Chris@45
|
3703 Pane *pane = m_paneStack->getCurrentPane();
|
Chris@45
|
3704 if (pane) {
|
Chris@595
|
3705 Layer *layer = pane->getSelectedLayer();
|
Chris@595
|
3706 if (layer) {
|
Chris@595
|
3707 m_document->removeLayerFromView(pane, layer);
|
Chris@595
|
3708 }
|
Chris@45
|
3709 }
|
Chris@45
|
3710 updateMenuStates();
|
Chris@45
|
3711 }
|
Chris@45
|
3712
|
Chris@45
|
3713 void
|
Chris@123
|
3714 MainWindowBase::editCurrentLayer()
|
Chris@123
|
3715 {
|
Chris@636
|
3716 Layer *layer = nullptr;
|
Chris@123
|
3717 Pane *pane = m_paneStack->getCurrentPane();
|
Chris@123
|
3718 if (pane) layer = pane->getSelectedLayer();
|
Chris@123
|
3719 if (!layer) return;
|
Chris@123
|
3720
|
Chris@123
|
3721 Model *model = layer->getModel();
|
Chris@123
|
3722 if (!model) return;
|
Chris@123
|
3723
|
Chris@124
|
3724 TabularModel *tabular = dynamic_cast<TabularModel *>(model);
|
Chris@124
|
3725 if (!tabular) {
|
Chris@124
|
3726 //!!! how to prevent this function from being active if not
|
Chris@124
|
3727 //appropriate model type? or will we ultimately support
|
Chris@124
|
3728 //tabular display for all editable models?
|
Chris@233
|
3729 SVDEBUG << "NOTE: Not a tabular model" << endl;
|
Chris@124
|
3730 return;
|
Chris@124
|
3731 }
|
Chris@124
|
3732
|
Chris@123
|
3733 if (m_layerDataDialogMap.find(layer) != m_layerDataDialogMap.end()) {
|
Chris@126
|
3734 if (!m_layerDataDialogMap[layer].isNull()) {
|
Chris@126
|
3735 m_layerDataDialogMap[layer]->show();
|
Chris@126
|
3736 m_layerDataDialogMap[layer]->raise();
|
Chris@126
|
3737 return;
|
Chris@126
|
3738 }
|
Chris@123
|
3739 }
|
Chris@123
|
3740
|
Chris@125
|
3741 QString title = layer->getLayerPresentationName();
|
Chris@125
|
3742
|
Chris@125
|
3743 ModelDataTableDialog *dialog = new ModelDataTableDialog(tabular, title, this);
|
Chris@128
|
3744 dialog->setAttribute(Qt::WA_DeleteOnClose);
|
Chris@128
|
3745
|
Chris@128
|
3746 connectLayerEditDialog(dialog);
|
Chris@123
|
3747
|
Chris@128
|
3748 m_layerDataDialogMap[layer] = dialog;
|
Chris@128
|
3749 m_viewDataDialogMap[pane].insert(dialog);
|
Chris@128
|
3750
|
Chris@128
|
3751 dialog->show();
|
Chris@128
|
3752 }
|
Chris@128
|
3753
|
Chris@128
|
3754 void
|
Chris@128
|
3755 MainWindowBase::connectLayerEditDialog(ModelDataTableDialog *dialog)
|
Chris@128
|
3756 {
|
Chris@123
|
3757 connect(m_viewManager,
|
Chris@435
|
3758 SIGNAL(globalCentreFrameChanged(sv_frame_t)),
|
Chris@123
|
3759 dialog,
|
Chris@435
|
3760 SLOT(userScrolledToFrame(sv_frame_t)));
|
Chris@127
|
3761
|
Chris@127
|
3762 connect(m_viewManager,
|
Chris@435
|
3763 SIGNAL(playbackFrameChanged(sv_frame_t)),
|
Chris@127
|
3764 dialog,
|
Chris@435
|
3765 SLOT(playbackScrolledToFrame(sv_frame_t)));
|
Chris@127
|
3766
|
Chris@123
|
3767 connect(dialog,
|
Chris@435
|
3768 SIGNAL(scrollToFrame(sv_frame_t)),
|
Chris@123
|
3769 m_viewManager,
|
Chris@435
|
3770 SLOT(setGlobalCentreFrame(sv_frame_t)));
|
Chris@129
|
3771
|
Chris@129
|
3772 connect(dialog,
|
Chris@435
|
3773 SIGNAL(scrollToFrame(sv_frame_t)),
|
Chris@129
|
3774 m_viewManager,
|
Chris@435
|
3775 SLOT(setPlaybackFrame(sv_frame_t)));
|
Chris@128
|
3776 }
|
Chris@123
|
3777
|
Chris@123
|
3778 void
|
Chris@73
|
3779 MainWindowBase::previousPane()
|
Chris@73
|
3780 {
|
Chris@73
|
3781 if (!m_paneStack) return;
|
Chris@73
|
3782
|
Chris@73
|
3783 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@73
|
3784 if (!currentPane) return;
|
Chris@73
|
3785
|
Chris@73
|
3786 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
|
Chris@73
|
3787 if (m_paneStack->getPane(i) == currentPane) {
|
Chris@73
|
3788 if (i == 0) return;
|
Chris@73
|
3789 m_paneStack->setCurrentPane(m_paneStack->getPane(i-1));
|
Chris@73
|
3790 updateMenuStates();
|
Chris@73
|
3791 return;
|
Chris@73
|
3792 }
|
Chris@73
|
3793 }
|
Chris@73
|
3794 }
|
Chris@73
|
3795
|
Chris@73
|
3796 void
|
Chris@73
|
3797 MainWindowBase::nextPane()
|
Chris@73
|
3798 {
|
Chris@73
|
3799 if (!m_paneStack) return;
|
Chris@73
|
3800
|
Chris@73
|
3801 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@73
|
3802 if (!currentPane) return;
|
Chris@73
|
3803
|
Chris@73
|
3804 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
|
Chris@73
|
3805 if (m_paneStack->getPane(i) == currentPane) {
|
Chris@73
|
3806 if (i == m_paneStack->getPaneCount()-1) return;
|
Chris@73
|
3807 m_paneStack->setCurrentPane(m_paneStack->getPane(i+1));
|
Chris@73
|
3808 updateMenuStates();
|
Chris@73
|
3809 return;
|
Chris@73
|
3810 }
|
Chris@73
|
3811 }
|
Chris@73
|
3812 }
|
Chris@73
|
3813
|
Chris@73
|
3814 void
|
Chris@73
|
3815 MainWindowBase::previousLayer()
|
Chris@73
|
3816 {
|
Chris@73
|
3817 if (!m_paneStack) return;
|
Chris@73
|
3818
|
Chris@73
|
3819 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@73
|
3820 if (!currentPane) return;
|
Chris@73
|
3821
|
Chris@403
|
3822 int count = currentPane->getLayerCount();
|
Chris@403
|
3823 if (count == 0) return;
|
Chris@403
|
3824
|
Chris@73
|
3825 Layer *currentLayer = currentPane->getSelectedLayer();
|
Chris@403
|
3826
|
Chris@403
|
3827 if (!currentLayer) {
|
Chris@403
|
3828 // The pane itself is current
|
Chris@403
|
3829 m_paneStack->setCurrentLayer
|
Chris@403
|
3830 (currentPane, currentPane->getFixedOrderLayer(count-1));
|
Chris@403
|
3831 } else {
|
Chris@403
|
3832 for (int i = 0; i < count; ++i) {
|
Chris@403
|
3833 if (currentPane->getFixedOrderLayer(i) == currentLayer) {
|
Chris@403
|
3834 if (i == 0) {
|
Chris@403
|
3835 m_paneStack->setCurrentLayer
|
Chris@636
|
3836 (currentPane, nullptr); // pane
|
Chris@403
|
3837 } else {
|
Chris@403
|
3838 m_paneStack->setCurrentLayer
|
Chris@403
|
3839 (currentPane, currentPane->getFixedOrderLayer(i-1));
|
Chris@403
|
3840 }
|
Chris@403
|
3841 break;
|
Chris@403
|
3842 }
|
Chris@73
|
3843 }
|
Chris@73
|
3844 }
|
Chris@403
|
3845
|
Chris@403
|
3846 updateMenuStates();
|
Chris@73
|
3847 }
|
Chris@73
|
3848
|
Chris@73
|
3849 void
|
Chris@73
|
3850 MainWindowBase::nextLayer()
|
Chris@73
|
3851 {
|
Chris@73
|
3852 if (!m_paneStack) return;
|
Chris@73
|
3853
|
Chris@73
|
3854 Pane *currentPane = m_paneStack->getCurrentPane();
|
Chris@73
|
3855 if (!currentPane) return;
|
Chris@73
|
3856
|
Chris@403
|
3857 int count = currentPane->getLayerCount();
|
Chris@403
|
3858 if (count == 0) return;
|
Chris@403
|
3859
|
Chris@73
|
3860 Layer *currentLayer = currentPane->getSelectedLayer();
|
Chris@403
|
3861
|
Chris@403
|
3862 if (!currentLayer) {
|
Chris@403
|
3863 // The pane itself is current
|
Chris@403
|
3864 m_paneStack->setCurrentLayer
|
Chris@403
|
3865 (currentPane, currentPane->getFixedOrderLayer(0));
|
Chris@403
|
3866 } else {
|
Chris@403
|
3867 for (int i = 0; i < count; ++i) {
|
Chris@403
|
3868 if (currentPane->getFixedOrderLayer(i) == currentLayer) {
|
Chris@403
|
3869 if (i == currentPane->getLayerCount()-1) {
|
Chris@403
|
3870 m_paneStack->setCurrentLayer
|
Chris@636
|
3871 (currentPane, nullptr); // pane
|
Chris@403
|
3872 } else {
|
Chris@403
|
3873 m_paneStack->setCurrentLayer
|
Chris@403
|
3874 (currentPane, currentPane->getFixedOrderLayer(i+1));
|
Chris@403
|
3875 }
|
Chris@403
|
3876 break;
|
Chris@403
|
3877 }
|
Chris@73
|
3878 }
|
Chris@73
|
3879 }
|
Chris@403
|
3880
|
Chris@403
|
3881 updateMenuStates();
|
Chris@73
|
3882 }
|
Chris@73
|
3883
|
Chris@73
|
3884 void
|
Chris@435
|
3885 MainWindowBase::playbackFrameChanged(sv_frame_t frame)
|
Chris@45
|
3886 {
|
Chris@45
|
3887 if (!(m_playSource && m_playSource->isPlaying()) || !getMainModel()) return;
|
Chris@45
|
3888
|
Chris@187
|
3889 updatePositionStatusDisplays();
|
Chris@187
|
3890
|
Chris@45
|
3891 RealTime now = RealTime::frame2RealTime
|
Chris@45
|
3892 (frame, getMainModel()->getSampleRate());
|
Chris@45
|
3893
|
Chris@45
|
3894 if (now.sec == m_lastPlayStatusSec) return;
|
Chris@45
|
3895
|
Chris@45
|
3896 RealTime then = RealTime::frame2RealTime
|
Chris@45
|
3897 (m_playSource->getPlayEndFrame(), getMainModel()->getSampleRate());
|
Chris@45
|
3898
|
Chris@45
|
3899 QString nowStr;
|
Chris@45
|
3900 QString thenStr;
|
Chris@45
|
3901 QString remainingStr;
|
Chris@45
|
3902
|
Chris@45
|
3903 if (then.sec > 10) {
|
Chris@45
|
3904 nowStr = now.toSecText().c_str();
|
Chris@45
|
3905 thenStr = then.toSecText().c_str();
|
Chris@45
|
3906 remainingStr = (then - now).toSecText().c_str();
|
Chris@45
|
3907 m_lastPlayStatusSec = now.sec;
|
Chris@45
|
3908 } else {
|
Chris@45
|
3909 nowStr = now.toText(true).c_str();
|
Chris@45
|
3910 thenStr = then.toText(true).c_str();
|
Chris@45
|
3911 remainingStr = (then - now).toText(true).c_str();
|
Chris@45
|
3912 }
|
Chris@45
|
3913
|
Chris@45
|
3914 m_myStatusMessage = tr("Playing: %1 of %2 (%3 remaining)")
|
Chris@45
|
3915 .arg(nowStr).arg(thenStr).arg(remainingStr);
|
Chris@45
|
3916
|
Chris@378
|
3917 getStatusLabel()->setText(m_myStatusMessage);
|
Chris@45
|
3918 }
|
Chris@45
|
3919
|
Chris@45
|
3920 void
|
Chris@486
|
3921 MainWindowBase::recordDurationChanged(sv_frame_t frame, sv_samplerate_t rate)
|
Chris@486
|
3922 {
|
Chris@486
|
3923 RealTime duration = RealTime::frame2RealTime(frame, rate);
|
Chris@486
|
3924 QString durStr = duration.toSecText().c_str();
|
Chris@486
|
3925
|
Chris@486
|
3926 m_myStatusMessage = tr("Recording: %1").arg(durStr);
|
Chris@486
|
3927
|
Chris@486
|
3928 getStatusLabel()->setText(m_myStatusMessage);
|
Chris@486
|
3929 }
|
Chris@486
|
3930
|
Chris@486
|
3931 void
|
Chris@435
|
3932 MainWindowBase::globalCentreFrameChanged(sv_frame_t )
|
Chris@45
|
3933 {
|
Chris@45
|
3934 if ((m_playSource && m_playSource->isPlaying()) || !getMainModel()) return;
|
Chris@636
|
3935 Pane *p = nullptr;
|
Chris@45
|
3936 if (!m_paneStack || !(p = m_paneStack->getCurrentPane())) return;
|
Chris@45
|
3937 if (!p->getFollowGlobalPan()) return;
|
Chris@45
|
3938 updateVisibleRangeDisplay(p);
|
Chris@45
|
3939 }
|
Chris@45
|
3940
|
Chris@45
|
3941 void
|
Chris@435
|
3942 MainWindowBase::viewCentreFrameChanged(View *v, sv_frame_t frame)
|
Chris@45
|
3943 {
|
Chris@233
|
3944 // SVDEBUG << "MainWindowBase::viewCentreFrameChanged(" << v << "," << frame << ")" << endl;
|
Chris@123
|
3945
|
Chris@123
|
3946 if (m_viewDataDialogMap.find(v) != m_viewDataDialogMap.end()) {
|
Chris@123
|
3947 for (DataDialogSet::iterator i = m_viewDataDialogMap[v].begin();
|
Chris@123
|
3948 i != m_viewDataDialogMap[v].end(); ++i) {
|
Chris@127
|
3949 (*i)->userScrolledToFrame(frame);
|
Chris@123
|
3950 }
|
Chris@123
|
3951 }
|
Chris@45
|
3952 if ((m_playSource && m_playSource->isPlaying()) || !getMainModel()) return;
|
Chris@636
|
3953 Pane *p = nullptr;
|
Chris@45
|
3954 if (!m_paneStack || !(p = m_paneStack->getCurrentPane())) return;
|
Chris@45
|
3955 if (v == p) updateVisibleRangeDisplay(p);
|
Chris@45
|
3956 }
|
Chris@45
|
3957
|
Chris@45
|
3958 void
|
Chris@624
|
3959 MainWindowBase::viewZoomLevelChanged(View *v, ZoomLevel, bool )
|
Chris@45
|
3960 {
|
Chris@45
|
3961 if ((m_playSource && m_playSource->isPlaying()) || !getMainModel()) return;
|
Chris@636
|
3962 Pane *p = nullptr;
|
Chris@45
|
3963 if (!m_paneStack || !(p = m_paneStack->getCurrentPane())) return;
|
Chris@45
|
3964 if (v == p) updateVisibleRangeDisplay(p);
|
Chris@45
|
3965 }
|
Chris@45
|
3966
|
Chris@45
|
3967 void
|
Chris@45
|
3968 MainWindowBase::layerAdded(Layer *)
|
Chris@45
|
3969 {
|
Chris@233
|
3970 // SVDEBUG << "MainWindowBase::layerAdded(" << layer << ")" << endl;
|
Chris@45
|
3971 updateMenuStates();
|
Chris@45
|
3972 }
|
Chris@45
|
3973
|
Chris@45
|
3974 void
|
Chris@45
|
3975 MainWindowBase::layerRemoved(Layer *)
|
Chris@45
|
3976 {
|
Chris@233
|
3977 // SVDEBUG << "MainWindowBase::layerRemoved(" << layer << ")" << endl;
|
Chris@45
|
3978 updateMenuStates();
|
Chris@45
|
3979 }
|
Chris@45
|
3980
|
Chris@45
|
3981 void
|
Chris@45
|
3982 MainWindowBase::layerAboutToBeDeleted(Layer *layer)
|
Chris@45
|
3983 {
|
Chris@233
|
3984 // SVDEBUG << "MainWindowBase::layerAboutToBeDeleted(" << layer << ")" << endl;
|
Chris@123
|
3985
|
Chris@128
|
3986 removeLayerEditDialog(layer);
|
Chris@123
|
3987
|
Chris@47
|
3988 if (m_timeRulerLayer && (layer == m_timeRulerLayer)) {
|
Chris@595
|
3989 // cerr << "(this is the time ruler layer)" << endl;
|
Chris@636
|
3990 m_timeRulerLayer = nullptr;
|
Chris@45
|
3991 }
|
Chris@45
|
3992 }
|
Chris@45
|
3993
|
Chris@45
|
3994 void
|
Chris@45
|
3995 MainWindowBase::layerInAView(Layer *layer, bool inAView)
|
Chris@45
|
3996 {
|
Chris@233
|
3997 // SVDEBUG << "MainWindowBase::layerInAView(" << layer << "," << inAView << ")" << endl;
|
Chris@128
|
3998
|
Chris@128
|
3999 if (!inAView) removeLayerEditDialog(layer);
|
Chris@45
|
4000
|
Chris@45
|
4001 // Check whether we need to add or remove model from play source
|
Chris@45
|
4002 Model *model = layer->getModel();
|
Chris@45
|
4003 if (model) {
|
Chris@45
|
4004 if (inAView) {
|
Chris@45
|
4005 m_playSource->addModel(model);
|
Chris@45
|
4006 } else {
|
Chris@45
|
4007 bool found = false;
|
Chris@45
|
4008 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
|
Chris@45
|
4009 Pane *pane = m_paneStack->getPane(i);
|
Chris@45
|
4010 if (!pane) continue;
|
Chris@45
|
4011 for (int j = 0; j < pane->getLayerCount(); ++j) {
|
Chris@45
|
4012 Layer *pl = pane->getLayer(j);
|
Chris@183
|
4013 if (pl &&
|
Chris@183
|
4014 !dynamic_cast<TimeRulerLayer *>(pl) &&
|
Chris@183
|
4015 (pl->getModel() == model)) {
|
Chris@45
|
4016 found = true;
|
Chris@45
|
4017 break;
|
Chris@45
|
4018 }
|
Chris@45
|
4019 }
|
Chris@45
|
4020 if (found) break;
|
Chris@45
|
4021 }
|
Chris@173
|
4022 if (!found) {
|
Chris@173
|
4023 m_playSource->removeModel(model);
|
Chris@173
|
4024 }
|
Chris@45
|
4025 }
|
Chris@45
|
4026 }
|
Chris@45
|
4027
|
Chris@45
|
4028 updateMenuStates();
|
Chris@45
|
4029 }
|
Chris@45
|
4030
|
Chris@45
|
4031 void
|
Chris@128
|
4032 MainWindowBase::removeLayerEditDialog(Layer *layer)
|
Chris@128
|
4033 {
|
Chris@128
|
4034 if (m_layerDataDialogMap.find(layer) != m_layerDataDialogMap.end()) {
|
Chris@128
|
4035
|
Chris@128
|
4036 ModelDataTableDialog *dialog = m_layerDataDialogMap[layer];
|
Chris@128
|
4037
|
Chris@128
|
4038 for (ViewDataDialogMap::iterator vi = m_viewDataDialogMap.begin();
|
Chris@128
|
4039 vi != m_viewDataDialogMap.end(); ++vi) {
|
Chris@128
|
4040 vi->second.erase(dialog);
|
Chris@128
|
4041 }
|
Chris@128
|
4042
|
Chris@128
|
4043 m_layerDataDialogMap.erase(layer);
|
Chris@128
|
4044 delete dialog;
|
Chris@128
|
4045 }
|
Chris@128
|
4046 }
|
Chris@128
|
4047
|
Chris@128
|
4048 void
|
Chris@45
|
4049 MainWindowBase::modelAdded(Model *model)
|
Chris@45
|
4050 {
|
Chris@233
|
4051 // SVDEBUG << "MainWindowBase::modelAdded(" << model << ")" << endl;
|
Chris@595
|
4052 std::cerr << "\nAdding model " << model->getTypeName() << " to playsource " << std::endl;
|
Chris@45
|
4053 m_playSource->addModel(model);
|
Chris@45
|
4054 }
|
Chris@45
|
4055
|
Chris@45
|
4056 void
|
Chris@45
|
4057 MainWindowBase::mainModelChanged(WaveFileModel *model)
|
Chris@45
|
4058 {
|
Chris@233
|
4059 // SVDEBUG << "MainWindowBase::mainModelChanged(" << model << ")" << endl;
|
Chris@45
|
4060 updateDescriptionLabel();
|
Chris@45
|
4061 if (model) m_viewManager->setMainModelSampleRate(model->getSampleRate());
|
Chris@475
|
4062 if (model && !(m_playTarget || m_audioIO) &&
|
Chris@475
|
4063 (m_soundOptions & WithAudioOutput)) {
|
Chris@475
|
4064 createAudioIO();
|
Chris@360
|
4065 }
|
Chris@45
|
4066 }
|
Chris@45
|
4067
|
Chris@45
|
4068 void
|
Chris@45
|
4069 MainWindowBase::modelAboutToBeDeleted(Model *model)
|
Chris@45
|
4070 {
|
Chris@233
|
4071 // SVDEBUG << "MainWindowBase::modelAboutToBeDeleted(" << model << ")" << endl;
|
Chris@45
|
4072 if (model == m_viewManager->getPlaybackModel()) {
|
Chris@636
|
4073 m_viewManager->setPlaybackModel(nullptr);
|
Chris@45
|
4074 }
|
Chris@45
|
4075 m_playSource->removeModel(model);
|
Chris@45
|
4076 }
|
Chris@45
|
4077
|
Chris@45
|
4078 void
|
Chris@55
|
4079 MainWindowBase::paneDeleteButtonClicked(Pane *pane)
|
Chris@55
|
4080 {
|
Chris@55
|
4081 bool found = false;
|
Chris@55
|
4082 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) {
|
Chris@55
|
4083 if (m_paneStack->getPane(i) == pane) {
|
Chris@55
|
4084 found = true;
|
Chris@55
|
4085 break;
|
Chris@55
|
4086 }
|
Chris@55
|
4087 }
|
Chris@55
|
4088 if (!found) {
|
Chris@233
|
4089 SVDEBUG << "MainWindowBase::paneDeleteButtonClicked: Unknown pane "
|
Chris@229
|
4090 << pane << endl;
|
Chris@55
|
4091 return;
|
Chris@55
|
4092 }
|
Chris@55
|
4093
|
Chris@55
|
4094 CommandHistory::getInstance()->startCompoundOperation
|
Chris@595
|
4095 (tr("Delete Pane"), true);
|
Chris@55
|
4096
|
Chris@55
|
4097 while (pane->getLayerCount() > 0) {
|
Chris@637
|
4098 Layer *layer = pane->getLayer(pane->getLayerCount() - 1);
|
Chris@55
|
4099 if (layer) {
|
Chris@55
|
4100 m_document->removeLayerFromView(pane, layer);
|
Chris@55
|
4101 } else {
|
Chris@55
|
4102 break;
|
Chris@55
|
4103 }
|
Chris@55
|
4104 }
|
Chris@55
|
4105
|
Chris@55
|
4106 RemovePaneCommand *command = new RemovePaneCommand(this, pane);
|
Chris@55
|
4107 CommandHistory::getInstance()->addCommand(command);
|
Chris@55
|
4108
|
Chris@55
|
4109 CommandHistory::getInstance()->endCompoundOperation();
|
Chris@55
|
4110
|
Chris@55
|
4111 updateMenuStates();
|
Chris@55
|
4112 }
|
Chris@55
|
4113
|
Chris@55
|
4114 void
|
Chris@429
|
4115 MainWindowBase::alignmentComplete(AlignmentModel *model)
|
Chris@429
|
4116 {
|
Chris@429
|
4117 cerr << "MainWindowBase::alignmentComplete(" << model << ")" << endl;
|
Chris@429
|
4118 }
|
Chris@429
|
4119
|
Chris@429
|
4120 void
|
Chris@45
|
4121 MainWindowBase::pollOSC()
|
Chris@45
|
4122 {
|
Chris@45
|
4123 if (!m_oscQueue || m_oscQueue->isEmpty()) return;
|
Chris@233
|
4124 SVDEBUG << "MainWindowBase::pollOSC: have " << m_oscQueue->getMessagesAvailable() << " messages" << endl;
|
Chris@45
|
4125
|
Chris@45
|
4126 if (m_openingAudioFile) return;
|
Chris@45
|
4127
|
Chris@45
|
4128 OSCMessage message = m_oscQueue->readMessage();
|
Chris@45
|
4129
|
Chris@45
|
4130 if (message.getTarget() != 0) {
|
Chris@45
|
4131 return; //!!! for now -- this class is target 0, others not handled yet
|
Chris@45
|
4132 }
|
Chris@45
|
4133
|
Chris@45
|
4134 handleOSCMessage(message);
|
Chris@45
|
4135 }
|
Chris@45
|
4136
|
Chris@45
|
4137 void
|
Chris@45
|
4138 MainWindowBase::inProgressSelectionChanged()
|
Chris@45
|
4139 {
|
Chris@636
|
4140 Pane *currentPane = nullptr;
|
Chris@45
|
4141 if (m_paneStack) currentPane = m_paneStack->getCurrentPane();
|
justin@331
|
4142 if (currentPane) {
|
justin@331
|
4143 //cerr << "JTEST: mouse event on selection pane" << endl;
|
justin@331
|
4144 updateVisibleRangeDisplay(currentPane);
|
justin@331
|
4145 }
|
Chris@45
|
4146 }
|
Chris@45
|
4147
|
Chris@45
|
4148 void
|
Chris@45
|
4149 MainWindowBase::contextHelpChanged(const QString &s)
|
Chris@45
|
4150 {
|
Chris@378
|
4151 QLabel *lab = getStatusLabel();
|
Chris@375
|
4152
|
Chris@45
|
4153 if (s == "" && m_myStatusMessage != "") {
|
Chris@378
|
4154 if (lab->text() != m_myStatusMessage) {
|
Chris@378
|
4155 lab->setText(m_myStatusMessage);
|
Chris@375
|
4156 }
|
Chris@45
|
4157 return;
|
Chris@45
|
4158 }
|
Chris@375
|
4159
|
Chris@378
|
4160 lab->setText(s);
|
Chris@45
|
4161 }
|
Chris@45
|
4162
|
Chris@45
|
4163 void
|
Chris@45
|
4164 MainWindowBase::openHelpUrl(QString url)
|
Chris@45
|
4165 {
|
Chris@45
|
4166 // This method mostly lifted from Qt Assistant source code
|
Chris@45
|
4167
|
Chris@45
|
4168 QProcess *process = new QProcess(this);
|
Chris@45
|
4169 connect(process, SIGNAL(finished(int)), process, SLOT(deleteLater()));
|
Chris@45
|
4170
|
Chris@45
|
4171 QStringList args;
|
Chris@45
|
4172
|
Chris@45
|
4173 #ifdef Q_OS_MAC
|
Chris@45
|
4174 args.append(url);
|
Chris@45
|
4175 process->start("open", args);
|
Chris@45
|
4176 #else
|
Chris@45
|
4177 #ifdef Q_OS_WIN32
|
Chris@599
|
4178 std::string pfiles;
|
Chris@599
|
4179 (void)getEnvUtf8("ProgramFiles", pfiles);
|
Chris@599
|
4180 QString command =
|
Chris@599
|
4181 QString::fromStdString(pfiles) +
|
Chris@599
|
4182 QString("\\Internet Explorer\\IEXPLORE.EXE");
|
Chris@358
|
4183
|
Chris@358
|
4184 args.append(url);
|
Chris@358
|
4185 process->start(command, args);
|
Chris@45
|
4186 #else
|
Chris@45
|
4187 if (!qgetenv("KDE_FULL_SESSION").isEmpty()) {
|
Chris@45
|
4188 args.append("exec");
|
Chris@45
|
4189 args.append(url);
|
Chris@45
|
4190 process->start("kfmclient", args);
|
Chris@45
|
4191 } else if (!qgetenv("BROWSER").isEmpty()) {
|
Chris@45
|
4192 args.append(url);
|
Chris@45
|
4193 process->start(qgetenv("BROWSER"), args);
|
Chris@45
|
4194 } else {
|
Chris@45
|
4195 args.append(url);
|
Chris@45
|
4196 process->start("firefox", args);
|
Chris@45
|
4197 }
|
Chris@45
|
4198 #endif
|
Chris@45
|
4199 #endif
|
Chris@45
|
4200 }
|
Chris@45
|
4201
|
Chris@483
|
4202 void
|
Chris@483
|
4203 MainWindowBase::openLocalFolder(QString path)
|
Chris@483
|
4204 {
|
Chris@483
|
4205 QDir d(path);
|
Chris@483
|
4206 if (d.exists()) {
|
Chris@483
|
4207 QStringList args;
|
Chris@483
|
4208 QString path = d.canonicalPath();
|
Chris@483
|
4209 #if defined Q_OS_WIN32
|
Chris@483
|
4210 // Although the Win32 API is quite happy to have
|
Chris@483
|
4211 // forward slashes as directory separators, Windows
|
Chris@483
|
4212 // Explorer is not
|
Chris@483
|
4213 path = path.replace('/', '\\');
|
Chris@483
|
4214 args << path;
|
Chris@483
|
4215 QProcess::execute("c:/windows/explorer.exe", args);
|
Chris@483
|
4216 #else
|
Chris@483
|
4217 args << path;
|
Chris@605
|
4218 QProcess process;
|
Chris@605
|
4219 QProcessEnvironment env = QProcessEnvironment::systemEnvironment();
|
Chris@606
|
4220 env.insert("LD_LIBRARY_PATH", "");
|
Chris@605
|
4221 process.setProcessEnvironment(env);
|
Chris@605
|
4222 process.start(
|
Chris@483
|
4223 #if defined Q_OS_MAC
|
Chris@483
|
4224 "/usr/bin/open",
|
Chris@483
|
4225 #else
|
Chris@483
|
4226 "/usr/bin/xdg-open",
|
Chris@483
|
4227 #endif
|
Chris@483
|
4228 args);
|
Chris@608
|
4229 process.waitForFinished();
|
Chris@483
|
4230 #endif
|
Chris@483
|
4231 }
|
Chris@483
|
4232 }
|
Chris@483
|
4233
|