diff --git a/ChangeLog b/ChangeLog
index 0d78982f3f..1ef41ca159 100644
--- a/ChangeLog
+++ b/ChangeLog
@@ -83,6 +83,35 @@
- The tempo used left of the first tempo marker is painted in a
darker color.
- The currently used Tempo Marker gets highlighted.
+ - Tags have been moved into Timeline (next to the Tempo Markers)
+ in order to make room to accommodate the cursor in the ruler to
+ highlight the current position
+ - Tags can be inserted by left-clicking the bottom area of the
+ Timeline (above the ruler).
+ - Clicking the ruler is now always enabled and automatically
+ switches transport into Song Mode.
+ - Full-size playhead
+ - The icons in the pattern list indicating whether a pattern is
+ playing in stacked pattern mode are now colored and can have four
+ different states: on, off, off next (pattern is played till the
+ end and then turned off), and on next (pattern is played as soon
+ as transport is looped again)
+ * PatternEditor UX tweaks:
+ - Relocating transport by clicking the ruler is now supported
+ (like in the SongEditor) and automatically switches transport
+ into Pattern Mode.
+ - Full-size playhead
+ - The ruler was decoupled from the currently selected pattern. It
+ always has the size of the largest playing pattern and always
+ shows the transport position using a playhead. Whether or not the
+ current pattern is played back is indicated by a full-height
+ cursor.
+ - All note properties except of the note key can now be altered in
+ both the drum pattern editor and the piano roll editor by
+ right-clicking and dragging a note.
+ - All notes of the currently playing patterns will be hinted in
+ stacked pattern mode. Even those exceeding the length of the
+ current pattern.
* OSC API
- Add command /Hydrogen/LOAD_DRUMKIT
- Add command /Hydrogen/UPGRADE_DRUMKIT
diff --git a/data/img/gray/patternEditor/instrument_line.png b/data/img/gray/patternEditor/instrument_line.png
deleted file mode 100644
index 827da4d207..0000000000
Binary files a/data/img/gray/patternEditor/instrument_line.png and /dev/null differ
diff --git a/data/img/gray/patternEditor/instrument_line_selected.png b/data/img/gray/patternEditor/instrument_line_selected.png
deleted file mode 100644
index cc137184f9..0000000000
Binary files a/data/img/gray/patternEditor/instrument_line_selected.png and /dev/null differ
diff --git a/data/img/gray/patternEditor/tickPosition.png b/data/img/gray/patternEditor/tickPosition.png
deleted file mode 100644
index 955f8710cc..0000000000
Binary files a/data/img/gray/patternEditor/tickPosition.png and /dev/null differ
diff --git a/data/img/gray/songEditor/playingPattern_empty.png b/data/img/gray/songEditor/playingPattern_empty.png
deleted file mode 100644
index 7929d92a4d..0000000000
Binary files a/data/img/gray/songEditor/playingPattern_empty.png and /dev/null differ
diff --git a/data/img/gray/songEditor/playingPattern_off.png b/data/img/gray/songEditor/playingPattern_off.png
deleted file mode 100644
index c2851e48b6..0000000000
Binary files a/data/img/gray/songEditor/playingPattern_off.png and /dev/null differ
diff --git a/data/img/gray/songEditor/playingPattern_on.png b/data/img/gray/songEditor/playingPattern_on.png
deleted file mode 100644
index 7cb1b37ee1..0000000000
Binary files a/data/img/gray/songEditor/playingPattern_on.png and /dev/null differ
diff --git a/data/img/gray/songEditor/songEditorLabelABG.png b/data/img/gray/songEditor/songEditorLabelABG.png
deleted file mode 100644
index defd659abf..0000000000
Binary files a/data/img/gray/songEditor/songEditorLabelABG.png and /dev/null differ
diff --git a/data/img/gray/songEditor/songEditorLabelBG.png b/data/img/gray/songEditor/songEditorLabelBG.png
deleted file mode 100644
index b7bbd6cc77..0000000000
Binary files a/data/img/gray/songEditor/songEditorLabelBG.png and /dev/null differ
diff --git a/data/img/gray/songEditor/songEditorLabelSBG.png b/data/img/gray/songEditor/songEditorLabelSBG.png
deleted file mode 100644
index 5404e205c1..0000000000
Binary files a/data/img/gray/songEditor/songEditorLabelSBG.png and /dev/null differ
diff --git a/data/img/scalable/icons/black/lock_closed.svg b/data/img/scalable/icons/black/lock_closed.svg
new file mode 100644
index 0000000000..d960590537
--- /dev/null
+++ b/data/img/scalable/icons/black/lock_closed.svg
@@ -0,0 +1,120 @@
+
+
diff --git a/data/img/scalable/icons/black/lock_open.svg b/data/img/scalable/icons/black/lock_open.svg
new file mode 100644
index 0000000000..45d2fefe69
--- /dev/null
+++ b/data/img/scalable/icons/black/lock_open.svg
@@ -0,0 +1,128 @@
+
+
diff --git a/data/img/scalable/icons/white/lock_closed.svg b/data/img/scalable/icons/white/lock_closed.svg
new file mode 100644
index 0000000000..47ce2fe8f4
--- /dev/null
+++ b/data/img/scalable/icons/white/lock_closed.svg
@@ -0,0 +1,120 @@
+
+
diff --git a/data/img/scalable/icons/white/lock_open.svg b/data/img/scalable/icons/white/lock_open.svg
new file mode 100644
index 0000000000..2ffedd84c2
--- /dev/null
+++ b/data/img/scalable/icons/white/lock_open.svg
@@ -0,0 +1,128 @@
+
+
diff --git a/data/themes/default.h2theme b/data/themes/default.h2theme
index 4cb3447751..aa298915da 100644
--- a/data/themes/default.h2theme
+++ b/data/themes/default.h2theme
@@ -1,31 +1,43 @@
- 1.1.0-'4130cbf4'
+ 1.1.1-'4228e825'
- 95,101,117
- 128,134,152
- 128,134,152
- 72,76,88
- 196,201,214
+ 128,134,152
+ 106,111,126
+ 149,157,178
+ 0,0,0
+ 54,57,67
+ 206,211,224
+ 83,89,103
+ 45,66,89
+ 255,255,255
+ 127,159,127
+ 240,223,175
+ 247,100,100
- 167,168,163
- 167,168,163
- 207,208,200
- 40,40,40
- 40,40,40
- 0,0,0
- 65,65,65
- 75,75,75
- 95,95,95
- 115,115,115
- 125,125,125
- 135,135,135
+ 165,166,160
+ 133,134,129
+ 194,195,187
+ 0,0,0
+ 193,194,186
+ 240,240,240
+ 247,100,100
+ 40,40,40
+ 89,131,175
+ 255,255,255
+ 0,0,0
+ 45,45,45
+ 55,55,55
+ 75,75,75
+ 95,95,95
+ 105,105,105
+ 115,115,115
- 0,0,255
- 85,85,85
+ 255,255,255
+ 199,199,199
58,62,72
@@ -46,16 +58,16 @@
64,64,66
- 95,143,182
- 0,0,0
+ 67,96,131
+ 255,255,255
164,170,190
- 0,0,0
+ 10,10,10
247,100,100
10,10,10
51,74,100
240,240,240
- 52,76,100
- 255,255,255
+ 0,0,0
+ 38,39,44
@@ -66,23 +78,23 @@
1.1
1
67,96,131
- 67,96,131
- 67,96,131
- 67,96,131
- 67,96,131
- 67,96,131
- 67,96,131
- 67,96,131
- 67,96,131
- 67,96,131
- 67,96,131
- 67,96,131
- 67,96,131
- 67,96,131
- 67,96,131
- 67,96,131
- 67,96,131
- 67,96,131
+ 67,114,175
+ 105,165,189
+ 148,189,202
+ 193,211,215
+ 229,201,130
+ 234,160,90
+ 218,97,60
+ 186,55,50
+ 152,22,26
+ 186,55,50
+ 218,97,60
+ 234,160,90
+ 229,201,130
+ 193,211,215
+ 148,189,202
+ 105,165,189
+ 67,114,175
67,96,131
67,96,131
67,96,131
@@ -115,12 +127,12 @@
67,96,131
67,96,131
67,96,131
- 1
+ 18
- Lucida Grande
- Lucida Grande
- Lucida Grande
+ DejaVu Sans
+ DejaVu Sans
+ DejaVu Sans
0
diff --git a/src/core/AudioEngine/AudioEngine.cpp b/src/core/AudioEngine/AudioEngine.cpp
index e997de446a..52526efbc3 100644
--- a/src/core/AudioEngine/AudioEngine.cpp
+++ b/src/core/AudioEngine/AudioEngine.cpp
@@ -111,6 +111,7 @@ AudioEngine::AudioEngine()
, m_pMetronomeInstrument( nullptr )
, m_nPatternStartTick( 0 )
, m_nPatternTickPosition( 0 )
+ , m_nPatternSize( MAX_NOTES )
, m_fSongSizeInTicks( 0 )
, m_nRealtimeFrames( 0 )
, m_nAddRealtimeNoteTickPosition( 0 )
@@ -126,7 +127,6 @@ AudioEngine::AudioEngine()
, m_currentTickTime( {0,0})
, m_fTickMismatch( 0 )
, m_fLastTickIntervalEnd( -1 )
- , m_nLastPlayingPatternsColumn( -1 )
, m_nFrameOffset( 0 )
, m_fTickOffset( 0 )
{
@@ -338,12 +338,10 @@ void AudioEngine::reset( bool bWithJackBroadcast ) {
m_nFrameOffset = 0;
m_fTickOffset = 0;
m_fLastTickIntervalEnd = -1;
- m_nLastPlayingPatternsColumn = -1;
updateBpmAndTickSize();
clearNoteQueue();
-
#ifdef H2CORE_HAVE_JACK
if ( pHydrogen->haveJackTransport() && bWithJackBroadcast ) {
@@ -429,11 +427,7 @@ void AudioEngine::locate( const double fTick, bool bWithJackBroadcast ) {
#endif
setFrames( nNewFrame );
- updateTransportPosition( fTick, pHydrogen->getSong()->isLoopEnabled() );
-
- // Ensure the playing patterns are updated regardless of whether
- // transport is rolling or not.
- updatePlayingPatterns( getColumn(), getTick(), getPatternStartTick() );
+ updateTransportPosition( fTick );
}
void AudioEngine::locateToFrame( const long long nFrame ) {
@@ -444,11 +438,7 @@ void AudioEngine::locateToFrame( const long long nFrame ) {
double fNewTick = computeTickFromFrame( nFrame );
- updateTransportPosition( fNewTick, pHydrogen->getSong()->isLoopEnabled() );
-
- // Ensure the playing patterns are updated regardless of whether
- // transport is rolling or not.
- updatePlayingPatterns( getColumn(), getTick(), getPatternStartTick() );
+ updateTransportPosition( fNewTick );
}
void AudioEngine::incrementTransportPosition( uint32_t nFrames ) {
@@ -470,10 +460,10 @@ void AudioEngine::incrementTransportPosition( uint32_t nFrames ) {
// .arg( fNewTick, 0, 'f' )
// .arg( getTickSize(), 0, 'f' ) );
- updateTransportPosition( fNewTick, pSong->isLoopEnabled() );
+ updateTransportPosition( fNewTick );
}
-void AudioEngine::updateTransportPosition( double fTick, bool bUseLoopMode ) {
+void AudioEngine::updateTransportPosition( double fTick ) {
const auto pHydrogen = Hydrogen::get_instance();
const auto pSong = pHydrogen->getSong();
@@ -487,67 +477,23 @@ void AudioEngine::updateTransportPosition( double fTick, bool bUseLoopMode ) {
// .arg( m_nPatternStartTick )
// .arg( m_nColumn )
// .arg( getFrames() ) );
-
- setTick( fTick );
- // Column, tick since the beginning of the current pattern, and
- // number of ticks till be the beginning of the current pattern
- // corresponding to nTick.
- int nNewColumn;
+ // Update m_nPatternStartTick, m_nPatternTickPosition, and
+ // m_nPatternSize.
if ( pHydrogen->getMode() == Song::Mode::Song ) {
+ updateSongTransportPosition( fTick );
+ }
+ else if ( pHydrogen->getMode() == Song::Mode::Pattern ) {
- nNewColumn = pHydrogen->getColumnForTick( std::floor( fTick ),
- bUseLoopMode,
- &m_nPatternStartTick );
-
- if ( fTick > m_fSongSizeInTicks &&
- m_fSongSizeInTicks != 0 ) {
- // When using the JACK audio driver the overall
- // transport position will be managed by an external
- // server. Since it is agnostic of all the looping in
- // its clients, it will only increment time and
- // Hydrogen has to take care of the looping itself.
- m_nPatternTickPosition =
- std::fmod( std::floor( fTick ) - m_nPatternStartTick,
- m_fSongSizeInTicks );
- } else {
- m_nPatternTickPosition = std::floor( fTick ) - m_nPatternStartTick;
- }
-
- if ( m_nColumn != nNewColumn ) {
- m_nLastPlayingPatternsColumn = m_nColumn;
- setColumn( nNewColumn );
- }
-
- } else if ( pHydrogen->getMode() == Song::Mode::Pattern ) {
-
- int nPatternSize;
- if ( m_pPlayingPatterns->size() != 0 ) {
- nPatternSize = m_pPlayingPatterns->longest_pattern_length();
- } else {
- nPatternSize = MAX_NOTES;
- }
-
- // Transport went past the end of the pattern or Pattern mode
- // was just activated.
- if ( fTick >= m_nPatternStartTick + nPatternSize ) {
- if ( nPatternSize > 0 ) {
- m_nPatternStartTick +=
- std::floor( ( std::floor( fTick ) - m_nPatternStartTick ) /
- nPatternSize ) * nPatternSize;
- } else {
- m_nPatternStartTick = std::floor( fTick );
- }
- }
-
- m_nPatternTickPosition = static_cast(std::floor( fTick ))
- - m_nPatternStartTick;
- if ( nPatternSize > 0 && m_nPatternTickPosition > nPatternSize ) {
- m_nPatternTickPosition = ( static_cast(std::floor( fTick ))
- - m_nPatternStartTick ) %
- nPatternSize;
+ // If the transport is rolling, pattern tick variables were
+ // already updated in the call to updateNoteQueue.
+ if ( getState() != State::Playing ) {
+ updatePatternTransportPosition( fTick );
}
}
+
+ setTick( fTick );
+
updateBpmAndTickSize();
// WARNINGLOG( QString( "[After] frame: %5, tick: %1, pTickPos: %2, pStartPos: %3, column: %4" )
@@ -559,6 +505,88 @@ void AudioEngine::updateTransportPosition( double fTick, bool bUseLoopMode ) {
}
+void AudioEngine::updatePatternTransportPosition( double fTick ) {
+
+ auto pHydrogen = Hydrogen::get_instance();
+
+ // In selected pattern mode we update the pattern size _before_
+ // checking the whether transport reached the end of a
+ // pattern. This way when switching from a shorter to one double
+ // its size transport will just continue till the end of the new,
+ // longer one. If we would not update pattern size, transport
+ // would be looped at half the length of the newly selected
+ // pattern, which looks like a glitch.
+ //
+ // The update of the playing pattern is done asynchronous in
+ // Hydrogen::setSelectedPatternNumber() and does not have to
+ // queried in here in each run.
+ // if ( pHydrogen->getPatternMode() == Song::PatternMode::Selected ) {
+ // updatePlayingPatterns( 0, fTick );
+ // }
+
+ // Transport went past the end of the pattern or Pattern mode
+ // was just activated.
+ if ( fTick >= static_cast(m_nPatternStartTick + m_nPatternSize) ||
+ fTick < static_cast(m_nPatternStartTick) ) {
+ m_nPatternStartTick +=
+ static_cast(std::floor( ( fTick -
+ static_cast(m_nPatternStartTick) ) /
+ static_cast(m_nPatternSize) )) *
+ m_nPatternSize;
+
+ // In stacked pattern mode we will only update the playing
+ // patterns if the transport of the original pattern is
+ // looped. This way all patterns start fresh at the beginning.
+ if ( pHydrogen->getPatternMode() == Song::PatternMode::Stacked ) {
+ // Updates m_nPatternSize.
+ updatePlayingPatterns( 0, fTick );
+ }
+ }
+
+ m_nPatternTickPosition = static_cast(std::floor( fTick )) -
+ m_nPatternStartTick;
+ if ( m_nPatternTickPosition > m_nPatternSize ) {
+ m_nPatternTickPosition = ( static_cast(std::floor( fTick ))
+ - m_nPatternStartTick ) %
+ m_nPatternSize;
+ }
+}
+
+void AudioEngine::updateSongTransportPosition( double fTick ) {
+
+ auto pHydrogen = Hydrogen::get_instance();
+ const auto pSong = pHydrogen->getSong();
+
+ if ( fTick < 0 ) {
+ ERRORLOG( QString( "Provided tick [%1] is negative!" )
+ .arg( fTick, 0, 'f' ) );
+ return;
+ }
+
+ int nNewColumn = pHydrogen->getColumnForTick( std::floor( fTick ),
+ pSong->isLoopEnabled(),
+ &m_nPatternStartTick );
+
+ // While the current tick position is constantly increasing,
+ // m_nPatternStartTick is only defined between 0 and
+ // m_fSongSizeInTicks. We will take care of the looping next.
+ if ( fTick > m_fSongSizeInTicks &&
+ m_fSongSizeInTicks != 0 ) {
+
+ m_nPatternTickPosition =
+ std::fmod( std::floor( fTick ) - m_nPatternStartTick,
+ m_fSongSizeInTicks );
+ }
+ else {
+ m_nPatternTickPosition = std::floor( fTick ) - m_nPatternStartTick;
+ }
+
+ if ( m_nColumn != nNewColumn ) {
+ setColumn( nNewColumn );
+ updatePlayingPatterns( nNewColumn, 0 );
+ }
+}
+
void AudioEngine::updateBpmAndTickSize() {
if ( ! ( m_state == State::Playing ||
m_state == State::Ready ||
@@ -1350,6 +1378,36 @@ void AudioEngine::raiseError( unsigned nErrorCode )
m_pEventQueue->push_event( EVENT_ERROR, nErrorCode );
}
+void AudioEngine::handleSelectedPattern() {
+ // Expects the AudioEngine being locked.
+
+ auto pHydrogen = Hydrogen::get_instance();
+ auto pSong = pHydrogen->getSong();
+ if ( pHydrogen->isPatternEditorLocked() ) {
+
+ int nColumn = m_nColumn;
+ if ( m_nColumn == -1 ) {
+ nColumn = 0;
+ }
+
+ auto pPatternList = pSong->getPatternList();
+ auto pColumn = ( *pSong->getPatternGroupVector() )[ nColumn ];
+
+ int nPatternNumber = -1;
+
+ int nIndex;
+ for ( const auto& pattern : *pColumn ) {
+ nIndex = pPatternList->index( pattern );
+
+ if ( nIndex > nPatternNumber ) {
+ nPatternNumber = nIndex;
+ }
+ }
+
+ pHydrogen->setSelectedPatternNumber( nPatternNumber, true );
+ }
+}
+
void AudioEngine::processPlayNotes( unsigned long nframes )
{
Hydrogen* pHydrogen = Hydrogen::get_instance();
@@ -1543,7 +1601,6 @@ int AudioEngine::audioEngine_process( uint32_t nframes, void* /*arg*/ )
// was started or stopped by the user.
if ( pAudioEngine->getNextState() == State::Playing ) {
if ( pAudioEngine->getState() == State::Ready ) {
- DEBUGLOG("set playing");
pAudioEngine->startPlayback();
}
@@ -1727,9 +1784,13 @@ void AudioEngine::setSong( std::shared_ptr pNewSong )
setupLadspaFX();
}
- // find the first pattern and set as current
+ // find the first pattern and set as current since we start in
+ // pattern mode.
if ( pNewSong->getPatternList()->size() > 0 ) {
m_pPlayingPatterns->add( pNewSong->getPatternList()->get( 0 ) );
+ m_nPatternSize = m_pPlayingPatterns->longest_pattern_length();
+ } else {
+ m_nPatternSize = MAX_NOTES;
}
Hydrogen::get_instance()->renameJackPorts( pNewSong );
@@ -1743,6 +1804,7 @@ void AudioEngine::setSong( std::shared_ptr pNewSong )
locate( 0 );
Hydrogen::get_instance()->setTimeline( pNewSong->getTimeline() );
+ Hydrogen::get_instance()->getTimeline()->activate();
this->unlock();
}
@@ -1784,6 +1846,18 @@ void AudioEngine::updateSongSize() {
return;
}
+ if ( m_pPlayingPatterns->size() > 0 ) {
+ m_nPatternSize = m_pPlayingPatterns->longest_pattern_length();
+ } else {
+ m_nPatternSize = MAX_NOTES;
+ }
+
+ EventQueue::get_instance()->push_event( EVENT_SONG_SIZE_CHANGED, 0 );
+
+ if ( pHydrogen->getMode() == Song::Mode::Pattern ) {
+ return;
+ }
+
double fNewSongSizeInTicks = static_cast( pSong->lengthInTicks() );
// WARNINGLOG( QString( "[Before] frame: %1, bpm: %2, tickSize: %3, column: %4, tick: %5, mod(tick): %6, pTickPos: %7, pStartPos: %8, m_fLastTickIntervalEnd: %9, m_fSongSizeInTicks: %10" )
@@ -1880,8 +1954,7 @@ void AudioEngine::updateSongSize() {
// After tick and frame information as well as notes are updated
// we will make the remainder of the transport information
// consistent.
- updateTransportPosition( getDoubleTick(),
- pSong->isLoopEnabled() );
+ updateTransportPosition( getDoubleTick() );
if ( m_nColumn == -1 ) {
stop();
@@ -1897,15 +1970,16 @@ void AudioEngine::updateSongSize() {
}
-void AudioEngine::flushPlayingPatterns() {
- m_pPlayingPatterns->clear();
+void AudioEngine::removePlayingPattern( int nIndex ) {
+ m_pPlayingPatterns->del( nIndex );
}
-void AudioEngine::updatePlayingPatterns( int nColumn, long nTick, long nPatternStartTick ) {
+void AudioEngine::updatePlayingPatterns( int nColumn, long nTick ) {
auto pHydrogen = Hydrogen::get_instance();
auto pSong = pHydrogen->getSong();
if ( pHydrogen->getMode() == Song::Mode::Song ) {
+ // Called when transport enteres a new column.
m_pPlayingPatterns->clear();
if ( nColumn < 0 || nColumn >= pSong->getPatternGroupVector()->size() ) {
@@ -1913,61 +1987,81 @@ void AudioEngine::updatePlayingPatterns( int nColumn, long nTick, long nPatternS
}
for ( const auto& ppattern : *( *( pSong->getPatternGroupVector() ) )[ nColumn ] ) {
- m_pPlayingPatterns->add( ppattern );
- ppattern->addFlattenedVirtualPatterns( m_pPlayingPatterns );
+ if ( ppattern != nullptr ) {
+ m_pPlayingPatterns->add( ppattern );
+ ppattern->addFlattenedVirtualPatterns( m_pPlayingPatterns );
+ }
+ }
+
+ if ( m_pPlayingPatterns->size() > 0 ) {
+ m_nPatternSize = m_pPlayingPatterns->longest_pattern_length();
+ } else {
+ m_nPatternSize = MAX_NOTES;
}
+
EventQueue::get_instance()->push_event( EVENT_PATTERN_CHANGED, 0 );
-
- } else if ( pHydrogen->getMode() == Song::Mode::Pattern ) {
- if ( Preferences::get_instance()->patternModePlaysSelected() ) {
- auto pSelectedPattern =
- pSong->getPatternList()->get( pHydrogen->getSelectedPatternNumber() );
- if ( m_pPlayingPatterns->size() != 1 ||
- ( m_pPlayingPatterns->size() == 1 &&
- m_pPlayingPatterns->get( 0 ) != pSelectedPattern ) ) {
-
- m_pPlayingPatterns->clear();
+ }
+ else if ( pHydrogen->getPatternMode() == Song::PatternMode::Selected ) {
+ // Called asynchronous when a different pattern number
+ // gets selected or the user switches from stacked into
+ // selected pattern mode.
+
+ auto pSelectedPattern =
+ pSong->getPatternList()->get( pHydrogen->getSelectedPatternNumber() );
+ if ( m_pPlayingPatterns->size() != 1 ||
+ ( m_pPlayingPatterns->size() == 1 &&
+ m_pPlayingPatterns->get( 0 ) != pSelectedPattern ) ) {
+
+ m_pPlayingPatterns->clear();
+
+ if ( pSelectedPattern != nullptr ) {
m_pPlayingPatterns->add( pSelectedPattern );
pSelectedPattern->addFlattenedVirtualPatterns( m_pPlayingPatterns );
-
- EventQueue::get_instance()->push_event( EVENT_PATTERN_CHANGED, 0 );
}
- } else {
- // Stacked pattern mode
- int nPatternSize;
- if ( m_pPlayingPatterns->size() != 0 ) {
- nPatternSize = m_pPlayingPatterns->longest_pattern_length();
+ if ( m_pPlayingPatterns->size() > 0 ) {
+ m_nPatternSize = m_pPlayingPatterns->longest_pattern_length();
} else {
- nPatternSize = 0;
+ m_nPatternSize = MAX_NOTES;
}
+
+ EventQueue::get_instance()->push_event( EVENT_PATTERN_CHANGED, 0 );
+ }
+ }
+ else if ( pHydrogen->getPatternMode() == Song::PatternMode::Stacked ) {
- if ( nPatternSize == 0 ||
- nTick >= nPatternStartTick + nPatternSize ) {
-
- if ( m_pNextPatterns->size() > 0 ) {
- for ( const auto& ppattern : *m_pNextPatterns ) {
- // If provided pattern is not part of the
- // list, a nullptr will be returned. Else, a
- // pointer to the deleted pattern will be
- // returned.
- if ( ( m_pPlayingPatterns->del( ppattern ) ) == nullptr ) {
- // pPattern was not present yet. It will
- // be added.
- m_pPlayingPatterns->add( ppattern );
- ppattern->addFlattenedVirtualPatterns( m_pPlayingPatterns );
- } else {
- // pPattern was already present. It will
- // be deleted.
- ppattern->removeFlattenedVirtualPatterns( m_pPlayingPatterns );
- }
- EventQueue::get_instance()->push_event( EVENT_PATTERN_CHANGED, 0 );
- }
- m_pNextPatterns->clear();
+ if ( m_pNextPatterns->size() > 0 ) {
+
+ for ( const auto& ppattern : *m_pNextPatterns ) {
+ // If provided pattern is not part of the
+ // list, a nullptr will be returned. Else, a
+ // pointer to the deleted pattern will be
+ // returned.
+ if ( ppattern == nullptr ) {
+ continue;
+ }
+
+ if ( ( m_pPlayingPatterns->del( ppattern ) ) == nullptr ) {
+ // pPattern was not present yet. It will
+ // be added.
+ m_pPlayingPatterns->add( ppattern );
+ ppattern->addFlattenedVirtualPatterns( m_pPlayingPatterns );
+ } else {
+ // pPattern was already present. It will
+ // be deleted.
+ ppattern->removeFlattenedVirtualPatterns( m_pPlayingPatterns );
}
+ EventQueue::get_instance()->push_event( EVENT_PATTERN_CHANGED, 0 );
+ }
+ m_pNextPatterns->clear();
+
+ if ( m_pPlayingPatterns->size() != 0 ) {
+ m_nPatternSize = m_pPlayingPatterns->longest_pattern_length();
+ } else {
+ m_nPatternSize = MAX_NOTES;
}
- } // Stacked mode
- } // Pattern mode
+ }
+ }
}
void AudioEngine::toggleNextPattern( int nPatternNumber ) {
@@ -2210,17 +2304,6 @@ int AudioEngine::updateNoteQueue( unsigned nFrames )
return 0;
}
- // Use local representations of the current transport position in
- // order for it to not get into a dirty state.
- int nColumn = m_nColumn;
-
- // Since we deal with a lookahead the pattern start and tick
- // position in the loop below are _not_ the same as the ones of
- // the audio engine itself. We set the pattern start tick to -1 to
- // signal the code below its state is dirty and needs to be
- // updated. This is more efficient than updating it in every iteration.
- long nPatternStartTick = -1;
- long nPatternTickPosition = -1;
long long nNoteStart;
float fUsedTickSize;
@@ -2246,31 +2329,18 @@ int AudioEngine::updateNoteQueue( unsigned nFrames )
stop();
return -1;
}
-
- nColumn = pHydrogen->getColumnForTick( nnTick,
- pSong->isLoopEnabled(),
- &nPatternStartTick );
- if ( nnTick >= std::floor( m_fSongSizeInTicks ) &&
- std::floor( m_fSongSizeInTicks ) != 0 ) {
- // When using the JACK audio driver the overall
- // transport position will be managed by an external
- // server. Since it is agnostic of all the looping in
- // its clients, it will only increment time and
- // Hydrogen has to take care of the looping itself.
- nPatternTickPosition = ( nnTick - nPatternStartTick )
- % static_cast(std::floor( m_fSongSizeInTicks ));
- } else {
- nPatternTickPosition = nnTick - nPatternStartTick;
- }
+ int nOldColumn = m_nColumn;
+
+ updateSongTransportPosition( static_cast(nnTick) );
// If no pattern list could not be found, either choose
// the first one if loop mode is activate or the
// function returns indicating that the end of the song is
// reached.
- if ( nColumn == -1 ||
+ if ( m_nColumn == -1 ||
( pSong->getLoopMode() == Song::LoopMode::Finishing &&
- nColumn < m_nLastPlayingPatternsColumn ) ) {
+ m_nColumn < nOldColumn ) ) {
INFOLOG( "End of Song" );
if( pHydrogen->getMidiOutput() != nullptr ){
@@ -2279,87 +2349,31 @@ int AudioEngine::updateNoteQueue( unsigned nFrames )
return -1;
}
-
- if ( nColumn != m_nLastPlayingPatternsColumn ) {
- updatePlayingPatterns( nColumn );
- m_nLastPlayingPatternsColumn = nColumn;
- }
}
//////////////////////////////////////////////////////////////
// PATTERN MODE
else if ( pHydrogen->getMode() == Song::Mode::Pattern ) {
- int nPatternSize = -1;
- if ( nPatternStartTick == -1 ) {
- if ( m_pPlayingPatterns->size() != 0 ) {
- nPatternSize = m_pPlayingPatterns->longest_pattern_length();
- } else {
- nPatternSize = MAX_NOTES;
- }
+ updatePatternTransportPosition( nnTick );
- if ( nPatternSize > 0 ) {
- nPatternStartTick =
- std::floor( static_cast(nnTick -
- std::max( nPatternStartTick,
- static_cast(0) )) /
- static_cast(nPatternSize) ) * nPatternSize;
- } else {
- nPatternStartTick = nnTick;
- }
- }
-
- updatePlayingPatterns( 0, nnTick, nPatternStartTick );
-
- if ( nPatternSize == -1 ||
- nPatternSize != m_pPlayingPatterns->longest_pattern_length() ) {
-
- if ( m_pPlayingPatterns->size() != 0 ) {
- nPatternSize = m_pPlayingPatterns->longest_pattern_length();
- } else {
- nPatternSize = MAX_NOTES;
- }
-
- if ( nPatternSize == 0 ) {
- ERRORLOG( "nPatternSize == 0" );
- }
-
- if ( nPatternStartTick == -1 ||
- nnTick >= nPatternStartTick + nPatternSize ) {
- if ( nPatternSize > 0 ) {
- nPatternStartTick +=
- std::floor( static_cast(nnTick -
- std::max( nPatternStartTick,
- static_cast(0) )) /
- static_cast(nPatternSize) ) * nPatternSize;
- } else {
- nPatternStartTick = nnTick;
- }
- }
- }
-
- nPatternTickPosition = nnTick - nPatternStartTick;
- if ( nPatternSize > 0 && nPatternTickPosition > nPatternSize ) {
- nPatternTickPosition = ( nnTick - nPatternStartTick ) %
- nPatternSize;
- }
+ // DEBUGLOG( QString( "[post] nnTick: %1, m_nPatternTickPosition: %2, m_nPatternStartTick: %3, m_nPatternSize: %4" )
+ // .arg( nnTick ).arg( m_nPatternTickPosition )
+ // .arg( m_nPatternStartTick ).arg( m_nPatternSize ) );
- // DEBUGLOG( QString( "[post] nnTick: %1, nPatternTickPosition: %2, nPatternStartTick: %3, nPatternSize: %4" )
- // .arg( nnTick ).arg( nPatternTickPosition )
- // .arg( nPatternStartTick ).arg( nPatternSize ) );
}
//////////////////////////////////////////////////////////////
// Metronome
// Only trigger the metronome at a predefined rate.
- if ( nPatternTickPosition % 48 == 0 ) {
+ if ( m_nPatternTickPosition % 48 == 0 ) {
float fPitch;
float fVelocity;
// Depending on whether the metronome beat will be issued
// at the beginning or in the remainder of the pattern,
// two different sounds and events will be used.
- if ( nPatternTickPosition == 0 ) {
+ if ( m_nPatternTickPosition == 0 ) {
fPitch = 3;
fVelocity = 1.0;
EventQueue::get_instance()->push_event( EVENT_METRONOME, 1 );
@@ -2408,7 +2422,7 @@ int AudioEngine::updateNoteQueue( unsigned nFrames )
// Loop over all notes at tick nPatternTickPosition
// (associated tick is determined by Note::__position
// at the time of insertion into the Pattern).
- FOREACH_NOTE_CST_IT_BOUND(notes,it,nPatternTickPosition) {
+ FOREACH_NOTE_CST_IT_BOUND(notes, it, m_nPatternTickPosition) {
Note *pNote = it->second;
if ( pNote ) {
pNote->set_just_recorded( false );
@@ -2421,8 +2435,8 @@ int AudioEngine::updateNoteQueue( unsigned nFrames )
/** Swing 16ths //
* delay the upbeat 16th-notes by a constant (manual) offset
*/
- if ( ( ( nPatternTickPosition % ( MAX_NOTES / 16 ) ) == 0 )
- && ( ( nPatternTickPosition % ( MAX_NOTES / 8 ) ) != 0 )
+ if ( ( ( m_nPatternTickPosition % ( MAX_NOTES / 16 ) ) == 0 )
+ && ( ( m_nPatternTickPosition % ( MAX_NOTES / 8 ) ) != 0 )
&& pSong->getSwingFactor() > 0 ) {
/* TODO: incorporate the factor MAX_NOTES / 32. either in Song::m_fSwingFactor
* or make it a member variable.
@@ -2501,7 +2515,7 @@ int AudioEngine::updateNoteQueue( unsigned nFrames )
pCopiedNote->computeNoteStart();
if ( pHydrogen->getMode() == Song::Mode::Song ) {
- float fPos = static_cast( nColumn ) +
+ float fPos = static_cast( m_nColumn ) +
pCopiedNote->get_position() % 192 / 192.f;
pCopiedNote->set_velocity( pNote->get_velocity() *
pAutomationPath->get_value( fPos ) );
@@ -3538,7 +3552,7 @@ bool AudioEngine::testNoteEnqueuing() {
//////////////////////////////////////////////////////////////////
pCoreActionController->activateSongMode( false );
- pPref->setPatternModePlaysSelected( true );
+ pHydrogen->setPatternMode( Song::PatternMode::Selected );
pHydrogen->setSelectedPatternNumber( 4 );
lock( RIGHT_HERE );
diff --git a/src/core/AudioEngine/AudioEngine.h b/src/core/AudioEngine/AudioEngine.h
index 2a926188f6..8aa5e75c1a 100644
--- a/src/core/AudioEngine/AudioEngine.h
+++ b/src/core/AudioEngine/AudioEngine.h
@@ -80,6 +80,20 @@ namespace H2Core
* thread to lock the engine and unlock() to make it accessible for
* other threads once again.
*
+ * The audio engine does not have one but two consistent states with
+ * respect it its member variables. #m_fTick, #m_nFrames,
+ * #m_fTickOffset, #m_fTickMismatch, #m_fBpm, #m_fTickSize,
+ * #m_nFrameOffset, #m_state, and #m_nRealtimeFrames are associated
+ * with the current transport position. #m_fLastTickIntervalEnd,
+ * #m_nColumn, #m_nPatternSize, #m_nPatternStartTick, and
+ * #m_nPatternTickPosition determine the current position
+ * updateNoteQueue() is adding notes from #m_pPlayingPatterns into
+ * #m_songNoteQueue. Since the latter is ahead of the current
+ * transport position by a non-constant (tempo-dependent) lookahead,
+ * both states are out of sync while in playback but in sync again
+ * once the transport gets relocated (which resets the lookahead). But
+ * within themselves both states are consistent.
+ *
* \ingroup docCore docAudioEngine
*/
class AudioEngine : public H2Core::TransportInfo, public H2Core::Object
@@ -411,7 +425,7 @@ class AudioEngine : public H2Core::TransportInfo, public H2Core::Object pSong );
/** Is allowed to call removeSong().*/
friend void Hydrogen::removeSong();
+ /** Is allowed to use locate() to directly set the position in
+ frames as well as to used setColumn and setPatternTickPos to
+ move the arrow in the SongEditorPositionRuler even when
+ playback is stopped.*/
+ friend void Hydrogen::updateSelectedPattern();
friend bool CoreActionController::locateToTick( long nTick, bool );
/** Is allowed to set m_state to State::Ready via setState()*/
friend int FakeDriver::connect();
friend void JackAudioDriver::updateTransportInfo();
friend void JackAudioDriver::relocateUsingBBT();
private:
+
+ /**
+ * Sets the Hydrogen::m_nSelectedPatternNumber to the pattern
+ * recorded notes will be inserted in.
+ */
+ void handleSelectedPattern();
+
+ inline void processPlayNotes( unsigned long nframes );
/**
* Converts a tick into frames under the assumption of a constant
* @a fTickSize since the beginning of the song (sample rate,
@@ -588,8 +611,6 @@ class AudioEngine : public H2Core::TransportInfo, public H2Core::Object
public:
/** possible keys */
enum Key { C=KEY_MIN, Cs, D, Ef, E, F, Fs, G, Af, A, Bf, B };
+ static QString KeyToQString( Key key );
+
/** possible octaves */
enum Octave { P8Z=-3, P8Y=-2, P8X=-1, P8=OCTAVE_DEFAULT, P8A=1, P8B=2, P8C=3 };
@@ -344,6 +346,20 @@ class Note : public H2Core::Object
return pow( 1.0594630943593, fPitch );
}
+ static inline Octave pitchToOctave( int nPitch ) {
+ if ( nPitch >= 0 ) {
+ return (Octave)(nPitch / 12);
+ } else {
+ return (Octave)((nPitch-11) / 12);
+ }
+ }
+ static inline Key pitchToKey( int nPitch ) {
+ return (Key)(nPitch - 12 * pitchToOctave( nPitch ));
+ }
+ static inline int octaveKeyToPitch( Octave octave, Key key ) {
+ return 12 * (int)octave + (int)key;
+ }
+
/**
* Returns the sample associated with the note for a specific
* InstrumentComponent @a nComponentID.
diff --git a/src/core/Basics/PatternList.cpp b/src/core/Basics/PatternList.cpp
index fbdc0e76c0..ed70c6caaa 100644
--- a/src/core/Basics/PatternList.cpp
+++ b/src/core/Basics/PatternList.cpp
@@ -255,7 +255,7 @@ QString PatternList::find_unused_pattern_name( QString sourceName, Pattern* igno
return unusedPatternNameCandidate;
}
-int PatternList::longest_pattern_length() {
+int PatternList::longest_pattern_length() const {
int nMax = -1;
for ( int i = 0; i < __patterns.size(); i++ ) {
nMax = std::max( nMax, __patterns[i]->get_length() );
diff --git a/src/core/Basics/PatternList.h b/src/core/Basics/PatternList.h
index 7ca0120d16..96736b5136 100644
--- a/src/core/Basics/PatternList.h
+++ b/src/core/Basics/PatternList.h
@@ -158,7 +158,7 @@ class AudioEngineLocking;
* Get the length of the longest pattern in the list
* \return pattern length in ticks, -1 if list is empty
*/
- int longest_pattern_length();
+ int longest_pattern_length() const;
/** Formatted string version for debugging purposes.
* \param sPrefix String prefix which will be added in front of
* every new line
diff --git a/src/core/Basics/Song.cpp b/src/core/Basics/Song.cpp
index 63ec5bb84e..b1bc6e6693 100644
--- a/src/core/Basics/Song.cpp
+++ b/src/core/Basics/Song.cpp
@@ -78,6 +78,7 @@ Song::Song( const QString& sName, const QString& sAuthor, float fBpm, float fVol
, m_pComponents( nullptr )
, m_sFilename( "" )
, m_loopMode( LoopMode::Disabled )
+ , m_patternMode( PatternMode::Selected )
, m_fHumanizeTimeValue( 0.0 )
, m_fHumanizeVelocityValue( 0.0 )
, m_fSwingFactor( 0.0 )
@@ -89,6 +90,7 @@ Song::Song( const QString& sName, const QString& sAuthor, float fBpm, float fVol
, m_pVelocityAutomationPath( nullptr )
, m_sLicense( "" )
, m_actionMode( ActionMode::selectMode )
+ , m_bIsPatternEditorLocked( false )
, m_nPanLawType ( Sampler::RATIO_STRAIGHT_POLYGONAL )
, m_fPanLawKNorm ( Sampler::K_NORM_DEFAULT )
{
@@ -148,13 +150,6 @@ void Song::setBpm( float fBpm ) {
void Song::setActionMode( Song::ActionMode actionMode ) {
m_actionMode = actionMode;
- if ( actionMode == Song::ActionMode::selectMode ) {
- EventQueue::get_instance()->push_event( EVENT_ACTION_MODE_CHANGE, 0 );
- } else if ( actionMode == Song::ActionMode::drawMode ) {
- EventQueue::get_instance()->push_event( EVENT_ACTION_MODE_CHANGE, 1 );
- } else {
- ERRORLOG( QString( "Unknown actionMode" ) );
- }
setIsModified( true );
}
@@ -1009,7 +1004,14 @@ std::shared_ptr SongReader::readSong( const QString& sFileName )
QString sNotes( LocalFileMng::readXmlString( songNode, "notes", "..." ) );
QString sLicense( LocalFileMng::readXmlString( songNode, "license", "Unknown license" ) );
bool bLoopEnabled = LocalFileMng::readXmlBool( songNode, "loopEnabled", false );
- pPreferences->setPatternModePlaysSelected( LocalFileMng::readXmlBool( songNode, "patternModeMode", true ) );
+ bool bPatternMode =
+ LocalFileMng::readXmlBool( songNode, "patternModeMode",
+ static_cast(Song::PatternMode::Selected) );
+
+ Song::PatternMode patternMode = Song::PatternMode::Selected;
+ if ( ! bPatternMode ) {
+ patternMode = Song::PatternMode::Stacked;
+ }
Song::Mode mode = Song::Mode::Pattern;
QString sMode = LocalFileMng::readXmlString( songNode, "mode", "pattern" );
if ( sMode == "song" ) {
@@ -1022,6 +1024,7 @@ std::shared_ptr SongReader::readSong( const QString& sFileName )
Song::ActionMode actionMode = static_cast( LocalFileMng::readXmlInt( songNode, "action_mode",
static_cast( Song::ActionMode::selectMode ) ) );
+ bool bIsPatternEditorLocked = LocalFileMng::readXmlBool( songNode, "isPatternEditorLocked", false );
float fHumanizeTimeValue = LocalFileMng::readXmlFloat( songNode, "humanize_time", 0.0 );
float fHumanizeVelocityValue = LocalFileMng::readXmlFloat( songNode, "humanize_velocity", 0.0 );
float fSwingFactor = LocalFileMng::readXmlFloat( songNode, "swing_factor", 0.0 );
@@ -1047,6 +1050,7 @@ std::shared_ptr SongReader::readSong( const QString& sFileName )
} else {
pSong->setLoopMode( Song::LoopMode::Disabled );
}
+ pSong->setPatternMode( patternMode );
pSong->setMode( mode );
pSong->setHumanizeTimeValue( fHumanizeTimeValue );
pSong->setHumanizeVelocityValue( fHumanizeVelocityValue );
@@ -1055,6 +1059,7 @@ std::shared_ptr SongReader::readSong( const QString& sFileName )
pSong->setPlaybackTrackEnabled( bPlaybackTrackEnabled );
pSong->setPlaybackTrackVolume( fPlaybackTrackVolume );
pSong->setActionMode( actionMode );
+ pSong->setIsPatternEditorLocked( bIsPatternEditorLocked );
pSong->setIsTimelineActivated( bIsTimelineActivated );
// pan law
diff --git a/src/core/Basics/Song.h b/src/core/Basics/Song.h
index 9cbb722a08..715b928865 100644
--- a/src/core/Basics/Song.h
+++ b/src/core/Basics/Song.h
@@ -82,7 +82,24 @@ class Song : public H2Core::Object, public std::enable_shared_from_this, public std::enable_shared_from_this, public std::enable_shared_from_this, public std::enable_shared_from_this, public std::enable_shared_from_this, public std::enable_shared_from_this, public std::enable_shared_from_this Song::getTimeline() const {
return m_pTimeline;
}
@@ -525,6 +571,15 @@ inline void Song::setLoopMode( Song::LoopMode loopMode )
setIsModified( true );
}
+inline Song::PatternMode Song::getPatternMode() const
+{
+ return m_patternMode;
+}
+inline void Song::setPatternMode( Song::PatternMode patternMode )
+{
+ m_patternMode = patternMode;
+}
+
inline float Song::getHumanizeTimeValue() const
{
return m_fHumanizeTimeValue;
diff --git a/src/core/CoreActionController.cpp b/src/core/CoreActionController.cpp
index 0dbdb99210..893a9d6dd7 100644
--- a/src/core/CoreActionController.cpp
+++ b/src/core/CoreActionController.cpp
@@ -799,6 +799,7 @@ bool CoreActionController::activateJackTimebaseMaster( bool bActivate ) {
bool CoreActionController::activateSongMode( bool bActivate ) {
auto pHydrogen = Hydrogen::get_instance();
+ auto pAudioEngine = pHydrogen->getAudioEngine();
if ( pHydrogen->getSong() == nullptr ) {
ERRORLOG( "no song set" );
@@ -807,11 +808,18 @@ bool CoreActionController::activateSongMode( bool bActivate ) {
pHydrogen->sequencer_stop();
if ( bActivate && pHydrogen->getMode() != Song::Mode::Song ) {
- locateToColumn( 0 );
pHydrogen->setMode( Song::Mode::Song );
} else if ( ! bActivate && pHydrogen->getMode() != Song::Mode::Pattern ) {
pHydrogen->setMode( Song::Mode::Pattern );
+
+ // Add the selected pattern to playing ones.
+ if ( pHydrogen->getPatternMode() == Song::PatternMode::Selected ) {
+ pAudioEngine->lock( RIGHT_HERE );
+ pAudioEngine->updatePlayingPatterns( 0, 0 );
+ pAudioEngine->unlock();
+ }
}
+ locateToColumn( 0 );
return true;
}
@@ -1194,13 +1202,12 @@ bool CoreActionController::extractDrumkit( const QString& sDrumkitPath, const QS
bool CoreActionController::locateToColumn( int nPatternGroup ) {
if ( nPatternGroup < -1 ) {
- ERRORLOG( QString( "Provided column [%1] too low. Assigning -1 (indicating the beginning of a song without showing a cursor in the SongEditorPositionRuler) instead." )
+ ERRORLOG( QString( "Provided column [%1] too low. Assigning 0 instead." )
.arg( nPatternGroup ) );
- nPatternGroup = -1;
+ nPatternGroup = 0;
}
auto pHydrogen = Hydrogen::get_instance();
-
if ( pHydrogen->getSong() == nullptr ) {
ERRORLOG( "no song set" );
return false;
@@ -1209,6 +1216,7 @@ bool CoreActionController::locateToColumn( int nPatternGroup ) {
auto pAudioEngine = pHydrogen->getAudioEngine();
EventQueue::get_instance()->push_event( EVENT_METRONOME, 1 );
+
long nTotalTick = pHydrogen->getTickForColumn( nPatternGroup );
if ( nTotalTick < 0 ) {
// There is no pattern inserted in the SongEditor.
@@ -1241,6 +1249,8 @@ bool CoreActionController::locateToTick( long nTick, bool bWithJackBroadcast ) {
pAudioEngine->locate( nTick, bWithJackBroadcast );
pAudioEngine->unlock();
+
+ EventQueue::get_instance()->push_event( EVENT_RELOCATION, 0 );
return true;
}
@@ -1290,7 +1300,11 @@ bool CoreActionController::setPattern( Pattern* pPattern, int nPatternPosition )
}
pPatternList->insert( nPatternPosition, pPattern );
- pHydrogen->setSelectedPatternNumber( nPatternPosition );
+ if ( pHydrogen->isPatternEditorLocked() ) {
+ pHydrogen->updateSelectedPattern();
+ } else {
+ pHydrogen->setSelectedPatternNumber( nPatternPosition );
+ }
pHydrogen->setIsModified( true );
// Update the SongEditor.
@@ -1367,8 +1381,7 @@ bool CoreActionController::removePattern( int nPatternNumber ) {
// mode.
for ( int ii = 0; ii < pPlayingPatterns->size(); ++ii ) {
if ( pPlayingPatterns->get( ii ) == pPattern ) {
- pAudioEngine->flushPlayingPatterns();
- pAudioEngine->updatePlayingPatterns( pAudioEngine->getColumn() );
+ pAudioEngine->removePlayingPattern( ii );
break;
}
}
diff --git a/src/core/EventQueue.h b/src/core/EventQueue.h
index 902bbe6547..b4bb430212 100644
--- a/src/core/EventQueue.h
+++ b/src/core/EventQueue.h
@@ -42,17 +42,12 @@ enum EventType {
EVENT_NONE,
EVENT_STATE,
/**
- * The current list of patterns the transport position resides in
- * changed.
+ * The list of currently played patterns in changed.
*
* In #Song::Mode::Song this is triggered every time the column of
* the SongEditor grid changed. Either by rolling transport or by
* relocation.
*
- * In #Song::Mode::Pattern with
- * Preferences::m_bPatternModePlaysSelected set true it is
- * triggered if the currently selected pattern changes.
- *
* It is handled by EventListener::patternChangedEvent().
*/
EVENT_PATTERN_CHANGED,
@@ -61,11 +56,9 @@ enum EventType {
*/
EVENT_PATTERN_MODIFIED,
/** Another pattern was selected via MIDI or the GUI without
- * affecting the audio transport (e.g in Song::PATTERN_MODE when
- * Preferences::m_bPatternModePlaysSelected is set to true). While
- * the selection in the former case already happens in the GUI,
- * this event will be used to tell it the selection was successful
- * and had been done.
+ * affecting the audio transport. While the selection in the
+ * former case already happens in the GUI, this event will be used
+ * to tell it the selection was successful and had been done.
*
* Handled by EventListener::selectedPatternChangedEvent().
*/
@@ -151,7 +144,8 @@ enum EventType {
and informs the GUI about a state change.*/
EVENT_JACK_TIMEBASE_STATE_CHANGED,
EVENT_SONG_MODE_ACTIVATION,
- /** Activate stacked mode (1) or "focus"/"PatternPlaysSelected"/normal mode */
+ /** Song::PatternMode::Stacked (0) or Song::PatternMode::Selected
+ (1) was activated */
EVENT_STACKED_MODE_ACTIVATION,
/** Toggles the button indicating the usage loop mode.*/
EVENT_LOOP_MODE_ACTIVATION,
@@ -163,7 +157,15 @@ enum EventType {
(either during playback or when relocated by the user)*/
EVENT_COLUMN_CHANGED,
/** A the current drumkit was replaced by a new one*/
- EVENT_DRUMKIT_LOADED
+ EVENT_DRUMKIT_LOADED,
+ /** Locks the PatternEditor on the pattern currently played back.*/
+ EVENT_PATTERN_EDITOR_LOCKED,
+ /** Triggered in case there is a relocation of the transport
+ * position due to an user interaction or an incoming MIDI/OSC
+ * command.
+ */
+ EVENT_RELOCATION,
+ EVENT_SONG_SIZE_CHANGED
};
/** Basic building block for the communication between the core of
diff --git a/src/core/Hydrogen.cpp b/src/core/Hydrogen.cpp
index 3ee77ed7cd..6e906acc5d 100644
--- a/src/core/Hydrogen.cpp
+++ b/src/core/Hydrogen.cpp
@@ -257,7 +257,6 @@ void Hydrogen::setSong( std::shared_ptr pSong )
std::shared_ptr pCurrentSong = getSong();
if ( pSong == pCurrentSong ) {
- DEBUGLOG( "pSong == pCurrentSong" );
return;
}
@@ -721,7 +720,7 @@ bool Hydrogen::instrumentHasNotes( std::shared_ptr pInst )
{
if( pPatternList->get( nPattern )->references( pInst ) )
{
- DEBUGLOG("Instrument " + pInst->get_name() + " has notes" );
+ INFOLOG("Instrument " + pInst->get_name() + " has notes" );
return true;
}
}
@@ -846,6 +845,13 @@ void Hydrogen::restartLadspaFX()
}
}
+void Hydrogen::updateSelectedPattern() {
+ if ( isPatternEditorLocked() ) {
+ m_pAudioEngine->lock( RIGHT_HERE );
+ m_pAudioEngine->handleSelectedPattern();
+ m_pAudioEngine->unlock();
+ }
+}
void Hydrogen::setSelectedPatternNumber( int nPat, bool bNeedsLock )
{
@@ -853,15 +859,18 @@ void Hydrogen::setSelectedPatternNumber( int nPat, bool bNeedsLock )
return;
}
- if ( Preferences::get_instance()->patternModePlaysSelected() ) {
+ if ( getPatternMode() == Song::PatternMode::Selected ) {
if ( bNeedsLock ) {
- getAudioEngine()->lock( RIGHT_HERE );
+ m_pAudioEngine->lock( RIGHT_HERE );
}
m_nSelectedPatternNumber = nPat;
+ // The specific values provided are not important since we a
+ // in selected pattern mode.
+ m_pAudioEngine->updatePlayingPatterns( 0, 0 );
if ( bNeedsLock ) {
- getAudioEngine()->unlock();
+ m_pAudioEngine->unlock();
}
} else {
m_nSelectedPatternNumber = nPat;
@@ -1075,28 +1084,6 @@ void Hydrogen::onJackMaster()
#endif
}
-void Hydrogen::setPlaysSelected( bool bPlaysSelected )
-{
- auto pAudioEngine = m_pAudioEngine;
-
- if ( getMode() != Song::Mode::Pattern ) {
- return;
- }
-
- auto pSong = getSong();
- auto pPref = Preferences::get_instance();
-
- if ( pPref->patternModePlaysSelected() != bPlaysSelected ) {
- pAudioEngine->lock( RIGHT_HERE );
-
- pPref->setPatternModePlaysSelected( bPlaysSelected );
-
- pAudioEngine->updatePlayingPatterns( pAudioEngine->getColumn() );
-
- pAudioEngine->unlock();
- }
-}
-
void Hydrogen::addInstrumentToDeathRow( std::shared_ptr pInstr ) {
__instrument_death_row.push_back( pInstr );
__kill_instruments();
@@ -1209,7 +1196,7 @@ bool Hydrogen::isUnderSessionManagement() const {
}
bool Hydrogen::isTimelineEnabled() const {
- if ( getSong()->getIsTimelineActivated() &&
+ if ( __song->getIsTimelineActivated() &&
getMode() == Song::Mode::Song &&
getJackTimebaseState() != JackAudioDriver::Timebase::Slave ) {
return true;
@@ -1218,6 +1205,89 @@ bool Hydrogen::isTimelineEnabled() const {
return false;
}
+bool Hydrogen::isPatternEditorLocked() const {
+ if ( getMode() == Song::Mode::Song ) {
+ if ( __song->getIsPatternEditorLocked() ) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void Hydrogen::setIsPatternEditorLocked( bool bValue ) {
+ if ( __song != nullptr ) {
+ __song->setIsPatternEditorLocked( bValue );
+
+ EventQueue::get_instance()->push_event( EVENT_PATTERN_EDITOR_LOCKED,
+ bValue );
+ }
+}
+
+Song::Mode Hydrogen::getMode() const {
+ if ( __song != nullptr ) {
+ return __song->getMode();
+ }
+
+ return Song::Mode::None;
+}
+
+void Hydrogen::setMode( Song::Mode mode ) {
+ if ( __song != nullptr && mode != __song->getMode() ) {
+ __song->setMode( mode );
+ EventQueue::get_instance()->push_event( EVENT_SONG_MODE_ACTIVATION,
+ ( mode == Song::Mode::Song) ? 1 : 0 );
+ }
+}
+
+Song::ActionMode Hydrogen::getActionMode() const {
+ if ( __song != nullptr ) {
+ return __song->getActionMode();
+ }
+ return Song::ActionMode::None;
+}
+
+void Hydrogen::setActionMode( Song::ActionMode mode ) {
+ if ( __song != nullptr ) {
+ __song->setActionMode( mode );
+ EventQueue::get_instance()->push_event( EVENT_ACTION_MODE_CHANGE,
+ ( mode == Song::ActionMode::drawMode ) ? 1 : 0 );
+ }
+}
+
+Song::PatternMode Hydrogen::getPatternMode() const {
+ if ( getMode() == Song::Mode::Pattern ) {
+ return __song->getPatternMode();
+ }
+ return Song::PatternMode::None;
+}
+
+void Hydrogen::setPatternMode( Song::PatternMode mode )
+{
+ if ( __song != nullptr &&
+ getPatternMode() != mode ) {
+ m_pAudioEngine->lock( RIGHT_HERE );
+
+ __song->setPatternMode( mode );
+ setIsModified( true );
+
+ if ( mode == Song::PatternMode::Selected ||
+ m_pAudioEngine->getState() != AudioEngine::State::Playing ) {
+ // Only update the playing patterns in selected pattern
+ // mode or if transport is not rolling. In stacked pattern
+ // mode with transport rolling
+ // AudioEngine::updatePatternTransportPosition() will call
+ // the functions and activate the next patterns once the
+ // current ones are looped.
+ m_pAudioEngine->updatePlayingPatterns( m_pAudioEngine->getColumn() );
+ }
+
+ m_pAudioEngine->unlock();
+ EventQueue::get_instance()->push_event( EVENT_STACKED_MODE_ACTIVATION,
+ ( mode == Song::PatternMode::Selected ) ? 1 : 0 );
+ }
+}
+
Hydrogen::Tempo Hydrogen::getTempoSource() const {
if ( getMode() == Song::Mode::Song ) {
if ( getJackTimebaseState() == JackAudioDriver::Timebase::Slave ) {
@@ -1269,7 +1339,6 @@ void Hydrogen::startNsmClient()
void Hydrogen::recalculateRubberband( float fBpm ) {
- DEBUGLOG( fBpm );
if ( !Preferences::get_instance()->getRubberBandBatchMode() ) {
return;
@@ -1336,13 +1405,6 @@ void Hydrogen::setIsModified( bool bIsModified ) {
}
}
-void Hydrogen::setMode( Song::Mode mode ) {
- if ( getSong() != nullptr ) {
- getSong()->setMode( mode );
- EventQueue::get_instance()->push_event( EVENT_SONG_MODE_ACTIVATION, ( mode == Song::Mode::Song) ? 1 : 0 );
- }
-}
-
void Hydrogen::setIsTimelineActivated( bool bEnabled ) {
if ( getSong() != nullptr ) {
auto pPref = Preferences::get_instance();
diff --git a/src/core/Hydrogen.h b/src/core/Hydrogen.h
index ac2ddef207..672314ae48 100644
--- a/src/core/Hydrogen.h
+++ b/src/core/Hydrogen.h
@@ -108,12 +108,6 @@ class Hydrogen : public H2Core::Object
void toggleNextPattern( int nPatternNumber );
/** Wrapper around AudioEngine::flushAndAddNextPattern().*/
void flushAndAddNextPattern( int nPatternNumber );
- /**
- * Switches playback to focused pattern.
- *
- * ("Focused pattern" or "PlaysSelected" is the opposite of "Stacked" mode)
- */
- void setPlaysSelected( bool bPlaysSelected );
/**
* Get the current song.
@@ -177,6 +171,18 @@ class Hydrogen : public H2Core::Object
EVENT_SONG_MODE_ACTIVATION and should be used by all parts of the
code except for song reading/setting.*/
void setMode( Song::Mode mode );
+
+ Song::ActionMode getActionMode() const;
+ /** Wrapper around Song::setActionMode() which also triggers
+ EVENT_ACTION_MODE_CHANGE and should be used by all parts of the
+ code except for song reading/setting.*/
+ void setActionMode( Song::ActionMode mode );
+
+ Song::PatternMode getPatternMode() const;
+ /** Wrapper around Song::setPatternMode() which also triggers
+ EVENT_STACKED_MODE_ACTIVATION and should be used by all parts of the
+ code except for song reading/setting.*/
+ void setPatternMode( Song::PatternMode mode );
/** Wrapper around both Song::setIsTimelineActivated (recent) and
Preferences::setUseTimelinebpm() (former place to store the
@@ -316,20 +322,18 @@ void previewSample( Sample *pSample );
/**
* Sets #m_nSelectedPatternNumber.
*
- * If Preferences::m_pPatternModePlaysSelected is set to true, the
- * AudioEngine is locked before @a nPat will be assigned. But in
- * any case the function will push the
- * #EVENT_SELECTED_PATTERN_CHANGED Event to the EventQueue.
- *
- * If @a nPat is equal to #m_nSelectedPatternNumber, the function
- * will return right away.
- *
*\param nPat Sets #m_nSelectedPatternNumber
* \param bNeedsLock Whether the function was called with the
* audio engine locked already or it should do so itself.
*/
void setSelectedPatternNumber( int nPat, bool bNeedsLock = true );
+ /**
+ * Updates the selected pattern to the one recorded note will be
+ * inserted to.
+ */
+ void updateSelectedPattern();
+
int getSelectedInstrumentNumber() const;
void setSelectedInstrumentNumber( int nInstrument );
@@ -450,6 +454,13 @@ void previewSample( Sample *pSample );
*/
bool isTimelineEnabled() const;
+ /**
+ * Convenience function checking whether using the Pattern Editor
+ * is locked in the song settings and the song is in song mode.
+ */
+ bool isPatternEditorLocked() const;
+ void setIsPatternEditorLocked( bool bValue );
+
Tempo getTempoSource() const;
/**
@@ -676,9 +687,6 @@ inline int Hydrogen::getSelectedInstrumentNumber() const
{
return m_nSelectedInstrumentNumber;
}
-inline Song::Mode Hydrogen::getMode() const {
- return getSong()->getMode();
-}
};
#endif
diff --git a/src/core/LocalFileMgr.cpp b/src/core/LocalFileMgr.cpp
index ab43cf438a..5af1612ae0 100644
--- a/src/core/LocalFileMgr.cpp
+++ b/src/core/LocalFileMgr.cpp
@@ -424,13 +424,20 @@ int SongWriter::writeSong( std::shared_ptr pSong, const QString& filename
LocalFileMng::writeXmlString( songNode, "notes", pSong->getNotes() );
LocalFileMng::writeXmlString( songNode, "license", pSong->getLicense() );
LocalFileMng::writeXmlBool( songNode, "loopEnabled", pSong->isLoopEnabled() );
- LocalFileMng::writeXmlBool( songNode, "patternModeMode", Preferences::get_instance()->patternModePlaysSelected());
+
+ bool bPatternMode = static_cast(Song::PatternMode::Selected);
+ if ( pSong->getPatternMode() == Song::PatternMode::Stacked ) {
+ bPatternMode = static_cast(Song::PatternMode::Stacked);
+ }
+ LocalFileMng::writeXmlBool( songNode, "patternModeMode", bPatternMode );
LocalFileMng::writeXmlString( songNode, "playbackTrackFilename", QString("%1").arg( pSong->getPlaybackTrackFilename() ) );
LocalFileMng::writeXmlBool( songNode, "playbackTrackEnabled", pSong->getPlaybackTrackEnabled() );
LocalFileMng::writeXmlString( songNode, "playbackTrackVolume", QString("%1").arg( pSong->getPlaybackTrackVolume() ) );
LocalFileMng::writeXmlString( songNode, "action_mode",
QString::number( static_cast( pSong->getActionMode() ) ) );
+ LocalFileMng::writeXmlBool( songNode, "isPatternEditorLocked",
+ pSong->getIsPatternEditorLocked() );
LocalFileMng::writeXmlBool( songNode, "isTimelineActivated", pSong->getIsTimelineActivated() );
if ( pSong->getMode() == Song::Mode::Song ) {
diff --git a/src/core/Logger.cpp b/src/core/Logger.cpp
index ac8f972595..09015e54cb 100644
--- a/src/core/Logger.cpp
+++ b/src/core/Logger.cpp
@@ -178,7 +178,7 @@ void Logger::log( unsigned level, const QString& class_name, const char* func_na
void Logger::flush() const {
int nTimeout = 100;
- for ( int ii = 0; ii < nTimeout; +ii ) {
+ for ( int ii = 0; ii < nTimeout; ++ii ) {
if ( __msg_queue.empty() ) {
break;
}
diff --git a/src/core/MidiAction.cpp b/src/core/MidiAction.cpp
index 0659c05c01..f68a45cfbc 100644
--- a/src/core/MidiAction.cpp
+++ b/src/core/MidiAction.cpp
@@ -397,10 +397,10 @@ bool MidiActionManager::select_next_pattern( std::shared_ptr pAction, Hy
.arg( pHydrogen->getSong()->getPatternList()->size() - 1 ) );
return false;
}
- if(Preferences::get_instance()->patternModePlaysSelected()) {
+ if ( pHydrogen->getPatternMode() == Song::PatternMode::Selected ) {
pHydrogen->setSelectedPatternNumber( row );
}
- else {
+ else if ( pHydrogen->getPatternMode() == Song::PatternMode::Stacked ) {
pHydrogen->toggleNextPattern( row );
}
return true;
@@ -421,9 +421,8 @@ bool MidiActionManager::select_only_next_pattern( std::shared_ptr pActio
.arg( pHydrogen->getSong()->getPatternList()->size() - 1 ) );
return false;
}
- if(Preferences::get_instance()->patternModePlaysSelected())
- {
- return true;
+ if ( pHydrogen->getPatternMode() == Song::PatternMode::Selected ) {
+ return select_next_pattern( pAction, pHydrogen );
}
pHydrogen->flushAndAddNextPattern( row );
@@ -438,7 +437,7 @@ bool MidiActionManager::select_next_pattern_relative( std::shared_ptr pA
}
bool ok;
- if(!Preferences::get_instance()->patternModePlaysSelected()) {
+ if( pHydrogen->getPatternMode() == Song::PatternMode::Stacked ) {
return true;
}
int row = pHydrogen->getSelectedPatternNumber() + pAction->getParameter1().toInt(&ok,10);
@@ -470,7 +469,7 @@ bool MidiActionManager::select_next_pattern_cc_absolute( std::shared_ptr
return false;
}
- if(Preferences::get_instance()->patternModePlaysSelected()) {
+ if( pHydrogen->getPatternMode() == Song::PatternMode::Selected ) {
pHydrogen->setSelectedPatternNumber( row );
}
else {
diff --git a/src/core/Object.h b/src/core/Object.h
index 9d8abd6b3d..f7d6088c72 100644
--- a/src/core/Object.h
+++ b/src/core/Object.h
@@ -123,7 +123,7 @@ class Base {
* displayed without line breaks.
*
* \return String presentation of current object.*/
- virtual QString toQString( const QString& sPrefix, bool bShort = true ) const;
+ virtual QString toQString( const QString& sPrefix = "", bool bShort = true ) const;
/** Prints content of toQString() via DEBUGLOG
*
* \param bShort Whether to display the content of the member
diff --git a/src/core/Preferences/Preferences.cpp b/src/core/Preferences/Preferences.cpp
index f4fdf0a30d..670fd90c03 100644
--- a/src/core/Preferences/Preferences.cpp
+++ b/src/core/Preferences/Preferences.cpp
@@ -221,7 +221,6 @@ Preferences::Preferences()
m_nOscTemporaryPort = -1;
//___ General properties ___
- m_bPatternModePlaysSelected = true;
m_brestoreLastSong = true;
m_brestoreLastPlaylist = false;
m_bUseLash = false;
@@ -329,7 +328,6 @@ void Preferences::loadPreferences( bool bGlobal )
m_bShowNoteOverwriteWarning = LocalFileMng::readXmlBool( rootNode, "showNoteOverwriteWarning", m_bShowNoteOverwriteWarning );
m_brestoreLastSong = LocalFileMng::readXmlBool( rootNode, "restoreLastSong", m_brestoreLastSong );
m_brestoreLastPlaylist = LocalFileMng::readXmlBool( rootNode, "restoreLastPlaylist", m_brestoreLastPlaylist );
- m_bPatternModePlaysSelected = LocalFileMng::readXmlBool( rootNode, "patternModePlaysSelected", true );
m_bUseLash = LocalFileMng::readXmlBool( rootNode, "useLash", false );
__useTimelineBpm = LocalFileMng::readXmlBool( rootNode, "useTimeLine", __useTimelineBpm );
m_nMaxBars = LocalFileMng::readXmlInt( rootNode, "maxBars", 400 );
@@ -839,8 +837,6 @@ void Preferences::savePreferences()
LocalFileMng::writeXmlString( rootNode, "restoreLastSong", m_brestoreLastSong ? "true": "false" );
LocalFileMng::writeXmlString( rootNode, "restoreLastPlaylist", m_brestoreLastPlaylist ? "true": "false" );
- LocalFileMng::writeXmlString( rootNode, "patternModePlaysSelected", m_bPatternModePlaysSelected ? "true": "false" );
-
LocalFileMng::writeXmlString( rootNode, "useLash", m_bsetLash ? "true": "false" );
LocalFileMng::writeXmlString( rootNode, "useTimeLine", __useTimelineBpm ? "true": "false" );
diff --git a/src/core/Preferences/Preferences.h b/src/core/Preferences/Preferences.h
index c49ba19161..638fef531c 100644
--- a/src/core/Preferences/Preferences.h
+++ b/src/core/Preferences/Preferences.h
@@ -532,10 +532,6 @@ class Preferences : public H2Core::Object
const std::shared_ptr getColorTheme() const;
void setColorTheme( const std::shared_ptr pNewColorTheme );
- /** \return #m_bPatternModePlaysSelected*/
- bool patternModePlaysSelected();
- /** \param b Sets #m_bPatternModePlaysSelected*/
- void setPatternModePlaysSelected( bool b );
bool useLash();
void setUseLash( bool b );
@@ -671,15 +667,7 @@ class Preferences : public H2Core::Object
QString m_sH2ProcessName; //Name of hydrogen's main process
///rubberband bpm change queue
bool m_useTheRubberbandBpmChangeEvent;
- /**
- * When transport is in Song::PATTERN_MODE and this variable is
- * set to true, the currently focused Pattern will be used for
- * playback.
- *
- * It is set by setPatternModePlaysSelected() and queried by
- * patternModePlaysSelected().
- */
- bool m_bPatternModePlaysSelected;
+
///< Restore last song?
bool m_brestoreLastSong;
bool m_brestoreLastPlaylist;
@@ -1336,13 +1324,6 @@ inline void Preferences::setColorTheme( const std::shared_ptr pNewCo
m_pTheme->setColorTheme( pNewColorTheme );
}
-inline bool Preferences::patternModePlaysSelected() {
- return m_bPatternModePlaysSelected;
-}
-inline void Preferences::setPatternModePlaysSelected( bool b ) {
- m_bPatternModePlaysSelected = b;
-}
-
inline bool Preferences::useLash(){
return m_bUseLash;
}
diff --git a/src/core/Preferences/Theme.cpp b/src/core/Preferences/Theme.cpp
index 672036ffbf..60924da283 100644
--- a/src/core/Preferences/Theme.cpp
+++ b/src/core/Preferences/Theme.cpp
@@ -30,24 +30,37 @@ namespace H2Core
{
ColorTheme::ColorTheme()
- : m_songEditor_backgroundColor( QColor(95, 101, 117) )
- , m_songEditor_alternateRowColor( QColor(128, 134, 152) )
- , m_songEditor_selectedRowColor( QColor(128, 134, 152) )
- , m_songEditor_lineColor( QColor(72, 76, 88) )
- , m_songEditor_textColor( QColor(196, 201, 214) )
- , m_patternEditor_backgroundColor( QColor(167, 168, 163) )
- , m_patternEditor_alternateRowColor( QColor(167, 168, 163) )
- , m_patternEditor_selectedRowColor( QColor(207, 208, 200) )
- , m_patternEditor_textColor( QColor(40, 40, 40) )
- , m_patternEditor_noteColor( QColor(40, 40, 40) )
- , m_patternEditor_lineColor( QColor(65, 65, 65) )
- , m_patternEditor_line1Color( QColor(75, 75, 75) )
- , m_patternEditor_line2Color( QColor(95, 95, 95) )
- , m_patternEditor_line3Color( QColor(115, 115, 115) )
- , m_patternEditor_line4Color( QColor(125, 125, 125) )
- , m_patternEditor_line5Color( QColor(135, 135, 135) )
- , m_selectionHighlightColor( QColor(0, 0, 255) )
- , m_selectionInactiveColor( QColor(85, 85, 85) )
+ : m_songEditor_backgroundColor( QColor( 128, 134, 152 ) )
+ , m_songEditor_alternateRowColor( QColor( 106, 111, 126 ) )
+ , m_songEditor_selectedRowColor( QColor( 149, 157, 178 ) )
+ , m_songEditor_selectedRowTextColor( QColor( 0, 0, 0 ) )
+ , m_songEditor_lineColor( QColor( 54, 57, 67 ) )
+ , m_songEditor_textColor( QColor( 206, 211, 224 ) )
+ , m_songEditor_automationBackgroundColor( QColor( 83, 89, 103 ) )
+ , m_songEditor_automationLineColor( QColor( 45, 66, 89 ) )
+ , m_songEditor_automationNodeColor( QColor( 255, 255, 255 ) )
+ , m_songEditor_stackedModeOnColor( QColor( 127, 159, 127 ) )
+ , m_songEditor_stackedModeOnNextColor( QColor( 240, 223, 175 ) )
+ , m_songEditor_stackedModeOffNextColor( QColor( 247, 100, 100 ) )
+ , m_patternEditor_backgroundColor( QColor( 165, 166, 160 ) )
+ , m_patternEditor_alternateRowColor( QColor( 133, 134, 129 ) )
+ , m_patternEditor_selectedRowColor( QColor( 194, 195, 187 ) )
+ , m_patternEditor_selectedRowTextColor( QColor( 0, 0, 0 ) )
+ , m_patternEditor_octaveRowColor( QColor( 193, 194, 186 ) )
+ , m_patternEditor_textColor( QColor( 240, 240, 240 ) )
+ , m_patternEditor_noteVelocityFullColor( QColor( 247, 100, 100 ) )
+ , m_patternEditor_noteVelocityDefaultColor( QColor( 40, 40, 40 ) )
+ , m_patternEditor_noteVelocityHalfColor( QColor( 89, 131, 175 ) )
+ , m_patternEditor_noteVelocityZeroColor( QColor( 255, 255, 255 ) )
+ , m_patternEditor_noteOffColor( QColor( 0, 0, 0 ) )
+ , m_patternEditor_lineColor( QColor(45, 45, 45) )
+ , m_patternEditor_line1Color( QColor(55, 55, 55) )
+ , m_patternEditor_line2Color( QColor(75, 75, 75) )
+ , m_patternEditor_line3Color( QColor(95, 95, 95) )
+ , m_patternEditor_line4Color( QColor(105, 105, 105) )
+ , m_patternEditor_line5Color( QColor(115, 115, 115) )
+ , m_selectionHighlightColor( QColor( 255, 255, 255 ) )
+ , m_selectionInactiveColor( QColor( 199, 199, 199 ) )
, m_windowColor( QColor( 58, 62, 72 ) )
, m_windowTextColor( QColor( 255, 255, 255 ) )
, m_baseColor( QColor( 88, 94, 112 ) )
@@ -70,10 +83,10 @@ ColorTheme::ColorTheme()
, m_buttonRedTextColor( QColor( 10, 10, 10 ) )
, m_spinBoxColor( QColor( 51, 74 , 100 ) )
, m_spinBoxTextColor( QColor( 240, 240, 240 ) )
- , m_automationColor( QColor( 67, 96, 131 ) )
- , m_automationCircleColor( QColor( 255, 255, 255 ) )
, m_accentColor( QColor( 67, 96, 131 ) )
, m_accentTextColor( QColor( 255, 255, 255 ) )
+ , m_playheadColor( QColor( 0, 0, 0 ) )
+ , m_cursorColor( QColor( 38, 39, 44 ) )
{
}
@@ -81,14 +94,26 @@ ColorTheme::ColorTheme( const std::shared_ptr pOther )
: m_songEditor_backgroundColor( pOther->m_songEditor_backgroundColor )
, m_songEditor_alternateRowColor( pOther->m_songEditor_alternateRowColor )
, m_songEditor_selectedRowColor( pOther->m_songEditor_selectedRowColor )
+ , m_songEditor_selectedRowTextColor( pOther->m_songEditor_selectedRowTextColor )
, m_songEditor_lineColor( pOther->m_songEditor_lineColor )
, m_songEditor_textColor( pOther->m_songEditor_textColor )
+ , m_songEditor_automationBackgroundColor( pOther->m_songEditor_automationBackgroundColor )
+ , m_songEditor_automationLineColor( pOther->m_songEditor_automationLineColor )
+ , m_songEditor_automationNodeColor( pOther->m_songEditor_automationNodeColor )
+ , m_songEditor_stackedModeOnColor( pOther->m_songEditor_stackedModeOnColor )
+ , m_songEditor_stackedModeOnNextColor( pOther->m_songEditor_stackedModeOnNextColor )
+ , m_songEditor_stackedModeOffNextColor( pOther->m_songEditor_stackedModeOffNextColor )
, m_patternEditor_backgroundColor( pOther->m_patternEditor_backgroundColor )
, m_patternEditor_alternateRowColor( pOther->m_patternEditor_alternateRowColor )
, m_patternEditor_selectedRowColor( pOther->m_patternEditor_selectedRowColor )
+ , m_patternEditor_selectedRowTextColor( pOther->m_patternEditor_selectedRowTextColor )
+ , m_patternEditor_octaveRowColor( pOther->m_patternEditor_octaveRowColor )
, m_patternEditor_textColor( pOther->m_patternEditor_textColor )
- , m_patternEditor_noteColor( pOther->m_patternEditor_noteColor )
- , m_patternEditor_noteoffColor( pOther->m_patternEditor_noteoffColor )
+ , m_patternEditor_noteVelocityFullColor( pOther->m_patternEditor_noteVelocityFullColor )
+ , m_patternEditor_noteVelocityDefaultColor( pOther->m_patternEditor_noteVelocityDefaultColor )
+ , m_patternEditor_noteVelocityHalfColor( pOther->m_patternEditor_noteVelocityHalfColor )
+ , m_patternEditor_noteVelocityZeroColor( pOther->m_patternEditor_noteVelocityZeroColor )
+ , m_patternEditor_noteOffColor( pOther->m_patternEditor_noteOffColor )
, m_patternEditor_lineColor( pOther->m_patternEditor_lineColor )
, m_patternEditor_line1Color( pOther->m_patternEditor_line1Color )
, m_patternEditor_line2Color( pOther->m_patternEditor_line2Color )
@@ -121,8 +146,8 @@ ColorTheme::ColorTheme( const std::shared_ptr pOther )
, m_buttonRedTextColor( pOther->m_buttonRedTextColor )
, m_spinBoxColor( pOther->m_spinBoxColor )
, m_spinBoxTextColor( pOther->m_spinBoxTextColor )
- , m_automationColor( pOther->m_automationColor )
- , m_automationCircleColor( pOther->m_automationCircleColor )
+ , m_playheadColor( pOther->m_playheadColor )
+ , m_cursorColor( pOther->m_cursorColor )
{
}
@@ -137,7 +162,7 @@ InterfaceTheme::InterfaceTheme()
, m_scalingPolicy( InterfaceTheme::ScalingPolicy::Smaller )
, m_iconColor( InterfaceTheme::IconColor::Black )
, m_coloringMethod( InterfaceTheme::ColoringMethod::Custom )
- , m_nVisiblePatternColors( 1 )
+ , m_nVisiblePatternColors( 18 )
, m_nMaxPatternColors( 50 ) {
std::vector m_patternColors( m_nMaxPatternColors );
for ( int ii = 0; ii < m_nMaxPatternColors; ii++ ) {
@@ -190,15 +215,43 @@ void Theme::setTheme( const std::shared_ptr pOther ) {
DEBUGLOG("");
m_pColorTheme->m_songEditor_backgroundColor = pOther->getColorTheme()->m_songEditor_backgroundColor;
m_pColorTheme->m_songEditor_alternateRowColor = pOther->getColorTheme()->m_songEditor_alternateRowColor;
- m_pColorTheme->m_songEditor_selectedRowColor = pOther->getColorTheme()->m_songEditor_selectedRowColor;
+ m_pColorTheme->m_songEditor_selectedRowColor =
+ pOther->getColorTheme()->m_songEditor_selectedRowColor;
+ m_pColorTheme->m_songEditor_selectedRowTextColor =
+ pOther->getColorTheme()->m_songEditor_selectedRowTextColor;
m_pColorTheme->m_songEditor_lineColor = pOther->getColorTheme()->m_songEditor_lineColor;
m_pColorTheme->m_songEditor_textColor = pOther->getColorTheme()->m_songEditor_textColor;
+ m_pColorTheme->m_songEditor_automationBackgroundColor =
+ pOther->getColorTheme()->m_songEditor_automationBackgroundColor;
+ m_pColorTheme->m_songEditor_automationLineColor =
+ pOther->getColorTheme()->m_songEditor_automationLineColor;
+ m_pColorTheme->m_songEditor_automationNodeColor =
+ pOther->getColorTheme()->m_songEditor_automationNodeColor;
+ m_pColorTheme->m_songEditor_stackedModeOnColor =
+ pOther->getColorTheme()->m_songEditor_stackedModeOnColor;
+ m_pColorTheme->m_songEditor_stackedModeOnNextColor =
+ pOther->getColorTheme()->m_songEditor_stackedModeOnNextColor;
+ m_pColorTheme->m_songEditor_stackedModeOffNextColor =
+ pOther->getColorTheme()->m_songEditor_stackedModeOffNextColor;
m_pColorTheme->m_patternEditor_backgroundColor = pOther->getColorTheme()->m_patternEditor_backgroundColor;
m_pColorTheme->m_patternEditor_alternateRowColor = pOther->getColorTheme()->m_patternEditor_alternateRowColor;
- m_pColorTheme->m_patternEditor_selectedRowColor = pOther->getColorTheme()->m_patternEditor_selectedRowColor;
+ m_pColorTheme->m_patternEditor_selectedRowColor =
+ pOther->getColorTheme()->m_patternEditor_selectedRowColor;
+ m_pColorTheme->m_patternEditor_selectedRowTextColor =
+ pOther->getColorTheme()->m_patternEditor_selectedRowTextColor;
+ m_pColorTheme->m_patternEditor_octaveRowColor =
+ pOther->getColorTheme()->m_patternEditor_octaveRowColor;
m_pColorTheme->m_patternEditor_textColor = pOther->getColorTheme()->m_patternEditor_textColor;
- m_pColorTheme->m_patternEditor_noteColor = pOther->getColorTheme()->m_patternEditor_noteColor;
- m_pColorTheme->m_patternEditor_noteoffColor = pOther->getColorTheme()->m_patternEditor_noteoffColor;
+ m_pColorTheme->m_patternEditor_noteVelocityFullColor =
+ pOther->getColorTheme()->m_patternEditor_noteVelocityFullColor;
+ m_pColorTheme->m_patternEditor_noteVelocityDefaultColor =
+ pOther->getColorTheme()->m_patternEditor_noteVelocityDefaultColor;
+ m_pColorTheme->m_patternEditor_noteVelocityHalfColor =
+ pOther->getColorTheme()->m_patternEditor_noteVelocityHalfColor;
+ m_pColorTheme->m_patternEditor_noteVelocityZeroColor =
+ pOther->getColorTheme()->m_patternEditor_noteVelocityZeroColor;
+ m_pColorTheme->m_patternEditor_noteOffColor =
+ pOther->getColorTheme()->m_patternEditor_noteOffColor;
m_pColorTheme->m_patternEditor_lineColor = pOther->getColorTheme()->m_patternEditor_lineColor;
m_pColorTheme->m_patternEditor_line1Color = pOther->getColorTheme()->m_patternEditor_line1Color;
m_pColorTheme->m_patternEditor_line2Color = pOther->getColorTheme()->m_patternEditor_line2Color;
@@ -231,8 +284,8 @@ void Theme::setTheme( const std::shared_ptr pOther ) {
m_pColorTheme->m_buttonRedTextColor = pOther->getColorTheme()->m_buttonRedTextColor;
m_pColorTheme->m_spinBoxColor = pOther->getColorTheme()->m_spinBoxColor;
m_pColorTheme->m_spinBoxTextColor = pOther->getColorTheme()->m_spinBoxTextColor;
- m_pColorTheme->m_automationColor = pOther->getColorTheme()->m_automationColor;
- m_pColorTheme->m_automationCircleColor = pOther->getColorTheme()->m_automationCircleColor;
+ m_pColorTheme->m_playheadColor = pOther->getColorTheme()->m_playheadColor;
+ m_pColorTheme->m_cursorColor = pOther->getColorTheme()->m_cursorColor;
m_pInterfaceTheme->m_sQTStyle = pOther->getInterfaceTheme()->m_sQTStyle;
m_pInterfaceTheme->m_fMixerFalloffSpeed = pOther->getInterfaceTheme()->m_fMixerFalloffSpeed;
@@ -264,19 +317,46 @@ void Theme::writeColorTheme( QDomNode* parent, std::shared_ptr pTheme )
QDomNode songEditorNode = doc.createElement( "songEditor" );
LocalFileMng::writeXmlColor( songEditorNode, "backgroundColor", pTheme->getColorTheme()->m_songEditor_backgroundColor );
LocalFileMng::writeXmlColor( songEditorNode, "alternateRowColor", pTheme->getColorTheme()->m_songEditor_alternateRowColor );
- LocalFileMng::writeXmlColor( songEditorNode, "selectedRowColor", pTheme->getColorTheme()->m_songEditor_selectedRowColor );
+ LocalFileMng::writeXmlColor( songEditorNode, "selectedRowColor",
+ pTheme->getColorTheme()->m_songEditor_selectedRowColor );
+ LocalFileMng::writeXmlColor( songEditorNode, "selectedRowTextColor",
+ pTheme->getColorTheme()->m_songEditor_selectedRowTextColor );
LocalFileMng::writeXmlColor( songEditorNode, "lineColor", pTheme->getColorTheme()->m_songEditor_lineColor );
LocalFileMng::writeXmlColor( songEditorNode, "textColor", pTheme->getColorTheme()->m_songEditor_textColor );
+ LocalFileMng::writeXmlColor( songEditorNode, "automationBackgroundColor",
+ pTheme->getColorTheme()->m_songEditor_automationBackgroundColor );
+ LocalFileMng::writeXmlColor( songEditorNode, "automationLineColor",
+ pTheme->getColorTheme()->m_songEditor_automationLineColor );
+ LocalFileMng::writeXmlColor( songEditorNode, "automationNodeColor",
+ pTheme->getColorTheme()->m_songEditor_automationNodeColor );
+ LocalFileMng::writeXmlColor( songEditorNode, "stackedModeOnColor",
+ pTheme->getColorTheme()->m_songEditor_stackedModeOnColor );
+ LocalFileMng::writeXmlColor( songEditorNode, "stackedModeOnNextColor",
+ pTheme->getColorTheme()->m_songEditor_stackedModeOnNextColor );
+ LocalFileMng::writeXmlColor( songEditorNode, "stackedModeOffNextColor",
+ pTheme->getColorTheme()->m_songEditor_stackedModeOffNextColor );
node.appendChild( songEditorNode );
// PATTERN EDITOR
QDomNode patternEditorNode = doc.createElement( "patternEditor" );
LocalFileMng::writeXmlColor( patternEditorNode, "backgroundColor", pTheme->getColorTheme()->m_patternEditor_backgroundColor );
LocalFileMng::writeXmlColor( patternEditorNode, "alternateRowColor", pTheme->getColorTheme()->m_patternEditor_alternateRowColor );
- LocalFileMng::writeXmlColor( patternEditorNode, "selectedRowColor", pTheme->getColorTheme()->m_patternEditor_selectedRowColor );
+ LocalFileMng::writeXmlColor( patternEditorNode, "selectedRowColor",
+ pTheme->getColorTheme()->m_patternEditor_selectedRowColor );
+ LocalFileMng::writeXmlColor( patternEditorNode, "selectedRowTextColor",
+ pTheme->getColorTheme()->m_patternEditor_selectedRowTextColor );
+ LocalFileMng::writeXmlColor( patternEditorNode, "octaveRowColor",
+ pTheme->getColorTheme()->m_patternEditor_octaveRowColor );
LocalFileMng::writeXmlColor( patternEditorNode, "textColor", pTheme->getColorTheme()->m_patternEditor_textColor );
- LocalFileMng::writeXmlColor( patternEditorNode, "noteColor", pTheme->getColorTheme()->m_patternEditor_noteColor );
- LocalFileMng::writeXmlColor( patternEditorNode, "noteoffColor", pTheme->getColorTheme()->m_patternEditor_noteoffColor );
+ LocalFileMng::writeXmlColor( patternEditorNode, "noteVelocityFullColor",
+ pTheme->getColorTheme()->m_patternEditor_noteVelocityFullColor );
+ LocalFileMng::writeXmlColor( patternEditorNode, "noteVelocityDefaultColor",
+ pTheme->getColorTheme()->m_patternEditor_noteVelocityDefaultColor );
+ LocalFileMng::writeXmlColor( patternEditorNode, "noteVelocityHalfColor",
+ pTheme->getColorTheme()->m_patternEditor_noteVelocityHalfColor );
+ LocalFileMng::writeXmlColor( patternEditorNode, "noteVelocityZeroColor",
+ pTheme->getColorTheme()->m_patternEditor_noteVelocityZeroColor );
+ LocalFileMng::writeXmlColor( patternEditorNode, "noteOffColor", pTheme->getColorTheme()->m_patternEditor_noteOffColor );
LocalFileMng::writeXmlColor( patternEditorNode, "lineColor", pTheme->getColorTheme()->m_patternEditor_lineColor );
LocalFileMng::writeXmlColor( patternEditorNode, "line1Color", pTheme->getColorTheme()->m_patternEditor_line1Color );
@@ -319,8 +399,8 @@ void Theme::writeColorTheme( QDomNode* parent, std::shared_ptr pTheme )
LocalFileMng::writeXmlColor( widgetNode, "buttonRedTextColor", pTheme->getColorTheme()->m_buttonRedTextColor );
LocalFileMng::writeXmlColor( widgetNode, "spinBoxColor", pTheme->getColorTheme()->m_spinBoxColor );
LocalFileMng::writeXmlColor( widgetNode, "spinBoxTextColor", pTheme->getColorTheme()->m_spinBoxTextColor );
- LocalFileMng::writeXmlColor( widgetNode, "automationColor", pTheme->getColorTheme()->m_automationColor );
- LocalFileMng::writeXmlColor( widgetNode, "automationCircleColor", pTheme->getColorTheme()->m_automationCircleColor );
+ LocalFileMng::writeXmlColor( widgetNode, "playheadColor", pTheme->getColorTheme()->m_playheadColor );
+ LocalFileMng::writeXmlColor( widgetNode, "cursorColor", pTheme->getColorTheme()->m_cursorColor );
node.appendChild( widgetNode );
parent->appendChild( node );
@@ -333,9 +413,32 @@ void Theme::readColorTheme( QDomNode parent, std::shared_ptr pTheme )
if ( !pSongEditorNode.isNull() ) {
pTheme->getColorTheme()->m_songEditor_backgroundColor = LocalFileMng::readXmlColor( pSongEditorNode, "backgroundColor", pTheme->getColorTheme()->m_songEditor_backgroundColor );
pTheme->getColorTheme()->m_songEditor_alternateRowColor = LocalFileMng::readXmlColor( pSongEditorNode, "alternateRowColor", pTheme->getColorTheme()->m_songEditor_alternateRowColor );
- pTheme->getColorTheme()->m_songEditor_selectedRowColor = LocalFileMng::readXmlColor( pSongEditorNode, "selectedRowColor", pTheme->getColorTheme()->m_songEditor_selectedRowColor );
+ pTheme->getColorTheme()->m_songEditor_selectedRowColor =
+ LocalFileMng::readXmlColor( pSongEditorNode, "selectedRowColor",
+ pTheme->getColorTheme()->m_songEditor_selectedRowColor );
+ pTheme->getColorTheme()->m_songEditor_selectedRowTextColor =
+ LocalFileMng::readXmlColor( pSongEditorNode, "selectedRowTextColor",
+ pTheme->getColorTheme()->m_songEditor_selectedRowTextColor );
pTheme->getColorTheme()->m_songEditor_lineColor = LocalFileMng::readXmlColor( pSongEditorNode, "lineColor", pTheme->getColorTheme()->m_songEditor_lineColor );
pTheme->getColorTheme()->m_songEditor_textColor = LocalFileMng::readXmlColor( pSongEditorNode, "textColor", pTheme->getColorTheme()->m_songEditor_textColor );
+ pTheme->getColorTheme()->m_songEditor_automationBackgroundColor =
+ LocalFileMng::readXmlColor( pSongEditorNode, "automationBackgroundColor",
+ pTheme->getColorTheme()->m_songEditor_automationBackgroundColor );
+ pTheme->getColorTheme()->m_songEditor_automationLineColor =
+ LocalFileMng::readXmlColor( pSongEditorNode, "automationLineColor",
+ pTheme->getColorTheme()->m_songEditor_automationLineColor );
+ pTheme->getColorTheme()->m_songEditor_automationNodeColor =
+ LocalFileMng::readXmlColor( pSongEditorNode, "automationNodeColor",
+ pTheme->getColorTheme()->m_songEditor_automationNodeColor );
+ pTheme->getColorTheme()->m_songEditor_stackedModeOnColor =
+ LocalFileMng::readXmlColor( pSongEditorNode, "stackedModeOnColor",
+ pTheme->getColorTheme()->m_songEditor_stackedModeOnColor );
+ pTheme->getColorTheme()->m_songEditor_stackedModeOnNextColor =
+ LocalFileMng::readXmlColor( pSongEditorNode, "stackedModeOnNextColor",
+ pTheme->getColorTheme()->m_songEditor_stackedModeOnNextColor );
+ pTheme->getColorTheme()->m_songEditor_stackedModeOffNextColor =
+ LocalFileMng::readXmlColor( pSongEditorNode, "stackedModeOffNextColor",
+ pTheme->getColorTheme()->m_songEditor_stackedModeOffNextColor );
} else {
WARNINGLOG( "songEditor node not found" );
}
@@ -345,10 +448,31 @@ void Theme::readColorTheme( QDomNode parent, std::shared_ptr pTheme )
if ( !pPatternEditorNode.isNull() ) {
pTheme->getColorTheme()->m_patternEditor_backgroundColor = LocalFileMng::readXmlColor( pPatternEditorNode, "backgroundColor", pTheme->getColorTheme()->m_patternEditor_backgroundColor );
pTheme->getColorTheme()->m_patternEditor_alternateRowColor = LocalFileMng::readXmlColor( pPatternEditorNode, "alternateRowColor", pTheme->getColorTheme()->m_patternEditor_alternateRowColor );
- pTheme->getColorTheme()->m_patternEditor_selectedRowColor = LocalFileMng::readXmlColor( pPatternEditorNode, "selectedRowColor", pTheme->getColorTheme()->m_patternEditor_selectedRowColor );
+ pTheme->getColorTheme()->m_patternEditor_selectedRowColor =
+ LocalFileMng::readXmlColor( pPatternEditorNode, "selectedRowColor",
+ pTheme->getColorTheme()->m_patternEditor_selectedRowColor );
+ pTheme->getColorTheme()->m_patternEditor_selectedRowTextColor =
+ LocalFileMng::readXmlColor( pPatternEditorNode, "selectedRowTextColor",
+ pTheme->getColorTheme()->m_patternEditor_selectedRowTextColor );
+ pTheme->getColorTheme()->m_patternEditor_octaveRowColor =
+ LocalFileMng::readXmlColor( pPatternEditorNode, "octaveRowColor",
+ pTheme->getColorTheme()->m_patternEditor_octaveRowColor );
pTheme->getColorTheme()->m_patternEditor_textColor = LocalFileMng::readXmlColor( pPatternEditorNode, "textColor", pTheme->getColorTheme()->m_patternEditor_textColor );
- pTheme->getColorTheme()->m_patternEditor_noteColor = LocalFileMng::readXmlColor( pPatternEditorNode, "noteColor", pTheme->getColorTheme()->m_patternEditor_noteColor );
- pTheme->getColorTheme()->m_patternEditor_noteoffColor = LocalFileMng::readXmlColor( pPatternEditorNode, "noteoffColor", pTheme->getColorTheme()->m_patternEditor_noteoffColor );
+ pTheme->getColorTheme()->m_patternEditor_noteVelocityFullColor =
+ LocalFileMng::readXmlColor( pPatternEditorNode, "noteVelocityFullColor",
+ pTheme->getColorTheme()->m_patternEditor_noteVelocityFullColor );
+ pTheme->getColorTheme()->m_patternEditor_noteVelocityDefaultColor =
+ LocalFileMng::readXmlColor( pPatternEditorNode, "noteVelocityDefaultColor",
+ pTheme->getColorTheme()->m_patternEditor_noteVelocityDefaultColor );
+ pTheme->getColorTheme()->m_patternEditor_noteVelocityHalfColor =
+ LocalFileMng::readXmlColor( pPatternEditorNode, "noteVelocityHalfColor",
+ pTheme->getColorTheme()->m_patternEditor_noteVelocityHalfColor );
+ pTheme->getColorTheme()->m_patternEditor_noteVelocityZeroColor =
+ LocalFileMng::readXmlColor( pPatternEditorNode, "noteVelocityZeroColor",
+ pTheme->getColorTheme()->m_patternEditor_noteVelocityZeroColor );
+ pTheme->getColorTheme()->m_patternEditor_noteOffColor =
+ LocalFileMng::readXmlColor( pPatternEditorNode, "noteOffColor",
+ pTheme->getColorTheme()->m_patternEditor_noteOffColor );
pTheme->getColorTheme()->m_patternEditor_lineColor = LocalFileMng::readXmlColor( pPatternEditorNode, "lineColor", pTheme->getColorTheme()->m_patternEditor_lineColor );
pTheme->getColorTheme()->m_patternEditor_line1Color = LocalFileMng::readXmlColor( pPatternEditorNode, "line1Color", pTheme->getColorTheme()->m_patternEditor_line1Color );
pTheme->getColorTheme()->m_patternEditor_line2Color = LocalFileMng::readXmlColor( pPatternEditorNode, "line2Color", pTheme->getColorTheme()->m_patternEditor_line2Color );
@@ -399,8 +523,12 @@ void Theme::readColorTheme( QDomNode parent, std::shared_ptr pTheme )
pTheme->getColorTheme()->m_buttonRedTextColor = LocalFileMng::readXmlColor( pWidgetNode, "buttonRedTextColor", pTheme->getColorTheme()->m_buttonRedTextColor );
pTheme->getColorTheme()->m_spinBoxColor = LocalFileMng::readXmlColor( pWidgetNode, "spinBoxColor", pTheme->getColorTheme()->m_spinBoxColor );
pTheme->getColorTheme()->m_spinBoxTextColor = LocalFileMng::readXmlColor( pWidgetNode, "spinBoxTextColor", pTheme->getColorTheme()->m_spinBoxTextColor );
- pTheme->getColorTheme()->m_automationColor = LocalFileMng::readXmlColor( pWidgetNode, "automationColor", pTheme->getColorTheme()->m_automationColor );
- pTheme->getColorTheme()->m_automationCircleColor = LocalFileMng::readXmlColor( pWidgetNode, "automationCircleColor", pTheme->getColorTheme()->m_automationCircleColor );
+ pTheme->getColorTheme()->m_playheadColor =
+ LocalFileMng::readXmlColor( pWidgetNode, "playheadColor",
+ pTheme->getColorTheme()->m_playheadColor );
+ pTheme->getColorTheme()->m_cursorColor =
+ LocalFileMng::readXmlColor( pWidgetNode, "cursorColor",
+ pTheme->getColorTheme()->m_cursorColor );
} else {
WARNINGLOG( "widget node not found" );
}
diff --git a/src/core/Preferences/Theme.h b/src/core/Preferences/Theme.h
index 0267d10512..8350484aac 100644
--- a/src/core/Preferences/Theme.h
+++ b/src/core/Preferences/Theme.h
@@ -49,15 +49,27 @@ class ColorTheme : public H2Core::Object
QColor m_songEditor_backgroundColor;
QColor m_songEditor_alternateRowColor;
QColor m_songEditor_selectedRowColor;
+ QColor m_songEditor_selectedRowTextColor;
QColor m_songEditor_lineColor;
QColor m_songEditor_textColor;
+ QColor m_songEditor_automationBackgroundColor;
+ QColor m_songEditor_automationLineColor;
+ QColor m_songEditor_automationNodeColor;
+ QColor m_songEditor_stackedModeOnColor;
+ QColor m_songEditor_stackedModeOnNextColor;
+ QColor m_songEditor_stackedModeOffNextColor;
QColor m_patternEditor_backgroundColor;
QColor m_patternEditor_alternateRowColor;
QColor m_patternEditor_selectedRowColor;
+ QColor m_patternEditor_selectedRowTextColor;
+ QColor m_patternEditor_octaveRowColor;
QColor m_patternEditor_textColor;
- QColor m_patternEditor_noteColor;
- QColor m_patternEditor_noteoffColor;
+ QColor m_patternEditor_noteVelocityFullColor;
+ QColor m_patternEditor_noteVelocityDefaultColor;
+ QColor m_patternEditor_noteVelocityHalfColor;
+ QColor m_patternEditor_noteVelocityZeroColor;
+ QColor m_patternEditor_noteOffColor;
QColor m_patternEditor_lineColor;
QColor m_patternEditor_line1Color;
QColor m_patternEditor_line2Color;
@@ -109,8 +121,8 @@ class ColorTheme : public H2Core::Object
QColor m_buttonRedTextColor;
QColor m_spinBoxColor;
QColor m_spinBoxTextColor;
- QColor m_automationColor;
- QColor m_automationCircleColor;
+ QColor m_playheadColor;
+ QColor m_cursorColor;
};
diff --git a/src/core/Timeline.cpp b/src/core/Timeline.cpp
index ddd26552d2..703b70e866 100644
--- a/src/core/Timeline.cpp
+++ b/src/core/Timeline.cpp
@@ -127,6 +127,14 @@ bool Timeline::hasColumnTempoMarker( int nColumn ) const {
}
std::shared_ptr Timeline::getTempoMarkerAtColumn( int nColumn ) const {
+ if ( isFirstTempoMarkerSpecial() && nColumn == 0 ) {
+
+ std::shared_ptr pTempoMarker = std::make_shared();
+ pTempoMarker->nColumn = 0;
+ pTempoMarker->fBpm = Hydrogen::get_instance()->getSong()->getBpm();
+ return pTempoMarker;
+ }
+
for ( const auto& tt : m_tempoMarkers ){
if ( tt->nColumn == nColumn ) {
return tt;
@@ -264,5 +272,39 @@ QString Timeline::toQString( const QString& sPrefix, bool bShort ) const {
return sOutput;
}
+QString Timeline::TempoMarker::toQString( const QString& sPrefix, bool bShort ) const {
+ QString s = Base::sPrintIndention;
+ QString sOutput;
+ if ( ! bShort ) {
+ sOutput = QString( "%1[TempoMarker]\n" ).arg( sPrefix )
+ .append( QString( "%1%2nColumn: %3\n" ).arg( sPrefix ).arg( s ).arg( nColumn ) )
+ .append( QString( "%1%2fBpm: %3\n" ).arg( sPrefix ).arg( s ).arg( fBpm ) );
+ } else {
+
+ sOutput = QString( "%1[TempoMarker] " ).arg( sPrefix )
+ .append( QString( "nColumn: %3, " ).arg( nColumn ) )
+ .append( QString( "fBpm: %3" ).arg( fBpm ) );
+ }
+
+ return sOutput;
+}
+
+QString Timeline::Tag::toQString( const QString& sPrefix, bool bShort ) const {
+ QString s = Base::sPrintIndention;
+ QString sOutput;
+ if ( ! bShort ) {
+ sOutput = QString( "%1[TempoMarker]\n" ).arg( sPrefix )
+ .append( QString( "%1%2nColumn: %3\n" ).arg( sPrefix ).arg( s ).arg( nColumn ) )
+ .append( QString( "%1%2sTag: %3\n" ).arg( sPrefix ).arg( s ).arg( sTag ) );
+ } else {
+
+ sOutput = QString( "%1[TempoMarker] " ).arg( sPrefix )
+ .append( QString( "nColumn: %3, " ).arg( nColumn ) )
+ .append( QString( "sTag: %3" ).arg( sTag ) );
+ }
+
+ return sOutput;
+}
+
};
diff --git a/src/core/Timeline.h b/src/core/Timeline.h
index 4768cd7c30..03be122691 100644
--- a/src/core/Timeline.h
+++ b/src/core/Timeline.h
@@ -80,6 +80,8 @@ class Timeline : public H2Core::Object
{
int nColumn; // beat position in timeline
float fBpm; // tempo in beats per minute
+
+ QString toQString( const QString& sPrefix = "", bool bShort = true ) const;
};
/**
@@ -90,6 +92,8 @@ class Timeline : public H2Core::Object
{
int nColumn; // beat position in timeline
QString sTag; // tag
+
+ QString toQString( const QString& sPrefix = "", bool bShort = true ) const;
};
/**
diff --git a/src/gui/src/CommonStrings.cpp b/src/gui/src/CommonStrings.cpp
index 91ad81b38d..180d45ee50 100644
--- a/src/gui/src/CommonStrings.cpp
+++ b/src/gui/src/CommonStrings.cpp
@@ -337,6 +337,7 @@ CommonStrings::CommonStrings(){
m_sTimelineEnabled = tr( "Enable the Timeline for custom tempo changes" );
m_sTimelineDisabledPatternMode = tr( "The Timeline is only available in Song Mode" );
m_sTimelineDisabledTimebaseSlave = tr( "In the presence of an external JACK Timebase master the tempo can not be altered from within Hydrogen" );
+ m_sPatternEditorLocked = tr( "Lock the Pattern Editor to only show and follow the pattern recorded notes will be inserted into while in Song Mode." );
/*: Displayed in the Preferences dialog in the info section for a
particular driver in case it is not properly supported on the
diff --git a/src/gui/src/CommonStrings.h b/src/gui/src/CommonStrings.h
index ab666a2093..6c4ceec4ef 100644
--- a/src/gui/src/CommonStrings.h
+++ b/src/gui/src/CommonStrings.h
@@ -118,10 +118,10 @@ class CommonStrings : public H2Core::Object {
const QString& getMidiTooltipHeading() const { return m_sMidiTooltipHeading; }
const QString& getMidiTooltipBound() const { return m_sMidiTooltipBound; }
const QString& getMidiTooltipUnbound() const { return m_sMidiTooltipUnbound; }
- const QString& getPatternSizeDisabledTooltip() const { return m_sPatternSizeDisabledTooltip; }
const QString& getShowDrumkitEditorTooltip() const { return m_sShowDrumkitEditorTooltip; }
const QString& getShowPianoRollEditorTooltip() const { return m_sShowPianoRollEditorTooltip; }
+ const QString& getPatternSizeDisabledTooltip() const { return m_sPatternSizeDisabledTooltip; }
const QString& getMidiSenseWindowTitle() const { return m_sMidiSenseWindowTitle; }
const QString& getMidiSenseInput() const { return m_sMidiSenseInput; }
@@ -137,6 +137,7 @@ class CommonStrings : public H2Core::Object {
const QString& getTimelineEnabled() const { return m_sTimelineEnabled; }
const QString& getTimelineDisabledPatternMode() const { return m_sTimelineDisabledPatternMode; }
const QString& getTimelineDisabledTimebaseSlave() const { return m_sTimelineDisabledTimebaseSlave; }
+ const QString& getPatternEditorLocked() const { return m_sPatternEditorLocked; }
const QString& getPreferencesNotCompiled() const { return m_sPreferencesNotCompiled; }
const QString& getPreferencesNone() const { return m_sPreferencesNone; }
@@ -247,6 +248,7 @@ class CommonStrings : public H2Core::Object {
QString m_sTimelineEnabled;
QString m_sTimelineDisabledPatternMode;
QString m_sTimelineDisabledTimebaseSlave;
+ QString m_sPatternEditorLocked;
QString m_sPreferencesNotCompiled;
QString m_sPreferencesNone;
diff --git a/src/gui/src/EventListener.h b/src/gui/src/EventListener.h
index aaf61d6c34..6869844cca 100644
--- a/src/gui/src/EventListener.h
+++ b/src/gui/src/EventListener.h
@@ -58,6 +58,9 @@ class EventListener
virtual void actionModeChangeEvent( int nValue ){ UNUSED( nValue ); }
virtual void updateSongEditorEvent( int nValue ){ UNUSED( nValue ); }
virtual void drumkitLoadedEvent(){}
+ virtual void patternEditorLockedEvent( int nValue ){ UNUSED( nValue ); }
+ virtual void relocationEvent(){}
+ virtual void songSizeChangedEvent(){}
virtual ~EventListener() {}
};
diff --git a/src/gui/src/HydrogenApp.cpp b/src/gui/src/HydrogenApp.cpp
index 1dfe898a7f..3e155278b0 100644
--- a/src/gui/src/HydrogenApp.cpp
+++ b/src/gui/src/HydrogenApp.cpp
@@ -258,6 +258,9 @@ void HydrogenApp::setupSinglePanedInterface()
} else {
m_pSongEditorPanel = new SongEditorPanel( m_pTab );
}
+ // trigger a relocation to sync the transport position of the
+ // editors in the panel.
+ H2Core::Hydrogen::get_instance()->getCoreActionController()->locateToColumn( 0 );
WindowProperties songEditorProp = pPref->getSongEditorProperties();
setWindowProperties( m_pSongEditorPanel, songEditorProp, SetWidth + SetHeight );
@@ -287,6 +290,8 @@ void HydrogenApp::setupSinglePanedInterface()
// PATTERN EDITOR
m_pPatternEditorPanel = new PatternEditorPanel( nullptr );
+ // Sync the playhead position in all editors all objects are available.
+ m_pPatternEditorPanel->getPatternEditorRuler()->updatePosition( true );
WindowProperties patternEditorProp = pPref->getPatternEditorProperties();
setWindowProperties( m_pPatternEditorPanel, patternEditorProp, SetWidth + SetHeight );
@@ -835,7 +840,19 @@ void HydrogenApp::onEventQueueTimer()
case EVENT_DRUMKIT_LOADED:
pListener->drumkitLoadedEvent();
break;
+
+ case EVENT_PATTERN_EDITOR_LOCKED:
+ pListener->patternEditorLockedEvent( event.value );
+ break;
+
+ case EVENT_RELOCATION:
+ pListener->relocationEvent();
+ break;
+ case EVENT_SONG_SIZE_CHANGED:
+ pListener->songSizeChangedEvent();
+ break;
+
default:
ERRORLOG( QString("[onEventQueueTimer] Unhandled event: %1").arg( event.type ) );
}
diff --git a/src/gui/src/InstrumentEditor/InstrumentEditor.cpp b/src/gui/src/InstrumentEditor/InstrumentEditor.cpp
index 72ee07c12e..2cd800fe2d 100644
--- a/src/gui/src/InstrumentEditor/InstrumentEditor.cpp
+++ b/src/gui/src/InstrumentEditor/InstrumentEditor.cpp
@@ -91,8 +91,9 @@ InstrumentEditor::InstrumentEditor( QWidget* pParent )
m_pInstrumentProp->move(0, 31);
m_pInstrumentProp->setPixmap( "/instrumentEditor/instrumentTab.png" );
- m_pNameLbl = new ClickableLabel( m_pInstrumentProp, QSize( 275, 28 ), "" );
- m_pNameLbl->move( 8, 5 );
+ m_pNameLbl = new ClickableLabel( m_pInstrumentProp, QSize( 279, 27 ), "",
+ ClickableLabel::Color::Bright, true, true );
+ m_pNameLbl->move( 5, 4 );
m_pNameLbl->setScaledContents( true );
/////////////
diff --git a/src/gui/src/InstrumentEditor/WaveDisplay.cpp b/src/gui/src/InstrumentEditor/WaveDisplay.cpp
index 06061a8e3e..d457d45a18 100644
--- a/src/gui/src/InstrumentEditor/WaveDisplay.cpp
+++ b/src/gui/src/InstrumentEditor/WaveDisplay.cpp
@@ -60,42 +60,45 @@ WaveDisplay::~WaveDisplay()
delete[] m_pPeakData;
}
-void WaveDisplay::paintEvent( QPaintEvent *ev )
-{
+void WaveDisplay::paintEvent( QPaintEvent *ev ) {
UNUSED(ev);
-
+ QPainter painter( this );
+
+ createBackground( &painter );
+}
+
+void WaveDisplay::createBackground( QPainter* painter ) {
auto pPref = H2Core::Preferences::get_instance();
- QPainter painter( this );
- painter.setRenderHint( QPainter::Antialiasing );
+ painter->setRenderHint( QPainter::Antialiasing );
QBrush brush = QBrush(Qt::red, m_Background);
brush.setStyle(Qt::TexturePattern);
- painter.setBrush(brush);
- painter.drawRect(0, 0, width(), height());
+ painter->setBrush(brush);
+ painter->drawRect(0, 0, width(), height());
if( m_pLayer ){
- painter.setPen( QColor( 102, 150, 205 ) );
+ painter->setPen( QColor( 102, 150, 205 ) );
int VCenter = height() / 2;
for ( int x = 0; x < width(); x++ ) {
- painter.drawLine( x, VCenter, x, m_pPeakData[x] + VCenter );
- painter.drawLine( x, VCenter, x, -m_pPeakData[x] + VCenter );
+ painter->drawLine( x, VCenter, x, m_pPeakData[x] + VCenter );
+ painter->drawLine( x, VCenter, x, -m_pPeakData[x] + VCenter );
}
}
QFont font( pPref->getApplicationFontFamily(), getPointSize( pPref->getFontSize() ) );
font.setWeight( 63 );
- painter.setFont( font );
- painter.setPen( QColor( 255 , 255, 255, 200 ) );
+ painter->setFont( font );
+ painter->setPen( QColor( 255 , 255, 255, 200 ) );
if( m_SampleNameAlignment == Qt::AlignCenter ){
- painter.drawText( 0, 0, width(), 20, m_SampleNameAlignment, m_sSampleName );
+ painter->drawText( 0, 0, width(), 20, m_SampleNameAlignment, m_sSampleName );
}
else if( m_SampleNameAlignment == Qt::AlignLeft )
{
// Use a small offnset iso. starting directly at the left border
- painter.drawText( 20, 0, width(), 20, m_SampleNameAlignment, m_sSampleName );
+ painter->drawText( 20, 0, width(), 20, m_SampleNameAlignment, m_sSampleName );
}
}
diff --git a/src/gui/src/InstrumentEditor/WaveDisplay.h b/src/gui/src/InstrumentEditor/WaveDisplay.h
index 9e69bf6b95..5b49933ff6 100644
--- a/src/gui/src/InstrumentEditor/WaveDisplay.h
+++ b/src/gui/src/InstrumentEditor/WaveDisplay.h
@@ -45,6 +45,7 @@ class WaveDisplay : public QWidget, protected WidgetWithScalableFont<8, 10, 12>
explicit WaveDisplay(QWidget* pParent);
~WaveDisplay();
+
virtual void updateDisplay( std::shared_ptr pLayer );
virtual void paintEvent( QPaintEvent *ev ) override;
@@ -60,6 +61,9 @@ public slots:
void doubleClicked(QWidget *pWidget);
protected:
+
+ void createBackground( QPainter* painter );
+
Qt::AlignmentFlag m_SampleNameAlignment;
QPixmap m_Background;
QString m_sSampleName;
diff --git a/src/gui/src/MainForm.cpp b/src/gui/src/MainForm.cpp
index 858913439a..89719455f0 100644
--- a/src/gui/src/MainForm.cpp
+++ b/src/gui/src/MainForm.cpp
@@ -832,7 +832,7 @@ void MainForm::showUserManual()
}
-void MainForm::action_file_export_pattern_as()
+void MainForm::action_file_export_pattern_as( int nPatternRow )
{
Hydrogen *pHydrogen = Hydrogen::get_instance();
@@ -840,8 +840,18 @@ void MainForm::action_file_export_pattern_as()
Hydrogen::get_instance()->sequencer_stop();
}
+ if ( nPatternRow == -1 ) {
+ nPatternRow = pHydrogen->getSelectedPatternNumber();
+ }
+
+ if ( nPatternRow == -1 ) {
+ QMessageBox::warning( this, "Hydrogen", tr("No pattern selected.") );
+ return;
+ }
+
std::shared_ptr pSong = pHydrogen->getSong();
- Pattern *pPattern = pSong->getPatternList()->get( pHydrogen->getSelectedPatternNumber() );
+
+ Pattern *pPattern = pSong->getPatternList()->get( nPatternRow );
QString sPath = Preferences::get_instance()->getLastExportPatternAsDirectory();
if ( ! Filesystem::dir_writable( sPath, false ) ){
@@ -930,8 +940,15 @@ void MainForm::action_file_openPattern()
if ( pNewPattern == nullptr ) {
QMessageBox::critical( this, "Hydrogen", HydrogenApp::get_instance()->getCommonStrings()->getPatternLoadError() );
} else {
+ int nRow;
+ if ( pHydrogen->getSelectedPatternNumber() == -1 ) {
+ nRow = pSong->getPatternList()->size();
+ } else {
+ nRow = pHydrogen->getSelectedPatternNumber() + 1;
+ }
+
SE_insertPatternAction* pAction =
- new SE_insertPatternAction( pHydrogen->getSelectedPatternNumber() + 1, pNewPattern );
+ new SE_insertPatternAction( nRow, pNewPattern );
HydrogenApp::get_instance()->m_pUndoStack->push( pAction );
}
}
@@ -2366,7 +2383,6 @@ void MainForm::startPlaybackAtCursor( QObject* pObject ) {
if ( pHydrogen->getMode() != Song::Mode::Pattern ) {
pCoreActionController->activateSongMode( false );
- pApp->getPlayerControl()->songModeActivationEvent( 0 );
}
// To provide a similar behaviour as when pressing
diff --git a/src/gui/src/MainForm.h b/src/gui/src/MainForm.h
index f7d7fb3b5b..5e2beb6554 100644
--- a/src/gui/src/MainForm.h
+++ b/src/gui/src/MainForm.h
@@ -136,7 +136,7 @@ public slots:
*/
void action_file_save_as();
void action_file_openPattern();
- void action_file_export_pattern_as();
+ void action_file_export_pattern_as( int nPatternRow = -1 );
bool action_file_exit();
void action_file_export();
diff --git a/src/gui/src/PatternEditor/DrumPatternEditor.cpp b/src/gui/src/PatternEditor/DrumPatternEditor.cpp
index 1ce845cc3c..0d1a3273a5 100644
--- a/src/gui/src/PatternEditor/DrumPatternEditor.cpp
+++ b/src/gui/src/PatternEditor/DrumPatternEditor.cpp
@@ -22,7 +22,8 @@
#include "DrumPatternEditor.h"
#include "PatternEditorPanel.h"
-#include "NotePropertiesRuler.h"
+#include "PatternEditorRuler.h"
+#include "PatternEditorInstrumentList.h"
#include
#include
@@ -42,6 +43,7 @@
#include "UndoActions.h"
#include "../HydrogenApp.h"
#include "../Mixer/Mixer.h"
+#include "../Skin.h"
#include
#include
@@ -51,30 +53,28 @@
using namespace H2Core;
DrumPatternEditor::DrumPatternEditor(QWidget* parent, PatternEditorPanel *panel)
- : PatternEditor( parent, panel ),
- m_nRealColumn(0),
- m_nColumn(0),
- m_nRow(0),
- m_nOldLength(0)
+ : PatternEditor( parent, panel )
{
+ m_editor = PatternEditor::Editor::DrumPattern;
auto pPref = H2Core::Preferences::get_instance();
m_nGridHeight = pPref->getPatternEditorGridHeight();
m_nEditorHeight = m_nGridHeight * MAX_INSTRUMENTS;
+ m_nActiveWidth = m_nEditorWidth;
resize( m_nEditorWidth, m_nEditorHeight );
Hydrogen::get_instance()->setSelectedInstrumentNumber( 0 );
+ createBackground();
}
DrumPatternEditor::~DrumPatternEditor()
{
}
-
-
void DrumPatternEditor::updateEditor( bool bPatternOnly )
{
- auto pAudioEngine = H2Core::Hydrogen::get_instance()->getAudioEngine();
+ auto pHydrogen = H2Core::Hydrogen::get_instance();
+ auto pAudioEngine = pHydrogen->getAudioEngine();
if ( pAudioEngine->getState() != H2Core::AudioEngine::State::Ready &&
pAudioEngine->getState() != H2Core::AudioEngine::State::Playing ) {
ERRORLOG( "FIXME: skipping pattern editor update (state should be READY or PLAYING)" );
@@ -83,21 +83,40 @@ void DrumPatternEditor::updateEditor( bool bPatternOnly )
updatePatternInfo();
- if ( m_pPattern ) {
- m_nEditorWidth = m_nMargin + m_fGridWidth * m_pPattern->get_length();
+ if ( m_pPattern != nullptr ) {
+
+ m_nActiveWidth = PatternEditor::nMargin + m_fGridWidth *
+ m_pPattern->get_length();
+
+ if ( pHydrogen->getPatternMode() == Song::PatternMode::Stacked ) {
+ m_nEditorWidth =
+ std::max( PatternEditor::nMargin + m_fGridWidth *
+ pAudioEngine->getPlayingPatterns()->longest_pattern_length() + 1,
+ static_cast(m_nActiveWidth) );
+ } else {
+ m_nEditorWidth = m_nActiveWidth;
+ }
}
else {
- m_nEditorWidth = m_nMargin + m_fGridWidth * MAX_NOTES;
+ m_nEditorWidth = PatternEditor::nMargin + m_fGridWidth * MAX_NOTES;
+ m_nActiveWidth = m_nEditorWidth;
}
resize( m_nEditorWidth, height() );
// redraw all
+ createBackground();
update( 0, 0, width(), height() );
}
void DrumPatternEditor::addOrRemoveNote( int nColumn, int nRealColumn, int row,
bool bDoAdd, bool bDoDelete ) {
+
+ if ( m_pPattern == nullptr || m_nSelectedPatternNumber == -1 ) {
+ // No pattern selected.
+ return;
+ }
+
std::shared_ptr pSong = Hydrogen::get_instance()->getSong();
auto pSelectedInstrument = pSong->getInstrumentList()->get( row );
H2Core::Note *pOldNote = m_pPattern->find_note( nColumn, nRealColumn, pSelectedInstrument );
@@ -154,8 +173,9 @@ void DrumPatternEditor::addOrRemoveNote( int nColumn, int nRealColumn, int row,
void DrumPatternEditor::mouseClickEvent( QMouseEvent *ev )
{
+ auto pHydrogenApp = HydrogenApp::get_instance();
Hydrogen *pHydrogen = Hydrogen::get_instance();
- if ( m_pPattern == nullptr ) {
+ if ( m_pPattern == nullptr || m_nSelectedPatternNumber == -1 ) {
return;
}
std::shared_ptr pSong = pHydrogen->getSong();
@@ -167,12 +187,11 @@ void DrumPatternEditor::mouseClickEvent( QMouseEvent *ev )
int nColumn = getColumn( ev->x(), /* bUseFineGrained=*/ true );
int nRealColumn = 0;
- if( ev->x() > m_nMargin ) {
- nRealColumn = ( ev->x() - m_nMargin) / static_cast(m_fGridWidth);
+ if( ev->x() > PatternEditor::nMargin ) {
+ nRealColumn = ( ev->x() - PatternEditor::nMargin) / static_cast(m_fGridWidth);
}
if ( nColumn >= (int)m_pPattern->get_length() ) {
- update( 0, 0, width(), height() );
return;
}
auto pSelectedInstrument = pSong->getInstrumentList()->get( row );
@@ -180,7 +199,6 @@ void DrumPatternEditor::mouseClickEvent( QMouseEvent *ev )
if( ev->button() == Qt::LeftButton && (ev->modifiers() & Qt::ShiftModifier) )
{
//shift + leftClick: add noteOff note
- HydrogenApp *pApp = HydrogenApp::get_instance();
Note *pNote = m_pPattern->find_note( nColumn, nRealColumn, pSelectedInstrument, false );
if ( pNote != nullptr ) {
SE_addOrDeleteNoteAction *action = new SE_addOrDeleteNoteAction( nColumn,
@@ -198,68 +216,144 @@ void DrumPatternEditor::mouseClickEvent( QMouseEvent *ev )
false,
false,
pNote->get_note_off() );
- pApp->m_pUndoStack->push( action );
+ pHydrogenApp->m_pUndoStack->push( action );
} else {
// Add stop-note
SE_addNoteOffAction *action = new SE_addNoteOffAction( nColumn, row, m_nSelectedPatternNumber,
pNote != nullptr );
- pApp->m_pUndoStack->push( action );
+ pHydrogenApp->m_pUndoStack->push( action );
}
}
else if ( ev->button() == Qt::LeftButton ) {
- pHydrogen->setSelectedInstrumentNumber( row );
addOrRemoveNote( nColumn, nRealColumn, row );
m_selection.clearSelection();
} else if ( ev->button() == Qt::RightButton ) {
m_pPopupMenu->popup( ev->globalPos() );
- pHydrogen->setSelectedInstrumentNumber( row );
-
- } else {
- // Other clicks may also set instrument
- pHydrogen->setSelectedInstrumentNumber( row );
}
m_pPatternEditorPanel->setCursorPosition( nColumn );
- HydrogenApp::get_instance()->setHideKeyboardCursor( true );
+
+ // Cursor either just got hidden or was moved.
+ if ( ! pHydrogenApp->hideKeyboardCursor() ) {
+ // Immediate update to prevent visual delay.
+ m_pPatternEditorPanel->getInstrumentList()->repaintInstrumentLines();
+ m_pPatternEditorPanel->getPatternEditorRuler()->update();
+ }
update();
}
+void DrumPatternEditor::mousePressEvent( QMouseEvent* ev ) {
+
+ if ( ev->x() > m_nActiveWidth ) {
+ return;
+ }
+
+ PatternEditor::mousePressEvent( ev );
+
+ auto pHydrogenApp = HydrogenApp::get_instance();
+ auto pHydrogen = Hydrogen::get_instance();
+ auto pSong = pHydrogen->getSong();
+ int nInstruments = pSong->getInstrumentList()->size();
+ int nRow = static_cast( ev->y() / static_cast(m_nGridHeight) );
+ if ( nRow >= nInstruments ) {
+ return;
+ }
+
+ pHydrogen->setSelectedInstrumentNumber( nRow );
+
+ // Hide cursor in case this behavior was selected in the
+ // Preferences.
+ bool bOldCursorHidden = pHydrogenApp->hideKeyboardCursor();
+ pHydrogenApp->setHideKeyboardCursor( true );
+
+ // Cursor just got hidden.
+ if ( bOldCursorHidden != pHydrogenApp->hideKeyboardCursor() ) {
+ // Immediate update to prevent visual delay.
+ m_pPatternEditorPanel->getInstrumentList()->repaintInstrumentLines();
+ m_pPatternEditorPanel->getPatternEditorRuler()->update();
+ update();
+ }
+
+ // Update cursor position
+ if ( ! HydrogenApp::get_instance()->hideKeyboardCursor() ) {
+ int nColumn = getColumn( ev->x(), /* bUseFineGrained=*/ true );
+ if ( ( m_pPattern != nullptr &&
+ nColumn >= (int)m_pPattern->get_length() ) ||
+ nColumn >= MAX_INSTRUMENTS ) {
+ return;
+ }
+
+ pHydrogen->setSelectedInstrumentNumber( nRow );
+ m_pPatternEditorPanel->setCursorPosition( nColumn );
+
+ update();
+ m_pPatternEditorPanel->getInstrumentList()->selectedInstrumentChangedEvent();
+ m_pPatternEditorPanel->getPatternEditorRuler()->update();
+ }
+}
+
+
void DrumPatternEditor::mouseDragStartEvent( QMouseEvent *ev )
{
- int row = (int)( ev->y() / (float)m_nGridHeight);
- Hydrogen *pHydrogen = Hydrogen::get_instance();
- std::shared_ptr pSong = pHydrogen->getSong();
+ if ( m_pPattern == nullptr ) {
+ return;
+ }
+
+ auto pHydrogen = Hydrogen::get_instance();
+ auto pSong = pHydrogen->getSong();
+
+ // Set the selected instrument _before_ it will be stored in
+ // PatternEditor::mouseDragStartEvent.
+ int nRow = std::floor(static_cast(ev->y()) /
+ static_cast(m_nGridHeight));
+ pHydrogen->setSelectedInstrumentNumber( nRow );
+ auto pSelectedInstrument = pSong->getInstrumentList()->get( nRow );
+
+ // Handles cursor repositioning and hiding and stores general
+ // properties.
+ PatternEditor::mouseDragStartEvent( ev );
+
int nColumn = getColumn( ev->x() );
+
if ( ev->button() == Qt::RightButton ) {
// Right button drag: adjust note length
int nRealColumn = 0;
- auto pSelectedInstrument = pSong->getInstrumentList()->get( row );
- if( ev->x() > m_nMargin ) {
- nRealColumn = ( ev->x() - m_nMargin) / static_cast(m_fGridWidth);
+ if( ev->x() > PatternEditor::nMargin ) {
+ nRealColumn =
+ static_cast(std::floor(
+ static_cast((ev->x() - PatternEditor::nMargin)) /
+ m_fGridWidth));
}
- m_pDraggedNote = m_pPattern->find_note( nColumn, nRealColumn, pSelectedInstrument, false );
- // needed for undo note length
- m_nRealColumn = nRealColumn;
- m_nColumn = nColumn;
- m_nRow = row;
- if( m_pDraggedNote ){
- m_nOldLength = m_pDraggedNote->get_length();
- } else {
- m_nOldLength = -1;
- }
- } else {
- // Other drag (selection or move) we'll set the cursor input position to the start of the gesture
- pHydrogen->setSelectedInstrumentNumber( row );
- m_pPatternEditorPanel->setCursorPosition( nColumn );
- HydrogenApp::get_instance()->setHideKeyboardCursor( true );
+ m_pDraggedNote = m_pPattern->find_note( nColumn, nRealColumn,
+ pSelectedInstrument, false );
+
+ // Store note-specific properties.
+ storeNoteProperties( m_pDraggedNote );
+
+ m_nRow = nRow;
}
}
+///
+/// Update the state during a Selection drag.
+///
+void DrumPatternEditor::mouseDragUpdateEvent( QMouseEvent *ev )
+{
+ int nRow = MAX_INSTRUMENTS - 1 -
+ static_cast(std::floor(static_cast(ev->y()) /
+ static_cast(m_nGridHeight)));
+ if ( nRow >= MAX_INSTRUMENTS ) {
+ return;
+ }
+
+ PatternEditor::mouseDragUpdateEvent( ev );
+}
+
void DrumPatternEditor::addOrDeleteNoteAction( int nColumn,
int row,
int selectedPatternNumber,
@@ -371,6 +465,10 @@ void DrumPatternEditor::moveNoteAction( int nColumn,
int nNewRow,
Note *pNote)
{
+ if ( m_pPattern == nullptr ) {
+ return;
+ }
+
Hydrogen *pHydrogen = Hydrogen::get_instance();
std::shared_ptr pSong = pHydrogen->getSong();
@@ -436,26 +534,6 @@ void DrumPatternEditor::moveNoteAction( int nColumn,
m_pPatternEditorPanel->updateEditors();
}
-
-void DrumPatternEditor::mouseDragEndEvent( QMouseEvent *ev )
-{
- UNUSED( ev );
- unsetCursor();
-
- if (m_pPattern == nullptr) {
- return;
- }
-
- if ( m_pDraggedNote ) {
- if ( m_pDraggedNote->get_note_off() ) return;
-
- SE_editNoteLenghtAction *action = new SE_editNoteLenghtAction( m_pDraggedNote->get_position(), m_pDraggedNote->get_position(), m_nRow, m_pDraggedNote->get_length(),m_nOldLength, m_nSelectedPatternNumber );
- HydrogenApp::get_instance()->m_pUndoStack->push( action );
- m_pDraggedNote = nullptr;
- }
-}
-
-
///
/// Move or copy notes.
///
@@ -464,6 +542,11 @@ void DrumPatternEditor::mouseDragEndEvent( QMouseEvent *ev )
///
void DrumPatternEditor::selectionMoveEndEvent( QInputEvent *ev )
{
+ if ( m_pPattern == nullptr || m_nSelectedPatternNumber == -1 ) {
+ // No pattern selected.
+ return;
+ }
+
updateModifiers( ev );
QPoint offset = movingGridOffset();
if ( offset.x() == 0 && offset.y() == 0 ) {
@@ -552,80 +635,6 @@ void DrumPatternEditor::selectionMoveEndEvent( QInputEvent *ev )
}
-void DrumPatternEditor::editNoteLengthAction( int nColumn, int nRealColumn, int row, int length, int selectedPatternNumber )
-{
- Hydrogen *pHydrogen = Hydrogen::get_instance();
- PatternList *pPatternList = pHydrogen->getSong()->getPatternList();
-
- H2Core::Pattern *pPattern = nullptr;
- if ( (selectedPatternNumber != -1) && ( (uint)selectedPatternNumber < pPatternList->size() ) ) {
- pPattern = pPatternList->get( selectedPatternNumber );
- }
-
- if( pPattern ) {
- Note *pDraggedNote = nullptr;
- std::shared_ptr pSong = pHydrogen->getSong();
- auto pSelectedInstrument = pSong->getInstrumentList()->get( row );
-
- m_pAudioEngine->lock( RIGHT_HERE );
-
- pDraggedNote = pPattern->find_note( nColumn, nRealColumn, pSelectedInstrument, false );
- if( pDraggedNote ){
- pDraggedNote->set_length( length );
- }
-
- pHydrogen->setIsModified( true );
- m_pAudioEngine->unlock();
-
- m_pPatternEditorPanel->updateEditors();
- }
-}
-
-
-///
-/// Update the state during a Selection drag.
-///
-void DrumPatternEditor::mouseDragUpdateEvent( QMouseEvent *ev )
-{
- if (m_pPattern == nullptr) {
- return;
- }
-
- int row = MAX_INSTRUMENTS - 1 - (ev->y() / (int)m_nGridHeight);
- if (row >= MAX_INSTRUMENTS) {
- return;
- }
-
- if ( m_pDraggedNote ) {
- if ( m_pDraggedNote->get_note_off() ) return;
- int nTickColumn = getColumn( ev->x() );
-
- m_pAudioEngine->lock( RIGHT_HERE ); // lock the audio engine
- int nLen = nTickColumn - (int)m_pDraggedNote->get_position();
-
- if (nLen <= 0) {
- nLen = -1;
- }
-
- float fNotePitch = m_pDraggedNote->get_octave() * 12 + m_pDraggedNote->get_key();
- float fStep = 0;
- if(nLen > -1){
- fStep = Note::pitchToFrequency( ( double )fNotePitch );
- }else
- {
- fStep = 1.0;
- }
- m_pDraggedNote->set_length( nLen * fStep);
-
- Hydrogen::get_instance()->setIsModified( true );
- m_pAudioEngine->unlock(); // unlock the audio engine
-
- m_pPatternEditorPanel->updateEditors();
- }
-
-}
-
-
///
/// Handle key press events.
///
@@ -633,10 +642,17 @@ void DrumPatternEditor::mouseDragUpdateEvent( QMouseEvent *ev )
///
void DrumPatternEditor::keyPressEvent( QKeyEvent *ev )
{
+ if ( m_pPattern == nullptr ) {
+ return;
+ }
+
+ auto pHydrogenApp = HydrogenApp::get_instance();
+ bool bOldCursorHidden = pHydrogenApp->hideKeyboardCursor();
+
const int nBlockSize = 5, nWordSize = 5;
- Hydrogen *pH2 = Hydrogen::get_instance();
- int nSelectedInstrument = pH2->getSelectedInstrumentNumber();
- int nMaxInstrument = pH2->getSong()->getInstrumentList()->size();
+ Hydrogen *pHydrogen = Hydrogen::get_instance();
+ int nSelectedInstrument = pHydrogen->getSelectedInstrumentNumber();
+ int nMaxInstrument = pHydrogen->getSong()->getInstrumentList()->size();
bool bUnhideCursor = true;
bool bIsSelectionKey = m_selection.keyPressEvent( ev );
@@ -670,10 +686,10 @@ void DrumPatternEditor::keyPressEvent( QKeyEvent *ev )
} else if ( ev->matches( QKeySequence::MoveToNextLine ) || ev->matches( QKeySequence::SelectNextLine ) ) {
if ( nSelectedInstrument + 1 < nMaxInstrument ) {
- pH2->setSelectedInstrumentNumber( nSelectedInstrument + 1 );
+ pHydrogen->setSelectedInstrumentNumber( nSelectedInstrument + 1 );
}
} else if ( ev->matches( QKeySequence::MoveToEndOfBlock ) || ev->matches( QKeySequence::SelectEndOfBlock ) ) {
- pH2->setSelectedInstrumentNumber( std::min( nSelectedInstrument + nBlockSize,
+ pHydrogen->setSelectedInstrumentNumber( std::min( nSelectedInstrument + nBlockSize,
nMaxInstrument-1 ) );
} else if ( ev->matches( QKeySequence::MoveToNextPage ) || ev->matches( QKeySequence::SelectNextPage ) ) {
@@ -685,17 +701,17 @@ void DrumPatternEditor::keyPressEvent( QKeyEvent *ev )
if ( nSelectedInstrument >= nMaxInstrument ) {
nSelectedInstrument = nMaxInstrument - 1;
}
- pH2->setSelectedInstrumentNumber( nSelectedInstrument );
+ pHydrogen->setSelectedInstrumentNumber( nSelectedInstrument );
} else if ( ev->matches( QKeySequence::MoveToEndOfDocument ) || ev->matches( QKeySequence::SelectEndOfDocument ) ) {
- pH2->setSelectedInstrumentNumber( nMaxInstrument-1 );
+ pHydrogen->setSelectedInstrumentNumber( nMaxInstrument-1 );
} else if ( ev->matches( QKeySequence::MoveToPreviousLine ) || ev->matches( QKeySequence::SelectPreviousLine ) ) {
if ( nSelectedInstrument > 0 ) {
- pH2->setSelectedInstrumentNumber( nSelectedInstrument - 1 );
+ pHydrogen->setSelectedInstrumentNumber( nSelectedInstrument - 1 );
}
} else if ( ev->matches( QKeySequence::MoveToStartOfBlock ) || ev->matches( QKeySequence::SelectStartOfBlock ) ) {
- pH2->setSelectedInstrumentNumber( std::max( nSelectedInstrument - nBlockSize, 0 ) );
+ pHydrogen->setSelectedInstrumentNumber( std::max( nSelectedInstrument - nBlockSize, 0 ) );
} else if ( ev->matches( QKeySequence::MoveToPreviousPage ) || ev->matches( QKeySequence::SelectPreviousPage ) ) {
QWidget *pParent = dynamic_cast< QWidget *>( parent() );
@@ -704,10 +720,10 @@ void DrumPatternEditor::keyPressEvent( QKeyEvent *ev )
if ( nSelectedInstrument < 0 ) {
nSelectedInstrument = 0;
}
- pH2->setSelectedInstrumentNumber( nSelectedInstrument );
+ pHydrogen->setSelectedInstrumentNumber( nSelectedInstrument );
} else if ( ev->matches( QKeySequence::MoveToStartOfDocument ) || ev->matches( QKeySequence::SelectStartOfDocument ) ) {
- pH2->setSelectedInstrumentNumber( 0 );
+ pHydrogen->setSelectedInstrumentNumber( 0 );
} else if ( ev->key() == Qt::Key_Enter || ev->key() == Qt::Key_Return ) {
// Key: Enter / Return: add or remove note at current position
@@ -749,13 +765,26 @@ void DrumPatternEditor::keyPressEvent( QKeyEvent *ev )
} else {
ev->ignore();
+ pHydrogenApp->setHideKeyboardCursor( true );
+
+ if ( bOldCursorHidden != pHydrogenApp->hideKeyboardCursor() ) {
+ m_pPatternEditorPanel->getInstrumentList()->repaintInstrumentLines();
+ m_pPatternEditorPanel->getPatternEditorRuler()->update();
+ update();
+ }
return;
}
if ( bUnhideCursor ) {
- HydrogenApp::get_instance()->setHideKeyboardCursor( false );
+ pHydrogenApp->setHideKeyboardCursor( false );
}
m_selection.updateKeyboardCursorPosition( getKeyboardCursorRect() );
m_pPatternEditorPanel->ensureCursorVisible();
+
+ if ( ! pHydrogenApp->hideKeyboardCursor() ) {
+ // Immediate update to prevent visual delay.
+ m_pPatternEditorPanel->getInstrumentList()->repaintInstrumentLines();
+ m_pPatternEditorPanel->getPatternEditorRuler()->update();
+ }
update();
ev->accept();
@@ -772,6 +801,11 @@ void DrumPatternEditor::keyReleaseEvent( QKeyEvent *ev ) {
///
std::vector DrumPatternEditor::elementsIntersecting( QRect r )
{
+ std::vector result;
+ if ( m_pPattern == nullptr ) {
+ return std::move( result );
+ }
+
std::shared_ptr pSong = Hydrogen::get_instance()->getSong();
InstrumentList * pInstrList = pSong->getInstrumentList();
uint h = m_nGridHeight / 3;
@@ -790,16 +824,15 @@ std::vector DrumPatternEditor::elementsInters
// Calculate the first and last position values that this rect will intersect with
- int x_min = (r.left() - m_nMargin - 1) / m_fGridWidth;
- int x_max = (r.right() - m_nMargin) / m_fGridWidth;
+ int x_min = (r.left() - PatternEditor::nMargin - 1) / m_fGridWidth;
+ int x_max = (r.right() - PatternEditor::nMargin) / m_fGridWidth;
const Pattern::notes_t* notes = m_pPattern->get_notes();
- std::vector result;
for (auto it = notes->lower_bound( x_min ); it != notes->end() && it->first <= x_max; ++it ) {
Note *note = it->second;
int nInstrument = pInstrList->index( note->get_instrument() );
- uint x_pos = m_nMargin + (it->first * m_fGridWidth);
+ uint x_pos = PatternEditor::nMargin + (it->first * m_fGridWidth);
uint y_pos = ( nInstrument * m_nGridHeight) + (m_nGridHeight / 2) - 3;
if ( r.contains( QPoint( x_pos, y_pos + h/2) ) ) {
@@ -816,7 +849,7 @@ std::vector DrumPatternEditor::elementsInters
QRect DrumPatternEditor::getKeyboardCursorRect()
{
- uint x = m_nMargin + m_pPatternEditorPanel->getCursorPosition() * m_fGridWidth;
+ uint x = PatternEditor::nMargin + m_pPatternEditorPanel->getCursorPosition() * m_fGridWidth;
int nSelectedInstrument = Hydrogen::get_instance()->getSelectedInstrumentNumber();
uint y = nSelectedInstrument * m_nGridHeight;
return QRect( x-m_fGridWidth*3, y+2, m_fGridWidth*6, m_nGridHeight-3 );
@@ -825,6 +858,10 @@ QRect DrumPatternEditor::getKeyboardCursorRect()
void DrumPatternEditor::selectAll()
{
+ if ( m_pPattern == nullptr ) {
+ return;
+ }
+
m_selection.clearSelection();
FOREACH_NOTE_CST_IT_BEGIN_END(m_pPattern->get_notes(), it) {
m_selection.addToSelection( it->second );
@@ -835,6 +872,11 @@ void DrumPatternEditor::selectAll()
void DrumPatternEditor::deleteSelection()
{
+ if ( m_nSelectedPatternNumber == -1 ) {
+ // No pattern selected.
+ return;
+ }
+
if ( m_selection.begin() != m_selection.end() ) {
// Selection exists, delete it.
Hydrogen *pHydrogen = Hydrogen::get_instance();
@@ -882,6 +924,11 @@ void DrumPatternEditor::deleteSelection()
///
void DrumPatternEditor::paste()
{
+ if ( m_pPattern == nullptr || m_nSelectedPatternNumber == -1 ) {
+ // No pattern selected.
+ return;
+ }
+
QClipboard *clipboard = QApplication::clipboard();
QUndoStack *pUndo = HydrogenApp::get_instance()->m_pUndoStack;
InstrumentList *pInstrList = Hydrogen::get_instance()->getSong()->getInstrumentList();
@@ -989,56 +1036,13 @@ void DrumPatternEditor::paste()
///
/// Draws a pattern
///
-void DrumPatternEditor::__draw_pattern(QPainter& painter)
+void DrumPatternEditor::drawPattern(QPainter& painter)
{
auto pPref = H2Core::Preferences::get_instance();
-
- const QColor selectedRowColor( pPref->getColorTheme()->m_patternEditor_selectedRowColor );
-
- __create_background( painter );
-
- if (m_pPattern == nullptr) {
- return;
- }
- int nNotes = m_pPattern->get_length();
- int nSelectedInstrument = Hydrogen::get_instance()->getSelectedInstrumentNumber();
std::shared_ptr pSong = Hydrogen::get_instance()->getSong();
-
InstrumentList * pInstrList = pSong->getInstrumentList();
-
- if ( m_nEditorHeight != (int)( m_nGridHeight * pInstrList->size() ) ) {
- // the number of instruments is changed...recreate all
- m_nEditorHeight = m_nGridHeight * pInstrList->size();
- resize( width(), m_nEditorHeight );
- }
-
- for ( uint nInstr = 0; nInstr < pInstrList->size(); ++nInstr ) {
- uint y = m_nGridHeight * nInstr;
- if ( nInstr == (uint)nSelectedInstrument ) { // selected instrument
- painter.fillRect( 0, y + 1, ( m_nMargin + nNotes * m_fGridWidth ), m_nGridHeight - 1, selectedRowColor );
- }
- }
-
-
- // draw the grid
- __draw_grid( painter );
-
-
- // Draw cursor
- if ( hasFocus() && !HydrogenApp::get_instance()->hideKeyboardCursor() ) {
- uint x = m_nMargin + m_pPatternEditorPanel->getCursorPosition() * m_fGridWidth;
- int nSelectedInstrument = Hydrogen::get_instance()->getSelectedInstrumentNumber();
- uint y = nSelectedInstrument * m_nGridHeight;
- QPen p( Qt::black );
- p.setWidth( 2 );
- painter.setPen( p );
- painter.setRenderHint( QPainter::Antialiasing );
- painter.drawRoundedRect( QRect( x-m_fGridWidth*3, y+2, m_fGridWidth*6, m_nGridHeight-3 ), 4, 4 );
- }
-
-
/*
BUGFIX
@@ -1080,7 +1084,7 @@ void DrumPatternEditor::__draw_pattern(QPainter& painter)
instruments.push( pNote->get_instrument() );
}
- __draw_note( pNote, painter, bIsForeground );
+ drawNote( pNote, painter, bIsForeground );
++noteIt;
}
@@ -1092,7 +1096,7 @@ void DrumPatternEditor::__draw_pattern(QPainter& painter)
if ( noteCount[ nInstrumentID ] > 1 ) {
// Draw "2x" text to the left of the note
int nInstrument = pInstrList->index( pInstrument );
- int x = m_nMargin + (nPosition * m_fGridWidth);
+ int x = PatternEditor::nMargin + (nPosition * m_fGridWidth);
int y = ( nInstrument * m_nGridHeight);
const int boxWidth = 128;
@@ -1118,7 +1122,7 @@ void DrumPatternEditor::__draw_pattern(QPainter& painter)
///
/// Draws a note
///
-void DrumPatternEditor::__draw_note( Note *note, QPainter& p, bool bIsForeground )
+void DrumPatternEditor::drawNote( Note *note, QPainter& p, bool bIsForeground )
{
InstrumentList *pInstrList = Hydrogen::get_instance()->getSong()->getInstrumentList();
int nInstrument = pInstrList->index( note->get_instrument() );
@@ -1127,63 +1131,27 @@ void DrumPatternEditor::__draw_note( Note *note, QPainter& p, bool bIsForeground
return;
}
- QPoint pos ( m_nMargin + note->get_position() * m_fGridWidth,
+ QPoint pos ( PatternEditor::nMargin + note->get_position() * m_fGridWidth,
( nInstrument * m_nGridHeight) + (m_nGridHeight / 2) - 3 );
drawNoteSymbol( p, pos, note, bIsForeground );
}
-
-
-
-void DrumPatternEditor::__draw_grid( QPainter& p )
+void DrumPatternEditor::drawBackground( QPainter& p)
{
-
- auto pPref = H2Core::Preferences::get_instance();
-
- // Start with generic pattern editor grid lining.
- drawGridLines( p );
-
- int nNotes = MAX_NOTES;
- if ( m_pPattern ) {
- nNotes = m_pPattern->get_length();
- }
-
- // fill the first half of the rect with a solid color
- const QColor backgroundColor( pPref->getColorTheme()->m_patternEditor_backgroundColor );
- const QColor selectedRowColor( pPref->getColorTheme()->m_patternEditor_selectedRowColor );
- int nSelectedInstrument = Hydrogen::get_instance()->getSelectedInstrumentNumber();
- std::shared_ptr pSong = Hydrogen::get_instance()->getSong();
- int nInstruments = pSong->getInstrumentList()->size();
- for ( uint i = 0; i < (uint)nInstruments; i++ ) {
- uint y = m_nGridHeight * i + 1;
- if ( i == (uint)nSelectedInstrument ) {
- p.fillRect( 0, y, (m_nMargin + nNotes * m_fGridWidth), (int)( m_nGridHeight * 0.7 ), selectedRowColor );
- }
- else {
- p.fillRect( 0, y, (m_nMargin + nNotes * m_fGridWidth), (int)( m_nGridHeight * 0.7 ), backgroundColor );
- }
- }
-
-}
-
-
-void DrumPatternEditor::__create_background( QPainter& p)
-{
-
auto pPref = H2Core::Preferences::get_instance();
+ auto pHydrogen = H2Core::Hydrogen::get_instance();
const QColor backgroundColor( pPref->getColorTheme()->m_patternEditor_backgroundColor );
+ const QColor backgroundInactiveColor( pPref->getColorTheme()->m_windowColor );
const QColor alternateRowColor( pPref->getColorTheme()->m_patternEditor_alternateRowColor );
+ const QColor selectedRowColor( pPref->getColorTheme()->m_patternEditor_selectedRowColor );
const QColor lineColor( pPref->getColorTheme()->m_patternEditor_lineColor );
+ const QColor lineInactiveColor( pPref->getColorTheme()->m_windowTextColor.darker( 170 ) );
- int nNotes = MAX_NOTES;
- if ( m_pPattern ) {
- nNotes = m_pPattern->get_length();
- }
-
- std::shared_ptr pSong = Hydrogen::get_instance()->getSong();
+ std::shared_ptr pSong = pHydrogen->getSong();
int nInstruments = pSong->getInstrumentList()->size();
+ int nSelectedInstrument = pHydrogen->getSelectedInstrumentNumber();
if ( m_nEditorHeight != (int)( m_nGridHeight * nInstruments ) ) {
// the number of instruments is changed...recreate all
@@ -1191,34 +1159,143 @@ void DrumPatternEditor::__create_background( QPainter& p)
resize( width(), m_nEditorHeight );
}
- p.fillRect(0, 0, m_nMargin + nNotes * m_fGridWidth, height(), backgroundColor);
- for ( uint i = 0; i < (uint)nInstruments; i++ ) {
- uint y = m_nGridHeight * i;
- if ( ( i % 2) != 0) {
- p.fillRect( 0, y, (m_nMargin + nNotes * m_fGridWidth), m_nGridHeight, alternateRowColor );
+ p.fillRect(0, 0, m_nActiveWidth, height(), backgroundColor);
+ p.fillRect(m_nActiveWidth, 0, m_nEditorWidth - m_nActiveWidth, height(),
+ backgroundInactiveColor);
+
+ for ( int ii = 0; ii < nInstruments; ii++ ) {
+ int y = static_cast(m_nGridHeight) * ii;
+ if ( ii == nSelectedInstrument ) {
+ p.fillRect( 0, y, m_nActiveWidth, m_nGridHeight,
+ selectedRowColor );
+ }
+ else if ( ( ii % 2 ) != 0 ) {
+ p.fillRect( 0, y, m_nActiveWidth, m_nGridHeight, alternateRowColor );
}
}
// horizontal lines
- p.setPen( lineColor );
+ p.setPen( QPen( lineColor, 2, Qt::SolidLine ) );
for ( uint i = 0; i < (uint)nInstruments; i++ ) {
uint y = m_nGridHeight * i + m_nGridHeight;
- p.drawLine( 0, y, (m_nMargin + nNotes * m_fGridWidth), y);
+ p.drawLine( 0, y, m_nActiveWidth, y);
+ }
+
+ if ( m_nActiveWidth + 1 < m_nEditorWidth ) {
+ p.setPen( lineInactiveColor );
+ for ( uint i = 0; i < (uint)nInstruments; i++ ) {
+ uint y = m_nGridHeight * i + m_nGridHeight;
+ p.drawLine( m_nActiveWidth, y, m_nEditorWidth, y);
+ }
}
- p.drawLine( 0, m_nEditorHeight, (m_nMargin + nNotes * m_fGridWidth), m_nEditorHeight );
+ // We skip the grid and cursor in case there is no pattern. This
+ // way it may be more obvious that it is not armed and does not
+ // expect user interaction.
+ if ( m_pPattern == nullptr ) {
+ return;
+ }
+ drawGridLines( p );
+
+ // The grid lines above are drawn full height. We will erase the
+ // upper part.
+ for ( int ii = 0; ii < nInstruments; ii++ ) {
+ int y = static_cast(m_nGridHeight) * ii;
+ if ( ii == nSelectedInstrument ) {
+ p.fillRect( 0, y, m_nActiveWidth, (int)( m_nGridHeight * 0.7 ), selectedRowColor );
+ } else {
+ if ( ( ii % 2 ) == 0 ) {
+ p.fillRect( 0, y, m_nActiveWidth, (int)( m_nGridHeight * 0.7 ), backgroundColor );
+ } else {
+ p.fillRect( 0, y, m_nActiveWidth,
+ (int)( m_nGridHeight * 0.7 ), alternateRowColor );
+ }
+ }
+
+ p.fillRect( m_nActiveWidth, y, m_nEditorWidth - m_nActiveWidth,
+ (int)( m_nGridHeight * 0.7 ), backgroundInactiveColor );
+ }
+
+ // borders
+ p.setPen( lineColor );
+ p.drawLine( 0, m_nEditorHeight -1 , m_nActiveWidth - 1, m_nEditorHeight - 1 );
+
+ if ( m_nEditorWidth > m_nActiveWidth + 1 ) {
+ p.setPen( lineInactiveColor );
+ p.drawLine( m_nActiveWidth - 1, m_nEditorHeight - 1, m_nEditorWidth - 1, m_nEditorHeight - 1 );
+ }
+
+ p.setPen( QPen( lineColor, 2, Qt::SolidLine ) );
+ p.drawLine( m_nEditorWidth, 0, m_nEditorWidth, m_nEditorHeight );
+
}
+void DrumPatternEditor::createBackground() {
+ // Resize pixmap if pixel ratio has changed
+ qreal pixelRatio = devicePixelRatio();
+ if ( m_pBackgroundPixmap->width() != m_nEditorWidth ||
+ m_pBackgroundPixmap->height() != m_nEditorHeight ||
+ m_pBackgroundPixmap->devicePixelRatio() != pixelRatio ) {
+ delete m_pBackgroundPixmap;
+ m_pBackgroundPixmap = new QPixmap( width() * pixelRatio, height() * pixelRatio );
+ m_pBackgroundPixmap->setDevicePixelRatio( pixelRatio );
+ }
+
+ QPainter painter( m_pBackgroundPixmap );
+
+ drawBackground( painter );
+
+ drawPattern( painter );
+}
-void DrumPatternEditor::paintEvent( QPaintEvent* /*ev*/ )
+void DrumPatternEditor::paintEvent( QPaintEvent* ev )
{
+ if (!isVisible()) {
+ return;
+ }
+
+ auto pPref = Preferences::get_instance();
+
+ qreal pixelRatio = devicePixelRatio();
+ if ( pixelRatio != m_pBackgroundPixmap->devicePixelRatio() ) {
+ createBackground();
+ }
+
QPainter painter( this );
- __draw_pattern( painter );
+ painter.drawPixmap( ev->rect(), *m_pBackgroundPixmap, QRectF( pixelRatio * ev->rect().x(),
+ pixelRatio * ev->rect().y(),
+ pixelRatio * ev->rect().width(),
+ pixelRatio * ev->rect().height() ) );
+
+ // Draw playhead
+ if ( m_nTick != -1 ) {
+ int nOffset = Skin::getPlayheadShaftOffset();
+ int nX = static_cast(static_cast(PatternEditor::nMargin) +
+ static_cast(m_nTick) *
+ m_fGridWidth );
+ Skin::setPlayheadPen( &painter, false );
+ painter.drawLine( nX, 0, nX, height() );
+ }
+
drawFocus( painter );
m_selection.paintSelection( &painter );
+
+ // Draw cursor
+ if ( hasFocus() && !HydrogenApp::get_instance()->hideKeyboardCursor() ) {
+ uint x = PatternEditor::nMargin + m_pPatternEditorPanel->getCursorPosition() * m_fGridWidth;
+ int nSelectedInstrument = Hydrogen::get_instance()->getSelectedInstrumentNumber();
+ uint y = nSelectedInstrument * m_nGridHeight;
+ QPen p( pPref->getColorTheme()->m_cursorColor );
+ p.setWidth( 2 );
+ painter.setPen( p );
+ painter.setBrush( Qt::NoBrush );
+ painter.setRenderHint( QPainter::Antialiasing );
+ painter.drawRoundedRect( QRect( x-m_fGridWidth*3, y+2, m_fGridWidth*6, m_nGridHeight-3 ), 4, 4 );
+ }
+
}
void DrumPatternEditor::drawFocus( QPainter& painter ) {
@@ -1266,53 +1343,27 @@ void DrumPatternEditor::hideEvent ( QHideEvent *ev )
UNUSED( ev );
}
-
-
-void DrumPatternEditor::focusInEvent ( QFocusEvent *ev )
-{
- UNUSED( ev );
- if ( ev->reason() == Qt::TabFocusReason || ev->reason() == Qt::BacktabFocusReason ) {
- m_pPatternEditorPanel->ensureCursorVisible();
- HydrogenApp::get_instance()->setHideKeyboardCursor( false );
- }
- updateEditor();
-}
-
void DrumPatternEditor::selectedInstrumentChangedEvent()
-{
- update( 0, 0, width(), height() );
-}
-
-/// This method is called from another thread (audio engine)
-void DrumPatternEditor::patternModifiedEvent()
-{
- update( 0, 0, width(), height() );
-}
-
-
-void DrumPatternEditor::patternChangedEvent()
{
updateEditor();
}
-
void DrumPatternEditor::selectedPatternChangedEvent()
{
updateEditor();
}
-
///NotePropertiesRuler undo redo action
void DrumPatternEditor::undoRedoAction( int column,
- QString mode,
- int nSelectedPatternNumber,
- int nSelectedInstrument,
- float velocity,
- float fPan,
- float leadLag,
- float probability,
- int noteKeyVal,
- int octaveKeyVal)
+ PatternEditor::Mode mode,
+ int nSelectedPatternNumber,
+ int nSelectedInstrument,
+ float velocity,
+ float fPan,
+ float leadLag,
+ float probability,
+ int noteKeyVal,
+ int octaveKeyVal)
{
Hydrogen *pHydrogen = Hydrogen::get_instance();
std::shared_ptr pSong = pHydrogen->getSong();
@@ -1323,7 +1374,7 @@ void DrumPatternEditor::undoRedoAction( int column,
pPattern = pPatternList->get( nSelectedPatternNumber );
}
- if(pPattern) {
+ if( pPattern != nullptr ) {
const Pattern::notes_t* notes = pPattern->get_notes();
FOREACH_NOTE_CST_IT_BOUND(notes,it,column) {
Note *pNote = it->second;
@@ -1333,23 +1384,25 @@ void DrumPatternEditor::undoRedoAction( int column,
continue;
}
- if ( mode == "VELOCITY" && !pNote->get_note_off() ) {
+ if ( mode == PatternEditor::Mode::Velocity &&
+ !pNote->get_note_off() ) {
pNote->set_velocity( velocity );
}
- else if ( mode == "PAN" ){
+ else if ( mode == PatternEditor::Mode::Pan ){
pNote->setPan( fPan );
}
- else if ( mode == "LEADLAG" ){
+ else if ( mode == PatternEditor::Mode::LeadLag ){
pNote->set_lead_lag( leadLag );
}
- else if ( mode == "NOTEKEY" ){
+ else if ( mode == PatternEditor::Mode::NoteKey ){
pNote->set_key_octave( (Note::Key)noteKeyVal, (Note::Octave)octaveKeyVal );
}
- else if ( mode == "PROBABILITY" ){
+ else if ( mode == PatternEditor::Mode::Probability ){
pNote->set_probability( probability );
}
pHydrogen->setIsModified( true );
+ NotePropertiesRuler::triggerStatusMessage( pNote, mode );
break;
}
diff --git a/src/gui/src/PatternEditor/DrumPatternEditor.h b/src/gui/src/PatternEditor/DrumPatternEditor.h
index 4b41e2c2c6..6f68e896f0 100644
--- a/src/gui/src/PatternEditor/DrumPatternEditor.h
+++ b/src/gui/src/PatternEditor/DrumPatternEditor.h
@@ -27,6 +27,7 @@
#include "../EventListener.h"
#include "../Selection.h"
#include "PatternEditor.h"
+#include "NotePropertiesRuler.h"
#include "../Widgets/WidgetWithScalableFont.h"
#include
@@ -52,8 +53,6 @@ class DrumPatternEditor : public PatternEditor, protected WidgetWithScalableFont
~DrumPatternEditor();
// Implements EventListener interface
- virtual void patternModifiedEvent() override;
- virtual void patternChangedEvent() override;
virtual void selectedPatternChangedEvent() override;
virtual void selectedInstrumentChangedEvent() override;
//~ Implements EventListener interface
@@ -80,9 +79,8 @@ class DrumPatternEditor : public PatternEditor, protected WidgetWithScalableFont
H2Core::Note *note);
void addOrRemoveNote( int nColumn, int nRealColumn, int row, bool bDoAdd = true, bool bDoDelete = true );
- void editNoteLengthAction( int nColumn, int nRealColumn, int row, int length, int selectedPatternNumber );
void undoRedoAction( int column,
- QString mode,
+ NotePropertiesRuler::Mode mode,
int nSelectedPatternNumber,
int nSelectedInstrument,
float velocity,
@@ -117,7 +115,6 @@ class DrumPatternEditor : public PatternEditor, protected WidgetWithScalableFont
virtual void mouseClickEvent( QMouseEvent *ev ) override;
virtual void mouseDragStartEvent( QMouseEvent *ev ) override;
virtual void mouseDragUpdateEvent( QMouseEvent *ev ) override;
- virtual void mouseDragEndEvent( QMouseEvent *ev ) override;
virtual void selectionMoveEndEvent( QInputEvent *ev ) override;
// Selected notes are indexed by their address to ensure that a
@@ -136,10 +133,10 @@ class DrumPatternEditor : public PatternEditor, protected WidgetWithScalableFont
void onPreferencesChanged( H2Core::Preferences::Changes changes );
private:
- void __draw_note( H2Core::Note* note, QPainter& painter, bool bIsForeground = true );
- void __draw_pattern( QPainter& painter );
- void __draw_grid( QPainter& painter );
- void __create_background( QPainter& pointer );
+ void createBackground() override;
+ void drawNote( H2Core::Note* note, QPainter& painter, bool bIsForeground = true );
+ void drawPattern( QPainter& painter );
+ void drawBackground( QPainter& pointer );
void drawFocus( QPainter& painter );
virtual void keyPressEvent (QKeyEvent *ev) override;
@@ -147,16 +144,11 @@ class DrumPatternEditor : public PatternEditor, protected WidgetWithScalableFont
virtual void showEvent ( QShowEvent *ev ) override;
virtual void hideEvent ( QHideEvent *ev ) override;
virtual void paintEvent(QPaintEvent *ev) override;
- virtual void focusInEvent( QFocusEvent *ev ) override;
+ virtual void mousePressEvent( QMouseEvent *ev ) override;
int findFreeCompoID( int startingPoint = 0 );
int findExistingCompo( QString SourceName );
QString renameCompo( QString OriginalName );
-
- int m_nRealColumn;
- int m_nColumn;
- int m_nRow;
- int m_nOldLength;
};
diff --git a/src/gui/src/PatternEditor/NotePropertiesRuler.cpp b/src/gui/src/PatternEditor/NotePropertiesRuler.cpp
index 033b6ca358..9f8485f886 100644
--- a/src/gui/src/PatternEditor/NotePropertiesRuler.cpp
+++ b/src/gui/src/PatternEditor/NotePropertiesRuler.cpp
@@ -25,7 +25,6 @@
#include
#include
#include
-#include
using namespace H2Core;
#include
@@ -35,43 +34,35 @@ using namespace H2Core;
#include "UndoActions.h"
#include "NotePropertiesRuler.h"
#include "PatternEditorPanel.h"
+#include "PatternEditorRuler.h"
#include "DrumPatternEditor.h"
#include "PianoRollEditor.h"
+#include "../Skin.h"
-NotePropertiesRuler::NotePropertiesRuler( QWidget *parent, PatternEditorPanel *pPatternEditorPanel, NotePropertiesMode mode )
+NotePropertiesRuler::NotePropertiesRuler( QWidget *parent, PatternEditorPanel *pPatternEditorPanel, PatternEditor::Mode mode )
: PatternEditor( parent, pPatternEditorPanel )
, m_bEntered( false )
{
- m_Mode = mode;
+
+ m_editor = PatternEditor::Editor::NotePropertiesRuler;
+ m_mode = mode;
m_fGridWidth = (Preferences::get_instance())->getPatternEditorGridWidth();
- m_nEditorWidth = m_nMargin + m_fGridWidth * ( MAX_NOTES * 4 );
+ m_nEditorWidth = PatternEditor::nMargin + m_fGridWidth * ( MAX_NOTES * 4 );
m_fLastSetValue = 0.0;
m_bValueHasBeenSet = false;
- m_bNeedsUpdate = true;
- if (m_Mode == VELOCITY ) {
- m_nEditorHeight = 100;
- }
- else if ( m_Mode == PAN ) {
- m_nEditorHeight = 100;
- }
- else if ( m_Mode == LEADLAG ) {
- m_nEditorHeight = 100;
- }
- else if ( m_Mode == NOTEKEY ) {
+ if ( m_mode == PatternEditor::Mode::NoteKey ) {
m_nEditorHeight = 210;
}
- if (m_Mode == PROBABILITY ) {
+ else {
m_nEditorHeight = 100;
}
resize( m_nEditorWidth, m_nEditorHeight );
setMinimumSize( m_nEditorWidth, m_nEditorHeight );
- m_pBackground = new QPixmap( m_nEditorWidth, m_nEditorHeight );
-
updateEditor();
show();
@@ -95,7 +86,6 @@ NotePropertiesRuler::NotePropertiesRuler( QWidget *parent, PatternEditorPanel *p
NotePropertiesRuler::~NotePropertiesRuler()
{
- //infoLog("DESTROY");
}
@@ -132,7 +122,10 @@ void NotePropertiesRuler::wheelEvent(QWheelEvent *ev )
#endif
m_pPatternEditorPanel->setCursorPosition( nColumn );
- HydrogenApp::get_instance()->setHideKeyboardCursor( true );
+
+ auto pHydrogenApp = HydrogenApp::get_instance();
+ bool bOldCursorHidden = pHydrogenApp->hideKeyboardCursor();
+ pHydrogenApp->setHideKeyboardCursor( true );
std::shared_ptr pSong = pHydrogen->getSong();
auto pSelectedInstrument = pSong->getInstrumentList()->get( pHydrogen->getSelectedInstrumentNumber() );
@@ -148,18 +141,30 @@ void NotePropertiesRuler::wheelEvent(QWheelEvent *ev )
notes.push_back( it->second );
}
}
-
+
+ bool bValueChanged = false;
for ( Note *pNote : notes ) {
assert( pNote );
if ( pNote->get_instrument() != pSelectedInstrument && !m_selection.isSelected( pNote ) ) {
continue;
}
+ bValueChanged = true;
adjustNotePropertyDelta( pNote, fDelta, /* bMessage=*/ true );
}
+
+ if ( bOldCursorHidden != pHydrogenApp->hideKeyboardCursor() ) {
+ // Immediate update to prevent visual delay.
+ m_pPatternEditorPanel->getPatternEditorRuler()->update();
+ if ( ! bValueChanged ) {
+ update();
+ }
+ }
- pHydrogen->setIsModified( true );
- addUndoAction();
- updateEditor();
+ if ( bValueChanged ) {
+ addUndoAction();
+ createBackground();
+ update();
+ }
}
@@ -175,6 +180,43 @@ void NotePropertiesRuler::mouseClickEvent( QMouseEvent *ev ) {
}
}
+void NotePropertiesRuler::mousePressEvent( QMouseEvent* ev ) {
+ if ( ev->x() > m_nActiveWidth ) {
+ return;
+ }
+
+ PatternEditor::mousePressEvent( ev );
+
+ auto pHydrogenApp = HydrogenApp::get_instance();
+
+ // Hide cursor in case this behavior was selected in the
+ // Preferences.
+ bool bOldCursorHidden = pHydrogenApp->hideKeyboardCursor();
+ pHydrogenApp->setHideKeyboardCursor( true );
+
+ // Cursor just got hidden.
+ if ( bOldCursorHidden != pHydrogenApp->hideKeyboardCursor() ) {
+ // Immediate update to prevent visual delay.
+ m_pPatternEditorPanel->getPatternEditorRuler()->update();
+ update();
+ }
+
+ // Update cursor position
+ if ( ! pHydrogenApp->hideKeyboardCursor() ) {
+ int nColumn = getColumn( ev->x(), /* bUseFineGrained=*/ true );
+ if ( ( m_pPattern != nullptr &&
+ nColumn >= (int)m_pPattern->get_length() ) ||
+ nColumn >= MAX_INSTRUMENTS ) {
+ return;
+ }
+
+ m_pPatternEditorPanel->setCursorPosition( nColumn );
+
+ update();
+ m_pPatternEditorPanel->getPatternEditorRuler()->update();
+ }
+}
+
void NotePropertiesRuler::mouseDragStartEvent( QMouseEvent *ev ) {
if ( m_selection.isMoving() ) {
prepareUndoAction( ev->x() );
@@ -205,12 +247,24 @@ void NotePropertiesRuler::selectionMoveUpdateEvent( QMouseEvent *ev ) {
float fDelta;
QPoint movingOffset = m_selection.movingOffset();
- if ( m_Mode == NOTEKEY ) {
+ if ( m_mode == PatternEditor::Mode::NoteKey ) {
fDelta = (float)-movingOffset.y() / 10;
} else {
fDelta = (float)-movingOffset.y() / height();
}
+ // Only send a status message for the update in case a single note
+ // was selected.
+ bool bSendStatusMsg = false;
+ int nNotes = 0;
+ for ( Note *pNote : m_selection ) {
+ ++nNotes;
+ }
+ if ( nNotes == 1 ) {
+ bSendStatusMsg = true;
+ }
+
+ bool bValueChanged = false;
for ( Note *pNote : m_selection ) {
if ( pNote->get_instrument() == pSelectedInstrument || m_selection.isSelected( pNote ) ) {
@@ -219,16 +273,22 @@ void NotePropertiesRuler::selectionMoveUpdateEvent( QMouseEvent *ev ) {
m_oldNotes[ pNote ] = new Note( pNote );
}
- adjustNotePropertyDelta( pNote, fDelta );
+ adjustNotePropertyDelta( pNote, fDelta, bSendStatusMsg );
+ bValueChanged = true;
}
}
- updateEditor();
+
+ if ( bValueChanged ) {
+ createBackground();
+ update();
+ }
}
void NotePropertiesRuler::selectionMoveEndEvent( QInputEvent *ev ) {
//! The "move" has already been reflected in the notes. Now just complete Undo event.
addUndoAction();
- updateEditor();
+ createBackground();
+ update();
}
void NotePropertiesRuler::clearOldNotes() {
@@ -242,32 +302,43 @@ void NotePropertiesRuler::clearOldNotes() {
void NotePropertiesRuler::selectionMoveCancelEvent() {
for ( auto it : m_oldNotes ) {
Note *pNote = it.first, *pOldNote = it.second;
- switch ( m_Mode ) {
- case VELOCITY:
+ switch ( m_mode ) {
+ case PatternEditor::Mode::Velocity:
pNote->set_velocity( pOldNote->get_velocity() );
break;
- case PAN:
+ case PatternEditor::Mode::Pan:
pNote->setPan( pOldNote->getPan() );
break;
- case LEADLAG:
+ case PatternEditor::Mode::LeadLag:
pNote->set_lead_lag( pOldNote->get_lead_lag() );
break;
- case NOTEKEY:
+ case PatternEditor::Mode::NoteKey:
pNote->set_key_octave( pOldNote->get_key(), pOldNote->get_octave() );
break;
- case PROBABILITY:
+ case PatternEditor::Mode::Probability:
pNote->set_probability( pOldNote->get_probability() );
break;
default:
break;
}
}
+
+ if ( m_oldNotes.size() == 0 ) {
+ for ( const auto& it : m_oldNotes ){
+ PatternEditor::triggerStatusMessage( it.second, m_mode );
+ }
+ }
+
clearOldNotes();
}
void NotePropertiesRuler::mouseMoveEvent( QMouseEvent *ev )
{
+ if ( m_pPattern == nullptr ) {
+ return;
+ }
+
if ( ev->buttons() == Qt::NoButton ) {
int nColumn = getColumn( ev->x() );
bool bFound = false;
@@ -291,7 +362,8 @@ void NotePropertiesRuler::propertyDragStart( QMouseEvent *ev )
{
setCursor( Qt::CrossCursor );
prepareUndoAction( ev->x() );
- updateEditor();
+ createBackground();
+ update();
}
@@ -341,7 +413,14 @@ void NotePropertiesRuler::propertyDragUpdate( QMouseEvent *ev )
int nColumn = getColumn( ev->x() );
m_pPatternEditorPanel->setCursorPosition( nColumn );
- HydrogenApp::get_instance()->setHideKeyboardCursor( true );
+
+ auto pHydrogenApp = HydrogenApp::get_instance();
+ auto pHydrogen = Hydrogen::get_instance();
+ auto pAudioEngine = pHydrogen->getAudioEngine();
+ auto pSong = pHydrogen->getSong();
+
+ bool bOldCursorHidden = pHydrogenApp->hideKeyboardCursor();
+ pHydrogenApp->setHideKeyboardCursor( true );
if ( m_nDragPreviousColumn != nColumn ) {
// Complete current undo action, and start a new one.
@@ -358,88 +437,90 @@ void NotePropertiesRuler::propertyDragUpdate( QMouseEvent *ev )
}
int keyval = val;
val = val / height(); // val is normalized, in [0;1]
- Hydrogen *pHydrogen = Hydrogen::get_instance();
int nSelectedInstrument = pHydrogen->getSelectedInstrumentNumber();
- std::shared_ptr pSong = pHydrogen->getSong();
auto pSelectedInstrument = pSong->getInstrumentList()->get( nSelectedInstrument );
- FOREACH_NOTE_CST_IT_BOUND( m_pPattern->get_notes(), it, nColumn ) {
+ bool bValueSet = false;
+
+ FOREACH_NOTE_CST_IT_BOUND( m_pPattern->get_notes(), it, nColumn ) {
Note *pNote = it->second;
- if ( pNote->get_instrument() != pSelectedInstrument && !m_selection.isSelected( pNote ) ) {
+ if ( pNote->get_instrument() != pSelectedInstrument &&
+ !m_selection.isSelected( pNote ) ) {
continue;
}
- if ( m_Mode == VELOCITY && !pNote->get_note_off() ) {
+ if ( m_mode == PatternEditor::Mode::Velocity && !pNote->get_note_off() ) {
pNote->set_velocity( val );
m_fLastSetValue = val;
- m_bValueHasBeenSet = true;
- char valueChar[100];
- sprintf( valueChar, "%#.2f", val);
- HydrogenApp::get_instance()->setStatusBarMessage( QString("Set note velocity [%1]").arg( valueChar ), 2000 );
+ bValueSet = true;
}
- else if ( m_Mode == PAN && !pNote->get_note_off() ){
+ else if ( m_mode == PatternEditor::Mode::Pan && !pNote->get_note_off() ){
if ( (ev->button() == Qt::MiddleButton)
|| (ev->modifiers() == Qt::ControlModifier && ev->button() == Qt::LeftButton) ) {
val = 0.5; // central pan
}
pNote->setPanWithRangeFrom0To1( val ); // checks the boundaries
m_fLastSetValue = pNote->getPanWithRangeFrom0To1();
- m_bValueHasBeenSet = true;
+ bValueSet = true;
+
}
- else if ( m_Mode == LEADLAG ){
- if ( (ev->button() == Qt::MiddleButton) || (ev->modifiers() == Qt::ControlModifier && ev->button() == Qt::LeftButton) ) {
+ else if ( m_mode == PatternEditor::Mode::LeadLag ){
+ if ( (ev->button() == Qt::MiddleButton) ||
+ (ev->modifiers() == Qt::ControlModifier &&
+ ev->button() == Qt::LeftButton) ) {
pNote->set_lead_lag(0.0);
- } else {
-
+ m_fLastSetValue = 0.0;
+ bValueSet = true;
+ }
+ else {
m_fLastSetValue = val * -2.0 + 1.0;
- m_bValueHasBeenSet = true;
- pNote->set_lead_lag((val * -2.0) + 1.0);
- char valueChar[100];
- if (pNote->get_lead_lag() < 0.0) {
- sprintf( valueChar, "%.2f", ( pNote->get_lead_lag() * -5)); // FIXME: '5' taken from fLeadLagFactor calculation in hydrogen.cpp
- HydrogenApp::get_instance()->setStatusBarMessage( QString("Leading beat by: %1 ticks").arg( valueChar ), 2000 );
- } else if (pNote->get_lead_lag() > 0.0) {
- sprintf( valueChar, "%.2f", ( pNote->get_lead_lag() * 5)); // FIXME: '5' taken from fLeadLagFactor calculation in hydrogen.cpp
- HydrogenApp::get_instance()->setStatusBarMessage( QString("Lagging beat by: %1 ticks").arg( valueChar ), 2000 );
- } else {
- HydrogenApp::get_instance()->setStatusBarMessage( QString("Note on beat"), 2000 );
- }
-
+ bValueSet = true;
+ pNote->set_lead_lag( m_fLastSetValue );
}
}
-
- else if ( m_Mode == NOTEKEY ){
- if ( (ev->button() == Qt::MiddleButton) || (ev->modifiers() == Qt::ControlModifier && ev->button() == Qt::LeftButton) ) {
- ;
- } else {
+ else if ( m_mode == PatternEditor::Mode::NoteKey ){
+ if ( ev->button() != Qt::MiddleButton &&
+ ! ( ev->modifiers() == Qt::ControlModifier &&
+ ev->button() == Qt::LeftButton ) ) {
//set the note height
int k = 666;
int o = 666;
- if(keyval >=6 && keyval<=125) {
- k = (keyval-6)/10;
- } else if(keyval>=135 && keyval<=205) {
- o = (keyval-166)/10;
- if(o==-4) o=-3; // 135
+ if( keyval >= 6 && keyval <= 125 ) {
+ k = ( keyval - 6 ) / 10;
+ }
+ else if( keyval >= 135 && keyval <= 205 ) {
+ o = ( keyval - 166 ) / 10;
+ if ( o == -4 ) {
+ o = -3; // 135
+ }
}
m_fLastSetValue = o * 12 + k;
- m_bValueHasBeenSet = true;
+ bValueSet = true;
pNote->set_key_octave((Note::Key)k,(Note::Octave)o); // won't set wrong values see Note::set_key_octave
}
}
- else if ( m_Mode == PROBABILITY && !pNote->get_note_off() ) {
+ else if ( m_mode == PatternEditor::Mode::Probability && !pNote->get_note_off() ) {
m_fLastSetValue = val;
- m_bValueHasBeenSet = true;
+ bValueSet = true;
pNote->set_probability( val );
- char valueChar[100];
- sprintf( valueChar, "%#.2f", val);
- HydrogenApp::get_instance()->setStatusBarMessage( QString("Set note probability [%1]").arg( valueChar ), 2000 );
+ }
+
+ if ( bValueSet ) {
+ PatternEditor::triggerStatusMessage( pNote, m_mode );
+ m_bValueHasBeenSet = true;
+ Hydrogen::get_instance()->setIsModified( true );
}
}
- m_nDragPreviousColumn = nColumn;
+ // Cursor just got hidden.
+ if ( bOldCursorHidden != pHydrogenApp->hideKeyboardCursor() ) {
+ // Immediate update to prevent visual delay.
+ m_pPatternEditorPanel->getPatternEditorRuler()->update();
+ }
- Hydrogen::get_instance()->setIsModified( true );
- updateEditor();
+ m_nDragPreviousColumn = nColumn;
+ createBackground();
+ update();
m_pPatternEditorPanel->getPianoRollEditor()->updateEditor();
m_pPatternEditorPanel->getDrumPatternEditor()->updateEditor();
@@ -449,7 +530,8 @@ void NotePropertiesRuler::propertyDragEnd()
{
addUndoAction();
unsetCursor();
- updateEditor();
+ createBackground();
+ update();
}
//! Adjust a note's property by applying a delta to the current value, and clipping to the appropriate
@@ -458,58 +540,43 @@ void NotePropertiesRuler::adjustNotePropertyDelta( Note *pNote, float fDelta, bo
{
Note *pOldNote = m_oldNotes[ pNote ];
assert( pOldNote );
- switch (m_Mode) {
- case VELOCITY:
+
+ bool bValueSet = false;
+
+ switch (m_mode) {
+ case PatternEditor::Mode::Velocity:
if ( !pNote->get_note_off() ) {
float fVelocity = qBound( VELOCITY_MIN, (pOldNote->get_velocity() + fDelta), VELOCITY_MAX );
pNote->set_velocity( fVelocity );
m_fLastSetValue = fVelocity;
- m_bValueHasBeenSet = true;
- if ( bMessage ) {
- char valueChar[100];
- sprintf( valueChar, "%#.2f", fVelocity );
- ( HydrogenApp::get_instance() )->setStatusBarMessage( QString( tr( "Set note velocity [%1]" ) )
- .arg( valueChar ), 2000 );
- }
+ bValueSet = true;
}
break;
- case PAN:
+ case PatternEditor::Mode::Pan:
if ( !pNote->get_note_off() ) {
float fVal = pOldNote->getPanWithRangeFrom0To1() + fDelta; // value in [0,1] or slight out of boundaries
pNote->setPanWithRangeFrom0To1( fVal ); // checks the boundaries as well
m_fLastSetValue = pNote->getPanWithRangeFrom0To1();
- m_bValueHasBeenSet = true;
+ bValueSet = true;
}
break;
- case LEADLAG:
+ case PatternEditor::Mode::LeadLag:
{
float fLeadLag = qBound( LEAD_LAG_MIN, pOldNote->get_lead_lag() - fDelta, LEAD_LAG_MAX );
pNote->set_lead_lag( fLeadLag );
m_fLastSetValue = fLeadLag;
- m_bValueHasBeenSet = true;
- if ( bMessage ) {
- char valueChar[100];
- if (pNote->get_lead_lag() < 0.0) {
- sprintf( valueChar, "%.2f", ( pNote->get_lead_lag() * -5)); // FIXME: '5' taken from fLeadLagFactor calculation in hydrogen.cpp
- HydrogenApp::get_instance()->setStatusBarMessage( QString("Leading beat by: %1 ticks").arg( valueChar ), 2000 );
- } else if (pNote->get_lead_lag() > 0.0) {
- sprintf( valueChar, "%.2f", ( pNote->get_lead_lag() * 5)); // FIXME: '5' taken from fLeadLagFactor calculation in hydrogen.cpp
- HydrogenApp::get_instance()->setStatusBarMessage( QString("Lagging beat by: %1 ticks").arg( valueChar ), 2000 );
- } else {
- HydrogenApp::get_instance()->setStatusBarMessage( QString("Note on beat"), 2000 );
- }
- }
+ bValueSet = true;
}
break;
- case PROBABILITY:
+ case PatternEditor::Mode::Probability:
if ( !pNote->get_note_off() ) {
float fProbability = qBound( 0.0f, pOldNote->get_probability() + fDelta, 1.0f );
pNote->set_probability( fProbability );
m_fLastSetValue = fProbability;
- m_bValueHasBeenSet = true;
+ bValueSet = true;
}
break;
- case NOTEKEY:
+ case PatternEditor::Mode::NoteKey:
int nPitch = qBound( 12 * OCTAVE_MIN, (int)( pOldNote->get_notekey_pitch() + fDelta ),
12 * OCTAVE_MAX + KEY_MAX );
Note::Octave octave;
@@ -523,18 +590,34 @@ void NotePropertiesRuler::adjustNotePropertyDelta( Note *pNote, float fDelta, bo
pNote->set_key_octave( key, octave );
m_fLastSetValue = 12 * octave + key;
- m_bValueHasBeenSet = true;
+ bValueSet = true;
break;
}
- Hydrogen::get_instance()->setIsModified( true );
+
+ if ( bValueSet ) {
+ Hydrogen::get_instance()->setIsModified( true );
+ m_bValueHasBeenSet = true;
+ if ( bMessage ) {
+ PatternEditor::triggerStatusMessage( pNote, m_mode );
+ }
+ }
}
void NotePropertiesRuler::keyPressEvent( QKeyEvent *ev )
{
+ if ( m_pPattern == nullptr ) {
+ return;
+ }
+
+ auto pHydrogenApp = HydrogenApp::get_instance();
+ bool bOldCursorHidden = pHydrogenApp->hideKeyboardCursor();
+
const int nWordSize = 5;
bool bIsSelectionKey = m_selection.keyPressEvent( ev );
bool bUnhideCursor = true;
+ bool bValueChanged = false;
+
if ( bIsSelectionKey ) {
// Key was claimed by selection
} else if ( ev->matches( QKeySequence::MoveToNextChar ) || ev->matches( QKeySequence::SelectNextChar ) ) {
@@ -562,6 +645,7 @@ void NotePropertiesRuler::keyPressEvent( QKeyEvent *ev )
m_pPatternEditorPanel->setCursorPosition(0);
} else {
+
// Value adjustments
float fDelta = 0.0;
bool bRepeatLastValue = false;
@@ -626,13 +710,16 @@ void NotePropertiesRuler::keyPressEvent( QKeyEvent *ev )
Note *pNote = it->second;
assert( pNote );
assert( pNote->get_position() == column );
- nNotes++;
- notes.push_back( pNote );
+ if ( pNote->get_instrument() ==
+ pSong->getInstrumentList()->get( nSelectedInstrument ) ) {
+ nNotes++;
+ notes.push_back( pNote );
+ }
}
}
// For the NoteKeyEditor, adjust the pitch by a whole semitone
- if ( m_Mode == NOTEKEY ) {
+ if ( m_mode == PatternEditor::Mode::NoteKey ) {
if ( fDelta > 0.0 ) {
fDelta = 1;
} else if ( fDelta < 0.0 ) {
@@ -640,118 +727,119 @@ void NotePropertiesRuler::keyPressEvent( QKeyEvent *ev )
}
}
- prepareUndoAction( m_nMargin + column * m_fGridWidth );
+ prepareUndoAction( PatternEditor::nMargin + column * m_fGridWidth );
for ( Note *pNote : notes ) {
-
- if ( pNote->get_instrument() != pSong->getInstrumentList()->get( nSelectedInstrument )
- && !m_selection.isSelected( pNote ) ) {
- continue;
- }
+ bValueChanged = true;
if ( !bRepeatLastValue ) {
+
// Apply delta to the property
adjustNotePropertyDelta( pNote, fDelta, nNotes == 1 );
} else {
+
+ bool bValueSet = false;
+
// Repeating last value
- switch (m_Mode) {
- case VELOCITY:
+ switch (m_mode) {
+ case PatternEditor::Mode::Velocity:
if ( !pNote->get_note_off() ) {
pNote->set_velocity( m_fLastSetValue );
+ bValueSet = true;
}
break;
- case PAN:
+ case PatternEditor::Mode::Pan:
if ( !pNote->get_note_off() ) {
if ( m_fLastSetValue > 1. ) { // TODO whats this for? is it ever reached?
printf( "reached m_fLastSetValue > 1 in NotePropertiesRuler.cpp\n" );
pNote->setPanWithRangeFrom0To1( m_fLastSetValue );
}
- break;
+ bValueSet = true;
}
- case LEADLAG:
+ break;
+ case PatternEditor::Mode::LeadLag:
pNote->set_lead_lag( m_fLastSetValue );
+ bValueSet = true;
break;
- case PROBABILITY:
+ case PatternEditor::Mode::Probability:
if ( !pNote->get_note_off() ) {
pNote->set_probability( m_fLastSetValue );
+ bValueSet = true;
}
break;
- case NOTEKEY:
+ case PatternEditor::Mode::NoteKey:
pNote->set_key_octave( (Note::Key)( (int)m_fLastSetValue % 12 ),
(Note::Octave)( (int)m_fLastSetValue / 12 ) );
+ bValueSet = true;
break;
}
+
+ if ( bValueSet ) {
+ if ( nNotes == 1 ) {
+ PatternEditor::triggerStatusMessage( pNote, m_mode );
+ }
+ Hydrogen::get_instance()->setIsModified( true );
+ }
}
}
addUndoAction();
} else {
- HydrogenApp::get_instance()->setHideKeyboardCursor( true );
+ pHydrogenApp->setHideKeyboardCursor( true );
ev->ignore();
+
+ // Cursor either just got hidden.
+ if ( bOldCursorHidden != pHydrogenApp->hideKeyboardCursor() ) {
+ // Immediate update to prevent visual delay.
+ m_pPatternEditorPanel->getPatternEditorRuler()->update();
+ update();
+ }
return;
}
}
if ( bUnhideCursor ) {
- HydrogenApp::get_instance()->setHideKeyboardCursor( false );
+ pHydrogenApp->setHideKeyboardCursor( false );
}
- m_selection.updateKeyboardCursorPosition( getKeyboardCursorRect() );
- updateEditor();
- ev->accept();
-
-}
-
-void NotePropertiesRuler::focusInEvent( QFocusEvent * ev )
-{
- if ( ev->reason() == Qt::TabFocusReason || ev->reason() == Qt::BacktabFocusReason ) {
- HydrogenApp::get_instance()->setHideKeyboardCursor( false );
+ // Cursor either just got hidden or was moved.
+ if ( ! HydrogenApp::get_instance()->hideKeyboardCursor() ||
+ bOldCursorHidden != pHydrogenApp->hideKeyboardCursor() ) {
+ // Immediate update to prevent visual delay.
+ m_pPatternEditorPanel->getPatternEditorRuler()->update();
}
- updateEditor();
-}
+ m_selection.updateKeyboardCursorPosition( getKeyboardCursorRect() );
+
+ if ( bValueChanged ) {
+ createBackground();
+ }
+ update();
+
+ ev->accept();
-void NotePropertiesRuler::focusOutEvent( QFocusEvent * ev )
-{
- updateEditor();
}
-
void NotePropertiesRuler::addUndoAction()
{
+ if ( m_nSelectedPatternNumber == -1 ) {
+ // No pattern selected.
+ return;
+ }
+
InstrumentList *pInstrumentList = Hydrogen::get_instance()->getSong()->getInstrumentList();
int nSize = m_oldNotes.size();
if ( nSize != 0 ) {
QUndoStack *pUndoStack = HydrogenApp::get_instance()->m_pUndoStack;
- QString sMode;
- switch ( m_Mode ) {
- case VELOCITY:
- sMode = "VELOCITY";
- break;
- case PAN:
- sMode = "PAN";
- break;
- case LEADLAG:
- sMode = "LEADLAG";
- break;
- case NOTEKEY:
- sMode = "NOTEKEY";
- break;
- case PROBABILITY:
- sMode = "PROBABILITY";
- break;
- default:
- break;
- }
if ( nSize != 1 ) {
- pUndoStack->beginMacro( QString( tr( "Edit %1 property of %2 notes" ) )
- .arg( sMode.toLower() )
+ pUndoStack->beginMacro( QString( tr( "Edit [%1] property of [%2] notes" ) )
+ .arg( NotePropertiesRuler::modeToQString( m_mode ) )
.arg( nSize ) );
}
for ( auto it : m_oldNotes ) {
Note *pNewNote = it.first, *pOldNote = it.second;
pUndoStack->push( new SE_editNotePropertiesVolumeAction( pNewNote->get_position(),
- sMode,
+ m_mode,
m_nSelectedPatternNumber,
pInstrumentList->index( pNewNote->get_instrument() ),
pNewNote->get_velocity(),
@@ -776,15 +864,50 @@ void NotePropertiesRuler::addUndoAction()
void NotePropertiesRuler::paintEvent( QPaintEvent *ev)
{
- QPainter painter(this);
- if ( m_bNeedsUpdate ) {
- finishUpdateEditor();
+ if (!isVisible()) {
+ return;
}
- painter.drawPixmap( ev->rect(), *m_pBackground, ev->rect() );
+ auto pPref = Preferences::get_instance();
+
+ qreal pixelRatio = devicePixelRatio();
+ if ( pixelRatio != m_pBackgroundPixmap->devicePixelRatio() ) {
+ createBackground();
+ }
+
+ QPainter painter(this);
+ painter.drawPixmap( ev->rect(), *m_pBackgroundPixmap,
+ QRectF( pixelRatio * ev->rect().x(),
+ pixelRatio * ev->rect().y(),
+ pixelRatio * ev->rect().width(),
+ pixelRatio * ev->rect().height() ) );
+
+ // Draw playhead
+ if ( m_nTick != -1 ) {
+
+ int nOffset = Skin::getPlayheadShaftOffset();
+ int nX = static_cast(static_cast(PatternEditor::nMargin) +
+ static_cast(m_nTick) *
+ m_fGridWidth );
+ Skin::setPlayheadPen( &painter, false );
+ painter.drawLine( nX, 0, nX, height() );
+ }
+
drawFocus( painter );
- // m_selection.paintSelection( &painter );
+ m_selection.paintSelection( &painter );
+
+ // cursor
+ if ( hasFocus() && ! HydrogenApp::get_instance()->hideKeyboardCursor() ) {
+ uint x = PatternEditor::nMargin + m_pPatternEditorPanel->getCursorPosition() * m_fGridWidth;
+
+ QPen pen( pPref->getColorTheme()->m_cursorColor );
+ pen.setWidth( 2 );
+ painter.setPen( pen );
+ painter.setBrush( Qt::NoBrush );
+ painter.setRenderHint( QPainter::Antialiasing );
+ painter.drawRoundedRect( QRect( x-m_fGridWidth*3, 0 + 3, m_fGridWidth*6, height() - 6 ), 4, 4 );
+ }
}
void NotePropertiesRuler::drawFocus( QPainter& painter ) {
@@ -806,20 +929,20 @@ void NotePropertiesRuler::drawFocus( QPainter& painter ) {
const QScrollArea* pScrollArea;
- switch ( m_Mode ) {
- case VELOCITY:
+ switch ( m_mode ) {
+ case PatternEditor::Mode::Velocity:
pScrollArea = HydrogenApp::get_instance()->getPatternEditorPanel()->getNoteVelocityScrollArea();
break;
- case PAN:
+ case PatternEditor::Mode::Pan:
pScrollArea = HydrogenApp::get_instance()->getPatternEditorPanel()->getNotePanScrollArea();
break;
- case LEADLAG:
+ case PatternEditor::Mode::LeadLag:
pScrollArea = HydrogenApp::get_instance()->getPatternEditorPanel()->getNoteLeadLagScrollArea();
break;
- case NOTEKEY:
+ case PatternEditor::Mode::NoteKey:
pScrollArea = HydrogenApp::get_instance()->getPatternEditorPanel()->getNoteNoteKeyScrollArea();
break;
- case PROBABILITY:
+ case PatternEditor::Mode::Probability:
pScrollArea = HydrogenApp::get_instance()->getPatternEditorPanel()->getNoteProbabilityScrollArea();
break;
default:
@@ -876,35 +999,59 @@ void NotePropertiesRuler::leaveEvent( QEvent *ev ) {
update();
}
-
-
-void NotePropertiesRuler::createVelocityBackground(QPixmap *pixmap)
-{
+void NotePropertiesRuler::drawDefaultBackground( QPainter& painter, int nHeight, int nIncrement ) {
+
auto pPref = H2Core::Preferences::get_instance();
- QColor res_1( pPref->getColorTheme()->m_patternEditor_line1Color );
-
- QColor backgroundColor( pPref->getColorTheme()->m_patternEditor_backgroundColor );
- QColor horizLinesColor( backgroundColor.red() - 20,
- backgroundColor.green() - 20,
- backgroundColor.blue() - 20 );
+ const QColor borderColor( pPref->getColorTheme()->m_patternEditor_lineColor );
+ const QColor lineColor( pPref->getColorTheme()->m_patternEditor_line5Color );
+ const QColor lineInactiveColor( pPref->getColorTheme()->m_windowTextColor.darker( 170 ) );
+ const QColor backgroundColor( pPref->getColorTheme()->m_patternEditor_backgroundColor );
+ const QColor backgroundInactiveColor( pPref->getColorTheme()->m_windowColor );
- unsigned nNotes = MAX_NOTES;
- if ( m_pPattern ) {
- nNotes = m_pPattern->get_length();
+ if ( nHeight == 0 ) {
+ nHeight = height();
+ }
+ if ( nIncrement == 0 ) {
+ nIncrement = nHeight / 10;
}
- QPainter p( pixmap );
+ painter.fillRect( 0, 0, m_nActiveWidth, height(), backgroundColor );
+ painter.fillRect( m_nActiveWidth, 0, m_nEditorWidth - m_nActiveWidth,
+ height(), backgroundInactiveColor );
+
+ drawGridLines( painter, Qt::DotLine );
+
+ painter.setPen( lineColor );
+ for (unsigned y = 0; y < nHeight; y += nIncrement ) {
+ painter.drawLine( PatternEditor::nMargin, y, m_nActiveWidth, y );
+ }
+
+ painter.setPen( borderColor );
+ painter.drawLine( 0, 0, m_nActiveWidth, 0 );
+ painter.drawLine( 0, m_nEditorHeight - 1, m_nActiveWidth, m_nEditorHeight - 1 );
+
+ if ( m_nActiveWidth + 1 < m_nEditorWidth ) {
+ painter.setPen( lineInactiveColor );
+ for (unsigned y = 0; y < nHeight; y += nIncrement ) {
+ painter.drawLine( m_nActiveWidth, y, m_nEditorWidth, y );
+ }
+
+ painter.drawLine( m_nActiveWidth, 0, m_nEditorWidth, 0 );
+ painter.drawLine( m_nActiveWidth, m_nEditorHeight - 1,
+ m_nEditorWidth, m_nEditorHeight - 1 );
+ }
+}
- p.fillRect( 0, 0, m_nMargin + nNotes * m_fGridWidth, height(), backgroundColor );
+void NotePropertiesRuler::createNormalizedBackground(QPixmap *pixmap)
+{
+ auto pPref = H2Core::Preferences::get_instance();
- drawGridLines( p, Qt::DotLine );
+ QColor borderColor( pPref->getColorTheme()->m_patternEditor_lineColor );
+ const QColor lineInactiveColor( pPref->getColorTheme()->m_windowTextColor.darker( 170 ) );
+ QPainter p( pixmap );
- // Horizontal lines at 10% intervals
- p.setPen( horizLinesColor );
- for (unsigned y = 0; y < m_nEditorHeight; y = y + (m_nEditorHeight / 10)) {
- p.drawLine( m_nMargin, y, 20 + nNotes * m_fGridWidth, y );
- }
+ drawDefaultBackground( p );
// draw velocity lines
if (m_pPattern != nullptr) {
@@ -927,20 +1074,29 @@ void NotePropertiesRuler::createVelocityBackground(QPixmap *pixmap)
&& !m_selection.isSelected( pNote ) ) {
continue;
}
- uint x_pos = m_nMargin + pos * m_fGridWidth;
+ uint x_pos = PatternEditor::nMargin + pos * m_fGridWidth;
uint line_end = height();
uint value = 0;
- if ( m_Mode == VELOCITY ) {
+ if ( m_mode == PatternEditor::Mode::Velocity ) {
value = (uint)(pNote->get_velocity() * height());
}
- else if ( m_Mode == PROBABILITY ) {
+ else if ( m_mode == PatternEditor::Mode::Probability ) {
value = (uint)(pNote->get_probability() * height());
}
uint line_start = line_end - value;
- QColor centerColor = DrumPatternEditor::computeNoteColor( pNote->get_velocity() );
+ QColor noteColor = DrumPatternEditor::computeNoteColor( pNote->get_velocity() );
int nLineWidth = 3;
+
+ p.fillRect( x_pos - 1 + xoffset, line_start,
+ nLineWidth, line_end - line_start,
+ noteColor );
+ p.setPen( QPen( Qt::black, 1 ) );
+ p.setRenderHint( QPainter::Antialiasing );
+ p.drawRoundedRect( x_pos - 1 - 1 + xoffset, line_start - 1,
+ nLineWidth + 2, line_end - line_start + 2, 2, 2 );
+
if ( m_selection.isSelected( pNote ) ) {
p.setPen( selectedPen );
p.setRenderHint( QPainter::Antialiasing );
@@ -948,47 +1104,48 @@ void NotePropertiesRuler::createVelocityBackground(QPixmap *pixmap)
nLineWidth + 4, line_end - line_start + 4 ,
4, 4 );
}
-
- p.fillRect( x_pos - 1 + xoffset, line_start, nLineWidth, line_end - line_start , centerColor );
xoffset++;
}
}
}
- p.setPen(res_1);
- p.drawLine(0, 0, m_nEditorWidth, 0);
- p.drawLine(0, m_nEditorHeight - 1, m_nEditorWidth, m_nEditorHeight - 1);
+
+ p.setPen( borderColor );
+ p.setRenderHint( QPainter::Antialiasing );
+ p.drawLine( 0, 0, m_nEditorWidth, 0 );
+ p.setPen( QPen( borderColor, 2 ) );
+ p.drawLine( 0, m_nEditorHeight, m_nEditorWidth, m_nEditorHeight );
+
+ if ( m_nActiveWidth + 1 < m_nEditorWidth ) {
+ p.setPen( lineInactiveColor );
+ p.drawLine( m_nActiveWidth, 0, m_nEditorWidth, 0 );
+ p.setPen( QPen( lineInactiveColor, 2 ) );
+ p.drawLine( m_nActiveWidth, m_nEditorHeight,
+ m_nEditorWidth, m_nEditorHeight );
+ }
}
-
-
-void NotePropertiesRuler::createPanBackground(QPixmap *pixmap)
+void NotePropertiesRuler::createCenteredBackground(QPixmap *pixmap)
{
auto pPref = H2Core::Preferences::get_instance();
- QColor backgroundColor( pPref->getColorTheme()->m_patternEditor_backgroundColor );
-
- QColor horizLinesColor( backgroundColor.red() - 20,
- backgroundColor.green() - 20,
- backgroundColor.blue() - 20 );
-
- QColor res_1( pPref->getColorTheme()->m_patternEditor_line1Color );
+ QColor baseLineColor( pPref->getColorTheme()->m_patternEditor_lineColor );
+ QColor borderColor( pPref->getColorTheme()->m_patternEditor_lineColor );
+ const QColor lineInactiveColor( pPref->getColorTheme()->m_windowTextColor.darker( 170 ) );
QPainter p( pixmap );
- unsigned nNotes = MAX_NOTES;
- if (m_pPattern) {
- nNotes = m_pPattern->get_length();
- }
- p.fillRect( 0, 0, m_nMargin + nNotes * m_fGridWidth, height(), backgroundColor );
+ drawDefaultBackground( p );
// central line
- p.setPen( horizLinesColor );
- p.drawLine(0, height() / 2.0, m_nEditorWidth, height() / 2.0);
-
- // vertical lines
- drawGridLines( p, Qt::DotLine );
+ p.setPen( baseLineColor );
+ p.drawLine(0, height() / 2.0, m_nActiveWidth, height() / 2.0);
+ if ( m_nActiveWidth + 1 < m_nEditorWidth ) {
+ p.setPen( lineInactiveColor );
+ p.drawLine( m_nActiveWidth, height() / 2.0,
+ m_nEditorWidth, height() / 2.0);
+ }
- if ( m_pPattern ) {
+ if ( m_pPattern != nullptr ) {
int nSelectedInstrument = Hydrogen::get_instance()->getSelectedInstrumentNumber();
std::shared_ptr pSong = Hydrogen::get_instance()->getSong();
QPen selectedPen( selectedNoteColor() );
@@ -1008,181 +1165,110 @@ void NotePropertiesRuler::createPanBackground(QPixmap *pixmap)
&& !m_selection.isSelected( pNote ) ) ) {
continue;
}
- uint x_pos = m_nMargin + pNote->get_position() * m_fGridWidth;
- QColor centerColor = DrumPatternEditor::computeNoteColor( pNote->get_velocity() );
+ uint x_pos = PatternEditor::nMargin + pNote->get_position() * m_fGridWidth;
+ QColor noteColor = DrumPatternEditor::computeNoteColor( pNote->get_velocity() );
p.setPen( Qt::NoPen );
- if ( pNote->getPan() == 0.f ) {
- // pan value is centered - draw circle
- int y_pos = (int)( height() * 0.5 );
- p.setBrush(QColor( centerColor ));
- p.drawEllipse( x_pos-4 + xoffset, y_pos-4, 8, 8);
- } else {
- int y_start = height() * 0.5;
- int y_width = (int)( - 0.5 * height() * pNote->getPan() );
- int nLineWidth = 3;
- p.fillRect( x_pos - 1 + xoffset, y_start, nLineWidth, y_width, QColor( centerColor) );
- p.fillRect( x_pos - 1 + xoffset, ( height() / 2.0 ) - 2 , nLineWidth, 5, QColor( centerColor ) );
+ float fValue = 0;
+ if ( m_mode == PatternEditor::Mode::Pan ) {
+ fValue = pNote->getPan();
+ } else if ( m_mode == PatternEditor::Mode::LeadLag ) {
+ fValue = -1 * pNote->get_lead_lag();
}
+ // Rounding in order to not miss the center due to
+ // rounding errors introduced in the Note class
+ // internals.
+ fValue *= 100;
+ fValue = std::round( fValue );
+ fValue /= 100;
+
int nLineWidth = 3;
- if ( m_selection.isSelected( pNote ) ) {
- p.setPen( selectedPen );
+ p.setPen( QPen( Qt::black, 1 ) );
+ p.setRenderHint( QPainter::Antialiasing );
+ if ( fValue == 0.f ) {
+ // value is centered - draw circle
+ int y_pos = (int)( height() * 0.5 );
+ p.setBrush(QColor( noteColor ));
+ p.drawEllipse( x_pos-4 + xoffset, y_pos-4, 8, 8);
p.setBrush( Qt::NoBrush );
- p.setRenderHint( QPainter::Antialiasing );
- p.drawRoundedRect( x_pos - 1 -2 + xoffset, 0,
- nLineWidth + 4, height() ,
- 4, 4 );
- }
- xoffset++;
- }
- }
- }
- p.setPen(res_1);
- p.drawLine(0, 0, m_nEditorWidth, 0);
- p.drawLine(0, m_nEditorHeight - 1, m_nEditorWidth, m_nEditorHeight - 1);
-}
-
-void NotePropertiesRuler::createLeadLagBackground(QPixmap *pixmap)
-{
- auto pPref = H2Core::Preferences::get_instance();
-
- QColor backgroundColor( pPref->getColorTheme()->m_patternEditor_backgroundColor );
-
- QColor horizLinesColor( backgroundColor.red() - 20,
- backgroundColor.green() - 20,
- backgroundColor.blue() - 20 );
-
- QColor res_1( pPref->getColorTheme()->m_patternEditor_line1Color );
-
- QPainter p( pixmap );
-
- unsigned nNotes = MAX_NOTES;
- if (m_pPattern) {
- nNotes = m_pPattern->get_length();
- }
- p.fillRect( 0, 0, m_nMargin + nNotes * m_fGridWidth, height(), backgroundColor );
-
- // central line
- p.setPen( horizLinesColor );
- p.drawLine(0, height() / 2.0, m_nEditorWidth, height() / 2.0);
-
- // vertical lines
- drawGridLines( p, Qt::DotLine );
-
- if ( m_pPattern ) {
- int nSelectedInstrument = Hydrogen::get_instance()->getSelectedInstrumentNumber();
- std::shared_ptr pSong = Hydrogen::get_instance()->getSong();
- QPen selectedPen( selectedNoteColor() );
- selectedPen.setWidth( 2 );
-
- const Pattern::notes_t* notes = m_pPattern->get_notes();
- FOREACH_NOTE_CST_IT_BEGIN_END(notes,it) {
- Note *pposNote = it->second;
- assert( pposNote );
- uint pos = pposNote->get_position();
- int xoffset = 0;
- FOREACH_NOTE_CST_IT_BOUND(notes,coit,pos) {
- Note *pNote = coit->second;
- assert( pNote );
- if ( pNote->get_instrument() != pSong->getInstrumentList()->get( nSelectedInstrument )
- && !m_selection.isSelected( pNote ) ) {
- continue;
+ if ( m_selection.isSelected( pNote ) ) {
+ p.setPen( selectedPen );
+ p.setRenderHint( QPainter::Antialiasing );
+ p.drawEllipse( x_pos - 6 + xoffset, y_pos - 6,
+ 12, 12);
+ }
}
+ else {
+ // value was altered - draw a rectangle
+ int nHeight = 0.5 * height() * std::abs( fValue ) + 5;
+ int nStartY = height() * 0.5 - 2;
+ if ( fValue >= 0 ) {
+ nStartY = nStartY - nHeight + 5;
+ }
- uint x_pos = m_nMargin + pNote->get_position() * m_fGridWidth;
-
- int red1 = (int) (pNote->get_velocity() * 255);
- int green1;
- int blue1;
- blue1 = ( 255 - (int) red1 )* .33;
- green1 = ( 255 - (int) red1 );
+ p.fillRect( x_pos - 1 + xoffset, nStartY,
+ nLineWidth, nHeight, QColor( noteColor ) );
+ p.drawRoundedRect( x_pos - 1 + xoffset - 1, nStartY - 1,
+ nLineWidth + 2, nHeight + 2, 2, 2 );
- p.setPen( Qt::NoPen );
- if (pNote->get_lead_lag() == 0) {
-
- // leadlag value is centered - draw circle
- int y_pos = (int)( height() * 0.5 );
- p.setBrush(QColor( 0 , 0 , 0 ));
- p.drawEllipse( x_pos-4 + xoffset, y_pos-4, 8, 8);
- } else {
- int y_start = (int)( height() * 0.5 );
- int y_end = y_start + ((pNote->get_lead_lag()/2) * height());
-
- int nLineWidth = 3;
- int red;
- int green;
- int blue = (int) (pNote->get_lead_lag() * 255);
- if (blue < 0) {
- red = blue *-1;
- blue = (int) red * .33;
- green = (int) red * .33;
- } else {
- red = (int) blue * .33;
- green = (int) blue * .33;
+ if ( m_selection.isSelected( pNote ) ) {
+ p.setPen( selectedPen );
+ p.drawRoundedRect( x_pos - 1 - 2 + xoffset, nStartY - 2,
+ nLineWidth + 4, nHeight + 4,
+ 4, 4 );
}
- p.fillRect( x_pos - 1 + xoffset, y_start, nLineWidth, y_end - y_start, QColor( red, green ,blue ) );
-
- p.fillRect( x_pos - 1 + xoffset, ( height() / 2.0 ) - 2 , nLineWidth, 5, QColor( red1, green1 ,blue1 ) );
}
-
- int nLineWidth = 3;
- if ( m_selection.isSelected( pNote ) ) {
- p.setPen( selectedPen );
- p.setBrush( Qt::NoBrush );
- p.setRenderHint( QPainter::Antialiasing );
- p.drawRoundedRect( x_pos - 1 -2 + xoffset, 0,
- nLineWidth + 4, height() ,
- 4, 4 );
- }
-
xoffset++;
- }
+ }
}
}
- p.setPen(res_1);
- p.drawLine(0, 0, m_nEditorWidth, 0);
- p.drawLine(0, m_nEditorHeight - 1, m_nEditorWidth, m_nEditorHeight - 1);
+
+ p.setPen( borderColor );
+ p.setRenderHint( QPainter::Antialiasing );
+ p.drawLine( 0, 0, m_nEditorWidth, 0 );
+ p.setPen( QPen( borderColor, 2 ) );
+ p.drawLine( 0, m_nEditorHeight, m_nEditorWidth, m_nEditorHeight );
+
+ if ( m_nActiveWidth + 1 < m_nEditorWidth ) {
+ p.setPen( lineInactiveColor );
+ p.drawLine( m_nActiveWidth, 0, m_nEditorWidth, 0 );
+ p.setPen( QPen( lineInactiveColor, 2 ) );
+ p.drawLine( m_nActiveWidth, m_nEditorHeight,
+ m_nEditorWidth, m_nEditorHeight );
+ }
}
-
-
void NotePropertiesRuler::createNoteKeyBackground(QPixmap *pixmap)
{
auto pPref = H2Core::Preferences::get_instance();
-
- QColor res_1( pPref->getColorTheme()->m_patternEditor_line1Color );
-
- QColor backgroundColor( pPref->getColorTheme()->m_patternEditor_backgroundColor );
+ QColor backgroundColor = pPref->getColorTheme()->m_patternEditor_backgroundColor;
+ const QColor backgroundInactiveColor( pPref->getColorTheme()->m_windowColor );
+ QColor alternateRowColor = pPref->getColorTheme()->m_patternEditor_alternateRowColor;
+ QColor octaveColor = pPref->getColorTheme()->m_patternEditor_octaveRowColor;
+ QColor lineColor( pPref->getColorTheme()->m_patternEditor_lineColor );
+ const QColor lineInactiveColor( pPref->getColorTheme()->m_windowTextColor.darker( 170 ) );
+ QColor textColor( pPref->getColorTheme()->m_patternEditor_textColor );
- QColor horizLinesColor( backgroundColor.red() - 100,
- backgroundColor.green() - 100,
- backgroundColor.blue() - 100 );
-
- unsigned nNotes = MAX_NOTES;
- if (m_pPattern) {
- nNotes = m_pPattern->get_length();
- }
QPainter p( pixmap );
+ p.fillRect( 0, 0, m_nEditorWidth, m_nEditorHeight, backgroundInactiveColor );
+ drawDefaultBackground( p, 80, 10 );
- p.fillRect( 0, 0, m_nMargin + nNotes * m_fGridWidth, height(), backgroundColor );
-
- p.setPen( horizLinesColor );
- for (unsigned y = 10; y < 80; y = y + 10 ) {
- p.setPen( QPen( res_1, 1, Qt::DashLine ) );
- if (y == 40) p.setPen( QPen( QColor(0,0,0), 1, Qt::SolidLine ) );
- p.drawLine( m_nMargin, y, m_nMargin + nNotes * m_fGridWidth, y );
- }
-
- for (unsigned y = 90; y < 210; y = y + 10 ) {
- p.setPen( QPen( QColor( 255, 255, 255 ), 9, Qt::SolidLine, Qt::FlatCap) );
- if ( y == 100 ||y == 120 ||y == 140 ||y == 170 ||y == 190) {
- p.setPen( QPen( QColor( 128, 128, 128 ), 9, Qt::SolidLine, Qt::FlatCap ) );
+ // fill the background of the key region;
+ for ( unsigned y = 90; y < 210; y = y + 10 ) {
+
+ if ( y == 100 || y == 120 || y == 140 || y == 170 || y == 190) {
+ p.setPen( QPen( alternateRowColor,
+ 9, Qt::SolidLine, Qt::FlatCap ) );
+ }
+ else {
+ p.setPen( QPen( octaveColor, 9, Qt::SolidLine, Qt::FlatCap) );
}
- p.drawLine( m_nMargin, y, m_nMargin + nNotes * m_fGridWidth, y );
+
+ p.drawLine( PatternEditor::nMargin, y, m_nActiveWidth, y );
}
// Annotate with note class names
@@ -1192,27 +1278,26 @@ void NotePropertiesRuler::createNoteKeyBackground(QPixmap *pixmap)
QFont font( pPref->getApplicationFontFamily(), getPointSize( pPref->getFontSize() ) );
p.setFont( font );
- p.setPen( QColor( 0, 0, 0 ) );
+ p.setPen( textColor );
for ( int n = 0; n < 12; n++ ) {
- p.drawText( 5, 90 + 10 * n +3, noteNames[n] );
+ p.drawText( 3, 90 + 10 * n +3, noteNames[n] );
}
- // vertical lines
- drawGridLines( p, Qt::DotLine );
-
- p.setPen(res_1);
- p.drawLine(0, 0, m_nEditorWidth, 0);
- p.drawLine(0, m_nEditorHeight - 1, m_nEditorWidth, m_nEditorHeight - 1);
-
-
- // Black outline each key
+ // Horizontal grid lines in the key region
+ p.setPen( QPen( lineColor, 1, Qt::SolidLine));
for (unsigned y = 90; y <= 210; y = y + 10 ) {
- p.setPen( QPen( QColor( 0, 0, 0 ), 1, Qt::SolidLine));
- p.drawLine( m_nMargin, y-5, m_nMargin + nNotes * m_fGridWidth, y-5);
+ p.drawLine( PatternEditor::nMargin, y - 5, m_nActiveWidth, y-5);
+ }
+
+ if ( m_nActiveWidth + 1 < m_nEditorWidth ) {
+ p.setPen( lineInactiveColor );
+ for (unsigned y = 90; y <= 210; y = y + 10 ) {
+ p.drawLine( m_nActiveWidth, y - 5, m_nEditorWidth, y-5);
+ }
}
//paint the octave
- if ( m_pPattern ) {
+ if ( m_pPattern != nullptr ) {
int nSelectedInstrument = Hydrogen::get_instance()->getSelectedInstrumentNumber();
std::shared_ptr pSong = Hydrogen::get_instance()->getSong();
QPen selectedPen( selectedNoteColor() );
@@ -1229,14 +1314,14 @@ void NotePropertiesRuler::createNoteKeyBackground(QPixmap *pixmap)
if ( !pNote->get_note_off() ) {
uint x_pos = 17 + pNote->get_position() * m_fGridWidth;
uint y_pos = (4-pNote->get_octave())*10-3;
- p.setBrush(QColor( 99, 160, 233 ));
+ p.setBrush( DrumPatternEditor::computeNoteColor( pNote->get_velocity() ) );
p.drawEllipse( x_pos, y_pos, 6, 6);
}
}
}
//paint the note
- if ( m_pPattern ) {
+ if ( m_pPattern != nullptr ) {
int nSelectedInstrument = Hydrogen::get_instance()->getSelectedInstrumentNumber();
std::shared_ptr pSong = Hydrogen::get_instance()->getSong();
QPen selectedPen( selectedNoteColor() );
@@ -1260,8 +1345,8 @@ void NotePropertiesRuler::createNoteKeyBackground(QPixmap *pixmap)
x_pos -= 1;
y_pos -= 1;
d += 2;
- p.setPen( Qt::NoPen );
- p.setBrush(QColor( 0, 0, 0));
+ p.setPen( QPen( Qt::black, 1 ) );
+ p.setBrush( DrumPatternEditor::computeNoteColor( pNote->get_velocity() ) );
p.drawEllipse( x_pos, y_pos, d, d);
// Paint selection outlines
@@ -1270,17 +1355,31 @@ void NotePropertiesRuler::createNoteKeyBackground(QPixmap *pixmap)
p.setPen( selectedPen );
p.setBrush( Qt::NoBrush );
p.setRenderHint( QPainter::Antialiasing );
- p.drawRoundedRect( x_pos - 1 -2 +3, 0,
- nLineWidth + 4 + 4, height() ,
+ p.drawRoundedRect( x_pos - 1 -2 +3, 2,
+ nLineWidth + 4 + 4, height() - 4,
4, 4 );
}
}
}
}
+
+ p.setPen( lineColor );
+ p.setRenderHint( QPainter::Antialiasing );
+ p.drawLine( 0, 0, m_nEditorWidth, 0 );
+ p.setPen( QPen( lineColor, 2 ) );
+ p.drawLine( 0, m_nEditorHeight, m_nEditorWidth, m_nEditorHeight );
+
+ if ( m_nActiveWidth + 1 < m_nEditorWidth ) {
+ p.setPen( lineInactiveColor );
+ p.drawLine( m_nActiveWidth, 0, m_nEditorWidth, 0 );
+ p.setPen( QPen( lineInactiveColor, 2 ) );
+ p.drawLine( m_nActiveWidth, m_nEditorHeight,
+ m_nEditorWidth, m_nEditorHeight );
+ }
}
-void NotePropertiesRuler::updateEditor( bool bPatternOnly )
+void NotePropertiesRuler::updateEditor( bool )
{
Hydrogen *pHydrogen = Hydrogen::get_instance();
PatternList *pPatternList = pHydrogen->getSong()->getPatternList();
@@ -1294,54 +1393,54 @@ void NotePropertiesRuler::updateEditor( bool bPatternOnly )
m_nSelectedPatternNumber = nSelectedPatternNumber;
// update editor width
- if ( m_pPattern ) {
- m_nEditorWidth = m_nMargin + m_pPattern->get_length() * m_fGridWidth;
+ if ( m_pPattern != nullptr ) {
+ m_nActiveWidth = PatternEditor::nMargin + m_fGridWidth *
+ m_pPattern->get_length();
+
+ if ( pHydrogen->getPatternMode() ==
+ Song::PatternMode::Stacked ) {
+ m_nEditorWidth =
+ std::max( PatternEditor::nMargin + m_fGridWidth *
+ pHydrogen->getAudioEngine()->getPlayingPatterns()->longest_pattern_length() + 1,
+ static_cast(m_nActiveWidth) );
+ } else {
+ m_nEditorWidth = m_nActiveWidth;
+ }
}
else {
- m_nEditorWidth = m_nMargin + MAX_NOTES * m_fGridWidth;
+ m_nEditorWidth = PatternEditor::nMargin + MAX_NOTES * m_fGridWidth;
+ m_nActiveWidth = m_nEditorWidth;
}
- if ( !m_bNeedsUpdate ) {
- m_bNeedsUpdate = true;
- update();
- }
+ createBackground();
+ update();
}
-void NotePropertiesRuler::finishUpdateEditor()
+void NotePropertiesRuler::createBackground()
{
- assert( m_bNeedsUpdate );
resize( m_nEditorWidth, height() );
-
- delete m_pBackground;
- m_pBackground = new QPixmap( m_nEditorWidth, m_nEditorHeight );
-
- if ( m_Mode == VELOCITY || m_Mode == PROBABILITY ) {
- createVelocityBackground( m_pBackground );
- }
- else if ( m_Mode == PAN ) {
- createPanBackground( m_pBackground );
+
+ qreal pixelRatio = devicePixelRatio();
+ if ( m_pBackgroundPixmap->width() != m_nEditorWidth ||
+ m_pBackgroundPixmap->height() != m_nEditorHeight ||
+ m_pBackgroundPixmap->devicePixelRatio() != pixelRatio ) {
+ delete m_pBackgroundPixmap;
+ m_pBackgroundPixmap = new QPixmap( m_nEditorWidth * pixelRatio ,
+ m_nEditorHeight * pixelRatio );
+ m_pBackgroundPixmap->setDevicePixelRatio( pixelRatio );
}
- else if ( m_Mode == LEADLAG ) {
- createLeadLagBackground( m_pBackground );
+
+ if ( m_mode == PatternEditor::Mode::Velocity ||
+ m_mode == PatternEditor::Mode::Probability ) {
+ createNormalizedBackground( m_pBackgroundPixmap );
}
- else if ( m_Mode == NOTEKEY ) {
- createNoteKeyBackground( m_pBackground );
+ else if ( m_mode == PatternEditor::Mode::Pan ||
+ m_mode == PatternEditor::Mode::LeadLag ) {
+ createCenteredBackground( m_pBackgroundPixmap );
}
-
- if ( hasFocus() && ! HydrogenApp::get_instance()->hideKeyboardCursor() ) {
- QPainter p( m_pBackground );
-
- uint x = m_nMargin + m_pPatternEditorPanel->getCursorPosition() * m_fGridWidth;
-
- QPen pen( Qt::black );
- pen.setWidth( 2 );
- p.setPen( pen );
- p.setRenderHint( QPainter::Antialiasing );
- p.drawRoundedRect( QRect( x-m_fGridWidth*3, 0 + 3, m_fGridWidth*6, height() - 6 ), 4, 4 );
+ else if ( m_mode == PatternEditor::Mode::NoteKey ) {
+ createNoteKeyBackground( m_pBackgroundPixmap );
}
-
- // redraw all
- m_bNeedsUpdate = false;
update();
}
@@ -1351,16 +1450,17 @@ void NotePropertiesRuler::selectedPatternChangedEvent()
updateEditor();
}
-
-
void NotePropertiesRuler::selectedInstrumentChangedEvent()
{
updateEditor();
}
-
std::vector NotePropertiesRuler::elementsIntersecting( QRect r ) {
std::vector result;
+ if ( m_pPattern == nullptr ) {
+ return std::move( result );
+ }
+
const Pattern::notes_t* notes = m_pPattern->get_notes();
std::shared_ptr pSong = Hydrogen::get_instance()->getSong();
int nSelectedInstrument = Hydrogen::get_instance()->getSelectedInstrumentNumber();
@@ -1382,14 +1482,16 @@ std::vector NotePropertiesRuler::elementsIn
}
int pos = it->first;
- uint x_pos = m_nMargin + pos * m_fGridWidth;
+ uint x_pos = PatternEditor::nMargin + pos * m_fGridWidth;
if ( r.intersects( QRect( x_pos, 0, 1, height() ) ) ) {
result.push_back( it->second );
}
}
- // Updating selection, we may need to repaint the whole widget.
- updateEditor();
+ // Updating selection, we may need to repaint the whole widget.
+ createBackground();
+ update();
+
return std::move(result);
}
@@ -1398,7 +1500,8 @@ std::vector NotePropertiesRuler::elementsIn
///
QRect NotePropertiesRuler::getKeyboardCursorRect()
{
- uint x = m_nMargin + m_pPatternEditorPanel->getCursorPosition() * m_fGridWidth;
+ uint x = PatternEditor::nMargin +
+ m_pPatternEditorPanel->getCursorPosition() * m_fGridWidth;
return QRect( x-m_fGridWidth*3, 3, m_fGridWidth*6, height()-6 );
}
@@ -1412,7 +1515,7 @@ void NotePropertiesRuler::onPreferencesChanged( H2Core::Preferences::Changes cha
if ( changes & ( H2Core::Preferences::Changes::Colors |
H2Core::Preferences::Changes::Font ) ) {
- m_bNeedsUpdate = true;
+ createBackground();
update();
}
}
diff --git a/src/gui/src/PatternEditor/NotePropertiesRuler.h b/src/gui/src/PatternEditor/NotePropertiesRuler.h
index e53fd5cc3f..847425a5a2 100644
--- a/src/gui/src/PatternEditor/NotePropertiesRuler.h
+++ b/src/gui/src/PatternEditor/NotePropertiesRuler.h
@@ -29,6 +29,7 @@
#include
#include
+#include
#include
#include
#include