changed the way selection works, and the general editing feel of piano roll. See ChangeLog for more details.
git-svn-id: https://lmms.svn.sf.net/svnroot/lmms/trunk/lmms@1837 0778d3d1-df1d-0410-868b-ea421aaaa00d
This commit is contained in:
4
AUTHORS
4
AUTHORS
@@ -14,6 +14,10 @@ Javier Serrano Polo
|
||||
<jasp00/at/terra/dot/es>
|
||||
development
|
||||
|
||||
Andrew Kelley
|
||||
<superjoe30/at/gmail/dot/com>
|
||||
development
|
||||
|
||||
Andreas Brandmaier
|
||||
<andy/at/brandmaier/dot/de>
|
||||
BitInvader plugin
|
||||
|
||||
25
ChangeLog
25
ChangeLog
@@ -1,3 +1,26 @@
|
||||
2008-11-13 Andrew Kelley <superjoe30/at/gmail/dot/com>
|
||||
* include/piano_roll.h
|
||||
* src/gui/piano_roll.cpp
|
||||
- changed the way selection works
|
||||
- you can select any combination of notes
|
||||
- delete any combination of notes
|
||||
- move multiple notes at once
|
||||
- resize multiple notes at once
|
||||
- etc
|
||||
- hold alt to disable quantization
|
||||
|
||||
* include/note.h
|
||||
* src/core/note.cpp
|
||||
- added m_selected so we know if the note is selected or not
|
||||
- added operator< so we can sort notes vector by start time
|
||||
|
||||
* include/pattern.h
|
||||
* src/tracks/pattern.cpp
|
||||
added rearrangeAllNotes for when we move multiple notes in the piano roll
|
||||
|
||||
* AUTHORS
|
||||
hope you don't mind :-)
|
||||
|
||||
2008-11-10 Tobias Doerffel <tobydox/at/users/dot/sourceforge/dot/net>
|
||||
|
||||
* src/core/track.cpp:
|
||||
@@ -25,7 +48,7 @@
|
||||
|
||||
2008-11-10 Andrew Kelley <superjoe30/at/gmail/dot/com>
|
||||
|
||||
* src/gui/piano_roll.h:
|
||||
* include/piano_roll.h:
|
||||
* src/gui/piano_roll.cpp:
|
||||
- made it so you can hold right click and drag around to delete notes
|
||||
- changed ctrl+click from opening automation window to shift+click
|
||||
|
||||
@@ -90,7 +90,8 @@ public:
|
||||
detuningHelper * _detuning = NULL );
|
||||
note( const note & _note );
|
||||
virtual ~note();
|
||||
|
||||
|
||||
void setSelected( const bool selected );
|
||||
void setLength( const midiTime & _length );
|
||||
void setPos( const midiTime & _pos );
|
||||
void setKey( const int _key );
|
||||
@@ -99,6 +100,16 @@ public:
|
||||
void quantizeLength( const int _q_grid );
|
||||
void quantizePos( const int _q_grid );
|
||||
|
||||
inline bool operator<(const note & rhs)
|
||||
{
|
||||
return m_pos < rhs.pos();
|
||||
}
|
||||
|
||||
inline bool getSelected( void ) const
|
||||
{
|
||||
return m_selected;
|
||||
}
|
||||
|
||||
inline midiTime endPos( void ) const
|
||||
{
|
||||
const int l = length();
|
||||
@@ -177,7 +188,7 @@ private:
|
||||
ChangePosition
|
||||
} ;*/
|
||||
|
||||
|
||||
bool m_selected; // for piano roll editing
|
||||
int m_key;
|
||||
volume m_volume;
|
||||
panning m_panning;
|
||||
|
||||
@@ -74,7 +74,7 @@ public:
|
||||
|
||||
note * rearrangeNote( const note * _note_to_proc,
|
||||
const bool _quant_pos = TRUE );
|
||||
|
||||
void rearrangeAllNotes( void );
|
||||
void clearNotes( void );
|
||||
|
||||
inline const noteVector & notes( void )
|
||||
|
||||
@@ -250,8 +250,10 @@ private:
|
||||
bool mouseOverNote( void );
|
||||
note * noteUnderMouse( void );
|
||||
noteVector::const_iterator noteIteratorUnderMouse( void );
|
||||
|
||||
|
||||
|
||||
// turn a selection rectangle into selected notes
|
||||
void computeSelectedNotes( bool shift );
|
||||
void clearSelectedNotes( void );
|
||||
|
||||
friend class engine;
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ note::note( const midiTime & _length, const midiTime & _pos,
|
||||
{
|
||||
//saveJournallingState( FALSE );
|
||||
// setJournalling( FALSE );
|
||||
|
||||
setSelected( false );
|
||||
if( _detuning )
|
||||
{
|
||||
m_detuning = sharedObject::ref( _detuning );
|
||||
@@ -71,6 +71,7 @@ note::note( const note & _note ) :
|
||||
m_length( _note.m_length ),
|
||||
m_pos( _note.m_pos )
|
||||
{
|
||||
setSelected( false );
|
||||
m_detuning = sharedObject::ref( _note.m_detuning );
|
||||
}
|
||||
|
||||
@@ -82,7 +83,10 @@ note::~note()
|
||||
sharedObject::unref( m_detuning );
|
||||
}
|
||||
|
||||
|
||||
void note::setSelected( const bool selected )
|
||||
{
|
||||
m_selected = selected;
|
||||
}
|
||||
|
||||
|
||||
void note::setLength( const midiTime & _length )
|
||||
|
||||
@@ -653,8 +653,27 @@ void pianoRoll::removeSelection( void )
|
||||
m_selectedTick = 0;
|
||||
m_selectStartKey = 0;
|
||||
m_selectedKeys = 0;
|
||||
|
||||
|
||||
}
|
||||
|
||||
void pianoRoll::clearSelectedNotes( void )
|
||||
{
|
||||
if( m_pattern != NULL )
|
||||
{
|
||||
// get note-vector of current pattern
|
||||
const noteVector & notes = m_pattern->notes();
|
||||
|
||||
// will be our iterator in the following loop
|
||||
noteVector::const_iterator it = notes.begin();
|
||||
while( it != notes.end() )
|
||||
{
|
||||
( *it )->setSelected( false );
|
||||
|
||||
++it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -895,6 +914,7 @@ void pianoRoll::keyReleaseEvent( QKeyEvent * _ke )
|
||||
switch( _ke->key() )
|
||||
{
|
||||
case Qt::Key_Control:
|
||||
computeSelectedNotes(_ke->modifiers() & Qt::ShiftModifier);
|
||||
m_editMode = m_ctrlMode;
|
||||
update();
|
||||
break;
|
||||
@@ -1065,6 +1085,8 @@ void pianoRoll::mousePressEvent( QMouseEvent * _me )
|
||||
{
|
||||
++it;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
|
||||
m_currentNote = *it;
|
||||
@@ -1098,8 +1120,16 @@ void pianoRoll::mousePressEvent( QMouseEvent * _me )
|
||||
// set move-cursor
|
||||
QCursor c( Qt::SizeAllCursor );
|
||||
QApplication::setOverrideCursor( c );
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
// if clicked on an unselected note, remove selection
|
||||
if( ! m_currentNote->getSelected() )
|
||||
{
|
||||
clearSelectedNotes();
|
||||
}
|
||||
|
||||
engine::getSong()->setModified();
|
||||
}
|
||||
else if( ( _me->buttons() == Qt::RightButton &&
|
||||
@@ -1135,6 +1165,10 @@ void pianoRoll::mousePressEvent( QMouseEvent * _me )
|
||||
m_action = ActionSelectNotes;
|
||||
|
||||
play_note = false;
|
||||
|
||||
// call mousemove to fix glitch where selection
|
||||
// appears in wrong spot on mousedown
|
||||
mouseMoveEvent( _me );
|
||||
}
|
||||
else if( _me->button() == Qt::RightButton &&
|
||||
m_editMode == ModeSelect )
|
||||
@@ -1183,11 +1217,121 @@ void pianoRoll::mousePressEvent( QMouseEvent * _me )
|
||||
}
|
||||
}
|
||||
|
||||
void pianoRoll::computeSelectedNotes(bool shift)
|
||||
{
|
||||
if( m_selectStartTick == 0 &&
|
||||
m_selectedTick == 0 &&
|
||||
m_selectStartKey == 0 &&
|
||||
m_selectedKeys == 0 )
|
||||
{
|
||||
// don't bother, there's no selection
|
||||
return;
|
||||
}
|
||||
|
||||
// setup selection-vars
|
||||
int sel_pos_start = m_selectStartTick;
|
||||
int sel_pos_end = m_selectStartTick+m_selectedTick;
|
||||
if( sel_pos_start > sel_pos_end )
|
||||
{
|
||||
qSwap<int>( sel_pos_start, sel_pos_end );
|
||||
}
|
||||
|
||||
int sel_key_start = m_selectStartKey - m_startKey + 1;
|
||||
int sel_key_end = sel_key_start + m_selectedKeys;
|
||||
if( sel_key_start > sel_key_end )
|
||||
{
|
||||
qSwap<int>( sel_key_start, sel_key_end );
|
||||
}
|
||||
|
||||
//int y_base = height() - PR_BOTTOM_MARGIN - m_notesEditHeight - 1;
|
||||
if( validPattern() == true )
|
||||
{
|
||||
const noteVector & notes = m_pattern->notes();
|
||||
|
||||
const int visible_keys = ( height() - PR_TOP_MARGIN -
|
||||
PR_BOTTOM_MARGIN - m_notesEditHeight ) /
|
||||
KEY_LINE_HEIGHT + 2;
|
||||
|
||||
QPolygon volumeHandles;
|
||||
|
||||
for( noteVector::const_iterator it = notes.begin();
|
||||
it != notes.end(); ++it )
|
||||
{
|
||||
// make a new selection unless they're holding shift
|
||||
if( ! shift )
|
||||
{
|
||||
( *it )->setSelected( false );
|
||||
}
|
||||
|
||||
Sint32 len_ticks = ( *it )->length();
|
||||
|
||||
if( len_ticks == 0 )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
else if( len_ticks < 0 )
|
||||
{
|
||||
len_ticks = 4;
|
||||
}
|
||||
|
||||
const int key = ( *it )->key() - m_startKey + 1;
|
||||
|
||||
Sint32 pos_ticks = ( *it )->pos();
|
||||
|
||||
int note_width = len_ticks * m_ppt /
|
||||
midiTime::ticksPerTact();
|
||||
const int x = ( pos_ticks - m_currentPosition ) *
|
||||
m_ppt / midiTime::ticksPerTact();
|
||||
// skip this note if not in visible area at all
|
||||
if( !( x + note_width >= 0 &&
|
||||
x <= width() - WHITE_KEY_WIDTH ) )
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
// is the note in visible area?
|
||||
if( key > 0 && key <= visible_keys )
|
||||
{
|
||||
// if the selection even barely overlaps the note
|
||||
if( key > sel_key_start &&
|
||||
key <= sel_key_end &&
|
||||
pos_ticks + len_ticks > sel_pos_start &&
|
||||
pos_ticks < sel_pos_end )
|
||||
{
|
||||
( *it )->setSelected( true );
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
removeSelection();
|
||||
update();
|
||||
}
|
||||
|
||||
|
||||
void pianoRoll::mouseReleaseEvent( QMouseEvent * _me )
|
||||
{
|
||||
if( _me->button() & Qt::LeftButton &&
|
||||
m_editMode == ModeSelect &&
|
||||
m_action == ActionSelectNotes )
|
||||
{
|
||||
// select the notes within the selection rectangle and
|
||||
// then destroy the selection rectangle
|
||||
|
||||
computeSelectedNotes( _me->modifiers() & Qt::ShiftModifier );
|
||||
|
||||
}
|
||||
else if( _me->button() & Qt::LeftButton &&
|
||||
m_action == ActionMoveNote )
|
||||
{
|
||||
// we moved one or more notes so they have to be
|
||||
// moved properly according to new starting-
|
||||
// time in the note-array of pattern
|
||||
|
||||
m_pattern->rearrangeAllNotes();
|
||||
}
|
||||
|
||||
if( validPattern() == true )
|
||||
{
|
||||
if( m_action == ActionChangeNoteVolume && m_currentNote != NULL )
|
||||
@@ -1364,46 +1508,88 @@ void pianoRoll::mouseMoveEvent( QMouseEvent * _me )
|
||||
else if( m_currentNote != NULL &&
|
||||
_me->buttons() & Qt::LeftButton && m_editMode == ModeDraw )
|
||||
{
|
||||
int key_num = getKey( _me->y() );
|
||||
//int key_num = getKey( _me->y() );
|
||||
int key_offset = getKey( _me->y() ) - m_currentNote->key();
|
||||
|
||||
if( m_action == ActionMoveNote )
|
||||
{
|
||||
x -= m_moveXOffset;
|
||||
}
|
||||
int pos_ticks = x * midiTime::ticksPerTact() / m_ppt +
|
||||
m_currentPosition;
|
||||
if( m_action == ActionMoveNote )
|
||||
{
|
||||
// moving note
|
||||
if( pos_ticks < 0 )
|
||||
{
|
||||
pos_ticks = 0;
|
||||
}
|
||||
m_currentNote->setPos( midiTime(
|
||||
pos_ticks ) );
|
||||
m_currentNote->setKey( key_num );
|
||||
|
||||
|
||||
int pos_ticks_offset = x * midiTime::ticksPerTact() / m_ppt +
|
||||
m_currentPosition - m_currentNote->pos().getTicks();
|
||||
|
||||
|
||||
// get note-vector of current pattern
|
||||
const noteVector & notes = m_pattern->notes();
|
||||
|
||||
// we moved the note so the note has to be
|
||||
// moved properly according to new starting-
|
||||
// time in the note-array of pattern
|
||||
m_currentNote = m_pattern->rearrangeNote(
|
||||
m_currentNote );
|
||||
}
|
||||
else
|
||||
// will be our iterator in the following loop
|
||||
noteVector::const_iterator it = notes.begin();
|
||||
while( it != notes.end() )
|
||||
{
|
||||
// resizing note
|
||||
int ticks_diff = pos_ticks -
|
||||
m_currentNote->pos();
|
||||
if( ticks_diff <= 0 )
|
||||
if( ( *it )->getSelected() || m_currentNote == *it )
|
||||
{
|
||||
ticks_diff = 1;
|
||||
|
||||
if( m_action == ActionMoveNote )
|
||||
{
|
||||
// moving note
|
||||
int pos_ticks = midiTime( ( *it )->pos().getTicks()
|
||||
+ pos_ticks_offset );
|
||||
int key_num = ( *it )->key() + key_offset;
|
||||
|
||||
if( pos_ticks < 0 )
|
||||
{
|
||||
pos_ticks = 0;
|
||||
}
|
||||
// TODO: upper/lower bound checks on key_num
|
||||
/*if( key_num < lower bound )
|
||||
{
|
||||
key_num = lower bound
|
||||
}
|
||||
else if( key_num > upper bound )
|
||||
{
|
||||
key_num = upper bound
|
||||
} */
|
||||
|
||||
( *it )->setPos( midiTime(
|
||||
pos_ticks ) );
|
||||
( *it )->setKey( key_num );
|
||||
if( ! ( _me->modifiers() & Qt::AltModifier ) )
|
||||
{
|
||||
( *it )->quantizePos( quantization() );
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
else
|
||||
{
|
||||
// resizing note
|
||||
//int pos_ticks = midiTime( ( *it )->pos().getTicks()
|
||||
// + pos_ticks_offset );
|
||||
//int ticks_diff = pos_ticks -
|
||||
// m_currentNote->pos();
|
||||
|
||||
int ticks_diff = pos_ticks_offset;
|
||||
|
||||
if( ticks_diff <= 0 )
|
||||
{
|
||||
ticks_diff = 1;
|
||||
}
|
||||
( *it )->setLength( midiTime( ticks_diff ) );
|
||||
|
||||
if( ! ( _me->modifiers() & Qt::AltModifier ) )
|
||||
{
|
||||
( *it )->quantizeLength( quantization() );
|
||||
}
|
||||
|
||||
m_lenOfNewNotes = ( *it )->length();
|
||||
m_pattern->dataChanged();
|
||||
}
|
||||
}
|
||||
m_currentNote->setLength( midiTime( ticks_diff ) );
|
||||
m_currentNote->quantizeLength( quantization() );
|
||||
m_lenOfNewNotes = m_currentNote->length();
|
||||
m_pattern->dataChanged();
|
||||
++it;
|
||||
}
|
||||
|
||||
|
||||
engine::getSong()->setModified();
|
||||
|
||||
}
|
||||
@@ -2099,7 +2285,9 @@ void pianoRoll::paintEvent( QPaintEvent * _pe )
|
||||
// is the note in visible area?
|
||||
if( key > 0 && key <= visible_keys )
|
||||
{
|
||||
bool is_selected = false;
|
||||
/* changing the way selection works
|
||||
|
||||
bool is_selected = false;
|
||||
// if we're in move-mode, we may only draw notes
|
||||
// in selected area, that have originally been
|
||||
// selected and not notes that are now in
|
||||
@@ -2122,13 +2310,14 @@ void pianoRoll::paintEvent( QPaintEvent * _pe )
|
||||
{
|
||||
is_selected = true;
|
||||
}
|
||||
*/
|
||||
|
||||
// we've done and checked all, lets draw the
|
||||
// we've done and checked all, let's draw the
|
||||
// note
|
||||
drawNoteRect( p, x + WHITE_KEY_WIDTH,
|
||||
y_base - key * KEY_LINE_HEIGHT,
|
||||
note_width,
|
||||
is_selected,
|
||||
( *it )->getSelected(),
|
||||
( *it )->length() < 0 );
|
||||
}
|
||||
// draw volume-line of note
|
||||
@@ -2716,7 +2905,34 @@ void pianoRoll::deleteSelectedNotes( void )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
bool update_after_delete = false;
|
||||
|
||||
|
||||
// get note-vector of current pattern
|
||||
const noteVector & notes = m_pattern->notes();
|
||||
|
||||
// will be our iterator in the following loop
|
||||
noteVector::const_iterator it = notes.begin();
|
||||
while( it != notes.end() )
|
||||
{
|
||||
if( ( *it )->getSelected() )
|
||||
{
|
||||
// delete this note
|
||||
m_pattern->removeNote( ( *it ) );
|
||||
update_after_delete = true;
|
||||
|
||||
// start over, make sure we get all the notes
|
||||
it = notes.begin();
|
||||
}
|
||||
else
|
||||
{
|
||||
++it;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*
|
||||
noteVector selected_notes;
|
||||
getSelectedNotes( selected_notes );
|
||||
|
||||
@@ -2726,7 +2942,7 @@ void pianoRoll::deleteSelectedNotes( void )
|
||||
{
|
||||
m_pattern->removeNote( selected_notes.front() );
|
||||
selected_notes.erase( selected_notes.begin() );
|
||||
}
|
||||
} */
|
||||
|
||||
if( update_after_delete == true )
|
||||
{
|
||||
@@ -2734,6 +2950,7 @@ void pianoRoll::deleteSelectedNotes( void )
|
||||
update();
|
||||
engine::getSongEditor()->update();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -269,7 +269,12 @@ note * pattern::rearrangeNote( const note * _note_to_proc,
|
||||
return( addNote( copy_of_note, _quant_pos ) );
|
||||
}
|
||||
|
||||
|
||||
void pattern::rearrangeAllNotes( void )
|
||||
{
|
||||
// sort notes by start time
|
||||
qSort(m_notes.begin(), m_notes.end());
|
||||
|
||||
}
|
||||
|
||||
|
||||
void pattern::clearNotes( void )
|
||||
|
||||
Reference in New Issue
Block a user