Chris@224: /* -*- c-basic-offset: 4 indent-tabs-mode: nil -*- vi:set ts=8 sts=4 sw=4: */ Chris@224: Chris@224: /* Chris@224: Sonic Visualiser Chris@224: An audio file viewer and annotation editor. Chris@224: Centre for Digital Music, Queen Mary, University of London. Chris@224: This file copyright 2006-2007 Chris Cannam and QMUL. Chris@224: Chris@224: This program is free software; you can redistribute it and/or Chris@224: modify it under the terms of the GNU General Public License as Chris@224: published by the Free Software Foundation; either version 2 of the Chris@224: License, or (at your option) any later version. See the file Chris@224: COPYING included with this distribution for more information. Chris@224: */ Chris@216: Chris@216: #include "MainWindow.h" Chris@216: #include "data/osc/OSCQueue.h" Chris@216: Chris@216: #include "layer/WaveformLayer.h" Chris@216: #include "view/ViewManager.h" Chris@216: #include "view/Pane.h" Chris@216: #include "view/PaneStack.h" Chris@216: #include "data/model/WaveFileModel.h" Chris@248: #include "widgets/CommandHistory.h" Chris@1035: #include "audio/AudioCallbackPlaySource.h" Chris@216: #include "framework/Document.h" Chris@216: #include "data/fileio/WavFileWriter.h" Chris@249: #include "transform/TransformFactory.h" Chris@1386: #include "widgets/LevelPanWidget.h" Chris@1431: #include "widgets/LevelPanToolButton.h" Chris@216: #include "widgets/AudioDial.h" Chris@216: Chris@1035: #include Chris@1035: Chris@216: #include Chris@2441: #include Chris@2441: #include Chris@2441: Chris@2524: #if (QT_VERSION >= 0x050600) Chris@2441: #define NOW (QTime::currentTime().toString(Qt::ISODateWithMs)) Chris@2524: #else Chris@2524: #define NOW (QTime::currentTime().toString(Qt::ISODate)) Chris@2524: #endif Chris@216: Chris@216: void Chris@216: MainWindow::handleOSCMessage(const OSCMessage &message) Chris@216: { Chris@2441: QElapsedTimer timer; Chris@2441: timer.start(); Chris@2441: Chris@2441: SVDEBUG << "OSCHandler at " << NOW << ": handling message: " Chris@2441: << message.toString() << endl; Chris@216: Chris@216: if (message.getMethod() == "open") { Chris@216: Chris@216: if (message.getArgCount() == 1 && Chris@216: message.getArg(0).canConvert(QVariant::String)) { Chris@216: QString path = message.getArg(0).toString(); Chris@2441: if (open(path, ReplaceMainModel) == FileOpenSucceeded) { Chris@2441: SVDEBUG << "OSCHandler: Opened path \"" Chris@2441: << path << "\"" << endl; Chris@2441: } else { Chris@2441: SVCERR << "OSCHandler: File open failed for path \"" Chris@2441: << path << "\"" << endl; Chris@216: } Chris@216: //!!! we really need to spin here and not return until the Chris@216: // file has been completely decoded... Chris@2519: } else { Chris@2519: SVCERR << "OSCHandler: Usage: /open " << endl; Chris@216: } Chris@216: Chris@216: } else if (message.getMethod() == "openadditional") { Chris@216: Chris@216: if (message.getArgCount() == 1 && Chris@216: message.getArg(0).canConvert(QVariant::String)) { Chris@216: QString path = message.getArg(0).toString(); Chris@2441: if (open(path, CreateAdditionalModel) == FileOpenSucceeded) { Chris@2441: SVDEBUG << "OSCHandler: Opened additional path \"" Chris@2441: << path << "\"" << endl; Chris@2441: } else { Chris@2441: SVCERR << "OSCHandler: File open failed for path \"" Chris@665: << path << "\"" << endl; Chris@216: } Chris@2519: } else { Chris@2519: SVCERR << "OSCHandler: Usage: /openadditional " << endl; Chris@216: } Chris@216: Chris@216: } else if (message.getMethod() == "recent" || Chris@216: message.getMethod() == "last") { Chris@216: Chris@216: int n = 0; Chris@216: if (message.getMethod() == "recent" && Chris@216: message.getArgCount() == 1 && Chris@216: message.getArg(0).canConvert(QVariant::Int)) { Chris@216: n = message.getArg(0).toInt() - 1; Chris@216: } Chris@216: std::vector recent = m_recentFiles.getRecent(); Chris@216: if (n >= 0 && n < int(recent.size())) { Chris@2441: QString path = recent[n]; Chris@2441: if (open(path, ReplaceMainModel) == FileOpenSucceeded) { Chris@2441: SVDEBUG << "OSCHandler: Opened recent path \"" Chris@2441: << path << "\"" << endl; Chris@2441: } else { Chris@2441: SVCERR << "OSCHandler: File open failed for path \"" Chris@2441: << path << "\"" << endl; Chris@216: } Chris@2519: } else { Chris@2519: SVCERR << "OSCHandler: Usage: /recent " << endl; Chris@2519: SVCERR << " or /last" << endl; Chris@216: } Chris@216: Chris@216: } else if (message.getMethod() == "save") { Chris@216: Chris@216: QString path; Chris@216: if (message.getArgCount() == 1 && Chris@216: message.getArg(0).canConvert(QVariant::String)) { Chris@216: path = message.getArg(0).toString(); Chris@216: if (QFileInfo(path).exists()) { Chris@2441: SVCERR << "OSCHandler: Refusing to overwrite existing file in save" << endl; Chris@2441: } else if (saveSessionFile(path)) { Chris@2441: SVDEBUG << "OSCHandler: Saved session to path \"" Chris@2441: << path << "\"" << endl; Chris@216: } else { Chris@2441: SVCERR << "OSCHandler: Save failed to path \"" Chris@2441: << path << "\"" << endl; Chris@216: } Chris@2519: } else { Chris@2519: SVCERR << "OSCHandler: Usage: /save " << endl; Chris@216: } Chris@216: Chris@216: } else if (message.getMethod() == "export") { Chris@216: Chris@216: QString path; Chris@216: if (getMainModel()) { Chris@216: if (message.getArgCount() == 1 && Chris@216: message.getArg(0).canConvert(QVariant::String)) { Chris@216: path = message.getArg(0).toString(); Chris@216: if (QFileInfo(path).exists()) { Chris@2441: SVCERR << "OSCHandler: Refusing to overwrite existing file in export" << endl; Chris@216: } else { Chris@216: WavFileWriter writer(path, Chris@216: getMainModel()->getSampleRate(), Chris@428: getMainModel()->getChannelCount(), Chris@428: WavFileWriter::WriteToTemporary); Chris@216: MultiSelection ms = m_viewManager->getSelection(); Chris@2441: if (writer.writeModel Chris@2441: (getMainModel().get(), Chris@2441: ms.getSelections().empty() ? nullptr : &ms)) { Chris@2441: SVDEBUG << "OSCHandler: Exported audio to path \"" Chris@2441: << path << "\"" << endl; Chris@216: } else { Chris@2441: SVCERR << "OSCHandler: Export failed to path \"" Chris@2441: << path << "\"" << endl; Chris@216: } Chris@216: } Chris@216: } Chris@2519: } else { Chris@2519: SVCERR << "OSCHandler: Usage: /export " << endl; Chris@216: } Chris@216: Chris@2242: } else if (message.getMethod() == "exportlayer") { Chris@2242: Chris@2242: QString path; Chris@2242: if (message.getArgCount() == 1 && Chris@2242: message.getArg(0).canConvert(QVariant::String)) { Chris@2242: path = message.getArg(0).toString(); Chris@2242: if (QFileInfo(path).exists()) { Chris@2519: SVCERR << "OSCHandler: Refusing to overwrite existing file in layer export" << endl; Chris@2242: } else { Chris@2242: Pane *currentPane = nullptr; Chris@2242: Layer *currentLayer = nullptr; Chris@2242: if (m_paneStack) currentPane = m_paneStack->getCurrentPane(); Chris@2242: if (currentPane) currentLayer = currentPane->getSelectedLayer(); Chris@2442: MultiSelection ms = m_viewManager->getSelection(); Chris@2242: if (currentLayer) { Chris@2242: QString error; Chris@2442: if (exportLayerTo Chris@2442: (currentLayer, currentPane, Chris@2442: ms.getSelections().empty() ? nullptr : &ms, Chris@2442: path, error)) { Chris@2442: SVDEBUG << "OSCHandler: Exported layer \"" Chris@2442: << currentLayer->getLayerPresentationName() Chris@2442: << "\" to path \"" << path << "\"" << endl; Chris@2442: } else { Chris@2442: SVCERR << "OSCHandler: Export failed to path \"" Chris@2442: << path << "\"" << endl; Chris@2242: } Chris@2242: } else { Chris@2242: SVCERR << "OSCHandler: No current layer to export" << endl; Chris@2242: } Chris@2242: } Chris@2519: } else { Chris@2519: SVCERR << "OSCHandler: Usage: /exportlayer " << endl; Chris@2519: } Chris@2519: Chris@2519: } else if (message.getMethod() == "exportimage") { Chris@2519: Chris@2519: QString path; Chris@2519: if (message.getArgCount() == 1 && Chris@2519: message.getArg(0).canConvert(QVariant::String)) { Chris@2519: path = message.getArg(0).toString(); Chris@2519: if (QFileInfo(path).exists()) { Chris@2519: SVCERR << "OSCHandler: Refusing to overwrite existing file in image export" << endl; Chris@2519: } else { Chris@2519: Pane *currentPane = nullptr; Chris@2519: if (m_paneStack) currentPane = m_paneStack->getCurrentPane(); Chris@2519: MultiSelection ms = m_viewManager->getSelection(); Chris@2519: if (currentPane) { Chris@2519: QImage *image = nullptr; Chris@2519: auto sel = ms.getSelections(); Chris@2519: if (!sel.empty()) { Chris@2519: sv_frame_t sf0 = sel.begin()->getStartFrame(); Chris@2519: sv_frame_t sf1 = sel.rbegin()->getEndFrame(); Chris@2519: image = currentPane->renderPartToNewImage(sf0, sf1); Chris@2519: } else { Chris@2519: image = currentPane->renderToNewImage(); Chris@2519: } Chris@2519: if (!image) { Chris@2519: SVCERR << "OSCHandler: Failed to create image from pane" Chris@2519: << endl; Chris@2519: } else if (!image->save(path, "PNG")) { Chris@2519: SVCERR << "OSCHandler: Export failed to image file \"" Chris@2519: << path << "\"" << endl; Chris@2519: } else { Chris@2519: SVDEBUG << "OSCHandler: Exported pane to image file \"" Chris@2519: << path << "\"" << endl; Chris@2519: } Chris@2519: delete image; Chris@2519: } else { Chris@2519: SVCERR << "OSCHandler: No current pane to export" << endl; Chris@2519: } Chris@2519: } Chris@2519: } else { Chris@2519: SVCERR << "OSCHandler: Usage: /exportimage " << endl; Chris@2519: } Chris@2519: Chris@2519: } else if (message.getMethod() == "exportsvg") { Chris@2519: Chris@2519: QString path; Chris@2519: if (message.getArgCount() == 1 && Chris@2519: message.getArg(0).canConvert(QVariant::String)) { Chris@2519: path = message.getArg(0).toString(); Chris@2519: if (QFileInfo(path).exists()) { Chris@2519: SVCERR << "OSCHandler: Refusing to overwrite existing file in SVG export" << endl; Chris@2519: } else { Chris@2519: Pane *currentPane = nullptr; Chris@2519: if (m_paneStack) currentPane = m_paneStack->getCurrentPane(); Chris@2519: MultiSelection ms = m_viewManager->getSelection(); Chris@2519: if (currentPane) { Chris@2519: bool result = false; Chris@2519: auto sel = ms.getSelections(); Chris@2519: if (!sel.empty()) { Chris@2519: sv_frame_t sf0 = sel.begin()->getStartFrame(); Chris@2519: sv_frame_t sf1 = sel.rbegin()->getEndFrame(); Chris@2519: result = currentPane->renderPartToSvgFile(path, sf0, sf1); Chris@2519: } else { Chris@2519: result = currentPane->renderToSvgFile(path); Chris@2519: } Chris@2519: if (!result) { Chris@2519: SVCERR << "OSCHandler: Export failed to SVG file \"" Chris@2519: << path << "\"" << endl; Chris@2519: } else { Chris@2519: SVDEBUG << "OSCHandler: Exported pane to SVG file \"" Chris@2519: << path << "\"" << endl; Chris@2519: } Chris@2519: } else { Chris@2519: SVCERR << "OSCHandler: No current pane to export" << endl; Chris@2519: } Chris@2519: } Chris@2519: } else { Chris@2519: SVCERR << "OSCHandler: Usage: /exportsvg " << endl; Chris@2242: } Chris@2242: Chris@216: } else if (message.getMethod() == "jump" || Chris@216: message.getMethod() == "play") { Chris@216: Chris@216: if (getMainModel()) { Chris@216: Chris@922: sv_frame_t frame = m_viewManager->getPlaybackFrame(); Chris@216: bool selection = false; Chris@216: bool play = (message.getMethod() == "play"); Chris@216: Chris@216: if (message.getArgCount() == 1) { Chris@216: Chris@216: if (message.getArg(0).canConvert(QVariant::String) && Chris@216: message.getArg(0).toString() == "selection") { Chris@216: Chris@216: selection = true; Chris@216: Chris@216: } else if (message.getArg(0).canConvert(QVariant::String) && Chris@216: message.getArg(0).toString() == "end") { Chris@216: Chris@216: frame = getMainModel()->getEndFrame(); Chris@216: Chris@216: } else if (message.getArg(0).canConvert(QVariant::Double)) { Chris@216: Chris@216: double time = message.getArg(0).toDouble(); Chris@216: if (time < 0.0) time = 0.0; Chris@216: Chris@216: frame = lrint(time * getMainModel()->getSampleRate()); Chris@216: } Chris@216: } Chris@216: Chris@216: if (frame > getMainModel()->getEndFrame()) { Chris@216: frame = getMainModel()->getEndFrame(); Chris@216: } Chris@216: Chris@216: if (play) { Chris@216: m_viewManager->setPlaySelectionMode(selection); Chris@216: } Chris@216: Chris@216: if (selection) { Chris@216: MultiSelection::SelectionList sl = m_viewManager->getSelections(); Chris@216: if (!sl.empty()) { Chris@216: frame = sl.begin()->getStartFrame(); Chris@216: } Chris@216: } Chris@216: Chris@1617: SVDEBUG << "OSCHandler: Setting playback frame to " << frame << endl; Chris@1617: Chris@216: m_viewManager->setPlaybackFrame(frame); Chris@216: Chris@1617: if (play) { Chris@1617: if (!m_playSource->isPlaying()) { Chris@1617: SVDEBUG << "OSCHandler: Play source is not yet playing, calling play()" << endl; Chris@1617: // handles audio device suspend/resume etc, as Chris@1617: // well as calling m_playSource->play(frame) Chris@1617: MainWindow::play(); Chris@1617: } else { Chris@1617: SVDEBUG << "OSCHandler: Play source is already playing, not starting it" << endl; Chris@1617: } Chris@1617: } else { Chris@1617: SVDEBUG << "OSCHandler: Jump only requested, not starting playback" << endl; Chris@216: } Chris@216: } Chris@216: Chris@313: } else if (message.getMethod() == "ffwd") { Chris@313: Chris@313: if (message.getArgCount() == 1) { Chris@313: Chris@313: if (message.getArg(0).canConvert(QVariant::String) && Chris@313: message.getArg(0).toString() == "similar") { Chris@313: Chris@2441: SVDEBUG << "OSCHandler: Calling ffwdSimilar" << endl; Chris@313: ffwdSimilar(); Chris@313: } Chris@313: } else { Chris@313: Chris@2441: SVDEBUG << "OSCHandler: Calling ffwd" << endl; Chris@313: ffwd(); Chris@313: } Chris@313: Chris@313: } else if (message.getMethod() == "rewind") { Chris@313: Chris@313: if (message.getArgCount() == 1) { Chris@313: Chris@313: if (message.getArg(0).canConvert(QVariant::String) && Chris@313: message.getArg(0).toString() == "similar") { Chris@313: Chris@2441: SVDEBUG << "OSCHandler: Calling rewindSimilar" << endl; Chris@313: rewindSimilar(); Chris@313: } Chris@313: } else { Chris@313: Chris@2441: SVDEBUG << "OSCHandler: Calling rewind" << endl; Chris@313: rewind(); Chris@313: } Chris@313: Chris@216: } else if (message.getMethod() == "stop") { Chris@216: Chris@2441: if (m_playSource->isPlaying()) { Chris@2441: // As with play, we want to use the MainWindow function Chris@2441: // rather than call m_playSource directly because that way Chris@2441: // the audio driver suspend/resume etc is handled properly Chris@2441: SVDEBUG << "OSCHandler: Calling stop" << endl; Chris@2441: MainWindow::stop(); Chris@2441: } else { Chris@2441: SVDEBUG << "OSCHandler: Not playing, doing nothing" << endl; Chris@2441: } Chris@216: Chris@216: } else if (message.getMethod() == "loop") { Chris@216: Chris@216: if (message.getArgCount() == 1 && Chris@216: message.getArg(0).canConvert(QVariant::String)) { Chris@216: Chris@216: QString str = message.getArg(0).toString(); Chris@216: if (str == "on") { Chris@2441: SVDEBUG << "OSCHandler: Enabling loop mode" << endl; Chris@216: m_viewManager->setPlayLoopMode(true); Chris@216: } else if (str == "off") { Chris@2441: SVDEBUG << "OSCHandler: Disabling loop mode" << endl; Chris@216: m_viewManager->setPlayLoopMode(false); Chris@216: } Chris@216: } Chris@216: Chris@216: } else if (message.getMethod() == "solo") { Chris@216: Chris@216: if (message.getArgCount() == 1 && Chris@216: message.getArg(0).canConvert(QVariant::String)) { Chris@216: Chris@216: QString str = message.getArg(0).toString(); Chris@216: if (str == "on") { Chris@2441: SVDEBUG << "OSCHandler: Enabling solo mode" << endl; Chris@216: m_viewManager->setPlaySoloMode(true); Chris@216: } else if (str == "off") { Chris@2441: SVDEBUG << "OSCHandler: Disabling solo mode" << endl; Chris@216: m_viewManager->setPlaySoloMode(false); Chris@216: } Chris@216: } Chris@216: Chris@216: } else if (message.getMethod() == "select" || Chris@216: message.getMethod() == "addselect") { Chris@216: Chris@216: if (getMainModel()) { Chris@216: Chris@920: sv_frame_t f0 = getMainModel()->getStartFrame(); Chris@920: sv_frame_t f1 = getMainModel()->getEndFrame(); Chris@216: Chris@216: bool done = false; Chris@216: Chris@216: if (message.getArgCount() == 2 && Chris@216: message.getArg(0).canConvert(QVariant::Double) && Chris@216: message.getArg(1).canConvert(QVariant::Double)) { Chris@216: Chris@216: double t0 = message.getArg(0).toDouble(); Chris@216: double t1 = message.getArg(1).toDouble(); Chris@216: if (t1 < t0) { double temp = t0; t0 = t1; t1 = temp; } Chris@216: if (t0 < 0.0) t0 = 0.0; Chris@216: if (t1 < 0.0) t1 = 0.0; Chris@216: Chris@216: f0 = lrint(t0 * getMainModel()->getSampleRate()); Chris@216: f1 = lrint(t1 * getMainModel()->getSampleRate()); Chris@2441: Chris@2441: SVDEBUG << "OSCHandler: Converted selection extents to frames " Chris@2441: << f0 << " and " << f1 << endl; Chris@216: Chris@216: Pane *pane = m_paneStack->getCurrentPane(); Chris@2126: Layer *layer = nullptr; Chris@216: if (pane) layer = pane->getSelectedLayer(); Chris@216: if (layer) { Chris@730: int resolution; Chris@216: layer->snapToFeatureFrame(pane, f0, resolution, Chris@2380: Layer::SnapLeft, -1); Chris@216: layer->snapToFeatureFrame(pane, f1, resolution, Chris@2380: Layer::SnapRight, -1); Chris@2441: Chris@2441: SVDEBUG << "OSCHandler: Snapped selection extents to " Chris@2441: << f0 << " and " << f1 << " for current layer \"" Chris@2441: << layer->getLayerPresentationName() << "\"" Chris@2441: << endl; Chris@216: } Chris@216: Chris@216: } else if (message.getArgCount() == 1 && Chris@216: message.getArg(0).canConvert(QVariant::String)) { Chris@2519: Chris@216: QString str = message.getArg(0).toString(); Chris@216: if (str == "none") { Chris@2441: SVDEBUG << "OSCHandler: Clearing selection" << endl; Chris@216: m_viewManager->clearSelections(); Chris@216: done = true; Chris@2441: } else if (str == "all") { Chris@2441: SVDEBUG << "OSCHandler: Selecting all" << endl; Chris@2441: f0 = getModelsStartFrame(); Chris@2441: f0 = getModelsEndFrame(); Chris@216: } Chris@216: } Chris@216: Chris@216: if (!done) { Chris@216: if (message.getMethod() == "select") { Chris@216: m_viewManager->setSelection(Selection(f0, f1)); Chris@216: } else { Chris@216: m_viewManager->addSelection(Selection(f0, f1)); Chris@216: } Chris@216: } Chris@2441: Chris@2441: SVDEBUG << "OSCHandler: Selection now is " Chris@2441: << m_viewManager->getSelection().toString() << endl; Chris@2441: Chris@2441: } else { Chris@2441: SVCERR << "OSCHandler: No main model, can't modify selection" Chris@2441: << endl; Chris@216: } Chris@216: Chris@216: } else if (message.getMethod() == "add") { Chris@216: Chris@216: if (getMainModel()) { Chris@216: Chris@216: if (message.getArgCount() >= 1 && Chris@216: message.getArg(0).canConvert(QVariant::String)) { Chris@216: Chris@216: int channel = -1; Chris@216: if (message.getArgCount() == 2 && Chris@216: message.getArg(0).canConvert(QVariant::Int)) { Chris@216: channel = message.getArg(0).toInt(); Chris@216: if (channel < -1 || Chris@2519: channel >= int(getMainModel()->getChannelCount())) { Chris@2519: SVCERR << "OSCHandler: channel " << channel Chris@2519: << " out of range (0 to " Chris@2519: << (getMainModel()->getChannelCount() - 1) Chris@2519: << ")" << endl; Chris@216: channel = -1; Chris@216: } Chris@216: } Chris@216: Chris@216: QString str = message.getArg(0).toString(); Chris@216: Chris@216: LayerFactory::LayerType type = Chris@216: LayerFactory::getInstance()->getLayerTypeForName(str); Chris@216: Chris@216: if (type == LayerFactory::UnknownLayer) { Chris@2441: SVCERR << "WARNING: OSCHandler: unknown layer " Chris@2441: << "type " << str << endl; Chris@216: } else { Chris@216: Chris@232: LayerConfiguration configuration(type, Chris@2300: getMainModelId(), Chris@232: channel); Chris@2441: Chris@2441: QString pname = LayerFactory::getInstance()-> Chris@2441: getLayerPresentationName(type); Chris@216: Chris@2441: addPane(configuration, tr("Add %1 Pane") .arg(pname)); Chris@2441: Chris@2441: SVDEBUG << "OSCHandler: Added pane \"" << pname Chris@2441: << "\"" << endl; Chris@216: } Chris@2519: } else { Chris@2519: SVCERR << "OSCHandler: Usage: /add []" Chris@2519: << endl; Chris@216: } Chris@216: } Chris@216: Chris@216: } else if (message.getMethod() == "undo") { Chris@216: Chris@2441: SVDEBUG << "OSCHandler: Calling undo" << endl; Chris@216: CommandHistory::getInstance()->undo(); Chris@216: Chris@216: } else if (message.getMethod() == "redo") { Chris@216: Chris@2441: SVDEBUG << "OSCHandler: Calling redo" << endl; Chris@216: CommandHistory::getInstance()->redo(); Chris@216: Chris@216: } else if (message.getMethod() == "set") { Chris@216: Chris@216: if (message.getArgCount() == 2 && Chris@216: message.getArg(0).canConvert(QVariant::String) && Chris@216: message.getArg(1).canConvert(QVariant::Double)) { Chris@216: Chris@216: QString property = message.getArg(0).toString(); Chris@216: float value = (float)message.getArg(1).toDouble(); Chris@216: Chris@216: if (property == "gain") { Chris@216: if (value < 0.0) value = 0.0; Chris@1386: m_mainLevelPan->setLevel(value); Chris@216: if (m_playTarget) m_playTarget->setOutputGain(value); Chris@1617: } else if (property == "speed") { Chris@1617: m_playSpeed->setMappedValue(value); Chris@216: } else if (property == "speedup") { Chris@1617: Chris@1617: // The speedup method existed before the speed method Chris@1617: // and is a bit weirder. Chris@1617: // Chris@1617: // For speed(x), x is a percentage of normal speed, so Chris@1617: // x=100 means play at the normal speed, x=50 means Chris@1617: // half speed, x=200 double speed etc. Chris@1617: // Chris@1617: // For speedup(x), x was some sort of modifier of Chris@1617: // percentage thing, so x=0 meant play at the normal Chris@1617: // speed, x=50 meant play at 150% of normal speed, Chris@1617: // x=100 meant play at double speed, and x=-100 rather Chris@1617: // bizarrely meant play at half speed. We handle this Chris@1617: // now by converting to speed percentage as follows: Chris@1617: Chris@1617: double percentage = 100.0; Chris@1617: if (value > 0.f) { Chris@1617: percentage = percentage + value; Chris@1617: } else { Chris@1617: percentage = 10000.0 / (percentage - value); Chris@1617: } Chris@1617: SVDEBUG << "OSCHandler: converted speedup(" << value Chris@1617: << ") into speed(" << percentage << ")" << endl; Chris@1617: Chris@1617: m_playSpeed->setMappedValue(percentage); Chris@1617: Chris@216: } else if (property == "overlays") { Chris@216: if (value < 0.5) { Chris@216: m_viewManager->setOverlayMode(ViewManager::NoOverlays); Chris@216: } else if (value < 1.5) { Chris@701: m_viewManager->setOverlayMode(ViewManager::StandardOverlays); Chris@216: } else { Chris@216: m_viewManager->setOverlayMode(ViewManager::AllOverlays); Chris@216: } Chris@216: } else if (property == "zoomwheels") { Chris@216: m_viewManager->setZoomWheelsEnabled(value > 0.5); Chris@216: } else if (property == "propertyboxes") { Chris@216: bool toggle = ((value < 0.5) != Chris@2339: (m_paneStack->getLayoutStyle() == Chris@2339: PaneStack::HiddenPropertyStacksLayout)); Chris@216: if (toggle) togglePropertyBoxes(); Chris@216: } Chris@216: Chris@216: } else { Chris@2126: PropertyContainer *container = nullptr; Chris@216: Pane *pane = m_paneStack->getCurrentPane(); Chris@216: if (pane && Chris@216: message.getArgCount() == 3 && Chris@216: message.getArg(0).canConvert(QVariant::String) && Chris@216: message.getArg(1).canConvert(QVariant::String) && Chris@216: message.getArg(2).canConvert(QVariant::String)) { Chris@216: if (message.getArg(0).toString() == "pane") { Chris@216: container = pane->getPropertyContainer(0); Chris@216: } else if (message.getArg(0).toString() == "layer") { Chris@216: container = pane->getSelectedLayer(); Chris@216: } Chris@2519: } else { Chris@2519: SVCERR << "OSCHandler: Usage: /set " << endl Chris@2519: << " or /set pane " << endl Chris@2519: << " or /set layer " << endl; Chris@216: } Chris@216: if (container) { Chris@216: QString nameString = message.getArg(1).toString(); Chris@216: QString valueString = message.getArg(2).toString(); Chris@248: Command *c = container->getSetPropertyCommand Chris@248: (nameString, valueString); Chris@248: if (c) CommandHistory::getInstance()->addCommand(c, true, true); Chris@216: } Chris@216: } Chris@216: Chris@216: } else if (message.getMethod() == "setcurrent") { Chris@216: Chris@216: int paneIndex = -1, layerIndex = -1; Chris@216: bool wantLayer = false; Chris@216: Chris@216: if (message.getArgCount() >= 1 && Chris@216: message.getArg(0).canConvert(QVariant::Int)) { Chris@216: Chris@216: paneIndex = message.getArg(0).toInt() - 1; Chris@216: Chris@216: if (message.getArgCount() >= 2 && Chris@216: message.getArg(1).canConvert(QVariant::Int)) { Chris@216: wantLayer = true; Chris@216: layerIndex = message.getArg(1).toInt() - 1; Chris@216: } Chris@2519: } else { Chris@2519: SVCERR << "OSCHandler: Usage: /setcurrent []" << endl; Chris@216: } Chris@216: Chris@216: if (paneIndex >= 0 && paneIndex < m_paneStack->getPaneCount()) { Chris@216: Pane *pane = m_paneStack->getPane(paneIndex); Chris@216: m_paneStack->setCurrentPane(pane); Chris@2441: SVDEBUG << "OSCHandler: Set current pane to index " Chris@2441: << paneIndex << " (pane id " << pane->getId() Chris@2441: << ")" << endl; Chris@216: if (layerIndex >= 0 && layerIndex < pane->getLayerCount()) { Chris@2437: Layer *layer = pane->getFixedOrderLayer(layerIndex); Chris@216: m_paneStack->setCurrentLayer(pane, layer); Chris@2441: SVDEBUG << "OSCHandler: Set current layer to index " Chris@2441: << layerIndex << " (layer \"" Chris@2441: << layer->getLayerPresentationName() << "\")" << endl; Chris@216: } else if (wantLayer && layerIndex == -1) { Chris@2126: m_paneStack->setCurrentLayer(pane, nullptr); Chris@2441: } else if (wantLayer) { Chris@2441: SVCERR << "OSCHandler: Layer index " Chris@2441: << layerIndex << " out of range for pane" << endl; Chris@2441: } Chris@216: } Chris@216: Chris@216: } else if (message.getMethod() == "delete") { Chris@216: Chris@216: if (message.getArgCount() == 1 && Chris@216: message.getArg(0).canConvert(QVariant::String)) { Chris@216: Chris@216: QString target = message.getArg(0).toString(); Chris@216: Chris@216: if (target == "pane") { Chris@216: Chris@2441: SVDEBUG << "OSCHandler: Calling deleteCurrentPane" << endl; Chris@216: deleteCurrentPane(); Chris@216: Chris@216: } else if (target == "layer") { Chris@216: Chris@2441: SVDEBUG << "OSCHandler: Calling deleteCurrentLayer" << endl; Chris@216: deleteCurrentLayer(); Chris@216: Chris@216: } else { Chris@216: Chris@2441: SVCERR << "WARNING: OSCHandler: Unknown delete target \"" Chris@2441: << target << "\"" << endl; Chris@216: } Chris@2519: } else { Chris@2519: SVCERR << "OSCHandler: Usage: /delete pane" << endl Chris@2519: << " or /delete layer" << endl; Chris@216: } Chris@216: Chris@216: } else if (message.getMethod() == "zoom") { Chris@216: Chris@216: if (message.getArgCount() == 1) { Chris@216: if (message.getArg(0).canConvert(QVariant::String) && Chris@216: message.getArg(0).toString() == "in") { Chris@216: zoomIn(); Chris@216: } else if (message.getArg(0).canConvert(QVariant::String) && Chris@216: message.getArg(0).toString() == "out") { Chris@216: zoomOut(); Chris@216: } else if (message.getArg(0).canConvert(QVariant::String) && Chris@216: message.getArg(0).toString() == "default") { Chris@216: zoomDefault(); Chris@312: } else if (message.getArg(0).canConvert(QVariant::String) && Chris@312: message.getArg(0).toString() == "fit") { Chris@312: zoomToFit(); Chris@216: } else if (message.getArg(0).canConvert(QVariant::Double)) { Chris@216: double level = message.getArg(0).toDouble(); Chris@216: Pane *currentPane = m_paneStack->getCurrentPane(); Chris@2011: ZoomLevel zoomLevel; Chris@2011: if (level >= 0.66) { Chris@2011: zoomLevel = ZoomLevel(ZoomLevel::FramesPerPixel, Chris@2011: int(round(level))); Chris@2011: } else { Chris@2011: zoomLevel = ZoomLevel(ZoomLevel::PixelsPerFrame, Chris@2011: int(round(1.0 / level))); Chris@2011: } Chris@2011: if (currentPane) { Chris@2441: SVDEBUG << "OSCHandler: Setting zoom level to " Chris@2441: << zoomLevel << endl; Chris@2011: currentPane->setZoomLevel(zoomLevel); Chris@2441: } else { Chris@2441: SVCERR << "OSCHandler: No current pane, can't set zoom" Chris@2441: << endl; Chris@2011: } Chris@216: } Chris@2519: } else { Chris@2519: SVCERR << "OSCHandler: Usage: /zoom " << endl Chris@2519: << " or /zoom in" << endl Chris@2519: << " or /zoom out" << endl Chris@2519: << " or /zoom fit" << endl Chris@2519: << " or /zoom default" << endl; Chris@216: } Chris@216: Chris@216: } else if (message.getMethod() == "zoomvertical") { Chris@216: Chris@216: Pane *pane = m_paneStack->getCurrentPane(); Chris@2126: Layer *layer = nullptr; Chris@216: if (pane && pane->getLayerCount() > 0) { Chris@216: layer = pane->getLayer(pane->getLayerCount() - 1); Chris@216: } Chris@216: int defaultStep = 0; Chris@216: int steps = 0; Chris@216: if (layer) { Chris@216: steps = layer->getVerticalZoomSteps(defaultStep); Chris@216: if (message.getArgCount() == 1 && steps > 0) { Chris@216: if (message.getArg(0).canConvert(QVariant::String) && Chris@216: message.getArg(0).toString() == "in") { Chris@216: int step = layer->getCurrentVerticalZoomStep() + 1; Chris@216: if (step < steps) layer->setVerticalZoomStep(step); Chris@216: } else if (message.getArg(0).canConvert(QVariant::String) && Chris@216: message.getArg(0).toString() == "out") { Chris@216: int step = layer->getCurrentVerticalZoomStep() - 1; Chris@216: if (step >= 0) layer->setVerticalZoomStep(step); Chris@216: } else if (message.getArg(0).canConvert(QVariant::String) && Chris@216: message.getArg(0).toString() == "default") { Chris@216: layer->setVerticalZoomStep(defaultStep); Chris@216: } Chris@216: } else if (message.getArgCount() == 2) { Chris@216: if (message.getArg(0).canConvert(QVariant::Double) && Chris@216: message.getArg(1).canConvert(QVariant::Double)) { Chris@216: double min = message.getArg(0).toDouble(); Chris@216: double max = message.getArg(1).toDouble(); Chris@216: layer->setDisplayExtents(min, max); Chris@216: } Chris@216: } Chris@216: } Chris@216: Chris@216: } else if (message.getMethod() == "quit") { Chris@2440: Chris@2440: SVDEBUG << "OSCHandler: Exiting abruptly" << endl; Chris@2529: Chris@2529: // discard any more pending OSC messages Chris@2529: if (m_oscQueue) { Chris@2529: while (!m_oscQueue->isEmpty()) { Chris@2529: (void)m_oscQueue->readMessage(); Chris@2529: } Chris@2529: } Chris@2529: Chris@2440: m_documentModified = false; // so we don't ask to save Chris@216: close(); Chris@216: Chris@216: } else if (message.getMethod() == "resize") { Chris@216: Chris@216: if (message.getArgCount() == 2) { Chris@216: Chris@216: int width = 0, height = 0; Chris@216: Chris@216: if (message.getArg(1).canConvert(QVariant::Int)) { Chris@216: Chris@216: height = message.getArg(1).toInt(); Chris@216: Chris@216: if (message.getArg(0).canConvert(QVariant::String) && Chris@216: message.getArg(0).toString() == "pane") { Chris@216: Chris@216: Pane *pane = m_paneStack->getCurrentPane(); Chris@216: if (pane) pane->resize(pane->width(), height); Chris@216: Chris@216: } else if (message.getArg(0).canConvert(QVariant::Int)) { Chris@216: Chris@216: width = message.getArg(0).toInt(); Chris@216: resize(width, height); Chris@216: } Chris@216: } Chris@216: } Chris@216: Chris@216: } else if (message.getMethod() == "transform") { Chris@216: Chris@2441: if (message.getArgCount() == 1 && Chris@216: message.getArg(0).canConvert(QVariant::String)) { Chris@216: Chris@2441: Pane *pane = m_paneStack->getCurrentPane(); Chris@2441: Chris@2441: if (getMainModel() && pane) { Chris@216: Chris@2441: TransformId transformId = message.getArg(0).toString(); Chris@2441: Chris@2441: Transform transform = TransformFactory::getInstance()-> Chris@2441: getDefaultTransformFor(transformId); Chris@2441: Chris@2441: SVDEBUG << "OSCHandler: Running transform on main model:" Chris@2441: << transform.toXmlString() << endl; Chris@1770: Chris@2441: Layer *newLayer = m_document->createDerivedLayer Chris@2441: (transform, getMainModelId()); Chris@2441: Chris@2441: if (newLayer) { Chris@2441: m_document->addLayerToView(pane, newLayer); Chris@2441: m_recentTransforms.add(transformId); Chris@2441: m_paneStack->setCurrentLayer(pane, newLayer); Chris@2441: } else { Chris@2441: SVCERR << "OSCHandler: Transform failed to run" << endl; Chris@2441: } Chris@2441: } else { Chris@2441: SVCERR << "OSCHandler: Lack main model or pane, " Chris@2441: << "can't run transform" << endl; Chris@216: } Chris@2441: } Chris@216: Chris@216: } else { Chris@2441: SVCERR << "WARNING: OSCHandler: Unknown or unsupported " Chris@665: << "method \"" << message.getMethod() Chris@665: << "\"" << endl; Chris@216: } Chris@2441: Chris@2441: SVDEBUG << "OSCHandler at " << NOW << ": finished message: " Chris@2441: << message.toString() << " in " << timer.elapsed() << "ms" << endl; Chris@216: }