annotate framework/MainWindowBase.cpp @ 644:6fd0ebfd2bbe

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