From a1b355828e347b8e7fca8118873fc834953c1d67 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Thu, 27 Dec 2018 18:43:13 +0100 Subject: [PATCH 01/53] Allow sub plugins for instruments aswell * Move m_key member of Effect into Plugin * Pass key to Instrument ctors and instantiaters * Add pluginKeys to all plugin selector widgets, and let them pass the keys when instantiating the instruments; or, if the keys must be passed over threads, pass the keys to the Engine using `Engine::setDndPluginKey()` * As instrument plugin libraries now also need to get their key passed, their second argument, which was always the same as the first, is now used to pass the sub plugin keys. This affects *all* instrument plugins. * Plugin.h: Add more virtuals to `SubPluginFeatures` in order to draw logos and images into instrument selector widgets * LadspaSubPluginFeatures: Implement the `displayName` virtual because the new behaviour to resolve displayNames is to first look at the SubPluginFeatures, which, without override, returns the superior plugin's name (Plugin.cpp) Additional: * PluginFactory.h: Allow setting up search paths without discovering plugins yet * Plugin.h: Add full documentation (should be checked) --- include/Effect.h | 7 - include/Engine.h | 4 + include/Instrument.h | 15 +- include/InstrumentTrack.h | 5 +- include/Ladspa2LMMS.h | 2 + include/Plugin.h | 169 +++++++++++++++--- include/PluginBrowser.h | 5 +- include/PluginFactory.h | 24 ++- plugins/FreeBoy/FreeBoy.cpp | 4 +- plugins/GigPlayer/GigPlayer.cpp | 4 +- .../LadspaEffect/LadspaSubPluginFeatures.cpp | 10 ++ .../LadspaEffect/LadspaSubPluginFeatures.h | 12 +- plugins/OpulenZ/OpulenZ.cpp | 4 +- plugins/Xpressive/Xpressive.cpp | 4 +- .../audio_file_processor.cpp | 5 +- plugins/bit_invader/bit_invader.cpp | 4 +- plugins/carlapatchbay/carlapatchbay.cpp | 4 +- plugins/carlarack/carlarack.cpp | 4 +- plugins/kicker/kicker.cpp | 4 +- plugins/lb302/lb302.cpp | 4 +- plugins/monstro/Monstro.cpp | 4 +- plugins/nes/Nes.cpp | 4 +- plugins/organic/organic.cpp | 4 +- plugins/patman/patman.cpp | 4 +- plugins/sf2_player/sf2_player.cpp | 4 +- plugins/sfxr/sfxr.cpp | 4 +- plugins/sid/sid_instrument.cpp | 5 +- plugins/stk/mallets/mallets.cpp | 4 +- .../triple_oscillator/TripleOscillator.cpp | 4 +- plugins/vestige/vestige.cpp | 4 +- plugins/vibed/vibed.cpp | 4 +- plugins/watsyn/Watsyn.cpp | 4 +- plugins/zynaddsubfx/ZynAddSubFx.cpp | 5 +- src/core/Effect.cpp | 5 +- src/core/Engine.cpp | 21 +++ src/core/Instrument.cpp | 27 ++- src/core/LadspaManager.cpp | 2 +- src/core/Plugin.cpp | 137 +++++++++++++- src/core/PluginFactory.cpp | 109 +++++++---- src/core/PresetPreviewPlayHandle.cpp | 4 +- src/gui/EffectSelectDialog.cpp | 100 +++++------ src/gui/FileBrowser.cpp | 11 +- src/gui/InstrumentView.cpp | 2 +- src/gui/PluginBrowser.cpp | 44 +++-- src/gui/TrackContainerView.cpp | 8 +- src/gui/widgets/TrackLabelButton.cpp | 8 +- src/tracks/InstrumentTrack.cpp | 31 +++- 47 files changed, 612 insertions(+), 245 deletions(-) diff --git a/include/Effect.h b/include/Effect.h index d6aa04d97..3d765fdc0 100644 --- a/include/Effect.h +++ b/include/Effect.h @@ -148,11 +148,6 @@ public: m_noRun = _state; } - inline const Descriptor::SubPluginFeatures::Key & key() const - { - return m_key; - } - EffectChain * effectChain() const { return m_parent; @@ -201,8 +196,6 @@ private: sampleFrame * _dst_buf, sample_rate_t _dst_sr, const f_cnt_t _frames ); - Descriptor::SubPluginFeatures::Key m_key; - ch_cnt_t m_processors; bool m_okay; diff --git a/include/Engine.h b/include/Engine.h index fc2547996..18960ec8f 100644 --- a/include/Engine.h +++ b/include/Engine.h @@ -111,6 +111,9 @@ public: return s_instanceOfMe; } + static void setDndPluginKey(void* newKey); + static void* pickDndPluginKey(); + signals: void initProgress(const QString &msg); @@ -137,6 +140,7 @@ private: static DummyTrackContainer * s_dummyTC; static Ladspa2LMMS * s_ladspaManager; + static void* s_dndPluginKey; // even though most methods are static, an instance is needed for Qt slots/signals static LmmsCore * s_instanceOfMe; diff --git a/include/Instrument.h b/include/Instrument.h index a373ae4ac..c3df04729 100644 --- a/include/Instrument.h +++ b/include/Instrument.h @@ -55,8 +55,9 @@ public: Q_DECLARE_FLAGS(Flags, Flag); - Instrument( InstrumentTrack * _instrument_track, - const Descriptor * _descriptor ); + Instrument(InstrumentTrack * _instrument_track, + const Descriptor * _descriptor, + const Descriptor::SubPluginFeatures::Key * key = nullptr); virtual ~Instrument() = default; // -------------------------------------------------------------------- @@ -113,10 +114,12 @@ public: // provided functions: // -------------------------------------------------------------------- - // instantiate instrument-plugin with given name or return NULL - // on failure - static Instrument * instantiate( const QString & _plugin_name, - InstrumentTrack * _instrument_track ); + //! instantiate instrument-plugin with given name or return NULL + //! on failure + static Instrument * instantiate(const QString & _plugin_name, + InstrumentTrack * _instrument_track, + const Plugin::Descriptor::SubPluginFeatures::Key* key, + bool keyFromDnd = false); virtual bool isFromTrack( const Track * _track ) const; diff --git a/include/InstrumentTrack.h b/include/InstrumentTrack.h index fb12e825a..dd78b2f9a 100644 --- a/include/InstrumentTrack.h +++ b/include/InstrumentTrack.h @@ -36,6 +36,7 @@ #include "Piano.h" #include "PianoView.h" #include "Pitch.h" +#include "Plugin.h" #include "Track.h" @@ -146,7 +147,9 @@ public: // load instrument whose name matches given one - Instrument * loadInstrument( const QString & _instrument_name ); + Instrument * loadInstrument(const QString & _instrument_name, + const Plugin::Descriptor::SubPluginFeatures::Key* key = nullptr, + bool keyFromDnd = false); AudioPort * audioPort() { diff --git a/include/Ladspa2LMMS.h b/include/Ladspa2LMMS.h index 14899487c..28fa25b89 100644 --- a/include/Ladspa2LMMS.h +++ b/include/Ladspa2LMMS.h @@ -30,6 +30,8 @@ #include "LadspaManager.h" +//! Class responsible for sorting found plugins (by LadspaManager) +//! into categories class LMMS_EXPORT Ladspa2LMMS : public LadspaManager { public: diff --git a/include/Plugin.h b/include/Plugin.h index a2cc7d696..034d8a06c 100644 --- a/include/Plugin.h +++ b/include/Plugin.h @@ -40,7 +40,27 @@ class PixmapLoader; class PluginView; class AutomatableModel; +/** + Abstract representation of a plugin + Such a plugin can be an Instrument, Effect, Tool plugin etc. + + Plugins have descriptors, containing meta info, which is used especially + by PluginFactory and friends. + + There are also Plugin keys (class Key, confusingly under + SubPluginFeatures), which contain pointers to the plugin descriptor. + + Some plugins have sub plugins, e.g. there is one CALF Plugin and for + each CALF effect, there is a CALF sub plugin. For those plugins, there + are keys for each sub plugin. These keys also link to the superior + Plugin::Descriptor. Additionally, they contain attributes that help the + superior Plugin saving them and recognizing them when loading. + + In case of sub plugins, the Descriptor has SubPluginFeatures. Those + are a bit like values to the sub plugins' keys (in terms of a key-value- + map). +*/ class LMMS_EXPORT Plugin : public Model, public JournallingObject { MM_OPERATORS @@ -59,9 +79,9 @@ public: Undefined = 255 } ; - // descriptor holds information about a plugin - every external plugin - // has to instantiate such a descriptor in an extern "C"-section so that - // the plugin-loader is able to access information about the plugin + //! Descriptor holds information about a plugin - every external plugin + //! has to instantiate such a Descriptor in an extern "C"-section so that + //! the plugin-loader is able to access information about the plugin struct Descriptor { const char * name; @@ -71,23 +91,49 @@ public: int version; PluginTypes type; const PixmapLoader * logo; - const char * supportedFileTypes; + const char * supportedFileTypes; //!< csv list of extensions inline bool supportsFileType( const QString& extension ) const { return QString( supportedFileTypes ).split( QChar( ',' ) ).contains( extension ); } + /** + Access to non-key-data of a sub plugin + + If you consider sub plugin keys as keys in a + key-value-map, this is the lookup for the corresponding + values. In order to have flexibility between different + plugin APIs, this is rather an array of fixed data, + but a bunch of virtual functions taking the key and + returning some values (or modifying objects of other + classes). + */ class LMMS_EXPORT SubPluginFeatures { public: + /** + Key reference a Plugin::Descriptor, and, + if the plugin has sub plugins, also reference + its sub plugin (using the attributes). + When keys are saved, those attributes are + written to XML in order to find the right sub + plugin when realoading. + + @note Any data that is not required to reference + the right Plugin or sub plugin should + not be here (but rather in + SubPluginFeatures, which are like values + in a key-value map). + */ struct Key { typedef QMap AttributeMap; inline Key( const Plugin::Descriptor * desc = NULL, - const QString & name = QString(), - const AttributeMap & am = AttributeMap() ) + const QString & name = QString(), + const AttributeMap & am = AttributeMap() + ) : desc( desc ), name( name ), @@ -101,12 +147,28 @@ public: inline bool isValid() const { - return desc != NULL && name.isNull() == false; + return desc != nullptr; } + //! Key to subplugin: reference to parent descriptor + //! Key to plugin: reference to its descriptor const Plugin::Descriptor* desc; + //! Descriptive name like "Calf Phaser". + //! Not required for key lookup and not saved + //! only used sometimes to temporary store descriptive names + //! @todo This is a bug, there should be a function + //! in SubPluginFeatures (to get the name) instead QString name; + //! Attributes that make up the key and identify + //! the sub plugin. They are being loaded and saved AttributeMap attributes; + + // helper functions to retrieve data that is + // not part of the key, but mapped via desc->subPluginFeatures + const char* additionalFileExtensions() const; + const char* displayName() const; + const char* description() const; + const PixmapLoader* logo() const; } ; typedef QList KeyList; @@ -125,11 +187,42 @@ public: { } + //! While PluginFactory only collects the plugins, + //! this function is used by widgets like EffectSelectDialog + //! to find all possible sub plugins virtual void listSubPluginKeys( const Plugin::Descriptor *, KeyList & ) const { } + private: + // You can add stuff values mapped by "Key" below + // The defaults are sane, i.e. redirect to sub plugins + // supererior descriptor + + virtual const char* additionalFileExtensions(const Key&) const + { + return nullptr; + } + + virtual const char* displayName(const Key& k) const + { + return k.isValid() + ? k.desc->displayName + : k.name.toUtf8().data(); + } + + virtual const char* description(const Key& k) const + { + return k.isValid() ? k.desc->description : ""; + } + + virtual const PixmapLoader* logo(const Key& k) const + { + Q_ASSERT(k.desc); + return k.desc->logo; + } + protected: const Plugin::PluginTypes m_type; } ; @@ -140,48 +233,66 @@ public: // typedef a list so we can easily work with list of plugin descriptors typedef QList DescriptorList; - // contructor of a plugin - Plugin( const Descriptor * descriptor, Model * parent ); + //! Constructor of a plugin + //! @param key Sub plugins must pass a key here, optional otherwise. + //! See the key() function + Plugin(const Descriptor * descriptor, Model * parent, + const Descriptor::SubPluginFeatures::Key *key = nullptr); virtual ~Plugin(); - // returns display-name out of descriptor - virtual QString displayName() const - { - return Model::displayName().isEmpty() - ? m_descriptor->displayName - : Model::displayName(); - } + //! Return display-name out of sub plugin or descriptor + virtual QString displayName() const; - // return plugin-type + //! Return logo out of sub plugin or descriptor + const PixmapLoader *logo() const; + + //! Return plugin type inline PluginTypes type( void ) const { return m_descriptor->type; } - // return plugin-descriptor for further information + //! Return plugin Descriptor inline const Descriptor * descriptor() const { return m_descriptor; } - // can be called if a file matching supportedFileTypes should be - // loaded/processed with the help of this plugin + //! Return the key referencing this plugin. If the Plugin has no + //! sub plugin features, the key is pretty useless. If it has, + //! this key will also contain the sub plugin attributes, and will be + //! a key to those SubPluginFeatures. + inline const Descriptor::SubPluginFeatures::Key & key() const + { + return m_key; + } + + //! Can be called if a file matching supportedFileTypes should be + //! loaded/processed with the help of this plugin virtual void loadFile( const QString & file ); - // Called if external source needs to change something but we cannot - // reference the class header. Should return null if not key not found. + //! Called if external source needs to change something but we cannot + //! reference the class header. Should return null if not key not found. virtual AutomatableModel* childModel( const QString & modelName ); - // returns an instance of a plugin whose name matches to given one - // if specified plugin couldn't be loaded, it creates a dummy-plugin - static Plugin * instantiate( const QString& pluginName, Model * parent, void * data ); + //! Overload if the argument passed to the plugin is a subPluginKey + //! If you can not pass the key and are aware that it's stored in + //! Engine::pickDndPluginKey(), use this function, too + static Plugin * instantiateWithKey(const QString& pluginName, Model * parent, + const Descriptor::SubPluginFeatures::Key *key, + bool keyFromDnd = false); - // create a view for the model + //! Return an instance of a plugin whose name matches to given one + //! if specified plugin couldn't be loaded, it creates a dummy-plugin + //! @param data Anything the plugin expects. If this is a pointer to a sub plugin key, + //! use instantiateWithKey instead + static Plugin * instantiate(const QString& pluginName, Model * parent, void *data); + + //! Create a view for the model PluginView * createView( QWidget * parent ); - protected: - // create a view for the model + //! Create a view for the model virtual PluginView* instantiateView( QWidget * ) = 0; void collectErrorForUI( QString errMsg ); @@ -189,6 +300,8 @@ protected: private: const Descriptor * m_descriptor; + Descriptor::SubPluginFeatures::Key m_key; + // pointer to instantiation-function in plugin typedef Plugin * ( * InstantiationHook )( Model * , void * ); diff --git a/include/PluginBrowser.h b/include/PluginBrowser.h index 75c7cd291..f7c46db72 100644 --- a/include/PluginBrowser.h +++ b/include/PluginBrowser.h @@ -60,7 +60,8 @@ class PluginDescWidget : public QWidget { Q_OBJECT public: - PluginDescWidget( const Plugin::Descriptor & _pd, QWidget * _parent ); + typedef Plugin::Descriptor::SubPluginFeatures::Key PluginKey; + PluginDescWidget( const PluginKey & _pk, QWidget * _parent ); protected: @@ -72,7 +73,7 @@ protected: private: constexpr static int DEFAULT_HEIGHT{24}; - const Plugin::Descriptor & m_pluginDescriptor; + PluginKey m_pluginKey; QPixmap m_logo; bool m_mouseOver; diff --git a/include/PluginFactory.h b/include/PluginFactory.h index 56d32c4e4..17b178108 100644 --- a/include/PluginFactory.h +++ b/include/PluginFactory.h @@ -26,10 +26,13 @@ #define PLUGINFACTORY_H #include +#include #include #include #include +#include +#include #include "lmms_export.h" #include "Plugin.h" @@ -41,12 +44,10 @@ class LMMS_EXPORT PluginFactory public: struct PluginInfo { - PluginInfo() : library(nullptr), descriptor(nullptr) {} - const QString name() const; QFileInfo file; - std::shared_ptr library; - Plugin::Descriptor* descriptor; + std::shared_ptr library = nullptr; + Plugin::Descriptor* descriptor = nullptr; bool isNull() const {return ! library;} }; @@ -56,6 +57,8 @@ public: PluginFactory(); ~PluginFactory(); + static void setupSearchPaths(); + /// Returns the singleton instance of PluginFactory. You won't need to call /// this directly, use pluginFactory instead. static PluginFactory* instance(); @@ -64,10 +67,17 @@ public: const Plugin::DescriptorList descriptors() const; const Plugin::DescriptorList descriptors(Plugin::PluginTypes type) const; + struct PluginInfoAndKey + { + PluginInfo info; + Plugin::Descriptor::SubPluginFeatures::Key key; + bool isNull() const { return info.isNull(); } + }; + /// Returns a list of all found plugins' PluginFactory::PluginInfo objects. const PluginInfoList& pluginInfos() const; /// Returns a plugin that support the given file extension - const PluginInfo pluginSupportingExtension(const QString& ext); + const PluginInfoAndKey pluginSupportingExtension(const QString& ext); /// Returns the PluginInfo object of the plugin with the given name. /// If the plugin is not found, an empty PluginInfo is returned (use @@ -84,7 +94,9 @@ public slots: private: DescriptorMap m_descriptors; PluginInfoList m_pluginInfos; - QMap m_pluginByExt; + + QMap m_pluginByExt; + QVector m_garbage; //!< cleaned up at destruction QHash m_errors; diff --git a/plugins/FreeBoy/FreeBoy.cpp b/plugins/FreeBoy/FreeBoy.cpp index dd05444a6..7e5252344 100644 --- a/plugins/FreeBoy/FreeBoy.cpp +++ b/plugins/FreeBoy/FreeBoy.cpp @@ -728,10 +728,10 @@ extern "C" { // necessary for getting instance out of shared lib -PLUGIN_EXPORT Plugin * lmms_plugin_main( Model *, void * _data ) +PLUGIN_EXPORT Plugin * lmms_plugin_main( Model *m, void * ) { return( new FreeBoyInstrument( - static_cast( _data ) ) ); + static_cast( m ) ) ); } diff --git a/plugins/GigPlayer/GigPlayer.cpp b/plugins/GigPlayer/GigPlayer.cpp index bf5990252..200439dae 100644 --- a/plugins/GigPlayer/GigPlayer.cpp +++ b/plugins/GigPlayer/GigPlayer.cpp @@ -1390,9 +1390,9 @@ extern "C" { // necessary for getting instance out of shared lib -PLUGIN_EXPORT Plugin * lmms_plugin_main( Model *, void * _data ) +PLUGIN_EXPORT Plugin * lmms_plugin_main( Model *m, void * ) { - return new GigInstrument( static_cast( _data ) ); + return new GigInstrument( static_cast( m ) ); } } diff --git a/plugins/LadspaEffect/LadspaSubPluginFeatures.cpp b/plugins/LadspaEffect/LadspaSubPluginFeatures.cpp index e9344e943..1b055fe73 100644 --- a/plugins/LadspaEffect/LadspaSubPluginFeatures.cpp +++ b/plugins/LadspaEffect/LadspaSubPluginFeatures.cpp @@ -44,6 +44,16 @@ LadspaSubPluginFeatures::LadspaSubPluginFeatures( Plugin::PluginTypes _type ) : +const char *LadspaSubPluginFeatures::displayName(const Plugin::Descriptor::SubPluginFeatures::Key &k) const +{ + const ladspa_key_t & lkey = subPluginKeyToLadspaKey(&k); + Ladspa2LMMS * lm = Engine::getLADSPAManager(); + return lm->getName(lkey).toUtf8().data(); +} + + + + void LadspaSubPluginFeatures::fillDescriptionWidget( QWidget * _parent, const Key * _key ) const { diff --git a/plugins/LadspaEffect/LadspaSubPluginFeatures.h b/plugins/LadspaEffect/LadspaSubPluginFeatures.h index 904c8133b..b7613827b 100644 --- a/plugins/LadspaEffect/LadspaSubPluginFeatures.h +++ b/plugins/LadspaEffect/LadspaSubPluginFeatures.h @@ -25,8 +25,8 @@ * */ -#ifndef _LADSPA_SUBPLUGIN_FEATURES_H -#define _LADSPA_SUBPLUGIN_FEATURES_H +#ifndef LADSPA_SUBPLUGIN_FEATURES_H +#define LADSPA_SUBPLUGIN_FEATURES_H #include "LadspaManager.h" #include "Plugin.h" @@ -37,11 +37,13 @@ class LadspaSubPluginFeatures : public Plugin::Descriptor::SubPluginFeatures public: LadspaSubPluginFeatures( Plugin::PluginTypes _type ); - virtual void fillDescriptionWidget( QWidget * _parent, - const Key * _key ) const; + const char* displayName(const Key& k) const override; + void fillDescriptionWidget( QWidget * _parent, + const Key * _key ) const override; virtual void listSubPluginKeys( const Plugin::Descriptor * _desc, - KeyList & _kl ) const; + KeyList & _kl ) const override; + static ladspa_key_t subPluginKeyToLadspaKey( const Key * _key ); diff --git a/plugins/OpulenZ/OpulenZ.cpp b/plugins/OpulenZ/OpulenZ.cpp index d8d5f3e26..8e5b7f098 100644 --- a/plugins/OpulenZ/OpulenZ.cpp +++ b/plugins/OpulenZ/OpulenZ.cpp @@ -79,9 +79,9 @@ Plugin::Descriptor PLUGIN_EXPORT opulenz_plugin_descriptor = }; // necessary for getting instance out of shared lib -PLUGIN_EXPORT Plugin * lmms_plugin_main( Model *, void * _data ) +PLUGIN_EXPORT Plugin * lmms_plugin_main( Model *m, void * ) { - return( new OpulenzInstrument( static_cast( _data ) ) ); + return( new OpulenzInstrument( static_cast( m ) ) ); } } diff --git a/plugins/Xpressive/Xpressive.cpp b/plugins/Xpressive/Xpressive.cpp index cb0293710..f0154f75b 100644 --- a/plugins/Xpressive/Xpressive.cpp +++ b/plugins/Xpressive/Xpressive.cpp @@ -877,8 +877,8 @@ void XpressiveView::helpClicked() { extern "C" { // necessary for getting instance out of shared lib -PLUGIN_EXPORT Plugin * lmms_plugin_main(Model *, void * _data) { - return (new Xpressive(static_cast(_data))); +PLUGIN_EXPORT Plugin * lmms_plugin_main(Model *m, void *) { + return (new Xpressive(static_cast(m))); } } diff --git a/plugins/audio_file_processor/audio_file_processor.cpp b/plugins/audio_file_processor/audio_file_processor.cpp index 6c080f7c9..ce14e1ac2 100644 --- a/plugins/audio_file_processor/audio_file_processor.cpp +++ b/plugins/audio_file_processor/audio_file_processor.cpp @@ -1277,10 +1277,9 @@ extern "C" { // necessary for getting instance out of shared lib -PLUGIN_EXPORT Plugin * lmms_plugin_main( Model *, void * _data ) +PLUGIN_EXPORT Plugin * lmms_plugin_main(Model * model, void *) { - return new audioFileProcessor( - static_cast( _data ) ); + return new audioFileProcessor(static_cast(model)); } diff --git a/plugins/bit_invader/bit_invader.cpp b/plugins/bit_invader/bit_invader.cpp index ecc77be0b..c370c409c 100644 --- a/plugins/bit_invader/bit_invader.cpp +++ b/plugins/bit_invader/bit_invader.cpp @@ -584,9 +584,9 @@ extern "C" { // necessary for getting instance out of shared lib -PLUGIN_EXPORT Plugin * lmms_plugin_main( Model *, void * _data ) +PLUGIN_EXPORT Plugin * lmms_plugin_main( Model *m, void * ) { - return( new bitInvader( static_cast( _data ) ) ); + return( new bitInvader( static_cast( m ) ) ); } diff --git a/plugins/carlapatchbay/carlapatchbay.cpp b/plugins/carlapatchbay/carlapatchbay.cpp index 69c71ce68..60e022156 100644 --- a/plugins/carlapatchbay/carlapatchbay.cpp +++ b/plugins/carlapatchbay/carlapatchbay.cpp @@ -43,9 +43,9 @@ Plugin::Descriptor PLUGIN_EXPORT carlapatchbay_plugin_descriptor = NULL } ; -PLUGIN_EXPORT Plugin* lmms_plugin_main(Model*, void* data) +PLUGIN_EXPORT Plugin* lmms_plugin_main(Model* m, void*) { - return new CarlaInstrument(static_cast(data), &carlapatchbay_plugin_descriptor, true); + return new CarlaInstrument(static_cast(m), &carlapatchbay_plugin_descriptor, true); } } diff --git a/plugins/carlarack/carlarack.cpp b/plugins/carlarack/carlarack.cpp index 8bc7d372d..d057eff83 100644 --- a/plugins/carlarack/carlarack.cpp +++ b/plugins/carlarack/carlarack.cpp @@ -43,9 +43,9 @@ Plugin::Descriptor PLUGIN_EXPORT carlarack_plugin_descriptor = NULL } ; -PLUGIN_EXPORT Plugin* lmms_plugin_main(Model*, void* data) +PLUGIN_EXPORT Plugin* lmms_plugin_main(Model* m, void*) { - return new CarlaInstrument(static_cast(data), &carlarack_plugin_descriptor, false); + return new CarlaInstrument(static_cast(m), &carlarack_plugin_descriptor, false); } } diff --git a/plugins/kicker/kicker.cpp b/plugins/kicker/kicker.cpp index 2087cf880..d204babab 100644 --- a/plugins/kicker/kicker.cpp +++ b/plugins/kicker/kicker.cpp @@ -367,9 +367,9 @@ extern "C" { // necessary for getting instance out of shared lib -PLUGIN_EXPORT Plugin * lmms_plugin_main( Model *, void * _data ) +PLUGIN_EXPORT Plugin * lmms_plugin_main( Model * m, void * ) { - return new kickerInstrument( static_cast( _data ) ); + return new kickerInstrument( static_cast( m ) ); } diff --git a/plugins/lb302/lb302.cpp b/plugins/lb302/lb302.cpp index d32f14e88..e556e42bf 100644 --- a/plugins/lb302/lb302.cpp +++ b/plugins/lb302/lb302.cpp @@ -1029,11 +1029,11 @@ extern "C" { // necessary for getting instance out of shared lib -PLUGIN_EXPORT Plugin * lmms_plugin_main( Model *, void * _data ) +PLUGIN_EXPORT Plugin * lmms_plugin_main( Model * m, void * ) { return( new lb302Synth( - static_cast( _data ) ) ); + static_cast( m ) ) ); } diff --git a/plugins/monstro/Monstro.cpp b/plugins/monstro/Monstro.cpp index baee242bb..fb089aa0c 100644 --- a/plugins/monstro/Monstro.cpp +++ b/plugins/monstro/Monstro.cpp @@ -1828,9 +1828,9 @@ extern "C" { // necessary for getting instance out of shared lib -PLUGIN_EXPORT Plugin * lmms_plugin_main( Model *, void * _data ) +PLUGIN_EXPORT Plugin * lmms_plugin_main( Model *m, void * ) { - return new MonstroInstrument( static_cast( _data ) ); + return new MonstroInstrument( static_cast( m ) ); } diff --git a/plugins/nes/Nes.cpp b/plugins/nes/Nes.cpp index 5b34dcb1f..ba64edb06 100644 --- a/plugins/nes/Nes.cpp +++ b/plugins/nes/Nes.cpp @@ -918,9 +918,9 @@ extern "C" { // necessary for getting instance out of shared lib -PLUGIN_EXPORT Plugin * lmms_plugin_main( Model *, void * _data ) +PLUGIN_EXPORT Plugin * lmms_plugin_main( Model *m, void * _data ) { - return( new NesInstrument( static_cast( _data ) ) ); + return( new NesInstrument( static_cast( m ) ) ); } diff --git a/plugins/organic/organic.cpp b/plugins/organic/organic.cpp index b6d45c8d3..63aee0dc6 100644 --- a/plugins/organic/organic.cpp +++ b/plugins/organic/organic.cpp @@ -636,9 +636,9 @@ extern "C" { // necessary for getting instance out of shared lib -PLUGIN_EXPORT Plugin * lmms_plugin_main( Model *, void * _data ) +PLUGIN_EXPORT Plugin * lmms_plugin_main( Model *m, void * ) { - return( new organicInstrument( static_cast( _data ) ) ); + return( new organicInstrument( static_cast( m ) ) ); } diff --git a/plugins/patman/patman.cpp b/plugins/patman/patman.cpp index 6ab7adce7..0ea9968da 100644 --- a/plugins/patman/patman.cpp +++ b/plugins/patman/patman.cpp @@ -66,9 +66,9 @@ Plugin::Descriptor PLUGIN_EXPORT patman_plugin_descriptor = // necessary for getting instance out of shared lib -PLUGIN_EXPORT Plugin * lmms_plugin_main( Model *, void * _data ) +PLUGIN_EXPORT Plugin * lmms_plugin_main( Model *m, void * ) { - return new patmanInstrument( static_cast( _data ) ); + return new patmanInstrument( static_cast( m ) ); } } diff --git a/plugins/sf2_player/sf2_player.cpp b/plugins/sf2_player/sf2_player.cpp index 4bf75777c..138e8165d 100644 --- a/plugins/sf2_player/sf2_player.cpp +++ b/plugins/sf2_player/sf2_player.cpp @@ -1150,9 +1150,9 @@ extern "C" { // necessary for getting instance out of shared lib -PLUGIN_EXPORT Plugin * lmms_plugin_main( Model *, void * _data ) +PLUGIN_EXPORT Plugin * lmms_plugin_main( Model *m, void * ) { - return new sf2Instrument( static_cast( _data ) ); + return new sf2Instrument( static_cast( m ) ); } diff --git a/plugins/sfxr/sfxr.cpp b/plugins/sfxr/sfxr.cpp index c99f46f88..2718d66d7 100644 --- a/plugins/sfxr/sfxr.cpp +++ b/plugins/sfxr/sfxr.cpp @@ -1122,9 +1122,9 @@ extern "C" { // necessary for getting instance out of shared lib -PLUGIN_EXPORT Plugin * lmms_plugin_main( Model*, void* data ) +PLUGIN_EXPORT Plugin * lmms_plugin_main( Model* m, void* ) { - return new sfxrInstrument( static_cast( data ) ); + return new sfxrInstrument( static_cast( m ) ); } diff --git a/plugins/sid/sid_instrument.cpp b/plugins/sid/sid_instrument.cpp index 2eb46be56..024e9ae61 100644 --- a/plugins/sid/sid_instrument.cpp +++ b/plugins/sid/sid_instrument.cpp @@ -792,10 +792,9 @@ extern "C" { // necessary for getting instance out of shared lib -PLUGIN_EXPORT Plugin * lmms_plugin_main( Model *, void * _data ) +PLUGIN_EXPORT Plugin * lmms_plugin_main( Model *m, void * ) { - return( new sidInstrument( - static_cast( _data ) ) ); + return( new sidInstrument( static_cast( m ) ) ); } diff --git a/plugins/stk/mallets/mallets.cpp b/plugins/stk/mallets/mallets.cpp index 7111bcdaa..ee1e1fbc3 100644 --- a/plugins/stk/mallets/mallets.cpp +++ b/plugins/stk/mallets/mallets.cpp @@ -756,9 +756,9 @@ extern "C" { // necessary for getting instance out of shared lib -PLUGIN_EXPORT Plugin * lmms_plugin_main( Model *, void * _data ) +PLUGIN_EXPORT Plugin * lmms_plugin_main( Model * m, void * ) { - return new malletsInstrument( static_cast( _data ) ); + return new malletsInstrument( static_cast( m ) ); } diff --git a/plugins/triple_oscillator/TripleOscillator.cpp b/plugins/triple_oscillator/TripleOscillator.cpp index 6b644fd46..a883f75fa 100644 --- a/plugins/triple_oscillator/TripleOscillator.cpp +++ b/plugins/triple_oscillator/TripleOscillator.cpp @@ -723,9 +723,9 @@ extern "C" { // necessary for getting instance out of shared lib -PLUGIN_EXPORT Plugin * lmms_plugin_main( Model *, void * _data ) +PLUGIN_EXPORT Plugin * lmms_plugin_main( Model* model, void * ) { - return new TripleOscillator( static_cast( _data ) ); + return new TripleOscillator( static_cast( model ) ); } } diff --git a/plugins/vestige/vestige.cpp b/plugins/vestige/vestige.cpp index 73ea33a51..fe91358b3 100644 --- a/plugins/vestige/vestige.cpp +++ b/plugins/vestige/vestige.cpp @@ -1167,9 +1167,9 @@ extern "C" { // necessary for getting instance out of shared lib -Q_DECL_EXPORT Plugin * lmms_plugin_main( Model *, void * _data ) +Q_DECL_EXPORT Plugin * lmms_plugin_main( Model *m, void * ) { - return new vestigeInstrument( static_cast( _data ) ); + return new vestigeInstrument( static_cast( m ) ); } diff --git a/plugins/vibed/vibed.cpp b/plugins/vibed/vibed.cpp index f33a90428..bde5c8917 100644 --- a/plugins/vibed/vibed.cpp +++ b/plugins/vibed/vibed.cpp @@ -682,9 +682,9 @@ extern "C" { // necessary for getting instance out of shared lib -PLUGIN_EXPORT Plugin * lmms_plugin_main( Model *, void * _data ) +PLUGIN_EXPORT Plugin * lmms_plugin_main( Model *m, void * ) { - return( new vibed( static_cast( _data ) ) ); + return( new vibed( static_cast( m ) ) ); } diff --git a/plugins/watsyn/Watsyn.cpp b/plugins/watsyn/Watsyn.cpp index 0122b293a..2787e39b7 100644 --- a/plugins/watsyn/Watsyn.cpp +++ b/plugins/watsyn/Watsyn.cpp @@ -1279,9 +1279,9 @@ extern "C" { // necessary for getting instance out of shared lib -PLUGIN_EXPORT Plugin * lmms_plugin_main( Model *, void * _data ) +PLUGIN_EXPORT Plugin * lmms_plugin_main( Model *m, void * ) { - return( new WatsynInstrument( static_cast( _data ) ) ); + return( new WatsynInstrument( static_cast( m ) ) ); } diff --git a/plugins/zynaddsubfx/ZynAddSubFx.cpp b/plugins/zynaddsubfx/ZynAddSubFx.cpp index ad8d9a78c..8235ad147 100644 --- a/plugins/zynaddsubfx/ZynAddSubFx.cpp +++ b/plugins/zynaddsubfx/ZynAddSubFx.cpp @@ -652,10 +652,9 @@ extern "C" { // necessary for getting instance out of shared lib -PLUGIN_EXPORT Plugin * lmms_plugin_main( Model *, void * _data ) +PLUGIN_EXPORT Plugin * lmms_plugin_main(Model * m, void *) { - - return new ZynAddSubFxInstrument( static_cast( _data ) ); + return new ZynAddSubFxInstrument(static_cast(m)); } diff --git a/src/core/Effect.cpp b/src/core/Effect.cpp index 8cdcc9c60..c84297753 100644 --- a/src/core/Effect.cpp +++ b/src/core/Effect.cpp @@ -36,9 +36,8 @@ Effect::Effect( const Plugin::Descriptor * _desc, Model * _parent, const Descriptor::SubPluginFeatures::Key * _key ) : - Plugin( _desc, _parent ), + Plugin( _desc, _parent, _key ), m_parent( NULL ), - m_key( _key ? *_key : Descriptor::SubPluginFeatures::Key() ), m_processors( 1 ), m_okay( true ), m_noRun( false ), @@ -117,7 +116,7 @@ Effect * Effect::instantiate( const QString& pluginName, Model * _parent, Descriptor::SubPluginFeatures::Key * _key ) { - Plugin * p = Plugin::instantiate( pluginName, _parent, _key ); + Plugin * p = Plugin::instantiateWithKey( pluginName, _parent, _key ); // check whether instantiated plugin is an effect if( dynamic_cast( p ) != NULL ) { diff --git a/src/core/Engine.cpp b/src/core/Engine.cpp index a53abbe5d..7f89c20f3 100644 --- a/src/core/Engine.cpp +++ b/src/core/Engine.cpp @@ -41,6 +41,7 @@ BBTrackContainer * LmmsCore::s_bbTrackContainer = NULL; Song * LmmsCore::s_song = NULL; ProjectJournal * LmmsCore::s_projectJournal = NULL; Ladspa2LMMS * LmmsCore::s_ladspaManager = NULL; +void* LmmsCore::s_dndPluginKey = nullptr; DummyTrackContainer * LmmsCore::s_dummyTC = NULL; @@ -112,4 +113,24 @@ void LmmsCore::updateFramesPerTick() DefaultTicksPerTact / s_song->getTempo(); } + + + +void LmmsCore::setDndPluginKey(void *newKey) +{ + assert(static_cast(newKey)); + s_dndPluginKey = newKey; +} + + + + +void *LmmsCore::pickDndPluginKey() +{ + return s_dndPluginKey; +} + + + + LmmsCore * LmmsCore::s_instanceOfMe = NULL; diff --git a/src/core/Instrument.cpp b/src/core/Instrument.cpp index 534bb783a..ba608da14 100644 --- a/src/core/Instrument.cpp +++ b/src/core/Instrument.cpp @@ -27,9 +27,10 @@ #include "DummyInstrument.h" -Instrument::Instrument( InstrumentTrack * _instrument_track, - const Descriptor * _descriptor ) : - Plugin( _descriptor, NULL/* _instrument_track*/ ), +Instrument::Instrument(InstrumentTrack * _instrument_track, + const Descriptor * _descriptor, + const Descriptor::SubPluginFeatures::Key *key) : + Plugin(_descriptor, NULL/* _instrument_track*/, key), m_instrumentTrack( _instrument_track ) { } @@ -56,19 +57,15 @@ f_cnt_t Instrument::beatLen( NotePlayHandle * ) const -Instrument * Instrument::instantiate( const QString & _plugin_name, - InstrumentTrack * _instrument_track ) +Instrument *Instrument::instantiate(const QString &_plugin_name, + InstrumentTrack *_instrument_track, const Descriptor::SubPluginFeatures::Key *key, bool keyFromDnd) { - Plugin * p = Plugin::instantiate( _plugin_name, _instrument_track, - _instrument_track ); - // check whether instantiated plugin is an instrument - if( dynamic_cast( p ) != NULL ) - { - // everything ok, so return pointer - return dynamic_cast( p ); - } - - // not quite... so delete plugin and return dummy instrument + if(keyFromDnd) + Q_ASSERT(!key); + // copy from above // TODO! common cleaner func + Plugin * p = Plugin::instantiateWithKey(_plugin_name, _instrument_track, key, keyFromDnd); + if(dynamic_cast(p)) + return dynamic_cast(p); delete p; return( new DummyInstrument( _instrument_track ) ); } diff --git a/src/core/LadspaManager.cpp b/src/core/LadspaManager.cpp index 4336e50a5..febbe5a91 100644 --- a/src/core/LadspaManager.cpp +++ b/src/core/LadspaManager.cpp @@ -40,7 +40,7 @@ LadspaManager::LadspaManager() { // Make sure plugin search paths are set up - PluginFactory::instance(); + PluginFactory::setupSearchPaths(); QStringList ladspaDirectories = QString( getenv( "LADSPA_PATH" ) ). split( LADSPA_PATH_SEPERATOR ); diff --git a/src/core/Plugin.cpp b/src/core/Plugin.cpp index b479d7d8d..e7f80b5ab 100644 --- a/src/core/Plugin.cpp +++ b/src/core/Plugin.cpp @@ -22,11 +22,15 @@ * */ +#include #include #include #include #include +// comment separator to prevent clang's header sorting +#include "lmmsconfig.h" + #include "Plugin.h" #include "embed.h" #include "Engine.h" @@ -53,10 +57,12 @@ static Plugin::Descriptor dummyPluginDescriptor = -Plugin::Plugin( const Descriptor * descriptor, Model * parent ) : - Model( parent ), +Plugin::Plugin(const Descriptor * descriptor, Model * parent, const + Descriptor::SubPluginFeatures::Key* key) : + Model(parent), JournallingObject(), - m_descriptor( descriptor ) + m_descriptor(descriptor), + m_key(key ? *key : Descriptor::SubPluginFeatures::Key(m_descriptor)) { if( m_descriptor == NULL ) { @@ -74,6 +80,87 @@ Plugin::~Plugin() +template +T use_this_or(T this_param, T or_param) +{ + return this_param ? this_param : or_param; +} + + + + +QString Plugin::displayName() const +{ + return Model::displayName().isEmpty() // currently always empty + ? (m_descriptor->subPluginFeatures && m_key.isValid()) + // get from sub plugin + ? m_key.displayName() + // get from plugin + : m_descriptor->displayName + : Model::displayName(); +} + + + + +const PixmapLoader* Plugin::logo() const +{ + return (m_descriptor->subPluginFeatures && m_key.isValid()) + ? m_key.logo() + : m_descriptor->logo; +} + + + + +const char *Plugin::Descriptor::SubPluginFeatures::Key::additionalFileExtensions() const +{ + Q_ASSERT(isValid()); + return desc->subPluginFeatures + // get from sub plugin + ? desc->subPluginFeatures->additionalFileExtensions(*this) + // get from plugin + : nullptr; +} + + + + +const char* Plugin::Descriptor::SubPluginFeatures::Key::displayName() const +{ + Q_ASSERT(isValid()); + return desc->subPluginFeatures + // get from sub plugin + ? use_this_or(desc->subPluginFeatures->displayName(*this), desc->displayName) + // get from plugin + : desc->displayName; +} + + + + +const PixmapLoader* Plugin::Descriptor::SubPluginFeatures::Key::logo() const +{ + Q_ASSERT(isValid()); + return desc->subPluginFeatures + ? use_this_or(desc->subPluginFeatures->logo(*this), desc->logo) + : desc->logo; +} + + + + +const char *Plugin::Descriptor::SubPluginFeatures::Key::description() const +{ + Q_ASSERT(isValid()); + return desc->subPluginFeatures + ? use_this_or(desc->subPluginFeatures->description(*this), desc->description) + : desc->description; +} + + + + void Plugin::loadFile( const QString & ) { } @@ -90,10 +177,37 @@ AutomatableModel * Plugin::childModel( const QString & ) #include "PluginFactory.h" -Plugin * Plugin::instantiate( const QString& pluginName, Model * parent, - void * data ) +Plugin * Plugin::instantiateWithKey(const QString& pluginName, Model * parent, + const Descriptor::SubPluginFeatures::Key *key, + bool keyFromDnd) +{ + if(keyFromDnd) + Q_ASSERT(!key); + const Descriptor::SubPluginFeatures::Key *keyPtr = keyFromDnd + ? static_cast(Engine::pickDndPluginKey()) + : key; + const PluginFactory::PluginInfo& pi = pluginFactory->pluginInfo(pluginName.toUtf8()); + if(keyPtr) + { + // descriptor is not yet set when loading - set it now + Descriptor::SubPluginFeatures::Key keyCopy = *keyPtr; + keyCopy.desc = pi.descriptor; + return Plugin::instantiate(pluginName, parent, &keyCopy); + } + else + return Plugin::instantiate(pluginName, parent, + // the keys are never touched anywhere + const_cast(keyPtr)); +} + + + + +Plugin * Plugin::instantiate(const QString& pluginName, Model * parent, + void *data) { const PluginFactory::PluginInfo& pi = pluginFactory->pluginInfo(pluginName.toUtf8()); + if( pi.isNull() ) { if( gui ) @@ -106,9 +220,15 @@ Plugin * Plugin::instantiate( const QString& pluginName, Model * parent, } return new DummyPlugin(); } + qDebug() << "Using PluginInfo for " << pluginName; - InstantiationHook instantiationHook = ( InstantiationHook ) pi.library->resolve( "lmms_plugin_main" ); - if( instantiationHook == NULL ) + Plugin* inst; + InstantiationHook instantiationHook; + if ((instantiationHook = ( InstantiationHook ) pi.library->resolve( "lmms_plugin_main" ))) + { + inst = instantiationHook(parent, data); + } + else { if( gui ) { @@ -120,7 +240,7 @@ Plugin * Plugin::instantiate( const QString& pluginName, Model * parent, return new DummyPlugin(); } - Plugin * inst = instantiationHook( parent, data ); + return inst; } @@ -181,4 +301,3 @@ QDomElement Plugin::Descriptor::SubPluginFeatures::Key::saveXML( } - diff --git a/src/core/PluginFactory.cpp b/src/core/PluginFactory.cpp index a87e49270..a25dca54a 100644 --- a/src/core/PluginFactory.cpp +++ b/src/core/PluginFactory.cpp @@ -28,8 +28,11 @@ #include #include #include +#include "lmmsconfig.h" #include "ConfigManager.h" +#include "Plugin.h" +#include "embed.h" #ifdef LMMS_BUILD_WIN32 QStringList nameFilters("*.dll"); @@ -45,6 +48,16 @@ qint64 qHash(const QFileInfo& fi) std::unique_ptr PluginFactory::s_instance; PluginFactory::PluginFactory() +{ + setupSearchPaths(); + discoverPlugins(); +} + +PluginFactory::~PluginFactory() +{ +} + +void PluginFactory::setupSearchPaths() { // Adds a search path relative to the main executable if the path exists. auto addRelativeIfExists = [](const QString & path) { @@ -76,12 +89,6 @@ PluginFactory::PluginFactory() QDir::addSearchPath("plugins", env_path); QDir::addSearchPath("plugins", ConfigManager::inst()->workingDir() + "plugins"); - - discoverPlugins(); -} - -PluginFactory::~PluginFactory() -{ } PluginFactory* PluginFactory::instance() @@ -107,9 +114,9 @@ const PluginFactory::PluginInfoList& PluginFactory::pluginInfos() const return m_pluginInfos; } -const PluginFactory::PluginInfo PluginFactory::pluginSupportingExtension(const QString& ext) +const PluginFactory::PluginInfoAndKey PluginFactory::pluginSupportingExtension(const QString& ext) { - return m_pluginByExt.value(ext, PluginInfo()); + return m_pluginByExt.value(ext, PluginInfoAndKey()); } const PluginFactory::PluginInfo PluginFactory::pluginInfo(const char* name) const @@ -150,42 +157,82 @@ void PluginFactory::discoverPlugins() for (const QFileInfo& file : files) { auto library = std::make_shared(file.absoluteFilePath()); - if (! library->load()) { m_errors[file.baseName()] = library->errorString(); qWarning("%s", library->errorString().toLocal8Bit().data()); continue; } - if (library->resolve("lmms_plugin_main") == nullptr) { - continue; - } - QString descriptorName = file.baseName() + "_plugin_descriptor"; - if( descriptorName.left(3) == "lib" ) + Plugin::Descriptor* pluginDescriptor = nullptr; + if (library->resolve("lmms_plugin_main")) { - descriptorName = descriptorName.mid(3); - } + QString descriptorName = file.baseName() + "_plugin_descriptor"; + if( descriptorName.left(3) == "lib" ) + { + descriptorName = descriptorName.mid(3); + } - Plugin::Descriptor* pluginDescriptor = reinterpret_cast(library->resolve(descriptorName.toUtf8().constData())); - if(pluginDescriptor == nullptr) + pluginDescriptor = reinterpret_cast(library->resolve(descriptorName.toUtf8().constData())); + if(pluginDescriptor == nullptr) + { + qWarning() << qApp->translate("PluginFactory", "LMMS plugin %1 does not have a plugin descriptor named %2!"). + arg(file.absoluteFilePath()).arg(descriptorName); + continue; + } + } + else { - qWarning() << qApp->translate("PluginFactory", "LMMS plugin %1 does not have a plugin descriptor named %2!"). - arg(file.absoluteFilePath()).arg(descriptorName); - continue; + qDebug() << "Ignoring" << file << "(no lmms_plugin_main())"; } - PluginInfo info; - info.file = file; - info.library = library; - info.descriptor = pluginDescriptor; - pluginInfos << info; - - for (const QString& ext : QString(info.descriptor->supportedFileTypes).split(',')) + if(pluginDescriptor) { - m_pluginByExt.insert(ext, info); - } + PluginInfo info; + info.file = file; + info.library = library; + info.descriptor = pluginDescriptor; + pluginInfos << info; - descriptors.insert(info.descriptor->type, info.descriptor); + qDebug() << "Add" << info.file << "with type" + << info.descriptor->type; + + auto addSupportedFileTypes = + [this](const char* supportedFileTypes, + const PluginInfo& info, + const Plugin::Descriptor::SubPluginFeatures::Key* key = nullptr) + { + if(supportedFileTypes) + for (const QString& ext : QString(supportedFileTypes).split(',')) + { + qDebug() << "Plugin " << info.name() << "supports" << ext; + PluginInfoAndKey infoAndKey; + infoAndKey.info = info; + infoAndKey.key = key + ? *key + : Plugin::Descriptor::SubPluginFeatures::Key(); + m_pluginByExt.insert(ext, infoAndKey); + } + }; + + if (info.descriptor->supportedFileTypes) + addSupportedFileTypes(info.descriptor->supportedFileTypes, info); + + if (info.descriptor->subPluginFeatures) + { + Plugin::Descriptor::SubPluginFeatures::KeyList + subPluginKeys; + info.descriptor->subPluginFeatures->listSubPluginKeys( + info.descriptor, + subPluginKeys); + for(const Plugin::Descriptor::SubPluginFeatures::Key& key + : subPluginKeys) + { + addSupportedFileTypes(key.additionalFileExtensions(), info, &key); + } + } + + descriptors.insert(info.descriptor->type, info.descriptor); + } } m_pluginInfos = pluginInfos; diff --git a/src/core/PresetPreviewPlayHandle.cpp b/src/core/PresetPreviewPlayHandle.cpp index dc36819b7..ca0e52194 100644 --- a/src/core/PresetPreviewPlayHandle.cpp +++ b/src/core/PresetPreviewPlayHandle.cpp @@ -137,8 +137,10 @@ PresetPreviewPlayHandle::PresetPreviewPlayHandle( const QString & _preset_file, suffix().toLower(); if( i == NULL || !i->descriptor()->supportsFileType( ext ) ) { + const PluginFactory::PluginInfoAndKey& infoAndKey = + pluginFactory->pluginSupportingExtension(ext); i = s_previewTC->previewInstrumentTrack()-> - loadInstrument(pluginFactory->pluginSupportingExtension(ext).name()); + loadInstrument(infoAndKey.info.name(), &infoAndKey.key); } if( i != NULL ) { diff --git a/src/gui/EffectSelectDialog.cpp b/src/gui/EffectSelectDialog.cpp index 64b180d48..4b59a19b1 100644 --- a/src/gui/EffectSelectDialog.cpp +++ b/src/gui/EffectSelectDialog.cpp @@ -53,11 +53,6 @@ EffectSelectDialog::EffectSelectDialog( QWidget * _parent ) : if( desc->subPluginFeatures ) { desc->subPluginFeatures->listSubPluginKeys( - // as iterators are always stated to be not - // equal with pointers, we dereference the - // iterator and take the address of the item, - // so we're on the safe side and the compiler - // likely will reduce that to just "it" desc, subPluginEffectKeys ); } @@ -79,14 +74,14 @@ EffectSelectDialog::EffectSelectDialog( QWidget * _parent ) : { QString name; QString type; - if( ( *it ).desc->subPluginFeatures ) + if( it->desc->subPluginFeatures ) { - name = ( *it ).name; - type = ( *it ).desc->displayName; + name = it->displayName(); + type = it->desc->displayName; } else { - name = ( *it ).desc->displayName; + name = it->desc->displayName; type = "LMMS"; } m_sourceModel.setItem( row, 0, new QStandardItem( name ) ); @@ -184,62 +179,63 @@ void EffectSelectDialog::rowChanged( const QModelIndex & _idx, { m_currentSelection = m_effectKeys[m_model.mapToSource( _idx ).row()]; } - if( m_currentSelection.desc ) + if( m_currentSelection.desc ) { m_descriptionWidget = new QWidget; - QHBoxLayout *hbox = new QHBoxLayout( m_descriptionWidget ); + QHBoxLayout *hbox = new QHBoxLayout( m_descriptionWidget ); - Plugin::Descriptor const & descriptor = *( m_currentSelection.desc ); + Plugin::Descriptor const & descriptor = *( m_currentSelection.desc ); - if ( descriptor.logo ) - { - QLabel *logoLabel = new QLabel( m_descriptionWidget ); - logoLabel->setPixmap( descriptor.logo->pixmap() ); - logoLabel->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); + const PixmapLoader* pixLoa = m_currentSelection.logo(); + if (pixLoa) + { + QLabel *logoLabel = new QLabel( m_descriptionWidget ); + logoLabel->setPixmap(pixLoa->pixmap()); + logoLabel->setSizePolicy(QSizePolicy::Minimum, QSizePolicy::Minimum); - hbox->addWidget( logoLabel ); - hbox->setAlignment( logoLabel, Qt::AlignTop); - } + hbox->addWidget( logoLabel ); + hbox->setAlignment( logoLabel, Qt::AlignTop); + } - QWidget *textualInfoWidget = new QWidget( m_descriptionWidget ); + QWidget *textualInfoWidget = new QWidget( m_descriptionWidget ); - hbox->addWidget(textualInfoWidget); + hbox->addWidget(textualInfoWidget); - QVBoxLayout * textWidgetLayout = new QVBoxLayout( textualInfoWidget); - textWidgetLayout->setMargin( 4 ); - textWidgetLayout->setSpacing( 0 ); + QVBoxLayout * textWidgetLayout = new QVBoxLayout( textualInfoWidget); + textWidgetLayout->setMargin( 4 ); + textWidgetLayout->setSpacing( 0 ); - if ( m_currentSelection.desc->subPluginFeatures ) - { - QWidget *subWidget = new QWidget(textualInfoWidget); - QVBoxLayout * subLayout = new QVBoxLayout( subWidget ); - subLayout->setMargin( 4 ); - subLayout->setSpacing( 0 ); - m_currentSelection.desc->subPluginFeatures-> - fillDescriptionWidget( subWidget, &m_currentSelection ); - for( QWidget * w : subWidget->findChildren() ) - { - if( w->parent() == subWidget ) - { - subLayout->addWidget( w ); - } - } + if ( m_currentSelection.desc->subPluginFeatures ) + { + QWidget *subWidget = new QWidget(textualInfoWidget); + QVBoxLayout * subLayout = new QVBoxLayout( subWidget ); + subLayout->setMargin( 4 ); + subLayout->setSpacing( 0 ); + m_currentSelection.desc->subPluginFeatures-> + fillDescriptionWidget( subWidget, &m_currentSelection ); + for( QWidget * w : subWidget->findChildren() ) + { + if( w->parent() == subWidget ) + { + subLayout->addWidget( w ); + } + } - textWidgetLayout->addWidget(subWidget); - } - else - { - QLabel *label = new QLabel(m_descriptionWidget); - QString labelText = "

" + tr("Name") + ": " + QString::fromUtf8(descriptor.displayName) + "

"; - labelText += "

" + tr("Description") + ": " + qApp->translate( "pluginBrowser", descriptor.description ) + "

"; - labelText += "

" + tr("Author") + ": " + QString::fromUtf8(descriptor.author) + "

"; + textWidgetLayout->addWidget(subWidget); + } + else + { + QLabel *label = new QLabel(m_descriptionWidget); + QString labelText = "

" + tr("Name") + ": " + QString::fromUtf8(descriptor.displayName) + "

"; + labelText += "

" + tr("Description") + ": " + qApp->translate( "pluginBrowser", descriptor.description ) + "

"; + labelText += "

" + tr("Author") + ": " + QString::fromUtf8(descriptor.author) + "

"; - label->setText(labelText); - textWidgetLayout->addWidget(label); - } + label->setText(labelText); + textWidgetLayout->addWidget(label); + } - ui->scrollArea->setWidget( m_descriptionWidget ); + ui->scrollArea->setWidget( m_descriptionWidget ); m_descriptionWidget->show(); } } diff --git a/src/gui/FileBrowser.cpp b/src/gui/FileBrowser.cpp index 05727d332..4311e4e05 100644 --- a/src/gui/FileBrowser.cpp +++ b/src/gui/FileBrowser.cpp @@ -180,7 +180,7 @@ void FileBrowser::reloadTree( void ) void FileBrowser::expandItems( QTreeWidgetItem * item ) { - int numChildren = item ? item->childCount() : m_fileBrowserTreeWidget->topLevelItemCount(); + int numChildren = item ? item->childCount() : m_fileBrowserTreeWidget->topLevelItemCount(); for( int i = 0; i < numChildren; ++i ) { QTreeWidgetItem * it = item ? item->child( i ) : m_fileBrowserTreeWidget->topLevelItem(i); @@ -241,7 +241,7 @@ void FileBrowser::addItems(const QString & path ) Directory *dd = new Directory( cur_file, path, m_filter ); m_fileBrowserTreeWidget->insertTopLevelItem( i,dd ); - dd->update(); + dd->update(); // add files to the directory orphan = false; break; } @@ -406,7 +406,7 @@ void FileBrowserTreeWidget::mousePressEvent(QMouseEvent * me ) delete tf; } else if( ( f->extension ()== "xiz" || f->extension() == "sf2" || f->extension() == "sf3" || f->extension() == "gig" || f->extension() == "pat" ) && - ! pluginFactory->pluginSupportingExtension(f->extension()).isNull() ) + ! pluginFactory->pluginSupportingExtension(f->extension()).info.isNull() ) { m_previewPlayHandle = new PresetPreviewPlayHandle( f->fullName(), f->handling() == FileItem::LoadByPlugin ); } @@ -549,8 +549,9 @@ void FileBrowserTreeWidget::handleFile(FileItem * f, InstrumentTrack * it ) if( i == NULL || !i->descriptor()->supportsFileType( e ) ) { - i = it->loadInstrument( - pluginFactory->pluginSupportingExtension(e).name() ); + PluginFactory::PluginInfoAndKey piakn = + pluginFactory->pluginSupportingExtension(e); + i = it->loadInstrument(piakn.info.name(), &piakn.key); } i->loadFile( f->fullName() ); break; diff --git a/src/gui/InstrumentView.cpp b/src/gui/InstrumentView.cpp index 9e8fc58c1..93e153f6f 100644 --- a/src/gui/InstrumentView.cpp +++ b/src/gui/InstrumentView.cpp @@ -57,7 +57,7 @@ void InstrumentView::setModel( Model * _model, bool ) if( dynamic_cast( _model ) != NULL ) { ModelView::setModel( _model ); - instrumentTrackWindow()->setWindowIcon( model()->descriptor()->logo->pixmap() ); + instrumentTrackWindow()->setWindowIcon( model()->logo()->pixmap() ); connect( model(), SIGNAL( destroyed( QObject * ) ), this, SLOT( close() ) ); } } diff --git a/src/gui/PluginBrowser.cpp b/src/gui/PluginBrowser.cpp index dc0fc35e0..27747bc53 100644 --- a/src/gui/PluginBrowser.cpp +++ b/src/gui/PluginBrowser.cpp @@ -31,6 +31,7 @@ #include #include "embed.h" +#include "Engine.h" #include "templates.h" #include "gui_templates.h" #include "StringPairDrag.h" @@ -85,9 +86,30 @@ PluginDescList::PluginDescList(QWidget *parent) : return qstricmp( d1->displayName, d2->displayName ) < 0 ? true : false; } ); - for (const Plugin::Descriptor* desc : descs) + + typedef Plugin::Descriptor::SubPluginFeatures::KeyList PluginKeyList; + typedef Plugin::Descriptor::SubPluginFeatures::Key PluginKey; + PluginKeyList subPluginKeys, pluginKeys; + + for (const Plugin::Descriptor* desc: descs) { - PluginDescWidget* p = new PluginDescWidget( *desc, this ); + if( desc->subPluginFeatures ) + { + desc->subPluginFeatures->listSubPluginKeys( + desc, + subPluginKeys ); + } + else + { + pluginKeys << PluginKey( desc, desc->name ); + } + } + + pluginKeys += subPluginKeys; + + for (const PluginKey& key : pluginKeys) + { + PluginDescWidget* p = new PluginDescWidget( key, this ); p->show(); layout->addWidget(p); } @@ -99,23 +121,23 @@ PluginDescList::PluginDescList(QWidget *parent) : -PluginDescWidget::PluginDescWidget( const Plugin::Descriptor & _pd, +PluginDescWidget::PluginDescWidget(const PluginKey &_pk, QWidget * _parent ) : QWidget( _parent ), - m_pluginDescriptor( _pd ), - m_logo( _pd.logo->pixmap() ), + m_pluginKey( _pk ), + m_logo( _pk.logo()->pixmap() ), m_mouseOver( false ) { setFixedHeight( DEFAULT_HEIGHT ); setMouseTracking( true ); setCursor( Qt::PointingHandCursor ); - setToolTip(_pd.description); + setToolTip(_pk.description()); } -void PluginDescWidget::paintEvent( QPaintEvent * e ) +void PluginDescWidget::paintEvent( QPaintEvent * ) { QPainter p( this ); @@ -140,8 +162,7 @@ void PluginDescWidget::paintEvent( QPaintEvent * e ) } p.setFont( f ); - p.drawText( 10 + logo_size.width(), 15, - m_pluginDescriptor.displayName ); + p.drawText( 10 + logo_size.width(), 15, m_pluginKey.displayName()); } @@ -171,8 +192,9 @@ void PluginDescWidget::mousePressEvent( QMouseEvent * _me ) { if( _me->button() == Qt::LeftButton ) { - new StringPairDrag( "instrument", m_pluginDescriptor.name, - m_logo, this ); + Engine::setDndPluginKey(&m_pluginKey); + new StringPairDrag("instrument", + QString::fromUtf8(m_pluginKey.desc->name), m_logo, this); leaveEvent( _me ); } } diff --git a/src/gui/TrackContainerView.cpp b/src/gui/TrackContainerView.cpp index 2772ef104..7c69d5eb8 100644 --- a/src/gui/TrackContainerView.cpp +++ b/src/gui/TrackContainerView.cpp @@ -384,8 +384,9 @@ void TrackContainerView::dropEvent( QDropEvent * _de ) InstrumentTrack * it = dynamic_cast( Track::create( Track::InstrumentTrack, m_tc ) ); - Instrument * i = it->loadInstrument( - pluginFactory->pluginSupportingExtension(FileItem::extension(value)).name()); + PluginFactory::PluginInfoAndKey piakn = + pluginFactory->pluginSupportingExtension(FileItem::extension(value)); + Instrument * i = it->loadInstrument(piakn.info.name(), &piakn.key); i->loadFile( value ); //it->toggledInstrumentTrackButton( true ); _de->accept(); @@ -529,7 +530,8 @@ InstrumentLoaderThread::InstrumentLoaderThread( QObject *parent, InstrumentTrack void InstrumentLoaderThread::run() { - Instrument *i = m_it->loadInstrument( m_name ); + Instrument *i = m_it->loadInstrument(m_name, nullptr, + true /*always DnD*/); QObject *parent = i->parent(); i->setParent( 0 ); i->moveToThread( m_containerThread ); diff --git a/src/gui/widgets/TrackLabelButton.cpp b/src/gui/widgets/TrackLabelButton.cpp index db310a05e..361db740e 100644 --- a/src/gui/widgets/TrackLabelButton.cpp +++ b/src/gui/widgets/TrackLabelButton.cpp @@ -195,9 +195,15 @@ void TrackLabelButton::paintEvent( QPaintEvent * _pe ) InstrumentTrack * it = dynamic_cast( m_trackView->getTrack() ); const PixmapLoader * pl; + auto get_logo = [](InstrumentTrack* it) -> const PixmapLoader* + { + return it->instrument()->key().isValid() + ? it->instrument()->key().logo() + : it->instrument()->descriptor()->logo; + }; if( it && it->instrument() && it->instrument()->descriptor() && - ( pl = it->instrument()->descriptor()->logo ) ) + ( pl = get_logo(it) ) ) { if( pl->pixmapName() != m_iconName ) { diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index 298430b03..2b25982df 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -735,7 +735,10 @@ void InstrumentTrack::saveTrackSpecificSettings( QDomDocument& doc, QDomElement { QDomElement i = doc.createElement( "instrument" ); i.setAttribute( "name", m_instrument->descriptor()->name ); - m_instrument->saveState( doc, i ); + QDomElement ins = m_instrument->saveState( doc, i ); + if(m_instrument->key().isValid()) { + ins.appendChild( m_instrument->key().saveXML( doc ) ); + } thisElement.appendChild( i ); } m_soundShaping.saveState( doc, thisElement ); @@ -796,9 +799,13 @@ void InstrumentTrack::loadTrackSpecificSettings( const QDomElement & thisElement } else if( node.nodeName() == "instrument" ) { + typedef Plugin::Descriptor::SubPluginFeatures::Key PluginKey; + PluginKey key( node.toElement().elementsByTagName( "key" ).item( 0 ).toElement() ); + delete m_instrument; m_instrument = NULL; - m_instrument = Instrument::instantiate( node.toElement().attribute( "name" ), this ); + m_instrument = Instrument::instantiate( + node.toElement().attribute( "name" ), this, &key); m_instrument->restoreState( node.firstChildElement() ); emit instrumentChanged(); @@ -812,7 +819,8 @@ void InstrumentTrack::loadTrackSpecificSettings( const QDomElement & thisElement { delete m_instrument; m_instrument = NULL; - m_instrument = Instrument::instantiate( node.nodeName(), this ); + m_instrument = Instrument::instantiate( + node.nodeName(), this, nullptr, true); if( m_instrument->nodeName() == node.nodeName() ) { m_instrument->restoreState( node.toElement() ); @@ -837,15 +845,20 @@ void InstrumentTrack::setPreviewMode( const bool value ) -Instrument * InstrumentTrack::loadInstrument( const QString & _plugin_name ) +Instrument * InstrumentTrack::loadInstrument(const QString & _plugin_name, + const Plugin::Descriptor::SubPluginFeatures::Key *key, bool keyFromDnd) { + if(keyFromDnd) + Q_ASSERT(!key); + silenceAllNotes( true ); lock(); delete m_instrument; - m_instrument = Instrument::instantiate( _plugin_name, this ); + m_instrument = Instrument::instantiate(_plugin_name, this, + key, keyFromDnd); unlock(); - setName( m_instrument->displayName() ); + setName(m_instrument->displayName()); emit instrumentChanged(); @@ -1733,7 +1746,7 @@ void InstrumentTrackWindow::dropEvent( QDropEvent* event ) if( type == "instrument" ) { - m_track->loadInstrument( value ); + m_track->loadInstrument( value, nullptr, true /* DnD */ ); Engine::getSong()->setModified(); @@ -1759,7 +1772,9 @@ void InstrumentTrackWindow::dropEvent( QDropEvent* event ) if( !i->descriptor()->supportsFileType( ext ) ) { - i = m_track->loadInstrument( pluginFactory->pluginSupportingExtension(ext).name() ); + PluginFactory::PluginInfoAndKey piakn = + pluginFactory->pluginSupportingExtension(ext); + i = m_track->loadInstrument(piakn.info.name(), &piakn.key); } i->loadFile( value ); From 4d64c422b22f3176aea30955575ab3af287e9e4f Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Thu, 27 Dec 2018 21:59:50 +0100 Subject: [PATCH 02/53] Fix Engine.cpp not compiling on some compilers --- src/core/Engine.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/Engine.cpp b/src/core/Engine.cpp index 7f89c20f3..50e25b0b4 100644 --- a/src/core/Engine.cpp +++ b/src/core/Engine.cpp @@ -29,6 +29,7 @@ #include "FxMixer.h" #include "Ladspa2LMMS.h" #include "Mixer.h" +#include "Plugin.h" #include "PresetPreviewPlayHandle.h" #include "ProjectJournal.h" #include "Song.h" @@ -118,7 +119,7 @@ void LmmsCore::updateFramesPerTick() void LmmsCore::setDndPluginKey(void *newKey) { - assert(static_cast(newKey)); + Q_ASSERT(static_cast(newKey)); s_dndPluginKey = newKey; } From fcd1a7ee86ca4e1471176853b28fb66762c124f7 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Thu, 27 Dec 2018 22:15:56 +0100 Subject: [PATCH 03/53] Fix or remove wrong or useless debug printfs --- src/core/Plugin.cpp | 1 - src/core/PluginFactory.cpp | 7 ++----- 2 files changed, 2 insertions(+), 6 deletions(-) diff --git a/src/core/Plugin.cpp b/src/core/Plugin.cpp index e7f80b5ab..7fcbd9795 100644 --- a/src/core/Plugin.cpp +++ b/src/core/Plugin.cpp @@ -220,7 +220,6 @@ Plugin * Plugin::instantiate(const QString& pluginName, Model * parent, } return new DummyPlugin(); } - qDebug() << "Using PluginInfo for " << pluginName; Plugin* inst; InstantiationHook instantiationHook; diff --git a/src/core/PluginFactory.cpp b/src/core/PluginFactory.cpp index a25dca54a..ec390ae6e 100644 --- a/src/core/PluginFactory.cpp +++ b/src/core/PluginFactory.cpp @@ -182,7 +182,7 @@ void PluginFactory::discoverPlugins() } else { - qDebug() << "Ignoring" << file << "(no lmms_plugin_main())"; + //qDebug() << "Ignoring" << file.absoluteFilePath() << "(no lmms_plugin_main())"; } if(pluginDescriptor) @@ -193,9 +193,6 @@ void PluginFactory::discoverPlugins() info.descriptor = pluginDescriptor; pluginInfos << info; - qDebug() << "Add" << info.file << "with type" - << info.descriptor->type; - auto addSupportedFileTypes = [this](const char* supportedFileTypes, const PluginInfo& info, @@ -204,7 +201,7 @@ void PluginFactory::discoverPlugins() if(supportedFileTypes) for (const QString& ext : QString(supportedFileTypes).split(',')) { - qDebug() << "Plugin " << info.name() << "supports" << ext; + //qDebug() << "Plugin " << info.name() << "supports" << ext; PluginInfoAndKey infoAndKey; infoAndKey.info = info; infoAndKey.key = key From f3b23830fbd9ef57ea9b1c6a2ad4aab7649881a0 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Thu, 27 Dec 2018 22:37:34 +0100 Subject: [PATCH 04/53] Fix missing IntrumentTrack header --- plugins/carlapatchbay/carlapatchbay.cpp | 1 + plugins/carlarack/carlarack.cpp | 1 + 2 files changed, 2 insertions(+) diff --git a/plugins/carlapatchbay/carlapatchbay.cpp b/plugins/carlapatchbay/carlapatchbay.cpp index 60e022156..ac00630d4 100644 --- a/plugins/carlapatchbay/carlapatchbay.cpp +++ b/plugins/carlapatchbay/carlapatchbay.cpp @@ -25,6 +25,7 @@ #include "carla.h" #include "embed.h" +#include "InstrumentTrack.h" extern "C" { diff --git a/plugins/carlarack/carlarack.cpp b/plugins/carlarack/carlarack.cpp index d057eff83..c0a39f9c2 100644 --- a/plugins/carlarack/carlarack.cpp +++ b/plugins/carlarack/carlarack.cpp @@ -25,6 +25,7 @@ #include "carla.h" #include "embed.h" +#include "InstrumentTrack.h" extern "C" { From d5dcebed83b21928975a641834dc08875407ff4e Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Fri, 18 Jan 2019 23:22:52 +0100 Subject: [PATCH 05/53] Use QString for SubPluginFeatures' virtuals The former virtuals returned `const char*`, which lead to invalid reads when `LadspaSubPluginFeatures` returned pointers to temporary `QByteArray::data`. --- include/Plugin.h | 12 +++++------ .../LadspaEffect/LadspaSubPluginFeatures.cpp | 4 ++-- .../LadspaEffect/LadspaSubPluginFeatures.h | 2 +- src/core/Plugin.cpp | 20 ++++++++++++++----- src/core/PluginFactory.cpp | 8 ++++---- 5 files changed, 28 insertions(+), 18 deletions(-) diff --git a/include/Plugin.h b/include/Plugin.h index 034d8a06c..48c5f90c7 100644 --- a/include/Plugin.h +++ b/include/Plugin.h @@ -165,9 +165,9 @@ public: // helper functions to retrieve data that is // not part of the key, but mapped via desc->subPluginFeatures - const char* additionalFileExtensions() const; - const char* displayName() const; - const char* description() const; + QString additionalFileExtensions() const; + QString displayName() const; + QString description() const; const PixmapLoader* logo() const; } ; @@ -200,19 +200,19 @@ public: // The defaults are sane, i.e. redirect to sub plugins // supererior descriptor - virtual const char* additionalFileExtensions(const Key&) const + virtual QString additionalFileExtensions(const Key&) const { return nullptr; } - virtual const char* displayName(const Key& k) const + virtual QString displayName(const Key& k) const { return k.isValid() ? k.desc->displayName : k.name.toUtf8().data(); } - virtual const char* description(const Key& k) const + virtual QString description(const Key& k) const { return k.isValid() ? k.desc->description : ""; } diff --git a/plugins/LadspaEffect/LadspaSubPluginFeatures.cpp b/plugins/LadspaEffect/LadspaSubPluginFeatures.cpp index 1b055fe73..4cefa90b5 100644 --- a/plugins/LadspaEffect/LadspaSubPluginFeatures.cpp +++ b/plugins/LadspaEffect/LadspaSubPluginFeatures.cpp @@ -44,11 +44,11 @@ LadspaSubPluginFeatures::LadspaSubPluginFeatures( Plugin::PluginTypes _type ) : -const char *LadspaSubPluginFeatures::displayName(const Plugin::Descriptor::SubPluginFeatures::Key &k) const +QString LadspaSubPluginFeatures::displayName(const Plugin::Descriptor::SubPluginFeatures::Key &k) const { const ladspa_key_t & lkey = subPluginKeyToLadspaKey(&k); Ladspa2LMMS * lm = Engine::getLADSPAManager(); - return lm->getName(lkey).toUtf8().data(); + return lm->getName(lkey); } diff --git a/plugins/LadspaEffect/LadspaSubPluginFeatures.h b/plugins/LadspaEffect/LadspaSubPluginFeatures.h index b7613827b..3f47734f9 100644 --- a/plugins/LadspaEffect/LadspaSubPluginFeatures.h +++ b/plugins/LadspaEffect/LadspaSubPluginFeatures.h @@ -37,7 +37,7 @@ class LadspaSubPluginFeatures : public Plugin::Descriptor::SubPluginFeatures public: LadspaSubPluginFeatures( Plugin::PluginTypes _type ); - const char* displayName(const Key& k) const override; + QString displayName(const Key& k) const override; void fillDescriptionWidget( QWidget * _parent, const Key * _key ) const override; diff --git a/src/core/Plugin.cpp b/src/core/Plugin.cpp index 7fcbd9795..2975cf104 100644 --- a/src/core/Plugin.cpp +++ b/src/core/Plugin.cpp @@ -89,6 +89,14 @@ T use_this_or(T this_param, T or_param) +QString use_this_or(QString this_param, QString or_param) +{ + return this_param.isNull() ? or_param : this_param; +} + + + + QString Plugin::displayName() const { return Model::displayName().isEmpty() // currently always empty @@ -113,7 +121,7 @@ const PixmapLoader* Plugin::logo() const -const char *Plugin::Descriptor::SubPluginFeatures::Key::additionalFileExtensions() const +QString Plugin::Descriptor::SubPluginFeatures::Key::additionalFileExtensions() const { Q_ASSERT(isValid()); return desc->subPluginFeatures @@ -126,12 +134,13 @@ const char *Plugin::Descriptor::SubPluginFeatures::Key::additionalFileExtensions -const char* Plugin::Descriptor::SubPluginFeatures::Key::displayName() const +QString Plugin::Descriptor::SubPluginFeatures::Key::displayName() const { Q_ASSERT(isValid()); return desc->subPluginFeatures // get from sub plugin - ? use_this_or(desc->subPluginFeatures->displayName(*this), desc->displayName) + ? use_this_or(desc->subPluginFeatures->displayName(*this), + QString::fromUtf8(desc->displayName)) // get from plugin : desc->displayName; } @@ -150,11 +159,12 @@ const PixmapLoader* Plugin::Descriptor::SubPluginFeatures::Key::logo() const -const char *Plugin::Descriptor::SubPluginFeatures::Key::description() const +QString Plugin::Descriptor::SubPluginFeatures::Key::description() const { Q_ASSERT(isValid()); return desc->subPluginFeatures - ? use_this_or(desc->subPluginFeatures->description(*this), desc->description) + ? use_this_or(desc->subPluginFeatures->description(*this), + QString::fromUtf8(desc->description)) : desc->description; } diff --git a/src/core/PluginFactory.cpp b/src/core/PluginFactory.cpp index ec390ae6e..f84227091 100644 --- a/src/core/PluginFactory.cpp +++ b/src/core/PluginFactory.cpp @@ -194,12 +194,12 @@ void PluginFactory::discoverPlugins() pluginInfos << info; auto addSupportedFileTypes = - [this](const char* supportedFileTypes, + [this](QString supportedFileTypes, const PluginInfo& info, const Plugin::Descriptor::SubPluginFeatures::Key* key = nullptr) { - if(supportedFileTypes) - for (const QString& ext : QString(supportedFileTypes).split(',')) + if(!supportedFileTypes.isNull()) + for (const QString& ext : supportedFileTypes.split(',')) { //qDebug() << "Plugin " << info.name() << "supports" << ext; PluginInfoAndKey infoAndKey; @@ -212,7 +212,7 @@ void PluginFactory::discoverPlugins() }; if (info.descriptor->supportedFileTypes) - addSupportedFileTypes(info.descriptor->supportedFileTypes, info); + addSupportedFileTypes(QString(info.descriptor->supportedFileTypes), info); if (info.descriptor->subPluginFeatures) { From 31dc8e18ff90489804747de8ef5e111da9d74b29 Mon Sep 17 00:00:00 2001 From: Javier Serrano Polo Date: Mon, 25 Feb 2019 03:53:05 +0100 Subject: [PATCH 06/53] Test deployment preparation in regular builds (#4847) Tests whether contributions break packaging inadvertently --- .travis.yml | 2 -- .travis/linux..script.sh | 5 ++++- .travis/linux.win32.script.sh | 2 ++ .travis/linux.win64.script.sh | 2 ++ .travis/osx..script.sh | 5 ++++- 5 files changed, 12 insertions(+), 4 deletions(-) diff --git a/.travis.yml b/.travis.yml index c48186fa3..fb0325a0d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -35,8 +35,6 @@ script: - . ${TRAVIS_BUILD_DIR}/.travis/${TRAVIS_OS_NAME}.${TARGET_OS}.script.sh after_script: - ccache -s -before_deploy: - - if [ "$TARGET_OS" != debian-sid ]; then make package; fi deploy: provider: releases api_key: diff --git a/.travis/linux..script.sh b/.travis/linux..script.sh index a68620483..8f3b763d3 100644 --- a/.travis/linux..script.sh +++ b/.travis/linux..script.sh @@ -7,8 +7,11 @@ if [ $QT5 ]; then source /opt/qt59/bin/qt59-env.sh fi -cmake -DUSE_WERROR=ON $CMAKE_FLAGS .. +cmake -DCMAKE_INSTALL_PREFIX=../target/ -DUSE_WERROR=ON $CMAKE_FLAGS .. make -j4 make tests ./tests/tests + +make install +make appimage diff --git a/.travis/linux.win32.script.sh b/.travis/linux.win32.script.sh index eb7cb9f7a..1ef063d55 100644 --- a/.travis/linux.win32.script.sh +++ b/.travis/linux.win32.script.sh @@ -6,3 +6,5 @@ export CMAKE_OPTS="$CMAKE_FLAGS -DUSE_WERROR=ON" ../cmake/build_mingw32.sh make -j4 + +make package diff --git a/.travis/linux.win64.script.sh b/.travis/linux.win64.script.sh index fb62cb5b3..b99a461de 100644 --- a/.travis/linux.win64.script.sh +++ b/.travis/linux.win64.script.sh @@ -6,3 +6,5 @@ export CMAKE_OPTS="$CMAKE_FLAGS -DUSE_WERROR=ON" ../cmake/build_mingw64.sh make -j4 + +make package diff --git a/.travis/osx..script.sh b/.travis/osx..script.sh index fb1473f79..58df3b624 100644 --- a/.travis/osx..script.sh +++ b/.travis/osx..script.sh @@ -7,8 +7,11 @@ if [ $QT5 ]; then export CMAKE_PREFIX_PATH="$(brew --prefix qt5)" fi -cmake $CMAKE_FLAGS -DUSE_WERROR=OFF .. +cmake -DCMAKE_INSTALL_PREFIX=../target/ $CMAKE_FLAGS -DUSE_WERROR=OFF .. make -j4 make tests ./tests/tests + +make install +make dmg From e34f75a8c7605cbc9f31fbb43c91255679ede6fb Mon Sep 17 00:00:00 2001 From: Tres Finocchiaro Date: Sun, 24 Feb 2019 21:57:53 -0500 Subject: [PATCH 07/53] Add needed macOS shortcuts (#4851) Fix insert bars, delete bars, delete notes on Apple keyboard --- src/gui/editors/AutomationEditor.cpp | 1 + src/gui/editors/PianoRoll.cpp | 1 + src/gui/editors/SongEditor.cpp | 4 ++-- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/src/gui/editors/AutomationEditor.cpp b/src/gui/editors/AutomationEditor.cpp index 0234be0ab..06eece23a 100644 --- a/src/gui/editors/AutomationEditor.cpp +++ b/src/gui/editors/AutomationEditor.cpp @@ -404,6 +404,7 @@ void AutomationEditor::keyPressEvent(QKeyEvent * ke ) } break; + case Qt::Key_Backspace: case Qt::Key_Delete: deleteSelectedValues(); ke->accept(); diff --git a/src/gui/editors/PianoRoll.cpp b/src/gui/editors/PianoRoll.cpp index 7bdf9dc07..fef788885 100644 --- a/src/gui/editors/PianoRoll.cpp +++ b/src/gui/editors/PianoRoll.cpp @@ -1184,6 +1184,7 @@ void PianoRoll::keyPressEvent(QKeyEvent* ke ) clearSelectedNotes(); break; + case Qt::Key_Backspace: case Qt::Key_Delete: deleteSelectedNotes(); ke->accept(); diff --git a/src/gui/editors/SongEditor.cpp b/src/gui/editors/SongEditor.cpp index f3b69e47d..286b01f15 100644 --- a/src/gui/editors/SongEditor.cpp +++ b/src/gui/editors/SongEditor.cpp @@ -315,13 +315,13 @@ void SongEditor::keyPressEvent( QKeyEvent * ke ) { if( /*_ke->modifiers() & Qt::ShiftModifier*/ gui->mainWindow()->isShiftPressed() == true && - ke->key() == Qt::Key_Insert ) + ( ke->key() == Qt::Key_Insert || ke->key() == Qt::Key_Enter || ke->key() == Qt::Key_Return ) ) { m_song->insertBar(); } else if(/* _ke->modifiers() & Qt::ShiftModifier &&*/ gui->mainWindow()->isShiftPressed() == true && - ke->key() == Qt::Key_Delete ) + ( ke->key() == Qt::Key_Delete || ke->key() == Qt::Key_Backspace ) ) { m_song->removeBar(); } From ad1fa16a9525776a19d8a760c484d0837c20d621 Mon Sep 17 00:00:00 2001 From: Javier Serrano Polo Date: Mon, 25 Feb 2019 19:06:01 +0100 Subject: [PATCH 08/53] Move apt_mingw_cache out of build directory (#4842) --- .travis.yml | 2 +- .travis/linux.win.download.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis.yml b/.travis.yml index fb0325a0d..6067c3cad 100644 --- a/.travis.yml +++ b/.travis.yml @@ -4,7 +4,7 @@ dist: trusty sudo: required cache: directories: - - apt_mingw_cache + - $HOME/apt_mingw_cache - $HOME/.ccache - $HOME/pbuilder-bases matrix: diff --git a/.travis/linux.win.download.sh b/.travis/linux.win.download.sh index 76d7928b4..aad5f1c69 100644 --- a/.travis/linux.win.download.sh +++ b/.travis/linux.win.download.sh @@ -1,7 +1,7 @@ #!/usr/bin/env bash set -e -CACHE_DIR=$TRAVIS_BUILD_DIR/apt_mingw_cache/$1 +CACHE_DIR=$HOME/apt_mingw_cache/$1 mkdir -p $CACHE_DIR pushd $CACHE_DIR From a233291c271a1fbb1da448bbc9efb8f53f3f6fbe Mon Sep 17 00:00:00 2001 From: Lukas W Date: Wed, 27 Feb 2019 09:50:48 +0100 Subject: [PATCH 09/53] Add missing include Compilation fails with debug build. Fixes regression from dd99f3a7c466e86e02a8a8811e4b41f471a8b15d --- src/core/MixHelpers.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/core/MixHelpers.cpp b/src/core/MixHelpers.cpp index d70b47e07..0957852c6 100644 --- a/src/core/MixHelpers.cpp +++ b/src/core/MixHelpers.cpp @@ -23,6 +23,9 @@ */ #include "MixHelpers.h" + +#include + #include "lmms_math.h" #include "ValueBuffer.h" From 05d5e2036de38b082560d47c04d6bd282a76bd43 Mon Sep 17 00:00:00 2001 From: Alexandre Almeida Date: Sun, 3 Mar 2019 05:44:20 -0300 Subject: [PATCH 10/53] Fix DrumSynth sscanf (#4869) --- src/core/DrumSynth.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/core/DrumSynth.cpp b/src/core/DrumSynth.cpp index e1fb77793..b39e8f52e 100644 --- a/src/core/DrumSynth.cpp +++ b/src/core/DrumSynth.cpp @@ -119,7 +119,10 @@ void DrumSynth::GetEnv(int env, const char *sec, const char *key, QString ini) char en[256], s[8]; int i=0, o=0, ep=0; GetPrivateProfileString(sec, key, "0,0 100,0", en, sizeof(en), ini); - en[255]=0; //be safe! + + //be safe! + en[255]=0; + s[0]=0; while(en[i]!=0) { From e54969c568d4e2f270ccb340959129ab653973a4 Mon Sep 17 00:00:00 2001 From: tresf Date: Tue, 5 Mar 2019 15:55:41 -0500 Subject: [PATCH 11/53] Add /sbin to AppImage search path Closes #4846 --- cmake/linux/package_linux.sh.in | 1 + 1 file changed, 1 insertion(+) diff --git a/cmake/linux/package_linux.sh.in b/cmake/linux/package_linux.sh.in index ee0389749..b02a4c688 100644 --- a/cmake/linux/package_linux.sh.in +++ b/cmake/linux/package_linux.sh.in @@ -97,6 +97,7 @@ mv "${APPDIR}usr/bin/lmms" "${APPDIR}usr/bin/lmms.real" cat >"${APPDIR}usr/bin/lmms" < /dev/null 2>&1; then CARLAPATH="\$(which carla)" CARLAPREFIX="\${CARLAPATH%/bin*}" From 009a451d0bbc3c944e08147d825f9941ae7b8ba7 Mon Sep 17 00:00:00 2001 From: Alexandre Almeida Date: Tue, 5 Mar 2019 18:36:01 -0300 Subject: [PATCH 12/53] Fix AudioFileProcessor tooltip (#4868) --- plugins/audio_file_processor/audio_file_processor.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/audio_file_processor/audio_file_processor.cpp b/plugins/audio_file_processor/audio_file_processor.cpp index 2fdb8e459..313554899 100644 --- a/plugins/audio_file_processor/audio_file_processor.cpp +++ b/plugins/audio_file_processor/audio_file_processor.cpp @@ -522,7 +522,7 @@ AudioFileProcessorView::AudioFileProcessorView( Instrument * _instrument, "loop_pingpong_on" ) ); m_loopPingPongButton->setInactiveGraphic( PLUGIN_NAME::getIconPixmap( "loop_pingpong_off" ) ); - ToolTip::add( m_loopPingPongButton, tr( "Enable loop" ) ); + ToolTip::add( m_loopPingPongButton, tr( "Enable ping-pong loop" ) ); m_loopPingPongButton->setWhatsThis( tr( "This button enables ping-pong-looping. " "The sample loops backwards and forwards between the end point " From 991ffcd2e0261e306e996ad5eb661388ef39c1e4 Mon Sep 17 00:00:00 2001 From: Tres Finocchiaro Date: Tue, 5 Mar 2019 16:42:36 -0500 Subject: [PATCH 13/53] Disable soundio on macOS Temporarily disable soundio on macOS to address stability issues with PortAudio Closes #4864 --- CMakeLists.txt | 1 + 1 file changed, 1 insertion(+) diff --git a/CMakeLists.txt b/CMakeLists.txt index e204a644a..2611d7086 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -73,6 +73,7 @@ OPTION(WANT_QT5 "Build with Qt5" OFF) IF(LMMS_BUILD_APPLE) # Fix linking on 10.14+. See issue #4762 on github LINK_DIRECTORIES(/usr/local/lib) + SET(WANT_SOUNDIO OFF) SET(WANT_ALSA OFF) SET(WANT_PULSEAUDIO OFF) SET(WANT_VST OFF) From 9e6ce0638d7e9d1ffb51ae32a0730c4c7e4e2379 Mon Sep 17 00:00:00 2001 From: tresf Date: Wed, 6 Mar 2019 23:39:53 -0500 Subject: [PATCH 14/53] Blacklist $HOME as VST directory Closes #4854 --- src/core/ConfigManager.cpp | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/ConfigManager.cpp b/src/core/ConfigManager.cpp index f52e10ac7..2ed0f8308 100644 --- a/src/core/ConfigManager.cpp +++ b/src/core/ConfigManager.cpp @@ -524,8 +524,9 @@ void ConfigManager::loadConfigFile( const QString & configFile ) cfg_file.close(); } - + // Plugins are searched recursively, blacklist problematic locations if( m_vstDir.isEmpty() || m_vstDir == QDir::separator() || m_vstDir == "/" || + m_vstDir == ensureTrailingSlash( QDir::homePath() ) || !QDir( m_vstDir ).exists() ) { #ifdef LMMS_BUILD_WIN32 From cdd1ddbb0cf3e2b09d7dc1daf0f4b2fa456456c1 Mon Sep 17 00:00:00 2001 From: Javier Serrano Polo Date: Thu, 7 Mar 2019 06:32:23 +0100 Subject: [PATCH 15/53] Sync Debian version (#4840) * Sync Debian version * Drop temporary logging --- .travis.yml | 2 ++ .travis/linux.debian-sid.script.sh | 34 ++++++++++++++++++++++++++++++ debian/changelog | 2 +- 3 files changed, 37 insertions(+), 1 deletion(-) diff --git a/.travis.yml b/.travis.yml index 6067c3cad..b543a956d 100644 --- a/.travis.yml +++ b/.travis.yml @@ -21,6 +21,8 @@ matrix: osx_image: xcode8.2 env: QT5=True - env: TARGET_OS=debian-sid TARGET_DEPLOY=True + git: + depth: false - env: TARGET_OS=debian-sid TARGET_ARCH=i386 - compiler: clang env: TARGET_OS=debian-sid diff --git a/.travis/linux.debian-sid.script.sh b/.travis/linux.debian-sid.script.sh index 7318ae5ac..47143b5fe 100755 --- a/.travis/linux.debian-sid.script.sh +++ b/.travis/linux.debian-sid.script.sh @@ -31,6 +31,40 @@ else sudo pbuilder --update --basetgz "$BASETGZ" fi +sync_version() { + local VERSION + local MMR + local STAGE + local EXTRA + + VERSION=$(git describe --tags --match v[0-9].[0-9].[0-9]*) + VERSION=${VERSION#v} + MMR=${VERSION%%-*} + case $VERSION in + *-*-*-*) + VERSION=${VERSION%-*} + STAGE=${VERSION#*-} + STAGE=${STAGE%-*} + EXTRA=${VERSION##*-} + VERSION=$MMR~$STAGE.$EXTRA + ;; + *-*-*) + VERSION=${VERSION%-*} + EXTRA=${VERSION##*-} + VERSION=$MMR.$EXTRA + ;; + *-*) + STAGE=${VERSION#*-} + VERSION=$MMR~$STAGE + ;; + esac + + sed "1 s/@VERSION@/$VERSION/" -i debian/changelog + echo Set Debian version to $VERSION +} + +sync_version + DIR="$PWD" cd .. dpkg-source -b "$DIR" diff --git a/debian/changelog b/debian/changelog index c44b1790c..86f03c427 100644 --- a/debian/changelog +++ b/debian/changelog @@ -1,4 +1,4 @@ -lmms (1.2.0~rc7.1) unstable; urgency=low +lmms (@VERSION@) unstable; urgency=low * Upstream integration. * Drop Debian menu entry (policy 9.6). From 9148ce1b6f7e3183483b79c6a1a95b515e7f9ab6 Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Sun, 10 Mar 2019 12:10:04 +0900 Subject: [PATCH 16/53] Fix loading 32bit VSTs when loading LMMS in the build directory Fix another regression in #4797 --- cmake/linux/package_linux.sh.in | 2 +- plugins/vst_base/RemoteVstPlugin/CMakeLists.txt | 1 + plugins/vst_base/RemoteVstPlugin32.cmake | 8 ++++---- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/cmake/linux/package_linux.sh.in b/cmake/linux/package_linux.sh.in index 2cefe73c6..2eff0f264 100644 --- a/cmake/linux/package_linux.sh.in +++ b/cmake/linux/package_linux.sh.in @@ -142,7 +142,7 @@ fi # Move executables so linuxdeployqt can find them ZYNLIB="${APPDIR}usr/lib/lmms/RemoteZynAddSubFx" -VSTLIB32="${APPDIR}usr/lib/lmms/RemoteVstPlugin32.exe.so" +VSTLIB32="${APPDIR}usr/lib/lmms/32/RemoteVstPlugin32.exe.so" VSTLIB64="${APPDIR}usr/lib/lmms/RemoteVstPlugin64.exe.so" ZYNBIN="${APPDIR}usr/bin/RemoteZynAddSubFx" diff --git a/plugins/vst_base/RemoteVstPlugin/CMakeLists.txt b/plugins/vst_base/RemoteVstPlugin/CMakeLists.txt index 2a9f84076..59dd19a0a 100644 --- a/plugins/vst_base/RemoteVstPlugin/CMakeLists.txt +++ b/plugins/vst_base/RemoteVstPlugin/CMakeLists.txt @@ -18,6 +18,7 @@ if(IS_WIN64 OR CMAKE_SIZEOF_VOID_P EQUAL 8) set(BITNESS 64) else() set(BITNESS 32) + set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/32") endif() FOREACH( OUTPUTCONFIG ${CMAKE_CONFIGURATION_TYPES} ) diff --git a/plugins/vst_base/RemoteVstPlugin32.cmake b/plugins/vst_base/RemoteVstPlugin32.cmake index 07bed93b6..507042148 100644 --- a/plugins/vst_base/RemoteVstPlugin32.cmake +++ b/plugins/vst_base/RemoteVstPlugin32.cmake @@ -8,7 +8,7 @@ IF(LMMS_BUILD_WIN32 AND NOT LMMS_BUILD_WIN64) INSTALL(FILES "${MINGW_PREFIX}/bin/Qt5Core.dll" DESTINATION "${PLUGIN_DIR}/32") INSTALL(FILES "${MINGW_PREFIX}/bin/zlib1.dll" DESTINATION "${PLUGIN_DIR}/32") ENDIF(MSVC) - INSTALL(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/../RemoteVstPlugin32.exe" DESTINATION "${PLUGIN_DIR}/32") + INSTALL(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/../32/RemoteVstPlugin32.exe" DESTINATION "${PLUGIN_DIR}/32") ELSEIF(LMMS_BUILD_WIN64 AND MSVC) SET(MSVC_VER ${CMAKE_CXX_COMPILER_VERSION}) @@ -46,7 +46,7 @@ ELSEIF(LMMS_BUILD_WIN64 AND MSVC) "-DCMAKE_PREFIX_PATH=${QT_32_PREFIX}" ) - INSTALL(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/../RemoteVstPlugin32.exe" DESTINATION "${PLUGIN_DIR}/32") + INSTALL(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/../32/RemoteVstPlugin32.exe" DESTINATION "${PLUGIN_DIR}/32") #TODO: find a solution when not using vcpkg for qt SET(VCPKG_ROOT_32 "${CMAKE_FIND_ROOT_PATH}/../x86-windows") @@ -66,7 +66,7 @@ ELSEIF(LMMS_BUILD_LINUX) "-DCMAKE_CXX_FLAGS=-m32" ) - INSTALL(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/../RemoteVstPlugin32" "${CMAKE_CURRENT_BINARY_DIR}/../RemoteVstPlugin32.exe.so" DESTINATION "${PLUGIN_DIR}/32") + INSTALL(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/../32/RemoteVstPlugin32" "${CMAKE_CURRENT_BINARY_DIR}/../32/RemoteVstPlugin32.exe.so" DESTINATION "${PLUGIN_DIR}/32") ELSEIF(CMAKE_TOOLCHAIN_FILE_32) ExternalProject_Add(RemoteVstPlugin32 @@ -78,7 +78,7 @@ ELSEIF(CMAKE_TOOLCHAIN_FILE_32) ) INSTALL(FILES "${CMAKE_PREFIX_PATH_32}/bin/Qt5Core.dll" DESTINATION "${PLUGIN_DIR}/32") INSTALL(FILES "${CMAKE_PREFIX_PATH_32}/bin/zlib1.dll" DESTINATION "${PLUGIN_DIR}/32") - INSTALL(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/../RemoteVstPlugin32.exe" DESTINATION "${PLUGIN_DIR}/32") + INSTALL(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/../32/RemoteVstPlugin32.exe" DESTINATION "${PLUGIN_DIR}/32") ELSE() MESSAGE(WARNING "Can't build RemoteVstPlugin32, unknown environment. Please supply CMAKE_TOOLCHAIN_FILE_32 and optionally CMAKE_PREFIX_PATH_32") RETURN() From 17e87c1d689e1548d0c98a8a2fce5b229c799dbd Mon Sep 17 00:00:00 2001 From: Lukas W Date: Sun, 10 Mar 2019 10:27:51 +0100 Subject: [PATCH 17/53] Fix MidiJack crash on exit * Fix uninitialized m_jackClient being used in MidiJack destructor * Fix destruction order in Mixer.cpp so that MidiJack doesn't access the deleted AudioJack instance Fixes #4688 --- src/core/Mixer.cpp | 2 +- src/core/midi/MidiJack.cpp | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/core/Mixer.cpp b/src/core/Mixer.cpp index fae7b35db..c87305641 100644 --- a/src/core/Mixer.cpp +++ b/src/core/Mixer.cpp @@ -186,8 +186,8 @@ Mixer::~Mixer() } delete m_fifo; - delete m_audioDev; delete m_midiClient; + delete m_audioDev; for( int i = 0; i < 3; i++ ) { diff --git a/src/core/midi/MidiJack.cpp b/src/core/midi/MidiJack.cpp index cea7f7381..568b6dae1 100644 --- a/src/core/midi/MidiJack.cpp +++ b/src/core/midi/MidiJack.cpp @@ -61,6 +61,7 @@ static void JackMidiShutdown(void *arg) MidiJack::MidiJack() : MidiClientRaw(), + m_jackClient( nullptr ), m_input_port( NULL ), m_output_port( NULL ), m_quit( false ) From 2a728080957053b54446d0cebca2417e27c27d14 Mon Sep 17 00:00:00 2001 From: Michael Gregorius Date: Sun, 3 Mar 2019 11:35:30 +0100 Subject: [PATCH 18/53] Fix #3926: QCursor in AFP Fix a crash that occurred on the following steps: 1. Add an AFP track. 2. Open it, and move the waveform display to overlap the track label button. 3. Close the AFP window and open it again by clicking the track label. 4. Move the mouse pointer. The problem occurs because the code makes the implicit assumption that AudioFileProcessorWaveView::enterEvent (and hence QApplication::setOverrideCursor) is called before AudioFileProcessorWaveView::mouseMoveEvent. This is not the case when the waveform display is on top of the track label. In this case the AFP windows is opened with the mouse being immediately positioned over the wave form display. There is no enter event and move events are issues directly. This then leads to a crash in AudioFileProcessorWaveView::mouseMoveEvent when trying to determine the value for is_size_cursor because the override cursor is still null but is dereferenced directly without checking. Only adding a check would not solve the problem because in that case the cursor would not change to the hand cursor when being moved inside the waveform display. The solution is to remove all calls to the global methods setOverrideCursor and restoreOverrideCursor and to only set the cursor locally. This fix is based on a patch by gi0e5b06 which is committed under 8a10c52 in his repository but for which he never created a pull request. --- .../audio_file_processor.cpp | 47 ++++++++++--------- .../audio_file_processor.h | 2 +- 2 files changed, 25 insertions(+), 24 deletions(-) diff --git a/plugins/audio_file_processor/audio_file_processor.cpp b/plugins/audio_file_processor/audio_file_processor.cpp index 313554899..6be59f3cd 100644 --- a/plugins/audio_file_processor/audio_file_processor.cpp +++ b/plugins/audio_file_processor/audio_file_processor.cpp @@ -795,6 +795,7 @@ AudioFileProcessorWaveView::AudioFileProcessorWaveView( QWidget * _parent, int _ m_graph.fill( Qt::transparent ); update(); + updateCursor(); } @@ -811,7 +812,7 @@ void AudioFileProcessorWaveView::isPlaying( f_cnt_t _current_frame ) void AudioFileProcessorWaveView::enterEvent( QEvent * _e ) { - QApplication::setOverrideCursor( Qt::OpenHandCursor ); + updateCursor(); } @@ -819,10 +820,7 @@ void AudioFileProcessorWaveView::enterEvent( QEvent * _e ) void AudioFileProcessorWaveView::leaveEvent( QEvent * _e ) { - while( QApplication::overrideCursor() ) - { - QApplication::restoreOverrideCursor(); - } + updateCursor(); } @@ -850,7 +848,7 @@ void AudioFileProcessorWaveView::mousePressEvent( QMouseEvent * _me ) else { m_draggingType = wave; - QApplication::setOverrideCursor( Qt::ClosedHandCursor ); + updateCursor(_me); } } @@ -862,7 +860,7 @@ void AudioFileProcessorWaveView::mouseReleaseEvent( QMouseEvent * _me ) m_isDragging = false; if( m_draggingType == wave ) { - QApplication::restoreOverrideCursor(); + updateCursor(_me); } } @@ -873,22 +871,7 @@ void AudioFileProcessorWaveView::mouseMoveEvent( QMouseEvent * _me ) { if( ! m_isDragging ) { - const bool is_size_cursor = - QApplication::overrideCursor()->shape() == Qt::SizeHorCursor; - - if( isCloseTo( _me->x(), m_startFrameX ) || - isCloseTo( _me->x(), m_endFrameX ) || - isCloseTo( _me->x(), m_loopFrameX ) ) - { - if( ! is_size_cursor ) - { - QApplication::setOverrideCursor( Qt::SizeHorCursor ); - } - } - else if( is_size_cursor ) - { - QApplication::restoreOverrideCursor(); - } + updateCursor(_me); return; } @@ -1261,6 +1244,24 @@ void AudioFileProcessorWaveView::reverse() +void AudioFileProcessorWaveView::updateCursor( QMouseEvent * _me ) +{ + bool const waveIsDragged = m_isDragging && (m_draggingType == wave); + bool const pointerCloseToStartEndOrLoop = (_me != nullptr ) && + ( isCloseTo( _me->x(), m_startFrameX ) || + isCloseTo( _me->x(), m_endFrameX ) || + isCloseTo( _me->x(), m_loopFrameX ) ); + + if( !m_isDragging && pointerCloseToStartEndOrLoop) + setCursor(Qt::SizeHorCursor); + else if( waveIsDragged ) + setCursor(Qt::ClosedHandCursor); + else + setCursor(Qt::OpenHandCursor); +} + + + void AudioFileProcessorWaveView::knob::slideTo( double _v, bool _check_bound ) { diff --git a/plugins/audio_file_processor/audio_file_processor.h b/plugins/audio_file_processor/audio_file_processor.h index 150807686..d17be147c 100644 --- a/plugins/audio_file_processor/audio_file_processor.h +++ b/plugins/audio_file_processor/audio_file_processor.h @@ -211,7 +211,6 @@ public: private: bool checkBound( double _v ) const; - } ; @@ -276,6 +275,7 @@ private: void updateGraph(); void reverse(); + void updateCursor( QMouseEvent * _me = nullptr ); static bool isCloseTo( int _a, int _b ) { From 9a91848b3667c7944bf1145dadd35f0f227a10e2 Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Mon, 11 Mar 2019 16:20:42 +0900 Subject: [PATCH 19/53] Fix CONTRIBUTORS override --- src/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 37da8f414..4ac6bf133 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -45,7 +45,7 @@ INCLUDE(GenQrc) ADD_GEN_QRC(LMMS_RCC_OUT lmms.qrc "${CMAKE_SOURCE_DIR}/doc/AUTHORS" "${CMAKE_SOURCE_DIR}/LICENSE.txt" - "${CMAKE_BINARY_DIR}/CONTRIBUTORS" + "${CONTRIBUTORS}" ) # Paths relative to lmms executable From 92805685b115778e5c74af2843e748fbb9c56ac0 Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Mon, 11 Mar 2019 16:20:42 +0900 Subject: [PATCH 20/53] Debian integration: update build dependencies It was wrongly done in 231a8407e8b7422f47c3b0f8d2d807b667c75c24. --- .travis/linux.debian-sid.script.sh | 5 +---- debian/control | 5 ++++- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/.travis/linux.debian-sid.script.sh b/.travis/linux.debian-sid.script.sh index a75a9f844..7f489031b 100755 --- a/.travis/linux.debian-sid.script.sh +++ b/.travis/linux.debian-sid.script.sh @@ -21,14 +21,11 @@ fi if [ ! -e "$BASETGZ.stamp" ] then mkdir -p "$HOME/pbuilder-bases" - # debootstrap fails to resolve dependencies which are virtual packages - # e.g. perl-openssl-abi-1.1 provided by perl-openssl-defaults, needed for building SWH - # See also: https://bugs.launchpad.net/ubuntu/+source/debootstrap/+bug/86536 sudo pbuilder --create --basetgz "$BASETGZ" --mirror $MIRROR \ --distribution sid --architecture $TARGET_ARCH \ --debootstrapopts --variant=buildd \ --debootstrapopts --keyring=$KEYRING \ - --debootstrapopts --include=perl,libxml2-utils,libxml-perl,liblist-moreutils-perl,perl-openssl-defaults + --debootstrapopts --include=perl touch "$BASETGZ.stamp" else sudo pbuilder --update --basetgz "$BASETGZ" diff --git a/debian/control b/debian/control index 0997676c9..10f99c2a4 100644 --- a/debian/control +++ b/debian/control @@ -17,6 +17,7 @@ Build-Depends: libfluidsynth-dev, libgig-dev, libjack-jackd2-dev, + liblist-moreutils-perl, libmp3lame-dev, libpulse-dev, libqt5x11extras5-dev, @@ -29,10 +30,12 @@ Build-Depends: libvorbis-dev, libxcb-keysyms1-dev, libxcb-util0-dev, + libxml-perl, + libxml2-utils, portaudio19-dev, qtbase5-private-dev, qttools5-dev, - wine32-tools [i386] + wine64-tools [amd64] | wine32-tools [i386] Standards-Version: 4.2.1.4 Homepage: http://lmms.io/ Vcs-Browser: https://salsa.debian.org/debian-edu-pkg-team/lmms.git From 97502a14aca2887fc618473bf02125df1151a41d Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Mon, 11 Mar 2019 16:20:42 +0900 Subject: [PATCH 21/53] Debian: don't bundle CALF LADSPA library separately anymore It's LMMS specific now --- debian/calf-ladspa.install | 1 - debian/control | 18 ++---------------- debian/lmms-bin.install | 5 +---- 3 files changed, 3 insertions(+), 21 deletions(-) delete mode 100644 debian/calf-ladspa.install diff --git a/debian/calf-ladspa.install b/debian/calf-ladspa.install deleted file mode 100644 index c25e49dbc..000000000 --- a/debian/calf-ladspa.install +++ /dev/null @@ -1 +0,0 @@ -usr/lib/*/lmms/ladspa/calf.so usr/lib/ladspa diff --git a/debian/control b/debian/control index 10f99c2a4..65b87e6d3 100644 --- a/debian/control +++ b/debian/control @@ -44,7 +44,7 @@ Package: lmms-bin Architecture: any Depends: lmms-common (>= ${source:Version}), ${shlibs:Depends}, ${misc:Depends}, stk -Recommends: calf-ladspa, tap-plugins, caps, +Recommends: tap-plugins, caps, lmms-vst-server:i386 (>= ${source:Version}) Suggests: fil-plugins, mcp-plugins, omins, freepats, fluid-soundfont-gm, ladspa-plugin @@ -66,7 +66,7 @@ Description: Linux Multimedia Studio - minimal installation Package: lmms Architecture: any -Depends: calf-ladspa, lmms-bin, ${misc:Depends} +Depends: lmms-bin, ${misc:Depends} Description: Linux Multimedia Studio LMMS aims to be a free alternative to popular (but commercial and closed- source) programs like FruityLoops, Cubase and Logic giving you the ability of @@ -105,17 +105,3 @@ Depends: wine32, wine, ${shlibs:Depends}, ${misc:Depends} Recommends: lmms-bin:any Description: Linux Multimedia Studio - VST server This package contains a helper application that loads VST plugins. - -Package: calf-ladspa -Architecture: any -Depends: ${shlibs:Depends}, ${misc:Depends} -Replaces: calf-plugins (<< 0.0.19) -Provides: ladspa-plugin -Description: Linux Multimedia Studio - Calf LADSPA plugins - Calf is a pack of audio plugins - effects and instruments. The goal is to - create a set of plugins using decent algorithms and parameter settings, - available in a form which is compatible with as many open source applications - as possible. - . - These plugins are distributed as part of Linux Multimedia Studio, but may be - used by other applications. diff --git a/debian/lmms-bin.install b/debian/lmms-bin.install index 5d19a3103..229fa02e3 100644 --- a/debian/lmms-bin.install +++ b/debian/lmms-bin.install @@ -1,7 +1,4 @@ usr/bin/lmms -usr/lib/*/lmms/ladspa/[a-b]* -usr/lib/*/lmms/ladspa/caps.so -usr/lib/*/lmms/ladspa/c[b-z]* -usr/lib/*/lmms/ladspa/[d-z]* +usr/lib/*/lmms/ladspa/* usr/lib/*/lmms/lib* usr/lib/*/lmms/RemoteZynAddSubFx From af40c764efbf0d29e2d6df1c38f19bb325ee509c Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Mon, 11 Mar 2019 16:20:42 +0900 Subject: [PATCH 22/53] Better Wine detection and support * Support more Wine packagings * Allow building 64-bit RemoteVstPlugin using 32-bit Wine tools if possible * Provide suitable library paths for creating AppImages --- cmake/linux/package_linux.sh.in | 4 +- cmake/modules/FindWine.cmake | 117 +++++++++++++++++++++++-------- cmake/modules/winegcc_wrapper.in | 8 +++ 3 files changed, 98 insertions(+), 31 deletions(-) diff --git a/cmake/linux/package_linux.sh.in b/cmake/linux/package_linux.sh.in index 2eff0f264..bd164bd6a 100644 --- a/cmake/linux/package_linux.sh.in +++ b/cmake/linux/package_linux.sh.in @@ -134,10 +134,10 @@ export LD_LIBRARY_PATH="${APPDIR}usr/lib/lmms/":$LD_LIBRARY_PATH # Handle wine linking if [ -d "@WINE_32_LIBRARY_DIR@" ]; then - export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$LD_LIBRARY_PATH/wine/:@WINE_32_LIBRARY_DIR@:@WINE_32_LIBRARY_DIR@wine/ + export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:@WINE_32_LIBRARY_DIRS@ fi if [ -d "@WINE_64_LIBRARY_DIR@" ]; then - export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:$LD_LIBRARY_PATH/wine/:@WINE_64_LIBRARY_DIR@:@WINE_64_LIBRARY_DIR@wine/ + export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:@WINE_64_LIBRARY_DIRS@ fi # Move executables so linuxdeployqt can find them diff --git a/cmake/modules/FindWine.cmake b/cmake/modules/FindWine.cmake index 225d6a824..50bf54edb 100644 --- a/cmake/modules/FindWine.cmake +++ b/cmake/modules/FindWine.cmake @@ -7,46 +7,89 @@ # WINE_DEFINITIONS - Compiler switches required for using wine # +MACRO(_findwine_find_flags output expression result) + STRING(REPLACE " " ";" WINEBUILD_FLAGS "${output}") + FOREACH(FLAG ${WINEBUILD_FLAGS}) + IF("${FLAG}" MATCHES "${expression}") + SET(${result} "${FLAG}") + ENDIF() + ENDFOREACH() +ENDMACRO() + LIST(APPEND CMAKE_PREFIX_PATH /opt/wine-stable /opt/wine-devel /opt/wine-staging /usr/lib/wine/) - -FIND_PATH(WINE_INCLUDE_DIR wine/exception.h PATH_SUFFIXES wine) FIND_PROGRAM(WINE_CXX NAMES wineg++ winegcc winegcc64 winegcc32 winegcc-stable - PATHS /usr/lib/wine) + PATHS /usr/lib/wine +) FIND_PROGRAM(WINE_BUILD NAMES winebuild) +# Detect wine paths and handle linking problems +IF(WINE_CXX) + EXEC_PROGRAM(${WINE_CXX} ARGS "-m32 -v /dev/zero" OUTPUT_VARIABLE WINEBUILD_OUTPUT_32) + EXEC_PROGRAM(${WINE_CXX} ARGS "-m64 -v /dev/zero" OUTPUT_VARIABLE WINEBUILD_OUTPUT_64) + _findwine_find_flags("${WINEBUILD_OUTPUT_32}" "^-isystem/usr/include$" BUGGED_WINEGCC) + _findwine_find_flags("${WINEBUILD_OUTPUT_32}" "^-isystem" WINEGCC_INCLUDE_DIR) + _findwine_find_flags("${WINEBUILD_OUTPUT_32}" "libwinecrt0\\.a.*" WINECRT_32) + _findwine_find_flags("${WINEBUILD_OUTPUT_64}" "libwinecrt0\\.a.*" WINECRT_64) + STRING(REGEX REPLACE "^-isystem" "" WINE_INCLUDE_HINT "${WINEGCC_INCLUDE_DIR}") + STRING(REGEX REPLACE "/wine/windows$" "" WINE_INCLUDE_HINT "${WINE_INCLUDE_HINT}") + STRING(REGEX REPLACE "libwinecrt0\\.a.*" "" WINE_32_LIBRARY_DIR "${WINECRT_32}") + STRING(REGEX REPLACE "libwinecrt0\\.a.*" "" WINE_64_LIBRARY_DIR "${WINECRT_64}") + + IF(BUGGED_WINEGCC) + MESSAGE(WARNING "Your winegcc is unusable due to https://bugs.winehq.org/show_bug.cgi?id=46293,\n + Consider either upgrading or downgrading wine.") + RETURN() + ENDIF() + IF(WINE_32_LIBRARY_DIR STREQUAL WINE_64_LIBRARY_DIR) + MESSAGE(STATUS "Old winegcc detected, trying to use workaround linking") + # Fix library search directory according to the target bitness + IF(WINE_32_LIBRARY_DIR MATCHES "/lib/(x86_64|i386)-") + # Debian systems + STRING(REPLACE "/lib/x86_64-" "/lib/i386-" WINE_32_LIBRARY_DIR "${WINE_32_LIBRARY_DIR}") + STRING(REPLACE "/lib/i386-" "/lib/x86_64-" WINE_64_LIBRARY_DIR "${WINE_64_LIBRARY_DIR}") + ELSEIF(WINE_32_LIBRARY_DIR MATCHES "/(lib|lib64)/wine/$") + # WineHQ (/opt/wine-stable, /opt/wine-devel, /opt/wine-staging) + STRING(REGEX REPLACE "/lib64/wine/$" "/lib/wine/" WINE_32_LIBRARY_DIR "${WINE_32_LIBRARY_DIR}") + STRING(REGEX REPLACE "/lib/wine/$" "/lib64/wine/" WINE_64_LIBRARY_DIR "${WINE_64_LIBRARY_DIR}") + ELSEIF(WINE_32_LIBRARY_DIR MATCHES "/lib32/.*/wine/") + # Systems with old multilib layout + STRING(REPLACE "/lib32/" "/lib/" WINE_64_LIBRARY_DIR "${WINE_32_LIBRARY_DIR}") + ELSEIF(WINE_32_LIBRARY_DIR MATCHES "/lib64/.*/wine/") + # We need to test if the corresponding 64bit library directory is lib or lib32 + STRING(REPLACE "/lib64/" "/lib32/" WINE_32_LIBRARY_DIR "${WINE_64_LIBRARY_DIR}") + IF(NOT EXISTS "${WINE_32_LIBRARY_DIR}") + STRING(REPLACE "/lib64/" "/lib/" WINE_32_LIBRARY_DIR "${WINE_64_LIBRARY_DIR}") + ENDIF() + ELSEIF(WINE_32_LIBRARY_DIR MATCHES "/lib/.*/wine/") + # Test if this directory is for 32bit or 64bit + STRING(REPLACE "/lib/" "/lib32/" WINE_32_LIBRARY_DIR "${WINE_64_LIBRARY_DIR}") + IF(NOT EXISTS "${WINE_32_LIBRARY_DIR}") + SET(WINE_32_LIBRARY_DIR "${WINE_64_LIBRARY_DIR}") + STRING(REPLACE "/lib/" "/lib64/" WINE_64_LIBRARY_DIR "${WINE_64_LIBRARY_DIR}") + ENDIF() + ELSE() + MESSAGE(WARNING "Can't detect wine installation layout. You may get some build errors.") + ENDIF() + SET(WINE_LIBRARY_FIX "${WINE_32_LIBRARY_DIR} and ${WINE_64_LIBRARY_DIR}") + ENDIF() +ENDIF() + +FIND_PATH(WINE_INCLUDE_DIR wine/exception.h + HINTS "${WINE_INCLUDE_HINT}" +) SET(_ARCHITECTURE ${CMAKE_LIBRARY_ARCHITECTURE}) -FIND_LIBRARY(WINE_LIBRARY NAMES wine PATH_SUFFIXES wine i386-linux-gnu/wine) +FIND_LIBRARY(WINE_LIBRARY NAMES wine + PATH_SUFFIXES wine i386-linux-gnu/wine + HINTS "${WINE_32_LIBRARY_DIR}" "${WINE_64_LIBRARY_DIR}" +) SET(CMAKE_LIBRARY_ARCHITECTURE ${_ARCHITECTURE}) SET(WINE_INCLUDE_DIRS ${WINE_INCLUDE_DIR} ) -SET(WINE_LIBRARIES ${WINE_LIBRARY} ) - -# Handle wine linking problems -EXEC_PROGRAM(${WINE_CXX} ARGS "-v -m32 /dev/zero" OUTPUT_VARIABLE WINEBUILD_OUTPUT) -STRING(REPLACE " " ";" WINEBUILD_FLAGS "${WINEBUILD_OUTPUT}") - -FOREACH(FLAG ${WINEBUILD_FLAGS}) - IF("${FLAG}" MATCHES "libwinecrt0.a.*") - STRING(REGEX REPLACE "/wine/libwinecrt0.a.*" "" FLAG "${FLAG}") - - SET(WINE_64_LIBRARY_DIR "${FLAG}/") - - # Debian systems - STRING(REPLACE "/lib/x86_64-" "/lib/i386-" FLAG "${FLAG}") - # Fedora systems - STRING(REPLACE "/lib/lib64" "/lib/i386" FLAG "${FLAG}") - # Gentoo systems - STRING(REPLACE "/lib/wine-" "/lib32/wine-" FLAG "${FLAG}") - # WineHQ (/opt/wine-stable, /opt/wine-devel, /opt/wine-staging) - STRING(REGEX REPLACE "/lib64$" "/lib" FLAG "${FLAG}") - - SET(WINE_32_LIBRARY_DIR "${FLAG}/") - ENDIF() -ENDFOREACH() +SET(WINE_LIBRARIES ${WINE_LIBRARY}) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Wine DEFAULT_MSG WINE_CXX WINE_LIBRARIES WINE_INCLUDE_DIRS) @@ -54,7 +97,23 @@ find_package_handle_standard_args(Wine DEFAULT_MSG WINE_CXX WINE_LIBRARIES WINE_ mark_as_advanced(WINE_INCLUDE_DIR WINE_LIBRARY WINE_CXX WINE_BUILD) IF(WINE_32_LIBRARY_DIR) - SET(WINE_32_FLAGS "-L${WINE_32_LIBRARY_DIR}wine/ -L${WINE_32_LIBRARY_DIR}") + IF(WINE_32_LIBRARY_DIR MATCHES "wine*/lib") + SET(WINE_32_FLAGS "-L${WINE_32_LIBRARY_DIR} -L${WINE_32_LIBRARY_DIR}../") + SET(WINE_32_LIBRARY_DIRS "${WINE_32_LIBRARY_DIR}:${WINE_32_LIBRARY_DIR}/..") + ELSE() + SET(WINE_32_FLAGS "-L${WINE_32_LIBRARY_DIR}") + SET(WINE_32_LIBRARY_DIRS "${WINE_32_LIBRARY_DIR}") + ENDIF() +ENDIF() + +IF(WINE_64_LIBRARY_DIR) + IF(WINE_64_LIBRARY_DIR MATCHES "wine*/lib") + SET(WINE_64_FLAGS "-L${WINE_64_LIBRARY_DIR} -L${WINE_64_LIBRARY_DIR}../") + SET(WINE_64_LIBRARY_DIRS "${WINE_64_LIBRARY_DIR}:${WINE_64_LIBRARY_DIR}/..") + ELSE() + SET(WINE_64_FLAGS "-L${WINE_64_LIBRARY_DIR}") + SET(WINE_64_LIBRARY_DIRS "${WINE_64_LIBRARY_DIR}") + ENDIF() ENDIF() # Create winegcc wrapper diff --git a/cmake/modules/winegcc_wrapper.in b/cmake/modules/winegcc_wrapper.in index d7d680be2..32d65dd68 100755 --- a/cmake/modules/winegcc_wrapper.in +++ b/cmake/modules/winegcc_wrapper.in @@ -22,6 +22,9 @@ while [ $# -gt 0 ]; do -m32) win32=true ;; + -m64) + win64=true + ;; *) ;; @@ -47,6 +50,11 @@ if [ "$win32" = true ] && [ "$no_link" != true ]; then extra_args="$extra_args @WINE_32_FLAGS@" fi +# Apply -m64 library fix if necessary +if [ "$win64" = true ] && [ "$no_link" != true ]; then + extra_args="$extra_args @WINE_64_FLAGS@" +fi + # Run winegcc export WINEBUILD=@WINE_BUILD@ @WINE_CXX@ $extra_args $args From 8c4514ff209de5ddc71a67acd2a0216227d641dc Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Mon, 11 Mar 2019 16:20:42 +0900 Subject: [PATCH 23/53] Check if winegcc works before building RemoteVstPlugin --- cmake/modules/CheckWineGcc.cmake | 27 ++++++++++++++++++++++++ plugins/vst_base/RemoteVstPlugin32.cmake | 6 ++++++ plugins/vst_base/RemoteVstPlugin64.cmake | 6 ++++++ 3 files changed, 39 insertions(+) create mode 100644 cmake/modules/CheckWineGcc.cmake diff --git a/cmake/modules/CheckWineGcc.cmake b/cmake/modules/CheckWineGcc.cmake new file mode 100644 index 000000000..2956198d8 --- /dev/null +++ b/cmake/modules/CheckWineGcc.cmake @@ -0,0 +1,27 @@ +INCLUDE(CheckCXXSourceCompiles) + +FUNCTION(CheckWineGcc BITNESS WINEGCC_EXECUTABLE RESULT) + FILE(WRITE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/winegcc_test.cxx" " + #include + #define USE_WS_PREFIX + #include + int main(int argc, const char* argv[]) { + return 0; + } + ") + EXECUTE_PROCESS(COMMAND ${WINEGCC_EXECUTABLE} "-m${BITNESS}" + "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/winegcc_test.cxx" + "-o" "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/winegcc_test" + OUTPUT_QUIET ERROR_QUIET + RESULT_VARIABLE WINEGCC_RESULT + ) + FILE(REMOVE "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/winegcc_test.cxx" + "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/winegcc_test" + "${CMAKE_BINARY_DIR}${CMAKE_FILES_DIRECTORY}/CMakeTmp/winegcc_test.exe.so" + ) + IF(WINEGCC_RESULT EQUAL 0) + SET(${RESULT} True PARENT_SCOPE) + ELSE() + SET(${RESULT} False PARENT_SCOPE) + ENDIF() +ENDFUNCTION() diff --git a/plugins/vst_base/RemoteVstPlugin32.cmake b/plugins/vst_base/RemoteVstPlugin32.cmake index 507042148..9a8f04529 100644 --- a/plugins/vst_base/RemoteVstPlugin32.cmake +++ b/plugins/vst_base/RemoteVstPlugin32.cmake @@ -58,6 +58,12 @@ ELSEIF(LMMS_BUILD_WIN64 AND MSVC) ELSEIF(LMMS_BUILD_LINUX) # Use winegcc + INCLUDE(CheckWineGcc) + CheckWineGcc(32 "${WINEGCC}" WINEGCC_WORKING) + IF(NOT WINEGCC_WORKING) + MESSAGE(WARNING "winegcc fails to complie 32-bit binaries, please make sure you have 32-bit GCC libraries") + RETURN() + ENDIF() ExternalProject_Add(RemoteVstPlugin32 "${EXTERNALPROJECT_ARGS}" CMAKE_ARGS diff --git a/plugins/vst_base/RemoteVstPlugin64.cmake b/plugins/vst_base/RemoteVstPlugin64.cmake index f802bc4b9..4b02bf8ab 100644 --- a/plugins/vst_base/RemoteVstPlugin64.cmake +++ b/plugins/vst_base/RemoteVstPlugin64.cmake @@ -2,6 +2,12 @@ IF(LMMS_BUILD_WIN64) ADD_SUBDIRECTORY(RemoteVstPlugin) INSTALL(PROGRAMS "${CMAKE_CURRENT_BINARY_DIR}/../RemoteVstPlugin64.exe" DESTINATION "${PLUGIN_DIR}") ELSEIF(LMMS_BUILD_LINUX) + INCLUDE(CheckWineGcc) + CheckWineGcc(64 "${WINEGCC}" WINEGCC_WORKING) + IF(NOT WINEGCC_WORKING) + MESSAGE(WARNING "winegcc fails to compile 64-bit binaries, please make sure you have 64-bit GCC libraries") + RETURN() + ENDIF() ExternalProject_Add(RemoteVstPlugin64 "${EXTERNALPROJECT_ARGS}" CMAKE_ARGS From ae4e40de97d4e4e544166f6988b02c40e70fdb2c Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Mon, 11 Mar 2019 16:20:42 +0900 Subject: [PATCH 24/53] Modify VST build systems to make debian integration work correctly --- CMakeLists.txt | 3 ++- cmake/modules/winegcc_wrapper.in | 3 +++ debian/control | 8 ++++---- debian/lmms-vst-server.install | 2 +- debian/rules | 14 ++++++++++---- plugins/vst_base/CMakeLists.txt | 16 ++++++++++------ plugins/vst_base/VstPlugin.cpp | 4 ++-- plugins/vst_base/vstbase/CMakeLists.txt | 3 +++ 8 files changed, 35 insertions(+), 18 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 777245268..36ad7f148 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -67,7 +67,8 @@ OPTION(WANT_STK "Include Stk (Synthesis Toolkit) support" ON) OPTION(WANT_SWH "Include Steve Harris's LADSPA plugins" ON) OPTION(WANT_TAP "Include Tom's Audio Processing LADSPA plugins" ON) OPTION(WANT_VST "Include VST support" ON) -OPTION(WANT_VST_NOWINE "Include partial VST support (without wine)" OFF) +OPTION(WANT_VST_32 "Include 32-bit VST support" ON) +OPTION(WANT_VST_64 "Include 64-bit VST support" ON) OPTION(WANT_WINMM "Include WinMM MIDI support" OFF) OPTION(WANT_DEBUG_FPE "Debug floating point exceptions" OFF) diff --git a/cmake/modules/winegcc_wrapper.in b/cmake/modules/winegcc_wrapper.in index 32d65dd68..d32aec664 100755 --- a/cmake/modules/winegcc_wrapper.in +++ b/cmake/modules/winegcc_wrapper.in @@ -45,6 +45,9 @@ fi # by FindWine.cmake extra_args="-I@WINE_INCLUDE_DIR@ -I@WINE_INCLUDE_DIR@/wine/windows" +# Apply manually specified flags +extra_args="$extra_args @WINE_CXX_FLAGS@" + # Apply -m32 library fix if necessary if [ "$win32" = true ] && [ "$no_link" != true ]; then extra_args="$extra_args @WINE_32_FLAGS@" diff --git a/debian/control b/debian/control index 65b87e6d3..463353df0 100644 --- a/debian/control +++ b/debian/control @@ -45,7 +45,8 @@ Architecture: any Depends: lmms-common (>= ${source:Version}), ${shlibs:Depends}, ${misc:Depends}, stk Recommends: tap-plugins, caps, - lmms-vst-server:i386 (>= ${source:Version}) + lmms-vst-server:i386 (>= ${source:Version}), + lmms-vst-server:amd64 (>= ${source:Version}) Suggests: fil-plugins, mcp-plugins, omins, freepats, fluid-soundfont-gm, ladspa-plugin Replaces: lmms-common (<< 1.0.0-1) @@ -99,9 +100,8 @@ Description: Linux Multimedia Studio - common files and some example projects. Package: lmms-vst-server -Architecture: i386 -# Order matters to avoid wine64 -Depends: wine32, wine, ${shlibs:Depends}, ${misc:Depends} +Architecture: amd64 i386 +Depends: wine64 [amd64] | wine64-development [amd64] | wine32 [i386] | wine32-development [i386], ${shlibs:Depends}, ${misc:Depends} Recommends: lmms-bin:any Description: Linux Multimedia Studio - VST server This package contains a helper application that loads VST plugins. diff --git a/debian/lmms-vst-server.install b/debian/lmms-vst-server.install index 1b520479d..60efaed7b 100644 --- a/debian/lmms-vst-server.install +++ b/debian/lmms-vst-server.install @@ -1 +1 @@ -usr/lib/*/lmms/RemoteVstPlugin* +usr/lib/*/lmms/{32/,}RemoteVstPlugin* diff --git a/debian/rules b/debian/rules index 5e8345845..aed094c22 100755 --- a/debian/rules +++ b/debian/rules @@ -6,6 +6,7 @@ DH_CMAKE_BUILD_DIR=obj -${DEB_BUILD_GNU_TYPE} DEB_HOST_MULTIARCH ?= $(shell dpkg-architecture -qDEB_HOST_MULTIARCH) DEB_HOST_ARCH ?= $(shell dpkg-architecture -qDEB_HOST_ARCH) DEB_HOST_ARCH_OS ?= $(shell dpkg-architecture -qDEB_HOST_ARCH_OS) +DEB_HOST_ARCH_BIT ?= $(shell dpkg-architecture -qDEB_HOST_ARCH_BITS) CMAKE_OPTS= -DCONTRIBUTORS=$(CURDIR)/doc/CONTRIBUTORS -DFORCE_VERSION=internal \ -DWANT_QT5=1 @@ -13,13 +14,18 @@ ifneq ($(DEB_HOST_ARCH_OS),linux) CMAKE_OPTS+= -DWANT_ALSA=0 endif -ifeq ($(DEB_HOST_ARCH),i386) +ifeq ($(DEB_HOST_ARCH),amd64) export PATH := $(PATH):/usr/lib/wine WINE_PATH := /usr/lib/$(DEB_HOST_MULTIARCH)/wine -CMAKE_OPTS+= -DWINE_CXX_FLAGS=-Wl,--enable-new-dtags,-rpath=$(WINE_PATH) +CMAKE_OPTS+= -DWANT_VST_32=OFF -DREMOTE_VST_PLUGIN_FILEPATH_32=../../i386-linux-gnu/lmms/32/RemoteVstPlugin32 \ + -DWINE_CXX_FLAGS=-Wl,--enable-new-dtags,-rpath=$(WINE_PATH) +else ifeq ($(DEB_HOST_ARCH),i386) +export PATH := $(PATH):/usr/lib/wine +WINE_PATH := /usr/lib/$(DEB_HOST_MULTIARCH)/wine +CMAKE_OPTS+= -DWANT_VST_64=OFF -DREMOTE_VST_PLUGIN_FILEPATH_64=../../x86_64-linux-gnu/lmms/RemoteVstPlugin64 \ + -DWINE_CXX_FLAGS=-Wl,--enable-new-dtags,-rpath=$(WINE_PATH) else -CMAKE_OPTS+= -DWANT_VST_NOWINE=1 \ - -DREMOTE_VST_PLUGIN_FILEPATH=../../i386-linux-gnu/lmms/RemoteVstPlugin +CMAKE_OPTS+= -DWANT_VST=OFF endif # Define NDEBUG. This helps with reproducible builds. diff --git a/plugins/vst_base/CMakeLists.txt b/plugins/vst_base/CMakeLists.txt index 314d5fc18..44ed0dcb3 100644 --- a/plugins/vst_base/CMakeLists.txt +++ b/plugins/vst_base/CMakeLists.txt @@ -5,11 +5,11 @@ ENDIF() INCLUDE(BuildPlugin) INCLUDE(ExternalProject) -ADD_SUBDIRECTORY(vstbase) +# These variables are not meant to be used normally, except packaging +SET(REMOTE_VST_PLUGIN_FILEPATH_32 "32/RemoteVstPlugin32" CACHE STRING "Relative file path to RemoteVstPlugin32") +SET(REMOTE_VST_PLUGIN_FILEPATH_64 "RemoteVstPlugin64" CACHE STRING "Relative file path to RemoteVstPlugin64") -IF(LMMS_BUILD_LINUX AND WANT_VST_NOWINE) - RETURN() -ENDIF() +ADD_SUBDIRECTORY(vstbase) SET(LMMS_BINARY_DIR ${CMAKE_BINARY_DIR}) SET(LMMS_SOURCE_DIR ${CMAKE_SOURCE_DIR}) @@ -29,6 +29,10 @@ SET(EXTERNALPROJECT_CMAKE_ARGS ) # build 32 bit version of RemoteVstPlugin -INCLUDE("${CMAKE_CURRENT_LIST_DIR}/RemoteVstPlugin32.cmake") +IF(WANT_VST_32) + INCLUDE("${CMAKE_CURRENT_LIST_DIR}/RemoteVstPlugin32.cmake") +ENDIF() # build 64 bit version of RemoteVstPlugin -INCLUDE("${CMAKE_CURRENT_LIST_DIR}/RemoteVstPlugin64.cmake") +IF(WANT_VST_64) + INCLUDE("${CMAKE_CURRENT_LIST_DIR}/RemoteVstPlugin64.cmake") +ENDIF() diff --git a/plugins/vst_base/VstPlugin.cpp b/plugins/vst_base/VstPlugin.cpp index 2e69802b2..7dbeee5a6 100644 --- a/plugins/vst_base/VstPlugin.cpp +++ b/plugins/vst_base/VstPlugin.cpp @@ -149,10 +149,10 @@ VstPlugin::VstPlugin( const QString & _plugin ) : switch(machineType) { case PE::MachineType::amd64: - tryLoad( "RemoteVstPlugin64" ); + tryLoad( REMOTE_VST_PLUGIN_FILEPATH_64 ); // Default: RemoteVstPlugin64 break; case PE::MachineType::i386: - tryLoad( "32/RemoteVstPlugin32" ); + tryLoad( REMOTE_VST_PLUGIN_FILEPATH_32 ); // Default: 32/RemoteVstPlugin32 break; default: m_failed = true; diff --git a/plugins/vst_base/vstbase/CMakeLists.txt b/plugins/vst_base/vstbase/CMakeLists.txt index 28c09edb3..bd5f98b56 100644 --- a/plugins/vst_base/vstbase/CMakeLists.txt +++ b/plugins/vst_base/vstbase/CMakeLists.txt @@ -1,3 +1,6 @@ +ADD_DEFINITIONS(-DREMOTE_VST_PLUGIN_FILEPATH_32="${REMOTE_VST_PLUGIN_FILEPATH_32}") +ADD_DEFINITIONS(-DREMOTE_VST_PLUGIN_FILEPATH_64="${REMOTE_VST_PLUGIN_FILEPATH_64}") + BUILD_PLUGIN(vstbase ../vst_base.cpp ../VstPlugin.cpp ../VstPlugin.h ../communication.h MOCFILES ../VstPlugin.h From 5eb6b138aa7f1c8ffa884c7d81d49a1850559f54 Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Mon, 11 Mar 2019 16:20:42 +0900 Subject: [PATCH 25/53] Allow creating AppImages on systems newer than linuxdeployqt officially supports Note that the additional -unsupported-allow-new-glibc switch may result in an AppImage which is unusable on old systems. --- cmake/linux/package_linux.sh.in | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/cmake/linux/package_linux.sh.in b/cmake/linux/package_linux.sh.in index bd164bd6a..e6d79185e 100644 --- a/cmake/linux/package_linux.sh.in +++ b/cmake/linux/package_linux.sh.in @@ -172,8 +172,10 @@ executables="${executables} -executable=${APPDIR}usr/lib/lmms/ladspa/pitch_scale # Bundle both qt and non-qt dependencies into appimage format echo -e "\nBundling and relinking system dependencies..." echo -e ">>>>> linuxdeployqt" > "$LOGFILE" +# FIXME: -unsupported-allow-new-glibc may result in an AppImage which is unusable on old systems. + # shellcheck disable=SC2086 -"$LINUXDEPLOYQT" "$DESKTOPFILE" $executables -bundle-non-qt-libs -verbose=$VERBOSITY $STRIP >> "$LOGFILE" 2>&1 +"$LINUXDEPLOYQT" "$DESKTOPFILE" $executables -unsupported-allow-new-glibc -bundle-non-qt-libs -verbose=$VERBOSITY $STRIP >> "$LOGFILE" 2>&1 success "Bundled and relinked dependencies" # Link to original location so lmms can find them From 61c3f87ee677fc1ae390ef036709f296fee64c64 Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Mon, 11 Mar 2019 17:06:39 +0900 Subject: [PATCH 26/53] Support FX Mixer for sample tracks and add controls to sample track window (#3866) This work is based on https://github.com/LMMS/lmms/pull/3632 by @grejppi. --- include/FxLineLcdSpinBox.h | 53 ++++ include/InstrumentTrack.h | 3 +- include/SampleTrack.h | 101 +++++++- include/Track.h | 4 + src/core/FxMixer.cpp | 31 ++- src/core/Track.cpp | 21 +- src/gui/CMakeLists.txt | 1 + src/gui/FxMixerView.cpp | 7 + src/gui/widgets/FxLineLcdSpinBox.cpp | 66 +++++ src/tracks/InstrumentTrack.cpp | 50 +--- src/tracks/SampleTrack.cpp | 362 ++++++++++++++++++++++++--- 11 files changed, 614 insertions(+), 85 deletions(-) create mode 100644 include/FxLineLcdSpinBox.h create mode 100644 src/gui/widgets/FxLineLcdSpinBox.cpp diff --git a/include/FxLineLcdSpinBox.h b/include/FxLineLcdSpinBox.h new file mode 100644 index 000000000..fa001b2bb --- /dev/null +++ b/include/FxLineLcdSpinBox.h @@ -0,0 +1,53 @@ +/* + * FxLineLcdSpinBox.h - a specialization of LcdSpnBox for setting FX channels + * + * Copyright (c) 2004-2014 Tobias Doerffel + * + * 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. + * + */ + +#ifndef FX_LINE_LCD_SPIN_BOX_H +#define FX_LINE_LCD_SPIN_BOX_H + +#include "LcdSpinBox.h" + +class TrackView; + + +class FxLineLcdSpinBox : public LcdSpinBox +{ + Q_OBJECT +public: + FxLineLcdSpinBox(int numDigits, QWidget * parent, const QString& name, TrackView * tv = NULL) : + LcdSpinBox(numDigits, parent, name), m_tv(tv) + {} + virtual ~FxLineLcdSpinBox() {} + + void setTrackView(TrackView * tv); + +protected: + virtual void mouseDoubleClickEvent(QMouseEvent* event); + virtual void contextMenuEvent(QContextMenuEvent* event); + +private: + TrackView * m_tv; + +}; + +#endif diff --git a/include/InstrumentTrack.h b/include/InstrumentTrack.h index fb12e825a..42d7910b9 100644 --- a/include/InstrumentTrack.h +++ b/include/InstrumentTrack.h @@ -52,6 +52,7 @@ class InstrumentTrackWindow; class InstrumentMidiIOView; class InstrumentMiscView; class Knob; +class FxLineLcdSpinBox; class LcdSpinBox; class LeftRightNav; class midiPortMenu; @@ -440,7 +441,7 @@ private: QLabel * m_pitchLabel; LcdSpinBox* m_pitchRangeSpinBox; QLabel * m_pitchRangeLabel; - LcdSpinBox * m_effectChannelNumber; + FxLineLcdSpinBox * m_effectChannelNumber; diff --git a/include/SampleTrack.h b/include/SampleTrack.h index decf52f3f..ccb5a020e 100644 --- a/include/SampleTrack.h +++ b/include/SampleTrack.h @@ -26,13 +26,19 @@ #define SAMPLE_TRACK_H #include +#include #include "AudioPort.h" +#include "FxMixer.h" +#include "FxLineLcdSpinBox.h" #include "Track.h" class EffectRackView; class Knob; class SampleBuffer; +class SampleTrackWindow; +class TrackLabelButton; +class QLineEdit; class SampleTCO : public TrackContentObject @@ -140,6 +146,11 @@ public: QDomElement & _parent ); virtual void loadTrackSpecificSettings( const QDomElement & _this ); + inline IntModel * effectChannelModel() + { + return &m_effectChannelModel; + } + inline AudioPort * audioPort() { return &m_audioPort; @@ -153,15 +164,18 @@ public: public slots: void updateTcos(); void setPlayingTcos( bool isPlaying ); + void updateEffectChannel(); private: FloatModel m_volumeModel; FloatModel m_panningModel; + IntModel m_effectChannelModel; AudioPort m_audioPort; friend class SampleTrackView; + friend class SampleTrackWindow; } ; @@ -174,6 +188,24 @@ public: SampleTrackView( SampleTrack* Track, TrackContainerView* tcv ); virtual ~SampleTrackView(); + SampleTrackWindow * getSampleTrackWindow() + { + return m_window; + } + + SampleTrack * model() + { + return castModel(); + } + + const SampleTrack * model() const + { + return castModel(); + } + + + virtual QMenu * createFxMenu( QString title, QString newFxLabel ); + public slots: void showEffects(); @@ -187,12 +219,77 @@ protected: } +private slots: + void assignFxLine( int channelIndex ); + void createFxLine(); + + private: - EffectRackView * m_effectRack; - QWidget * m_effWindow; + SampleTrackWindow * m_window; Knob * m_volumeKnob; Knob * m_panningKnob; + TrackLabelButton * m_tlb; + + + friend class SampleTrackWindow; + +} ; + + + +class SampleTrackWindow : public QWidget, public ModelView, public SerializingObjectHook +{ + Q_OBJECT +public: + SampleTrackWindow(SampleTrackView * tv); + virtual ~SampleTrackWindow(); + + SampleTrack * model() + { + return castModel(); + } + + const SampleTrack * model() const + { + return castModel(); + } + + void setSampleTrackView(SampleTrackView * tv); + + SampleTrackView *sampleTrackView() + { + return m_stv; + } + + +public slots: + void textChanged(const QString & new_name); + void toggleVisibility(bool on); + void updateName(); + + +protected: + // capture close-events for toggling sample-track-button + virtual void closeEvent(QCloseEvent * ce); + + virtual void saveSettings(QDomDocument & doc, QDomElement & element); + virtual void loadSettings(const QDomElement & element); + +private: + virtual void modelChanged(); + + SampleTrack * m_track; + SampleTrackView * m_stv; + + // widgets on the top of an sample-track-window + QLineEdit * m_nameLineEdit; + Knob * m_volumeKnob; + Knob * m_panningKnob; + FxLineLcdSpinBox * m_effectChannelNumber; + + EffectRackView * m_effectRack; + } ; diff --git a/include/Track.h b/include/Track.h index 302dcb5cc..f7f71de13 100644 --- a/include/Track.h +++ b/include/Track.h @@ -675,6 +675,10 @@ public: virtual void update(); + // Create a menu for assigning/creating channels for this track + // Currently instrument track and sample track supports it + virtual QMenu * createFxMenu(QString title, QString newFxLabel); + public slots: virtual bool close(); diff --git a/src/core/FxMixer.cpp b/src/core/FxMixer.cpp index 0e5f200d6..032090bf1 100644 --- a/src/core/FxMixer.cpp +++ b/src/core/FxMixer.cpp @@ -32,6 +32,7 @@ #include "Song.h" #include "InstrumentTrack.h" +#include "SampleTrack.h" #include "BBTrackContainer.h" FxRoute::FxRoute( FxChannel * from, FxChannel * to, float amount ) : @@ -305,6 +306,22 @@ void FxMixer::deleteChannel( int index ) inst->effectChannelModel()->setValue(val-1); } } + else if( t->type() == Track::SampleTrack ) + { + SampleTrack* strk = dynamic_cast( t ); + int val = strk->effectChannelModel()->value(0); + if( val == index ) + { + // we are deleting this track's fx send + // send to master + strk->effectChannelModel()->setValue(0); + } + else if( val > index ) + { + // subtract 1 to make up for the missing channel + strk->effectChannelModel()->setValue(val-1); + } + } } FxChannel * ch = m_fxChannels[index]; @@ -379,6 +396,19 @@ void FxMixer::moveChannelLeft( int index ) inst->effectChannelModel()->setValue(a); } } + else if( trackList[i]->type() == Track::SampleTrack ) + { + SampleTrack * strk = (SampleTrack *) trackList[i]; + int val = strk->effectChannelModel()->value(0); + if( val == a ) + { + strk->effectChannelModel()->setValue(b); + } + else if( val == b ) + { + strk->effectChannelModel()->setValue(a); + } + } } } @@ -780,4 +810,3 @@ void FxMixer::validateChannelName( int index, int oldIndex ) m_fxChannels[index]->m_name = tr( "FX %1" ).arg( index ); } } - diff --git a/src/core/Track.cpp b/src/core/Track.cpp index 5e6758fde..6c38711c4 100644 --- a/src/core/Track.cpp +++ b/src/core/Track.cpp @@ -1924,13 +1924,15 @@ void TrackOperationsWidget::updateMenu() { toMenu->addAction( tr( "Clear this track" ), this, SLOT( clearTrack() ) ); } - if( InstrumentTrackView * trackView = dynamic_cast( m_trackView ) ) + if (QMenu *fxMenu = m_trackView->createFxMenu(tr("FX %1: %2"), tr("Assign to new FX Channel"))) { - QMenu *fxMenu = trackView->createFxMenu( tr( "FX %1: %2" ), tr( "Assign to new FX Channel" )); toMenu->addMenu(fxMenu); + } + if (InstrumentTrackView * trackView = dynamic_cast(m_trackView)) + { toMenu->addSeparator(); - toMenu->addMenu( trackView->midiMenu() ); + toMenu->addMenu(trackView->midiMenu()); } if( dynamic_cast( m_trackView ) ) { @@ -2677,6 +2679,19 @@ void TrackView::update() +/*! \brief Create a menu for assigning/creating channels for this track. + * + */ +QMenu * TrackView::createFxMenu(QString title, QString newFxLabel) +{ + Q_UNUSED(title) + Q_UNUSED(newFxLabel) + return NULL; +} + + + + /*! \brief Close this track View. * */ diff --git a/src/gui/CMakeLists.txt b/src/gui/CMakeLists.txt index d5ff64612..f17ef105f 100644 --- a/src/gui/CMakeLists.txt +++ b/src/gui/CMakeLists.txt @@ -56,6 +56,7 @@ SET(LMMS_SRCS gui/widgets/FadeButton.cpp gui/widgets/Fader.cpp gui/widgets/FxLine.cpp + gui/widgets/FxLineLcdSpinBox.cpp gui/widgets/Graph.cpp gui/widgets/GroupBox.cpp gui/widgets/InstrumentFunctionViews.cpp diff --git a/src/gui/FxMixerView.cpp b/src/gui/FxMixerView.cpp index 440e37d10..0a6a54866 100644 --- a/src/gui/FxMixerView.cpp +++ b/src/gui/FxMixerView.cpp @@ -47,6 +47,7 @@ #include "Mixer.h" #include "gui_templates.h" #include "InstrumentTrack.h" +#include "SampleTrack.h" #include "Song.h" #include "BBTrackContainer.h" @@ -251,6 +252,12 @@ void FxMixerView::updateMaxChannelSelector() inst->effectChannelModel()->setRange(0, m_fxChannelViews.size()-1,1); } + else if( trackList[i]->type() == Track::SampleTrack ) + { + SampleTrack * strk = (SampleTrack *) trackList[i]; + strk->effectChannelModel()->setRange(0, + m_fxChannelViews.size()-1,1); + } } } } diff --git a/src/gui/widgets/FxLineLcdSpinBox.cpp b/src/gui/widgets/FxLineLcdSpinBox.cpp new file mode 100644 index 000000000..bfe4a9637 --- /dev/null +++ b/src/gui/widgets/FxLineLcdSpinBox.cpp @@ -0,0 +1,66 @@ +/* + * FxLineLcdSpinBox.cpp - a specialization of LcdSpnBox for setting FX channels + * + * Copyright (c) 2004-2014 Tobias Doerffel + * + * 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 "FxLineLcdSpinBox.h" + +#include "CaptionMenu.h" +#include "FxMixerView.h" +#include "GuiApplication.h" +#include "Track.h" + +void FxLineLcdSpinBox::setTrackView(TrackView * tv) +{ + m_tv = tv; +} + +void FxLineLcdSpinBox::mouseDoubleClickEvent(QMouseEvent* event) +{ + gui->fxMixerView()->setCurrentFxLine(model()->value()); + + gui->fxMixerView()->parentWidget()->show(); + gui->fxMixerView()->show();// show fxMixer window + gui->fxMixerView()->setFocus();// set focus to fxMixer window + //engine::getFxMixerView()->raise(); +} + +void FxLineLcdSpinBox::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(nullptr); + + QPointer contextMenu = new CaptionMenu(model()->displayName(), this); + + if (QMenu *fxMenu = m_tv->createFxMenu( + tr("Assign to:"), tr("New FX Channel"))) + { + contextMenu->addMenu(fxMenu); + + contextMenu->addSeparator(); + } + addDefaultActions(contextMenu); + contextMenu->exec(QCursor::pos()); +} diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index a927e8aad..1dc0642d6 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -47,6 +47,7 @@ #include "EffectRackView.h" #include "embed.h" #include "FileBrowser.h" +#include "FxLineLcdSpinBox.h" #include "FxMixer.h" #include "FxMixerView.h" #include "GuiApplication.h" @@ -1249,52 +1250,6 @@ QMenu * InstrumentTrackView::createFxMenu(QString title, QString newFxLabel) -class fxLineLcdSpinBox : public LcdSpinBox -{ - Q_OBJECT - public: - fxLineLcdSpinBox( int _num_digits, QWidget * _parent, - const QString & _name ) : - LcdSpinBox( _num_digits, _parent, _name ) {} - - protected: - virtual void mouseDoubleClickEvent ( QMouseEvent * _me ) - { - gui->fxMixerView()->setCurrentFxLine( model()->value() ); - - gui->fxMixerView()->parentWidget()->show(); - gui->fxMixerView()->show();// show fxMixer window - gui->fxMixerView()->setFocus();// set focus to fxMixer window - //engine::getFxMixerView()->raise(); - } - - virtual void 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 ); - - QPointer contextMenu = new CaptionMenu( model()->displayName(), this ); - - // This condition is here just as a safety check, fxLineLcdSpinBox is aways - // created inside a TabWidget inside an InstrumentTrackWindow - if ( InstrumentTrackWindow* window = dynamic_cast( (QWidget *)this->parent()->parent() ) ) - { - QMenu *fxMenu = window->instrumentTrackView()->createFxMenu( tr( "Assign to:" ), tr( "New FX channel" ) ); - contextMenu->addMenu( fxMenu ); - - contextMenu->addSeparator(); - } - addDefaultActions( contextMenu ); - contextMenu->exec( QCursor::pos() ); - } - -}; - - - // #### ITW: InstrumentTrackWindow::InstrumentTrackWindow( InstrumentTrackView * _itv ) : QWidget(), @@ -1415,7 +1370,7 @@ InstrumentTrackWindow::InstrumentTrackWindow( InstrumentTrackView * _itv ) : // setup spinbox for selecting FX-channel - m_effectChannelNumber = new fxLineLcdSpinBox( 2, NULL, tr( "FX channel" ) ); + m_effectChannelNumber = new FxLineLcdSpinBox( 2, NULL, tr( "FX channel" ), m_itv ); basicControlsLayout->addWidget( m_effectChannelNumber, 0, 6 ); basicControlsLayout->setAlignment( m_effectChannelNumber, widgetAlignment ); @@ -1536,6 +1491,7 @@ void InstrumentTrackWindow::setInstrumentTrackView( InstrumentTrackView* view ) } m_itv = view; + m_effectChannelNumber->setTrackView(m_itv); } diff --git a/src/tracks/SampleTrack.cpp b/src/tracks/SampleTrack.cpp index 8fa6fd50f..c965af140 100644 --- a/src/tracks/SampleTrack.cpp +++ b/src/tracks/SampleTrack.cpp @@ -28,6 +28,7 @@ #include #include #include +#include #include #include #include @@ -48,6 +49,8 @@ #include "MainWindow.h" #include "Mixer.h" #include "EffectRackView.h" +#include "FxMixerView.h" +#include "TabWidget.h" #include "TrackLabelButton.h" SampleTCO::SampleTCO( Track * _track ) : @@ -471,7 +474,7 @@ void SampleTCOView::paintEvent( QPaintEvent * pe ) bool muted = m_tco->getTrack()->isMuted() || m_tco->isMuted(); // state: selected, muted, normal - c = isSelected() ? selectedColor() : ( muted ? mutedBackgroundColor() + c = isSelected() ? selectedColor() : ( muted ? mutedBackgroundColor() : painter.background().color() ); lingrad.setColorAt( 1, c.darker( 300 ) ); @@ -515,7 +518,7 @@ void SampleTCOView::paintEvent( QPaintEvent * pe ) // inner border p.setPen( c.lighter( 160 ) ); - p.drawRect( 1, 1, rect().right() - TCO_BORDER_WIDTH, + p.drawRect( 1, 1, rect().right() - TCO_BORDER_WIDTH, rect().bottom() - TCO_BORDER_WIDTH ); // outer border @@ -531,7 +534,7 @@ void SampleTCOView::paintEvent( QPaintEvent * pe ) embed::getIconPixmap( "muted", size, size ) ); } - // recording sample tracks is not possible at the moment + // recording sample tracks is not possible at the moment /* if( m_tco->isRecord() ) { @@ -562,10 +565,14 @@ SampleTrack::SampleTrack( TrackContainer* tc ) : tr( "Volume" ) ), m_panningModel( DefaultPanning, PanningLeft, PanningRight, 0.1f, this, tr( "Panning" ) ), + m_effectChannelModel( 0, 0, 0, this, tr( "FX channel" ) ), m_audioPort( tr( "Sample track" ), true, &m_volumeModel, &m_panningModel, &m_mutedModel ) { setName( tr( "Sample track" ) ); m_panningModel.setCenterValue( DefaultPanning ); + m_effectChannelModel.setRange( 0, Engine::fxMixer()->numChannels()-1, 1); + + connect( &m_effectChannelModel, SIGNAL( dataChanged() ), this, SLOT( updateEffectChannel() ) ); } @@ -693,6 +700,7 @@ void SampleTrack::saveTrackSpecificSettings( QDomDocument & _doc, #endif m_volumeModel.saveSettings( _doc, _this, "vol" ); m_panningModel.saveSettings( _doc, _this, "pan" ); + m_effectChannelModel.saveSettings( _doc, _this, "fxch" ); } @@ -715,6 +723,8 @@ void SampleTrack::loadTrackSpecificSettings( const QDomElement & _this ) } m_volumeModel.loadSettings( _this, "vol" ); m_panningModel.loadSettings( _this, "pan" ); + m_effectChannelModel.setRange( 0, Engine::fxMixer()->numChannels() - 1 ); + m_effectChannelModel.loadSettings( _this, "fxch" ); } @@ -742,6 +752,14 @@ void SampleTrack::setPlayingTcos( bool isPlaying ) +void SampleTrack::updateEffectChannel() +{ + m_audioPort.setNextFxChannel( m_effectChannelModel.value() ); +} + + + + SampleTrackView::SampleTrackView( SampleTrack * _t, TrackContainerView* tcv ) : @@ -749,13 +767,13 @@ SampleTrackView::SampleTrackView( SampleTrack * _t, TrackContainerView* tcv ) : { setFixedHeight( 32 ); - TrackLabelButton * tlb = new TrackLabelButton( this, - getTrackSettingsWidget() ); - connect( tlb, SIGNAL( clicked( bool ) ), - this, SLOT( showEffects() ) ); - tlb->setIcon( embed::getIconPixmap( "sample_track" ) ); - tlb->move( 3, 1 ); - tlb->show(); + m_tlb = new TrackLabelButton(this, getTrackSettingsWidget()); + m_tlb->setCheckable(true); + connect(m_tlb, SIGNAL(clicked( bool )), + this, SLOT(showEffects())); + m_tlb->setIcon(embed::getIconPixmap("sample_track")); + m_tlb->move(3, 1); + m_tlb->show(); m_volumeKnob = new Knob( knobSmall_17, getTrackSettingsWidget(), tr( "Track volume" ) ); @@ -779,16 +797,10 @@ SampleTrackView::SampleTrackView( SampleTrack * _t, TrackContainerView* tcv ) : m_panningKnob->setLabel( tr( "PAN" ) ); m_panningKnob->show(); - m_effectRack = new EffectRackView( _t->audioPort()->effects() ); - m_effectRack->setFixedSize( 240, 242 ); - - m_effWindow = gui->mainWindow()->addWindowedWidget( m_effectRack ); - m_effWindow->setAttribute( Qt::WA_DeleteOnClose, false ); - m_effWindow->layout()->setSizeConstraint( QLayout::SetFixedSize ); - m_effWindow->setWindowTitle( _t->name() ); - m_effWindow->hide(); - setModel( _t ); + + m_window = new SampleTrackWindow(this); + m_window->toggleVisibility(false); } @@ -796,7 +808,50 @@ SampleTrackView::SampleTrackView( SampleTrack * _t, TrackContainerView* tcv ) : SampleTrackView::~SampleTrackView() { - m_effWindow->deleteLater(); + if(m_window != NULL) + { + m_window->setSampleTrackView(NULL); + m_window->parentWidget()->hide(); + } + m_window = NULL; +} + + + +QMenu * SampleTrackView::createFxMenu(QString title, QString newFxLabel) +{ + int channelIndex = model()->effectChannelModel()->value(); + + FxChannel *fxChannel = Engine::fxMixer()->effectChannel(channelIndex); + + // If title allows interpolation, pass channel index and name + if (title.contains("%2")) + { + title = title.arg(channelIndex).arg(fxChannel->m_name); + } + + QMenu *fxMenu = new QMenu(title); + + QSignalMapper * fxMenuSignalMapper = new QSignalMapper(fxMenu); + + fxMenu->addAction(newFxLabel, this, SLOT(createFxLine())); + fxMenu->addSeparator(); + + for (int i = 0; i < Engine::fxMixer()->numChannels(); ++i) + { + FxChannel * currentChannel = Engine::fxMixer()->effectChannel(i); + + if (currentChannel != fxChannel) + { + QString label = tr("FX %1: %2").arg(currentChannel->m_channelIndex).arg(currentChannel->m_name); + QAction * action = fxMenu->addAction(label, fxMenuSignalMapper, SLOT(map())); + fxMenuSignalMapper->setMapping(action, currentChannel->m_channelIndex); + } + } + + connect(fxMenuSignalMapper, SIGNAL(mapped(int)), this, SLOT(assignFxLine(int))); + + return fxMenu; } @@ -804,16 +859,7 @@ SampleTrackView::~SampleTrackView() void SampleTrackView::showEffects() { - if( m_effWindow->isHidden() ) - { - m_effectRack->show(); - m_effWindow->show(); - m_effWindow->raise(); - } - else - { - m_effWindow->hide(); - } + m_window->toggleVisibility(m_window->parentWidget()->isHidden()); } @@ -821,7 +867,261 @@ void SampleTrackView::showEffects() void SampleTrackView::modelChanged() { SampleTrack * st = castModel(); - m_volumeKnob->setModel( &st->m_volumeModel ); + m_volumeKnob->setModel(&st->m_volumeModel); TrackView::modelChanged(); } + + + +SampleTrackWindow::SampleTrackWindow(SampleTrackView * tv) : + QWidget(), + ModelView(NULL, this), + m_track(tv->model()), + m_stv(tv) +{ + // init own layout + widgets + setFocusPolicy(Qt::StrongFocus); + QVBoxLayout * vlayout = new QVBoxLayout(this); + vlayout->setMargin(0); + vlayout->setSpacing(0); + + TabWidget* generalSettingsWidget = new TabWidget(tr("GENERAL SETTINGS"), this); + + QVBoxLayout* generalSettingsLayout = new QVBoxLayout(generalSettingsWidget); + + generalSettingsLayout->setContentsMargins(8, 18, 8, 8); + generalSettingsLayout->setSpacing(6); + + QWidget* nameWidget = new QWidget(generalSettingsWidget); + QHBoxLayout* nameLayout = new QHBoxLayout(nameWidget); + nameLayout->setContentsMargins(0, 0, 0, 0); + nameLayout->setSpacing(2); + + // setup line edit for changing sample track name + m_nameLineEdit = new QLineEdit; + m_nameLineEdit->setFont(pointSize<9>(m_nameLineEdit->font())); + connect(m_nameLineEdit, SIGNAL(textChanged(const QString &)), + this, SLOT(textChanged(const QString &))); + + m_nameLineEdit->setSizePolicy(QSizePolicy(QSizePolicy::Expanding, QSizePolicy::Preferred)); + nameLayout->addWidget(m_nameLineEdit); + + + generalSettingsLayout->addWidget(nameWidget); + + + QGridLayout* basicControlsLayout = new QGridLayout; + basicControlsLayout->setHorizontalSpacing(3); + basicControlsLayout->setVerticalSpacing(0); + basicControlsLayout->setContentsMargins(0, 0, 0, 0); + + QString labelStyleSheet = "font-size: 6pt;"; + Qt::Alignment labelAlignment = Qt::AlignHCenter | Qt::AlignTop; + Qt::Alignment widgetAlignment = Qt::AlignHCenter | Qt::AlignCenter; + + // set up volume knob + m_volumeKnob = new Knob(knobBright_26, NULL, tr("Sample volume")); + m_volumeKnob->setVolumeKnob(true); + m_volumeKnob->setHintText(tr("Volume:"), "%"); + + basicControlsLayout->addWidget(m_volumeKnob, 0, 0); + basicControlsLayout->setAlignment(m_volumeKnob, widgetAlignment); + + QLabel *label = new QLabel(tr("VOL"), this); + label->setStyleSheet(labelStyleSheet); + basicControlsLayout->addWidget(label, 1, 0); + basicControlsLayout->setAlignment(label, labelAlignment); + + + // set up panning knob + m_panningKnob = new Knob(knobBright_26, NULL, tr("Panning")); + m_panningKnob->setHintText(tr("Panning:"), ""); + + basicControlsLayout->addWidget(m_panningKnob, 0, 1); + basicControlsLayout->setAlignment(m_panningKnob, widgetAlignment); + + label = new QLabel(tr("PAN"),this); + label->setStyleSheet(labelStyleSheet); + basicControlsLayout->addWidget(label, 1, 1); + basicControlsLayout->setAlignment(label, labelAlignment); + + + basicControlsLayout->setColumnStretch(2, 1); + + + // setup spinbox for selecting FX-channel + m_effectChannelNumber = new FxLineLcdSpinBox(2, NULL, tr("FX channel"), m_stv); + + basicControlsLayout->addWidget(m_effectChannelNumber, 0, 3); + basicControlsLayout->setAlignment(m_effectChannelNumber, widgetAlignment); + + label = new QLabel(tr("FX"), this); + label->setStyleSheet(labelStyleSheet); + basicControlsLayout->addWidget(label, 1, 3); + basicControlsLayout->setAlignment(label, labelAlignment); + + generalSettingsLayout->addLayout(basicControlsLayout); + + m_effectRack = new EffectRackView(tv->model()->audioPort()->effects()); + m_effectRack->setFixedSize(240, 242); + + vlayout->addWidget(generalSettingsWidget); + vlayout->addWidget(m_effectRack); + + + setModel(tv->model()); + + QMdiSubWindow * subWin = gui->mainWindow()->addWindowedWidget(this); + Qt::WindowFlags flags = subWin->windowFlags(); + flags |= Qt::MSWindowsFixedSizeDialogHint; + flags &= ~Qt::WindowMaximizeButtonHint; + subWin->setWindowFlags(flags); + + // Hide the Size and Maximize options from the system menu + // since the dialog size is fixed. + QMenu * systemMenu = subWin->systemMenu(); + systemMenu->actions().at(2)->setVisible(false); // Size + systemMenu->actions().at(4)->setVisible(false); // Maximize + + subWin->setWindowIcon(embed::getIconPixmap("sample_track")); + subWin->setFixedSize(subWin->size()); + subWin->hide(); +} + + + +SampleTrackWindow::~SampleTrackWindow() +{ +} + + + +void SampleTrackWindow::setSampleTrackView(SampleTrackView* tv) +{ + if(m_stv && tv) + { + m_stv->m_tlb->setChecked(false); + } + + m_stv = tv; +} + + + +void SampleTrackWindow::modelChanged() +{ + m_track = castModel(); + + m_nameLineEdit->setText(m_track->name()); + + m_track->disconnect(SIGNAL(nameChanged()), this); + + connect(m_track, SIGNAL(nameChanged()), + this, SLOT(updateName())); + + m_volumeKnob->setModel(&m_track->m_volumeModel); + m_panningKnob->setModel(&m_track->m_panningModel); + m_effectChannelNumber->setModel(&m_track->m_effectChannelModel); + + updateName(); +} + + + +/*! \brief Create and assign a new FX Channel for this track */ +void SampleTrackView::createFxLine() +{ + int channelIndex = gui->fxMixerView()->addNewChannel(); + + Engine::fxMixer()->effectChannel(channelIndex)->m_name = getTrack()->name(); + + assignFxLine(channelIndex); +} + + + + +/*! \brief Assign a specific FX Channel for this track */ +void SampleTrackView::assignFxLine(int channelIndex) +{ + model()->effectChannelModel()->setValue(channelIndex); + + gui->fxMixerView()->setCurrentFxLine(channelIndex); +} + + + +void SampleTrackWindow::updateName() +{ + setWindowTitle(m_track->name().length() > 25 ? (m_track->name().left(24) + "...") : m_track->name()); + + if(m_nameLineEdit->text() != m_track->name()) + { + m_nameLineEdit->setText(m_track->name()); + } +} + + + +void SampleTrackWindow::textChanged(const QString& new_name) +{ + m_track->setName(new_name); + Engine::getSong()->setModified(); +} + + + +void SampleTrackWindow::toggleVisibility(bool on) +{ + if(on) + { + show(); + parentWidget()->show(); + parentWidget()->raise(); + } + else + { + parentWidget()->hide(); + } +} + + + + +void SampleTrackWindow::closeEvent(QCloseEvent* ce) +{ + ce->ignore(); + + if(gui->mainWindow()->workspace()) + { + parentWidget()->hide(); + } + else + { + hide(); + } + + m_stv->m_tlb->setFocus(); + m_stv->m_tlb->setChecked(false); +} + + + +void SampleTrackWindow::saveSettings(QDomDocument& doc, QDomElement & element) +{ + MainWindow::saveWidgetState(this, element); + Q_UNUSED(element) +} + + + +void SampleTrackWindow::loadSettings(const QDomElement& element) +{ + MainWindow::restoreWidgetState(this, element); + if(isVisible()) + { + m_stv->m_tlb->setChecked(true); + } +} + From 04768ee3e14a57d323112717bef10088411d9ae9 Mon Sep 17 00:00:00 2001 From: tresf Date: Mon, 11 Mar 2019 14:24:12 -0400 Subject: [PATCH 27/53] Fix zyn pitch on project load/export Closes #3451 --- plugins/zynaddsubfx/ZynAddSubFx.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/plugins/zynaddsubfx/ZynAddSubFx.cpp b/plugins/zynaddsubfx/ZynAddSubFx.cpp index fff13c62d..a75d573a8 100644 --- a/plugins/zynaddsubfx/ZynAddSubFx.cpp +++ b/plugins/zynaddsubfx/ZynAddSubFx.cpp @@ -291,6 +291,7 @@ void ZynAddSubFxInstrument::loadSettings( const QDomElement & _this ) emit settingsChanged(); } + emit instrumentTrack()->pitchModel()->dataChanged(); } From 37290ace1d8824fc4e80779d9de7ebf5cfba4313 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Mon, 11 Mar 2019 23:03:43 +0100 Subject: [PATCH 28/53] Add info about LadspaControls::m_noLink --- plugins/LadspaEffect/LadspaControls.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/plugins/LadspaEffect/LadspaControls.cpp b/plugins/LadspaEffect/LadspaControls.cpp index 1a2f26a35..8e65e0e93 100644 --- a/plugins/LadspaEffect/LadspaControls.cpp +++ b/plugins/LadspaEffect/LadspaControls.cpp @@ -153,6 +153,9 @@ void LadspaControls::linkPort( int _port, bool _state ) { first->unlinkControls( m_controls[proc][_port] ); } + + // m_stereoLinkModel.setValue() will call updateLinkStatesFromGlobal() + // m_noLink will make sure that this will not unlink any other ports m_noLink = true; m_stereoLinkModel.setValue( false ); } From 97d5529c18bee51d638607a192ab91b131d41086 Mon Sep 17 00:00:00 2001 From: tresf Date: Tue, 12 Mar 2019 00:25:17 -0400 Subject: [PATCH 29/53] Fix compilation on Qt4 --- plugins/zynaddsubfx/ZynAddSubFx.cpp | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/plugins/zynaddsubfx/ZynAddSubFx.cpp b/plugins/zynaddsubfx/ZynAddSubFx.cpp index a75d573a8..e929f7c19 100644 --- a/plugins/zynaddsubfx/ZynAddSubFx.cpp +++ b/plugins/zynaddsubfx/ZynAddSubFx.cpp @@ -27,6 +27,7 @@ #include #include #include +#include #include #include #include @@ -291,7 +292,10 @@ void ZynAddSubFxInstrument::loadSettings( const QDomElement & _this ) emit settingsChanged(); } +// FIXME: Remove this check in future versions. Slots are public in Qt5+ +#if QT_VERSION >= 0x050000 emit instrumentTrack()->pitchModel()->dataChanged(); +#endif } From 295b899df287c3ea44d5d49e59a12eec4a069fee Mon Sep 17 00:00:00 2001 From: Javier Serrano Polo Date: Wed, 13 Mar 2019 23:27:54 +0100 Subject: [PATCH 30/53] Avoid shallow clones in all Debian sid builds (#4888) hallow clone may break version detection. This is fatal in Debian builds, so use full clone. Note: This is safe for stable-1.2 but needs review after merging to master due to submodules. See #4888 for more information. --- .travis.yml | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/.travis.yml b/.travis.yml index b543a956d..5f8002cb1 100644 --- a/.travis.yml +++ b/.travis.yml @@ -24,8 +24,12 @@ matrix: git: depth: false - env: TARGET_OS=debian-sid TARGET_ARCH=i386 + git: + depth: false - compiler: clang env: TARGET_OS=debian-sid + git: + depth: false before_install: - . ${TRAVIS_BUILD_DIR}/.travis/${TRAVIS_OS_NAME}.${TARGET_OS}.before_install.sh install: From 17f6235500ca3b820ba53891681ac8f443514a65 Mon Sep 17 00:00:00 2001 From: Dominic Clark Date: Sun, 3 Mar 2019 16:01:07 +0000 Subject: [PATCH 31/53] Add VST always-on-top config option --- include/SetupDialog.h | 5 ++++ plugins/vst_base/VstPlugin.cpp | 4 +++- src/gui/SetupDialog.cpp | 44 ++++++++++++++++++++++++++++------ 3 files changed, 45 insertions(+), 8 deletions(-) diff --git a/include/SetupDialog.h b/include/SetupDialog.h index 338ea93dd..d0607022d 100644 --- a/include/SetupDialog.h +++ b/include/SetupDialog.h @@ -126,6 +126,9 @@ private slots: void toggleDisplayWaveform( bool en ); void toggleDisableAutoquit( bool en ); + void vstEmbedMethodChanged(); + void toggleVSTAlwaysOnTop( bool en ); + void setLanguage( int lang ); @@ -207,6 +210,8 @@ private: QComboBox* m_vstEmbedComboBox; QString m_vstEmbedMethod; + LedCheckBox * m_vstAlwaysOnTopCheckBox; + bool m_vstAlwaysOnTop; } ; diff --git a/plugins/vst_base/VstPlugin.cpp b/plugins/vst_base/VstPlugin.cpp index a97802bdc..e0e1347fe 100644 --- a/plugins/vst_base/VstPlugin.cpp +++ b/plugins/vst_base/VstPlugin.cpp @@ -329,7 +329,9 @@ bool VstPlugin::processMessage( const message & _m ) case IdVstPluginWindowID: m_pluginWindowID = _m.getInt(); - if( m_embedMethod == "none" ) + if( m_embedMethod == "none" + && ConfigManager::inst()->value( + "ui", "vstalwaysontop" ).toInt() ) { #ifdef LMMS_BUILD_WIN32 // We're changing the owner, not the parent, diff --git a/src/gui/SetupDialog.cpp b/src/gui/SetupDialog.cpp index 4c261f581..746e7e651 100644 --- a/src/gui/SetupDialog.cpp +++ b/src/gui/SetupDialog.cpp @@ -138,7 +138,9 @@ SetupDialog::SetupDialog( ConfigTabs _tab_to_open ) : "displaywaveform").toInt() ), m_disableAutoQuit(ConfigManager::inst()->value( "ui", "disableautoquit", "1" ).toInt() ), - m_vstEmbedMethod( ConfigManager::inst()->vstEmbedMethod() ) + m_vstEmbedMethod( ConfigManager::inst()->vstEmbedMethod() ), + m_vstAlwaysOnTop( ConfigManager::inst()->value( "ui", + "vstalwaysontop" ).toInt() ) { setWindowIcon( embed::getIconPixmap( "setup_general" ) ); setWindowTitle( tr( "Setup LMMS" ) ); @@ -346,7 +348,7 @@ SetupDialog::SetupDialog( ConfigTabs _tab_to_open ) : } TabWidget* embed_tw = new TabWidget( tr( "PLUGIN EMBEDDING" ), general); - embed_tw->setFixedHeight( 48 ); + embed_tw->setFixedHeight( 66 ); m_vstEmbedComboBox = new QComboBox( embed_tw ); m_vstEmbedComboBox->move( XDelta, YDelta ); @@ -365,6 +367,17 @@ SetupDialog::SetupDialog( ConfigTabs _tab_to_open ) : m_vstEmbedComboBox->addItem( tr( "Embed using XEmbed protocol" ), "xembed" ); } m_vstEmbedComboBox->setCurrentIndex( m_vstEmbedComboBox->findData( m_vstEmbedMethod ) ); + connect( m_vstEmbedComboBox, SIGNAL( currentIndexChanged( int ) ), + this, SLOT( vstEmbedMethodChanged() ) ); + + m_vstAlwaysOnTopCheckBox = new LedCheckBox( + tr( "Keep plugin windows on top when not embedded" ), + embed_tw ); + m_vstAlwaysOnTopCheckBox->move( 20, 44 ); + m_vstAlwaysOnTopCheckBox->setChecked( m_vstAlwaysOnTop ); + m_vstAlwaysOnTopCheckBox->setVisible( m_vstEmbedMethod == "none" ); + connect( m_vstAlwaysOnTopCheckBox, SIGNAL( toggled( bool ) ), + this, SLOT( toggleVSTAlwaysOnTop( bool ) ) ); TabWidget * lang_tw = new TabWidget( tr( "LANGUAGE" ), general ); lang_tw->setFixedHeight( 48 ); @@ -1094,11 +1107,9 @@ void SetupDialog::accept() QString::number( m_disableAutoQuit ) ); ConfigManager::inst()->setValue( "app", "language", m_lang ); ConfigManager::inst()->setValue( "ui", "vstembedmethod", -#if QT_VERSION >= 0x050000 - m_vstEmbedComboBox->currentData().toString() ); -#else - m_vstEmbedComboBox->itemData(m_vstEmbedComboBox->currentIndex()).toString() ); -#endif + m_vstEmbedMethod ); + ConfigManager::inst()->setValue( "ui", "vstalwaysontop", + QString::number( m_vstAlwaysOnTop ) ); ConfigManager::inst()->setWorkingDir(QDir::fromNativeSeparators(m_workingDir)); @@ -1316,6 +1327,25 @@ void SetupDialog::toggleOneInstrumentTrackWindow( bool _enabled ) m_oneInstrumentTrackWindow = _enabled; } + +void SetupDialog::vstEmbedMethodChanged() +{ +#if QT_VERSION >= 0x050000 + m_vstEmbedMethod = m_vstEmbedComboBox->currentData().toString(); +#else + m_vstEmbedMethod = m_vstEmbedComboBox->itemData( + m_vstEmbedComboBox->currentIndex()).toString(); +#endif + m_vstAlwaysOnTopCheckBox->setVisible( m_vstEmbedMethod == "none" ); +} + + +void SetupDialog::toggleVSTAlwaysOnTop( bool en ) +{ + m_vstAlwaysOnTop = en; +} + + void SetupDialog::setLanguage( int lang ) { m_lang = m_languages[lang]; From 6fef905dfe9a556d0e51738d90fff707d678ed44 Mon Sep 17 00:00:00 2001 From: Dominic Clark Date: Sun, 3 Mar 2019 16:02:37 +0000 Subject: [PATCH 32/53] Ensure VST windows show properly in taskbar --- plugins/vst_base/RemoteVstPlugin.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/plugins/vst_base/RemoteVstPlugin.cpp b/plugins/vst_base/RemoteVstPlugin.cpp index fbffe4b34..8dd25c11f 100644 --- a/plugins/vst_base/RemoteVstPlugin.cpp +++ b/plugins/vst_base/RemoteVstPlugin.cpp @@ -775,10 +775,6 @@ void RemoteVstPlugin::initEditor() SWP_NOMOVE | SWP_NOZORDER ); pluginDispatch( effEditTop ); - if (! EMBED) { - showEditor(); - } - #ifdef LMMS_BUILD_LINUX m_windowID = (intptr_t) GetProp( m_window, "__wine_x11_whole_window" ); #else From 205b57531d8b3d11c6d666f302426057c41d3fb0 Mon Sep 17 00:00:00 2001 From: Dominic Clark Date: Sun, 3 Mar 2019 16:06:04 +0000 Subject: [PATCH 33/53] Don't show error when loading empty VeSTige instance --- plugins/vestige/vestige.cpp | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/plugins/vestige/vestige.cpp b/plugins/vestige/vestige.cpp index 0cfa31d21..fc07e46d5 100644 --- a/plugins/vestige/vestige.cpp +++ b/plugins/vestige/vestige.cpp @@ -181,7 +181,13 @@ vestigeInstrument::~vestigeInstrument() void vestigeInstrument::loadSettings( const QDomElement & _this ) { - loadFile( _this.attribute( "plugin" ) ); + QString plugin = _this.attribute( "plugin" ); + if( plugin.isEmpty() ) + { + return; + } + + loadFile( plugin ); m_pluginMutex.lock(); if( m_plugin != NULL ) { From 3aeacca7acf7fe77a3f0545451dbe4e3332c1628 Mon Sep 17 00:00:00 2001 From: Dominic Clark Date: Sun, 3 Mar 2019 16:11:02 +0000 Subject: [PATCH 34/53] Fix layout of VstSyncData struct Ensure member of type double is 8-byte aligned for consistent layout between 32- and 64-bit Linux builds. --- include/VstSyncController.h | 2 +- include/VstSyncData.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/include/VstSyncController.h b/include/VstSyncController.h index 682291b9e..970a48ad3 100644 --- a/include/VstSyncController.h +++ b/include/VstSyncController.h @@ -76,10 +76,10 @@ private slots: private: struct VstSyncData { - bool isPlaying; double ppqPos; int timeSigNumer; int timeSigDenom; + bool isPlaying; bool isCycle; bool hasSHM; float cycleStart; diff --git a/include/VstSyncData.h b/include/VstSyncData.h index f9696252a..6c2f1bbd2 100644 --- a/include/VstSyncData.h +++ b/include/VstSyncData.h @@ -41,10 +41,10 @@ struct VstSyncData { - bool isPlaying; double ppqPos; int timeSigNumer; int timeSigDenom; + bool isPlaying; bool isCycle; bool hasSHM; float cycleStart; From 2d71d6163bf9bb3bbdc715a265209397d58c8c1c Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Fri, 15 Mar 2019 18:42:34 +0100 Subject: [PATCH 35/53] Rework after code reading * Fix possible segfault in `SubPluginFeatures::displayName` * Minor fixes --- include/Plugin.h | 6 +++--- src/core/Plugin.cpp | 4 ++-- src/core/PluginFactory.cpp | 23 +++++++++++------------ 3 files changed, 16 insertions(+), 17 deletions(-) diff --git a/include/Plugin.h b/include/Plugin.h index 48c5f90c7..31c83c4ba 100644 --- a/include/Plugin.h +++ b/include/Plugin.h @@ -202,19 +202,19 @@ public: virtual QString additionalFileExtensions(const Key&) const { - return nullptr; + return QString(); } virtual QString displayName(const Key& k) const { return k.isValid() ? k.desc->displayName - : k.name.toUtf8().data(); + : k.name; } virtual QString description(const Key& k) const { - return k.isValid() ? k.desc->description : ""; + return k.isValid() ? k.desc->description : QString(); } virtual const PixmapLoader* logo(const Key& k) const diff --git a/src/core/Plugin.cpp b/src/core/Plugin.cpp index 2975cf104..1562044f9 100644 --- a/src/core/Plugin.cpp +++ b/src/core/Plugin.cpp @@ -127,8 +127,8 @@ QString Plugin::Descriptor::SubPluginFeatures::Key::additionalFileExtensions() c return desc->subPluginFeatures // get from sub plugin ? desc->subPluginFeatures->additionalFileExtensions(*this) - // get from plugin - : nullptr; + // no sub plugin, so no *additional* file extensions + : QString(); } diff --git a/src/core/PluginFactory.cpp b/src/core/PluginFactory.cpp index f84227091..abf642122 100644 --- a/src/core/PluginFactory.cpp +++ b/src/core/PluginFactory.cpp @@ -180,10 +180,6 @@ void PluginFactory::discoverPlugins() continue; } } - else - { - //qDebug() << "Ignoring" << file.absoluteFilePath() << "(no lmms_plugin_main())"; - } if(pluginDescriptor) { @@ -199,15 +195,18 @@ void PluginFactory::discoverPlugins() const Plugin::Descriptor::SubPluginFeatures::Key* key = nullptr) { if(!supportedFileTypes.isNull()) - for (const QString& ext : supportedFileTypes.split(',')) { - //qDebug() << "Plugin " << info.name() << "supports" << ext; - PluginInfoAndKey infoAndKey; - infoAndKey.info = info; - infoAndKey.key = key - ? *key - : Plugin::Descriptor::SubPluginFeatures::Key(); - m_pluginByExt.insert(ext, infoAndKey); + for (const QString& ext : supportedFileTypes.split(',')) + { + //qDebug() << "Plugin " << info.name() + // << "supports" << ext; + PluginInfoAndKey infoAndKey; + infoAndKey.info = info; + infoAndKey.key = key + ? *key + : Plugin::Descriptor::SubPluginFeatures::Key(); + m_pluginByExt.insert(ext, infoAndKey); + } } }; From f8ba88d55a7f0069c54edb3b80a2ad8e7dc4187b Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sat, 16 Mar 2019 20:10:19 +0100 Subject: [PATCH 36/53] Make instrument window's piano optional --- include/Instrument.h | 2 ++ src/tracks/InstrumentTrack.cpp | 9 ++++++++- 2 files changed, 10 insertions(+), 1 deletion(-) diff --git a/include/Instrument.h b/include/Instrument.h index a373ae4ac..6ab8e0c66 100644 --- a/include/Instrument.h +++ b/include/Instrument.h @@ -63,6 +63,8 @@ public: // functions that can/should be re-implemented: // -------------------------------------------------------------------- + virtual bool hasNoteInput() const { return true; } + // if the plugin doesn't play each note, it can create an instrument- // play-handle and re-implement this method, so that it mixes its // output buffer only once per mixer-period diff --git a/src/tracks/InstrumentTrack.cpp b/src/tracks/InstrumentTrack.cpp index 298430b03..6005402f1 100644 --- a/src/tracks/InstrumentTrack.cpp +++ b/src/tracks/InstrumentTrack.cpp @@ -1650,6 +1650,8 @@ void InstrumentTrackWindow::updateInstrumentView() modelChanged(); // Get the instrument window to refresh m_track->dataChanged(); // Get the text on the trackButton to change + + m_pianoView->setVisible(m_track->m_instrument->hasNoteInput()); } } @@ -1704,7 +1706,9 @@ void InstrumentTrackWindow::closeEvent( QCloseEvent* event ) void InstrumentTrackWindow::focusInEvent( QFocusEvent* ) { - m_pianoView->setFocus(); + if(m_pianoView->isVisible()) { + m_pianoView->setFocus(); + } } @@ -1836,6 +1840,9 @@ void InstrumentTrackWindow::viewInstrumentInDirection(int d) // scroll the SongEditor/BB-editor to make sure the new trackview label is visible bringToFront->trackContainerView()->scrollToTrackView(bringToFront); + + // get the instrument window to refresh + modelChanged(); } bringToFront->getInstrumentTrackWindow()->setFocus(); } From aac516e27fb441bf754da145fb72a2705fa54a30 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sat, 16 Mar 2019 20:59:45 +0100 Subject: [PATCH 37/53] Forbid copying the Knob class --- include/Knob.h | 1 + 1 file changed, 1 insertion(+) diff --git a/include/Knob.h b/include/Knob.h index 319b38184..245963ce2 100644 --- a/include/Knob.h +++ b/include/Knob.h @@ -74,6 +74,7 @@ class LMMS_EXPORT Knob : public QWidget, public FloatModelView public: Knob( knobTypes _knob_num, QWidget * _parent = NULL, const QString & _name = QString() ); Knob( QWidget * _parent = NULL, const QString & _name = QString() ); //!< default ctor + Knob( const Knob& other ) = delete; virtual ~Knob(); // TODO: remove From 7e75a82f7ec5ef7f1d743d78c6bd68b6145ea54f Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Sat, 16 Mar 2019 22:05:46 +0100 Subject: [PATCH 38/53] Always instantiate at least a dummy plugin --- src/core/Plugin.cpp | 36 ++++++++++++++++++++++------------ src/gui/EffectSelectDialog.cpp | 14 +++++++++---- 2 files changed, 33 insertions(+), 17 deletions(-) diff --git a/src/core/Plugin.cpp b/src/core/Plugin.cpp index b479d7d8d..12edb8644 100644 --- a/src/core/Plugin.cpp +++ b/src/core/Plugin.cpp @@ -90,10 +90,12 @@ AutomatableModel * Plugin::childModel( const QString & ) #include "PluginFactory.h" -Plugin * Plugin::instantiate( const QString& pluginName, Model * parent, - void * data ) +Plugin * Plugin::instantiate(const QString& pluginName, Model * parent, + void *data) { const PluginFactory::PluginInfo& pi = pluginFactory->pluginInfo(pluginName.toUtf8()); + + Plugin* inst; if( pi.isNull() ) { if( gui ) @@ -104,23 +106,31 @@ Plugin * Plugin::instantiate( const QString& pluginName, Model * parent, arg( pluginName ).arg( pluginFactory->errorString(pluginName) ), QMessageBox::Ok | QMessageBox::Default ); } - return new DummyPlugin(); + inst = new DummyPlugin(); } - - InstantiationHook instantiationHook = ( InstantiationHook ) pi.library->resolve( "lmms_plugin_main" ); - if( instantiationHook == NULL ) + else { - if( gui ) + InstantiationHook instantiationHook; + if ((instantiationHook = ( InstantiationHook ) pi.library->resolve( "lmms_plugin_main" ))) { - QMessageBox::information( NULL, - tr( "Error while loading plugin" ), - tr( "Failed to load plugin \"%1\"!").arg( pluginName ), - QMessageBox::Ok | QMessageBox::Default ); + inst = instantiationHook(parent, data); + if(!inst) { + inst = new DummyPlugin(); + } + } + else + { + if( gui ) + { + QMessageBox::information( NULL, + tr( "Error while loading plugin" ), + tr( "Failed to load plugin \"%1\"!").arg( pluginName ), + QMessageBox::Ok | QMessageBox::Default ); + } + inst = new DummyPlugin(); } - return new DummyPlugin(); } - Plugin * inst = instantiationHook( parent, data ); return inst; } diff --git a/src/gui/EffectSelectDialog.cpp b/src/gui/EffectSelectDialog.cpp index 64b180d48..3c641acec 100644 --- a/src/gui/EffectSelectDialog.cpp +++ b/src/gui/EffectSelectDialog.cpp @@ -27,6 +27,7 @@ #include "ui_EffectSelectDialog.h" #include "gui_templates.h" +#include "DummyEffect.h" #include "embed.h" #include "PluginFactory.h" @@ -147,12 +148,17 @@ EffectSelectDialog::~EffectSelectDialog() Effect * EffectSelectDialog::instantiateSelectedPlugin( EffectChain * _parent ) { - if( !m_currentSelection.name.isEmpty() && m_currentSelection.desc ) + Effect* result = nullptr; + if(!m_currentSelection.name.isEmpty() && m_currentSelection.desc) { - return Effect::instantiate( m_currentSelection.desc->name, - _parent, &m_currentSelection ); + result = Effect::instantiate(m_currentSelection.desc->name, + _parent, &m_currentSelection); } - return NULL; + if(!result) + { + result = new DummyEffect(_parent, QDomElement()); + } + return result; } From 0c3db1045cda28f613f4aecf6a946b6c8f19eb5b Mon Sep 17 00:00:00 2001 From: Martin Pavelek Date: Sun, 17 Mar 2019 15:00:47 +0100 Subject: [PATCH 39/53] Fix Blackman-Harris window formula (#4895) Adds missing parentheses --- plugins/Eq/EqSpectrumView.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/plugins/Eq/EqSpectrumView.cpp b/plugins/Eq/EqSpectrumView.cpp index 677c896fb..456ca4871 100644 --- a/plugins/Eq/EqSpectrumView.cpp +++ b/plugins/Eq/EqSpectrumView.cpp @@ -45,11 +45,11 @@ EqAnalyser::EqAnalyser() : const float a2 = 0.14128; const float a3 = 0.01168; - for(int i = 0; i < FFT_BUFFER_SIZE; i++) + for (int i = 0; i < FFT_BUFFER_SIZE; i++) { - m_fftWindow[i] = ( a0 - a1 * cosf( 2 * F_PI * i / (float)FFT_BUFFER_SIZE - 1 ) - + a2 * cosf( 4 * F_PI * i / (float)FFT_BUFFER_SIZE-1) - - a3 * cos( 6 * F_PI * i / (float)FFT_BUFFER_SIZE - 1.0 )); + m_fftWindow[i] = (a0 - a1 * cos(2 * F_PI * i / ((float)FFT_BUFFER_SIZE - 1.0)) + + a2 * cos(4 * F_PI * i / ((float)FFT_BUFFER_SIZE - 1.0)) + - a3 * cos(6 * F_PI * i / ((float)FFT_BUFFER_SIZE - 1.0))); } clear(); } From dd6c18e62b9963baeefbe4fb07b2e6bd67dc2174 Mon Sep 17 00:00:00 2001 From: Lukas W Date: Sat, 9 Mar 2019 18:57:12 +0100 Subject: [PATCH 40/53] Automation Editor: Don't accept drag events when there's no pattern Fixes #4857 --- src/gui/editors/AutomationEditor.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/gui/editors/AutomationEditor.cpp b/src/gui/editors/AutomationEditor.cpp index 06eece23a..fafb83d0d 100644 --- a/src/gui/editors/AutomationEditor.cpp +++ b/src/gui/editors/AutomationEditor.cpp @@ -2565,6 +2565,9 @@ void AutomationEditorWindow::dropEvent( QDropEvent *_de ) void AutomationEditorWindow::dragEnterEvent( QDragEnterEvent *_dee ) { + if (! m_editor->validPattern() ) { + return; + } StringPairDrag::processDragEnterEvent( _dee, "automatable_model" ); } From e1adfc3952389f0e6c18c3bf7daef40c4ca72481 Mon Sep 17 00:00:00 2001 From: Lukas W Date: Fri, 1 Mar 2019 09:00:46 +0100 Subject: [PATCH 41/53] TCO drag: Fix Ctrl+Drag crash Fix some assumptions that source and target of a drag actions are the same track container. Instead of looking up necessary information (track name, type and container id) by track index, add it to the metadata. Refactor canPasteSelection to take QDropEvent instead of the drop event's QMimeData. Coincidentally, this fixes the method to be consistent with its documentation. Fixes #4844 --- include/Track.h | 2 +- src/core/Track.cpp | 27 ++++++++++++++++++--------- 2 files changed, 19 insertions(+), 10 deletions(-) diff --git a/include/Track.h b/include/Track.h index 00ee5d0a6..f548168b7 100644 --- a/include/Track.h +++ b/include/Track.h @@ -341,7 +341,7 @@ public: } } - bool canPasteSelection( MidiTime tcoPos, const QMimeData * mimeData ); + bool canPasteSelection( MidiTime tcoPos, const QDropEvent *de ); bool pasteSelection( MidiTime tcoPos, QDropEvent * de ); MidiTime endPosition( const MidiTime & posStart ); diff --git a/src/core/Track.cpp b/src/core/Track.cpp index b300d94a3..cd1d23b16 100644 --- a/src/core/Track.cpp +++ b/src/core/Track.cpp @@ -497,7 +497,7 @@ void TrackContentObjectView::dragEnterEvent( QDragEnterEvent * dee ) { TrackContentWidget * tcw = getTrackView()->getTrackContentWidget(); MidiTime tcoPos = MidiTime( m_tco->startPosition().getTact(), 0 ); - if( tcw->canPasteSelection( tcoPos, dee->mimeData() ) == false ) + if( tcw->canPasteSelection( tcoPos, dee ) == false ) { dee->ignore(); } @@ -602,9 +602,12 @@ DataFile TrackContentObjectView::createTCODataFiles( it != tcoViews.end(); ++it ) { // Insert into the dom under the "tcos" element - int trackIndex = tc->tracks().indexOf( ( *it )->m_trackView->getTrack() ); + Track* tcoTrack = ( *it )->m_trackView->getTrack(); + int trackIndex = tc->tracks().indexOf( tcoTrack ); QDomElement tcoElement = dataFile.createElement( "tco" ); tcoElement.setAttribute( "trackIndex", trackIndex ); + tcoElement.setAttribute( "trackType", tcoTrack->type() ); + tcoElement.setAttribute( "trackName", tcoTrack->name() ); ( *it )->m_tco->saveState( dataFile, tcoElement ); tcoParent.appendChild( tcoElement ); } @@ -621,6 +624,7 @@ DataFile TrackContentObjectView::createTCODataFiles( QDomElement metadata = dataFile.createElement( "copyMetadata" ); // initialTrackIndex is the index of the track that was touched metadata.setAttribute( "initialTrackIndex", initialTrackIndex ); + metadata.setAttribute( "trackContainerId", tc->id() ); // grabbedTCOPos is the pos of the tact containing the TCO we grabbed metadata.setAttribute( "grabbedTCOPos", m_tco->startPosition() ); @@ -1316,7 +1320,7 @@ MidiTime TrackContentWidget::getPosition( int mouseX ) void TrackContentWidget::dragEnterEvent( QDragEnterEvent * dee ) { MidiTime tcoPos = MidiTime( getPosition( dee->pos().x() ).getTact(), 0 ); - if( canPasteSelection( tcoPos, dee->mimeData() ) == false ) + if( canPasteSelection( tcoPos, dee ) == false ) { dee->ignore(); } @@ -1335,8 +1339,10 @@ void TrackContentWidget::dragEnterEvent( QDragEnterEvent * dee ) * \param tcoPos the position of the TCO slot being pasted on * \param de the DropEvent generated */ -bool TrackContentWidget::canPasteSelection( MidiTime tcoPos, const QMimeData * mimeData ) +bool TrackContentWidget::canPasteSelection( MidiTime tcoPos, const QDropEvent* de ) { + const QMimeData * mimeData = de->mimeData(); + Track * t = getTrack(); QString type = StringPairDrag::decodeMimeKey( mimeData ); QString value = StringPairDrag::decodeMimeValue( mimeData ); @@ -1366,7 +1372,9 @@ bool TrackContentWidget::canPasteSelection( MidiTime tcoPos, const QMimeData * m const int currentTrackIndex = tracks.indexOf( t ); // Don't paste if we're on the same tact - if( tcoPos == grabbedTCOTact && currentTrackIndex == initialTrackIndex ) + auto sourceTrackContainerId = metadata.attributeNode( "trackContainerId" ).value().toUInt(); + if( de->source() && sourceTrackContainerId == t->trackContainer()->id() && + tcoPos == grabbedTCOTact && currentTrackIndex == initialTrackIndex ) { return false; } @@ -1389,9 +1397,9 @@ bool TrackContentWidget::canPasteSelection( MidiTime tcoPos, const QMimeData * m } // Track must be of the same type - Track * startTrack = tracks.at( trackIndex ); + auto startTrackType = tcoElement.attributeNode("trackType").value().toInt(); Track * endTrack = tracks.at( finalTrackIndex ); - if( startTrack->type() != endTrack->type() ) + if( startTrackType != endTrack->type() ) { return false; } @@ -1407,7 +1415,7 @@ bool TrackContentWidget::canPasteSelection( MidiTime tcoPos, const QMimeData * m */ bool TrackContentWidget::pasteSelection( MidiTime tcoPos, QDropEvent * de ) { - if( canPasteSelection( tcoPos, de->mimeData() ) == false ) + if( canPasteSelection( tcoPos, de ) == false ) { return false; } @@ -1478,7 +1486,8 @@ bool TrackContentWidget::pasteSelection( MidiTime tcoPos, QDropEvent * de ) tco->selectViewOnCreate( true ); } //check tco name, if the same as source track name dont copy - if( tco->name() == tracks[trackIndex]->name() ) + QString sourceTrackName = outerTCOElement.attributeNode( "trackName" ).value(); + if( tco->name() == sourceTrackName ) { tco->setName( "" ); } From 79524168b36dd1ecad9adb2585f88cc09bb6abe8 Mon Sep 17 00:00:00 2001 From: tresf Date: Mon, 18 Mar 2019 12:37:07 -0400 Subject: [PATCH 42/53] Bump zyn submodule Per #4642 --- plugins/zynaddsubfx/zynaddsubfx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/plugins/zynaddsubfx/zynaddsubfx b/plugins/zynaddsubfx/zynaddsubfx index c22acd61e..ccac06336 160000 --- a/plugins/zynaddsubfx/zynaddsubfx +++ b/plugins/zynaddsubfx/zynaddsubfx @@ -1 +1 @@ -Subproject commit c22acd61eb5d074988acea5fc1b6930151345c42 +Subproject commit ccac06336b363b9afe7ff4aea02bfa2d48187e1a From ea5cbe6789911ac00292460f66e82628ed614f81 Mon Sep 17 00:00:00 2001 From: Tres Finocchiaro Date: Mon, 18 Mar 2019 12:41:01 -0400 Subject: [PATCH 43/53] Allow new Zyn bank creation on Linux (#4905) Allow new Zyn bank creation on Linux Closes #4642 --- .../zynaddsubfx/zynaddsubfx/src/Misc/Bank.cpp | 35 +++++++++++++++++-- .../zynaddsubfx/zynaddsubfx/src/Misc/Bank.h | 7 ++++ 2 files changed, 39 insertions(+), 3 deletions(-) diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Misc/Bank.cpp b/plugins/zynaddsubfx/zynaddsubfx/src/Misc/Bank.cpp index 5cb43e4ff..28b69f845 100644 --- a/plugins/zynaddsubfx/zynaddsubfx/src/Misc/Bank.cpp +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Misc/Bank.cpp @@ -188,6 +188,7 @@ void Bank::loadfromslot(unsigned int ninstrument, Part *part) */ int Bank::loadbank(string bankdirname) { + normalizedirsuffix(bankdirname); DIR *dir = opendir(bankdirname.c_str()); clearbank(); @@ -255,9 +256,15 @@ int Bank::newbank(string newbankdirname) string bankdir; bankdir = config.cfg.bankRootDirList[0]; - if(((bankdir[bankdir.size() - 1]) != '/') - && ((bankdir[bankdir.size() - 1]) != '\\')) - bankdir += "/"; + expanddirname(bankdir); + normalizedirsuffix(bankdir); + +// FIXME: Zyn should automatically handle creation of parent directory +#ifdef WIN32 + if(mkdir(bankdir.c_str()) < 0) return -1; +#else + if(mkdir(bankdir.c_str(), S_IRWXU | S_IRWXG | S_IROTH | S_IXOTH)) return -1; +#endif bankdir += newbankdirname; #ifdef WIN32 @@ -355,6 +362,8 @@ void Bank::rescanforbanks() void Bank::scanrootdir(string rootdir) { + expanddirname(rootdir); + DIR *dir = opendir(rootdir.c_str()); if(dir == NULL) return; @@ -472,3 +481,23 @@ Bank::ins_t::ins_t() { info.PADsynth_used = false; } + +void Bank::expanddirname(std::string &dirname) { + if (dirname.empty()) + return; + + // if the directory name starts with a ~ and the $HOME variable is + // defined in the environment, replace ~ by the content of $HOME + if (dirname.at(0) == '~') { + char *home_dirname = getenv("HOME"); + if (home_dirname != NULL) { + dirname = std::string(home_dirname) + dirname.substr(1); + } + } +} + +void Bank::normalizedirsuffix(string &dirname) const { + if(((dirname[dirname.size() - 1]) != '/') + && ((dirname[dirname.size() - 1]) != '\\')) + dirname += "/"; +} diff --git a/plugins/zynaddsubfx/zynaddsubfx/src/Misc/Bank.h b/plugins/zynaddsubfx/zynaddsubfx/src/Misc/Bank.h index e9f56e2fb..a0ae74ce1 100644 --- a/plugins/zynaddsubfx/zynaddsubfx/src/Misc/Bank.h +++ b/plugins/zynaddsubfx/zynaddsubfx/src/Misc/Bank.h @@ -99,6 +99,13 @@ class Bank std::string dirname; void scanrootdir(std::string rootdir); //scans a root dir for banks + + /** Expends ~ prefix in dirname, if any */ + void expanddirname(std::string &dirname); + + /** Ensure that the directory name is suffixed by a + * directory separator */ + void normalizedirsuffix(std::string &dirname) const; }; #endif From f79c2929a5b9baa81279d016341a01bf160eaa3c Mon Sep 17 00:00:00 2001 From: necrashter Date: Tue, 19 Mar 2019 12:06:02 +0300 Subject: [PATCH 44/53] Fix empty editors after closing them and creating a new project (#4891) --- include/Editor.h | 1 + src/gui/editors/Editor.cpp | 18 ++++++++++++++++-- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/include/Editor.h b/include/Editor.h index 4b9017e9e..c93864439 100644 --- a/include/Editor.h +++ b/include/Editor.h @@ -47,6 +47,7 @@ protected: DropToolBar * addDropToolBar(Qt::ToolBarArea whereToAdd, QString const & windowTitle); DropToolBar * addDropToolBar(QWidget * parent, Qt::ToolBarArea whereToAdd, QString const & windowTitle); + virtual void closeEvent( QCloseEvent * _ce ); protected slots: virtual void play() {} virtual void record() {} diff --git a/src/gui/editors/Editor.cpp b/src/gui/editors/Editor.cpp index bdc3e55d4..9aa81fabb 100644 --- a/src/gui/editors/Editor.cpp +++ b/src/gui/editors/Editor.cpp @@ -32,6 +32,7 @@ #include #include #include +#include void Editor::setPauseIcon(bool displayPauseIcon) @@ -121,8 +122,18 @@ QAction *Editor::playAction() const return m_playAction; } - - +void Editor::closeEvent( QCloseEvent * _ce ) +{ + if( parentWidget() ) + { + parentWidget()->hide(); + } + else + { + hide(); + } + _ce->ignore(); + } DropToolBar::DropToolBar(QWidget* parent) : QToolBar(parent) { @@ -138,3 +149,6 @@ void DropToolBar::dropEvent(QDropEvent* event) { dropped(event); } + + + From 52d1f5588a02cad0adbec46a3ce3435f003402a3 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Fri, 22 Mar 2019 11:20:37 +0100 Subject: [PATCH 45/53] Remove useless include --- include/Plugin.h | 4 ++-- src/core/Plugin.cpp | 7 +++---- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/include/Plugin.h b/include/Plugin.h index 31c83c4ba..9a0757c35 100644 --- a/include/Plugin.h +++ b/include/Plugin.h @@ -196,8 +196,8 @@ public: private: - // You can add stuff values mapped by "Key" below - // The defaults are sane, i.e. redirect to sub plugins + // You can add values mapped by "Key" below + // The defaults are sane, i.e. redirect to sub plugin's // supererior descriptor virtual QString additionalFileExtensions(const Key&) const diff --git a/src/core/Plugin.cpp b/src/core/Plugin.cpp index cd1cf8b99..411f6fe58 100644 --- a/src/core/Plugin.cpp +++ b/src/core/Plugin.cpp @@ -22,16 +22,14 @@ * */ +#include "Plugin.h" + #include #include #include #include #include -// comment separator to prevent clang's header sorting -#include "lmmsconfig.h" - -#include "Plugin.h" #include "embed.h" #include "Engine.h" #include "GuiApplication.h" @@ -314,3 +312,4 @@ QDomElement Plugin::Descriptor::SubPluginFeatures::Key::saveXML( } + From 34835811e1877191ac46c34012015c67965e5dc7 Mon Sep 17 00:00:00 2001 From: Johannes Lorenz Date: Fri, 22 Mar 2019 20:35:21 +0100 Subject: [PATCH 46/53] Fix invalid display names --- include/Plugin.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/include/Plugin.h b/include/Plugin.h index 9a0757c35..af42b0f10 100644 --- a/include/Plugin.h +++ b/include/Plugin.h @@ -207,9 +207,7 @@ public: virtual QString displayName(const Key& k) const { - return k.isValid() - ? k.desc->displayName - : k.name; + return k.isValid() ? k.name : QString(); } virtual QString description(const Key& k) const From 032c324dbc592173f94d24f835f8a7fdc8f73879 Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Mon, 25 Mar 2019 15:30:45 +0900 Subject: [PATCH 47/53] Travis: use carla instead of carla-git Due to some breaking changes in the development branch of Carla, we can't use the package right now. Fortunately, the carla package now points to 2.0 series. So we will use it. See also: https://kx.studio/News/?action=view&url=changes-in-kxstudio-repos-regarding-carla-and-jack2 --- .travis/linux..install.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.travis/linux..install.sh b/.travis/linux..install.sh index a4e0ea1cf..3e86eecfe 100644 --- a/.travis/linux..install.sh +++ b/.travis/linux..install.sh @@ -22,4 +22,4 @@ sudo apt-get install -y $PACKAGES sudo add-apt-repository -y ppa:kxstudio-debian/libs sudo add-apt-repository -y ppa:kxstudio-debian/apps sudo apt-get update -sudo apt-get install -y carla-git +sudo apt-get install -y carla From f18efb470ff96b5531da265f3ad3ddee9b9be311 Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Tue, 26 Mar 2019 09:54:48 +0900 Subject: [PATCH 48/53] Travis: fix shellcheck warnings for the Debian sid script --- .travis/linux.debian-sid.script.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.travis/linux.debian-sid.script.sh b/.travis/linux.debian-sid.script.sh index 1c62ce6a4..9b8db416c 100755 --- a/.travis/linux.debian-sid.script.sh +++ b/.travis/linux.debian-sid.script.sh @@ -1,4 +1,4 @@ -#!/bin/sh +#!/bin/bash set -e : "${TARGET_ARCH:=amd64}" @@ -60,7 +60,7 @@ sync_version() { esac sed "1 s/@VERSION@/$VERSION/" -i debian/changelog - echo Set Debian version to $VERSION + echo "Set Debian version to $VERSION" } sync_version From 4dce466873b3249c0b1a97f6e3a782062623e065 Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Wed, 27 Mar 2019 07:05:38 +0900 Subject: [PATCH 49/53] Remove some Qt4 compatibility code --- include/Model.h | 17 ----------------- plugins/VstEffect/VstEffectControls.cpp | 10 ---------- plugins/vestige/vestige.cpp | 10 ---------- plugins/zynaddsubfx/ZynAddSubFx.cpp | 3 --- src/gui/SetupDialog.cpp | 5 ----- 5 files changed, 45 deletions(-) diff --git a/include/Model.h b/include/Model.h index bc9f5c046..b40c21029 100644 --- a/include/Model.h +++ b/include/Model.h @@ -41,10 +41,6 @@ public: m_displayName( _display_name ), m_defaultConstructed( _default_constructed ) { -#if QT_VERSION < 0x050000 - connect( this, SIGNAL( dataChanged() ), this, - SLOT( thisDataChanged() ), Qt::DirectConnection ); -#endif } virtual ~Model() @@ -89,19 +85,6 @@ signals: // emitted if properties of the model (e.g. ranges) have changed void propertiesChanged(); -#if QT_VERSION < 0x050000 - // emitted along with dataChanged(), but with this model as an argument - // workaround for when QObject::sender() and Qt5 are unavailable - void dataChanged( Model * ); - -private slots: - void thisDataChanged() - { - emit dataChanged( this ); - } - -signals: -#endif } ; diff --git a/plugins/VstEffect/VstEffectControls.cpp b/plugins/VstEffect/VstEffectControls.cpp index d92717d37..a0f97ce71 100644 --- a/plugins/VstEffect/VstEffectControls.cpp +++ b/plugins/VstEffect/VstEffectControls.cpp @@ -90,13 +90,8 @@ void VstEffectControls::loadSettings( const QDomElement & _this ) knobFModel[ i ]->setInitValue(LocaleHelper::toFloat(s_dumpValues.at(2))); } -#if QT_VERSION < 0x050000 - connect( knobFModel[i], SIGNAL( dataChanged( Model * ) ), - this, SLOT( setParameter( Model * ) ), Qt::DirectConnection ); -#else connect( knobFModel[i], &FloatModel::dataChanged, this, [this, i]() { setParameter( knobFModel[i] ); }, Qt::DirectConnection); -#endif } } @@ -383,13 +378,8 @@ manageVSTEffectView::manageVSTEffectView( VstEffect * _eff, VstEffectControls * } FloatModel * model = m_vi->knobFModel[i]; -#if QT_VERSION < 0x050000 - connect( model, SIGNAL( dataChanged( Model * ) ), this, - SLOT( setParameter( Model * ) ), Qt::DirectConnection ); -#else connect( model, &FloatModel::dataChanged, this, [this, model]() { setParameter( model ); }, Qt::DirectConnection); -#endif vstKnobs[ i ] ->setModel( model ); } diff --git a/plugins/vestige/vestige.cpp b/plugins/vestige/vestige.cpp index f4abed916..1d54538b6 100644 --- a/plugins/vestige/vestige.cpp +++ b/plugins/vestige/vestige.cpp @@ -223,13 +223,8 @@ void vestigeInstrument::loadSettings( const QDomElement & _this ) knobFModel[ i ]->setInitValue(LocaleHelper::toFloat(s_dumpValues.at(2))); } -#if QT_VERSION < 0x050000 - connect( knobFModel[i], SIGNAL( dataChanged( Model * ) ), - this, SLOT( setParameter( Model * ) ), Qt::DirectConnection ); -#else connect( knobFModel[i], &FloatModel::dataChanged, this, [this, i]() { setParameter( knobFModel[i] ); }, Qt::DirectConnection); -#endif } } m_pluginMutex.unlock(); @@ -984,13 +979,8 @@ manageVestigeInstrumentView::manageVestigeInstrumentView( Instrument * _instrume } FloatModel * model = m_vi->knobFModel[i]; -#if QT_VERSION < 0x050000 - connect( model, SIGNAL( dataChanged( Model * ) ), this, - SLOT( setParameter( Model * ) ), Qt::DirectConnection ); -#else connect( model, &FloatModel::dataChanged, this, [this, model]() { setParameter( model ); }, Qt::DirectConnection); -#endif vstKnobs[i] ->setModel( model ); } diff --git a/plugins/zynaddsubfx/ZynAddSubFx.cpp b/plugins/zynaddsubfx/ZynAddSubFx.cpp index fec5b3855..277c22596 100644 --- a/plugins/zynaddsubfx/ZynAddSubFx.cpp +++ b/plugins/zynaddsubfx/ZynAddSubFx.cpp @@ -291,10 +291,7 @@ void ZynAddSubFxInstrument::loadSettings( const QDomElement & _this ) emit settingsChanged(); } -// FIXME: Remove this check in future versions. Slots are public in Qt5+ -#if QT_VERSION >= 0x050000 emit instrumentTrack()->pitchModel()->dataChanged(); -#endif } diff --git a/src/gui/SetupDialog.cpp b/src/gui/SetupDialog.cpp index e8c7d5f82..f2ad13ee9 100644 --- a/src/gui/SetupDialog.cpp +++ b/src/gui/SetupDialog.cpp @@ -1075,12 +1075,7 @@ void SetupDialog::toggleOneInstrumentTrackWindow( bool _enabled ) void SetupDialog::vstEmbedMethodChanged() { -#if QT_VERSION >= 0x050000 m_vstEmbedMethod = m_vstEmbedComboBox->currentData().toString(); -#else - m_vstEmbedMethod = m_vstEmbedComboBox->itemData( - m_vstEmbedComboBox->currentIndex()).toString(); -#endif m_vstAlwaysOnTopCheckBox->setVisible( m_vstEmbedMethod == "none" ); } From 93ec816d4ca45d1b542a89cfc63c489f66e41498 Mon Sep 17 00:00:00 2001 From: Hyunjin Song Date: Thu, 28 Mar 2019 10:48:01 +0900 Subject: [PATCH 50/53] SetupDialog: fix file dialog not opening for theme directory It was caused by a typo affecting a signal-slot connection. --- src/gui/SetupDialog.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/gui/SetupDialog.cpp b/src/gui/SetupDialog.cpp index f2ad13ee9..5ab9630b2 100644 --- a/src/gui/SetupDialog.cpp +++ b/src/gui/SetupDialog.cpp @@ -440,7 +440,7 @@ SetupDialog::SetupDialog( ConfigTabs _tab_to_open ) : #endif addPathEntry("Themes directory", m_artworkDir, SLOT(setArtworkDir(const QString &)), - SLOT(openArtwortDir()), + SLOT(openArtworkDir()), m_adLineEdit, pathSelectors); pathSelectorLayout->addStretch(); addPathEntry("Background artwork", m_backgroundArtwork, From 3ef33dbbe994141ce3d7c5da7b9d44bcb1fdffb2 Mon Sep 17 00:00:00 2001 From: CYBERDEViLNL Date: Sat, 30 Mar 2019 23:51:56 +0100 Subject: [PATCH 51/53] PluginBrowser: Tree layout and search bar --- include/PluginBrowser.h | 25 +++++--- src/gui/PluginBrowser.cpp | 125 +++++++++++++++++++++++++++++++------- 2 files changed, 119 insertions(+), 31 deletions(-) diff --git a/include/PluginBrowser.h b/include/PluginBrowser.h index f7c46db72..3cc54c6e4 100644 --- a/include/PluginBrowser.h +++ b/include/PluginBrowser.h @@ -31,6 +31,10 @@ #include "SideBarWidget.h" #include "Plugin.h" +class QLineEdit; +class QTreeWidget; +class QTreeWidgetItem; + class PluginBrowser : public SideBarWidget { @@ -39,18 +43,18 @@ public: PluginBrowser( QWidget * _parent ); virtual ~PluginBrowser() = default; +private slots: + void onFilterChanged( const QString & filter ); + private: + void addPlugins(); + void updateRootVisibility( int index ); + void updateRootVisibilities(); + QWidget * m_view; -}; - - - - -class PluginDescList : public QWidget -{ - Q_OBJECT -public: - PluginDescList(QWidget* parent); + QTreeWidget * m_descTree; + QTreeWidgetItem * m_lmmsRoot; + QTreeWidgetItem * m_lv2Root; }; @@ -62,6 +66,7 @@ class PluginDescWidget : public QWidget public: typedef Plugin::Descriptor::SubPluginFeatures::Key PluginKey; PluginDescWidget( const PluginKey & _pk, QWidget * _parent ); + QString name() const; protected: diff --git a/src/gui/PluginBrowser.cpp b/src/gui/PluginBrowser.cpp index 27747bc53..1e73b7cdc 100644 --- a/src/gui/PluginBrowser.cpp +++ b/src/gui/PluginBrowser.cpp @@ -24,11 +24,13 @@ #include "PluginBrowser.h" +#include #include -#include +#include #include -#include +#include #include +#include #include "embed.h" #include "Engine.h" @@ -60,23 +62,91 @@ PluginBrowser::PluginBrowser( QWidget * _parent ) : m_view ); hint->setWordWrap( true ); - QScrollArea* scrollarea = new QScrollArea( m_view ); - PluginDescList* descList = new PluginDescList( m_view ); - scrollarea->setWidget(descList); - scrollarea->setWidgetResizable(true); + QLineEdit * searchBar = new QLineEdit( m_view ); + searchBar->setPlaceholderText( "Search" ); + searchBar->setMaxLength( 64 ); + searchBar->setClearButtonEnabled( true ); - view_layout->addWidget(hint); - view_layout->addWidget(scrollarea); + m_descTree = new QTreeWidget( m_view ); + m_descTree->setColumnCount( 1 ); + m_descTree->header()->setVisible( false ); + m_descTree->setIndentation( 10 ); + m_descTree->setSelectionMode( QAbstractItemView::NoSelection ); + + connect( searchBar, SIGNAL( textEdited( const QString & ) ), + this, SLOT( onFilterChanged( const QString & ) ) ); + + view_layout->addWidget( hint ); + view_layout->addWidget( searchBar ); + view_layout->addWidget( m_descTree ); + + // Add LMMS root to the tree + m_lmmsRoot = new QTreeWidgetItem(); + m_lmmsRoot->setText( 0, "LMMS" ); + m_descTree->insertTopLevelItem( 0, m_lmmsRoot ); + m_lmmsRoot->setExpanded( true ); + + // Add LV2 root to the tree + m_lv2Root = new QTreeWidgetItem(); + m_lv2Root->setText( 0, "LV2" ); + m_descTree->insertTopLevelItem( 1, m_lv2Root ); + + // Add plugins to the tree roots + addPlugins(); + + // Resize + m_descTree->header()->setSectionResizeMode( QHeaderView::ResizeToContents ); + + // Hide empty roots + updateRootVisibilities(); } - - -PluginDescList::PluginDescList(QWidget *parent) : - QWidget(parent) +void PluginBrowser::updateRootVisibility( int rootIndex ) { - QVBoxLayout* layout = new QVBoxLayout(this); + QTreeWidgetItem * root = m_descTree->topLevelItem( rootIndex ); + root->setHidden( !root->childCount() ); +} + +void PluginBrowser::updateRootVisibilities() +{ + int rootCount = m_descTree->topLevelItemCount(); + for (int rootIndex = 0; rootIndex < rootCount; ++rootIndex) + { + updateRootVisibility( rootIndex ); + } +} + + +void PluginBrowser::onFilterChanged( const QString & filter ) +{ + int rootCount = m_descTree->topLevelItemCount(); + for (int rootIndex = 0; rootIndex < rootCount; ++rootIndex) + { + QTreeWidgetItem * root = m_descTree->topLevelItem( rootIndex ); + + int itemCount = root->childCount(); + for (int itemIndex = 0; itemIndex < itemCount; ++itemIndex) + { + QTreeWidgetItem * item = root->child( itemIndex ); + PluginDescWidget * descWidget = static_cast + (m_descTree->itemWidget( item, 0)); + if (descWidget->name().contains(filter, Qt::CaseInsensitive)) + { + item->setHidden( false ); + } + else + { + item->setHidden( true ); + } + } + } +} + + +void PluginBrowser::addPlugins() +{ QList descs = pluginFactory->descriptors(Plugin::Instrument); std::sort( descs.begin(), @@ -93,7 +163,7 @@ PluginDescList::PluginDescList(QWidget *parent) : for (const Plugin::Descriptor* desc: descs) { - if( desc->subPluginFeatures ) + if ( desc->subPluginFeatures ) { desc->subPluginFeatures->listSubPluginKeys( desc, @@ -109,13 +179,18 @@ PluginDescList::PluginDescList(QWidget *parent) : for (const PluginKey& key : pluginKeys) { - PluginDescWidget* p = new PluginDescWidget( key, this ); - p->show(); - layout->addWidget(p); + QTreeWidgetItem * item = new QTreeWidgetItem(); + if ( key.desc->name == QStringLiteral("lv2instrument") ) + { + m_lv2Root->addChild( item ); + } + else + { + m_lmmsRoot->addChild( item ); + } + PluginDescWidget* p = new PluginDescWidget( key, m_descTree ); + m_descTree->setItemWidget( item, 0, p ); } - - setLayout(layout); - layout->addStretch(); } @@ -137,6 +212,14 @@ PluginDescWidget::PluginDescWidget(const PluginKey &_pk, +QString PluginDescWidget::name() const +{ + return m_pluginKey.displayName(); +} + + + + void PluginDescWidget::paintEvent( QPaintEvent * ) { @@ -190,7 +273,7 @@ void PluginDescWidget::leaveEvent( QEvent * _e ) void PluginDescWidget::mousePressEvent( QMouseEvent * _me ) { - if( _me->button() == Qt::LeftButton ) + if ( _me->button() == Qt::LeftButton ) { Engine::setDndPluginKey(&m_pluginKey); new StringPairDrag("instrument", From 07dcea129440d6f34029f0fd5aa82830d9502d45 Mon Sep 17 00:00:00 2001 From: Lost Robot <34612565+DouglasDGI@users.noreply.github.com> Date: Sat, 13 Apr 2019 14:26:41 -0600 Subject: [PATCH 52/53] Replace Monstro icons --- plugins/monstro/exp.png | Bin 522 -> 539 bytes plugins/monstro/moog.png | Bin 599 -> 498 bytes plugins/monstro/noise.png | Bin 563 -> 711 bytes plugins/monstro/ramp.png | Bin 520 -> 443 bytes plugins/monstro/saw.png | Bin 529 -> 489 bytes plugins/monstro/sin.png | Bin 578 -> 596 bytes plugins/monstro/sinabs.png | Bin 468 -> 530 bytes plugins/monstro/sqr.png | Bin 427 -> 345 bytes plugins/monstro/sqrsoft.png | Bin 539 -> 453 bytes plugins/monstro/tri.png | Bin 602 -> 566 bytes 10 files changed, 0 insertions(+), 0 deletions(-) diff --git a/plugins/monstro/exp.png b/plugins/monstro/exp.png index 9fe634881a8d4c47b380b1963604c840b40a623f..acb7a55cbc2671ed17d4cb0e8ae080028f1160a2 100644 GIT binary patch literal 539 zcmV+$0_6RPP)AHt5#EFVf5n0=;+-!GkA{9z&r^f&L9)_o9nLkQOoK2y##( z1Vt#AKgRbU2x^Bdx9^?~wb*2neW~a4Ja3-Y^M2srf&U(=VsARt(VTO3Rhwxi-pL)r z5q!XaXV~EH1rU(L8z<-q1~b^mdV6ZuJ;9dftwvrEU(^%qDzU$VdWd^-R;HTD`(4BK zhf*5Du&NXRq|VH}*Lwq9!5T1gzfbMHRMmGFEcaUfLOWPmT(hM##$*7><@0DL7>d=^ zS_j}lZs#8i?sjxm%X$n@N1tp@#*3fEhc-Z00o2d))Xs5#TiDJz&qOti%J)zv{IsF_ zrS=Hbf{2clPpWHUua5d!*0yubBCw2#i{&Qs$s1eE4vCLW#H#CiL1I8&+@BA((l__b z-K?_&ECaZvI(r!e_>v4CUT9?+)dFCigP)e zSad^gZEa<4bO1wgWnpw>WFU8GbZ8()Nlj2!fese{00DAIL_t(2&z+DzOI%SD#D8<| zeJ<>3wDB3N6wIa(QdPH-TTJE z^$)T(p5}0dGiTEmyj!;+TFXJ8AskT{N!uQ?NdWx*EaZ!S6L{NXC)%ohUhF{_9sW9@ZOo^U_>c$u2(B@Z5$C`T4R%`vy>Ey7Zx!@tunX)U&Ly(Jw{hTwGnzPHT>t<8 M07*qoM6N<$g8hfkK>z>% diff --git a/plugins/monstro/moog.png b/plugins/monstro/moog.png index 6d9005966f448c6da2cd4003fc4f06d6c4e2af7e..f206bd0da044290a77383c72779c5c414d340b26 100644 GIT binary patch literal 498 zcmVpP^PU6$`RsJX4)30IxMk)iSo}_+Rp#>oM>FLm6{cI| zEt|tfcU>5gUc+0$&)w}Qg&r^1RXda}Mu6x~`*3YmztzxHss9NMW{N8!I;Q9iIKPKO zXAc3(ND@HaH1;QLwW=wQPE_`Dk-)*PVLMZr6e zSad^gZEa<4bO1wgWnpw>WFU8GbZ8()Nlj2!fese{00F;AL_t(2&vnp0Z;f#j2k_5% zp66cGmXHvGGWZk!a;Hi}B0|C-NJR7%SWFVH0PlcJ;vKk46Tx6022)KmmZk>8U_i80 zz4!SYLxl>y<4Haz-*dv6OwMGze$1veTn{ei>iBM5x!LUXX*oj?lx7aj7SHh z(O@akt1O0F*DU{qrurhj>t;F~VNZr{nHEuoU8(;MG(RPhb-@8_305L3XDAl^kp|qz zXy!E0+KnC}`Ho?vIl7LXYf9FhxTX9+KB4o)Ee}0t7;{TCdXs-o|!CeB@cq_5iZsP`8#3?<)KYSFKc8*)#)0SPN0{}C?3VQ#dQllDegpgfD0vEEnjUs zGpDXfIwwrxW$>(!&dUAW?-IgKdX)OSb-TwtSF*0)jqY*$<>f`z1&c8z9qs2?r=R~#` z*NCM-T32K4yF1waY^tVf`179MAj!ueY|%_Jre!E6y=yU5|7!JAvIwJ&Yi zPv_S`9MW%Q*WxdV^#C2CvquH4Bi0Cj*f#)ty)4SE@|$P{(l$~|i_i_=MS~uIk_2}E z)bv0oOQ!)Wa2>1fJp46?M67>Jlk~J@KORz*G=NpYdFMY&p5Q9|$mN-D3-n8YM-ST3~V zMKzfz91*i5CWVPq(|}Wduu8#GOukn9>r!Dn)wDmC+Utu-uH5W89ZR*mcVfL*&sp~4`6~#bT&^`1}v}zfB=p9025uYC+j9q85B5{JKda5WM+PrdoYVg zrN=8;TJYG0V$n^>&UE002ovPDHLkV1i(}F8Tlf literal 563 zcmV-30?hr1P)e zSad^gZEa<4bO1wgWnpw>WFU8GbZ8()Nlj2!fese{00EmxL_t(2&ppw-Zd_Fq#qr-h z_s$IC4>^`i5vAb;Kp;^eA)&kjrCS;v$&V7cbdgY`G?9|HM8L=b#t?h%$DB=vJOfBeT2UBN~TNnQpK?1$)9hnzWb275_};Yp(fi%r>Zk_ z5+Q^iYUj;d{9$t43l~^o2)Fdh=n1z>t%`#}?Bzd5aLS{%riF1B#Zfaf$kjuajkYAo! zl$xTDRH@+Z7pm`*nO9mdC$RY&0|TRsr;B5V#`&Y?>^+zgWsZG(|9hTUYIdXE*<9D_ zj)EzNc^xNT2{UnxHVKQiaEms%m6cq7K+%U^$-pUUQK03N7BfZD_kXuu`8Ut|{5Kx? zABXnmZo70t;_9mu1 z=B)fk_mCwmvE~`EQOOOrvVPrinb?0J^w6~E+}OzEhA6Y*{I*BTvaFWdFYTLIJ#C5T zKi!Hq*0u5(8mifT+he14{GPMn5X0A_FMnMRsA(}@8aeUG0+Zs~GdCS#Sby`&?w6hY iC%T0`9t&<3I>2w`k#~xF+qZU57<#(;xvX6vrPa102y>e zSad^gZEa<4bO1wgWnpw>WFU8GbZ8()Nlj2!fese{00D4GL_t(2&vnwhOVx1{$MM(s z-m8fZA+=^xL+DrzZmprAi|x@=5R_|6YXlNS1pNyxMx_u$e}QdMLtD@iSw8E1_?-s5 zSb6W6&zsLV?{i}6b1p`>1=6dmJZ|jUT)P@=J*q_bj>M=i8O2buPfcd@gs_7j*pwzS z+#MY@a{a3s-NAx(rUo{J4QaY{=*D)#(8RjZ4J;5LlWn|@5LrB^in;S=40i*I`3w0b!xpGJ7rzUi;`YXGMo&TeJ6ssos15p(EJ@!mQ_6|a(&K)) zv1_ld?ll%2>AT-C+8tVAeO&eP&`zl=Cr^)6Ei_wZdnFN(>l3Pxe!ErWO{RAl#)6x@ zp(8R^FKM^-Y^i%5e86-;1MSW`WnDEfFaJ(lyQ0-7er0%qhkpP>51QPMrc!7C0000< KMNUMnLSTYBVbo0k diff --git a/plugins/monstro/saw.png b/plugins/monstro/saw.png index 56c077f2c529fea4f55046656dcc36e66c5bc71c..3122b082f8dbabcfde52d71584a8ec3712f45fab 100644 GIT binary patch literal 489 zcmVfBv=d#2GYbp#6Tn_i9sSUh{PaeqDe{G zPA?S$yU+AtHM{p15MQmnXK}Z4@A)O?o^yczI`kLRc5oR9)gU5*!mh)srf_X24FEC- zv@cF&1z_l-I z=ZgKPLYkPu(0a>_*AIMtFN>r3+d+W#0RYVFZlZDYdrnDgXCKB4mOB6dg;h7+Fi|S+ z2ae|MCP8`=0HCqxI_7k_>cm%AoH1amy@j)G!kqi9C*tFe#d!m?0sxvZ<@T6Ml_ntp zAeO&h7R<@l=3u-KH#hvuU&gYJLxNQj0JKuW1;^Ue zSad^gZEa<4bO1wgWnpw>WFU8GbZ8()Nlj2!fese{00DVPL_t(2&xMh_YE)qmhM#xN zmo;XSxL|9nEF=pyA&8x=Bq3?yB?tV$LR*<;hlL#{HJE#xo8~cgo0a$D*$mMVXHqvp`eMF1zLcQfUHX&C|cu8 zSX{%*0zZH+=`f((A-W~aEEW)Rh|OU*WgG^aJjQqqmK@xa5b(;-mGHi#i*M0DjP)xP zB~}DD^H70g&RhSAxNW7=GzM^1unbHA>nT{r?4sdVs++p-nY7!ZMR3(-UgEI}?)NKp zLgc@rQ8$&lOlET(Sdw@_<`E^|M@OmZ3q{>j1Pj1C=96Ky7-QqqH093L-n*l2N^k=( zaCao}baR{DpMEbinP~Pa7J(H&hI^5D^7R*6ooOLs>jclXBZ4a53XdhCh^STbs2nnKSNbdXfNZB8)TbOVgT?P^azou T!DIAa00000NkvXXu0mjfI`-7i diff --git a/plugins/monstro/sin.png b/plugins/monstro/sin.png index acabd5a171a9fc5ccefec61c360de9c09629563b..3ac3c9afa238aec201672993437ba6406c79a551 100644 GIT binary patch literal 596 zcmV-a0;~OrP)6+beFTQ+BPvC_e&4mI^- zv>??WeWODZ#DZgo4k9r|$# z=jH$~dvB)I#@Uf-8tedc*TxN;n=>#oz92Gz%iNhGY#2lq|_p!)b#rSKLYu&w(5B62zv_V{A=y#7aAu8h4ibX!Dk5EtRC4E3 zew9|rhZo`(e8-vq1Tq%aPu9bG7!RfvTh|=GN_q6dKP_ck1e@!D1K~iFj{oYC{}GWL_4#}|6<$75tf5-Nqyz4fi0o6e zSad^gZEa<4bO1wgWnpw>WFU8GbZ8()Nlj2!fese{00F8=L_t(2&u!65i;V#o2k_tX zzVA7VTP~e(X`(EI5xHcc>{y76Vj~~H);GY1VK1|h`$FV4LXk`oGiB1rQiZAR{9SdCIrJ=`DtfA^hyo6WkenX*~z6=7S1)tUAL6T*{X z`L}zsGDjM**bC#y?qEkQ9_+_TG)Fa1;~l&7|ZWTwZ_xSf6dzs?7?(f_%- QUjP6A07*qoM6N<$f=v?tU;qFB diff --git a/plugins/monstro/sinabs.png b/plugins/monstro/sinabs.png index 2216224e69d09e64184be0033f77821712ea0c58..e28d1ec21d4a3b3ef6ed2c7e26f5acbfdc9220ac 100644 GIT binary patch literal 530 zcmV+t0`2{YP)Wzp=8_llFDr zOHHrePhI|e>J8xfi-Swy!r=n$RRE+NDbyO%rCNDo?yWb5F+rVQVU)vhN>=8haCK{E(V2afYJeZyU z$gTSB*3fN0l(PUdo>f``G-pJ6F{t!r7V+o2>ou{99#xyOkLRyu!|}7 zE*tTbbf}eo@3e@wmElbh{37MC04d&VzhvKAjsDDg_w1Y`47f*D z1aMi~;~@Lm+Q(tuC*oK3OVsa@6#x{sPxeSJ0W1mY{ENx@mr?$Ma((|H=bR<}0-%(L UDvD>tcK`qY07*qoM6N<$g6{q6e zSad^gZEa<4bO1wgWnpw>WFU8GbZ8()Nlj2!fese{00BHnL_t(2&+W{=YEy9-fZ^vm z`I(p@LQ}y%xC&MfD+LEZbP)%q&h}CqI&|)R=p6_SwnOcp1dDXlEZSzULnTd4a?a(49qBkbTornl0zupQw!MwwpqTA$A^TvTPKUl*GZcCn7{II8I2 z=x?t_*sY7rGSn}nb|u-u!(=Zw5q2_c;SOFWli-Q;vTD35$&2I$zTizF(!Jz%aGYTu zlGWg*#1(WFb+J=b6DFUZ$$G1qSF%ecW zT`R#V&N7{-)EX70`R6{>T4YBXtsl}^gsYV_&$J&L^cu5X*GxE!@Jl%Cb89rc7 zTEyey9Jr+IPc7=7mYz~R>7MtL!Q67Wq4u8cP^m8~^QH9PKllx$f`URePxCGS0000< KMNUMnLSTYeH^UwP diff --git a/plugins/monstro/sqr.png b/plugins/monstro/sqr.png index 83d5e4869038b5d02b01cf876afb598e62036928..d5d3c211c6e109e02863df08a81c1f60ac721135 100644 GIT binary patch literal 345 zcmeAS@N?(olHy`uVBq!ia0vp^{6H+o!3HFmxV|j}Qfx`y?k)`fL2$v|<&%LToCO|{ z#S9GG!XV7ZFl&wkP>{XE)7O>#F$*sPy9i(De{GT_i(`nz>Er|njRP5VFDBY6x~ji@VwNPFp7`gm z(*OCbiAs9C|Nk4`Nl!>PuugJaN8>}e!~g$Jf9RK#^dRZS`4?U1T?@|k{QsYCnW;aQ z|MBa$H49Ii{P|wa?yS!gmKz5$(<~pVn4Zk}pRD+7KF{Qit0~+57cvVAA66A(V`FOz z?%TwBB+KMhv48)>nLGd4Ge2IsP%5D%_K|E^c|Vd;Ir8L`7~m+2du4sipy zW6=|L7__l;ZvMdWO2v)$aCQCe1fIhWoK}>UhL)BdzxaGrL8}&1D@T0S_1c$@e@_on z;xdYOu%<#gRZpuV%Yn>SAU@e{pYL05`Z_f9U(|(dcdcec-M`~-CHMC3;HUr1 zxBNE0Vc%9)BjB~@=+parN1M&8Rut5q?LW)@{Y~)Jz0W)!OSGr%EsQb$$E+?KWp-8Z S`F~&-GI+ZBxvXrkk diff --git a/plugins/monstro/sqrsoft.png b/plugins/monstro/sqrsoft.png index 2e5626d20538b8aaca92137852baa9687602a89b..037fcab12bf443e946f6682723d10159b1d5adf8 100644 GIT binary patch literal 453 zcmV;$0XqJPP)^!sl=%!CVuPt=E?i#c|;52XW@b$86P0Bgq4}}1wSBwOfg)M@wOzAU(2ri z@vilo*;FY`zz4u+RU&EO&^WyUI7OqvWIf-m0kHzg9zCY&3BL-eT}>y`t;OJ^`YfY9 zr?=tj+~D1J0;cVL(gT3!x_je#48P=$3IwOM<)9cA0Q~aYK&|PAj*;7x=M4p=``v$k z#Dsun9?diQCIuYNYK1m8t%HMRubx>O68wlJekQi0a#P v`o78IFkSb$#yl7GM8>&Fap!e zSad^gZEa<4bO1wgWnpw>WFU8GbZ8()Nlj2!fese{00DzZL_t(2&yCW*YE)qmhVkEg z=j^T_0f~(kk@zQ|g4ie`R)UQ}3L9_0I}ij@T8Y?r2e}8a(9*_En;=9a5ReEKN)V#U z?%8w3W`mo=!grWvhKG4)n7FZ-XM;x(Rx!vl!mFyy^%USsY)F*~V5_#$iz?=yU>(wk}Tvhc9! zS=ifB4nW9hw54b#J)IO1sZ@>HAss3FOT!&x#w-*Fq10nBi9-wY7%r)R??#U+s*Z$hJiXG*Udk^%DvyT6g0r=!Iy}yAHt&nbRm|RFvIk80o6W~V zVbAjdfJj`;JgWjY^~(Rnx%I;wCO5%OZNdxp<}=ShJ_vH%ZyY@wh|^oN zw*m3#P)WvXpVFuV-7bY^+q(;wL&H7_dLv)&-0O>Ha^nGT-W>JaV&u4F^FDkK))z?h91poj507*qoM6N<$ Eg8o4HO#lD@ literal 602 zcmV-g0;Te zSad^gZEa<4bO1wgWnpw>WFU8GbZ8()Nlj2!fese{00F{DL_t(2&t1|zOO*i_#_`|% zc+PX|&`^S#6i&Wy93r97MaZG`nWC)^qFU(mzd|FK&(uagQ#+ae z6&lr|xY^!9`;{mzl)_wu`}i5*zA#r@DEF)Xq0E@v(}==srjC@!fs~m#QJ9_F(~udr zP}LR9OHH98JP=NTo^W#AJIzbYs_PocjMHhwFtn4ENK4p?@CsXzmXeiWXrE4B?Te}_ zhErM!Ytpi?gUuZKj!j`lT2@#aPH9zLQS4VjTPs-*X7DA`=ZSEZ=`+3vGs%Ls)_x_5 z$vq8Yvq1-vo~DEId-|FVk{$#d<0~P>tpFZM$H8u#8Th7-GlO7PI>y5rA;q}iuC#(B zVN30O`NLrQvAX;#)-I-fpOi^%Cyb(zkCP5ufgTq&KwQKsH# z!l+wG=vHzxVU($dqX;w7Vj-*sC64NJ*q<{NSH~2Q{W;@09ZE+@DLidR?=TiVX!^aq oq4rO Date: Sat, 13 Apr 2019 14:30:46 -0600 Subject: [PATCH 53/53] Shrink ComboBox arrow section slightly This is being done for two reasons: 1. The new Monstro icons (and the icons for Microwave when it's finished) are too large. 2. All ComboBoxes (subjectively) look much nicer this way. --- src/gui/widgets/ComboBox.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/src/gui/widgets/ComboBox.cpp b/src/gui/widgets/ComboBox.cpp index 0673ee32d..c796bfa74 100644 --- a/src/gui/widgets/ComboBox.cpp +++ b/src/gui/widgets/ComboBox.cpp @@ -42,7 +42,7 @@ QPixmap * ComboBox::s_background = NULL; QPixmap * ComboBox::s_arrow = NULL; QPixmap * ComboBox::s_arrowSelected = NULL; -const int CB_ARROW_BTN_WIDTH = 20; +const int CB_ARROW_BTN_WIDTH = 18; ComboBox::ComboBox( QWidget * _parent, const QString & _name ) : @@ -198,7 +198,7 @@ void ComboBox::paintEvent( QPaintEvent * _pe ) QPixmap * arrow = m_pressed ? s_arrowSelected : s_arrow; - p.drawPixmap( width() - CB_ARROW_BTN_WIDTH + 5, 4, *arrow ); + p.drawPixmap( width() - CB_ARROW_BTN_WIDTH + 3, 4, *arrow ); if( model() && model()->size() > 0 ) { @@ -251,4 +251,3 @@ void ComboBox::setItem( QAction* item ) -