Merge pull request #670 from diizy/master
PianoRoll: improvements for note/panning widget
This commit is contained in:
@@ -28,13 +28,14 @@
|
||||
#define PIANO_ROLL_H
|
||||
|
||||
#include <QtGui/QWidget>
|
||||
#include <QtGui/QInputDialog>
|
||||
|
||||
#include "ComboBoxModel.h"
|
||||
#include "SerializingObject.h"
|
||||
#include "note.h"
|
||||
#include "lmms_basics.h"
|
||||
#include "song.h"
|
||||
|
||||
#include "tooltip.h"
|
||||
|
||||
class QPainter;
|
||||
class QPixmap;
|
||||
@@ -111,6 +112,8 @@ protected:
|
||||
void selectAll();
|
||||
void getSelectedNotes( NoteVector & _selected_notes );
|
||||
|
||||
// for entering values with dblclick in the vol/pan bars
|
||||
void enterValue( note* n );
|
||||
|
||||
protected slots:
|
||||
void play();
|
||||
@@ -145,7 +148,6 @@ protected slots:
|
||||
void changeNoteEditMode( int i );
|
||||
void markSemiTone( int i );
|
||||
|
||||
|
||||
signals:
|
||||
void currentPatternChanged();
|
||||
void semiToneMarkerMenuScaleSetEnabled(bool);
|
||||
@@ -241,6 +243,7 @@ private:
|
||||
|
||||
static PianoRollKeyTypes prKeyOrder[];
|
||||
|
||||
static textFloat * s_textFloat;
|
||||
|
||||
QWidget * m_toolBar;
|
||||
|
||||
|
||||
@@ -67,7 +67,7 @@
|
||||
#include "text_float.h"
|
||||
#include "timeline.h"
|
||||
#include "tool_button.h"
|
||||
#include "tooltip.h"
|
||||
#include "text_float.h"
|
||||
|
||||
|
||||
typedef AutomationPattern::timeMap timeMap;
|
||||
@@ -126,6 +126,8 @@ QPixmap * PianoRoll::s_toolSelect = NULL;
|
||||
QPixmap * PianoRoll::s_toolMove = NULL;
|
||||
QPixmap * PianoRoll::s_toolOpen = NULL;
|
||||
|
||||
textFloat * PianoRoll::s_textFloat = NULL;
|
||||
|
||||
// used for drawing of piano
|
||||
PianoRoll::PianoRollKeyTypes PianoRoll::prKeyOrder[] =
|
||||
{
|
||||
@@ -275,6 +277,12 @@ PianoRoll::PianoRoll() :
|
||||
s_toolOpen = new QPixmap( embed::getIconPixmap(
|
||||
"automation" ) );
|
||||
}
|
||||
|
||||
// init text-float
|
||||
if( s_textFloat == NULL )
|
||||
{
|
||||
s_textFloat = new textFloat;
|
||||
}
|
||||
|
||||
setAttribute( Qt::WA_OpaquePaintEvent, true );
|
||||
|
||||
@@ -1420,6 +1428,7 @@ void PianoRoll::leaveEvent( QEvent * _e )
|
||||
}
|
||||
|
||||
QWidget::leaveEvent( _e );
|
||||
s_textFloat->hide();
|
||||
}
|
||||
|
||||
|
||||
@@ -1863,11 +1872,34 @@ void PianoRoll::mouseDoubleClickEvent( QMouseEvent * _me )
|
||||
return;
|
||||
}
|
||||
|
||||
// if they clicked in the note edit area, clear selection
|
||||
// if they clicked in the note edit area, enter value for the volume bar
|
||||
if( _me->x() > noteEditLeft() && _me->x() < noteEditRight()
|
||||
&& _me->y() > noteEditTop() && _me->y() < noteEditBottom() )
|
||||
{
|
||||
clearSelectedNotes();
|
||||
// get values for going through notes
|
||||
int pixel_range = 4;
|
||||
int x = _me->x() - WHITE_KEY_WIDTH;
|
||||
int ticks_start = ( x-pixel_range/2 ) *
|
||||
MidiTime::ticksPerTact() / m_ppt + m_currentPosition;
|
||||
int ticks_end = ( x+pixel_range/2 ) *
|
||||
MidiTime::ticksPerTact() / m_ppt + m_currentPosition;
|
||||
|
||||
// get note-vector of current pattern
|
||||
NoteVector notes;
|
||||
notes += m_pattern->notes();
|
||||
|
||||
// go through notes to figure out which one we want to change
|
||||
note * n = NULL;
|
||||
foreach( note * i, notes )
|
||||
{
|
||||
if( i->pos().getTicks() >= ticks_start
|
||||
&& i->pos().getTicks() <= ticks_end
|
||||
&& i->length().getTicks() != 0 )
|
||||
{
|
||||
n = i;
|
||||
}
|
||||
}
|
||||
if( n != NULL ) { enterValue( n ); }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2023,6 +2055,7 @@ void PianoRoll::computeSelectedNotes(bool shift)
|
||||
|
||||
void PianoRoll::mouseReleaseEvent( QMouseEvent * _me )
|
||||
{
|
||||
s_textFloat->hide();
|
||||
bool mustRepaint = false;
|
||||
|
||||
if( _me->button() & Qt::LeftButton )
|
||||
@@ -2184,7 +2217,7 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * _me )
|
||||
}
|
||||
}
|
||||
else if( ( edit_note == true || m_action == ActionChangeNoteProperty ) &&
|
||||
_me->buttons() & Qt::LeftButton )
|
||||
( _me->buttons() & Qt::LeftButton || _me->buttons() & Qt::MiddleButton ) )
|
||||
{
|
||||
// editing note properties
|
||||
|
||||
@@ -2194,38 +2227,66 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * _me )
|
||||
|
||||
// convert to ticks so that we can check which notes
|
||||
// are in the range
|
||||
int ticks_start = (x-pixel_range/2) *
|
||||
int ticks_start = ( x-pixel_range/2 ) *
|
||||
MidiTime::ticksPerTact() / m_ppt + m_currentPosition;
|
||||
int ticks_end = (x+pixel_range/2) *
|
||||
int ticks_end = ( x+pixel_range/2 ) *
|
||||
MidiTime::ticksPerTact() / m_ppt + m_currentPosition;
|
||||
|
||||
// get note-vector of current pattern
|
||||
const NoteVector & notes = m_pattern->notes();
|
||||
|
||||
// determine what volume/panning to set note to
|
||||
volume_t vol = tLimit<int>( MinVolume +
|
||||
( ( (float)noteEditBottom() ) - ( (float)_me->y() ) ) /
|
||||
( (float)( noteEditBottom() - noteEditTop() ) ) *
|
||||
( MaxVolume - MinVolume ),
|
||||
MinVolume, MaxVolume );
|
||||
panning_t pan = tLimit<int>( PanningLeft +
|
||||
( (float)( noteEditBottom() - _me->y() ) ) /
|
||||
( (float)( noteEditBottom() - noteEditTop() ) ) *
|
||||
( (float)( PanningRight - PanningLeft ) ),
|
||||
PanningLeft, PanningRight);
|
||||
|
||||
// if middle-click, set to defaults
|
||||
volume_t vol;
|
||||
panning_t pan;
|
||||
|
||||
if( _me->buttons() & Qt::LeftButton )
|
||||
{
|
||||
vol = tLimit<int>( MinVolume +
|
||||
( ( (float)noteEditBottom() ) - ( (float)_me->y() ) ) /
|
||||
( (float)( noteEditBottom() - noteEditTop() ) ) *
|
||||
( MaxVolume - MinVolume ),
|
||||
MinVolume, MaxVolume );
|
||||
pan = tLimit<int>( PanningLeft +
|
||||
( (float)( noteEditBottom() - _me->y() ) ) /
|
||||
( (float)( noteEditBottom() - noteEditTop() ) ) *
|
||||
( (float)( PanningRight - PanningLeft ) ),
|
||||
PanningLeft, PanningRight);
|
||||
}
|
||||
else
|
||||
{
|
||||
vol = DefaultVolume;
|
||||
pan = DefaultPanning;
|
||||
}
|
||||
|
||||
if( m_noteEditMode == NoteEditVolume )
|
||||
{
|
||||
m_lastNoteVolume = vol;
|
||||
//! \todo display velocity for MIDI-based instruments
|
||||
// possibly dBV values too? not sure if it makes sense for note volumes...
|
||||
s_textFloat->setText( tr("Volume: %1%").arg( vol ) );
|
||||
}
|
||||
else if( m_noteEditMode == NoteEditPanning )
|
||||
{
|
||||
m_lastNotePanning = pan;
|
||||
if( pan < 0 )
|
||||
{
|
||||
s_textFloat->setText( tr("Panning: %1% left").arg( qAbs( pan ) ) );
|
||||
}
|
||||
else if( pan > 0 )
|
||||
{
|
||||
s_textFloat->setText( tr("Panning: %1% right").arg( qAbs( pan ) ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
s_textFloat->setText( tr("Panning: center") );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
// loop through vector
|
||||
bool on_note = false;
|
||||
bool use_selection = isSelection();
|
||||
NoteVector::ConstIterator it = notes.begin()+notes.size()-1;
|
||||
for( int i = 0; i < notes.size(); ++i )
|
||||
@@ -2236,6 +2297,7 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * _me )
|
||||
&& n->length().getTicks() != 0
|
||||
&& ( n->selected() || ! use_selection ) )
|
||||
{
|
||||
on_note = true;
|
||||
m_pattern->dataChanged();
|
||||
|
||||
// play the note so that the user can tell how loud it is
|
||||
@@ -2269,10 +2331,22 @@ void PianoRoll::mouseMoveEvent( QMouseEvent * _me )
|
||||
}
|
||||
}
|
||||
|
||||
// set textfloat visible if we're on a note
|
||||
if( on_note )
|
||||
{
|
||||
s_textFloat->moveGlobal( this, QPoint( _me->x() + 4, _me->y() + 16 ) );
|
||||
s_textFloat->show();
|
||||
}
|
||||
else
|
||||
{
|
||||
s_textFloat->hide();
|
||||
}
|
||||
|
||||
--it;
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
else if( _me->buttons() == Qt::NoButton && m_editMode == ModeDraw )
|
||||
{
|
||||
// set move- or resize-cursor
|
||||
@@ -3262,6 +3336,69 @@ void PianoRoll::resizeEvent( QResizeEvent * )
|
||||
void PianoRoll::wheelEvent( QWheelEvent * _we )
|
||||
{
|
||||
_we->accept();
|
||||
// handle wheel events for note edit area - for editing note vol/pan with mousewheel
|
||||
if( _we->x() > noteEditLeft() && _we->x() < noteEditRight()
|
||||
&& _we->y() > noteEditTop() && _we->y() < noteEditBottom() )
|
||||
{
|
||||
// get values for going through notes
|
||||
int pixel_range = 8;
|
||||
int x = _we->x() - WHITE_KEY_WIDTH;
|
||||
int ticks_start = ( x-pixel_range/2 ) *
|
||||
MidiTime::ticksPerTact() / m_ppt + m_currentPosition;
|
||||
int ticks_end = ( x+pixel_range/2 ) *
|
||||
MidiTime::ticksPerTact() / m_ppt + m_currentPosition;
|
||||
|
||||
// get note-vector of current pattern
|
||||
NoteVector notes;
|
||||
notes += m_pattern->notes();
|
||||
|
||||
// go through notes to figure out which one we want to change
|
||||
note * n = NULL;
|
||||
foreach( note * i, notes )
|
||||
{
|
||||
if( i->pos().getTicks() >= ticks_start
|
||||
&& i->pos().getTicks() <= ticks_end
|
||||
&& i->length().getTicks() != 0 )
|
||||
{
|
||||
n = i;
|
||||
}
|
||||
}
|
||||
if( n != NULL )
|
||||
{
|
||||
const int step = _we->delta() > 0 ? 1.0 : -1.0;
|
||||
if( m_noteEditMode == NoteEditVolume )
|
||||
{
|
||||
volume_t vol = tLimit<int>( n->getVolume() + step, MinVolume, MaxVolume );
|
||||
n->setVolume( vol );
|
||||
s_textFloat->setText( tr("Volume: %1%").arg( vol ) );
|
||||
m_lastNoteVolume = vol;
|
||||
}
|
||||
else if( m_noteEditMode == NoteEditPanning )
|
||||
{
|
||||
panning_t pan = tLimit<int>( n->getPanning() + step, PanningLeft, PanningRight );
|
||||
n->setPanning( pan );
|
||||
if( pan < 0 )
|
||||
{
|
||||
s_textFloat->setText( tr("Panning: %1% left").arg( qAbs( pan ) ) );
|
||||
}
|
||||
else if( pan > 0 )
|
||||
{
|
||||
s_textFloat->setText( tr("Panning: %1% right").arg( qAbs( pan ) ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
s_textFloat->setText( tr("Panning: center") );
|
||||
}
|
||||
m_lastNotePanning = pan;
|
||||
}
|
||||
s_textFloat->moveGlobal( this, QPoint( _we->x() + 4, _we->y() + 16 ) );
|
||||
s_textFloat->setVisibilityTimeOut( 1000 );
|
||||
update();
|
||||
}
|
||||
}
|
||||
|
||||
// not in note edit area, so handle scrolling/zooming
|
||||
else
|
||||
if( _we->modifiers() & Qt::ControlModifier )
|
||||
{
|
||||
if( _we->delta() > 0 )
|
||||
@@ -3601,6 +3738,42 @@ void PianoRoll::getSelectedNotes( NoteVector & _selected_notes )
|
||||
}
|
||||
|
||||
|
||||
void PianoRoll::enterValue( note* n )
|
||||
{
|
||||
if( m_noteEditMode == NoteEditVolume )
|
||||
{
|
||||
bool ok;
|
||||
int new_val;
|
||||
new_val = QInputDialog::getInt( this, "Piano roll: note volume",
|
||||
tr( "Please enter a new value between %1 and %2:" ).
|
||||
arg( MinVolume ).arg( MaxVolume ),
|
||||
n->getVolume(),
|
||||
MinVolume, MaxVolume, 1, &ok );
|
||||
|
||||
if( ok )
|
||||
{
|
||||
n->setVolume( new_val );
|
||||
m_lastNoteVolume = new_val;
|
||||
}
|
||||
}
|
||||
else if( m_noteEditMode == NoteEditPanning )
|
||||
{
|
||||
bool ok;
|
||||
int new_val;
|
||||
new_val = QInputDialog::getInt( this, "Piano roll: note panning",
|
||||
tr( "Please enter a new value between %1 and %2:" ).
|
||||
arg( PanningLeft ).arg( PanningRight ),
|
||||
n->getPanning(),
|
||||
PanningLeft, PanningRight, 1, &ok );
|
||||
|
||||
if( ok )
|
||||
{
|
||||
n->setPanning( new_val );
|
||||
m_lastNotePanning = new_val;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void PianoRoll::copy_to_clipboard( const NoteVector & _notes ) const
|
||||
|
||||
Reference in New Issue
Block a user