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 | 120 ++++++------- 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, 622 insertions(+), 255 deletions(-) diff --git a/include/Effect.h b/include/Effect.h index d6aa04d9710..3d765fdc03c 100644 --- a/include/Effect.h +++ b/include/Effect.h @@ -148,11 +148,6 @@ class LMMS_EXPORT Effect : public Plugin m_noRun = _state; } - inline const Descriptor::SubPluginFeatures::Key & key() const - { - return m_key; - } - EffectChain * effectChain() const { return m_parent; @@ -201,8 +196,6 @@ class LMMS_EXPORT Effect : public Plugin 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 fc25479969b..18960ec8f4c 100644 --- a/include/Engine.h +++ b/include/Engine.h @@ -111,6 +111,9 @@ class LMMS_EXPORT LmmsCore : public QObject return s_instanceOfMe; } + static void setDndPluginKey(void* newKey); + static void* pickDndPluginKey(); + signals: void initProgress(const QString &msg); @@ -137,6 +140,7 @@ class LMMS_EXPORT LmmsCore : public QObject 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 a373ae4ac47..c3df04729fb 100644 --- a/include/Instrument.h +++ b/include/Instrument.h @@ -55,8 +55,9 @@ class LMMS_EXPORT Instrument : public Plugin 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 @@ class LMMS_EXPORT Instrument : public Plugin // 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 fb12e825a41..dd78b2f9ad3 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 @@ class LMMS_EXPORT InstrumentTrack : public Track, public MidiEventProcessor // 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 14899487cf9..28fa25b894c 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 a2cc7d696ab..034d8a06c2e 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 @@ class LMMS_EXPORT Plugin : public Model, public JournallingObject 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 @@ class LMMS_EXPORT Plugin : public Model, public JournallingObject 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 @@ class LMMS_EXPORT Plugin : public Model, public JournallingObject 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 @@ class LMMS_EXPORT Plugin : public Model, public JournallingObject { } + //! 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 @@ class LMMS_EXPORT Plugin : public Model, public JournallingObject // 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 logo out of sub plugin or descriptor + const PixmapLoader *logo() const; - // return plugin-type + //! 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 - PluginView * createView( QWidget * parent ); + //! 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 @@ class LMMS_EXPORT Plugin : public Model, public JournallingObject 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 75c7cd2919b..f7c46db7293 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 @@ class PluginDescWidget : public QWidget 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 56d32c4e421..17b178108f0 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 @@ class LMMS_EXPORT PluginFactory 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 @@ class LMMS_EXPORT PluginFactory 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 dd05444a69c..7e525234416 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 bf599025216..200439dae92 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 e9344e9438f..1b055fe73a7 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 904c8133bbb..b7613827ba6 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 d8d5f3e263a..8e5b7f098ca 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 cb02937106e..f0154f75b02 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 6c080f7c9ac..ce14e1ac220 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 ecc77be0bc5..c370c409c1d 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 69c71ce68c8..60e02215624 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 8bc7d372dd9..d057eff83f0 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 2087cf88068..d204babab23 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 d32f14e8840..e556e42bf00 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 baee242bb78..fb089aa0ce6 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 5b34dcb1f60..ba64edb06d2 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 b6d45c8d36c..63aee0dc694 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 6ab7adce719..0ea9968dae7 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 4bf75777ce8..138e8165d17 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 c99f46f88cf..2718d66d70f 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 2eb46be565f..024e9ae6159 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 7111bcdaa51..ee1e1fbc371 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 6b644fd468b..a883f75fa32 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 73ea33a512b..fe91358b344 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 f33a9042853..bde5c8917a4 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 0122b293a23..2787e39b711 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 ad8d9a78c83..8235ad147dc 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 8cdcc9c600a..c842977532f 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 a53abbe5d45..7f89c20f3ce 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 534bb783a23..ba608da14a2 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 4336e50a5d8..febbe5a9192 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 b479d7d8da6..e7f80b5ab7d 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 a87e492702d..a25dca54ae2 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); + } + + 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; + } } - - Plugin::Descriptor* pluginDescriptor = reinterpret_cast(library->resolve(descriptorName.toUtf8().constData())); - if(pluginDescriptor == nullptr) + 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; + + 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); } - - descriptors.insert(info.descriptor->type, info.descriptor); } m_pluginInfos = pluginInfos; diff --git a/src/core/PresetPreviewPlayHandle.cpp b/src/core/PresetPreviewPlayHandle.cpp index dc36819b7d7..ca0e5219499 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 64b180d4817..4b59a19b108 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 ); - - 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); - - hbox->addWidget( logoLabel ); - hbox->setAlignment( logoLabel, Qt::AlignTop); - } - - QWidget *textualInfoWidget = new QWidget( m_descriptionWidget ); - - hbox->addWidget(textualInfoWidget); - - 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 ); - } - } - - 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); - } - - ui->scrollArea->setWidget( m_descriptionWidget ); + QHBoxLayout *hbox = new QHBoxLayout( m_descriptionWidget ); + + Plugin::Descriptor const & descriptor = *( m_currentSelection.desc ); + + 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); + } + + QWidget *textualInfoWidget = new QWidget( m_descriptionWidget ); + + hbox->addWidget(textualInfoWidget); + + 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 ); + } + } + + 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); + } + + ui->scrollArea->setWidget( m_descriptionWidget ); m_descriptionWidget->show(); } } diff --git a/src/gui/FileBrowser.cpp b/src/gui/FileBrowser.cpp index 05727d33295..4311e4e053b 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 9e8fc58c132..93e153f6f97 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 dc0fc35e091..27747bc5363 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) + { + 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( *desc, this ); + 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 2772ef104d0..7c69d5eb824 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 db310a05ec6..361db740e63 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 298430b030e..2b25982df2a 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 7f89c20f3ce..50e25b0b416 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 e7f80b5ab7d..7fcbd9795ed 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 a25dca54ae2..ec390ae6e30 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 60e02215624..ac00630d44d 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 d057eff83f0..c0a39f9c258 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 034d8a06c2e..48c5f90c7bd 100644 --- a/include/Plugin.h +++ b/include/Plugin.h @@ -165,9 +165,9 @@ class LMMS_EXPORT Plugin : public Model, public JournallingObject // 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 @@ class LMMS_EXPORT Plugin : public Model, public JournallingObject // 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 1b055fe73a7..4cefa90b5f6 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 b7613827ba6..3f47734f9c8 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 7fcbd9795ed..2975cf10498 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 ec390ae6e30..f84227091f8 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 c48186fa39e..fb0325a0d9b 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 a68620483b9..8f3b763d33e 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 eb7cb9f7aea..1ef063d552f 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 fb62cb5b31f..b99a461de3a 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 fb1473f7908..58df3b624a9 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 0234be0ab72..06eece23acf 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 7bdf9dc077c..fef788885db 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 f3b69e47dd5..286b01f15c3 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 fb0325a0d9b..6067c3cad47 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 76d7928b441..aad5f1c6984 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 d70b47e070e..0957852c615 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 e1fb77793e7..b39e8f52e6a 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 ee03897493e..b02a4c68855 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 2fdb8e459b8..3135548999f 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 e204a644aa6..2611d70867f 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 f52e10ac781..2ed0f8308eb 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 6067c3cad47..b543a956d7e 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 7318ae5acc0..47143b5fee3 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 c44b1790c4d..86f03c427d0 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 2cefe73c6ea..2eff0f264da 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 2a9f84076b0..59dd19a0aa6 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 07bed93b61a..5070421483b 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 fae7b35dba5..c873056419a 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 cea7f7381e7..568b6dae164 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 3135548999f..6be59f3cd0c 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 150807686e9..d17be147c0c 100644 --- a/plugins/audio_file_processor/audio_file_processor.h +++ b/plugins/audio_file_processor/audio_file_processor.h @@ -211,7 +211,6 @@ class AudioFileProcessorWaveView : public QWidget private: bool checkBound( double _v ) const; - } ; @@ -276,6 +275,7 @@ public slots: 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 37da8f414f9..4ac6bf1332f 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 a75a9f8442c..7f489031b82 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 0997676c953..10f99c2a468 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 c25e49dbcb7..00000000000 --- 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 10f99c2a468..65b87e6d335 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 5d19a3103ac..229fa02e315 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 2eff0f264da..bd164bd6ae3 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 225d6a824ba..50bf54edbc6 100644 --- a/cmake/modules/FindWine.cmake +++ b/cmake/modules/FindWine.cmake @@ -7,46 +7,89 @@ # WINE_DEFINITIONS - Compiler switches required for using wine # -LIST(APPEND CMAKE_PREFIX_PATH /opt/wine-stable /opt/wine-devel /opt/wine-staging /usr/lib/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 d7d680be239..32d65dd684b 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 00000000000..2956198d894 --- /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 5070421483b..9a8f0452948 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 f802bc4b902..4b02bf8abf4 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 77724526852..36ad7f14848 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 32d65dd684b..d32aec66432 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 65b87e6d335..463353df0ef 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 1b520479d01..60efaed7b08 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 5e8345845f9..aed094c2253 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 314d5fc18e4..44ed0dcb3be 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 2e69802b2c2..7dbeee5a6b9 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 28c09edb380..bd5f98b561e 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 bd164bd6ae3..e6d79185e47 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 | 358 ++++++++++++++++++++++++--- 11 files changed, 612 insertions(+), 83 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 00000000000..fa001b2bbe8 --- /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 fb12e825a41..42d7910b932 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 @@ protected slots: 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 decf52f3f15..ccb5a020e85 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 @@ class SampleTrack : public Track QDomElement & _parent ); virtual void loadTrackSpecificSettings( const QDomElement & _this ); + inline IntModel * effectChannelModel() + { + return &m_effectChannelModel; + } + inline AudioPort * audioPort() { return &m_audioPort; @@ -153,15 +164,18 @@ class SampleTrack : public Track 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 @@ class SampleTrackView : public TrackView 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 @@ public slots: } +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 302dcb5cc1d..f7f71de1356 100644 --- a/include/Track.h +++ b/include/Track.h @@ -675,6 +675,10 @@ class TrackView : public QWidget, public ModelView, public JournallingObject 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 0e5f200d6d5..032090bf1d5 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 5e6758fdecb..6c38711c428 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 d5ff6461237..f17ef105fb5 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 440e37d1077..0a6a54866fe 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 00000000000..bfe4a9637f9 --- /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 a927e8aadf1..1dc0642d62d 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 8fa6fd50fdd..c965af140f2 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,24 +808,58 @@ 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; } - -void SampleTrackView::showEffects() +QMenu * SampleTrackView::createFxMenu(QString title, QString newFxLabel) { - if( m_effWindow->isHidden() ) + int channelIndex = model()->effectChannelModel()->value(); + + FxChannel *fxChannel = Engine::fxMixer()->effectChannel(channelIndex); + + // If title allows interpolation, pass channel index and name + if (title.contains("%2")) { - m_effectRack->show(); - m_effWindow->show(); - m_effWindow->raise(); + title = title.arg(channelIndex).arg(fxChannel->m_name); } - else + + 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) { - m_effWindow->hide(); + 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; +} + + + + +void SampleTrackView::showEffects() +{ + 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 fff13c62dc7..a75d573a813 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 1a2f26a352b..8e65e0e9342 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 a75d573a813..e929f7c191d 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 b543a956d7e..5f8002cb191 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 338ea93dd77..d0607022d8e 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 slots: 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 a97802bdc70..e0e1347fe53 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 4c261f58124..746e7e65158 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 fbffe4b349e..8dd25c11f83 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 0cfa31d2195..fc07e46d5dc 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 682291b9ee3..970a48ad3cb 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 f9696252a42..6c2f1bbd2a6 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 48c5f90c7bd..31c83c4ba93 100644 --- a/include/Plugin.h +++ b/include/Plugin.h @@ -202,19 +202,19 @@ class LMMS_EXPORT Plugin : public Model, public JournallingObject 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 2975cf10498..1562044f98f 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 f84227091f8..abf6421229e 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 a373ae4ac47..6ab8e0c66a8 100644 --- a/include/Instrument.h +++ b/include/Instrument.h @@ -63,6 +63,8 @@ class LMMS_EXPORT Instrument : public Plugin // 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 298430b030e..6005402f123 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 319b38184f9..245963ce2b3 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 b479d7d8da6..12edb864491 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 64b180d4817..3c641acec79 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 677c896fbb7..456ca4871db 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 06eece23acf..fafb83d0dbc 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 00ee5d0a63b..f548168b7a7 100644 --- a/include/Track.h +++ b/include/Track.h @@ -341,7 +341,7 @@ class TrackContentWidget : public QWidget, public JournallingObject } } - 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 b300d94a347..cd1d23b1625 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 c22acd61eb5..ccac06336b3 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 5cb43e4ff25..28b69f845b8 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 e9f56e2fb42..a0ae74ce1e0 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 4b9017e9eb5..c9386443982 100644 --- a/include/Editor.h +++ b/include/Editor.h @@ -47,6 +47,7 @@ class Editor : public QMainWindow 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 bdc3e55d4bb..9aa81fabbee 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 31c83c4ba93..9a0757c3509 100644 --- a/include/Plugin.h +++ b/include/Plugin.h @@ -196,8 +196,8 @@ class LMMS_EXPORT Plugin : public Model, public JournallingObject 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 cd1cf8b9924..411f6fe5899 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 9a0757c3509..af42b0f1028 100644 --- a/include/Plugin.h +++ b/include/Plugin.h @@ -207,9 +207,7 @@ class LMMS_EXPORT Plugin : public Model, public JournallingObject 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 a4e0ea1cf9b..3e86eecfe87 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 1c62ce6a410..9b8db416c4d 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 bc9f5c04646..b40c21029de 100644 --- a/include/Model.h +++ b/include/Model.h @@ -41,10 +41,6 @@ class LMMS_EXPORT Model : public QObject 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 @@ class LMMS_EXPORT Model : public QObject // 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 d92717d3781..a0f97ce715e 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 f4abed91616..1d54538b63e 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 fec5b3855da..277c22596a6 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 e8c7d5f82b2..f2ad13ee99d 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 f2ad13ee99d..5ab9630b2e1 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 | 121 ++++++++++++++++++++++++++++++++------ 2 files changed, 117 insertions(+), 29 deletions(-) diff --git a/include/PluginBrowser.h b/include/PluginBrowser.h index f7c46db7293..3cc54c6e47a 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 @@ class PluginBrowser : public SideBarWidget PluginBrowser( QWidget * _parent ); virtual ~PluginBrowser() = default; -private: - QWidget * m_view; -}; - - +private slots: + void onFilterChanged( const QString & filter ); +private: + void addPlugins(); + void updateRootVisibility( int index ); + void updateRootVisibilities(); -class PluginDescList : public QWidget -{ - Q_OBJECT -public: - PluginDescList(QWidget* parent); + QWidget * m_view; + 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 27747bc5363..1e73b7cdcbf 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 ); + + 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 ); - view_layout->addWidget(hint); - view_layout->addWidget(scrollarea); + // 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(); } +void PluginBrowser::updateRootVisibility( int rootIndex ) +{ + QTreeWidgetItem * root = m_descTree->topLevelItem( rootIndex ); + root->setHidden( !root->childCount() ); +} -PluginDescList::PluginDescList(QWidget *parent) : - QWidget(parent) +void PluginBrowser::updateRootVisibilities() { - QVBoxLayout* layout = new QVBoxLayout(this); + 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 0673ee32d4b..c796bfa74c8 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 ) -