From 287276db6f7e2dae3a55a423c390e8c8178b78a3 Mon Sep 17 00:00:00 2001 From: Wong Cho Ching Date: Sun, 2 Feb 2014 18:28:46 +0800 Subject: [PATCH 1/3] Automation: No longer getting destroyed after exporting PLUS rendering bugfix --- src/core/AutomationPattern.cpp | 69 ++++++++++++--------- src/gui/AutomationEditor.cpp | 110 +++++++++++++++++---------------- 2 files changed, 97 insertions(+), 82 deletions(-) diff --git a/src/core/AutomationPattern.cpp b/src/core/AutomationPattern.cpp index 382f906fc..44bbaaa1f 100644 --- a/src/core/AutomationPattern.cpp +++ b/src/core/AutomationPattern.cpp @@ -84,8 +84,6 @@ AutomationPattern::~AutomationPattern() void AutomationPattern::addObject( AutomatableModel * _obj, bool _search_dup ) { - bool addIt = true; - if( _search_dup ) { for( objectVector::iterator it = m_objects.begin(); @@ -95,27 +93,26 @@ void AutomationPattern::addObject( AutomatableModel * _obj, bool _search_dup ) { // Already exists // TODO: Maybe let the user know in some non-annoying way - addIt = false; - break; + return; } } } - if( addIt ) + // the automation track is empty + if( m_objects.isEmpty() && hasAutomation() == false ) { - // been empty before and model's current value is not its init value? - if( m_objects.isEmpty() && hasAutomation() == false && _obj->isAtInitValue() == false ) - { - // then initialize first value - putValue( 0, _obj->value(), false ); - } - - m_objects += _obj; - - connect( _obj, SIGNAL( destroyed( jo_id_t ) ), - this, SLOT( objectDestroyed( jo_id_t ) ), - Qt::DirectConnection ); + // then initialize first value + putValue( MidiTime(0), _obj->value(), false ); } + + m_objects += _obj; + + connect( _obj, SIGNAL( destroyed( jo_id_t ) ), + this, SLOT( objectDestroyed( jo_id_t ) ), + Qt::DirectConnection ); + + emit dataChanged(); + } @@ -584,18 +581,18 @@ void AutomationPattern::resolveAllIDs() AutomationPattern * a = dynamic_cast( *j ); if( a ) { - for( QVector::Iterator k = a->m_idsToResolve.begin(); - k != a->m_idsToResolve.end(); ++k ) - { - JournallingObject * o = engine::projectJournal()-> - journallingObject( *k ); - if( o && dynamic_cast( o ) ) - { - a->addObject( dynamic_cast( o ), false ); - } - } - a->m_idsToResolve.clear(); - a->dataChanged(); + for( QVector::Iterator k = a->m_idsToResolve.begin(); + k != a->m_idsToResolve.end(); ++k ) + { + JournallingObject * o = engine::projectJournal()-> + journallingObject( *k ); + if( o && dynamic_cast( o ) ) + { + a->addObject( dynamic_cast( o ), false ); + } + } + a->m_idsToResolve.clear(); + a->dataChanged(); } } } @@ -639,6 +636,20 @@ void AutomationPattern::objectDestroyed( jo_id_t _id ) // case we had to remove ourselves if we're the global automation // pattern of the destroyed object m_idsToResolve += _id; + + for( objectVector::Iterator objIt = m_objects.begin(); + objIt != m_objects.end(); objIt++ ) + { + Q_ASSERT( !(*objIt).isNull() ); + if( (*objIt)->id() == _id ) + { + //Assign to objIt so that this loop work even break; is removed. + objIt = m_objects.erase( objIt ); + break; + } + } + + emit dataChanged(); } diff --git a/src/gui/AutomationEditor.cpp b/src/gui/AutomationEditor.cpp index ffc230637..31e582a7c 100644 --- a/src/gui/AutomationEditor.cpp +++ b/src/gui/AutomationEditor.cpp @@ -1546,72 +1546,76 @@ void AutomationEditor::paintEvent( QPaintEvent * _pe ) { int len_ticks = 4; timeMap & time_map = m_pattern->getTimeMap(); - timeMap::iterator it = time_map.begin(); - p.setPen( QColor( 0xCF, 0xD9, 0xFF ) ); - while( it+1 != time_map.end() ) + //Don't bother doing/rendering anything if there is no automation points + if( time_map.size() > 0 ) { - // skip this section if it occurs completely before the - // visible area - int next_x = xCoordOfTick( (it+1).key() ); - if( next_x < 0 ) + timeMap::iterator it = time_map.begin(); + p.setPen( QColor( 0xCF, 0xD9, 0xFF ) ); + while( it+1 != time_map.end() ) { - ++it; - continue; - } + // skip this section if it occurs completely before the + // visible area + int next_x = xCoordOfTick( (it+1).key() ); + if( next_x < 0 ) + { + ++it; + continue; + } - int x = xCoordOfTick( it.key() ); - if( x > width() ) - { - break; - } + int x = xCoordOfTick( it.key() ); + if( x > width() ) + { + break; + } - bool is_selected = FALSE; - // if we're in move-mode, we may only draw - // values in selected area, that have originally - // been selected and not values that are now in - // selection because the user moved it... - if( m_editMode == MOVE ) - { - if( m_selValuesForMove.contains( it.key() ) ) + bool is_selected = FALSE; + // if we're in move-mode, we may only draw + // values in selected area, that have originally + // been selected and not values that are now in + // selection because the user moved it... + if( m_editMode == MOVE ) + { + if( m_selValuesForMove.contains( it.key() ) ) + { + is_selected = TRUE; + } + } + else if( it.value() >= selLevel_start && + it.value() <= selLevel_end && + it.key() >= sel_pos_start && + it.key() + len_ticks <= sel_pos_end ) { is_selected = TRUE; } - } - else if( it.value() >= selLevel_start && - it.value() <= selLevel_end && - it.key() >= sel_pos_start && - it.key() + len_ticks <= sel_pos_end ) - { - is_selected = TRUE; + + float *values = m_pattern->valuesAfter( it.key() ); + for( int i = 0; i < (it+1).key() - it.key(); i++ ) + { + drawLevelTick( p, it.key() + i, values[i], + is_selected ); + } + delete [] values; + + // Draw circle + drawAutomationPoint(p, it); + + ++it; } - float *values = m_pattern->valuesAfter( it.key() ); - for( int i = 0; i < (it+1).key() - it.key(); i++ ) - { - drawLevelTick( p, it.key() + i, values[i], - is_selected ); - } - delete [] values; + Q_ASSERT( it == time_map.end()-1 ); - // Draw circle + for( int i = it.key(), x = xCoordOfTick( i ); x <= width(); + i++, x = xCoordOfTick( i ) ) + { + // TODO: Find out if the section after the last control + // point is able to be selected and if so set this + // boolean correctly + drawLevelTick( p, i, it.value(), false ); + } + // Draw circle(the last one) drawAutomationPoint(p, it); - - ++it; } - - Q_ASSERT( it == time_map.end()-1 ); - - for( int i = it.key(), x = xCoordOfTick( i ); x <= width(); - i++, x = xCoordOfTick( i ) ) - { - // TODO: Find out if the section after the last control - // point is able to be selected and if so set this - // boolean correctly - drawLevelTick( p, i, it.value(), false ); - } - // Draw circle(the last one) - drawAutomationPoint(p, it); } else { From f7963d5b3b526d8050c3bbb894206ca6be81b52e Mon Sep 17 00:00:00 2001 From: Wong Cho Ching Date: Sun, 2 Feb 2014 19:40:00 +0800 Subject: [PATCH 2/3] Rendering tweak and fixes #234 --- include/AutomationPattern.h | 2 ++ include/AutomationPatternView.h | 2 ++ src/core/AutomationPattern.cpp | 6 ++-- src/gui/AutomationPatternView.cpp | 46 +++++++++++++++++++++++++++++++ 4 files changed, 54 insertions(+), 2 deletions(-) diff --git a/include/AutomationPattern.h b/include/AutomationPattern.h index 858ec131e..526dc21e7 100644 --- a/include/AutomationPattern.h +++ b/include/AutomationPattern.h @@ -169,6 +169,8 @@ private: bool m_dragging; + static const float DEFAULT_MIN_VALUE = 0; + static const float DEFAULT_MAX_VALUE = 1; friend class AutomationPatternView; diff --git a/include/AutomationPatternView.h b/include/AutomationPatternView.h index 4a58cc30c..7bb2b9f5b 100644 --- a/include/AutomationPatternView.h +++ b/include/AutomationPatternView.h @@ -66,6 +66,8 @@ private: QPixmap m_paintPixmap; bool m_needsUpdate; + void scaleTimemapToFit(); + } ; diff --git a/src/core/AutomationPattern.cpp b/src/core/AutomationPattern.cpp index 44bbaaa1f..8f928a8d1 100644 --- a/src/core/AutomationPattern.cpp +++ b/src/core/AutomationPattern.cpp @@ -98,7 +98,7 @@ void AutomationPattern::addObject( AutomatableModel * _obj, bool _search_dup ) } } - // the automation track is empty + // the automation track is unconnected and there is nothing in the track if( m_objects.isEmpty() && hasAutomation() == false ) { // then initialize first value @@ -155,7 +155,7 @@ const AutomatableModel * AutomationPattern::firstObject() const return m; } - static FloatModel _fm( 0, 0, 1, 0.001 ); + static FloatModel _fm( 0, DEFAULT_MIN_VALUE, DEFAULT_MAX_VALUE, 0.001 ); return &_fm; } @@ -714,4 +714,6 @@ void AutomationPattern::generateTangents( timeMap::const_iterator it, } + + #include "moc_AutomationPattern.cxx" diff --git a/src/gui/AutomationPatternView.cpp b/src/gui/AutomationPatternView.cpp index e66c728b4..f69b5d4d8 100644 --- a/src/gui/AutomationPatternView.cpp +++ b/src/gui/AutomationPatternView.cpp @@ -111,6 +111,12 @@ void AutomationPatternView::disconnectObject( QAction * _a ) m_pat->m_objects.end(), dynamic_cast( j ) ) ); update(); + + //If automation editor is opened, update its display after disconnection + if( engine::automationEditor() ) + { + engine::automationEditor()->updateAfterPatternChange(); + } } } @@ -333,6 +339,13 @@ void AutomationPatternView::dropEvent( QDropEvent * _de ) { engine::automationEditor()->setCurrentPattern( m_pat ); } + + //This is the only model that's just added to AutomationPattern. + if( m_pat->m_objects.size() == 1 ) + { + //scale the points to fit the new min. and max. value + this->scaleTimemapToFit(); + } } else { @@ -343,5 +356,38 @@ void AutomationPatternView::dropEvent( QDropEvent * _de ) +/** + * @brief With nothing connected, the automation points are in a small scale. + * Without this function, if the user set the automation points before + * connecting it to a model, his auto points would be lost because the scale is + * changed. This function preserve the auto points over different scale when a + * first model is connected. + */ +void AutomationPatternView::scaleTimemapToFit() +{ + float oldMin = AutomationPattern::DEFAULT_MIN_VALUE; + float oldMax = AutomationPattern::DEFAULT_MAX_VALUE; + float newMin = m_pat->firstObject()->minValue(); + float newMax = m_pat->firstObject()->maxValue(); + + for( AutomationPattern::timeMap::iterator it = m_pat->m_timeMap.begin(); + it != m_pat->m_timeMap.end(); ++it ) + { + if( *it < oldMin ) + { + *it = oldMin; + } + else if( *it > oldMax ) + { + *it = oldMax; + } + *it = (*it)*(newMax-newMin)/(oldMax-oldMin)+newMin; + } + + m_pat->generateTangents(); +} + + + #include "moc_AutomationPatternView.cxx" From 0f746d81638b3edf09936616a621c126b1c8d3f4 Mon Sep 17 00:00:00 2001 From: Wong Cho Ching Date: Sun, 2 Feb 2014 20:25:24 +0800 Subject: [PATCH 3/3] Preserves automation scale when everything is disconnected --- include/AutomationPattern.h | 10 +++++++++ include/AutomationPatternView.h | 2 +- src/gui/AutomationPatternView.cpp | 34 ++++++++++++++++++++----------- 3 files changed, 33 insertions(+), 13 deletions(-) diff --git a/include/AutomationPattern.h b/include/AutomationPattern.h index 526dc21e7..2eaaeba4d 100644 --- a/include/AutomationPattern.h +++ b/include/AutomationPattern.h @@ -111,6 +111,16 @@ public: return m_tangents; } + inline float getMin() const + { + return firstObject()->minValue(); + } + + inline float getMax() const + { + return firstObject()->maxValue(); + } + inline bool hasAutomation() const { return m_timeMap.isEmpty() == false; diff --git a/include/AutomationPatternView.h b/include/AutomationPatternView.h index 7bb2b9f5b..4fec18513 100644 --- a/include/AutomationPatternView.h +++ b/include/AutomationPatternView.h @@ -66,7 +66,7 @@ private: QPixmap m_paintPixmap; bool m_needsUpdate; - void scaleTimemapToFit(); + void scaleTimemapToFit( float oldMin, float oldMax ); } ; diff --git a/src/gui/AutomationPatternView.cpp b/src/gui/AutomationPatternView.cpp index f69b5d4d8..922e60230 100644 --- a/src/gui/AutomationPatternView.cpp +++ b/src/gui/AutomationPatternView.cpp @@ -107,6 +107,9 @@ void AutomationPatternView::disconnectObject( QAction * _a ) journallingObject( _a->data().toInt() ); if( j && dynamic_cast( j ) ) { + float oldMin = m_pat->getMin(); + float oldMax = m_pat->getMax(); + m_pat->m_objects.erase( qFind( m_pat->m_objects.begin(), m_pat->m_objects.end(), dynamic_cast( j ) ) ); @@ -117,6 +120,13 @@ void AutomationPatternView::disconnectObject( QAction * _a ) { engine::automationEditor()->updateAfterPatternChange(); } + + //if there is no more connection connected to the AutomationPattern + if( m_pat->m_objects.size() == 0 ) + { + //scale the points to fit the new min. and max. value + this->scaleTimemapToFit( oldMin, oldMax ); + } } } @@ -344,7 +354,8 @@ void AutomationPatternView::dropEvent( QDropEvent * _de ) if( m_pat->m_objects.size() == 1 ) { //scale the points to fit the new min. and max. value - this->scaleTimemapToFit(); + this->scaleTimemapToFit( AutomationPattern::DEFAULT_MIN_VALUE, + AutomationPattern::DEFAULT_MAX_VALUE ); } } else @@ -357,18 +368,17 @@ void AutomationPatternView::dropEvent( QDropEvent * _de ) /** - * @brief With nothing connected, the automation points are in a small scale. - * Without this function, if the user set the automation points before - * connecting it to a model, his auto points would be lost because the scale is - * changed. This function preserve the auto points over different scale when a - * first model is connected. + * @brief Preserves the auto points over different scale */ -void AutomationPatternView::scaleTimemapToFit() +void AutomationPatternView::scaleTimemapToFit( float oldMin, float oldMax ) { - float oldMin = AutomationPattern::DEFAULT_MIN_VALUE; - float oldMax = AutomationPattern::DEFAULT_MAX_VALUE; - float newMin = m_pat->firstObject()->minValue(); - float newMax = m_pat->firstObject()->maxValue(); + float newMin = m_pat->getMin(); + float newMax = m_pat->getMax(); + + if( oldMin == newMin && oldMax == newMax ) + { + return; + } for( AutomationPattern::timeMap::iterator it = m_pat->m_timeMap.begin(); it != m_pat->m_timeMap.end(); ++it ) @@ -381,7 +391,7 @@ void AutomationPatternView::scaleTimemapToFit() { *it = oldMax; } - *it = (*it)*(newMax-newMin)/(oldMax-oldMin)+newMin; + *it = (*it-oldMin)*(newMax-newMin)/(oldMax-oldMin)+newMin; } m_pat->generateTangents();