annotate framework/MainWindowBase.cpp @ 588:d122d3595a32

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