/* * Carla plugin host * Copyright (C) 2011-2023 Filipe Coelho * * 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 #include #include #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 #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 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 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 inline T static_cast_void(const void* ptr) { return static_cast(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(plugin.build), static_cast(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(&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(qdata.constData()); PluginInfo plugin = { data->API, static_cast(data->build), static_cast(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(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 = {}; int fLastTableIndex = 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 ui.tableWidget->setRowCount(fLastTableIndex); for (int i=0; iitem(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); } } #ifdef CARLA_FRONTEND_NO_CACHED_PLUGIN_API void addPluginToTable(const CarlaPluginDiscoveryInfo* const info) { #ifdef BUILDING_CARLA_OBS if (info->io.cvIns != 0 || info->io.cvOuts != 0) { carla_stdout("addPluginToTable %p %s - ignored, has CV", info, info->filename); return; } if (info->io.audioIns > 8 || info->io.audioOuts > 8) { carla_stdout("addPluginToTable %p %s - ignored, has > 8 audio IO", info, info->filename); return; } #endif carla_stdout("addPluginToTable %p %s", info, info->filename); ui.tableWidget->setRowCount(fLastTableIndex + 1); 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, }; const int index = fLastTableIndex; const bool isFav = false; QTableWidgetItem* const itemFav = new QTableWidgetItem; itemFav->setCheckState(isFav ? Qt::Checked : Qt::Unchecked); itemFav->setText(isFav ? " " : " "); const QString searchablePluginText = ( QString::fromUtf8(info->label) + QString::fromUtf8(info->filename) + QString::fromUtf8(info->metadata.name) + QString::fromUtf8(info->metadata.maker)).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, searchablePluginText); fLastTableIndex += 1; carla_stdout("addPluginToTable %p %s END", info, info->filename); } static void _discoveryCallback(void* const ptr, const CarlaPluginDiscoveryInfo* const info) { carla_stdout("_discoveryCallback %p %s", info, info->filename); static_cast(ptr)->addPluginToTable(info); } #else void addPluginToTable(const PluginInfo& plugin, const PluginType ptype) { if (plugin.API != PLUGIN_QUERY_API_VERSION) return; 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 = fLastTableIndex; #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 = (plugin.name + plugin.label + plugin.maker + plugin.filename).toLower(); ui.tableWidget->setItem(index, TABLEWIDGET_ITEM_FAVORITE, itemFav); ui.tableWidget->setItem(index, TABLEWIDGET_ITEM_NAME, new QTableWidgetItem(plugin.name)); ui.tableWidget->setItem(index, TABLEWIDGET_ITEM_LABEL, new QTableWidgetItem(plugin.label)); ui.tableWidget->setItem(index, TABLEWIDGET_ITEM_MAKER, new QTableWidgetItem(plugin.maker)); ui.tableWidget->setItem(index, TABLEWIDGET_ITEM_BINARY, new QTableWidgetItem(QFileInfo(plugin.filename).fileName())); QTableWidgetItem* const itemName = ui.tableWidget->item(index, TABLEWIDGET_ITEM_NAME); itemName->setData(Qt::UserRole+1, asVariant(plugincopy)); itemName->setData(Qt::UserRole+2, pluginText); fLastTableIndex += 1; } uint reAddInternalHelper(QSafePluginListSettings& settingsDB, const PluginType ptype, const char* const path) { QString ptypeStr, ptypeStrTr; switch (ptype) { case PLUGIN_INTERNAL: ptypeStr = "Internal"; ptypeStrTr = tr("Internal"); break; case PLUGIN_LV2: ptypeStr = "LV2"; ptypeStrTr = ptypeStr; break; case PLUGIN_AU: ptypeStr = "AU"; ptypeStrTr = ptypeStr; break; case PLUGIN_SFZ: ptypeStr = "SFZ"; ptypeStrTr = ptypeStr; break; // TODO(jsfx) what to do here? default: return 0; } QPluginInfoList plugins = settingsDB.valuePluginInfoList("Plugins/" + ptypeStr); uint pluginCount = settingsDB.valueUInt("PluginCount/" + ptypeStr, 0); if (ptype == PLUGIN_AU) carla_juce_init(); const uint pluginCountNew = carla_get_cached_plugin_count(ptype, path); #if 0 if (pluginCountNew != pluginCount || plugins.size() != pluginCount || (plugins.size() > 0 && plugins[0].API != PLUGIN_QUERY_API_VERSION)) #endif { plugins.clear(); pluginCount = pluginCountNew; QApplication::instance()->processEvents(QEventLoop::ExcludeUserInputEvents, 50); if (ptype == PLUGIN_AU) carla_juce_idle(); for (uint i=0; iprocessEvents(QEventLoop::ExcludeUserInputEvents, 50); if (ptype == PLUGIN_AU) carla_juce_idle(); } } settingsDB.setValue("Plugins/" + ptypeStr, plugins); settingsDB.setValue("PluginCount/" + ptypeStr, pluginCount); } if (ptype == PLUGIN_AU) carla_juce_cleanup(); // prepare rows in advance ui.tableWidget->setRowCount(fLastTableIndex + plugins.size()); for (const PluginInfo& plugin : plugins) addPluginToTable(plugin, ptype); return pluginCount; } #endif void reAddPlugins() { #ifdef CARLA_FRONTEND_NO_CACHED_PLUGIN_API fLastTableIndex = 0; ui.tableWidget->setSortingEnabled(false); ui.tableWidget->clearContents(); DefaultPaths paths; paths.init(); paths.loadFromEnv(); #ifdef BUILDING_CARLA_OBS QCarlaString binPath(get_carla_bin_path()); binPath += CARLA_OS_SEP_STR "carla-discovery-native"; #ifdef CARLA_OS_WIN binPath += ".exe"; #endif #else QCarlaString binPath("/usr/lib/carla/carla-discovery-native"); #endif CarlaPluginDiscoveryHandle handle = carla_plugin_discovery_start(binPath.toUtf8().constData(), PLUGIN_VST3, paths.vst3.toUtf8().constData(), _discoveryCallback, this); CARLA_SAFE_ASSERT_RETURN(handle != nullptr,); while (carla_plugin_discovery_idle(handle)) QCoreApplication::processEvents(QEventLoop::ExcludeUserInputEvents, 50); carla_plugin_discovery_stop(handle); ui.tableWidget->setRowCount(fLastTableIndex); constexpr const char* const txt = "Have %1 plugins"; ui.label->setText(fRealParent->tr(txt) .arg(QString::number(fLastTableIndex)) ); #else QSafePluginListSettings settingsDB("falkTX", "CarlaPlugins5"); fLastTableIndex = 0; ui.tableWidget->setSortingEnabled(false); ui.tableWidget->clearContents(); DefaultPaths paths; paths.init(); paths.loadFromEnv(); QCarlaString LV2_PATH; { const QSafeSettings settings("falkTX", "Carla2"); LV2_PATH = settings.valueString(CARLA_KEY_PATHS_LV2, paths.lv2); } // ------------------------------------------------------------------------------------------------------------ // plugins handled through backend const uint internalCount = reAddInternalHelper(settingsDB, PLUGIN_INTERNAL, ""); const uint lv2Count = reAddInternalHelper(settingsDB, PLUGIN_LV2, LV2_PATH.toUtf8()); #ifdef CARLA_OS_MAC const uint auCount = reAddInternalHelper(settingsDB, PLUGIN_AU, ""); #else const uint auCount = 0; #endif // ------------------------------------------------------------------------------------------------------------ // LADSPA QList ladspaPlugins; ladspaPlugins += settingsDB.valuePluginInfoList("Plugins/LADSPA_native"); ladspaPlugins += settingsDB.valuePluginInfoList("Plugins/LADSPA_posix32"); ladspaPlugins += settingsDB.valuePluginInfoList("Plugins/LADSPA_posix64"); ladspaPlugins += settingsDB.valuePluginInfoList("Plugins/LADSPA_win32"); ladspaPlugins += settingsDB.valuePluginInfoList("Plugins/LADSPA_win64"); // ------------------------------------------------------------------------------------------------------------ // DSSI QList dssiPlugins; dssiPlugins += settingsDB.valuePluginInfoList("Plugins/DSSI_native"); dssiPlugins += settingsDB.valuePluginInfoList("Plugins/DSSI_posix32"); dssiPlugins += settingsDB.valuePluginInfoList("Plugins/DSSI_posix64"); dssiPlugins += settingsDB.valuePluginInfoList("Plugins/DSSI_win32"); dssiPlugins += settingsDB.valuePluginInfoList("Plugins/DSSI_win64"); // ------------------------------------------------------------------------------------------------------------ // VST2 QList vst2Plugins; vst2Plugins += settingsDB.valuePluginInfoList("Plugins/VST2_native"); vst2Plugins += settingsDB.valuePluginInfoList("Plugins/VST2_posix32"); vst2Plugins += settingsDB.valuePluginInfoList("Plugins/VST2_posix64"); vst2Plugins += settingsDB.valuePluginInfoList("Plugins/VST2_win32"); vst2Plugins += settingsDB.valuePluginInfoList("Plugins/VST2_win64"); // ------------------------------------------------------------------------------------------------------------ // VST3 QList vst3Plugins; vst3Plugins += settingsDB.valuePluginInfoList("Plugins/VST3_native"); vst3Plugins += settingsDB.valuePluginInfoList("Plugins/VST3_posix32"); vst3Plugins += settingsDB.valuePluginInfoList("Plugins/VST3_posix64"); vst3Plugins += settingsDB.valuePluginInfoList("Plugins/VST3_win32"); vst3Plugins += settingsDB.valuePluginInfoList("Plugins/VST3_win64"); // ------------------------------------------------------------------------------------------------------------ // CLAP QList clapPlugins; clapPlugins += settingsDB.valuePluginInfoList("Plugins/CLAP_native"); clapPlugins += settingsDB.valuePluginInfoList("Plugins/CLAP_posix32"); clapPlugins += settingsDB.valuePluginInfoList("Plugins/CLAP_posix64"); clapPlugins += settingsDB.valuePluginInfoList("Plugins/CLAP_win32"); clapPlugins += settingsDB.valuePluginInfoList("Plugins/CLAP_win64"); #ifdef CARLA_OS_MAC // ------------------------------------------------------------------------------------------------------------ // AU (extra non-cached) QList auPlugins32; auPlugins32 += settingsDB.valuePluginInfoList("Plugins/AU_posix32"); #endif // ------------------------------------------------------------------------------------------------------------ // JSFX QPluginInfoList jsfxPlugins = settingsDB.valuePluginInfoList("Plugins/JSFX"); // ------------------------------------------------------------------------------------------------------------ // Kits QList sf2s; sf2s += settingsDB.valuePluginInfoList("Plugins/SF2"); QPluginInfoList sfzs = settingsDB.valuePluginInfoList("Plugins/SFZ"); // ------------------------------------------------------------------------------------------------------------ // count plugins first, so we can create rows in advance int ladspaCount = 0; int dssiCount = 0; int vstCount = 0; int vst3Count = 0; int clapCount = 0; int au32Count = 0; int jsfxCount = jsfxPlugins.size(); int sf2Count = 0; int sfzCount = sfzs.size(); for (const QPluginInfoList& plugins : ladspaPlugins) ladspaCount += plugins.size(); for (const QPluginInfoList& plugins : dssiPlugins) dssiCount += plugins.size(); for (const QPluginInfoList& plugins : vst2Plugins) vstCount += plugins.size(); for (const QPluginInfoList& plugins : vst3Plugins) vst3Count += plugins.size(); for (const QPluginInfoList& plugins : clapPlugins) clapCount += plugins.size(); #ifdef CARLA_OS_MAC for (const QPluginInfoList& plugins : auPlugins32) au32Count += plugins.size(); #endif for (const QPluginInfoList& plugins : sf2s) sf2Count += plugins.size(); ui.tableWidget->setRowCount(fLastTableIndex + ladspaCount + dssiCount + vstCount + vst3Count + clapCount + auCount + au32Count + jsfxCount + sf2Count + sfzCount); 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(internalCount)) .arg(QString::number(ladspaCount)) .arg(QString::number(dssiCount)) .arg(QString::number(lv2Count)) .arg(QString::number(vstCount)) .arg(QString::number(vst3Count)) .arg(QString::number(clapCount)) #ifdef CARLA_OS_MAC .arg(QString::number(auCount+au32Count)) #endif .arg(QString::number(jsfxCount)) .arg(QString::number(sf2Count+sfzCount)) ); // ------------------------------------------------------------------------------------------------------------ // now add all plugins to the table for (const QPluginInfoList& plugins : ladspaPlugins) for (const PluginInfo& plugin : plugins) addPluginToTable(plugin, PLUGIN_LADSPA); for (const QPluginInfoList& plugins : dssiPlugins) for (const PluginInfo& plugin : plugins) addPluginToTable(plugin, PLUGIN_DSSI); for (const QPluginInfoList& plugins : vst2Plugins) for (const PluginInfo& plugin : plugins) addPluginToTable(plugin, PLUGIN_VST2); for (const QPluginInfoList& plugins : vst3Plugins) for (const PluginInfo& plugin : plugins) addPluginToTable(plugin, PLUGIN_VST3); for (const QPluginInfoList& plugins : clapPlugins) for (const PluginInfo& plugin : plugins) addPluginToTable(plugin, PLUGIN_CLAP); #ifdef CARLA_OS_MAC for (const QPluginInfoList& plugins : auPlugins32) for (const PluginInfo& plugin : plugins) addPluginToTable(plugin, PLUGIN_AU); #endif for (const PluginInfo& plugin : jsfxPlugins) addPluginToTable(plugin, PLUGIN_JSFX); for (const QPluginInfoList& sf2 : sf2s) for (const PluginInfo& sf2_i : sf2) addPluginToTable(sf2_i, PLUGIN_SF2); for (const PluginInfo& sfz : sfzs) addPluginToTable(sfz, PLUGIN_SFZ); #endif // ------------------------------------------------------------------------------------------------------------ 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("---"); } } }; 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.reAddPlugins(); slot_focusSearchFieldAndSelectAll(); } PluginListDialog::~PluginListDialog() { 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); } // -------------------------------------------------------------------------------------------------------------------- // private methods void PluginListDialog::loadSettings() { const QSafeSettings settings("falkTX", "CarlaDatabase2"); self.fFavoritePlugins = settings.valueStringList("PluginDatabase/Favorites"); self.fFavoritePluginsChanged = false; restoreGeometry(settings.valueByteArray("PluginDatabase/Geometry")); 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_6"); 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", saveGeometry()); settings.setValue("PluginDatabase/TableGeometry_6", 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(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; } // --------------------------------------------------------------------------------------------------------------------