diff --git a/CMakeLists.txt b/CMakeLists.txt index 6c67e9316..34ca5a20f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -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) diff --git a/plugins/VstEffect/VstEffectControlDialog.cpp b/plugins/VstEffect/VstEffectControlDialog.cpp index 678bb9136..797818979 100644 --- a/plugins/VstEffect/VstEffectControlDialog.cpp +++ b/plugins/VstEffect/VstEffectControlDialog.cpp @@ -41,10 +41,10 @@ #include #include - 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(); + } } diff --git a/plugins/VstEffect/VstEffectControlDialog.h b/plugins/VstEffect/VstEffectControlDialog.h index ddbbef878..3cd9af360 100644 --- a/plugins/VstEffect/VstEffectControlDialog.h +++ b/plugins/VstEffect/VstEffectControlDialog.h @@ -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 ); } ; diff --git a/plugins/VstEffect/VstEffectControls.cpp b/plugins/VstEffect/VstEffectControls.cpp index 60f0e7b82..173a76913 100644 --- a/plugins/VstEffect/VstEffectControls.cpp +++ b/plugins/VstEffect/VstEffectControls.cpp @@ -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 & 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 ); diff --git a/plugins/VstEffect/VstEffectControls.h b/plugins/VstEffect/VstEffectControls.h index 7328f2f42..e4f099fd1 100644 --- a/plugins/VstEffect/VstEffectControls.h +++ b/plugins/VstEffect/VstEffectControls.h @@ -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; } ; diff --git a/plugins/stk/mallets/mallets.h b/plugins/stk/mallets/mallets.h index 649b8ff45..3928c531c 100644 --- a/plugins/stk/mallets/mallets.h +++ b/plugins/stk/mallets/mallets.h @@ -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; } diff --git a/plugins/vestige/vestige.cpp b/plugins/vestige/vestige.cpp index e1869a74e..b8204f404 100644 --- a/plugins/vestige/vestige.cpp +++ b/plugins/vestige/vestige.cpp @@ -24,6 +24,8 @@ #include "vestige.h" +#include + #include #include #include @@ -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 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 & 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 ) diff --git a/plugins/vst_base/RemoteVstPlugin.cpp b/plugins/vst_base/RemoteVstPlugin.cpp index 71ebf74c0..a51ac9d9a 100644 --- a/plugins/vst_base/RemoteVstPlugin.cpp +++ b/plugins/vst_base/RemoteVstPlugin.cpp @@ -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( 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; diff --git a/plugins/vst_base/VstPlugin.cpp b/plugins/vst_base/VstPlugin.cpp index 5c7504dd1..26c789275 100644 --- a/plugins/vst_base/VstPlugin.cpp +++ b/plugins/vst_base/VstPlugin.cpp @@ -34,9 +34,9 @@ #include #ifdef LMMS_BUILD_LINUX +# include # if QT_VERSION < 0x050000 # include -# include # else # include "X11EmbedContainer.h" # include @@ -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 +#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 & _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) diff --git a/plugins/vst_base/VstPlugin.h b/plugins/vst_base/VstPlugin.h index f3d6bea8e..9e8b39771 100644 --- a/plugins/vst_base/VstPlugin.h +++ b/plugins/vst_base/VstPlugin.h @@ -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 & _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 m_pluginWidget; - QPointer m_pluginSubWindow; int m_pluginWindowID; QSize m_pluginGeometry; const QString m_embedMethod; diff --git a/src/core/AutomatableModel.cpp b/src/core/AutomatableModel.cpp index 425ec59ef..30732e052 100644 --- a/src/core/AutomatableModel.cpp +++ b/src/core/AutomatableModel.cpp @@ -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; diff --git a/src/core/ControllerConnection.cpp b/src/core/ControllerConnection.cpp index 280ed709c..4f04cbc20 100644 --- a/src/core/ControllerConnection.cpp +++ b/src/core/ControllerConnection.cpp @@ -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(); } diff --git a/src/core/Song.cpp b/src/core/Song.cpp index 775c0c588..9b4483611 100644 --- a/src/core/Song.cpp +++ b/src/core/Song.cpp @@ -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(); } diff --git a/src/core/Track.cpp b/src/core/Track.cpp index 2a59334aa..e7aa15cd0 100644 --- a/src/core/Track.cpp +++ b/src/core/Track.cpp @@ -1881,6 +1881,7 @@ void TrackOperationsWidget::cloneTrack() void TrackOperationsWidget::clearTrack() { Track * t = m_trackView->getTrack(); + t->addJournalCheckPoint(); t->lock(); t->deleteTCOs(); t->unlock(); diff --git a/src/gui/SubWindow.cpp b/src/gui/SubWindow.cpp index 0abe22558..9e3876d24 100644 --- a/src/gui/SubWindow.cpp +++ b/src/gui/SubWindow.cpp @@ -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(); + } } diff --git a/src/gui/editors/PianoRoll.cpp b/src/gui/editors/PianoRoll.cpp index 1ec23724c..ffcc10c1a 100644 --- a/src/gui/editors/PianoRoll.cpp +++ b/src/gui/editors/PianoRoll.cpp @@ -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; } diff --git a/src/gui/widgets/EffectView.cpp b/src/gui/widgets/EffectView.cpp index a4600155b..97f8bbed0 100644 --- a/src/gui/widgets/EffectView.cpp +++ b/src/gui/widgets/EffectView.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #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( 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 - } diff --git a/src/gui/widgets/FxLine.cpp b/src/gui/widgets/FxLine.cpp index d30ff5a7c..3314301fd 100644 --- a/src/gui/widgets/FxLine.cpp +++ b/src/gui/widgets/FxLine.cpp @@ -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(); diff --git a/tests/src/tracks/AutomationTrackTest.cpp b/tests/src/tracks/AutomationTrackTest.cpp index 65c3b4874..f9f77fac4 100644 --- a/tests/src/tracks/AutomationTrackTest.cpp +++ b/tests/src/tracks/AutomationTrackTest.cpp @@ -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"