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