From da9fcfcfb47e63149caa9c97839a19200eb41c84 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Thu, 1 Nov 2018 21:42:38 +0100 Subject: [PATCH] Allow drag-dropping onto Controller Rack * Implement connecting automatable model to controller by dragging its widget onto a controller in the controller rack * Implement saving of automatable models with controller connections when their names must be quoted * Let Engine::getAutomatableModel() return existing osc connections, too (in order to overwrite them) --- include/AutomatableModel.h | 2 ++ include/ControllerRackView.h | 2 ++ src/core/AutomatableModel.cpp | 44 +++++++++++++++++++++++--- src/core/Engine.cpp | 24 +++++++++----- src/core/spa/SpaInstrument.cpp | 1 + src/gui/widgets/ControllerRackView.cpp | 35 ++++++++++++++++++-- 6 files changed, 93 insertions(+), 15 deletions(-) diff --git a/include/AutomatableModel.h b/include/AutomatableModel.h index 8dbe80efddc..78f4882b423 100644 --- a/include/AutomatableModel.h +++ b/include/AutomatableModel.h @@ -283,6 +283,8 @@ public slots: private: + static bool mustQuoteName(const QString &name); + virtual void saveSettings( QDomDocument& doc, QDomElement& element ) { saveSettings( doc, element, "value" ); diff --git a/include/ControllerRackView.h b/include/ControllerRackView.h index cb393f5cacb..86edcdc5280 100644 --- a/include/ControllerRackView.h +++ b/include/ControllerRackView.h @@ -63,6 +63,8 @@ public slots: protected: virtual void closeEvent( QCloseEvent * _ce ); + virtual void dragEnterEvent( QDragEnterEvent *dee ); + virtual void dropEvent( QDropEvent * de ); private slots: void addController(); diff --git a/src/core/AutomatableModel.cpp b/src/core/AutomatableModel.cpp index cb8aa704682..62b783da4fd 100644 --- a/src/core/AutomatableModel.cpp +++ b/src/core/AutomatableModel.cpp @@ -89,16 +89,23 @@ bool AutomatableModel::isAutomated() const } + +bool AutomatableModel::mustQuoteName(const QString& name) +{ + QRegExp reg("^[A-Za-z0-9._-]+$"); + return !reg.exactMatch(name); +} + void AutomatableModel::saveSettings( QDomDocument& doc, QDomElement& element, const QString& name ) { + bool mustQuote = mustQuoteName(name); + if( isAutomated() || m_scaleType != Linear ) { // automation needs tuple of data (name, id, value) // scale type also needs an extra value // => it must be appended as a node - QRegExp reg("^[A-Za-z0-9._-]+$"); - bool mustQuote = !reg.exactMatch(name); QDomElement me = doc.createElement( mustQuote ? QString("automatablemodel") : name ); me.setAttribute( "id", ProjectJournal::idToSave( id() ) ); me.setAttribute( "value", m_value ); @@ -110,8 +117,18 @@ void AutomatableModel::saveSettings( QDomDocument& doc, QDomElement& element, co } else { - // non automation, linear scale (default), can be saved as attribute - element.setAttribute( name, m_value ); + if(mustQuote) + { + QDomElement me = doc.createElement( "automatablemodel" ); + me.setAttribute( "nodename", name ); + me.setAttribute( "value", m_value ); + element.appendChild( me ); + } + else + { + // non automation, linear scale (default), can be saved as attribute + element.setAttribute( name, m_value ); + } } if( m_controllerConnection && m_controllerConnection->getController()->type() @@ -131,7 +148,13 @@ void AutomatableModel::saveSettings( QDomDocument& doc, QDomElement& element, co element.appendChild( controllerElement ); } - QDomElement element = doc.createElement( name ); + bool mustQuote = mustQuoteName(name); + QString elementName = mustQuote ? "controllerconnection" + : name; + + QDomElement element = doc.createElement( elementName ); + if(mustQuote) + element.setAttribute( "nodename", name ); m_controllerConnection->saveSettings( doc, element ); controllerElement.appendChild( element ); @@ -170,6 +193,17 @@ void AutomatableModel::loadSettings( const QDomElement& element, const QString& if( connectionNode.isElement() ) { QDomNode thisConnection = connectionNode.toElement().namedItem( name ); + if( !thisConnection.isElement() ) + { + thisConnection = connectionNode.toElement().namedItem( "controllerconnection" ); + QDomElement tcElement = thisConnection.toElement(); + // sanity check + if( tcElement.isNull() || tcElement.attribute( "nodename" ) != name ) + { + // no, that wasn't it, act as if we never found one + thisConnection.clear(); + } + } if( thisConnection.isElement() ) { setControllerConnection( new ControllerConnection( (Controller*)NULL ) ); diff --git a/src/core/Engine.cpp b/src/core/Engine.cpp index 66fe47badba..5301695e716 100644 --- a/src/core/Engine.cpp +++ b/src/core/Engine.cpp @@ -144,16 +144,24 @@ AutomatableModel *LmmsCore::getAutomatableModel(const QString& val, bool hasOsc) } else { - /* TODO: name */ - AutomatableModel* spamod = SpaOscModelFactory(itr.value(), url.path()).res; - //SpaOscModel* spamod = new SpaOscModel(itr.value(), url.path().toUtf8().data()); - if(spamod) + QMap& connectedModels + = itr.value()->connectedModels; + auto itr2 = connectedModels.find(url.path()); + if(itr2 != connectedModels.end()) { - itr.value()->connectedModels.insert(url.path(), spamod); - mod = spamod; + return *itr2; } - else { - qDebug() << "LMMS: Could not create model from OSC port (received \"" << val << "\")"; + else + { + AutomatableModel* spamod = SpaOscModelFactory(itr.value(), url.path()).res; + if(spamod) + { + itr.value()->connectedModels.insert(url.path(), spamod); + mod = spamod; + } + else { + qDebug() << "LMMS: Could not create model from OSC port (received \"" << val << "\")"; + } } } } diff --git a/src/core/spa/SpaInstrument.cpp b/src/core/spa/SpaInstrument.cpp index 1c995f74477..5b82c41fcb4 100644 --- a/src/core/spa/SpaInstrument.cpp +++ b/src/core/spa/SpaInstrument.cpp @@ -206,6 +206,7 @@ void SpaInstrument::loadSettings( const QDomElement & _this ) for(QDomElement portnode = elem.firstChildElement(); !portnode.isNull(); portnode = portnode.nextSiblingElement()) + if(portnode.nodeName() != "connection") { QString name = portnode.nodeName(); if(name == "automatablemodel") diff --git a/src/gui/widgets/ControllerRackView.cpp b/src/gui/widgets/ControllerRackView.cpp index 621c41c73e6..fd9f34c7295 100644 --- a/src/gui/widgets/ControllerRackView.cpp +++ b/src/gui/widgets/ControllerRackView.cpp @@ -36,9 +36,11 @@ #include "GuiApplication.h" #include "MainWindow.h" #include "GroupBox.h" +#include "ControllerConnection.h" #include "ControllerRackView.h" #include "ControllerView.h" #include "LfoController.h" +#include "StringPairDrag.h" ControllerRackView::ControllerRackView( ) : @@ -87,6 +89,8 @@ ControllerRackView::ControllerRackView( ) : subWin->resize( 350, 200 ); subWin->setFixedWidth( 350 ); subWin->setMinimumHeight( 200 ); + + setAcceptDrops(true); } @@ -202,7 +206,7 @@ void ControllerRackView::addController() void ControllerRackView::closeEvent( QCloseEvent * _ce ) - { +{ if( parentWidget() ) { parentWidget()->hide(); @@ -212,5 +216,32 @@ void ControllerRackView::closeEvent( QCloseEvent * _ce ) hide(); } _ce->ignore(); - } +} + +void ControllerRackView::dragEnterEvent( QDragEnterEvent *dee ) +{ + StringPairDrag::processDragEnterEvent( dee, "automatable_model" ); +} + +void ControllerRackView::dropEvent( QDropEvent *de ) +{ + QString type = StringPairDrag::decodeKey( de ); + QString val = StringPairDrag::decodeValue( de ); + // qDebug() << "DROP: type/val:" << type << ", " << val; + + if( type == "automatable_model" ) + { + AutomatableModel* mod = + Engine::getAutomatableModel( val, + de->mimeData()->hasFormat( "application/x-osc-stringpair" )); + + de->acceptProposedAction(); + + if( mod->controllerConnection() ) + { + delete mod->controllerConnection(); + mod->setControllerConnection( nullptr ); + } + } +}