annotate framework/MainWindowBase.cpp @ 646:3168deb50199 single-point

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