Files
lmms/src/core/automation_editor.cpp
T

2260 lines
49 KiB
C++

#ifndef SINGLE_SOURCE_COMPILE
/*
* automation_editor.cpp - implementation of automationEditor which is used for
* actual setting of dynamic values
*
* Copyright (c) 2006-2007 Javier Serrano Polo <jasp00/at/users.sourceforge.net>
*
* This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public
* License as published by the Free Software Foundation; either
* version 2 of the License, or (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
* General Public License for more details.
*
* You should have received a copy of the GNU General Public
* License along with this program (see COPYING); if not, write to the
* Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor,
* Boston, MA 02110-1301 USA.
*
*/
#include "qt3support.h"
#ifdef QT4
#include <Qt/QtXml>
#include <QtGui/QApplication>
#include <QtGui/QButtonGroup>
#include <QtGui/QPainter>
#include <QtGui/QKeyEvent>
#include <QtGui/QWheelEvent>
#include <QtGui/QLayout>
#include <QtGui/QLabel>
#else
#include <qapplication.h>
#include <qbuttongroup.h>
#include <qpainter.h>
#include <qlayout.h>
#include <qlabel.h>
#include <qdom.h>
#define addButton insert
#define setCheckable setToggleButton
#endif
#ifndef __USE_XOPEN
#define __USE_XOPEN
#endif
#include <math.h>
#include "automation_editor.h"
#include "song_editor.h"
#include "main_window.h"
#include "embed.h"
#include "pixmap_button.h"
#include "templates.h"
#include "gui_templates.h"
#include "timeline.h"
#include "tooltip.h"
#include "midi.h"
#include "tool_button.h"
#include "text_float.h"
#include "combobox.h"
#include "bb_editor.h"
#include "piano_roll.h"
QPixmap * automationEditor::s_toolDraw = NULL;
QPixmap * automationEditor::s_toolErase = NULL;
QPixmap * automationEditor::s_toolSelect = NULL;
QPixmap * automationEditor::s_toolMove = NULL;
automationEditor::automationEditor( engine * _engine ) :
QWidget( _engine->getMainWindow()->workspace() ),
journallingObject( _engine ),
m_paintPixmap(),
m_pattern( NULL ),
m_min_level( 0 ),
m_max_level( 0 ),
m_scroll_level( 0 ),
m_bottom_level( 0 ),
m_top_level( 0 ),
m_currentPosition(),
m_action( NONE ),
m_moveStartLevel( 0 ),
m_moveStartTact64th( 0 ),
m_ppt( DEFAULT_PPT ),
m_y_delta( DEFAULT_Y_DELTA ),
m_y_auto( TRUE ),
m_editMode( DRAW ),
m_scrollBack( FALSE )
{
// init pixmaps
if( s_toolDraw == NULL )
{
s_toolDraw = new QPixmap( embed::getIconPixmap(
"edit_draw" ) );
}
if( s_toolErase == NULL )
{
s_toolErase= new QPixmap( embed::getIconPixmap(
"edit_erase" ) );
}
if( s_toolSelect == NULL )
{
s_toolSelect = new QPixmap( embed::getIconPixmap(
"edit_select" ) );
}
if( s_toolMove == NULL )
{
s_toolMove = new QPixmap( embed::getIconPixmap(
"edit_move" ) );
}
#ifdef QT4
// add us to workspace
eng()->getMainWindow()->workspace()->addWindow( this );
#endif
// add time-line
m_timeLine = new timeLine( VALUES_WIDTH, 32, m_ppt,
eng()->getSongEditor()->getPlayPos(
songEditor::PLAY_AUTOMATION_PATTERN ),
m_currentPosition, this, eng() );
connect( this, SIGNAL( positionChanged( const midiTime & ) ),
m_timeLine, SLOT( updatePosition( const midiTime & ) ) );
connect( m_timeLine, SIGNAL( positionChanged( const midiTime & ) ),
this, SLOT( updatePosition( const midiTime & ) ) );
m_toolBar = new QWidget( this );
m_toolBar->setFixedHeight( 32 );
m_toolBar->move( 0, 0 );
#ifdef QT4
m_toolBar->setAutoFillBackground( TRUE );
QPalette pal;
pal.setBrush( m_toolBar->backgroundRole(),
embed::getIconPixmap( "toolbar_bg" ) );
m_toolBar->setPalette( pal );
#else
m_toolBar->setPaletteBackgroundPixmap( embed::getIconPixmap(
"toolbar_bg" ) );
#endif
QHBoxLayout * tb_layout = new QHBoxLayout( m_toolBar );
tb_layout->setMargin( 0 );
tb_layout->setSpacing( 0 );
// init control-buttons at the top
m_playButton = new toolButton( embed::getIconPixmap( "play" ),
tr( "Play/pause current pattern (Space)" ),
this, SLOT( play() ), m_toolBar );
m_stopButton = new toolButton( embed::getIconPixmap( "stop" ),
tr( "Stop playing of current pattern (Space)" ),
this, SLOT( stop() ), m_toolBar );
#ifdef QT4
m_playButton->setWhatsThis(
#else
QWhatsThis::add( m_playButton,
#endif
tr( "Click here, if you want to play the current pattern. "
"This is useful while editing it. The pattern is "
"automatically looped when its end is reached." ) );
#ifdef QT4
m_stopButton->setWhatsThis(
#else
QWhatsThis::add( m_stopButton,
#endif
tr( "Click here, if you want to stop playing of current "
"pattern." ) );
removeSelection();
// init scrollbars
m_leftRightScroll = new QScrollBar( Qt::Horizontal, this );
m_topBottomScroll = new QScrollBar( Qt::Vertical, this );
connect( m_leftRightScroll, SIGNAL( valueChanged( int ) ), this,
SLOT( horScrolled( int ) ) );
connect( m_topBottomScroll, SIGNAL( valueChanged( int ) ), this,
SLOT( verScrolled( int ) ) );
// init edit-buttons at the top
m_drawButton = new toolButton( embed::getIconPixmap( "edit_draw" ),
tr( "Draw mode (Shift+D)" ),
this, SLOT( drawButtonToggled() ),
m_toolBar );
m_drawButton->setCheckable( TRUE );
m_drawButton->setChecked( TRUE );
m_eraseButton = new toolButton( embed::getIconPixmap( "edit_erase" ),
tr( "Erase mode (Shift+E)" ),
this, SLOT( eraseButtonToggled() ),
m_toolBar );
m_eraseButton->setCheckable( TRUE );
m_selectButton = new toolButton( embed::getIconPixmap(
"edit_select" ),
tr( "Select mode (Shift+S)" ),
this, SLOT( selectButtonToggled() ),
m_toolBar );
m_selectButton->setCheckable( TRUE );
m_moveButton = new toolButton( embed::getIconPixmap( "edit_move" ),
tr( "Move selection mode (Shift+M)" ),
this, SLOT( moveButtonToggled() ),
m_toolBar );
m_moveButton->setCheckable( TRUE );
QButtonGroup * tool_button_group = new QButtonGroup( this );
tool_button_group->addButton( m_drawButton );
tool_button_group->addButton( m_eraseButton );
tool_button_group->addButton( m_selectButton );
tool_button_group->addButton( m_moveButton );
tool_button_group->setExclusive( TRUE );
#ifndef QT4
tool_button_group->hide();
#endif
#ifdef QT4
m_drawButton->setWhatsThis(
#else
QWhatsThis::add( m_drawButton,
#endif
tr( "If you click here, draw-mode will be activated. In this "
"mode you can add and move single values. This "
"is the default-mode which is used most of the time. "
"You can also press 'Shift+D' on your keyboard to "
"activate this mode." ) );
#ifdef QT4
m_eraseButton->setWhatsThis(
#else
QWhatsThis::add( m_eraseButton,
#endif
tr( "If you click here, erase-mode will be activated. In this "
"mode you can erase single values. You can also press "
"'Shift+E' on your keyboard to activate this mode." ) );
#ifdef QT4
m_selectButton->setWhatsThis(
#else
QWhatsThis::add( m_selectButton,
#endif
tr( "If you click here, select-mode will be activated. In this "
"mode you can select values. This is neccessary "
"if you want to cut, copy, paste, delete or move "
"values. You can also press 'Shift+S' on your keyboard "
"to activate this mode." ) );
#ifdef QT4
m_moveButton->setWhatsThis(
#else
QWhatsThis::add( m_moveButton,
#endif
tr( "If you click here, move-mode will be activated. In this "
"mode you can move the values you selected in select-"
"mode. You can also press 'Shift+M' on your keyboard "
"to activate this mode." ) );
m_cutButton = new toolButton( embed::getIconPixmap( "edit_cut" ),
tr( "Cut selected values (Ctrl+X)" ),
this, SLOT( cutSelectedValues() ),
m_toolBar );
m_copyButton = new toolButton( embed::getIconPixmap( "edit_copy" ),
tr( "Copy selected values (Ctrl+C)" ),
this, SLOT( copySelectedValues() ),
m_toolBar );
m_pasteButton = new toolButton( embed::getIconPixmap( "edit_paste" ),
tr( "Paste values from clipboard "
"(Ctrl+V)" ),
this, SLOT( pasteValues() ),
m_toolBar );
#ifdef QT4
m_cutButton->setWhatsThis(
#else
QWhatsThis::add( m_cutButton,
#endif
tr( "If you click here, selected values will be cut into the "
"clipboard. You can paste them anywhere in any pattern "
"by clicking on the paste-button." ) );
#ifdef QT4
m_copyButton->setWhatsThis(
#else
QWhatsThis::add( m_copyButton,
#endif
tr( "If you click here, selected values will be copied into "
"the clipboard. You can paste them anywhere in any "
"pattern by clicking on the paste-button." ) );
#ifdef QT4
m_pasteButton->setWhatsThis(
#else
QWhatsThis::add( m_pasteButton,
#endif
tr( "If you click here, the values from the clipboard will be "
"pasted at the first visible tact." ) );
// setup zooming-stuff
QLabel * zoom_x_lbl = new QLabel( m_toolBar );
zoom_x_lbl->setPixmap( embed::getIconPixmap( "zoom_x" ) );
m_zoomingXComboBox = new comboBox( m_toolBar, NULL, eng(), NULL );
m_zoomingXComboBox->setFixedSize( 80, 22 );
for( int i = 0; i < 6; ++i )
{
m_zoomingXComboBox->addItem( QString::number( 25 << i ) + "%" );
}
m_zoomingXComboBox->setValue( m_zoomingXComboBox->findText( "100%" ) );
connect( m_zoomingXComboBox, SIGNAL( activated( const QString & ) ),
this, SLOT( zoomingXChanged( const QString & ) ) );
QLabel * zoom_y_lbl = new QLabel( m_toolBar );
zoom_y_lbl->setPixmap( embed::getIconPixmap( "zoom_y" ) );
m_zoomingYComboBox = new comboBox( m_toolBar, NULL, eng(), NULL );
m_zoomingYComboBox->setFixedSize( 80, 22 );
m_zoomingYComboBox->addItem( "Auto" );
for( int i = 0; i < 6; ++i )
{
m_zoomingYComboBox->addItem( QString::number( 25 << i ) + "%" );
}
m_zoomingYComboBox->setValue( m_zoomingYComboBox->findText( "Auto" ) );
connect( m_zoomingYComboBox, SIGNAL( activated( const QString & ) ),
this, SLOT( zoomingYChanged( const QString & ) ) );
// setup quantize-stuff
QLabel * quantize_lbl = new QLabel( m_toolBar );
quantize_lbl->setPixmap( embed::getIconPixmap( "quantize" ) );
m_quantizeComboBox = new comboBox( m_toolBar, NULL, eng(), NULL );
m_quantizeComboBox->setFixedSize( 60, 22 );
for( int i = 0; i < 7; ++i )
{
m_quantizeComboBox->addItem( "1/" + QString::number( 1 << i ) );
}
m_quantizeComboBox->setValue( m_quantizeComboBox->findText(
"1/16" ) );
tb_layout->addSpacing( 5 );
tb_layout->addWidget( m_playButton );
tb_layout->addWidget( m_stopButton );
tb_layout->addSpacing( 10 );
tb_layout->addWidget( m_drawButton );
tb_layout->addWidget( m_eraseButton );
tb_layout->addWidget( m_selectButton );
tb_layout->addWidget( m_moveButton );
tb_layout->addSpacing( 10 );
tb_layout->addWidget( m_cutButton );
tb_layout->addWidget( m_copyButton );
tb_layout->addWidget( m_pasteButton );
tb_layout->addSpacing( 10 );
m_timeLine->addToolButtons( m_toolBar );
tb_layout->addSpacing( 15 );
tb_layout->addWidget( zoom_x_lbl );
tb_layout->addSpacing( 4 );
tb_layout->addWidget( m_zoomingXComboBox );
tb_layout->addSpacing( 10 );
tb_layout->addWidget( zoom_y_lbl );
tb_layout->addSpacing( 4 );
tb_layout->addWidget( m_zoomingYComboBox );
tb_layout->addSpacing( 10 );
tb_layout->addWidget( quantize_lbl );
tb_layout->addSpacing( 4 );
tb_layout->addWidget( m_quantizeComboBox );
tb_layout->addStretch();
// setup our actual window
setWindowIcon( embed::getIconPixmap( "automation" ) );
resize( INITIAL_WIDTH, INITIAL_HEIGHT );
setCurrentPattern( NULL );
#ifndef QT4
setBackgroundMode( Qt::NoBackground );
#endif
setMouseTracking( TRUE );
hide();
}
automationEditor::~automationEditor()
{
}
void automationEditor::setCurrentPattern( automationPattern * _new_pattern )
{
m_pattern = _new_pattern;
m_currentPosition = 0;
if( validPattern() == FALSE )
{
setWindowTitle( tr( "Automation Editor - no pattern" ) );
m_min_level = m_max_level = m_scroll_level = 0;
resizeEvent( NULL );
return;
}
m_min_level = m_pattern->object()->minLevel();
m_max_level = m_pattern->object()->maxLevel();
m_scroll_level = ( m_min_level + m_max_level ) / 2;
timeMap & time_map = m_pattern->getTimeMap();
//TODO: This is currently unused
int central_key = 0;
if( !time_map.isEmpty() )
{
// determine the central key so that we can scroll to it
int total_values = 0;
for( timeMap::iterator it = time_map.begin();
it != time_map.end(); ++it )
{
#ifdef QT3
central_key += it.data();
#else
central_key += it.value();
#endif
++total_values;
}
}
// resizeEvent() does the rest for us (scrolling, range-checking
// of levels and so on...)
resizeEvent( NULL );
setWindowTitle( tr( "Automation Editor - %1" ).arg(
m_pattern->name() ) );
update();
}
void automationEditor::saveSettings( QDomDocument & _doc, QDomElement & _this )
{
mainWindow::saveWidgetState( this, _this );
}
void automationEditor::loadSettings( const QDomElement & _this )
{
mainWindow::restoreWidgetState( this, _this );
}
inline void automationEditor::drawValueRect( QPainter & _p,
Uint16 _x, Uint16 _y,
Sint16 _width, Sint16 _height,
const bool _is_selected )
{
QColor current_color( 0xFF, 0xB0, 0x00 );
if( _is_selected == TRUE )
{
current_color.setRgb( 0x00, 0x40, 0xC0 );
}
_p.fillRect( _x, _y, _width, _height, current_color );
_p.setPen( QColor( 0xFF, 0xDF, 0x20 ) );
_p.drawLine( _x - 1, _y, _x + 1, _y );
_p.drawLine( _x, _y - 1, _x, _y + 1 );
// _p.setPen( QColor( 0xFF, 0x9F, 0x00 ) );
// _p.setPen( QColor( 0xFF, 0xFF, 0x40 ) );
}
void automationEditor::updatePaintPixmap( void )
{
if( m_paintPixmap.isNull() == TRUE || m_paintPixmap.size() != size() )
{
m_paintPixmap = QPixmap( size() );
}
m_paintPixmap.fill( QColor( 0, 0, 0 ) );
QPainter p( &m_paintPixmap );
// set font-size to 8
p.setFont( pointSize<8>( p.font() ) );
int grid_height = height() - TOP_MARGIN - SCROLLBAR_SIZE;
// start drawing at the bottom
int grid_bottom = height() - SCROLLBAR_SIZE - 1;
p.fillRect( 0, TOP_MARGIN, VALUES_WIDTH, height() - TOP_MARGIN,
QColor( 0x33, 0x33, 0x33 ) );
// print value numbers
int font_height = p.fontMetrics().height();
Qt::Alignment text_flags =
(Qt::Alignment)( Qt::AlignRight | Qt::AlignVCenter );
if( m_pattern )
{
if( m_y_auto )
{
int y[] = { grid_bottom, TOP_MARGIN + font_height / 2 };
int level[] = { m_min_level, m_max_level };
for( int i = 0; i < 2; ++i )
{
const QString & label = m_pattern->object()
->levelToLabel( level[i] );
p.setPen( QColor( 240, 240, 240 ) );
p.drawText( 1, y[i] - font_height + 1,
VALUES_WIDTH - 10, 2 * font_height,
text_flags, label );
p.setPen( QColor( 0, 0, 0 ) );
p.drawText( 0, y[i] - font_height,
VALUES_WIDTH - 10, 2 * font_height,
text_flags, label );
}
}
else
{
int y = grid_bottom;
int level = m_bottom_level;
int printable = tMax( 1, 5 * DEFAULT_Y_DELTA
/ m_y_delta );
int module = level % printable;
if( module )
{
int inv_module = ( printable - module )
% printable;
y -= inv_module * m_y_delta;
level += inv_module;
}
for( ; y >= TOP_MARGIN && level <= m_top_level;
y -= printable * m_y_delta, level += printable )
{
const QString & label = m_pattern->object()
->levelToLabel( level );
p.setPen( QColor( 240, 240, 240 ) );
p.drawText( 1, y - font_height + 1,
VALUES_WIDTH - 10, 2 * font_height,
text_flags, label );
p.setPen( QColor( 0, 0, 0 ) );
p.drawText( 0, y - font_height,
VALUES_WIDTH - 10, 2 * font_height,
text_flags, label );
}
}
}
// set clipping area, because we are not allowed to paint over
// keyboard...
p.setClipRect( VALUES_WIDTH, TOP_MARGIN, width() - VALUES_WIDTH,
grid_height );
// draw vertical raster
int tact_16th = m_currentPosition / 4;
const int offset = ( m_currentPosition % 4 ) * m_ppt /
DEFAULT_STEPS_PER_TACT / 4;
if( m_pattern )
{
int x_line_end = m_y_auto || m_top_level < m_max_level ?
TOP_MARGIN :
grid_bottom - ( m_top_level - m_bottom_level )
* m_y_delta;
for( int x = VALUES_WIDTH - offset; x < width();
x += m_ppt / DEFAULT_STEPS_PER_TACT, ++tact_16th )
{
if( x >= VALUES_WIDTH )
{
// every tact-start needs to be a bright line
if( tact_16th % 16 == 0 )
{
p.setPen( QColor( 0x7F, 0x7F, 0x7F ) );
}
// normal line
else if( tact_16th % 4 == 0 )
{
p.setPen( QColor( 0x5F, 0x5F, 0x5F ) );
}
// weak line
else
{
p.setPen( QColor( 0x3F, 0x3F, 0x3F ) );
}
p.drawLine( x, grid_bottom, x, x_line_end );
}
}
if( m_y_auto )
{
QPen pen( QColor( 0x4F, 0x4F, 0x4F ) );
p.setPen( pen );
p.drawLine( VALUES_WIDTH, grid_bottom, width(),
grid_bottom );
pen.setStyle( Qt::DotLine );
p.setPen( pen );
float y_delta = ( grid_bottom - TOP_MARGIN ) / 8.0f;
for( int i = 1; i < 8; ++i )
{
int y = (int)( grid_bottom - i * y_delta );
p.drawLine( VALUES_WIDTH, y, width(), y );
}
}
else
{
for( int y = grid_bottom, level = m_bottom_level;
y >= TOP_MARGIN && level <= m_top_level;
y -= m_y_delta, ++level )
{
if( level % 5 == 0 )
{
p.setPen( QColor( 0x4F, 0x4F, 0x4F ) );
}
else
{
p.setPen( QColor( 0x3F, 0x3F, 0x3F ) );
}
// draw level line
p.drawLine( VALUES_WIDTH, y, width(), y );
}
}
}
// following code draws all visible values
// setup selection-vars
int sel_pos_start = m_selectStartTact64th;
int sel_pos_end = m_selectStartTact64th + m_selectedTact64th;
if( sel_pos_start > sel_pos_end )
{
qSwap<int>( sel_pos_start, sel_pos_end );
}
int sel_level_start = m_selectStartLevel;
int sel_level_end = sel_level_start + m_selectedLevels;
if( sel_level_start > sel_level_end )
{
qSwap<int>( sel_level_start, sel_level_end );
}
if( validPattern() == TRUE )
{
timeMap & time_map = m_pattern->getTimeMap();
timeMap::iterator it = time_map.end();
do
{
--it;
Sint32 len_tact_64th = 4;
#ifdef QT3
const int level = it.data();
#else
const int level = it.value();
#endif
Sint32 pos_tact_64th = -it.key();
const int x = ( pos_tact_64th - m_currentPosition ) *
m_ppt / 64;
if( x > width() - VALUES_WIDTH )
{
break;
}
int rect_width;
if( it != time_map.begin() )
{
timeMap::iterator it_prev = it;
--it_prev;
Sint32 next_pos_tact_64th = -it_prev.key();
int next_x = ( next_pos_tact_64th
- m_currentPosition ) * m_ppt / 64;
// skip this value if not in visible area at all
if( next_x < 0 )
{
continue;
}
rect_width = next_x - x;
}
else
{
rect_width = width() - x;
}
// is the value in visible area?
if( ( level >= m_bottom_level && level <= m_top_level )
|| ( level > m_top_level && m_top_level >= 0 )
|| ( level < m_bottom_level
&& m_bottom_level <= 0 ) )
{
bool is_selected = FALSE;
// if we're in move-mode, we may only draw
// values in selected area, that have originally
// been selected and not values that are now in
// selection because the user moved it...
if( m_editMode == MOVE )
{
if( m_selValuesForMove.contains(
it.key() ) )
{
is_selected = TRUE;
}
}
else if( level > sel_level_start &&
level <= sel_level_end &&
pos_tact_64th >= sel_pos_start &&
pos_tact_64th + len_tact_64th <=
sel_pos_end )
{
is_selected = TRUE;
}
// we've done and checked all, lets draw the
// value
int y_start;
int rect_height;
if( m_y_auto )
{
y_start = grid_bottom
- ( grid_bottom - TOP_MARGIN )
* ( level - m_min_level )
/ ( m_max_level - m_min_level );
int y_end = grid_bottom
+ ( grid_bottom - TOP_MARGIN )
* m_min_level
/ ( m_max_level - m_min_level );
rect_height = y_end - y_start;
}
else
{
y_start = grid_bottom - ( level
- m_bottom_level )
* m_y_delta;
rect_height = level * m_y_delta;
}
drawValueRect( p, x + VALUES_WIDTH, y_start,
rect_width, rect_height,
is_selected );
}
} while( it != time_map.begin() );
}
else
{
QFont f = p.font();
f.setBold( TRUE );
p.setFont( pointSize<14>( f ) );
p.setPen( QColor( 0, 255, 0 ) );
p.drawText( VALUES_WIDTH + 20, TOP_MARGIN + 40,
width() - VALUES_WIDTH - 20 - SCROLLBAR_SIZE,
grid_height - 40, Qt::TextWordWrap,
tr( "Please open an automation pattern with "
"the context menu of a control!" ) );
}
p.setClipRect( VALUES_WIDTH, TOP_MARGIN, width() - VALUES_WIDTH,
grid_height );
// now draw selection-frame
int x = ( sel_pos_start - m_currentPosition ) * m_ppt / 64;
int w = ( sel_pos_end - sel_pos_start ) * m_ppt / 64;
int y, h;
if( m_y_auto )
{
y = grid_bottom - (int)roundf( ( grid_bottom - TOP_MARGIN )
* ( sel_level_start - m_min_level )
/ (float)( m_max_level - m_min_level ) );
h = grid_bottom - (int)roundf( ( grid_bottom - TOP_MARGIN )
* ( sel_level_end - m_min_level )
/ (float)( m_max_level - m_min_level ) ) - y;
}
else
{
y = grid_bottom - ( sel_level_start - m_bottom_level )
* m_y_delta;
h = ( sel_level_start - sel_level_end ) * m_y_delta;
}
p.setPen( QColor( 0, 64, 192 ) );
p.drawRect( x + VALUES_WIDTH, y, w, h );
int l = ( validPattern() == TRUE )? (int) m_pattern->length() : 0;
// reset scroll-range
m_leftRightScroll->setRange( 0, l );
#ifdef QT4
m_leftRightScroll->setSingleStep( 1 );
m_leftRightScroll->setPageStep( l );
#else
m_leftRightScroll->setSteps( 1, l );
#endif
}
void automationEditor::removeSelection( void )
{
m_selectStartTact64th = 0;
m_selectedTact64th = 0;
m_selectStartLevel = 0;
m_selectedLevels = 0;
}
void automationEditor::closeEvent( QCloseEvent * _ce )
{
QApplication::restoreOverrideCursor();
hide();
_ce->ignore ();
}
void automationEditor::keyPressEvent( QKeyEvent * _ke )
{
switch( _ke->key() )
{
case Qt::Key_Up:
m_topBottomScroll->setValue(
m_topBottomScroll->value() - 1 );
break;
case Qt::Key_Down:
m_topBottomScroll->setValue(
m_topBottomScroll->value() + 1 );
break;
case Qt::Key_Left:
{
if( ( m_timeLine->pos() -= 16 ) < 0 )
{
m_timeLine->pos().setTact( 0 );
m_timeLine->pos().setTact64th( 0 );
}
m_timeLine->updatePosition();
break;
}
case Qt::Key_Right:
{
m_timeLine->pos() += 16;
m_timeLine->updatePosition();
break;
}
case Qt::Key_C:
if( _ke->modifiers() & Qt::ControlModifier )
{
copySelectedValues();
}
else
{
_ke->ignore();
}
break;
case Qt::Key_X:
if( _ke->modifiers() & Qt::ControlModifier )
{
cutSelectedValues();
}
else
{
_ke->ignore();
}
break;
case Qt::Key_V:
if( _ke->modifiers() & Qt::ControlModifier )
{
pasteValues();
}
else
{
_ke->ignore();
}
break;
case Qt::Key_A:
if( _ke->modifiers() & Qt::ControlModifier )
{
m_selectButton->setChecked( TRUE );
selectAll();
update();
}
else
{
_ke->ignore();
}
break;
case Qt::Key_D:
if( _ke->modifiers() & Qt::ShiftModifier )
{
m_drawButton->setChecked( TRUE );
}
else
{
_ke->ignore();
}
break;
case Qt::Key_E:
if( _ke->modifiers() & Qt::ShiftModifier )
{
m_eraseButton->setChecked( TRUE );
}
else
{
_ke->ignore();
}
break;
case Qt::Key_S:
if( _ke->modifiers() & Qt::ShiftModifier )
{
m_selectButton->setChecked( TRUE );
}
else
{
_ke->ignore();
}
break;
case Qt::Key_M:
if( _ke->modifiers() & Qt::ShiftModifier )
{
m_moveButton->setChecked( TRUE );
}
else
{
_ke->ignore();
}
break;
case Qt::Key_Delete:
deleteSelectedValues();
break;
case Qt::Key_Space:
if( eng()->getSongEditor()->playing() )
{
stop();
}
else
{
play();
}
break;
case Qt::Key_Home:
m_timeLine->pos().setTact( 0 );
m_timeLine->pos().setTact64th( 0 );
m_timeLine->updatePosition();
break;
default:
_ke->ignore();
break;
}
}
void automationEditor::leaveEvent( QEvent * _e )
{
while( QApplication::overrideCursor() != NULL )
{
QApplication::restoreOverrideCursor();
}
QWidget::leaveEvent( _e );
}
void automationEditor::mousePressEvent( QMouseEvent * _me )
{
if( validPattern() == FALSE )
{
return;
}
if( _me->y() > TOP_MARGIN )
{
int level = getLevel( _me->y() );
int x = _me->x();
if( x > VALUES_WIDTH )
{
// set or move value
x -= VALUES_WIDTH;
// get tact-64th in which the user clicked
int pos_tact_64th = x * 64 / m_ppt +
m_currentPosition;
// get time map of current pattern
timeMap & time_map = m_pattern->getTimeMap();
// will be our iterator in the following loop
timeMap::iterator it = time_map.begin();
// loop through whole time-map...
while( it != time_map.end() )
{
midiTime len = 4;
// and check whether the user clicked on an
// existing value
if( pos_tact_64th >= -it.key() &&
len > 0 &&
pos_tact_64th <= -it.key() + len &&
#ifdef QT3
it.data() == level )
#else
it.value() == level )
#endif
{
break;
}
++it;
}
// left button??
if( _me->button() == Qt::LeftButton &&
m_editMode == DRAW )
{
// did it reach end of map because
// there's no value??
if( it == time_map.end() )
{
// then set new value
midiTime value_pos( pos_tact_64th );
midiTime new_time =
m_pattern->putValue( value_pos,
level );
// reset it so that it can be used for
// ops (move, resize) after this
// code-block
it = time_map.find( -new_time );
}
// move it
m_action = MOVE_VALUE;
int aligned_x = (int)( (float)( (
-it.key() -
m_currentPosition ) *
m_ppt ) / 64.0f );
m_moveXOffset = x - aligned_x - 1;
// set move-cursor
QCursor c( Qt::SizeAllCursor );
QApplication::setOverrideCursor( c );
eng()->getSongEditor()->setModified();
}
else if( ( _me->button() == Qt::RightButton &&
m_editMode == DRAW ) ||
m_editMode == ERASE )
{
// erase single value
if( it != time_map.end() )
{
m_pattern->removeValue( -it.key() );
eng()->getSongEditor()->setModified();
}
}
else if( _me->button() == Qt::LeftButton &&
m_editMode == SELECT )
{
// select an area of values
m_selectStartTact64th = pos_tact_64th;
m_selectedTact64th = 0;
m_selectStartLevel = level;
m_selectedLevels = 1;
m_action = SELECT_VALUES;
}
else if( _me->button() == Qt::RightButton &&
m_editMode == SELECT )
{
// when clicking right in select-move, we
// switch to move-mode
m_moveButton->setChecked( TRUE );
}
else if( _me->button() == Qt::LeftButton &&
m_editMode == MOVE )
{
// move selection (including selected values)
// save position where move-process began
m_moveStartTact64th = pos_tact_64th;
m_moveStartLevel = level;
m_action = MOVE_SELECTION;
eng()->getSongEditor()->setModified();
}
else if( _me->button() == Qt::RightButton &&
m_editMode == MOVE )
{
// when clicking right in select-move, we
// switch to draw-mode
m_drawButton->setChecked( TRUE );
}
update();
}
}
}
void automationEditor::mouseReleaseEvent( QMouseEvent * _me )
{
m_action = NONE;
if( m_editMode == DRAW )
{
QApplication::restoreOverrideCursor();
}
}
void automationEditor::mouseMoveEvent( QMouseEvent * _me )
{
if( validPattern() == FALSE )
{
update();
return;
}
if( _me->y() > TOP_MARGIN )
{
int level = getLevel( _me->y() );
int x = _me->x();
if( _me->x() <= VALUES_WIDTH )
{
update();
return;
}
x -= VALUES_WIDTH;
if(
#ifdef QT4
_me->buttons() &
#else
_me->state() ==
#endif
Qt::LeftButton && m_editMode == DRAW )
{
if( m_action == MOVE_VALUE )
{
x -= m_moveXOffset;
}
int pos_tact_64th = x * 64 / m_ppt +
m_currentPosition;
if( m_action == MOVE_VALUE )
{
// moving value
if( pos_tact_64th < 0 )
{
pos_tact_64th = 0;
}
// we moved the value so the value has to be
// moved properly according to new starting-
// time in the time map of pattern
m_pattern->removeValue(
midiTime( pos_tact_64th ) );
m_pattern->putValue( midiTime( pos_tact_64th ),
level );
}
eng()->getSongEditor()->setModified();
}
else if(
#ifdef QT4
_me->buttons() &
#else
_me->state() ==
#endif
Qt::NoButton && m_editMode == DRAW )
{
// set move- or resize-cursor
// get tact-64th in which the cursor is posated
int pos_tact_64th = ( x * 64 ) / m_ppt +
m_currentPosition;
// get time map of current pattern
timeMap & time_map = m_pattern->getTimeMap();
// will be our iterator in the following loop
timeMap::iterator it = time_map.begin();
// loop through whole time map...
while( it != time_map.end() )
{
// and check whether the cursor is over an
// existing value
if( pos_tact_64th >= -it.key() &&
pos_tact_64th <= -it.key() +
//TODO: Add constant
4 &&
#ifdef QT3
it.data() == level )
#else
it.value() == level )
#endif
{
break;
}
++it;
}
// did it reach end of map because there's
// no value??
if( it != time_map.end() )
{
if( QApplication::overrideCursor() )
{
if( QApplication::overrideCursor()->shape() != Qt::SizeAllCursor )
{
while( QApplication::overrideCursor() != NULL )
{
QApplication::restoreOverrideCursor();
}
QCursor c( Qt::SizeAllCursor );
QApplication::setOverrideCursor(
c );
}
}
else
{
QCursor c( Qt::SizeAllCursor );
QApplication::setOverrideCursor( c );
}
}
else
{
// the cursor is over no value, so restore
// cursor
while( QApplication::overrideCursor() != NULL )
{
QApplication::restoreOverrideCursor();
}
}
}
else if(
#ifdef QT4
_me->buttons() &
#else
_me->modifiers() ==
#endif
Qt::LeftButton &&
m_editMode == SELECT &&
m_action == SELECT_VALUES )
{
// change size of selection
if( x < 0 && m_currentPosition > 0 )
{
x = 0;
QCursor::setPos( mapToGlobal( QPoint(
VALUES_WIDTH, _me->y() ) ) );
if( m_currentPosition >= 4 )
{
m_leftRightScroll->setValue(
m_currentPosition - 4 );
}
else
{
m_leftRightScroll->setValue( 0 );
}
}
else if( x > width() - VALUES_WIDTH )
{
x = width() - VALUES_WIDTH;
QCursor::setPos( mapToGlobal( QPoint( width(),
_me->y() ) ) );
m_leftRightScroll->setValue( m_currentPosition +
4 );
}
// get tact-64th in which the cursor is posated
int pos_tact_64th = x * 64 / m_ppt +
m_currentPosition;
m_selectedTact64th = pos_tact_64th -
m_selectStartTact64th;
if( (int) m_selectStartTact64th + m_selectedTact64th <
0 )
{
m_selectedTact64th = -static_cast<int>(
m_selectStartTact64th );
}
m_selectedLevels = level - m_selectStartLevel;
if( level <= m_selectStartLevel )
{
--m_selectedLevels;
}
}
else if(
#ifdef QT4
_me->buttons() &
#else
_me->modifiers() ==
#endif
Qt::LeftButton &&
m_editMode == MOVE &&
m_action == MOVE_SELECTION )
{
// move selection + selected values
// do horizontal move-stuff
int pos_tact_64th = x * 64 / m_ppt +
m_currentPosition;
int tact_64th_diff = pos_tact_64th -
m_moveStartTact64th;
if( m_selectedTact64th > 0 )
{
if( (int) m_selectStartTact64th +
tact_64th_diff < 0 )
{
tact_64th_diff = -m_selectStartTact64th;
}
}
else
{
if( (int) m_selectStartTact64th +
m_selectedTact64th + tact_64th_diff <
0 )
{
tact_64th_diff = -(
m_selectStartTact64th +
m_selectedTact64th );
}
}
m_selectStartTact64th += tact_64th_diff;
int tact_diff = tact_64th_diff / 64;
tact_64th_diff = tact_64th_diff % 64;
// do vertical move-stuff
int level_diff = level - m_moveStartLevel;
if( m_selectedLevels > 0 )
{
if( m_selectStartLevel + level_diff
< m_min_level )
{
level_diff = m_min_level -
m_selectStartLevel;
}
else if( m_selectStartLevel + m_selectedLevels +
level_diff > m_max_level )
{
level_diff = m_max_level -
m_selectStartLevel -
m_selectedLevels;
}
}
else
{
if( m_selectStartLevel + m_selectedLevels +
level_diff < m_min_level )
{
level_diff = m_min_level -
m_selectStartLevel -
m_selectedLevels;
}
else if( m_selectStartLevel + level_diff >
m_max_level )
{
level_diff = m_max_level -
m_selectStartLevel;
}
}
m_selectStartLevel += level_diff;
timeMap new_selValuesForMove;
for( timeMap::iterator it = m_selValuesForMove.begin();
it != m_selValuesForMove.end(); ++it )
{
midiTime new_value_pos;
if( -it.key() )
{
int value_tact = ( -it.key() >> 6 )
+ tact_diff;
int value_tact_64th = ( -it.key() & 63 )
+ tact_64th_diff;
// ensure value_tact_64th range
if( value_tact_64th >> 6 )
{
value_tact += value_tact_64th
>> 6;
value_tact_64th &= 63;
}
m_pattern->removeValue( -it.key() );
new_value_pos = midiTime( value_tact,
value_tact_64th );
}
#ifdef QT3
new_selValuesForMove[
-m_pattern->putValue( new_value_pos,
it.data () + level_diff,
FALSE )]
= it.data() + level_diff;
#else
new_selValuesForMove[
-m_pattern->putValue( new_value_pos,
it.value () + level_diff,
FALSE )]
= it.value() + level_diff;
#endif
}
m_selValuesForMove = new_selValuesForMove;
m_moveStartTact64th = pos_tact_64th;
m_moveStartLevel = level;
}
}
else
{
if(
#ifdef QT4
_me->buttons() &
#else
_me->modifiers() ==
#endif
Qt::LeftButton &&
m_editMode == SELECT &&
m_action == SELECT_VALUES )
{
int x = _me->x() - VALUES_WIDTH;
if( x < 0 && m_currentPosition > 0 )
{
x = 0;
QCursor::setPos( mapToGlobal( QPoint( VALUES_WIDTH,
_me->y() ) ) );
if( m_currentPosition >= 4 )
{
m_leftRightScroll->setValue(
m_currentPosition - 4 );
}
else
{
m_leftRightScroll->setValue( 0 );
}
}
else if( x > width() - VALUES_WIDTH )
{
x = width() - VALUES_WIDTH;
QCursor::setPos( mapToGlobal( QPoint( width(),
_me->y() ) ) );
m_leftRightScroll->setValue( m_currentPosition +
4 );
}
// get tact-64th in which the cursor is posated
int pos_tact_64th = x * 64 / m_ppt +
m_currentPosition;
m_selectedTact64th = pos_tact_64th -
m_selectStartTact64th;
if( (int) m_selectStartTact64th + m_selectedTact64th <
0 )
{
m_selectedTact64th = -static_cast<int>(
m_selectStartTact64th );
}
int level = getLevel( _me->y() );
if( level <= m_bottom_level )
{
QCursor::setPos( mapToGlobal( QPoint( _me->x(),
height() -
SCROLLBAR_SIZE ) ) );
m_topBottomScroll->setValue(
m_topBottomScroll->value() + 1 );
level = m_bottom_level;
}
else if( level >= m_top_level )
{
QCursor::setPos( mapToGlobal( QPoint( _me->x(),
TOP_MARGIN ) ) );
m_topBottomScroll->setValue(
m_topBottomScroll->value() - 1 );
level = m_top_level;
}
m_selectedLevels = level - m_selectStartLevel;
if( level <= m_selectStartLevel )
{
--m_selectedLevels;
}
}
QApplication::restoreOverrideCursor();
}
update();
}
void automationEditor::paintEvent( QPaintEvent * )
{
updatePaintPixmap();
#ifdef QT4
QPainter p( this );
#else
QPixmap draw_pm( size() );
draw_pm.fill( QColor( 0, 0, 0 ) );
QPainter p( &draw_pm, this );
#endif
p.drawPixmap( 0, 0, m_paintPixmap );
p.setClipRect( VALUES_WIDTH, TOP_MARGIN, width() - VALUES_WIDTH,
height() - TOP_MARGIN - SCROLLBAR_SIZE );
if( validPattern() == TRUE )
{
drawCross( p );
}
const QPixmap * cursor = NULL;
// draw current edit-mode-icon below the cursor
switch( m_editMode )
{
case DRAW: cursor = s_toolDraw; break;
case ERASE: cursor = s_toolErase; break;
case SELECT: cursor = s_toolSelect; break;
case MOVE: cursor = s_toolMove; break;
}
p.drawPixmap( mapFromGlobal( QCursor::pos() ) + QPoint( 8, 8 ),
*cursor );
#ifndef QT4
// and blit all the drawn stuff on the screen...
bitBlt( this, rect().topLeft(), &draw_pm );
#endif
}
inline void automationEditor::drawCross( QPainter & _p )
{
QPoint mouse_pos = mapFromGlobal( QCursor::pos() );
int level = getLevel( mouse_pos.y() );
int grid_bottom = height() - SCROLLBAR_SIZE - 1;
int cross_y = m_y_auto ?
grid_bottom - (int)roundf( ( grid_bottom - TOP_MARGIN )
* ( level - m_min_level )
/ (float)( m_max_level - m_min_level ) ) :
grid_bottom - ( level - m_bottom_level ) * m_y_delta;
_p.setPen( QColor( 0xFF, 0x33, 0x33 ) );
_p.drawLine( VALUES_WIDTH, cross_y, width(), cross_y );
_p.drawLine( mouse_pos.x(), TOP_MARGIN, mouse_pos.x(),
height() - SCROLLBAR_SIZE );
}
// responsible for moving/resizing scrollbars after window-resizing
void automationEditor::resizeEvent( QResizeEvent * )
{
m_leftRightScroll->setGeometry( VALUES_WIDTH, height() - SCROLLBAR_SIZE,
width() - VALUES_WIDTH,
SCROLLBAR_SIZE );
int grid_height = height() - TOP_MARGIN - SCROLLBAR_SIZE;
m_topBottomScroll->setGeometry( width() - SCROLLBAR_SIZE, TOP_MARGIN,
SCROLLBAR_SIZE, grid_height );
int half_grid = grid_height / 2;
int total_pixels = ( m_max_level - m_min_level ) * m_y_delta + 1;
if( !m_y_auto && grid_height < total_pixels )
{
int min_scroll = m_min_level + (int)floorf( half_grid
/ (float)m_y_delta );
int max_scroll = m_max_level - (int)floorf( ( grid_height
- half_grid ) / (float)m_y_delta );
m_topBottomScroll->setRange( min_scroll, max_scroll );
}
else
{
m_topBottomScroll->setRange( m_scroll_level, m_scroll_level );
}
#ifdef QT4
m_topBottomScroll->setSingleStep( 1 );
m_topBottomScroll->setPageStep( 20 );
#else
m_topBottomScroll->setSteps( 1, 20 );
#endif
m_topBottomScroll->setValue( m_scroll_level );
if( eng()->getSongEditor() )
{
eng()->getSongEditor()->getPlayPos(
songEditor::PLAY_AUTOMATION_PATTERN
).m_timeLine->setFixedWidth( width() );
}
m_toolBar->setFixedWidth( width() );
updateTopBottomLevels();
update();
}
void automationEditor::wheelEvent( QWheelEvent * _we )
{
_we->accept();
if( eng()->getMainWindow()->isCtrlPressed() == TRUE )
{
if( _we->delta() > 0 )
{
m_ppt = tMin( m_ppt * 2, m_y_delta *
DEFAULT_STEPS_PER_TACT * 8 );
}
else if( m_ppt >= 72 )
{
m_ppt /= 2;
}
// update combobox with zooming-factor
m_zoomingXComboBox->setValue(
m_zoomingXComboBox->findText( QString::number(
static_cast<int>( m_ppt * 100 /
DEFAULT_PPT ) ) +"%" ) );
// update timeline
m_timeLine->setPixelsPerTact( m_ppt );
update();
}
else if( eng()->getMainWindow()->isShiftPressed() )
{
m_leftRightScroll->setValue( m_leftRightScroll->value() -
_we->delta() * 2 / 15 );
}
else
{
m_topBottomScroll->setValue( m_topBottomScroll->value() -
_we->delta() / 30 );
}
}
int automationEditor::getLevel( int _y )
{
int level_line_y = height() - SCROLLBAR_SIZE - 1;
// pressed level
int level = m_bottom_level + (int)roundf( m_y_auto ?
( m_max_level - m_min_level ) * ( level_line_y - _y )
/ (float)( level_line_y - TOP_MARGIN ) :
( level_line_y - _y ) / (float)m_y_delta );
// some range-checking-stuff
if( level < m_bottom_level )
{
level = m_bottom_level;
}
else if( level > m_top_level )
{
level = m_top_level;
}
return( level );
}
void automationEditor::play( void )
{
if( validPattern() == FALSE )
{
return;
}
if( !m_pattern->getTrack() )
{
if( eng()->getSongEditor()->playMode() !=
songEditor::PLAY_PATTERN )
{
eng()->getSongEditor()->stop();
eng()->getSongEditor()->playPattern( (pattern *)
eng()->getPianoRoll()->currentPattern() );
m_playButton->setIcon( embed::getIconPixmap(
"pause" ) );
}
else if( eng()->getSongEditor()->playing() )
{
eng()->getSongEditor()->pause();
m_playButton->setIcon( embed::getIconPixmap( "play" ) );
}
else if( eng()->getSongEditor()->paused() )
{
eng()->getSongEditor()->resumeFromPause();
m_playButton->setIcon( embed::getIconPixmap(
"pause" ) );
}
else
{
m_playButton->setIcon( embed::getIconPixmap(
"pause" ) );
eng()->getSongEditor()->playPattern( (pattern *)
eng()->getPianoRoll()->currentPattern() );
}
}
else if( inBBEditor() )
{
if( eng()->getSongEditor()->playing() )
{
m_playButton->setIcon( embed::getIconPixmap( "play" ) );
}
else
{
m_playButton->setIcon( embed::getIconPixmap(
"pause" ) );
}
eng()->getBBEditor()->play();
}
else
{
if( eng()->getSongEditor()->playing() )
{
eng()->getSongEditor()->pause();
m_playButton->setIcon( embed::getIconPixmap( "play" ) );
}
else if( eng()->getSongEditor()->paused() )
{
eng()->getSongEditor()->resumeFromPause();
m_playButton->setIcon( embed::getIconPixmap(
"pause" ) );
}
else
{
m_playButton->setIcon( embed::getIconPixmap(
"pause" ) );
eng()->getSongEditor()->play();
}
}
}
void automationEditor::stop( void )
{
if( !validPattern() )
{
return;
}
if( m_pattern->getTrack() && inBBEditor() )
{
eng()->getBBEditor()->stop();
}
else
{
eng()->getSongEditor()->stop();
}
m_playButton->setIcon( embed::getIconPixmap( "play" ) );
m_playButton->update();
m_scrollBack = TRUE;
}
void automationEditor::horScrolled( int _new_pos )
{
m_currentPosition = _new_pos;
emit positionChanged( m_currentPosition );
update();
}
void automationEditor::verScrolled( int _new_pos )
{
m_scroll_level = _new_pos;
updateTopBottomLevels();
update();
}
void automationEditor::drawButtonToggled( void )
{
m_editMode = DRAW;
removeSelection();
update();
}
void automationEditor::eraseButtonToggled( void )
{
m_editMode = ERASE;
removeSelection();
update();
}
void automationEditor::selectButtonToggled( void )
{
m_editMode = SELECT;
removeSelection();
update();
}
void automationEditor::moveButtonToggled( void )
{
m_editMode = MOVE;
m_selValuesForMove.clear();
getSelectedValues( m_selValuesForMove );
update();
}
void automationEditor::selectAll( void )
{
if( validPattern() == FALSE )
{
return;
}
//TODO: Add constant
int len_tact_64th = 4;
timeMap & time_map = m_pattern->getTimeMap();
timeMap::iterator it = time_map.begin();
m_selectStartTact64th = 0;
m_selectedTact64th = -it.key() + len_tact_64th;
#ifdef QT3
m_selectStartLevel = it.data();
#else
m_selectStartLevel = it.value();
#endif
m_selectedLevels = 1;
while( ++it != time_map.end() )
{
#ifdef QT3
const int level = it.data();
#else
const int level = it.value();
#endif
if( level < m_selectStartLevel )
{
// if we move start-level down, we have to add
// the difference between old and new start-level
// to m_selectedLevels, otherwise the selection
// is just moved down...
m_selectedLevels += m_selectStartLevel - level;
m_selectStartLevel = level;
}
else if( level >= m_selectStartLevel + m_selectedLevels )
{
m_selectedLevels = level - m_selectStartLevel + 1;
}
}
}
// returns vector with pointers to all selected values
void automationEditor::getSelectedValues( timeMap & _selected_values )
{
if( validPattern() == FALSE )
{
return;
}
int sel_pos_start = m_selectStartTact64th;
int sel_pos_end = sel_pos_start + m_selectedTact64th;
if( sel_pos_start > sel_pos_end )
{
qSwap<int>( sel_pos_start, sel_pos_end );
}
int sel_level_start = m_selectStartLevel;
int sel_level_end = sel_level_start + m_selectedLevels;
if( sel_level_start > sel_level_end )
{
qSwap<int>( sel_level_start, sel_level_end );
}
timeMap & time_map = m_pattern->getTimeMap();
for( timeMap::iterator it = time_map.begin(); it != time_map.end();
++it )
{
//TODO: Add constant
Sint32 len_tact_64th = 4;
#ifdef QT3
int level = it.data();
#else
int level = it.value();
#endif
Sint32 pos_tact_64th = -it.key();
if( level > sel_level_start && level <= sel_level_end &&
pos_tact_64th >= sel_pos_start &&
pos_tact_64th+len_tact_64th <= sel_pos_end )
{
_selected_values[it.key()] = level;
}
}
}
void automationEditor::copySelectedValues( void )
{
m_valuesToCopy.clear();
timeMap selected_values;
getSelectedValues( selected_values );
if( !selected_values.isEmpty() )
{
for( timeMap::iterator it = selected_values.begin();
it != selected_values.end(); ++it )
{
#ifdef QT3
m_valuesToCopy[it.key()] = it.data();
#else
m_valuesToCopy[it.key()] = it.value();
#endif
}
textFloat::displayMessage( tr( "Values copied" ),
tr( "All selected values were copied to the "
"clipboard." ),
embed::getIconPixmap( "edit_copy" ), 2000 );
}
}
void automationEditor::cutSelectedValues( void )
{
if( validPattern() == FALSE )
{
return;
}
m_valuesToCopy.clear();
timeMap selected_values;
getSelectedValues( selected_values );
if( !selected_values.isEmpty() )
{
eng()->getSongEditor()->setModified();
for( timeMap::iterator it = selected_values.begin();
it != selected_values.end(); ++it )
{
#ifdef QT3
m_valuesToCopy[it.key()] = it.data();
#else
m_valuesToCopy[it.key()] = it.value();
#endif
m_pattern->removeValue( -it.key() );
}
}
update();
eng()->getSongEditor()->update();
}
void automationEditor::pasteValues( void )
{
if( validPattern() == FALSE )
{
return;
}
if( !m_valuesToCopy.isEmpty() )
{
for( timeMap::iterator it = m_valuesToCopy.begin();
it != m_valuesToCopy.end(); ++it )
{
m_pattern->putValue( -it.key() + m_currentPosition,
#ifdef QT3
it.data() );
#else
it.value() );
#endif
}
// we only have to do the following lines if we pasted at
// least one value...
eng()->getSongEditor()->setModified();
update();
eng()->getSongEditor()->update();
}
}
void automationEditor::deleteSelectedValues( void )
{
if( validPattern() == FALSE )
{
return;
}
timeMap selected_values;
getSelectedValues( selected_values );
const bool update_after_delete = !selected_values.empty();
for( timeMap::iterator it = selected_values.begin();
it != selected_values.end(); ++it )
{
m_pattern->removeValue( -it.key() );
}
if( update_after_delete == TRUE )
{
eng()->getSongEditor()->setModified();
update();
eng()->getSongEditor()->update();
}
}
void automationEditor::updatePosition( const midiTime & _t )
{
if( ( eng()->getSongEditor()->playing() &&
eng()->getSongEditor()->playMode() ==
songEditor::PLAY_AUTOMATION_PATTERN ) ||
m_scrollBack == TRUE )
{
const int w = width() - VALUES_WIDTH;
if( _t > m_currentPosition + w * 64 / m_ppt )
{
m_leftRightScroll->setValue( _t.getTact() * 64 );
}
else if( _t < m_currentPosition )
{
midiTime t = tMax( _t - w * 64 * 64 / m_ppt, 0 );
m_leftRightScroll->setValue( t.getTact() * 64 );
}
m_scrollBack = FALSE;
}
}
void automationEditor::zoomingXChanged( const QString & _zfac )
{
m_ppt = _zfac.left( _zfac.length() - 1 ).toInt() * DEFAULT_PPT / 100;
#ifdef LMMS_DEBUG
assert( m_ppt > 0 );
#endif
m_timeLine->setPixelsPerTact( m_ppt );
update();
}
void automationEditor::zoomingYChanged( const QString & _zfac )
{
m_y_auto = _zfac == "Auto";
if( !m_y_auto )
{
m_y_delta = _zfac.left( _zfac.length() - 1 ).toInt()
* DEFAULT_Y_DELTA / 100;
}
#ifdef LMMS_DEBUG
assert( m_y_delta > 0 );
#endif
resizeEvent( NULL );
}
int automationEditor::quantization( void ) const
{
return( 64 / m_quantizeComboBox->currentText().right(
m_quantizeComboBox->currentText().length() -
2 ).toInt() );
}
void automationEditor::updateTopBottomLevels( void )
{
if( m_y_auto )
{
m_bottom_level = m_min_level;
m_top_level = m_max_level;
return;
}
int total_pixels = ( m_max_level - m_min_level ) * m_y_delta + 1;
int grid_height = height() - TOP_MARGIN - SCROLLBAR_SIZE;
int half_grid = grid_height / 2;
if( total_pixels > grid_height )
{
int central_level = m_min_level + m_max_level - m_scroll_level;
m_bottom_level = central_level - (int)roundf( half_grid
/ (float)m_y_delta );
if( m_bottom_level < m_min_level )
{
m_bottom_level = m_min_level;
m_top_level = m_min_level + (int)floorf( grid_height
/ (float)m_y_delta );
}
else
{
m_top_level = m_bottom_level + (int)floorf( grid_height
/ (float)m_y_delta );
if( m_top_level > m_max_level )
{
m_top_level = m_max_level;
m_bottom_level = m_max_level - (int)floorf(
grid_height / (float)m_y_delta );
}
}
}
else
{
m_bottom_level = m_min_level;
m_top_level = m_max_level;
}
}
inline bool automationEditor::inBBEditor( void )
{
return( m_pattern->getTrack()->getTrackContainer()
== eng()->getBBEditor() );
}
void automationEditor::update( void )
{
QWidget::update();
// Note detuning?
if( m_pattern && !m_pattern->getTrack() )
{
eng()->getPianoRoll()->update();
}
}
#include "automation_editor.moc"
#ifdef QT3
#undef addButton
#undef setCheckable
#endif
#endif