code:
/*
 * TempoSyncKnob.cpp - adds bpm to ms conversion for knob class
 *
 * Copyright (c) 2005-2007 Danny McRae <khjklujn/at/yahoo.com>
 * Copyright (c) 2005-2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
 *
 * This file is part of LMMS - https://lmms.io
 *
 * 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 <QMdiArea>

#include "TempoSyncKnob.h"
#include "Engine.h"
#include "CaptionMenu.h"
#include "embed.h"
#include "GuiApplication.h"
#include "MainWindow.h"
#include "MeterDialog.h"
#include "Song.h"

TempoSyncKnob::TempoSyncKnob( knobTypes _knob_num, QWidget * _parent,
						const QString & _name ) :
	Knob( _knob_num, _parent, _name ),
	m_tempoSyncIcon( embed::getIconPixmap( "tempo_sync" ) ),
	m_tempoSyncDescription( tr( "Tempo Sync" ) ),
	m_custom( NULL )
{
}

TempoSyncKnob::~TempoSyncKnob()
{
	if( m_custom )
	{
		delete m_custom->parentWidget();
	}
}

void TempoSyncKnob::modelChanged()
{
	if( model() == NULL )
	{
		qWarning( "no TempoSyncKnobModel has been set!" );
	}
	if( m_custom != NULL )
	{
		m_custom->setModel( &model()->m_custom );
	}
	connect( model(), SIGNAL( syncModeChanged( TempoSyncMode ) ),
			this, SLOT( updateDescAndIcon() ) );
	connect( this, SIGNAL( sliderMoved( float ) ),
			model(), SLOT( disableSync() ) );
	updateDescAndIcon();
}

void TempoSyncKnob::contextMenuEvent( QContextMenuEvent * )
{
	mouseReleaseEvent( NULL );

	CaptionMenu contextMenu( model()->displayName(), this );
	addDefaultActions( &contextMenu );
	contextMenu.addSeparator();

	float limit = 60000.0f / ( Engine::getSong()->getTempo() *
							model()->m_scale );

	QMenu * syncMenu = contextMenu.addMenu( m_tempoSyncIcon,
						m_tempoSyncDescription );
	if( limit / 8.0f <= model()->maxValue() )
	{

	connect( syncMenu, SIGNAL( triggered( QAction * ) ),
			model(), SLOT( setTempoSync( QAction * ) ) );
	syncMenu->addAction( embed::getIconPixmap( "note_none" ),
		tr( "No Sync" ) )->setData( (int) TempoSyncKnobModel::SyncNone );
	if( limit / 0.125f <= model()->maxValue() )
	{
		syncMenu->addAction( embed::getIconPixmap( "note_double_whole" ),
				tr( "Eight beats" ) )->setData(
				(int) TempoSyncKnobModel::SyncDoubleWholeNote );
	}
	if( limit / 0.25f <= model()->maxValue() )
	{
		syncMenu->addAction( embed::getIconPixmap( "note_whole" ),
					tr( "Whole note" ) )->setData(
					(int) TempoSyncKnobModel::SyncWholeNote );
	}
	if( limit / 0.5f <= model()->maxValue() )
	{
		syncMenu->addAction( embed::getIconPixmap( "note_half" ),
					tr( "Half note" ) )->setData(
					(int) TempoSyncKnobModel::SyncHalfNote );
	}
	if( limit <= model()->maxValue() )
	{
		syncMenu->addAction( embed::getIconPixmap( "note_quarter" ),
					tr( "Quarter note" ) )->setData(
				(int) TempoSyncKnobModel::SyncQuarterNote );
	}
	if( limit / 2.0f <= model()->maxValue() )
	{
		syncMenu->addAction( embed::getIconPixmap( "note_eighth" ),
					tr( "8th note" ) )->setData(
				(int) TempoSyncKnobModel::SyncEighthNote );
	}
	if( limit / 4.0f <= model()->maxValue() )
	{
		syncMenu->addAction( embed::getIconPixmap( "note_sixteenth" ),
					tr( "16th note" ) )->setData(
				(int) TempoSyncKnobModel::SyncSixteenthNote );
	}
	syncMenu->addAction( embed::getIconPixmap( "note_thirtysecond" ),
					tr( "32nd note" ) )->setData(
				(int) TempoSyncKnobModel::SyncThirtysecondNote );
	syncMenu->addAction( embed::getIconPixmap( "dont_know" ),
				tr( "Custom..." ),
				this, SLOT( showCustom() )
						)->setData(
					(int) TempoSyncKnobModel::SyncCustom );
	contextMenu.addSeparator();

	}
	contextMenu.exec( QCursor::pos() );

	delete syncMenu;
}

void TempoSyncKnob::updateDescAndIcon()
{
	if( model()->m_tempoSyncMode )
	{
		switch( model()->m_tempoSyncMode )
		{
			case TempoSyncKnobModel::SyncCustom:
				m_tempoSyncDescription = tr( "Custom " ) +
						"(" +
			QString::number( model()->m_custom.numeratorModel().value() ) +
						"/" +
			QString::number( model()->m_custom.denominatorModel().value() ) +
						")";
				break;
			case TempoSyncKnobModel::SyncDoubleWholeNote:
				m_tempoSyncDescription = tr(
						"Synced to Eight Beats" );
				break;
			case TempoSyncKnobModel::SyncWholeNote:
				m_tempoSyncDescription = tr(
						"Synced to Whole Note" );
				break;
			case TempoSyncKnobModel::SyncHalfNote:
				m_tempoSyncDescription = tr(
							"Synced to Half Note" );
				break;
			case TempoSyncKnobModel::SyncQuarterNote:
				m_tempoSyncDescription = tr(
						"Synced to Quarter Note" );
				break;
			case TempoSyncKnobModel::SyncEighthNote:
				m_tempoSyncDescription = tr(
							"Synced to 8th Note" );
				break;
			case TempoSyncKnobModel::SyncSixteenthNote:
				m_tempoSyncDescription = tr(
							"Synced to 16th Note" );
				break;
			case TempoSyncKnobModel::SyncThirtysecondNote:
				m_tempoSyncDescription = tr(
							"Synced to 32nd Note" );
				break;
			default: ;
		}
	}
	else
	{
		m_tempoSyncDescription = tr( "Tempo Sync" );
	}
	if( m_custom != NULL &&
		model()->m_tempoSyncMode != TempoSyncKnobModel::SyncCustom )
	{
		m_custom->parentWidget()->hide();
	}

	switch( model()->m_tempoSyncMode )
	{
		case TempoSyncKnobModel::SyncNone:
			m_tempoSyncIcon = embed::getIconPixmap( "tempo_sync" );
			break;
		case TempoSyncKnobModel::SyncCustom:
			m_tempoSyncIcon = embed::getIconPixmap( "dont_know" );
			break;
		case TempoSyncKnobModel::SyncDoubleWholeNote:
			m_tempoSyncIcon = embed::getIconPixmap( "note_double_whole" );
			break;
		case TempoSyncKnobModel::SyncWholeNote:
			m_tempoSyncIcon = embed::getIconPixmap( "note_whole" );
			break;
		case TempoSyncKnobModel::SyncHalfNote:
			m_tempoSyncIcon = embed::getIconPixmap( "note_half" );
			break;
		case TempoSyncKnobModel::SyncQuarterNote:
			m_tempoSyncIcon = embed::getIconPixmap( "note_quarter" );
			break;
		case TempoSyncKnobModel::SyncEighthNote:
			m_tempoSyncIcon = embed::getIconPixmap( "note_eighth" );
			break;
		case TempoSyncKnobModel::SyncSixteenthNote:
			m_tempoSyncIcon = embed::getIconPixmap( "note_sixteenth" );
			break;
		case TempoSyncKnobModel::SyncThirtysecondNote:
			m_tempoSyncIcon = embed::getIconPixmap( "note_thirtysecond" );
			break;
		default:
			qWarning( "TempoSyncKnob::calculateTempoSyncTime:"
						"invalid TempoSyncMode" );
			break;
	}

	emit syncDescriptionChanged( m_tempoSyncDescription );
	emit syncIconChanged();
}

const QString & TempoSyncKnob::syncDescription()
{
	return m_tempoSyncDescription;
}

void TempoSyncKnob::setSyncDescription( const QString & _new_description )
{
	m_tempoSyncDescription = _new_description;
	emit syncDescriptionChanged( _new_description );
}

const QPixmap & TempoSyncKnob::syncIcon()
{
	return m_tempoSyncIcon;
}

void TempoSyncKnob::setSyncIcon( const QPixmap & _new_icon )
{
	m_tempoSyncIcon = _new_icon;
	emit syncIconChanged();
}

void TempoSyncKnob::showCustom()
{
	if( m_custom == NULL )
	{
		m_custom = new MeterDialog( gui->mainWindow()->workspace() );
		QMdiSubWindow * subWindow = gui->mainWindow()->addWindowedWidget( m_custom );
		Qt::WindowFlags flags = subWindow->windowFlags();
		flags &= ~Qt::WindowMaximizeButtonHint;
		subWindow->setWindowFlags( flags );
		subWindow->setFixedSize( subWindow->size() );
		m_custom->setWindowTitle( "Meter" );
		m_custom->setModel( &model()->m_custom );
	}
	m_custom->parentWidget()->show();
	model()->setTempoSync( TempoSyncKnobModel::SyncCustom );
}


code:
/*
 * TextFloat.cpp - class textFloat, a floating text-label
 *
 * Copyright (c) 2005-2010 Tobias Doerffel <tobydox/at/users.sourceforge.net>
 *
 * This file is part of LMMS - https://lmms.io
 *
 * 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 <QTimer>
#include <QPainter>
#include <QStyleOption>

#include "TextFloat.h"
#include "gui_templates.h"
#include "GuiApplication.h"
#include "MainWindow.h"

TextFloat::TextFloat() :
	QWidget( gui->mainWindow(), Qt::ToolTip ),
	m_title(),
	m_text(),
	m_pixmap()
{
	resize( 20, 20 );
	hide();

	setAttribute( Qt::WA_TranslucentBackground, true );
	setStyle( QApplication::style() );
	setFont( pointSize<8>( font() ) );
}

void TextFloat::setTitle( const QString & _title )
{
	m_title = _title;
	updateSize();
}

void TextFloat::setText( const QString & _text )
{
	m_text = _text;
	updateSize();
}

void TextFloat::setPixmap( const QPixmap & _pixmap )
{
	m_pixmap = _pixmap;
	updateSize();
}

void TextFloat::setVisibilityTimeOut( int _msecs )
{
	QTimer::singleShot( _msecs, this, SLOT( hide() ) );
	show();
}

TextFloat * TextFloat::displayMessage( const QString & _msg, int _timeout,
					QWidget * _parent, int _add_y_margin )
{
	QWidget * mw = gui->mainWindow();
	TextFloat * tf = new TextFloat;
	if( _parent != NULL )
	{
		tf->moveGlobal( _parent, QPoint( _parent->width() + 2, 0 ) );
	}
	else
	{
		tf->moveGlobal( mw, QPoint( 32, mw->height() - tf->height() - 8 - _add_y_margin ) );
	}
	tf->setText( _msg );
	tf->show();
	if( _timeout > 0 )
	{
		tf->setAttribute( Qt::WA_DeleteOnClose, true );
		QTimer::singleShot( _timeout, tf, SLOT( close() ) );
	}
	return( tf );
}

TextFloat * TextFloat::displayMessage( const QString & _title,
					const QString & _msg,
					const QPixmap & _pixmap,
					int _timeout, QWidget * _parent )
{
	TextFloat * tf = displayMessage( _msg, _timeout, _parent, 16 );
	tf->setTitle( _title );
	tf->setPixmap( _pixmap );
	return( tf );
}

void TextFloat::paintEvent( QPaintEvent * _pe )
{
	QStyleOption opt;
    opt.init( this );
	QPainter p( this );
	p.fillRect( 0, 0, width(), height(), QColor( 0, 0, 0, 0 ) );

/*	p.setPen( p.pen().brush().color() );
	p.setBrush( p.background() );*/

	p.setFont( pointSize<8>( p.font() ) );

	style()->drawPrimitive( QStyle::PE_Widget, &opt, &p, this );

/*	p.drawRect( 0, 0, rect().right(), rect().bottom() );*/

	if( m_title.isEmpty() )
	{
		p.drawText( opt.rect, Qt::AlignCenter, m_text );
	}
	else
	{
		int text_x = opt.rect.left() + 2;
		int text_y = opt.rect.top() + 12;
		if( m_pixmap.isNull() == false )
		{
			p.drawPixmap( opt.rect.topLeft() + QPoint( 5, 5 ), m_pixmap );
			text_x += m_pixmap.width() + 8;
		}
		p.drawText( text_x, text_y + 16, m_text );
		QFont f = p.font();
		f.setBold( true );
		p.setFont( f );
		p.drawText( text_x, text_y, m_title );
	}
}

void TextFloat::mousePressEvent( QMouseEvent * )
{
	close();
}

void TextFloat::updateSize()
{
	QFontMetrics metrics( pointSize<8>( font() ) );
	QRect textBound = metrics.boundingRect( m_text );
	if( !m_title.isEmpty() )
	{
		QFont f = pointSize<8>( font() );
		f.setBold( true );
		int title_w = QFontMetrics( f ).boundingRect( m_title ).width();
		if( title_w > textBound.width() )
		{
			textBound.setWidth( title_w );
		}
		textBound.setHeight( textBound.height() * 2 + 8 );
	}
	if( m_pixmap.isNull() == false )
	{
		textBound.setWidth( textBound.width() + m_pixmap.width() + 10 );
	}
	resize( textBound.width() + 5, textBound.height()+2 );
	//move( QPoint( parentWidget()->width() + 5, 5 ) );
	update();
}

repo_name: CYBERDEViL/LMMS

license: mit


code:
/*
 * TimeDisplayWidget.cpp - widget for displaying current playback time
 *
 * Copyright (c) 2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
 *
 * This file is part of LMMS - https://lmms.io
 *
 * 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 <QMouseEvent>

#include "TimeDisplayWidget.h"
#include "GuiApplication.h"
#include "MainWindow.h"
#include "Engine.h"
#include "ToolTip.h"
#include "Song.h"

TimeDisplayWidget::TimeDisplayWidget() :
	QWidget(),
	m_displayMode( MinutesSeconds ),
	m_spinBoxesLayout( this ),
	m_majorLCD( 4, this ),
	m_minorLCD( 2, this ),
	m_milliSecondsLCD( 3, this )
{
	m_spinBoxesLayout.setSpacing( 0 );
	m_spinBoxesLayout.setMargin( 0 );
	m_spinBoxesLayout.addWidget( &m_majorLCD );
	m_spinBoxesLayout.addWidget( &m_minorLCD );
	m_spinBoxesLayout.addWidget( &m_milliSecondsLCD );

	setMaximumHeight( 32 );

	ToolTip::add( this, tr( "Time units" ) );

	// update labels of LCD spinboxes
	setDisplayMode( m_displayMode );

	connect( gui->mainWindow(), SIGNAL( periodicUpdate() ),
					this, SLOT( updateTime() ) );
}

void TimeDisplayWidget::setDisplayMode( DisplayMode displayMode )
{
	m_displayMode = displayMode;

	switch( m_displayMode )
	{
		case MinutesSeconds:
			m_majorLCD.setLabel( tr( "MIN" ) );
			m_minorLCD.setLabel( tr( "SEC" ) );
			m_milliSecondsLCD.setLabel( tr( "MSEC" ) );
			break;

		case BarsTicks:
			m_majorLCD.setLabel( tr( "BAR" ) );
			m_minorLCD.setLabel( tr( "BEAT" ) );
			m_milliSecondsLCD.setLabel( tr( "TICK" ) );
			break;

		default: break;
	}
}

void TimeDisplayWidget::updateTime()
{
	Song* s = Engine::getSong();

	switch( m_displayMode )
	{
		case MinutesSeconds:
			int msec;
			msec = s->getMilliseconds();
			m_majorLCD.setValue(msec / 60000);
			m_minorLCD.setValue((msec / 1000) % 60);
			m_milliSecondsLCD.setValue(msec % 1000);
			break;

		case BarsTicks:
			int tick;
			tick = s->getPlayPos().getTicks();
			m_majorLCD.setValue((int)(tick / s->ticksPerBar()) + 1);
			m_minorLCD.setValue((tick % s->ticksPerBar()) /
						 (s->ticksPerBar() / s->getTimeSigModel().getNumerator() ) +1);
			m_milliSecondsLCD.setValue((tick % s->ticksPerBar()) %
							(s->ticksPerBar() / s->getTimeSigModel().getNumerator()));
			break;

		default: break;
	}
}

void TimeDisplayWidget::mousePressEvent( QMouseEvent* mouseEvent )
{
	if( mouseEvent->button() == Qt::LeftButton )
	{
		if( m_displayMode == MinutesSeconds )
		{
			setDisplayMode( BarsTicks );
		}
		else
		{
			setDisplayMode( MinutesSeconds );
		}
	}
}


code:
/*
 * ToolButton.cpp - implementation of LMMS-tool-button for common (cool) look
 *
 * Copyright (c) 2005-2006 Tobias Doerffel <tobydox/at/users.sourceforge.net>
 *
 * This file is part of LMMS - https://lmms.io
 *
 * 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 "ToolButton.h"
#include "ToolTip.h"

ToolButton::ToolButton( const QPixmap & _pixmap, const QString & _tooltip,
			QObject * _receiver, const char * _slot,
			QWidget * _parent ) :
	QToolButton( _parent )
{
	setAutoFillBackground( false );

	if( _receiver != NULL && _slot != NULL )
	{
		connect( this, SIGNAL( clicked() ), _receiver, _slot );
	}
	ToolTip::add( this, _tooltip );
	setIcon( _pixmap );
}


code:
/*
 * ToolTip.cpp - namespace toolTip, a tooltip-wrapper for LMMS
 *
 * Copyright (c) 2005-2006 Tobias Doerffel <tobydox/at/users.sourceforge.net>
 *
 * This file is part of LMMS - https://lmms.io
 *
 * 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 <QToolTip>

#include "ToolTip.h"
#include "ConfigManager.h"

void ToolTip::add( QWidget * _w, const QString & _txt )
{
	if( !ConfigManager::inst()->value( "tooltips", "disabled" ).toInt() )
	{
		_w->setToolTip( _txt );
	}
}


code:
/*
 * TrackContentWidget.cpp - implementation of TrackContentWidget class
 *
 * Copyright (c) 2004-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
 *
 * This file is part of LMMS - https://lmms.io
 *
 * 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 "TrackContentWidget.h"

#include <QApplication>
#include <QContextMenuEvent>
#include <QMenu>
#include <QPainter>

#include "AutomationPattern.h"
#include "BBEditor.h"
#include "BBTrackContainer.h"
#include "Clipboard.h"
#include "DataFile.h"
#include "Engine.h"
#include "GuiApplication.h"
#include "Song.h"
#include "SongEditor.h"
#include "StringPairDrag.h"
#include "TrackContainerView.h"
#include "TrackContentObjectView.h"
#include "TrackView.h"

/*! Alternate between a darker and a lighter background color every 4 bars
 */
const int BARS_PER_GROUP = 4;

/*! \brief Create a new trackContentWidget
 *
 *  Creates a new track content widget for the given track.
 *  The content widget comprises the 'grip bar' and the 'tools' button
 *  for the track's context menu.
 *
 * \param parent The parent track.
 */
TrackContentWidget::TrackContentWidget( TrackView * parent ) :
	QWidget( parent ),
	m_trackView( parent ),
	m_darkerColor( Qt::SolidPattern ),
	m_lighterColor( Qt::SolidPattern ),
	m_gridColor( Qt::SolidPattern ),
	m_embossColor( Qt::SolidPattern )
{
	setAcceptDrops( true );

	connect( parent->trackContainerView(),
			SIGNAL( positionChanged( const TimePos & ) ),
			this, SLOT( changePosition( const TimePos & ) ) );

	setStyle( QApplication::style() );

	updateBackground();
}

/*! \brief Destroy this trackContentWidget
 *
 *  Destroys the trackContentWidget.
 */
TrackContentWidget::~TrackContentWidget()
{
}

void TrackContentWidget::updateBackground()
{
	const TrackContainerView * tcv = m_trackView->trackContainerView();

	// Assume even-pixels-per-bar. Makes sense, should be like this anyways
	int ppb = static_cast<int>( tcv->pixelsPerBar() );

	int w = ppb * BARS_PER_GROUP;
	int h = height();
	m_background = QPixmap( w * 2, height() );
	QPainter pmp( &m_background );

	pmp.fillRect( 0, 0, w, h, darkerColor() );
	pmp.fillRect( w, 0, w , h, lighterColor() );

	// draw lines
	// vertical lines
	pmp.setPen( QPen( gridColor(), 1 ) );
	for( float x = 0; x < w * 2; x += ppb )
	{
		pmp.drawLine( QLineF( x, 0.0, x, h ) );
	}

	pmp.setPen( QPen( embossColor(), 1 ) );
	for( float x = 1.0; x < w * 2; x += ppb )
	{
		pmp.drawLine( QLineF( x, 0.0, x, h ) );
	}

	// horizontal line
	pmp.setPen( QPen( gridColor(), 1 ) );
	pmp.drawLine( 0, h-1, w*2, h-1 );

	pmp.end();

	// Force redraw
	update();
}

/*! \brief Adds a trackContentObjectView to this widget.
 *
 *  Adds a(nother) trackContentObjectView to our list of views.  We also
 *  check that our position is up-to-date.
 *
 * \param tcov The trackContentObjectView to add.
 */
void TrackContentWidget::addTCOView( TrackContentObjectView * tcov )
{
	TrackContentObject * tco = tcov->getTrackContentObject();

	m_tcoViews.push_back( tcov );

	tco->saveJournallingState( false );
	changePosition();
	tco->restoreJournallingState();
}

/*! \brief Removes the given trackContentObjectView to this widget.
 *
 *  Removes the given trackContentObjectView from our list of views.
 *
 * \param tcov The trackContentObjectView to add.
 */
void TrackContentWidget::removeTCOView( TrackContentObjectView * tcov )
{
	tcoViewVector::iterator it = std::find( m_tcoViews.begin(),
						m_tcoViews.end(),
						tcov );
	if( it != m_tcoViews.end() )
	{
		m_tcoViews.erase( it );
		Engine::getSong()->setModified();
	}
}

/*! \brief Update ourselves by updating all the tCOViews attached.
 *
 */
void TrackContentWidget::update()
{
	for( tcoViewVector::iterator it = m_tcoViews.begin();
				it != m_tcoViews.end(); ++it )
	{
		( *it )->setFixedHeight( height() - 1 );
		( *it )->update();
	}
	QWidget::update();
}

// resposible for moving track-content-widgets to appropriate position after
// change of visible viewport
/*! \brief Move the trackContentWidget to a new place in time
 *
 * \param newPos The MIDI time to move to.
 */
void TrackContentWidget::changePosition( const TimePos & newPos )
{
	if( m_trackView->trackContainerView() == gui->getBBEditor()->trackContainerView() )
	{
		const int curBB = Engine::getBBTrackContainer()->currentBB();
		setUpdatesEnabled( false );

		// first show TCO for current BB...
		for( tcoViewVector::iterator it = m_tcoViews.begin();
						it != m_tcoViews.end(); ++it )
		{
		if( ( *it )->getTrackContentObject()->
						startPosition().getBar() == curBB )
			{
				( *it )->move( 0, ( *it )->y() );
				( *it )->raise();
				( *it )->show();
			}
			else
			{
				( *it )->lower();
			}
		}
		// ...then hide others to avoid flickering
		for( tcoViewVector::iterator it = m_tcoViews.begin();
					it != m_tcoViews.end(); ++it )
		{
			if( ( *it )->getTrackContentObject()->
						startPosition().getBar() != curBB )
			{
				( *it )->hide();
			}
		}
		setUpdatesEnabled( true );
		return;
	}

	TimePos pos = newPos;
	if( pos < 0 )
	{
		pos = m_trackView->trackContainerView()->currentPosition();
	}

	const int begin = pos;
	const int end = endPosition( pos );
	const float ppb = m_trackView->trackContainerView()->pixelsPerBar();

	setUpdatesEnabled( false );
	for( tcoViewVector::iterator it = m_tcoViews.begin();
						it != m_tcoViews.end(); ++it )
	{
		TrackContentObjectView * tcov = *it;
		TrackContentObject * tco = tcov->getTrackContentObject();

		tco->changeLength( tco->length() );

		const int ts = tco->startPosition();
		const int te = tco->endPosition()-3;
		if( ( ts >= begin && ts <= end ) ||
			( te >= begin && te <= end ) ||
			( ts <= begin && te >= end ) )
		{
			tcov->move( static_cast<int>( ( ts - begin ) * ppb /
						TimePos::ticksPerBar() ),
								tcov->y() );
			if( !tcov->isVisible() )
			{
				tcov->show();
			}
		}
		else
		{
			tcov->move( -tcov->width()-10, tcov->y() );
		}
	}
	setUpdatesEnabled( true );

	// redraw background
//	update();
}

/*! \brief Return the position of the trackContentWidget in bars.
 *
 * \param mouseX the mouse's current X position in pixels.
 */
TimePos TrackContentWidget::getPosition( int mouseX )
{
	TrackContainerView * tv = m_trackView->trackContainerView();
	return TimePos( tv->currentPosition() +
					 mouseX *
					 TimePos::ticksPerBar() /
					 static_cast<int>( tv->pixelsPerBar() ) );
}

/*! \brief Respond to a drag enter event on the trackContentWidget
 *
 * \param dee the Drag Enter Event to respond to
 */
void TrackContentWidget::dragEnterEvent( QDragEnterEvent * dee )
{
	TimePos tcoPos = getPosition( dee->pos().x() );
	if( canPasteSelection( tcoPos, dee ) == false )
	{
		dee->ignore();
	}
	else
	{
		StringPairDrag::processDragEnterEvent( dee, "tco_" +
						QString::number( getTrack()->type() ) );
	}
}

/*! \brief Returns whether a selection of TCOs can be pasted into this
 *
 * \param tcoPos the position of the TCO slot being pasted on
 * \param de the DropEvent generated
 */
bool TrackContentWidget::canPasteSelection( TimePos tcoPos, const QDropEvent* de )
{
	const QMimeData * mimeData = de->mimeData();

	// If the source of the DropEvent is the current instance of LMMS we don't allow pasting in the same bar
	// if it's another instance of LMMS we allow it
	return de->source()
		? canPasteSelection( tcoPos, mimeData )
		: canPasteSelection( tcoPos, mimeData, true );
}

// Overloaded method to make it possible to call this method without a Drag&Drop event
bool TrackContentWidget::canPasteSelection( TimePos tcoPos, const QMimeData* md , bool allowSameBar )
{
	// For decodeKey() and decodeValue()
	using namespace Clipboard;

	Track * t = getTrack();
	QString type = decodeKey( md );
	QString value = decodeValue( md );

	// We can only paste into tracks of the same type
	if( type != ( "tco_" + QString::number( t->type() ) ) ||
		m_trackView->trackContainerView()->fixedTCOs() == true )
	{
		return false;
	}

	// value contains XML needed to reconstruct TCOs and place them
	DataFile dataFile( value.toUtf8() );

	// Extract the metadata and which TCO was grabbed
	QDomElement metadata = dataFile.content().firstChildElement( "copyMetadata" );
	QDomAttr tcoPosAttr = metadata.attributeNode( "grabbedTCOPos" );
	TimePos grabbedTCOPos = tcoPosAttr.value().toInt();
	TimePos grabbedTCOBar = TimePos( grabbedTCOPos.getBar(), 0 );

	// Extract the track index that was originally clicked
	QDomAttr tiAttr = metadata.attributeNode( "initialTrackIndex" );
	const int initialTrackIndex = tiAttr.value().toInt();

	// Get the current track's index
	const TrackContainer::TrackList tracks = t->trackContainer()->tracks();
	const int currentTrackIndex = tracks.indexOf( t );

	// Don't paste if we're on the same bar and allowSameBar is false
	auto sourceTrackContainerId = metadata.attributeNode( "trackContainerId" ).value().toUInt();
	if( !allowSameBar && sourceTrackContainerId == t->trackContainer()->id() &&
			tcoPos == grabbedTCOBar && currentTrackIndex == initialTrackIndex )
	{
		return false;
	}

	// Extract the tco data
	QDomElement tcoParent = dataFile.content().firstChildElement( "tcos" );
	QDomNodeList tcoNodes = tcoParent.childNodes();

	// Determine if all the TCOs will land on a valid track
	for( int i = 0; i < tcoNodes.length(); i++ )
	{
		QDomElement tcoElement = tcoNodes.item( i ).toElement();
		int trackIndex = tcoElement.attributeNode( "trackIndex" ).value().toInt();
		int finalTrackIndex = trackIndex + currentTrackIndex - initialTrackIndex;

		// Track must be in TrackContainer's tracks
		if( finalTrackIndex < 0 || finalTrackIndex >= tracks.size() )
		{
			return false;
		}

		// Track must be of the same type
		auto startTrackType = tcoElement.attributeNode("trackType").value().toInt();
		Track * endTrack = tracks.at( finalTrackIndex );
		if( startTrackType != endTrack->type() )
		{
			return false;
		}
	}

	return true;
}

/*! \brief Pastes a selection of TCOs onto the track
 *
 * \param tcoPos the position of the TCO slot being pasted on
 * \param de the DropEvent generated
 */
bool TrackContentWidget::pasteSelection( TimePos tcoPos, QDropEvent * de )
{
	const QMimeData * mimeData = de->mimeData();

	if( canPasteSelection( tcoPos, de ) == false )
	{
		return false;
	}

	// We set skipSafetyCheck to true because we already called canPasteSelection
	return pasteSelection( tcoPos, mimeData, true );
}

// Overloaded method so we can call it without a Drag&Drop event
bool TrackContentWidget::pasteSelection( TimePos tcoPos, const QMimeData * md, bool skipSafetyCheck )
{
	// For decodeKey() and decodeValue()
	using namespace Clipboard;

	// When canPasteSelection was already called before, skipSafetyCheck will skip this
	if( !skipSafetyCheck && canPasteSelection( tcoPos, md ) == false )
	{
		return false;
	}

	QString type = decodeKey( md );
	QString value = decodeValue( md );

	getTrack()->addJournalCheckPoint();

	// value contains XML needed to reconstruct TCOs and place them
	DataFile dataFile( value.toUtf8() );

	// Extract the tco data
	QDomElement tcoParent = dataFile.content().firstChildElement( "tcos" );
	QDomNodeList tcoNodes = tcoParent.childNodes();

	// Extract the track index that was originally clicked
	QDomElement metadata = dataFile.content().firstChildElement( "copyMetadata" );
	QDomAttr tiAttr = metadata.attributeNode( "initialTrackIndex" );
	int initialTrackIndex = tiAttr.value().toInt();
	QDomAttr tcoPosAttr = metadata.attributeNode( "grabbedTCOPos" );
	TimePos grabbedTCOPos = tcoPosAttr.value().toInt();

	// Snap the mouse position to the beginning of the dropped bar, in ticks
	const TrackContainer::TrackList tracks = getTrack()->trackContainer()->tracks();
	const int currentTrackIndex = tracks.indexOf( getTrack() );

	bool wasSelection = m_trackView->trackContainerView()->rubberBand()->selectedObjects().count();

	// Unselect the old group
		const QVector<selectableObject *> so =
			m_trackView->trackContainerView()->selectedObjects();
		for( QVector<selectableObject *>::const_iterator it = so.begin();
		    	it != so.end(); ++it )
		{
			( *it )->setSelected( false );
		}

	// TODO -- Need to draw the hovericon either way, or ghost the TCOs
	// onto their final position.

	float snapSize = gui->songEditor()->m_editor->getSnapSize();
	// All patterns should be offset the same amount as the grabbed pattern
	TimePos offset = TimePos(tcoPos - grabbedTCOPos);
	// Users expect clips to "fall" backwards, so bias the offset
	offset -= TimePos::ticksPerBar() * snapSize / 2;
	// The offset is quantized (rather than the positions) to preserve fine adjustments
	offset = offset.quantize(snapSize);

	// Get the leftmost TCO and fix the offset if it reaches below bar 0
	TimePos leftmostPos = grabbedTCOPos;
	for(int i = 0; i < tcoNodes.length(); ++i)
	{
		QDomElement outerTCOElement = tcoNodes.item(i).toElement();
		QDomElement tcoElement = outerTCOElement.firstChildElement();

		TimePos pos = tcoElement.attributeNode("pos").value().toInt();

		if(pos < leftmostPos) { leftmostPos = pos; }
	}
	// Fix offset if it sets the left most TCO to a negative position
	offset = std::max(offset.getTicks(), -leftmostPos.getTicks());

	for( int i = 0; i<tcoNodes.length(); i++ )
	{
		QDomElement outerTCOElement = tcoNodes.item( i ).toElement();
		QDomElement tcoElement = outerTCOElement.firstChildElement();

		int trackIndex = outerTCOElement.attributeNode( "trackIndex" ).value().toInt();
		int finalTrackIndex = trackIndex + ( currentTrackIndex - initialTrackIndex );
		Track * t = tracks.at( finalTrackIndex );

		// The new position is the old position plus the offset.
		TimePos pos = tcoElement.attributeNode( "pos" ).value().toInt() + offset;
		// If we land on ourselves, offset by one snap
		TimePos shift = TimePos::ticksPerBar() * gui->songEditor()->m_editor->getSnapSize();
		if (offset == 0 && initialTrackIndex == currentTrackIndex) { pos += shift; }

		TrackContentObject * tco = t->createTCO( pos );
		tco->restoreState( tcoElement );
		tco->movePosition(pos); // Because we restored the state, we need to move the TCO again.
		if( wasSelection )
		{
			tco->selectViewOnCreate( true );
		}
	}

	AutomationPattern::resolveAllIDs();

	return true;
}

/*! \brief Respond to a drop event on the trackContentWidget
 *
 * \param de the Drop Event to respond to
 */
void TrackContentWidget::dropEvent( QDropEvent * de )
{
	TimePos tcoPos = TimePos( getPosition( de->pos().x() ) );
	if( pasteSelection( tcoPos, de ) == true )
	{
		de->accept();
	}
}

/*! \brief Respond to a mouse press on the trackContentWidget
 *
 * \param me the mouse press event to respond to
 */
void TrackContentWidget::mousePressEvent( QMouseEvent * me )
{
	if( m_trackView->trackContainerView()->allowRubberband() == true )
	{
		QWidget::mousePressEvent( me );
	}
	else if( me->modifiers() & Qt::ShiftModifier )
	{
		QWidget::mousePressEvent( me );
	}
	else if( me->button() == Qt::LeftButton &&
			!m_trackView->trackContainerView()->fixedTCOs() )
	{
		QVector<selectableObject*> so =  m_trackView->trackContainerView()->rubberBand()->selectedObjects();
		for( int i = 0; i < so.count(); ++i )
		{
			so.at( i )->setSelected( false);
		}
		getTrack()->addJournalCheckPoint();
		const TimePos pos = getPosition( me->x() ).getBar() *
						TimePos::ticksPerBar();
		getTrack()->createTCO(pos);
	}
}

/*! \brief Repaint the trackContentWidget on command
 *
 * \param pe the Paint Event to respond to
 */
void TrackContentWidget::paintEvent( QPaintEvent * pe )
{
	// Assume even-pixels-per-bar. Makes sense, should be like this anyways
	const TrackContainerView * tcv = m_trackView->trackContainerView();
	int ppb = static_cast<int>( tcv->pixelsPerBar() );
	QPainter p( this );
	// Don't draw background on BB-Editor
	if( m_trackView->trackContainerView() != gui->getBBEditor()->trackContainerView() )
	{
		p.drawTiledPixmap( rect(), m_background, QPoint(
				tcv->currentPosition().getBar() * ppb, 0 ) );
	}
}

/*! \brief Updates the background tile pixmap on size changes.
 *
 * \param resizeEvent the resize event to pass to base class
 */
void TrackContentWidget::resizeEvent( QResizeEvent * resizeEvent )
{
	// Update backgroud
	updateBackground();
	// Force redraw
	QWidget::resizeEvent( resizeEvent );
}

/*! \brief Return the track shown by the trackContentWidget
 *
 */
Track * TrackContentWidget::getTrack()
{
	return m_trackView->getTrack();
}

/*! \brief Return the end position of the trackContentWidget in Bars.
 *
 * \param posStart the starting position of the Widget (from getPosition())
 */
TimePos TrackContentWidget::endPosition( const TimePos & posStart )
{
	const float ppb = m_trackView->trackContainerView()->pixelsPerBar();
	const int w = width();
	return posStart + static_cast<int>( w * TimePos::ticksPerBar() / ppb );
}

void TrackContentWidget::contextMenuEvent( QContextMenuEvent * cme )
{
	// For hasFormat(), MimeType enum class and getMimeData()
	using namespace Clipboard;

	if( cme->modifiers() )
	{
		return;
	}

	// If we don't have TCO data in the clipboard there's no need to create this menu
	// since "paste" is the only action at the moment.
	if( ! hasFormat( MimeType::StringPair )  )
	{
		return;
	}

	QMenu contextMenu( this );
	QAction *pasteA = contextMenu.addAction( embed::getIconPixmap( "edit_paste" ),
					tr( "Paste" ), [this, cme](){ contextMenuAction( cme, Paste ); } );
	// If we can't paste in the current TCW for some reason, disable the action so the user knows
	pasteA->setEnabled( canPasteSelection( getPosition( cme->x() ), getMimeData() ) ? true : false );

	contextMenu.exec( QCursor::pos() );
}

void TrackContentWidget::contextMenuAction( QContextMenuEvent * cme, ContextMenuAction action )
{
	// For getMimeData()
	using namespace Clipboard;

	switch( action )
	{
		case Paste:
		// Paste the selection on the TimePos of the context menu event
		TimePos tcoPos = getPosition( cme->x() );

		pasteSelection( tcoPos, getMimeData() );
		break;
	}
}

// qproperty access methods
//! \brief CSS theming qproperty access method
QBrush TrackContentWidget::darkerColor() const
{ return m_darkerColor; }

//! \brief CSS theming qproperty access method
QBrush TrackContentWidget::lighterColor() const
{ return m_lighterColor; }

//! \brief CSS theming qproperty access method
QBrush TrackContentWidget::gridColor() const
{ return m_gridColor; }

//! \brief CSS theming qproperty access method
QBrush TrackContentWidget::embossColor() const
{ return m_embossColor; }

//! \brief CSS theming qproperty access method
void TrackContentWidget::setDarkerColor( const QBrush & c )
{ m_darkerColor = c; }

//! \brief CSS theming qproperty access method
void TrackContentWidget::setLighterColor( const QBrush & c )
{ m_lighterColor = c; }

//! \brief CSS theming qproperty access method
void TrackContentWidget::setGridColor( const QBrush & c )
{ m_gridColor = c; }

//! \brief CSS theming qproperty access method
void TrackContentWidget::setEmbossColor( const QBrush & c )
{ m_embossColor = c; }


code:
/*
 * TrackLabelButton.cpp - implementation of class trackLabelButton, a label
 *                          which is renamable by double-clicking it
 *
 * Copyright (c) 2004-2008 Tobias Doerffel <tobydox/at/users.sourceforge.net>
 *
 * This file is part of LMMS - https://lmms.io
 *
 * 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 "TrackLabelButton.h"

#include <QApplication>
#include <QMouseEvent>

#include "ConfigManager.h"
#include "embed.h"
#include "Engine.h"
#include "Instrument.h"
#include "InstrumentTrack.h"
#include "RenameDialog.h"
#include "Song.h"
#include "TrackRenameLineEdit.h"

TrackLabelButton::TrackLabelButton( TrackView * _tv, QWidget * _parent ) :
	QToolButton( _parent ),
	m_trackView( _tv ),
	m_iconName()
{
	setAttribute( Qt::WA_OpaquePaintEvent, true );
	setAcceptDrops( true );
	setCursor( QCursor( embed::getIconPixmap( "hand" ), 3, 3 ) );
	setToolButtonStyle( Qt::ToolButtonTextBesideIcon );
	m_renameLineEdit = new TrackRenameLineEdit( this );
	m_renameLineEdit->hide();

	if( ConfigManager::inst()->value( "ui", "compacttrackbuttons" ).toInt() )
	{
		setFixedSize( 32, 29 );
	}
	else
	{
		setFixedSize( 160, 29 );
		m_renameLineEdit->move( 30, ( height() / 2 ) - ( m_renameLineEdit->sizeHint().height() / 2 ) );
		m_renameLineEdit->setFixedWidth( width() - 33 );
		connect( m_renameLineEdit, SIGNAL( editingFinished() ), this, SLOT( renameFinished() ) );
	}

	setIconSize( QSize( 24, 24 ) );
	connect( m_trackView->getTrack(), SIGNAL( dataChanged() ), this, SLOT( update() ) );
	connect( m_trackView->getTrack(), SIGNAL( nameChanged() ), this, SLOT( nameChanged() ) );
}

TrackLabelButton::~TrackLabelButton()
{
}

void TrackLabelButton::rename()
{
	if( ConfigManager::inst()->value( "ui", "compacttrackbuttons" ).toInt() )
	{
		QString txt = m_trackView->getTrack()->name();
		RenameDialog renameDlg( txt );
		renameDlg.exec();
		if( txt != text() )
		{
			m_trackView->getTrack()->setName( txt );
			Engine::getSong()->setModified();
		}
	}
	else
	{
		QString txt = m_trackView->getTrack()->name();
		m_renameLineEdit->show();
		m_renameLineEdit->setText( txt );
		m_renameLineEdit->selectAll();
		m_renameLineEdit->setFocus();
	}
}

void TrackLabelButton::renameFinished()
{
	if( !( ConfigManager::inst()->value( "ui", "compacttrackbuttons" ).toInt() ) )
	{
		m_renameLineEdit->hide();
		if( m_renameLineEdit->text() != "" )
		{
			if( m_renameLineEdit->text() != m_trackView->getTrack()->name() )
			{
				setText( elideName( m_renameLineEdit->text() ) );
				m_trackView->getTrack()->setName( m_renameLineEdit->text() );
				Engine::getSong()->setModified();
			}
		}
	}
}

void TrackLabelButton::nameChanged()
{
	setText( elideName( m_trackView->getTrack()->name() ) );
}

void TrackLabelButton::dragEnterEvent( QDragEnterEvent * _dee )
{
	m_trackView->dragEnterEvent( _dee );
}

void TrackLabelButton::dropEvent( QDropEvent * _de )
{
	m_trackView->dropEvent( _de );
	setChecked( true );
}

void TrackLabelButton::mousePressEvent( QMouseEvent * _me )
{
	if( _me->button() == Qt::RightButton )
	{
		rename();
	}
	else
	{
		m_buttonRect = QRect( this->mapToGlobal( pos() ), size() );
		_me->ignore();
	}
}

void TrackLabelButton::mouseDoubleClickEvent( QMouseEvent * _me )
{
	rename();
}

void TrackLabelButton::mouseReleaseEvent( QMouseEvent *_me )
{
	if( m_buttonRect.contains( _me->globalPos(), true ) && m_renameLineEdit->isHidden() )
	{
		QToolButton::mousePressEvent( _me );
	}
	QToolButton::mouseReleaseEvent( _me );
	_me->ignore();
}

void TrackLabelButton::paintEvent( QPaintEvent * _pe )
{
	if( m_trackView->getTrack()->type() == Track::InstrumentTrack )
	{
		InstrumentTrack * it =
			dynamic_cast<InstrumentTrack *>( m_trackView->getTrack() );
		const PixmapLoader * pl;
		auto get_logo = [](InstrumentTrack* it) -> const PixmapLoader*
		{
			return it->instrument()->key().isValid()
				? it->instrument()->key().logo()
				: it->instrument()->descriptor()->logo;
		};
		if( it && it->instrument() &&
			it->instrument()->descriptor() &&
			( pl = get_logo(it) ) )
		{
			if( pl->pixmapName() != m_iconName )
			{
				m_iconName = pl->pixmapName();
				setIcon( pl->pixmap() );
			}
		}
	}
	QToolButton::paintEvent( _pe );
}

void TrackLabelButton::resizeEvent(QResizeEvent *_re)
{
	setText( elideName( m_trackView->getTrack()->displayName() ) );
}

QString TrackLabelButton::elideName( const QString &name )
{
	const int spacing = 16;
	const int maxTextWidth = width() - spacing - iconSize().width();
	if( maxTextWidth < 1 )
	{
		setToolTip( m_trackView->getTrack()->displayName() );
		return QString( " " );
	}
	setToolTip( "" );
	QFontMetrics metrics( font() );
	QString elidedName = metrics.elidedText( name, Qt::ElideRight, maxTextWidth );
	return elidedName;
}


code:
/*
 * TrackOperationsWidget.cpp - implementation of TrackOperationsWidget class
 *
 * Copyright (c) 2004-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
 *
 * This file is part of LMMS - https://lmms.io
 *
 * 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 "TrackOperationsWidget.h"

#include <QMenu>
#include <QMouseEvent>
#include <QPainter>
#include <QPushButton>

#include "AutomationPattern.h"
#include "AutomationTrack.h"
#include "ColorChooser.h"
#include "ConfigManager.h"
#include "DataFile.h"
#include "embed.h"
#include "Engine.h"
#include "gui_templates.h"
#include "InstrumentTrack.h"
#include "PixmapButton.h"
#include "Song.h"
#include "StringPairDrag.h"
#include "ToolTip.h"
#include "Track.h"
#include "TrackContainerView.h"
#include "TrackView.h"

/*! \brief Create a new trackOperationsWidget
 *
 * The trackOperationsWidget is the grip and the mute button of a track.
 *
 * \param parent the trackView to contain this widget
 */
TrackOperationsWidget::TrackOperationsWidget( TrackView * parent ) :
	QWidget( parent ),             /*!< The parent widget */
	m_trackView( parent )          /*!< The parent track view */
{
	ToolTip::add( this, tr( "Press <%1> while clicking on move-grip "
				"to begin a new drag'n'drop action." ).arg(UI_CTRL_KEY) );

	QMenu * toMenu = new QMenu( this );
	toMenu->setFont( pointSize<9>( toMenu->font() ) );
	connect( toMenu, SIGNAL( aboutToShow() ), this, SLOT( updateMenu() ) );

	setObjectName( "automationEnabled" );

	m_trackOps = new QPushButton( this );
	m_trackOps->move( 12, 1 );
	m_trackOps->setFocusPolicy( Qt::NoFocus );
	m_trackOps->setMenu( toMenu );
	ToolTip::add( m_trackOps, tr( "Actions" ) );

	m_muteBtn = new PixmapButton( this, tr( "Mute" ) );
	m_muteBtn->setActiveGraphic( embed::getIconPixmap( "led_off" ) );
	m_muteBtn->setInactiveGraphic( embed::getIconPixmap( "led_green" ) );
	m_muteBtn->setCheckable( true );

	m_soloBtn = new PixmapButton( this, tr( "Solo" ) );
	m_soloBtn->setActiveGraphic( embed::getIconPixmap( "led_red" ) );
	m_soloBtn->setInactiveGraphic( embed::getIconPixmap( "led_off" ) );
	m_soloBtn->setCheckable( true );

	if( ConfigManager::inst()->value( "ui",
					  "compacttrackbuttons" ).toInt() )
	{
		m_muteBtn->move( 46, 0 );
		m_soloBtn->move( 46, 16 );
	}
	else
	{
		m_muteBtn->move( 46, 8 );
		m_soloBtn->move( 62, 8 );
	}

	m_muteBtn->show();
	ToolTip::add( m_muteBtn, tr( "Mute" ) );

	m_soloBtn->show();
	ToolTip::add( m_soloBtn, tr( "Solo" ) );

	connect( this, SIGNAL( trackRemovalScheduled( TrackView * ) ),
			m_trackView->trackContainerView(),
				SLOT( deleteTrackView( TrackView * ) ),
							Qt::QueuedConnection );

	connect( m_trackView->getTrack()->getMutedModel(), SIGNAL( dataChanged() ),
			this, SLOT( update() ) );

}

/*! \brief Destroy an existing trackOperationsWidget
 *
 */
TrackOperationsWidget::~TrackOperationsWidget()
{
}

/*! \brief Respond to trackOperationsWidget mouse events
 *
 *  If it's the left mouse button, and Ctrl is held down, and we're
 *  not a Beat+Bassline Editor track, then start a new drag event to
 *  copy this track.
 *
 *  Otherwise, ignore all other events.
 *
 *  \param me The mouse event to respond to.
 */
void TrackOperationsWidget::mousePressEvent( QMouseEvent * me )
{
	if( me->button() == Qt::LeftButton &&
		me->modifiers() & Qt::ControlModifier &&
			m_trackView->getTrack()->type() != Track::BBTrack )
	{
		DataFile dataFile( DataFile::DragNDropData );
		m_trackView->getTrack()->saveState( dataFile, dataFile.content() );
		new StringPairDrag( QString( "track_%1" ).arg(
					m_trackView->getTrack()->type() ),
			dataFile.toString(), m_trackView->getTrackSettingsWidget()->grab(),
									this );
	}
	else if( me->button() == Qt::LeftButton )
	{
		// track-widget (parent-widget) initiates track-move
		me->ignore();
	}
}

/*! \brief Repaint the trackOperationsWidget
 *
 *  If we're not moving, and in the Beat+Bassline Editor, then turn
 *  automation on or off depending on its previous state and show
 *  ourselves.
 *
 *  Otherwise, hide ourselves.
 *
 *  \todo Flesh this out a bit - is it correct?
 *  \param pe The paint event to respond to
 */
void TrackOperationsWidget::paintEvent( QPaintEvent * pe )
{
	QPainter p( this );

	p.fillRect( rect(), palette().brush(QPalette::Background) );

	if( m_trackView->getTrack()->useColor() && ! m_trackView->getTrack()->getMutedModel()->value() )
	{
		QRect coloredRect( 0, 0, 10, m_trackView->getTrack()->getHeight() );

		p.fillRect( coloredRect, m_trackView->getTrack()->color() );
	}

	if( m_trackView->isMovingTrack() == false )
	{
		p.drawPixmap( 2, 2, embed::getIconPixmap("track_op_grip"));
	}
	else
	{
		p.drawPixmap( 2, 2, embed::getIconPixmap("track_op_grip_c"));
	}
}

/*! \brief Clone this track
 *
 */
void TrackOperationsWidget::cloneTrack()
{
	TrackContainerView *tcView = m_trackView->trackContainerView();

	Track *newTrack = m_trackView->getTrack()->clone();
	TrackView *newTrackView = tcView->createTrackView( newTrack );

	int index = tcView->trackViews().indexOf( m_trackView );
	int i = tcView->trackViews().size();
	while ( i != index + 1 )
	{
		tcView->moveTrackView( newTrackView, i - 1 );
		i--;
	}
}

/*! \brief Clear this track - clears all TCOs from the track */
void TrackOperationsWidget::clearTrack()
{
	Track * t = m_trackView->getTrack();
	t->addJournalCheckPoint();
	t->lock();
	t->deleteTCOs();
	t->unlock();
}

/*! \brief Remove this track from the track list
 *
 */
void TrackOperationsWidget::removeTrack()
{
	emit trackRemovalScheduled( m_trackView );
}

void TrackOperationsWidget::changeTrackColor()
{
	QColor new_color = ColorChooser( this ).withPalette( ColorChooser::Palette::Track )-> \
		getColor( m_trackView->getTrack()->color() );

	if( ! new_color.isValid() )
	{ return; }

	emit colorChanged( new_color );

	Engine::getSong()->setModified();
	update();
}

void TrackOperationsWidget::resetTrackColor()
{
	emit colorReset();
	Engine::getSong()->setModified();
	update();
}

void TrackOperationsWidget::randomTrackColor()
{
	QColor buffer = ColorChooser::getPalette( ColorChooser::Palette::Track )[ rand() % 48 ];

	emit colorChanged( buffer );
	Engine::getSong()->setModified();
	update();
}

void TrackOperationsWidget::useTrackColor()
{
	emit colorParented();
	Engine::getSong()->setModified();
}

/*! \brief Update the trackOperationsWidget context menu
 *
 *  For all track types, we have the Clone and Remove options.
 *  For instrument-tracks we also offer the MIDI-control-menu
 *  For automation tracks, extra options: turn on/off recording
 *  on all TCOs (same should be added for sample tracks when
 *  sampletrack recording is implemented)
 */
void TrackOperationsWidget::updateMenu()
{
	QMenu * toMenu = m_trackOps->menu();
	toMenu->clear();
	toMenu->addAction( embed::getIconPixmap( "edit_copy", 16, 16 ),
						tr( "Clone this track" ),
						this, SLOT( cloneTrack() ) );
	toMenu->addAction( embed::getIconPixmap( "cancel", 16, 16 ),
						tr( "Remove this track" ),
						this, SLOT( removeTrack() ) );

	if( ! m_trackView->trackContainerView()->fixedTCOs() )
	{
		toMenu->addAction( tr( "Clear this track" ), this, SLOT( clearTrack() ) );
	}
	if (QMenu *fxMenu = m_trackView->createFxMenu(tr("FX %1: %2"), tr("Assign to new FX Channel")))
	{
		toMenu->addMenu(fxMenu);
	}

	if (InstrumentTrackView * trackView = dynamic_cast<InstrumentTrackView *>(m_trackView))
	{
		toMenu->addSeparator();
		toMenu->addMenu(trackView->midiMenu());
	}
	if( dynamic_cast<AutomationTrackView *>( m_trackView ) )
	{
		toMenu->addAction( tr( "Turn all recording on" ), this, SLOT( recordingOn() ) );
		toMenu->addAction( tr( "Turn all recording off" ), this, SLOT( recordingOff() ) );
	}

	toMenu->addSeparator();
	toMenu->addAction( embed::getIconPixmap( "colorize" ),
						tr( "Change color" ), this, SLOT( changeTrackColor() ) );
	toMenu->addAction( embed::getIconPixmap( "colorize" ),
						tr( "Reset color to default" ), this, SLOT( resetTrackColor() ) );
	toMenu->addAction( embed::getIconPixmap( "colorize" ),
						tr( "Set random color" ), this, SLOT( randomTrackColor() ) );
	toMenu->addSeparator();
	toMenu->addAction( embed::getIconPixmap( "colorize" ),
						tr( "Clear clip colors" ), this, SLOT( useTrackColor() ) );
}

void TrackOperationsWidget::toggleRecording( bool on )
{
	AutomationTrackView * atv = dynamic_cast<AutomationTrackView *>( m_trackView );
	if( atv )
	{
		for( TrackContentObject * tco : atv->getTrack()->getTCOs() )
		{
			AutomationPattern * ap = dynamic_cast<AutomationPattern *>( tco );
			if( ap ) { ap->setRecording( on ); }
		}
		atv->update();
	}
}

void TrackOperationsWidget::recordingOn()
{
	toggleRecording( true );
}

void TrackOperationsWidget::recordingOff()
{
	toggleRecording( false );
}


code:
/*
 * TrackRenameLineEdit.cpp - implementation of class TrackRenameLineEdit, which
 * 							 represents the text field that appears when one
 *							 double-clicks a track's label to rename it
 *
 * Copyright (c) 2004-2008 Tobias Doerffel <tobydox/at/users.sourceforge.net>
 * Copyright (c) 2017 Alexandre Almeida <http://m374lx.users.sourceforge.net/>
 *
 * This file is part of LMMS - https://lmms.io
 *
 * 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 "TrackRenameLineEdit.h"

#include <QKeyEvent>

TrackRenameLineEdit::TrackRenameLineEdit( QWidget * parent ) :
	QLineEdit( parent )
{
}

void TrackRenameLineEdit::show()
{
	m_oldName = text();
	QLineEdit::show();
}

void TrackRenameLineEdit::keyPressEvent( QKeyEvent * ke )
{
	if( ke->key() == Qt::Key_Escape )
	{
		setText( m_oldName );
		hide();
	}

	QLineEdit::keyPressEvent( ke );
}


code:
#cmakedefine LMMS_BUILD_LINUX
#cmakedefine LMMS_BUILD_WIN32
#cmakedefine LMMS_BUILD_WIN64
#cmakedefine LMMS_BUILD_APPLE
#cmakedefine LMMS_BUILD_OPENBSD
#cmakedefine LMMS_BUILD_FREEBSD
#cmakedefine LMMS_BUILD_HAIKU

#cmakedefine LMMS_HOST_X86
#cmakedefine LMMS_HOST_X86_64

#cmakedefine LMMS_HAVE_ALSA
#cmakedefine LMMS_HAVE_FLUIDSYNTH
#cmakedefine LMMS_HAVE_JACK
#cmakedefine LMMS_HAVE_JACK_PRENAME
#cmakedefine LMMS_HAVE_WEAKJACK
#cmakedefine LMMS_HAVE_LV2
#cmakedefine LMMS_HAVE_SUIL
#cmakedefine LMMS_HAVE_MP3LAME
#cmakedefine LMMS_HAVE_OGGVORBIS
#cmakedefine LMMS_HAVE_OSS
#cmakedefine LMMS_HAVE_SNDIO
#cmakedefine LMMS_HAVE_PORTAUDIO
#cmakedefine LMMS_HAVE_SOUNDIO
#cmakedefine LMMS_HAVE_PULSEAUDIO
#cmakedefine LMMS_HAVE_SDL
#cmakedefine LMMS_HAVE_SDL2
#cmakedefine LMMS_HAVE_STK
#cmakedefine LMMS_HAVE_VST
#cmakedefine LMMS_HAVE_SF_COMPLEVEL

#cmakedefine LMMS_DEBUG_FPE

#cmakedefine LMMS_HAVE_STDINT_H
#cmakedefine LMMS_HAVE_STDLIB_H
#cmakedefine LMMS_HAVE_PTHREAD_H
#cmakedefine LMMS_HAVE_UNISTD_H
#cmakedefine LMMS_HAVE_SYS_TYPES_H
#cmakedefine LMMS_HAVE_SYS_IPC_H
#cmakedefine LMMS_HAVE_SEMAPHORE_H
#cmakedefine LMMS_HAVE_SYS_SHM_H
#cmakedefine LMMS_HAVE_SYS_TIME_H
#cmakedefine LMMS_HAVE_SYS_TIMES_H
#cmakedefine LMMS_HAVE_SCHED_H
#cmakedefine LMMS_HAVE_SYS_SOUNDCARD_H
#cmakedefine LMMS_HAVE_SOUNDCARD_H
#cmakedefine LMMS_HAVE_FCNTL_H
#cmakedefine LMMS_HAVE_SYS_IOCTL_H
#cmakedefine LMMS_HAVE_CTYPE_H
#cmakedefine LMMS_HAVE_STRING_H
#cmakedefine LMMS_HAVE_PROCESS_H
#cmakedefine LMMS_HAVE_LOCALE_H

/* defines for libsamplerate */

/* Target processor clips on negative float to int conversion. */
#cmakedefine CPU_CLIPS_NEGATIVE 1
#ifndef CPU_CLIPS_NEGATIVE
#define CPU_CLIPS_NEGATIVE 0
#endif

/* Target processor clips on positive float to int conversion. */
#cmakedefine CPU_CLIPS_POSITIVE 1
#ifndef CPU_CLIPS_POSITIVE
#define CPU_CLIPS_POSITIVE 0
#endif

/* Define if you have C99's lrint function. */
#cmakedefine HAVE_LRINT 1

/* Define if you have C99's lrintf function. */
#cmakedefine HAVE_LRINTF 1


code:
#define LMMS_VERSION "@VERSION@"
#define LMMS_PROJECT_COPYRIGHT "@PROJECT_COPYRIGHT@"


code:
/*
 * AutomationTrack.cpp - AutomationTrack handles automation of objects without
 *                       a track
 *
 * Copyright (c) 2008-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
 * Copyright (c) 2006-2008 Javier Serrano Polo <jasp00/at/users.sourceforge.net>
 *
 * This file is part of LMMS - https://lmms.io
 *
 * 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 "AutomationTrack.h"
#include "AutomationPattern.h"
#include "Engine.h"
#include "embed.h"
#include "ProjectJournal.h"
#include "StringPairDrag.h"
#include "TrackContainerView.h"
#include "TrackLabelButton.h"

AutomationTrack::AutomationTrack( TrackContainer* tc, bool _hidden ) :
	Track( _hidden ? HiddenAutomationTrack : Track::AutomationTrack, tc )
{
	setName( tr( "Automation track" ) );
}

bool AutomationTrack::play( const TimePos & time_start, const fpp_t _frames,
							const f_cnt_t _frame_base, int _tco_num )
{
	return false;
}

TrackView * AutomationTrack::createView( TrackContainerView* tcv )
{
	return new AutomationTrackView( this, tcv );
}

TrackContentObject* AutomationTrack::createTCO(const TimePos & pos)
{
	AutomationPattern* p = new AutomationPattern(this);
	p->movePosition(pos);
	return p;
}

void AutomationTrack::saveTrackSpecificSettings( QDomDocument & _doc,
							QDomElement & _this )
{
}

void AutomationTrack::loadTrackSpecificSettings( const QDomElement & _this )
{
	// just in case something somehow wrent wrong...
	if( type() == HiddenAutomationTrack )
	{
		setMuted( false );
	}
}

AutomationTrackView::AutomationTrackView( AutomationTrack * _at, TrackContainerView* tcv ) :
	TrackView( _at, tcv )
{
        setFixedHeight( 32 );
	TrackLabelButton * tlb = new TrackLabelButton( this,
						getTrackSettingsWidget() );
	tlb->setIcon( embed::getIconPixmap( "automation_track" ) );
	tlb->move( 3, 1 );
	tlb->show();
	setModel( _at );
}

void AutomationTrackView::dragEnterEvent( QDragEnterEvent * _dee )
{
	StringPairDrag::processDragEnterEvent( _dee, "automatable_model" );
}

void AutomationTrackView::dropEvent( QDropEvent * _de )
{
	QString type = StringPairDrag::decodeKey( _de );
	QString val = StringPairDrag::decodeValue( _de );
	if( type == "automatable_model" )
	{
		AutomatableModel * mod = dynamic_cast<AutomatableModel *>(
				Engine::projectJournal()->
					journallingObject( val.toInt() ) );
		if( mod != NULL )
		{
			TimePos pos = TimePos( trackContainerView()->
							currentPosition() +
				( _de->pos().x() -
					getTrackContentWidget()->x() ) *
						TimePos::ticksPerBar() /
		static_cast<int>( trackContainerView()->pixelsPerBar() ) )
				.toAbsoluteBar();

			if( pos.getTicks() < 0 )
			{
				pos.setTicks( 0 );
			}

			TrackContentObject * tco = getTrack()->createTCO( pos );
			AutomationPattern * pat = dynamic_cast<AutomationPattern *>( tco );
			pat->addObject( mod );
		}
	}

	update();
}


code:
/*
 * BBTrack.cpp - implementation of class BBTrack and bbTCO
 *
 * Copyright (c) 2004-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
 *
 * This file is part of LMMS - https://lmms.io
 *
 * 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 "BBTrack.h"

#include <QDomElement>
#include <QMenu>
#include <QPainter>

#include "BBEditor.h"
#include "BBTrackContainer.h"
#include "embed.h"
#include "Engine.h"
#include "gui_templates.h"
#include "GuiApplication.h"
#include "MainWindow.h"
#include "Mixer.h"
#include "RenameDialog.h"
#include "Song.h"
#include "SongEditor.h"
#include "ToolTip.h"
#include "TrackLabelButton.h"

BBTrack::infoMap BBTrack::s_infoMap;

BBTCO::BBTCO( Track * _track ) :
	TrackContentObject( _track )
{
	bar_t t = Engine::getBBTrackContainer()->lengthOfBB( bbTrackIndex() );
	if( t > 0 )
	{
		saveJournallingState( false );
		changeLength( TimePos( t, 0 ) );
		restoreJournallingState();
	}
	setAutoResize( false );
}

void BBTCO::saveSettings( QDomDocument & doc, QDomElement & element )
{
	element.setAttribute( "name", name() );
	if( element.parentNode().nodeName() == "clipboard" )
	{
		element.setAttribute( "pos", -1 );
	}
	else
	{
		element.setAttribute( "pos", startPosition() );
	}
	element.setAttribute( "len", length() );
	element.setAttribute( "muted", isMuted() );
	if( usesCustomClipColor() )
	{
		element.setAttribute( "color", color().name() );
	}
}

void BBTCO::loadSettings( const QDomElement & element )
{
	setName( element.attribute( "name" ) );
	if( element.attribute( "pos" ).toInt() >= 0 )
	{
		movePosition( element.attribute( "pos" ).toInt() );
	}
	changeLength( element.attribute( "len" ).toInt() );
	if( element.attribute( "muted" ).toInt() != isMuted() )
	{
		toggleMute();
	}

	// for colors saved in 1.3-onwards
	if( element.hasAttribute( "color" ) && !element.hasAttribute( "usestyle" ) )
	{
		useCustomClipColor( true );
		setColor( element.attribute( "color" ) );
	}

	// for colors saved before 1.3
	else
	{
		if( element.hasAttribute( "color" ) )
		{ setColor( QColor( element.attribute( "color" ).toUInt() ) ); }

		// usestyle attribute is no longer used
	}
}

int BBTCO::bbTrackIndex()
{
	return dynamic_cast<BBTrack *>( getTrack() )->index();
}

TrackContentObjectView * BBTCO::createView( TrackView * _tv )
{
	return new BBTCOView( this, _tv );
}

BBTCOView::BBTCOView( TrackContentObject * _tco, TrackView * _tv ) :
	TrackContentObjectView( _tco, _tv ),
	m_bbTCO( dynamic_cast<BBTCO *>( _tco ) ),
	m_paintPixmap()
{
	connect( _tco->getTrack(), SIGNAL( dataChanged() ),
			this, SLOT( update() ) );

	setStyle( QApplication::style() );
}

void BBTCOView::constructContextMenu( QMenu * _cm )
{
	QAction * a = new QAction( embed::getIconPixmap( "bb_track" ),
					tr( "Open in Beat+Bassline-Editor" ),
					_cm );
	_cm->insertAction( _cm->actions()[0], a );
	connect( a, SIGNAL( triggered( bool ) ),
			this, SLOT( openInBBEditor() ) );
	_cm->insertSeparator( _cm->actions()[1] );
	_cm->addSeparator();
	_cm->addAction( embed::getIconPixmap( "reload" ), tr( "Reset name" ),
						this, SLOT( resetName() ) );
	_cm->addAction( embed::getIconPixmap( "edit_rename" ),
						tr( "Change name" ),
						this, SLOT( changeName() ) );
}

void BBTCOView::mouseDoubleClickEvent( QMouseEvent * )
{
	openInBBEditor();
}

void BBTCOView::paintEvent( QPaintEvent * )
{
	QPainter painter( this );

	if( !needsUpdate() )
	{
		painter.drawPixmap( 0, 0, m_paintPixmap );
		return;
	}

	setNeedsUpdate( false );

	if (m_paintPixmap.isNull() || m_paintPixmap.size() != size())
	{
		m_paintPixmap = QPixmap(size());
	}

	QPainter p( &m_paintPixmap );

	QLinearGradient lingrad( 0, 0, 0, height() );
	QColor c = getColorForDisplay( painter.background().color() );

	lingrad.setColorAt( 0, c.lighter( 130 ) );
	lingrad.setColorAt( 1, c.lighter( 70 ) );

	// paint a black rectangle under the pattern to prevent glitches with transparent backgrounds
	p.fillRect( rect(), QColor( 0, 0, 0 ) );

	if( gradient() )
	{
		p.fillRect( rect(), lingrad );
	}
	else
	{
		p.fillRect( rect(), c );
	}

	// bar lines
	const int lineSize = 3;
	p.setPen( c.darker( 200 ) );

	bar_t t = Engine::getBBTrackContainer()->lengthOfBB( m_bbTCO->bbTrackIndex() );
	if( m_bbTCO->length() > TimePos::ticksPerBar() && t > 0 )
	{
		for( int x = static_cast<int>( t * pixelsPerBar() );
								x < width() - 2;
			x += static_cast<int>( t * pixelsPerBar() ) )
		{
			p.drawLine( x, TCO_BORDER_WIDTH, x, TCO_BORDER_WIDTH + lineSize );
			p.drawLine( x, rect().bottom() - ( TCO_BORDER_WIDTH + lineSize ),
			 	x, rect().bottom() - TCO_BORDER_WIDTH );
		}
	}

	// pattern name
	paintTextLabel(m_bbTCO->name(), p);

	// inner border
	p.setPen( c.lighter( 130 ) );
	p.drawRect( 1, 1, rect().right() - TCO_BORDER_WIDTH,
		rect().bottom() - TCO_BORDER_WIDTH );

	// outer border
	p.setPen( c.darker( 300 ) );
	p.drawRect( 0, 0, rect().right(), rect().bottom() );

	// draw the 'muted' pixmap only if the pattern was manualy muted
	if( m_bbTCO->isMuted() )
	{
		const int spacing = TCO_BORDER_WIDTH;
		const int size = 14;
		p.drawPixmap( spacing, height() - ( size + spacing ),
			embed::getIconPixmap( "muted", size, size ) );
	}

	p.end();

	painter.drawPixmap( 0, 0, m_paintPixmap );

}

void BBTCOView::openInBBEditor()
{
	Engine::getBBTrackContainer()->setCurrentBB( m_bbTCO->bbTrackIndex() );

	gui->mainWindow()->toggleBBEditorWin( true );
}

void BBTCOView::resetName() { m_bbTCO->setName(""); }

void BBTCOView::changeName()
{
	QString s = m_bbTCO->name();
	RenameDialog rename_dlg( s );
	rename_dlg.exec();
	m_bbTCO->setName( s );
}

void BBTCOView::update()
{
	ToolTip::add(this, m_bbTCO->name());

	TrackContentObjectView::update();
}

BBTrack::BBTrack( TrackContainer* tc ) :
	Track( Track::BBTrack, tc )
{
	int bbNum = s_infoMap.size();
	s_infoMap[this] = bbNum;

	setName( tr( "Beat/Bassline %1" ).arg( bbNum ) );
	Engine::getBBTrackContainer()->createTCOsForBB( bbNum );
	Engine::getBBTrackContainer()->setCurrentBB( bbNum );
	Engine::getBBTrackContainer()->updateComboBox();

	connect( this, SIGNAL( nameChanged() ),
		Engine::getBBTrackContainer(), SLOT( updateComboBox() ) );
}

BBTrack::~BBTrack()
{
	Engine::mixer()->removePlayHandlesOfTypes( this,
					PlayHandle::TypeNotePlayHandle
					| PlayHandle::TypeInstrumentPlayHandle
					| PlayHandle::TypeSamplePlayHandle );

	const int bb = s_infoMap[this];
	Engine::getBBTrackContainer()->removeBB( bb );
	for( infoMap::iterator it = s_infoMap.begin(); it != s_infoMap.end();
									++it )
	{
		if( it.value() > bb )
		{
			--it.value();
		}
	}
	s_infoMap.remove( this );

	// remove us from TC so bbTrackContainer::numOfBBs() returns a smaller
	// value and thus combobox-updating in bbTrackContainer works well
	trackContainer()->removeTrack( this );
	Engine::getBBTrackContainer()->updateComboBox();
}

// play _frames frames of given TCO within starting with _start
bool BBTrack::play( const TimePos & _start, const fpp_t _frames,
					const f_cnt_t _offset, int _tco_num )
{
	if( isMuted() )
	{
		return false;
	}

	if( _tco_num >= 0 )
	{
		return Engine::getBBTrackContainer()->play( _start, _frames, _offset, s_infoMap[this] );
	}

	tcoVector tcos;
	getTCOsInRange( tcos, _start, _start + static_cast<int>( _frames / Engine::framesPerTick() ) );

	if( tcos.size() == 0 )
	{
		return false;
	}

	TimePos lastPosition;
	TimePos lastLen;
	for( tcoVector::iterator it = tcos.begin(); it != tcos.end(); ++it )
	{
		if( !( *it )->isMuted() &&
				( *it )->startPosition() >= lastPosition )
		{
			lastPosition = ( *it )->startPosition();
			lastLen = ( *it )->length();
		}
	}

	if( _start - lastPosition < lastLen )
	{
		return Engine::getBBTrackContainer()->play( _start - lastPosition, _frames, _offset, s_infoMap[this] );
	}
	return false;
}

TrackView * BBTrack::createView( TrackContainerView* tcv )
{
	return new BBTrackView( this, tcv );
}

TrackContentObject* BBTrack::createTCO(const TimePos & pos)
{
	BBTCO* bbtco = new BBTCO(this);
	bbtco->movePosition(pos);
	return bbtco;
}

void BBTrack::saveTrackSpecificSettings( QDomDocument & _doc,
							QDomElement & _this )
{
//	_this.setAttribute( "icon", m_trackLabel->pixmapFile() );
/*	_this.setAttribute( "current", s_infoMap[this] ==
					engine::getBBEditor()->currentBB() );*/
	if( s_infoMap[this] == 0 &&
			_this.parentNode().parentNode().nodeName() != "clone" &&
			_this.parentNode().parentNode().nodeName() != "journaldata" )
	{
		( (JournallingObject *)( Engine::getBBTrackContainer() ) )->
						saveState( _doc, _this );
	}
	if( _this.parentNode().parentNode().nodeName() == "clone" )
	{
		_this.setAttribute( "clonebbt", s_infoMap[this] );
	}
}

void BBTrack::loadTrackSpecificSettings( const QDomElement & _this )
{
/*	if( _this.attribute( "icon" ) != "" )
	{
		m_trackLabel->setPixmapFile( _this.attribute( "icon" ) );
	}*/

	if( _this.hasAttribute( "clonebbt" ) )
	{
		const int src = _this.attribute( "clonebbt" ).toInt();
		const int dst = s_infoMap[this];
		TrackContainer::TrackList tl =
					Engine::getBBTrackContainer()->tracks();
		// copy TCOs of all tracks from source BB (at bar "src") to destination
		// TCOs (which are created if they do not exist yet)
		for( TrackContainer::TrackList::iterator it = tl.begin();
							it != tl.end(); ++it )
		{
			TrackContentObject::copyStateTo( ( *it )->getTCO( src ),
				( *it )->getTCO( dst ) );
		}
		setName( tr( "Clone of %1" ).arg(
					_this.parentNode().toElement().attribute( "name" ) ) );
	}
	else
	{
		QDomNode node = _this.namedItem(
					TrackContainer::classNodeName() );
		if( node.isElement() )
		{
			( (JournallingObject *)Engine::getBBTrackContainer() )->
					restoreState( node.toElement() );
		}
	}
/*	doesn't work yet because BBTrack-ctor also sets current bb so if
	bb-tracks are created after this function is called, this doesn't
	help at all....
	if( _this.attribute( "current" ).toInt() )
	{
		engine::getBBEditor()->setCurrentBB( s_infoMap[this] );
	}*/
}

// return pointer to BBTrack specified by _bb_num
BBTrack * BBTrack::findBBTrack( int _bb_num )
{
	for( infoMap::iterator it = s_infoMap.begin(); it != s_infoMap.end();
									++it )
	{
		if( it.value() == _bb_num )
		{
			return it.key();
		}
	}
	return NULL;
}

void BBTrack::swapBBTracks( Track * _track1, Track * _track2 )
{
	BBTrack * t1 = dynamic_cast<BBTrack *>( _track1 );
	BBTrack * t2 = dynamic_cast<BBTrack *>( _track2 );
	if( t1 != NULL && t2 != NULL )
	{
		qSwap( s_infoMap[t1], s_infoMap[t2] );
		Engine::getBBTrackContainer()->swapBB( s_infoMap[t1],
								s_infoMap[t2] );
		Engine::getBBTrackContainer()->setCurrentBB( s_infoMap[t1] );
	}
}

BBTrackView::BBTrackView( BBTrack * _bbt, TrackContainerView* tcv ) :
	TrackView( _bbt, tcv ),
	m_bbTrack( _bbt )
{
	setFixedHeight( 32 );
	// drag'n'drop with bb-tracks only causes troubles (and makes no sense
	// too), so disable it
	setAcceptDrops( false );

	m_trackLabel = new TrackLabelButton( this, getTrackSettingsWidget() );
	m_trackLabel->setIcon( embed::getIconPixmap( "bb_track" ) );
	m_trackLabel->move( 3, 1 );
	m_trackLabel->show();
	connect( m_trackLabel, SIGNAL( clicked( bool ) ),
			this, SLOT( clickedTrackLabel() ) );
	setModel( _bbt );
}

BBTrackView::~BBTrackView()
{
	gui->getBBEditor()->removeBBView( BBTrack::s_infoMap[m_bbTrack] );
}

bool BBTrackView::close()
{
	gui->getBBEditor()->removeBBView( BBTrack::s_infoMap[m_bbTrack] );
	return TrackView::close();
}

void BBTrackView::clickedTrackLabel()
{
	Engine::getBBTrackContainer()->setCurrentBB( m_bbTrack->index() );
	gui->getBBEditor()->parentWidget()->show();
	gui->getBBEditor()->setFocus( Qt::ActiveWindowFocusReason );
}


code:
set(LMMS_SRCS
	${LMMS_SRCS}
	tracks/AutomationTrack.cpp
	tracks/BBTrack.cpp
	tracks/InstrumentTrack.cpp
	tracks/Pattern.cpp
	tracks/SampleTrack.cpp

	PARENT_SCOPE
)