annotate framework/MainWindowBase.cpp @ 650:ed9cb577eb7c single-point

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