From f2e0b40f43ccb70a33c3159623491e41acb807f3 Mon Sep 17 00:00:00 2001 From: falkTX Date: Sun, 4 Jun 2023 14:38:52 +0200 Subject: [PATCH] Rework C++ plugin list dialog and use it, delete python version Signed-off-by: falkTX --- source/frontend/CarlaFrontend.h | 12 +- source/frontend/Makefile | 4 +- source/frontend/carla_frontend.py | 35 +- source/frontend/carla_host.py | 40 +- source/frontend/pluginlist/__init__.py | 19 - source/frontend/pluginlist/discovery.py | 411 --- source/frontend/pluginlist/discoverythread.py | 851 ----- .../frontend/pluginlist/pluginlistdialog.cpp | 2728 ++++++++--------- .../frontend/pluginlist/pluginlistdialog.hpp | 92 +- .../frontend/pluginlist/pluginlistdialog.py | 1000 ------ .../frontend/pluginlist/pluginlistdialog.ui | 47 +- .../pluginlist/pluginlistrefreshdialog.cpp | 590 ---- .../pluginlist/pluginlistrefreshdialog.hpp | 81 - .../pluginlist/pluginlistrefreshdialog.py | 473 --- .../pluginlist/pluginlistrefreshdialog.ui | 610 ---- .../pluginlist/pluginrefreshdialog.hpp | 65 + .../pluginlist/pluginrefreshdialog.ui | 190 ++ source/frontend/utils/qcarlastring.hpp | 35 + 18 files changed, 1756 insertions(+), 5527 deletions(-) delete mode 100644 source/frontend/pluginlist/__init__.py delete mode 100644 source/frontend/pluginlist/discovery.py delete mode 100644 source/frontend/pluginlist/discoverythread.py delete mode 100755 source/frontend/pluginlist/pluginlistdialog.py delete mode 100644 source/frontend/pluginlist/pluginlistrefreshdialog.cpp delete mode 100644 source/frontend/pluginlist/pluginlistrefreshdialog.hpp delete mode 100755 source/frontend/pluginlist/pluginlistrefreshdialog.py delete mode 100644 source/frontend/pluginlist/pluginlistrefreshdialog.ui create mode 100644 source/frontend/pluginlist/pluginrefreshdialog.hpp create mode 100644 source/frontend/pluginlist/pluginrefreshdialog.ui diff --git a/source/frontend/CarlaFrontend.h b/source/frontend/CarlaFrontend.h index a75a7091e..b0800f92a 100644 --- a/source/frontend/CarlaFrontend.h +++ b/source/frontend/CarlaFrontend.h @@ -32,7 +32,6 @@ typedef struct { } JackAppDialogResults; typedef struct { - uint API; uint build; uint type; uint hints; @@ -48,14 +47,10 @@ typedef struct { uint cvOuts; uint midiIns; uint midiOuts; - uint parametersIns; - uint parametersOuts; + uint parameterIns; + uint parameterOuts; } PluginListDialogResults; -typedef struct { - char todo; -} PluginListRefreshDialogResults; - // -------------------------------------------------------------------------------------------------------------------- CARLA_API void @@ -67,9 +62,6 @@ carla_frontend_createAndExecJackAppDialog(void* parent, const char* projectFilen CARLA_API const PluginListDialogResults* carla_frontend_createAndExecPluginListDialog(void* parent/*, const HostSettings& hostSettings*/); -CARLA_API const PluginListRefreshDialogResults* -carla_frontend_createAndExecPluginListRefreshDialog(void* parent, bool useSystemIcons); - // -------------------------------------------------------------------------------------------------------------------- #ifdef __cplusplus diff --git a/source/frontend/Makefile b/source/frontend/Makefile index 0fa76f562..f5af3541e 100644 --- a/source/frontend/Makefile +++ b/source/frontend/Makefile @@ -73,8 +73,7 @@ CPP_FILES = \ carla_frontend.cpp \ dialogs/aboutjucedialog.cpp \ dialogs/jackappdialog.cpp \ - pluginlist/pluginlistdialog.cpp \ - pluginlist/pluginlistrefreshdialog.cpp + pluginlist/pluginlistdialog.cpp OBJS = $(CPP_FILES:%=$(OBJDIR)/%.o) @@ -139,7 +138,6 @@ UIs = $(DIALOG_UI_FILES:dialogs/%.ui=dialogs/ui_%.h) UIs += $(PLUGINLIST_UI_FILES:pluginlist/%.ui=pluginlist/ui_%.h) UIs += $(DIALOG_UI_FILES:%.ui=%_ui.py) -UIs += $(PLUGINLIST_UI_FILES:%.ui=%_ui.py) # old stuff, not yet converted UIs += \ diff --git a/source/frontend/carla_frontend.py b/source/frontend/carla_frontend.py index 8e36b0673..c54dc3176 100644 --- a/source/frontend/carla_frontend.py +++ b/source/frontend/carla_frontend.py @@ -23,7 +23,7 @@ # Imports (ctypes) from ctypes import ( - c_bool, c_char, c_char_p, c_int, c_void_p, cast, + c_bool, c_char, c_char_p, c_uint, c_uint64, c_void_p, cast, cdll, Structure, POINTER ) @@ -55,15 +55,23 @@ class JackApplicationDialogResults(Structure): class PluginListDialogResults(Structure): _fields_ = [ - ("btype", c_int), - ("ptype", c_int), - ("binary", c_char_p), - ("label", c_char_p) - ] - -class PluginListRefreshDialogResults(Structure): - _fields_ = [ - ("todo", c_char) + ("build", c_uint), + ("type", c_uint), + ("hints", c_uint), + ("category", c_char_p), + ("filename", c_char_p), + ("name", c_char_p), + ("label", c_char_p), + ("maker", c_char_p), + ("uniqueId", c_uint64), + ("audioIns", c_uint), + ("audioOuts", c_uint), + ("cvIns", c_uint), + ("cvOuts", c_uint), + ("midiIns", c_uint), + ("midiOuts", c_uint), + ("parametersIns", c_uint), + ("parametersOuts", c_uint), ] # ------------------------------------------------------------------------------------------------------------ @@ -82,9 +90,6 @@ class CarlaFrontendLib(): self.lib.carla_frontend_createAndExecPluginListDialog.argtypes = (c_void_p,) # , c_bool) self.lib.carla_frontend_createAndExecPluginListDialog.restype = POINTER(PluginListDialogResults) - self.lib.carla_frontend_createAndExecPluginListRefreshDialog.argtypes = (c_void_p, c_bool) - self.lib.carla_frontend_createAndExecPluginListRefreshDialog.restype = POINTER(PluginListRefreshDialogResults) - # -------------------------------------------------------------------------------------------------------- def createAndExecAboutJuceDialog(self, parent): @@ -97,8 +102,4 @@ class CarlaFrontendLib(): def createAndExecPluginListDialog(self, parent, useSystemIcons): return structToDictOrNull(self.lib.carla_frontend_createAndExecPluginListDialog(unwrapinstance(parent))) - def createAndExecPluginListRefreshDialog(self, parent, useSystemIcons): - return structToDictOrNull(self.lib.carla_frontend_createAndExecPluginListRefreshDialog(unwrapinstance(parent), - useSystemIcons)) - # ------------------------------------------------------------------------------------------------------------ diff --git a/source/frontend/carla_host.py b/source/frontend/carla_host.py index fcd10770c..3ee548521 100644 --- a/source/frontend/carla_host.py +++ b/source/frontend/carla_host.py @@ -62,7 +62,6 @@ from carla_utils import * from carla_widgets import * from patchcanvas import patchcanvas -from pluginlist import PluginListDialog from widgets.digitalpeakmeter import DigitalPeakMeter from widgets.pixmapkeyboard import PixmapKeyboardHArea @@ -1208,25 +1207,14 @@ class HostWindow(QMainWindow): # Plugins (menu actions) def showAddPluginDialog(self): - #ret = gCarla.felib.createAndExecPluginListDialog(self.fParentOrSelf, - #self.fSavedSettings[CARLA_KEY_MAIN_SYSTEM_ICONS]) - #print(ret) - #return - #ret = gCarla.felib.createAndExecPluginListRefreshDialog(self.fParentOrSelf, - #self.fSavedSettings[CARLA_KEY_MAIN_SYSTEM_ICONS]) - #print(ret) - #return - - if self.fPluginDatabaseDialog is None: - self.fPluginDatabaseDialog = PluginListDialog(self.fParentOrSelf, self.host, - self.fSavedSettings[CARLA_KEY_MAIN_SYSTEM_ICONS]) - dialog = self.fPluginDatabaseDialog - dialog.hasLoadedLv2Plugins = self.fHasLoadedLv2Plugins - - ret = dialog.exec_() - - if dialog.fFavoritePluginsChanged: - self.fFavoritePlugins = dialog.fFavoritePlugins + # TODO self.fHasLoadedLv2Plugins + ret = gCarla.felib.createAndExecPluginListDialog(self.fParentOrSelf, + self.fSavedSettings[CARLA_KEY_MAIN_SYSTEM_ICONS]) + print(ret) + + # TODO + #if dialog.fFavoritePluginsChanged: + #self.fFavoritePlugins = dialog.fFavoritePlugins if not ret: return @@ -1235,12 +1223,12 @@ class HostWindow(QMainWindow): QMessageBox.warning(self, self.tr("Warning"), self.tr("Cannot add new plugins while engine is stopped")) return - btype = dialog.fRetPlugin['build'] - ptype = dialog.fRetPlugin['type'] - filename = dialog.fRetPlugin['filename'] - label = dialog.fRetPlugin['label'] - uniqueId = dialog.fRetPlugin['uniqueId'] - extraPtr = self.getExtraPtr(dialog.fRetPlugin) + btype = ret['build'] + ptype = ret['type'] + filename = ret['filename'] + label = ret['label'] + uniqueId = ret['uniqueId'] + extraPtr = None return (btype, ptype, filename, label, uniqueId, extraPtr) diff --git a/source/frontend/pluginlist/__init__.py b/source/frontend/pluginlist/__init__.py deleted file mode 100644 index 0ebb033a3..000000000 --- a/source/frontend/pluginlist/__init__.py +++ /dev/null @@ -1,19 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -# Carla plugin host -# Copyright (C) 2011-2022 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. - -from .pluginlistdialog import PluginListDialog diff --git a/source/frontend/pluginlist/discovery.py b/source/frontend/pluginlist/discovery.py deleted file mode 100644 index 9eff5c231..000000000 --- a/source/frontend/pluginlist/discovery.py +++ /dev/null @@ -1,411 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -# Carla plugin list code -# Copyright (C) 2011-2022 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. - -# --------------------------------------------------------------------------------------------------------------------- -# Imports (Global) - -import os - -from copy import deepcopy -from subprocess import Popen, PIPE -from PyQt5.QtCore import qWarning - -# --------------------------------------------------------------------------------------------------------------------- -# Imports (Carla) - -from carla_backend import ( - BINARY_NATIVE, - BINARY_NONE, - PLUGIN_AU, - PLUGIN_DSSI, - PLUGIN_LADSPA, - PLUGIN_LV2, - PLUGIN_NONE, - PLUGIN_SF2, - PLUGIN_SFZ, - PLUGIN_VST2, - PLUGIN_VST3, - PLUGIN_CLAP, -) - -from carla_shared import ( - LINUX, - MACOS, - WINDOWS, -) - -from carla_utils import getPluginCategoryAsString - -# --------------------------------------------------------------------------------------------------------------------- -# Plugin Query (helper functions) - -def findBinaries(binPath, pluginType, OS): - binaries = [] - - if OS == "HAIKU": - extensions = ("") if pluginType == PLUGIN_VST2 else (".so",) - elif OS == "MACOS": - extensions = (".dylib", ".so") - elif OS == "WINDOWS": - extensions = (".dll",) - else: - extensions = (".so",) - - for root, _, files in os.walk(binPath): - for name in tuple(name for name in files if name.lower().endswith(extensions)): - binaries.append(os.path.join(root, name)) - - return binaries - -def findVST3Binaries(binPath): - binaries = [] - - for root, dirs, files in os.walk(binPath): - for name in tuple(name for name in (files+dirs) if name.lower().endswith(".vst3")): - binaries.append(os.path.join(root, name)) - - return binaries - -def findCLAPBinaries(binPath): - binaries = [] - - for root, _, files in os.walk(binPath, followlinks=True): - for name in tuple(name for name in files if name.lower().endswith(".clap")): - binaries.append(os.path.join(root, name)) - - return binaries - -def findLV2Bundles(bundlePath): - bundles = [] - - for root, _, _2 in os.walk(bundlePath, followlinks=True): - if root == bundlePath: - continue - if os.path.exists(os.path.join(root, "manifest.ttl")): - bundles.append(root) - - return bundles - -def findMacBundles(bundlePath, pluginType): - bundles = [] - - if pluginType == PLUGIN_VST2: - extension = ".vst" - elif pluginType == PLUGIN_VST3: - extension = ".vst3" - elif pluginType == PLUGIN_CLAP: - extension = ".clap" - else: - return bundles - - for root, dirs, _ in os.walk(bundlePath, followlinks=True): - for name in tuple(name for name in dirs if name.lower().endswith(extension)): - bundles.append(os.path.join(root, name)) - - return bundles - -def findFilenames(filePath, stype): - filenames = [] - - if stype == "sf2": - extensions = (".sf2",".sf3",) - else: - return [] - - for root, _, files in os.walk(filePath): - for name in tuple(name for name in files if name.lower().endswith(extensions)): - filenames.append(os.path.join(root, name)) - - return filenames - -# --------------------------------------------------------------------------------------------------------------------- -# Plugin Query - -# NOTE: this code is ugly, it is meant to be replaced, so let it be as-is for now - -PLUGIN_QUERY_API_VERSION = 12 - -PyPluginInfo = { - 'API': PLUGIN_QUERY_API_VERSION, - 'valid': False, - 'build': BINARY_NONE, - 'type': PLUGIN_NONE, - 'hints': 0x0, - 'category': "", - 'filename': "", - 'name': "", - 'label': "", - 'maker': "", - 'uniqueId': 0, - 'audio.ins': 0, - 'audio.outs': 0, - 'cv.ins': 0, - 'cv.outs': 0, - 'midi.ins': 0, - 'midi.outs': 0, - 'parameters.ins': 0, - 'parameters.outs': 0 -} - -gDiscoveryProcess = None - -def findWinePrefix(filename, recursionLimit = 10): - if recursionLimit == 0 or len(filename) < 5 or "/" not in filename: - return "" - - path = filename[:filename.rfind("/")] - - if os.path.isdir(path + "/dosdevices"): - return path - - return findWinePrefix(path, recursionLimit-1) - -def runCarlaDiscovery(itype, stype, filename, tool, wineSettings=None): - if not os.path.exists(tool): - qWarning(f"runCarlaDiscovery() - tool '{tool}' does not exist") - return [] - - command = [] - - if LINUX or MACOS: - command.append("env") - command.append("LANG=C") - command.append("LD_PRELOAD=") - if wineSettings is not None: - command.append("WINEDEBUG=-all") - - if wineSettings['autoPrefix']: - winePrefix = findWinePrefix(filename) - else: - winePrefix = "" - - if not winePrefix: - envWinePrefix = os.getenv("WINEPREFIX") - - if envWinePrefix: - winePrefix = envWinePrefix - elif wineSettings['fallbackPrefix']: - winePrefix = os.path.expanduser(wineSettings['fallbackPrefix']) - else: - winePrefix = os.path.expanduser("~/.wine") - - wineCMD = wineSettings['executable'] if wineSettings['executable'] else "wine" - - if tool.endswith("64.exe") and os.path.exists(wineCMD + "64"): - wineCMD += "64" - - command.append("WINEPREFIX=" + winePrefix) - command.append(wineCMD) - - command.append(tool) - command.append(stype) - command.append(filename) - - # pylint: disable=global-statement - global gDiscoveryProcess - # pylint: enable=global-statement - - # pylint: disable=consider-using-with - gDiscoveryProcess = Popen(command, stdout=PIPE) - # pylint: enable=consider-using-with - - pinfo = None - plugins = [] - fakeLabel = os.path.basename(filename).rsplit(".", 1)[0] - - while True: - try: - line = gDiscoveryProcess.stdout.readline().decode("utf-8", errors="ignore") - except: - print("ERROR: discovery readline failed") - break - - # line is valid, strip it - if line: - line = line.strip() - - # line is invalid, try poll() again - elif gDiscoveryProcess.poll() is None: - continue - - # line is invalid and poll() failed, stop here - else: - break - - if line == "carla-discovery::init::------------": - pinfo = deepcopy(PyPluginInfo) - pinfo['type'] = itype - pinfo['filename'] = filename if filename != ":all" else "" - - elif line == "carla-discovery::end::------------": - if pinfo is not None: - plugins.append(pinfo) - del pinfo - pinfo = None - - elif line == "Segmentation fault": - print(f"carla-discovery::crash::{filename} crashed during discovery") - - elif line.startswith("err:module:import_dll Library"): - print(line) - - elif line.startswith("carla-discovery::info::"): - print(f"{line} - {filename}") - - elif line.startswith("carla-discovery::warning::"): - print(f"{line} - {filename}") - - elif line.startswith("carla-discovery::error::"): - print(f"{line} - {filename}") - - elif line.startswith("carla-discovery::"): - if pinfo is None: - continue - - try: - prop, value = line.replace("carla-discovery::", "").split("::", 1) - except: - continue - - # pylint: disable=unsupported-assignment-operation - if prop == "build": - if value.isdigit(): - pinfo['build'] = int(value) - elif prop == "name": - pinfo['name'] = value if value else fakeLabel - elif prop == "label": - pinfo['label'] = value if value else fakeLabel - elif prop == "filename": - pinfo['filename'] = value - elif prop == "maker": - pinfo['maker'] = value - elif prop == "category": - pinfo['category'] = value - elif prop == "uniqueId": - if value.isdigit(): - pinfo['uniqueId'] = int(value) - elif prop == "hints": - if value.isdigit(): - pinfo['hints'] = int(value) - elif prop == "audio.ins": - if value.isdigit(): - pinfo['audio.ins'] = int(value) - elif prop == "audio.outs": - if value.isdigit(): - pinfo['audio.outs'] = int(value) - elif prop == "cv.ins": - if value.isdigit(): - pinfo['cv.ins'] = int(value) - elif prop == "cv.outs": - if value.isdigit(): - pinfo['cv.outs'] = int(value) - elif prop == "midi.ins": - if value.isdigit(): - pinfo['midi.ins'] = int(value) - elif prop == "midi.outs": - if value.isdigit(): - pinfo['midi.outs'] = int(value) - elif prop == "parameters.ins": - if value.isdigit(): - pinfo['parameters.ins'] = int(value) - elif prop == "parameters.outs": - if value.isdigit(): - pinfo['parameters.outs'] = int(value) - elif prop == "uri": - if value: - pinfo['label'] = value - else: - # cannot use empty URIs - del pinfo - pinfo = None - continue - else: - print(f"{line} - {filename} (unknown property)") - # pylint: enable=unsupported-assignment-operation - - tmp = gDiscoveryProcess - gDiscoveryProcess = None - del tmp - - return plugins - -def killDiscovery(): - # pylint: disable=global-variable-not-assigned - global gDiscoveryProcess - # pylint: enable=global-variable-not-assigned - - if gDiscoveryProcess is not None: - gDiscoveryProcess.kill() - -def checkPluginCached(desc, ptype): - pinfo = deepcopy(PyPluginInfo) - 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['audio.ins'] = desc['audioIns'] - pinfo['audio.outs'] = desc['audioOuts'] - - pinfo['cv.ins'] = desc['cvIns'] - pinfo['cv.outs'] = desc['cvOuts'] - - pinfo['midi.ins'] = desc['midiIns'] - pinfo['midi.outs'] = desc['midiOuts'] - - pinfo['parameters.ins'] = desc['parameterIns'] - pinfo['parameters.outs'] = desc['parameterOuts'] - - if ptype == PLUGIN_LV2: - pinfo['filename'], pinfo['label'] = pinfo['label'].split('\\' if WINDOWS else '/',1) - - elif ptype == PLUGIN_SFZ: - pinfo['filename'] = pinfo['label'] - pinfo['label'] = pinfo['name'] - - return pinfo - -def checkPluginLADSPA(filename, tool, wineSettings=None): - return runCarlaDiscovery(PLUGIN_LADSPA, "LADSPA", filename, tool, wineSettings) - -def checkPluginDSSI(filename, tool, wineSettings=None): - return runCarlaDiscovery(PLUGIN_DSSI, "DSSI", filename, tool, wineSettings) - -def checkPluginLV2(filename, tool, wineSettings=None): - return runCarlaDiscovery(PLUGIN_LV2, "LV2", filename, tool, wineSettings) - -def checkPluginVST2(filename, tool, wineSettings=None): - return runCarlaDiscovery(PLUGIN_VST2, "VST2", filename, tool, wineSettings) - -def checkPluginVST3(filename, tool, wineSettings=None): - return runCarlaDiscovery(PLUGIN_VST3, "VST3", filename, tool, wineSettings) - -def checkPluginCLAP(filename, tool, wineSettings=None): - return runCarlaDiscovery(PLUGIN_CLAP, "CLAP", filename, tool, wineSettings) - -def checkFileSF2(filename, tool): - return runCarlaDiscovery(PLUGIN_SF2, "SF2", filename, tool) - -def checkFileSFZ(filename, tool): - return runCarlaDiscovery(PLUGIN_SFZ, "SFZ", filename, tool) - -def checkAllPluginsAU(tool): - return runCarlaDiscovery(PLUGIN_AU, "AU", ":all", tool) diff --git a/source/frontend/pluginlist/discoverythread.py b/source/frontend/pluginlist/discoverythread.py deleted file mode 100644 index 852710310..000000000 --- a/source/frontend/pluginlist/discoverythread.py +++ /dev/null @@ -1,851 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -# Carla plugin host -# Copyright (C) 2011-2022 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. - -# --------------------------------------------------------------------------------------------------------------------- -# Imports (Global) - -import os - -from PyQt5.QtCore import pyqtSignal, QThread -from PyQt5.QtWidgets import QWidget - -# --------------------------------------------------------------------------------------------------------------------- -# Imports (Custom) - -from carla_backend import ( - PLUGIN_AU, - PLUGIN_DSSI, - PLUGIN_JSFX, - PLUGIN_LADSPA, - PLUGIN_LV2, - PLUGIN_SFZ, - PLUGIN_VST2, - PLUGIN_VST3, - PLUGIN_CLAP, -) - -from carla_shared import ( - CARLA_DEFAULT_DSSI_PATH, - CARLA_DEFAULT_JSFX_PATH, - CARLA_DEFAULT_LADSPA_PATH, - CARLA_DEFAULT_LV2_PATH, - CARLA_DEFAULT_SF2_PATH, - CARLA_DEFAULT_SFZ_PATH, - CARLA_DEFAULT_VST2_PATH, - CARLA_DEFAULT_VST3_PATH, - CARLA_DEFAULT_CLAP_PATH, - CARLA_DEFAULT_WINE_AUTO_PREFIX, - CARLA_DEFAULT_WINE_EXECUTABLE, - CARLA_DEFAULT_WINE_FALLBACK_PREFIX, - CARLA_KEY_PATHS_DSSI, - CARLA_KEY_PATHS_JSFX, - CARLA_KEY_PATHS_LADSPA, - CARLA_KEY_PATHS_LV2, - CARLA_KEY_PATHS_SF2, - CARLA_KEY_PATHS_SFZ, - CARLA_KEY_PATHS_VST2, - CARLA_KEY_PATHS_VST3, - CARLA_KEY_PATHS_CLAP, - CARLA_KEY_WINE_AUTO_PREFIX, - CARLA_KEY_WINE_EXECUTABLE, - CARLA_KEY_WINE_FALLBACK_PREFIX, - HAIKU, - LINUX, - MACOS, - WINDOWS, - gCarla, - splitter, -) - -from utils import QSafeSettings - -from .discovery import ( - checkAllPluginsAU, - checkFileSF2, - checkPluginCached, - checkPluginDSSI, - checkPluginLADSPA, - checkPluginVST2, - checkPluginVST3, - checkPluginCLAP, - findBinaries, - findFilenames, - findMacBundles, - findVST3Binaries, - findCLAPBinaries -) - -# --------------------------------------------------------------------------------------------------------------------- -# Separate Thread for Plugin Search - -class SearchPluginsThread(QThread): - pluginLook = pyqtSignal(float, str) - - def __init__(self, parent: QWidget, pathBinaries: str): - QThread.__init__(self, parent) - - self.fContinueChecking = False - self.fPathBinaries = pathBinaries - - self.fCheckNative = False - self.fCheckPosix32 = False - self.fCheckPosix64 = False - self.fCheckWin32 = False - self.fCheckWin64 = False - - self.fCheckLADSPA = False - self.fCheckDSSI = False - self.fCheckLV2 = False - self.fCheckVST2 = False - self.fCheckVST3 = False - self.fCheckCLAP = False - self.fCheckAU = False - self.fCheckSF2 = False - self.fCheckSFZ = False - self.fCheckJSFX = False - - if WINDOWS: - toolNative = "carla-discovery-native.exe" - self.fWineSettings = None - - else: - toolNative = "carla-discovery-native" - settings = QSafeSettings("falkTX", "Carla2") - self.fWineSettings = { - 'executable' : settings.value(CARLA_KEY_WINE_EXECUTABLE, - CARLA_DEFAULT_WINE_EXECUTABLE, str), - 'autoPrefix' : settings.value(CARLA_KEY_WINE_AUTO_PREFIX, - CARLA_DEFAULT_WINE_AUTO_PREFIX, bool), - 'fallbackPrefix': settings.value(CARLA_KEY_WINE_FALLBACK_PREFIX, - CARLA_DEFAULT_WINE_FALLBACK_PREFIX, str), - } - del settings - - self.fToolNative = os.path.join(pathBinaries, toolNative) - - if not os.path.exists(self.fToolNative): - self.fToolNative = "" - - self.fCurCount = 0 - self.fCurPercentValue = 0 - self.fLastCheckValue = 0 - self.fSomethingChanged = False - - # ----------------------------------------------------------------------------------------------------------------- - # public methods - - def hasSomethingChanged(self): - return self.fSomethingChanged - - def setSearchBinaryTypes(self, native: bool, posix32: bool, posix64: bool, win32: bool, win64: bool): - self.fCheckNative = native - self.fCheckPosix32 = posix32 - self.fCheckPosix64 = posix64 - self.fCheckWin32 = win32 - self.fCheckWin64 = win64 - - def setSearchPluginTypes(self, ladspa: bool, dssi: bool, lv2: bool, vst2: bool, vst3: bool, clap: bool, - au: bool, sf2: bool, sfz: bool, jsfx: bool): - self.fCheckLADSPA = ladspa - self.fCheckDSSI = dssi - self.fCheckLV2 = lv2 - self.fCheckVST2 = vst2 - self.fCheckVST3 = vst3 - self.fCheckCLAP = clap - self.fCheckAU = au and MACOS - self.fCheckSF2 = sf2 - self.fCheckSFZ = sfz - self.fCheckJSFX = jsfx - - def stop(self): - self.fContinueChecking = False - - # ----------------------------------------------------------------------------------------------------------------- - # protected reimplemented methods - - def run(self): - settingsDB = QSafeSettings("falkTX", "CarlaPlugins5") - - self.fContinueChecking = True - self.fCurCount = 0 - - if self.fCheckNative and not self.fToolNative: - self.fCheckNative = False - - # looking for plugins via external discovery - pluginCount = 0 - - if self.fCheckLADSPA: - pluginCount += 1 - if self.fCheckDSSI: - pluginCount += 1 - if self.fCheckVST2: - pluginCount += 1 - if self.fCheckVST3: - pluginCount += 1 - if self.fCheckCLAP: - pluginCount += 1 - - # Increase count by the number of externally discoverable plugin types - if self.fCheckNative: - self.fCurCount += pluginCount - - if self.fCheckPosix32: - self.fCurCount += pluginCount - - if self.fCheckPosix64: - self.fCurCount += pluginCount - - if self.fCheckWin32: - self.fCurCount += pluginCount - - if self.fCheckWin64: - self.fCurCount += pluginCount - - if self.fCheckLV2: - if self.fCheckNative: - self.fCurCount += 1 - else: - self.fCheckLV2 = False - - if self.fCheckAU: - if self.fCheckNative or self.fCheckPosix32: - self.fCurCount += int(self.fCheckNative) + int(self.fCheckPosix32) - else: - self.fCheckAU = False - - if self.fCheckSF2: - if self.fCheckNative: - self.fCurCount += 1 - else: - self.fCheckSF2 = False - - if self.fCheckSFZ: - if self.fCheckNative: - self.fCurCount += 1 - else: - self.fCheckSFZ = False - - if self.fCheckJSFX: - if self.fCheckNative: - self.fCurCount += 1 - else: - self.fCheckJSFX = False - - if self.fCurCount == 0: - return - - self.fCurPercentValue = 100.0 / self.fCurCount - self.fLastCheckValue = 0.0 - - del pluginCount - - if HAIKU: - OS = "HAIKU" - elif LINUX: - OS = "LINUX" - elif MACOS: - OS = "MACOS" - elif WINDOWS: - OS = "WINDOWS" - else: - OS = "UNKNOWN" - - if not self.fContinueChecking: - return - - self.fSomethingChanged = True - - if self.fCheckLADSPA: - if self.fCheckNative: - plugins = self._checkLADSPA(OS, self.fToolNative) - settingsDB.setValue("Plugins/LADSPA_native", plugins) - if not self.fContinueChecking: - return - - if self.fCheckPosix32: - tool = os.path.join(self.fPathBinaries, "carla-discovery-posix32") - plugins = self._checkLADSPA(OS, tool) - settingsDB.setValue("Plugins/LADSPA_posix32", plugins) - if not self.fContinueChecking: - return - - if self.fCheckPosix64: - tool = os.path.join(self.fPathBinaries, "carla-discovery-posix64") - plugins = self._checkLADSPA(OS, tool) - settingsDB.setValue("Plugins/LADSPA_posix64", plugins) - if not self.fContinueChecking: - return - - if self.fCheckWin32: - tool = os.path.join(self.fPathBinaries, "carla-discovery-win32.exe") - plugins = self._checkLADSPA("WINDOWS", tool, not WINDOWS) - settingsDB.setValue("Plugins/LADSPA_win32", plugins) - if not self.fContinueChecking: - return - - if self.fCheckWin64: - tool = os.path.join(self.fPathBinaries, "carla-discovery-win64.exe") - plugins = self._checkLADSPA("WINDOWS", tool, not WINDOWS) - settingsDB.setValue("Plugins/LADSPA_win64", plugins) - - settingsDB.sync() - if not self.fContinueChecking: - return - - if self.fCheckDSSI: - if self.fCheckNative: - plugins = self._checkDSSI(OS, self.fToolNative) - settingsDB.setValue("Plugins/DSSI_native", plugins) - if not self.fContinueChecking: - return - - if self.fCheckPosix32: - plugins = self._checkDSSI(OS, os.path.join(self.fPathBinaries, "carla-discovery-posix32")) - settingsDB.setValue("Plugins/DSSI_posix32", plugins) - if not self.fContinueChecking: - return - - if self.fCheckPosix64: - plugins = self._checkDSSI(OS, os.path.join(self.fPathBinaries, "carla-discovery-posix64")) - settingsDB.setValue("Plugins/DSSI_posix64", plugins) - if not self.fContinueChecking: - return - - if self.fCheckWin32: - tool = os.path.join(self.fPathBinaries, "carla-discovery-win32.exe") - plugins = self._checkDSSI("WINDOWS", tool, not WINDOWS) - settingsDB.setValue("Plugins/DSSI_win32", plugins) - if not self.fContinueChecking: - return - - if self.fCheckWin64: - tool = os.path.join(self.fPathBinaries, "carla-discovery-win64.exe") - plugins = self._checkDSSI("WINDOWS", tool, not WINDOWS) - settingsDB.setValue("Plugins/DSSI_win64", plugins) - - settingsDB.sync() - if not self.fContinueChecking: - return - - if self.fCheckLV2: - plugins = self._checkCached(True) - settingsDB.setValue("Plugins/LV2", plugins) - settingsDB.sync() - if not self.fContinueChecking: - return - - if self.fCheckVST2: - if self.fCheckNative: - plugins = self._checkVST2(OS, self.fToolNative) - settingsDB.setValue("Plugins/VST2_native", plugins) - if not self.fContinueChecking: - return - - if self.fCheckPosix32: - plugins = self._checkVST2(OS, os.path.join(self.fPathBinaries, "carla-discovery-posix32")) - settingsDB.setValue("Plugins/VST2_posix32", plugins) - if not self.fContinueChecking: - return - - if self.fCheckPosix64: - plugins = self._checkVST2(OS, os.path.join(self.fPathBinaries, "carla-discovery-posix64")) - settingsDB.setValue("Plugins/VST2_posix64", plugins) - if not self.fContinueChecking: - return - - if self.fCheckWin32: - tool = os.path.join(self.fPathBinaries, "carla-discovery-win32.exe") - plugins = self._checkVST2("WINDOWS", tool, not WINDOWS) - settingsDB.setValue("Plugins/VST2_win32", plugins) - if not self.fContinueChecking: - return - - if self.fCheckWin64: - tool = os.path.join(self.fPathBinaries, "carla-discovery-win64.exe") - plugins = self._checkVST2("WINDOWS", tool, not WINDOWS) - settingsDB.setValue("Plugins/VST2_win64", plugins) - if not self.fContinueChecking: - return - - settingsDB.sync() - if not self.fContinueChecking: - return - - if self.fCheckVST3: - if self.fCheckNative and (LINUX or MACOS or WINDOWS): - plugins = self._checkVST3(self.fToolNative) - settingsDB.setValue("Plugins/VST3_native", plugins) - if not self.fContinueChecking: - return - - if self.fCheckPosix32: - plugins = self._checkVST3(os.path.join(self.fPathBinaries, "carla-discovery-posix32")) - settingsDB.setValue("Plugins/VST3_posix32", plugins) - if not self.fContinueChecking: - return - - if self.fCheckPosix64: - plugins = self._checkVST3(os.path.join(self.fPathBinaries, "carla-discovery-posix64")) - settingsDB.setValue("Plugins/VST3_posix64", plugins) - if not self.fContinueChecking: - return - - if self.fCheckWin32: - tool = os.path.join(self.fPathBinaries, "carla-discovery-win32.exe") - plugins = self._checkVST3(tool, not WINDOWS) - settingsDB.setValue("Plugins/VST3_win32", plugins) - if not self.fContinueChecking: - return - - if self.fCheckWin64: - tool = os.path.join(self.fPathBinaries, "carla-discovery-win64.exe") - plugins = self._checkVST3(tool, not WINDOWS) - settingsDB.setValue("Plugins/VST3_win64", plugins) - if not self.fContinueChecking: - return - - settingsDB.sync() - if not self.fContinueChecking: - return - - if self.fCheckCLAP: - if self.fCheckNative: - plugins = self._checkCLAP(self.fToolNative) - settingsDB.setValue("Plugins/CLAP_native", plugins) - if not self.fContinueChecking: - return - - if self.fCheckPosix32: - plugins = self._checkCLAP(os.path.join(self.fPathBinaries, "carla-discovery-posix32")) - settingsDB.setValue("Plugins/CLAP_posix32", plugins) - if not self.fContinueChecking: - return - - if self.fCheckPosix64: - plugins = self._checkCLAP(os.path.join(self.fPathBinaries, "carla-discovery-posix64")) - settingsDB.setValue("Plugins/CLAP_posix64", plugins) - if not self.fContinueChecking: - return - - if self.fCheckWin32: - tool = os.path.join(self.fPathBinaries, "carla-discovery-win32.exe") - plugins = self._checkCLAP(tool, not WINDOWS) - settingsDB.setValue("Plugins/CLAP_win32", plugins) - if not self.fContinueChecking: - return - - if self.fCheckWin64: - tool = os.path.join(self.fPathBinaries, "carla-discovery-win64.exe") - plugins = self._checkCLAP(tool, not WINDOWS) - settingsDB.setValue("Plugins/CLAP_win64", plugins) - if not self.fContinueChecking: - return - - settingsDB.sync() - if not self.fContinueChecking: - return - - if self.fCheckAU: - if self.fCheckNative: - plugins = self._checkCached(False) - settingsDB.setValue("Plugins/AU", plugins) - settingsDB.sync() - if not self.fContinueChecking: - return - - if self.fCheckPosix32: - plugins = self._checkAU(os.path.join(self.fPathBinaries, "carla-discovery-posix32")) - settingsDB.setValue("Plugins/AU_posix32", self.fAuPlugins) - if not self.fContinueChecking: - return - - settingsDB.sync() - if not self.fContinueChecking: - return - - if self.fCheckSF2: - settings = QSafeSettings("falkTX", "Carla2") - SF2_PATH = settings.value(CARLA_KEY_PATHS_SF2, CARLA_DEFAULT_SF2_PATH, list) - del settings - - kits = self._checkKIT(SF2_PATH, "sf2") - settingsDB.setValue("Plugins/SF2", kits) - settingsDB.sync() - if not self.fContinueChecking: - return - - if self.fCheckSFZ: - kits = self._checkSfzCached() - settingsDB.setValue("Plugins/SFZ", kits) - settingsDB.sync() - - if self.fCheckJSFX: - kits = self._checkJsfxCached() - settingsDB.setValue("Plugins/JSFX", kits) - settingsDB.sync() - - # ----------------------------------------------------------------------------------------------------------------- - # private methods - - def _checkLADSPA(self, OS, tool, isWine=False): - ladspaBinaries = [] - ladspaPlugins = [] - - self._pluginLook(self.fLastCheckValue, "LADSPA plugins...") - - settings = QSafeSettings("falkTX", "Carla2") - LADSPA_PATH = settings.value(CARLA_KEY_PATHS_LADSPA, CARLA_DEFAULT_LADSPA_PATH, list) - del settings - - for iPATH in LADSPA_PATH: - binaries = findBinaries(iPATH, PLUGIN_LADSPA, OS) - for binary in binaries: - if binary not in ladspaBinaries: - ladspaBinaries.append(binary) - - ladspaBinaries.sort() - - if not self.fContinueChecking: - return ladspaPlugins - - for i in range(len(ladspaBinaries)): - ladspa = ladspaBinaries[i] - percent = ( float(i) / len(ladspaBinaries) ) * self.fCurPercentValue - self._pluginLook((self.fLastCheckValue + percent) * 0.9, ladspa) - - plugins = checkPluginLADSPA(ladspa, tool, self.fWineSettings if isWine else None) - if plugins: - ladspaPlugins.append(plugins) - - if not self.fContinueChecking: - break - - self.fLastCheckValue += self.fCurPercentValue - return ladspaPlugins - - def _checkDSSI(self, OS, tool, isWine=False): - dssiBinaries = [] - dssiPlugins = [] - - self._pluginLook(self.fLastCheckValue, "DSSI plugins...") - - settings = QSafeSettings("falkTX", "Carla2") - DSSI_PATH = settings.value(CARLA_KEY_PATHS_DSSI, CARLA_DEFAULT_DSSI_PATH, list) - del settings - - for iPATH in DSSI_PATH: - binaries = findBinaries(iPATH, PLUGIN_DSSI, OS) - for binary in binaries: - if binary not in dssiBinaries: - dssiBinaries.append(binary) - - dssiBinaries.sort() - - if not self.fContinueChecking: - return dssiPlugins - - for i in range(len(dssiBinaries)): - dssi = dssiBinaries[i] - percent = ( float(i) / len(dssiBinaries) ) * self.fCurPercentValue - self._pluginLook(self.fLastCheckValue + percent, dssi) - - plugins = checkPluginDSSI(dssi, tool, self.fWineSettings if isWine else None) - if plugins: - dssiPlugins.append(plugins) - - if not self.fContinueChecking: - break - - self.fLastCheckValue += self.fCurPercentValue - return dssiPlugins - - def _checkVST2(self, OS, tool, isWine=False): - vst2Binaries = [] - vst2Plugins = [] - - if MACOS and not isWine: - self._pluginLook(self.fLastCheckValue, "VST2 bundles...") - else: - self._pluginLook(self.fLastCheckValue, "VST2 plugins...") - - settings = QSafeSettings("falkTX", "Carla2") - VST2_PATH = settings.value(CARLA_KEY_PATHS_VST2, CARLA_DEFAULT_VST2_PATH, list) - del settings - - for iPATH in VST2_PATH: - if MACOS and not isWine: - binaries = findMacBundles(iPATH, PLUGIN_VST2) - else: - binaries = findBinaries(iPATH, PLUGIN_VST2, OS) - for binary in binaries: - if binary not in vst2Binaries: - vst2Binaries.append(binary) - - vst2Binaries.sort() - - if not self.fContinueChecking: - return vst2Plugins - - for i in range(len(vst2Binaries)): - vst2 = vst2Binaries[i] - percent = ( float(i) / len(vst2Binaries) ) * self.fCurPercentValue - self._pluginLook(self.fLastCheckValue + percent, vst2) - - plugins = checkPluginVST2(vst2, tool, self.fWineSettings if isWine else None) - if plugins: - vst2Plugins.append(plugins) - - if not self.fContinueChecking: - break - - self.fLastCheckValue += self.fCurPercentValue - return vst2Plugins - - def _checkVST3(self, tool, isWine=False): - vst3Binaries = [] - vst3Plugins = [] - - if MACOS and not isWine: - self._pluginLook(self.fLastCheckValue, "VST3 bundles...") - else: - self._pluginLook(self.fLastCheckValue, "VST3 plugins...") - - settings = QSafeSettings("falkTX", "Carla2") - VST3_PATH = settings.value(CARLA_KEY_PATHS_VST3, CARLA_DEFAULT_VST3_PATH, list) - del settings - - for iPATH in VST3_PATH: - if MACOS and not isWine: - binaries = findMacBundles(iPATH, PLUGIN_VST3) - else: - binaries = findVST3Binaries(iPATH) - for binary in binaries: - if binary not in vst3Binaries: - vst3Binaries.append(binary) - - vst3Binaries.sort() - - if not self.fContinueChecking: - return vst3Plugins - - for i in range(len(vst3Binaries)): - vst3 = vst3Binaries[i] - percent = ( float(i) / len(vst3Binaries) ) * self.fCurPercentValue - self._pluginLook(self.fLastCheckValue + percent, vst3) - - plugins = checkPluginVST3(vst3, tool, self.fWineSettings if isWine else None) - if plugins: - vst3Plugins.append(plugins) - - if not self.fContinueChecking: - break - - self.fLastCheckValue += self.fCurPercentValue - return vst3Plugins - - def _checkCLAP(self, tool, isWine=False): - clapBinaries = [] - clapPlugins = [] - - self._pluginLook(self.fLastCheckValue, "CLAP plugins...") - - settings = QSafeSettings("falkTX", "Carla2") - CLAP_PATH = settings.value(CARLA_KEY_PATHS_CLAP, CARLA_DEFAULT_CLAP_PATH, list) - del settings - - for iPATH in CLAP_PATH: - if MACOS and not isWine: - binaries = findMacBundles(iPATH, PLUGIN_CLAP) - else: - binaries = findCLAPBinaries(iPATH) - for binary in binaries: - if binary not in clapBinaries: - clapBinaries.append(binary) - - clapBinaries.sort() - - if not self.fContinueChecking: - return clapPlugins - - for i in range(len(clapBinaries)): - clap = clapBinaries[i] - percent = ( float(i) / len(clapBinaries) ) * self.fCurPercentValue - self._pluginLook(self.fLastCheckValue + percent, clap) - - plugins = checkPluginCLAP(clap, tool, self.fWineSettings if isWine else None) - if plugins: - clapPlugins.append(plugins) - - if not self.fContinueChecking: - break - - self.fLastCheckValue += self.fCurPercentValue - return clapPlugins - - def _checkAU(self, tool): - auPlugins = [] - - plugins = checkAllPluginsAU(tool) - if plugins: - auPlugins.append(plugins) - - self.fLastCheckValue += self.fCurPercentValue - return auPlugins - - def _checkKIT(self, kitPATH, kitExtension): - kitFiles = [] - kitPlugins = [] - - for iPATH in kitPATH: - files = findFilenames(iPATH, kitExtension) - for file_ in files: - if file_ not in kitFiles: - kitFiles.append(file_) - - kitFiles.sort() - - if not self.fContinueChecking: - return kitPlugins - - for i in range(len(kitFiles)): - kit = kitFiles[i] - percent = ( float(i) / len(kitFiles) ) * self.fCurPercentValue - self._pluginLook(self.fLastCheckValue + percent, kit) - - if kitExtension == "sf2": - plugins = checkFileSF2(kit, self.fToolNative) - else: - plugins = None - - if plugins: - kitPlugins.append(plugins) - - if not self.fContinueChecking: - break - - self.fLastCheckValue += self.fCurPercentValue - return kitPlugins - - def _checkCached(self, isLV2): - if isLV2: - settings = QSafeSettings("falkTX", "Carla2") - PLUG_PATH = splitter.join(settings.value(CARLA_KEY_PATHS_LV2, CARLA_DEFAULT_LV2_PATH, list)) - del settings - PLUG_TEXT = "LV2" - PLUG_TYPE = PLUGIN_LV2 - else: # AU - PLUG_PATH = "" - PLUG_TEXT = "AU" - PLUG_TYPE = PLUGIN_AU - - plugins = [] - self._pluginLook(self.fLastCheckValue, f"{PLUG_TEXT} plugins...") - - if not isLV2: - gCarla.utils.juce_init() - - count = gCarla.utils.get_cached_plugin_count(PLUG_TYPE, PLUG_PATH) - - if not self.fContinueChecking: - return plugins - - for i in range(count): - descInfo = gCarla.utils.get_cached_plugin_info(PLUG_TYPE, i) - - percent = ( float(i) / count ) * self.fCurPercentValue - self._pluginLook(self.fLastCheckValue + percent, descInfo['label']) - - if not descInfo['valid']: - continue - - plugins.append(checkPluginCached(descInfo, PLUG_TYPE)) - - if not self.fContinueChecking: - break - - if not isLV2: - gCarla.utils.juce_cleanup() - - self.fLastCheckValue += self.fCurPercentValue - return plugins - - def _checkSfzCached(self): - settings = QSafeSettings("falkTX", "Carla2") - PLUG_PATH = splitter.join(settings.value(CARLA_KEY_PATHS_SFZ, CARLA_DEFAULT_SFZ_PATH, list)) - del settings - - sfzKits = [] - self._pluginLook(self.fLastCheckValue, "SFZ kits...") - - count = gCarla.utils.get_cached_plugin_count(PLUGIN_SFZ, PLUG_PATH) - - if not self.fContinueChecking: - return sfzKits - - for i in range(count): - descInfo = gCarla.utils.get_cached_plugin_info(PLUGIN_SFZ, i) - - percent = ( float(i) / count ) * self.fCurPercentValue - self._pluginLook(self.fLastCheckValue + percent, descInfo['label']) - - if not descInfo['valid']: - continue - - sfzKits.append(checkPluginCached(descInfo, PLUGIN_SFZ)) - - if not self.fContinueChecking: - break - - self.fLastCheckValue += self.fCurPercentValue - return sfzKits - - def _checkJsfxCached(self): - settings = QSafeSettings("falkTX", "Carla2") - PLUG_PATH = splitter.join(settings.value(CARLA_KEY_PATHS_JSFX, CARLA_DEFAULT_JSFX_PATH, list)) - del settings - - jsfxPlugins = [] - self._pluginLook(self.fLastCheckValue, "JSFX plugins...") - - count = gCarla.utils.get_cached_plugin_count(PLUGIN_JSFX, PLUG_PATH) - - if not self.fContinueChecking: - return jsfxPlugins - - for i in range(count): - descInfo = gCarla.utils.get_cached_plugin_info(PLUGIN_JSFX, i) - - percent = ( float(i) / count ) * self.fCurPercentValue - self._pluginLook(self.fLastCheckValue + percent, descInfo['label']) - - if not descInfo['valid']: - continue - - jsfxPlugins.append(checkPluginCached(descInfo, PLUGIN_JSFX)) - - if not self.fContinueChecking: - break - - self.fLastCheckValue += self.fCurPercentValue - return jsfxPlugins - - def _pluginLook(self, percent, plugin): - self.pluginLook.emit(percent, plugin) - -# --------------------------------------------------------------------------------------------------------------------- diff --git a/source/frontend/pluginlist/pluginlistdialog.cpp b/source/frontend/pluginlist/pluginlistdialog.cpp index 8d3385052..e7def2bbd 100644 --- a/source/frontend/pluginlist/pluginlistdialog.cpp +++ b/source/frontend/pluginlist/pluginlistdialog.cpp @@ -1,21 +1,11 @@ /* * 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. + * SPDX-License-Identifier: GPL-2.0-or-later */ #include "pluginlistdialog.hpp" +#include "pluginrefreshdialog.hpp" #ifdef __clang__ # pragma clang diagnostic push @@ -27,7 +17,7 @@ # pragma GCC diagnostic ignored "-Wdeprecated-copy" #endif -#include "ui_pluginlistdialog.h" +#include #include #include #include @@ -42,6 +32,7 @@ #include "qsafesettings.hpp" #include "CarlaBackendUtils.hpp" +#include "CarlaJuceUtils.hpp" #include "CarlaFrontend.h" #include "CarlaUtils.h" @@ -49,12 +40,6 @@ #include -#ifdef BUILDING_CARLA_OBS -extern "C" { -const char *get_carla_bin_path(void); -} -#endif - CARLA_BACKEND_USE_NAMESPACE // -------------------------------------------------------------------------------------------------------------------- @@ -74,9 +59,10 @@ CARLA_BACKEND_USE_NAMESPACE // Carla Settings defaults // -------------------------------------------------------------------------------------------------------------------- -// utils +// getenv with a fallback value if unset -const char* getEnv(const char* const env, const char* const fallback) +static inline +const char* getEnvWithFallback(const char* const env, const char* const fallback) { if (const char* const value = std::getenv(env)) return value; @@ -84,80 +70,31 @@ const char* getEnv(const char* const env, const char* const fallback) 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) +// Plugin paths (from env vars first, then default locations) -struct DefaultPaths { +struct PluginPaths { QCarlaString ladspa; QCarlaString dssi; QCarlaString lv2; QCarlaString vst2; QCarlaString vst3; QCarlaString clap; + QCarlaString jsfx; QCarlaString sf2; QCarlaString sfz; - QCarlaString jsfx; - void init() + PluginPaths() { - const QCarlaString HOME = getHome(); + // get common env vars + const QString HOME = QDir::toNativeSeparators(QDir::homePath()); #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 envAPPDATA = std::getenv("APPDATA"); + const char *const envLOCALAPPDATA = getEnvWithFallback("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 envCOMMONPROGRAMFILES = std::getenv("COMMONPROGRAMFILES"); const char* const envCOMMONPROGRAMFILESx86 = std::getenv("COMMONPROGRAMFILES(x86)"); // Small integrity tests @@ -183,43 +120,213 @@ struct DefaultPaths { const QCarlaString LOCALAPPDATA(envLOCALAPPDATA); const QCarlaString PROGRAMFILES(envPROGRAMFILES); const QCarlaString COMMONPROGRAMFILES(envCOMMONPROGRAMFILES); +#elif !defined(CARLA_OS_MAC) + const QCarlaString CONFIG_HOME(getEnvWithFallback("XDG_CONFIG_HOME", (HOME + "/.config").toUtf8())); +#endif - ladspa = APPDATA + "\\LADSPA"; - ladspa += ";" + PROGRAMFILES + "\\LADSPA"; + // now set paths, listing format path spec if available + if (const char *const envLADSPA = std::getenv("LADSPA_PATH")) + { + ladspa = envLADSPA; + } + else + { + // no official spec, use common paths +#if defined(CARLA_OS_WIN) + ladspa = APPDATA + "\\LADSPA"; + ladspa += ";" + PROGRAMFILES + "\\LADSPA"; +#elif defined(CARLA_OS_HAIKU) + ladspa = HOME + "/.ladspa"; + ladspa += ":/system/add-ons/media/ladspaplugins"; + ladspa += ":/system/lib/ladspa"; +#elif defined(CARLA_OS_MAC) + ladspa = HOME + "/Library/Audio/Plug-Ins/LADSPA"; + ladspa += ":/Library/Audio/Plug-Ins/LADSPA"; +#else + ladspa = HOME + "/.ladspa"; + ladspa += ":/usr/local/lib/ladspa"; + ladspa += ":/usr/lib/ladspa"; +#endif + } - dssi = APPDATA + "\\DSSI"; - dssi += ";" + PROGRAMFILES + "\\DSSI"; + if (const char *const envDSSI = std::getenv("DSSI_PATH")) + { + dssi = envDSSI; + } + else + { + // no official spec, use common paths +#if defined(CARLA_OS_WIN) + dssi = APPDATA + "\\DSSI"; + dssi += ";" + PROGRAMFILES + "\\DSSI"; +#elif defined(CARLA_OS_HAIKU) + dssi = HOME + "/.dssi"; + dssi += ":/system/add-ons/media/dssiplugins"; + dssi += ":/system/lib/dssi"; +#elif defined(CARLA_OS_MAC) + dssi = HOME + "/Library/Audio/Plug-Ins/DSSI"; + dssi += ":/Library/Audio/Plug-Ins/DSSI"; +#else + dssi = HOME + "/.dssi"; + dssi += ":/usr/local/lib/dssi"; + dssi += ":/usr/lib/dssi"; +#endif + } - lv2 = APPDATA + "\\LV2"; - lv2 += ";" + COMMONPROGRAMFILES + "\\LV2"; + if (const char *const envLV2 = std::getenv("LV2_PATH")) + { + lv2 = envLV2; + } + else + { + // https://lv2plug.in/pages/filesystem-hierarchy-standard.html +#if defined(CARLA_OS_WIN) + lv2 = APPDATA + "\\LV2"; + lv2 += ";" + COMMONPROGRAMFILES + "\\LV2"; +#elif defined(CARLA_OS_HAIKU) + lv2 = HOME + "/.lv2"; + lv2 += ":/system/add-ons/media/lv2plugins"; +#elif defined(CARLA_OS_MAC) + lv2 = HOME + "/Library/Audio/Plug-Ins/LV2"; + lv2 += ":/Library/Audio/Plug-Ins/LV2"; +#else + lv2 = HOME + "/.lv2"; + lv2 += ":/usr/local/lib/lv2"; + lv2 += ":/usr/lib/lv2"; +#endif + } - vst2 = PROGRAMFILES + "\\VstPlugins"; - vst2 += ";" + PROGRAMFILES + "\\Steinberg\\VstPlugins"; + if (const char *const envVST2 = std::getenv("VST_PATH")) + { + vst2 = envVST2; + } + else + { +#if defined(CARLA_OS_WIN) + // https://helpcenter.steinberg.de/hc/en-us/articles/115000177084 + vst2 = PROGRAMFILES + "\\VSTPlugins"; + vst2 += ";" + PROGRAMFILES + "\\Steinberg\\VSTPlugins"; + vst2 += ";" + COMMONPROGRAMFILES + "\\VST2"; + vst2 += ";" + COMMONPROGRAMFILES + "\\Steinberg\\VST2"; +#elif defined(CARLA_OS_HAIKU) + vst2 = HOME + "/.vst"; + vst2 += ":/system/add-ons/media/vstplugins"; +#elif defined(CARLA_OS_MAC) + // https://helpcenter.steinberg.de/hc/en-us/articles/115000171310 + vst2 = HOME + "/Library/Audio/Plug-Ins/VST"; + vst2 += ":/Library/Audio/Plug-Ins/VST"; +#else + // no official spec, use common paths + vst2 = HOME + "/.vst"; + vst2 += ":" + HOME + "/.lxvst"; + vst2 += ":/usr/local/lib/vst"; + vst2 += ":/usr/local/lib/lxvst"; + vst2 += ":/usr/lib/vst"; + vst2 += ":/usr/lib/lxvst"; +#endif + } - jsfx = APPDATA + "\\REAPER\\Effects"; - //jsfx += ";" + PROGRAMFILES + "\\REAPER\\InstallData\\Effects"; + if (const char *const envVST3 = std::getenv("VST3_PATH")) + { + vst3 = envVST3; + } + else + { + // https://steinbergmedia.github.io/vst3_dev_portal/pages/Technical+Documentation/Locations+Format/Plugin+Locations.html +#if defined(CARLA_OS_WIN) + vst3 = LOCALAPPDATA + "\\Programs\\Common\\VST3"; + vst3 += ";" + COMMONPROGRAMFILES + "\\VST3"; +#elif defined(CARLA_OS_HAIKU) + vst3 = HOME + "/.vst3"; + vst3 += ":/system/add-ons/media/vst3plugins"; +#elif defined(CARLA_OS_MAC) + vst3 = HOME + "/Library/Audio/Plug-Ins/VST3"; + vst3 += ":/Library/Audio/Plug-Ins/VST3"; +#else + vst3 = HOME + "/.vst3"; + vst3 += ":/usr/local/lib/vst3"; + vst3 += ":/usr/lib/vst3"; +#endif + } - #ifdef CARLA_OS_WIN64 - vst2 += ";" + COMMONPROGRAMFILES + "\\VST2"; - #endif + if (const char *const envCLAP = std::getenv("CLAP_PATH")) + { + clap = envCLAP; + } + else + { + // https://github.com/free-audio/clap/blob/main/include/clap/entry.h +#if defined(CARLA_OS_WIN) + clap = LOCALAPPDATA + "\\Programs\\Common\\CLAP"; + clap += ";" + COMMONPROGRAMFILES + "\\CLAP"; +#elif defined(CARLA_OS_HAIKU) + clap = HOME + "/.clap"; + clap += ":/system/add-ons/media/clapplugins"; +#elif defined(CARLA_OS_MAC) + clap = HOME + "/Library/Audio/Plug-Ins/CLAP"; + clap += ":/Library/Audio/Plug-Ins/CLAP"; +#else + clap = HOME + "/.clap"; + clap += ":/usr/local/lib/clap"; + clap += ":/usr/lib/clap"; +#endif + } - vst3 = COMMONPROGRAMFILES + "\\VST3"; - vst3 += ";" + LOCALAPPDATA + "\\Programs\\Common\\VST3"; + if (const char *const envJSFX = std::getenv("JSFX_PATH")) + { + jsfx = envJSFX; + } + else + { + // REAPER user data directory +#if defined(CARLA_OS_WIN) + jsfx = APPDATA + "\\REAPER\\Effects"; +#elif defined(CARLA_OS_MAC) + jsfx = HOME + "/Library/Application Support/REAPER/Effects"; +#else + jsfx = CONFIG_HOME + "/REAPER/Effects"; +#endif + } - clap = COMMONPROGRAMFILES + "\\CLAP"; - clap += ";" + LOCALAPPDATA + "\\Programs\\Common\\CLAP"; + if (const char *const envSF2 = std::getenv("SF2_PATH")) + { + sf2 = envSF2; + } + else + { +#if defined(CARLA_OS_WIN) + sf2 = APPDATA + "\\SF2"; +#else + sf2 = HOME + "/.sounds/sf2"; + sf2 += ":" + HOME + "/.sounds/sf3"; + sf2 += ":/usr/share/sounds/sf2"; + sf2 += ":/usr/share/sounds/sf3"; + sf2 += ":/usr/share/soundfonts"; +#endif + } - sf2 = APPDATA + "\\SF2"; - sfz = APPDATA + "\\SFZ"; + if (const char *const envSFZ = std::getenv("SFZ_PATH")) + { + sfz = envSFZ; + } + else + { +#if defined(CARLA_OS_WIN) + sfz = APPDATA + "\\SFZ"; +#else + sfz = HOME + "/.sounds/sfz"; + sfz += ":/usr/share/sounds/sfz"; +#endif + } +#ifdef CARLA_OS_WIN 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"; + vst2 += ";" + PROGRAMFILESx86 + "\\VSTPlugins"; + vst2 += ";" + PROGRAMFILESx86 + "\\Steinberg\\VSTPlugins"; } if (envCOMMONPROGRAMFILESx86 != nullptr) @@ -228,95 +335,7 @@ struct DefaultPaths { 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")) @@ -327,14 +346,16 @@ struct DefaultPaths { if (QDir(winePrefix).exists()) { - vst2 += ":" + winePrefix + "/drive_c/Program Files/VstPlugins"; + vst2 += ":" + winePrefix + "/drive_c/Program Files/VSTPlugins"; + vst2 += ":" + winePrefix + "/drive_c/Program Files/Steinberg/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"; + vst2 += ":" + winePrefix + "/drive_c/Program Files (x86)/VSTPlugins"; + vst2 += ":" + winePrefix + "/drive_c/Program Files (x86)/Steinberg/VSTPlugins"; vst3 += ":" + winePrefix + "/drive_c/Program Files (x86)/Common Files/VST3"; clap += ":" + winePrefix + "/drive_c/Program Files (x86)/Common Files/CLAP"; } @@ -342,76 +363,8 @@ struct DefaultPaths { } #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 @@ -426,910 +379,404 @@ int fontMetricsHorizontalAdvance(const QFontMetrics& fontMetrics, const QString& } // -------------------------------------------------------------------------------------------------------------------- +// Qt-compatible plugin info -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; - } +// base details, nicely packed and POD-only so we can directly use as binary +struct PluginInfoHeader { + uint16_t build; + uint16_t type; + uint32_t hints; + uint64_t uniqueId; + uint16_t audioIns; + uint16_t audioOuts; + uint16_t cvIns; + uint16_t cvOuts; + uint16_t midiIns; + uint16_t midiOuts; + uint16_t parameterIns; + uint16_t parameterOuts; }; -// -------------------------------------------------------------------------------------------------------------------- - -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; +// full details, now with non-POD types +struct PluginInfo : PluginInfoHeader { + QString category; + QString filename; + QString name; + QString label; + QString maker; }; -QVariant asVariant(const PluginInfo& plugin) +// convert PluginInfo to Qt types +static QVariant asByteArray(const PluginInfo& info) { - 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)); + QByteArray qdata; + + // start with the POD data, stored as-is + qdata.append(static_cast(static_cast(&info)), sizeof(PluginInfoHeader)); + // then all the strings, with a null terminating byte { - const QByteArray qcategory(plugin.category.toUtf8()); + const QByteArray qcategory(info.category.toUtf8()); qdata += qcategory.constData(); qdata += '\0'; } { - const QByteArray qfilename(plugin.filename.toUtf8()); + const QByteArray qfilename(info.filename.toUtf8()); qdata += qfilename.constData(); qdata += '\0'; } { - const QByteArray qname(plugin.name.toUtf8()); + const QByteArray qname(info.name.toUtf8()); qdata += qname.constData(); qdata += '\0'; } { - const QByteArray qlabel(plugin.label.toUtf8()); + const QByteArray qlabel(info.label.toUtf8()); qdata += qlabel.constData(); qdata += '\0'; } { - const QByteArray qmaker(plugin.maker.toUtf8()); + const QByteArray qmaker(info.maker.toUtf8()); qdata += qmaker.constData(); qdata += '\0'; } - QVariant var; - var.setValue(qdata); - return var; + return qdata; } -PluginInfo asPluginInfo(const QVariant& var) +static QVariant asVariant(const PluginInfo& info) { - 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; + return QVariant(asByteArray(info)); } -// -------------------------------------------------------------------------------------------------------------------- -// 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 fPluginsInternal; - std::vector fPluginsLADSPA; - std::vector fPluginsDSSI; - std::vector fPluginsLV2; - std::vector fPluginsVST2; - std::vector fPluginsVST3; - std::vector fPluginsCLAP; - #ifdef CARLA_OS_MAC - std::vector fPluginsAU; - #endif - std::vector fPluginsJSFX; - std::vector fPluginsSF2; - std::vector 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")) {} +// convert Qt types to PluginInfo +static PluginInfo asPluginInfo(const QByteArray &qdata) +{ + // make sure data is big enough to fit POD data + 5 strings + CARLA_SAFE_ASSERT_RETURN(static_cast(qdata.size()) >= sizeof(PluginInfoHeader) + sizeof(char) * 5, {}); + + // read POD data first + const PluginInfoHeader* const data + = static_cast(static_cast(qdata.constData())); + PluginInfo info = {}; + info.build = data->build; + info.type = data->type; + info.hints = data->hints; + info.uniqueId = data->uniqueId; + info.audioIns = data->audioIns; + info.audioOuts = data->audioOuts; + info.cvIns = data->cvIns; + info.cvOuts = data->cvOuts; + info.midiIns = data->midiIns; + info.midiOuts = data->midiOuts; + info.parameterIns = data->parameterIns; + info.parameterOuts = data->parameterOuts; + + // then all the strings, keeping the same order as in `asVariant` + const char* sdata = static_cast(static_cast(data + 1)); + + info.category = QString::fromUtf8(sdata); + sdata += info.category.size() + 1; + + info.filename = QString::fromUtf8(sdata); + sdata += info.filename.size() + 1; + + info.name = QString::fromUtf8(sdata); + sdata += info.name.size() + 1; + + info.label = QString::fromUtf8(sdata); + sdata += info.label.size() + 1; + + info.maker = QString::fromUtf8(sdata); + sdata += info.maker.size() + 1; + + return info; +} - static Self& create(QWidget* const parent) - { - Self* const self = new Self(parent); - return *self; - } +static PluginInfo asPluginInfo(const QVariant& var) +{ + return asPluginInfo(var.toByteArray()); +} - inline QString tr(const char* const txt) - { - return fRealParent != nullptr ? fRealParent->tr(txt) : QString::fromUtf8(txt); - } +static QList asPluginInfoList(const QVariant& var) +{ + QCarlaByteArray qdata(var.toByteArray()); - void createFavoritePluginDict() - { -#if 0 - return { - 'name' : plugin['name'], - 'build' : plugin['build'], - 'type' : plugin['type'], - 'filename': plugin['filename'], - 'label' : plugin['label'], - 'uniqueId': plugin['uniqueId'], - } -#endif - } + QList plist; - void checkFilters() + while (!qdata.isEmpty()) { - 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(); + const PluginInfo info = asPluginInfo(qdata); + CARLA_SAFE_ASSERT_RETURN(info.build != BINARY_NONE, {}); -#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(); 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); - } - } - - 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; - } + plist.append(info); + qdata = qdata.sliced(sizeof(PluginInfoHeader) + + info.category.size() + info.filename.size() + info.name.size() + + info.label.size() + info.maker.size() + 5); } - 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 + return plist; +} - 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, - }; +// -------------------------------------------------------------------------------------------------------------------- +// Qt-compatible plugin favorite - if (sha1sum != nullptr) - { - QSafeSettings settings("falkTX", "CarlaDatabase2"); - settings.setValue(QString("PluginCache/%1").arg(sha1sum), asVariant(pinfo)); - } +// base details, nicely packed and POD-only so we can directly use as binary +struct PluginFavoriteHeader { + uint16_t type; + uint64_t uniqueId; +}; - addPlugin(pinfo); - } +// full details, now with non-POD types +struct PluginFavorite : PluginFavoriteHeader { + QString filename; + QString label; - static void _discoveryCallback(void* const ptr, const CarlaPluginDiscoveryInfo* const info, const char* const sha1sum) + PluginFavorite() { - static_cast(ptr)->discoveryCallback(info, sha1sum); + type = PLUGIN_NONE; + uniqueId = 0; } - bool checkCacheCallback(const char*, const char* const sha1sum) + PluginFavorite(uint16_t t, uint64_t u, const QString& f, const QString& l) + : filename(f), label(l) { - if (sha1sum == nullptr) - return false; - - // TODO check filename - - 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)); + type = t; + uniqueId = u; } - static bool _checkCacheCallback(void* const ptr, const char* const filename, const char* const sha1sum) + bool operator==(const PluginFavorite& other) const { - return static_cast(ptr)->checkCacheCallback(filename, sha1sum); + return type == other.type && uniqueId == other.uniqueId && filename == other.filename && label == other.label; } +}; - 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())); +// convert PluginFavorite to Qt types +static QByteArray asByteArray(const PluginFavorite& fav) +{ + QByteArray qdata; + + // start with the POD data, stored as-is + qdata.append(static_cast(static_cast(&fav)), sizeof(PluginFavoriteHeader)); - QTableWidgetItem* const itemName = ui.tableWidget->item(index, TABLEWIDGET_ITEM_NAME); - itemName->setData(Qt::UserRole+1, asVariant(pinfo)); - itemName->setData(Qt::UserRole+2, pluginText); + // then all the strings, with a null terminating byte + { + const QByteArray qfilename(fav.filename.toUtf8()); + qdata += qfilename.constData(); + qdata += '\0'; } - 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; - } + const QByteArray qlabel(fav.label.toUtf8()); + qdata += qlabel.constData(); + qdata += '\0'; + } - return false; - } + return qdata; +} - // TESTING - reAddPlugins(); +static QVariant asVariant(const QList& favlist) +{ + QByteArray qdata; - // 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: - default: - // the end - reAddPlugins(); - return true; - } + for (const PluginFavorite &fav : favlist) + qdata += asByteArray(fav); - 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 QVariant(qdata); +} - return false; - } +// convert Qt types to PluginInfo +static PluginFavorite asPluginFavorite(const QByteArray& qdata) +{ + // make sure data is big enough to fit POD data + 3 strings + CARLA_SAFE_ASSERT_RETURN(static_cast(qdata.size()) >= sizeof(PluginFavoriteHeader) + sizeof(char) * 3, {}); - 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())) - ); + // read POD data first + const PluginFavoriteHeader* const data + = static_cast(static_cast(qdata.constData())); + PluginFavorite fav = { data->type, data->uniqueId, {}, {} }; - // ------------------------------------------------------------------------------------------------------------ - // now add all plugins to the table + // then all the strings, keeping the same order as in `asVariant` + const char* sdata = static_cast(static_cast(data + 1)); - fLastTableWidgetIndex = 0; + fav.filename = QString::fromUtf8(sdata); + sdata += fav.filename.size() + 1; - for (const PluginInfo& plugin : fPluginsInternal) - addPluginToTable(plugin); + fav.label = QString::fromUtf8(sdata); + sdata += fav.label.size() + 1; - for (const PluginInfo& plugin : fPluginsLADSPA) - addPluginToTable(plugin); + return fav; +} - for (const PluginInfo& plugin : fPluginsDSSI) - addPluginToTable(plugin); +static QList asPluginFavoriteList(const QVariant& var) +{ + QCarlaByteArray qdata(var.toByteArray()); - for (const PluginInfo& plugin : fPluginsLV2) - addPluginToTable(plugin); + QList favlist; - for (const PluginInfo& plugin : fPluginsVST2) - addPluginToTable(plugin); + while (!qdata.isEmpty()) + { + const PluginFavorite fav = asPluginFavorite(qdata); + CARLA_SAFE_ASSERT_RETURN(fav.type != PLUGIN_NONE, {}); - for (const PluginInfo& plugin : fPluginsVST3) - addPluginToTable(plugin); + favlist.append(fav); + qdata = qdata.sliced(sizeof(PluginFavoriteHeader) + fav.filename.size() + fav.label.size() + 2); + } - for (const PluginInfo& plugin : fPluginsCLAP) - addPluginToTable(plugin); + return favlist; +} - #ifdef CARLA_OS_MAC - for (const PluginInfo& plugin : fPluginsAU) - addPluginToTable(plugin); - #endif +// create PluginFavorite from PluginInfo data +static PluginFavorite asPluginFavorite(const PluginInfo& info) +{ + return PluginFavorite(info.type, info.uniqueId, info.filename, info.label); +} - for (const PluginInfo& plugin : fPluginsJSFX) - addPluginToTable(plugin); +// -------------------------------------------------------------------------------------------------------------------- +// discovery callbacks - for (const PluginInfo& plugin : fPluginsSF2) - addPluginToTable(plugin); +static void discoveryCallback(void* const ptr, const CarlaPluginDiscoveryInfo* const info, const char* const sha1sum) +{ + static_cast(ptr)->addPluginInfo(info, sha1sum); +} - for (const PluginInfo& plugin : fPluginsSFZ) - addPluginToTable(plugin); +static bool checkCacheCallback(void* const ptr, const char* const filename, const char* const sha1sum) +{ + if (sha1sum == nullptr) + return false; - CARLA_SAFE_ASSERT_INT2(fLastTableWidgetIndex == ui.tableWidget->rowCount(), - fLastTableWidgetIndex, ui.tableWidget->rowCount()); + return static_cast(ptr)->checkPluginCache(filename, sha1sum); +} - // ------------------------------------------------------------------------------------------------------------ +// -------------------------------------------------------------------------------------------------------------------- - ui.tableWidget->setSortingEnabled(true); +struct PluginListDialog::PrivateData { + int lastTableWidgetIndex = 0; + int timerId = 0; + PluginInfo retPlugin; - checkFilters(); - checkPlugin(ui.tableWidget->currentRow()); - } + // To be changed by parent + bool hasLoadedLv2Plugins = false; - void checkPlugin(const int row) - { - if (row >= 0) + struct Discovery { + PluginType ptype = PLUGIN_NONE; + bool firstInit = true; + bool ignoreCache = false; + bool checkInvalid = false; + CarlaPluginDiscoveryHandle handle = nullptr; + QCarlaString tool; + CarlaScopedPointer dialog; + Discovery() { - 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); + tool = carla_get_library_folder(); + tool += CARLA_OS_SEP_STR "carla-discovery-native"; +#ifdef CARLA_OS_WIN + tool += ".exe"; +#endif } - else + + ~Discovery() + { + if (handle != nullptr) + carla_plugin_discovery_stop(handle); + } + } discovery; + + PluginPaths paths; + + struct { + std::vector internal; + std::vector ladspa; + std::vector dssi; + std::vector lv2; + std::vector vst2; + std::vector vst3; + std::vector clap; + #ifdef CARLA_OS_MAC + std::vector au; + #endif + std::vector jsfx; + std::vector kits; + QMap> cache; + QList favorites; + + bool add(const PluginInfo& pinfo) { - 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("---"); + switch (pinfo.type) + { + case PLUGIN_INTERNAL: internal.push_back(pinfo); return true; + case PLUGIN_LADSPA: ladspa.push_back(pinfo); return true; + case PLUGIN_DSSI: dssi.push_back(pinfo); return true; + case PLUGIN_LV2: lv2.push_back(pinfo); return true; + case PLUGIN_VST2: vst2.push_back(pinfo); return true; + case PLUGIN_VST3: vst3.push_back(pinfo); return true; + case PLUGIN_CLAP: clap.push_back(pinfo); return true; + #ifdef CARLA_OS_MAC + case PLUGIN_AU: au.push_back(pinfo); return true; + #endif + case PLUGIN_JSFX: jsfx.push_back(pinfo); return true; + case PLUGIN_SF2: + case PLUGIN_SFZ: kits.push_back(pinfo); return true; + default: return false; + } } - } + } plugins; }; // -------------------------------------------------------------------------------------------------------------------- - -#if QT_VERSION >= 0x60000 - #define PLG_SUFFIX "_2qt6" - #define TG_SUFFIX "_7qt6" -#else - #define PLG_SUFFIX "_2" - #define TG_SUFFIX "_7" -#endif +// Plugin List Dialog PluginListDialog::PluginListDialog(QWidget* const parent, const HostSettings& hostSettings) : QDialog(parent), - self(Self::create(parent)) + p(new PrivateData) { - self.ui.setupUi(this); - self.hostSettings = hostSettings; + ui.setupUi(this); + + // p->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); + ui.b_add->setEnabled(false); + + ui.tab_info->tabBar()->hide(); + ui.tab_reqs->tabBar()->hide(); + + // do not resize info frame so much + const QLayout *const infoLayout = ui.tw_info->layout(); + const QMargins infoMargins = infoLayout->contentsMargins(); + ui.tab_info->setMinimumWidth(infoMargins.left() + infoMargins.right() + infoLayout->spacing() * 3 + + fontMetricsHorizontalAdvance(ui.la_id->fontMetrics(), "Has Custom GUI: 9999999999")); + + // start with no plugin selected + checkPlugin(-1); + + // custom action that listens for Ctrl+F shortcut + addAction(ui.act_focus_search); #if BINARY_NATIVE == BINARY_POSIX32 || BINARY_NATIVE == BINARY_WIN32 - self.ui.ch_bridged->setText(tr("Bridged (64bit)")); + ui.ch_bridged->setText(tr("Bridged (64bit)")); #else - self.ui.ch_bridged->setText(tr("Bridged (32bit)")); + 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); + ui.ch_bridged_wine->setChecked(false); + 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); + ui.ch_au->setChecked(false); + ui.ch_au->setEnabled(false); + 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); // ---------------------------------------------------------------------------------------------------------------- @@ -1343,20 +790,20 @@ PluginListDialog::PluginListDialog(QWidget* const parent, const HostSettings& ho #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) + ui.ch_native.setChecked(True) + ui.ch_native.setEnabled(False) + ui.ch_native.setVisible(True) + ui.ch_bridged.setChecked(False) + ui.ch_bridged.setEnabled(False) + ui.ch_bridged.setVisible(False) + ui.ch_bridged_wine.setChecked(False) + ui.ch_bridged_wine.setEnabled(False) + 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) + ui.ch_bridged_wine.setChecked(False) + ui.ch_bridged_wine.setEnabled(False) + ui.ch_bridged_wine.setVisible(False) #endif // ---------------------------------------------------------------------------------------------------------------- @@ -1365,11 +812,11 @@ PluginListDialog::PluginListDialog(QWidget* const parent, const HostSettings& ho 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); + ui.b_add.setIcon(getIcon('list-add', 16, 'svgz')) + ui.b_cancel.setIcon(getIcon('dialog-cancel', 16, 'svgz')) + ui.b_clear_filters.setIcon(getIcon('edit-clear', 16, 'svgz')) + ui.b_refresh.setIcon(getIcon('view-refresh', 16, 'svgz')) + QTableWidgetItem* const hhi = ui.tableWidget->horizontalHeaderItem(TW_FAVORITE); hhi.setIcon(getIcon('bookmarks', 16, 'svgz')) #endif } @@ -1377,80 +824,60 @@ PluginListDialog::PluginListDialog(QWidget* const parent, const HostSettings& ho // ---------------------------------------------------------------------------------------------------------------- // 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); + QObject::connect(this, &QDialog::finished, this, &PluginListDialog::saveSettings); + QObject::connect(ui.b_add, &QPushButton::clicked, this, &QDialog::accept); + QObject::connect(ui.b_cancel, &QPushButton::clicked, this, &QDialog::reject); + + QObject::connect(ui.b_refresh, &QPushButton::clicked, this, &PluginListDialog::refreshPlugins); + QObject::connect(ui.b_clear_filters, &QPushButton::clicked, this, &PluginListDialog::clearFilters); + QObject::connect(ui.lineEdit, &QLineEdit::textChanged, this, &PluginListDialog::checkFilters); + QObject::connect(ui.tableWidget, &QTableWidget::currentCellChanged, this, &PluginListDialog::checkPlugin); + QObject::connect(ui.tableWidget, &QTableWidget::cellClicked, this, &PluginListDialog::cellClicked); + QObject::connect(ui.tableWidget, &QTableWidget::cellDoubleClicked, this, &PluginListDialog::cellDoubleClicked); + + QObject::connect(ui.ch_internal, &QCheckBox::clicked, this, &PluginListDialog::checkFilters); + QObject::connect(ui.ch_ladspa, &QCheckBox::clicked, this, &PluginListDialog::checkFilters); + QObject::connect(ui.ch_dssi, &QCheckBox::clicked, this, &PluginListDialog::checkFilters); + QObject::connect(ui.ch_lv2, &QCheckBox::clicked, this, &PluginListDialog::checkFilters); + QObject::connect(ui.ch_vst, &QCheckBox::clicked, this, &PluginListDialog::checkFilters); + QObject::connect(ui.ch_vst3, &QCheckBox::clicked, this, &PluginListDialog::checkFilters); + QObject::connect(ui.ch_clap, &QCheckBox::clicked, this, &PluginListDialog::checkFilters); + QObject::connect(ui.ch_au, &QCheckBox::clicked, this, &PluginListDialog::checkFilters); + QObject::connect(ui.ch_jsfx, &QCheckBox::clicked, this, &PluginListDialog::checkFilters); + QObject::connect(ui.ch_kits, &QCheckBox::clicked, this, &PluginListDialog::checkFilters); + QObject::connect(ui.ch_effects, &QCheckBox::clicked, this, &PluginListDialog::checkFilters); + QObject::connect(ui.ch_instruments, &QCheckBox::clicked, this, &PluginListDialog::checkFilters); + QObject::connect(ui.ch_midi, &QCheckBox::clicked, this, &PluginListDialog::checkFilters); + QObject::connect(ui.ch_other, &QCheckBox::clicked, this, &PluginListDialog::checkFilters); + QObject::connect(ui.ch_native, &QCheckBox::clicked, this, &PluginListDialog::checkFilters); + QObject::connect(ui.ch_bridged, &QCheckBox::clicked, this, &PluginListDialog::checkFilters); + QObject::connect(ui.ch_bridged_wine, &QCheckBox::clicked, this, &PluginListDialog::checkFilters); + QObject::connect(ui.ch_favorites, &QCheckBox::clicked, this, &PluginListDialog::checkFilters); + QObject::connect(ui.ch_rtsafe, &QCheckBox::clicked, this, &PluginListDialog::checkFilters); + QObject::connect(ui.ch_cv, &QCheckBox::clicked, this, &PluginListDialog::checkFilters); + QObject::connect(ui.ch_gui, &QCheckBox::clicked, this, &PluginListDialog::checkFilters); + QObject::connect(ui.ch_inline_display, &QCheckBox::clicked, this, &PluginListDialog::checkFilters); + QObject::connect(ui.ch_stereo, &QCheckBox::clicked, this, &PluginListDialog::checkFilters); + QObject::connect(ui.ch_cat_all, &QCheckBox::clicked, this, &PluginListDialog::checkFiltersCategoryAll); + QObject::connect(ui.ch_cat_delay, &QCheckBox::clicked, this, &PluginListDialog::checkFiltersCategorySpecific); + QObject::connect(ui.ch_cat_distortion, &QCheckBox::clicked, this, &PluginListDialog::checkFiltersCategorySpecific); + QObject::connect(ui.ch_cat_dynamics, &QCheckBox::clicked, this, &PluginListDialog::checkFiltersCategorySpecific); + QObject::connect(ui.ch_cat_eq, &QCheckBox::clicked, this, &PluginListDialog::checkFiltersCategorySpecific); + QObject::connect(ui.ch_cat_filter, &QCheckBox::clicked, this, &PluginListDialog::checkFiltersCategorySpecific); + QObject::connect(ui.ch_cat_modulator, &QCheckBox::clicked, this, &PluginListDialog::checkFiltersCategorySpecific); + QObject::connect(ui.ch_cat_synth, &QCheckBox::clicked, this, &PluginListDialog::checkFiltersCategorySpecific); + QObject::connect(ui.ch_cat_utility, &QCheckBox::clicked, this, &PluginListDialog::checkFiltersCategorySpecific); + QObject::connect(ui.ch_cat_other, &QCheckBox::clicked, this, &PluginListDialog::checkFiltersCategorySpecific); + + QObject::connect(ui.act_focus_search, &QAction::triggered, this, &PluginListDialog::focusSearchFieldAndSelectAll); } PluginListDialog::~PluginListDialog() { - if (self.fTimerId != 0) - killTimer(self.fTimerId); + if (p->timerId != 0) + killTimer(p->timerId); - delete &self; + delete p; } // -------------------------------------------------------------------------------------------------------------------- @@ -1458,27 +885,208 @@ PluginListDialog::~PluginListDialog() const PluginInfo& PluginListDialog::getSelectedPluginInfo() const { - return self.fRetPlugin; + return p->retPlugin; +} + +void PluginListDialog::addPluginInfo(const CarlaPluginDiscoveryInfo* const info, const char* const sha1sum) +{ + if (info == nullptr) + { + if (sha1sum != nullptr) + { + QSafeSettings settings("falkTX", "CarlaDatabase3"); + settings.setValue(QString("PluginCache/%1").arg(sha1sum), QByteArray()); + + const QString qsha1sum(sha1sum); + p->plugins.cache[qsha1sum] = {}; + } + return; + } + + PluginInfo pinfo = {}; + pinfo.build = static_cast(info->btype); + pinfo.type = static_cast(info->ptype); + pinfo.hints = info->metadata.hints; + pinfo.uniqueId = info->uniqueId; + pinfo.audioIns = static_cast(info->io.audioIns); + pinfo.audioOuts = static_cast(info->io.audioOuts); + pinfo.cvIns = static_cast(info->io.cvIns); + pinfo.cvOuts = static_cast(info->io.cvOuts); + pinfo.midiIns = static_cast(info->io.midiIns); + pinfo.midiOuts = static_cast(info->io.midiOuts); + pinfo.parameterIns = static_cast(info->io.parameterIns); + pinfo.parameterOuts = static_cast(info->io.parameterOuts); + pinfo.category = getPluginCategoryAsString(info->metadata.category); + pinfo.filename = QString::fromUtf8(info->filename); + pinfo.name = QString::fromUtf8(info->metadata.name); + pinfo.label = QString::fromUtf8(info->label); + pinfo.maker = QString::fromUtf8(info->metadata.maker); + + if (sha1sum != nullptr) + { + QSafeSettings settings("falkTX", "CarlaDatabase3"); + const QString qsha1sum(sha1sum); + const QString key = QString("PluginCache/%1").arg(sha1sum); + + // single sha1sum can contain >1 plugin + QByteArray qdata; + if (p->plugins.cache.contains(qsha1sum)) + qdata = settings.valueByteArray(key); + qdata += asVariant(pinfo).toByteArray(); + + settings.setValue(key, qdata); + + p->plugins.cache[qsha1sum].append(pinfo); + } + + p->plugins.add(pinfo); +} + +bool PluginListDialog::checkPluginCache(const char* const filename, const char* const sha1sum) +{ + // sha1sum is always valid for this call + const QString qsha1sum(sha1sum); + + if (filename != nullptr) + p->discovery.dialog->progressBar->setFormat(filename); + + if (!p->plugins.cache.contains(qsha1sum)) + return false; + + const QList& plist(p->plugins.cache[qsha1sum]); + + if (plist.isEmpty()) + return p->discovery.ignoreCache || !p->discovery.checkInvalid; + + // if filename does not match, abort (hash collision?) + if (filename == nullptr || plist.first().filename != filename) + { + p->plugins.cache.remove(qsha1sum); + return false; + } + + for (const PluginInfo& info : plist) + p->plugins.add(info); + + return true; } // -------------------------------------------------------------------------------------------------------------------- // protected methods +void PluginListDialog::done(const int r) +{ + if (r == QDialog::Accepted && ui.tableWidget->currentRow() >= 0) + { + QTableWidgetItem* const widget = ui.tableWidget->item(ui.tableWidget->currentRow(), TW_NAME); + p->retPlugin = asPluginInfo(widget->data(Qt::UserRole + UR_PLUGIN_INFO)); + } else { + p->retPlugin = {}; + } + + QDialog::done(r); +} + void PluginListDialog::showEvent(QShowEvent* const event) { - slot_focusSearchFieldAndSelectAll(); + focusSearchFieldAndSelectAll(); QDialog::showEvent(event); + + // Set up initial discovery + if (p->discovery.firstInit) + { + p->discovery.firstInit = false; + + p->discovery.dialog = new PluginRefreshDialog(this); + p->discovery.dialog->b_start->setEnabled(false); + p->discovery.dialog->b_skip->setEnabled(true); + p->discovery.dialog->ch_updated->setChecked(true); + p->discovery.dialog->ch_invalid->setChecked(false); + p->discovery.dialog->group->setEnabled(false); + p->discovery.dialog->progressBar->setFormat("Starting initial discovery..."); + p->discovery.dialog->show(); + + QObject::connect(p->discovery.dialog->b_skip, &QPushButton::clicked, + this, &PluginListDialog::refreshPluginsSkip); + QObject::connect(p->discovery.dialog, &QDialog::finished, + this, &PluginListDialog::refreshPluginsStop); + + p->timerId = startTimer(0); + } } void PluginListDialog::timerEvent(QTimerEvent* const event) { - if (event->timerId() == self.fTimerId) + if (event->timerId() == p->timerId) { - if (self.idle()) - { - killTimer(self.fTimerId); - self.fTimerId = 0; - } + // TODO all formats + do { + // discovery in progress, keep it going + if (p->discovery.handle != nullptr) + { + if (!carla_plugin_discovery_idle(p->discovery.handle)) + { + carla_plugin_discovery_stop(p->discovery.handle); + p->discovery.handle = nullptr; + } + break; + } + + // start next discovery + QCarlaString path; + switch (p->discovery.ptype) + { + case PLUGIN_NONE: + ui.label->setText(tr("Discovering internal plugins...")); + p->discovery.ptype = PLUGIN_INTERNAL; + break; + case PLUGIN_INTERNAL: + ui.label->setText(tr("Discovering LV2 plugins...")); + path = p->paths.lv2; + p->discovery.ptype = PLUGIN_LV2; + break; + case PLUGIN_LV2: + if (p->paths.jsfx.isNotEmpty()) + { + ui.label->setText(tr("Discovering JSFX plugins...")); + path = p->paths.jsfx; + p->discovery.ptype = PLUGIN_JSFX; + break; + } + [[fallthrough]]; + case PLUGIN_JSFX: + ui.label->setText(tr("Discovering LADSPA plugins...")); + path = p->paths.ladspa; + p->discovery.ptype = PLUGIN_LADSPA; + break; + case PLUGIN_LADSPA: + ui.label->setText(tr("Discovering VST2 plugins...")); + path = p->paths.vst2; + p->discovery.ptype = PLUGIN_VST2; + break; + case PLUGIN_VST2: + ui.label->setText(tr("Discovering VST3 plugins...")); + path = p->paths.vst3; + p->discovery.ptype = PLUGIN_VST3; + break; + case PLUGIN_VST3: + ui.label->setText(tr("Discovering CLAP plugins...")); + path = p->paths.clap; + p->discovery.ptype = PLUGIN_CLAP; + break; + default: + // discovery complete + refreshPluginsStop(); + } + + if (p->timerId == 0) + break; + + p->discovery.handle = carla_plugin_discovery_start(p->discovery.tool.toUtf8().constData(), + p->discovery.ptype, + path.toUtf8().constData(), + discoveryCallback, checkCacheCallback, this); + } while (false); } QDialog::timerEvent(event); @@ -1487,318 +1095,708 @@ void PluginListDialog::timerEvent(QTimerEvent* const event) // -------------------------------------------------------------------------------------------------------------------- // private methods +void PluginListDialog::addPluginsToTable() +{ + // ---------------------------------------------------------------------------------------------------------------- + // sum plugins first, creating all needed rows in advance + + ui.tableWidget->setSortingEnabled(false); + ui.tableWidget->clearContents(); + + ui.tableWidget->setRowCount( + int(p->plugins.internal.size() + p->plugins.ladspa.size() + p->plugins.dssi.size() + + p->plugins.lv2.size() + p->plugins.vst2.size() + p->plugins.vst3.size() + p->plugins.clap.size() + + #ifdef CARLA_OS_MAC + p->plugins.au.size()) + + #endif + p->plugins.jsfx.size() + p->plugins.kits.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"; + + // TODO all formats + ui.label->setText(tr(txt) + .arg(QString::number(p->plugins.internal.size())) + .arg(QString::number(p->plugins.ladspa.size())) + .arg(QString::number(p->plugins.dssi.size())) + .arg(QString::number(p->plugins.lv2.size())) + .arg(QString::number(p->plugins.vst2.size())) + .arg(QString::number(p->plugins.vst3.size())) + .arg(QString::number(p->plugins.clap.size())) + #ifdef CARLA_OS_MAC + .arg(QString::number(p->plugins.au.size())) + #endif + .arg(QString::number(p->plugins.jsfx.size())) + .arg(QString::number(p->plugins.kits.size()))); + + // ---------------------------------------------------------------------------------------------------------------- + // now add all plugins to the table + + auto addPluginToTable = [=](const PluginInfo& info) { + const int index = p->lastTableWidgetIndex++; + const bool isFav = p->plugins.favorites.contains(asPluginFavorite(info)); + + QTableWidgetItem* const itemFav = new QTableWidgetItem; + itemFav->setCheckState(isFav ? Qt::Checked : Qt::Unchecked); + itemFav->setText(isFav ? " " : " "); + + const QString pluginText = (info.name + info.label + info.maker + info.filename).toLower(); + ui.tableWidget->setItem(index, TW_FAVORITE, itemFav); + ui.tableWidget->setItem(index, TW_NAME, new QTableWidgetItem(info.name)); + ui.tableWidget->setItem(index, TW_LABEL, new QTableWidgetItem(info.label)); + ui.tableWidget->setItem(index, TW_MAKER, new QTableWidgetItem(info.maker)); + ui.tableWidget->setItem(index, TW_BINARY, new QTableWidgetItem(QFileInfo(info.filename).fileName())); + + QTableWidgetItem *const itemName = ui.tableWidget->item(index, TW_NAME); + itemName->setData(Qt::UserRole + UR_PLUGIN_INFO, asVariant(info)); + itemName->setData(Qt::UserRole + UR_SEARCH_TEXT, pluginText); + }; + + p->lastTableWidgetIndex = 0; + + for (const PluginInfo &plugin : p->plugins.internal) + addPluginToTable(plugin); + + for (const PluginInfo &plugin : p->plugins.ladspa) + addPluginToTable(plugin); + + for (const PluginInfo &plugin : p->plugins.dssi) + addPluginToTable(plugin); + + for (const PluginInfo &plugin : p->plugins.lv2) + addPluginToTable(plugin); + + for (const PluginInfo &plugin : p->plugins.vst2) + addPluginToTable(plugin); + + for (const PluginInfo &plugin : p->plugins.vst3) + addPluginToTable(plugin); + + for (const PluginInfo& plugin : p->plugins.clap) + addPluginToTable(plugin); + + #ifdef CARLA_OS_MAC + for (const PluginInfo& plugin : p->plugins.au) + addPluginToTable(plugin); + #endif + + for (const PluginInfo& plugin : p->plugins.jsfx) + addPluginToTable(plugin); + + for (const PluginInfo& plugin : p->plugins.kits) + addPluginToTable(plugin); + + CARLA_SAFE_ASSERT_INT2(p->lastTableWidgetIndex == ui.tableWidget->rowCount(), + p->lastTableWidgetIndex, ui.tableWidget->rowCount()); + + // ---------------------------------------------------------------------------------------------------------------- + // and reenable sorting + filtering + + ui.tableWidget->setSortingEnabled(true); + + checkFilters(); + checkPlugin(ui.tableWidget->currentRow()); +} + 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)); + const QSafeSettings settings("falkTX", "CarlaDatabase3"); + + restoreGeometry(settings.valueByteArray("PluginDatabase/Geometry")); + ui.ch_effects->setChecked(settings.valueBool("PluginDatabase/ShowEffects", true)); + ui.ch_instruments->setChecked(settings.valueBool("PluginDatabase/ShowInstruments", true)); + ui.ch_midi->setChecked(settings.valueBool("PluginDatabase/ShowMIDI", true)); + ui.ch_other->setChecked(settings.valueBool("PluginDatabase/ShowOther", true)); + ui.ch_internal->setChecked(settings.valueBool("PluginDatabase/ShowInternal", true)); + ui.ch_ladspa->setChecked(settings.valueBool("PluginDatabase/ShowLADSPA", true)); + ui.ch_dssi->setChecked(settings.valueBool("PluginDatabase/ShowDSSI", true)); + ui.ch_lv2->setChecked(settings.valueBool("PluginDatabase/ShowLV2", true)); + ui.ch_vst->setChecked(settings.valueBool("PluginDatabase/ShowVST2", true)); + ui.ch_vst3->setChecked(settings.valueBool("PluginDatabase/ShowVST3", true)); + ui.ch_clap->setChecked(settings.valueBool("PluginDatabase/ShowCLAP", true)); #ifdef CARLA_OS_MAC - self.ui.ch_au->setChecked(settings.valueBool("PluginDatabase/ShowAU", true)); + 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", "")); + ui.ch_jsfx->setChecked(settings.valueBool("PluginDatabase/ShowJSFX", true)); + ui.ch_kits->setChecked(settings.valueBool("PluginDatabase/ShowKits", true)); + ui.ch_native->setChecked(settings.valueBool("PluginDatabase/ShowNative", true)); + ui.ch_bridged->setChecked(settings.valueBool("PluginDatabase/ShowBridged", true)); + ui.ch_bridged_wine->setChecked(settings.valueBool("PluginDatabase/ShowBridgedWine", true)); + ui.ch_favorites->setChecked(settings.valueBool("PluginDatabase/ShowFavorites", false)); + ui.ch_rtsafe->setChecked(settings.valueBool("PluginDatabase/ShowRtSafe", false)); + ui.ch_cv->setChecked(settings.valueBool("PluginDatabase/ShowHasCV", false)); + ui.ch_gui->setChecked(settings.valueBool("PluginDatabase/ShowHasGUI", false)); + ui.ch_inline_display->setChecked(settings.valueBool("PluginDatabase/ShowHasInlineDisplay", false)); + ui.ch_stereo->setChecked(settings.valueBool("PluginDatabase/ShowStereoOnly", false)); + 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); + ui.ch_cat_all->setChecked(true); + ui.ch_cat_delay->setChecked(false); + ui.ch_cat_distortion->setChecked(false); + ui.ch_cat_dynamics->setChecked(false); + ui.ch_cat_eq->setChecked(false); + ui.ch_cat_filter->setChecked(false); + ui.ch_cat_modulator->setChecked(false); + ui.ch_cat_synth->setChecked(false); + ui.ch_cat_utility->setChecked(false); + 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:")); + ui.ch_cat_all->setChecked(false); + ui.ch_cat_delay->setChecked(categories.contains(":delay:")); + ui.ch_cat_distortion->setChecked(categories.contains(":distortion:")); + ui.ch_cat_dynamics->setChecked(categories.contains(":dynamics:")); + ui.ch_cat_eq->setChecked(categories.contains(":eq:")); + ui.ch_cat_filter->setChecked(categories.contains(":filter:")); + ui.ch_cat_modulator->setChecked(categories.contains(":modulator:")); + ui.ch_cat_synth->setChecked(categories.contains(":synth:")); + ui.ch_cat_utility->setChecked(categories.contains(":utility:")); + ui.ch_cat_other->setChecked(categories.contains(":other:")); } - const QByteArray tableGeometry = settings.valueByteArray("PluginDatabase/TableGeometry" TG_SUFFIX); - QHeaderView* const horizontalHeader = self.ui.tableWidget->horizontalHeader(); + const QByteArray tableGeometry = settings.valueByteArray("PluginDatabase/TableGeometry"); + QHeaderView* const horizontalHeader = 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); + ui.tableWidget->setColumnWidth(TW_NAME, 250); + ui.tableWidget->setColumnWidth(TW_LABEL, 200); + ui.tableWidget->setColumnWidth(TW_MAKER, 150); + ui.tableWidget->sortByColumn(TW_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); + horizontalHeader->setSectionResizeMode(TW_FAVORITE, QHeaderView::Fixed); + ui.tableWidget->setColumnWidth(TW_FAVORITE, 24); + ui.tableWidget->setSortingEnabled(true); + + p->plugins.favorites = asPluginFavoriteList(settings.valueByteArray("PluginListDialog/Favorites")); + + // load entire plugin cache + const QStringList keys = settings.allKeys(); + for (const QCarlaString key : keys) + { + if (!key.startsWith("PluginCache/")) + continue; + + qWarning() << key << " | " << key.sliced(12); + + const QByteArray data(settings.valueByteArray(key)); + + if (data.isEmpty()) + p->plugins.cache.insert(key.sliced(12), {}); + else + p->plugins.cache.insert(key.sliced(12), asPluginInfoList(data)); + } } // ----------------------------------------------------------------------------------------------------------------- // private slots -void PluginListDialog::slot_cellClicked(int row, int column) +void PluginListDialog::cellClicked(const int row, const int column) { - if (column != self.TABLEWIDGET_ITEM_FAVORITE) + if (column != TW_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 + const PluginInfo info = asPluginInfo(ui.tableWidget->item(row, TW_NAME)->data(Qt::UserRole + UR_PLUGIN_INFO)); + const PluginFavorite fav = asPluginFavorite(info); + const bool isFavorite = p->plugins.favorites.contains(fav); - if (widget->checkState() == Qt::Checked) + if (ui.tableWidget->item(row, TW_FAVORITE)->checkState() == Qt::Checked) { + if (!isFavorite) + p->plugins.favorites.append(fav); + } + else if (isFavorite) + { + p->plugins.favorites.removeAll(fav); + } + + QSafeSettings settings("falkTX", "CarlaDatabase3"); + settings.setValue("PluginListDialog/Favorites", asVariant(p->plugins.favorites)); +} + +void PluginListDialog::cellDoubleClicked(int, const int column) +{ + if (column != TW_FAVORITE) + done(QDialog::Accepted); +} + +void PluginListDialog::focusSearchFieldAndSelectAll() +{ + ui.lineEdit->setFocus(); + ui.lineEdit->selectAll(); +} + +void PluginListDialog::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(); + + const bool hideNonFavs = ui.ch_favorites->isChecked(); + 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 (not plugin in self.fFavoritePlugins) - { - self.fFavoritePlugins.append(plugin); - self.fFavoritePluginsChanged = true; - } + 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 - } - else + + for (int i=0, c=ui.tableWidget->rowCount(); iitem(i, TW_NAME)->data(Qt::UserRole + UR_PLUGIN_INFO)); + const QString ptext = ui.tableWidget->item(i, TW_NAME)->data(Qt::UserRole + UR_SEARCH_TEXT).toString(); + + const uint16_t aIns = info.audioIns; + const uint16_t aOuts = info.audioOuts; + const uint16_t cvIns = info.cvIns; + const uint16_t cvOuts = info.cvOuts; + const uint16_t mIns = info.midiIns; + const uint16_t mOuts = info.midiOuts; + const uint32_t phints = info.hints; + const uint16_t ptype = info.type; + const QString categ = info.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 = info.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 - try: - self.fFavoritePlugins.remove(plugin); - self.fFavoritePluginsChanged = True; - except ValueError: - pass + const bool isBridged = bool(not isNative and info.build in nativeBins); + const bool isBridgedWine = bool(not isNative and info.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); + else if (hideNonFavs && !p->plugins.favorites.contains(asPluginFavorite(info))) + ui.tableWidget->hideRow(i); + 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); } } -void PluginListDialog::slot_cellDoubleClicked(int, int column) +void PluginListDialog::checkFiltersCategoryAll(const bool clicked) +{ + const bool notClicked = !clicked; + ui.ch_cat_delay->setChecked(notClicked); + ui.ch_cat_distortion->setChecked(notClicked); + ui.ch_cat_dynamics->setChecked(notClicked); + ui.ch_cat_eq->setChecked(notClicked); + ui.ch_cat_filter->setChecked(notClicked); + ui.ch_cat_modulator->setChecked(notClicked); + ui.ch_cat_synth->setChecked(notClicked); + ui.ch_cat_utility->setChecked(notClicked); + ui.ch_cat_other->setChecked(notClicked); + checkFilters(); +} + +void PluginListDialog::checkFiltersCategorySpecific(bool clicked) { - if (column != self.TABLEWIDGET_ITEM_FAVORITE) - slot_addPlugin(); + if (clicked) + { + ui.ch_cat_all->setChecked(false); + } + else if (! (ui.ch_cat_delay->isChecked() || + ui.ch_cat_distortion->isChecked() || + ui.ch_cat_dynamics->isChecked() || + ui.ch_cat_eq->isChecked() || + ui.ch_cat_filter->isChecked() || + ui.ch_cat_modulator->isChecked() || + ui.ch_cat_synth->isChecked() || + ui.ch_cat_utility->isChecked() || + ui.ch_cat_other->isChecked())) + { + ui.ch_cat_all->setChecked(true); + } + checkFilters(); } -void PluginListDialog::slot_focusSearchFieldAndSelectAll() +void PluginListDialog::clearFilters() { - self.ui.lineEdit->setFocus(); - self.ui.lineEdit->selectAll(); + auto setCheckedWithoutSignaling = [](QCheckBox* const w, const bool checked) + { + w->blockSignals(true); + w->setChecked(checked); + w->blockSignals(false); + }; + + setCheckedWithoutSignaling(ui.ch_internal, true); + setCheckedWithoutSignaling(ui.ch_ladspa, true); + setCheckedWithoutSignaling(ui.ch_dssi, true); + setCheckedWithoutSignaling(ui.ch_lv2, true); + setCheckedWithoutSignaling(ui.ch_vst, true); + setCheckedWithoutSignaling(ui.ch_vst3, true); + setCheckedWithoutSignaling(ui.ch_clap, true); + setCheckedWithoutSignaling(ui.ch_jsfx, true); + setCheckedWithoutSignaling(ui.ch_kits, true); + + setCheckedWithoutSignaling(ui.ch_instruments, true); + setCheckedWithoutSignaling(ui.ch_effects, true); + setCheckedWithoutSignaling(ui.ch_midi, true); + setCheckedWithoutSignaling(ui.ch_other, true); + + setCheckedWithoutSignaling(ui.ch_native, true); + setCheckedWithoutSignaling(ui.ch_bridged, false); + setCheckedWithoutSignaling(ui.ch_bridged_wine, false); + + setCheckedWithoutSignaling(ui.ch_favorites, false); + setCheckedWithoutSignaling(ui.ch_rtsafe, false); + setCheckedWithoutSignaling(ui.ch_stereo, false); + setCheckedWithoutSignaling(ui.ch_cv, false); + setCheckedWithoutSignaling(ui.ch_gui, false); + setCheckedWithoutSignaling(ui.ch_inline_display, false); + + if (ui.ch_au->isEnabled()) + setCheckedWithoutSignaling(ui.ch_au, true); + + setCheckedWithoutSignaling(ui.ch_cat_all, true); + setCheckedWithoutSignaling(ui.ch_cat_delay, false); + setCheckedWithoutSignaling(ui.ch_cat_distortion, false); + setCheckedWithoutSignaling(ui.ch_cat_dynamics, false); + setCheckedWithoutSignaling(ui.ch_cat_eq, false); + setCheckedWithoutSignaling(ui.ch_cat_filter, false); + setCheckedWithoutSignaling(ui.ch_cat_modulator, false); + setCheckedWithoutSignaling(ui.ch_cat_synth, false); + setCheckedWithoutSignaling(ui.ch_cat_utility, false); + setCheckedWithoutSignaling(ui.ch_cat_other, false); + + ui.lineEdit->blockSignals(true); + ui.lineEdit->clear(); + ui.lineEdit->blockSignals(false); + + checkFilters(); } -void PluginListDialog::slot_addPlugin() +// -------------------------------------------------------------------------------------------------------------------- + +void PluginListDialog::checkPlugin(const int row) { - if (self.ui.tableWidget->currentRow() >= 0) + if (row >= 0) { - self.fRetPlugin = asPluginInfo(self.ui.tableWidget->item(self.ui.tableWidget->currentRow(), - self.TABLEWIDGET_ITEM_NAME)->data(Qt::UserRole+1)); - accept(); + ui.b_add->setEnabled(true); + + const PluginInfo info = asPluginInfo(ui.tableWidget->item(row, TW_NAME)->data(Qt::UserRole + UR_PLUGIN_INFO)); + + const bool isSynth = info.hints & PLUGIN_IS_SYNTH; + const bool isEffect = info.audioIns > 0 && info.audioOuts > 0 && !isSynth; + const bool isMidi = info.audioIns == 0 && info.audioOuts == 0 && info.midiIns > 0 && info.midiOuts > 0; + + QString ptype; + /**/ if (isSynth) + ptype = "Instrument"; + else if (isEffect) + ptype = "Effect"; + else if (isMidi) + ptype = "MIDI Plugin"; + else + ptype = "Other"; + + QString parch; + /**/ if (info.build == BINARY_NATIVE) + parch = tr("Native"); + else if (info.build == BINARY_POSIX32) + parch = "posix32"; + else if (info.build == BINARY_POSIX64) + parch = "posix64"; + else if (info.build == BINARY_WIN32) + parch = "win32"; + else if (info.build == BINARY_WIN64) + parch = "win64"; + else if (info.build == BINARY_OTHER) + parch = tr("Other"); + else if (info.build == BINARY_WIN32) + parch = tr("Unknown"); + + ui.l_format->setText(getPluginTypeAsString(static_cast(info.type))); + + ui.l_type->setText(ptype); + ui.l_arch->setText(parch); + ui.l_id->setText(QString::number(info.uniqueId)); + ui.l_ains->setText(QString::number(info.audioIns)); + ui.l_aouts->setText(QString::number(info.audioOuts)); + ui.l_cvins->setText(QString::number(info.cvIns)); + ui.l_cvouts->setText(QString::number(info.cvOuts)); + ui.l_mins->setText(QString::number(info.midiIns)); + ui.l_mouts->setText(QString::number(info.midiOuts)); + ui.l_pins->setText(QString::number(info.parameterIns)); + ui.l_pouts->setText(QString::number(info.parameterOuts)); + ui.l_gui->setText(info.hints & PLUGIN_HAS_CUSTOM_UI ? tr("Yes") : tr("No")); + ui.l_idisp->setText(info.hints & PLUGIN_HAS_INLINE_DISPLAY ? tr("Yes") : tr("No")); + ui.l_bridged->setText(info.hints & PLUGIN_IS_BRIDGE ? tr("Yes") : tr("No")); + ui.l_synth->setText(isSynth ? tr("Yes") : tr("No")); } else { - reject(); + 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("---"); } } -void PluginListDialog::slot_checkPlugin(const int row) -{ - self.checkPlugin(row); -} +// -------------------------------------------------------------------------------------------------------------------- -void PluginListDialog::slot_checkFilters() +void PluginListDialog::refreshPlugins() { - self.checkFilters(); + refreshPluginsStop(); + + p->discovery.dialog = new PluginRefreshDialog(this); + p->discovery.dialog->show(); + + QObject::connect(p->discovery.dialog->b_start, &QPushButton::clicked, + this, &PluginListDialog::refreshPluginsStart); + QObject::connect(p->discovery.dialog->b_skip, &QPushButton::clicked, + this, &PluginListDialog::refreshPluginsSkip); + QObject::connect(p->discovery.dialog, &QDialog::finished, + this, &PluginListDialog::refreshPluginsStop); } -void PluginListDialog::slot_checkFiltersCategoryAll(const bool clicked) +void PluginListDialog::refreshPluginsStart() { - 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(); + // remove old plugins + p->plugins.internal.clear(); + p->plugins.ladspa.clear(); + p->plugins.dssi.clear(); + p->plugins.lv2.clear(); + p->plugins.vst2.clear(); + p->plugins.vst3.clear(); + p->plugins.clap.clear(); + #ifdef CARLA_OS_MAC + p->plugins.au.clear(); + #endif + p->plugins.jsfx.clear(); + p->plugins.kits.clear(); + p->discovery.dialog->b_start->setEnabled(false); + p->discovery.dialog->b_skip->setEnabled(true); + p->discovery.ignoreCache = p->discovery.dialog->ch_all->isChecked(); + p->discovery.checkInvalid = + p->discovery.dialog->ch_invalid->isChecked(); + if (p->discovery.ignoreCache) + p->plugins.cache.clear(); + + // start discovery again + p->discovery.ptype = PLUGIN_NONE; + + if (p->timerId == 0) + p->timerId = startTimer(0); } -void PluginListDialog::slot_checkFiltersCategorySpecific(bool clicked) +void PluginListDialog::refreshPluginsStop() { - if (clicked) + // stop previous discovery if still running + if (p->discovery.handle != nullptr) { - self.ui.ch_cat_all->setChecked(false); + carla_plugin_discovery_stop(p->discovery.handle); + p->discovery.handle = nullptr; } - 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())) + + if (p->discovery.dialog) { - self.ui.ch_cat_all->setChecked(true); + p->discovery.dialog->close(); + p->discovery.dialog = nullptr; } - self.checkFilters(); -} -void PluginListDialog::slot_refreshPlugins() -{ -#if 0 - if (PluginRefreshW(this, self.hostSettings, self.hasLoadedLv2Plugins).exec()) - reAddPlugins(); -#endif + if (p->timerId != 0) + { + killTimer(p->timerId); + p->timerId = 0; + addPluginsToTable(); + } } -void PluginListDialog::slot_clearFilters() +void PluginListDialog::refreshPluginsSkip() { - 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(); + // TODO } // -------------------------------------------------------------------------------------------------------------------- -void PluginListDialog::slot_saveSettings() +void PluginListDialog::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()) + QSafeSettings settings("falkTX", "CarlaDatabase3"); + + settings.setValue("PluginDatabase/Geometry", saveGeometry()); + settings.setValue("PluginDatabase/TableGeometry", ui.tableWidget->horizontalHeader()->saveState()); + settings.setValue("PluginDatabase/ShowEffects", ui.ch_effects->isChecked()); + settings.setValue("PluginDatabase/ShowInstruments", ui.ch_instruments->isChecked()); + settings.setValue("PluginDatabase/ShowMIDI", ui.ch_midi->isChecked()); + settings.setValue("PluginDatabase/ShowOther", ui.ch_other->isChecked()); + settings.setValue("PluginDatabase/ShowInternal", ui.ch_internal->isChecked()); + settings.setValue("PluginDatabase/ShowLADSPA", ui.ch_ladspa->isChecked()); + settings.setValue("PluginDatabase/ShowDSSI", ui.ch_dssi->isChecked()); + settings.setValue("PluginDatabase/ShowLV2", ui.ch_lv2->isChecked()); + settings.setValue("PluginDatabase/ShowVST2", ui.ch_vst->isChecked()); + settings.setValue("PluginDatabase/ShowVST3", ui.ch_vst3->isChecked()); + settings.setValue("PluginDatabase/ShowCLAP", ui.ch_clap->isChecked()); + settings.setValue("PluginDatabase/ShowAU", ui.ch_au->isChecked()); + settings.setValue("PluginDatabase/ShowJSFX", ui.ch_jsfx->isChecked()); + settings.setValue("PluginDatabase/ShowKits", ui.ch_kits->isChecked()); + settings.setValue("PluginDatabase/ShowNative", ui.ch_native->isChecked()); + settings.setValue("PluginDatabase/ShowBridged", ui.ch_bridged->isChecked()); + settings.setValue("PluginDatabase/ShowBridgedWine", ui.ch_bridged_wine->isChecked()); + settings.setValue("PluginDatabase/ShowFavorites", ui.ch_favorites->isChecked()); + settings.setValue("PluginDatabase/ShowRtSafe", ui.ch_rtsafe->isChecked()); + settings.setValue("PluginDatabase/ShowHasCV", ui.ch_cv->isChecked()); + settings.setValue("PluginDatabase/ShowHasGUI", ui.ch_gui->isChecked()); + settings.setValue("PluginDatabase/ShowHasInlineDisplay", ui.ch_inline_display->isChecked()); + settings.setValue("PluginDatabase/ShowStereoOnly", ui.ch_stereo->isChecked()); + settings.setValue("PluginDatabase/SearchText", ui.lineEdit->text()); + + if (ui.ch_cat_all->isChecked()) { settings.setValue("PluginDatabase/ShowCategory", "all"); } else { QCarlaString categories; - if (self.ui.ch_cat_delay->isChecked()) + if (ui.ch_cat_delay->isChecked()) categories += ":delay"; - if (self.ui.ch_cat_distortion->isChecked()) + if (ui.ch_cat_distortion->isChecked()) categories += ":distortion"; - if (self.ui.ch_cat_dynamics->isChecked()) + if (ui.ch_cat_dynamics->isChecked()) categories += ":dynamics"; - if (self.ui.ch_cat_eq->isChecked()) + if (ui.ch_cat_eq->isChecked()) categories += ":eq"; - if (self.ui.ch_cat_filter->isChecked()) + if (ui.ch_cat_filter->isChecked()) categories += ":filter"; - if (self.ui.ch_cat_modulator->isChecked()) + if (ui.ch_cat_modulator->isChecked()) categories += ":modulator"; - if (self.ui.ch_cat_synth->isChecked()) + if (ui.ch_cat_synth->isChecked()) categories += ":synth"; - if (self.ui.ch_cat_utility->isChecked()) + if (ui.ch_cat_utility->isChecked()) categories += ":utility"; - if (self.ui.ch_cat_other->isChecked()) + if (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); + settings.setValue("PluginListDialog/Favorites", asVariant(p->plugins.favorites)); } // -------------------------------------------------------------------------------------------------------------------- @@ -1826,7 +1824,6 @@ carla_frontend_createAndExecPluginListDialog(void* const parent/*, const HostSet label = plugin.label.toUtf8(); maker = plugin.maker.toUtf8(); - ret.API = plugin.API; ret.build = plugin.build; ret.type = plugin.type; ret.hints = plugin.hints; @@ -1835,14 +1832,15 @@ carla_frontend_createAndExecPluginListDialog(void* const parent/*, const HostSet ret.name = name; ret.label = label; ret.maker = maker; + ret.uniqueId = plugin.uniqueId; 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; + ret.parameterIns = plugin.parameterIns; + ret.parameterOuts = plugin.parameterOuts; return &ret; } diff --git a/source/frontend/pluginlist/pluginlistdialog.hpp b/source/frontend/pluginlist/pluginlistdialog.hpp index 9168246a3..275afc046 100644 --- a/source/frontend/pluginlist/pluginlistdialog.hpp +++ b/source/frontend/pluginlist/pluginlistdialog.hpp @@ -1,18 +1,7 @@ /* * 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. + * SPDX-License-Identifier: GPL-2.0-or-later */ #pragma once @@ -35,9 +24,7 @@ # pragma GCC diagnostic pop #endif -#include "CarlaBackend.h" - -static constexpr const uint PLUGIN_QUERY_API_VERSION = 12; +#include "ui_pluginlistdialog.h" struct HostSettings { bool showPluginBridges; @@ -45,73 +32,76 @@ struct HostSettings { bool useSystemIcons; }; -struct PluginInfo { - uint API; - CARLA_BACKEND_NAMESPACE::BinaryType build; - CARLA_BACKEND_NAMESPACE::PluginType type; - uint hints; - QString category; - QString filename; - QString name; - QString label; - QString maker; - uint64_t uniqueId; - uint audioIns; - uint audioOuts; - uint cvIns; - uint cvOuts; - uint midiIns; - uint midiOuts; - uint parametersIns; - uint parametersOuts; -}; +class QSafeSettings; +typedef struct _CarlaPluginDiscoveryInfo CarlaPluginDiscoveryInfo; +struct PluginInfo; // -------------------------------------------------------------------------------------------------------------------- // Plugin List Dialog class PluginListDialog : public QDialog { - struct Self; - Self& self; + enum TableIndex { + TW_FAVORITE, + TW_NAME, + TW_LABEL, + TW_MAKER, + TW_BINARY, + }; + + enum UserRoles { + UR_PLUGIN_INFO = 1, + UR_SEARCH_TEXT, + }; + + struct PrivateData; + PrivateData *const p; + + Ui_PluginListDialog ui; // ---------------------------------------------------------------------------------------------------------------- + // public methods public: explicit PluginListDialog(QWidget* parent, const HostSettings& hostSettings); ~PluginListDialog() override; - // ---------------------------------------------------------------------------------------------------------------- - // public methods - const PluginInfo& getSelectedPluginInfo() const; + void addPluginInfo(const CarlaPluginDiscoveryInfo* info, const char* sha1sum); + bool checkPluginCache(const char* filename, const char* sha1sum); // ---------------------------------------------------------------------------------------------------------------- // protected methods protected: + void done(int) override; void showEvent(QShowEvent*) override; void timerEvent(QTimerEvent*) override; // ---------------------------------------------------------------------------------------------------------------- // private methods +private: + void addPluginsToTable(); void loadSettings(); // ---------------------------------------------------------------------------------------------------------------- // private slots private Q_SLOTS: - void slot_cellClicked(int row, int column); - void slot_cellDoubleClicked(int row, int column); - void slot_focusSearchFieldAndSelectAll(); - void slot_addPlugin(); - void slot_checkPlugin(int row); - void slot_checkFilters(); - void slot_checkFiltersCategoryAll(bool clicked); - void slot_checkFiltersCategorySpecific(bool clicked); - void slot_refreshPlugins(); - void slot_clearFilters(); - void slot_saveSettings(); + void cellClicked(int row, int column); + void cellDoubleClicked(int row, int column); + void focusSearchFieldAndSelectAll(); + void checkFilters(); + void checkFiltersCategoryAll(bool clicked); + void checkFiltersCategorySpecific(bool clicked); + void clearFilters(); + void checkPlugin(int row); + void refreshPlugins(); + void refreshPluginsStart(); + void refreshPluginsStop(); + void refreshPluginsSkip(); + void saveSettings(); }; // -------------------------------------------------------------------------------------------------------------------- diff --git a/source/frontend/pluginlist/pluginlistdialog.py b/source/frontend/pluginlist/pluginlistdialog.py deleted file mode 100755 index 193352d21..000000000 --- a/source/frontend/pluginlist/pluginlistdialog.py +++ /dev/null @@ -1,1000 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -# Carla plugin host -# Copyright (C) 2011-2022 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. - -# --------------------------------------------------------------------------------------------------------------------- -# Imports (Global) - -import os - -from PyQt5.QtCore import pyqtSlot, Qt, QByteArray, QEventLoop -from PyQt5.QtWidgets import QApplication, QDialog, QHeaderView, QTableWidgetItem, QWidget - -# --------------------------------------------------------------------------------------------------------------------- -# Imports (Carla) - -from carla_backend import ( - BINARY_NATIVE, - BINARY_OTHER, - BINARY_POSIX32, - BINARY_POSIX64, - BINARY_WIN32, - BINARY_WIN64, - PLUGIN_AU, - PLUGIN_DSSI, - PLUGIN_HAS_CUSTOM_UI, - PLUGIN_HAS_INLINE_DISPLAY, - PLUGIN_INTERNAL, - PLUGIN_IS_BRIDGE, - PLUGIN_IS_RTSAFE, - PLUGIN_IS_SYNTH, - PLUGIN_JSFX, - PLUGIN_LADSPA, - PLUGIN_LV2, - PLUGIN_SF2, - PLUGIN_SFZ, - PLUGIN_VST2, - PLUGIN_VST3, - PLUGIN_CLAP, -) - -from carla_shared import ( - CARLA_DEFAULT_LV2_PATH, - CARLA_KEY_PATHS_LV2, - HAIKU, - LINUX, - MACOS, - WINDOWS, - fontMetricsHorizontalAdvance, - gCarla, - getIcon, - kIs64bit, - splitter, -) - -from carla_utils import getPluginTypeAsString - -from utils import QSafeSettings - -# --------------------------------------------------------------------------------------------------------------------- -# Imports (Local) - -from .discovery import PLUGIN_QUERY_API_VERSION, checkPluginCached -from .pluginlistdialog_ui import Ui_PluginListDialog -from .pluginlistrefreshdialog import PluginRefreshW - -# --------------------------------------------------------------------------------------------------------------------- -# Plugin Database Dialog - -class PluginListDialog(QDialog): - TABLEWIDGET_ITEM_FAVORITE = 0 - TABLEWIDGET_ITEM_NAME = 1 - TABLEWIDGET_ITEM_LABEL = 2 - TABLEWIDGET_ITEM_MAKER = 3 - TABLEWIDGET_ITEM_BINARY = 4 - - def __init__(self, parent: QWidget, host, useSystemIcons: bool): - QDialog.__init__(self, parent) - self.host = host - self.ui = Ui_PluginListDialog() - self.ui.setupUi(self) - - # To be changed by parent - self.hasLoadedLv2Plugins = False - - # ---------------------------------------------------------------------------------------------------- - # Internal stuff - - self.fLastTableIndex = 0 - self.fRetPlugin = None - self.fRealParent = parent - self.fFavoritePlugins = [] - self.fFavoritePluginsChanged = False - self.fUseSystemIcons = useSystemIcons # TODO cpp - - self.fTrYes = self.tr("Yes") - self.fTrNo = self.tr("No") - self.fTrNative = self.tr("Native") - - # ---------------------------------------------------------------------------------------------------- - # Set-up GUI - - self.ui.b_add.setEnabled(False) - self.addAction(self.ui.act_focus_search) - self.ui.act_focus_search.triggered.connect(self.slot_focusSearchFieldAndSelectAll) - - if BINARY_NATIVE in (BINARY_POSIX32, BINARY_WIN32): - self.ui.ch_bridged.setText(self.tr("Bridged (64bit)")) - else: - self.ui.ch_bridged.setText(self.tr("Bridged (32bit)")) - - if not (LINUX or MACOS): - self.ui.ch_bridged_wine.setChecked(False) - self.ui.ch_bridged_wine.setEnabled(False) - - if MACOS: - self.setWindowModality(Qt.WindowModal) - else: - self.ui.ch_au.setChecked(False) - self.ui.ch_au.setEnabled(False) - self.ui.ch_au.setVisible(False) - - self.ui.tab_info.tabBar().hide() - self.ui.tab_reqs.tabBar().hide() - # FIXME, why /2 needed? - self.ui.tab_info.setMinimumWidth(int(self.ui.la_id.width()/2) + - fontMetricsHorizontalAdvance(self.ui.l_id.fontMetrics(), "9999999999") + 6*3) - self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint) - - # ---------------------------------------------------------------------------------------------------- - # Load settings - - self.loadSettings() - - # ---------------------------------------------------------------------------------------------------- - # Disable bridges if not enabled in settings - - # 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) - - # ---------------------------------------------------------------------------------------------------- - # Set-up Icons - - if useSystemIcons: - 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')) - hhi = self.ui.tableWidget.horizontalHeaderItem(self.TABLEWIDGET_ITEM_FAVORITE) - hhi.setIcon(getIcon('bookmarks', 16, 'svgz')) - - # ---------------------------------------------------------------------------------------------------- - # Set-up connections - - self.finished.connect(self.slot_saveSettings) - self.ui.b_add.clicked.connect(self.slot_addPlugin) - self.ui.b_cancel.clicked.connect(self.reject) - self.ui.b_refresh.clicked.connect(self.slot_refreshPlugins) - self.ui.b_clear_filters.clicked.connect(self.slot_clearFilters) - self.ui.lineEdit.textChanged.connect(self.slot_checkFilters) - self.ui.tableWidget.currentCellChanged.connect(self.slot_checkPlugin) - self.ui.tableWidget.cellClicked.connect(self.slot_cellClicked) - self.ui.tableWidget.cellDoubleClicked.connect(self.slot_cellDoubleClicked) - - self.ui.ch_internal.clicked.connect(self.slot_checkFilters) - self.ui.ch_ladspa.clicked.connect(self.slot_checkFilters) - self.ui.ch_dssi.clicked.connect(self.slot_checkFilters) - self.ui.ch_lv2.clicked.connect(self.slot_checkFilters) - self.ui.ch_vst.clicked.connect(self.slot_checkFilters) - self.ui.ch_vst3.clicked.connect(self.slot_checkFilters) - self.ui.ch_clap.clicked.connect(self.slot_checkFilters) - self.ui.ch_au.clicked.connect(self.slot_checkFilters) - self.ui.ch_jsfx.clicked.connect(self.slot_checkFilters) - self.ui.ch_kits.clicked.connect(self.slot_checkFilters) - self.ui.ch_effects.clicked.connect(self.slot_checkFilters) - self.ui.ch_instruments.clicked.connect(self.slot_checkFilters) - self.ui.ch_midi.clicked.connect(self.slot_checkFilters) - self.ui.ch_other.clicked.connect(self.slot_checkFilters) - self.ui.ch_native.clicked.connect(self.slot_checkFilters) - self.ui.ch_bridged.clicked.connect(self.slot_checkFilters) - self.ui.ch_bridged_wine.clicked.connect(self.slot_checkFilters) - self.ui.ch_favorites.clicked.connect(self.slot_checkFilters) - self.ui.ch_rtsafe.clicked.connect(self.slot_checkFilters) - self.ui.ch_cv.clicked.connect(self.slot_checkFilters) - self.ui.ch_gui.clicked.connect(self.slot_checkFilters) - self.ui.ch_inline_display.clicked.connect(self.slot_checkFilters) - self.ui.ch_stereo.clicked.connect(self.slot_checkFilters) - self.ui.ch_cat_all.clicked.connect(self.slot_checkFiltersCategoryAll) - self.ui.ch_cat_delay.clicked.connect(self.slot_checkFiltersCategorySpecific) - self.ui.ch_cat_distortion.clicked.connect(self.slot_checkFiltersCategorySpecific) - self.ui.ch_cat_dynamics.clicked.connect(self.slot_checkFiltersCategorySpecific) - self.ui.ch_cat_eq.clicked.connect(self.slot_checkFiltersCategorySpecific) - self.ui.ch_cat_filter.clicked.connect(self.slot_checkFiltersCategorySpecific) - self.ui.ch_cat_modulator.clicked.connect(self.slot_checkFiltersCategorySpecific) - self.ui.ch_cat_synth.clicked.connect(self.slot_checkFiltersCategorySpecific) - self.ui.ch_cat_utility.clicked.connect(self.slot_checkFiltersCategorySpecific) - self.ui.ch_cat_other.clicked.connect(self.slot_checkFiltersCategorySpecific) - - # ---------------------------------------------------------------------------------------------------- - # Post-connect setup - - self._reAddPlugins() - self.slot_focusSearchFieldAndSelectAll() - - # -------------------------------------------------------------------------------------------------------- - - @pyqtSlot(int, int) - def slot_cellClicked(self, row, column): - if column != self.TABLEWIDGET_ITEM_FAVORITE: - return - - widget = self.ui.tableWidget.item(row, self.TABLEWIDGET_ITEM_FAVORITE) - plugin = self.ui.tableWidget.item(row, self.TABLEWIDGET_ITEM_NAME).data(Qt.UserRole+1) - plugin = self._createFavoritePluginDict(plugin) - - if widget.checkState() == Qt.Checked: - if not plugin in self.fFavoritePlugins: - self.fFavoritePlugins.append(plugin) - self.fFavoritePluginsChanged = True - else: - try: - self.fFavoritePlugins.remove(plugin) - self.fFavoritePluginsChanged = True - except ValueError: - pass - - @pyqtSlot(int, int) - def slot_cellDoubleClicked(self, _, column): - if column != self.TABLEWIDGET_ITEM_FAVORITE: - self.slot_addPlugin() - - @pyqtSlot() - def slot_focusSearchFieldAndSelectAll(self): - self.ui.lineEdit.setFocus() - self.ui.lineEdit.selectAll() - - @pyqtSlot() - def slot_addPlugin(self): - if self.ui.tableWidget.currentRow() >= 0: - self.fRetPlugin = self.ui.tableWidget.item(self.ui.tableWidget.currentRow(), - self.TABLEWIDGET_ITEM_NAME).data(Qt.UserRole+1) - self.accept() - else: - self.reject() - - @pyqtSlot(int) - def slot_checkPlugin(self, row): - if row >= 0: - self.ui.b_add.setEnabled(True) - plugin = self.ui.tableWidget.item(row, self.TABLEWIDGET_ITEM_NAME).data(Qt.UserRole+1) - - isSynth = bool(plugin['hints'] & PLUGIN_IS_SYNTH) - isEffect = bool(plugin['audio.ins'] > 0 < plugin['audio.outs'] and not isSynth) - isMidi = bool(plugin['audio.ins'] == 0 and - plugin['audio.outs'] == 0 and - plugin['midi.ins'] > 0 < plugin['midi.outs']) - # isKit = bool(plugin['type'] in (PLUGIN_SF2, PLUGIN_SFZ)) - # isOther = bool(not (isEffect or isSynth or isMidi or isKit)) - - if isSynth: - ptype = "Instrument" - elif isEffect: - ptype = "Effect" - elif isMidi: - ptype = "MIDI Plugin" - else: - ptype = "Other" - - if plugin['build'] == BINARY_NATIVE: - parch = self.fTrNative - elif plugin['build'] == BINARY_POSIX32: - parch = "posix32" - elif plugin['build'] == BINARY_POSIX64: - parch = "posix64" - elif plugin['build'] == BINARY_WIN32: - parch = "win32" - elif plugin['build'] == BINARY_WIN64: - parch = "win64" - elif plugin['build'] == BINARY_OTHER: - parch = self.tr("Other") - elif plugin['build'] == BINARY_WIN32: - parch = self.tr("Unknown") - - self.ui.l_format.setText(getPluginTypeAsString(plugin['type'])) - self.ui.l_type.setText(ptype) - self.ui.l_arch.setText(parch) - self.ui.l_id.setText(str(plugin['uniqueId'])) - self.ui.l_ains.setText(str(plugin['audio.ins'])) - self.ui.l_aouts.setText(str(plugin['audio.outs'])) - self.ui.l_cvins.setText(str(plugin['cv.ins'])) - self.ui.l_cvouts.setText(str(plugin['cv.outs'])) - self.ui.l_mins.setText(str(plugin['midi.ins'])) - self.ui.l_mouts.setText(str(plugin['midi.outs'])) - self.ui.l_pins.setText(str(plugin['parameters.ins'])) - self.ui.l_pouts.setText(str(plugin['parameters.outs'])) - self.ui.l_gui.setText(self.fTrYes if plugin['hints'] & PLUGIN_HAS_CUSTOM_UI else self.fTrNo) - self.ui.l_idisp.setText(self.fTrYes if plugin['hints'] & PLUGIN_HAS_INLINE_DISPLAY else self.fTrNo) - self.ui.l_bridged.setText(self.fTrYes if plugin['hints'] & PLUGIN_IS_BRIDGE else self.fTrNo) - self.ui.l_synth.setText(self.fTrYes if isSynth else self.fTrNo) - else: - self.ui.b_add.setEnabled(False) - self.ui.l_format.setText("---") - self.ui.l_type.setText("---") - self.ui.l_arch.setText("---") - self.ui.l_id.setText("---") - self.ui.l_ains.setText("---") - self.ui.l_aouts.setText("---") - self.ui.l_cvins.setText("---") - self.ui.l_cvouts.setText("---") - self.ui.l_mins.setText("---") - self.ui.l_mouts.setText("---") - self.ui.l_pins.setText("---") - self.ui.l_pouts.setText("---") - self.ui.l_gui.setText("---") - self.ui.l_idisp.setText("---") - self.ui.l_bridged.setText("---") - self.ui.l_synth.setText("---") - - @pyqtSlot() - def slot_checkFilters(self): - self._checkFilters() - - @pyqtSlot(bool) - def slot_checkFiltersCategoryAll(self, clicked): - self.ui.ch_cat_delay.setChecked(not clicked) - self.ui.ch_cat_distortion.setChecked(not clicked) - self.ui.ch_cat_dynamics.setChecked(not clicked) - self.ui.ch_cat_eq.setChecked(not clicked) - self.ui.ch_cat_filter.setChecked(not clicked) - self.ui.ch_cat_modulator.setChecked(not clicked) - self.ui.ch_cat_synth.setChecked(not clicked) - self.ui.ch_cat_utility.setChecked(not clicked) - self.ui.ch_cat_other.setChecked(not clicked) - self._checkFilters() - - @pyqtSlot(bool) - def slot_checkFiltersCategorySpecific(self, clicked): - if clicked: - self.ui.ch_cat_all.setChecked(False) - elif not (self.ui.ch_cat_delay.isChecked() or - self.ui.ch_cat_distortion.isChecked() or - self.ui.ch_cat_dynamics.isChecked() or - self.ui.ch_cat_eq.isChecked() or - self.ui.ch_cat_filter.isChecked() or - self.ui.ch_cat_modulator.isChecked() or - self.ui.ch_cat_synth.isChecked() or - self.ui.ch_cat_utility.isChecked() or - self.ui.ch_cat_other.isChecked()): - self.ui.ch_cat_all.setChecked(True) - self._checkFilters() - - @pyqtSlot() - def slot_refreshPlugins(self): - if PluginRefreshW(self, self.host, self.fUseSystemIcons, self.hasLoadedLv2Plugins).exec_(): - self._reAddPlugins() - - if self.fRealParent: - self.fRealParent.setLoadRDFsNeeded() - - @pyqtSlot() - def slot_clearFilters(self): - self.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() - - self.blockSignals(False) - - self._checkFilters() - - # -------------------------------------------------------------------------------------------------------- - - @pyqtSlot() - def slot_saveSettings(self): - settings = QSafeSettings("falkTX", "CarlaDatabase2") - settings.setValue("PluginDatabase/Geometry", self.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: - 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: - categories += ":" - settings.setValue("PluginDatabase/ShowCategory", categories) - - if self.fFavoritePluginsChanged: - settings.setValue("PluginDatabase/Favorites", self.fFavoritePlugins) - - # -------------------------------------------------------------------------------------------------------- - - def loadSettings(self): - settings = QSafeSettings("falkTX", "CarlaDatabase2") - self.fFavoritePlugins = settings.value("PluginDatabase/Favorites", [], list) - self.fFavoritePluginsChanged = False - - self.restoreGeometry(settings.value("PluginDatabase/Geometry", QByteArray(), QByteArray)) - self.ui.ch_effects.setChecked(settings.value("PluginDatabase/ShowEffects", True, bool)) - self.ui.ch_instruments.setChecked(settings.value("PluginDatabase/ShowInstruments", True, bool)) - self.ui.ch_midi.setChecked(settings.value("PluginDatabase/ShowMIDI", True, bool)) - self.ui.ch_other.setChecked(settings.value("PluginDatabase/ShowOther", True, bool)) - self.ui.ch_internal.setChecked(settings.value("PluginDatabase/ShowInternal", True, bool)) - self.ui.ch_ladspa.setChecked(settings.value("PluginDatabase/ShowLADSPA", True, bool)) - self.ui.ch_dssi.setChecked(settings.value("PluginDatabase/ShowDSSI", True, bool)) - self.ui.ch_lv2.setChecked(settings.value("PluginDatabase/ShowLV2", True, bool)) - self.ui.ch_vst.setChecked(settings.value("PluginDatabase/ShowVST2", True, bool)) - self.ui.ch_vst3.setChecked(settings.value("PluginDatabase/ShowVST3", True, bool)) - self.ui.ch_clap.setChecked(settings.value("PluginDatabase/ShowCLAP", True, bool)) - self.ui.ch_au.setChecked(settings.value("PluginDatabase/ShowAU", MACOS, bool)) - self.ui.ch_jsfx.setChecked(settings.value("PluginDatabase/ShowJSFX", True, bool)) - self.ui.ch_kits.setChecked(settings.value("PluginDatabase/ShowKits", True, bool)) - self.ui.ch_native.setChecked(settings.value("PluginDatabase/ShowNative", True, bool)) - self.ui.ch_bridged.setChecked(settings.value("PluginDatabase/ShowBridged", True, bool)) - self.ui.ch_bridged_wine.setChecked(settings.value("PluginDatabase/ShowBridgedWine", True, bool)) - self.ui.ch_favorites.setChecked(settings.value("PluginDatabase/ShowFavorites", False, bool)) - self.ui.ch_rtsafe.setChecked(settings.value("PluginDatabase/ShowRtSafe", False, bool)) - self.ui.ch_cv.setChecked(settings.value("PluginDatabase/ShowHasCV", False, bool)) - self.ui.ch_gui.setChecked(settings.value("PluginDatabase/ShowHasGUI", False, bool)) - self.ui.ch_inline_display.setChecked(settings.value("PluginDatabase/ShowHasInlineDisplay", False, bool)) - self.ui.ch_stereo.setChecked(settings.value("PluginDatabase/ShowStereoOnly", False, bool)) - self.ui.lineEdit.setText(settings.value("PluginDatabase/SearchText", "", str)) - - categories = settings.value("PluginDatabase/ShowCategory", "all", str) - if categories == "all" or len(categories) < 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(":delay:" in categories) - self.ui.ch_cat_distortion.setChecked(":distortion:" in categories) - self.ui.ch_cat_dynamics.setChecked(":dynamics:" in categories) - self.ui.ch_cat_eq.setChecked(":eq:" in categories) - self.ui.ch_cat_filter.setChecked(":filter:" in categories) - self.ui.ch_cat_modulator.setChecked(":modulator:" in categories) - self.ui.ch_cat_synth.setChecked(":synth:" in categories) - self.ui.ch_cat_utility.setChecked(":utility:" in categories) - self.ui.ch_cat_other.setChecked(":other:" in categories) - - tableGeometry = settings.value("PluginDatabase/TableGeometry_6", QByteArray(), QByteArray) - horizontalHeader = self.ui.tableWidget.horizontalHeader() - if not 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) - - # -------------------------------------------------------------------------------------------------------- - - def _createFavoritePluginDict(self, plugin): - return { - 'name' : plugin['name'], - 'build' : plugin['build'], - 'type' : plugin['type'], - 'filename': plugin['filename'], - 'label' : plugin['label'], - 'uniqueId': plugin['uniqueId'], - } - - def _checkFilters(self): - text = self.ui.lineEdit.text().lower() - - hideEffects = not self.ui.ch_effects.isChecked() - hideInstruments = not self.ui.ch_instruments.isChecked() - hideMidi = not self.ui.ch_midi.isChecked() - hideOther = not self.ui.ch_other.isChecked() - - hideInternal = not self.ui.ch_internal.isChecked() - hideLadspa = not self.ui.ch_ladspa.isChecked() - hideDssi = not self.ui.ch_dssi.isChecked() - hideLV2 = not self.ui.ch_lv2.isChecked() - hideVST2 = not self.ui.ch_vst.isChecked() - hideVST3 = not self.ui.ch_vst3.isChecked() - hideCLAP = not self.ui.ch_clap.isChecked() - hideAU = not self.ui.ch_au.isChecked() - hideJSFX = not self.ui.ch_jsfx.isChecked() - hideKits = not self.ui.ch_kits.isChecked() - - hideNative = not self.ui.ch_native.isChecked() - hideBridged = not self.ui.ch_bridged.isChecked() - hideBridgedWine = not self.ui.ch_bridged_wine.isChecked() - - hideNonFavs = self.ui.ch_favorites.isChecked() - hideNonRtSafe = self.ui.ch_rtsafe.isChecked() - hideNonCV = self.ui.ch_cv.isChecked() - hideNonGui = self.ui.ch_gui.isChecked() - hideNonIDisp = self.ui.ch_inline_display.isChecked() - hideNonStereo = self.ui.ch_stereo.isChecked() - - 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 = [] - - self.ui.tableWidget.setRowCount(self.fLastTableIndex) - - for i in range(self.fLastTableIndex): - plugin = self.ui.tableWidget.item(i, self.TABLEWIDGET_ITEM_NAME).data(Qt.UserRole+1) - ptext = self.ui.tableWidget.item(i, self.TABLEWIDGET_ITEM_NAME).data(Qt.UserRole+2) - aIns = plugin['audio.ins'] - aOuts = plugin['audio.outs'] - cvIns = plugin['cv.ins'] - cvOuts = plugin['cv.outs'] - mIns = plugin['midi.ins'] - mOuts = plugin['midi.outs'] - phints = plugin['hints'] - ptype = plugin['type'] - categ = plugin['category'] - isSynth = bool(phints & PLUGIN_IS_SYNTH) - isEffect = bool(aIns > 0 < aOuts and not isSynth) - isMidi = bool(aIns == 0 and aOuts == 0 and mIns > 0 < mOuts) - isKit = bool(ptype in (PLUGIN_SF2, PLUGIN_SFZ)) - isOther = bool(not (isEffect or isSynth or isMidi or isKit)) - isNative = bool(plugin['build'] == BINARY_NATIVE) - isRtSafe = bool(phints & PLUGIN_IS_RTSAFE) - isStereo = bool((aIns == 2 and aOuts == 2) or (isSynth and aOuts == 2)) - hasCV = bool(cvIns + cvOuts > 0) - hasGui = bool(phints & PLUGIN_HAS_CUSTOM_UI) - hasIDisp = bool(phints & PLUGIN_HAS_INLINE_DISPLAY) - - isBridged = bool(not isNative and plugin['build'] in nativeBins) - isBridgedWine = bool(not isNative and plugin['build'] in wineBins) - - if hideEffects and isEffect: - self.ui.tableWidget.hideRow(i) - elif hideInstruments and isSynth: - self.ui.tableWidget.hideRow(i) - elif hideMidi and isMidi: - self.ui.tableWidget.hideRow(i) - elif hideOther and isOther: - self.ui.tableWidget.hideRow(i) - elif hideKits and isKit: - self.ui.tableWidget.hideRow(i) - elif hideInternal and ptype == PLUGIN_INTERNAL: - self.ui.tableWidget.hideRow(i) - elif hideLadspa and ptype == PLUGIN_LADSPA: - self.ui.tableWidget.hideRow(i) - elif hideDssi and ptype == PLUGIN_DSSI: - self.ui.tableWidget.hideRow(i) - elif hideLV2 and ptype == PLUGIN_LV2: - self.ui.tableWidget.hideRow(i) - elif hideVST2 and ptype == PLUGIN_VST2: - self.ui.tableWidget.hideRow(i) - elif hideVST3 and ptype == PLUGIN_VST3: - self.ui.tableWidget.hideRow(i) - elif hideCLAP and ptype == PLUGIN_CLAP: - self.ui.tableWidget.hideRow(i) - elif hideAU and ptype == PLUGIN_AU: - self.ui.tableWidget.hideRow(i) - elif hideJSFX and ptype == PLUGIN_JSFX: - self.ui.tableWidget.hideRow(i) - elif hideNative and isNative: - self.ui.tableWidget.hideRow(i) - elif hideBridged and isBridged: - self.ui.tableWidget.hideRow(i) - elif hideBridgedWine and isBridgedWine: - self.ui.tableWidget.hideRow(i) - elif hideNonRtSafe and not isRtSafe: - self.ui.tableWidget.hideRow(i) - elif hideNonCV and not hasCV: - self.ui.tableWidget.hideRow(i) - elif hideNonGui and not hasGui: - self.ui.tableWidget.hideRow(i) - elif hideNonIDisp and not hasIDisp: - self.ui.tableWidget.hideRow(i) - elif hideNonStereo and not isStereo: - self.ui.tableWidget.hideRow(i) - elif text and not all(t in ptext for t in text.strip().split(' ')): - self.ui.tableWidget.hideRow(i) - elif hideNonFavs and self._createFavoritePluginDict(plugin) not in self.fFavoritePlugins: - self.ui.tableWidget.hideRow(i) - elif (self.ui.ch_cat_all.isChecked() or - (self.ui.ch_cat_delay.isChecked() and categ == "delay") or - (self.ui.ch_cat_distortion.isChecked() and categ == "distortion") or - (self.ui.ch_cat_dynamics.isChecked() and categ == "dynamics") or - (self.ui.ch_cat_eq.isChecked() and categ == "eq") or - (self.ui.ch_cat_filter.isChecked() and categ == "filter") or - (self.ui.ch_cat_modulator.isChecked() and categ == "modulator") or - (self.ui.ch_cat_synth.isChecked() and categ == "synth") or - (self.ui.ch_cat_utility.isChecked() and categ == "utility") or - (self.ui.ch_cat_other.isChecked() and categ == "other")): - self.ui.tableWidget.showRow(i) - else: - self.ui.tableWidget.hideRow(i) - - # -------------------------------------------------------------------------------------------------------- - - def _addPluginToTable(self, plugin, ptype): - if plugin['API'] != PLUGIN_QUERY_API_VERSION: - return - if ptype in (PLUGIN_INTERNAL, PLUGIN_LV2, PLUGIN_SF2, PLUGIN_SFZ, PLUGIN_JSFX): - plugin['build'] = BINARY_NATIVE - - index = self.fLastTableIndex - - isFav = bool(self._createFavoritePluginDict(plugin) in self.fFavoritePlugins) - itemFav = QTableWidgetItem() - itemFav.setCheckState(Qt.Checked if isFav else Qt.Unchecked) - itemFav.setText(" " if isFav else " ") - - pluginText = (plugin['name']+plugin['label']+plugin['maker']+plugin['filename']).lower() - self.ui.tableWidget.setItem(index, self.TABLEWIDGET_ITEM_FAVORITE, itemFav) - self.ui.tableWidget.setItem(index, self.TABLEWIDGET_ITEM_NAME, QTableWidgetItem(plugin['name'])) - self.ui.tableWidget.setItem(index, self.TABLEWIDGET_ITEM_LABEL, QTableWidgetItem(plugin['label'])) - self.ui.tableWidget.setItem(index, self.TABLEWIDGET_ITEM_MAKER, QTableWidgetItem(plugin['maker'])) - self.ui.tableWidget.setItem(index, self.TABLEWIDGET_ITEM_BINARY, QTableWidgetItem(os.path.basename(plugin['filename']))) - - itemName = self.ui.tableWidget.item(index, self.TABLEWIDGET_ITEM_NAME) - itemName.setData(Qt.UserRole+1, plugin) - itemName.setData(Qt.UserRole+2, pluginText) - - self.fLastTableIndex += 1 - - # -------------------------------------------------------------------------------------------------------- - - def _reAddInternalHelper(self, settingsDB, ptype, path): - if ptype == PLUGIN_INTERNAL: - ptypeStr = "Internal" - ptypeStrTr = self.tr("Internal") - elif ptype == PLUGIN_LV2: - ptypeStr = "LV2" - ptypeStrTr = ptypeStr - elif ptype == PLUGIN_AU: - ptypeStr = "AU" - ptypeStrTr = ptypeStr - #elif ptype == PLUGIN_SFZ: - #ptypeStr = "SFZ" - #ptypeStrTr = ptypeStr - # TODO(jsfx) what to do here? - else: - return 0 - - plugins = settingsDB.value("Plugins/" + ptypeStr, [], list) - pluginCount = settingsDB.value("PluginCount/" + ptypeStr, 0, int) - - if ptype == PLUGIN_AU: - gCarla.utils.juce_init() - - pluginCountNew = gCarla.utils.get_cached_plugin_count(ptype, path) - - if pluginCountNew != pluginCount or len(plugins) != pluginCount or (len(plugins) > 0 and plugins[0]['API'] != PLUGIN_QUERY_API_VERSION): - plugins = [] - pluginCount = pluginCountNew - - QApplication.processEvents(QEventLoop.ExcludeUserInputEvents, 50) - if ptype == PLUGIN_AU: - gCarla.utils.juce_idle() - - for i in range(pluginCountNew): - descInfo = gCarla.utils.get_cached_plugin_info(ptype, i) - - if not descInfo['valid']: - continue - - info = checkPluginCached(descInfo, ptype) - - plugins.append(info) - - if i % 50 == 0: - QApplication.processEvents(QEventLoop.ExcludeUserInputEvents, 50) - if ptype == PLUGIN_AU: - gCarla.utils.juce_idle() - - settingsDB.setValue("Plugins/" + ptypeStr, plugins) - settingsDB.setValue("PluginCount/" + ptypeStr, pluginCount) - - if ptype == PLUGIN_AU: - gCarla.utils.juce_cleanup() - - # prepare rows in advance - self.ui.tableWidget.setRowCount(self.fLastTableIndex + len(plugins)) - - for plugin in plugins: - self._addPluginToTable(plugin, ptype) - - return pluginCount - - def _reAddPlugins(self): - settingsDB = QSafeSettings("falkTX", "CarlaPlugins5") - - self.fLastTableIndex = 0 - self.ui.tableWidget.setSortingEnabled(False) - self.ui.tableWidget.clearContents() - - settings = QSafeSettings("falkTX", "Carla2") - LV2_PATH = splitter.join(settings.value(CARLA_KEY_PATHS_LV2, CARLA_DEFAULT_LV2_PATH, list)) - del settings - - # ---------------------------------------------------------------------------------------------------- - # plugins handled through backend - - internalCount = self._reAddInternalHelper(settingsDB, PLUGIN_INTERNAL, "") - lv2Count = self._reAddInternalHelper(settingsDB, PLUGIN_LV2, LV2_PATH) - auCount = self._reAddInternalHelper(settingsDB, PLUGIN_AU, "") if MACOS else 0 - - # ---------------------------------------------------------------------------------------------------- - # LADSPA - - ladspaPlugins = [] - ladspaPlugins += settingsDB.value("Plugins/LADSPA_native", [], list) - ladspaPlugins += settingsDB.value("Plugins/LADSPA_posix32", [], list) - ladspaPlugins += settingsDB.value("Plugins/LADSPA_posix64", [], list) - ladspaPlugins += settingsDB.value("Plugins/LADSPA_win32", [], list) - ladspaPlugins += settingsDB.value("Plugins/LADSPA_win64", [], list) - - # ---------------------------------------------------------------------------------------------------- - # DSSI - - dssiPlugins = [] - dssiPlugins += settingsDB.value("Plugins/DSSI_native", [], list) - dssiPlugins += settingsDB.value("Plugins/DSSI_posix32", [], list) - dssiPlugins += settingsDB.value("Plugins/DSSI_posix64", [], list) - dssiPlugins += settingsDB.value("Plugins/DSSI_win32", [], list) - dssiPlugins += settingsDB.value("Plugins/DSSI_win64", [], list) - - # ---------------------------------------------------------------------------------------------------- - # VST2 - - vst2Plugins = [] - vst2Plugins += settingsDB.value("Plugins/VST2_native", [], list) - vst2Plugins += settingsDB.value("Plugins/VST2_posix32", [], list) - vst2Plugins += settingsDB.value("Plugins/VST2_posix64", [], list) - vst2Plugins += settingsDB.value("Plugins/VST2_win32", [], list) - vst2Plugins += settingsDB.value("Plugins/VST2_win64", [], list) - - # ---------------------------------------------------------------------------------------------------- - # VST3 - - vst3Plugins = [] - vst3Plugins += settingsDB.value("Plugins/VST3_native", [], list) - vst3Plugins += settingsDB.value("Plugins/VST3_posix32", [], list) - vst3Plugins += settingsDB.value("Plugins/VST3_posix64", [], list) - vst3Plugins += settingsDB.value("Plugins/VST3_win32", [], list) - vst3Plugins += settingsDB.value("Plugins/VST3_win64", [], list) - - # ---------------------------------------------------------------------------------------------------- - # CLAP - - clapPlugins = [] - clapPlugins += settingsDB.value("Plugins/CLAP_native", [], list) - clapPlugins += settingsDB.value("Plugins/CLAP_posix32", [], list) - clapPlugins += settingsDB.value("Plugins/CLAP_posix64", [], list) - clapPlugins += settingsDB.value("Plugins/CLAP_win32", [], list) - clapPlugins += settingsDB.value("Plugins/CLAP_win64", [], list) - - # ---------------------------------------------------------------------------------------------------- - # AU (extra non-cached) - - auPlugins32 = settingsDB.value("Plugins/AU_posix32", [], list) if MACOS else [] - - # ---------------------------------------------------------------------------------------------------- - # JSFX - - jsfxPlugins = settingsDB.value("Plugins/JSFX", [], list) - - # ---------------------------------------------------------------------------------------------------- - # Kits - - sf2s = settingsDB.value("Plugins/SF2", [], list) - sfzs = settingsDB.value("Plugins/SFZ", [], list) - - # ---------------------------------------------------------------------------------------------------- - # count plugins first, so we can create rows in advance - - ladspaCount = 0 - dssiCount = 0 - vstCount = 0 - vst3Count = 0 - clapCount = 0 - au32Count = 0 - jsfxCount = len(jsfxPlugins) - sf2Count = 0 - sfzCount = len(sfzs) - - for plugins in ladspaPlugins: - ladspaCount += len(plugins) - - for plugins in dssiPlugins: - dssiCount += len(plugins) - - for plugins in vst2Plugins: - vstCount += len(plugins) - - for plugins in vst3Plugins: - vst3Count += len(plugins) - - for plugins in clapPlugins: - clapCount += len(plugins) - - for plugins in auPlugins32: - au32Count += len(plugins) - - for plugins in sf2s: - sf2Count += len(plugins) - - self.ui.tableWidget.setRowCount(self.fLastTableIndex + - ladspaCount + dssiCount + vstCount + vst3Count + clapCount + - auCount + au32Count + jsfxCount + sf2Count + sfzCount) - - if MACOS: - self.ui.label.setText(self.tr("Have %i Internal, %i LADSPA, %i DSSI, %i LV2, %i VST2, %i VST3, %i CLAP, %i AudioUnit and %i JSFX plugins, plus %i Sound Kits" % ( - internalCount, ladspaCount, dssiCount, lv2Count, vstCount, vst3Count, clapCount, auCount+au32Count, jsfxCount, sf2Count+sfzCount))) - else: - self.ui.label.setText(self.tr("Have %i Internal, %i LADSPA, %i DSSI, %i LV2, %i VST2, %i VST3, %i CLAP and %i JSFX plugins, plus %i Sound Kits" % ( - internalCount, ladspaCount, dssiCount, lv2Count, vstCount, vst3Count, clapCount, jsfxCount, sf2Count+sfzCount))) - - # ---------------------------------------------------------------------------------------------------- - # now add all plugins to the table - - for plugins in ladspaPlugins: - for plugin in plugins: - self._addPluginToTable(plugin, PLUGIN_LADSPA) - - for plugins in dssiPlugins: - for plugin in plugins: - self._addPluginToTable(plugin, PLUGIN_DSSI) - - for plugins in vst2Plugins: - for plugin in plugins: - self._addPluginToTable(plugin, PLUGIN_VST2) - - for plugins in vst3Plugins: - for plugin in plugins: - self._addPluginToTable(plugin, PLUGIN_VST3) - - for plugins in clapPlugins: - for plugin in plugins: - self._addPluginToTable(plugin, PLUGIN_CLAP) - - for plugins in auPlugins32: - for plugin in plugins: - self._addPluginToTable(plugin, PLUGIN_AU) - - for plugin in jsfxPlugins: - self._addPluginToTable(plugin, PLUGIN_JSFX) - - for sf2 in sf2s: - for sf2_i in sf2: - self._addPluginToTable(sf2_i, PLUGIN_SF2) - - for sfz in sfzs: - self._addPluginToTable(sfz, PLUGIN_SFZ) - - # ---------------------------------------------------------------------------------------------------- - - self.ui.tableWidget.setSortingEnabled(True) - self._checkFilters() - self.slot_checkPlugin(self.ui.tableWidget.currentRow()) - - # -------------------------------------------------------------------------------------------------------- - - def showEvent(self, event): - self.slot_focusSearchFieldAndSelectAll() - QDialog.showEvent(self, event) - -# --------------------------------------------------------------------------------------------------------------------- -# Testing - -if __name__ == '__main__': - import sys - - class _host: - pathBinaries = "" - showPluginBridges = False - showWineBridges = False - _host = _host() - - _app = QApplication(sys.argv) - _gui = PluginListDialog(None, _host, True) - - if _gui.exec_(): - print(f"Result: {_gui.fRetPlugin}") - -# --------------------------------------------------------------------------------------------------------------------- diff --git a/source/frontend/pluginlist/pluginlistdialog.ui b/source/frontend/pluginlist/pluginlistdialog.ui index 5903f45f5..f3d0564da 100644 --- a/source/frontend/pluginlist/pluginlistdialog.ui +++ b/source/frontend/pluginlist/pluginlistdialog.ui @@ -35,7 +35,7 @@ 0 - + true @@ -133,7 +133,7 @@ &Add Plugin - + :/16x16/list-add.svgz:/16x16/list-add.svgz @@ -144,7 +144,7 @@ Cancel - + :/16x16/dialog-cancel.svgz:/16x16/dialog-cancel.svgz @@ -155,7 +155,7 @@ - + true @@ -166,7 +166,7 @@ Refresh - + :/16x16/view-refresh.svgz:/16x16/view-refresh.svgz @@ -177,7 +177,7 @@ Reset filters - + :/16x16/edit-clear.svgz:/16x16/edit-clear.svgz @@ -186,7 +186,7 @@ - + true @@ -582,12 +582,12 @@ false - - 22 - 12 + + 22 + @@ -599,7 +599,7 @@ - + :/16x16/bookmarks.svgz:/16x16/bookmarks.svgz @@ -635,8 +635,8 @@ 0 0 - 158 - 237 + 135 + 282 @@ -733,8 +733,8 @@ 0 0 - 158 - 156 + 113 + 120 @@ -785,6 +785,14 @@ + + + 0 + 0 + 100 + 282 + + Category @@ -879,8 +887,8 @@ 0 0 - 158 - 130 + 124 + 93 @@ -967,8 +975,7 @@ tab_info - + - - + diff --git a/source/frontend/pluginlist/pluginlistrefreshdialog.cpp b/source/frontend/pluginlist/pluginlistrefreshdialog.cpp deleted file mode 100644 index 0f6fdaf07..000000000 --- a/source/frontend/pluginlist/pluginlistrefreshdialog.cpp +++ /dev/null @@ -1,590 +0,0 @@ -/* - * Carla plugin host - * Copyright (C) 2011-2022 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 "pluginlistrefreshdialog.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_pluginlistrefreshdialog.h" -#include -#include - -#ifdef __clang__ -# pragma clang diagnostic pop -#elif defined(__GNUC__) && __GNUC__ >= 8 -# pragma GCC diagnostic pop -#endif - -#include "qsafesettings.hpp" - -#include "CarlaFrontend.h" -#include "CarlaUtils.h" - -#include - -static bool hasFeature(const char* const* const features, const char* const feature) -{ - if (features == nullptr) - return false; - - for (int i=0; features[i] != nullptr; ++i) - { - if (std::strcmp(features[i], feature) == 0) - return true; - } - - return false; -} - -// -------------------------------------------------------------------------------------------------------------------- -// Jack Application Dialog - -struct PluginListRefreshDialog::Self { - Ui_PluginRefreshW ui; - QPixmap fIconYes; - QPixmap fIconNo; - - Self() {} - - static Self& create() - { - Self* const self = new Self(); - return *self; - } -}; - -PluginListRefreshDialog::PluginListRefreshDialog(QWidget* const parent, const bool useSystemIcons) - : QDialog(parent), - self(Self::create()) -{ - self.ui.setupUi(this); - - // ---------------------------------------------------------------------------------------------------------------- - // Internal stuff - - #ifdef CARLA_OS_WIN - #define APP_EXT ".exe" - #else - #define APP_EXT "" - #endif - - QString hostPathBinaries; - const bool hasNative = QFileInfo::exists(QCarlaString(hostPathBinaries) + CARLA_OS_SEP_STR "carla-discovery-native" APP_EXT); - const bool hasPosix32 = QFileInfo::exists(QCarlaString(hostPathBinaries) + CARLA_OS_SEP_STR "carla-discovery-posix32"); - const bool hasPosix64 = QFileInfo::exists(QCarlaString(hostPathBinaries) + CARLA_OS_SEP_STR "carla-discovery-posix64"); - const bool hasWin32 = QFileInfo::exists(QCarlaString(hostPathBinaries) + CARLA_OS_SEP_STR "carla-discovery-win32.exe"); - const bool hasWin64 = QFileInfo::exists(QCarlaString(hostPathBinaries) + CARLA_OS_SEP_STR "carla-discovery-win64.exe"); - - // self.fThread = SearchPluginsThread(self, host.pathBinaries) - - // ---------------------------------------------------------------------------------------------------------------- - // Set-up Icons - - if (useSystemIcons) - { -// self.ui.b_start->setIcon(getIcon("arrow-right", 16, "svgz")); -// self.ui.b_close->setIcon(getIcon("window-close", 16, "svgz")); -// if QT_VERSION >= 0x50600: -// size = int(16 * self.devicePixelRatioF()) -// else: -// size = 16 -// self.fIconYes = QPixmap(getIcon('dialog-ok-apply', 16, 'svgz').pixmap(size)) -// self.fIconNo = QPixmap(getIcon('dialog-error', 16, 'svgz').pixmap(size)) - } - else - { - self.fIconYes = QPixmap(":/16x16/dialog-ok-apply.svgz"); - self.fIconNo = QPixmap(":/16x16/dialog-error.svgz"); - } - - // ---------------------------------------------------------------------------------------------------------------- - // UI setup - - // FIXME remove LRDF - self.ui.ico_rdflib->setPixmap(self.fIconNo); - - self.ui.b_skip->hide(); - - #if defined(CARLA_OS_HAIKU) - self.ui.ch_posix32->setText("Haiku 32bit"); - self.ui.ch_posix64->setText("Haiku 64bit"); - #elif defined(CARLA_OS_LINUX) - self.ui.ch_posix32->setText("Linux 32bit"); - self.ui.ch_posix64->setText("Linux 64bit"); - #elif defined(CARLA_OS_MAC) - self.ui.ch_posix32->setText("MacOS 32bit"); - self.ui.ch_posix64->setText("MacOS 64bit"); - #endif - - #ifndef CARLA_OS_WIN - if (hasPosix32) - { - self.ui.ico_posix32->setPixmap(self.fIconYes); - } - else - #endif - { - self.ui.ico_posix32->setPixmap(self.fIconNo); - self.ui.ch_posix32->setEnabled(false); - } - - #ifndef CARLA_OS_WIN - if (hasPosix64) - { - self.ui.ico_posix64->setPixmap(self.fIconYes); - } - else - #endif - { - self.ui.ico_posix64->setPixmap(self.fIconNo); - self.ui.ch_posix64->setEnabled(false); - } - - if (hasWin32) - { - self.ui.ico_win32->setPixmap(self.fIconYes); - } - else - { - self.ui.ico_win32->setPixmap(self.fIconNo); - self.ui.ch_win32->setEnabled(false); - } - - if (hasWin64) - { - self.ui.ico_win64->setPixmap(self.fIconYes); - } - else - { - self.ui.ico_win64->setPixmap(self.fIconNo); - self.ui.ch_win64->setEnabled(false); - } - - bool hasNonNative; - - #if defined(CARLA_OS_WIN) - #ifdef CARLA_OS_64BIT - hasNonNative = hasWin32; - self.ui.ch_win64->setEnabled(false); - self.ui.ch_win64->setVisible(false); - self.ui.ico_win64->setVisible(false); - self.ui.label_win64->setVisible(false); - #else - hasNonNative = hasWin64; - self.ui.ch_win32->setEnabled(false); - self.ui.ch_win32->setVisible(false); - self.ui.ico_win32->setVisible(false); - self.ui.label_win32->setVisible(false); - #endif - - self.ui.ch_posix32->setEnabled(false); - self.ui.ch_posix32->setVisible(false); - self.ui.ch_posix64->setEnabled(false); - self.ui.ch_posix64->setVisible(false); - self.ui.ico_posix32->hide(); - self.ui.ico_posix64->hide(); - self.ui.label_posix32->hide(); - self.ui.label_posix64->hide(); - self.ui.ico_rdflib->hide(); - self.ui.label_rdflib->hide(); - #elif defined(CARLA_OS_64BIT) - hasNonNative = hasPosix32 || hasWin32 || hasWin64; - self.ui.ch_posix64->setEnabled(false); - self.ui.ch_posix64->setVisible(false); - self.ui.ico_posix64->setVisible(false); - self.ui.label_posix64->setVisible(false); - #else - hasNonNative = hasPosix64 || hasWin32 || hasWin64; - self.ui.ch_posix32->setEnabled(false); - self.ui.ch_posix32->setVisible(false); - self.ui.ico_posix32->setVisible(false); - self.ui.label_posix32->setVisible(false); - #endif - - #ifdef CARLA_OS_MAC - setWindowModality(Qt::WindowModal); - #else - self.ui.ch_au->setEnabled(false); - self.ui.ch_au->setVisible(false); - #endif - - if (hasNative) - { - self.ui.ico_native->setPixmap(self.fIconYes); - } - else - { - self.ui.ico_native->setPixmap(self.fIconNo); - self.ui.ch_native->setEnabled(false); - self.ui.ch_sf2->setEnabled(false); - if (! hasNonNative) - { - self.ui.ch_ladspa->setEnabled(false); - self.ui.ch_dssi->setEnabled(false); - self.ui.ch_vst->setEnabled(false); - self.ui.ch_vst3->setEnabled(false); - self.ui.ch_clap->setEnabled(false); - } - } - - // TODO - // if (! hasLoadedLv2Plugins) - self.ui.lv2_restart_notice->hide(); - - setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); - - // ---------------------------------------------------------------------------------------------------------------- - // Load settings - - loadSettings(); - - // ---------------------------------------------------------------------------------------------------------------- - // Hide bridges if disabled - - // TODO - bool showPluginBridges = true; - bool showWineBridges = true; - - #ifdef CARLA_OS_WIN - #ifndef CARLA_OS_64BIT - // NOTE: We Assume win32 carla build will not run win64 plugins - showPluginBridges = false; - #endif - showWineBridges = false; - #endif - - if (! showPluginBridges) - { - self.ui.ch_native->setChecked(true); - self.ui.ch_native->setEnabled(false); - self.ui.ch_native->setVisible(false); - self.ui.ch_posix32->setChecked(false); - self.ui.ch_posix32->setEnabled(false); - self.ui.ch_posix32->setVisible(false); - self.ui.ch_posix64->setChecked(false); - self.ui.ch_posix64->setEnabled(false); - self.ui.ch_posix64->setVisible(false); - self.ui.ch_win32->setChecked(false); - self.ui.ch_win32->setEnabled(false); - self.ui.ch_win32->setVisible(false); - self.ui.ch_win64->setChecked(false); - self.ui.ch_win64->setEnabled(false); - self.ui.ch_win64->setVisible(false); - self.ui.ico_posix32->hide(); - self.ui.ico_posix64->hide(); - self.ui.ico_win32->hide(); - self.ui.ico_win64->hide(); - self.ui.label_posix32->hide(); - self.ui.label_posix64->hide(); - self.ui.label_win32->hide(); - self.ui.label_win64->hide(); - self.ui.sep_format->hide(); - } - else if (! showWineBridges) - { - self.ui.ch_win32->setChecked(false); - self.ui.ch_win32->setEnabled(false); - self.ui.ch_win32->setVisible(false); - self.ui.ch_win64->setChecked(false); - self.ui.ch_win64->setEnabled(false); - self.ui.ch_win64->setVisible(false); - self.ui.ico_win32->hide(); - self.ui.ico_win64->hide(); - self.ui.label_win32->hide(); - self.ui.label_win64->hide(); - } - - // Disable non-supported features - const char* const* const features = carla_get_supported_features(); - - if (! hasFeature(features, "sf2")) - { - self.ui.ch_sf2->setChecked(false); - self.ui.ch_sf2->setEnabled(false); - } - - #ifdef CARLA_OS_MAC - if (! hasFeature(features, "juce")) - { - self.ui.ch_au->setChecked(false); - self.ui.ch_au->setEnabled(false); - } - #endif - - // ---------------------------------------------------------------------------------------------------------------- - // Resize to minimum size, as it's very likely UI stuff was hidden - - resize(minimumSize()); - - // ---------------------------------------------------------------------------------------------------------------- - // Set-up connections - - connect(this, &QDialog::finished, - this, &PluginListRefreshDialog::slot_saveSettings); -// self.ui.b_start.clicked.connect(self.slot_start) -// self.ui.b_skip.clicked.connect(self.slot_skip) -// self.ui.ch_native.clicked.connect(self.slot_checkTools) -// self.ui.ch_posix32.clicked.connect(self.slot_checkTools) -// self.ui.ch_posix64.clicked.connect(self.slot_checkTools) -// self.ui.ch_win32.clicked.connect(self.slot_checkTools) -// self.ui.ch_win64.clicked.connect(self.slot_checkTools) -// self.ui.ch_ladspa.clicked.connect(self.slot_checkTools) -// self.ui.ch_dssi.clicked.connect(self.slot_checkTools) -// self.ui.ch_lv2.clicked.connect(self.slot_checkTools) -// self.ui.ch_vst.clicked.connect(self.slot_checkTools) -// self.ui.ch_vst3.clicked.connect(self.slot_checkTools) -// self.ui.ch_clap.clicked.connect(self.slot_checkTools) -// self.ui.ch_au.clicked.connect(self.slot_checkTools) -// self.ui.ch_sf2.clicked.connect(self.slot_checkTools) -// self.ui.ch_sfz.clicked.connect(self.slot_checkTools) -// self.ui.ch_jsfx.clicked.connect(self.slot_checkTools) -// self.fThread.pluginLook.connect(self.slot_handlePluginLook) -// self.fThread.finished.connect(self.slot_handlePluginThreadFinished) - -// connect(this, &QDialog::finished, -// this, &JackAppDialog::slot_saveSettings); -// connect(self.ui.cb_session_mgr, static_cast(&QComboBox::currentIndexChanged), -// this, &JackAppDialog::slot_sessionManagerChanged); -// connect(self.ui.le_command, &QLineEdit::textChanged, -// this, &JackAppDialog::slot_commandChanged); - - // ---------------------------------------------------------------------------------------------------------------- - // Post-connect setup - - slot_checkTools(); -} - -PluginListRefreshDialog::~PluginListRefreshDialog() -{ - delete &self; -} - -// ----------------------------------------------------------------------------------------------------------------- -// public methods - -// ----------------------------------------------------------------------------------------------------------------- -// protected methods - -void PluginListRefreshDialog::closeEvent(QCloseEvent* const event) -{ - /* - if (self.fThread.isRunning()) - { - self.fThread.stop(); - killDiscovery(); - #self.fThread.terminate(); - self.fThread.wait(); - } - - if (self.fThread.hasSomethingChanged()) - accept(); - else - */ - reject(); - - QDialog::closeEvent(event); -} - -// ----------------------------------------------------------------------------------------------------------------- -// private methods - -void PluginListRefreshDialog::loadSettings() -{ - const QSafeSettings settings("falkTX", "CarlaRefresh2"); - - bool check; - - check = settings.valueBool("PluginDatabase/SearchLADSPA", true) and self.ui.ch_ladspa->isEnabled(); - self.ui.ch_ladspa->setChecked(check); - - check = settings.valueBool("PluginDatabase/SearchDSSI", true) and self.ui.ch_dssi->isEnabled(); - self.ui.ch_dssi->setChecked(check); - - check = settings.valueBool("PluginDatabase/SearchLV2", true) and self.ui.ch_lv2->isEnabled(); - self.ui.ch_lv2->setChecked(check); - - check = settings.valueBool("PluginDatabase/SearchVST2", true) and self.ui.ch_vst->isEnabled(); - self.ui.ch_vst->setChecked(check); - - check = settings.valueBool("PluginDatabase/SearchVST3", true) and self.ui.ch_vst3->isEnabled(); - self.ui.ch_vst3->setChecked(check); - - check = settings.valueBool("PluginDatabase/SearchCLAP", true) and self.ui.ch_clap->isEnabled(); - self.ui.ch_clap->setChecked(check); - - #ifdef CARLA_OS_MAC - check = settings.valueBool("PluginDatabase/SearchAU", true) and self.ui.ch_au->isEnabled(); - #else - check = false; - #endif - self.ui.ch_au->setChecked(check); - - check = settings.valueBool("PluginDatabase/SearchSF2", false) and self.ui.ch_sf2->isEnabled(); - self.ui.ch_sf2->setChecked(check); - - check = settings.valueBool("PluginDatabase/SearchSFZ", false) and self.ui.ch_sfz->isEnabled(); - self.ui.ch_sfz->setChecked(check); - - check = settings.valueBool("PluginDatabase/SearchJSFX", true) and self.ui.ch_jsfx->isEnabled(); - self.ui.ch_jsfx->setChecked(check); - - check = settings.valueBool("PluginDatabase/SearchNative", true) and self.ui.ch_native->isEnabled(); - self.ui.ch_native->setChecked(check); - - check = settings.valueBool("PluginDatabase/SearchPOSIX32", false) and self.ui.ch_posix32->isEnabled(); - self.ui.ch_posix32->setChecked(check); - - check = settings.valueBool("PluginDatabase/SearchPOSIX64", false) and self.ui.ch_posix64->isEnabled(); - self.ui.ch_posix64->setChecked(check); - - check = settings.valueBool("PluginDatabase/SearchWin32", false) and self.ui.ch_win32->isEnabled(); - self.ui.ch_win32->setChecked(check); - - check = settings.valueBool("PluginDatabase/SearchWin64", false) and self.ui.ch_win64->isEnabled(); - self.ui.ch_win64->setChecked(check); - - self.ui.ch_do_checks->setChecked(settings.valueBool("PluginDatabase/DoChecks", false)); -} - -// ----------------------------------------------------------------------------------------------------------------- -// private slots - -void PluginListRefreshDialog::slot_saveSettings() -{ - QSafeSettings settings("falkTX", "CarlaRefresh2"); - settings.setValue("PluginDatabase/SearchLADSPA", self.ui.ch_ladspa->isChecked()); - settings.setValue("PluginDatabase/SearchDSSI", self.ui.ch_dssi->isChecked()); - settings.setValue("PluginDatabase/SearchLV2", self.ui.ch_lv2->isChecked()); - settings.setValue("PluginDatabase/SearchVST2", self.ui.ch_vst->isChecked()); - settings.setValue("PluginDatabase/SearchVST3", self.ui.ch_vst3->isChecked()); - settings.setValue("PluginDatabase/SearchCLAP", self.ui.ch_clap->isChecked()); - settings.setValue("PluginDatabase/SearchAU", self.ui.ch_au->isChecked()); - settings.setValue("PluginDatabase/SearchSF2", self.ui.ch_sf2->isChecked()); - settings.setValue("PluginDatabase/SearchSFZ", self.ui.ch_sfz->isChecked()); - settings.setValue("PluginDatabase/SearchJSFX", self.ui.ch_jsfx->isChecked()); - settings.setValue("PluginDatabase/SearchNative", self.ui.ch_native->isChecked()); - settings.setValue("PluginDatabase/SearchPOSIX32", self.ui.ch_posix32->isChecked()); - settings.setValue("PluginDatabase/SearchPOSIX64", self.ui.ch_posix64->isChecked()); - settings.setValue("PluginDatabase/SearchWin32", self.ui.ch_win32->isChecked()); - settings.setValue("PluginDatabase/SearchWin64", self.ui.ch_win64->isChecked()); - settings.setValue("PluginDatabase/DoChecks", self.ui.ch_do_checks->isChecked()); -} - -void PluginListRefreshDialog::slot_start() -{ - self.ui.progressBar->setMinimum(0); - self.ui.progressBar->setMaximum(100); - self.ui.progressBar->setValue(0); - self.ui.b_start->setEnabled(false); - self.ui.b_skip->setVisible(true); - self.ui.b_close->setVisible(false); - self.ui.group_types->setEnabled(false); - self.ui.group_options->setEnabled(false); - -// if gCarla.utils: -// if self.ui.ch_do_checks.isChecked(): -// gCarla.utils.unsetenv("CARLA_DISCOVERY_NO_PROCESSING_CHECKS") -// else: -// gCarla.utils.setenv("CARLA_DISCOVERY_NO_PROCESSING_CHECKS", "true") -// -// native, posix32, posix64, win32, win64 = (self.ui.ch_native.isChecked(), -// self.ui.ch_posix32.isChecked(), self.ui.ch_posix64.isChecked(), -// self.ui.ch_win32.isChecked(), self.ui.ch_win64.isChecked()) -// -// ladspa, dssi, lv2, vst, vst3, clap, au, sf2, sfz, jsfx = (self.ui.ch_ladspa.isChecked(), self.ui.ch_dssi.isChecked(), -// self.ui.ch_lv2.isChecked(), self.ui.ch_vst.isChecked(), -// self.ui.ch_vst3.isChecked(), self.ui.ch_clap.isChecked(), -// self.ui.ch_au.isChecked(), self.ui.ch_sf2.isChecked(), -// self.ui.ch_sfz.isChecked(), self.ui.ch_jsfx.isChecked()) -// -// self.fThread.setSearchBinaryTypes(native, posix32, posix64, win32, win64) -// self.fThread.setSearchPluginTypes(ladspa, dssi, lv2, vst, vst3, clap, au, sf2, sfz, jsfx) -// self.fThread.start() -} - -void PluginListRefreshDialog::slot_skip() -{ - // killDiscovery(); -} - -void PluginListRefreshDialog::slot_checkTools() -{ - const bool enabled1 = self.ui.ch_native->isChecked() - || self.ui.ch_posix32->isChecked() - || self.ui.ch_posix64->isChecked() - || self.ui.ch_win32->isChecked() - || self.ui.ch_win64->isChecked(); - - const bool enabled2 = self.ui.ch_ladspa->isChecked() - || self.ui.ch_dssi->isChecked() - || self.ui.ch_lv2->isChecked() - || self.ui.ch_vst->isChecked() - || self.ui.ch_vst3->isChecked() - || self.ui.ch_clap->isChecked() - || self.ui.ch_au->isChecked() - || self.ui.ch_sf2->isChecked() - || self.ui.ch_sfz->isChecked() - || self.ui.ch_jsfx->isChecked(); - - self.ui.b_start->setEnabled(enabled1 and enabled2); -} - -void PluginListRefreshDialog::slot_handlePluginLook(const int percent, const QString plugin) -{ - self.ui.progressBar->setFormat(plugin); - self.ui.progressBar->setValue(percent); -} - -void PluginListRefreshDialog::slot_handlePluginThreadFinished() -{ - self.ui.progressBar->setMinimum(0); - self.ui.progressBar->setMaximum(1); - self.ui.progressBar->setValue(1); - self.ui.progressBar->setFormat(tr("Done")); - self.ui.b_start->setEnabled(true); - self.ui.b_skip->setVisible(false); - self.ui.b_close->setVisible(true); - self.ui.group_types->setEnabled(true); - self.ui.group_options->setEnabled(true); -} - -// -------------------------------------------------------------------------------------------------------------------- - -const PluginListRefreshDialogResults* -carla_frontend_createAndExecPluginListRefreshDialog(void* const parent, const bool useSystemIcons) -{ - PluginListRefreshDialog gui(reinterpret_cast(parent), useSystemIcons); - - if (gui.exec()) - { - static PluginListRefreshDialogResults ret = {}; - - return &ret; - } - - return nullptr; -} - -// -------------------------------------------------------------------------------------------------------------------- diff --git a/source/frontend/pluginlist/pluginlistrefreshdialog.hpp b/source/frontend/pluginlist/pluginlistrefreshdialog.hpp deleted file mode 100644 index 0afdd3027..000000000 --- a/source/frontend/pluginlist/pluginlistrefreshdialog.hpp +++ /dev/null @@ -1,81 +0,0 @@ -/* - * Carla plugin host - * Copyright (C) 2011-2022 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. - */ - -#pragma once - -#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 - -#ifdef __clang__ -# pragma clang diagnostic pop -#elif defined(__GNUC__) && __GNUC__ >= 8 -# pragma GCC diagnostic pop -#endif - -#include "qcarlastring.hpp" - -// -------------------------------------------------------------------------------------------------------------------- -// Jack Application Dialog - -class PluginListRefreshDialog : public QDialog -{ - struct Self; - Self& self; - - // ---------------------------------------------------------------------------------------------------------------- - -public: - explicit PluginListRefreshDialog(QWidget* parent, bool useSystemIcons); - ~PluginListRefreshDialog() override; - - // ---------------------------------------------------------------------------------------------------------------- - // public methods - - // ---------------------------------------------------------------------------------------------------------------- - // protected methods - -protected: - void closeEvent(QCloseEvent*) override; - - // ---------------------------------------------------------------------------------------------------------------- - // private methods - -private: - void loadSettings(); - - // ---------------------------------------------------------------------------------------------------------------- - // private slots - -private slots: - void slot_saveSettings(); - void slot_start(); - void slot_skip(); - void slot_checkTools(); - void slot_handlePluginLook(int percent, QString plugin); - void slot_handlePluginThreadFinished(); -}; - -// -------------------------------------------------------------------------------------------------------------------- diff --git a/source/frontend/pluginlist/pluginlistrefreshdialog.py b/source/frontend/pluginlist/pluginlistrefreshdialog.py deleted file mode 100755 index 3d027203f..000000000 --- a/source/frontend/pluginlist/pluginlistrefreshdialog.py +++ /dev/null @@ -1,473 +0,0 @@ -#!/usr/bin/env python3 -# -*- coding: utf-8 -*- - -# Carla plugin host -# Copyright (C) 2011-2022 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. - -# --------------------------------------------------------------------------------------------------------------------- -# Imports (Global) - -import os - -from PyQt5.QtCore import pyqtSlot, Qt, QT_VERSION -from PyQt5.QtGui import QPixmap -from PyQt5.QtWidgets import QDialog, QWidget - -# --------------------------------------------------------------------------------------------------------------------- -# Imports (Carla) - -from carla_shared import ( - HAIKU, - LINUX, - MACOS, - WINDOWS, - gCarla, - getIcon, - kIs64bit, -) - -from utils import QSafeSettings - -# --------------------------------------------------------------------------------------------------------------------- -# Imports (Local) - -from .discovery import killDiscovery -from .discoverythread import SearchPluginsThread -from .pluginlistrefreshdialog_ui import Ui_PluginRefreshW - -# --------------------------------------------------------------------------------------------------------------------- -# Plugin Refresh Dialog - -class PluginRefreshW(QDialog): - def __init__(self, parent: QWidget, host, useSystemIcons: bool, hasLoadedLv2Plugins: bool): - QDialog.__init__(self, parent) - self.host = host - self.ui = Ui_PluginRefreshW() - self.ui.setupUi(self) - - # ------------------------------------------------------------------------------------------------------------- - # Internal stuff - - toolNative = "carla-discovery-native.exe" if WINDOWS else "carla-discovery-native" - hasNative = os.path.exists(os.path.join(host.pathBinaries, toolNative)) - hasPosix32 = os.path.exists(os.path.join(host.pathBinaries, "carla-discovery-posix32")) - hasPosix64 = os.path.exists(os.path.join(host.pathBinaries, "carla-discovery-posix64")) - hasWin32 = os.path.exists(os.path.join(host.pathBinaries, "carla-discovery-win32.exe")) - hasWin64 = os.path.exists(os.path.join(host.pathBinaries, "carla-discovery-win64.exe")) - - self.fThread = SearchPluginsThread(self, host.pathBinaries) - - # ------------------------------------------------------------------------------------------------------------- - # Set-up Icons - - if useSystemIcons: - self.ui.b_start.setIcon(getIcon('arrow-right', 16, 'svgz')) - self.ui.b_close.setIcon(getIcon('window-close', 16, 'svgz')) - if QT_VERSION >= 0x50600: - size = int(16 * self.devicePixelRatioF()) - else: - size = 16 - self.fIconYes = QPixmap(getIcon('dialog-ok-apply', 16, 'svgz').pixmap(size)) - self.fIconNo = QPixmap(getIcon('dialog-error', 16, 'svgz').pixmap(size)) - else: - self.fIconYes = QPixmap(":/16x16/dialog-ok-apply.svgz") - self.fIconNo = QPixmap(":/16x16/dialog-error.svgz") - - # ------------------------------------------------------------------------------------------------------------- - # Set-up GUI - - # FIXME remove LRDF - self.ui.ico_rdflib.setPixmap(self.fIconNo) - - self.ui.b_skip.setVisible(False) - - if HAIKU: - self.ui.ch_posix32.setText("Haiku 32bit") - self.ui.ch_posix64.setText("Haiku 64bit") - elif LINUX: - self.ui.ch_posix32.setText("Linux 32bit") - self.ui.ch_posix64.setText("Linux 64bit") - elif MACOS: - self.ui.ch_posix32.setText("MacOS 32bit") - self.ui.ch_posix64.setText("MacOS 64bit") - - if hasPosix32 and not WINDOWS: - self.ui.ico_posix32.setPixmap(self.fIconYes) - else: - self.ui.ico_posix32.setPixmap(self.fIconNo) - self.ui.ch_posix32.setEnabled(False) - - if hasPosix64 and not WINDOWS: - self.ui.ico_posix64.setPixmap(self.fIconYes) - else: - self.ui.ico_posix64.setPixmap(self.fIconNo) - self.ui.ch_posix64.setEnabled(False) - - if hasWin32: - self.ui.ico_win32.setPixmap(self.fIconYes) - else: - self.ui.ico_win32.setPixmap(self.fIconNo) - self.ui.ch_win32.setEnabled(False) - - if hasWin64: - self.ui.ico_win64.setPixmap(self.fIconYes) - else: - self.ui.ico_win64.setPixmap(self.fIconNo) - self.ui.ch_win64.setEnabled(False) - - if WINDOWS: - if kIs64bit: - hasNonNative = hasWin32 - self.ui.ch_win64.setEnabled(False) - self.ui.ch_win64.setVisible(False) - self.ui.ico_win64.setVisible(False) - self.ui.label_win64.setVisible(False) - else: - hasNonNative = hasWin64 - self.ui.ch_win32.setEnabled(False) - self.ui.ch_win32.setVisible(False) - self.ui.ico_win32.setVisible(False) - self.ui.label_win32.setVisible(False) - - self.ui.ch_posix32.setEnabled(False) - self.ui.ch_posix32.setVisible(False) - self.ui.ch_posix64.setEnabled(False) - self.ui.ch_posix64.setVisible(False) - self.ui.ico_posix32.hide() - self.ui.ico_posix64.hide() - self.ui.label_posix32.hide() - self.ui.label_posix64.hide() - self.ui.ico_rdflib.hide() - self.ui.label_rdflib.hide() - - else: - if kIs64bit: - hasNonNative = bool(hasPosix32 or hasWin32 or hasWin64) - self.ui.ch_posix64.setEnabled(False) - self.ui.ch_posix64.setVisible(False) - self.ui.ico_posix64.setVisible(False) - self.ui.label_posix64.setVisible(False) - else: - hasNonNative = bool(hasPosix64 or hasWin32 or hasWin64) - self.ui.ch_posix32.setEnabled(False) - self.ui.ch_posix32.setVisible(False) - self.ui.ico_posix32.setVisible(False) - self.ui.label_posix32.setVisible(False) - - if MACOS: - self.setWindowModality(Qt.WindowModal) - else: - self.ui.ch_au.setEnabled(False) - self.ui.ch_au.setVisible(False) - - if hasNative: - self.ui.ico_native.setPixmap(self.fIconYes) - else: - self.ui.ico_native.setPixmap(self.fIconNo) - self.ui.ch_native.setEnabled(False) - self.ui.ch_sf2.setEnabled(False) - if not hasNonNative: - self.ui.ch_ladspa.setEnabled(False) - self.ui.ch_dssi.setEnabled(False) - self.ui.ch_vst.setEnabled(False) - self.ui.ch_vst3.setEnabled(False) - self.ui.ch_clap.setEnabled(False) - - if not hasLoadedLv2Plugins: - self.ui.lv2_restart_notice.hide() - - self.setWindowFlags(self.windowFlags() & ~Qt.WindowContextHelpButtonHint) - - # ------------------------------------------------------------------------------------------------------------- - # Load settings - - self.loadSettings() - - # ------------------------------------------------------------------------------------------------------------- - # Hide bridges if disabled - - # 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(False) - self.ui.ch_posix32.setChecked(False) - self.ui.ch_posix32.setEnabled(False) - self.ui.ch_posix32.setVisible(False) - self.ui.ch_posix64.setChecked(False) - self.ui.ch_posix64.setEnabled(False) - self.ui.ch_posix64.setVisible(False) - self.ui.ch_win32.setChecked(False) - self.ui.ch_win32.setEnabled(False) - self.ui.ch_win32.setVisible(False) - self.ui.ch_win64.setChecked(False) - self.ui.ch_win64.setEnabled(False) - self.ui.ch_win64.setVisible(False) - self.ui.ico_posix32.hide() - self.ui.ico_posix64.hide() - self.ui.ico_win32.hide() - self.ui.ico_win64.hide() - self.ui.label_posix32.hide() - self.ui.label_posix64.hide() - self.ui.label_win32.hide() - self.ui.label_win64.hide() - self.ui.sep_format.hide() - - elif not (WINDOWS or host.showWineBridges): - self.ui.ch_win32.setChecked(False) - self.ui.ch_win32.setEnabled(False) - self.ui.ch_win32.setVisible(False) - self.ui.ch_win64.setChecked(False) - self.ui.ch_win64.setEnabled(False) - self.ui.ch_win64.setVisible(False) - self.ui.ico_win32.hide() - self.ui.ico_win64.hide() - self.ui.label_win32.hide() - self.ui.label_win64.hide() - - # Disable non-supported features - features = gCarla.utils.get_supported_features() if gCarla.utils else () - - if "sf2" not in features: - self.ui.ch_sf2.setChecked(False) - self.ui.ch_sf2.setEnabled(False) - - if MACOS and "juce" not in features: - self.ui.ch_au.setChecked(False) - self.ui.ch_au.setEnabled(False) - - # ------------------------------------------------------------------------------------------------------------- - # Resize to minimum size, as it's very likely UI stuff was hidden - - self.resize(self.minimumSize()) - - # ------------------------------------------------------------------------------------------------------------- - # Set-up connections - - self.finished.connect(self.slot_saveSettings) - self.ui.b_start.clicked.connect(self.slot_start) - self.ui.b_skip.clicked.connect(self.slot_skip) - self.ui.ch_native.clicked.connect(self.slot_checkTools) - self.ui.ch_posix32.clicked.connect(self.slot_checkTools) - self.ui.ch_posix64.clicked.connect(self.slot_checkTools) - self.ui.ch_win32.clicked.connect(self.slot_checkTools) - self.ui.ch_win64.clicked.connect(self.slot_checkTools) - self.ui.ch_ladspa.clicked.connect(self.slot_checkTools) - self.ui.ch_dssi.clicked.connect(self.slot_checkTools) - self.ui.ch_lv2.clicked.connect(self.slot_checkTools) - self.ui.ch_vst.clicked.connect(self.slot_checkTools) - self.ui.ch_vst3.clicked.connect(self.slot_checkTools) - self.ui.ch_clap.clicked.connect(self.slot_checkTools) - self.ui.ch_au.clicked.connect(self.slot_checkTools) - self.ui.ch_sf2.clicked.connect(self.slot_checkTools) - self.ui.ch_sfz.clicked.connect(self.slot_checkTools) - self.ui.ch_jsfx.clicked.connect(self.slot_checkTools) - self.fThread.pluginLook.connect(self.slot_handlePluginLook) - self.fThread.finished.connect(self.slot_handlePluginThreadFinished) - - # ------------------------------------------------------------------------------------------------------------- - # Post-connect setup - - self.slot_checkTools() - - # ----------------------------------------------------------------------------------------------------------------- - - def loadSettings(self): - settings = QSafeSettings("falkTX", "CarlaRefresh2") - - check = settings.value("PluginDatabase/SearchLADSPA", True, bool) and self.ui.ch_ladspa.isEnabled() - self.ui.ch_ladspa.setChecked(check) - - check = settings.value("PluginDatabase/SearchDSSI", True, bool) and self.ui.ch_dssi.isEnabled() - self.ui.ch_dssi.setChecked(check) - - check = settings.value("PluginDatabase/SearchLV2", True, bool) and self.ui.ch_lv2.isEnabled() - self.ui.ch_lv2.setChecked(check) - - check = settings.value("PluginDatabase/SearchVST2", True, bool) and self.ui.ch_vst.isEnabled() - self.ui.ch_vst.setChecked(check) - - check = settings.value("PluginDatabase/SearchVST3", True, bool) and self.ui.ch_vst3.isEnabled() - self.ui.ch_vst3.setChecked(check) - - check = settings.value("PluginDatabase/SearchCLAP", True, bool) and self.ui.ch_clap.isEnabled() - self.ui.ch_clap.setChecked(check) - - if MACOS: - check = settings.value("PluginDatabase/SearchAU", True, bool) and self.ui.ch_au.isEnabled() - else: - check = False - self.ui.ch_au.setChecked(check) - - check = settings.value("PluginDatabase/SearchSF2", False, bool) and self.ui.ch_sf2.isEnabled() - self.ui.ch_sf2.setChecked(check) - - check = settings.value("PluginDatabase/SearchSFZ", False, bool) and self.ui.ch_sfz.isEnabled() - self.ui.ch_sfz.setChecked(check) - - check = settings.value("PluginDatabase/SearchJSFX", True, bool) and self.ui.ch_jsfx.isEnabled() - self.ui.ch_jsfx.setChecked(check) - - check = settings.value("PluginDatabase/SearchNative", True, bool) and self.ui.ch_native.isEnabled() - self.ui.ch_native.setChecked(check) - - check = settings.value("PluginDatabase/SearchPOSIX32", False, bool) and self.ui.ch_posix32.isEnabled() - self.ui.ch_posix32.setChecked(check) - - check = settings.value("PluginDatabase/SearchPOSIX64", False, bool) and self.ui.ch_posix64.isEnabled() - self.ui.ch_posix64.setChecked(check) - - check = settings.value("PluginDatabase/SearchWin32", False, bool) and self.ui.ch_win32.isEnabled() - self.ui.ch_win32.setChecked(check) - - check = settings.value("PluginDatabase/SearchWin64", False, bool) and self.ui.ch_win64.isEnabled() - self.ui.ch_win64.setChecked(check) - - self.ui.ch_do_checks.setChecked(settings.value("PluginDatabase/DoChecks", False, bool)) - - # ----------------------------------------------------------------------------------------------------------------- - - @pyqtSlot() - def slot_saveSettings(self): - settings = QSafeSettings("falkTX", "CarlaRefresh2") - settings.setValue("PluginDatabase/SearchLADSPA", self.ui.ch_ladspa.isChecked()) - settings.setValue("PluginDatabase/SearchDSSI", self.ui.ch_dssi.isChecked()) - settings.setValue("PluginDatabase/SearchLV2", self.ui.ch_lv2.isChecked()) - settings.setValue("PluginDatabase/SearchVST2", self.ui.ch_vst.isChecked()) - settings.setValue("PluginDatabase/SearchVST3", self.ui.ch_vst3.isChecked()) - settings.setValue("PluginDatabase/SearchCLAP", self.ui.ch_clap.isChecked()) - settings.setValue("PluginDatabase/SearchAU", self.ui.ch_au.isChecked()) - settings.setValue("PluginDatabase/SearchSF2", self.ui.ch_sf2.isChecked()) - settings.setValue("PluginDatabase/SearchSFZ", self.ui.ch_sfz.isChecked()) - settings.setValue("PluginDatabase/SearchJSFX", self.ui.ch_jsfx.isChecked()) - settings.setValue("PluginDatabase/SearchNative", self.ui.ch_native.isChecked()) - settings.setValue("PluginDatabase/SearchPOSIX32", self.ui.ch_posix32.isChecked()) - settings.setValue("PluginDatabase/SearchPOSIX64", self.ui.ch_posix64.isChecked()) - settings.setValue("PluginDatabase/SearchWin32", self.ui.ch_win32.isChecked()) - settings.setValue("PluginDatabase/SearchWin64", self.ui.ch_win64.isChecked()) - settings.setValue("PluginDatabase/DoChecks", self.ui.ch_do_checks.isChecked()) - - # ----------------------------------------------------------------------------------------------------------------- - - @pyqtSlot() - def slot_start(self): - self.ui.progressBar.setMinimum(0) - self.ui.progressBar.setMaximum(100) - self.ui.progressBar.setValue(0) - self.ui.b_start.setEnabled(False) - self.ui.b_skip.setVisible(True) - self.ui.b_close.setVisible(False) - self.ui.group_types.setEnabled(False) - self.ui.group_options.setEnabled(False) - - if gCarla.utils: - if self.ui.ch_do_checks.isChecked(): - gCarla.utils.unsetenv("CARLA_DISCOVERY_NO_PROCESSING_CHECKS") - else: - gCarla.utils.setenv("CARLA_DISCOVERY_NO_PROCESSING_CHECKS", "true") - - native, posix32, posix64, win32, win64 = (self.ui.ch_native.isChecked(), - self.ui.ch_posix32.isChecked(), self.ui.ch_posix64.isChecked(), - self.ui.ch_win32.isChecked(), self.ui.ch_win64.isChecked()) - - ladspa, dssi, lv2, vst, vst3, clap, au, sf2, sfz, jsfx = (self.ui.ch_ladspa.isChecked(), self.ui.ch_dssi.isChecked(), - self.ui.ch_lv2.isChecked(), self.ui.ch_vst.isChecked(), - self.ui.ch_vst3.isChecked(), self.ui.ch_clap.isChecked(), - self.ui.ch_au.isChecked(), self.ui.ch_sf2.isChecked(), - self.ui.ch_sfz.isChecked(), self.ui.ch_jsfx.isChecked()) - - self.fThread.setSearchBinaryTypes(native, posix32, posix64, win32, win64) - self.fThread.setSearchPluginTypes(ladspa, dssi, lv2, vst, vst3, clap, au, sf2, sfz, jsfx) - self.fThread.start() - - # ----------------------------------------------------------------------------------------------------------------- - - @pyqtSlot() - def slot_skip(self): - killDiscovery() - - # ----------------------------------------------------------------------------------------------------------------- - - @pyqtSlot() - def slot_checkTools(self): - enabled1 = bool(self.ui.ch_native.isChecked() or - self.ui.ch_posix32.isChecked() or self.ui.ch_posix64.isChecked() or - self.ui.ch_win32.isChecked() or self.ui.ch_win64.isChecked()) - - enabled2 = bool(self.ui.ch_ladspa.isChecked() or self.ui.ch_dssi.isChecked() or - self.ui.ch_lv2.isChecked() or self.ui.ch_vst.isChecked() or - self.ui.ch_vst3.isChecked() or self.ui.ch_clap.isChecked() or - self.ui.ch_au.isChecked() or self.ui.ch_sf2.isChecked() or - self.ui.ch_sfz.isChecked() or self.ui.ch_jsfx.isChecked()) - - self.ui.b_start.setEnabled(enabled1 and enabled2) - - # ----------------------------------------------------------------------------------------------------------------- - - @pyqtSlot(float, str) - def slot_handlePluginLook(self, percent, plugin): - self.ui.progressBar.setFormat(plugin) - self.ui.progressBar.setValue(int(percent)) - - # ----------------------------------------------------------------------------------------------------------------- - - @pyqtSlot() - def slot_handlePluginThreadFinished(self): - self.ui.progressBar.setMinimum(0) - self.ui.progressBar.setMaximum(1) - self.ui.progressBar.setValue(1) - self.ui.progressBar.setFormat(self.tr("Done")) - self.ui.b_start.setEnabled(True) - self.ui.b_skip.setVisible(False) - self.ui.b_close.setVisible(True) - self.ui.group_types.setEnabled(True) - self.ui.group_options.setEnabled(True) - - # ----------------------------------------------------------------------------------------------------------------- - - def closeEvent(self, event): - if self.fThread.isRunning(): - self.fThread.stop() - killDiscovery() - #self.fThread.terminate() - self.fThread.wait() - - if self.fThread.hasSomethingChanged(): - self.accept() - else: - self.reject() - - QDialog.closeEvent(self, event) - -# --------------------------------------------------------------------------------------------------------------------- -# Testing - -if __name__ == '__main__': - import sys - # pylint: disable=ungrouped-imports - from PyQt5.QtWidgets import QApplication - # pylint: enable=ungrouped-imports - - class _host: - pathBinaries = "" - showPluginBridges = False - showWineBridges = False - _host = _host() - - _app = QApplication(sys.argv) - _gui = PluginRefreshW(None, _host, True, False) - _app.exec_() - -# --------------------------------------------------------------------------------------------------------------------- diff --git a/source/frontend/pluginlist/pluginlistrefreshdialog.ui b/source/frontend/pluginlist/pluginlistrefreshdialog.ui deleted file mode 100644 index 9f1ed1e02..000000000 --- a/source/frontend/pluginlist/pluginlistrefreshdialog.ui +++ /dev/null @@ -1,610 +0,0 @@ - - - PluginRefreshW - - - - 0 - 0 - 613 - 369 - - - - Carla - Refresh - - - - - - - - Qt::Horizontal - - - QSizePolicy::Preferred - - - - 30 - 20 - - - - - - - - Search for new... - - - Qt::AlignCenter - - - - - - - - LADSPA - - - - - - - DSSI - - - - - - - LV2 - - - - - - - VST2 - - - - - - - VST3 - - - - - - - CLAP - - - - - - - AU - - - - - - - JSFX - - - - - - - 0 - - - 1 - - - Qt::Horizontal - - - - - - - SF2/3 - - - - - - - SFZ - - - - - - - Qt::Horizontal - - - - 40 - 5 - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - 0 - - - 1 - - - Qt::Vertical - - - - - - - - - Native - - - - - - - POSIX 32bit - - - - - - - POSIX 64bit - - - - - - - Windows 32bit - - - - - - - Windows 64bit - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - - - - Qt::Horizontal - - - QSizePolicy::Fixed - - - - 20 - 20 - - - - - - - - - - Available tools: - - - Qt::AlignCenter - - - true - - - - - - - 16 - 16 - - - - - - - :/16x16/dialog-ok-apply.svgz - - - true - - - - - - - - 16 - 16 - - - - - - - :/16x16/dialog-ok-apply.svgz - - - true - - - - - - - python3-rdflib (LADSPA-RDF support) - - - - - - - carla-discovery-win64 - - - - - - - carla-discovery-native - - - - - - - - 16 - 16 - - - - - - - :/16x16/dialog-ok-apply.svgz - - - true - - - - - - - - 16 - 16 - - - - - - - :/16x16/dialog-ok-apply.svgz - - - true - - - - - - - carla-discovery-posix32 - - - - - - - - 16 - 16 - - - - - - - :/16x16/dialog-ok-apply.svgz - - - true - - - - - - - - 16 - 16 - - - - - - - :/16x16/dialog-ok-apply.svgz - - - true - - - - - - - carla-discovery-posix64 - - - - - - - carla-discovery-win32 - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - Options: - - - Qt::AlignCenter - - - true - - - - - - Carla will run small processing checks when scanning the plugins (to make sure they won't crash). -You can disable these checks to get a faster scanning time (at your own risk). - - - Run processing checks while scanning - - - - - - - - - - Qt::Vertical - - - - 20 - 40 - - - - - - - - - - Qt::Horizontal - - - QSizePolicy::Preferred - - - - 30 - 20 - - - - - - - - - - Qt::Vertical - - - - 20 - 6 - - - - - - - - - - - - 22 - 22 - - - - - - - :/16x16/dialog-information.svgz - - - true - - - Qt::AlignRight|Qt::AlignTrailing|Qt::AlignVCenter - - - - - - - Restart Carla to list the new LV2 plugins - - - - - - - - - - - - - 0 - 0 - - - - 100 - - - 0 - - - Press 'Scan' to begin the search - - - - - - - Scan - - - - :/16x16/arrow-right.svgz:/16x16/arrow-right.svgz - - - - - - - >> Skip - - - - - - - Close - - - - :/16x16/window-close.svgz:/16x16/window-close.svgz - - - - - - - - - - - - - - b_close - clicked() - PluginRefreshW - close() - - - 426 - 231 - - - 236 - 125 - - - - - diff --git a/source/frontend/pluginlist/pluginrefreshdialog.hpp b/source/frontend/pluginlist/pluginrefreshdialog.hpp new file mode 100644 index 000000000..ce7c34baf --- /dev/null +++ b/source/frontend/pluginlist/pluginrefreshdialog.hpp @@ -0,0 +1,65 @@ +/* + * Carla plugin host + * Copyright (C) 2011-2023 Filipe Coelho + * SPDX-License-Identifier: GPL-2.0-or-later + */ + +#pragma once + +#include "ui_pluginrefreshdialog.h" + +#include "qsafesettings.hpp" + +// -------------------------------------------------------------------------------------------------------------------- +// Plugin Refresh Dialog + +struct PluginRefreshDialog : QDialog, Ui_PluginRefreshDialog { + explicit PluginRefreshDialog(QWidget* const parent) + : QDialog(parent) + { + setupUi(this); + + setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint); + #ifdef CARLA_OS_MAC + setWindowModality(Qt::WindowModal); + #endif + + b_skip->setEnabled(false); + ch_invalid->setEnabled(false); + + // ------------------------------------------------------------------------------------------------------------ + // Load settings + + { + const QSafeSettings settings; + + restoreGeometry(settings.valueByteArray("PluginRefreshDialog/Geometry")); + + if (settings.valueBool("PluginRefreshDialog/RefreshAll", false)) + ch_all->setChecked(true); + else + ch_updated->setChecked(true); + + ch_invalid->setChecked(settings.valueBool("PluginRefreshDialog/CheckInvalid", false)); + } + + // ------------------------------------------------------------------------------------------------------------ + // Set-up connections + + QObject::connect(this, &QDialog::finished, this, &PluginRefreshDialog::saveSettings); + } + + // ---------------------------------------------------------------------------------------------------------------- + // private slots + +private Q_SLOTS: + void saveSettings() + { + QSafeSettings settings; + settings.setValue("PluginRefreshDialog/Geometry", saveGeometry()); + settings.setValue("PluginRefreshDialog/RefreshAll", ch_all->isChecked()); + settings.setValue("PluginRefreshDialog/CheckInvalid", ch_invalid->isChecked()); + } +}; + +// -------------------------------------------------------------------------------------------------------------------- diff --git a/source/frontend/pluginlist/pluginrefreshdialog.ui b/source/frontend/pluginlist/pluginrefreshdialog.ui new file mode 100644 index 000000000..b25da3c7d --- /dev/null +++ b/source/frontend/pluginlist/pluginrefreshdialog.ui @@ -0,0 +1,190 @@ + + + PluginRefreshDialog + + + + 0 + 0 + 873 + 179 + + + + Plugin Refresh + + + + + + + + Qt::Horizontal + + + QSizePolicy::Preferred + + + + 30 + 20 + + + + + + + + Search for: + + + Qt::AlignCenter + + + + + + + + All plugins, ignoring cache + + + + + + + Updated plugins only + + + + + + + Check previously invalid plugins + + + + + + + + + + + + Qt::Horizontal + + + QSizePolicy::Preferred + + + + 20 + 20 + + + + + + + + + + Qt::Vertical + + + + 20 + 6 + + + + + + + + + + + 0 + 0 + + + + 100 + + + 0 + + + Press 'Scan' to begin the search + + + + + + + Scan + + + + :/16x16/arrow-right.svgz:/16x16/arrow-right.svgz + + + + + + + >> Skip + + + + + + + Close + + + + + + + + + + + + + b_close + clicked() + PluginRefreshDialog + close() + + + 426 + 231 + + + 236 + 125 + + + + + ch_updated + toggled(bool) + ch_invalid + setEnabled(bool) + + + 436 + 78 + + + 436 + 105 + + + + + diff --git a/source/frontend/utils/qcarlastring.hpp b/source/frontend/utils/qcarlastring.hpp index 6b3692b25..c625a3284 100644 --- a/source/frontend/utils/qcarlastring.hpp +++ b/source/frontend/utils/qcarlastring.hpp @@ -49,6 +49,11 @@ public: explicit inline QCarlaString(const char* const str) : QString(fromUtf8(str)) {} +#if QT_VERSION < 0x60000 + explicit inline QCarlaString(const QChar* const str, const qsizetype size) + : QString(str, size) {} +#endif + inline QCarlaString(const QString& s) : QString(s) {} @@ -66,6 +71,36 @@ public: { return simplified().remove(' '); } + +#if QT_VERSION < 0x60000 + inline QCarlaString sliced(const qsizetype pos) const + { + return QCarlaString(data() + pos, size() - pos); + } +#endif +}; + +//--------------------------------------------------------------------------------------------------------------------- +// Custom QByteArray class with a few extra methods + +class QCarlaByteArray : public QByteArray +{ +public: + explicit inline QCarlaByteArray() + : QByteArray() {} + + explicit inline QCarlaByteArray(const char* const data, const qsizetype size) + : QByteArray(data, size) {} + + inline QCarlaByteArray(const QByteArray& b) + : QByteArray(b) {} + +#if QT_VERSION < 0x60000 + inline QCarlaByteArray sliced(const qsizetype pos) const + { + return QCarlaByteArray(data() + pos, size() - pos); + } +#endif }; //---------------------------------------------------------------------------------------------------------------------