Merge branch 'stable-1.2'
# Conflicts: # src/gui/widgets/EffectView.cpp
This commit is contained in:
@@ -32,7 +32,7 @@ SET(PROJECT_COPYRIGHT "2008-${PROJECT_YEAR} ${PROJECT_AUTHOR}")
|
||||
SET(VERSION_MAJOR "1")
|
||||
SET(VERSION_MINOR "2")
|
||||
SET(VERSION_RELEASE "0")
|
||||
SET(VERSION_STAGE "rc5")
|
||||
SET(VERSION_STAGE "rc6")
|
||||
SET(VERSION_BUILD "0")
|
||||
SET(VERSION "${VERSION_MAJOR}.${VERSION_MINOR}.${VERSION_RELEASE}")
|
||||
IF(VERSION_STAGE)
|
||||
|
||||
@@ -41,10 +41,10 @@
|
||||
#include <QToolBar>
|
||||
#include <QLabel>
|
||||
|
||||
|
||||
VstEffectControlDialog::VstEffectControlDialog( VstEffectControls * _ctl ) :
|
||||
EffectControlDialog( _ctl ),
|
||||
m_pluginWidget( NULL ),
|
||||
|
||||
m_plugin( NULL ),
|
||||
tbLabel( NULL )
|
||||
{
|
||||
@@ -62,24 +62,18 @@ VstEffectControlDialog::VstEffectControlDialog( VstEffectControls * _ctl ) :
|
||||
embed_vst = m_plugin->embedMethod() != "none";
|
||||
|
||||
if (embed_vst) {
|
||||
m_plugin->createUI( nullptr, true );
|
||||
m_pluginWidget = m_plugin->pluginWidget( false );
|
||||
|
||||
#ifdef LMMS_BUILD_WIN32
|
||||
if( !m_pluginWidget )
|
||||
{
|
||||
m_pluginWidget = m_plugin->pluginWidget( false );
|
||||
if (m_plugin->hasEditor() && ! m_plugin->pluginWidget()) {
|
||||
m_plugin->createUI(this);
|
||||
}
|
||||
#endif
|
||||
|
||||
m_pluginWidget = m_plugin->pluginWidget();
|
||||
}
|
||||
}
|
||||
|
||||
if ( m_plugin && (!embed_vst || m_pluginWidget) )
|
||||
if (m_plugin)
|
||||
{
|
||||
setWindowTitle( m_plugin->name() );
|
||||
|
||||
QPushButton * btn = new QPushButton( tr( "Show/hide" ) );
|
||||
QPushButton * btn = new QPushButton( tr( "Show/hide" ));
|
||||
|
||||
if (embed_vst) {
|
||||
btn->setCheckable( true );
|
||||
@@ -87,14 +81,15 @@ VstEffectControlDialog::VstEffectControlDialog( VstEffectControls * _ctl ) :
|
||||
connect( btn, SIGNAL( toggled( bool ) ),
|
||||
SLOT( togglePluginUI( bool ) ) );
|
||||
} else {
|
||||
connect( btn, SIGNAL( clicked( bool ) ),
|
||||
SLOT( togglePluginUI( bool ) ) );
|
||||
connect( btn, SIGNAL( clicked() ),
|
||||
m_plugin.data(), SLOT( toggleUI() ) );
|
||||
}
|
||||
|
||||
btn->setMinimumWidth( 78 );
|
||||
btn->setMaximumWidth( 78 );
|
||||
btn->setMinimumHeight( 24 );
|
||||
btn->setMaximumHeight( 24 );
|
||||
m_togglePluginButton = btn;
|
||||
|
||||
m_managePluginButton = new PixmapButton( this, "" );
|
||||
m_managePluginButton->setCheckable( false );
|
||||
@@ -206,7 +201,7 @@ VstEffectControlDialog::VstEffectControlDialog( VstEffectControls * _ctl ) :
|
||||
|
||||
int newSize = 0;
|
||||
|
||||
if (embed_vst) {
|
||||
if (m_pluginWidget) {
|
||||
newSize = m_pluginWidget->width() + 20;
|
||||
}
|
||||
newSize = std::max(newSize, 250);
|
||||
@@ -222,7 +217,7 @@ VstEffectControlDialog::VstEffectControlDialog( VstEffectControls * _ctl ) :
|
||||
l->addItem( new QSpacerItem( newSize - 20, 30, QSizePolicy::Fixed,
|
||||
QSizePolicy::Fixed ), 1, 0 );
|
||||
l->addWidget( resize, 2, 0, 1, 1, Qt::AlignCenter );
|
||||
if (embed_vst) {
|
||||
if (m_pluginWidget) {
|
||||
l->addWidget( m_pluginWidget, 3, 0, 1, 1, Qt::AlignCenter );
|
||||
}
|
||||
l->setRowStretch( 5, 1 );
|
||||
@@ -261,12 +256,28 @@ void VstEffectControlDialog::paintEvent( QPaintEvent * )
|
||||
}
|
||||
}
|
||||
|
||||
void VstEffectControlDialog::showEvent(QShowEvent *_se)
|
||||
{
|
||||
EffectControlDialog::showEvent( _se );
|
||||
// Workaround for a (unexplained) bug where on project-load the effect
|
||||
// control window has size 0 and would only restore to the proper size upon
|
||||
// moving the window or interacting with it.
|
||||
if (parentWidget()) {
|
||||
parentWidget()->adjustSize();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
VstEffectControlDialog::~VstEffectControlDialog()
|
||||
{
|
||||
//delete m_pluginWidget;
|
||||
#if !(QT_VERSION < 0x050000 && defined(LMMS_BUILD_LINUX))
|
||||
if (m_pluginWidget && layout()) {
|
||||
layout()->removeWidget(m_pluginWidget);
|
||||
m_pluginWidget->setParent(nullptr);
|
||||
}
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
@@ -278,6 +289,14 @@ void VstEffectControlDialog::togglePluginUI( bool checked )
|
||||
return;
|
||||
}
|
||||
|
||||
m_plugin->toggleUI();
|
||||
if ( m_togglePluginButton->isChecked() != checked ) {
|
||||
m_togglePluginButton->setChecked( checked );
|
||||
}
|
||||
|
||||
if ( checked ) {
|
||||
m_plugin->showUI();
|
||||
} else {
|
||||
m_plugin->hideUI();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -50,10 +50,12 @@ public:
|
||||
|
||||
protected:
|
||||
virtual void paintEvent( QPaintEvent * _pe );
|
||||
virtual void showEvent( QShowEvent* _se ) override;
|
||||
|
||||
private:
|
||||
QWidget * m_pluginWidget;
|
||||
|
||||
QPushButton * m_togglePluginButton;
|
||||
PixmapButton * m_openPresetButton;
|
||||
PixmapButton * m_rolLPresetButton;
|
||||
PixmapButton * m_rolRPresetButton;
|
||||
@@ -64,7 +66,7 @@ private:
|
||||
|
||||
QLabel * tbLabel;
|
||||
|
||||
private slots:
|
||||
public slots:
|
||||
void togglePluginUI( bool checked );
|
||||
} ;
|
||||
|
||||
|
||||
@@ -40,7 +40,8 @@ VstEffectControls::VstEffectControls( VstEffect * _eff ) :
|
||||
m_subWindow( NULL ),
|
||||
knobFModel( NULL ),
|
||||
ctrHandle( NULL ),
|
||||
lastPosInMenu (0)
|
||||
lastPosInMenu (0),
|
||||
m_vstGuiVisible ( true )
|
||||
// m_presetLabel ( NULL )
|
||||
{
|
||||
}
|
||||
@@ -64,6 +65,8 @@ void VstEffectControls::loadSettings( const QDomElement & _this )
|
||||
m_effect->m_pluginMutex.lock();
|
||||
if( m_effect->m_plugin != NULL )
|
||||
{
|
||||
m_vstGuiVisible = _this.attribute( "guivisible" ).toInt();
|
||||
|
||||
m_effect->m_plugin->loadSettings( _this );
|
||||
|
||||
const QMap<QString, QString> & dump = m_effect->m_plugin->parameterDump();
|
||||
@@ -138,8 +141,16 @@ void VstEffectControls::saveSettings( QDomDocument & _doc, QDomElement & _this )
|
||||
|
||||
int VstEffectControls::controlCount()
|
||||
{
|
||||
return m_effect->m_plugin != NULL &&
|
||||
m_effect->m_plugin->hasEditor() ? 1 : 0;
|
||||
return m_effect->m_plugin != NULL ? 1 : 0;
|
||||
}
|
||||
|
||||
|
||||
|
||||
EffectControlDialog *VstEffectControls::createView()
|
||||
{
|
||||
auto dialog = new VstEffectControlDialog( this );
|
||||
dialog->togglePluginUI( m_vstGuiVisible );
|
||||
return dialog;
|
||||
}
|
||||
|
||||
|
||||
@@ -306,7 +317,7 @@ manageVSTEffectView::manageVSTEffectView( VstEffect * _eff, VstEffectControls *
|
||||
m_vi->m_subWindow->setWidget(m_vi->m_scrollArea);
|
||||
m_vi->m_subWindow->setWindowTitle( _eff->m_plugin->name() + tr( " - VST parameter control" ) );
|
||||
m_vi->m_subWindow->setWindowIcon( PLUGIN_NAME::getIconPixmap( "logo" ) );
|
||||
//m_vi->m_subWindow->setAttribute(Qt::WA_DeleteOnClose);
|
||||
m_vi->m_subWindow->setAttribute(Qt::WA_DeleteOnClose, false);
|
||||
|
||||
|
||||
l->setContentsMargins( 20, 10, 10, 10 );
|
||||
|
||||
@@ -59,10 +59,7 @@ public:
|
||||
|
||||
virtual int controlCount();
|
||||
|
||||
virtual EffectControlDialog * createView()
|
||||
{
|
||||
return new VstEffectControlDialog( this );
|
||||
}
|
||||
virtual EffectControlDialog * createView();
|
||||
|
||||
|
||||
protected slots:
|
||||
@@ -96,6 +93,7 @@ private:
|
||||
friend class VstEffectControlDialog;
|
||||
friend class manageVSTEffectView;
|
||||
|
||||
bool m_vstGuiVisible;
|
||||
} ;
|
||||
|
||||
|
||||
|
||||
@@ -85,7 +85,7 @@ public:
|
||||
|
||||
inline ~malletsSynth()
|
||||
{
|
||||
m_voice->noteOff( 0.0 );
|
||||
if (m_voice) {m_voice->noteOff(0.0);}
|
||||
delete[] m_delay;
|
||||
delete m_voice;
|
||||
}
|
||||
|
||||
@@ -24,6 +24,8 @@
|
||||
|
||||
#include "vestige.h"
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include <QDropEvent>
|
||||
#include <QMessageBox>
|
||||
#include <QPainter>
|
||||
@@ -74,6 +76,59 @@ Plugin::Descriptor PLUGIN_EXPORT vestige_plugin_descriptor =
|
||||
}
|
||||
|
||||
|
||||
class vstSubWin : public QMdiSubWindow
|
||||
{
|
||||
public:
|
||||
vstSubWin( QWidget * _parent ) :
|
||||
QMdiSubWindow( _parent )
|
||||
{
|
||||
setAttribute( Qt::WA_DeleteOnClose, false );
|
||||
setWindowFlags( Qt::WindowCloseButtonHint );
|
||||
}
|
||||
|
||||
virtual ~vstSubWin()
|
||||
{
|
||||
}
|
||||
|
||||
virtual void closeEvent( QCloseEvent * e )
|
||||
{
|
||||
// ignore close-events - for some reason otherwise the VST GUI
|
||||
// remains hidden when re-opening
|
||||
hide();
|
||||
e->ignore();
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
class VstInstrumentPlugin : public VstPlugin
|
||||
{
|
||||
public:
|
||||
using VstPlugin::VstPlugin;
|
||||
|
||||
void createUI( QWidget *parent ) override
|
||||
{
|
||||
Q_UNUSED(parent);
|
||||
if ( embedMethod() != "none" ) {
|
||||
m_pluginSubWindow.reset(new vstSubWin( gui->mainWindow()->workspace() ));
|
||||
VstPlugin::createUI( m_pluginSubWindow.get() );
|
||||
m_pluginSubWindow->setWidget(pluginWidget());
|
||||
} else {
|
||||
VstPlugin::createUI( nullptr );
|
||||
}
|
||||
}
|
||||
|
||||
/// Overwrite editor() to return the sub window instead of the embed widget
|
||||
/// itself. This makes toggleUI() and related functions toggle the
|
||||
/// sub window's visibility.
|
||||
QWidget* editor() override
|
||||
{
|
||||
return m_pluginSubWindow.get();
|
||||
}
|
||||
private:
|
||||
unique_ptr<QMdiSubWindow> m_pluginSubWindow;
|
||||
};
|
||||
|
||||
|
||||
QPixmap * VestigeInstrumentView::s_artwork = NULL;
|
||||
QPixmap * manageVestigeInstrumentView::s_artwork = NULL;
|
||||
|
||||
@@ -128,6 +183,12 @@ void vestigeInstrument::loadSettings( const QDomElement & _this )
|
||||
{
|
||||
m_plugin->loadSettings( _this );
|
||||
|
||||
if ( _this.attribute( "guivisible" ).toInt() ) {
|
||||
m_plugin->showUI();
|
||||
} else {
|
||||
m_plugin->hideUI();
|
||||
}
|
||||
|
||||
const QMap<QString, QString> & dump = m_plugin->parameterDump();
|
||||
paramCount = dump.size();
|
||||
char paramStr[35];
|
||||
@@ -268,7 +329,7 @@ void vestigeInstrument::loadFile( const QString & _file )
|
||||
}
|
||||
|
||||
m_pluginMutex.lock();
|
||||
m_plugin = new VstPlugin( m_pluginDLL );
|
||||
m_plugin = new VstInstrumentPlugin( m_pluginDLL );
|
||||
if( m_plugin->failed() )
|
||||
{
|
||||
m_pluginMutex.unlock();
|
||||
@@ -279,6 +340,7 @@ void vestigeInstrument::loadFile( const QString & _file )
|
||||
return;
|
||||
}
|
||||
|
||||
m_plugin->createUI(nullptr);
|
||||
m_plugin->showUI();
|
||||
|
||||
if( set_ch_name )
|
||||
|
||||
@@ -134,6 +134,8 @@ public:
|
||||
|
||||
void init( const std::string & _plugin_file );
|
||||
void initEditor();
|
||||
void showEditor();
|
||||
void hideEditor();
|
||||
void destroyEditor();
|
||||
|
||||
virtual void process( const sampleFrame * _in, sampleFrame * _out );
|
||||
@@ -293,8 +295,8 @@ public:
|
||||
static DWORD WINAPI processingThread( LPVOID _param );
|
||||
static bool setupMessageWindow();
|
||||
static DWORD WINAPI guiEventLoop();
|
||||
static LRESULT CALLBACK messageWndProc( HWND hwnd, UINT uMsg,
|
||||
WPARAM wParam, LPARAM lParam );
|
||||
static LRESULT CALLBACK wndProc( HWND hwnd, UINT uMsg,
|
||||
WPARAM wParam, LPARAM lParam );
|
||||
|
||||
|
||||
private:
|
||||
@@ -507,27 +509,28 @@ bool RemoteVstPlugin::processMessage( const message & _m )
|
||||
switch( _m.id )
|
||||
{
|
||||
case IdShowUI:
|
||||
initEditor();
|
||||
showEditor();
|
||||
return true;
|
||||
|
||||
case IdHideUI:
|
||||
destroyEditor();
|
||||
hideEditor();
|
||||
return true;
|
||||
|
||||
case IdToggleUI:
|
||||
if( m_window )
|
||||
if( m_window && IsWindowVisible( m_window ) )
|
||||
{
|
||||
destroyEditor();
|
||||
hideEditor();
|
||||
}
|
||||
else
|
||||
{
|
||||
initEditor();
|
||||
showEditor();
|
||||
}
|
||||
return true;
|
||||
|
||||
case IdIsUIVisible:
|
||||
bool visible = m_window && IsWindowVisible( m_window );
|
||||
sendMessage( message( IdIsUIVisible )
|
||||
.addInt( m_window ? 1 : 0 ) );
|
||||
.addInt( visible ? 1 : 0 ) );
|
||||
return true;
|
||||
}
|
||||
}
|
||||
@@ -709,7 +712,7 @@ void RemoteVstPlugin::initEditor()
|
||||
dwStyle = WS_OVERLAPPEDWINDOW & ~WS_MAXIMIZEBOX;
|
||||
}
|
||||
|
||||
m_window = CreateWindowEx( 0, "LVSL", pluginName(),
|
||||
m_window = CreateWindowEx( WS_EX_APPWINDOW, "LVSL", pluginName(),
|
||||
dwStyle,
|
||||
0, 0, 10, 10, NULL, NULL, hInst, NULL );
|
||||
if( m_window == NULL )
|
||||
@@ -727,13 +730,15 @@ void RemoteVstPlugin::initEditor()
|
||||
m_windowWidth = er->right - er->left;
|
||||
m_windowHeight = er->bottom - er->top;
|
||||
|
||||
SetWindowPos( m_window, 0, 0, 0, m_windowWidth + 8,
|
||||
m_windowHeight + 26, SWP_NOACTIVATE |
|
||||
RECT windowSize = { 0, 0, m_windowWidth, m_windowHeight };
|
||||
AdjustWindowRect( &windowSize, dwStyle, false );
|
||||
SetWindowPos( m_window, 0, 0, 0, windowSize.right - windowSize.left,
|
||||
windowSize.bottom - windowSize.top, SWP_NOACTIVATE |
|
||||
SWP_NOMOVE | SWP_NOZORDER );
|
||||
pluginDispatch( effEditTop );
|
||||
|
||||
if (! EMBED) {
|
||||
ShowWindow( m_window, SW_SHOWNORMAL );
|
||||
showEditor();
|
||||
}
|
||||
|
||||
#ifdef LMMS_BUILD_LINUX
|
||||
@@ -747,6 +752,26 @@ void RemoteVstPlugin::initEditor()
|
||||
|
||||
|
||||
|
||||
void RemoteVstPlugin::showEditor() {
|
||||
if( !EMBED && !HEADLESS && m_window )
|
||||
{
|
||||
ShowWindow( m_window, SW_SHOWNORMAL );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void RemoteVstPlugin::hideEditor() {
|
||||
if( !EMBED && !HEADLESS && m_window )
|
||||
{
|
||||
ShowWindow( m_window, SW_HIDE );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void RemoteVstPlugin::destroyEditor()
|
||||
{
|
||||
if( m_window == NULL )
|
||||
@@ -1884,8 +1909,6 @@ bool RemoteVstPlugin::setupMessageWindow()
|
||||
__MessageHwnd = CreateWindowEx( 0, "LVSL", "dummy",
|
||||
0, 0, 0, 0, 0, NULL, NULL,
|
||||
hInst, NULL );
|
||||
SetWindowLongPtr( __MessageHwnd, GWLP_WNDPROC,
|
||||
reinterpret_cast<LONG_PTR>( RemoteVstPlugin::messageWndProc ) );
|
||||
// install GUI update timer
|
||||
SetTimer( __MessageHwnd, 1000, 50, NULL );
|
||||
|
||||
@@ -1910,7 +1933,7 @@ DWORD WINAPI RemoteVstPlugin::guiEventLoop()
|
||||
|
||||
|
||||
|
||||
LRESULT CALLBACK RemoteVstPlugin::messageWndProc( HWND hwnd, UINT uMsg,
|
||||
LRESULT CALLBACK RemoteVstPlugin::wndProc( HWND hwnd, UINT uMsg,
|
||||
WPARAM wParam, LPARAM lParam )
|
||||
{
|
||||
if( uMsg == WM_TIMER && __plugin->isInitialized() )
|
||||
@@ -1947,9 +1970,9 @@ LRESULT CALLBACK RemoteVstPlugin::messageWndProc( HWND hwnd, UINT uMsg,
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if( uMsg == WM_SYSCOMMAND && wParam == SC_CLOSE )
|
||||
else if( uMsg == WM_SYSCOMMAND && (wParam & 0xfff0) == SC_CLOSE )
|
||||
{
|
||||
__plugin->destroyEditor();
|
||||
__plugin->hideEditor();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -2004,7 +2027,7 @@ int main( int _argc, char * * _argv )
|
||||
|
||||
WNDCLASS wc;
|
||||
wc.style = CS_HREDRAW | CS_VREDRAW;
|
||||
wc.lpfnWndProc = DefWindowProc;
|
||||
wc.lpfnWndProc = RemoteVstPlugin::wndProc;
|
||||
wc.cbClsExtra = 0;
|
||||
wc.cbWndExtra = 0;
|
||||
wc.hInstance = hInst;
|
||||
|
||||
@@ -34,9 +34,9 @@
|
||||
#include <QMdiSubWindow>
|
||||
|
||||
#ifdef LMMS_BUILD_LINUX
|
||||
# include <QX11Info>
|
||||
# if QT_VERSION < 0x050000
|
||||
# include <QX11EmbedContainer>
|
||||
# include <QX11Info>
|
||||
# else
|
||||
# include "X11EmbedContainer.h"
|
||||
# include <QWindow>
|
||||
@@ -62,27 +62,9 @@
|
||||
#include "templates.h"
|
||||
#include "FileDialog.h"
|
||||
|
||||
class vstSubWin : public QMdiSubWindow
|
||||
{
|
||||
public:
|
||||
vstSubWin( QWidget * _parent ) :
|
||||
QMdiSubWindow( _parent )
|
||||
{
|
||||
setAttribute( Qt::WA_DeleteOnClose, false );
|
||||
}
|
||||
|
||||
virtual ~vstSubWin()
|
||||
{
|
||||
}
|
||||
|
||||
virtual void closeEvent( QCloseEvent * e )
|
||||
{
|
||||
// ignore close-events - for some reason otherwise the VST GUI
|
||||
// remains hidden when re-opening
|
||||
hide();
|
||||
e->ignore();
|
||||
}
|
||||
} ;
|
||||
#ifdef LMMS_BUILD_LINUX
|
||||
# include <X11/Xlib.h>
|
||||
#endif
|
||||
|
||||
|
||||
VstPlugin::VstPlugin( const QString & _plugin ) :
|
||||
@@ -124,7 +106,6 @@ VstPlugin::VstPlugin( const QString & _plugin ) :
|
||||
|
||||
VstPlugin::~VstPlugin()
|
||||
{
|
||||
delete m_pluginSubWindow;
|
||||
delete m_pluginWidget;
|
||||
}
|
||||
|
||||
@@ -174,41 +155,8 @@ void VstPlugin::tryLoad( const QString &remoteVstPluginExecutable )
|
||||
|
||||
|
||||
|
||||
void VstPlugin::hideEditor()
|
||||
{
|
||||
QWidget * w = pluginWidget();
|
||||
if( w )
|
||||
{
|
||||
w->hide();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void VstPlugin::toggleEditor()
|
||||
{
|
||||
QWidget * w = pluginWidget();
|
||||
if( w )
|
||||
{
|
||||
w->setVisible( !w->isVisible() );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
void VstPlugin::loadSettings( const QDomElement & _this )
|
||||
{
|
||||
if( _this.attribute( "guivisible" ).toInt() )
|
||||
{
|
||||
showUI();
|
||||
}
|
||||
else
|
||||
{
|
||||
hideUI();
|
||||
}
|
||||
|
||||
const int num_params = _this.attribute( "numparams" ).toInt();
|
||||
// if it exists try to load settings chunk
|
||||
if( _this.hasAttribute( "chunk" ) )
|
||||
@@ -286,7 +234,7 @@ void VstPlugin::toggleUI()
|
||||
}
|
||||
else if (pluginWidget())
|
||||
{
|
||||
toggleEditor();
|
||||
toggleEditorVisibility();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -362,21 +310,9 @@ void VstPlugin::setParameterDump( const QMap<QString, QString> & _pdump )
|
||||
unlock();
|
||||
}
|
||||
|
||||
QWidget *VstPlugin::pluginWidget(bool _top_widget)
|
||||
QWidget *VstPlugin::pluginWidget()
|
||||
{
|
||||
if ( m_embedMethod == "none" || !m_pluginWidget )
|
||||
{
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
if ( _top_widget && m_pluginWidget->parentWidget() == m_pluginSubWindow )
|
||||
{
|
||||
return m_pluginSubWindow;
|
||||
}
|
||||
else
|
||||
{
|
||||
return m_pluginWidget;
|
||||
}
|
||||
return m_pluginWidget;
|
||||
}
|
||||
|
||||
|
||||
@@ -392,6 +328,22 @@ bool VstPlugin::processMessage( const message & _m )
|
||||
|
||||
case IdVstPluginWindowID:
|
||||
m_pluginWindowID = _m.getInt();
|
||||
if( m_embedMethod == "none" )
|
||||
{
|
||||
#ifdef LMMS_BUILD_WIN32
|
||||
// We're changing the owner, not the parent,
|
||||
// so this is legal despite MSDN's warning
|
||||
SetWindowLongPtr( (HWND)(intptr_t) m_pluginWindowID,
|
||||
GWLP_HWNDPARENT,
|
||||
(LONG_PTR) gui->mainWindow()->winId() );
|
||||
#endif
|
||||
|
||||
#ifdef LMMS_BUILD_LINUX
|
||||
XSetTransientForHint( QX11Info::display(),
|
||||
m_pluginWindowID,
|
||||
gui->mainWindow()->winId() );
|
||||
#endif
|
||||
}
|
||||
break;
|
||||
|
||||
case IdVstPluginEditorGeometry:
|
||||
@@ -458,6 +410,10 @@ bool VstPlugin::processMessage( const message & _m )
|
||||
}
|
||||
|
||||
|
||||
QWidget *VstPlugin::editor()
|
||||
{
|
||||
return m_pluginWidget;
|
||||
}
|
||||
|
||||
|
||||
void VstPlugin::openPreset( )
|
||||
@@ -579,15 +535,10 @@ void VstPlugin::showUI()
|
||||
}
|
||||
else if ( m_embedMethod != "headless" )
|
||||
{
|
||||
if (! pluginWidget()) {
|
||||
createUI( NULL, false );
|
||||
}
|
||||
|
||||
QWidget * w = pluginWidget();
|
||||
if( w )
|
||||
{
|
||||
w->show();
|
||||
if (! editor()) {
|
||||
qWarning() << "VstPlugin::showUI called before VstPlugin::createUI";
|
||||
}
|
||||
toggleEditorVisibility( true );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -599,7 +550,7 @@ void VstPlugin::hideUI()
|
||||
}
|
||||
else if ( pluginWidget() != nullptr )
|
||||
{
|
||||
hideEditor();
|
||||
toggleEditorVisibility( false );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -654,22 +605,39 @@ QByteArray VstPlugin::saveChunk()
|
||||
return a;
|
||||
}
|
||||
|
||||
void VstPlugin::createUI( QWidget * parent, bool isEffect )
|
||||
void VstPlugin::toggleEditorVisibility( int visible )
|
||||
{
|
||||
QWidget* w = editor();
|
||||
if ( ! w ) {
|
||||
return;
|
||||
}
|
||||
|
||||
if ( visible < 0 ) {
|
||||
visible = ! w->isVisible();
|
||||
}
|
||||
w->setVisible( visible );
|
||||
}
|
||||
|
||||
void VstPlugin::createUI( QWidget * parent )
|
||||
{
|
||||
if ( m_pluginWidget ) {
|
||||
qWarning() << "VstPlugin::createUI called twice";
|
||||
m_pluginWidget->setParent( parent );
|
||||
return;
|
||||
}
|
||||
|
||||
if( m_pluginWindowID == 0 )
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
QWidget* container = nullptr;
|
||||
m_pluginSubWindow = new vstSubWin( gui->mainWindow()->workspace() );
|
||||
auto sw = m_pluginSubWindow.data();
|
||||
|
||||
#if QT_VERSION >= 0x050100
|
||||
if (m_embedMethod == "qt" )
|
||||
{
|
||||
QWindow* vw = QWindow::fromWinId(m_pluginWindowID);
|
||||
container = QWidget::createWindowContainer(vw, sw );
|
||||
container = QWidget::createWindowContainer(vw, parent );
|
||||
container->installEventFilter(this);
|
||||
} else
|
||||
#endif
|
||||
@@ -708,7 +676,11 @@ void VstPlugin::createUI( QWidget * parent, bool isEffect )
|
||||
#ifdef LMMS_BUILD_LINUX
|
||||
if (m_embedMethod == "xembed" )
|
||||
{
|
||||
QX11EmbedContainer * embedContainer = new QX11EmbedContainer( sw );
|
||||
if (parent)
|
||||
{
|
||||
parent->setAttribute(Qt::WA_NativeWindow);
|
||||
}
|
||||
QX11EmbedContainer * embedContainer = new QX11EmbedContainer( parent );
|
||||
connect(embedContainer, SIGNAL(clientIsEmbedded()), this, SLOT(handleClientEmbed()));
|
||||
embedContainer->embedClient( m_pluginWindowID );
|
||||
container = embedContainer;
|
||||
@@ -716,31 +688,13 @@ void VstPlugin::createUI( QWidget * parent, bool isEffect )
|
||||
#endif
|
||||
{
|
||||
qCritical() << "Unknown embed method" << m_embedMethod;
|
||||
delete m_pluginSubWindow;
|
||||
return;
|
||||
}
|
||||
|
||||
container->setFixedSize( m_pluginGeometry );
|
||||
container->setWindowTitle( name() );
|
||||
|
||||
if( parent == NULL )
|
||||
{
|
||||
m_pluginWidget = container;
|
||||
|
||||
sw->setWidget(container);
|
||||
|
||||
if( isEffect )
|
||||
{
|
||||
sw->setAttribute( Qt::WA_TranslucentBackground );
|
||||
sw->setWindowFlags( Qt::FramelessWindowHint );
|
||||
}
|
||||
else
|
||||
{
|
||||
sw->setWindowFlags( Qt::WindowCloseButtonHint );
|
||||
}
|
||||
};
|
||||
|
||||
container->setFixedSize( m_pluginGeometry );
|
||||
m_pluginWidget = container;
|
||||
}
|
||||
|
||||
bool VstPlugin::eventFilter(QObject *obj, QEvent *event)
|
||||
|
||||
@@ -54,8 +54,10 @@ public:
|
||||
return m_pluginWindowID != 0;
|
||||
}
|
||||
|
||||
void hideEditor();
|
||||
void toggleEditor();
|
||||
/// Same as pluginWidget(), but can be overwritten in sub-classes to modify
|
||||
/// behavior the UI. This is used in VstInstrumentPlugin to wrap the VST UI
|
||||
/// in a QMdiSubWindow
|
||||
virtual QWidget* editor();
|
||||
|
||||
inline const QString & name() const
|
||||
{
|
||||
@@ -93,7 +95,7 @@ public:
|
||||
void setParameterDump( const QMap<QString, QString> & _pdump );
|
||||
|
||||
|
||||
QWidget * pluginWidget( bool _top_widget = true );
|
||||
QWidget * pluginWidget();
|
||||
|
||||
virtual void loadSettings( const QDomElement & _this );
|
||||
virtual void saveSettings( QDomDocument & _doc, QDomElement & _this );
|
||||
@@ -103,9 +105,8 @@ public:
|
||||
return "vstplugin";
|
||||
}
|
||||
|
||||
void toggleUI() override;
|
||||
|
||||
void createUI( QWidget *parent, bool isEffect );
|
||||
virtual void createUI(QWidget *parent);
|
||||
bool eventFilter(QObject *obj, QEvent *event);
|
||||
|
||||
QString embedMethod() const;
|
||||
@@ -123,6 +124,7 @@ public slots:
|
||||
|
||||
void showUI() override;
|
||||
void hideUI() override;
|
||||
void toggleUI() override;
|
||||
|
||||
void handleClientEmbed();
|
||||
|
||||
@@ -130,9 +132,10 @@ private:
|
||||
void loadChunk( const QByteArray & _chunk );
|
||||
QByteArray saveChunk();
|
||||
|
||||
void toggleEditorVisibility(int visible = -1);
|
||||
|
||||
QString m_plugin;
|
||||
QPointer<QWidget> m_pluginWidget;
|
||||
QPointer<vstSubWin> m_pluginSubWindow;
|
||||
int m_pluginWindowID;
|
||||
QSize m_pluginGeometry;
|
||||
const QString m_embedMethod;
|
||||
|
||||
@@ -107,7 +107,8 @@ void AutomatableModel::saveSettings( QDomDocument& doc, QDomElement& element, co
|
||||
element.setAttribute( name, m_value );
|
||||
}
|
||||
|
||||
if( m_controllerConnection )
|
||||
if( m_controllerConnection && m_controllerConnection->getController()->type()
|
||||
!= Controller::DummyController )
|
||||
{
|
||||
QDomElement controllerElement;
|
||||
|
||||
|
||||
@@ -162,6 +162,11 @@ void ControllerConnection::finalizeConnections()
|
||||
c->setController( Engine::getSong()->
|
||||
controllers().at( c->m_controllerId ) );
|
||||
}
|
||||
else if (c->getController()->type() == Controller::DummyController)
|
||||
{
|
||||
delete c;
|
||||
--i;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -199,7 +204,7 @@ void ControllerConnection::loadSettings( const QDomElement & _this )
|
||||
}
|
||||
else
|
||||
{
|
||||
if( _this.attribute( "id" ).toInt() >= 0 )
|
||||
if( _this.attribute( "id", "-1" ).toInt() >= 0 )
|
||||
{
|
||||
m_controllerId = _this.attribute( "id" ).toInt();
|
||||
}
|
||||
|
||||
@@ -802,7 +802,7 @@ AutomationPattern * Song::tempoAutomationPattern()
|
||||
|
||||
AutomatedValueMap Song::automatedValuesAt(MidiTime time, int tcoNum) const
|
||||
{
|
||||
return TrackContainer::automatedValuesFromTracks(TrackList(tracks()) << m_globalAutomationTrack, time, tcoNum);
|
||||
return TrackContainer::automatedValuesFromTracks(TrackList{m_globalAutomationTrack} << tracks(), time, tcoNum);
|
||||
}
|
||||
|
||||
|
||||
@@ -1101,6 +1101,11 @@ void Song::loadProject( const QString & fileName )
|
||||
// now that everything is loaded
|
||||
ControllerConnection::finalizeConnections();
|
||||
|
||||
// Remove dummy controllers that was added for correct connections
|
||||
m_controllers.erase(std::remove_if(m_controllers.begin(), m_controllers.end(),
|
||||
[](Controller* c){return c->type() == Controller::DummyController;}),
|
||||
m_controllers.end());
|
||||
|
||||
// resolve all IDs so that autoModels are automated
|
||||
AutomationPattern::resolveAllIDs();
|
||||
|
||||
@@ -1235,9 +1240,13 @@ void Song::restoreControllerStates( const QDomElement & element )
|
||||
while( !node.isNull() && !isCancelled() )
|
||||
{
|
||||
Controller * c = Controller::create( node.toElement(), this );
|
||||
Q_ASSERT( c != NULL );
|
||||
|
||||
addController( c );
|
||||
if (c) {addController(c);}
|
||||
else
|
||||
{
|
||||
// Fix indices to ensure correct connections
|
||||
m_controllers.append(Controller::create(
|
||||
Controller::DummyController, this));
|
||||
}
|
||||
|
||||
node = node.nextSibling();
|
||||
}
|
||||
|
||||
@@ -1881,6 +1881,7 @@ void TrackOperationsWidget::cloneTrack()
|
||||
void TrackOperationsWidget::clearTrack()
|
||||
{
|
||||
Track * t = m_trackView->getTrack();
|
||||
t->addJournalCheckPoint();
|
||||
t->lock();
|
||||
t->deleteTCOs();
|
||||
t->unlock();
|
||||
|
||||
@@ -123,8 +123,11 @@ void SubWindow::paintEvent( QPaintEvent * )
|
||||
p.drawLine( width() - 1, m_titleBarHeight, width() - 1, height() - 1 );
|
||||
|
||||
// window icon
|
||||
QPixmap winicon( widget()->windowIcon().pixmap( m_buttonSize ) );
|
||||
p.drawPixmap( 3, 3, m_buttonSize.width(), m_buttonSize.height(), winicon );
|
||||
if( widget() )
|
||||
{
|
||||
QPixmap winicon( widget()->windowIcon().pixmap( m_buttonSize ) );
|
||||
p.drawPixmap( 3, 3, m_buttonSize.width(), m_buttonSize.height(), winicon );
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -324,25 +327,31 @@ void SubWindow::adjustTitleBar()
|
||||
// we're keeping the restore button around if we open projects
|
||||
// from older versions that have saved minimized windows
|
||||
m_restoreBtn->setVisible( isMaximized() || isMinimized() );
|
||||
|
||||
// title QLabel adjustments
|
||||
m_windowTitle->setAlignment( Qt::AlignHCenter );
|
||||
m_windowTitle->setFixedWidth( widget()->width() - ( menuButtonSpace + buttonBarWidth ) );
|
||||
m_windowTitle->move( menuButtonSpace,
|
||||
( m_titleBarHeight / 2 ) - ( m_windowTitle->sizeHint().height() / 2 ) - 1 );
|
||||
|
||||
// if minimized we can't use widget()->width(). We have to hard code the width,
|
||||
// as the width of all minimized windows is the same.
|
||||
if( isMinimized() )
|
||||
{
|
||||
m_restoreBtn->move( m_maximizeBtn->isHidden() ? middleButtonPos : leftButtonPos );
|
||||
m_windowTitle->setFixedWidth( 120 );
|
||||
}
|
||||
|
||||
// truncate the label string if the window is to small. Adds "..."
|
||||
elideText( m_windowTitle, widget()->windowTitle() );
|
||||
m_windowTitle->setTextInteractionFlags( Qt::NoTextInteraction );
|
||||
m_windowTitle->adjustSize();
|
||||
if( widget() )
|
||||
{
|
||||
// title QLabel adjustments
|
||||
m_windowTitle->setAlignment( Qt::AlignHCenter );
|
||||
m_windowTitle->setFixedWidth( widget()->width() - ( menuButtonSpace + buttonBarWidth ) );
|
||||
m_windowTitle->move( menuButtonSpace,
|
||||
( m_titleBarHeight / 2 ) - ( m_windowTitle->sizeHint().height() / 2 ) - 1 );
|
||||
|
||||
// if minimized we can't use widget()->width(). We have to hard code the width,
|
||||
// as the width of all minimized windows is the same.
|
||||
if( isMinimized() )
|
||||
{
|
||||
m_windowTitle->setFixedWidth( 120 );
|
||||
}
|
||||
|
||||
// truncate the label string if the window is to small. Adds "..."
|
||||
elideText( m_windowTitle, widget()->windowTitle() );
|
||||
m_windowTitle->setTextInteractionFlags( Qt::NoTextInteraction );
|
||||
m_windowTitle->adjustSize();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -178,6 +178,7 @@ PianoRoll::PianoRoll() :
|
||||
m_startKey( INITIAL_START_KEY ),
|
||||
m_lastKey( 0 ),
|
||||
m_editMode( ModeDraw ),
|
||||
m_ctrlMode( ModeDraw ),
|
||||
m_mouseDownRight( false ),
|
||||
m_scrollBack( false ),
|
||||
m_barLineColor( 0, 0, 0 ),
|
||||
@@ -951,6 +952,8 @@ void PianoRoll::clearSelectedNotes()
|
||||
|
||||
void PianoRoll::shiftSemiTone( int amount ) // shift notes by amount semitones
|
||||
{
|
||||
if (!hasValidPattern()) {return;}
|
||||
|
||||
bool useAllNotes = ! isSelection();
|
||||
for( Note *note : m_pattern->notes() )
|
||||
{
|
||||
@@ -975,6 +978,8 @@ void PianoRoll::shiftSemiTone( int amount ) // shift notes by amount semitones
|
||||
|
||||
void PianoRoll::shiftPos( int amount ) //shift notes pos by amount
|
||||
{
|
||||
if (!hasValidPattern()) {return;}
|
||||
|
||||
bool useAllNotes = ! isSelection();
|
||||
|
||||
bool first = true;
|
||||
@@ -1065,12 +1070,18 @@ void PianoRoll::keyPressEvent(QKeyEvent* ke )
|
||||
{
|
||||
// shift selection up an octave
|
||||
// if nothing selected, shift _everything_
|
||||
shiftSemiTone( 12 * direction );
|
||||
if (hasValidPattern())
|
||||
{
|
||||
shiftSemiTone( 12 * direction );
|
||||
}
|
||||
}
|
||||
else if((ke->modifiers() & Qt::ShiftModifier) && m_action == ActionNone)
|
||||
{
|
||||
// Move selected notes up by one semitone
|
||||
shiftSemiTone( 1 * direction );
|
||||
if (hasValidPattern())
|
||||
{
|
||||
shiftSemiTone( 1 * direction );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1100,22 +1111,32 @@ void PianoRoll::keyPressEvent(QKeyEvent* ke )
|
||||
if( ke->modifiers() & Qt::ControlModifier && m_action == ActionNone )
|
||||
{
|
||||
// Move selected notes by one bar to the left
|
||||
shiftPos( direction * MidiTime::ticksPerTact() );
|
||||
if (hasValidPattern())
|
||||
{
|
||||
shiftPos( direction * MidiTime::ticksPerTact() );
|
||||
}
|
||||
}
|
||||
else if( ke->modifiers() & Qt::ShiftModifier && m_action == ActionNone)
|
||||
{
|
||||
// move notes
|
||||
bool quantized = ! ( ke->modifiers() & Qt::AltModifier );
|
||||
int amt = quantized ? quantization() : 1;
|
||||
shiftPos( direction * amt );
|
||||
if (hasValidPattern())
|
||||
{
|
||||
bool quantized = ! ( ke->modifiers() & Qt::AltModifier );
|
||||
int amt = quantized ? quantization() : 1;
|
||||
shiftPos( direction * amt );
|
||||
}
|
||||
}
|
||||
else if( ke->modifiers() & Qt::AltModifier)
|
||||
{
|
||||
// switch to editing a pattern adjacent to this one in the song editor
|
||||
Pattern * p = direction > 0 ? m_pattern->nextPattern() : m_pattern->previousPattern();
|
||||
if(p != NULL)
|
||||
if (hasValidPattern())
|
||||
{
|
||||
setCurrentPattern(p);
|
||||
Pattern * p = direction > 0 ? m_pattern->nextPattern()
|
||||
: m_pattern->previousPattern();
|
||||
if(p != NULL)
|
||||
{
|
||||
setCurrentPattern(p);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -3190,6 +3211,7 @@ void PianoRoll::wheelEvent(QWheelEvent * we )
|
||||
if( we->x() > noteEditLeft() && we->x() < noteEditRight()
|
||||
&& we->y() > noteEditTop() && we->y() < noteEditBottom() )
|
||||
{
|
||||
if (!hasValidPattern()) {return;}
|
||||
// get values for going through notes
|
||||
int pixel_range = 8;
|
||||
int x = we->x() - WHITE_KEY_WIDTH;
|
||||
@@ -3332,8 +3354,9 @@ void PianoRoll::focusOutEvent( QFocusEvent * )
|
||||
m_pattern->instrumentTrack()->pianoModel()->midiEventProcessor()->processInEvent( MidiEvent( MidiNoteOff, -1, i, 0 ) );
|
||||
m_pattern->instrumentTrack()->pianoModel()->setKeyState( i, false );
|
||||
}
|
||||
update();
|
||||
}
|
||||
m_editMode = m_ctrlMode;
|
||||
update();
|
||||
}
|
||||
|
||||
|
||||
@@ -3541,7 +3564,7 @@ void PianoRoll::verScrolled( int new_pos )
|
||||
|
||||
void PianoRoll::setEditMode(int mode)
|
||||
{
|
||||
m_editMode = (EditModes) mode;
|
||||
m_ctrlMode = m_editMode = (EditModes) mode;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@
|
||||
#include <QMdiArea>
|
||||
#include <QMdiSubWindow>
|
||||
#include <QPainter>
|
||||
#include <QLayout>
|
||||
|
||||
#include "EffectView.h"
|
||||
#include "DummyEffect.h"
|
||||
@@ -49,13 +50,13 @@ EffectView::EffectView( Effect * _model, QWidget * _parent ) :
|
||||
m_controlView( NULL )
|
||||
{
|
||||
setFixedSize( 210, 60 );
|
||||
|
||||
|
||||
// Disable effects that are of type "DummyEffect"
|
||||
bool isEnabled = !dynamic_cast<DummyEffect *>( effect() );
|
||||
m_bypass = new LedCheckBox( this, "", isEnabled ? LedCheckBox::Green : LedCheckBox::Red );
|
||||
m_bypass->move( 3, 3 );
|
||||
m_bypass->setEnabled( isEnabled );
|
||||
|
||||
|
||||
ToolTip::add( m_bypass, tr( "On/Off" ) );
|
||||
|
||||
|
||||
@@ -97,7 +98,9 @@ EffectView::EffectView( Effect * _model, QWidget * _parent ) :
|
||||
{
|
||||
m_subWindow = gui->mainWindow()->addWindowedWidget( m_controlView );
|
||||
m_subWindow->setSizePolicy( QSizePolicy::Fixed, QSizePolicy::Fixed );
|
||||
m_subWindow->setFixedSize( m_subWindow->size() );
|
||||
if (m_subWindow->layout()) {
|
||||
m_subWindow->layout()->setSizeConstraint(QLayout::SetFixedSize);
|
||||
}
|
||||
|
||||
Qt::WindowFlags flags = m_subWindow->windowFlags();
|
||||
flags &= ~Qt::WindowMaximizeButtonHint;
|
||||
@@ -120,18 +123,7 @@ EffectView::EffectView( Effect * _model, QWidget * _parent ) :
|
||||
|
||||
EffectView::~EffectView()
|
||||
{
|
||||
|
||||
#ifdef LMMS_BUILD_LINUX
|
||||
|
||||
delete m_subWindow;
|
||||
#else
|
||||
if( m_subWindow )
|
||||
{
|
||||
// otherwise on win32 build VST GUI can get lost
|
||||
m_subWindow->hide();
|
||||
}
|
||||
#endif
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -264,6 +264,7 @@ void FxLine::renameChannel()
|
||||
void FxLine::renameFinished()
|
||||
{
|
||||
m_inRename = false;
|
||||
m_renameLineEdit->deselect();
|
||||
m_renameLineEdit->setReadOnly( true );
|
||||
m_renameLineEdit->setFixedWidth( 65 );
|
||||
m_lcd->show();
|
||||
|
||||
@@ -193,6 +193,30 @@ private slots:
|
||||
QCOMPARE(song->automatedValuesAt(5)[&model], 0.5f);
|
||||
QCOMPARE(song->automatedValuesAt(MidiTime::ticksPerTact() + 5)[&model], 0.5f);
|
||||
}
|
||||
|
||||
void testGlobalAutomation()
|
||||
{
|
||||
// Global automation should not have priority, see https://github.com/LMMS/lmms/issues/4268
|
||||
// Tests regression caused by 75077f6200a5aee3a5821aae48a3b8466ed8714a
|
||||
auto song = Engine::getSong();
|
||||
|
||||
auto globalTrack = song->globalAutomationTrack();
|
||||
AutomationPattern globalPattern(globalTrack);
|
||||
|
||||
AutomationTrack localTrack(song);
|
||||
AutomationPattern localPattern(&localTrack);
|
||||
|
||||
FloatModel model;
|
||||
globalPattern.setProgressionType(AutomationPattern::DiscreteProgression);
|
||||
localPattern.setProgressionType(AutomationPattern::DiscreteProgression);
|
||||
globalPattern.addObject(&model);
|
||||
localPattern.addObject(&model);
|
||||
globalPattern.putValue(0, 100.0f, false);
|
||||
localPattern.putValue(0, 50.0f, false);
|
||||
|
||||
QCOMPARE(song->automatedValuesAt(0)[&model], 50.0f);
|
||||
}
|
||||
|
||||
} AutomationTrackTest;
|
||||
|
||||
#include "AutomationTrackTest.moc"
|
||||
|
||||
Reference in New Issue
Block a user