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