mirror of
https://github.com/LMMS/lmms.git
synced 2026-03-05 14:46:18 -05:00
446 lines
11 KiB
C++
446 lines
11 KiB
C++
/*
|
|
* ControllerConnectionDialog.cpp - dialog allowing the user to create and
|
|
* modify links between controllers and models
|
|
*
|
|
* Copyright (c) 2008 Paul Giblock <drfaygo/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 <QLayout>
|
|
#include <QLineEdit>
|
|
#include <QPushButton>
|
|
#include <QScrollArea>
|
|
#include <QMessageBox>
|
|
|
|
#include "ControllerConnectionDialog.h"
|
|
#include "ControllerConnection.h"
|
|
#include "MidiController.h"
|
|
#include "MidiClient.h"
|
|
#include "MidiPortMenu.h"
|
|
#include "Mixer.h"
|
|
#include "LcdSpinBox.h"
|
|
#include "LedCheckbox.h"
|
|
#include "ComboBox.h"
|
|
#include "GroupBox.h"
|
|
#include "Song.h"
|
|
#include "ToolButton.h"
|
|
|
|
#include "gui_templates.h"
|
|
#include "embed.h"
|
|
|
|
|
|
class AutoDetectMidiController : public MidiController
|
|
{
|
|
public:
|
|
AutoDetectMidiController( Model* parent ) :
|
|
MidiController( parent ),
|
|
m_detectedMidiChannel( 0 ),
|
|
m_detectedMidiController( 0 )
|
|
{
|
|
updateName();
|
|
}
|
|
|
|
|
|
virtual ~AutoDetectMidiController()
|
|
{
|
|
}
|
|
|
|
|
|
void processInEvent( const MidiEvent& event, const MidiTime& time, f_cnt_t offset = 0 ) override
|
|
{
|
|
if( event.type() == MidiControlChange &&
|
|
( m_midiPort.inputChannel() == 0 || m_midiPort.inputChannel() == event.channel() + 1 ) )
|
|
{
|
|
m_detectedMidiChannel = event.channel() + 1;
|
|
m_detectedMidiController = event.controllerNumber() + 1;
|
|
m_detectedMidiPort = Engine::mixer()->midiClient()->sourcePortName( event );
|
|
|
|
emit valueChanged();
|
|
}
|
|
}
|
|
|
|
|
|
// Would be a nice copy ctor, but too hard to add copy ctor because
|
|
// model has none.
|
|
MidiController* copyToMidiController( Model* parent )
|
|
{
|
|
MidiController* c = new MidiController( parent );
|
|
c->m_midiPort.setInputChannel( m_midiPort.inputChannel() );
|
|
c->m_midiPort.setInputController( m_midiPort.inputController() );
|
|
c->subscribeReadablePorts( m_midiPort.readablePorts() );
|
|
c->updateName();
|
|
|
|
return c;
|
|
}
|
|
|
|
|
|
void useDetected()
|
|
{
|
|
m_midiPort.setInputChannel( m_detectedMidiChannel );
|
|
m_midiPort.setInputController( m_detectedMidiController );
|
|
|
|
const MidiPort::Map& map = m_midiPort.readablePorts();
|
|
for( MidiPort::Map::ConstIterator it = map.begin(); it != map.end(); ++it )
|
|
{
|
|
m_midiPort.subscribeReadablePort( it.key(),
|
|
m_detectedMidiPort.isEmpty() || ( it.key() == m_detectedMidiPort ) );
|
|
}
|
|
}
|
|
|
|
|
|
public slots:
|
|
void reset()
|
|
{
|
|
m_midiPort.setInputChannel( 0 );
|
|
m_midiPort.setInputController( 0 );
|
|
}
|
|
|
|
|
|
private:
|
|
int m_detectedMidiChannel;
|
|
int m_detectedMidiController;
|
|
QString m_detectedMidiPort;
|
|
|
|
} ;
|
|
|
|
|
|
|
|
|
|
ControllerConnectionDialog::ControllerConnectionDialog( QWidget * _parent,
|
|
const AutomatableModel * _target_model ) :
|
|
QDialog( _parent ),
|
|
m_readablePorts( NULL ),
|
|
m_midiAutoDetect( false ),
|
|
m_controller( NULL ),
|
|
m_targetModel( _target_model ),
|
|
m_midiController( NULL )
|
|
{
|
|
setWindowIcon( embed::getIconPixmap( "setup_audio" ) );
|
|
setWindowTitle( tr( "Connection Settings" ) );
|
|
setModal( true );
|
|
|
|
// Midi stuff
|
|
m_midiGroupBox = new GroupBox( tr( "MIDI CONTROLLER" ), this );
|
|
m_midiGroupBox->setGeometry( 8, 10, 240, 80 );
|
|
connect( m_midiGroupBox->model(), SIGNAL( dataChanged() ),
|
|
this, SLOT( midiToggled() ) );
|
|
|
|
m_midiChannelSpinBox = new LcdSpinBox( 2, m_midiGroupBox,
|
|
tr( "Input channel" ) );
|
|
m_midiChannelSpinBox->addTextForValue( 0, "--" );
|
|
m_midiChannelSpinBox->setLabel( tr( "CHANNEL" ) );
|
|
m_midiChannelSpinBox->move( 8, 24 );
|
|
|
|
m_midiControllerSpinBox = new LcdSpinBox( 3, m_midiGroupBox,
|
|
tr( "Input controller" ) );
|
|
m_midiControllerSpinBox->addTextForValue( 0, "---" );
|
|
m_midiControllerSpinBox->setLabel( tr( "CONTROLLER" ) );
|
|
m_midiControllerSpinBox->move( 68, 24 );
|
|
|
|
|
|
m_midiAutoDetectCheckBox =
|
|
new LedCheckBox( tr("Auto Detect"),
|
|
m_midiGroupBox, tr("Auto Detect") );
|
|
m_midiAutoDetectCheckBox->setModel( &m_midiAutoDetect );
|
|
m_midiAutoDetectCheckBox->move( 8, 60 );
|
|
connect( &m_midiAutoDetect, SIGNAL( dataChanged() ),
|
|
this, SLOT( autoDetectToggled() ) );
|
|
|
|
// when using with non-raw-clients we can provide buttons showing
|
|
// our port-menus when being clicked
|
|
if( !Engine::mixer()->midiClient()->isRaw() )
|
|
{
|
|
m_readablePorts = new MidiPortMenu( MidiPort::Input );
|
|
connect( m_readablePorts, SIGNAL( triggered( QAction * ) ),
|
|
this, SLOT( enableAutoDetect( QAction * ) ) );
|
|
ToolButton * rp_btn = new ToolButton( m_midiGroupBox );
|
|
rp_btn->setText( tr( "MIDI-devices to receive "
|
|
"MIDI-events from" ) );
|
|
rp_btn->setIcon( embed::getIconPixmap( "piano" ) );
|
|
rp_btn->setGeometry( 160, 24, 32, 32 );
|
|
rp_btn->setMenu( m_readablePorts );
|
|
rp_btn->setPopupMode( QToolButton::InstantPopup );
|
|
}
|
|
|
|
|
|
// User stuff
|
|
m_userGroupBox = new GroupBox( tr( "USER CONTROLLER" ), this );
|
|
m_userGroupBox->setGeometry( 8, 100, 240, 60 );
|
|
connect( m_userGroupBox->model(), SIGNAL( dataChanged() ),
|
|
this, SLOT( userToggled() ) );
|
|
|
|
m_userController = new ComboBox( m_userGroupBox, "Controller" );
|
|
m_userController->setGeometry( 10, 24, 200, 22 );
|
|
for (Controller * c : Engine::getSong()->controllers())
|
|
{
|
|
m_userController->model()->addItem( c->name() );
|
|
}
|
|
connect( m_userController->model(), SIGNAL( dataUnchanged() ),
|
|
this, SLOT( userSelected() ) );
|
|
connect( m_userController->model(), SIGNAL( dataChanged() ),
|
|
this, SLOT( userSelected() ) );
|
|
|
|
|
|
// Mapping functions
|
|
m_mappingBox = new TabWidget( tr( "MAPPING FUNCTION" ), this );
|
|
m_mappingBox->setGeometry( 8, 170, 240, 64 );
|
|
m_mappingFunction = new QLineEdit( m_mappingBox );
|
|
m_mappingFunction->setGeometry( 10, 20, 170, 16 );
|
|
m_mappingFunction->setText( "input" );
|
|
m_mappingFunction->setReadOnly( true );
|
|
|
|
|
|
// Buttons
|
|
QWidget * buttons = new QWidget( this );
|
|
buttons->setGeometry( 8, 240, 240, 32 );
|
|
|
|
QHBoxLayout * btn_layout = new QHBoxLayout( buttons );
|
|
btn_layout->setSpacing( 0 );
|
|
btn_layout->setMargin( 0 );
|
|
|
|
QPushButton * select_btn = new QPushButton(
|
|
embed::getIconPixmap( "add" ),
|
|
tr( "OK" ), buttons );
|
|
connect( select_btn, SIGNAL( clicked() ),
|
|
this, SLOT( selectController() ) );
|
|
|
|
QPushButton * cancel_btn = new QPushButton(
|
|
embed::getIconPixmap( "cancel" ),
|
|
tr( "Cancel" ), buttons );
|
|
connect( cancel_btn, SIGNAL( clicked() ),
|
|
this, SLOT( reject() ) );
|
|
|
|
btn_layout->addStretch();
|
|
btn_layout->addSpacing( 10 );
|
|
btn_layout->addWidget( select_btn );
|
|
btn_layout->addSpacing( 10 );
|
|
btn_layout->addWidget( cancel_btn );
|
|
btn_layout->addSpacing( 10 );
|
|
|
|
setFixedSize( 256, 280 );
|
|
|
|
// Crazy MIDI View stuff
|
|
|
|
// TODO, handle by making this a model for the Dialog "view"
|
|
ControllerConnection * cc = NULL;
|
|
if( m_targetModel )
|
|
{
|
|
cc = m_targetModel->controllerConnection();
|
|
|
|
if( cc && cc->getController()->type() != Controller::DummyController && Engine::getSong() )
|
|
{
|
|
if ( cc->getController()->type() == Controller::MidiController )
|
|
{
|
|
m_midiGroupBox->model()->setValue( true );
|
|
// ensure controller is created
|
|
midiToggled();
|
|
|
|
MidiController * cont = (MidiController*)( cc->getController() );
|
|
m_midiChannelSpinBox->model()->setValue( cont->m_midiPort.inputChannel() );
|
|
m_midiControllerSpinBox->model()->setValue( cont->m_midiPort.inputController() );
|
|
|
|
m_midiController->subscribeReadablePorts( static_cast<MidiController*>( cc->getController() )->m_midiPort.readablePorts() );
|
|
}
|
|
else
|
|
{
|
|
int idx = Engine::getSong()->controllers().indexOf( cc->getController() );
|
|
|
|
if( idx >= 0 )
|
|
{
|
|
m_userGroupBox->model()->setValue( true );
|
|
m_userController->model()->setValue( idx );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if( !cc )
|
|
{
|
|
m_midiGroupBox->model()->setValue( true );
|
|
}
|
|
|
|
show();
|
|
}
|
|
|
|
|
|
|
|
|
|
ControllerConnectionDialog::~ControllerConnectionDialog()
|
|
{
|
|
delete m_readablePorts;
|
|
|
|
delete m_midiController;
|
|
}
|
|
|
|
|
|
|
|
|
|
void ControllerConnectionDialog::selectController()
|
|
{
|
|
// Midi
|
|
if( m_midiGroupBox->model()->value() > 0 )
|
|
{
|
|
if( m_midiControllerSpinBox->model()->value() > 0 )
|
|
{
|
|
MidiController * mc;
|
|
mc = m_midiController->copyToMidiController( Engine::getSong() );
|
|
|
|
/*
|
|
if( m_targetModel->getTrack() &&
|
|
!m_targetModel->getTrack()->displayName().isEmpty() )
|
|
{
|
|
mc->m_midiPort.setName( QString( "%1 (%2)" ).
|
|
arg( m_targetModel->getTrack()->displayName() ).
|
|
arg( m_targetModel->displayName() ) );
|
|
}
|
|
else
|
|
{
|
|
mc->m_midiPort.setName( m_targetModel->displayName() );
|
|
}
|
|
*/
|
|
mc->m_midiPort.setName( m_targetModel->fullDisplayName() );
|
|
m_controller = mc;
|
|
}
|
|
}
|
|
// User
|
|
else
|
|
{
|
|
if( m_userGroupBox->model()->value() > 0 &&
|
|
Engine::getSong()->controllers().size() )
|
|
{
|
|
m_controller = Engine::getSong()->controllers().at(
|
|
m_userController->model()->value() );
|
|
}
|
|
|
|
if( m_controller && m_controller->hasModel( m_targetModel ) )
|
|
{
|
|
QMessageBox::warning(this, tr("LMMS"), tr("Cycle Detected."));
|
|
return;
|
|
}
|
|
|
|
}
|
|
|
|
accept();
|
|
}
|
|
|
|
|
|
|
|
|
|
void ControllerConnectionDialog::midiToggled()
|
|
{
|
|
int enabled = m_midiGroupBox->model()->value();
|
|
if( enabled != 0 )
|
|
{
|
|
if( m_userGroupBox->model()->value() != 0 )
|
|
{
|
|
m_userGroupBox->model()->setValue( 0 );
|
|
}
|
|
|
|
if( !m_midiController )
|
|
{
|
|
m_midiController = new AutoDetectMidiController( Engine::getSong() );
|
|
|
|
MidiPort::Map map = m_midiController->m_midiPort.readablePorts();
|
|
for( MidiPort::Map::Iterator it = map.begin(); it != map.end(); ++it )
|
|
{
|
|
it.value() = true;
|
|
}
|
|
m_midiController->subscribeReadablePorts( map );
|
|
|
|
m_midiChannelSpinBox->setModel( &m_midiController->m_midiPort.m_inputChannelModel );
|
|
m_midiControllerSpinBox->setModel( &m_midiController->m_midiPort.m_inputControllerModel );
|
|
|
|
if( m_readablePorts )
|
|
{
|
|
m_readablePorts->setModel( &m_midiController->m_midiPort );
|
|
}
|
|
|
|
connect( m_midiController, SIGNAL( valueChanged() ), this, SLOT( midiValueChanged() ) );
|
|
}
|
|
}
|
|
m_midiAutoDetect.setValue( enabled );
|
|
|
|
m_midiChannelSpinBox->setEnabled( enabled );
|
|
m_midiControllerSpinBox->setEnabled( enabled );
|
|
m_midiAutoDetectCheckBox->setEnabled( enabled );
|
|
}
|
|
|
|
|
|
|
|
|
|
void ControllerConnectionDialog::userToggled()
|
|
{
|
|
int enabled = m_userGroupBox->model()->value();
|
|
if( enabled != 0 && m_midiGroupBox->model()->value() != 0 )
|
|
{
|
|
m_midiGroupBox->model()->setValue( 0 );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
void ControllerConnectionDialog::userSelected()
|
|
{
|
|
m_userGroupBox->model()->setValue( 1 );
|
|
userToggled();
|
|
}
|
|
|
|
|
|
|
|
|
|
void ControllerConnectionDialog::autoDetectToggled()
|
|
{
|
|
if( m_midiAutoDetect.value() )
|
|
{
|
|
m_midiController->reset();
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
void ControllerConnectionDialog::midiValueChanged()
|
|
{
|
|
if( m_midiAutoDetect.value() )
|
|
{
|
|
m_midiController->useDetected();
|
|
if( m_readablePorts )
|
|
{
|
|
m_readablePorts->updateMenu();
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
|
|
void ControllerConnectionDialog::enableAutoDetect( QAction * _a )
|
|
{
|
|
if( _a->isChecked() )
|
|
{
|
|
m_midiAutoDetectCheckBox->model()->setValue( true );
|
|
}
|
|
}
|
|
|
|
|
|
|
|
|
|
|