code:
/*
 * TrackContentObjectView.cpp - implementation of TrackContentObjectView 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 "TrackContentObjectView.h"

#include <set>

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

#include "AutomationPattern.h"
#include "Clipboard.h"
#include "ColorChooser.h"
#include "ComboBoxModel.h"
#include "DataFile.h"
#include "Engine.h"
#include "embed.h"
#include "GuiApplication.h"
#include "InstrumentTrack.h"
#include "Note.h"
#include "Pattern.h"
#include "SampleTrack.h"
#include "Song.h"
#include "SongEditor.h"
#include "StringPairDrag.h"
#include "TextFloat.h"
#include "TrackContainer.h"
#include "TrackContainerView.h"
#include "TrackView.h"

/*! The width of the resize grip in pixels
 */
const int RESIZE_GRIP_WIDTH = 4;

/*! A pointer for that text bubble used when moving segments, etc.
 *
 * In a number of situations, LMMS displays a floating text bubble
 * beside the cursor as you move or resize elements of a track about.
 * This pointer keeps track of it, as you only ever need one at a time.
 */
TextFloat * TrackContentObjectView::s_textFloat = NULL;

/*! \brief Create a new trackContentObjectView
 *
 *  Creates a new track content object view for the given
 *  track content object in the given track view.
 *
 * \param _tco The track content object to be displayed
 * \param _tv  The track view that will contain the new object
 */
TrackContentObjectView::TrackContentObjectView( TrackContentObject * tco,
							TrackView * tv ) :
	selectableObject( tv->getTrackContentWidget() ),
	ModelView( NULL, this ),
	m_tco( tco ),
	m_trackView( tv ),
	m_action( NoAction ),
	m_initialMousePos( QPoint( 0, 0 ) ),
	m_initialMouseGlobalPos( QPoint( 0, 0 ) ),
	m_initialTCOPos( TimePos(0) ),
	m_initialTCOEnd( TimePos(0) ),
	m_initialOffsets( QVector<TimePos>() ),
	m_hint( NULL ),
	m_mutedColor( 0, 0, 0 ),
	m_mutedBackgroundColor( 0, 0, 0 ),
	m_selectedColor( 0, 0, 0 ),
	m_textColor( 0, 0, 0 ),
	m_textShadowColor( 0, 0, 0 ),
	m_BBPatternBackground( 0, 0, 0 ),
	m_gradient( true ),
	m_mouseHotspotHand( 0, 0 ),
	m_cursorSetYet( false ),
	m_needsUpdate( true )
{
	if( s_textFloat == NULL )
	{
		s_textFloat = new TextFloat;
		s_textFloat->setPixmap( embed::getIconPixmap( "clock" ) );
	}

	setAttribute( Qt::WA_OpaquePaintEvent, true );
	setAttribute( Qt::WA_DeleteOnClose, true );
	setFocusPolicy( Qt::StrongFocus );
	setCursor( QCursor( embed::getIconPixmap( "hand" ), m_mouseHotspotHand.width(), m_mouseHotspotHand.height() ) );
	move( 0, 0 );
	show();

	setFixedHeight( tv->getTrackContentWidget()->height() - 1);
	setAcceptDrops( true );
	setMouseTracking( true );

	connect( m_tco, SIGNAL( lengthChanged() ),
			this, SLOT( updateLength() ) );
	connect( gui->songEditor()->m_editor->zoomingModel(), SIGNAL( dataChanged() ), this, SLOT( updateLength() ) );
	connect( m_tco, SIGNAL( positionChanged() ),
			this, SLOT( updatePosition() ) );
	connect( m_tco, SIGNAL( destroyedTCO() ), this, SLOT( close() ) );
	setModel( m_tco );
	connect( m_tco, SIGNAL( trackColorChanged() ), this, SLOT( update() ) );
	connect( m_trackView->getTrackOperationsWidget(), SIGNAL( colorParented() ), this, SLOT( useTrackColor() ) );

	m_trackView->getTrackContentWidget()->addTCOView( this );
	updateLength();
	updatePosition();
}

/*! \brief Destroy a trackContentObjectView
 *
 *  Destroys the given track content object view.
 *
 */
TrackContentObjectView::~TrackContentObjectView()
{
	delete m_hint;
	// we have to give our track-container the focus because otherwise the
	// op-buttons of our track-widgets could become focus and when the user
	// presses space for playing song, just one of these buttons is pressed
	// which results in unwanted effects
	m_trackView->trackContainerView()->setFocus();
}

/*! \brief Update a TrackContentObjectView
 *
 *  TCO's get drawn only when needed,
 *  and when a TCO is updated,
 *  it needs to be redrawn.
 *
 */
void TrackContentObjectView::update()
{
	if( !m_cursorSetYet )
	{
		setCursor( QCursor( embed::getIconPixmap( "hand" ), m_mouseHotspotHand.width(), m_mouseHotspotHand.height() ) );
		m_cursorSetYet = true;
	}

	if( fixedTCOs() )
	{
		updateLength();
	}
	m_needsUpdate = true;
	selectableObject::update();
}

/*! \brief Does this trackContentObjectView have a fixed TCO?
 *
 *  Returns whether the containing trackView has fixed
 *  TCOs.
 *
 * \todo What the hell is a TCO here - track content object?  And in
 *  what circumstance are they fixed?
 */
bool TrackContentObjectView::fixedTCOs()
{
	return m_trackView->trackContainerView()->fixedTCOs();
}

// qproperty access functions, to be inherited & used by TCOviews
//! \brief CSS theming qproperty access method
QColor TrackContentObjectView::mutedColor() const
{ return m_mutedColor; }

QColor TrackContentObjectView::mutedBackgroundColor() const
{ return m_mutedBackgroundColor; }

QColor TrackContentObjectView::selectedColor() const
{ return m_selectedColor; }

QColor TrackContentObjectView::textColor() const
{ return m_textColor; }

QColor TrackContentObjectView::textBackgroundColor() const
{
	return m_textBackgroundColor;
}

QColor TrackContentObjectView::textShadowColor() const
{ return m_textShadowColor; }

QColor TrackContentObjectView::BBPatternBackground() const
{ return m_BBPatternBackground; }

bool TrackContentObjectView::gradient() const
{ return m_gradient; }

//! \brief CSS theming qproperty access method
void TrackContentObjectView::setMutedColor( const QColor & c )
{ m_mutedColor = QColor( c ); }

void TrackContentObjectView::setMutedBackgroundColor( const QColor & c )
{ m_mutedBackgroundColor = QColor( c ); }

void TrackContentObjectView::setSelectedColor( const QColor & c )
{ m_selectedColor = QColor( c ); }

void TrackContentObjectView::setTextColor( const QColor & c )
{ m_textColor = QColor( c ); }

void TrackContentObjectView::setTextBackgroundColor( const QColor & c )
{
	m_textBackgroundColor = c;
}

void TrackContentObjectView::setTextShadowColor( const QColor & c )
{ m_textShadowColor = QColor( c ); }

void TrackContentObjectView::setBBPatternBackground( const QColor & c )
{ m_BBPatternBackground = QColor( c ); }

void TrackContentObjectView::setGradient( const bool & b )
{ m_gradient = b; }

void TrackContentObjectView::setMouseHotspotHand(const QSize & s)
{
	m_mouseHotspotHand = s;
}

// access needsUpdate member variable
bool TrackContentObjectView::needsUpdate()
{ return m_needsUpdate; }
void TrackContentObjectView::setNeedsUpdate( bool b )
{ m_needsUpdate = b; }

/*! \brief Close a trackContentObjectView
 *
 *  Closes a track content object view by asking the track
 *  view to remove us and then asking the QWidget to close us.
 *
 * \return Boolean state of whether the QWidget was able to close.
 */
bool TrackContentObjectView::close()
{
	m_trackView->getTrackContentWidget()->removeTCOView( this );
	return QWidget::close();
}

/*! \brief Removes a trackContentObjectView from its track view.
 *
 *  Like the close() method, this asks the track view to remove this
 *  track content object view.  However, the track content object is
 *  scheduled for later deletion rather than closed immediately.
 *
 */
void TrackContentObjectView::remove()
{
	m_trackView->getTrack()->addJournalCheckPoint();

	// delete ourself
	close();
	m_tco->deleteLater();
}

/*! \brief Updates a trackContentObjectView's length
 *
 *  If this track content object view has a fixed TCO, then we must
 *  keep the width of our parent.  Otherwise, calculate our width from
 *  the track content object's length in pixels adding in the border.
 *
 */
void TrackContentObjectView::updateLength()
{
	if( fixedTCOs() )
	{
		setFixedWidth( parentWidget()->width() );
	}
	else
	{
		setFixedWidth(
		static_cast<int>( m_tco->length() * pixelsPerBar() /
					TimePos::ticksPerBar() ) + 1 /*+
						TCO_BORDER_WIDTH * 2-1*/ );
	}
	m_trackView->trackContainerView()->update();
}

/*! \brief Updates a trackContentObjectView's position.
 *
 *  Ask our track view to change our position.  Then make sure that the
 *  track view is updated in case this position has changed the track
 *  view's length.
 *
 */
void TrackContentObjectView::updatePosition()
{
	m_trackView->getTrackContentWidget()->changePosition();
	// moving a TCO can result in change of song-length etc.,
	// therefore we update the track-container
	m_trackView->trackContainerView()->update();
}

void TrackContentObjectView::changeClipColor()
{
	// Get a color from the user
	QColor new_color = ColorChooser( this ).withPalette( ColorChooser::Palette::Track )->getColor( m_tco->color() );
	if( ! new_color.isValid() )
	{ return; }

	// Use that color
	m_tco->setColor( new_color );
	m_tco->useCustomClipColor( true );
	update();
}

void TrackContentObjectView::useTrackColor()
{
	m_tco->useCustomClipColor( false );
	update();
}

/*! \brief Change the trackContentObjectView's display when something
 *  being dragged enters it.
 *
 *  We need to notify Qt to change our display if something being
 *  dragged has entered our 'airspace'.
 *
 * \param dee The QDragEnterEvent to watch.
 */
void TrackContentObjectView::dragEnterEvent( QDragEnterEvent * dee )
{
	TrackContentWidget * tcw = getTrackView()->getTrackContentWidget();
	TimePos tcoPos = TimePos( m_tco->startPosition() );

	if( tcw->canPasteSelection( tcoPos, dee ) == false )
	{
		dee->ignore();
	}
	else
	{
		StringPairDrag::processDragEnterEvent( dee, "tco_" +
					QString::number( m_tco->getTrack()->type() ) );
	}
}

/*! \brief Handle something being dropped on this trackContentObjectView.
 *
 *  When something has been dropped on this trackContentObjectView, and
 *  it's a track content object, then use an instance of our dataFile reader
 *  to take the xml of the track content object and turn it into something
 *  we can write over our current state.
 *
 * \param de The QDropEvent to handle.
 */
void TrackContentObjectView::dropEvent( QDropEvent * de )
{
	QString type = StringPairDrag::decodeKey( de );
	QString value = StringPairDrag::decodeValue( de );

	// Track must be the same type to paste into
	if( type != ( "tco_" + QString::number( m_tco->getTrack()->type() ) ) )
	{
		return;
	}

	// Defer to rubberband paste if we're in that mode
	if( m_trackView->trackContainerView()->allowRubberband() == true )
	{
		TrackContentWidget * tcw = getTrackView()->getTrackContentWidget();
		TimePos tcoPos = TimePos( m_tco->startPosition() );

		if( tcw->pasteSelection( tcoPos, de ) == true )
		{
			de->accept();
		}
		return;
	}

	// Don't allow pasting a tco into itself.
	QObject* qwSource = de->source();
	if( qwSource != NULL &&
	    dynamic_cast<TrackContentObjectView *>( qwSource ) == this )
	{
		return;
	}

	// Copy state into existing tco
	DataFile dataFile( value.toUtf8() );
	TimePos pos = m_tco->startPosition();
	QDomElement tcos = dataFile.content().firstChildElement( "tcos" );
	m_tco->restoreState( tcos.firstChildElement().firstChildElement() );
	m_tco->movePosition( pos );
	AutomationPattern::resolveAllIDs();
	de->accept();
}

/*! \brief Handle a dragged selection leaving our 'airspace'.
 *
 * \param e The QEvent to watch.
 */
void TrackContentObjectView::leaveEvent( QEvent * e )
{
	if( cursor().shape() != Qt::BitmapCursor )
	{
		setCursor( QCursor( embed::getIconPixmap( "hand" ), m_mouseHotspotHand.width(), m_mouseHotspotHand.height() ) );
	}
	if( e != NULL )
	{
		QWidget::leaveEvent( e );
	}
}

/*! \brief Create a DataFile suitable for copying multiple trackContentObjects.
 *
 *	trackContentObjects in the vector are written to the "tcos" node in the
 *  DataFile.  The trackContentObjectView's initial mouse position is written
 *  to the "initialMouseX" node in the DataFile.  When dropped on a track,
 *  this is used to create copies of the TCOs.
 *
 * \param tcos The trackContectObjects to save in a DataFile
 */
DataFile TrackContentObjectView::createTCODataFiles(
    				const QVector<TrackContentObjectView *> & tcoViews) const
{
	Track * t = m_trackView->getTrack();
	TrackContainer * tc = t->trackContainer();
	DataFile dataFile( DataFile::DragNDropData );
	QDomElement tcoParent = dataFile.createElement( "tcos" );

	typedef QVector<TrackContentObjectView *> tcoViewVector;
	for( tcoViewVector::const_iterator it = tcoViews.begin();
			it != tcoViews.end(); ++it )
	{
		// Insert into the dom under the "tcos" element
		Track* tcoTrack = ( *it )->m_trackView->getTrack();
		int trackIndex = tc->tracks().indexOf( tcoTrack );
		QDomElement tcoElement = dataFile.createElement( "tco" );
		tcoElement.setAttribute( "trackIndex", trackIndex );
		tcoElement.setAttribute( "trackType", tcoTrack->type() );
		tcoElement.setAttribute( "trackName", tcoTrack->name() );
		( *it )->m_tco->saveState( dataFile, tcoElement );
		tcoParent.appendChild( tcoElement );
	}

	dataFile.content().appendChild( tcoParent );

	// Add extra metadata needed for calculations later
	int initialTrackIndex = tc->tracks().indexOf( t );
	if( initialTrackIndex < 0 )
	{
		printf("Failed to find selected track in the TrackContainer.\n");
		return dataFile;
	}
	QDomElement metadata = dataFile.createElement( "copyMetadata" );
	// initialTrackIndex is the index of the track that was touched
	metadata.setAttribute( "initialTrackIndex", initialTrackIndex );
	metadata.setAttribute( "trackContainerId", tc->id() );
	// grabbedTCOPos is the pos of the bar containing the TCO we grabbed
	metadata.setAttribute( "grabbedTCOPos", m_tco->startPosition() );

	dataFile.content().appendChild( metadata );

	return dataFile;
}

void TrackContentObjectView::paintTextLabel(QString const & text, QPainter & painter)
{
	if (text.trimmed() == "")
	{
		return;
	}

	painter.setRenderHint( QPainter::TextAntialiasing );

	QFont labelFont = this->font();
	labelFont.setHintingPreference( QFont::PreferFullHinting );
	painter.setFont( labelFont );

	const int textTop = TCO_BORDER_WIDTH + 1;
	const int textLeft = TCO_BORDER_WIDTH + 3;

	QFontMetrics fontMetrics(labelFont);
	QString elidedPatternName = fontMetrics.elidedText(text, Qt::ElideMiddle, width() - 2 * textLeft);

	if (elidedPatternName.length() < 2)
	{
		elidedPatternName = text.trimmed();
	}

	painter.fillRect(QRect(0, 0, width(), fontMetrics.height() + 2 * textTop), textBackgroundColor());

	int const finalTextTop = textTop + fontMetrics.ascent();
	painter.setPen(textShadowColor());
	painter.drawText( textLeft + 1, finalTextTop + 1, elidedPatternName );
	painter.setPen( textColor() );
	painter.drawText( textLeft, finalTextTop, elidedPatternName );
}

/*! \brief Handle a mouse press on this trackContentObjectView.
 *
 *  Handles the various ways in which a trackContentObjectView can be
 *  used with a click of a mouse button.
 *
 *  * If our container supports rubber band selection then handle
 *    selection events.
 *  * or if shift-left button, add this object to the selection
 *  * or if ctrl-left button, start a drag-copy event
 *  * or if just plain left button, resize if we're resizeable
 *  * or if ctrl-middle button, mute the track content object
 *  * or if middle button, maybe delete the track content object.
 *
 * \param me The QMouseEvent to handle.
 */
void TrackContentObjectView::mousePressEvent( QMouseEvent * me )
{
	// Right now, active is only used on right/mid clicks actions, so we use a ternary operator
	// to avoid the overhead of calling getClickedTCOs when it's not used
	auto active = me->button() == Qt::LeftButton
		? QVector<TrackContentObjectView *>()
		: getClickedTCOs();

	setInitialPos( me->pos() );
	setInitialOffsets();
	if( !fixedTCOs() && me->button() == Qt::LeftButton )
	{
		if( me->modifiers() & Qt::ControlModifier )
		{
			if( isSelected() )
			{
				m_action = CopySelection;
			}
			else
			{
				m_action = ToggleSelected;
			}
		}
		else if( !me->modifiers()
			|| (me->modifiers() & Qt::AltModifier)
			|| (me->modifiers() & Qt::ShiftModifier) )
		{
			if( isSelected() )
			{
				m_action = MoveSelection;
			}
			else
			{
				gui->songEditor()->m_editor->selectAllTcos( false );
				m_tco->addJournalCheckPoint();

				// move or resize
				m_tco->setJournalling( false );

				setInitialPos( me->pos() );
				setInitialOffsets();

				SampleTCO * sTco = dynamic_cast<SampleTCO*>( m_tco );
				if( me->x() < RESIZE_GRIP_WIDTH && sTco
						&& !m_tco->getAutoResize() )
				{
					m_action = ResizeLeft;
					setCursor( Qt::SizeHorCursor );
				}
				else if( m_tco->getAutoResize() || me->x() < width() - RESIZE_GRIP_WIDTH )
				{
					m_action = Move;
					setCursor( Qt::SizeAllCursor );
				}
				else
				{
					m_action = Resize;
					setCursor( Qt::SizeHorCursor );
				}

				if( m_action == Move )
				{
					s_textFloat->setTitle( tr( "Current position" ) );
					s_textFloat->setText( QString( "%1:%2" ).
						arg( m_tco->startPosition().getBar() + 1 ).
						arg( m_tco->startPosition().getTicks() %
								TimePos::ticksPerBar() ) );
				}
				else if( m_action == Resize || m_action == ResizeLeft )
				{
					s_textFloat->setTitle( tr( "Current length" ) );
					s_textFloat->setText( tr( "%1:%2 (%3:%4 to %5:%6)" ).
							arg( m_tco->length().getBar() ).
							arg( m_tco->length().getTicks() %
									TimePos::ticksPerBar() ).
							arg( m_tco->startPosition().getBar() + 1 ).
							arg( m_tco->startPosition().getTicks() %
									TimePos::ticksPerBar() ).
							arg( m_tco->endPosition().getBar() + 1 ).
							arg( m_tco->endPosition().getTicks() %
									TimePos::ticksPerBar() ) );
				}
				// s_textFloat->reparent( this );
				// setup text-float as if TCO was already moved/resized
				s_textFloat->moveGlobal( this, QPoint( width() + 2, height() + 2) );
				s_textFloat->show();
			}

			delete m_hint;
			QString hint = m_action == Move || m_action == MoveSelection
						? tr( "Press <%1> and drag to make a copy." )
						: tr( "Press <%1> for free resizing." );
			m_hint = TextFloat::displayMessage( tr( "Hint" ), hint.arg(UI_CTRL_KEY),
					embed::getIconPixmap( "hint" ), 0 );
		}
	}
	else if( me->button() == Qt::RightButton )
	{
		if( me->modifiers() & Qt::ControlModifier )
		{
			toggleMute( active );
		}
		else if( me->modifiers() & Qt::ShiftModifier && !fixedTCOs() )
		{
			remove( active );
		}
	}
	else if( me->button() == Qt::MidButton )
	{
		if( me->modifiers() & Qt::ControlModifier )
		{
			toggleMute( active );
		}
		else if( !fixedTCOs() )
		{
			remove( active );
		}
	}
}

/*! \brief Handle a mouse movement (drag) on this trackContentObjectView.
 *
 *  Handles the various ways in which a trackContentObjectView can be
 *  used with a mouse drag.
 *
 *  * If in move mode, move ourselves in the track,
 *  * or if in move-selection mode, move the entire selection,
 *  * or if in resize mode, resize ourselves,
 *  * otherwise ???
 *
 * \param me The QMouseEvent to handle.
 * \todo what does the final else case do here?
 */
void TrackContentObjectView::mouseMoveEvent( QMouseEvent * me )
{
	if( m_action == CopySelection || m_action == ToggleSelected )
	{
		if( mouseMovedDistance( me, 2 ) == true )
		{
			QVector<TrackContentObjectView *> tcoViews;
			if( m_action == CopySelection )
			{
				// Collect all selected TCOs
				QVector<selectableObject *> so =
					m_trackView->trackContainerView()->selectedObjects();
				for( auto it = so.begin(); it != so.end(); ++it )
				{
					TrackContentObjectView * tcov =
						dynamic_cast<TrackContentObjectView *>( *it );
					if( tcov != NULL )
					{
						tcoViews.push_back( tcov );
					}
				}
			}
			else
			{
				gui->songEditor()->m_editor->selectAllTcos( false );
				tcoViews.push_back( this );
			}
			// Clear the action here because mouseReleaseEvent will not get
			// triggered once we go into drag.
			m_action = NoAction;

			// Write the TCOs to the DataFile for copying
			DataFile dataFile = createTCODataFiles( tcoViews );

			// TODO -- thumbnail for all selected
			QPixmap thumbnail = grab().scaled(
				128, 128,
				Qt::KeepAspectRatio,
				Qt::SmoothTransformation );
			new StringPairDrag( QString( "tco_%1" ).arg(
								m_tco->getTrack()->type() ),
								dataFile.toString(), thumbnail, this );
		}
	}

	if( me->modifiers() & Qt::ControlModifier )
	{
		delete m_hint;
		m_hint = NULL;
	}

	const float ppb = m_trackView->trackContainerView()->pixelsPerBar();
	if( m_action == Move )
	{
		TimePos newPos = draggedTCOPos( me );

		m_tco->movePosition(newPos);
		newPos = m_tco->startPosition(); // Get the real position the TCO was dragged to for the label
		m_trackView->getTrackContentWidget()->changePosition();
		s_textFloat->setText( QString( "%1:%2" ).
				arg( newPos.getBar() + 1 ).
				arg( newPos.getTicks() %
						TimePos::ticksPerBar() ) );
		s_textFloat->moveGlobal( this, QPoint( width() + 2, height() + 2 ) );
	}
	else if( m_action == MoveSelection )
	{
		// 1: Find the position we want to move the grabbed TCO to
		TimePos newPos = draggedTCOPos( me );

		// 2: Handle moving the other selected TCOs the same distance
		QVector<selectableObject *> so =
			m_trackView->trackContainerView()->selectedObjects();
		QVector<TrackContentObject *> tcos; // List of selected clips
		int leftmost = 0; // Leftmost clip's offset from grabbed clip
		// Populate tcos, find leftmost
		for( QVector<selectableObject *>::iterator it = so.begin();
							it != so.end(); ++it )
		{
			TrackContentObjectView * tcov =
				dynamic_cast<TrackContentObjectView *>( *it );
			if( tcov == NULL ) { continue; }
			tcos.push_back( tcov->m_tco );
			int index = std::distance( so.begin(), it );
			leftmost = std::min(leftmost, m_initialOffsets[index].getTicks());
		}
		// Make sure the leftmost clip doesn't get moved to a negative position
		if ( newPos.getTicks() + leftmost < 0 ) { newPos = -leftmost; }

		for( QVector<TrackContentObject *>::iterator it = tcos.begin();
							it != tcos.end(); ++it )
		{
			int index = std::distance( tcos.begin(), it );
			( *it )->movePosition( newPos + m_initialOffsets[index] );
		}
	}
	else if( m_action == Resize || m_action == ResizeLeft )
	{
		// If the user is holding alt, or pressed ctrl after beginning the drag, don't quantize
		const bool unquantized = (me->modifiers() & Qt::ControlModifier) || (me->modifiers() & Qt::AltModifier);
		const float snapSize = gui->songEditor()->m_editor->getSnapSize();
		// Length in ticks of one snap increment
		const TimePos snapLength = TimePos( (int)(snapSize * TimePos::ticksPerBar()) );

		if( m_action == Resize )
		{
			// The clip's new length
			TimePos l = static_cast<int>( me->x() * TimePos::ticksPerBar() / ppb );

			if ( unquantized )
			{	// We want to preserve this adjusted offset,
				// even if the user switches to snapping later
				setInitialPos( m_initialMousePos );
				// Don't resize to less than 1 tick
				m_tco->changeLength( qMax<int>( 1, l ) );
			}
			else if ( me->modifiers() & Qt::ShiftModifier )
			{	// If shift is held, quantize clip's end position
				TimePos end = TimePos( m_initialTCOPos + l ).quantize( snapSize );
				// The end position has to be after the clip's start
				TimePos min = m_initialTCOPos.quantize( snapSize );
				if ( min <= m_initialTCOPos ) min += snapLength;
				m_tco->changeLength( qMax<int>(min - m_initialTCOPos, end - m_initialTCOPos) );
			}
			else
			{	// Otherwise, resize in fixed increments
				TimePos initialLength = m_initialTCOEnd - m_initialTCOPos;
				TimePos offset = TimePos( l - initialLength ).quantize( snapSize );
				// Don't resize to less than 1 tick
				TimePos min = TimePos( initialLength % snapLength );
				if (min < 1) min += snapLength;
				m_tco->changeLength( qMax<int>( min, initialLength + offset) );
			}
		}
		else
		{
			SampleTCO * sTco = dynamic_cast<SampleTCO*>( m_tco );
			if( sTco )
			{
				const int x = mapToParent( me->pos() ).x() - m_initialMousePos.x();

				TimePos t = qMax( 0, (int)
									m_trackView->trackContainerView()->currentPosition() +
									static_cast<int>( x * TimePos::ticksPerBar() / ppb ) );

				if( unquantized )
				{	// We want to preserve this adjusted offset,
					// even if the user switches to snapping later
					setInitialPos( m_initialMousePos );
					//Don't resize to less than 1 tick
					t = qMin<int>( m_initialTCOEnd - 1, t);
				}
				else if( me->modifiers() & Qt::ShiftModifier )
				{	// If shift is held, quantize clip's start position
					// Don't let the start position move past the end position
					TimePos max = m_initialTCOEnd.quantize( snapSize );
					if ( max >= m_initialTCOEnd ) max -= snapLength;
					t = qMin<int>( max, t.quantize( snapSize ) );
				}
				else
				{	// Otherwise, resize in fixed increments
					// Don't resize to less than 1 tick
					TimePos initialLength = m_initialTCOEnd - m_initialTCOPos;
					TimePos minLength = TimePos( initialLength % snapLength );
					if (minLength < 1) minLength += snapLength;
					TimePos offset = TimePos(t - m_initialTCOPos).quantize( snapSize );
					t = qMin<int>( m_initialTCOEnd - minLength, m_initialTCOPos + offset );
				}

				TimePos oldPos = m_tco->startPosition();
				if( m_tco->length() + ( oldPos - t ) >= 1 )
				{
					m_tco->movePosition( t );
					m_tco->changeLength( m_tco->length() + ( oldPos - t ) );
					sTco->setStartTimeOffset( sTco->startTimeOffset() + ( oldPos - t ) );
				}
			}
		}
		s_textFloat->setText( tr( "%1:%2 (%3:%4 to %5:%6)" ).
				arg( m_tco->length().getBar() ).
				arg( m_tco->length().getTicks() %
						TimePos::ticksPerBar() ).
				arg( m_tco->startPosition().getBar() + 1 ).
				arg( m_tco->startPosition().getTicks() %
						TimePos::ticksPerBar() ).
				arg( m_tco->endPosition().getBar() + 1 ).
				arg( m_tco->endPosition().getTicks() %
						TimePos::ticksPerBar() ) );
		s_textFloat->moveGlobal( this, QPoint( width() + 2, height() + 2) );
	}
	else
	{
		SampleTCO * sTco = dynamic_cast<SampleTCO*>( m_tco );
		if( ( me->x() > width() - RESIZE_GRIP_WIDTH && !me->buttons() && !m_tco->getAutoResize() )
		||  ( me->x() < RESIZE_GRIP_WIDTH && !me->buttons() && sTco && !m_tco->getAutoResize() ) )
		{
			setCursor( Qt::SizeHorCursor );
		}
		else
		{
			leaveEvent( NULL );
		}
	}
}

/*! \brief Handle a mouse release on this trackContentObjectView.
 *
 *  If we're in move or resize mode, journal the change as appropriate.
 *  Then tidy up.
 *
 * \param me The QMouseEvent to handle.
 */
void TrackContentObjectView::mouseReleaseEvent( QMouseEvent * me )
{
	// If the CopySelection was chosen as the action due to mouse movement,
	// it will have been cleared.  At this point Toggle is the desired action.
	// An active StringPairDrag will prevent this method from being called,
	// so a real CopySelection would not have occurred.
	if( m_action == CopySelection ||
	    ( m_action == ToggleSelected && mouseMovedDistance( me, 2 ) == false ) )
	{
		setSelected( !isSelected() );
	}

	if( m_action == Move || m_action == Resize || m_action == ResizeLeft )
	{
		// TODO: Fix m_tco->setJournalling() consistency
		m_tco->setJournalling( true );
	}
	m_action = NoAction;
	delete m_hint;
	m_hint = NULL;
	s_textFloat->hide();
	leaveEvent( NULL );
	selectableObject::mouseReleaseEvent( me );
}

/*! \brief Set up the context menu for this trackContentObjectView.
 *
 *  Set up the various context menu events that can apply to a
 *  track content object view.
 *
 * \param cme The QContextMenuEvent to add the actions to.
 */
void TrackContentObjectView::contextMenuEvent( QContextMenuEvent * cme )
{
	QVector<TrackContentObjectView*> selectedTCOs = getClickedTCOs();

	// Depending on whether we right-clicked a selection or an individual TCO we will have
	// different labels for the actions.
	bool individualTCO = selectedTCOs.size() <= 1;

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

	QMenu contextMenu( this );

	if( fixedTCOs() == false )
	{
		contextMenu.addAction(
			embed::getIconPixmap( "cancel" ),
			individualTCO
				? tr("Delete (middle mousebutton)")
				: tr("Delete selection (middle mousebutton)"),
			[this](){ contextMenuAction( Remove ); } );

		contextMenu.addSeparator();

		contextMenu.addAction(
			embed::getIconPixmap( "edit_cut" ),
			individualTCO
				? tr("Cut")
				: tr("Cut selection"),
			[this](){ contextMenuAction( Cut ); } );

		if (canMergeSelection(selectedTCOs))
		{
			contextMenu.addAction(
				embed::getIconPixmap("edit_merge"),
				tr("Merge Selection"),
				[this]() { contextMenuAction(Merge); }
			);
		}
	}

	contextMenu.addAction(
		embed::getIconPixmap( "edit_copy" ),
		individualTCO
			? tr("Copy")
			: tr("Copy selection"),
		[this](){ contextMenuAction( Copy ); } );

	contextMenu.addAction(
		embed::getIconPixmap( "edit_paste" ),
		tr( "Paste" ),
		[this](){ contextMenuAction( Paste ); } );

	contextMenu.addSeparator();

	contextMenu.addAction(
		embed::getIconPixmap( "muted" ),
		(individualTCO
			? tr("Mute/unmute (<%1> + middle click)")
			: tr("Mute/unmute selection (<%1> + middle click)")).arg(UI_CTRL_KEY),
		[this](){ contextMenuAction( Mute ); } );

	contextMenu.addSeparator();

	contextMenu.addAction( embed::getIconPixmap( "colorize" ),
			tr( "Set clip color" ), this, SLOT( changeClipColor() ) );
	contextMenu.addAction( embed::getIconPixmap( "colorize" ),
			tr( "Use track color" ), this, SLOT( useTrackColor() ) );

	constructContextMenu( &contextMenu );

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

// This method processes the actions from the context menu of the TCO View.
void TrackContentObjectView::contextMenuAction( ContextMenuAction action )
{
	QVector<TrackContentObjectView *> active = getClickedTCOs();
	// active will be later used for the remove, copy, cut or toggleMute methods

	switch( action )
	{
		case Remove:
			remove( active );
			break;
		case Cut:
			cut( active );
			break;
		case Copy:
			copy( active );
			break;
		case Paste:
			paste();
			break;
		case Mute:
			toggleMute( active );
			break;
		case Merge:
			mergeTCOs(active);
			break;
	}
}

QVector<TrackContentObjectView *> TrackContentObjectView::getClickedTCOs()
{
	// Get a list of selected selectableObjects
	QVector<selectableObject *> sos = gui->songEditor()->m_editor->selectedObjects();

	// Convert to a list of selected TCOVs
	QVector<TrackContentObjectView *> selection;
	selection.reserve( sos.size() );
	for( auto so: sos )
	{
		TrackContentObjectView *tcov = dynamic_cast<TrackContentObjectView *> ( so );
		if( tcov != nullptr )
		{
			selection.append( tcov );
		}
	}

	// If we clicked part of the selection, affect all selected clips. Otherwise affect the clip we clicked
	return selection.contains(this)
		? selection
		: QVector<TrackContentObjectView *>( 1, this );
}

void TrackContentObjectView::remove( QVector<TrackContentObjectView *> tcovs )
{
	for( auto tcov: tcovs )
	{
		// No need to check if it's nullptr because we check when building the QVector
		tcov->remove();
	}
}

void TrackContentObjectView::copy( QVector<TrackContentObjectView *> tcovs )
{
	// For copyStringPair()
	using namespace Clipboard;

	// Write the TCOs to a DataFile for copying
	DataFile dataFile = createTCODataFiles( tcovs );

	// Copy the TCO type as a key and the TCO data file to the clipboard
	copyStringPair( QString( "tco_%1" ).arg( m_tco->getTrack()->type() ),
		dataFile.toString() );
}

void TrackContentObjectView::cut( QVector<TrackContentObjectView *> tcovs )
{
	// Copy the selected TCOs
	copy( tcovs );

	// Now that the TCOs are copied we can delete them, since we are cutting
	remove( tcovs );
}

void TrackContentObjectView::paste()
{
	// For getMimeData()
	using namespace Clipboard;

	// If possible, paste the selection on the TimePos of the selected Track and remove it
	TimePos tcoPos = TimePos( m_tco->startPosition() );

	TrackContentWidget *tcw = getTrackView()->getTrackContentWidget();

	if( tcw->pasteSelection( tcoPos, getMimeData() ) )
	{
		// If we succeed on the paste we delete the TCO we pasted on
		remove();
	}
}

void TrackContentObjectView::toggleMute( QVector<TrackContentObjectView *> tcovs )
{
	for( auto tcov: tcovs )
	{
		// No need to check for nullptr because we check while building the tcovs QVector
		tcov->getTrackContentObject()->toggleMute();
	}
}

bool TrackContentObjectView::canMergeSelection(QVector<TrackContentObjectView*> tcovs)
{
	// Can't merge a single TCO
	if (tcovs.size() < 2) { return false; }

	// We check if the owner of the first TCO is an Instrument Track
	bool isInstrumentTrack = dynamic_cast<InstrumentTrackView*>(tcovs.at(0)->getTrackView());

	// Then we create a set with all the TCOs owners
	std::set<TrackView*> ownerTracks;
	for (auto tcov: tcovs) { ownerTracks.insert(tcov->getTrackView()); }

	// Can merge if there's only one owner track and it's an Instrument Track
	return isInstrumentTrack && ownerTracks.size() == 1;
}

void TrackContentObjectView::mergeTCOs(QVector<TrackContentObjectView*> tcovs)
{
	// Get the track that we are merging TCOs in
	InstrumentTrack* track =
		dynamic_cast<InstrumentTrack*>(tcovs.at(0)->getTrackView()->getTrack());

	if (!track)
	{
		qWarning("Warning: Couldn't retrieve InstrumentTrack in mergeTCOs()");
		return;
	}

	// For Undo/Redo
	track->addJournalCheckPoint();
	track->saveJournallingState(false);

	// Find the earliest position of all the selected TCOVs
	const auto earliestTCOV = std::min_element(tcovs.constBegin(), tcovs.constEnd(),
		[](TrackContentObjectView* a, TrackContentObjectView* b)
		{
			return a->getTrackContentObject()->startPosition() <
				b->getTrackContentObject()->startPosition();
		}
	);

	const TimePos earliestPos = (*earliestTCOV)->getTrackContentObject()->startPosition();

	// Create a pattern where all notes will be added
	Pattern* newPattern = dynamic_cast<Pattern*>(track->createTCO(earliestPos));
	if (!newPattern)
	{
		qWarning("Warning: Failed to convert TCO to Pattern on mergeTCOs");
		return;
	}

	newPattern->saveJournallingState(false);

	// Add the notes and remove the TCOs that are being merged
	for (auto tcov: tcovs)
	{
		// Convert TCOV to PatternView
		PatternView* pView = dynamic_cast<PatternView*>(tcov);

		if (!pView)
		{
			qWarning("Warning: Non-pattern TCO on InstrumentTrack");
			continue;
		}

		NoteVector currentTCONotes = pView->getPattern()->notes();
		TimePos pViewPos = pView->getPattern()->startPosition();

		for (Note* note: currentTCONotes)
		{
			Note* newNote = newPattern->addNote(*note, false);
			TimePos originalNotePos = newNote->pos();
			newNote->setPos(originalNotePos + (pViewPos - earliestPos));
		}

		// We disable the journalling system before removing, so the
		// removal doesn't get added to the undo/redo history
		tcov->getTrackContentObject()->saveJournallingState(false);
		// No need to check for nullptr because we check while building the tcovs QVector
		tcov->remove();
	}

	// Update length since we might have moved notes beyond the end of the pattern length
	newPattern->updateLength();
	// Rearrange notes because we might have moved them
	newPattern->rearrangeAllNotes();
	// Restore journalling states now that the operation is finished
	newPattern->restoreJournallingState();
	track->restoreJournallingState();
	// Update song
	Engine::getSong()->setModified();
	gui->songEditor()->update();
}

/*! \brief How many pixels a bar takes for this trackContentObjectView.
 *
 * \return the number of pixels per bar.
 */
float TrackContentObjectView::pixelsPerBar()
{
	return m_trackView->trackContainerView()->pixelsPerBar();
}

/*! \brief Save the offsets between all selected tracks and a clicked track */
void TrackContentObjectView::setInitialOffsets()
{
	QVector<selectableObject *> so = m_trackView->trackContainerView()->selectedObjects();
	QVector<TimePos> offsets;
	for( QVector<selectableObject *>::iterator it = so.begin();
						it != so.end(); ++it )
	{
		TrackContentObjectView * tcov =
			dynamic_cast<TrackContentObjectView *>( *it );
		if( tcov == NULL )
		{
			continue;
		}
		offsets.push_back( tcov->m_tco->startPosition() - m_initialTCOPos );
	}

	m_initialOffsets = offsets;
}

/*! \brief Detect whether the mouse moved more than n pixels on screen.
 *
 * \param _me The QMouseEvent.
 * \param distance The threshold distance that the mouse has moved to return true.
 */
bool TrackContentObjectView::mouseMovedDistance( QMouseEvent * me, int distance )
{
	QPoint dPos = mapToGlobal( me->pos() ) - m_initialMouseGlobalPos;
	const int pixelsMoved = dPos.manhattanLength();
	return ( pixelsMoved > distance || pixelsMoved < -distance );
}

/*! \brief Calculate the new position of a dragged TCO from a mouse event
 *
 *
 * \param me The QMouseEvent
 */
TimePos TrackContentObjectView::draggedTCOPos( QMouseEvent * me )
{
	//Pixels per bar
	const float ppb = m_trackView->trackContainerView()->pixelsPerBar();
	// The pixel distance that the mouse has moved
	const int mouseOff = mapToGlobal(me->pos()).x() - m_initialMouseGlobalPos.x();
	TimePos newPos = m_initialTCOPos + mouseOff * TimePos::ticksPerBar() / ppb;
	TimePos offset = newPos - m_initialTCOPos;
	// If the user is holding alt, or pressed ctrl after beginning the drag, don't quantize
	if (    me->button() != Qt::NoButton
		|| (me->modifiers() & Qt::ControlModifier)
		|| (me->modifiers() & Qt::AltModifier)    )
	{
		// We want to preserve this adjusted offset,
		// even if the user switches to snapping
		setInitialPos( m_initialMousePos );
	}
	else if ( me->modifiers() & Qt::ShiftModifier )
	{	// If shift is held, quantize position (Default in 1.2.0 and earlier)
		// or end position, whichever is closest to the actual position
		TimePos startQ = newPos.quantize( gui->songEditor()->m_editor->getSnapSize() );
		// Find start position that gives snapped clip end position
		TimePos endQ = ( newPos + m_tco->length() );
		endQ = endQ.quantize( gui->songEditor()->m_editor->getSnapSize() );
		endQ = endQ - m_tco->length();
		// Select the position closest to actual position
		if ( abs(newPos - startQ) < abs(newPos - endQ) ) newPos = startQ;
		else newPos = endQ;
	}
	else
	{	// Otherwise, quantize moved distance (preserves user offsets)
		newPos = m_initialTCOPos + offset.quantize( gui->songEditor()->m_editor->getSnapSize() );
	}
	return newPos;
}

// Return the color that the TCO's background should be
QColor TrackContentObjectView::getColorForDisplay( QColor defaultColor )
{
	// Get the pure TCO color
	auto tcoColor = m_tco->hasColor()
					? m_tco->usesCustomClipColor()
						? m_tco->color()
						: m_tco->getTrack()->color()
					: defaultColor;

	// Set variables
	QColor c, mutedCustomColor;
	bool muted = m_tco->getTrack()->isMuted() || m_tco->isMuted();
	mutedCustomColor = tcoColor;
	mutedCustomColor.setHsv( mutedCustomColor.hsvHue(), mutedCustomColor.hsvSaturation() / 4, mutedCustomColor.value() );

	// Change the pure color by state: selected, muted, colored, normal
	if( isSelected() )
	{
		c = m_tco->hasColor()
			? ( muted
				? mutedCustomColor.darker( 350 )
				: tcoColor.darker( 150 ) )
			: selectedColor();
	}
	else
	{
		if( muted )
		{
			c = m_tco->hasColor()
				? mutedCustomColor.darker( 250 )
				: mutedBackgroundColor();
		}
		else
		{
			c = tcoColor;
		}
	}

	// Return color to caller
	return c;
}


code:
/*
 * TrackView.cpp - implementation of TrackView 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 "TrackView.h"

#include <QApplication>
#include <QHBoxLayout>
#include <QMenu>
#include <QMouseEvent>
#include <QPainter>
#include <QStyleOption>
#include <QtGlobal>

#include "ConfigManager.h"
#include "DataFile.h"
#include "Engine.h"
#include "FadeButton.h"
#include "Mixer.h"
#include "PixmapButton.h"
#include "StringPairDrag.h"
#include "ToolTip.h"
#include "Track.h"
#include "TrackContainerView.h"
#include "TrackContentObjectView.h"

/*! \brief Create a new track View.
 *
 *  The track View is handles the actual display of the track, including
 *  displaying its various widgets and the track segments.
 *
 *  \param track The track to display.
 *  \param tcv The track Container View for us to be displayed in.
 *  \todo Is my description of these properties correct?
 */
TrackView::TrackView( Track * track, TrackContainerView * tcv ) :
	QWidget( tcv->contentWidget() ),   /*!< The Track Container View's content widget. */
	ModelView( NULL, this ),            /*!< The model view of this track */
	m_track( track ),                  /*!< The track we're displaying */
	m_trackContainerView( tcv ),       /*!< The track Container View we're displayed in */
	m_trackOperationsWidget( this ),    /*!< Our trackOperationsWidget */
	m_trackSettingsWidget( this ),      /*!< Our trackSettingsWidget */
	m_trackContentWidget( this ),       /*!< Our trackContentWidget */
	m_action( NoAction )                /*!< The action we're currently performing */
{
	setAutoFillBackground( true );
	QPalette pal;
	pal.setColor( backgroundRole(), QColor( 32, 36, 40 ) );
	setPalette( pal );

	m_trackSettingsWidget.setAutoFillBackground( true );

	QHBoxLayout * layout = new QHBoxLayout( this );
	layout->setMargin( 0 );
	layout->setSpacing( 0 );
	layout->addWidget( &m_trackOperationsWidget );
	layout->addWidget( &m_trackSettingsWidget );
	layout->addWidget( &m_trackContentWidget, 1 );
	setFixedHeight( m_track->getHeight() );

	resizeEvent( NULL );

	setAcceptDrops( true );
	setAttribute( Qt::WA_DeleteOnClose, true );

	connect( m_track, SIGNAL( destroyedTrack() ), this, SLOT( close() ) );
	connect( m_track,
		SIGNAL( trackContentObjectAdded( TrackContentObject * ) ),
			this, SLOT( createTCOView( TrackContentObject * ) ),
			Qt::QueuedConnection );

	connect( &m_track->m_mutedModel, SIGNAL( dataChanged() ),
			&m_trackContentWidget, SLOT( update() ) );

	connect(&m_track->m_mutedModel, SIGNAL(dataChanged()),
			this, SLOT(muteChanged()));

	connect( &m_track->m_soloModel, SIGNAL( dataChanged() ),
			m_track, SLOT( toggleSolo() ), Qt::DirectConnection );

	connect( &m_trackOperationsWidget, SIGNAL( colorChanged( QColor & ) ),
			m_track, SLOT( trackColorChanged( QColor & ) ) );

	connect( &m_trackOperationsWidget, SIGNAL( colorReset() ),
			m_track, SLOT( trackColorReset() ) );

	// create views for already existing TCOs
	for( Track::tcoVector::iterator it =
					m_track->m_trackContentObjects.begin();
			it != m_track->m_trackContentObjects.end(); ++it )
	{
		createTCOView( *it );
	}

	m_trackContainerView->addTrackView( this );
}

/*! \brief Destroy this track View.
 *
 */
TrackView::~TrackView()
{
}

/*! \brief Resize this track View.
 *
 *  \param re the Resize Event to handle.
 */
void TrackView::resizeEvent( QResizeEvent * re )
{
	if( ConfigManager::inst()->value( "ui",
					  "compacttrackbuttons" ).toInt() )
	{
		m_trackOperationsWidget.setFixedSize( TRACK_OP_WIDTH_COMPACT, height() - 1 );
		m_trackSettingsWidget.setFixedSize( DEFAULT_SETTINGS_WIDGET_WIDTH_COMPACT, height() - 1 );
	}
	else
	{
		m_trackOperationsWidget.setFixedSize( TRACK_OP_WIDTH, height() - 1 );
		m_trackSettingsWidget.setFixedSize( DEFAULT_SETTINGS_WIDGET_WIDTH, height() - 1 );
	}
	m_trackContentWidget.setFixedHeight( height() );
}

/*! \brief Update this track View and all its content objects.
 *
 */
void TrackView::update()
{
	m_trackContentWidget.update();
	if( !m_trackContainerView->fixedTCOs() )
	{
		m_trackContentWidget.changePosition();
	}
	QWidget::update();
}

/*! \brief Create a menu for assigning/creating channels for this track.
 *
 */
QMenu * TrackView::createFxMenu(QString title, QString newFxLabel)
{
	Q_UNUSED(title)
	Q_UNUSED(newFxLabel)
	return NULL;
}

/*! \brief Close this track View.
 *
 */
bool TrackView::close()
{
	m_trackContainerView->removeTrackView( this );
	return QWidget::close();
}

/*! \brief Register that the model of this track View has changed.
 *
 */
void TrackView::modelChanged()
{
	m_track = castModel<Track>();
	Q_ASSERT( m_track != NULL );
	connect( m_track, SIGNAL( destroyedTrack() ), this, SLOT( close() ) );
	m_trackOperationsWidget.m_muteBtn->setModel( &m_track->m_mutedModel );
	m_trackOperationsWidget.m_soloBtn->setModel( &m_track->m_soloModel );
	ModelView::modelChanged();
	setFixedHeight( m_track->getHeight() );
}

/*! \brief Start a drag event on this track View.
 *
 *  \param dee the DragEnterEvent to start.
 */
void TrackView::dragEnterEvent( QDragEnterEvent * dee )
{
	StringPairDrag::processDragEnterEvent( dee, "track_" +
					QString::number( m_track->type() ) );
}

/*! \brief Accept a drop event on this track View.
 *
 *  We only accept drop events that are of the same type as this track.
 *  If so, we decode the data from the drop event by just feeding it
 *  back into the engine as a state.
 *
 *  \param de the DropEvent to handle.
 */
void TrackView::dropEvent( QDropEvent * de )
{
	QString type = StringPairDrag::decodeKey( de );
	QString value = StringPairDrag::decodeValue( de );
	if( type == ( "track_" + QString::number( m_track->type() ) ) )
	{
		// value contains our XML-data so simply create a
		// DataFile which does the rest for us...
		DataFile dataFile( value.toUtf8() );
		Engine::mixer()->requestChangeInModel();
		m_track->restoreState( dataFile.content().firstChild().toElement() );
		Engine::mixer()->doneChangeInModel();
		de->accept();
	}
}

/*! \brief Handle a mouse press event on this track View.
 *
 *  If this track container supports rubber band selection, let the
 *  widget handle that and don't bother with any other handling.
 *
 *  If the left mouse button is pressed, we handle two things.  If
 *  SHIFT is pressed, then we resize vertically.  Otherwise we start
 *  the process of moving this track to a new position.
 *
 *  Otherwise we let the widget handle the mouse event as normal.
 *
 *  \param me the MouseEvent to handle.
 */
void TrackView::mousePressEvent( QMouseEvent * me )
{

	// If previously dragged too small, restore on shift-leftclick
	if( height() < DEFAULT_TRACK_HEIGHT &&
		me->modifiers() & Qt::ShiftModifier &&
		me->button() == Qt::LeftButton )
	{
		setFixedHeight( DEFAULT_TRACK_HEIGHT );
		m_track->setHeight( DEFAULT_TRACK_HEIGHT );
	}

	int widgetTotal = ConfigManager::inst()->value( "ui",
							"compacttrackbuttons" ).toInt()==1 ?
		DEFAULT_SETTINGS_WIDGET_WIDTH_COMPACT + TRACK_OP_WIDTH_COMPACT :
		DEFAULT_SETTINGS_WIDGET_WIDTH + TRACK_OP_WIDTH;
	if( m_trackContainerView->allowRubberband() == true  && me->x() > widgetTotal )
	{
		QWidget::mousePressEvent( me );
	}
	else if( me->button() == Qt::LeftButton )
	{
		if( me->modifiers() & Qt::ShiftModifier )
		{
			m_action = ResizeTrack;
			QCursor::setPos( mapToGlobal( QPoint( me->x(),
								height() ) ) );
			QCursor c( Qt::SizeVerCursor);
			QApplication::setOverrideCursor( c );
		}
		else
		{
			if( me->x()>10 ) // 10 = The width of the grip + 2 pixels to the left and right.
			{
				QWidget::mousePressEvent( me );
				return;
			}

			m_action = MoveTrack;

			QCursor c( Qt::SizeVerCursor );
			QApplication::setOverrideCursor( c );
			// update because in move-mode, all elements in
			// track-op-widgets are hidden as a visual feedback
			m_trackOperationsWidget.update();
		}

		me->accept();
	}
	else
	{
		QWidget::mousePressEvent( me );
	}
}

/*! \brief Handle a mouse move event on this track View.
 *
 *  If this track container supports rubber band selection, let the
 *  widget handle that and don't bother with any other handling.
 *
 *  Otherwise if we've started the move process (from mousePressEvent())
 *  then move ourselves into that position, reordering the track list
 *  with moveTrackViewUp() and moveTrackViewDown() to suit.  We make a
 *  note of this in the undo journal in case the user wants to undo this
 *  move.
 *
 *  Likewise if we've started a resize process, handle this too, making
 *  sure that we never go below the minimum track height.
 *
 *  \param me the MouseEvent to handle.
 */
void TrackView::mouseMoveEvent( QMouseEvent * me )
{
	int widgetTotal = ConfigManager::inst()->value( "ui",
							"compacttrackbuttons" ).toInt()==1 ?
		DEFAULT_SETTINGS_WIDGET_WIDTH_COMPACT + TRACK_OP_WIDTH_COMPACT :
		DEFAULT_SETTINGS_WIDGET_WIDTH + TRACK_OP_WIDTH;
	if( m_trackContainerView->allowRubberband() == true && me->x() > widgetTotal )
	{
		QWidget::mouseMoveEvent( me );
	}
	else if( m_action == MoveTrack )
	{
		// look which track-widget the mouse-cursor is over
		const int yPos =
			m_trackContainerView->contentWidget()->mapFromGlobal( me->globalPos() ).y();
		const TrackView * trackAtY = m_trackContainerView->trackViewAt( yPos );

		// debug code
		//	qDebug( "y position %d", yPos );

		// a track-widget not equal to ourself?
		if( trackAtY != NULL && trackAtY != this )
		{
			// then move us up/down there!
			if( me->y() < 0 )
			{
				m_trackContainerView->moveTrackViewUp( this );
			}
			else
			{
				m_trackContainerView->moveTrackViewDown( this );
			}
		}
	}
	else if( m_action == ResizeTrack )
	{
		setFixedHeight( qMax<int>( me->y(), MINIMAL_TRACK_HEIGHT ) );
		m_trackContainerView->realignTracks();
		m_track->setHeight( height() );
	}

	if( height() < DEFAULT_TRACK_HEIGHT )
	{
		ToolTip::add( this, m_track->m_name );
	}
}

/*! \brief Handle a mouse release event on this track View.
 *
 *  \param me the MouseEvent to handle.
 */
void TrackView::mouseReleaseEvent( QMouseEvent * me )
{
	m_action = NoAction;
	while( QApplication::overrideCursor() != NULL )
	{
		QApplication::restoreOverrideCursor();
	}
	m_trackOperationsWidget.update();

	QWidget::mouseReleaseEvent( me );
}

/*! \brief Repaint this track View.
 *
 *  \param pe the PaintEvent to start.
 */
void TrackView::paintEvent( QPaintEvent * pe )
{
	QStyleOption opt;
	opt.initFrom( this );
	QPainter p( this );
	style()->drawPrimitive( QStyle::PE_Widget, &opt, &p, this );
}

/*! \brief Create a TrackContentObject View in this track View.
 *
 *  \param tco the TrackContentObject to create the view for.
 *  \todo is this a good description for what this method does?
 */
void TrackView::createTCOView( TrackContentObject * tco )
{
	TrackContentObjectView * tv = tco->createView( this );
	if( tco->getSelectViewOnCreate() == true )
	{
		tv->setSelected( true );
	}
	tco->selectViewOnCreate( false );
}

void TrackView::muteChanged()
{
	FadeButton * indicator = getActivityIndicator();
	if (indicator) { setIndicatorMute(indicator, m_track->m_mutedModel.value()); }
}

void TrackView::setIndicatorMute(FadeButton* indicator, bool muted)
{
	QPalette::ColorRole role = muted ? QPalette::Highlight : QPalette::BrightText;
	indicator->setActiveColor(QApplication::palette().color(QPalette::Active, role));
}

repo_name: CYBERDEViL/LMMS

license: mit


code:
/* ColorChooser.cpp - definition of ColorChooser class.
 *
 * Copyright (c) 2020 russiankumar <adityakumar4644/at/gmail/dot/com>
 *
 * 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 <ColorChooser.h>

//! Set global palette via array, checking bounds
void ColorChooser::setPalette (QVector<QColor> colors)
{
	const int max = qMin (colors.size(), 48);
	for (int i = 0; i < max; i++)
	{
		ColorChooser::setStandardColor (i, colors[i]);
	}
}

//! Set global paletter via enum
void ColorChooser::setPalette (Palette palette)
{
	setPalette (getPalette (palette));
}

//! Set palette via enum, return self pointer for chaining
ColorChooser* ColorChooser::withPalette (Palette palette)
{
	setPalette (palette);
	return this;
}

//! Return a certain palette
QVector<QColor> ColorChooser::getPalette (Palette palette)
{
	switch (palette)
	{
		case Palette::Mixer: return nicePalette(140);
		case Palette::Track: return nicePalette(150);
		default: return defaultPalette();
	}
}

//! Copy the current QColorDialog palette into an array
QVector<QColor> ColorChooser::defaultPalette()
{
	QVector <QColor> result (48);
	for (int i = 0; i < 48; i++)
	{
		result[i] = (QColorDialog::standardColor(i));
	}
	return result;
}

//! Generate a nice palette, with adjustable value
QVector<QColor> ColorChooser::nicePalette (int base)
{
	QVector <QColor> result (48);
	for (int x = 0; x < 8; x++)
	{
		for (int y = 0; y < 6; y++)
		{
			result[6 * x + y].setHsl (qMax(0, 44 * x - 1), 150 - 20 * y, base - 10 * y);
		}
	}
	return result;
}


code:
/*
 * FileDialog.cpp - implementation of class FileDialog
 *
 * Copyright (c) 2014 Lukas W <lukaswhl/at/gmail.com>
 *
 * 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 <QList>
#include <QUrl>
#include <QDesktopServices>
#include <QListView>

#include "ConfigManager.h"
#include "FileDialog.h"

FileDialog::FileDialog( QWidget *parent, const QString &caption,
					   const QString &directory, const QString &filter ) :
	QFileDialog( parent, caption, directory, filter )
{
#if QT_VERSION > 0x050200
	setOption( QFileDialog::DontUseCustomDirectoryIcons );
#endif

	setOption( QFileDialog::DontUseNativeDialog );

	// Add additional locations to the sidebar
	QList<QUrl> urls = sidebarUrls();
	urls << QUrl::fromLocalFile( QStandardPaths::writableLocation( QStandardPaths::DesktopLocation ) );
	// Find downloads directory
	QDir downloadDir( QDir::homePath() + "/Downloads" );
	if ( ! downloadDir.exists() )
		downloadDir.setPath(QStandardPaths::writableLocation( QStandardPaths::DownloadLocation ));
	if ( downloadDir.exists() )
		urls << QUrl::fromLocalFile( downloadDir.absolutePath() );

	urls << QUrl::fromLocalFile( QStandardPaths::writableLocation( QStandardPaths::MusicLocation ) );
	urls << QUrl::fromLocalFile( ConfigManager::inst()->workingDir() );

	// Add `/Volumes` directory on OS X systems, this allows the user to browse
	// external disk drives.
#ifdef LMMS_BUILD_APPLE
	QDir volumesDir( QDir("/Volumes") );
	if ( volumesDir.exists() )
		urls << QUrl::fromLocalFile( volumesDir.absolutePath() );
#endif

	setSidebarUrls(urls);
}

QString FileDialog::getExistingDirectory(QWidget *parent,
										const QString &caption,
										const QString &directory,
										QFileDialog::Options options)
{
	FileDialog dialog(parent, caption, directory, QString());
	dialog.setFileMode(QFileDialog::Directory);
	dialog.setOptions(dialog.options() | options);
	if (dialog.exec() == QDialog::Accepted) {
		return dialog.selectedFiles().value(0);
	}
	return QString();
}

QString FileDialog::getOpenFileName(QWidget *parent,
									const QString &caption,
									const QString &directory,
									const QString &filter,
									QString *selectedFilter)
{
	FileDialog dialog(parent, caption, directory, filter);
	if (selectedFilter && !selectedFilter->isEmpty())
		dialog.selectNameFilter(*selectedFilter);
	if (dialog.exec() == QDialog::Accepted) {
		if (selectedFilter)
			*selectedFilter = dialog.selectedNameFilter();
		return dialog.selectedFiles().value(0);
	}
	return QString();
}

void FileDialog::clearSelection()
{
    QListView *view = findChild<QListView*>();
	Q_ASSERT( view );
	view->clearSelection();
}


code:
/*
 * VersionedSaveDialog.cpp - implementation of class VersionedSaveDialog
 *
 * Copyright (c) 2014 Lukas W <lukaswhl/at/gmail.com>
 *
 * 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 <QFontMetrics>
#include <QLayout>
#include <QLineEdit>
#include <QMessageBox>
#include <QPushButton>
#include <QGroupBox>
#include <QLabel>

#include "DeprecationHelper.h"
#include "VersionedSaveDialog.h"
#include "LedCheckbox.h"

VersionedSaveDialog::VersionedSaveDialog( QWidget *parent,
										  QWidget *saveOptionsWidget,
										  const QString &caption,
										  const QString &directory,
										  const QString &filter ) :
	FileDialog(parent, caption, directory, filter)
{
	setAcceptMode( QFileDialog::AcceptSave );
	setFileMode( QFileDialog::AnyFile );

	// Create + and - buttons
	QPushButton *plusButton( new QPushButton( "+", this) );
	plusButton->setToolTip( tr( "Increment version number" ) );
	QPushButton *minusButton( new QPushButton( "-", this ) );
	minusButton->setToolTip( tr( "Decrement version number" ) );
	plusButton->setFixedWidth(horizontalAdvance(plusButton->fontMetrics(), "+") + 30);
	minusButton->setFixedWidth(horizontalAdvance(minusButton->fontMetrics(), "+") + 30);

	// Add buttons to grid layout. For doing this, remove the lineEdit and
	// replace it with a HBox containing lineEdit and the buttons.
	QGridLayout *layout = dynamic_cast<QGridLayout*>( this->layout() );
	QWidget *lineEdit = findChild<QLineEdit*>();
	layout->removeWidget( lineEdit );

	QHBoxLayout* hLayout( new QHBoxLayout() );
	hLayout->addWidget( lineEdit );
	hLayout->addWidget( plusButton );
	hLayout->addWidget( minusButton );
	layout->addLayout( hLayout, 2, 1 );

	if (saveOptionsWidget) {
		auto groupBox = new QGroupBox(tr("Save Options"));
		auto optionsLayout = new QGridLayout;

		optionsLayout->addWidget(saveOptionsWidget, 0, 0, Qt::AlignLeft);

		groupBox->setLayout(optionsLayout);

		layout->addWidget(groupBox, layout->rowCount() + 1, 0, 1, -1);
	}

	// Connect + and - buttons
	connect( plusButton, SIGNAL( clicked() ), this, SLOT( incrementVersion() ));
	connect( minusButton, SIGNAL( clicked() ), this, SLOT( decrementVersion() ));
}

bool VersionedSaveDialog::changeFileNameVersion(QString &fileName, bool increment )
{
	static QRegExp regexp( "[- ]\\d+(\\.\\w+)?$" );

	int idx = regexp.indexIn( fileName );
	// For file names without extension (no ".mmpz")
	int insertIndex = fileName.lastIndexOf( '.' );
	if ( insertIndex < idx+1 )
		insertIndex = fileName.size();

	if ( idx == -1 )
	{
		// Can't decrement if there is no version number
		if ( increment == false )
			return false;
		else
			fileName.insert( insertIndex, "-01" );
	}
	else
	{
		// Find current version number
		QString number = fileName.mid( idx+1, insertIndex - idx - 1 );
		bool ok;
		ushort version = number.toUShort( &ok );
		Q_ASSERT( ok );

		// Can't decrement 0
		if ( !increment && version == 0 )
			return false;
		// Replace version number
		version = increment ? version + 1 : version - 1;
		QString newnumber = QString( "%1" ).arg( version, 2, 10, QChar( '0' ) );

		fileName.replace( idx+1, number.length(), newnumber );
	}
	return true;
}

void VersionedSaveDialog::incrementVersion()
{
	const QStringList& selected = selectedFiles();
	if ( selected.size() != 1 )
		return;
	QString file = selected[0];
	changeFileNameVersion( file, true );
	clearSelection();
	selectFile( file );
}

void VersionedSaveDialog::decrementVersion()
{
	const QStringList& selected = selectedFiles();
	if ( selected.size() != 1 )
		return;
	QString file = selected[0];
	changeFileNameVersion( file, false );
	clearSelection();
	selectFile( file );
}

bool VersionedSaveDialog::fileExistsQuery( QString FileName, QString WindowTitle )
{
	bool fileExists = false;
	if( QFile( FileName ).exists() )
	{
		QMessageBox mb;
		mb.setWindowTitle( WindowTitle );
		mb.setText( FileName + tr( " already exists. "
			"Do you want to replace it?" ) );
		mb.setIcon( QMessageBox::Warning );
		mb.setStandardButtons(
			QMessageBox::Yes | QMessageBox::No );

		if( mb.exec() == QMessageBox::Yes )
		{
			fileExists = true;
		}
	}
	return fileExists;
}

SaveOptionsWidget::SaveOptionsWidget(Song::SaveOptions &saveOptions) {
	auto *layout = new QVBoxLayout();

	m_discardMIDIConnectionsCheckbox = new LedCheckBox(nullptr);
	m_discardMIDIConnectionsCheckbox->setText(tr("Discard MIDI connections"));
	m_discardMIDIConnectionsCheckbox->setModel(&saveOptions.discardMIDIConnections);
	layout->addWidget(m_discardMIDIConnectionsCheckbox);

	setLayout(layout);
}


code:
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>AboutDialog</class>
 <widget class="QDialog" name="AboutDialog">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>504</width>
    <height>286</height>
   </rect>
  </property>
  <property name="minimumSize">
   <size>
    <width>504</width>
    <height>286</height>
   </size>
  </property>
  <property name="windowTitle">
   <string>About LMMS</string>
  </property>
  <layout class="QVBoxLayout" name="verticalLayout_3">
   <item>
    <layout class="QHBoxLayout" name="horizontalLayout">
     <item>
      <widget class="QLabel" name="iconLabel">
       <property name="minimumSize">
        <size>
         <width>64</width>
         <height>64</height>
        </size>
       </property>
      </widget>
     </item>
     <item>
      <layout class="QVBoxLayout" name="verticalLayout_2">
       <item>
        <widget class="QLabel" name="label">
         <property name="styleSheet">
          <string notr="true">font:12pt; font-weight:bold;</string>
         </property>
         <property name="text">
          <string>LMMS</string>
         </property>
        </widget>
       </item>
       <item>
        <widget class="QLabel" name="versionLabel">
         <property name="text">
          <string>Version %1 (%2/%3, Qt %4, %5).</string>
         </property>
        </widget>
       </item>
      </layout>
     </item>
     <item>
      <spacer name="horizontalSpacer">
       <property name="orientation">
        <enum>Qt::Horizontal</enum>
       </property>
       <property name="sizeHint" stdset="0">
        <size>
         <width>0</width>
         <height>0</height>
        </size>
       </property>
      </spacer>
     </item>
    </layout>
   </item>
   <item>
    <widget class="QTabWidget" name="tabWidget">
     <property name="currentIndex">
      <number>0</number>
     </property>
     <widget class="QWidget" name="tab">
      <attribute name="title">
       <string>About</string>
      </attribute>
      <layout class="QVBoxLayout" name="verticalLayout">
       <item>
        <spacer name="verticalSpacer_3">
         <property name="orientation">
          <enum>Qt::Vertical</enum>
         </property>
         <property name="sizeType">
          <enum>QSizePolicy::Fixed</enum>
         </property>
         <property name="sizeHint" stdset="0">
          <size>
           <width>0</width>
           <height>0</height>
          </size>
         </property>
        </spacer>
       </item>
       <item>
        <widget class="QLabel" name="label_2">
         <property name="text">
          <string>LMMS - easy music production for everyone.</string>
         </property>
         <property name="wordWrap">
          <bool>true</bool>
         </property>
        </widget>
       </item>
       <item>
        <spacer name="verticalSpacer_2">
         <property name="orientation">
          <enum>Qt::Vertical</enum>
         </property>
         <property name="sizeType">
          <enum>QSizePolicy::Fixed</enum>
         </property>
         <property name="sizeHint" stdset="0">
          <size>
           <width>0</width>
           <height>0</height>
          </size>
         </property>
        </spacer>
       </item>
       <item>
        <widget class="QLabel" name="copyrightLabel">
         <property name="text">
          <string>Copyright © %1.</string>
         </property>
         <property name="wordWrap">
          <bool>true</bool>
         </property>
        </widget>
       </item>
       <item>
        <spacer name="verticalSpacer_4">
         <property name="orientation">
          <enum>Qt::Vertical</enum>
         </property>
         <property name="sizeType">
          <enum>QSizePolicy::Fixed</enum>
         </property>
         <property name="sizeHint" stdset="0">
          <size>
           <width>0</width>
           <height>0</height>
          </size>
         </property>
        </spacer>
       </item>
       <item>
        <widget class="QLabel" name="label_4">
         <property name="text">
          <string>
https://lmms.io
</string>
         </property>
         <property name="wordWrap">
          <bool>true</bool>
         </property>
        </widget>
       </item>
       <item>
        <spacer name="verticalSpacer">
         <property name="orientation">
          <enum>Qt::Vertical</enum>
         </property>
         <property name="sizeHint" stdset="0">
          <size>
           <width>0</width>
           <height>0</height>
          </size>
         </property>
        </spacer>
       </item>
      </layout>
     </widget>
     <widget class="QWidget" name="tab_2">
      <attribute name="title">
       <string>Authors</string>
      </attribute>
      <layout class="QVBoxLayout" name="verticalLayout_4">
       <item>
        <widget class="QTextEdit" name="authorLabel">
         <property name="readOnly">
          <bool>true</bool>
         </property>
        </widget>
       </item>
      </layout>
     </widget>
     <widget class="QWidget" name="tab_3">
      <attribute name="title">
       <string>Involved</string>
      </attribute>
      <layout class="QVBoxLayout" name="verticalLayout_7">
       <item>
        <widget class="QLabel" name="label_5">
         <property name="text">
          <string>Contributors ordered by number of commits:</string>
         </property>
        </widget>
       </item>
       <item>
        <widget class="QTextEdit" name="involvedLabel">
         <property name="readOnly">
          <bool>true</bool>
         </property>
        </widget>
       </item>
      </layout>
     </widget>
     <widget class="QWidget" name="Seite">
      <attribute name="title">
       <string>Translation</string>
      </attribute>
      <layout class="QVBoxLayout" name="verticalLayout_5">
       <item>
        <widget class="QTextEdit" name="translationLabel">
         <property name="readOnly">
          <bool>true</bool>
         </property>
         <property name="plainText">
          <string>Current language not translated (or native English).
If you're interested in translating LMMS in another language or want to improve existing translations, you're welcome to help us! Simply contact the maintainer!</string>
         </property>
        </widget>
       </item>
      </layout>
     </widget>
     <widget class="QWidget" name="Seite_2">
      <attribute name="title">
       <string>License</string>
      </attribute>
      <layout class="QVBoxLayout" name="verticalLayout_6">
       <item>
        <widget class="QTextEdit" name="licenseLabel">
         <property name="readOnly">
          <bool>true</bool>
         </property>
        </widget>
       </item>
      </layout>
     </widget>
    </widget>
   </item>
   <item>
    <widget class="QDialogButtonBox" name="buttonBox">
     <property name="orientation">
      <enum>Qt::Horizontal</enum>
     </property>
     <property name="standardButtons">
      <set>QDialogButtonBox::Close</set>
     </property>
    </widget>
   </item>
  </layout>
 </widget>
 <resources/>
 <connections>
  <connection>
   <sender>buttonBox</sender>
   <signal>accepted()</signal>
   <receiver>AboutDialog</receiver>
   <slot>accept()</slot>
  </connection>
  <connection>
   <sender>buttonBox</sender>
   <signal>rejected()</signal>
   <receiver>AboutDialog</receiver>
   <slot>reject()</slot>
  </connection>
 </connections>
</ui>


code:
<?xml version="1.0" encoding="UTF-8"?>
<ui version="4.0">
 <class>ExportProjectDialog</class>
 <widget class="QDialog" name="ExportProjectDialog">
  <property name="geometry">
   <rect>
    <x>0</x>
    <y>0</y>
    <width>379</width>
    <height>400</height>
   </rect>
  </property>
  <property name="minimumSize">
   <size>
    <width>379</width>
    <height>400</height>
   </size>
  </property>
  <property name="maximumSize">
   <size>
    <width>379</width>
    <height>400</height>
   </size>
  </property>
  <property name="windowTitle">
   <string>Export project</string>
  </property>
  <layout class="QVBoxLayout">
   <item>
    <widget class="QCheckBox" name="exportLoopCB">
     <property name="text">
      <string>Export as loop (remove extra bar)</string>
     </property>
    </widget>
   </item>
   <item>
    <widget class="QCheckBox" name="renderMarkersCB">
     <property name="text">
      <string>Export between loop markers</string>
     </property>
    </widget>
   </item>
   <item>
    <widget class="QWidget" name="loopRepeatWidget" native="true">
     <layout class="QHBoxLayout" name="loopRepeatHL">
      <property name="leftMargin">
       <number>0</number>
      </property>
      <property name="topMargin">
       <number>0</number>
      </property>
      <property name="rightMargin">
       <number>0</number>
      </property>
      <property name="bottomMargin">
       <number>0</number>
      </property>
      <item>
       <widget class="QLabel" name="labelLoopRepeat">
        <property name="text">
         <string>Render Looped Section:</string>
        </property>
       </widget>
      </item>
      <item>
       <widget class="QSpinBox" name="loopCountSB">
        <property name="suffix">
         <string> time(s)</string>
        </property>
        <property name="minimum">
         <number>1</number>
        </property>
        <property name="maximum">
         <number>99</number>
        </property>
        <property name="value">
         <number>1</number>
        </property>
       </widget>
      </item>
     </layout>
    </widget>
   </item>
   <item>
    <layout class="QHBoxLayout">
     <item>
      <widget class="QGroupBox" name="outputGroupBox">
       <property name="title">
        <string>File format settings</string>
       </property>
       <layout class="QVBoxLayout">
        <item>
         <widget class="QLabel" name="label">
          <property name="text">
           <string>File format:</string>
          </property>
         </widget>
        </item>
        <item>
         <widget class="QComboBox" name="fileFormatCB"/>
        </item>
        <item>
         <widget class="QWidget" name="sampleRateWidget" native="true">
          <layout class="QVBoxLayout" name="verticalLayout">
           <property name="leftMargin">
            <number>0</number>
           </property>
           <property name="topMargin">
            <number>0</number>
           </property>
           <property name="rightMargin">
            <number>0</number>
           </property>
           <property name="bottomMargin">
            <number>0</number>
           </property>
           <item>
            <widget class="QLabel" name="labelSampleRate">
             <property name="text">
              <string>Sampling rate:</string>
             </property>
            </widget>
           </item>
           <item>
            <widget class="QComboBox" name="samplerateCB">
             <item>
              <property name="text">
               <string>44100 Hz</string>
              </property>
             </item>
             <item>
              <property name="text">
               <string>48000 Hz</string>
              </property>
             </item>
             <item>
              <property name="text">
               <string>88200 Hz</string>
              </property>
             </item>
             <item>
              <property name="text">
               <string>96000 Hz</string>
              </property>
             </item>
             <item>
              <property name="text">
               <string>192000 Hz</string>
              </property>
             </item>
            </widget>
           </item>
          </layout>
         </widget>
        </item>
        <item>
         <widget class="QWidget" name="depthWidget" native="true">
          <layout class="QVBoxLayout">
           <property name="leftMargin">
            <number>0</number>
           </property>
           <property name="topMargin">
            <number>0</number>
           </property>
           <property name="rightMargin">
            <number>0</number>
           </property>
           <property name="bottomMargin">
            <number>0</number>
           </property>
           <item>
            <widget class="QLabel" name="labelBitDepth">
             <property name="text">
              <string>Bit depth:</string>
             </property>
            </widget>
           </item>
           <item>
            <widget class="QComboBox" name="depthCB">
             <property name="currentIndex">
              <number>1</number>
             </property>
             <item>
              <property name="text">
               <string>16 Bit integer</string>
              </property>
             </item>
             <item>
              <property name="text">
               <string>24 Bit integer</string>
              </property>
             </item>
             <item>
              <property name="text">
               <string>32 Bit float</string>
              </property>
             </item>
            </widget>
           </item>
          </layout>
         </widget>
        </item>
        <item>
         <widget class="QWidget" name="stereoModeWidget" native="true">
          <layout class="QVBoxLayout" name="verticalLayout_4">
           <property name="leftMargin">
            <number>0</number>
           </property>
           <property name="topMargin">
            <number>0</number>
           </property>
           <property name="rightMargin">
            <number>0</number>
           </property>
           <property name="bottomMargin">
            <number>0</number>
           </property>
           <item>
            <widget class="QLabel" name="labelStereoMode_3">
             <property name="text">
              <string>Stereo mode:</string>
             </property>
            </widget>
           </item>
           <item>
            <widget class="QComboBox" name="stereoModeComboBox">
             <property name="currentIndex">
              <number>1</number>
             </property>
             <item>
              <property name="text">
               <string>Mono</string>
              </property>
             </item>
             <item>
              <property name="text">
               <string>Stereo</string>
              </property>
             </item>
             <item>
              <property name="text">
               <string>Joint stereo</string>
              </property>
             </item>
            </widget>
           </item>
          </layout>
         </widget>
        </item>
        <item>
         <widget class="QWidget" name="compressionWidget" native="true">
          <layout class="QVBoxLayout" name="_2">
           <property name="leftMargin">
            <number>0</number>
           </property>
           <property name="topMargin">
            <number>0</number>
           </property>
           <property name="rightMargin">
            <number>0</number>
           </property>
           <property name="bottomMargin">
            <number>0</number>
           </property>
           <item>
            <widget class="QLabel" name="labelCompLevel">
             <property name="text">
              <string>Compression level:</string>
             </property>
            </widget>
           </item>
           <item>
            <widget class="QComboBox" name="compLevelCB">
             <property name="currentIndex">
              <number>-1</number>
             </property>
            </widget>
           </item>
          </layout>
         </widget>
        </item>
        <item>
         <widget class="QWidget" name="bitrateWidget" native="true">
          <layout class="QVBoxLayout">
           <property name="leftMargin">
            <number>0</number>
           </property>
           <property name="topMargin">
            <number>0</number>
           </property>
           <property name="rightMargin">
            <number>0</number>
           </property>
           <property name="bottomMargin">
            <number>0</number>
           </property>
           <item>
            <widget class="QLabel" name="labelBitRate">
             <property name="text">
              <string>Bitrate:</string>
             </property>
            </widget>
           </item>
           <item>
            <widget class="QComboBox" name="bitrateCB">
             <property name="currentIndex">
              <number>2</number>
             </property>
             <item>
              <property name="text">
               <string>64 KBit/s</string>
              </property>
             </item>
             <item>
              <property name="text">
               <string>128 KBit/s</string>
              </property>
             </item>
             <item>
              <property name="text">
               <string>160 KBit/s</string>
              </property>
             </item>
             <item>
              <property name="text">
               <string>192 KBit/s</string>
              </property>
             </item>
             <item>
              <property name="text">
               <string>256 KBit/s</string>
              </property>
             </item>
             <item>
              <property name="text">
               <string>320 KBit/s</string>
              </property>
             </item>
            </widget>
           </item>
           <item>
            <widget class="QCheckBox" name="checkBoxVariableBitRate">
             <property name="text">
              <string>Use variable bitrate</string>
             </property>
            </widget>
           </item>
          </layout>
         </widget>
        </item>
        <item>
         <spacer>
          <property name="orientation">
           <enum>Qt::Vertical</enum>
          </property>
          <property name="sizeHint" stdset="0">
           <size>
            <width>0</width>
            <height>0</height>
           </size>
          </property>
         </spacer>
        </item>
       </layout>
      </widget>
     </item>
     <item>
      <widget class="QGroupBox" name="qualityGroupBox">
       <property name="title">
        <string>Quality settings</string>
       </property>
       <layout class="QVBoxLayout">
        <item>
         <widget class="QLabel" name="label_2">
          <property name="text">
           <string>Interpolation:</string>
          </property>
         </widget>
        </item>
        <item>
         <widget class="QComboBox" name="interpolationCB">
          <property name="currentIndex">
           <number>2</number>
          </property>
          <item>
           <property name="text">
            <string>Zero order hold</string>
           </property>
          </item>
          <item>
           <property name="text">
            <string>Sinc worst (fastest)</string>
           </property>
          </item>
          <item>
           <property name="text">
            <string>Sinc medium (recommended)</string>
           </property>
          </item>
          <item>
           <property name="text">
            <string>Sinc best (slowest)</string>
           </property>
          </item>
         </widget>
        </item>
        <item>
         <widget class="QLabel" name="label_4">
          <property name="text">
           <string>Oversampling:</string>
          </property>
         </widget>
        </item>
        <item>
         <widget class="QComboBox" name="oversamplingCB">
          <item>
           <property name="text">
            <string>1x (None)</string>
           </property>
          </item>
          <item>
           <property name="text">
            <string>2x</string>
           </property>
          </item>
          <item>
           <property name="text">
            <string>4x</string>
           </property>
          </item>
          <item>
           <property name="text">
            <string>8x</string>
           </property>
          </item>
         </widget>
        </item>
        <item>
         <spacer>
          <property name="orientation">
           <enum>Qt::Vertical</enum>
          </property>
          <property name="sizeHint" stdset="0">
           <size>
            <width>0</width>
            <height>0</height>
           </size>
          </property>
         </spacer>
        </item>
       </layout>
      </widget>
     </item>
    </layout>
   </item>
   <item>
    <layout class="QHBoxLayout">
     <item>
      <spacer>
       <property name="orientation">
        <enum>Qt::Horizontal</enum>
       </property>
       <property name="sizeHint" stdset="0">
        <size>
         <width>0</width>
         <height>0</height>
        </size>
       </property>
      </spacer>
     </item>
     <item>
      <widget class="QPushButton" name="startButton">
       <property name="text">
        <string>Start</string>
       </property>
      </widget>
     </item>
     <item>
      <widget class="QPushButton" name="cancelButton">
       <property name="text">
        <string>Cancel</string>
       </property>
      </widget>
     </item>
    </layout>
   </item>
   <item>
    <widget class="QProgressBar" name="progressBar">
     <property name="enabled">
      <bool>false</bool>
     </property>
     <property name="value">
      <number>0</number>
     </property>
    </widget>
   </item>
  </layout>
 </widget>
 <resources/>
 <connections>
  <connection>
   <sender>cancelButton</sender>
   <signal>clicked()</signal>
   <receiver>ExportProjectDialog</receiver>
   <slot>reject()</slot>
   <hints>
    <hint type="sourcelabel">
     <x>511</x>
     <y>372</y>
    </hint>
    <hint type="destinationlabel">
     <x>202</x>
     <y>175</y>
    </hint>
   </hints>
  </connection>
  <connection>
   <sender>fileFormatCB</sender>
   <signal>currentIndexChanged(int)</signal>
   <receiver>ExportProjectDialog</receiver>
   <slot>onFileFormatChanged(int)</slot>
   <hints>
    <hint type="sourcelabel">
     <x>111</x>
     <y>85</y>
    </hint>
    <hint type="destinationlabel">
     <x>518</x>
     <y>212</y>
    </hint>
   </hints>
  </connection>
 </connections>
 <slots>
  <slot>onFileFormatChanged(int)</slot>
 </slots>
</ui>