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