Mercurial > hg > sonic-visualiser
comparison main/MainWindow.cpp @ 0:cd5d7ff8ef38
* Reorganising code base. This revision will not compile.
author | Chris Cannam |
---|---|
date | Mon, 31 Jul 2006 12:03:45 +0000 |
parents | |
children | 40116f709d3b |
comparison
equal
deleted
inserted
replaced
-1:000000000000 | 0:cd5d7ff8ef38 |
---|---|
1 /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ | |
2 | |
3 /* | |
4 Sonic Visualiser | |
5 An audio file viewer and annotation editor. | |
6 Centre for Digital Music, Queen Mary, University of London. | |
7 This file copyright 2006 Chris Cannam. | |
8 | |
9 This program is free software; you can redistribute it and/or | |
10 modify it under the terms of the GNU General Public License as | |
11 published by the Free Software Foundation; either version 2 of the | |
12 License, or (at your option) any later version. See the file | |
13 COPYING included with this distribution for more information. | |
14 */ | |
15 | |
16 #include "../version.h" | |
17 | |
18 #include "MainWindow.h" | |
19 #include "Document.h" | |
20 #include "PreferencesDialog.h" | |
21 | |
22 #include "widgets/Pane.h" | |
23 #include "widgets/PaneStack.h" | |
24 #include "model/WaveFileModel.h" | |
25 #include "model/SparseOneDimensionalModel.h" | |
26 #include "base/ViewManager.h" | |
27 #include "base/Preferences.h" | |
28 #include "layer/WaveformLayer.h" | |
29 #include "layer/TimeRulerLayer.h" | |
30 #include "layer/TimeInstantLayer.h" | |
31 #include "layer/TimeValueLayer.h" | |
32 #include "layer/Colour3DPlotLayer.h" | |
33 #include "widgets/Fader.h" | |
34 #include "widgets/Panner.h" | |
35 #include "widgets/PropertyBox.h" | |
36 #include "widgets/PropertyStack.h" | |
37 #include "widgets/AudioDial.h" | |
38 #include "widgets/LayerTree.h" | |
39 #include "widgets/ListInputDialog.h" | |
40 #include "audioio/AudioCallbackPlaySource.h" | |
41 #include "audioio/AudioCallbackPlayTarget.h" | |
42 #include "audioio/AudioTargetFactory.h" | |
43 #include "fileio/AudioFileReaderFactory.h" | |
44 #include "fileio/DataFileReaderFactory.h" | |
45 #include "fileio/WavFileWriter.h" | |
46 #include "fileio/CSVFileWriter.h" | |
47 #include "fileio/BZipFileDevice.h" | |
48 #include "fileio/RecentFiles.h" | |
49 #include "transform/TransformFactory.h" | |
50 #include "base/PlayParameterRepository.h" | |
51 #include "base/XmlExportable.h" | |
52 #include "base/CommandHistory.h" | |
53 #include "base/Profiler.h" | |
54 #include "base/Clipboard.h" | |
55 | |
56 // For version information | |
57 #include "vamp/vamp.h" | |
58 #include "vamp-sdk/PluginBase.h" | |
59 #include "plugin/api/ladspa.h" | |
60 #include "plugin/api/dssi.h" | |
61 | |
62 #include <QApplication> | |
63 #include <QPushButton> | |
64 #include <QFileDialog> | |
65 #include <QMessageBox> | |
66 #include <QGridLayout> | |
67 #include <QLabel> | |
68 #include <QAction> | |
69 #include <QMenuBar> | |
70 #include <QToolBar> | |
71 #include <QInputDialog> | |
72 #include <QStatusBar> | |
73 #include <QTreeView> | |
74 #include <QFile> | |
75 #include <QTextStream> | |
76 #include <QProcess> | |
77 | |
78 #include <iostream> | |
79 #include <cstdio> | |
80 #include <errno.h> | |
81 | |
82 using std::cerr; | |
83 using std::endl; | |
84 | |
85 | |
86 MainWindow::MainWindow() : | |
87 m_document(0), | |
88 m_paneStack(0), | |
89 m_viewManager(0), | |
90 m_panner(0), | |
91 m_timeRulerLayer(0), | |
92 m_playSource(0), | |
93 m_playTarget(0), | |
94 m_mainMenusCreated(false), | |
95 m_paneMenu(0), | |
96 m_layerMenu(0), | |
97 m_existingLayersMenu(0), | |
98 m_rightButtonMenu(0), | |
99 m_rightButtonLayerMenu(0), | |
100 m_documentModified(false), | |
101 m_preferencesDialog(0) | |
102 { | |
103 setWindowTitle(tr("Sonic Visualiser")); | |
104 | |
105 UnitDatabase::getInstance()->registerUnit("Hz"); | |
106 UnitDatabase::getInstance()->registerUnit("dB"); | |
107 | |
108 connect(CommandHistory::getInstance(), SIGNAL(commandExecuted()), | |
109 this, SLOT(documentModified())); | |
110 connect(CommandHistory::getInstance(), SIGNAL(documentRestored()), | |
111 this, SLOT(documentRestored())); | |
112 | |
113 QFrame *frame = new QFrame; | |
114 setCentralWidget(frame); | |
115 | |
116 QGridLayout *layout = new QGridLayout; | |
117 | |
118 m_viewManager = new ViewManager(); | |
119 connect(m_viewManager, SIGNAL(selectionChanged()), | |
120 this, SLOT(updateMenuStates())); | |
121 | |
122 m_descriptionLabel = new QLabel; | |
123 | |
124 m_paneStack = new PaneStack(frame, m_viewManager); | |
125 connect(m_paneStack, SIGNAL(currentPaneChanged(Pane *)), | |
126 this, SLOT(currentPaneChanged(Pane *))); | |
127 connect(m_paneStack, SIGNAL(currentLayerChanged(Pane *, Layer *)), | |
128 this, SLOT(currentLayerChanged(Pane *, Layer *))); | |
129 connect(m_paneStack, SIGNAL(rightButtonMenuRequested(Pane *, QPoint)), | |
130 this, SLOT(rightButtonMenuRequested(Pane *, QPoint))); | |
131 | |
132 m_panner = new Panner(frame); | |
133 m_panner->setViewManager(m_viewManager); | |
134 m_panner->setFixedHeight(40); | |
135 | |
136 m_panLayer = new WaveformLayer; | |
137 m_panLayer->setChannelMode(WaveformLayer::MergeChannels); | |
138 // m_panLayer->setScale(WaveformLayer::MeterScale); | |
139 m_panLayer->setAutoNormalize(true); | |
140 m_panLayer->setBaseColour(Qt::darkGreen); | |
141 m_panLayer->setAggressiveCacheing(true); | |
142 m_panner->addLayer(m_panLayer); | |
143 | |
144 m_playSource = new AudioCallbackPlaySource(m_viewManager); | |
145 | |
146 connect(m_playSource, SIGNAL(sampleRateMismatch(size_t, size_t, bool)), | |
147 this, SLOT(sampleRateMismatch(size_t, size_t, bool))); | |
148 | |
149 m_fader = new Fader(frame, false); | |
150 | |
151 m_playSpeed = new AudioDial(frame); | |
152 m_playSpeed->setMinimum(1); | |
153 m_playSpeed->setMaximum(10); | |
154 m_playSpeed->setValue(10); | |
155 m_playSpeed->setFixedWidth(24); | |
156 m_playSpeed->setFixedHeight(24); | |
157 m_playSpeed->setNotchesVisible(true); | |
158 m_playSpeed->setPageStep(1); | |
159 m_playSpeed->setToolTip(tr("Playback speed: Full")); | |
160 m_playSpeed->setDefaultValue(10); | |
161 connect(m_playSpeed, SIGNAL(valueChanged(int)), | |
162 this, SLOT(playSpeedChanged(int))); | |
163 | |
164 layout->addWidget(m_paneStack, 0, 0, 1, 3); | |
165 layout->addWidget(m_panner, 1, 0); | |
166 layout->addWidget(m_fader, 1, 1); | |
167 layout->addWidget(m_playSpeed, 1, 2); | |
168 frame->setLayout(layout); | |
169 | |
170 connect(m_viewManager, SIGNAL(outputLevelsChanged(float, float)), | |
171 this, SLOT(outputLevelsChanged(float, float))); | |
172 | |
173 connect(Preferences::getInstance(), | |
174 SIGNAL(propertyChanged(PropertyContainer::PropertyName)), | |
175 this, | |
176 SLOT(preferenceChanged(PropertyContainer::PropertyName))); | |
177 | |
178 setupMenus(); | |
179 setupToolbars(); | |
180 | |
181 // statusBar()->addWidget(m_descriptionLabel); | |
182 | |
183 newSession(); | |
184 } | |
185 | |
186 MainWindow::~MainWindow() | |
187 { | |
188 closeSession(); | |
189 delete m_playTarget; | |
190 delete m_playSource; | |
191 delete m_viewManager; | |
192 Profiles::getInstance()->dump(); | |
193 } | |
194 | |
195 void | |
196 MainWindow::setupMenus() | |
197 { | |
198 QAction *action = 0; | |
199 QMenu *menu = 0; | |
200 QToolBar *toolbar = 0; | |
201 | |
202 if (!m_mainMenusCreated) { | |
203 m_rightButtonMenu = new QMenu(); | |
204 } | |
205 | |
206 if (m_rightButtonLayerMenu) { | |
207 m_rightButtonLayerMenu->clear(); | |
208 } else { | |
209 m_rightButtonLayerMenu = m_rightButtonMenu->addMenu(tr("&Layer")); | |
210 m_rightButtonMenu->addSeparator(); | |
211 } | |
212 | |
213 if (!m_mainMenusCreated) { | |
214 | |
215 CommandHistory::getInstance()->registerMenu(m_rightButtonMenu); | |
216 m_rightButtonMenu->addSeparator(); | |
217 | |
218 menu = menuBar()->addMenu(tr("&File")); | |
219 toolbar = addToolBar(tr("File Toolbar")); | |
220 | |
221 QIcon icon(":icons/filenew.png"); | |
222 icon.addFile(":icons/filenew-22.png"); | |
223 action = new QAction(icon, tr("&New Session"), this); | |
224 action->setShortcut(tr("Ctrl+N")); | |
225 action->setStatusTip(tr("Clear the current Sonic Visualiser session and start a new one")); | |
226 connect(action, SIGNAL(triggered()), this, SLOT(newSession())); | |
227 menu->addAction(action); | |
228 toolbar->addAction(action); | |
229 | |
230 icon = QIcon(":icons/fileopen.png"); | |
231 icon.addFile(":icons/fileopen-22.png"); | |
232 | |
233 action = new QAction(icon, tr("&Open Session..."), this); | |
234 action->setShortcut(tr("Ctrl+O")); | |
235 action->setStatusTip(tr("Open a previously saved Sonic Visualiser session file")); | |
236 connect(action, SIGNAL(triggered()), this, SLOT(openSession())); | |
237 menu->addAction(action); | |
238 | |
239 action = new QAction(icon, tr("&Open..."), this); | |
240 action->setStatusTip(tr("Open a session file, audio file, or layer")); | |
241 connect(action, SIGNAL(triggered()), this, SLOT(openSomething())); | |
242 toolbar->addAction(action); | |
243 | |
244 icon = QIcon(":icons/filesave.png"); | |
245 icon.addFile(":icons/filesave-22.png"); | |
246 action = new QAction(icon, tr("&Save Session"), this); | |
247 action->setShortcut(tr("Ctrl+S")); | |
248 action->setStatusTip(tr("Save the current session into a Sonic Visualiser session file")); | |
249 connect(action, SIGNAL(triggered()), this, SLOT(saveSession())); | |
250 connect(this, SIGNAL(canSave(bool)), action, SLOT(setEnabled(bool))); | |
251 menu->addAction(action); | |
252 toolbar->addAction(action); | |
253 | |
254 icon = QIcon(":icons/filesaveas.png"); | |
255 icon.addFile(":icons/filesaveas-22.png"); | |
256 action = new QAction(icon, tr("Save Session &As..."), this); | |
257 action->setStatusTip(tr("Save the current session into a new Sonic Visualiser session file")); | |
258 connect(action, SIGNAL(triggered()), this, SLOT(saveSessionAs())); | |
259 menu->addAction(action); | |
260 toolbar->addAction(action); | |
261 | |
262 menu->addSeparator(); | |
263 | |
264 action = new QAction(tr("&Import Audio File..."), this); | |
265 action->setShortcut(tr("Ctrl+I")); | |
266 action->setStatusTip(tr("Import an existing audio file")); | |
267 connect(action, SIGNAL(triggered()), this, SLOT(importAudio())); | |
268 menu->addAction(action); | |
269 | |
270 action = new QAction(tr("Import Secondary Audio File..."), this); | |
271 action->setShortcut(tr("Ctrl+Shift+I")); | |
272 action->setStatusTip(tr("Import an extra audio file as a separate layer")); | |
273 connect(action, SIGNAL(triggered()), this, SLOT(importMoreAudio())); | |
274 connect(this, SIGNAL(canImportMoreAudio(bool)), action, SLOT(setEnabled(bool))); | |
275 menu->addAction(action); | |
276 | |
277 action = new QAction(tr("&Export Audio File..."), this); | |
278 action->setStatusTip(tr("Export selection as an audio file")); | |
279 connect(action, SIGNAL(triggered()), this, SLOT(exportAudio())); | |
280 connect(this, SIGNAL(canExportAudio(bool)), action, SLOT(setEnabled(bool))); | |
281 menu->addAction(action); | |
282 | |
283 menu->addSeparator(); | |
284 | |
285 action = new QAction(tr("Import Annotation &Layer..."), this); | |
286 action->setShortcut(tr("Ctrl+L")); | |
287 action->setStatusTip(tr("Import layer data from an existing file")); | |
288 connect(action, SIGNAL(triggered()), this, SLOT(importLayer())); | |
289 connect(this, SIGNAL(canImportLayer(bool)), action, SLOT(setEnabled(bool))); | |
290 menu->addAction(action); | |
291 | |
292 action = new QAction(tr("Export Annotation Layer..."), this); | |
293 action->setStatusTip(tr("Export layer data to a file")); | |
294 connect(action, SIGNAL(triggered()), this, SLOT(exportLayer())); | |
295 connect(this, SIGNAL(canExportLayer(bool)), action, SLOT(setEnabled(bool))); | |
296 menu->addAction(action); | |
297 | |
298 menu->addSeparator(); | |
299 m_recentFilesMenu = menu->addMenu(tr("&Recent Files")); | |
300 menu->addMenu(m_recentFilesMenu); | |
301 setupRecentFilesMenu(); | |
302 connect(RecentFiles::getInstance(), SIGNAL(recentFilesChanged()), | |
303 this, SLOT(setupRecentFilesMenu())); | |
304 | |
305 menu->addSeparator(); | |
306 action = new QAction(tr("&Preferences..."), this); | |
307 action->setStatusTip(tr("Adjust the application preferences")); | |
308 connect(action, SIGNAL(triggered()), this, SLOT(preferences())); | |
309 menu->addAction(action); | |
310 | |
311 /*!!! | |
312 menu->addSeparator(); | |
313 | |
314 action = new QAction(tr("Play / Pause"), this); | |
315 action->setShortcut(tr("Space")); | |
316 action->setStatusTip(tr("Start or stop playback from the current position")); | |
317 connect(action, SIGNAL(triggered()), this, SLOT(play())); | |
318 menu->addAction(action); | |
319 */ | |
320 | |
321 menu->addSeparator(); | |
322 action = new QAction(QIcon(":/icons/exit.png"), | |
323 tr("&Quit"), this); | |
324 action->setShortcut(tr("Ctrl+Q")); | |
325 connect(action, SIGNAL(triggered()), this, SLOT(close())); | |
326 menu->addAction(action); | |
327 | |
328 menu = menuBar()->addMenu(tr("&Edit")); | |
329 CommandHistory::getInstance()->registerMenu(menu); | |
330 | |
331 menu->addSeparator(); | |
332 | |
333 action = new QAction(QIcon(":/icons/editcut.png"), | |
334 tr("Cu&t"), this); | |
335 action->setShortcut(tr("Ctrl+X")); | |
336 connect(action, SIGNAL(triggered()), this, SLOT(cut())); | |
337 connect(this, SIGNAL(canEditSelection(bool)), action, SLOT(setEnabled(bool))); | |
338 menu->addAction(action); | |
339 m_rightButtonMenu->addAction(action); | |
340 | |
341 action = new QAction(QIcon(":/icons/editcopy.png"), | |
342 tr("&Copy"), this); | |
343 action->setShortcut(tr("Ctrl+C")); | |
344 connect(action, SIGNAL(triggered()), this, SLOT(copy())); | |
345 connect(this, SIGNAL(canEditSelection(bool)), action, SLOT(setEnabled(bool))); | |
346 menu->addAction(action); | |
347 m_rightButtonMenu->addAction(action); | |
348 | |
349 action = new QAction(QIcon(":/icons/editpaste.png"), | |
350 tr("&Paste"), this); | |
351 action->setShortcut(tr("Ctrl+V")); | |
352 connect(action, SIGNAL(triggered()), this, SLOT(paste())); | |
353 connect(this, SIGNAL(canPaste(bool)), action, SLOT(setEnabled(bool))); | |
354 menu->addAction(action); | |
355 m_rightButtonMenu->addAction(action); | |
356 | |
357 action = new QAction(tr("&Delete Selected Items"), this); | |
358 action->setShortcut(tr("Del")); | |
359 connect(action, SIGNAL(triggered()), this, SLOT(deleteSelected())); | |
360 connect(this, SIGNAL(canEditSelection(bool)), action, SLOT(setEnabled(bool))); | |
361 menu->addAction(action); | |
362 m_rightButtonMenu->addAction(action); | |
363 | |
364 menu->addSeparator(); | |
365 m_rightButtonMenu->addSeparator(); | |
366 | |
367 action = new QAction(tr("Select &All"), this); | |
368 action->setShortcut(tr("Ctrl+A")); | |
369 connect(action, SIGNAL(triggered()), this, SLOT(selectAll())); | |
370 connect(this, SIGNAL(canSelect(bool)), action, SLOT(setEnabled(bool))); | |
371 menu->addAction(action); | |
372 m_rightButtonMenu->addAction(action); | |
373 | |
374 action = new QAction(tr("Select &Visible Range"), this); | |
375 action->setShortcut(tr("Ctrl+Shift+A")); | |
376 connect(action, SIGNAL(triggered()), this, SLOT(selectVisible())); | |
377 connect(this, SIGNAL(canSelect(bool)), action, SLOT(setEnabled(bool))); | |
378 menu->addAction(action); | |
379 | |
380 action = new QAction(tr("Select to &Start"), this); | |
381 action->setShortcut(tr("Shift+Left")); | |
382 connect(action, SIGNAL(triggered()), this, SLOT(selectToStart())); | |
383 connect(this, SIGNAL(canSelect(bool)), action, SLOT(setEnabled(bool))); | |
384 menu->addAction(action); | |
385 | |
386 action = new QAction(tr("Select to &End"), this); | |
387 action->setShortcut(tr("Shift+Right")); | |
388 connect(action, SIGNAL(triggered()), this, SLOT(selectToEnd())); | |
389 connect(this, SIGNAL(canSelect(bool)), action, SLOT(setEnabled(bool))); | |
390 menu->addAction(action); | |
391 | |
392 action = new QAction(tr("C&lear Selection"), this); | |
393 action->setShortcut(tr("Esc")); | |
394 connect(action, SIGNAL(triggered()), this, SLOT(clearSelection())); | |
395 connect(this, SIGNAL(canClearSelection(bool)), action, SLOT(setEnabled(bool))); | |
396 menu->addAction(action); | |
397 m_rightButtonMenu->addAction(action); | |
398 | |
399 menu->addSeparator(); | |
400 | |
401 action = new QAction(tr("&Insert Instant at Playback Position"), this); | |
402 action->setShortcut(tr("Enter")); | |
403 connect(action, SIGNAL(triggered()), this, SLOT(insertInstant())); | |
404 connect(this, SIGNAL(canInsertInstant(bool)), action, SLOT(setEnabled(bool))); | |
405 menu->addAction(action); | |
406 | |
407 menu = menuBar()->addMenu(tr("&View")); | |
408 | |
409 QActionGroup *overlayGroup = new QActionGroup(this); | |
410 | |
411 action = new QAction(tr("&No Text Overlays"), this); | |
412 action->setShortcut(tr("0")); | |
413 action->setStatusTip(tr("Show no texts for frame times, layer names etc")); | |
414 connect(action, SIGNAL(triggered()), this, SLOT(showNoOverlays())); | |
415 action->setCheckable(true); | |
416 action->setChecked(false); | |
417 overlayGroup->addAction(action); | |
418 menu->addAction(action); | |
419 | |
420 action = new QAction(tr("Basic &Text Overlays"), this); | |
421 action->setShortcut(tr("9")); | |
422 action->setStatusTip(tr("Show texts for frame times etc, but not layer names etc")); | |
423 connect(action, SIGNAL(triggered()), this, SLOT(showBasicOverlays())); | |
424 action->setCheckable(true); | |
425 action->setChecked(true); | |
426 overlayGroup->addAction(action); | |
427 menu->addAction(action); | |
428 | |
429 action = new QAction(tr("&All Text Overlays"), this); | |
430 action->setShortcut(tr("8")); | |
431 action->setStatusTip(tr("Show texts for frame times, layer names etc")); | |
432 connect(action, SIGNAL(triggered()), this, SLOT(showAllOverlays())); | |
433 action->setCheckable(true); | |
434 action->setChecked(false); | |
435 overlayGroup->addAction(action); | |
436 menu->addAction(action); | |
437 | |
438 menu->addSeparator(); | |
439 | |
440 action = new QAction(tr("Scroll &Left"), this); | |
441 action->setShortcut(tr("Left")); | |
442 action->setStatusTip(tr("Scroll the current pane to the left")); | |
443 connect(action, SIGNAL(triggered()), this, SLOT(scrollLeft())); | |
444 connect(this, SIGNAL(canScroll(bool)), action, SLOT(setEnabled(bool))); | |
445 menu->addAction(action); | |
446 | |
447 action = new QAction(tr("Scroll &Right"), this); | |
448 action->setShortcut(tr("Right")); | |
449 action->setStatusTip(tr("Scroll the current pane to the right")); | |
450 connect(action, SIGNAL(triggered()), this, SLOT(scrollRight())); | |
451 connect(this, SIGNAL(canScroll(bool)), action, SLOT(setEnabled(bool))); | |
452 menu->addAction(action); | |
453 | |
454 action = new QAction(tr("Jump Left"), this); | |
455 action->setShortcut(tr("Ctrl+Left")); | |
456 action->setStatusTip(tr("Scroll the current pane a big step to the left")); | |
457 connect(action, SIGNAL(triggered()), this, SLOT(jumpLeft())); | |
458 connect(this, SIGNAL(canScroll(bool)), action, SLOT(setEnabled(bool))); | |
459 menu->addAction(action); | |
460 | |
461 action = new QAction(tr("Jump Right"), this); | |
462 action->setShortcut(tr("Ctrl+Right")); | |
463 action->setStatusTip(tr("Scroll the current pane a big step to the right")); | |
464 connect(action, SIGNAL(triggered()), this, SLOT(jumpRight())); | |
465 connect(this, SIGNAL(canScroll(bool)), action, SLOT(setEnabled(bool))); | |
466 menu->addAction(action); | |
467 | |
468 menu->addSeparator(); | |
469 | |
470 action = new QAction(QIcon(":/icons/zoom-in.png"), | |
471 tr("Zoom &In"), this); | |
472 action->setShortcut(tr("Up")); | |
473 action->setStatusTip(tr("Increase the zoom level")); | |
474 connect(action, SIGNAL(triggered()), this, SLOT(zoomIn())); | |
475 connect(this, SIGNAL(canZoom(bool)), action, SLOT(setEnabled(bool))); | |
476 menu->addAction(action); | |
477 | |
478 action = new QAction(QIcon(":/icons/zoom-out.png"), | |
479 tr("Zoom &Out"), this); | |
480 action->setShortcut(tr("Down")); | |
481 action->setStatusTip(tr("Decrease the zoom level")); | |
482 connect(action, SIGNAL(triggered()), this, SLOT(zoomOut())); | |
483 connect(this, SIGNAL(canZoom(bool)), action, SLOT(setEnabled(bool))); | |
484 menu->addAction(action); | |
485 | |
486 action = new QAction(tr("Restore &Default Zoom"), this); | |
487 connect(action, SIGNAL(triggered()), this, SLOT(zoomDefault())); | |
488 connect(this, SIGNAL(canZoom(bool)), action, SLOT(setEnabled(bool))); | |
489 menu->addAction(action); | |
490 | |
491 action = new QAction(tr("Zoom to &Fit"), this); | |
492 action->setStatusTip(tr("Zoom to show the whole file")); | |
493 connect(action, SIGNAL(triggered()), this, SLOT(zoomToFit())); | |
494 connect(this, SIGNAL(canZoom(bool)), action, SLOT(setEnabled(bool))); | |
495 menu->addAction(action); | |
496 | |
497 /*!!! This one doesn't work properly yet | |
498 | |
499 menu->addSeparator(); | |
500 | |
501 action = new QAction(tr("Show &Layer Hierarchy"), this); | |
502 action->setShortcut(tr("Alt+L")); | |
503 connect(action, SIGNAL(triggered()), this, SLOT(showLayerTree())); | |
504 menu->addAction(action); | |
505 */ | |
506 } | |
507 | |
508 if (m_paneMenu) { | |
509 m_paneActions.clear(); | |
510 m_paneMenu->clear(); | |
511 } else { | |
512 m_paneMenu = menuBar()->addMenu(tr("&Pane")); | |
513 } | |
514 | |
515 if (m_layerMenu) { | |
516 m_layerTransformActions.clear(); | |
517 m_layerActions.clear(); | |
518 m_layerMenu->clear(); | |
519 } else { | |
520 m_layerMenu = menuBar()->addMenu(tr("&Layer")); | |
521 } | |
522 | |
523 TransformFactory::TransformList transforms = | |
524 TransformFactory::getInstance()->getAllTransforms(); | |
525 | |
526 std::vector<QString> types = | |
527 TransformFactory::getInstance()->getAllTransformTypes(); | |
528 | |
529 std::map<QString, QMenu *> transformMenus; | |
530 | |
531 for (std::vector<QString>::iterator i = types.begin(); i != types.end(); ++i) { | |
532 transformMenus[*i] = m_layerMenu->addMenu(*i); | |
533 m_rightButtonLayerMenu->addMenu(transformMenus[*i]); | |
534 } | |
535 | |
536 for (unsigned int i = 0; i < transforms.size(); ++i) { | |
537 | |
538 QString description = transforms[i].description; | |
539 if (description == "") description = transforms[i].name; | |
540 | |
541 QString actionText = description; | |
542 if (transforms[i].configurable) { | |
543 actionText = QString("%1...").arg(actionText); | |
544 } | |
545 | |
546 action = new QAction(actionText, this); | |
547 connect(action, SIGNAL(triggered()), this, SLOT(addLayer())); | |
548 m_layerTransformActions[action] = transforms[i].name; | |
549 connect(this, SIGNAL(canAddLayer(bool)), action, SLOT(setEnabled(bool))); | |
550 transformMenus[transforms[i].type]->addAction(action); | |
551 } | |
552 | |
553 m_rightButtonLayerMenu->addSeparator(); | |
554 | |
555 menu = m_paneMenu; | |
556 | |
557 action = new QAction(QIcon(":/icons/pane.png"), tr("Add &New Pane"), this); | |
558 action->setShortcut(tr("Alt+N")); | |
559 action->setStatusTip(tr("Add a new pane containing only a time ruler")); | |
560 connect(action, SIGNAL(triggered()), this, SLOT(addPane())); | |
561 connect(this, SIGNAL(canAddPane(bool)), action, SLOT(setEnabled(bool))); | |
562 m_paneActions[action] = PaneConfiguration(LayerFactory::TimeRuler); | |
563 menu->addAction(action); | |
564 | |
565 menu->addSeparator(); | |
566 | |
567 menu = m_layerMenu; | |
568 | |
569 menu->addSeparator(); | |
570 | |
571 LayerFactory::LayerTypeSet emptyLayerTypes = | |
572 LayerFactory::getInstance()->getValidEmptyLayerTypes(); | |
573 | |
574 for (LayerFactory::LayerTypeSet::iterator i = emptyLayerTypes.begin(); | |
575 i != emptyLayerTypes.end(); ++i) { | |
576 | |
577 QIcon icon; | |
578 QString mainText, tipText, channelText; | |
579 LayerFactory::LayerType type = *i; | |
580 QString name = LayerFactory::getInstance()->getLayerPresentationName(type); | |
581 | |
582 icon = QIcon(QString(":/icons/%1.png") | |
583 .arg(LayerFactory::getInstance()->getLayerIconName(type))); | |
584 | |
585 mainText = tr("Add New %1 Layer").arg(name); | |
586 tipText = tr("Add a new empty layer of type %1").arg(name); | |
587 | |
588 action = new QAction(icon, mainText, this); | |
589 action->setStatusTip(tipText); | |
590 | |
591 if (type == LayerFactory::Text) { | |
592 action->setShortcut(tr("Alt+T")); | |
593 } | |
594 | |
595 connect(action, SIGNAL(triggered()), this, SLOT(addLayer())); | |
596 connect(this, SIGNAL(canAddLayer(bool)), action, SLOT(setEnabled(bool))); | |
597 m_layerActions[action] = type; | |
598 menu->addAction(action); | |
599 m_rightButtonLayerMenu->addAction(action); | |
600 } | |
601 | |
602 m_rightButtonLayerMenu->addSeparator(); | |
603 menu->addSeparator(); | |
604 | |
605 int channels = 1; | |
606 if (getMainModel()) channels = getMainModel()->getChannelCount(); | |
607 | |
608 if (channels < 1) channels = 1; | |
609 | |
610 LayerFactory::LayerType backgroundTypes[] = { | |
611 LayerFactory::Waveform, | |
612 LayerFactory::Spectrogram, | |
613 LayerFactory::MelodicRangeSpectrogram, | |
614 LayerFactory::PeakFrequencySpectrogram | |
615 }; | |
616 | |
617 for (unsigned int i = 0; | |
618 i < sizeof(backgroundTypes)/sizeof(backgroundTypes[0]); ++i) { | |
619 | |
620 for (int menuType = 0; menuType <= 1; ++menuType) { // pane, layer | |
621 | |
622 if (menuType == 0) menu = m_paneMenu; | |
623 else menu = m_layerMenu; | |
624 | |
625 QMenu *submenu = 0; | |
626 | |
627 for (int c = 0; c <= channels; ++c) { | |
628 | |
629 if (c == 1 && channels == 1) continue; | |
630 bool isDefault = (c == 0); | |
631 bool isOnly = (isDefault && (channels == 1)); | |
632 | |
633 if (menuType == 1) { | |
634 if (isDefault) isOnly = true; | |
635 else continue; | |
636 } | |
637 | |
638 QIcon icon; | |
639 QString mainText, shortcutText, tipText, channelText; | |
640 LayerFactory::LayerType type = backgroundTypes[i]; | |
641 bool mono = true; | |
642 | |
643 switch (type) { | |
644 | |
645 case LayerFactory::Waveform: | |
646 icon = QIcon(":/icons/waveform.png"); | |
647 mainText = tr("Add &Waveform"); | |
648 if (menuType == 0) { | |
649 shortcutText = tr("Alt+W"); | |
650 tipText = tr("Add a new pane showing a waveform view"); | |
651 } else { | |
652 tipText = tr("Add a new layer showing a waveform view"); | |
653 } | |
654 mono = false; | |
655 break; | |
656 | |
657 case LayerFactory::Spectrogram: | |
658 mainText = tr("Add &Spectrogram"); | |
659 if (menuType == 0) { | |
660 shortcutText = tr("Alt+S"); | |
661 tipText = tr("Add a new pane showing a dB spectrogram"); | |
662 } else { | |
663 tipText = tr("Add a new layer showing a dB spectrogram"); | |
664 } | |
665 break; | |
666 | |
667 case LayerFactory::MelodicRangeSpectrogram: | |
668 mainText = tr("Add &Melodic Range Spectrogram"); | |
669 if (menuType == 0) { | |
670 shortcutText = tr("Alt+M"); | |
671 tipText = tr("Add a new pane showing a spectrogram set up for a pitch overview"); | |
672 } else { | |
673 tipText = tr("Add a new layer showing a spectrogram set up for a pitch overview"); | |
674 } | |
675 break; | |
676 | |
677 case LayerFactory::PeakFrequencySpectrogram: | |
678 mainText = tr("Add &Peak Frequency Spectrogram"); | |
679 if (menuType == 0) { | |
680 shortcutText = tr("Alt+P"); | |
681 tipText = tr("Add a new pane showing a spectrogram set up for tracking frequencies"); | |
682 } else { | |
683 tipText = tr("Add a new layer showing a spectrogram set up for tracking frequencies"); | |
684 } | |
685 break; | |
686 | |
687 default: break; | |
688 } | |
689 | |
690 if (isOnly) { | |
691 | |
692 action = new QAction(icon, mainText, this); | |
693 action->setShortcut(shortcutText); | |
694 action->setStatusTip(tipText); | |
695 if (menuType == 0) { | |
696 connect(action, SIGNAL(triggered()), this, SLOT(addPane())); | |
697 connect(this, SIGNAL(canAddPane(bool)), action, SLOT(setEnabled(bool))); | |
698 m_paneActions[action] = PaneConfiguration(type); | |
699 } else { | |
700 connect(action, SIGNAL(triggered()), this, SLOT(addLayer())); | |
701 connect(this, SIGNAL(canAddLayer(bool)), action, SLOT(setEnabled(bool))); | |
702 m_layerActions[action] = type; | |
703 } | |
704 menu->addAction(action); | |
705 | |
706 } else { | |
707 | |
708 QString actionText; | |
709 if (c == 0) | |
710 if (mono) actionText = tr("&All Channels Mixed"); | |
711 else actionText = tr("&All Channels"); | |
712 else actionText = tr("Channel &%1").arg(c); | |
713 | |
714 if (!submenu) { | |
715 submenu = menu->addMenu(mainText); | |
716 } | |
717 | |
718 action = new QAction(icon, actionText, this); | |
719 if (isDefault) action->setShortcut(shortcutText); | |
720 action->setStatusTip(tipText); | |
721 if (menuType == 0) { | |
722 connect(action, SIGNAL(triggered()), this, SLOT(addPane())); | |
723 connect(this, SIGNAL(canAddPane(bool)), action, SLOT(setEnabled(bool))); | |
724 m_paneActions[action] = PaneConfiguration(type, c - 1); | |
725 } else { | |
726 connect(action, SIGNAL(triggered()), this, SLOT(addLayer())); | |
727 connect(this, SIGNAL(canAddLayer(bool)), action, SLOT(setEnabled(bool))); | |
728 m_layerActions[action] = type; | |
729 } | |
730 submenu->addAction(action); | |
731 } | |
732 } | |
733 } | |
734 } | |
735 | |
736 menu = m_paneMenu; | |
737 | |
738 menu->addSeparator(); | |
739 | |
740 action = new QAction(QIcon(":/icons/editdelete.png"), tr("&Delete Pane"), this); | |
741 action->setShortcut(tr("Alt+D")); | |
742 action->setStatusTip(tr("Delete the currently selected pane")); | |
743 connect(action, SIGNAL(triggered()), this, SLOT(deleteCurrentPane())); | |
744 connect(this, SIGNAL(canDeleteCurrentPane(bool)), action, SLOT(setEnabled(bool))); | |
745 menu->addAction(action); | |
746 | |
747 menu = m_layerMenu; | |
748 | |
749 action = new QAction(QIcon(":/icons/timeruler.png"), tr("Add &Time Ruler"), this); | |
750 action->setStatusTip(tr("Add a new layer showing a time ruler")); | |
751 connect(action, SIGNAL(triggered()), this, SLOT(addLayer())); | |
752 connect(this, SIGNAL(canAddLayer(bool)), action, SLOT(setEnabled(bool))); | |
753 m_layerActions[action] = LayerFactory::TimeRuler; | |
754 menu->addAction(action); | |
755 | |
756 menu->addSeparator(); | |
757 | |
758 m_existingLayersMenu = menu->addMenu(tr("Add &Existing Layer")); | |
759 m_rightButtonLayerMenu->addMenu(m_existingLayersMenu); | |
760 setupExistingLayersMenu(); | |
761 | |
762 m_rightButtonLayerMenu->addSeparator(); | |
763 menu->addSeparator(); | |
764 | |
765 action = new QAction(tr("&Rename Layer..."), this); | |
766 action->setShortcut(tr("Alt+R")); | |
767 action->setStatusTip(tr("Rename the currently active layer")); | |
768 connect(action, SIGNAL(triggered()), this, SLOT(renameCurrentLayer())); | |
769 connect(this, SIGNAL(canRenameLayer(bool)), action, SLOT(setEnabled(bool))); | |
770 menu->addAction(action); | |
771 m_rightButtonLayerMenu->addAction(action); | |
772 | |
773 action = new QAction(QIcon(":/icons/editdelete.png"), tr("&Delete Layer"), this); | |
774 action->setShortcut(tr("Alt+Shift+D")); | |
775 action->setStatusTip(tr("Delete the currently active layer")); | |
776 connect(action, SIGNAL(triggered()), this, SLOT(deleteCurrentLayer())); | |
777 connect(this, SIGNAL(canDeleteCurrentLayer(bool)), action, SLOT(setEnabled(bool))); | |
778 menu->addAction(action); | |
779 m_rightButtonLayerMenu->addAction(action); | |
780 | |
781 if (!m_mainMenusCreated) { | |
782 | |
783 menu = menuBar()->addMenu(tr("&Help")); | |
784 | |
785 action = new QAction(tr("&Help Reference"), this); | |
786 action->setStatusTip(tr("Open the Sonic Visualiser reference manual")); | |
787 connect(action, SIGNAL(triggered()), this, SLOT(help())); | |
788 menu->addAction(action); | |
789 | |
790 action = new QAction(tr("Sonic Visualiser on the &Web"), this); | |
791 action->setStatusTip(tr("Open the Sonic Visualiser website")); | |
792 connect(action, SIGNAL(triggered()), this, SLOT(website())); | |
793 menu->addAction(action); | |
794 | |
795 action = new QAction(tr("&About Sonic Visualiser"), this); | |
796 action->setStatusTip(tr("Show information about Sonic Visualiser")); | |
797 connect(action, SIGNAL(triggered()), this, SLOT(about())); | |
798 menu->addAction(action); | |
799 /* | |
800 action = new QAction(tr("About &Qt"), this); | |
801 action->setStatusTip(tr("Show information about Qt")); | |
802 connect(action, SIGNAL(triggered()), | |
803 QApplication::getInstance(), SLOT(aboutQt())); | |
804 menu->addAction(action); | |
805 */ | |
806 } | |
807 | |
808 m_mainMenusCreated = true; | |
809 } | |
810 | |
811 void | |
812 MainWindow::setupRecentFilesMenu() | |
813 { | |
814 m_recentFilesMenu->clear(); | |
815 std::vector<QString> files = RecentFiles::getInstance()->getRecentFiles(); | |
816 for (size_t i = 0; i < files.size(); ++i) { | |
817 QAction *action = new QAction(files[i], this); | |
818 connect(action, SIGNAL(triggered()), this, SLOT(openRecentFile())); | |
819 m_recentFilesMenu->addAction(action); | |
820 } | |
821 } | |
822 | |
823 void | |
824 MainWindow::setupExistingLayersMenu() | |
825 { | |
826 if (!m_existingLayersMenu) return; // should have been created by setupMenus | |
827 | |
828 // std::cerr << "MainWindow::setupExistingLayersMenu" << std::endl; | |
829 | |
830 m_existingLayersMenu->clear(); | |
831 m_existingLayerActions.clear(); | |
832 | |
833 std::vector<Layer *> orderedLayers; | |
834 std::set<Layer *> observedLayers; | |
835 | |
836 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) { | |
837 | |
838 Pane *pane = m_paneStack->getPane(i); | |
839 if (!pane) continue; | |
840 | |
841 for (int j = 0; j < pane->getLayerCount(); ++j) { | |
842 | |
843 Layer *layer = pane->getLayer(j); | |
844 if (!layer) continue; | |
845 if (observedLayers.find(layer) != observedLayers.end()) { | |
846 std::cerr << "found duplicate layer " << layer << std::endl; | |
847 continue; | |
848 } | |
849 | |
850 // std::cerr << "found new layer " << layer << " (name = " | |
851 // << layer->getLayerPresentationName().toStdString() << ")" << std::endl; | |
852 | |
853 orderedLayers.push_back(layer); | |
854 observedLayers.insert(layer); | |
855 } | |
856 } | |
857 | |
858 std::map<QString, int> observedNames; | |
859 | |
860 for (int i = 0; i < orderedLayers.size(); ++i) { | |
861 | |
862 QString name = orderedLayers[i]->getLayerPresentationName(); | |
863 int n = ++observedNames[name]; | |
864 if (n > 1) name = QString("%1 <%2>").arg(name).arg(n); | |
865 | |
866 QAction *action = new QAction(name, this); | |
867 connect(action, SIGNAL(triggered()), this, SLOT(addLayer())); | |
868 connect(this, SIGNAL(canAddLayer(bool)), action, SLOT(setEnabled(bool))); | |
869 m_existingLayerActions[action] = orderedLayers[i]; | |
870 | |
871 m_existingLayersMenu->addAction(action); | |
872 } | |
873 } | |
874 | |
875 void | |
876 MainWindow::setupToolbars() | |
877 { | |
878 QToolBar *toolbar = addToolBar(tr("Transport Toolbar")); | |
879 | |
880 QAction *action = toolbar->addAction(QIcon(":/icons/rewind-start.png"), | |
881 tr("Rewind to Start")); | |
882 action->setShortcut(tr("Home")); | |
883 action->setStatusTip(tr("Rewind to the start")); | |
884 connect(action, SIGNAL(triggered()), this, SLOT(rewindStart())); | |
885 connect(this, SIGNAL(canPlay(bool)), action, SLOT(setEnabled(bool))); | |
886 | |
887 action = toolbar->addAction(QIcon(":/icons/rewind.png"), | |
888 tr("Rewind")); | |
889 action->setShortcut(tr("PageUp")); | |
890 action->setStatusTip(tr("Rewind to the previous time instant in the current layer")); | |
891 connect(action, SIGNAL(triggered()), this, SLOT(rewind())); | |
892 connect(this, SIGNAL(canRewind(bool)), action, SLOT(setEnabled(bool))); | |
893 | |
894 action = toolbar->addAction(QIcon(":/icons/playpause.png"), | |
895 tr("Play / Pause")); | |
896 action->setCheckable(true); | |
897 action->setShortcut(tr("Space")); | |
898 action->setStatusTip(tr("Start or stop playback from the current position")); | |
899 connect(action, SIGNAL(triggered()), this, SLOT(play())); | |
900 connect(m_playSource, SIGNAL(playStatusChanged(bool)), | |
901 action, SLOT(setChecked(bool))); | |
902 connect(this, SIGNAL(canPlay(bool)), action, SLOT(setEnabled(bool))); | |
903 | |
904 action = toolbar->addAction(QIcon(":/icons/ffwd.png"), | |
905 tr("Fast Forward")); | |
906 action->setShortcut(tr("PageDown")); | |
907 action->setStatusTip(tr("Fast forward to the next time instant in the current layer")); | |
908 connect(action, SIGNAL(triggered()), this, SLOT(ffwd())); | |
909 connect(this, SIGNAL(canFfwd(bool)), action, SLOT(setEnabled(bool))); | |
910 | |
911 action = toolbar->addAction(QIcon(":/icons/ffwd-end.png"), | |
912 tr("Fast Forward to End")); | |
913 action->setShortcut(tr("End")); | |
914 action->setStatusTip(tr("Fast-forward to the end")); | |
915 connect(action, SIGNAL(triggered()), this, SLOT(ffwdEnd())); | |
916 connect(this, SIGNAL(canPlay(bool)), action, SLOT(setEnabled(bool))); | |
917 | |
918 toolbar = addToolBar(tr("Play Mode Toolbar")); | |
919 | |
920 action = toolbar->addAction(QIcon(":/icons/playselection.png"), | |
921 tr("Constrain Playback to Selection")); | |
922 action->setCheckable(true); | |
923 action->setChecked(m_viewManager->getPlaySelectionMode()); | |
924 action->setShortcut(tr("s")); | |
925 action->setStatusTip(tr("Constrain playback to the selected area")); | |
926 connect(action, SIGNAL(triggered()), this, SLOT(playSelectionToggled())); | |
927 connect(this, SIGNAL(canPlaySelection(bool)), action, SLOT(setEnabled(bool))); | |
928 | |
929 action = toolbar->addAction(QIcon(":/icons/playloop.png"), | |
930 tr("Loop Playback")); | |
931 action->setCheckable(true); | |
932 action->setChecked(m_viewManager->getPlayLoopMode()); | |
933 action->setShortcut(tr("l")); | |
934 action->setStatusTip(tr("Loop playback")); | |
935 connect(action, SIGNAL(triggered()), this, SLOT(playLoopToggled())); | |
936 connect(this, SIGNAL(canPlay(bool)), action, SLOT(setEnabled(bool))); | |
937 | |
938 toolbar = addToolBar(tr("Edit Toolbar")); | |
939 CommandHistory::getInstance()->registerToolbar(toolbar); | |
940 | |
941 toolbar = addToolBar(tr("Tools Toolbar")); | |
942 QActionGroup *group = new QActionGroup(this); | |
943 | |
944 action = toolbar->addAction(QIcon(":/icons/navigate.png"), | |
945 tr("Navigate")); | |
946 action->setCheckable(true); | |
947 action->setChecked(true); | |
948 action->setShortcut(tr("1")); | |
949 connect(action, SIGNAL(triggered()), this, SLOT(toolNavigateSelected())); | |
950 group->addAction(action); | |
951 m_toolActions[ViewManager::NavigateMode] = action; | |
952 | |
953 action = toolbar->addAction(QIcon(":/icons/select.png"), | |
954 tr("Select")); | |
955 action->setCheckable(true); | |
956 action->setShortcut(tr("2")); | |
957 connect(action, SIGNAL(triggered()), this, SLOT(toolSelectSelected())); | |
958 group->addAction(action); | |
959 m_toolActions[ViewManager::SelectMode] = action; | |
960 | |
961 action = toolbar->addAction(QIcon(":/icons/move.png"), | |
962 tr("Edit")); | |
963 action->setCheckable(true); | |
964 action->setShortcut(tr("3")); | |
965 connect(action, SIGNAL(triggered()), this, SLOT(toolEditSelected())); | |
966 connect(this, SIGNAL(canEditLayer(bool)), action, SLOT(setEnabled(bool))); | |
967 group->addAction(action); | |
968 m_toolActions[ViewManager::EditMode] = action; | |
969 | |
970 action = toolbar->addAction(QIcon(":/icons/draw.png"), | |
971 tr("Draw")); | |
972 action->setCheckable(true); | |
973 action->setShortcut(tr("4")); | |
974 connect(action, SIGNAL(triggered()), this, SLOT(toolDrawSelected())); | |
975 connect(this, SIGNAL(canEditLayer(bool)), action, SLOT(setEnabled(bool))); | |
976 group->addAction(action); | |
977 m_toolActions[ViewManager::DrawMode] = action; | |
978 | |
979 // action = toolbar->addAction(QIcon(":/icons/text.png"), | |
980 // tr("Text")); | |
981 // action->setCheckable(true); | |
982 // action->setShortcut(tr("5")); | |
983 // connect(action, SIGNAL(triggered()), this, SLOT(toolTextSelected())); | |
984 // group->addAction(action); | |
985 // m_toolActions[ViewManager::TextMode] = action; | |
986 | |
987 toolNavigateSelected(); | |
988 } | |
989 | |
990 void | |
991 MainWindow::updateMenuStates() | |
992 { | |
993 bool haveCurrentPane = | |
994 (m_paneStack && | |
995 (m_paneStack->getCurrentPane() != 0)); | |
996 bool haveCurrentLayer = | |
997 (haveCurrentPane && | |
998 (m_paneStack->getCurrentPane()->getSelectedLayer())); | |
999 bool haveMainModel = | |
1000 (getMainModel() != 0); | |
1001 bool havePlayTarget = | |
1002 (m_playTarget != 0); | |
1003 bool haveSelection = | |
1004 (m_viewManager && | |
1005 !m_viewManager->getSelections().empty()); | |
1006 bool haveCurrentEditableLayer = | |
1007 (haveCurrentLayer && | |
1008 m_paneStack->getCurrentPane()->getSelectedLayer()-> | |
1009 isLayerEditable()); | |
1010 bool haveCurrentTimeInstantsLayer = | |
1011 (haveCurrentLayer && | |
1012 dynamic_cast<TimeInstantLayer *> | |
1013 (m_paneStack->getCurrentPane()->getSelectedLayer())); | |
1014 bool haveCurrentTimeValueLayer = | |
1015 (haveCurrentLayer && | |
1016 dynamic_cast<TimeValueLayer *> | |
1017 (m_paneStack->getCurrentPane()->getSelectedLayer())); | |
1018 bool haveCurrentColour3DPlot = | |
1019 (haveCurrentLayer && | |
1020 dynamic_cast<Colour3DPlotLayer *> | |
1021 (m_paneStack->getCurrentPane()->getSelectedLayer())); | |
1022 bool haveClipboardContents = | |
1023 (m_viewManager && | |
1024 !m_viewManager->getClipboard().empty()); | |
1025 | |
1026 emit canAddPane(haveMainModel); | |
1027 emit canDeleteCurrentPane(haveCurrentPane); | |
1028 emit canZoom(haveMainModel && haveCurrentPane); | |
1029 emit canScroll(haveMainModel && haveCurrentPane); | |
1030 emit canAddLayer(haveMainModel && haveCurrentPane); | |
1031 emit canImportMoreAudio(haveMainModel); | |
1032 emit canImportLayer(haveMainModel && haveCurrentPane); | |
1033 emit canExportAudio(haveMainModel); | |
1034 emit canExportLayer(haveMainModel && | |
1035 (haveCurrentEditableLayer || haveCurrentColour3DPlot)); | |
1036 emit canDeleteCurrentLayer(haveCurrentLayer); | |
1037 emit canRenameLayer(haveCurrentLayer); | |
1038 emit canEditLayer(haveCurrentEditableLayer); | |
1039 emit canSelect(haveMainModel && haveCurrentPane); | |
1040 emit canPlay(/*!!! haveMainModel && */ havePlayTarget); | |
1041 emit canFfwd(haveCurrentTimeInstantsLayer || haveCurrentTimeValueLayer); | |
1042 emit canRewind(haveCurrentTimeInstantsLayer || haveCurrentTimeValueLayer); | |
1043 emit canPaste(haveCurrentEditableLayer && haveClipboardContents); | |
1044 emit canInsertInstant(haveCurrentPane); | |
1045 emit canPlaySelection(haveMainModel && havePlayTarget && haveSelection); | |
1046 emit canClearSelection(haveSelection); | |
1047 emit canEditSelection(haveSelection && haveCurrentEditableLayer); | |
1048 emit canSave(m_sessionFile != "" && m_documentModified); | |
1049 } | |
1050 | |
1051 void | |
1052 MainWindow::updateDescriptionLabel() | |
1053 { | |
1054 if (!getMainModel()) { | |
1055 m_descriptionLabel->setText(tr("No audio file loaded.")); | |
1056 return; | |
1057 } | |
1058 | |
1059 QString description; | |
1060 | |
1061 size_t ssr = getMainModel()->getSampleRate(); | |
1062 size_t tsr = ssr; | |
1063 if (m_playSource) tsr = m_playSource->getTargetSampleRate(); | |
1064 | |
1065 if (ssr != tsr) { | |
1066 description = tr("%1Hz (resampling to %2Hz)").arg(ssr).arg(tsr); | |
1067 } else { | |
1068 description = QString("%1Hz").arg(ssr); | |
1069 } | |
1070 | |
1071 description = QString("%1 - %2") | |
1072 .arg(RealTime::frame2RealTime(getMainModel()->getEndFrame(), ssr) | |
1073 .toText(false).c_str()) | |
1074 .arg(description); | |
1075 | |
1076 m_descriptionLabel->setText(description); | |
1077 } | |
1078 | |
1079 void | |
1080 MainWindow::documentModified() | |
1081 { | |
1082 // std::cerr << "MainWindow::documentModified" << std::endl; | |
1083 | |
1084 if (!m_documentModified) { | |
1085 setWindowTitle(tr("%1 (modified)").arg(windowTitle())); | |
1086 } | |
1087 | |
1088 m_documentModified = true; | |
1089 updateMenuStates(); | |
1090 } | |
1091 | |
1092 void | |
1093 MainWindow::documentRestored() | |
1094 { | |
1095 // std::cerr << "MainWindow::documentRestored" << std::endl; | |
1096 | |
1097 if (m_documentModified) { | |
1098 QString wt(windowTitle()); | |
1099 wt.replace(tr(" (modified)"), ""); | |
1100 setWindowTitle(wt); | |
1101 } | |
1102 | |
1103 m_documentModified = false; | |
1104 updateMenuStates(); | |
1105 } | |
1106 | |
1107 void | |
1108 MainWindow::playLoopToggled() | |
1109 { | |
1110 QAction *action = dynamic_cast<QAction *>(sender()); | |
1111 | |
1112 if (action) { | |
1113 m_viewManager->setPlayLoopMode(action->isChecked()); | |
1114 } else { | |
1115 m_viewManager->setPlayLoopMode(!m_viewManager->getPlayLoopMode()); | |
1116 } | |
1117 } | |
1118 | |
1119 void | |
1120 MainWindow::playSelectionToggled() | |
1121 { | |
1122 QAction *action = dynamic_cast<QAction *>(sender()); | |
1123 | |
1124 if (action) { | |
1125 m_viewManager->setPlaySelectionMode(action->isChecked()); | |
1126 } else { | |
1127 m_viewManager->setPlaySelectionMode(!m_viewManager->getPlaySelectionMode()); | |
1128 } | |
1129 } | |
1130 | |
1131 void | |
1132 MainWindow::currentPaneChanged(Pane *) | |
1133 { | |
1134 updateMenuStates(); | |
1135 } | |
1136 | |
1137 void | |
1138 MainWindow::currentLayerChanged(Pane *, Layer *) | |
1139 { | |
1140 updateMenuStates(); | |
1141 } | |
1142 | |
1143 void | |
1144 MainWindow::toolNavigateSelected() | |
1145 { | |
1146 m_viewManager->setToolMode(ViewManager::NavigateMode); | |
1147 } | |
1148 | |
1149 void | |
1150 MainWindow::toolSelectSelected() | |
1151 { | |
1152 m_viewManager->setToolMode(ViewManager::SelectMode); | |
1153 } | |
1154 | |
1155 void | |
1156 MainWindow::toolEditSelected() | |
1157 { | |
1158 m_viewManager->setToolMode(ViewManager::EditMode); | |
1159 } | |
1160 | |
1161 void | |
1162 MainWindow::toolDrawSelected() | |
1163 { | |
1164 m_viewManager->setToolMode(ViewManager::DrawMode); | |
1165 } | |
1166 | |
1167 //void | |
1168 //MainWindow::toolTextSelected() | |
1169 //{ | |
1170 // m_viewManager->setToolMode(ViewManager::TextMode); | |
1171 //} | |
1172 | |
1173 void | |
1174 MainWindow::selectAll() | |
1175 { | |
1176 if (!getMainModel()) return; | |
1177 m_viewManager->setSelection(Selection(getMainModel()->getStartFrame(), | |
1178 getMainModel()->getEndFrame())); | |
1179 } | |
1180 | |
1181 void | |
1182 MainWindow::selectToStart() | |
1183 { | |
1184 if (!getMainModel()) return; | |
1185 m_viewManager->setSelection(Selection(getMainModel()->getStartFrame(), | |
1186 m_viewManager->getGlobalCentreFrame())); | |
1187 } | |
1188 | |
1189 void | |
1190 MainWindow::selectToEnd() | |
1191 { | |
1192 if (!getMainModel()) return; | |
1193 m_viewManager->setSelection(Selection(m_viewManager->getGlobalCentreFrame(), | |
1194 getMainModel()->getEndFrame())); | |
1195 } | |
1196 | |
1197 void | |
1198 MainWindow::selectVisible() | |
1199 { | |
1200 Model *model = getMainModel(); | |
1201 if (!model) return; | |
1202 | |
1203 Pane *currentPane = m_paneStack->getCurrentPane(); | |
1204 if (!currentPane) return; | |
1205 | |
1206 size_t startFrame, endFrame; | |
1207 | |
1208 if (currentPane->getStartFrame() < 0) startFrame = 0; | |
1209 else startFrame = currentPane->getStartFrame(); | |
1210 | |
1211 if (currentPane->getEndFrame() > model->getEndFrame()) endFrame = model->getEndFrame(); | |
1212 else endFrame = currentPane->getEndFrame(); | |
1213 | |
1214 m_viewManager->setSelection(Selection(startFrame, endFrame)); | |
1215 } | |
1216 | |
1217 void | |
1218 MainWindow::clearSelection() | |
1219 { | |
1220 m_viewManager->clearSelections(); | |
1221 } | |
1222 | |
1223 void | |
1224 MainWindow::cut() | |
1225 { | |
1226 Pane *currentPane = m_paneStack->getCurrentPane(); | |
1227 if (!currentPane) return; | |
1228 | |
1229 Layer *layer = currentPane->getSelectedLayer(); | |
1230 if (!layer) return; | |
1231 | |
1232 Clipboard &clipboard = m_viewManager->getClipboard(); | |
1233 clipboard.clear(); | |
1234 | |
1235 MultiSelection::SelectionList selections = m_viewManager->getSelections(); | |
1236 | |
1237 CommandHistory::getInstance()->startCompoundOperation(tr("Cut"), true); | |
1238 | |
1239 for (MultiSelection::SelectionList::iterator i = selections.begin(); | |
1240 i != selections.end(); ++i) { | |
1241 layer->copy(*i, clipboard); | |
1242 layer->deleteSelection(*i); | |
1243 } | |
1244 | |
1245 CommandHistory::getInstance()->endCompoundOperation(); | |
1246 } | |
1247 | |
1248 void | |
1249 MainWindow::copy() | |
1250 { | |
1251 Pane *currentPane = m_paneStack->getCurrentPane(); | |
1252 if (!currentPane) return; | |
1253 | |
1254 Layer *layer = currentPane->getSelectedLayer(); | |
1255 if (!layer) return; | |
1256 | |
1257 Clipboard &clipboard = m_viewManager->getClipboard(); | |
1258 clipboard.clear(); | |
1259 | |
1260 MultiSelection::SelectionList selections = m_viewManager->getSelections(); | |
1261 | |
1262 for (MultiSelection::SelectionList::iterator i = selections.begin(); | |
1263 i != selections.end(); ++i) { | |
1264 layer->copy(*i, clipboard); | |
1265 } | |
1266 } | |
1267 | |
1268 void | |
1269 MainWindow::paste() | |
1270 { | |
1271 Pane *currentPane = m_paneStack->getCurrentPane(); | |
1272 if (!currentPane) return; | |
1273 | |
1274 //!!! if we have no current layer, we should create one of the most | |
1275 // appropriate type | |
1276 | |
1277 Layer *layer = currentPane->getSelectedLayer(); | |
1278 if (!layer) return; | |
1279 | |
1280 Clipboard &clipboard = m_viewManager->getClipboard(); | |
1281 Clipboard::PointList contents = clipboard.getPoints(); | |
1282 /* | |
1283 long minFrame = 0; | |
1284 bool have = false; | |
1285 for (int i = 0; i < contents.size(); ++i) { | |
1286 if (!contents[i].haveFrame()) continue; | |
1287 if (!have || contents[i].getFrame() < minFrame) { | |
1288 minFrame = contents[i].getFrame(); | |
1289 have = true; | |
1290 } | |
1291 } | |
1292 | |
1293 long frameOffset = long(m_viewManager->getGlobalCentreFrame()) - minFrame; | |
1294 | |
1295 layer->paste(clipboard, frameOffset); | |
1296 */ | |
1297 layer->paste(clipboard, 0, true); | |
1298 } | |
1299 | |
1300 void | |
1301 MainWindow::deleteSelected() | |
1302 { | |
1303 if (m_paneStack->getCurrentPane() && | |
1304 m_paneStack->getCurrentPane()->getSelectedLayer()) { | |
1305 | |
1306 MultiSelection::SelectionList selections = | |
1307 m_viewManager->getSelections(); | |
1308 | |
1309 for (MultiSelection::SelectionList::iterator i = selections.begin(); | |
1310 i != selections.end(); ++i) { | |
1311 | |
1312 m_paneStack->getCurrentPane()->getSelectedLayer()->deleteSelection(*i); | |
1313 } | |
1314 } | |
1315 } | |
1316 | |
1317 void | |
1318 MainWindow::insertInstant() | |
1319 { | |
1320 int frame = m_viewManager->getPlaybackFrame(); | |
1321 | |
1322 Pane *pane = m_paneStack->getCurrentPane(); | |
1323 if (!pane) { | |
1324 return; | |
1325 } | |
1326 | |
1327 Layer *layer = dynamic_cast<TimeInstantLayer *> | |
1328 (pane->getSelectedLayer()); | |
1329 | |
1330 if (!layer) { | |
1331 for (int i = pane->getLayerCount(); i > 0; --i) { | |
1332 layer = dynamic_cast<TimeInstantLayer *>(pane->getLayer(i - 1)); | |
1333 if (layer) break; | |
1334 } | |
1335 | |
1336 if (!layer) { | |
1337 CommandHistory::getInstance()->startCompoundOperation | |
1338 (tr("Add Point"), true); | |
1339 layer = m_document->createEmptyLayer(LayerFactory::TimeInstants); | |
1340 if (layer) { | |
1341 m_document->addLayerToView(pane, layer); | |
1342 m_paneStack->setCurrentLayer(pane, layer); | |
1343 } | |
1344 CommandHistory::getInstance()->endCompoundOperation(); | |
1345 } | |
1346 } | |
1347 | |
1348 if (layer) { | |
1349 | |
1350 Model *model = layer->getModel(); | |
1351 SparseOneDimensionalModel *sodm = dynamic_cast<SparseOneDimensionalModel *> | |
1352 (model); | |
1353 | |
1354 if (sodm) { | |
1355 SparseOneDimensionalModel::Point point | |
1356 (frame, QString("%1").arg(sodm->getPointCount() + 1)); | |
1357 CommandHistory::getInstance()->addCommand | |
1358 (new SparseOneDimensionalModel::AddPointCommand(sodm, point, | |
1359 tr("Add Points")), | |
1360 true, true); // bundled | |
1361 } | |
1362 } | |
1363 } | |
1364 | |
1365 void | |
1366 MainWindow::importAudio() | |
1367 { | |
1368 QString orig = m_audioFile; | |
1369 | |
1370 // std::cerr << "orig = " << orig.toStdString() << std::endl; | |
1371 | |
1372 if (orig == "") orig = "."; | |
1373 | |
1374 QString path = QFileDialog::getOpenFileName | |
1375 (this, tr("Select an audio file"), orig, | |
1376 tr("Audio files (%1)\nAll files (*.*)") | |
1377 .arg(AudioFileReaderFactory::getKnownExtensions())); | |
1378 | |
1379 if (path != "") { | |
1380 if (!openAudioFile(path, ReplaceMainModel)) { | |
1381 QMessageBox::critical(this, tr("Failed to open file"), | |
1382 tr("Audio file \"%1\" could not be opened").arg(path)); | |
1383 } | |
1384 } | |
1385 } | |
1386 | |
1387 void | |
1388 MainWindow::importMoreAudio() | |
1389 { | |
1390 QString orig = m_audioFile; | |
1391 | |
1392 // std::cerr << "orig = " << orig.toStdString() << std::endl; | |
1393 | |
1394 if (orig == "") orig = "."; | |
1395 | |
1396 QString path = QFileDialog::getOpenFileName | |
1397 (this, tr("Select an audio file"), orig, | |
1398 tr("Audio files (%1)\nAll files (*.*)") | |
1399 .arg(AudioFileReaderFactory::getKnownExtensions())); | |
1400 | |
1401 if (path != "") { | |
1402 if (!openAudioFile(path, CreateAdditionalModel)) { | |
1403 QMessageBox::critical(this, tr("Failed to open file"), | |
1404 tr("Audio file \"%1\" could not be opened").arg(path)); | |
1405 } | |
1406 } | |
1407 } | |
1408 | |
1409 void | |
1410 MainWindow::exportAudio() | |
1411 { | |
1412 if (!getMainModel()) return; | |
1413 | |
1414 QString path = QFileDialog::getSaveFileName | |
1415 (this, tr("Select a file to export to"), ".", | |
1416 tr("WAV audio files (*.wav)\nAll files (*.*)")); | |
1417 | |
1418 if (path == "") return; | |
1419 | |
1420 if (!path.endsWith(".wav")) path = path + ".wav"; | |
1421 | |
1422 bool ok = false; | |
1423 QString error; | |
1424 | |
1425 WavFileWriter *writer = 0; | |
1426 MultiSelection ms = m_viewManager->getSelection(); | |
1427 MultiSelection::SelectionList selections = m_viewManager->getSelections(); | |
1428 | |
1429 bool multiple = false; | |
1430 | |
1431 if (selections.empty()) { | |
1432 | |
1433 writer = new WavFileWriter(path, getMainModel()->getSampleRate(), | |
1434 getMainModel(), 0); | |
1435 | |
1436 } else if (selections.size() == 1) { | |
1437 | |
1438 QStringList items; | |
1439 items << tr("Export the selected region only") | |
1440 << tr("Export the whole audio file"); | |
1441 | |
1442 bool ok = false; | |
1443 QString item = ListInputDialog::getItem | |
1444 (this, tr("Select region to export"), | |
1445 tr("Which region from the original audio file do you want to export?"), | |
1446 items, 0, &ok); | |
1447 | |
1448 if (!ok || item.isEmpty()) return; | |
1449 | |
1450 if (item == items[0]) { | |
1451 | |
1452 writer = new WavFileWriter(path, getMainModel()->getSampleRate(), | |
1453 getMainModel(), &ms); | |
1454 | |
1455 } else { | |
1456 | |
1457 writer = new WavFileWriter(path, getMainModel()->getSampleRate(), | |
1458 getMainModel(), 0); | |
1459 } | |
1460 } else { | |
1461 | |
1462 QStringList items; | |
1463 items << tr("Export the selected regions into a single audio file") | |
1464 << tr("Export the selected regions into separate files") | |
1465 << tr("Export the whole audio file"); | |
1466 | |
1467 bool ok = false; | |
1468 QString item = ListInputDialog::getItem | |
1469 (this, tr("Select region to export"), | |
1470 tr("Multiple regions of the original audio file are selected.\nWhat do you want to export?"), | |
1471 items, 0, &ok); | |
1472 | |
1473 if (!ok || item.isEmpty()) return; | |
1474 | |
1475 if (item == items[0]) { | |
1476 | |
1477 writer = new WavFileWriter(path, getMainModel()->getSampleRate(), | |
1478 getMainModel(), &ms); | |
1479 | |
1480 } else if (item == items[2]) { | |
1481 | |
1482 writer = new WavFileWriter(path, getMainModel()->getSampleRate(), | |
1483 getMainModel(), 0); | |
1484 | |
1485 } else { | |
1486 | |
1487 multiple = true; | |
1488 | |
1489 int n = 1; | |
1490 QString base = path; | |
1491 base.replace(".wav", ""); | |
1492 | |
1493 for (MultiSelection::SelectionList::iterator i = selections.begin(); | |
1494 i != selections.end(); ++i) { | |
1495 | |
1496 MultiSelection subms; | |
1497 subms.setSelection(*i); | |
1498 | |
1499 QString subpath = QString("%1.%2.wav").arg(base).arg(n); | |
1500 ++n; | |
1501 | |
1502 if (QFileInfo(subpath).exists()) { | |
1503 error = tr("Fragment file %1 already exists, aborting").arg(subpath); | |
1504 break; | |
1505 } | |
1506 | |
1507 WavFileWriter subwriter(subpath, getMainModel()->getSampleRate(), | |
1508 getMainModel(), &subms); | |
1509 subwriter.write(); | |
1510 ok = subwriter.isOK(); | |
1511 | |
1512 if (!ok) { | |
1513 error = subwriter.getError(); | |
1514 break; | |
1515 } | |
1516 } | |
1517 } | |
1518 } | |
1519 | |
1520 if (writer) { | |
1521 writer->write(); | |
1522 ok = writer->isOK(); | |
1523 error = writer->getError(); | |
1524 delete writer; | |
1525 } | |
1526 | |
1527 if (ok) { | |
1528 if (!multiple) { | |
1529 RecentFiles::getInstance()->addFile(path); | |
1530 } | |
1531 } else { | |
1532 QMessageBox::critical(this, tr("Failed to write file"), error); | |
1533 } | |
1534 } | |
1535 | |
1536 void | |
1537 MainWindow::importLayer() | |
1538 { | |
1539 Pane *pane = m_paneStack->getCurrentPane(); | |
1540 | |
1541 if (!pane) { | |
1542 // shouldn't happen, as the menu action should have been disabled | |
1543 std::cerr << "WARNING: MainWindow::importLayer: no current pane" << std::endl; | |
1544 return; | |
1545 } | |
1546 | |
1547 if (!getMainModel()) { | |
1548 // shouldn't happen, as the menu action should have been disabled | |
1549 std::cerr << "WARNING: MainWindow::importLayer: No main model -- hence no default sample rate available" << std::endl; | |
1550 return; | |
1551 } | |
1552 | |
1553 QString path = QFileDialog::getOpenFileName | |
1554 (this, tr("Select file"), ".", | |
1555 tr("All supported files (%1)\nSonic Visualiser Layer XML files (*.svl)\nComma-separated data files (*.csv)\nSpace-separated .lab files (*.lab)\nMIDI files (*.mid)\nText files (*.txt)\nAll files (*.*)").arg(DataFileReaderFactory::getKnownExtensions())); | |
1556 | |
1557 if (path != "") { | |
1558 | |
1559 if (!openLayerFile(path)) { | |
1560 QMessageBox::critical(this, tr("Failed to open file"), | |
1561 tr("File %1 could not be opened.").arg(path)); | |
1562 return; | |
1563 } | |
1564 } | |
1565 } | |
1566 | |
1567 bool | |
1568 MainWindow::openLayerFile(QString path) | |
1569 { | |
1570 Pane *pane = m_paneStack->getCurrentPane(); | |
1571 | |
1572 if (!pane) { | |
1573 // shouldn't happen, as the menu action should have been disabled | |
1574 std::cerr << "WARNING: MainWindow::openLayerFile: no current pane" << std::endl; | |
1575 return false; | |
1576 } | |
1577 | |
1578 if (!getMainModel()) { | |
1579 // shouldn't happen, as the menu action should have been disabled | |
1580 std::cerr << "WARNING: MainWindow::openLayerFile: No main model -- hence no default sample rate available" << std::endl; | |
1581 return false; | |
1582 } | |
1583 | |
1584 if (path.endsWith(".svl") || path.endsWith(".xml")) { | |
1585 | |
1586 PaneCallback callback(this); | |
1587 QFile file(path); | |
1588 | |
1589 if (!file.open(QIODevice::ReadOnly | QIODevice::Text)) { | |
1590 std::cerr << "ERROR: MainWindow::openLayerFile(" | |
1591 << path.toStdString() | |
1592 << "): Failed to open file for reading" << std::endl; | |
1593 return false; | |
1594 } | |
1595 | |
1596 SVFileReader reader(m_document, callback); | |
1597 reader.setCurrentPane(pane); | |
1598 | |
1599 QXmlInputSource inputSource(&file); | |
1600 reader.parse(inputSource); | |
1601 | |
1602 if (!reader.isOK()) { | |
1603 std::cerr << "ERROR: MainWindow::openLayerFile(" | |
1604 << path.toStdString() | |
1605 << "): Failed to read XML file: " | |
1606 << reader.getErrorString().toStdString() << std::endl; | |
1607 return false; | |
1608 } | |
1609 | |
1610 RecentFiles::getInstance()->addFile(path); | |
1611 return true; | |
1612 | |
1613 } else { | |
1614 | |
1615 Model *model = DataFileReaderFactory::load(path, getMainModel()->getSampleRate()); | |
1616 | |
1617 if (model) { | |
1618 Layer *newLayer = m_document->createImportedLayer(model); | |
1619 if (newLayer) { | |
1620 m_document->addLayerToView(pane, newLayer); | |
1621 RecentFiles::getInstance()->addFile(path); | |
1622 return true; | |
1623 } | |
1624 } | |
1625 } | |
1626 | |
1627 return false; | |
1628 } | |
1629 | |
1630 void | |
1631 MainWindow::exportLayer() | |
1632 { | |
1633 Pane *pane = m_paneStack->getCurrentPane(); | |
1634 if (!pane) return; | |
1635 | |
1636 Layer *layer = pane->getSelectedLayer(); | |
1637 if (!layer) return; | |
1638 | |
1639 Model *model = layer->getModel(); | |
1640 if (!model) return; | |
1641 | |
1642 QString path = QFileDialog::getSaveFileName | |
1643 (this, tr("Select a file to export to"), ".", | |
1644 tr("Sonic Visualiser Layer XML files (*.svl)\nComma-separated data files (*.csv)\nText files (*.txt)\nAll files (*.*)")); | |
1645 | |
1646 if (path == "") return; | |
1647 | |
1648 if (QFileInfo(path).suffix() == "") path += ".svl"; | |
1649 | |
1650 QString error; | |
1651 | |
1652 if (path.endsWith(".xml") || path.endsWith(".svl")) { | |
1653 | |
1654 QFile file(path); | |
1655 if (!file.open(QIODevice::WriteOnly | QIODevice::Text)) { | |
1656 error = tr("Failed to open file %1 for writing").arg(path); | |
1657 } else { | |
1658 QTextStream out(&file); | |
1659 out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" | |
1660 << "<!DOCTYPE sonic-visualiser>\n" | |
1661 << "<sv>\n" | |
1662 << " <data>\n"; | |
1663 | |
1664 model->toXml(out, " "); | |
1665 | |
1666 out << " </data>\n" | |
1667 << " <display>\n"; | |
1668 | |
1669 layer->toXml(out, " "); | |
1670 | |
1671 out << " </display>\n" | |
1672 << "</sv>\n"; | |
1673 } | |
1674 | |
1675 } else { | |
1676 | |
1677 CSVFileWriter writer(path, model, | |
1678 (path.endsWith(".csv") ? "," : "\t")); | |
1679 writer.write(); | |
1680 | |
1681 if (!writer.isOK()) { | |
1682 error = writer.getError(); | |
1683 } | |
1684 } | |
1685 | |
1686 if (error != "") { | |
1687 QMessageBox::critical(this, tr("Failed to write file"), error); | |
1688 } else { | |
1689 RecentFiles::getInstance()->addFile(path); | |
1690 } | |
1691 } | |
1692 | |
1693 bool | |
1694 MainWindow::openAudioFile(QString path, AudioFileOpenMode mode) | |
1695 { | |
1696 if (!(QFileInfo(path).exists() && | |
1697 QFileInfo(path).isFile() && | |
1698 QFileInfo(path).isReadable())) { | |
1699 return false; | |
1700 } | |
1701 | |
1702 WaveFileModel *newModel = new WaveFileModel(path); | |
1703 | |
1704 if (!newModel->isOK()) { | |
1705 delete newModel; | |
1706 return false; | |
1707 } | |
1708 | |
1709 bool setAsMain = true; | |
1710 static bool prevSetAsMain = true; | |
1711 | |
1712 if (mode == CreateAdditionalModel) setAsMain = false; | |
1713 else if (mode == AskUser) { | |
1714 if (m_document->getMainModel()) { | |
1715 | |
1716 QStringList items; | |
1717 items << tr("Replace the existing main waveform") | |
1718 << tr("Load this file into a new waveform pane"); | |
1719 | |
1720 bool ok = false; | |
1721 QString item = ListInputDialog::getItem | |
1722 (this, tr("Select target for import"), | |
1723 tr("You already have an audio waveform loaded.\nWhat would you like to do with the new audio file?"), | |
1724 items, prevSetAsMain ? 0 : 1, &ok); | |
1725 | |
1726 if (!ok || item.isEmpty()) { | |
1727 delete newModel; | |
1728 return false; | |
1729 } | |
1730 | |
1731 setAsMain = (item == items[0]); | |
1732 prevSetAsMain = setAsMain; | |
1733 } | |
1734 } | |
1735 | |
1736 if (setAsMain) { | |
1737 | |
1738 Model *prevMain = getMainModel(); | |
1739 if (prevMain) m_playSource->removeModel(prevMain); | |
1740 | |
1741 PlayParameterRepository::getInstance()->clear(); | |
1742 | |
1743 // The clear() call will have removed the parameters for the | |
1744 // main model. Re-add them with the new one. | |
1745 PlayParameterRepository::getInstance()->addModel(newModel); | |
1746 | |
1747 m_document->setMainModel(newModel); | |
1748 setupMenus(); | |
1749 | |
1750 if (m_sessionFile == "") { | |
1751 setWindowTitle(tr("Sonic Visualiser: %1") | |
1752 .arg(QFileInfo(path).fileName())); | |
1753 CommandHistory::getInstance()->clear(); | |
1754 CommandHistory::getInstance()->documentSaved(); | |
1755 m_documentModified = false; | |
1756 } else { | |
1757 setWindowTitle(tr("Sonic Visualiser: %1 [%2]") | |
1758 .arg(QFileInfo(m_sessionFile).fileName()) | |
1759 .arg(QFileInfo(path).fileName())); | |
1760 if (m_documentModified) { | |
1761 m_documentModified = false; | |
1762 documentModified(); // so as to restore "(modified)" window title | |
1763 } | |
1764 } | |
1765 | |
1766 m_audioFile = path; | |
1767 | |
1768 } else { // !setAsMain | |
1769 | |
1770 CommandHistory::getInstance()->startCompoundOperation | |
1771 (tr("Import \"%1\"").arg(QFileInfo(path).fileName()), true); | |
1772 | |
1773 m_document->addImportedModel(newModel); | |
1774 | |
1775 AddPaneCommand *command = new AddPaneCommand(this); | |
1776 CommandHistory::getInstance()->addCommand(command); | |
1777 | |
1778 Pane *pane = command->getPane(); | |
1779 | |
1780 if (!m_timeRulerLayer) { | |
1781 m_timeRulerLayer = m_document->createMainModelLayer | |
1782 (LayerFactory::TimeRuler); | |
1783 } | |
1784 | |
1785 m_document->addLayerToView(pane, m_timeRulerLayer); | |
1786 | |
1787 Layer *newLayer = m_document->createImportedLayer(newModel); | |
1788 | |
1789 if (newLayer) { | |
1790 m_document->addLayerToView(pane, newLayer); | |
1791 } | |
1792 | |
1793 CommandHistory::getInstance()->endCompoundOperation(); | |
1794 } | |
1795 | |
1796 updateMenuStates(); | |
1797 RecentFiles::getInstance()->addFile(path); | |
1798 | |
1799 return true; | |
1800 } | |
1801 | |
1802 void | |
1803 MainWindow::createPlayTarget() | |
1804 { | |
1805 if (m_playTarget) return; | |
1806 | |
1807 m_playTarget = AudioTargetFactory::createCallbackTarget(m_playSource); | |
1808 if (!m_playTarget) { | |
1809 QMessageBox::warning | |
1810 (this, tr("Couldn't open audio device"), | |
1811 tr("Could not open an audio device for playback.\nAudio playback will not be available during this session.\n"), | |
1812 QMessageBox::Ok, 0); | |
1813 } | |
1814 connect(m_fader, SIGNAL(valueChanged(float)), | |
1815 m_playTarget, SLOT(setOutputGain(float))); | |
1816 } | |
1817 | |
1818 WaveFileModel * | |
1819 MainWindow::getMainModel() | |
1820 { | |
1821 if (!m_document) return 0; | |
1822 return m_document->getMainModel(); | |
1823 } | |
1824 | |
1825 void | |
1826 MainWindow::newSession() | |
1827 { | |
1828 if (!checkSaveModified()) return; | |
1829 | |
1830 closeSession(); | |
1831 createDocument(); | |
1832 | |
1833 Pane *pane = m_paneStack->addPane(); | |
1834 | |
1835 if (!m_timeRulerLayer) { | |
1836 m_timeRulerLayer = m_document->createMainModelLayer | |
1837 (LayerFactory::TimeRuler); | |
1838 } | |
1839 | |
1840 m_document->addLayerToView(pane, m_timeRulerLayer); | |
1841 | |
1842 Layer *waveform = m_document->createMainModelLayer(LayerFactory::Waveform); | |
1843 m_document->addLayerToView(pane, waveform); | |
1844 | |
1845 m_panner->registerView(pane); | |
1846 | |
1847 CommandHistory::getInstance()->clear(); | |
1848 CommandHistory::getInstance()->documentSaved(); | |
1849 documentRestored(); | |
1850 updateMenuStates(); | |
1851 } | |
1852 | |
1853 void | |
1854 MainWindow::createDocument() | |
1855 { | |
1856 m_document = new Document; | |
1857 | |
1858 connect(m_document, SIGNAL(layerAdded(Layer *)), | |
1859 this, SLOT(layerAdded(Layer *))); | |
1860 connect(m_document, SIGNAL(layerRemoved(Layer *)), | |
1861 this, SLOT(layerRemoved(Layer *))); | |
1862 connect(m_document, SIGNAL(layerAboutToBeDeleted(Layer *)), | |
1863 this, SLOT(layerAboutToBeDeleted(Layer *))); | |
1864 connect(m_document, SIGNAL(layerInAView(Layer *, bool)), | |
1865 this, SLOT(layerInAView(Layer *, bool))); | |
1866 | |
1867 connect(m_document, SIGNAL(modelAdded(Model *)), | |
1868 this, SLOT(modelAdded(Model *))); | |
1869 connect(m_document, SIGNAL(mainModelChanged(WaveFileModel *)), | |
1870 this, SLOT(mainModelChanged(WaveFileModel *))); | |
1871 connect(m_document, SIGNAL(modelAboutToBeDeleted(Model *)), | |
1872 this, SLOT(modelAboutToBeDeleted(Model *))); | |
1873 | |
1874 connect(m_document, SIGNAL(modelGenerationFailed(QString)), | |
1875 this, SLOT(modelGenerationFailed(QString))); | |
1876 connect(m_document, SIGNAL(modelRegenerationFailed(QString)), | |
1877 this, SLOT(modelRegenerationFailed(QString))); | |
1878 } | |
1879 | |
1880 void | |
1881 MainWindow::closeSession() | |
1882 { | |
1883 if (!checkSaveModified()) return; | |
1884 | |
1885 while (m_paneStack->getPaneCount() > 0) { | |
1886 | |
1887 Pane *pane = m_paneStack->getPane(m_paneStack->getPaneCount() - 1); | |
1888 | |
1889 while (pane->getLayerCount() > 0) { | |
1890 m_document->removeLayerFromView | |
1891 (pane, pane->getLayer(pane->getLayerCount() - 1)); | |
1892 } | |
1893 | |
1894 m_panner->unregisterView(pane); | |
1895 m_paneStack->deletePane(pane); | |
1896 } | |
1897 | |
1898 while (m_paneStack->getHiddenPaneCount() > 0) { | |
1899 | |
1900 Pane *pane = m_paneStack->getHiddenPane | |
1901 (m_paneStack->getHiddenPaneCount() - 1); | |
1902 | |
1903 while (pane->getLayerCount() > 0) { | |
1904 m_document->removeLayerFromView | |
1905 (pane, pane->getLayer(pane->getLayerCount() - 1)); | |
1906 } | |
1907 | |
1908 m_panner->unregisterView(pane); | |
1909 m_paneStack->deletePane(pane); | |
1910 } | |
1911 | |
1912 delete m_document; | |
1913 m_document = 0; | |
1914 m_viewManager->clearSelections(); | |
1915 m_timeRulerLayer = 0; // document owned this | |
1916 | |
1917 m_sessionFile = ""; | |
1918 setWindowTitle(tr("Sonic Visualiser")); | |
1919 | |
1920 CommandHistory::getInstance()->clear(); | |
1921 CommandHistory::getInstance()->documentSaved(); | |
1922 documentRestored(); | |
1923 } | |
1924 | |
1925 void | |
1926 MainWindow::openSession() | |
1927 { | |
1928 if (!checkSaveModified()) return; | |
1929 | |
1930 QString orig = m_audioFile; | |
1931 if (orig == "") orig = "."; | |
1932 else orig = QFileInfo(orig).absoluteDir().canonicalPath(); | |
1933 | |
1934 QString path = QFileDialog::getOpenFileName | |
1935 (this, tr("Select a session file"), orig, | |
1936 tr("Sonic Visualiser session files (*.sv)\nAll files (*.*)")); | |
1937 | |
1938 if (path.isEmpty()) return; | |
1939 | |
1940 if (!(QFileInfo(path).exists() && | |
1941 QFileInfo(path).isFile() && | |
1942 QFileInfo(path).isReadable())) { | |
1943 QMessageBox::critical(this, tr("Failed to open file"), | |
1944 tr("File \"%1\" does not exist or is not a readable file").arg(path)); | |
1945 return; | |
1946 } | |
1947 | |
1948 if (!openSessionFile(path)) { | |
1949 QMessageBox::critical(this, tr("Failed to open file"), | |
1950 tr("Session file \"%1\" could not be opened").arg(path)); | |
1951 } | |
1952 } | |
1953 | |
1954 void | |
1955 MainWindow::openSomething() | |
1956 { | |
1957 QString orig = m_audioFile; | |
1958 if (orig == "") orig = "."; | |
1959 else orig = QFileInfo(orig).absoluteDir().canonicalPath(); | |
1960 | |
1961 bool canImportLayer = (getMainModel() != 0 && | |
1962 m_paneStack != 0 && | |
1963 m_paneStack->getCurrentPane() != 0); | |
1964 | |
1965 QString importSpec; | |
1966 | |
1967 if (canImportLayer) { | |
1968 importSpec = tr("All supported files (*.sv %1 %2)\nSonic Visualiser session files (*.sv)\nAudio files (%1)\nLayer files (%2)\nAll files (*.*)") | |
1969 .arg(AudioFileReaderFactory::getKnownExtensions()) | |
1970 .arg(DataFileReaderFactory::getKnownExtensions()); | |
1971 } else { | |
1972 importSpec = tr("All supported files (*.sv %1)\nSonic Visualiser session files (*.sv)\nAudio files (%1)\nAll files (*.*)") | |
1973 .arg(AudioFileReaderFactory::getKnownExtensions()); | |
1974 } | |
1975 | |
1976 QString path = QFileDialog::getOpenFileName | |
1977 (this, tr("Select a file to open"), orig, importSpec); | |
1978 | |
1979 if (path.isEmpty()) return; | |
1980 | |
1981 if (!(QFileInfo(path).exists() && | |
1982 QFileInfo(path).isFile() && | |
1983 QFileInfo(path).isReadable())) { | |
1984 QMessageBox::critical(this, tr("Failed to open file"), | |
1985 tr("File \"%1\" does not exist or is not a readable file").arg(path)); | |
1986 return; | |
1987 } | |
1988 | |
1989 if (path.endsWith(".sv")) { | |
1990 | |
1991 if (!checkSaveModified()) return; | |
1992 | |
1993 if (!openSessionFile(path)) { | |
1994 QMessageBox::critical(this, tr("Failed to open file"), | |
1995 tr("Session file \"%1\" could not be opened").arg(path)); | |
1996 } | |
1997 | |
1998 } else { | |
1999 | |
2000 if (!openAudioFile(path, AskUser)) { | |
2001 | |
2002 if (!canImportLayer || !openLayerFile(path)) { | |
2003 | |
2004 QMessageBox::critical(this, tr("Failed to open file"), | |
2005 tr("File \"%1\" could not be opened").arg(path)); | |
2006 } | |
2007 } | |
2008 } | |
2009 } | |
2010 | |
2011 void | |
2012 MainWindow::openRecentFile() | |
2013 { | |
2014 QObject *obj = sender(); | |
2015 QAction *action = dynamic_cast<QAction *>(obj); | |
2016 | |
2017 if (!action) { | |
2018 std::cerr << "WARNING: MainWindow::openRecentFile: sender is not an action" | |
2019 << std::endl; | |
2020 return; | |
2021 } | |
2022 | |
2023 QString path = action->text(); | |
2024 if (path == "") return; | |
2025 | |
2026 if (path.endsWith("sv")) { | |
2027 | |
2028 if (!checkSaveModified()) return ; | |
2029 | |
2030 if (!openSessionFile(path)) { | |
2031 QMessageBox::critical(this, tr("Failed to open file"), | |
2032 tr("Session file \"%1\" could not be opened").arg(path)); | |
2033 } | |
2034 | |
2035 } else { | |
2036 | |
2037 if (!openAudioFile(path, AskUser)) { | |
2038 | |
2039 bool canImportLayer = (getMainModel() != 0 && | |
2040 m_paneStack != 0 && | |
2041 m_paneStack->getCurrentPane() != 0); | |
2042 | |
2043 if (!canImportLayer || !openLayerFile(path)) { | |
2044 | |
2045 QMessageBox::critical(this, tr("Failed to open file"), | |
2046 tr("File \"%1\" could not be opened").arg(path)); | |
2047 } | |
2048 } | |
2049 } | |
2050 } | |
2051 | |
2052 bool | |
2053 MainWindow::openSomeFile(QString path) | |
2054 { | |
2055 if (openAudioFile(path)) { | |
2056 return true; | |
2057 } else if (openSessionFile(path)) { | |
2058 return true; | |
2059 } else { | |
2060 return false; | |
2061 } | |
2062 } | |
2063 | |
2064 bool | |
2065 MainWindow::openSessionFile(QString path) | |
2066 { | |
2067 BZipFileDevice bzFile(path); | |
2068 if (!bzFile.open(QIODevice::ReadOnly)) { | |
2069 std::cerr << "Failed to open session file \"" << path.toStdString() | |
2070 << "\": " << bzFile.errorString().toStdString() << std::endl; | |
2071 return false; | |
2072 } | |
2073 | |
2074 QString error; | |
2075 closeSession(); | |
2076 createDocument(); | |
2077 | |
2078 PaneCallback callback(this); | |
2079 m_viewManager->clearSelections(); | |
2080 | |
2081 SVFileReader reader(m_document, callback); | |
2082 QXmlInputSource inputSource(&bzFile); | |
2083 reader.parse(inputSource); | |
2084 | |
2085 if (!reader.isOK()) { | |
2086 error = tr("SV XML file read error:\n%1").arg(reader.getErrorString()); | |
2087 } | |
2088 | |
2089 bzFile.close(); | |
2090 | |
2091 bool ok = (error == ""); | |
2092 | |
2093 if (ok) { | |
2094 setWindowTitle(tr("Sonic Visualiser: %1") | |
2095 .arg(QFileInfo(path).fileName())); | |
2096 m_sessionFile = path; | |
2097 setupMenus(); | |
2098 CommandHistory::getInstance()->clear(); | |
2099 CommandHistory::getInstance()->documentSaved(); | |
2100 m_documentModified = false; | |
2101 updateMenuStates(); | |
2102 RecentFiles::getInstance()->addFile(path); | |
2103 } else { | |
2104 setWindowTitle(tr("Sonic Visualiser")); | |
2105 } | |
2106 | |
2107 return ok; | |
2108 } | |
2109 | |
2110 void | |
2111 MainWindow::closeEvent(QCloseEvent *e) | |
2112 { | |
2113 if (!checkSaveModified()) { | |
2114 e->ignore(); | |
2115 return; | |
2116 } | |
2117 | |
2118 e->accept(); | |
2119 return; | |
2120 } | |
2121 | |
2122 bool | |
2123 MainWindow::checkSaveModified() | |
2124 { | |
2125 // Called before some destructive operation (e.g. new session, | |
2126 // exit program). Return true if we can safely proceed, false to | |
2127 // cancel. | |
2128 | |
2129 if (!m_documentModified) return true; | |
2130 | |
2131 int button = | |
2132 QMessageBox::warning(this, | |
2133 tr("Session modified"), | |
2134 tr("The current session has been modified.\nDo you want to save it?"), | |
2135 QMessageBox::Yes, | |
2136 QMessageBox::No, | |
2137 QMessageBox::Cancel); | |
2138 | |
2139 if (button == QMessageBox::Yes) { | |
2140 saveSession(); | |
2141 if (m_documentModified) { // save failed -- don't proceed! | |
2142 return false; | |
2143 } else { | |
2144 return true; // saved, so it's safe to continue now | |
2145 } | |
2146 } else if (button == QMessageBox::No) { | |
2147 m_documentModified = false; // so we know to abandon it | |
2148 return true; | |
2149 } | |
2150 | |
2151 // else cancel | |
2152 return false; | |
2153 } | |
2154 | |
2155 void | |
2156 MainWindow::saveSession() | |
2157 { | |
2158 if (m_sessionFile != "") { | |
2159 if (!saveSessionFile(m_sessionFile)) { | |
2160 QMessageBox::critical(this, tr("Failed to save file"), | |
2161 tr("Session file \"%1\" could not be saved.").arg(m_sessionFile)); | |
2162 } else { | |
2163 CommandHistory::getInstance()->documentSaved(); | |
2164 documentRestored(); | |
2165 } | |
2166 } else { | |
2167 saveSessionAs(); | |
2168 } | |
2169 } | |
2170 | |
2171 void | |
2172 MainWindow::saveSessionAs() | |
2173 { | |
2174 QString orig = m_audioFile; | |
2175 if (orig == "") orig = "."; | |
2176 else orig = QFileInfo(orig).absoluteDir().canonicalPath(); | |
2177 | |
2178 QString path; | |
2179 bool good = false; | |
2180 | |
2181 while (!good) { | |
2182 | |
2183 path = QFileDialog::getSaveFileName | |
2184 (this, tr("Select a file to save to"), orig, | |
2185 tr("Sonic Visualiser session files (*.sv)\nAll files (*.*)"), 0, | |
2186 QFileDialog::DontConfirmOverwrite); // we'll do that | |
2187 | |
2188 if (path.isEmpty()) return; | |
2189 | |
2190 if (!path.endsWith(".sv")) path = path + ".sv"; | |
2191 | |
2192 QFileInfo fi(path); | |
2193 | |
2194 if (fi.isDir()) { | |
2195 QMessageBox::critical(this, tr("Directory selected"), | |
2196 tr("File \"%1\" is a directory").arg(path)); | |
2197 continue; | |
2198 } | |
2199 | |
2200 if (fi.exists()) { | |
2201 if (QMessageBox::question(this, tr("File exists"), | |
2202 tr("The file \"%1\" already exists.\nDo you want to overwrite it?").arg(path), | |
2203 QMessageBox::Ok, | |
2204 QMessageBox::Cancel) == QMessageBox::Ok) { | |
2205 good = true; | |
2206 } else { | |
2207 continue; | |
2208 } | |
2209 } | |
2210 | |
2211 good = true; | |
2212 } | |
2213 | |
2214 if (!saveSessionFile(path)) { | |
2215 QMessageBox::critical(this, tr("Failed to save file"), | |
2216 tr("Session file \"%1\" could not be saved.").arg(m_sessionFile)); | |
2217 } else { | |
2218 setWindowTitle(tr("Sonic Visualiser: %1") | |
2219 .arg(QFileInfo(path).fileName())); | |
2220 m_sessionFile = path; | |
2221 CommandHistory::getInstance()->documentSaved(); | |
2222 documentRestored(); | |
2223 RecentFiles::getInstance()->addFile(path); | |
2224 } | |
2225 } | |
2226 | |
2227 bool | |
2228 MainWindow::saveSessionFile(QString path) | |
2229 { | |
2230 BZipFileDevice bzFile(path); | |
2231 if (!bzFile.open(QIODevice::WriteOnly)) { | |
2232 std::cerr << "Failed to open session file \"" << path.toStdString() | |
2233 << "\" for writing: " | |
2234 << bzFile.errorString().toStdString() << std::endl; | |
2235 return false; | |
2236 } | |
2237 | |
2238 QApplication::setOverrideCursor(QCursor(Qt::WaitCursor)); | |
2239 | |
2240 QTextStream out(&bzFile); | |
2241 toXml(out); | |
2242 out.flush(); | |
2243 | |
2244 QApplication::restoreOverrideCursor(); | |
2245 | |
2246 if (bzFile.errorString() != "") { | |
2247 QMessageBox::critical(this, tr("Failed to write file"), | |
2248 tr("Failed to write to file \"%1\": %2") | |
2249 .arg(path).arg(bzFile.errorString())); | |
2250 bzFile.close(); | |
2251 return false; | |
2252 } | |
2253 | |
2254 bzFile.close(); | |
2255 return true; | |
2256 } | |
2257 | |
2258 void | |
2259 MainWindow::toXml(QTextStream &out) | |
2260 { | |
2261 QString indent(" "); | |
2262 | |
2263 out << "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"; | |
2264 out << "<!DOCTYPE sonic-visualiser>\n"; | |
2265 out << "<sv>\n"; | |
2266 | |
2267 m_document->toXml(out, "", ""); | |
2268 | |
2269 out << "<display>\n"; | |
2270 | |
2271 out << QString(" <window width=\"%1\" height=\"%2\"/>\n") | |
2272 .arg(width()).arg(height()); | |
2273 | |
2274 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) { | |
2275 | |
2276 Pane *pane = m_paneStack->getPane(i); | |
2277 | |
2278 if (pane) { | |
2279 pane->toXml(out, indent); | |
2280 } | |
2281 } | |
2282 | |
2283 out << "</display>\n"; | |
2284 | |
2285 m_viewManager->getSelection().toXml(out); | |
2286 | |
2287 out << "</sv>\n"; | |
2288 } | |
2289 | |
2290 Pane * | |
2291 MainWindow::addPaneToStack() | |
2292 { | |
2293 AddPaneCommand *command = new AddPaneCommand(this); | |
2294 CommandHistory::getInstance()->addCommand(command); | |
2295 return command->getPane(); | |
2296 } | |
2297 | |
2298 void | |
2299 MainWindow::zoomIn() | |
2300 { | |
2301 Pane *currentPane = m_paneStack->getCurrentPane(); | |
2302 if (currentPane) currentPane->zoom(true); | |
2303 } | |
2304 | |
2305 void | |
2306 MainWindow::zoomOut() | |
2307 { | |
2308 Pane *currentPane = m_paneStack->getCurrentPane(); | |
2309 if (currentPane) currentPane->zoom(false); | |
2310 } | |
2311 | |
2312 void | |
2313 MainWindow::zoomToFit() | |
2314 { | |
2315 Pane *currentPane = m_paneStack->getCurrentPane(); | |
2316 if (!currentPane) return; | |
2317 | |
2318 Model *model = getMainModel(); | |
2319 if (!model) return; | |
2320 | |
2321 size_t start = model->getStartFrame(); | |
2322 size_t end = model->getEndFrame(); | |
2323 size_t pixels = currentPane->width(); | |
2324 size_t zoomLevel = (end - start) / pixels; | |
2325 | |
2326 currentPane->setZoomLevel(zoomLevel); | |
2327 currentPane->setStartFrame(start); | |
2328 } | |
2329 | |
2330 void | |
2331 MainWindow::zoomDefault() | |
2332 { | |
2333 Pane *currentPane = m_paneStack->getCurrentPane(); | |
2334 if (currentPane) currentPane->setZoomLevel(1024); | |
2335 } | |
2336 | |
2337 void | |
2338 MainWindow::scrollLeft() | |
2339 { | |
2340 Pane *currentPane = m_paneStack->getCurrentPane(); | |
2341 if (currentPane) currentPane->scroll(false, false); | |
2342 } | |
2343 | |
2344 void | |
2345 MainWindow::jumpLeft() | |
2346 { | |
2347 Pane *currentPane = m_paneStack->getCurrentPane(); | |
2348 if (currentPane) currentPane->scroll(false, true); | |
2349 } | |
2350 | |
2351 void | |
2352 MainWindow::scrollRight() | |
2353 { | |
2354 Pane *currentPane = m_paneStack->getCurrentPane(); | |
2355 if (currentPane) currentPane->scroll(true, false); | |
2356 } | |
2357 | |
2358 void | |
2359 MainWindow::jumpRight() | |
2360 { | |
2361 Pane *currentPane = m_paneStack->getCurrentPane(); | |
2362 if (currentPane) currentPane->scroll(true, true); | |
2363 } | |
2364 | |
2365 void | |
2366 MainWindow::showNoOverlays() | |
2367 { | |
2368 m_viewManager->setOverlayMode(ViewManager::NoOverlays); | |
2369 } | |
2370 | |
2371 void | |
2372 MainWindow::showBasicOverlays() | |
2373 { | |
2374 m_viewManager->setOverlayMode(ViewManager::BasicOverlays); | |
2375 } | |
2376 | |
2377 void | |
2378 MainWindow::showAllOverlays() | |
2379 { | |
2380 m_viewManager->setOverlayMode(ViewManager::AllOverlays); | |
2381 } | |
2382 | |
2383 void | |
2384 MainWindow::play() | |
2385 { | |
2386 if (m_playSource->isPlaying()) { | |
2387 m_playSource->stop(); | |
2388 } else { | |
2389 m_playSource->play(m_viewManager->getPlaybackFrame()); | |
2390 } | |
2391 } | |
2392 | |
2393 void | |
2394 MainWindow::ffwd() | |
2395 { | |
2396 if (!getMainModel()) return; | |
2397 | |
2398 int frame = m_viewManager->getPlaybackFrame(); | |
2399 ++frame; | |
2400 | |
2401 Pane *pane = m_paneStack->getCurrentPane(); | |
2402 if (!pane) return; | |
2403 | |
2404 Layer *layer = pane->getSelectedLayer(); | |
2405 | |
2406 if (!dynamic_cast<TimeInstantLayer *>(layer) && | |
2407 !dynamic_cast<TimeValueLayer *>(layer)) return; | |
2408 | |
2409 size_t resolution = 0; | |
2410 if (!layer->snapToFeatureFrame(pane, frame, resolution, Layer::SnapRight)) { | |
2411 frame = getMainModel()->getEndFrame(); | |
2412 } | |
2413 | |
2414 m_viewManager->setPlaybackFrame(frame); | |
2415 } | |
2416 | |
2417 void | |
2418 MainWindow::ffwdEnd() | |
2419 { | |
2420 if (!getMainModel()) return; | |
2421 m_viewManager->setPlaybackFrame(getMainModel()->getEndFrame()); | |
2422 } | |
2423 | |
2424 void | |
2425 MainWindow::rewind() | |
2426 { | |
2427 if (!getMainModel()) return; | |
2428 | |
2429 int frame = m_viewManager->getPlaybackFrame(); | |
2430 if (frame > 0) --frame; | |
2431 | |
2432 Pane *pane = m_paneStack->getCurrentPane(); | |
2433 if (!pane) return; | |
2434 | |
2435 Layer *layer = pane->getSelectedLayer(); | |
2436 | |
2437 if (!dynamic_cast<TimeInstantLayer *>(layer) && | |
2438 !dynamic_cast<TimeValueLayer *>(layer)) return; | |
2439 | |
2440 size_t resolution = 0; | |
2441 if (!layer->snapToFeatureFrame(pane, frame, resolution, Layer::SnapLeft)) { | |
2442 frame = getMainModel()->getEndFrame(); | |
2443 } | |
2444 | |
2445 m_viewManager->setPlaybackFrame(frame); | |
2446 } | |
2447 | |
2448 void | |
2449 MainWindow::rewindStart() | |
2450 { | |
2451 if (!getMainModel()) return; | |
2452 m_viewManager->setPlaybackFrame(getMainModel()->getStartFrame()); | |
2453 } | |
2454 | |
2455 void | |
2456 MainWindow::stop() | |
2457 { | |
2458 m_playSource->stop(); | |
2459 } | |
2460 | |
2461 void | |
2462 MainWindow::addPane() | |
2463 { | |
2464 QObject *s = sender(); | |
2465 QAction *action = dynamic_cast<QAction *>(s); | |
2466 | |
2467 if (!action) { | |
2468 std::cerr << "WARNING: MainWindow::addPane: sender is not an action" | |
2469 << std::endl; | |
2470 return; | |
2471 } | |
2472 | |
2473 PaneActionMap::iterator i = m_paneActions.find(action); | |
2474 | |
2475 if (i == m_paneActions.end()) { | |
2476 std::cerr << "WARNING: MainWindow::addPane: unknown action " | |
2477 << action->objectName().toStdString() << std::endl; | |
2478 return; | |
2479 } | |
2480 | |
2481 CommandHistory::getInstance()->startCompoundOperation | |
2482 (action->text(), true); | |
2483 | |
2484 AddPaneCommand *command = new AddPaneCommand(this); | |
2485 CommandHistory::getInstance()->addCommand(command); | |
2486 | |
2487 Pane *pane = command->getPane(); | |
2488 | |
2489 if (i->second.layer != LayerFactory::TimeRuler) { | |
2490 if (!m_timeRulerLayer) { | |
2491 // std::cerr << "no time ruler layer, creating one" << std::endl; | |
2492 m_timeRulerLayer = m_document->createMainModelLayer | |
2493 (LayerFactory::TimeRuler); | |
2494 } | |
2495 | |
2496 // std::cerr << "adding time ruler layer " << m_timeRulerLayer << std::endl; | |
2497 | |
2498 m_document->addLayerToView(pane, m_timeRulerLayer); | |
2499 } | |
2500 | |
2501 Layer *newLayer = m_document->createLayer(i->second.layer); | |
2502 m_document->setModel(newLayer, m_document->getMainModel()); | |
2503 m_document->setChannel(newLayer, i->second.channel); | |
2504 m_document->addLayerToView(pane, newLayer); | |
2505 | |
2506 m_paneStack->setCurrentPane(pane); | |
2507 | |
2508 CommandHistory::getInstance()->endCompoundOperation(); | |
2509 | |
2510 updateMenuStates(); | |
2511 } | |
2512 | |
2513 MainWindow::AddPaneCommand::AddPaneCommand(MainWindow *mw) : | |
2514 m_mw(mw), | |
2515 m_pane(0), | |
2516 m_prevCurrentPane(0), | |
2517 m_added(false) | |
2518 { | |
2519 } | |
2520 | |
2521 MainWindow::AddPaneCommand::~AddPaneCommand() | |
2522 { | |
2523 if (m_pane && !m_added) { | |
2524 m_mw->m_paneStack->deletePane(m_pane); | |
2525 } | |
2526 } | |
2527 | |
2528 QString | |
2529 MainWindow::AddPaneCommand::getName() const | |
2530 { | |
2531 return tr("Add Pane"); | |
2532 } | |
2533 | |
2534 void | |
2535 MainWindow::AddPaneCommand::execute() | |
2536 { | |
2537 if (!m_pane) { | |
2538 m_prevCurrentPane = m_mw->m_paneStack->getCurrentPane(); | |
2539 m_pane = m_mw->m_paneStack->addPane(); | |
2540 } else { | |
2541 m_mw->m_paneStack->showPane(m_pane); | |
2542 } | |
2543 | |
2544 m_mw->m_paneStack->setCurrentPane(m_pane); | |
2545 m_mw->m_panner->registerView(m_pane); | |
2546 m_added = true; | |
2547 } | |
2548 | |
2549 void | |
2550 MainWindow::AddPaneCommand::unexecute() | |
2551 { | |
2552 m_mw->m_paneStack->hidePane(m_pane); | |
2553 m_mw->m_paneStack->setCurrentPane(m_prevCurrentPane); | |
2554 m_mw->m_panner->unregisterView(m_pane); | |
2555 m_added = false; | |
2556 } | |
2557 | |
2558 MainWindow::RemovePaneCommand::RemovePaneCommand(MainWindow *mw, Pane *pane) : | |
2559 m_mw(mw), | |
2560 m_pane(pane), | |
2561 m_added(true) | |
2562 { | |
2563 } | |
2564 | |
2565 MainWindow::RemovePaneCommand::~RemovePaneCommand() | |
2566 { | |
2567 if (m_pane && !m_added) { | |
2568 m_mw->m_paneStack->deletePane(m_pane); | |
2569 } | |
2570 } | |
2571 | |
2572 QString | |
2573 MainWindow::RemovePaneCommand::getName() const | |
2574 { | |
2575 return tr("Remove Pane"); | |
2576 } | |
2577 | |
2578 void | |
2579 MainWindow::RemovePaneCommand::execute() | |
2580 { | |
2581 m_prevCurrentPane = m_mw->m_paneStack->getCurrentPane(); | |
2582 m_mw->m_paneStack->hidePane(m_pane); | |
2583 m_mw->m_panner->unregisterView(m_pane); | |
2584 m_added = false; | |
2585 } | |
2586 | |
2587 void | |
2588 MainWindow::RemovePaneCommand::unexecute() | |
2589 { | |
2590 m_mw->m_paneStack->showPane(m_pane); | |
2591 m_mw->m_paneStack->setCurrentPane(m_prevCurrentPane); | |
2592 m_mw->m_panner->registerView(m_pane); | |
2593 m_added = true; | |
2594 } | |
2595 | |
2596 void | |
2597 MainWindow::addLayer() | |
2598 { | |
2599 QObject *s = sender(); | |
2600 QAction *action = dynamic_cast<QAction *>(s); | |
2601 | |
2602 if (!action) { | |
2603 std::cerr << "WARNING: MainWindow::addLayer: sender is not an action" | |
2604 << std::endl; | |
2605 return; | |
2606 } | |
2607 | |
2608 Pane *pane = m_paneStack->getCurrentPane(); | |
2609 | |
2610 if (!pane) { | |
2611 std::cerr << "WARNING: MainWindow::addLayer: no current pane" << std::endl; | |
2612 return; | |
2613 } | |
2614 | |
2615 ExistingLayerActionMap::iterator ei = m_existingLayerActions.find(action); | |
2616 | |
2617 if (ei != m_existingLayerActions.end()) { | |
2618 Layer *newLayer = ei->second; | |
2619 m_document->addLayerToView(pane, newLayer); | |
2620 m_paneStack->setCurrentLayer(pane, newLayer); | |
2621 return; | |
2622 } | |
2623 | |
2624 TransformActionMap::iterator i = m_layerTransformActions.find(action); | |
2625 | |
2626 if (i == m_layerTransformActions.end()) { | |
2627 | |
2628 LayerActionMap::iterator i = m_layerActions.find(action); | |
2629 | |
2630 if (i == m_layerActions.end()) { | |
2631 std::cerr << "WARNING: MainWindow::addLayer: unknown action " | |
2632 << action->objectName().toStdString() << std::endl; | |
2633 return; | |
2634 } | |
2635 | |
2636 LayerFactory::LayerType type = i->second; | |
2637 | |
2638 LayerFactory::LayerTypeSet emptyTypes = | |
2639 LayerFactory::getInstance()->getValidEmptyLayerTypes(); | |
2640 | |
2641 Layer *newLayer; | |
2642 | |
2643 if (emptyTypes.find(type) != emptyTypes.end()) { | |
2644 | |
2645 newLayer = m_document->createEmptyLayer(type); | |
2646 m_toolActions[ViewManager::DrawMode]->trigger(); | |
2647 | |
2648 } else { | |
2649 | |
2650 newLayer = m_document->createMainModelLayer(type); | |
2651 } | |
2652 | |
2653 m_document->addLayerToView(pane, newLayer); | |
2654 m_paneStack->setCurrentLayer(pane, newLayer); | |
2655 | |
2656 return; | |
2657 } | |
2658 | |
2659 TransformName transform = i->second; | |
2660 TransformFactory *factory = TransformFactory::getInstance(); | |
2661 | |
2662 QString configurationXml; | |
2663 | |
2664 int channel = -1; | |
2665 // pick up the default channel from any existing layers on the same pane | |
2666 for (int j = 0; j < pane->getLayerCount(); ++j) { | |
2667 int c = LayerFactory::getInstance()->getChannel(pane->getLayer(j)); | |
2668 if (c != -1) { | |
2669 channel = c; | |
2670 break; | |
2671 } | |
2672 } | |
2673 | |
2674 bool needConfiguration = false; | |
2675 | |
2676 if (factory->isTransformConfigurable(transform)) { | |
2677 needConfiguration = true; | |
2678 } else { | |
2679 int minChannels, maxChannels; | |
2680 int myChannels = m_document->getMainModel()->getChannelCount(); | |
2681 if (factory->getTransformChannelRange(transform, | |
2682 minChannels, | |
2683 maxChannels)) { | |
2684 // std::cerr << "myChannels: " << myChannels << ", minChannels: " << minChannels << ", maxChannels: " << maxChannels << std::endl; | |
2685 needConfiguration = (myChannels > maxChannels && maxChannels == 1); | |
2686 } | |
2687 } | |
2688 | |
2689 if (needConfiguration) { | |
2690 bool ok = | |
2691 factory->getConfigurationForTransform | |
2692 (transform, m_document->getMainModel(), channel, configurationXml); | |
2693 if (!ok) return; | |
2694 } | |
2695 | |
2696 Layer *newLayer = m_document->createDerivedLayer(transform, | |
2697 m_document->getMainModel(), | |
2698 channel, | |
2699 configurationXml); | |
2700 | |
2701 if (newLayer) { | |
2702 m_document->addLayerToView(pane, newLayer); | |
2703 m_document->setChannel(newLayer, channel); | |
2704 } | |
2705 | |
2706 updateMenuStates(); | |
2707 } | |
2708 | |
2709 void | |
2710 MainWindow::deleteCurrentPane() | |
2711 { | |
2712 CommandHistory::getInstance()->startCompoundOperation | |
2713 (tr("Delete Pane"), true); | |
2714 | |
2715 Pane *pane = m_paneStack->getCurrentPane(); | |
2716 if (pane) { | |
2717 while (pane->getLayerCount() > 0) { | |
2718 Layer *layer = pane->getLayer(0); | |
2719 if (layer) { | |
2720 m_document->removeLayerFromView(pane, layer); | |
2721 } else { | |
2722 break; | |
2723 } | |
2724 } | |
2725 | |
2726 RemovePaneCommand *command = new RemovePaneCommand(this, pane); | |
2727 CommandHistory::getInstance()->addCommand(command); | |
2728 } | |
2729 | |
2730 CommandHistory::getInstance()->endCompoundOperation(); | |
2731 | |
2732 updateMenuStates(); | |
2733 } | |
2734 | |
2735 void | |
2736 MainWindow::deleteCurrentLayer() | |
2737 { | |
2738 Pane *pane = m_paneStack->getCurrentPane(); | |
2739 if (pane) { | |
2740 Layer *layer = pane->getSelectedLayer(); | |
2741 if (layer) { | |
2742 m_document->removeLayerFromView(pane, layer); | |
2743 } | |
2744 } | |
2745 updateMenuStates(); | |
2746 } | |
2747 | |
2748 void | |
2749 MainWindow::renameCurrentLayer() | |
2750 { | |
2751 Pane *pane = m_paneStack->getCurrentPane(); | |
2752 if (pane) { | |
2753 Layer *layer = pane->getSelectedLayer(); | |
2754 if (layer) { | |
2755 bool ok = false; | |
2756 QString newName = QInputDialog::getText | |
2757 (this, tr("Rename Layer"), | |
2758 tr("New name for this layer:"), | |
2759 QLineEdit::Normal, layer->objectName(), &ok); | |
2760 if (ok) { | |
2761 layer->setObjectName(newName); | |
2762 setupExistingLayersMenu(); | |
2763 } | |
2764 } | |
2765 } | |
2766 } | |
2767 | |
2768 void | |
2769 MainWindow::playSpeedChanged(int speed) | |
2770 { | |
2771 int factor = 11 - speed; | |
2772 m_playSpeed->setToolTip(tr("Playback speed: %1") | |
2773 .arg(factor > 1 ? | |
2774 QString("1/%1").arg(factor) : | |
2775 tr("Full"))); | |
2776 m_playSource->setSlowdownFactor(factor); | |
2777 } | |
2778 | |
2779 void | |
2780 MainWindow::outputLevelsChanged(float left, float right) | |
2781 { | |
2782 m_fader->setPeakLeft(left); | |
2783 m_fader->setPeakRight(right); | |
2784 } | |
2785 | |
2786 void | |
2787 MainWindow::sampleRateMismatch(size_t requested, size_t actual, | |
2788 bool willResample) | |
2789 { | |
2790 if (!willResample) { | |
2791 //!!! more helpful message needed | |
2792 QMessageBox::information | |
2793 (this, tr("Sample rate mismatch"), | |
2794 tr("The sample rate of this audio file (%1 Hz) does not match\nthe current playback rate (%2 Hz).\n\nThe file will play at the wrong speed.") | |
2795 .arg(requested).arg(actual)); | |
2796 } | |
2797 | |
2798 /*!!! Let's not do this for now, and see how we go -- now that we're putting | |
2799 sample rate information in the status bar | |
2800 | |
2801 QMessageBox::information | |
2802 (this, tr("Sample rate mismatch"), | |
2803 tr("The sample rate of this audio file (%1 Hz) does not match\nthat of the output audio device (%2 Hz).\n\nThe file will be resampled automatically during playback.") | |
2804 .arg(requested).arg(actual)); | |
2805 */ | |
2806 | |
2807 updateDescriptionLabel(); | |
2808 } | |
2809 | |
2810 void | |
2811 MainWindow::layerAdded(Layer *layer) | |
2812 { | |
2813 // std::cerr << "MainWindow::layerAdded(" << layer << ")" << std::endl; | |
2814 // setupExistingLayersMenu(); | |
2815 updateMenuStates(); | |
2816 } | |
2817 | |
2818 void | |
2819 MainWindow::layerRemoved(Layer *layer) | |
2820 { | |
2821 // std::cerr << "MainWindow::layerRemoved(" << layer << ")" << std::endl; | |
2822 setupExistingLayersMenu(); | |
2823 updateMenuStates(); | |
2824 } | |
2825 | |
2826 void | |
2827 MainWindow::layerAboutToBeDeleted(Layer *layer) | |
2828 { | |
2829 // std::cerr << "MainWindow::layerAboutToBeDeleted(" << layer << ")" << std::endl; | |
2830 if (layer == m_timeRulerLayer) { | |
2831 // std::cerr << "(this is the time ruler layer)" << std::endl; | |
2832 m_timeRulerLayer = 0; | |
2833 } | |
2834 } | |
2835 | |
2836 void | |
2837 MainWindow::layerInAView(Layer *layer, bool inAView) | |
2838 { | |
2839 // std::cerr << "MainWindow::layerInAView(" << layer << "," << inAView << ")" << std::endl; | |
2840 | |
2841 // Check whether we need to add or remove model from play source | |
2842 Model *model = layer->getModel(); | |
2843 if (model) { | |
2844 if (inAView) { | |
2845 m_playSource->addModel(model); | |
2846 } else { | |
2847 bool found = false; | |
2848 for (int i = 0; i < m_paneStack->getPaneCount(); ++i) { | |
2849 Pane *pane = m_paneStack->getPane(i); | |
2850 if (!pane) continue; | |
2851 for (int j = 0; j < pane->getLayerCount(); ++j) { | |
2852 Layer *pl = pane->getLayer(j); | |
2853 if (pl && pl->getModel() == model) { | |
2854 found = true; | |
2855 break; | |
2856 } | |
2857 } | |
2858 if (found) break; | |
2859 } | |
2860 if (!found) m_playSource->removeModel(model); | |
2861 } | |
2862 } | |
2863 | |
2864 setupExistingLayersMenu(); | |
2865 updateMenuStates(); | |
2866 } | |
2867 | |
2868 void | |
2869 MainWindow::modelAdded(Model *model) | |
2870 { | |
2871 // std::cerr << "MainWindow::modelAdded(" << model << ")" << std::endl; | |
2872 m_playSource->addModel(model); | |
2873 } | |
2874 | |
2875 void | |
2876 MainWindow::mainModelChanged(WaveFileModel *model) | |
2877 { | |
2878 // std::cerr << "MainWindow::mainModelChanged(" << model << ")" << std::endl; | |
2879 updateDescriptionLabel(); | |
2880 m_panLayer->setModel(model); | |
2881 if (model) m_viewManager->setMainModelSampleRate(model->getSampleRate()); | |
2882 if (model && !m_playTarget) createPlayTarget(); | |
2883 } | |
2884 | |
2885 void | |
2886 MainWindow::modelAboutToBeDeleted(Model *model) | |
2887 { | |
2888 // std::cerr << "MainWindow::modelAboutToBeDeleted(" << model << ")" << std::endl; | |
2889 m_playSource->removeModel(model); | |
2890 } | |
2891 | |
2892 void | |
2893 MainWindow::modelGenerationFailed(QString transformName) | |
2894 { | |
2895 QMessageBox::warning | |
2896 (this, | |
2897 tr("Failed to generate layer"), | |
2898 tr("The layer transform \"%1\" failed to run.\nThis probably means that a plugin failed to initialise.") | |
2899 .arg(transformName), | |
2900 QMessageBox::Ok, 0); | |
2901 } | |
2902 | |
2903 void | |
2904 MainWindow::modelRegenerationFailed(QString layerName, QString transformName) | |
2905 { | |
2906 QMessageBox::warning | |
2907 (this, | |
2908 tr("Failed to regenerate layer"), | |
2909 tr("Failed to regenerate derived layer \"%1\".\nThe layer transform \"%2\" failed to run.\nThis probably means the layer used a plugin that is not currently available.") | |
2910 .arg(layerName).arg(transformName), | |
2911 QMessageBox::Ok, 0); | |
2912 } | |
2913 | |
2914 void | |
2915 MainWindow::rightButtonMenuRequested(Pane *pane, QPoint position) | |
2916 { | |
2917 // std::cerr << "MainWindow::rightButtonMenuRequested(" << pane << ", " << position.x() << ", " << position.y() << ")" << std::endl; | |
2918 m_paneStack->setCurrentPane(pane); | |
2919 m_rightButtonMenu->popup(position); | |
2920 } | |
2921 | |
2922 void | |
2923 MainWindow::showLayerTree() | |
2924 { | |
2925 QTreeView *view = new QTreeView(); | |
2926 LayerTreeModel *tree = new LayerTreeModel(m_paneStack); | |
2927 view->expand(tree->index(0, 0, QModelIndex())); | |
2928 view->setModel(tree); | |
2929 view->show(); | |
2930 } | |
2931 | |
2932 void | |
2933 MainWindow::preferenceChanged(PropertyContainer::PropertyName name) | |
2934 { | |
2935 if (name == "Property Box Layout") { | |
2936 if (Preferences::getInstance()->getPropertyBoxLayout() == | |
2937 Preferences::VerticallyStacked) { | |
2938 m_paneStack->setLayoutStyle(PaneStack::PropertyStackPerPaneLayout); | |
2939 } else { | |
2940 m_paneStack->setLayoutStyle(PaneStack::SinglePropertyStackLayout); | |
2941 } | |
2942 } | |
2943 } | |
2944 | |
2945 void | |
2946 MainWindow::preferences() | |
2947 { | |
2948 if (!m_preferencesDialog.isNull()) { | |
2949 m_preferencesDialog->show(); | |
2950 m_preferencesDialog->raise(); | |
2951 return; | |
2952 } | |
2953 | |
2954 m_preferencesDialog = new PreferencesDialog(this); | |
2955 | |
2956 // DeleteOnClose is safe here, because m_preferencesDialog is a | |
2957 // QPointer that will be zeroed when the dialog is deleted. We | |
2958 // use it in preference to leaving the dialog lying around because | |
2959 // if you Cancel the dialog, it resets the preferences state | |
2960 // without resetting its own widgets, so its state will be | |
2961 // incorrect when next shown unless we construct it afresh | |
2962 m_preferencesDialog->setAttribute(Qt::WA_DeleteOnClose); | |
2963 | |
2964 m_preferencesDialog->show(); | |
2965 } | |
2966 | |
2967 void | |
2968 MainWindow::website() | |
2969 { | |
2970 openHelpUrl(tr("http://www.sonicvisualiser.org/")); | |
2971 } | |
2972 | |
2973 void | |
2974 MainWindow::help() | |
2975 { | |
2976 openHelpUrl(tr("http://www.sonicvisualiser.org/doc/reference/en/")); | |
2977 } | |
2978 | |
2979 void | |
2980 MainWindow::openHelpUrl(QString url) | |
2981 { | |
2982 // This method mostly lifted from Qt Assistant source code | |
2983 | |
2984 QProcess *process = new QProcess(this); | |
2985 connect(process, SIGNAL(finished(int)), process, SLOT(deleteLater())); | |
2986 | |
2987 QStringList args; | |
2988 | |
2989 #ifdef Q_OS_MAC | |
2990 args.append(url); | |
2991 process->start("open", args); | |
2992 #else | |
2993 #ifdef Q_OS_WIN32 | |
2994 | |
2995 QString pf(getenv("ProgramFiles")); | |
2996 QString command = pf + QString("\\Internet Explorer\\IEXPLORE.EXE"); | |
2997 | |
2998 args.append(url); | |
2999 process->start(command, args); | |
3000 | |
3001 #else | |
3002 #ifdef Q_WS_X11 | |
3003 if (!qgetenv("KDE_FULL_SESSION").isEmpty()) { | |
3004 args.append("exec"); | |
3005 args.append(url); | |
3006 process->start("kfmclient", args); | |
3007 } else if (!qgetenv("BROWSER").isEmpty()) { | |
3008 args.append(url); | |
3009 process->start(qgetenv("BROWSER"), args); | |
3010 } else { | |
3011 args.append(url); | |
3012 process->start("firefox", args); | |
3013 } | |
3014 #endif | |
3015 #endif | |
3016 #endif | |
3017 } | |
3018 | |
3019 void | |
3020 MainWindow::about() | |
3021 { | |
3022 bool debug = false; | |
3023 QString version = "(unknown version)"; | |
3024 | |
3025 #ifdef BUILD_DEBUG | |
3026 debug = true; | |
3027 #endif | |
3028 #ifdef SV_VERSION | |
3029 #ifdef SVNREV | |
3030 version = tr("Release %1 : Revision %2").arg(SV_VERSION).arg(SVNREV); | |
3031 #else | |
3032 version = tr("Release %1").arg(SV_VERSION); | |
3033 #endif | |
3034 #else | |
3035 #ifdef SVNREV | |
3036 version = tr("Unreleased : Revision %1").arg(SVNREV); | |
3037 #endif | |
3038 #endif | |
3039 | |
3040 QString aboutText; | |
3041 | |
3042 aboutText += tr("<h3>About Sonic Visualiser</h3>"); | |
3043 aboutText += tr("<p>Sonic Visualiser is a program for viewing and exploring audio data for semantic music analysis and annotation.</p>"); | |
3044 aboutText += tr("<p>%1 : %2 build</p>") | |
3045 .arg(version) | |
3046 .arg(debug ? tr("Debug") : tr("Release")); | |
3047 | |
3048 #ifdef BUILD_STATIC | |
3049 aboutText += tr("<p>Statically linked"); | |
3050 #ifndef QT_SHARED | |
3051 aboutText += tr("<br>With Qt (v%1) © Trolltech AS").arg(QT_VERSION_STR); | |
3052 #endif | |
3053 #ifdef HAVE_JACK | |
3054 aboutText += tr("<br>With JACK audio output (v%1) © Paul Davis and Jack O'Quin").arg(JACK_VERSION); | |
3055 #endif | |
3056 #ifdef HAVE_PORTAUDIO | |
3057 aboutText += tr("<br>With PortAudio audio output © Ross Bencina and Phil Burk"); | |
3058 #endif | |
3059 #ifdef HAVE_OGGZ | |
3060 aboutText += tr("<br>With Ogg file decoder (oggz v%1, fishsound v%2) © CSIRO Australia").arg(OGGZ_VERSION).arg(FISHSOUND_VERSION); | |
3061 #endif | |
3062 #ifdef HAVE_MAD | |
3063 aboutText += tr("<br>With MAD mp3 decoder (v%1) © Underbit Technologies Inc").arg(MAD_VERSION); | |
3064 #endif | |
3065 #ifdef HAVE_SAMPLERATE | |
3066 aboutText += tr("<br>With libsamplerate (v%1) © Erik de Castro Lopo").arg(SAMPLERATE_VERSION); | |
3067 #endif | |
3068 #ifdef HAVE_SNDFILE | |
3069 aboutText += tr("<br>With libsndfile (v%1) © Erik de Castro Lopo").arg(SNDFILE_VERSION); | |
3070 #endif | |
3071 #ifdef HAVE_FFTW3 | |
3072 aboutText += tr("<br>With FFTW3 (v%1) © Matteo Frigo and MIT").arg(FFTW3_VERSION); | |
3073 #endif | |
3074 #ifdef HAVE_VAMP | |
3075 aboutText += tr("<br>With Vamp plugin support (API v%1, SDK v%2) © Chris Cannam").arg(VAMP_API_VERSION).arg(VAMP_SDK_VERSION); | |
3076 #endif | |
3077 aboutText += tr("<br>With LADSPA plugin support (API v%1) © Richard Furse, Paul Davis, Stefan Westerfeld").arg(LADSPA_VERSION); | |
3078 aboutText += tr("<br>With DSSI plugin support (API v%1) © Chris Cannam, Steve Harris, Sean Bolton").arg(DSSI_VERSION); | |
3079 aboutText += "</p>"; | |
3080 #endif | |
3081 | |
3082 aboutText += | |
3083 "<p>Sonic Visualiser Copyright © 2005 - 2006 Chris Cannam<br>" | |
3084 "Centre for Digital Music, Queen Mary, University of London.</p>" | |
3085 "<p>This program is free software; you can redistribute it and/or<br>" | |
3086 "modify it under the terms of the GNU General Public License as<br>" | |
3087 "published by the Free Software Foundation; either version 2 of the<br>" | |
3088 "License, or (at your option) any later version.<br>See the file " | |
3089 "COPYING included with this distribution for more information.</p>"; | |
3090 | |
3091 QMessageBox::about(this, tr("About Sonic Visualiser"), aboutText); | |
3092 } | |
3093 | |
3094 | |
3095 #ifdef INCLUDE_MOCFILES | |
3096 #include "MainWindow.moc.cpp" | |
3097 #endif |