code:
/*
 * Knob.cpp - powerful knob-widget
 *
 * 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 <memory>
#include <QApplication>
#include <QBitmap>
#include <QFontMetrics>
#include <QInputDialog>
#include <QMouseEvent>
#include <QPainter>

#ifndef __USE_XOPEN
#define __USE_XOPEN
#endif

#include "lmms_math.h"
#include "Knob.h"
#include "CaptionMenu.h"
#include "ConfigManager.h"
#include "ControllerConnection.h"
#include "DeprecationHelper.h"
#include "embed.h"
#include "gui_templates.h"
#include "GuiApplication.h"
#include "LocaleHelper.h"
#include "MainWindow.h"
#include "ProjectJournal.h"
#include "Song.h"
#include "stdshims.h"
#include "StringPairDrag.h"
#include "TextFloat.h"

TextFloat * Knob::s_textFloat = NULL;

Knob::Knob( knobTypes _knob_num, QWidget * _parent, const QString & _name ) :
	QWidget( _parent ),
	FloatModelView( new FloatModel( 0, 0, 0, 1, NULL, _name, true ), this ),
	m_label( "" ),
	m_isHtmlLabel(false),
	m_tdRenderer(nullptr),
	m_volumeKnob( false ),
	m_volumeRatio( 100.0, 0.0, 1000000.0 ),
	m_buttonPressed( false ),
	m_angle( -10 ),
	m_lineWidth( 0 ),
	m_textColor( 255, 255, 255 ),
	m_knobNum( _knob_num )
{
	initUi( _name );
}

Knob::Knob( QWidget * _parent, const QString & _name ) :
	Knob( knobBright_26, _parent, _name )
{
}

void Knob::initUi( const QString & _name )
{
	if( s_textFloat == NULL )
	{
		s_textFloat = new TextFloat;
	}

	setWindowTitle( _name );

	onKnobNumUpdated();
	setTotalAngle( 270.0f );
	setInnerRadius( 1.0f );
	setOuterRadius( 10.0f );
	setFocusPolicy( Qt::ClickFocus );

	// This is a workaround to enable style sheets for knobs which are not styled knobs.
	//
	// It works as follows: the palette colors that are assigned as the line color previously
	// had been hard coded in the drawKnob method for the different knob types. Now the
	// drawKnob method uses the line color to draw the lines. By assigning the palette colors
	// as the line colors here the knob lines will be drawn in this color unless the stylesheet
	// overrides that color.
	switch (knobNum())
	{
	case knobSmall_17:
	case knobBright_26:
	case knobDark_28:
		m_lineActiveColor = QApplication::palette().color(QPalette::Active, QPalette::WindowText);
		m_arcActiveColor = QColor(QApplication::palette().color(
									QPalette::Active, QPalette::WindowText));
		m_arcActiveColor.setAlpha(70);
		break;
	case knobVintage_32:
		m_lineActiveColor = QApplication::palette().color(QPalette::Active, QPalette::Shadow);
		m_arcActiveColor = QColor(QApplication::palette().color(
									QPalette::Active, QPalette::Shadow));
		m_arcActiveColor.setAlpha(70);
		break;
	default:
		break;
	}

	doConnections();
}

void Knob::onKnobNumUpdated()
{
	if( m_knobNum != knobStyled )
	{
		QString knobFilename;
		switch (m_knobNum)
		{
		case knobDark_28:
			knobFilename = "knob01";
			break;
		case knobBright_26:
			knobFilename = "knob02";
			break;
		case knobSmall_17:
			knobFilename = "knob03";
			break;
		case knobVintage_32:
			knobFilename = "knob05";
			break;
		case knobStyled: // only here to stop the compiler from complaining
			break;
		}

		// If knobFilename is still empty here we should get the fallback pixmap of size 1x1
		m_knobPixmap = make_unique<QPixmap>(QPixmap(embed::getIconPixmap(knobFilename.toUtf8().constData())));
		if (!this->isEnabled())
		{
			convertPixmapToGrayScale(*m_knobPixmap.get());
		}
		setFixedSize( m_knobPixmap->width(), m_knobPixmap->height() );
	}
}

void Knob::setLabel( const QString & txt )
{
	m_label = txt;
	m_isHtmlLabel = false;
	if( m_knobPixmap )
	{
		setFixedSize(qMax<int>( m_knobPixmap->width(),
					horizontalAdvance(QFontMetrics(pointSizeF(font(), 6.5)), m_label)),
						m_knobPixmap->height() + 10);
	}

	update();
}

void Knob::setHtmlLabel(const QString &htmltxt)
{
	m_label = htmltxt;
	m_isHtmlLabel = true;
	// Put the rendered HTML content into cache
	if (!m_tdRenderer)
	{
		m_tdRenderer = new QTextDocument(this);
	}

	m_tdRenderer->setHtml(QString("%2").arg(textColor().name(), m_label));

	if (m_knobPixmap)
	{
		setFixedSize(m_knobPixmap->width(),
				m_knobPixmap->height() + 15);
	}

	update();
}

void Knob::setTotalAngle( float angle )
{
	if( angle < 10.0 )
	{
		m_totalAngle = 10.0;
	}
	else
	{
		m_totalAngle = angle;
	}

	update();
}

float Knob::innerRadius() const
{
	return m_innerRadius;
}

void Knob::setInnerRadius( float r )
{
	m_innerRadius = r;
}

float Knob::outerRadius() const
{
	return m_outerRadius;
}

void Knob::setOuterRadius( float r )
{
	m_outerRadius = r;
}

knobTypes Knob::knobNum() const
{
	return m_knobNum;
}

void Knob::setknobNum( knobTypes k )
{
	if( m_knobNum != k )
	{
		m_knobNum = k;
		onKnobNumUpdated();
	}
}

QPointF Knob::centerPoint() const
{
	return m_centerPoint;
}

float Knob::centerPointX() const
{
	return m_centerPoint.x();
}

void Knob::setCenterPointX( float c )
{
	m_centerPoint.setX( c );
}

float Knob::centerPointY() const
{
	return m_centerPoint.y();
}

void Knob::setCenterPointY( float c )
{
	m_centerPoint.setY( c );
}

float Knob::lineWidth() const
{
	return m_lineWidth;
}

void Knob::setLineWidth( float w )
{
	m_lineWidth = w;
}

QColor Knob::outerColor() const
{
	return m_outerColor;
}

void Knob::setOuterColor( const QColor & c )
{
	m_outerColor = c;
}

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

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

QLineF Knob::calculateLine( const QPointF & _mid, float _radius, float _innerRadius ) const
{
	const float rarc = m_angle * F_PI / 180.0;
	const float ca = cos( rarc );
	const float sa = -sin( rarc );

	return QLineF( _mid.x() - sa*_innerRadius, _mid.y() - ca*_innerRadius,
					_mid.x() - sa*_radius, _mid.y() - ca*_radius );
}

bool Knob::updateAngle()
{
	int angle = 0;
	if( model() && model()->maxValue() != model()->minValue() )
	{
		angle = angleFromValue( model()->inverseScaledValue( model()->value() ), model()->minValue(), model()->maxValue(), m_totalAngle );
	}
	if( qAbs( angle - m_angle ) > 0 )
	{
		m_angle = angle;
		return true;
	}
	return false;
}

void Knob::drawKnob( QPainter * _p )
{
	bool enabled = this->isEnabled();
	QColor currentArcColor = enabled ? m_arcActiveColor : m_arcInactiveColor;
	QColor currentLineColor = enabled ? m_lineActiveColor : m_lineInactiveColor;

	if( updateAngle() == false && !m_cache.isNull() )
	{
		_p->drawImage( 0, 0, m_cache );
		return;
	}

	m_cache = QImage( size(), QImage::Format_ARGB32 );
	m_cache.fill( qRgba( 0, 0, 0, 0 ) );

	QPainter p( &m_cache );

	QPoint mid;

	if( m_knobNum == knobStyled )
	{
		p.setRenderHint( QPainter::Antialiasing );

		// Perhaps this can move to setOuterRadius()
		if( m_outerColor.isValid() )
		{
			QRadialGradient gradient( centerPoint(), outerRadius() );
			gradient.setColorAt( 0.4, _p->pen().brush().color() );
			gradient.setColorAt( 1, m_outerColor );

			p.setPen( QPen( gradient, lineWidth(),
						Qt::SolidLine, Qt::RoundCap ) );
		}
		else {
			QPen pen = p.pen();
			pen.setWidth( (int) lineWidth() );
			pen.setCapStyle( Qt::RoundCap );

			p.setPen( pen );
		}

		p.drawLine( calculateLine( centerPoint(), outerRadius(),
							innerRadius() ) );
		p.end();
		_p->drawImage( 0, 0, m_cache );
		return;
	}

	// Old-skool knobs
	const float radius = m_knobPixmap->width() / 2.0f - 1;
	mid = QPoint( width() / 2, m_knobPixmap->height() / 2 );

	p.drawPixmap( static_cast<int>(
				width() / 2 - m_knobPixmap->width() / 2 ), 0,
				*m_knobPixmap );

	p.setRenderHint( QPainter::Antialiasing );

	const int centerAngle = angleFromValue( model()->inverseScaledValue( model()->centerValue() ), model()->minValue(), model()->maxValue(), m_totalAngle );

	const int arcLineWidth = 2;
	const int arcRectSize = m_knobPixmap->width() - arcLineWidth;

	p.setPen(QPen(currentArcColor, 2));
	p.drawArc( mid.x() - arcRectSize/2, 1, arcRectSize, arcRectSize, 315*16, 16*m_totalAngle );

	p.setPen(QPen(currentLineColor, 2));
	switch( m_knobNum )
	{
		case knobSmall_17:
		{
			p.drawLine( calculateLine( mid, radius-2 ) );
			break;
		}
		case knobBright_26:
		{
			p.drawLine( calculateLine( mid, radius-5 ) );
			break;
		}
		case knobDark_28:
		{
			const float rb = qMax<float>( ( radius - 10 ) / 3.0,
									0.0 );
			const float re = qMax<float>( ( radius - 4 ), 0.0 );
			QLineF ln = calculateLine( mid, re, rb );
			ln.translate( 1, 1 );
			p.drawLine( ln );
			break;
		}
		case knobVintage_32:
		{
			p.drawLine( calculateLine( mid, radius-2, 2 ) );
			break;
		}
		case knobStyled:
			break;
	}

	p.drawArc( mid.x() - arcRectSize/2, 1, arcRectSize, arcRectSize, (90-centerAngle)*16, -16*(m_angle-centerAngle) );

	p.end();

	_p->drawImage( 0, 0, m_cache );
}

float Knob::getValue( const QPoint & _p )
{
	float value;

	// knob value increase is linear to mouse movement
	value = .4f * _p.y();

	// if shift pressed we want slower movement
	if( gui->mainWindow()->isShiftPressed() )
	{
		value /= 4.0f;
		value = qBound( -4.0f, value, 4.0f );
	}
	return value * pageSize();
}

void Knob::contextMenuEvent( QContextMenuEvent * )
{
	// for the case, the user clicked right while pressing left mouse-
	// button, the context-menu appears while mouse-cursor is still hidden
	// and it isn't shown again until user does something which causes
	// an QApplication::restoreOverrideCursor()-call...
	mouseReleaseEvent( NULL );

	CaptionMenu contextMenu( model()->displayName(), this );
	addDefaultActions( &contextMenu );
	contextMenu.addAction( QPixmap(),
		model()->isScaleLogarithmic() ? tr( "Set linear" ) : tr( "Set logarithmic" ),
		this, SLOT( toggleScale() ) );
	contextMenu.addSeparator();
	contextMenu.exec( QCursor::pos() );
}

void Knob::toggleScale()
{
	model()->setScaleLogarithmic( ! model()->isScaleLogarithmic() );
	update();
}

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

void Knob::dropEvent( QDropEvent * _de )
{
	QString type = StringPairDrag::decodeKey( _de );
	QString val = StringPairDrag::decodeValue( _de );
	if( type == "float_value" )
	{
		model()->setValue( LocaleHelper::toFloat(val) );
		_de->accept();
	}
	else if( type == "automatable_model" )
	{
		AutomatableModel * mod = dynamic_cast<AutomatableModel *>(
				Engine::projectJournal()->
					journallingObject( val.toInt() ) );
		if( mod != NULL )
		{
			AutomatableModel::linkModels( model(), mod );
			mod->setValue( model()->value() );
		}
	}
}

void Knob::mousePressEvent( QMouseEvent * _me )
{
	if( _me->button() == Qt::LeftButton &&
			! ( _me->modifiers() & Qt::ControlModifier ) &&
			! ( _me->modifiers() & Qt::ShiftModifier ) )
	{
		AutomatableModel *thisModel = model();
		if( thisModel )
		{
			thisModel->addJournalCheckPoint();
			thisModel->saveJournallingState( false );
		}

		const QPoint & p = _me->pos();
		m_lastMousePos = p;
		m_leftOver = 0.0f;

		emit sliderPressed();

		s_textFloat->setText( displayValue() );
		s_textFloat->moveGlobal( this,
				QPoint( width() + 2, 0 ) );
		s_textFloat->show();
		m_buttonPressed = true;
	}
	else if( _me->button() == Qt::LeftButton &&
			(_me->modifiers() & Qt::ShiftModifier) )
	{
		new StringPairDrag( "float_value",
					QString::number( model()->value() ),
							QPixmap(), this );
	}
	else
	{
		FloatModelView::mousePressEvent( _me );
	}
}

void Knob::mouseMoveEvent( QMouseEvent * _me )
{
	if( m_buttonPressed && _me->pos() != m_lastMousePos )
	{
		// knob position is changed depending on last mouse position
		setPosition( _me->pos() - m_lastMousePos );
		emit sliderMoved( model()->value() );
		// original position for next time is current position
		m_lastMousePos = _me->pos();
	}
	s_textFloat->setText( displayValue() );
}

void Knob::mouseReleaseEvent( QMouseEvent* event )
{
	if( event && event->button() == Qt::LeftButton )
	{
		AutomatableModel *thisModel = model();
		if( thisModel )
		{
			thisModel->restoreJournallingState();
		}
	}

	m_buttonPressed = false;

	emit sliderReleased();

	QApplication::restoreOverrideCursor();

	s_textFloat->hide();
}

void Knob::focusOutEvent( QFocusEvent * _fe )
{
	// make sure we don't loose mouse release event
	mouseReleaseEvent( NULL );
	QWidget::focusOutEvent( _fe );
}

void Knob::mouseDoubleClickEvent( QMouseEvent * )
{
	enterValue();
}

void Knob::paintEvent( QPaintEvent * _me )
{
	QPainter p( this );

	drawKnob( &p );
	if( !m_label.isEmpty() )
	{
		if (!m_isHtmlLabel)
		{
			p.setFont(pointSizeF(p.font(), 6.5));
			p.setPen(textColor());
			p.drawText(width() / 2 -
				horizontalAdvance(p.fontMetrics(), m_label) / 2,
				height() - 2, m_label);
		}
		else
		{
			m_tdRenderer->setDefaultFont(pointSizeF(p.font(), 6.5));
			p.translate((width() - m_tdRenderer->idealWidth()) / 2, (height() - m_tdRenderer->pageSize().height()) / 2);
			m_tdRenderer->drawContents(&p);
		}
	}
}

void Knob::wheelEvent(QWheelEvent * we)
{
	we->accept();
	const float stepMult = model()->range() / 2000 / model()->step<float>();
	const int inc = ((we->angleDelta().y() > 0 ) ? 1 : -1) * ((stepMult < 1 ) ? 1 : stepMult);
	model()->incValue( inc );

	s_textFloat->setText( displayValue() );
	s_textFloat->moveGlobal( this, QPoint( width() + 2, 0 ) );
	s_textFloat->setVisibilityTimeOut( 1000 );

	emit sliderMoved( model()->value() );
}

void Knob::setPosition( const QPoint & _p )
{
	const float value = getValue( _p ) + m_leftOver;
	const float step = model()->step<float>();
	const float oldValue = model()->value();

	if( model()->isScaleLogarithmic() ) // logarithmic code
	{
		const float pos = model()->minValue() < 0
			? oldValue / qMax( qAbs( model()->maxValue() ), qAbs( model()->minValue() ) )
			: ( oldValue - model()->minValue() ) / model()->range();
		const float ratio = 0.1f + qAbs( pos ) * 15.f;
		float newValue = value * ratio;
		if( qAbs( newValue ) >= step )
		{
			float roundedValue = qRound( ( oldValue - value ) / step ) * step;
			model()->setValue( roundedValue );
			m_leftOver = 0.0f;
		}
		else
		{
			m_leftOver = value;
		}
	}

	else // linear code
	{
		if( qAbs( value ) >= step )
		{
			float roundedValue = qRound( ( oldValue - value ) / step ) * step;
			model()->setValue( roundedValue );
			m_leftOver = 0.0f;
		}
		else
		{
			m_leftOver = value;
		}
	}
}

void Knob::enterValue()
{
	bool ok;
	float new_val;

	if( isVolumeKnob() &&
		ConfigManager::inst()->value( "app", "displaydbfs" ).toInt() )
	{
		new_val = QInputDialog::getDouble(
			this, tr( "Set value" ),
			tr( "Please enter a new value between "
					"-96.0 dBFS and 6.0 dBFS:" ),
				ampToDbfs( model()->getRoundedValue() / 100.0 ),
							-96.0, 6.0, model()->getDigitCount(), &ok );
		if( new_val <= -96.0 )
		{
			new_val = 0.0f;
		}
		else
		{
			new_val = dbfsToAmp( new_val ) * 100.0;
		}
	}
	else
	{
		new_val = QInputDialog::getDouble(
				this, tr( "Set value" ),
				tr( "Please enter a new value between "
						"%1 and %2:" ).
						arg( model()->minValue() ).
						arg( model()->maxValue() ),
					model()->getRoundedValue(),
					model()->minValue(),
					model()->maxValue(), model()->getDigitCount(), &ok );
	}

	if( ok )
	{
		model()->setValue( new_val );
	}
}

void Knob::friendlyUpdate()
{
	if (model() && (model()->controllerConnection() == NULL ||
		model()->controllerConnection()->getController()->frequentUpdates() == false ||
				Controller::runningFrames() % (256*4) == 0))
	{
		update();
	}
}

QString Knob::displayValue() const
{
	if( isVolumeKnob() &&
		ConfigManager::inst()->value( "app", "displaydbfs" ).toInt() )
	{
		return m_description.trimmed() + QString( " %1 dBFS" ).
				arg( ampToDbfs( model()->getRoundedValue() / volumeRatio() ),
								3, 'f', 2 );
	}
	return m_description.trimmed() + QString( " %1" ).
					arg( model()->getRoundedValue() ) + m_unit;
}

void Knob::doConnections()
{
	if( model() != NULL )
	{
		QObject::connect( model(), SIGNAL( dataChanged() ),
					this, SLOT( friendlyUpdate() ) );

		QObject::connect( model(), SIGNAL( propertiesChanged() ),
						this, SLOT( update() ) );
	}
}

void Knob::changeEvent(QEvent * ev)
{
	if (ev->type() == QEvent::EnabledChange)
	{
		onKnobNumUpdated();
		if (!m_label.isEmpty())
		{
			setLabel(m_label);
		}
		m_cache = QImage();
		update();
	}
}

void convertPixmapToGrayScale(QPixmap& pixMap)
{
	QImage temp = pixMap.toImage().convertToFormat(QImage::Format_ARGB32);
	for (int i = 0; i < temp.height(); ++i)
	{
		for (int j = 0; j < temp.width(); ++j)
		{
			const auto pix = temp.pixelColor(i, j);
			const auto gscale = 0.2126 * pix.redF() + 0.7152 * pix.greenF() + 0.0722 * pix.blueF();
			const auto pixGray = QColor::fromRgbF(gscale, gscale, gscale, pix.alphaF());
			temp.setPixelColor(i, j, pixGray);
		}
	}
	pixMap.convertFromImage(temp);
}


code:
/*
 * LadspaControlView.cpp - widget for controlling a LADSPA port
 *
 * Copyright (c) 2006-2008 Danny McRae <khjklujn/at/users.sourceforge.net>
 * Copyright (c) 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 <QLayout>

#include "LadspaControl.h"
#include "LadspaControlView.h"
#include "LadspaBase.h"
#include "LedCheckbox.h"
#include "TempoSyncKnob.h"
#include "ToolTip.h"

LadspaControlView::LadspaControlView( QWidget * _parent,
						LadspaControl * _ctl ) :
	QWidget( _parent ),
	ModelView( _ctl, this ),
	m_ctl( _ctl )
{
	QHBoxLayout * layout = new QHBoxLayout( this );
	layout->setMargin( 0 );
	layout->setSpacing( 0 );

	LedCheckBox * link = NULL;

	if( m_ctl->m_link )
	{
		link = new LedCheckBox( "", this );
		link->setModel( &m_ctl->m_linkEnabledModel );
		ToolTip::add( link, tr( "Link channels" ) );
		layout->addWidget( link );
	}

	Knob * knb = NULL;

	switch( m_ctl->port()->data_type )
	{
		case TOGGLED:
		{
			LedCheckBox * toggle = new LedCheckBox(
				m_ctl->port()->name, this, QString(), LedCheckBox::Green );
			toggle->setModel( m_ctl->toggledModel() );
			layout->addWidget( toggle );
			if( link != NULL )
			{
				setFixedSize( link->width() + toggle->width(),
							toggle->height() );
			}
			else
			{
				setFixedSize( toggle->width(),
							toggle->height() );
			}
			break;
		}

		case INTEGER:
		case ENUM:
		case FLOATING:
			knb = new Knob( knobBright_26, this, m_ctl->port()->name );
			break;

		case TIME:
			knb = new TempoSyncKnob( knobBright_26, this, m_ctl->port()->name );
			break;

		default:
			break;
	}

	if( knb != NULL )
	{
		if( m_ctl->port()->data_type != TIME )
		{
			knb->setModel( m_ctl->knobModel() );
		}
		else
		{
			knb->setModel( m_ctl->tempoSyncKnobModel() );
		}
		knb->setLabel( m_ctl->port()->name );
		knb->setHintText( tr( "Value:" ), "" );
		layout->addWidget( knb );
		if( link != NULL )
		{
			setFixedSize( link->width() + knb->width(),
						knb->height() );
		}
		else
		{
			setFixedSize( knb->width(), knb->height() );
		}
	}
}

LadspaControlView::~LadspaControlView()
{
}

repo_name: CYBERDEViL/LMMS

license: mit


code:
/*
 * LcdSpinBox.cpp - class LcdSpinBox, an improved QLCDNumber
 *
 * Copyright (c) 2005-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
 * Copyright (c) 2008 Paul Giblock <pgllama/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 <cmath>
#include <QApplication>
#include <QLabel>
#include <QMouseEvent>
#include <QPainter>
#include <QStyleOptionFrameV2>
#include <QInputDialog>

#include "LcdSpinBox.h"
#include "CaptionMenu.h"
#include "GuiApplication.h"
#include "MainWindow.h"

LcdSpinBox::LcdSpinBox( int numDigits, QWidget* parent, const QString& name ) :
	LcdWidget( numDigits, parent, name ),
	IntModelView( new IntModel( 0, 0, 0, NULL, name, true ), this ),
	m_remainder( 0.f ),
	m_mouseMoving( false ),
	m_lastMousePos(),
	m_displayOffset( 0 )
{
}

LcdSpinBox::LcdSpinBox( int numDigits, const QString& style, QWidget* parent, const QString& name ) :
	LcdWidget( numDigits, style, parent, name ),
	IntModelView( new IntModel( 0, 0, 0, NULL, name, true ), this ),
	m_remainder( 0.f ),
	m_mouseMoving( false ),
	m_lastMousePos(),
	m_displayOffset( 0 )
{
}

void LcdSpinBox::update()
{
	setValue( model()->value() + m_displayOffset );

	QWidget::update();
}

void LcdSpinBox::contextMenuEvent( QContextMenuEvent* event )
{
	// for the case, the user clicked right while pressing left mouse-
	// button, the context-menu appears while mouse-cursor is still hidden
	// and it isn't shown again until user does something which causes
	// an QApplication::restoreOverrideCursor()-call...
	mouseReleaseEvent( NULL );

	CaptionMenu contextMenu( model()->displayName() );
	addDefaultActions( &contextMenu );
	contextMenu.exec( QCursor::pos() );
}

void LcdSpinBox::mousePressEvent( QMouseEvent* event )
{
	if( event->button() == Qt::LeftButton &&
		! ( event->modifiers() & Qt::ControlModifier ) &&
						event->y() < cellHeight() + 2  )
	{
		m_mouseMoving = true;
		m_lastMousePos = event->globalPos();

		AutomatableModel *thisModel = model();
		if( thisModel )
		{
			thisModel->addJournalCheckPoint();
			thisModel->saveJournallingState( false );
		}
	}
	else
	{
		IntModelView::mousePressEvent( event );
	}
}

void LcdSpinBox::mouseMoveEvent( QMouseEvent* event )
{
	if( m_mouseMoving )
	{
		int dy = event->globalY() - m_lastMousePos.y();
		if( dy )
		{
			float fdy = static_cast<float>(dy);
			if( event->modifiers() & Qt::ShiftModifier ) {
				fdy = qBound( -4.f, fdy/4.f, 4.f );
			}
			float floatValNotRounded =
				model()->value() + m_remainder - fdy / 2.f * model()->step<int>();
			float floatValRounded = roundf( floatValNotRounded );
			m_remainder = floatValNotRounded - floatValRounded;
			model()->setInitValue( floatValRounded );
			emit manualChange();
			m_lastMousePos = event->globalPos();
		}
	}
}

void LcdSpinBox::mouseReleaseEvent( QMouseEvent* )
{
	if( m_mouseMoving )
	{
		model()->restoreJournallingState();
		QApplication::restoreOverrideCursor();
		m_mouseMoving = false;
	}
}

void LcdSpinBox::wheelEvent(QWheelEvent * we)
{
	we->accept();
	model()->setInitValue(model()->value() + ((we->angleDelta().y() > 0) ? 1 : -1) * model()->step<int>());
	emit manualChange();
}

void LcdSpinBox::mouseDoubleClickEvent( QMouseEvent * )
{
	enterValue();
}

void LcdSpinBox::enterValue()
{
	bool ok;
	int new_val;

	new_val = QInputDialog::getInt(
			this, tr( "Set value" ),
			tr( "Please enter a new value between %1 and %2:" ).
			arg( model()->minValue() ).
			arg( model()->maxValue() ),
			model()->value(),
			model()->minValue(),
			model()->maxValue(),
			model()->step<int>(), &ok );

	if( ok )
	{
		model()->setValue( new_val );
	}
}


code:
/*
 * LcdWidget.cpp - a widget for displaying numbers in LCD style
 *
 * Copyright (c) 2005-2014 Tobias Doerffel <tobydox/at/users.sourceforge.net>
 * Copyright (c) 2008 Paul Giblock <pgllama/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 <QApplication>
#include <QLabel>
#include <QMouseEvent>
#include <QPainter>
#include <QStyleOptionFrameV2>

#include "LcdWidget.h"
#include "DeprecationHelper.h"
#include "embed.h"
#include "gui_templates.h"
#include "MainWindow.h"

LcdWidget::LcdWidget( QWidget* parent, const QString& name ) :
	LcdWidget( 1, parent, name )
{
}

LcdWidget::LcdWidget( int numDigits, QWidget* parent, const QString& name ) :
	LcdWidget( numDigits, QString("19green"), parent, name )
{
}

LcdWidget::LcdWidget( int numDigits, const QString& style, QWidget* parent, const QString& name ) :
	QWidget( parent ),
	m_label(),
	m_textColor( 255, 255, 255 ),
	m_textShadowColor( 64, 64, 64 ),
	m_numDigits( numDigits )
{
	initUi( name, style );
}

LcdWidget::~LcdWidget()
{
	delete m_lcdPixmap;
}

void LcdWidget::setValue( int value )
{
	QString s = m_textForValue[value];
	if( s.isEmpty() )
	{
		s = QString::number( value );
		// TODO: if pad == true
		/*
		while( (int) s.length() < m_numDigits )
		{
			s = "0" + s;
		}
		*/
	}

	m_display = s;

	update();
}

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

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

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

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

void LcdWidget::paintEvent( QPaintEvent* )
{
	QPainter p( this );

	QSize cellSize( m_cellWidth, m_cellHeight );

	QRect cellRect( 0, 0, m_cellWidth, m_cellHeight );

	int margin = 1;  // QStyle::PM_DefaultFrameWidth;
	//int lcdWidth = m_cellWidth * m_numDigits + (margin*m_marginWidth)*2;

//	p.translate( width() / 2 - lcdWidth / 2, 0 );
	p.save();

	p.translate( margin, margin );

	// Left Margin
	p.drawPixmap( cellRect, *m_lcdPixmap,
			QRect( QPoint( charsPerPixmap*m_cellWidth,
				isEnabled()?0:m_cellHeight ),
			cellSize ) );

	p.translate( m_marginWidth, 0 );

	// Padding
	for( int i=0; i < m_numDigits - m_display.length(); i++ )
	{
		p.drawPixmap( cellRect, *m_lcdPixmap,
			QRect( QPoint( 10 * m_cellWidth, isEnabled()?0:m_cellHeight) , cellSize ) );
		p.translate( m_cellWidth, 0 );
	}

	// Digits
	for( int i=0; i < m_display.length(); i++ )
	{
		int val = m_display[i].digitValue();
		if( val < 0 )
		{
			if( m_display[i] == '-' )
				val = 11;
			else
				val = 10;
		}
		p.drawPixmap( cellRect, *m_lcdPixmap,
				QRect( QPoint( val*m_cellWidth,
					isEnabled()?0:m_cellHeight ),
				cellSize ) );
		p.translate( m_cellWidth, 0 );
	}

	// Right Margin
	p.drawPixmap( QRect( 0, 0, m_marginWidth-1, m_cellHeight ), *m_lcdPixmap,
			QRect( charsPerPixmap*m_cellWidth, isEnabled()?0:m_cellHeight,
				m_cellWidth / 2, m_cellHeight ) );

	p.restore();

	// Border
	QStyleOptionFrame opt;
	opt.initFrom( this );
	opt.state = QStyle::State_Sunken;
	opt.rect = QRect( 0, 0, m_cellWidth * m_numDigits + (margin+m_marginWidth)*2 - 1,
			m_cellHeight + (margin*2) );

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

	p.resetTransform();

	// Label
	if( !m_label.isEmpty() )
	{
		p.setFont( pointSizeF( p.font(), 6.5 ) );
		p.setPen( textShadowColor() );
		p.drawText(width() / 2 -
				horizontalAdvance(p.fontMetrics(), m_label) / 2 + 1,
						height(), m_label);
		p.setPen( textColor() );
		p.drawText(width() / 2 -
				horizontalAdvance(p.fontMetrics(), m_label) / 2,
						height() - 1, m_label);
	}

}

void LcdWidget::setLabel( const QString& label )
{
	m_label = label;
	updateSize();
}

void LcdWidget::setMarginWidth( int width )
{
	m_marginWidth = width;

	updateSize();
}

void LcdWidget::updateSize()
{
	int margin = 1;
	if (m_label.isEmpty()) {
		setFixedSize( m_cellWidth * m_numDigits + 2*(margin+m_marginWidth),
				m_cellHeight + (2*margin) );
	}
	else {
		setFixedSize(qMax<int>(
				m_cellWidth * m_numDigits + 2*(margin+m_marginWidth),
				horizontalAdvance(QFontMetrics(pointSizeF(font(), 6.5)), m_label)),
				m_cellHeight + (2*margin) + 9);
	}

	update();
}

void LcdWidget::initUi(const QString& name , const QString& style)
{
	setEnabled( true );

	setWindowTitle( name );

	// We should make a factory for these or something.
	//m_lcdPixmap = new QPixmap( embed::getIconPixmap( QString( "lcd_" + style ).toUtf8().constData() ) );
	//m_lcdPixmap = new QPixmap( embed::getIconPixmap( "lcd_19green" ) ); // TODO!!
	m_lcdPixmap = new QPixmap( embed::getIconPixmap( QString( "lcd_" + style ).toUtf8().constData() ) );

	m_cellWidth = m_lcdPixmap->size().width() / LcdWidget::charsPerPixmap;
	m_cellHeight = m_lcdPixmap->size().height() / 2;

	m_marginWidth =  m_cellWidth / 2;

	updateSize();
}


code:
/*
 * LedCheckbox.cpp - class LedCheckBox, an improved QCheckBox
 *
 * Copyright (c) 2005-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 <QFontMetrics>
#include <QPainter>

#include "LedCheckbox.h"
#include "DeprecationHelper.h"
#include "embed.h"
#include "gui_templates.h"

static const QString names[LedCheckBox::NumColors] =
{
	"led_yellow", "led_green", "led_red"
} ;

LedCheckBox::LedCheckBox( const QString & _text, QWidget * _parent,
				const QString & _name, LedColors _color ) :
	AutomatableButton( _parent, _name ),
	m_text( _text )
{
	initUi( _color );
}

LedCheckBox::LedCheckBox( QWidget * _parent,
				const QString & _name, LedColors _color ) :
	LedCheckBox( QString(), _parent, _name, _color )
{
}

LedCheckBox::~LedCheckBox()
{
	delete m_ledOnPixmap;
	delete m_ledOffPixmap;
}

void LedCheckBox::setText( const QString &s )
{
	m_text = s;
	onTextUpdated();
}

void LedCheckBox::paintEvent( QPaintEvent * )
{
	QPainter p( this );
	p.setFont( pointSize<7>( font() ) );

	if( model()->value() == true )
		{
			p.drawPixmap( 0, 0, *m_ledOnPixmap );
	}
	else
	{
		p.drawPixmap( 0, 0, *m_ledOffPixmap );
	}

	p.setPen( QColor( 64, 64, 64 ) );
	p.drawText( m_ledOffPixmap->width() + 4, 11, text() );
	p.setPen( QColor( 255, 255, 255 ) );
	p.drawText( m_ledOffPixmap->width() + 3, 10, text() );
}

void LedCheckBox::initUi( LedColors _color )
{
	setCheckable( true );

	if( _color >= NumColors || _color < Yellow )
	{
		_color = Yellow;
	}
	m_ledOnPixmap = new QPixmap( embed::getIconPixmap(
					names[_color].toUtf8().constData() ) );
	m_ledOffPixmap = new QPixmap( embed::getIconPixmap( "led_off" ) );

	setFont( pointSize<7>( font() ) );
	setText( m_text );
}

void LedCheckBox::onTextUpdated()
{
	setFixedSize(m_ledOffPixmap->width() + 5 + horizontalAdvance(QFontMetrics(font()),
				text()),
				m_ledOffPixmap->height());
}


code:
/*
 * LeftRightNav.cpp - side-by-side left-facing and right-facing arrows for navigation (looks like < > )
 *
 * Copyright (c) 2015 Colin Wallace <wallacoloo/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 "LeftRightNav.h"
#include "ToolTip.h"
#include "embed.h"

LeftRightNav::LeftRightNav(QWidget *parent)
 : QWidget(parent),
   m_layout(this),
   m_leftBtn(this, tr("Previous")),
   m_rightBtn(this, tr("Next"))
{
	m_layout.setContentsMargins(0, 0, 0, 0);
	m_layout.setSpacing(2);

	m_leftBtn.setCheckable(false);
	m_rightBtn.setCheckable(false);

	m_leftBtn.setCursor(Qt::PointingHandCursor);
	m_rightBtn.setCursor(Qt::PointingHandCursor);

	m_leftBtn.setActiveGraphic(embed::getIconPixmap(
							"stepper-left-press"));
	m_rightBtn.setActiveGraphic(embed::getIconPixmap(
							"stepper-right-press" ));

	m_leftBtn.setInactiveGraphic(embed::getIconPixmap(
							"stepper-left" ));
	m_rightBtn.setInactiveGraphic(embed::getIconPixmap(
							"stepper-right"));

	connect(&m_leftBtn, SIGNAL(clicked()), this,
						SIGNAL(onNavLeft()));
	connect(&m_rightBtn, SIGNAL(clicked()), this,
						SIGNAL(onNavRight()));

	ToolTip::add(&m_leftBtn, tr("Previous"));
	ToolTip::add(&m_rightBtn, tr("Next"));

	m_leftBtn.setWindowTitle(tr("Previous"));
	m_rightBtn.setWindowTitle(tr("Next"));

	// AutomatableButton's right click menu (contains irrelevant options like copying and pasting values)
	m_leftBtn.setContextMenuPolicy(Qt::NoContextMenu);
	m_rightBtn.setContextMenuPolicy(Qt::NoContextMenu);

	m_layout.addWidget(&m_leftBtn);
	m_layout.addWidget(&m_rightBtn);
}

PixmapButton* LeftRightNav::getLeftBtn()
{
	return &m_leftBtn;
}
PixmapButton* LeftRightNav::getRightBtn()
{
	return &m_rightBtn;
}

void LeftRightNav::setShortcuts(const QKeySequence &leftShortcut, const QKeySequence &rightShortcut)
{
	m_leftBtn.setShortcut(leftShortcut);
	m_rightBtn.setShortcut(rightShortcut);

	ToolTip::add(&m_leftBtn, tr("Previous (%1)").arg(leftShortcut.toString()));
	ToolTip::add(&m_rightBtn, tr("Next (%1)").arg(rightShortcut.toString()));
}


code:
/*
 * LinkedModelGroupViews.h - view for groups of linkable models
 *
 * Copyright (c) 2019-2019 Johannes Lorenz <j.git$$$lorenz-ho.me, $$$=@>
 *
 * 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 "LinkedModelGroupViews.h"

#include <QPushButton>
#include "Controls.h"
#include "ControlLayout.h"
#include "LinkedModelGroups.h"

/*
	LinkedModelGroupViewBase
*/

LinkedModelGroupView::LinkedModelGroupView(QWidget* parent,
	LinkedModelGroup *model, std::size_t colNum) :
	QWidget(parent),
	m_model(model),
	m_colNum(colNum),
	m_layout(new ControlLayout(this))
{
	// This is required to remove the focus of the line edit
	// when e.g. another spin box is being clicked.
	// Removing the focus is wanted because in many cases, the user wants to
	// quickly play notes on the virtual keyboard.
	setFocusPolicy( Qt::StrongFocus );
}

LinkedModelGroupView::~LinkedModelGroupView() {}

void LinkedModelGroupView::modelChanged(LinkedModelGroup *group)
{
	// reconnect models
	group->foreach_model([this](const std::string& str,
		const LinkedModelGroup::ModelInfo& minf)
	{
		auto itr = m_widgets.find(str);
		// in case there are new or deleted widgets, the subclass has already
		// modified m_widgets, so this will go into the else case
		if (itr == m_widgets.end())
		{
			// no widget? this can happen when the whole view is being destroyed
			// (for some strange reasons)
		}
		else
		{
			itr->second->setModel(minf.m_model);
		}
	});

	m_model = group;
}

void LinkedModelGroupView::addControl(Control* ctrl, const std::string& id,
	const std::string &display, bool removable)
{
	int wdgNum = static_cast<int>(m_widgets.size());
	if (ctrl)
	{
		QWidget* box = new QWidget(this);
		QHBoxLayout* boxLayout = new QHBoxLayout(box);
		boxLayout->addWidget(ctrl->topWidget());

		if (removable)
		{
			QPushButton* removeBtn = new QPushButton;
			removeBtn->setIcon( embed::getIconPixmap( "discard" ) );
			QObject::connect(removeBtn, &QPushButton::clicked,
				this, [this,ctrl](bool){
					AutomatableModel* controlModel = ctrl->model();
					// remove control out of model group
					// (will also remove it from the UI)
					m_model->removeControl(controlModel);
					// delete model (includes disconnecting all connections)
					delete controlModel;
				},
				Qt::DirectConnection);
			boxLayout->addWidget(removeBtn);
		}

		// required, so the Layout knows how to sort/filter widgets by string
		box->setObjectName(QString::fromStdString(display));
		m_layout->addWidget(box);

		// take ownership of control and add it
		m_widgets.emplace(id, std::unique_ptr<Control>(ctrl));
		++wdgNum;
	}

	if (isHidden()) { setHidden(false); }
}

void LinkedModelGroupView::removeControl(const QString& key)
{
	auto itr = m_widgets.find(key.toStdString());
	if (itr != m_widgets.end())
	{
		QLayoutItem* item = m_layout->itemByString(key);
		Q_ASSERT(!!item);
		QWidget* wdg = item->widget();
		Q_ASSERT(!!wdg);

		// remove item from layout
		m_layout->removeItem(item);
		// the widget still exists and is visible - remove it now
		delete wdg;
		// erase widget pointer from dictionary
		m_widgets.erase(itr);
		// repaint immediately, so we don't have dangling model views
		m_layout->update();
	}
}

void LinkedModelGroupView::removeFocusFromSearchBar()
{
	m_layout->removeFocusFromSearchBar();
}

/*
	LinkedModelGroupsViewBase
*/

void LinkedModelGroupsView::modelChanged(LinkedModelGroups *groups)
{
	LinkedModelGroupView* groupView = getGroupView();
	LinkedModelGroup* group0 = groups->getGroup(0);
	if (group0 && groupView)
	{
		groupView->modelChanged(group0);
	}
}


code:
/*
 * MeterDialog.cpp - dialog for entering meter settings
 *
 * Copyright (c) 2008-2009 Tobias Doerffel <tobydox/at/users.sourceforge.net>
 * Copyright (c) 2006-2008 Danny McRae <khjklujn/at/yahoo.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 <QLayout>
#include <QPushButton>
#include <QLabel>

#include "MeterDialog.h"
#include "MeterModel.h"
#include "gui_templates.h"
#include "LcdSpinBox.h"
#include "ToolTip.h"

MeterDialog::MeterDialog( QWidget * _parent, bool _simple ) :
	QWidget( _parent ),
	ModelView( NULL, this )
{
	QVBoxLayout * vlayout = new QVBoxLayout( this );
	vlayout->setSpacing( 0 );
	vlayout->setMargin( 0 );

	QWidget * num = new QWidget( this );
	QHBoxLayout * num_layout = new QHBoxLayout( num );
	num_layout->setSpacing( 0 );
	num_layout->setMargin( 0 );

	m_numerator = new LcdSpinBox( 2, num, tr( "Meter Numerator" ) );
	ToolTip::add( m_numerator, tr( "Meter numerator" ) );

	num_layout->addWidget( m_numerator );

	if( !_simple )
	{
		QLabel * num_label = new QLabel( tr( "Meter Numerator" ), num );
		QFont f = num_label->font();
		num_label->setFont( pointSize<7>( f ) );
		num_layout->addSpacing( 5 );
		num_layout->addWidget( num_label );
	}
	num_layout->addStretch();

	QWidget * den = new QWidget( this );
	QHBoxLayout * den_layout = new QHBoxLayout( den );
	den_layout->setSpacing( 0 );
	den_layout->setMargin( 0 );

	m_denominator = new LcdSpinBox( 2, den, tr( "Meter Denominator" ) );
	ToolTip::add( m_denominator, tr( "Meter denominator" ) );
	if( _simple )
	{
		m_denominator->setLabel( tr( "TIME SIG" ) );
	}

	den_layout->addWidget( m_denominator );

	if( !_simple )
	{
		QLabel * den_label = new QLabel( tr( "Meter Denominator" ),
									den );
		QFont f = den_label->font();
		den_label->setFont( pointSize<7>( f ) );
		den_layout->addSpacing( 5 );
		den_layout->addWidget( den_label );
	}
	den_layout->addStretch();

	vlayout->addSpacing( _simple ? 1 : 3 );
	vlayout->addWidget( num );
	vlayout->addSpacing( 2 );
	vlayout->addWidget( den );
	vlayout->addStretch();
}

MeterDialog::~MeterDialog()
{
}

void MeterDialog::modelChanged()
{
	MeterModel * mm = castModel<MeterModel>();
	m_numerator->setModel( &mm->numeratorModel() );
	m_denominator->setModel( &mm->denominatorModel() );
}


code:
/*
 * MidiPortMenu.cpp - a menu for subscribing a MidiPort to several external
 *                      MIDI ports
 *
 * Copyright (c) 2008-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 "MidiPortMenu.h"
#include "gui_templates.h"

MidiPortMenu::MidiPortMenu( MidiPort::Modes _mode ) :
	ModelView( NULL, this ),
	m_mode( _mode )
{
	setFont( pointSize<9>( font() ) );
	connect( this, SIGNAL( triggered( QAction * ) ),
			this, SLOT( activatedPort( QAction * ) ) );
}

MidiPortMenu::~MidiPortMenu()
{
}

void MidiPortMenu::modelChanged()
{
	MidiPort * mp = castModel<MidiPort>();
	if( m_mode == MidiPort::Input )
	{
		connect( mp, SIGNAL( readablePortsChanged() ),
				this, SLOT( updateMenu() ) );
	}
	else if( m_mode == MidiPort::Output )
	{
		connect( mp, SIGNAL( writablePortsChanged() ),
				this, SLOT( updateMenu() ) );
	}
	updateMenu();
}

void MidiPortMenu::activatedPort( QAction * _item )
{
	if( m_mode == MidiPort::Input )
	{
		castModel<MidiPort>()->subscribeReadablePort( _item->text(),
							_item->isChecked() );
	}
	else if( m_mode == MidiPort::Output )
	{
		castModel<MidiPort>()->subscribeWritablePort( _item->text(),
							_item->isChecked() );
	}
}

void MidiPortMenu::updateMenu()
{
	MidiPort * mp = castModel<MidiPort>();
	const MidiPort::Map & map = ( m_mode == MidiPort::Input ) ?
				mp->readablePorts() : mp->writablePorts();
	clear();
	for( MidiPort::Map::ConstIterator it = map.begin();
							it != map.end(); ++it )
	{
		QAction * a = addAction( it.key() );
		a->setCheckable( true );
		a->setChecked( it.value() );
	}
}


code:
/*
 * NStateButton.cpp - implementation of n-state-button
 *
 * 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 <QMouseEvent>

#include "NStateButton.h"
#include "ToolTip.h"

NStateButton::NStateButton( QWidget * _parent ) :
	ToolButton( _parent ),
	m_generalToolTip( "" ),
	m_curState( -1 )
{
}

NStateButton::~NStateButton()
{
	while( m_states.size() )
	{
		m_states.erase( m_states.begin() );
	}
}

void NStateButton::addState( const QPixmap & _pm, const QString & _tooltip )
{
	m_states.push_back( qMakePair( _pm, _tooltip ) );
	// first inserted pixmap?
	if( m_states.size() == 1 )
	{
		// and set state to first pixmap
		changeState( 0 );
	}
}

void NStateButton::changeState( int _n )
{
	if( _n >= 0 && _n < (int) m_states.size() )
	{
		m_curState = _n;

		const QString & _tooltip =
			( m_states[m_curState].second != "" ) ?
				m_states[m_curState].second :
					m_generalToolTip;
		ToolTip::add( this, _tooltip );

		setIcon( m_states[m_curState].first );

		emit changedState( m_curState );
	}
}

void NStateButton::mousePressEvent( QMouseEvent * _me )
{
	if( _me->button() == Qt::LeftButton && m_states.size() )
	{
		changeState( ( ++m_curState ) % m_states.size() );
	}
	ToolButton::mousePressEvent( _me );
}


code:
/*
 * Oscilloscope.cpp
 *
 * 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 <QMouseEvent>
#include <QPainter>

#include "Oscilloscope.h"
#include "GuiApplication.h"
#include "gui_templates.h"
#include "MainWindow.h"
#include "Mixer.h"
#include "Engine.h"
#include "ToolTip.h"
#include "Song.h"
#include "embed.h"
#include "BufferManager.h"

Oscilloscope::Oscilloscope( QWidget * _p ) :
	QWidget( _p ),
	m_background( embed::getIconPixmap( "output_graph" ) ),
	m_points( new QPointF[Engine::mixer()->framesPerPeriod()] ),
	m_active( false ),
	m_normalColor(71, 253, 133),
	m_clippingColor(255, 64, 64)
{
	setFixedSize( m_background.width(), m_background.height() );
	setAttribute( Qt::WA_OpaquePaintEvent, true );
	setActive( ConfigManager::inst()->value( "ui", "displaywaveform").toInt() );

	const fpp_t frames = Engine::mixer()->framesPerPeriod();
	m_buffer = new sampleFrame[frames];

	BufferManager::clear( m_buffer, frames );

	ToolTip::add( this, tr( "Oscilloscope" ) );
}

Oscilloscope::~Oscilloscope()
{
	delete[] m_buffer;
	delete[] m_points;
}

void Oscilloscope::updateAudioBuffer( const surroundSampleFrame * buffer )
{
	if( !Engine::getSong()->isExporting() )
	{
		const fpp_t fpp = Engine::mixer()->framesPerPeriod();
		memcpy( m_buffer, buffer, sizeof( surroundSampleFrame ) * fpp );
	}
}

void Oscilloscope::setActive( bool _active )
{
	m_active = _active;
	if( m_active )
	{
		connect( gui->mainWindow(),
					SIGNAL( periodicUpdate() ),
					this, SLOT( update() ) );
		connect( Engine::mixer(),
			SIGNAL( nextAudioBuffer( const surroundSampleFrame* ) ),
			this, SLOT( updateAudioBuffer( const surroundSampleFrame* ) ) );
	}
	else
	{
		disconnect( gui->mainWindow(),
					SIGNAL( periodicUpdate() ),
					this, SLOT( update() ) );
		disconnect( Engine::mixer(),
			SIGNAL( nextAudioBuffer( const surroundSampleFrame* ) ),
			this, SLOT( updateAudioBuffer( const surroundSampleFrame* ) ) );
		// we have to update (remove last waves),
		// because timer doesn't do that anymore
		update();
	}
}

QColor const & Oscilloscope::normalColor() const
{
	return m_normalColor;
}

void Oscilloscope::setNormalColor(QColor const & normalColor)
{
	m_normalColor = normalColor;
}

QColor const & Oscilloscope::clippingColor() const
{
	return m_clippingColor;
}

void Oscilloscope::setClippingColor(QColor const & clippingColor)
{
	m_clippingColor = clippingColor;
}

void Oscilloscope::paintEvent( QPaintEvent * )
{
	QPainter p( this );

	p.drawPixmap( 0, 0, m_background );

	if( m_active && !Engine::getSong()->isExporting() )
	{
		Mixer const * mixer = Engine::mixer();

		float master_output = mixer->masterGain();

		const fpp_t frames = mixer->framesPerPeriod();
		Mixer::StereoSample peakValues = mixer->getPeakValues(m_buffer, frames);
		const float max_level = qMax<float>( peakValues.left, peakValues.right );

		// Set the color of the line according to the maximum level
		float const maxLevelWithAppliedMasterGain = max_level * master_output;
		p.setPen(QPen(determineLineColor(maxLevelWithAppliedMasterGain), 0.7));

		p.setRenderHint( QPainter::Antialiasing );

		// now draw all that stuff
		int w = width() - 4;
		const qreal xd = static_cast<qreal>(w) / frames;
		const qreal half_h = -( height() - 6 ) / 3.0 * static_cast<qreal>(master_output) - 1;
		int x_base = 2;
		const qreal y_base = height() / 2 - 0.5;

		for( ch_cnt_t ch = 0; ch < DEFAULT_CHANNELS; ++ch )
		{
			for( int frame = 0; frame < frames; ++frame )
			{
				sample_t const clippedSample = Mixer::clip(m_buffer[frame][ch]);
				m_points[frame] = QPointF(
					x_base + static_cast<qreal>(frame) * xd,
					y_base + ( static_cast<qreal>(clippedSample) * half_h ) );
			}
			p.drawPolyline( m_points, frames );
		}
	}
	else
	{
		p.setPen( QColor( 192, 192, 192 ) );
		p.setFont( pointSize<7>( p.font() ) );
		p.drawText( 6, height()-5, tr( "Click to enable" ) );
	}
}

void Oscilloscope::mousePressEvent( QMouseEvent * _me )
{
	if( _me->button() == Qt::LeftButton )
	{
		setActive( !m_active );
	}
}

QColor const & Oscilloscope::determineLineColor(float level) const
{
	if( level <= 1.0f )
	{
		return normalColor();
	}
	else
	{
		return clippingColor();
	}
}


code:
/*
 * PixmapButton.cpp - implementation of pixmap-button (often used as "themed"
 *                     checkboxes/radiobuttons etc)
 *
 * Copyright (c) 2004-2013 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 <QPainter>

#include "PixmapButton.h"
#include "MainWindow.h"
#include "embed.h"

PixmapButton::PixmapButton( QWidget * _parent, const QString & _name ) :
	AutomatableButton( _parent, _name ),
	m_activePixmap(),
	m_inactivePixmap(),
	m_pressed( false )
{
	setActiveGraphic( embed::getIconPixmap( "led_yellow" ) );
	setInactiveGraphic( embed::getIconPixmap( "led_off" ), false );
}

PixmapButton::~PixmapButton()
{
}

void PixmapButton::paintEvent( QPaintEvent * )
{
	QPainter p( this );

	if( ( model() != NULL && model()->value() ) || m_pressed )
	{
		if( !m_activePixmap.isNull() )
		{
			p.drawPixmap( 0, 0, m_activePixmap );
		}
	}
	else if( !m_inactivePixmap.isNull() )
	{
		p.drawPixmap( 0, 0, m_inactivePixmap );
	}
}

void PixmapButton::mousePressEvent( QMouseEvent * _me )
{
	// Show pressing graphics if this isn't checkable
	if( !isCheckable() )
	{
		m_pressed = true;
		update();
	}

	AutomatableButton::mousePressEvent( _me );
}

void PixmapButton::mouseReleaseEvent( QMouseEvent * _me )
{
	AutomatableButton::mouseReleaseEvent( _me );

	if( !isCheckable() )
	{
		m_pressed = false;
		update();
	}
}

void PixmapButton::mouseDoubleClickEvent( QMouseEvent * _me )
{
	emit doubleClicked();
	_me->accept();
}

void PixmapButton::setActiveGraphic( const QPixmap & _pm )
{
	m_activePixmap = _pm;
	resize( m_activePixmap.width(), m_activePixmap.height() );
}

void PixmapButton::setInactiveGraphic( const QPixmap & _pm, bool _update )
{
	m_inactivePixmap = _pm;
	if( _update )
	{
		update();
	}
}

QSize PixmapButton::sizeHint() const
{
	if( ( model() != NULL && model()->value() ) || m_pressed )
	{
		return m_activePixmap.size() / devicePixelRatio();
	}
	else
	{
		return m_inactivePixmap.size() / devicePixelRatio();
	}
}


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

#include <QPainter>

#include "GuiApplication.h"
#include "Song.h"

PositionLine::PositionLine(QWidget* parent) :
	QWidget(parent),
	m_hasTailGradient(false),
	m_lineColor(0, 0, 0, 0)
{
	resize(8, height());

	setAttribute(Qt::WA_NoSystemBackground, true);
	setAttribute(Qt::WA_TransparentForMouseEvents);
}

void PositionLine::paintEvent(QPaintEvent* pe)
{
	QPainter p(this);
	QColor c = QColor(m_lineColor);

	// If width is 1, we don't need a gradient
	if (width() == 1)
	{
		c.setAlpha(153);
		p.fillRect(rect(), c);
	}
	// If width > 1, we need the gradient
	else
	{
		// Create the gradient trail behind the line
		QLinearGradient gradient(rect().bottomLeft(), rect().bottomRight());
		qreal w = (width() - 1.0) / width();

		// If gradient is enabled, we're in focus and we're playing, enable gradient
		if (m_hasTailGradient &&
			Engine::getSong()->isPlaying() &&
			(Engine::getSong()->playMode() == Song::Mode_PlaySong ||
			 Engine::getSong()->playMode() == Song::Mode_PlayPattern))
		{
			c.setAlpha(60);
			gradient.setColorAt(w, c);
		}
		else
		{
			c.setAlpha(0);
			gradient.setColorAt(w, c);
		}

		// Fill in the remaining parts
		c.setAlpha(0);
		gradient.setColorAt(0, c);
		c.setAlpha(153);
		gradient.setColorAt(1, c);

		// Fill line
		p.fillRect(rect(), gradient);
	}
}

// NOTE: the move() implementation fixes a bug where the position line would appear
// in an unexpected location when positioned at the start of the track
void PositionLine::zoomChange(double zoom)
{
	int playHeadPos = x() + width() - 1;

	resize(8.0 * zoom, height());
	move(playHeadPos - width() + 1, y());

	update();
}


code:
/*
 * ProjectNotes.cpp - implementation of project-notes-editor
 *
 * Copyright (c) 2005-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 "ProjectNotes.h"

#include <QAction>
#include <QApplication>
#include <QColorDialog>
#include <QComboBox>
#include <QFontDatabase>
#include <QLineEdit>
#include <QMdiArea>
#include <QTextEdit>
#include <QToolBar>
#include <QDomCDATASection>

#include "embed.h"
#include "Engine.h"
#include "GuiApplication.h"
#include "MainWindow.h"
#include "Song.h"

ProjectNotes::ProjectNotes() :
	QMainWindow( gui->mainWindow()->workspace() )
{
	m_edit = new QTextEdit( this );
	m_edit->setAutoFillBackground( true );
	QPalette pal;
	pal.setColor( m_edit->backgroundRole(), QColor( 64, 64, 64 ) );
	m_edit->setPalette( pal );
	m_edit->show();

	clear();

	connect( m_edit,
		SIGNAL( currentCharFormatChanged( const QTextCharFormat & ) ),
		this, SLOT( formatChanged( const QTextCharFormat & ) ) );
//	connect( m_edit, SIGNAL( currentAlignmentChanged( int ) ),
//			this, SLOT( alignmentChanged( int ) ) );
	connect( m_edit, SIGNAL( textChanged() ),
			Engine::getSong(), SLOT( setModified() ) );

	setupActions();

	setCentralWidget( m_edit );
	setWindowTitle( tr( "Project Notes" ) );
	setWindowIcon( embed::getIconPixmap( "project_notes" ) );

	gui->mainWindow()->addWindowedWidget( this );
	parentWidget()->setAttribute( Qt::WA_DeleteOnClose, false );
	parentWidget()->move( 700, 10 );
	parentWidget()->resize( 400, 300 );
	parentWidget()->hide();
}

ProjectNotes::~ProjectNotes()
{
}

void ProjectNotes::clear()
{
	m_edit->setHtml( tr( "Enter project notes here" ) );
	m_edit->selectAll();
	m_edit->setTextColor( QColor( 224, 224, 224 ) );
	QTextCursor cursor = m_edit->textCursor();
	cursor.clearSelection();
	m_edit->setTextCursor( cursor );
}

void ProjectNotes::setText( const QString & _text )
{
	m_edit->setHtml( _text );
}

void ProjectNotes::setupActions()
{
	QToolBar * tb = addToolBar( tr( "Edit Actions" ) );
	QAction * a;

	a = new QAction( embed::getIconPixmap( "edit_undo" ), tr( "&Undo" ),
									this );
	a->setShortcut( tr( "%1+Z" ).arg(UI_CTRL_KEY) );
	connect( a, SIGNAL( triggered() ), m_edit, SLOT( undo() ) );
	tb->addAction( a );

	a = new QAction( embed::getIconPixmap( "edit_redo" ), tr( "&Redo" ),
									this );
	a->setShortcut( tr( "%1+Y" ).arg(UI_CTRL_KEY) );
	connect( a, SIGNAL( triggered() ), m_edit, SLOT( redo() ) );
	tb->addAction( a );

	a = new QAction( embed::getIconPixmap( "edit_copy" ), tr( "&Copy" ),
									this );
	a->setShortcut( tr( "%1+C" ).arg(UI_CTRL_KEY) );
	connect( a, SIGNAL( triggered() ), m_edit, SLOT( copy() ) );
	tb->addAction( a );

	a = new QAction( embed::getIconPixmap( "edit_cut" ), tr( "Cu&t" ),
									this );
	a->setShortcut( tr( "%1+X" ).arg(UI_CTRL_KEY) );
	connect( a, SIGNAL( triggered() ), m_edit, SLOT( cut() ) );
	tb->addAction( a );

	a = new QAction( embed::getIconPixmap( "edit_paste" ), tr( "&Paste" ),
									this );
	a->setShortcut( tr( "%1+V" ).arg(UI_CTRL_KEY) );
	connect( a, SIGNAL( triggered() ), m_edit, SLOT( paste() ) );
	tb->addAction( a );

	tb = addToolBar( tr( "Format Actions" ) );

	m_comboFont = new QComboBox( tb );
	m_comboFont->setEditable( true );
	QFontDatabase db;
	m_comboFont->addItems( db.families() );
	connect( m_comboFont, SIGNAL( activated( const QString & ) ),
			m_edit, SLOT( setFontFamily( const QString & ) ) );
	m_comboFont->lineEdit()->setText( QApplication::font().family() );

	m_comboSize = new QComboBox( tb );
	m_comboSize->setEditable( true );
	QList<int> sizes = db.standardSizes();
	QList<int>::Iterator it = sizes.begin();
	for ( ; it != sizes.end(); ++it )
	{
		m_comboSize->addItem( QString::number( *it ) );
	}
	connect( m_comboSize, SIGNAL( activated( const QString & ) ),
		     this, SLOT( textSize( const QString & ) ) );
	m_comboSize->lineEdit()->setText( QString::number(
					QApplication::font().pointSize() ) );

	m_actionTextBold = new QAction( embed::getIconPixmap( "text_bold" ),
							tr( "&Bold" ), this );
	m_actionTextBold->setShortcut( tr( "%1+B" ).arg(UI_CTRL_KEY) );
	m_actionTextBold->setCheckable( true );
	connect( m_actionTextBold, SIGNAL( triggered() ), this,
							SLOT( textBold() ) );

	m_actionTextItalic = new QAction( embed::getIconPixmap( "text_italic" ),
							tr( "&Italic" ), this );
	m_actionTextItalic->setShortcut( tr( "%1+I" ).arg(UI_CTRL_KEY) );
	m_actionTextItalic->setCheckable( true );
	connect( m_actionTextItalic, SIGNAL( triggered() ), this,
							SLOT( textItalic() ) );

	m_actionTextUnderline = new QAction( embed::getIconPixmap(
								"text_under" ),
						tr( "&Underline" ), this );
	m_actionTextUnderline->setShortcut( tr( "%1+U" ).arg(UI_CTRL_KEY) );
	m_actionTextUnderline->setCheckable( true );
	connect( m_actionTextUnderline, SIGNAL( triggered() ), this,
						SLOT( textUnderline() ) );

	QActionGroup * grp = new QActionGroup( tb );
	connect( grp, SIGNAL( triggered( QAction * ) ), this,
					SLOT( textAlign( QAction * ) ) );

	m_actionAlignLeft = new QAction( embed::getIconPixmap( "text_left" ),
						tr( "&Left" ), m_edit );
	m_actionAlignLeft->setShortcut( tr( "%1+L" ).arg(UI_CTRL_KEY) );
	m_actionAlignLeft->setCheckable( true );
	grp->addAction( m_actionAlignLeft );

	m_actionAlignCenter = new QAction( embed::getIconPixmap(
								"text_center" ),
						tr( "C&enter" ), m_edit );
	m_actionAlignCenter->setShortcutContext( Qt::WidgetShortcut );
	m_actionAlignCenter->setShortcut( tr( "%1+E" ).arg(UI_CTRL_KEY) );
	m_actionAlignCenter->setCheckable( true );
	grp->addAction( m_actionAlignCenter );

	m_actionAlignRight = new QAction( embed::getIconPixmap( "text_right" ),
						tr( "&Right" ), m_edit );
	m_actionAlignRight->setShortcutContext( Qt::WidgetShortcut );
	m_actionAlignRight->setShortcut( tr( "%1+R" ).arg(UI_CTRL_KEY) );
	m_actionAlignRight->setCheckable( true );
	grp->addAction( m_actionAlignRight );

	m_actionAlignJustify = new QAction( embed::getIconPixmap(
								"text_block" ),
						tr( "&Justify" ), m_edit );
	m_actionAlignJustify->setShortcut( tr( "%1+J" ).arg(UI_CTRL_KEY) );
	m_actionAlignJustify->setCheckable( true );
	grp->addAction( m_actionAlignJustify );

	QPixmap pix( 16, 16 );
	pix.fill( Qt::black );
	m_actionTextColor = new QAction( pix, tr( "&Color..." ), this );
	connect( m_actionTextColor, SIGNAL( triggered() ), this,
							SLOT( textColor() ) );

	tb->addWidget( m_comboFont );
	tb->addWidget( m_comboSize );
	tb->addAction( m_actionTextBold );
	tb->addAction( m_actionTextItalic );
	tb->addAction( m_actionTextUnderline );

	tb->addAction( m_actionAlignLeft );
	tb->addAction( m_actionAlignCenter );
	tb->addAction( m_actionAlignRight );
	tb->addAction( m_actionAlignJustify );

	tb->addAction( m_actionTextColor );
}

void ProjectNotes::textBold()
{
	m_edit->setFontWeight( m_actionTextBold->isChecked() ? QFont::Bold :
								QFont::Normal );
	Engine::getSong()->setModified();
}

void ProjectNotes::textUnderline()
{
	m_edit->setFontUnderline( m_actionTextUnderline->isChecked() );
	Engine::getSong()->setModified();
}

void ProjectNotes::textItalic()
{
	m_edit->setFontItalic( m_actionTextItalic->isChecked() );
	Engine::getSong()->setModified();
}

void ProjectNotes::textFamily( const QString & _f )
{
	m_edit->setFontFamily( _f );
	m_edit->viewport()->setFocus();
	Engine::getSong()->setModified();
}

void ProjectNotes::textSize( const QString & _p )
{
	m_edit->setFontPointSize( _p.toInt() );
	m_edit->viewport()->setFocus();
	Engine::getSong()->setModified();
}

void ProjectNotes::textColor()
{
	QColor col = QColorDialog::getColor( m_edit->textColor(), this );
	if ( !col.isValid() )
	{
		return;
	}
	m_edit->setTextColor( col );
	QPixmap pix( 16, 16 );
	pix.fill( Qt::black );
	m_actionTextColor->setIcon( pix );
}

void ProjectNotes::textAlign( QAction * _a )
{
	if( _a == m_actionAlignLeft )
	{
		m_edit->setAlignment( Qt::AlignLeft );
	}
	else if( _a == m_actionAlignCenter )
	{
		m_edit->setAlignment( Qt::AlignHCenter );
	}
	else if( _a == m_actionAlignRight )
	{
		m_edit->setAlignment( Qt::AlignRight );
	}
	else if( _a == m_actionAlignJustify )
	{
		m_edit->setAlignment( Qt::AlignJustify );
	}
}

void ProjectNotes::formatChanged( const QTextCharFormat & _f )
{
	QFont font = _f.font();
	m_comboFont->lineEdit()->setText( font.family() );
	m_comboSize->lineEdit()->setText( QString::number( font.pointSize() ) );
	m_actionTextBold->setChecked( font.bold() );
	m_actionTextItalic->setChecked( font.italic() );
	m_actionTextUnderline->setChecked( font.underline() );

	QPixmap pix( 16, 16 );
	pix.fill( _f.foreground().color() );
	m_actionTextColor->setIcon( pix );

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

void ProjectNotes::alignmentChanged( int _a )
{
	if ( _a & Qt::AlignLeft )
	{
		m_actionAlignLeft->setChecked( true );
	}
	else if ( ( _a & Qt::AlignHCenter ) )
	{
		m_actionAlignCenter->setChecked( true );
	}
	else if ( ( _a & Qt::AlignRight ) )
	{
		m_actionAlignRight->setChecked( true );
	}
	else if ( ( _a & Qt::AlignJustify ) )
	{
		m_actionAlignJustify->setChecked( true );
	}
	Engine::getSong()->setModified();
}

void ProjectNotes::saveSettings( QDomDocument & _doc, QDomElement & _this )
{
	MainWindow::saveWidgetState( this, _this );

	QDomCDATASection ds = _doc.createCDATASection( m_edit->toHtml() );
	_this.appendChild( ds );
}

void ProjectNotes::loadSettings( const QDomElement & _this )
{
	MainWindow::restoreWidgetState( this, _this );
	m_edit->setHtml( _this.text() );
}

void ProjectNotes::closeEvent( QCloseEvent * _ce )
{
	if( parentWidget() )
	{
		parentWidget()->hide();
	}
	else
	{
		hide();
	}
	_ce->ignore();
 }


code:
/*
 * RenameDialog.cpp - implementation of dialog for renaming something
 *
 * 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 <QKeyEvent>
#include <QLineEdit>

#include "RenameDialog.h"

RenameDialog::RenameDialog( QString & _string ) :
	QDialog(),
	m_stringToEdit( _string ),
	m_originalString( _string )
{
	setWindowTitle( tr("Rename...") );
	setFixedHeight( 30 );
	m_stringLE = new QLineEdit( this );
	m_stringLE->setText( _string );
	m_stringLE->setGeometry ( 10, 5, 220, 20 );
	m_stringLE->selectAll();
	connect( m_stringLE, SIGNAL( textChanged( const QString & ) ), this,
				SLOT( textChanged( const QString & ) ) );
	connect( m_stringLE, SIGNAL( returnPressed() ), this,
							SLOT( accept() ) );
}

RenameDialog::~RenameDialog()
{
}

void RenameDialog::resizeEvent (QResizeEvent * event) {
	m_stringLE->setGeometry ( 10, 5, width() - 20, 20 );
}

void RenameDialog::keyPressEvent( QKeyEvent * _ke )
{
	if( _ke->key() == Qt::Key_Escape )
	{
		m_stringLE->setText( m_originalString );
		accept();
	}
}

void RenameDialog::textChanged( const QString & _new_string )
{
	m_stringToEdit = _new_string;
}


code:
/*
 * Rubberband.cpp - rubberband - either own implementation for Qt3 or wrapper
 *                               for Qt4
 *
 * Copyright (c) 2006-2011 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 "Rubberband.h"

RubberBand::RubberBand( QWidget * _parent ) :
	QRubberBand( Rectangle, _parent )
{
}

RubberBand::~RubberBand()
{
}

QVector<selectableObject *> RubberBand::selectedObjects() const
{
	QVector<selectableObject *> so = selectableObjects();
	for( QVector<selectableObject *>::iterator it = so.begin();
							it != so.end(); )
	{
		if( ( *it )->isSelected() == false )
		{
			it = so.erase( it );
		}
		else
		{
			++it;
		}
	}
	return( so );
}

void RubberBand::resizeEvent( QResizeEvent * _re )
{
	QRubberBand::resizeEvent( _re );
}

QVector<selectableObject *> RubberBand::selectableObjects() const
{
	QVector<selectableObject *> so;
	if( parentWidget() == NULL )
	{
		return( so );
	}

	QList<selectableObject *> l =
			parentWidget()->findChildren<selectableObject *>();
	for( QList<selectableObject *>::iterator it = l.begin(); it != l.end();
									++it )
	{
		so.push_back( *it );
	}
	return( so );
}


code:
#include "SendButtonIndicator.h"

#include "FxMixer.h"

QPixmap * SendButtonIndicator::s_qpmOff = NULL;
QPixmap * SendButtonIndicator::s_qpmOn = NULL;

SendButtonIndicator:: SendButtonIndicator( QWidget * _parent, FxLine * _owner,
										   FxMixerView * _mv) :
	QLabel( _parent ),
	m_parent( _owner ),
	m_mv( _mv )
{
	if( ! s_qpmOff )
	{
		s_qpmOff = new QPixmap( embed::getIconPixmap( "mixer_send_off", 29, 20 ) );
	}

	if( ! s_qpmOn )
	{
		s_qpmOn = new QPixmap( embed::getIconPixmap( "mixer_send_on", 29, 20 ) );
	}

	// don't do any initializing yet, because the FxMixerView and FxLine
	// that were passed to this constructor are not done with their constructors
	// yet.
	setPixmap( *s_qpmOff );
}

void SendButtonIndicator::mousePressEvent( QMouseEvent * e )
{
	FxMixer * mix = Engine::fxMixer();
	int from = m_mv->currentFxLine()->channelIndex();
	int to = m_parent->channelIndex();
	FloatModel * sendModel = mix->channelSendModel(from, to);
	if( sendModel == NULL )
	{
		// not sending. create a mixer send.
		mix->createChannelSend( from, to );
	}
	else
	{
		// sending. delete the mixer send.
		mix->deleteChannelSend( from, to );
	}

	m_mv->updateFxLine(m_parent->channelIndex());
	updateLightStatus();
}

FloatModel * SendButtonIndicator::getSendModel()
{
	FxMixer * mix = Engine::fxMixer();
	return mix->channelSendModel(
		m_mv->currentFxLine()->channelIndex(), m_parent->channelIndex());
}

void SendButtonIndicator::updateLightStatus()
{
	setPixmap( getSendModel() == NULL ? *s_qpmOff : *s_qpmOn );
}


code:
/*
 * SideBar.cpp - side-bar in LMMS' MainWindow
 *
 * 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 <QStyleOptionToolButton>
#include <QStylePainter>
#include <QToolButton>

#include "SideBar.h"
#include "SideBarWidget.h"
#include "ToolTip.h"

// internal helper class allowing to create QToolButtons with
// vertical orientation
class SideBarButton : public QToolButton
{
public:
	SideBarButton( Qt::Orientation _orientation, QWidget * _parent ) :
		QToolButton( _parent ),
		m_orientation( _orientation )
	{
	}

	virtual ~SideBarButton() = default;

	Qt::Orientation orientation() const
	{
		return m_orientation;
	}

	QSize sizeHint() const override
	{
		QSize s = QToolButton::sizeHint();
		s.setWidth( s.width() + 8 );
		if( orientation() == Qt::Horizontal )
		{
			return s;
		}
		return QSize( s.height(), s.width() );
	}

protected:
	void paintEvent( QPaintEvent * ) override
	{
		QStylePainter p( this );
		QStyleOptionToolButton opt;
		initStyleOption( &opt );
		if( orientation() == Qt::Vertical )
		{
			const QSize s = sizeHint();
			p.rotate( 270 );
			p.translate( -s.height(), 0 );
			opt.rect = QRect( 0, 0, s.height(), s.width() );
		}
		p.drawComplexControl( QStyle::CC_ToolButton, opt );
	}

private:
	Qt::Orientation m_orientation;

} ;

SideBar::SideBar( Qt::Orientation _orientation, QWidget * _parent ) :
	QToolBar( _parent ),
	m_btnGroup( this )
{
	setOrientation( _orientation );
	setIconSize( QSize( 16, 16 ) );

	m_btnGroup.setExclusive( false );
	connect( &m_btnGroup, SIGNAL( buttonClicked( QAbstractButton * ) ),
				this, SLOT( toggleButton( QAbstractButton * ) ) );
}

SideBar::~SideBar()
{
}

void SideBar::appendTab( SideBarWidget *widget )
{
	SideBarButton *button = new SideBarButton( orientation(), this );
	button->setText( " " + widget->title() );
	button->setIcon( widget->icon() );
	button->setLayoutDirection( Qt::RightToLeft );
	button->setCheckable( true );
	m_widgets[button] = widget;
	m_btnGroup.addButton( button );
	addWidget( button );

	widget->hide();
	widget->setMinimumWidth( 200 );

	ToolTip::add( button, widget->title() );

	connect(widget, &SideBarWidget::closeButtonClicked,
		[=]() { button->click(); });
}

void SideBar::toggleButton( QAbstractButton * button )
{
	QToolButton *toolButton = NULL;
	QWidget *activeWidget = NULL;

	for( auto it = m_widgets.begin(); it != m_widgets.end(); ++it )
	{
		QToolButton *curBtn = it.key();
		QWidget *curWidget = it.value();

		if( curBtn == button )
		{
			toolButton = curBtn;
			activeWidget = curWidget;
		}
		else
		{
			curBtn->setChecked( false );
			curBtn->setToolButtonStyle( Qt::ToolButtonIconOnly );
		}

		if( curWidget )
		{
			curWidget->hide();
		}
	}

	if( toolButton && activeWidget )
	{
		activeWidget->setVisible( button->isChecked() );
		toolButton->setToolButtonStyle( button->isChecked() ?
				Qt::ToolButtonTextBesideIcon : Qt::ToolButtonIconOnly );
	}
}


code:
/*
 * SideBarWidget.cpp - implementation of base-widget for side-bar
 *
 * Copyright (c) 2004-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 "SideBarWidget.h"

#include <QApplication>
#include <QFontMetrics>
#include <QPainter>

#include "embed.h"

SideBarWidget::SideBarWidget( const QString & _title, const QPixmap & _icon,
							QWidget * _parent ) :
	QWidget( _parent ),
	m_title( _title ),
	m_icon(_icon),
	m_buttonSize(17, 17)
{
	m_contents = new QWidget( this );
	m_layout = new QVBoxLayout( m_contents );
	m_layout->setSpacing( 5 );
	m_layout->setMargin( 0 );
	m_closeBtn = new QPushButton(embed::getIconPixmap("close"), QString(), this);
	m_closeBtn->resize(m_buttonSize);
	m_closeBtn->setToolTip(tr("Close"));
	connect(m_closeBtn, &QPushButton::clicked,
		[=]() { this->closeButtonClicked(); });
}

SideBarWidget::~SideBarWidget()
{
}

void SideBarWidget::paintEvent( QPaintEvent * )
{
	QPainter p( this );
	p.fillRect( 0, 0, width(), 27, palette().highlight().color() );

	QFont f = p.font();
	f.setBold( true );
	f.setUnderline( true );
	f.setPointSize( f.pointSize() + 2 );
	p.setFont( f );

	p.setPen( palette().highlightedText().color() );

	const int tx = m_icon.width()+4;

	QFontMetrics metrics( f );
	const int ty = metrics.ascent();
	p.drawText( tx, ty, m_title );

	p.drawPixmap( 2, 2, m_icon.transformed( QTransform().rotate( -90 ) ) );
}

void SideBarWidget::resizeEvent( QResizeEvent * )
{
	const int MARGIN = 6;
	m_contents->setGeometry( MARGIN, 40 + MARGIN, width() - MARGIN * 2,
						height() - MARGIN * 2 - 40 );
	m_closeBtn->move(m_contents->geometry().width() - MARGIN - 5, 5);
}


code:
/*
 * StepRecoderWidget.cpp - widget that provide gui markers for step recording
 *
 * 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 "StepRecorderWidget.h"
#include "TextFloat.h"
#include "embed.h"

StepRecorderWidget::StepRecorderWidget(
		QWidget * parent,
		const int ppb,
		const int marginTop,
		const int marginBottom,
		const int marginLeft,
		const int marginRight) :
	QWidget(parent),
	m_marginTop(marginTop),
	m_marginBottom(marginBottom),
	m_marginLeft(marginLeft),
	m_marginRight(marginRight)
{
	const QColor baseColor =  QColor(255, 0, 0);// QColor(204, 163, 0); // Orange
	m_colorLineEnd   = baseColor.lighter(150);
	m_colorLineStart = baseColor.darker(120);

	setAttribute(Qt::WA_NoSystemBackground, true);
	setPixelsPerBar(ppb);

	m_top = m_marginTop;
	m_left = m_marginLeft;
}

void StepRecorderWidget::setPixelsPerBar(int ppb)
{
	m_ppb = ppb;
}

void StepRecorderWidget::setCurrentPosition(TimePos currentPosition)
{
	m_currentPosition = currentPosition;
}

void StepRecorderWidget::setMargins(const QMargins &qm)
{
	m_left = qm.left();
	m_right = qm.right();
	m_top = qm.top();
	m_bottom = qm.bottom();
}

QMargins StepRecorderWidget::margins()
{
	return QMargins(m_left, m_top, m_right, m_bottom);
}

void StepRecorderWidget::setBottomMargin(const int marginBottom)
{
	m_marginBottom = marginBottom;
}

void StepRecorderWidget::setStartPosition(TimePos pos)
{
	m_curStepStartPos = pos;
}

void StepRecorderWidget::setEndPosition(TimePos pos)
{
	m_curStepEndPos = pos;
	emit positionChanged(m_curStepEndPos);
}

void StepRecorderWidget::showHint()
{
	TextFloat::displayMessage(tr( "Hint" ), tr("Move recording curser using <Left/Right> arrows"),
		embed::getIconPixmap("hint"));
}

void StepRecorderWidget::setStepsLength(TimePos stepsLength)
{
	m_stepsLength = stepsLength;
}

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

	updateBoundaries();

	move(0, 0);

	//draw steps ruler
	painter.setPen(m_colorLineEnd);

	TimePos curPos = m_curStepEndPos;
	int x = xCoordOfTick(curPos);
	while(x <= m_right)
	{
		const int w = 2;
		const int h = 4;
		painter.drawRect(x - 1, m_top, w, h);
		curPos += m_stepsLength;
		x = xCoordOfTick(curPos);
	}

	//draw current step start/end position lines
	if(m_curStepStartPos != m_curStepEndPos)
	{
		drawVerLine(&painter, m_curStepStartPos, m_colorLineStart, m_top, m_bottom);
	}

	drawVerLine(&painter, m_curStepEndPos, m_colorLineEnd, m_top, m_bottom);

	//if the line is adjacent to the keyboard at the left - it cannot be seen.
	//add another line to make it clearer
	if(m_curStepEndPos == 0)
	{
		drawVerLine(&painter, xCoordOfTick(m_curStepEndPos) + 1, m_colorLineEnd, m_top, m_bottom);
	}
}

int StepRecorderWidget::xCoordOfTick(int tick)
{
	return m_marginLeft + ((tick - m_currentPosition) * m_ppb / TimePos::ticksPerBar());
}

void StepRecorderWidget::drawVerLine(QPainter* painter, int x, const QColor& color, int top, int bottom)
{
	if(x >= m_marginLeft && x <= (width() - m_marginRight))
	{
		painter->setPen(color);
		painter->drawLine( x, top, x, bottom );
	}
}

void StepRecorderWidget::drawVerLine(QPainter* painter, const TimePos& pos, const QColor& color, int top, int bottom)
{
	drawVerLine(painter, xCoordOfTick(pos), color, top, bottom);
}

void StepRecorderWidget::updateBoundaries()
{
	setFixedSize(parentWidget()->size());

	m_bottom = height() - m_marginBottom;
	m_right = width() - m_marginTop;

	//(no need to change top and left as they are static)
}


code:
/*
 * TabBar.cpp - implementation of tab-bar
 *
 * 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 "TabBar.h"
#include "TabButton.h"
#include "gui_templates.h"
#include "ToolTip.h"

TabBar::TabBar( QWidget * _parent, QBoxLayout::Direction _dir ) :
	QWidget( _parent ),
	m_layout( new QBoxLayout( _dir, this ) ),
	m_exclusive( false )
{
	m_layout->setMargin( 8 );
	m_layout->setSpacing( 0 );

	setLayout( m_layout );
}

TabButton * TabBar::addTab( QWidget * _w, const QString & _text, int _id,
				bool _add_stretch, bool _text_is_tooltip )
{
	// already tab with id?
	if( m_tabs.contains( _id ) )
	{
		// then remove it
		removeTab( _id );
	}
	QString caption = ( _text_is_tooltip ) ? QString( "" ) : _text;
	// create tab-button
	TabButton * b = new TabButton( caption, _id, this );
	connect( b, SIGNAL( clicked( int ) ), this, SLOT( tabClicked( int ) ) );
	b->setIconSize( QSize( 48, 48 ) );
	b->setFixedSize( 64, 64 );
	b->show();
	if( _text_is_tooltip )
	{
		ToolTip::add( b, _text );
	}

	// small workaround, because QBoxLayout::addWidget(...) doesn't
	// work properly, so we first have to remove all tabs from the
	// layout and them add them in the correct order
	QMap<int, QPair<TabButton *, QWidget *> >::iterator it;
	for( it = m_tabs.begin(); it != m_tabs.end(); ++it )
	{
		m_layout->removeWidget( it.value().first );
	}
	m_tabs.insert( _id, qMakePair( b, _w ) );
	for( it = m_tabs.begin(); it != m_tabs.end(); ++it )
	{
		m_layout->addWidget( it.value().first );
	}

	if( _add_stretch )
	{
		m_layout->addStretch();
	}

	// we assume, parent-widget is a widget acting as widget-stack so all
	// widgets have the same size and only the one on the top is visible
	_w->setFixedSize( _w->parentWidget()->size() );

	b->setFont( pointSize<8>( b->font() ) );

	return( b );
}

void TabBar::removeTab( int _id )
{
	// find tab-button and delete it
	if( m_tabs.find( _id ) != m_tabs.end() )
	{
		delete m_tabs[_id].first;
		m_tabs.erase( m_tabs.find( _id ) );
	}
}

void TabBar::setActiveTab( int _id )
{
	setTabState( _id, true );
	hideAll( _id );
	if( allHidden() )
	{
		emit allWidgetsHidden();
	}
	else
	{
		emit widgetShown();
	}
}

int TabBar::activeTab()
{
	QMap<int, QPair<TabButton *, QWidget *> >::iterator it;
	for( it = m_tabs.begin(); it != m_tabs.end(); ++it )
	{
		if( tabState( it.key() ) == true )
		{
			return( it.key() );
		}
	}
	return( -1 );
}

bool TabBar::tabState( int _id )
{
	if( m_tabs.find( _id ) == m_tabs.end() )
	{
		return( false );
	}
	return( m_tabs[_id].first->isChecked() );
}

void TabBar::setTabState( int _id, bool _checked )
{
	if( m_tabs.find( _id ) != m_tabs.end() )
	{
		m_tabs[_id].first->setChecked( _checked );
	}
}

void TabBar::hideAll( int _exception )
{
	QMap<int, QPair<TabButton *, QWidget *> >::iterator it;
	for( it = m_tabs.begin(); it != m_tabs.end(); ++it )
	{
		if( it.key() != _exception )
		{
			setTabState( it.key(), false );
		}
		it.value().second->hide();
	}
	if( m_tabs.find( _exception ) != m_tabs.end() )
	{
		if( tabState( _exception ) )
		{
			m_tabs[_exception].second->show();
		}
		else
		{
			m_tabs[_exception].second->hide();
		}
	}
}

void TabBar::tabClicked( int _id )
{
	if( m_exclusive == true && activeTab() == -1 )
	{
		setActiveTab( _id );
	}
	else
	{
		bool all_hidden_before = allHidden();
		// disable tabbar-buttons except the one clicked
		hideAll( _id );
		bool now_hidden = allHidden();
		if( all_hidden_before == true && now_hidden == false )
		{
			emit widgetShown();
		}
		else if( all_hidden_before == false && now_hidden == true )
		{
			emit allWidgetsHidden();
		}
	}
}

bool TabBar::allHidden()
{
	QMap<int, QPair<TabButton *, QWidget *> >::iterator it;
	for( it = m_tabs.begin(); it != m_tabs.end(); ++it )
	{
		if( !it.value().second->isHidden() )
		{
			return( false );
		}
	}
	return( true );
}


code:
/*
 * TabWidget.cpp - tabwidget for LMMS
 *
 * Copyright (c) 2005-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 "TabWidget.h"

#include <QMouseEvent>
#include <QPainter>
#include <QPixmap>
#include <QToolTip>
#include <QWheelEvent>

#include "DeprecationHelper.h"
#include "embed.h"
#include "gui_templates.h"

TabWidget::TabWidget(const QString & caption, QWidget * parent, bool usePixmap,
					 bool resizable) :
	QWidget( parent ),
	m_resizable( resizable ),
	m_activeTab( 0 ),
	m_caption( caption ),
	m_usePixmap( usePixmap ),
	m_tabText( 0, 0, 0 ),
	m_tabTitleText( 0, 0, 0 ),
	m_tabSelected( 0, 0, 0 ),
	m_tabBackground( 0, 0, 0 ),
	m_tabBorder( 0, 0, 0 )
{

	// Create taller tabbar when it's to display artwork tabs
	m_tabbarHeight = usePixmap ? GRAPHIC_TAB_HEIGHT : TEXT_TAB_HEIGHT;

	m_tabheight = caption.isEmpty() ? m_tabbarHeight - 3 : m_tabbarHeight - 4;

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

	setAutoFillBackground( true );
	QColor bg_color = QApplication::palette().color( QPalette::Active, QPalette::Background ). darker( 132 );
	QPalette pal = palette();
	pal.setColor( QPalette::Background, bg_color );
	setPalette( pal );

}

void TabWidget::addTab( QWidget * w, const QString & name, const char *pixmap, int idx )
{
	setFont( pointSize<8>( font() ) );

	// Append tab when position is not given
	if( idx < 0/* || m_widgets.contains( idx ) == true*/ )
	{
		while( m_widgets.contains( ++idx ) == true )
		{
		}
	}

	// Tab's width when it is a text tab. This isn't correct for artwork tabs, but it's fixed later during the PaintEvent
	int tab_width = horizontalAdvance(fontMetrics(), name) + 10;

	// Register new tab
	widgetDesc d = { w, pixmap, name, tab_width };
	m_widgets[idx] = d;

	// Position tab's window
	if (!m_resizable)
	{
		w->setFixedSize( width() - 4, height() - m_tabbarHeight );
	}
	w->move( 2, m_tabbarHeight - 1 );
	w->hide();

	// Show tab's window if it's active
	if( m_widgets.contains( m_activeTab ) )
	{
		// make sure new tab doesn't overlap current widget
		m_widgets[m_activeTab].w->show();
		m_widgets[m_activeTab].w->raise();
	}
}

void TabWidget::setActiveTab( int idx )
{
	if( m_widgets.contains( idx ) )
	{
		int old_active = m_activeTab;
		m_activeTab = idx;
		m_widgets[m_activeTab].w->raise();
		m_widgets[m_activeTab].w->show();
		if( old_active != idx && m_widgets.contains( old_active ) )
		{
			m_widgets[old_active].w->hide();
		}
		update();
	}
}

// Return the index of the tab at position "pos"
int TabWidget::findTabAtPos( const QPoint *pos )
{

	if( pos->y() > 1 && pos->y() < m_tabbarHeight - 1 )
	{
		int cx = ((m_caption == "") ? 4 : 14) + horizontalAdvance(fontMetrics(), m_caption);

		for( widgetStack::iterator it = m_widgets.begin(); it != m_widgets.end(); ++it )
		{
			if( pos->x() >= cx && pos->x() <= cx + ( *it ).nwidth )
			{
				return( it.key() );
			}
			cx += ( *it ).nwidth;
		}
	}

	// Haven't found any tab at position "pos"
	return( -1 );
}

// Overload the QWidget::event handler to display tooltips (from https://doc.qt.io/qt-4.8/qt-widgets-tooltips-example.html)
bool TabWidget::event(QEvent *event)
{

	if ( event->type() == QEvent::ToolTip )
	{
		QHelpEvent *helpEvent = static_cast<QHelpEvent *>(event);

		int idx = findTabAtPos( & helpEvent->pos() );

		if ( idx != -1 )
		{
			// Display tab's tooltip
			QToolTip::showText( helpEvent->globalPos(), m_widgets[idx].name );
		}
		else
		{
			// The tooltip event doesn't relate to any tab, let's ignore it
			QToolTip::hideText();
			event->ignore();
		}

		return true;
	}

	// not a Tooltip event, let's propagate it to the other event handlers
	return QWidget::event(event);
}

// Activate tab when clicked
void TabWidget::mousePressEvent( QMouseEvent * me )
{

	// Find index of tab that has been clicked
	QPoint pos = me->pos();
	int idx = findTabAtPos( &pos );

	// When found, activate tab that has been clicked
	if ( idx != -1 )
	{
		setActiveTab( idx );
		update();
		return;
	}
}

void TabWidget::resizeEvent( QResizeEvent * )
{
	if (!m_resizable)
	{
		for ( widgetStack::iterator it = m_widgets.begin();
							it != m_widgets.end(); ++it )
		{
			( *it ).w->setFixedSize( width() - 4, height() - m_tabbarHeight );
		}
	}
}

void TabWidget::paintEvent( QPaintEvent * pe )
{
	QPainter p( this );
	p.setFont( pointSize<7>( font() ) );

	// Draw background
	QBrush bg_color = p.background();
	p.fillRect( 0, 0, width() - 1, height() - 1, bg_color );

	// Draw external borders
	p.setPen( tabBorder() );
	p.drawRect( 0, 0, width() - 1, height() - 1 );

	// Draw tabs' bar background
	p.fillRect( 1, 1, width() - 2, m_tabheight + 2, tabBackground() );

	// Draw title, if any
	if( ! m_caption.isEmpty() )
	{
		p.setFont( pointSize<8>( p.font() ) );
		p.setPen( tabTitleText() );
		p.drawText( 5, 11, m_caption );
	}

	// Calculate the tabs' x (tabs are painted next to the caption)
	int tab_x_offset = m_caption.isEmpty() ? 4 : 14 + horizontalAdvance(fontMetrics(), m_caption);

	// Compute tabs' width depending on the number of tabs (only applicable for artwork tabs)
	widgetStack::iterator first = m_widgets.begin();
	widgetStack::iterator last = m_widgets.end();
	int tab_width = width();
	if ( first != last )
	{
		tab_width = ( width() - tab_x_offset ) / std::distance( first, last );
	}

	// Draw all tabs
	p.setPen( tabText() );
	for( widgetStack::iterator it = first ; it != last ; ++it )
	{
		// Draw a text tab or a artwork tab.
		if( m_usePixmap )
		{
			// Fixes tab's width, because original size is only correct for text tabs
			( *it ).nwidth = tab_width;

			// Get artwork
			QPixmap artwork( embed::getIconPixmap( ( *it ).pixmap ) );

			// Highlight active tab
			if( it.key() == m_activeTab )
			{
				p.fillRect( tab_x_offset, 0, ( *it ).nwidth, m_tabbarHeight - 1, tabSelected() );
			}

			// Draw artwork
			p.drawPixmap(tab_x_offset + ( ( *it ).nwidth - artwork.width() ) / 2, 1, artwork );
		}
		else
		{
			// Highlight tab when active
			if( it.key() == m_activeTab )
			{
				p.fillRect( tab_x_offset, 2, ( *it ).nwidth - 6, m_tabbarHeight - 4, tabSelected() );
			}

			// Draw text
			p.drawText( tab_x_offset + 3, m_tabheight + 1, ( *it ).name );
		}

		// Next tab's horizontal position
		tab_x_offset += ( *it ).nwidth;
	}
}

// Switch between tabs with mouse wheel
void TabWidget::wheelEvent( QWheelEvent * we )
{
	if(position(we).y() > m_tabheight)
	{
		return;
	}

	we->accept();
	int dir = (we->angleDelta().y() < 0) ? 1 : -1;
	int tab = m_activeTab;
	while( tab > -1 && static_cast<int>( tab ) < m_widgets.count() )
	{
		tab += dir;
		if( m_widgets.contains( tab ) )
		{
			break;
		}
	}
	setActiveTab( tab );
}

// Let parent widgets know how much space this tab widget needs
QSize TabWidget::minimumSizeHint() const
{
	if (m_resizable)
	{
		int maxWidth = 0, maxHeight = 0;
		for ( widgetStack::const_iterator it = m_widgets.begin();
							it != m_widgets.end(); ++it )
		{
			maxWidth = std::max(maxWidth, it->w->minimumSizeHint().width());
			maxHeight = std::max(maxHeight, it->w->minimumSizeHint().height());
		}
		// "-1" :
		// in "addTab", under "Position tab's window", the widget is
		// moved up by 1 pixel
		return QSize(maxWidth + 4, maxHeight + m_tabbarHeight - 1);
	}
	else { return QWidget::minimumSizeHint(); }
}

QSize TabWidget::sizeHint() const
{
	if (m_resizable)
	{
		int maxWidth = 0, maxHeight = 0;
		for ( widgetStack::const_iterator it = m_widgets.begin();
							it != m_widgets.end(); ++it )
		{
			maxWidth = std::max(maxWidth, it->w->sizeHint().width());
			maxHeight = std::max(maxHeight, it->w->sizeHint().height());
		}
		// "-1" :
		// in "addTab", under "Position tab's window", the widget is
		// moved up by 1 pixel
		return QSize(maxWidth + 4, maxHeight + m_tabbarHeight - 1);
	}
	else { return QWidget::sizeHint(); }
}

// Return the color to be used to draw a TabWidget's title text (if any)
QColor TabWidget::tabTitleText() const
{
	return m_tabTitleText;
}

// Set the color to be used to draw a TabWidget's title text (if any)
void TabWidget::setTabTitleText( const QColor & c )
{
	m_tabTitleText = c;
}

// Return the color to be used to draw a TabWidget's text (if any)
QColor TabWidget::tabText() const
{
	return m_tabText;
}

// Set the color to be used to draw a TabWidget's text (if any)
void TabWidget::setTabText( const QColor & c )
{
	m_tabText = c;
}

// Return the color to be used to highlight a TabWidget'selected tab (if any)
QColor TabWidget::tabSelected() const
{
	return m_tabSelected;
}

// Set the color to be used to highlight a TabWidget'selected tab (if any)
void TabWidget::setTabSelected( const QColor & c )
{
	m_tabSelected = c;
}

// Return the color to be used for the TabWidget's background
QColor TabWidget::tabBackground() const
{
	return m_tabBackground;
}

// Set the color to be used for the TabWidget's background
void TabWidget::setTabBackground( const QColor & c )
{
	m_tabBackground = c;
}

// Return the color to be used for the TabWidget's borders
QColor TabWidget::tabBorder() const
{
	return m_tabBorder;
}

// Set the color to be used for the TabWidget's borders
void TabWidget::setTabBorder( const QColor & c )
{
	m_tabBorder = c;
}