carla/source/frontend/pluginlist/pluginlistdialog.cpp

1851 lines
67 KiB
C++

/*
* Carla plugin host
* Copyright (C) 2011-2023 Filipe Coelho <falktx@falktx.com>
*
* 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 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.
*
* For a full copy of the GNU General Public License see the doc/GPL.txt file.
*/
#include "pluginlistdialog.hpp"
#ifdef __clang__
# pragma clang diagnostic push
# pragma clang diagnostic ignored "-Wdeprecated-copy-with-user-provided-copy"
# pragma clang diagnostic ignored "-Wdeprecated-register"
#elif defined(__GNUC__) && __GNUC__ >= 8
# pragma GCC diagnostic push
# pragma GCC diagnostic ignored "-Wclass-memaccess"
# pragma GCC diagnostic ignored "-Wdeprecated-copy"
#endif
#include "ui_pluginlistdialog.h"
#include <QtCore/QDir>
#include <QtCore/QFileInfo>
#include <QtCore/QList>
#ifdef __clang__
# pragma clang diagnostic pop
#elif defined(__GNUC__) && __GNUC__ >= 8
# pragma GCC diagnostic pop
#endif
#include "qcarlastring.hpp"
#include "qsafesettings.hpp"
#include "CarlaBackendUtils.hpp"
#include "CarlaFrontend.h"
#include "CarlaUtils.h"
#include "CarlaString.hpp"
#include <cstdlib>
#ifdef BUILDING_CARLA_OBS
extern "C" {
const char *get_carla_bin_path(void);
}
#endif
CARLA_BACKEND_USE_NAMESPACE
// --------------------------------------------------------------------------------------------------------------------
// Carla Settings keys
#define CARLA_KEY_PATHS_LADSPA "Paths/LADSPA"
#define CARLA_KEY_PATHS_DSSI "Paths/DSSI"
#define CARLA_KEY_PATHS_LV2 "Paths/LV2"
#define CARLA_KEY_PATHS_VST2 "Paths/VST2"
#define CARLA_KEY_PATHS_VST3 "Paths/VST3"
#define CARLA_KEY_PATHS_CLAP "Paths/CLAP"
#define CARLA_KEY_PATHS_SF2 "Paths/SF2"
#define CARLA_KEY_PATHS_SFZ "Paths/SFZ"
#define CARLA_KEY_PATHS_JSFX "Paths/JSFX"
// --------------------------------------------------------------------------------------------------------------------
// Carla Settings defaults
// --------------------------------------------------------------------------------------------------------------------
// utils
const char* getEnv(const char* const env, const char* const fallback)
{
if (const char* const value = std::getenv(env))
return value;
return fallback;
}
QCarlaString getTMP()
{
QCarlaString tmp;
if (const char* const envTMP = std::getenv("TMP"))
{
tmp = envTMP;
}
else
{
#ifdef CARLA_OS_WIN
qWarning("TMP variable not set");
#endif
tmp = QDir::tempPath();
}
if (!QDir(tmp).exists())
{
qWarning("TMP does not exist");
tmp = "/";
}
return tmp;
}
QCarlaString getHome()
{
QCarlaString home;
if (const char* const envHOME = std::getenv("HOME"))
{
home = envHOME;
}
else
{
#ifndef CARLA_OS_WIN
qWarning("HOME variable not set");
#endif
home = QDir::toNativeSeparators(QDir::homePath());
}
if (!QDir(home).exists())
{
qWarning("HOME does not exist");
home = getTMP();
}
return home;
}
// --------------------------------------------------------------------------------------------------------------------
// Default Plugin Folders (get)
struct DefaultPaths {
QCarlaString ladspa;
QCarlaString dssi;
QCarlaString lv2;
QCarlaString vst2;
QCarlaString vst3;
QCarlaString clap;
QCarlaString sf2;
QCarlaString sfz;
QCarlaString jsfx;
void init()
{
const QCarlaString HOME = getHome();
#if defined(CARLA_OS_WIN)
const char* const envAPPDATA = std::getenv("APPDATA");
const char* const envLOCALAPPDATA = getEnv("LOCALAPPDATA", envAPPDATA);
const char* const envPROGRAMFILES = std::getenv("PROGRAMFILES");
const char* const envPROGRAMFILESx86 = std::getenv("PROGRAMFILES(x86)");
const char* const envCOMMONPROGRAMFILES = std::getenv("COMMONPROGRAMFILES");
const char* const envCOMMONPROGRAMFILESx86 = std::getenv("COMMONPROGRAMFILES(x86)");
// Small integrity tests
if (envAPPDATA == nullptr)
{
qFatal("APPDATA variable not set, cannot continue");
abort();
}
if (envPROGRAMFILES == nullptr)
{
qFatal("PROGRAMFILES variable not set, cannot continue");
abort();
}
if (envCOMMONPROGRAMFILES == nullptr)
{
qFatal("COMMONPROGRAMFILES variable not set, cannot continue");
abort();
}
const QCarlaString APPDATA(envAPPDATA);
const QCarlaString LOCALAPPDATA(envLOCALAPPDATA);
const QCarlaString PROGRAMFILES(envPROGRAMFILES);
const QCarlaString COMMONPROGRAMFILES(envCOMMONPROGRAMFILES);
ladspa = APPDATA + "\\LADSPA";
ladspa += ";" + PROGRAMFILES + "\\LADSPA";
dssi = APPDATA + "\\DSSI";
dssi += ";" + PROGRAMFILES + "\\DSSI";
lv2 = APPDATA + "\\LV2";
lv2 += ";" + COMMONPROGRAMFILES + "\\LV2";
vst2 = PROGRAMFILES + "\\VstPlugins";
vst2 += ";" + PROGRAMFILES + "\\Steinberg\\VstPlugins";
jsfx = APPDATA + "\\REAPER\\Effects";
//jsfx += ";" + PROGRAMFILES + "\\REAPER\\InstallData\\Effects";
#ifdef CARLA_OS_WIN64
vst2 += ";" + COMMONPROGRAMFILES + "\\VST2";
#endif
vst3 = COMMONPROGRAMFILES + "\\VST3";
vst3 += ";" + LOCALAPPDATA + "\\Programs\\Common\\VST3";
clap = COMMONPROGRAMFILES + "\\CLAP";
clap += ";" + LOCALAPPDATA + "\\Programs\\Common\\CLAP";
sf2 = APPDATA + "\\SF2";
sfz = APPDATA + "\\SFZ";
if (envPROGRAMFILESx86 != nullptr)
{
const QCarlaString PROGRAMFILESx86(envPROGRAMFILESx86);
ladspa += ";" + PROGRAMFILESx86 + "\\LADSPA";
dssi += ";" + PROGRAMFILESx86 + "\\DSSI";
vst2 += ";" + PROGRAMFILESx86 + "\\VstPlugins";
vst2 += ";" + PROGRAMFILESx86 + "\\Steinberg\\VstPlugins";
//jsfx += ";" + PROGRAMFILESx86 + "\\REAPER\\InstallData\\Effects";
}
if (envCOMMONPROGRAMFILESx86 != nullptr)
{
const QCarlaString COMMONPROGRAMFILESx86(envCOMMONPROGRAMFILESx86);
vst3 += COMMONPROGRAMFILESx86 + "\\VST3";
clap += COMMONPROGRAMFILESx86 + "\\CLAP";
}
#elif defined(CARLA_OS_HAIKU)
ladspa = HOME + "/.ladspa";
ladspa += ":/system/add-ons/media/ladspaplugins";
ladspa += ":/system/lib/ladspa";
dssi = HOME + "/.dssi";
dssi += ":/system/add-ons/media/dssiplugins";
dssi += ":/system/lib/dssi";
lv2 = HOME + "/.lv2";
lv2 += ":/system/add-ons/media/lv2plugins";
vst2 = HOME + "/.vst";
vst2 += ":/system/add-ons/media/vstplugins";
vst3 = HOME + "/.vst3";
vst3 += ":/system/add-ons/media/vst3plugins";
clap = HOME + "/.clap";
clap += ":/system/add-ons/media/clapplugins";
#elif defined(CARLA_OS_MAC)
ladspa = HOME + "/Library/Audio/Plug-Ins/LADSPA";
ladspa += ":/Library/Audio/Plug-Ins/LADSPA";
dssi = HOME + "/Library/Audio/Plug-Ins/DSSI";
dssi += ":/Library/Audio/Plug-Ins/DSSI";
lv2 = HOME + "/Library/Audio/Plug-Ins/LV2";
lv2 += ":/Library/Audio/Plug-Ins/LV2";
vst2 = HOME + "/Library/Audio/Plug-Ins/VST";
vst2 += ":/Library/Audio/Plug-Ins/VST";
vst3 = HOME + "/Library/Audio/Plug-Ins/VST3";
vst3 += ":/Library/Audio/Plug-Ins/VST3";
clap = HOME + "/Library/Audio/Plug-Ins/CLAP";
clap += ":/Library/Audio/Plug-Ins/CLAP";
jsfx = HOME + "/Library/Application Support/REAPER/Effects";
//jsfx += ":/Applications/REAPER.app/Contents/InstallFiles/Effects";
#else
const QCarlaString CONFIG_HOME(getEnv("XDG_CONFIG_HOME", (HOME + "/.config").toUtf8()));
ladspa = HOME + "/.ladspa";
ladspa += ":/usr/lib/ladspa";
ladspa += ":/usr/local/lib/ladspa";
dssi = HOME + "/.dssi";
dssi += ":/usr/lib/dssi";
dssi += ":/usr/local/lib/dssi";
lv2 = HOME + "/.lv2";
lv2 += ":/usr/lib/lv2";
lv2 += ":/usr/local/lib/lv2";
vst2 = HOME + "/.vst";
vst2 += ":/usr/lib/vst";
vst2 += ":/usr/local/lib/vst";
vst2 += HOME + "/.lxvst";
vst2 += ":/usr/lib/lxvst";
vst2 += ":/usr/local/lib/lxvst";
vst3 = HOME + "/.vst3";
vst3 += ":/usr/lib/vst3";
vst3 += ":/usr/local/lib/vst3";
clap = HOME + "/.clap";
clap += ":/usr/lib/clap";
clap += ":/usr/local/lib/clap";
sf2 = HOME + "/.sounds/sf2";
sf2 += ":" + HOME + "/.sounds/sf3";
sf2 += ":/usr/share/sounds/sf2";
sf2 += ":/usr/share/sounds/sf3";
sf2 += ":/usr/share/soundfonts";
sfz = HOME + "/.sounds/sfz";
sfz += ":/usr/share/sounds/sfz";
jsfx = CONFIG_HOME + "/REAPER/Effects";
//jsfx += ":" + "/opt/REAPER/InstallData/Effects";
#endif
#ifndef CARLA_OS_WIN
QCarlaString winePrefix;
if (const char* const envWINEPREFIX = std::getenv("WINEPREFIX"))
winePrefix = envWINEPREFIX;
if (winePrefix.isEmpty())
winePrefix = HOME + "/.wine";
if (QDir(winePrefix).exists())
{
vst2 += ":" + winePrefix + "/drive_c/Program Files/VstPlugins";
vst3 += ":" + winePrefix + "/drive_c/Program Files/Common Files/VST3";
clap += ":" + winePrefix + "/drive_c/Program Files/Common Files/CLAP";
#ifdef CARLA_OS_64BIT
if (QDir(winePrefix + "/drive_c/Program Files (x86)").exists())
{
vst2 += ":" + winePrefix + "/drive_c/Program Files (x86)/VstPlugins";
vst3 += ":" + winePrefix + "/drive_c/Program Files (x86)/Common Files/VST3";
clap += ":" + winePrefix + "/drive_c/Program Files (x86)/Common Files/CLAP";
}
#endif
}
#endif
}
void loadFromEnv()
{
if (const char* const envLADSPA = std::getenv("LADSPA_PATH"))
ladspa = envLADSPA;
if (const char* const envDSSI = std::getenv("DSSI_PATH"))
dssi = envDSSI;
if (const char* const envLV2 = std::getenv("LV2_PATH"))
lv2 = envLV2;
if (const char* const envVST = std::getenv("VST_PATH"))
vst2 = envVST;
if (const char* const envVST3 = std::getenv("VST3_PATH"))
vst3 = envVST3;
if (const char* const envCLAP = std::getenv("CLAP_PATH"))
clap = envCLAP;
if (const char* const envSF2 = std::getenv("SF2_PATH"))
sf2 = envSF2;
if (const char* const envSFZ = std::getenv("SFZ_PATH"))
sfz = envSFZ;
if (const char* const envJSFX = std::getenv("JSFX_PATH"))
jsfx = envJSFX;
}
};
// --------------------------------------------------------------------------------------------------------------------
PluginInfo checkPluginCached(const CarlaCachedPluginInfo* const desc, const PluginType ptype)
{
PluginInfo pinfo = {};
pinfo.API = PLUGIN_QUERY_API_VERSION;
pinfo.build = BINARY_NATIVE;
pinfo.type = ptype;
pinfo.hints = desc->hints;
pinfo.name = desc->name;
pinfo.label = desc->label;
pinfo.maker = desc->maker;
pinfo.category = getPluginCategoryAsString(desc->category);
pinfo.audioIns = desc->audioIns;
pinfo.audioOuts = desc->audioOuts;
pinfo.cvIns = desc->cvIns;
pinfo.cvOuts = desc->cvOuts;
pinfo.midiIns = desc->midiIns;
pinfo.midiOuts = desc->midiOuts;
pinfo.parametersIns = desc->parameterIns;
pinfo.parametersOuts = desc->parameterOuts;
switch (ptype)
{
case PLUGIN_LV2:
{
const QString label(desc->label);
pinfo.filename = label.split(CARLA_OS_SEP).first();
pinfo.label = label.section(CARLA_OS_SEP, 1);
}
break;
case PLUGIN_SFZ:
pinfo.filename = pinfo.label;
pinfo.label = pinfo.name;
break;
default:
break;
}
return pinfo;
}
// --------------------------------------------------------------------------------------------------------------------
// Backwards-compatible horizontalAdvance/width call, depending on Qt version
static inline
int fontMetricsHorizontalAdvance(const QFontMetrics& fontMetrics, const QString& string)
{
#if QT_VERSION >= 0x50b00
return fontMetrics.horizontalAdvance(string);
#else
return fontMetrics.width(string);
#endif
}
// --------------------------------------------------------------------------------------------------------------------
typedef QList<PluginInfo> QPluginInfoList;
class QSafePluginListSettings : public QSafeSettings
{
public:
inline QSafePluginListSettings()
: QSafeSettings() {}
inline QSafePluginListSettings(const QString& organization, const QString& application)
: QSafeSettings(organization, application) {}
QPluginInfoList valuePluginInfoList(const QString& key) const
{
/*
QVariant var(value(key, {}));
if (!var.isNull() && var.convert(QVariant::List) && var.isValid())
{
QList<QVariant> varVariant(var.toList());
QPluginInfoList varReal;
varReal.reserve(varVariant.size());
for (QVariant v : varVariant)
{
CARLA_SAFE_ASSERT_BREAK(!v.isNull());
CARLA_SAFE_ASSERT_BREAK(v.convert(QVariant::UserType));
CARLA_SAFE_ASSERT_BREAK(v.isValid());
// varReal.append(v.toU);
}
}
*/
return {};
// TODO
(void)key;
}
void setValue(const QString& key, const uint value)
{
QSafeSettings::setValue(key, value);
}
void setValue(const QString& key, const QPluginInfoList& value)
{
return;
// TODO
(void)key;
(void)value;
}
};
// --------------------------------------------------------------------------------------------------------------------
template <class T> inline T static_cast_void(const void* ptr)
{
return static_cast<T>(ptr);
}
struct PluginInfoBytes {
uint API;
uint build;
uint type;
uint hints;
uint64_t uniqueId;
uint audioIns;
uint audioOuts;
uint cvIns;
uint cvOuts;
uint midiIns;
uint midiOuts;
uint parametersIns;
uint parametersOuts;
};
QVariant asVariant(const PluginInfo& plugin)
{
const PluginInfoBytes data = {
plugin.API,
static_cast<uint>(plugin.build),
static_cast<uint>(plugin.type),
plugin.hints,
plugin.uniqueId,
plugin.audioIns,
plugin.audioOuts,
plugin.cvIns,
plugin.cvOuts,
plugin.midiIns,
plugin.midiOuts,
plugin.parametersIns,
plugin.parametersOuts
};
QByteArray qdata(static_cast_void<const char*>(&data), sizeof(data));
{
const QByteArray qcategory(plugin.category.toUtf8());
qdata += qcategory.constData();
qdata += '\0';
}
{
const QByteArray qfilename(plugin.filename.toUtf8());
qdata += qfilename.constData();
qdata += '\0';
}
{
const QByteArray qname(plugin.name.toUtf8());
qdata += qname.constData();
qdata += '\0';
}
{
const QByteArray qlabel(plugin.label.toUtf8());
qdata += qlabel.constData();
qdata += '\0';
}
{
const QByteArray qmaker(plugin.maker.toUtf8());
qdata += qmaker.constData();
qdata += '\0';
}
QVariant var;
var.setValue(qdata);
return var;
}
PluginInfo asPluginInfo(const QVariant& var)
{
const QByteArray qdata(var.toByteArray());
CARLA_SAFE_ASSERT_RETURN((size_t)qdata.size() >= sizeof(PluginInfoBytes) + sizeof(char)*5, {});
const PluginInfoBytes* const data = static_cast_void<const PluginInfoBytes*>(qdata.constData());
PluginInfo plugin = {
data->API,
static_cast<BinaryType>(data->build),
static_cast<PluginType>(data->type),
data->hints,
{}, {}, {}, {}, {},
data->uniqueId,
data->audioIns,
data->audioOuts,
data->cvIns,
data->cvOuts,
data->midiIns,
data->midiOuts,
data->parametersIns,
data->parametersOuts
};
const char* sdata = static_cast_void<const char*>(data) + sizeof(PluginInfoBytes);
plugin.category = QString::fromUtf8(sdata);
sdata += plugin.category.size() + 1;
plugin.filename = QString::fromUtf8(sdata);
sdata += plugin.filename.size() + 1;
plugin.name = QString::fromUtf8(sdata);
sdata += plugin.name.size() + 1;
plugin.label = QString::fromUtf8(sdata);
sdata += plugin.label.size() + 1;
plugin.maker = QString::fromUtf8(sdata);
sdata += plugin.maker.size() + 1;
return plugin;
}
// --------------------------------------------------------------------------------------------------------------------
// Plugin List Dialog
struct PluginListDialog::Self {
enum TableIndex {
TABLEWIDGET_ITEM_FAVORITE,
TABLEWIDGET_ITEM_NAME,
TABLEWIDGET_ITEM_LABEL,
TABLEWIDGET_ITEM_MAKER,
TABLEWIDGET_ITEM_BINARY,
};
// To be changed by parent
bool hasLoadedLv2Plugins = false;
Ui_PluginListDialog ui;
HostSettings hostSettings = {};
struct Discovery {
CarlaPluginDiscoveryHandle handle = nullptr;
PluginType ptype = PLUGIN_NONE;
DefaultPaths paths;
QString tool;
Discovery()
{
paths.init();
paths.loadFromEnv();
#ifdef BUILDING_CARLA_OBS
tool = QString::fromUtf8(get_carla_bin_path());
tool += CARLA_OS_SEP_STR "carla-discovery-native";
#ifdef CARLA_OS_WIN
tool += ".exe";
#endif
#else
tool = "/usr/lib/carla/carla-discovery-native";
#endif
}
~Discovery()
{
if (handle != nullptr)
carla_plugin_discovery_stop(handle);
}
} fDiscovery;
int fLastTableWidgetIndex = 0;
std::vector<PluginInfo> fPluginsInternal;
std::vector<PluginInfo> fPluginsLADSPA;
std::vector<PluginInfo> fPluginsDSSI;
std::vector<PluginInfo> fPluginsLV2;
std::vector<PluginInfo> fPluginsVST2;
std::vector<PluginInfo> fPluginsVST3;
std::vector<PluginInfo> fPluginsCLAP;
#ifdef CARLA_OS_MAC
std::vector<PluginInfo> fPluginsAU;
#endif
std::vector<PluginInfo> fPluginsJSFX;
std::vector<PluginInfo> fPluginsSF2;
std::vector<PluginInfo> fPluginsSFZ;
int fTimerId = 0;
PluginInfo fRetPlugin;
QWidget* const fRealParent;
QStringList fFavoritePlugins;
bool fFavoritePluginsChanged = false;
const QString fTrYes;
const QString fTrNo;
const QString fTrNative;
Self(QWidget* const parent)
: fRealParent(parent),
fTrYes(tr("Yes")),
fTrNo(tr("No")),
fTrNative(tr("Native")) {}
static Self& create(QWidget* const parent)
{
Self* const self = new Self(parent);
return *self;
}
inline QString tr(const char* const txt)
{
return fRealParent != nullptr ? fRealParent->tr(txt) : QString::fromUtf8(txt);
}
void createFavoritePluginDict()
{
#if 0
return {
'name' : plugin['name'],
'build' : plugin['build'],
'type' : plugin['type'],
'filename': plugin['filename'],
'label' : plugin['label'],
'uniqueId': plugin['uniqueId'],
}
#endif
}
void checkFilters()
{
const QCarlaString text = ui.lineEdit->text().toLower();
const bool hideEffects = !ui.ch_effects->isChecked();
const bool hideInstruments = !ui.ch_instruments->isChecked();
const bool hideMidi = !ui.ch_midi->isChecked();
const bool hideOther = !ui.ch_other->isChecked();
const bool hideInternal = !ui.ch_internal->isChecked();
const bool hideLadspa = !ui.ch_ladspa->isChecked();
const bool hideDssi = !ui.ch_dssi->isChecked();
const bool hideLV2 = !ui.ch_lv2->isChecked();
const bool hideVST2 = !ui.ch_vst->isChecked();
const bool hideVST3 = !ui.ch_vst3->isChecked();
const bool hideCLAP = !ui.ch_clap->isChecked();
const bool hideAU = !ui.ch_au->isChecked();
const bool hideJSFX = !ui.ch_jsfx->isChecked();
const bool hideKits = !ui.ch_kits->isChecked();
const bool hideNative = !ui.ch_native->isChecked();
const bool hideBridged = !ui.ch_bridged->isChecked();
const bool hideBridgedWine = !ui.ch_bridged_wine->isChecked();
#if 0
const bool hideNonFavs = ui.ch_favorites->isChecked();
#endif
const bool hideNonRtSafe = ui.ch_rtsafe->isChecked();
const bool hideNonCV = ui.ch_cv->isChecked();
const bool hideNonGui = ui.ch_gui->isChecked();
const bool hideNonIDisp = ui.ch_inline_display->isChecked();
const bool hideNonStereo = ui.ch_stereo->isChecked();
#if 0
if HAIKU or LINUX or MACOS:
nativeBins = [BINARY_POSIX32, BINARY_POSIX64]
wineBins = [BINARY_WIN32, BINARY_WIN64]
elif WINDOWS:
nativeBins = [BINARY_WIN32, BINARY_WIN64]
wineBins = []
else:
nativeBins = []
wineBins = []
#endif
for (int i=0, c=ui.tableWidget->rowCount(); i<c; ++i)
{
const PluginInfo plugin = asPluginInfo(ui.tableWidget->item(i, TABLEWIDGET_ITEM_NAME)->data(Qt::UserRole+1));
const QString ptext = ui.tableWidget->item(i, TABLEWIDGET_ITEM_NAME)->data(Qt::UserRole+2).toString();
const uint aIns = plugin.audioIns;
const uint aOuts = plugin.audioOuts;
const uint cvIns = plugin.cvIns;
const uint cvOuts = plugin.cvOuts;
const uint mIns = plugin.midiIns;
const uint mOuts = plugin.midiOuts;
const uint phints = plugin.hints;
const PluginType ptype = plugin.type;
const QString categ = plugin.category;
const bool isSynth = phints & PLUGIN_IS_SYNTH;
const bool isEffect = aIns > 0 && aOuts > 0 && !isSynth;
const bool isMidi = aIns == 0 && aOuts == 0 && mIns > 0 && mOuts > 0;
const bool isKit = ptype == PLUGIN_SF2 || ptype == PLUGIN_SFZ;
const bool isOther = !(isEffect || isSynth || isMidi || isKit);
const bool isNative = plugin.build == BINARY_NATIVE;
const bool isRtSafe = phints & PLUGIN_IS_RTSAFE;
const bool isStereo = (aIns == 2 && aOuts == 2) || (isSynth && aOuts == 2);
const bool hasCV = cvIns + cvOuts > 0;
const bool hasGui = phints & PLUGIN_HAS_CUSTOM_UI;
const bool hasIDisp = phints & PLUGIN_HAS_INLINE_DISPLAY;
#if 0
const bool isBridged = bool(not isNative and plugin.build in nativeBins);
const bool isBridgedWine = bool(not isNative and plugin.build in wineBins);
#else
const bool isBridged = false;
const bool isBridgedWine = false;
#endif
const auto hasText = [text, ptext]() {
const QStringList textSplit = text.strip().split(' ');
for (const QString& t : textSplit)
if (ptext.contains(t))
return true;
return false;
};
/**/ if (hideEffects && isEffect)
ui.tableWidget->hideRow(i);
else if (hideInstruments && isSynth)
ui.tableWidget->hideRow(i);
else if (hideMidi && isMidi)
ui.tableWidget->hideRow(i);
else if (hideOther && isOther)
ui.tableWidget->hideRow(i);
else if (hideKits && isKit)
ui.tableWidget->hideRow(i);
else if (hideInternal && ptype == PLUGIN_INTERNAL)
ui.tableWidget->hideRow(i);
else if (hideLadspa && ptype == PLUGIN_LADSPA)
ui.tableWidget->hideRow(i);
else if (hideDssi && ptype == PLUGIN_DSSI)
ui.tableWidget->hideRow(i);
else if (hideLV2 && ptype == PLUGIN_LV2)
ui.tableWidget->hideRow(i);
else if (hideVST2 && ptype == PLUGIN_VST2)
ui.tableWidget->hideRow(i);
else if (hideVST3 && ptype == PLUGIN_VST3)
ui.tableWidget->hideRow(i);
else if (hideCLAP && ptype == PLUGIN_CLAP)
ui.tableWidget->hideRow(i);
else if (hideAU && ptype == PLUGIN_AU)
ui.tableWidget->hideRow(i);
else if (hideJSFX && ptype == PLUGIN_JSFX)
ui.tableWidget->hideRow(i);
else if (hideNative && isNative)
ui.tableWidget->hideRow(i);
else if (hideBridged && isBridged)
ui.tableWidget->hideRow(i);
else if (hideBridgedWine && isBridgedWine)
ui.tableWidget->hideRow(i);
else if (hideNonRtSafe && not isRtSafe)
ui.tableWidget->hideRow(i);
else if (hideNonCV && not hasCV)
ui.tableWidget->hideRow(i);
else if (hideNonGui && not hasGui)
ui.tableWidget->hideRow(i);
else if (hideNonIDisp && not hasIDisp)
ui.tableWidget->hideRow(i);
else if (hideNonStereo && not isStereo)
ui.tableWidget->hideRow(i);
else if (text.isNotEmpty() && ! hasText())
ui.tableWidget->hideRow(i);
#if 0
else if (hideNonFavs && _createFavoritePluginDict(plugin) not in fFavoritePlugins)
ui.tableWidget->hideRow(i);
#endif
else if (ui.ch_cat_all->isChecked() or
(ui.ch_cat_delay->isChecked() && categ == "delay") or
(ui.ch_cat_distortion->isChecked() && categ == "distortion") or
(ui.ch_cat_dynamics->isChecked() && categ == "dynamics") or
(ui.ch_cat_eq->isChecked() && categ == "eq") or
(ui.ch_cat_filter->isChecked() && categ == "filter") or
(ui.ch_cat_modulator->isChecked() && categ == "modulator") or
(ui.ch_cat_synth->isChecked() && categ == "synth") or
(ui.ch_cat_utility->isChecked() && categ == "utility") or
(ui.ch_cat_other->isChecked() && categ == "other"))
ui.tableWidget->showRow(i);
else
ui.tableWidget->hideRow(i);
}
}
bool addPlugin(const PluginInfo& pinfo)
{
switch (pinfo.type)
{
case PLUGIN_INTERNAL: fPluginsInternal.push_back(pinfo); return true;
case PLUGIN_LADSPA: fPluginsLADSPA.push_back(pinfo); return true;
case PLUGIN_DSSI: fPluginsDSSI.push_back(pinfo); return true;
case PLUGIN_LV2: fPluginsLV2.push_back(pinfo); return true;
case PLUGIN_VST2: fPluginsVST2.push_back(pinfo); return true;
case PLUGIN_VST3: fPluginsVST3.push_back(pinfo); return true;
case PLUGIN_CLAP: fPluginsCLAP.push_back(pinfo); return true;
#ifdef CARLA_OS_MAC
case PLUGIN_AU: fPluginsAU.push_back(pinfo); return true;
#endif
case PLUGIN_JSFX: fPluginsJSFX.push_back(pinfo); return true;
case PLUGIN_SF2: fPluginsSF2.push_back(pinfo); return true;
case PLUGIN_SFZ: fPluginsSFZ.push_back(pinfo); return true;
default: return false;
}
}
void discoveryCallback(const CarlaPluginDiscoveryInfo* const info, const char* const sha1sum)
{
if (info == nullptr)
{
if (sha1sum != nullptr)
{
QSafeSettings settings("falkTX", "CarlaDatabase2");
settings.setValue(QString("PluginCache/%1").arg(sha1sum), QByteArray());
}
return;
}
#ifdef BUILDING_CARLA_OBS
if (info->io.cvIns != 0 || info->io.cvOuts != 0)
{
carla_stdout("discoveryCallback %p %s - ignored, has CV", info, info->filename);
return;
}
if (info->io.audioIns > 8 || info->io.audioOuts > 8)
{
carla_stdout("discoveryCallback %p %s - ignored, has > 8 audio IO", info, info->filename);
return;
}
#endif
const PluginInfo pinfo = {
PLUGIN_QUERY_API_VERSION,
info->btype,
info->ptype,
info->metadata.hints,
getPluginCategoryAsString(info->metadata.category),
QString::fromUtf8(info->filename),
QString::fromUtf8(info->metadata.name),
QString::fromUtf8(info->label),
QString::fromUtf8(info->metadata.maker),
info->uniqueId,
info->io.audioIns,
info->io.audioOuts,
info->io.cvIns,
info->io.cvOuts,
info->io.midiIns,
info->io.midiOuts,
info->io.parameterIns,
info->io.parameterOuts,
};
if (sha1sum != nullptr)
{
QSafeSettings settings("falkTX", "CarlaDatabase2");
settings.setValue(QString("PluginCache/%1").arg(sha1sum), asVariant(pinfo));
}
addPlugin(pinfo);
}
static void _discoveryCallback(void* const ptr, const CarlaPluginDiscoveryInfo* const info, const char* const sha1sum)
{
static_cast<PluginListDialog::Self*>(ptr)->discoveryCallback(info, sha1sum);
}
bool checkCacheCallback(const char* const filename, const char* const sha1sum)
{
if (sha1sum == nullptr)
return false;
const QString key = QString("PluginCache/%1").arg(sha1sum);
const QSafeSettings settings("falkTX", "CarlaDatabase2");
if (! settings.contains(key))
return false;
const QByteArray data(settings.valueByteArray(key));
if (data.isEmpty())
return true;
return addPlugin(asPluginInfo(data));
}
static bool _checkCacheCallback(void* const ptr, const char* const filename, const char* const sha1sum)
{
return static_cast<PluginListDialog::Self*>(ptr)->checkCacheCallback(filename, sha1sum);
}
void addPluginToTable(const PluginInfo& pinfo)
{
// PluginInfo plugincopy = plugin;
// switch (ptype)
// {
// case PLUGIN_INTERNAL:
// case PLUGIN_LV2:
// case PLUGIN_SF2:
// case PLUGIN_SFZ:
// case PLUGIN_JSFX:
// plugincopy.build = BINARY_NATIVE;
// break;
// default:
// break;
// }
const int index = fLastTableWidgetIndex++;
#if 0
const bool isFav = bool(self._createFavoritePluginDict(plugin) in self.fFavoritePlugins)
#else
const bool isFav = false;
#endif
QTableWidgetItem* const itemFav = new QTableWidgetItem;
itemFav->setCheckState(isFav ? Qt::Checked : Qt::Unchecked);
itemFav->setText(isFav ? " " : " ");
const QString pluginText = (pinfo.name + pinfo.label + pinfo.maker + pinfo.filename).toLower();
ui.tableWidget->setItem(index, TABLEWIDGET_ITEM_FAVORITE, itemFav);
ui.tableWidget->setItem(index, TABLEWIDGET_ITEM_NAME, new QTableWidgetItem(pinfo.name));
ui.tableWidget->setItem(index, TABLEWIDGET_ITEM_LABEL, new QTableWidgetItem(pinfo.label));
ui.tableWidget->setItem(index, TABLEWIDGET_ITEM_MAKER, new QTableWidgetItem(pinfo.maker));
ui.tableWidget->setItem(index, TABLEWIDGET_ITEM_BINARY, new QTableWidgetItem(QFileInfo(pinfo.filename).fileName()));
QTableWidgetItem* const itemName = ui.tableWidget->item(index, TABLEWIDGET_ITEM_NAME);
itemName->setData(Qt::UserRole+1, asVariant(pinfo));
itemName->setData(Qt::UserRole+2, pluginText);
}
bool idle()
{
// discovery in progress, keep it going
if (fDiscovery.handle != nullptr)
{
if (! carla_plugin_discovery_idle(fDiscovery.handle))
{
carla_plugin_discovery_stop(fDiscovery.handle);
fDiscovery.handle = nullptr;
}
return false;
}
// TESTING
reAddPlugins();
// start next discovery
QString path;
switch (fDiscovery.ptype)
{
case PLUGIN_NONE:
ui.label->setText(tr("Discovering internal plugins..."));
fDiscovery.ptype = PLUGIN_INTERNAL;
break;
case PLUGIN_INTERNAL:
ui.label->setText(tr("Discovering LADSPA plugins..."));
path = fDiscovery.paths.ladspa;
fDiscovery.ptype = PLUGIN_LADSPA;
break;
case PLUGIN_LADSPA:
ui.label->setText(tr("Discovering DSSI plugins..."));
path = fDiscovery.paths.dssi;
fDiscovery.ptype = PLUGIN_DSSI;
break;
case PLUGIN_DSSI:
ui.label->setText(tr("Discovering LV2 plugins..."));
path = fDiscovery.paths.lv2;
fDiscovery.ptype = PLUGIN_LV2;
break;
case PLUGIN_LV2:
ui.label->setText(tr("Discovering VST2 plugins..."));
path = fDiscovery.paths.vst2;
fDiscovery.ptype = PLUGIN_VST2;
break;
case PLUGIN_VST2:
ui.label->setText(tr("Discovering VST3 plugins..."));
path = fDiscovery.paths.vst3;
fDiscovery.ptype = PLUGIN_VST3;
break;
case PLUGIN_VST3:
ui.label->setText(tr("Discovering CLAP plugins..."));
path = fDiscovery.paths.clap;
fDiscovery.ptype = PLUGIN_CLAP;
break;
case PLUGIN_CLAP:
#ifdef CARLA_OS_MAC
ui.label->setText(tr("Discovering AU plugins..."));
fDiscovery.ptype = PLUGIN_AU;
break;
case PLUGIN_AU:
#endif
if (fDiscovery.paths.jsfx.isNotEmpty())
{
ui.label->setText(tr("Discovering JSFX plugins..."));
path = fDiscovery.paths.jsfx;
fDiscovery.ptype = PLUGIN_JSFX;
break;
}
// fall-through
case PLUGIN_JSFX:
ui.label->setText(tr("Discovering SF2 kits..."));
path = fDiscovery.paths.sf2;
fDiscovery.ptype = PLUGIN_SF2;
break;
case PLUGIN_SF2:
if (fDiscovery.paths.sfz.isNotEmpty())
{
ui.label->setText(tr("Discovering SFZ kits..."));
path = fDiscovery.paths.sfz;
fDiscovery.ptype = PLUGIN_SFZ;
break;
}
// fall-through
case PLUGIN_SFZ:
// the end
reAddPlugins();
return true;
}
fDiscovery.handle = carla_plugin_discovery_start(fDiscovery.tool.toUtf8().constData(),
fDiscovery.ptype,
path.toUtf8().constData(),
_discoveryCallback,
_checkCacheCallback,
this);
CARLA_SAFE_ASSERT_RETURN(fDiscovery.handle != nullptr, false);
return false;
}
void reAddPlugins()
{
// ------------------------------------------------------------------------------------------------------------
// count plugins first, so we can create rows in advance
ui.tableWidget->setSortingEnabled(false);
ui.tableWidget->clearContents();
ui.tableWidget->setRowCount(fPluginsInternal.size() +
fPluginsLADSPA.size() +
fPluginsDSSI.size() +
fPluginsLV2.size() +
fPluginsVST2.size() +
fPluginsVST3.size() +
fPluginsCLAP.size() +
#ifdef CARLA_OS_MAC
fPluginsAU.size() +
#endif
fPluginsJSFX.size() +
fPluginsSF2.size() +
fPluginsSFZ.size());
constexpr const char* const txt = "Have %1 Internal, %2 LADSPA, %3 DSSI, %4 LV2, %5 VST2, %6 VST3, %7 CLAP"
#ifdef CARLA_OS_MAC
", %8 AudioUnit and %9 JSFX plugins, plus %10 Sound Kits"
#endif
" and %8 JSFX plugins, plus %9 Sound Kits";
ui.label->setText(fRealParent->tr(txt)
.arg(QString::number(fPluginsInternal.size()))
.arg(QString::number(fPluginsLADSPA.size()))
.arg(QString::number(fPluginsDSSI.size()))
.arg(QString::number(fPluginsLV2.size()))
.arg(QString::number(fPluginsVST2.size()))
.arg(QString::number(fPluginsVST3.size()))
.arg(QString::number(fPluginsCLAP.size()))
#ifdef CARLA_OS_MAC
.arg(QString::number(fPluginsAU.size()))
#endif
.arg(QString::number(fPluginsJSFX.size()))
.arg(QString::number(fPluginsSF2.size() + fPluginsSFZ.size()))
);
// ------------------------------------------------------------------------------------------------------------
// now add all plugins to the table
fLastTableWidgetIndex = 0;
for (const PluginInfo& plugin : fPluginsInternal)
addPluginToTable(plugin);
for (const PluginInfo& plugin : fPluginsLADSPA)
addPluginToTable(plugin);
for (const PluginInfo& plugin : fPluginsDSSI)
addPluginToTable(plugin);
for (const PluginInfo& plugin : fPluginsLV2)
addPluginToTable(plugin);
for (const PluginInfo& plugin : fPluginsVST2)
addPluginToTable(plugin);
for (const PluginInfo& plugin : fPluginsVST3)
addPluginToTable(plugin);
for (const PluginInfo& plugin : fPluginsCLAP)
addPluginToTable(plugin);
#ifdef CARLA_OS_MAC
for (const PluginInfo& plugin : fPluginsAU)
addPluginToTable(plugin);
#endif
for (const PluginInfo& plugin : fPluginsJSFX)
addPluginToTable(plugin);
for (const PluginInfo& plugin : fPluginsSF2)
addPluginToTable(plugin);
for (const PluginInfo& plugin : fPluginsSFZ)
addPluginToTable(plugin);
CARLA_SAFE_ASSERT_INT2(fLastTableWidgetIndex == ui.tableWidget->rowCount(),
fLastTableWidgetIndex, ui.tableWidget->rowCount());
// ------------------------------------------------------------------------------------------------------------
ui.tableWidget->setSortingEnabled(true);
checkFilters();
checkPlugin(ui.tableWidget->currentRow());
}
void checkPlugin(const int row)
{
if (row >= 0)
{
ui.b_add->setEnabled(true);
PluginInfo plugin = asPluginInfo(ui.tableWidget->item(row, TABLEWIDGET_ITEM_NAME)->data(Qt::UserRole+1));
const bool isSynth = plugin.hints & PLUGIN_IS_SYNTH;
const bool isEffect = plugin.audioIns > 0 && plugin.audioOuts > 0 && !isSynth;
const bool isMidi = plugin.audioIns == 0 &&
plugin.audioOuts == 0 &&
plugin.midiIns > 0 && plugin.midiOuts > 0;
// const bool isKit = plugin['type'] in (PLUGIN_SF2, PLUGIN_SFZ);
// const bool isOther = ! (isEffect || isSynth || isMidi || isKit);
QString ptype;
/**/ if (isSynth)
ptype = "Instrument";
else if (isEffect)
ptype = "Effect";
else if (isMidi)
ptype = "MIDI Plugin";
else
ptype = "Other";
QString parch;
/**/ if (plugin.build == BINARY_NATIVE)
parch = fTrNative;
else if (plugin.build == BINARY_POSIX32)
parch = "posix32";
else if (plugin.build == BINARY_POSIX64)
parch = "posix64";
else if (plugin.build == BINARY_WIN32)
parch = "win32";
else if (plugin.build == BINARY_WIN64)
parch = "win64";
else if (plugin.build == BINARY_OTHER)
parch = tr("Other");
else if (plugin.build == BINARY_WIN32)
parch = tr("Unknown");
ui.l_format->setText(getPluginTypeAsString(plugin.type));
ui.l_type->setText(ptype);
ui.l_arch->setText(parch);
ui.l_id->setText(QString::number(plugin.uniqueId));
ui.l_ains->setText(QString::number(plugin.audioIns));
ui.l_aouts->setText(QString::number(plugin.audioOuts));
ui.l_cvins->setText(QString::number(plugin.cvIns));
ui.l_cvouts->setText(QString::number(plugin.cvOuts));
ui.l_mins->setText(QString::number(plugin.midiIns));
ui.l_mouts->setText(QString::number(plugin.midiOuts));
ui.l_pins->setText(QString::number(plugin.parametersIns));
ui.l_pouts->setText(QString::number(plugin.parametersOuts));
ui.l_gui->setText(plugin.hints & PLUGIN_HAS_CUSTOM_UI ? fTrYes : fTrNo);
ui.l_idisp->setText(plugin.hints & PLUGIN_HAS_INLINE_DISPLAY ? fTrYes : fTrNo);
ui.l_bridged->setText(plugin.hints & PLUGIN_IS_BRIDGE ? fTrYes : fTrNo);
ui.l_synth->setText(isSynth ? fTrYes : fTrNo);
}
else
{
ui.b_add->setEnabled(false);
ui.l_format->setText("---");
ui.l_type->setText("---");
ui.l_arch->setText("---");
ui.l_id->setText("---");
ui.l_ains->setText("---");
ui.l_aouts->setText("---");
ui.l_cvins->setText("---");
ui.l_cvouts->setText("---");
ui.l_mins->setText("---");
ui.l_mouts->setText("---");
ui.l_pins->setText("---");
ui.l_pouts->setText("---");
ui.l_gui->setText("---");
ui.l_idisp->setText("---");
ui.l_bridged->setText("---");
ui.l_synth->setText("---");
}
}
};
// --------------------------------------------------------------------------------------------------------------------
#if QT_VERSION >= 0x60000
#define PLG_SUFFIX "_2qt6"
#define TG_SUFFIX "_7qt6"
#else
#define PLG_SUFFIX "_2"
#define TG_SUFFIX "_7"
#endif
PluginListDialog::PluginListDialog(QWidget* const parent, const HostSettings& hostSettings)
: QDialog(parent),
self(Self::create(parent))
{
self.ui.setupUi(this);
self.hostSettings = hostSettings;
// ----------------------------------------------------------------------------------------------------------------
// Set-up GUI
self.ui.b_add->setEnabled(false);
addAction(self.ui.act_focus_search);
QObject::connect(self.ui.act_focus_search, &QAction::triggered,
this, &PluginListDialog::slot_focusSearchFieldAndSelectAll);
#if BINARY_NATIVE == BINARY_POSIX32 || BINARY_NATIVE == BINARY_WIN32
self.ui.ch_bridged->setText(tr("Bridged (64bit)"));
#else
self.ui.ch_bridged->setText(tr("Bridged (32bit)"));
#endif
#if !(defined(CARLA_OS_LINUX) || defined(CARLA_OS_MAC))
self.ui.ch_bridged_wine->setChecked(false);
self.ui.ch_bridged_wine->setEnabled(false);
#endif
#ifdef CARLA_OS_MAC
setWindowModality(Qt::WindowModal);
#else
self.ui.ch_au->setChecked(false);
self.ui.ch_au->setEnabled(false);
self.ui.ch_au->setVisible(false);
#endif
self.ui.tab_info->tabBar()->hide();
self.ui.tab_reqs->tabBar()->hide();
// FIXME, why /2 needed?
self.ui.tab_info->setMinimumWidth(self.ui.la_id->width()/2 +
fontMetricsHorizontalAdvance(self.ui.l_id->fontMetrics(), "9999999999") + 6*3);
setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
// ----------------------------------------------------------------------------------------------------------------
// Load settings
loadSettings();
// ----------------------------------------------------------------------------------------------------------------
// Disable bridges if not enabled in settings
#if 0
// NOTE: We Assume win32 carla build will not run win64 plugins
if (WINDOWS and not kIs64bit) or not host.showPluginBridges:
self.ui.ch_native.setChecked(True)
self.ui.ch_native.setEnabled(False)
self.ui.ch_native.setVisible(True)
self.ui.ch_bridged.setChecked(False)
self.ui.ch_bridged.setEnabled(False)
self.ui.ch_bridged.setVisible(False)
self.ui.ch_bridged_wine.setChecked(False)
self.ui.ch_bridged_wine.setEnabled(False)
self.ui.ch_bridged_wine.setVisible(False)
elif not host.showWineBridges:
self.ui.ch_bridged_wine.setChecked(False)
self.ui.ch_bridged_wine.setEnabled(False)
self.ui.ch_bridged_wine.setVisible(False)
#endif
// ----------------------------------------------------------------------------------------------------------------
// Set-up Icons
if (hostSettings.useSystemIcons)
{
#if 0
self.ui.b_add.setIcon(getIcon('list-add', 16, 'svgz'))
self.ui.b_cancel.setIcon(getIcon('dialog-cancel', 16, 'svgz'))
self.ui.b_clear_filters.setIcon(getIcon('edit-clear', 16, 'svgz'))
self.ui.b_refresh.setIcon(getIcon('view-refresh', 16, 'svgz'))
QTableWidgetItem* const hhi = self.ui.tableWidget->horizontalHeaderItem(self.TABLEWIDGET_ITEM_FAVORITE);
hhi.setIcon(getIcon('bookmarks', 16, 'svgz'))
#endif
}
// ----------------------------------------------------------------------------------------------------------------
// Set-up connections
QObject::connect(this, &QDialog::finished, this, &PluginListDialog::slot_saveSettings);
QObject::connect(self.ui.b_add, &QPushButton::clicked, this, &PluginListDialog::slot_addPlugin);
QObject::connect(self.ui.b_cancel, &QPushButton::clicked, this, &QDialog::reject);
QObject::connect(self.ui.b_refresh, &QPushButton::clicked, this, &PluginListDialog::slot_refreshPlugins);
QObject::connect(self.ui.b_clear_filters, &QPushButton::clicked, this, &PluginListDialog::slot_clearFilters);
QObject::connect(self.ui.lineEdit, &QLineEdit::textChanged, this, &PluginListDialog::slot_checkFilters);
QObject::connect(self.ui.tableWidget, &QTableWidget::currentCellChanged,
this, &PluginListDialog::slot_checkPlugin);
QObject::connect(self.ui.tableWidget, &QTableWidget::cellClicked,
this, &PluginListDialog::slot_cellClicked);
QObject::connect(self.ui.tableWidget, &QTableWidget::cellDoubleClicked,
this, &PluginListDialog::slot_cellDoubleClicked);
QObject::connect(self.ui.ch_internal, &QCheckBox::clicked, this, &PluginListDialog::slot_checkFilters);
QObject::connect(self.ui.ch_ladspa, &QCheckBox::clicked, this, &PluginListDialog::slot_checkFilters);
QObject::connect(self.ui.ch_dssi, &QCheckBox::clicked, this, &PluginListDialog::slot_checkFilters);
QObject::connect(self.ui.ch_lv2, &QCheckBox::clicked, this, &PluginListDialog::slot_checkFilters);
QObject::connect(self.ui.ch_vst, &QCheckBox::clicked, this, &PluginListDialog::slot_checkFilters);
QObject::connect(self.ui.ch_vst3, &QCheckBox::clicked, this, &PluginListDialog::slot_checkFilters);
QObject::connect(self.ui.ch_clap, &QCheckBox::clicked, this, &PluginListDialog::slot_checkFilters);
QObject::connect(self.ui.ch_au, &QCheckBox::clicked, this, &PluginListDialog::slot_checkFilters);
QObject::connect(self.ui.ch_jsfx, &QCheckBox::clicked, this, &PluginListDialog::slot_checkFilters);
QObject::connect(self.ui.ch_kits, &QCheckBox::clicked, this, &PluginListDialog::slot_checkFilters);
QObject::connect(self.ui.ch_effects, &QCheckBox::clicked, this, &PluginListDialog::slot_checkFilters);
QObject::connect(self.ui.ch_instruments, &QCheckBox::clicked, this, &PluginListDialog::slot_checkFilters);
QObject::connect(self.ui.ch_midi, &QCheckBox::clicked, this, &PluginListDialog::slot_checkFilters);
QObject::connect(self.ui.ch_other, &QCheckBox::clicked, this, &PluginListDialog::slot_checkFilters);
QObject::connect(self.ui.ch_native, &QCheckBox::clicked, this, &PluginListDialog::slot_checkFilters);
QObject::connect(self.ui.ch_bridged, &QCheckBox::clicked, this, &PluginListDialog::slot_checkFilters);
QObject::connect(self.ui.ch_bridged_wine, &QCheckBox::clicked, this, &PluginListDialog::slot_checkFilters);
QObject::connect(self.ui.ch_favorites, &QCheckBox::clicked, this, &PluginListDialog::slot_checkFilters);
QObject::connect(self.ui.ch_rtsafe, &QCheckBox::clicked, this, &PluginListDialog::slot_checkFilters);
QObject::connect(self.ui.ch_cv, &QCheckBox::clicked, this, &PluginListDialog::slot_checkFilters);
QObject::connect(self.ui.ch_gui, &QCheckBox::clicked, this, &PluginListDialog::slot_checkFilters);
QObject::connect(self.ui.ch_inline_display, &QCheckBox::clicked, this, &PluginListDialog::slot_checkFilters);
QObject::connect(self.ui.ch_stereo, &QCheckBox::clicked, this, &PluginListDialog::slot_checkFilters);
QObject::connect(self.ui.ch_cat_all, &QCheckBox::clicked,
this, &PluginListDialog::slot_checkFiltersCategoryAll);
QObject::connect(self.ui.ch_cat_delay, &QCheckBox::clicked,
this, &PluginListDialog::slot_checkFiltersCategorySpecific);
QObject::connect(self.ui.ch_cat_distortion, &QCheckBox::clicked,
this, &PluginListDialog::slot_checkFiltersCategorySpecific);
QObject::connect(self.ui.ch_cat_dynamics, &QCheckBox::clicked,
this, &PluginListDialog::slot_checkFiltersCategorySpecific);
QObject::connect(self.ui.ch_cat_eq, &QCheckBox::clicked,
this, &PluginListDialog::slot_checkFiltersCategorySpecific);
QObject::connect(self.ui.ch_cat_filter, &QCheckBox::clicked,
this, &PluginListDialog::slot_checkFiltersCategorySpecific);
QObject::connect(self.ui.ch_cat_modulator, &QCheckBox::clicked,
this, &PluginListDialog::slot_checkFiltersCategorySpecific);
QObject::connect(self.ui.ch_cat_synth, &QCheckBox::clicked,
this, &PluginListDialog::slot_checkFiltersCategorySpecific);
QObject::connect(self.ui.ch_cat_utility, &QCheckBox::clicked,
this, &PluginListDialog::slot_checkFiltersCategorySpecific);
QObject::connect(self.ui.ch_cat_other, &QCheckBox::clicked, this,
&PluginListDialog::slot_checkFiltersCategorySpecific);
// ----------------------------------------------------------------------------------------------------------------
// Post-connect setup
self.checkPlugin(-1);
self.idle();
slot_focusSearchFieldAndSelectAll();
self.fTimerId = startTimer(0);
}
PluginListDialog::~PluginListDialog()
{
if (self.fTimerId != 0)
killTimer(self.fTimerId);
delete &self;
}
// --------------------------------------------------------------------------------------------------------------------
// public methods
const PluginInfo& PluginListDialog::getSelectedPluginInfo() const
{
return self.fRetPlugin;
}
// --------------------------------------------------------------------------------------------------------------------
// protected methods
void PluginListDialog::showEvent(QShowEvent* const event)
{
slot_focusSearchFieldAndSelectAll();
QDialog::showEvent(event);
}
void PluginListDialog::timerEvent(QTimerEvent* const event)
{
if (event->timerId() == self.fTimerId)
{
if (self.idle())
{
killTimer(self.fTimerId);
self.fTimerId = 0;
}
}
QDialog::timerEvent(event);
}
// --------------------------------------------------------------------------------------------------------------------
// private methods
void PluginListDialog::loadSettings()
{
const QSafeSettings settings("falkTX", "CarlaDatabase2");
self.fFavoritePlugins = settings.valueStringList("PluginDatabase/Favorites");
self.fFavoritePluginsChanged = false;
restoreGeometry(settings.valueByteArray("PluginDatabase/Geometry" PLG_SUFFIX));
self.ui.ch_effects->setChecked(settings.valueBool("PluginDatabase/ShowEffects", true));
self.ui.ch_instruments->setChecked(settings.valueBool("PluginDatabase/ShowInstruments", true));
self.ui.ch_midi->setChecked(settings.valueBool("PluginDatabase/ShowMIDI", true));
self.ui.ch_other->setChecked(settings.valueBool("PluginDatabase/ShowOther", true));
self.ui.ch_internal->setChecked(settings.valueBool("PluginDatabase/ShowInternal", true));
self.ui.ch_ladspa->setChecked(settings.valueBool("PluginDatabase/ShowLADSPA", true));
self.ui.ch_dssi->setChecked(settings.valueBool("PluginDatabase/ShowDSSI", true));
self.ui.ch_lv2->setChecked(settings.valueBool("PluginDatabase/ShowLV2", true));
self.ui.ch_vst->setChecked(settings.valueBool("PluginDatabase/ShowVST2", true));
self.ui.ch_vst3->setChecked(settings.valueBool("PluginDatabase/ShowVST3", true));
self.ui.ch_clap->setChecked(settings.valueBool("PluginDatabase/ShowCLAP", true));
#ifdef CARLA_OS_MAC
self.ui.ch_au->setChecked(settings.valueBool("PluginDatabase/ShowAU", true));
#endif
self.ui.ch_jsfx->setChecked(settings.valueBool("PluginDatabase/ShowJSFX", true));
self.ui.ch_kits->setChecked(settings.valueBool("PluginDatabase/ShowKits", true));
self.ui.ch_native->setChecked(settings.valueBool("PluginDatabase/ShowNative", true));
self.ui.ch_bridged->setChecked(settings.valueBool("PluginDatabase/ShowBridged", true));
self.ui.ch_bridged_wine->setChecked(settings.valueBool("PluginDatabase/ShowBridgedWine", true));
self.ui.ch_favorites->setChecked(settings.valueBool("PluginDatabase/ShowFavorites", false));
self.ui.ch_rtsafe->setChecked(settings.valueBool("PluginDatabase/ShowRtSafe", false));
self.ui.ch_cv->setChecked(settings.valueBool("PluginDatabase/ShowHasCV", false));
self.ui.ch_gui->setChecked(settings.valueBool("PluginDatabase/ShowHasGUI", false));
self.ui.ch_inline_display->setChecked(settings.valueBool("PluginDatabase/ShowHasInlineDisplay", false));
self.ui.ch_stereo->setChecked(settings.valueBool("PluginDatabase/ShowStereoOnly", false));
self.ui.lineEdit->setText(settings.valueString("PluginDatabase/SearchText", ""));
const QString categories = settings.valueString("PluginDatabase/ShowCategory", "all");
if (categories == "all" or categories.length() < 2)
{
self.ui.ch_cat_all->setChecked(true);
self.ui.ch_cat_delay->setChecked(false);
self.ui.ch_cat_distortion->setChecked(false);
self.ui.ch_cat_dynamics->setChecked(false);
self.ui.ch_cat_eq->setChecked(false);
self.ui.ch_cat_filter->setChecked(false);
self.ui.ch_cat_modulator->setChecked(false);
self.ui.ch_cat_synth->setChecked(false);
self.ui.ch_cat_utility->setChecked(false);
self.ui.ch_cat_other->setChecked(false);
}
else
{
self.ui.ch_cat_all->setChecked(false);
self.ui.ch_cat_delay->setChecked(categories.contains(":delay:"));
self.ui.ch_cat_distortion->setChecked(categories.contains(":distortion:"));
self.ui.ch_cat_dynamics->setChecked(categories.contains(":dynamics:"));
self.ui.ch_cat_eq->setChecked(categories.contains(":eq:"));
self.ui.ch_cat_filter->setChecked(categories.contains(":filter:"));
self.ui.ch_cat_modulator->setChecked(categories.contains(":modulator:"));
self.ui.ch_cat_synth->setChecked(categories.contains(":synth:"));
self.ui.ch_cat_utility->setChecked(categories.contains(":utility:"));
self.ui.ch_cat_other->setChecked(categories.contains(":other:"));
}
const QByteArray tableGeometry = settings.valueByteArray("PluginDatabase/TableGeometry" TG_SUFFIX);
QHeaderView* const horizontalHeader = self.ui.tableWidget->horizontalHeader();
if (! tableGeometry.isNull())
{
horizontalHeader->restoreState(tableGeometry);
}
else
{
self.ui.tableWidget->setColumnWidth(self.TABLEWIDGET_ITEM_NAME, 250);
self.ui.tableWidget->setColumnWidth(self.TABLEWIDGET_ITEM_LABEL, 200);
self.ui.tableWidget->setColumnWidth(self.TABLEWIDGET_ITEM_MAKER, 150);
self.ui.tableWidget->sortByColumn(self.TABLEWIDGET_ITEM_NAME, Qt::AscendingOrder);
}
horizontalHeader->setSectionResizeMode(self.TABLEWIDGET_ITEM_FAVORITE, QHeaderView::Fixed);
self.ui.tableWidget->setColumnWidth(self.TABLEWIDGET_ITEM_FAVORITE, 24);
self.ui.tableWidget->setSortingEnabled(true);
}
// -----------------------------------------------------------------------------------------------------------------
// private slots
void PluginListDialog::slot_cellClicked(int row, int column)
{
if (column != self.TABLEWIDGET_ITEM_FAVORITE)
return;
const QTableWidgetItem* const widget = self.ui.tableWidget->item(row, self.TABLEWIDGET_ITEM_FAVORITE);
const PluginInfo plugin = asPluginInfo(self.ui.tableWidget->item(row, self.TABLEWIDGET_ITEM_NAME)->data(Qt::UserRole+1));
#if 0
plugin = self._createFavoritePluginDict(plugin);
#endif
if (widget->checkState() == Qt::Checked)
{
#if 0
if (not plugin in self.fFavoritePlugins)
{
self.fFavoritePlugins.append(plugin);
self.fFavoritePluginsChanged = true;
}
#endif
}
else
{
#if 0
try:
self.fFavoritePlugins.remove(plugin);
self.fFavoritePluginsChanged = True;
except ValueError:
pass
#endif
}
}
void PluginListDialog::slot_cellDoubleClicked(int, int column)
{
if (column != self.TABLEWIDGET_ITEM_FAVORITE)
slot_addPlugin();
}
void PluginListDialog::slot_focusSearchFieldAndSelectAll()
{
self.ui.lineEdit->setFocus();
self.ui.lineEdit->selectAll();
}
void PluginListDialog::slot_addPlugin()
{
if (self.ui.tableWidget->currentRow() >= 0)
{
self.fRetPlugin = asPluginInfo(self.ui.tableWidget->item(self.ui.tableWidget->currentRow(),
self.TABLEWIDGET_ITEM_NAME)->data(Qt::UserRole+1));
accept();
}
else
{
reject();
}
}
void PluginListDialog::slot_checkPlugin(const int row)
{
self.checkPlugin(row);
}
void PluginListDialog::slot_checkFilters()
{
self.checkFilters();
}
void PluginListDialog::slot_checkFiltersCategoryAll(const bool clicked)
{
const bool notClicked = !clicked;
self.ui.ch_cat_delay->setChecked(notClicked);
self.ui.ch_cat_distortion->setChecked(notClicked);
self.ui.ch_cat_dynamics->setChecked(notClicked);
self.ui.ch_cat_eq->setChecked(notClicked);
self.ui.ch_cat_filter->setChecked(notClicked);
self.ui.ch_cat_modulator->setChecked(notClicked);
self.ui.ch_cat_synth->setChecked(notClicked);
self.ui.ch_cat_utility->setChecked(notClicked);
self.ui.ch_cat_other->setChecked(notClicked);
self.checkFilters();
}
void PluginListDialog::slot_checkFiltersCategorySpecific(bool clicked)
{
if (clicked)
{
self.ui.ch_cat_all->setChecked(false);
}
else if (! (self.ui.ch_cat_delay->isChecked() ||
self.ui.ch_cat_distortion->isChecked() ||
self.ui.ch_cat_dynamics->isChecked() ||
self.ui.ch_cat_eq->isChecked() ||
self.ui.ch_cat_filter->isChecked() ||
self.ui.ch_cat_modulator->isChecked() ||
self.ui.ch_cat_synth->isChecked() ||
self.ui.ch_cat_utility->isChecked() ||
self.ui.ch_cat_other->isChecked()))
{
self.ui.ch_cat_all->setChecked(true);
}
self.checkFilters();
}
void PluginListDialog::slot_refreshPlugins()
{
#if 0
if (PluginRefreshW(this, self.hostSettings, self.hasLoadedLv2Plugins).exec())
reAddPlugins();
#endif
}
void PluginListDialog::slot_clearFilters()
{
blockSignals(true);
self.ui.ch_internal->setChecked(true);
self.ui.ch_ladspa->setChecked(true);
self.ui.ch_dssi->setChecked(true);
self.ui.ch_lv2->setChecked(true);
self.ui.ch_vst->setChecked(true);
self.ui.ch_vst3->setChecked(true);
self.ui.ch_clap->setChecked(true);
self.ui.ch_jsfx->setChecked(true);
self.ui.ch_kits->setChecked(true);
self.ui.ch_instruments->setChecked(true);
self.ui.ch_effects->setChecked(true);
self.ui.ch_midi->setChecked(true);
self.ui.ch_other->setChecked(true);
self.ui.ch_native->setChecked(true);
self.ui.ch_bridged->setChecked(false);
self.ui.ch_bridged_wine->setChecked(false);
self.ui.ch_favorites->setChecked(false);
self.ui.ch_rtsafe->setChecked(false);
self.ui.ch_stereo->setChecked(false);
self.ui.ch_cv->setChecked(false);
self.ui.ch_gui->setChecked(false);
self.ui.ch_inline_display->setChecked(false);
if (self.ui.ch_au->isEnabled())
self.ui.ch_au->setChecked(true);
self.ui.ch_cat_all->setChecked(true);
self.ui.ch_cat_delay->setChecked(false);
self.ui.ch_cat_distortion->setChecked(false);
self.ui.ch_cat_dynamics->setChecked(false);
self.ui.ch_cat_eq->setChecked(false);
self.ui.ch_cat_filter->setChecked(false);
self.ui.ch_cat_modulator->setChecked(false);
self.ui.ch_cat_synth->setChecked(false);
self.ui.ch_cat_utility->setChecked(false);
self.ui.ch_cat_other->setChecked(false);
self.ui.lineEdit->clear();
blockSignals(false);
self.checkFilters();
}
// --------------------------------------------------------------------------------------------------------------------
void PluginListDialog::slot_saveSettings()
{
QSafeSettings settings("falkTX", "CarlaDatabase2");
settings.setValue("PluginDatabase/Geometry" PLG_SUFFIX, saveGeometry());
settings.setValue("PluginDatabase/TableGeometry" TG_SUFFIX, self.ui.tableWidget->horizontalHeader()->saveState());
settings.setValue("PluginDatabase/ShowEffects", self.ui.ch_effects->isChecked());
settings.setValue("PluginDatabase/ShowInstruments", self.ui.ch_instruments->isChecked());
settings.setValue("PluginDatabase/ShowMIDI", self.ui.ch_midi->isChecked());
settings.setValue("PluginDatabase/ShowOther", self.ui.ch_other->isChecked());
settings.setValue("PluginDatabase/ShowInternal", self.ui.ch_internal->isChecked());
settings.setValue("PluginDatabase/ShowLADSPA", self.ui.ch_ladspa->isChecked());
settings.setValue("PluginDatabase/ShowDSSI", self.ui.ch_dssi->isChecked());
settings.setValue("PluginDatabase/ShowLV2", self.ui.ch_lv2->isChecked());
settings.setValue("PluginDatabase/ShowVST2", self.ui.ch_vst->isChecked());
settings.setValue("PluginDatabase/ShowVST3", self.ui.ch_vst3->isChecked());
settings.setValue("PluginDatabase/ShowCLAP", self.ui.ch_clap->isChecked());
settings.setValue("PluginDatabase/ShowAU", self.ui.ch_au->isChecked());
settings.setValue("PluginDatabase/ShowJSFX", self.ui.ch_jsfx->isChecked());
settings.setValue("PluginDatabase/ShowKits", self.ui.ch_kits->isChecked());
settings.setValue("PluginDatabase/ShowNative", self.ui.ch_native->isChecked());
settings.setValue("PluginDatabase/ShowBridged", self.ui.ch_bridged->isChecked());
settings.setValue("PluginDatabase/ShowBridgedWine", self.ui.ch_bridged_wine->isChecked());
settings.setValue("PluginDatabase/ShowFavorites", self.ui.ch_favorites->isChecked());
settings.setValue("PluginDatabase/ShowRtSafe", self.ui.ch_rtsafe->isChecked());
settings.setValue("PluginDatabase/ShowHasCV", self.ui.ch_cv->isChecked());
settings.setValue("PluginDatabase/ShowHasGUI", self.ui.ch_gui->isChecked());
settings.setValue("PluginDatabase/ShowHasInlineDisplay", self.ui.ch_inline_display->isChecked());
settings.setValue("PluginDatabase/ShowStereoOnly", self.ui.ch_stereo->isChecked());
settings.setValue("PluginDatabase/SearchText", self.ui.lineEdit->text());
if (self.ui.ch_cat_all->isChecked())
{
settings.setValue("PluginDatabase/ShowCategory", "all");
}
else
{
QCarlaString categories;
if (self.ui.ch_cat_delay->isChecked())
categories += ":delay";
if (self.ui.ch_cat_distortion->isChecked())
categories += ":distortion";
if (self.ui.ch_cat_dynamics->isChecked())
categories += ":dynamics";
if (self.ui.ch_cat_eq->isChecked())
categories += ":eq";
if (self.ui.ch_cat_filter->isChecked())
categories += ":filter";
if (self.ui.ch_cat_modulator->isChecked())
categories += ":modulator";
if (self.ui.ch_cat_synth->isChecked())
categories += ":synth";
if (self.ui.ch_cat_utility->isChecked())
categories += ":utility";
if (self.ui.ch_cat_other->isChecked())
categories += ":other";
if (categories.isNotEmpty())
categories += ":";
settings.setValue("PluginDatabase/ShowCategory", categories);
}
if (self.fFavoritePluginsChanged)
settings.setValue("PluginDatabase/Favorites", self.fFavoritePlugins);
}
// --------------------------------------------------------------------------------------------------------------------
const PluginListDialogResults*
carla_frontend_createAndExecPluginListDialog(void* const parent/*, const HostSettings& hostSettings*/)
{
const HostSettings hostSettings = {};
PluginListDialog gui(reinterpret_cast<QWidget*>(parent), hostSettings);
if (gui.exec())
{
static PluginListDialogResults ret;
static CarlaString category;
static CarlaString filename;
static CarlaString name;
static CarlaString label;
static CarlaString maker;
const PluginInfo& plugin(gui.getSelectedPluginInfo());
category = plugin.category.toUtf8();
filename = plugin.filename.toUtf8();
name = plugin.name.toUtf8();
label = plugin.label.toUtf8();
maker = plugin.maker.toUtf8();
ret.API = plugin.API;
ret.build = plugin.build;
ret.type = plugin.type;
ret.hints = plugin.hints;
ret.category = category;
ret.filename = filename;
ret.name = name;
ret.label = label;
ret.maker = maker;
ret.audioIns = plugin.audioIns;
ret.audioOuts = plugin.audioOuts;
ret.cvIns = plugin.cvIns;
ret.cvOuts = plugin.cvOuts;
ret.midiIns = plugin.midiIns;
ret.midiOuts = plugin.midiOuts;
ret.parametersIns = plugin.parametersIns;
ret.parametersOuts = plugin.parametersOuts;
return &ret;
}
return nullptr;
}
// --------------------------------------------------------------------------------------------------------------------