diff --git a/data/themes/classic/edit_unlink.svg b/data/themes/classic/edit_unlink.svg
new file mode 100644
index 00000000000..15526dea44a
--- /dev/null
+++ b/data/themes/classic/edit_unlink.svg
@@ -0,0 +1,76 @@
+
+
+
+
diff --git a/data/themes/default/edit_unlink.svg b/data/themes/default/edit_unlink.svg
new file mode 100644
index 00000000000..15526dea44a
--- /dev/null
+++ b/data/themes/default/edit_unlink.svg
@@ -0,0 +1,76 @@
+
+
+
+
diff --git a/include/AutomatableModel.h b/include/AutomatableModel.h
index 0f05ea343f5..f7d9b470e80 100644
--- a/include/AutomatableModel.h
+++ b/include/AutomatableModel.h
@@ -77,8 +77,6 @@ class LMMS_EXPORT AutomatableModel : public Model, public JournallingObject
{
Q_OBJECT
public:
- using AutoModelVector = std::vector;
-
enum class ScaleType
{
Linear,
@@ -150,22 +148,26 @@ class LMMS_EXPORT AutomatableModel : public Model, public JournallingObject
template
inline T value( int frameOffset = 0 ) const
{
- if (m_controllerConnection)
+ // TODO
+ // The `m_value` should only be updated whenever the Controller value changes,
+ // instead of the Model calling `controller->currentValue()` every time.
+ // This becomes even worse in the case of linked Models, where it has to
+ // loop through the list of all links.
+
+ if (m_useControllerValue)
{
- if (!m_useControllerValue)
+ if (m_controllerConnection)
{
- return castValue(m_value);
+ return castValue(controllerValue(frameOffset));
}
- else
+ for (auto next = m_nextLink; next != this; next = next->m_nextLink)
{
- return castValue(controllerValue(frameOffset));
+ if (next->controllerConnection() && next->useControllerValue())
+ {
+ return castValue(fittedValue(next->controllerValue(frameOffset)));
+ }
}
}
- else if (hasLinkedModels())
- {
- return castValue( controllerValue( frameOffset ) );
- }
-
return castValue( m_value );
}
@@ -211,8 +213,7 @@ class LMMS_EXPORT AutomatableModel : public Model, public JournallingObject
void setInitValue( const float value );
- void setAutomatedValue( const float value );
- void setValue( const float value );
+ void setValue(const float value, const bool isAutomated = false);
void incValue( int steps )
{
@@ -249,11 +250,10 @@ class LMMS_EXPORT AutomatableModel : public Model, public JournallingObject
m_centerValue = centerVal;
}
- //! link @p m1 and @p m2, let @p m1 take the values of @p m2
- static void linkModels( AutomatableModel* m1, AutomatableModel* m2 );
- static void unlinkModels( AutomatableModel* m1, AutomatableModel* m2 );
-
- void unlinkAllModels();
+ //! link this to @p model, copying the value from @p model
+ void linkToModel(AutomatableModel* model);
+ //! @return number of other models linked to this
+ size_t countLinks() const;
/**
* @brief Saves settings (value, automation links and controller connections) of AutomatableModel into
@@ -276,9 +276,9 @@ class LMMS_EXPORT AutomatableModel : public Model, public JournallingObject
virtual QString displayValue( const float val ) const = 0;
- bool hasLinkedModels() const
+ bool isLinked() const
{
- return !m_linkedModels.empty();
+ return m_nextLink != this;
}
// a way to track changed values in the model and avoid using signals/slots - useful for speed-critical code.
@@ -311,13 +311,14 @@ class LMMS_EXPORT AutomatableModel : public Model, public JournallingObject
s_periodCounter = 0;
}
- bool useControllerValue()
+ bool useControllerValue() const
{
return m_useControllerValue;
}
public slots:
virtual void reset();
+ void unlink();
void unlinkControllerConnection();
void setUseControllerValue(bool b = true);
@@ -367,9 +368,15 @@ public slots:
loadSettings( element, "value" );
}
- void linkModel( AutomatableModel* model );
- void unlinkModel( AutomatableModel* model );
+ void setValueInternal(const float value);
+ //! linking is stored in a linked list ring
+ //! @return the model whose `m_nextLink` is `this`,
+ //! or `this` if there are no linked models
+ AutomatableModel* getLastLinkedModel() const;
+ //! @return true if the `model` is in the linked list
+ bool isLinkedToModel(AutomatableModel* model) const;
+
//! @brief Scales @value from linear to logarithmic.
//! Value should be within [0,1]
template T logToLinearScale( T value ) const;
@@ -389,16 +396,15 @@ public slots:
float m_centerValue;
bool m_valueChanged;
-
- // currently unused?
- float m_oldValue;
- int m_setValueDepth;
+ float m_oldValue; //!< used by valueBuffer for interpolation
// used to determine if step size should be applied strictly (ie. always)
// or only when value set from gui (default)
bool m_hasStrictStepSize;
- AutoModelVector m_linkedModels;
+ //! an `AutomatableModel` can be linked together with others in a linked list
+ //! the list has no end, the last model is connected to the first forming a ring
+ AutomatableModel* m_nextLink;
//! NULL if not appended to controller, otherwise connection info
diff --git a/include/AutomatableModelView.h b/include/AutomatableModelView.h
index 12b2e4d4919..2d4a88ac085 100644
--- a/include/AutomatableModelView.h
+++ b/include/AutomatableModelView.h
@@ -98,7 +98,6 @@ public slots:
void execConnectionDialog();
void removeConnection();
void editSongGlobalAutomation();
- void unlinkAllModels();
void removeSongGlobalAutomation();
private slots:
diff --git a/plugins/Vestige/Vestige.cpp b/plugins/Vestige/Vestige.cpp
index 014efbc7a09..53e06f10131 100644
--- a/plugins/Vestige/Vestige.cpp
+++ b/plugins/Vestige/Vestige.cpp
@@ -1072,8 +1072,8 @@ void ManageVestigeInstrumentView::syncPlugin( void )
std::snprintf(paramStr.data(), paramStr.size(), "param%d", i);
s_dumpValues = dump[paramStr.data()].split(":");
float f_value = LocaleHelper::toFloat(s_dumpValues.at(2));
- m_vi->knobFModel[ i ]->setAutomatedValue( f_value );
- m_vi->knobFModel[ i ]->setInitValue( f_value );
+ m_vi->knobFModel[i]->setValue(f_value, true);
+ m_vi->knobFModel[i]->setInitValue(f_value);
}
}
syncParameterText();
diff --git a/plugins/VstEffect/VstEffectControls.cpp b/plugins/VstEffect/VstEffectControls.cpp
index 2f31d75eee9..840c1089376 100644
--- a/plugins/VstEffect/VstEffectControls.cpp
+++ b/plugins/VstEffect/VstEffectControls.cpp
@@ -465,8 +465,8 @@ void ManageVSTEffectView::syncPlugin()
std::snprintf(paramStr.data(), paramStr.size(), "param%d", i);
s_dumpValues = dump[paramStr.data()].split(":");
float f_value = LocaleHelper::toFloat(s_dumpValues.at(2));
- m_vi2->knobFModel[ i ]->setAutomatedValue( f_value );
- m_vi2->knobFModel[ i ]->setInitValue( f_value );
+ m_vi2->knobFModel[i]->setValue(f_value, true);
+ m_vi2->knobFModel[i]->setInitValue(f_value);
}
}
syncParameterText();
diff --git a/src/core/AutomatableModel.cpp b/src/core/AutomatableModel.cpp
index 4006c1d8513..92e536a7a68 100644
--- a/src/core/AutomatableModel.cpp
+++ b/src/core/AutomatableModel.cpp
@@ -53,8 +53,9 @@ AutomatableModel::AutomatableModel(
m_range( max - min ),
m_centerValue( m_minValue ),
m_valueChanged( false ),
- m_setValueDepth( 0 ),
+ m_oldValue(val),
m_hasStrictStepSize( false ),
+ m_nextLink(this),
m_controllerConnection( nullptr ),
m_valueBuffer( static_cast( Engine::audioEngine()->framesPerPeriod() ) ),
m_lastUpdatedPeriod( -1 ),
@@ -71,13 +72,9 @@ AutomatableModel::AutomatableModel(
AutomatableModel::~AutomatableModel()
{
- while( m_linkedModels.empty() == false )
- {
- m_linkedModels.back()->unlinkModel(this);
- m_linkedModels.erase( m_linkedModels.end() - 1 );
- }
+ unlink();
- if( m_controllerConnection )
+ if (m_controllerConnection)
{
delete m_controllerConnection;
}
@@ -294,41 +291,42 @@ void AutomatableModel::loadSettings( const QDomElement& element, const QString&
-void AutomatableModel::setValue( const float value )
+void AutomatableModel::setValue(const float value, const bool isAutomated)
{
- m_oldValue = m_value;
- ++m_setValueDepth;
- const float old_val = m_value;
+ if (fittedValue(value) == m_value)
+ {
+ // TODO check why we need this signal, is there a better solution?
+ if (!isAutomated) { emit dataUnchanged(); }
+ return;
+ }
+
+ if (!isAutomated) { addJournalCheckPoint(); }
- m_value = fittedValue( value );
- if( old_val != m_value )
+ // set value for this and the other linked models
+ setValueInternal(value);
+ for (auto model = m_nextLink; model != this; model = model->m_nextLink)
{
- // add changes to history so user can undo it
- addJournalCheckPoint();
+ model->setValueInternal(value);
+ }
+}
- // notify linked models
- for (const auto& linkedModel : m_linkedModels)
- {
- if (linkedModel->m_setValueDepth < 1 && linkedModel->fittedValue(value) != linkedModel->m_value)
- {
- bool journalling = linkedModel->testAndSetJournalling(isJournalling());
- linkedModel->setValue(value);
- linkedModel->setJournalling(journalling);
- }
- }
+
+
+
+void AutomatableModel::setValueInternal(const float value)
+{
+ m_oldValue = m_value;
+ m_value = fittedValue(value);
+
+ if (m_oldValue != m_value)
+ {
m_valueChanged = true;
emit dataChanged();
}
- else
- {
- emit dataUnchanged();
- }
- --m_setValueDepth;
}
-
template T AutomatableModel::logToLinearScale( T value ) const
{
return castValue( lmms::logToLinearScale( minValue(), maxValue(), static_cast( value ) ) );
@@ -362,34 +360,6 @@ void AutomatableModel::roundAt( T& value, const T& where ) const
-void AutomatableModel::setAutomatedValue( const float value )
-{
- setUseControllerValue(false);
-
- m_oldValue = m_value;
- ++m_setValueDepth;
- const float oldValue = m_value;
-
- const float scaled_value = scaledValue( value );
-
- m_value = fittedValue( scaled_value );
-
- if( oldValue != m_value )
- {
- // notify linked models
- for (const auto& linkedModel : m_linkedModels)
- {
- if (!(linkedModel->controllerConnection()) && linkedModel->m_setValueDepth < 1 &&
- linkedModel->fittedValue(m_value) != linkedModel->m_value)
- {
- linkedModel->setAutomatedValue(value);
- }
- }
- m_valueChanged = true;
- emit dataChanged();
- }
- --m_setValueDepth;
-}
@@ -459,79 +429,68 @@ float AutomatableModel::fittedValue( float value ) const
-
-void AutomatableModel::linkModel( AutomatableModel* model )
+AutomatableModel* AutomatableModel::getLastLinkedModel() const
{
- auto containsModel = std::find(m_linkedModels.begin(), m_linkedModels.end(), model) != m_linkedModels.end();
- if (!containsModel && model != this)
+ for (auto model = m_nextLink; ; model = model->m_nextLink)
{
- m_linkedModels.push_back( model );
-
- if( !model->hasLinkedModels() )
- {
- QObject::connect( this, SIGNAL(dataChanged()),
- model, SIGNAL(dataChanged()), Qt::DirectConnection );
- }
+ // The last model in the circular reference links back to this
+ if (model->m_nextLink == this) { return model; }
}
}
-void AutomatableModel::unlinkModel( AutomatableModel* model )
+bool AutomatableModel::isLinkedToModel(AutomatableModel* model) const
{
- auto it = std::find(m_linkedModels.begin(), m_linkedModels.end(), model);
- if( it != m_linkedModels.end() )
+ if (model == this) { return true; }
+ for (auto next = m_nextLink; next != this; next = next->m_nextLink)
{
- m_linkedModels.erase( it );
+ if (next == model) { return true; }
}
+ return false;
}
-
-
-
-
-
-void AutomatableModel::linkModels( AutomatableModel* model1, AutomatableModel* model2 )
+size_t AutomatableModel::countLinks() const
{
- auto model1ContainsModel2 = std::find(model1->m_linkedModels.begin(), model1->m_linkedModels.end(), model2) != model1->m_linkedModels.end();
- if (!model1ContainsModel2 && model1 != model2)
+ size_t output = 0;
+ for (auto model = m_nextLink; model != this; model = model->m_nextLink)
{
- // copy data
- model1->m_value = model2->m_value;
- if (model1->valueBuffer() && model2->valueBuffer())
- {
- std::copy_n(model2->valueBuffer()->data(),
- model1->valueBuffer()->length(),
- model1->valueBuffer()->data());
- }
- // send dataChanged() before linking (because linking will
- // connect the two dataChanged() signals)
- emit model1->dataChanged();
- // finally: link the models
- model1->linkModel( model2 );
- model2->linkModel( model1 );
+ output++;
}
+ return output;
}
-void AutomatableModel::unlinkModels( AutomatableModel* model1, AutomatableModel* model2 )
+void AutomatableModel::linkToModel(AutomatableModel* other)
{
- model1->unlinkModel( model2 );
- model2->unlinkModel( model1 );
+ if (isLinkedToModel(other)) { return; }
+
+ // copy data from other to this
+ setValue(other->m_value);
+ if (valueBuffer() && other->valueBuffer())
+ {
+ std::copy_n(other->valueBuffer()->data(),
+ valueBuffer()->length(),
+ valueBuffer()->data());
+ emit dataChanged();
+ }
+ // link the models
+ AutomatableModel* thisEnd = getLastLinkedModel();
+ AutomatableModel* otherEnd = other->getLastLinkedModel();
+ thisEnd->m_nextLink = other;
+ otherEnd->m_nextLink = this;
}
-void AutomatableModel::unlinkAllModels()
+void AutomatableModel::unlink()
{
- for( AutomatableModel* model : m_linkedModels )
- {
- unlinkModels( this, model );
- }
+ getLastLinkedModel()->m_nextLink = m_nextLink;
+ m_nextLink = this;
}
@@ -555,11 +514,11 @@ void AutomatableModel::setControllerConnection( ControllerConnection* c )
float AutomatableModel::controllerValue( int frameOffset ) const
{
- if( m_controllerConnection )
+ assert(m_controllerConnection != nullptr);
+
+ float v = 0;
+ switch (m_scaleType)
{
- float v = 0;
- switch(m_scaleType)
- {
case ScaleType::Linear:
v = minValue() + ( range() * controllerConnection()->currentValue( frameOffset ) );
break;
@@ -571,24 +530,14 @@ float AutomatableModel::controllerValue( int frameOffset ) const
qFatal("AutomatableModel::controllerValue(int)"
"lacks implementation for a scale type");
break;
- }
- if (approximatelyEqual(m_step, 1) && m_hasStrictStepSize)
- {
- return std::round(v);
- }
- return v;
}
-
- AutomatableModel* lm = m_linkedModels.front();
- if (lm->controllerConnection() && lm->useControllerValue())
+ if (approximatelyEqual(m_step, 1) && m_hasStrictStepSize)
{
- return fittedValue( lm->controllerValue( frameOffset ) );
+ return std::round(v);
}
-
- return fittedValue( lm->m_value );
+ return v;
}
-
ValueBuffer * AutomatableModel::valueBuffer()
{
QMutexLocker m( &m_valueBufferMutex );
@@ -602,6 +551,11 @@ ValueBuffer * AutomatableModel::valueBuffer()
float val = m_value; // make sure our m_value doesn't change midway
+ // TODO
+ // Let the Controller set the value of connected Models,
+ // instead of each Model checking the Controller value every time.
+
+ // Get value buffer from our controller
if (m_controllerConnection && m_useControllerValue && m_controllerConnection->getController()->isSampleExact())
{
auto vb = m_controllerConnection->valueBuffer();
@@ -634,32 +588,36 @@ ValueBuffer * AutomatableModel::valueBuffer()
}
}
- if (!m_controllerConnection)
+ // Get value buffer from one of the linked models' controller
+ if (m_useControllerValue)
{
- AutomatableModel* lm = nullptr;
- if (hasLinkedModels())
+ for (auto next = m_nextLink; next != this; next = next->m_nextLink)
{
- lm = m_linkedModels.front();
- }
- if (lm && lm->controllerConnection() && lm->useControllerValue() &&
- lm->controllerConnection()->getController()->isSampleExact())
- {
- auto vb = lm->valueBuffer();
- float * values = vb->values();
- float * nvalues = m_valueBuffer.values();
- for (int i = 0; i < vb->length(); i++)
- {
- nvalues[i] = fittedValue(values[i]);
- }
- m_lastUpdatedPeriod = s_periodCounter;
- m_hasSampleExactData = true;
- return &m_valueBuffer;
+ if (next->controllerConnection() && next->useControllerValue() &&
+ next->controllerConnection()->getController()->isSampleExact())
+ {
+ auto vb = next->valueBuffer();
+ float* values = vb->values();
+ float* nvalues = m_valueBuffer.values();
+ for (int i = 0; i < vb->length(); i++)
+ {
+ nvalues[i] = fittedValue(values[i]);
+ }
+ m_lastUpdatedPeriod = s_periodCounter;
+ m_hasSampleExactData = true;
+ return &m_valueBuffer;
+ }
}
}
- if( m_oldValue != val )
+ // Note: if there are linked models but no controller, `setValue()` will have
+ // updated `m_value` and `m_oldValue` across all linked models, so the `valueBuffer()`
+ // will be the same (even if it is calculated separatly for each model).
+
+ // Populate value buffer by interpolatating between the old and new value
+ if (m_oldValue != val)
{
- m_valueBuffer.interpolate( m_oldValue, val );
+ m_valueBuffer.interpolate(m_oldValue, val);
m_oldValue = val;
m_lastUpdatedPeriod = s_periodCounter;
m_hasSampleExactData = true;
diff --git a/src/core/LadspaControl.cpp b/src/core/LadspaControl.cpp
index 3282a0c7a0e..4227f7c4fb2 100644
--- a/src/core/LadspaControl.cpp
+++ b/src/core/LadspaControl.cpp
@@ -291,16 +291,15 @@ void LadspaControl::linkControls( LadspaControl * _control )
switch( m_port->data_type )
{
case BufferDataType::Toggled:
- BoolModel::linkModels( &m_toggledModel, _control->toggledModel() );
+ _control->toggledModel()->linkToModel(&m_toggledModel);
break;
case BufferDataType::Integer:
case BufferDataType::Enum:
case BufferDataType::Floating:
- FloatModel::linkModels( &m_knobModel, _control->knobModel() );
+ _control->knobModel()->linkToModel(&m_knobModel);
break;
case BufferDataType::Time:
- TempoSyncKnobModel::linkModels( &m_tempoSyncKnobModel,
- _control->tempoSyncKnobModel() );
+ _control->tempoSyncKnobModel()->linkToModel(&m_tempoSyncKnobModel);
break;
default:
break;
@@ -342,16 +341,15 @@ void LadspaControl::unlinkControls( LadspaControl * _control )
switch( m_port->data_type )
{
case BufferDataType::Toggled:
- BoolModel::unlinkModels( &m_toggledModel, _control->toggledModel() );
+ _control->toggledModel()->unlink();
break;
case BufferDataType::Integer:
case BufferDataType::Enum:
case BufferDataType::Floating:
- FloatModel::unlinkModels( &m_knobModel, _control->knobModel() );
+ _control->knobModel()->unlink();
break;
case BufferDataType::Time:
- TempoSyncKnobModel::unlinkModels( &m_tempoSyncKnobModel,
- _control->tempoSyncKnobModel() );
+ _control->tempoSyncKnobModel()->unlink();
break;
default:
break;
diff --git a/src/core/LinkedModelGroups.cpp b/src/core/LinkedModelGroups.cpp
index c52bce43310..3778d4d80da 100644
--- a/src/core/LinkedModelGroups.cpp
+++ b/src/core/LinkedModelGroups.cpp
@@ -46,7 +46,7 @@ void LinkedModelGroup::linkControls(LinkedModelGroup *other)
{
auto itr2 = other->m_models.find(id);
Q_ASSERT(itr2 != other->m_models.end());
- AutomatableModel::linkModels(inf.m_model, itr2->second.m_model);
+ inf.m_model->linkToModel(itr2->second.m_model);
});
}
diff --git a/src/core/Song.cpp b/src/core/Song.cpp
index 441780d414a..e49d99c7cb1 100644
--- a/src/core/Song.cpp
+++ b/src/core/Song.cpp
@@ -413,7 +413,7 @@ void Song::processAutomations(const TrackList &tracklist, TimePos timeStart, fpp
for (auto it = m_oldAutomatedValues.begin(); it != m_oldAutomatedValues.end(); it++)
{
AutomatableModel * am = it.key();
- if (am->controllerConnection() && !values.contains(am))
+ if (!values.contains(am))
{
am->setUseControllerValue(true);
}
@@ -423,13 +423,18 @@ void Song::processAutomations(const TrackList &tracklist, TimePos timeStart, fpp
// Apply values
for (auto it = values.begin(); it != values.end(); it++)
{
- if (! recordedModels.contains(it.key()))
- {
- it.key()->setAutomatedValue(it.value());
- }
- else if (!it.key()->useControllerValue())
+ AutomatableModel* model = it.key();
+ bool isRecording = recordedModels.contains(model);
+ model->setUseControllerValue(isRecording);
+
+ if (!isRecording)
{
- it.key()->setUseControllerValue(true);
+ /* TODO
+ * Remove scaleValue() from here when automation editor's
+ * Y axis can be set to logarithmic, and automation clips store
+ * the actual values, and not the invertedScaledValue.
+ */
+ model->setValue(model->scaledValue(it.value()), true);
}
}
}
diff --git a/src/gui/AutomatableModelView.cpp b/src/gui/AutomatableModelView.cpp
index 2faf74064a5..ef40da8158b 100644
--- a/src/gui/AutomatableModelView.cpp
+++ b/src/gui/AutomatableModelView.cpp
@@ -96,11 +96,11 @@ void AutomatableModelView::addDefaultActions( QMenu* menu )
menu->addSeparator();
- if( model->hasLinkedModels() )
+ if (model->isLinked())
{
- menu->addAction( embed::getIconPixmap( "edit-delete" ),
- AutomatableModel::tr( "Remove all linked controls" ),
- amvSlots, SLOT(unlinkAllModels()));
+ menu->addAction(embed::getIconPixmap("edit_unlink"),
+ AutomatableModel::tr("Remove all linked controls"),
+ model, SLOT(unlink()));
menu->addSeparator();
}
@@ -276,11 +276,6 @@ void AutomatableModelViewSlots::removeSongGlobalAutomation()
}
-void AutomatableModelViewSlots::unlinkAllModels()
-{
- m_amv->modelUntyped()->unlinkAllModels();
-}
-
void AutomatableModelViewSlots::copyToClipboard()
{
// For copyString() and MimeType enum class
diff --git a/src/gui/widgets/FloatModelEditorBase.cpp b/src/gui/widgets/FloatModelEditorBase.cpp
index ed09fa261a4..a492bad0d1b 100644
--- a/src/gui/widgets/FloatModelEditorBase.cpp
+++ b/src/gui/widgets/FloatModelEditorBase.cpp
@@ -146,8 +146,7 @@ void FloatModelEditorBase::dropEvent(QDropEvent * de)
auto mod = dynamic_cast(Engine::projectJournal()->journallingObject(val.toInt()));
if (mod != nullptr)
{
- AutomatableModel::linkModels(model(), mod);
- mod->setValue(model()->value());
+ model()->linkToModel(mod);
}
}
}
@@ -418,12 +417,16 @@ void FloatModelEditorBase::enterValue()
void FloatModelEditorBase::friendlyUpdate()
{
- if (model() && (model()->controllerConnection() == nullptr ||
- model()->controllerConnection()->getController()->frequentUpdates() == false ||
- Controller::runningFrames() % (256*4) == 0))
- {
- update();
- }
+ if (model() == nullptr) { return; }
+
+ // If the controller changes constantly, only repaint every 1024th frame
+ if (model()->useControllerValue()
+ && model()->controllerConnection()
+ && model()->controllerConnection()->getController()->frequentUpdates()
+ && Controller::runningFrames() % (256 * 4) != 0)
+ { return; }
+
+ update();
}
diff --git a/tests/src/core/AutomatableModelTest.cpp b/tests/src/core/AutomatableModelTest.cpp
index 9bc5fd6ace2..6fa86d20c1e 100644
--- a/tests/src/core/AutomatableModelTest.cpp
+++ b/tests/src/core/AutomatableModelTest.cpp
@@ -78,7 +78,7 @@ private slots: // tests
{
using namespace lmms;
- BoolModel m1(false), m2(false);
+ BoolModel m1(true), m2(false);
QObject::connect(&m1, SIGNAL(dataChanged()),
this, SLOT(onM1Changed()));
@@ -86,17 +86,19 @@ private slots: // tests
this, SLOT(onM2Changed()));
resetChanged();
- AutomatableModel::linkModels(&m1, &m1);
+ m1.linkToModel(&m1);
QVERIFY(!m1Changed); // cannot link to itself
QVERIFY(!m2Changed);
+ QVERIFY(m1.countLinks() == 0);
resetChanged();
- AutomatableModel::linkModels(&m1, &m2);
- QVERIFY(m1Changed); // since m1 takes the value of m2
+ m1.linkToModel(&m2);
+ QVERIFY(m1.value() == m2.value()); // since m1 takes the value of m2
QVERIFY(!m2Changed); // the second model is the source
+ QVERIFY(m1.countLinks() == 1);
resetChanged();
- AutomatableModel::linkModels(&m1, &m2);
+ m1.linkToModel(&m2);
QVERIFY(!m1Changed); // it's already linked
QVERIFY(!m2Changed);
@@ -104,15 +106,15 @@ private slots: // tests
BoolModel m3(false);
m1.setValue(1.f);
m2.setValue(1.f);
- AutomatableModel::linkModels(&m1, &m2);
+ m1.linkToModel(&m2);
QVERIFY(m1.value());
QVERIFY(m2.value());
QVERIFY(!m3.value());
- AutomatableModel::linkModels(&m2, &m3); // drag m3, drop on m2
+ m2.linkToModel(&m3); // drag m3, drop on m2
// m2 should take m3's (0) value
- // due to a bug(?), this does not happen
- QVERIFY(m2.value());
+ QVERIFY(m2.value() == m3.value());
QVERIFY(!m3.value());
+ QVERIFY(m1.countLinks() == 2);
}
};