annotate framework/MainWindowBase.cpp @ 638:8c5379570f36 fix-static-analysis

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