/*
 * Copyright(c) 2002-2008 by Alex >Comix< Cominu [comix@users.sourceforge.net]
 *
 * This file is part of Composite
 *
 * Composite 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.
 *
 * Composite 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; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
 *
 */

#include <Tritium/Preferences.hpp>
#include <Tritium/Engine.hpp>
#include <Tritium/Sampler.hpp>
#include <Tritium/Instrument.hpp>
#include <Tritium/InstrumentList.hpp>
#include <Tritium/Pattern.hpp>
#include <Tritium/Note.hpp>
using namespace Tritium;

#include <cassert>

#include "../CompositeApp.hpp"

#include "NotePropertiesRuler.hpp"
#include "PatternEditorPanel.hpp"
#include "DrumPatternEditor.hpp"


NotePropertiesRuler::NotePropertiesRuler( QWidget *parent, PatternEditorPanel *pPatternEditorPanel, NotePropertiesMode mode )
 : QWidget( parent )
 , m_mode( mode )
 , m_pPatternEditorPanel( pPatternEditorPanel )
 , m_pPattern()
{
	//infoLog("INIT");
	//setAttribute(Qt::WA_NoBackground);

	m_nGridWidth = (g_engine->get_preferences())->getPatternEditorGridWidth();
	m_nEditorWidth = 20 + m_nGridWidth * ( MAX_NOTES * 4 );

	if (m_mode == VELOCITY ) {
		m_nEditorHeight = 100;
	}
	else if ( m_mode == PAN ) {
		m_nEditorHeight = 100;
	}
	else if ( m_mode == LEADLAG ) {
		m_nEditorHeight = 100;
	}

	resize( m_nEditorWidth, m_nEditorHeight );
	setMinimumSize( m_nEditorWidth, m_nEditorHeight );

	m_pBackground = new QPixmap( m_nEditorWidth, m_nEditorHeight );

	updateEditor();
	show();

	CompositeApp::get_instance()->addEventListener( this );
}




NotePropertiesRuler::~NotePropertiesRuler()
{
	//infoLog("DESTROY");
}



void NotePropertiesRuler::mousePressEvent(QMouseEvent *ev)
{
//	infoLog( "mousePressEvent()" );
	if (m_pPattern == NULL) return;

	DrumPatternEditor *pPatternEditor = m_pPatternEditorPanel->getDrumPatternEditor();
	int nBase;
	if (pPatternEditor->isUsingTriplets()) {
		nBase = 3;
	}
	else {
		nBase = 4;
	}
	int width = (m_nGridWidth * 4 *  MAX_NOTES) / ( nBase * pPatternEditor->getResolution());
	int x_pos = ev->x();
	int column;
	column = (x_pos - 20) + (width / 2);
	column = column / width;
	column = (column * 4 * MAX_NOTES) / ( nBase * pPatternEditor->getResolution() );
	float val = height() - ev->y();
	if (val > height()) {
		val = height();
	}
	else if (val < 0.0) {
		val = 0.0;
	}
	val = val / height();

	int nSelectedInstrument = g_engine->getSelectedInstrumentNumber();
	T<Song>::shared_ptr pSong = (g_engine)->getSong();

	Pattern::note_map_t::iterator pos;
	T<InstrumentList>::shared_ptr instrument_list = g_engine->get_sampler()->get_instrument_list();
	for ( pos = m_pPattern->note_map.lower_bound( column ); pos != m_pPattern->note_map.upper_bound( column ); ++pos ) {
		Note *pNote = pos->second;
		assert( pNote );
		assert( (int)pos->first == column );
		if ( pNote->get_instrument() != instrument_list->get( nSelectedInstrument ) ) {
			continue;
		}

		if ( m_mode == VELOCITY ) {
			pNote->set_velocity( val );

			QString msg = QString( trUtf8("Set note velocity [%1]") )
				.arg(val, 0, 'f', 2);
			CompositeApp::get_instance()->setStatusBarMessage( msg, 2000 );
		}
		else if ( m_mode == PAN ){
			float pan_L, pan_R;
			if ( (ev->button() == Qt::MidButton) || (ev->modifiers() == Qt::ControlModifier && ev->button() == Qt::LeftButton) ) {
				val = 0.5;
			}
			if ( val > 0.5 ) {
				pan_L = 1.0 - val;
				pan_R = 0.5;
			}
			else {
				pan_L = 0.5;
				pan_R = val;
			}

			pNote->set_pan_l( pan_L );
			pNote->set_pan_r( pan_R );
		}
		else if ( m_mode == LEADLAG ){
			if ( (ev->button() == Qt::MidButton) || (ev->modifiers() == Qt::ControlModifier && ev->button() == Qt::LeftButton) ) {
				pNote->set_leadlag(0.0);
			} else {
				pNote->set_leadlag((val * -2.0) + 1.0);
				if (pNote->get_leadlag() < 0.0) {
					// FIXME: '5' taken from fLeadLagFactor calculation in Engine.cpp
					QString msg = QString( trUtf8("Leading beat by: %1 ticks") )
						.arg( (pNote->get_leadlag() * -5), 0, 'f', 2 );
					CompositeApp::get_instance()->setStatusBarMessage( msg, 2000 );
				} else if (pNote->get_leadlag() > 0.0) {
					// FIXME: '5' taken from fLeadLagFactor calculation in Engine.cpp
					QString msg = QString( trUtf8("Lagging beat by: %1 ticks") )
						.arg( (pNote->get_leadlag() * 5), 0, 'f', 2 );
					CompositeApp::get_instance()->setStatusBarMessage( msg, 2000 );
				} else {
					CompositeApp::get_instance()->setStatusBarMessage( QString("Note on beat"), 2000 );
				}

			}
		}
		pSong->set_modified( true );
		updateEditor();
		break;
	}
}

void NotePropertiesRuler::wheelEvent(QWheelEvent *ev)
{
//      infoLog( "mousePressEvent()" );
	if (m_pPattern == NULL) return;

	float delta;
	if (ev->modifiers() == Qt::ControlModifier) {
		delta = 0.01; // fine control
	} else { 
		delta = 0.05; // course control
	}
		
	if ( ev->delta() < 0 ) {
		delta = (delta * -1.0);
	}

	DrumPatternEditor *pPatternEditor = m_pPatternEditorPanel->getDrumPatternEditor();
	int nBase;
	if (pPatternEditor->isUsingTriplets()) {
		nBase = 3;
	}
	else {
		nBase = 4;
	}
	int width = (m_nGridWidth * 4 *  MAX_NOTES) / ( nBase * pPatternEditor->getResolution());
	int x_pos = ev->x();
	int column;
	column = (x_pos - 20) + (width / 2);
	column = column / width;
	column = (column * 4 * MAX_NOTES) / ( nBase * pPatternEditor->getResolution() );

	int nSelectedInstrument = g_engine->getSelectedInstrumentNumber();
	T<Song>::shared_ptr pSong = (g_engine)->getSong();

	Pattern::note_map_t::iterator pos;
	T<InstrumentList>::shared_ptr instrument_list = g_engine->get_sampler()->get_instrument_list();
	for ( pos = m_pPattern->note_map.lower_bound( column ); pos != m_pPattern->note_map.upper_bound( column ); ++pos ) {
		Note *pNote = pos->second;
		assert( pNote );
		assert( pos->first == column );
		if ( pNote->get_instrument() != instrument_list->get( nSelectedInstrument ) ) {
			continue;
		}
		if ( m_mode == VELOCITY ) {
			float val = pNote->get_velocity() + delta;
			if (val > 1.0) {
				val = 1.0;
			}
			else if (val < 0.0) {
				val = 0.0;
			}

			pNote->set_velocity(val);

			QString msg = QString( trUtf8("Set note velocity [%1]") )
				.arg(val, 0, 'f', 2);
			( CompositeApp::get_instance() )->setStatusBarMessage( msg, 2000 );
		}
		else if ( m_mode == PAN ){
			float pan_L, pan_R;

			float val = (pNote->get_pan_r() - pNote->get_pan_l() + 0.5) + delta;
			if (val > 1.0) {
				val = 1.0;
			}
			else if (val < 0.0) {
				val = 0.0;
			}
			if ( val > 0.5 ) {
				pan_L = 1.0 - val;
				pan_R = 0.5;
			}
			else {
				pan_L = 0.5;
				pan_R = val;
			}

			pNote->set_pan_l(pan_L);
			pNote->set_pan_r(pan_R);
		}
		else if ( m_mode == LEADLAG ){
			float val = (pNote->get_leadlag() - 1.0)/-2.0 + delta;
			if (val > 1.0) {
				val = 1.0;
			}
			else if (val < 0.0) {
				val = 0.0;
			}
			pNote->set_leadlag((val * -2.0) + 1.0);
			if (pNote->get_leadlag() < 0.0) {
				// FIXME: '5' taken from fLeadLagFactor calculation in Engine.cpp
				QString msg = QString( trUtf8("Leading beat by: %1 ticks") )
					.arg( (pNote->get_leadlag() * -5), 0, 'f', 2 );
				CompositeApp::get_instance()->setStatusBarMessage( msg, 2000 );
			} else if (pNote->get_leadlag() > 0.0) {
				// FIXME: '5' taken from fLeadLagFactor calculation in Engine.cpp
				QString msg = QString( trUtf8("Lagging beat by: %1 ticks") )
					.arg( (pNote->get_leadlag() * 5), 0, 'f', 2 );
				CompositeApp::get_instance()->setStatusBarMessage( msg, 2000 );
			} else {
				CompositeApp::get_instance()->setStatusBarMessage( QString("Note on beat"), 2000 );
			}
		}

		pSong->set_modified( true );
		updateEditor();
		break;
	}
}


 void NotePropertiesRuler::mouseMoveEvent( QMouseEvent *ev )
{
//	infoLog( "mouse move" );
	mousePressEvent( ev );
}



void NotePropertiesRuler::paintEvent( QPaintEvent *ev)
{
	QPainter painter(this);
	painter.drawPixmap( ev->rect(), *m_pBackground, ev->rect() );
}



void NotePropertiesRuler::createVelocityBackground(QPixmap *pixmap)
{
	if ( !isVisible() ) {
		return;
	}

	UIStyle *pStyle = g_engine->get_preferences()->getDefaultUIStyle();

	H2RGBColor valueColor(
			(int)( pStyle->m_patternEditor_backgroundColor.getRed() * ( 1 - 0.3 ) ),
			(int)( pStyle->m_patternEditor_backgroundColor.getGreen() * ( 1 - 0.3 ) ),
			(int)( pStyle->m_patternEditor_backgroundColor.getBlue() * ( 1 - 0.3 ) )
	);

	QColor res_1( pStyle->m_patternEditor_line1Color.getRed(), pStyle->m_patternEditor_line1Color.getGreen(), pStyle->m_patternEditor_line1Color.getBlue() );
	QColor res_2( pStyle->m_patternEditor_line2Color.getRed(), pStyle->m_patternEditor_line2Color.getGreen(), pStyle->m_patternEditor_line2Color.getBlue() );
	QColor res_3( pStyle->m_patternEditor_line3Color.getRed(), pStyle->m_patternEditor_line3Color.getGreen(), pStyle->m_patternEditor_line3Color.getBlue() );
	QColor res_4( pStyle->m_patternEditor_line4Color.getRed(), pStyle->m_patternEditor_line4Color.getGreen(), pStyle->m_patternEditor_line4Color.getBlue() );
	QColor res_5( pStyle->m_patternEditor_line5Color.getRed(), pStyle->m_patternEditor_line5Color.getGreen(), pStyle->m_patternEditor_line5Color.getBlue() );

	QColor backgroundColor( pStyle->m_patternEditor_backgroundColor.getRed(), pStyle->m_patternEditor_backgroundColor.getGreen(), pStyle->m_patternEditor_backgroundColor.getBlue() );
	QColor horizLinesColor(
			pStyle->m_patternEditor_backgroundColor.getRed() - 20,
			pStyle->m_patternEditor_backgroundColor.getGreen() - 20,
			pStyle->m_patternEditor_backgroundColor.getBlue() - 20
	);

	unsigned nNotes = MAX_NOTES;
	if (m_pPattern) {
		nNotes = m_pPattern->get_length();
	}


	QPainter p( pixmap );

	p.fillRect( 0, 0, width(), height(), QColor(0,0,0) );
	p.fillRect( 0, 0, 20 + nNotes * m_nGridWidth, height(), backgroundColor );


	// vertical lines

	DrumPatternEditor *pPatternEditor = m_pPatternEditorPanel->getDrumPatternEditor();
	int nBase;
	if (pPatternEditor->isUsingTriplets()) {
		nBase = 3;
	}
	else {
		nBase = 4;
	}

	int n4th = 4 * MAX_NOTES / (nBase * 4);
	int n8th = 4 * MAX_NOTES / (nBase * 8);
	int n16th = 4 * MAX_NOTES / (nBase * 16);
	int n32th = 4 * MAX_NOTES / (nBase * 32);
	int n64th = 4 * MAX_NOTES / (nBase * 64);
	int nResolution = pPatternEditor->getResolution();


	if ( !pPatternEditor->isUsingTriplets() ) {

		for (uint i = 0; i < nNotes + 1; i++) {
			uint x = 20 + i * m_nGridWidth;

			if ( (i % n4th) == 0 ) {
				if (nResolution >= 4) {
					p.setPen( QPen( res_1, 0, Qt::DotLine ) );
					p.drawLine(x, 0, x, m_nEditorHeight);
				}
			}
			else if ( (i % n8th) == 0 ) {
				if (nResolution >= 8) {
					p.setPen( QPen( res_2, 0, Qt::DotLine ) );
					p.drawLine(x, 0, x, m_nEditorHeight);
				}
			}
			else if ( (i % n16th) == 0 ) {
				if (nResolution >= 16) {
					p.setPen( QPen( res_3, 0, Qt::DotLine ) );
					p.drawLine(x, 0, x, m_nEditorHeight);
				}
			}
			else if ( (i % n32th) == 0 ) {
				if (nResolution >= 32) {
					p.setPen( QPen( res_4, 0, Qt::DotLine ) );
					p.drawLine(x, 0, x, m_nEditorHeight);
				}
			}
			else if ( (i % n64th) == 0 ) {
				if (nResolution >= 64) {
					p.setPen( QPen( res_5, 0, Qt::DotLine ) );
					p.drawLine(x, 0, x, m_nEditorHeight);
				}
			}
		}
	}
	else {	// Triplets
		uint nCounter = 0;
		int nSize = 4 * MAX_NOTES / (nBase * nResolution);

		for (uint i = 0; i < nNotes + 1; i++) {
			uint x = 20 + i * m_nGridWidth;

			if ( (i % nSize) == 0) {
				if ((nCounter % 3) == 0) {
					p.setPen( QPen( res_1, 0, Qt::DotLine ) );
				}
				else {
					p.setPen( QPen( res_3, 0, Qt::DotLine ) );
				}
				p.drawLine(x, 0, x, m_nEditorHeight);
				nCounter++;
			}
		}
	}

	p.setPen( horizLinesColor );
	for (unsigned y = 0; y < m_nEditorHeight; y = y + (m_nEditorHeight / 10)) {
		p.drawLine(20, y, 20 + nNotes * m_nGridWidth, y);
	}

	// draw velocity lines
	if (m_pPattern != NULL) {
		int nSelectedInstrument = g_engine->getSelectedInstrumentNumber();
		T<Song>::shared_ptr pSong = g_engine->getSong();
		Pattern::note_map_t::iterator pos;
		T<InstrumentList>::shared_ptr instrument_list = g_engine->get_sampler()->get_instrument_list();
		for ( pos = m_pPattern->note_map.begin(); pos != m_pPattern->note_map.end(); ++pos ) {
			Note *pNote = pos->second;
			assert( pNote );
			if ( pNote->get_instrument() != instrument_list->get( nSelectedInstrument ) ) {
				continue;
			}

			uint x_pos = 20 + (pos->first) * m_nGridWidth;

			uint line_end = height();

			uint velocity = (uint)(pNote->get_velocity() * height());
			uint line_start = line_end - velocity;

			QColor sideColor(
				(int)( valueColor.getRed() * ( 1 - pNote->get_velocity() ) ),
				(int)( valueColor.getGreen() * ( 1 - pNote->get_velocity() ) ),
				(int)( valueColor.getBlue() * ( 1 - pNote->get_velocity() ) )
			);
			p.fillRect( (int)( x_pos - m_nGridWidth / 2.0 ) + 1, line_start, m_nGridWidth, line_end - line_start, sideColor );

			QColor centerColor(
				(int)( valueColor.getRed() * ( 1 - pNote->get_velocity() ) ),
				(int)( valueColor.getGreen() * ( 1 - pNote->get_velocity() ) ),
				(int)( valueColor.getBlue() * ( 1 - pNote->get_velocity() ) )
			);
			int nLineWidth = (int)( m_nGridWidth / 2.0 );
			int nSpace = (int)( ( m_nGridWidth -nLineWidth ) / 2.0 );
			p.fillRect( (int)( x_pos - nSpace ) + 1, line_start, nLineWidth, line_end - line_start, centerColor );
		}
	}
	p.setPen(res_1);
	p.drawLine(0, 0, m_nEditorWidth, 0);
	p.drawLine(0, m_nEditorHeight - 1, m_nEditorWidth, m_nEditorHeight - 1);
}



void NotePropertiesRuler::createPanBackground(QPixmap *pixmap)
{
	if ( !isVisible() ) {
		return;
	}


	UIStyle *pStyle = g_engine->get_preferences()->getDefaultUIStyle();

	QColor backgroundColor( pStyle->m_patternEditor_backgroundColor.getRed(), pStyle->m_patternEditor_backgroundColor.getGreen(), pStyle->m_patternEditor_backgroundColor.getBlue() );

	//QColor backgroundColor( 255, 255, 255 );
	QColor blackKeysColor( 240, 240, 240 );
	QColor horizLinesColor(
			pStyle->m_patternEditor_backgroundColor.getRed() - 20,
			pStyle->m_patternEditor_backgroundColor.getGreen() - 20,
			pStyle->m_patternEditor_backgroundColor.getBlue() - 20
	);
	H2RGBColor valueColor(
			(int)( pStyle->m_patternEditor_backgroundColor.getRed() * ( 1 - 0.3 ) ),
			(int)( pStyle->m_patternEditor_backgroundColor.getGreen() * ( 1 - 0.3 ) ),
			(int)( pStyle->m_patternEditor_backgroundColor.getBlue() * ( 1 - 0.3 ) )
	);

	QColor res_1( pStyle->m_patternEditor_line1Color.getRed(), pStyle->m_patternEditor_line1Color.getGreen(), pStyle->m_patternEditor_line1Color.getBlue() );
	QColor res_2( pStyle->m_patternEditor_line2Color.getRed(), pStyle->m_patternEditor_line2Color.getGreen(), pStyle->m_patternEditor_line2Color.getBlue() );
	QColor res_3( pStyle->m_patternEditor_line3Color.getRed(), pStyle->m_patternEditor_line3Color.getGreen(), pStyle->m_patternEditor_line3Color.getBlue() );
	QColor res_4( pStyle->m_patternEditor_line4Color.getRed(), pStyle->m_patternEditor_line4Color.getGreen(), pStyle->m_patternEditor_line4Color.getBlue() );
	QColor res_5( pStyle->m_patternEditor_line5Color.getRed(), pStyle->m_patternEditor_line5Color.getGreen(), pStyle->m_patternEditor_line5Color.getBlue() );

	QPainter p( pixmap );

	p.fillRect( 0, 0, width(), height(), QColor(0, 0, 0) );

	unsigned nNotes = MAX_NOTES;
	if (m_pPattern) {
		nNotes = m_pPattern->get_length();
	}
	p.fillRect( 0, 0, 20 + nNotes * m_nGridWidth, height(), backgroundColor );


	// central line
	p.setPen( horizLinesColor );
	p.drawLine(0, height() / 2.0, m_nEditorWidth, height() / 2.0);



	// vertical lines
	DrumPatternEditor *pPatternEditor = m_pPatternEditorPanel->getDrumPatternEditor();
	int nBase;
	if (pPatternEditor->isUsingTriplets()) {
		nBase = 3;
	}
	else {
		nBase = 4;
	}

	int n4th = 4 * MAX_NOTES / (nBase * 4);
	int n8th = 4 * MAX_NOTES / (nBase * 8);
	int n16th = 4 * MAX_NOTES / (nBase * 16);
	int n32th = 4 * MAX_NOTES / (nBase * 32);
	int n64th = 4 * MAX_NOTES / (nBase * 64);

	int nResolution = pPatternEditor->getResolution();

	if ( !pPatternEditor->isUsingTriplets() ) {

		for (uint i = 0; i < nNotes +1 ; i++) {
			uint x = 20 + i * m_nGridWidth;

			if ( (i % n4th) == 0 ) {
				if (nResolution >= 4) {
					p.setPen( QPen( res_1, 0, Qt::DotLine ) );
					p.drawLine(x, 0, x, m_nEditorHeight);
				}
			}
			else if ( (i % n8th) == 0 ) {
				if (nResolution >= 8) {
					p.setPen( QPen( res_2, 0, Qt::DotLine ) );
					p.drawLine(x, 0, x, m_nEditorHeight);
				}
			}
			else if ( (i % n16th) == 0 ) {
				if (nResolution >= 16) {
					p.setPen( QPen( res_3, 0, Qt::DotLine ) );
					p.drawLine(x, 0, x, m_nEditorHeight);
				}
			}
			else if ( (i % n32th) == 0 ) {
				if (nResolution >= 32) {
					p.setPen( QPen( res_4, 0, Qt::DotLine ) );
					p.drawLine(x, 0, x, m_nEditorHeight);
				}
			}
			else if ( (i % n64th) == 0 ) {
				if (nResolution >= 64) {
					p.setPen( QPen( res_5, 0, Qt::DotLine ) );
					p.drawLine(x, 0, x, m_nEditorHeight);
				}
			}
		}
	}
	else {	// Triplets
		uint nCounter = 0;
		int nSize = 4 * MAX_NOTES / (nBase * nResolution);

		for (uint i = 0; i < nNotes +1; i++) {
			uint x = 20 + i * m_nGridWidth;

			if ( (i % nSize) == 0) {
				if ((nCounter % 3) == 0) {
					p.setPen( QPen( res_1, 0, Qt::DotLine ) );
				}
				else {
					p.setPen( QPen( res_3, 0, Qt::DotLine ) );
				}
				p.drawLine(x, 0, x, m_nEditorHeight);
				nCounter++;
			}
		}
	}

	if ( m_pPattern ) {
		int nSelectedInstrument = g_engine->getSelectedInstrumentNumber();
		T<Song>::shared_ptr pSong = g_engine->getSong();

		Pattern::note_map_t::iterator pos;
		T<InstrumentList>::shared_ptr instrument_list = g_engine->get_sampler()->get_instrument_list();
		for ( pos = m_pPattern->note_map.begin(); pos != m_pPattern->note_map.end(); ++pos ) {
			Note *pNote = pos->second;
			assert( pNote );
			if ( pNote->get_instrument() != instrument_list->get( nSelectedInstrument ) ) {
				continue;
			}
			uint x_pos = 20 + pos->first * m_nGridWidth;

			if (pNote->get_pan_r() == pNote->get_pan_l()) {
				// pan value is centered - draw circle
				int y_pos = (int)( height() * 0.5 );
				p.setBrush(QColor( 0, 0, 0 ));
				p.drawEllipse( x_pos-4, y_pos-4, 8, 8);
			} else {
				int y_start = (int)( pNote->get_pan_l() * height() );
				int y_end = (int)( height() - pNote->get_pan_r() * height() );

				int nLineWidth = 3;
				p.fillRect( x_pos - 1, y_start, nLineWidth, y_end - y_start, QColor( 0, 0 ,0 ) );

				p.fillRect( x_pos - 1, ( height() / 2.0 ) - 2 , nLineWidth, 5, QColor( 0, 0 ,0 ) );
			}
		}
	}

	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) 
{
	if ( !isVisible() ) {   
		return;
	}
 
 
	UIStyle *pStyle = g_engine->get_preferences()->getDefaultUIStyle();
	
	QColor backgroundColor( pStyle->m_patternEditor_backgroundColor.getRed(), pStyle->m_patternEditor_backgroundColor.getGreen(), pStyle->m_patternEditor_backgroundColor.getBlue() );
	QColor blackKeysColor( 240, 240, 240 );
	QColor horizLinesColor(
			pStyle->m_patternEditor_backgroundColor.getRed() - 20,
			pStyle->m_patternEditor_backgroundColor.getGreen() - 20,
			pStyle->m_patternEditor_backgroundColor.getBlue() - 20
	);
	H2RGBColor valueColor(
			(int)( pStyle->m_patternEditor_backgroundColor.getRed() * ( 1 - 0.3 ) ),
			(int)( pStyle->m_patternEditor_backgroundColor.getGreen() * ( 1 - 0.3 ) ),
			(int)( pStyle->m_patternEditor_backgroundColor.getBlue() * ( 1 - 0.3 ) )
	);
 
	QColor res_1( pStyle->m_patternEditor_line1Color.getRed(), pStyle->m_patternEditor_line1Color.getGreen(), pStyle->m_patternEditor_line1Color.getBlue() );
	QColor res_2( pStyle->m_patternEditor_line2Color.getRed(), pStyle->m_patternEditor_line2Color.getGreen(), pStyle->m_patternEditor_line2Color.getBlue() );
	QColor res_3( pStyle->m_patternEditor_line3Color.getRed(), pStyle->m_patternEditor_line3Color.getGreen(), pStyle->m_patternEditor_line3Color.getBlue() );
	QColor res_4( pStyle->m_patternEditor_line4Color.getRed(), pStyle->m_patternEditor_line4Color.getGreen(), pStyle->m_patternEditor_line4Color.getBlue() );
	QColor res_5( pStyle->m_patternEditor_line5Color.getRed(), pStyle->m_patternEditor_line5Color.getGreen(), pStyle->m_patternEditor_line5Color.getBlue() );
 
	QPainter p( pixmap );
 
	p.fillRect( 0, 0, width(), height(), QColor(0, 0, 0) );
 
	unsigned nNotes = MAX_NOTES;
	if (m_pPattern) {
		nNotes = m_pPattern->get_length();
	}
	p.fillRect( 0, 0, 20 + nNotes * m_nGridWidth, height(), backgroundColor );
 
 
	// central line
	p.setPen( horizLinesColor );
	p.drawLine(0, height() / 2.0, m_nEditorWidth, height() / 2.0);
 
 
 
	// vertical lines
	DrumPatternEditor *pPatternEditor = m_pPatternEditorPanel->getDrumPatternEditor();
	int nBase;
	if (pPatternEditor->isUsingTriplets()) {
		nBase = 3;
	}
	else {
		nBase = 4;
	}
 
	int n4th = 4 * MAX_NOTES / (nBase * 4);
	int n8th = 4 * MAX_NOTES / (nBase * 8);
	int n16th = 4 * MAX_NOTES / (nBase * 16);
	int n32th = 4 * MAX_NOTES / (nBase * 32);
	int n64th = 4 * MAX_NOTES / (nBase * 64);
 
	int nResolution = pPatternEditor->getResolution();
 
	if ( !pPatternEditor->isUsingTriplets() ) {
 
		for (uint i = 0; i < nNotes + 1; i++) {
			uint x = 20 + i * m_nGridWidth;
 
			if ( (i % n4th) == 0 ) {
				if (nResolution >= 4) {
					p.setPen( QPen( res_1, 0, Qt::DotLine ) );
					p.drawLine(x, 0, x, m_nEditorHeight);
				}
			}
			else if ( (i % n8th) == 0 ) {
				if (nResolution >= 8) {
					p.setPen( QPen( res_2, 0, Qt::DotLine ) );
					p.drawLine(x, 0, x, m_nEditorHeight);
				}
			}
			else if ( (i % n16th) == 0 ) {
				if (nResolution >= 16) {
					p.setPen( QPen( res_3, 0, Qt::DotLine ) );
					p.drawLine(x, 0, x, m_nEditorHeight);
				}
			}
			else if ( (i % n32th) == 0 ) {
				if (nResolution >= 32) {
					p.setPen( QPen( res_4, 0, Qt::DotLine ) );
					p.drawLine(x, 0, x, m_nEditorHeight);
				}
			}
			else if ( (i % n64th) == 0 ) {
				if (nResolution >= 64) {
					p.setPen( QPen( res_5, 0, Qt::DotLine ) );
					p.drawLine(x, 0, x, m_nEditorHeight);
				}
			}
		}
	}
	else {  // Triplets
		uint nCounter = 0;
		int nSize = 4 * MAX_NOTES / (nBase * nResolution);
 
		for (uint i = 0; i < nNotes + 1; i++) {
			uint x = 20 + i * m_nGridWidth;
 
			if ( (i % nSize) == 0) {
				if ((nCounter % 3) == 0) {
					p.setPen( QPen( res_1, 0, Qt::DotLine ) );
				}
				else {
					p.setPen( QPen( res_3, 0, Qt::DotLine ) );
				}
				p.drawLine(x, 0, x, m_nEditorHeight);
				nCounter++;
			}
		}
	}
 
	if ( m_pPattern ) {
		int nSelectedInstrument = g_engine->getSelectedInstrumentNumber();
		T<Song>::shared_ptr pSong = g_engine->getSong();
 
		Pattern::note_map_t::iterator pos;
		T<InstrumentList>::shared_ptr instrument_list = g_engine->get_sampler()->get_instrument_list();
		for ( pos = m_pPattern->note_map.begin(); pos != m_pPattern->note_map.end(); ++pos ) {
			Note *pNote = pos->second;
			assert( pNote );
			if ( pNote->get_instrument() != instrument_list->get( nSelectedInstrument ) ) {
				continue;
			}
			uint x_pos = 20 + pos->first * m_nGridWidth;

			if (pNote->get_leadlag() == 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, y_pos-4, 8, 8);
			} else {
				int y_start = (int)( height() * 0.5 );
				int y_end = y_start + ((pNote->get_leadlag()/2) * height());
	 
				int nLineWidth = 3;
				int red;
				int green;
				int blue = (int) (pNote->get_leadlag() * 255);
				if (blue < 0)  {
					red = blue *-1;
					blue = (int) red * .33;
					green = (int) red * .33;
				} else {
					red = (int) blue * .33;
					green = (int) blue * .33;
				}
				p.fillRect( x_pos - 1, y_start, nLineWidth, y_end - y_start, QColor( red, green ,blue ) );
	 
				p.fillRect( x_pos - 1, ( height() / 2.0 ) - 2 , nLineWidth, 5, QColor( red, green ,blue ) );
			}
 
		}
	}
 
	p.setPen(res_1);
	p.drawLine(0, 0, m_nEditorWidth, 0);
	p.drawLine(0, m_nEditorHeight - 1, m_nEditorWidth, m_nEditorHeight - 1);
}


void NotePropertiesRuler::updateEditor()
{
	Engine *pEngine = g_engine;
	PatternList *pPatternList = pEngine->getSong()->get_pattern_list();
	int nSelectedPatternNumber = pEngine->getSelectedPatternNumber();
	if ( (nSelectedPatternNumber != -1) && ( (uint)nSelectedPatternNumber < pPatternList->get_size() ) ) {
		m_pPattern = pPatternList->get( nSelectedPatternNumber );
	}
	else {
		m_pPattern.reset();
	}


	// update editor width
	int editorWidth;
	if ( m_pPattern ) {
		editorWidth = 20 + m_pPattern->get_length() * m_nGridWidth;
	}
	else {
		editorWidth =  20 + MAX_NOTES * m_nGridWidth;
	}
	resize( editorWidth, height() );
		
	delete m_pBackground;
	m_pBackground = new QPixmap( editorWidth, m_nEditorHeight );

	if ( m_mode == VELOCITY ) {
		createVelocityBackground( m_pBackground );
	}
	else if ( m_mode == PAN ) {
		createPanBackground( m_pBackground );
	}
	else if ( m_mode == LEADLAG ) {
		createLeadLagBackground( m_pBackground );
	}

	// redraw all
	update();
}



void NotePropertiesRuler::zoomIn()
{
	if (m_nGridWidth >= 3){
		m_nGridWidth *= 2;
	}else
	{
		m_nGridWidth *= 1.5;
	}
	updateEditor();
}



void NotePropertiesRuler::zoomOut()
{
	if ( m_nGridWidth > 1.5 ) {
		if (m_nGridWidth > 3){
			m_nGridWidth /=  2;
		}else
		{
			m_nGridWidth /= 1.5;
		}
	updateEditor();
	}
}



void NotePropertiesRuler::selectedPatternChangedEvent()
{
	updateEditor();
}



void NotePropertiesRuler::selectedInstrumentChangedEvent()
{
	updateEditor();
}



