Skip some unnecessary plugin scanning steps
[carla.git] / source / frontend / pluginlist / pluginlistdialog.cpp
blobf3dafac2f17d9f3d180327e1c54d92aaad015fac
1 /*
2 * Carla plugin host
3 * Copyright (C) 2011-2023 Filipe Coelho <falktx@falktx.com>
4 * SPDX-License-Identifier: GPL-2.0-or-later
5 */
7 #include "pluginlistdialog.hpp"
8 #include "pluginrefreshdialog.hpp"
10 #ifdef __clang__
11 # pragma clang diagnostic push
12 # pragma clang diagnostic ignored "-Wdeprecated-copy-with-user-provided-copy"
13 # pragma clang diagnostic ignored "-Wdeprecated-register"
14 #elif defined(__GNUC__) && __GNUC__ >= 8
15 # pragma GCC diagnostic push
16 # pragma GCC diagnostic ignored "-Wclass-memaccess"
17 # pragma GCC diagnostic ignored "-Wdeprecated-copy"
18 #endif
20 #include <QtCore/QDir>
21 #include <QtCore/QFileInfo>
22 #include <QtCore/QList>
23 #include <QtCore/QTimer>
25 #ifdef __clang__
26 # pragma clang diagnostic pop
27 #elif defined(__GNUC__) && __GNUC__ >= 8
28 # pragma GCC diagnostic pop
29 #endif
31 #include "qcarlastring.hpp"
32 #include "qsafesettings.hpp"
34 #include "CarlaBackendUtils.hpp"
35 #include "CarlaJuceUtils.hpp"
36 #include "CarlaUtils.h"
38 #include "CarlaString.hpp"
40 #include <cstdlib>
42 CARLA_BACKEND_USE_NAMESPACE
44 // --------------------------------------------------------------------------------------------------------------------
45 // getenv with a fallback value if unset
47 static inline
48 const char* getEnvWithFallback(const char* const env, const char* const fallback)
50 if (const char* const value = std::getenv(env))
51 return value;
53 return fallback;
56 // --------------------------------------------------------------------------------------------------------------------
57 // Plugin paths (from env vars first, then default locations)
59 struct PluginPaths {
60 #ifndef CARLA_FRONTEND_ONLY_EMBEDDABLE_PLUGINS
61 QCarlaString ladspa;
62 QCarlaString dssi;
63 #endif
64 QCarlaString lv2;
65 QCarlaString vst2;
66 QCarlaString vst3;
67 QCarlaString clap;
68 #ifndef CARLA_FRONTEND_ONLY_EMBEDDABLE_PLUGINS
69 QCarlaString jsfx;
70 QCarlaString sf2;
71 QCarlaString sfz;
72 #endif
74 PluginPaths()
76 // get common env vars
77 const QString HOME = QDir::toNativeSeparators(QDir::homePath());
79 #if defined(CARLA_OS_WIN)
80 const char *const envAPPDATA = std::getenv("APPDATA");
81 const char *const envLOCALAPPDATA = getEnvWithFallback("LOCALAPPDATA", envAPPDATA);
82 const char *const envPROGRAMFILES = std::getenv("PROGRAMFILES");
83 const char* const envPROGRAMFILESx86 = std::getenv("PROGRAMFILES(x86)");
84 const char *const envCOMMONPROGRAMFILES = std::getenv("COMMONPROGRAMFILES");
85 const char* const envCOMMONPROGRAMFILESx86 = std::getenv("COMMONPROGRAMFILES(x86)");
87 // Small integrity tests
88 if (envAPPDATA == nullptr)
90 qFatal("APPDATA variable not set, cannot continue");
91 abort();
94 if (envPROGRAMFILES == nullptr)
96 qFatal("PROGRAMFILES variable not set, cannot continue");
97 abort();
100 if (envCOMMONPROGRAMFILES == nullptr)
102 qFatal("COMMONPROGRAMFILES variable not set, cannot continue");
103 abort();
106 const QCarlaString APPDATA(envAPPDATA);
107 const QCarlaString LOCALAPPDATA(envLOCALAPPDATA);
108 const QCarlaString PROGRAMFILES(envPROGRAMFILES);
109 const QCarlaString COMMONPROGRAMFILES(envCOMMONPROGRAMFILES);
110 #elif !defined(CARLA_OS_MAC)
111 const QCarlaString CONFIG_HOME(getEnvWithFallback("XDG_CONFIG_HOME", (HOME + "/.config").toUtf8()));
112 #endif
114 #ifndef CARLA_FRONTEND_ONLY_EMBEDDABLE_PLUGINS
115 // now set paths, listing format path spec if available
116 if (const char *const envLADSPA = std::getenv("LADSPA_PATH"))
118 ladspa = envLADSPA;
120 else
122 // no official spec, use common paths
123 #if defined(CARLA_OS_WIN)
124 ladspa = APPDATA + "\\LADSPA";
125 ladspa += ";" + PROGRAMFILES + "\\LADSPA";
126 #elif defined(CARLA_OS_HAIKU)
127 ladspa = HOME + "/.ladspa";
128 ladspa += ":/system/add-ons/media/ladspaplugins";
129 ladspa += ":/system/lib/ladspa";
130 #elif defined(CARLA_OS_MAC)
131 ladspa = HOME + "/Library/Audio/Plug-Ins/LADSPA";
132 ladspa += ":/Library/Audio/Plug-Ins/LADSPA";
133 #else
134 ladspa = HOME + "/.ladspa";
135 ladspa += ":/usr/local/lib/ladspa";
136 ladspa += ":/usr/lib/ladspa";
137 #endif
140 if (const char *const envDSSI = std::getenv("DSSI_PATH"))
142 dssi = envDSSI;
144 else
146 // no official spec, use common paths
147 #if defined(CARLA_OS_WIN)
148 dssi = APPDATA + "\\DSSI";
149 dssi += ";" + PROGRAMFILES + "\\DSSI";
150 #elif defined(CARLA_OS_HAIKU)
151 dssi = HOME + "/.dssi";
152 dssi += ":/system/add-ons/media/dssiplugins";
153 dssi += ":/system/lib/dssi";
154 #elif defined(CARLA_OS_MAC)
155 dssi = HOME + "/Library/Audio/Plug-Ins/DSSI";
156 dssi += ":/Library/Audio/Plug-Ins/DSSI";
157 #else
158 dssi = HOME + "/.dssi";
159 dssi += ":/usr/local/lib/dssi";
160 dssi += ":/usr/lib/dssi";
161 #endif
163 #endif // !CARLA_FRONTEND_ONLY_EMBEDDABLE_PLUGINS
165 if (const char *const envLV2 = std::getenv("LV2_PATH"))
167 lv2 = envLV2;
169 else
171 // https://lv2plug.in/pages/filesystem-hierarchy-standard.html
172 #if defined(CARLA_OS_WIN)
173 lv2 = APPDATA + "\\LV2";
174 lv2 += ";" + COMMONPROGRAMFILES + "\\LV2";
175 #elif defined(CARLA_OS_HAIKU)
176 lv2 = HOME + "/.lv2";
177 lv2 += ":/system/add-ons/media/lv2plugins";
178 #elif defined(CARLA_OS_MAC)
179 lv2 = HOME + "/Library/Audio/Plug-Ins/LV2";
180 lv2 += ":/Library/Audio/Plug-Ins/LV2";
181 #else
182 lv2 = HOME + "/.lv2";
183 lv2 += ":/usr/local/lib/lv2";
184 lv2 += ":/usr/lib/lv2";
185 #endif
188 if (const char *const envVST2 = std::getenv("VST_PATH"))
190 vst2 = envVST2;
192 else
194 #if defined(CARLA_OS_WIN)
195 // https://helpcenter.steinberg.de/hc/en-us/articles/115000177084
196 vst2 = PROGRAMFILES + "\\VSTPlugins";
197 vst2 += ";" + PROGRAMFILES + "\\Steinberg\\VSTPlugins";
198 vst2 += ";" + COMMONPROGRAMFILES + "\\VST2";
199 vst2 += ";" + COMMONPROGRAMFILES + "\\Steinberg\\VST2";
200 #elif defined(CARLA_OS_HAIKU)
201 vst2 = HOME + "/.vst";
202 vst2 += ":/system/add-ons/media/vstplugins";
203 #elif defined(CARLA_OS_MAC)
204 // https://helpcenter.steinberg.de/hc/en-us/articles/115000171310
205 vst2 = HOME + "/Library/Audio/Plug-Ins/VST";
206 vst2 += ":/Library/Audio/Plug-Ins/VST";
207 #else
208 // no official spec, use common paths
209 vst2 = HOME + "/.vst";
210 vst2 += ":" + HOME + "/.lxvst";
211 vst2 += ":/usr/local/lib/vst";
212 vst2 += ":/usr/local/lib/lxvst";
213 vst2 += ":/usr/lib/vst";
214 vst2 += ":/usr/lib/lxvst";
215 #endif
218 if (const char *const envVST3 = std::getenv("VST3_PATH"))
220 vst3 = envVST3;
222 else
224 // https://steinbergmedia.github.io/vst3_dev_portal/pages/Technical+Documentation/Locations+Format/Plugin+Locations.html
225 #if defined(CARLA_OS_WIN)
226 vst3 = LOCALAPPDATA + "\\Programs\\Common\\VST3";
227 vst3 += ";" + COMMONPROGRAMFILES + "\\VST3";
228 #elif defined(CARLA_OS_HAIKU)
229 vst3 = HOME + "/.vst3";
230 vst3 += ":/system/add-ons/media/vst3plugins";
231 #elif defined(CARLA_OS_MAC)
232 vst3 = HOME + "/Library/Audio/Plug-Ins/VST3";
233 vst3 += ":/Library/Audio/Plug-Ins/VST3";
234 #else
235 vst3 = HOME + "/.vst3";
236 vst3 += ":/usr/local/lib/vst3";
237 vst3 += ":/usr/lib/vst3";
238 #endif
241 if (const char *const envCLAP = std::getenv("CLAP_PATH"))
243 clap = envCLAP;
245 else
247 // https://github.com/free-audio/clap/blob/main/include/clap/entry.h
248 #if defined(CARLA_OS_WIN)
249 clap = LOCALAPPDATA + "\\Programs\\Common\\CLAP";
250 clap += ";" + COMMONPROGRAMFILES + "\\CLAP";
251 #elif defined(CARLA_OS_HAIKU)
252 clap = HOME + "/.clap";
253 clap += ":/system/add-ons/media/clapplugins";
254 #elif defined(CARLA_OS_MAC)
255 clap = HOME + "/Library/Audio/Plug-Ins/CLAP";
256 clap += ":/Library/Audio/Plug-Ins/CLAP";
257 #else
258 clap = HOME + "/.clap";
259 clap += ":/usr/local/lib/clap";
260 clap += ":/usr/lib/clap";
261 #endif
264 #ifndef CARLA_FRONTEND_ONLY_EMBEDDABLE_PLUGINS
265 if (const char *const envJSFX = std::getenv("JSFX_PATH"))
267 jsfx = envJSFX;
269 else
271 // REAPER user data directory
272 #if defined(CARLA_OS_WIN)
273 jsfx = APPDATA + "\\REAPER\\Effects";
274 #elif defined(CARLA_OS_MAC)
275 jsfx = HOME + "/Library/Application Support/REAPER/Effects";
276 #else
277 jsfx = CONFIG_HOME + "/REAPER/Effects";
278 #endif
281 if (const char *const envSF2 = std::getenv("SF2_PATH"))
283 sf2 = envSF2;
285 else
287 #if defined(CARLA_OS_WIN)
288 sf2 = APPDATA + "\\SF2";
289 #else
290 sf2 = HOME + "/.sounds/sf2";
291 sf2 += ":" + HOME + "/.sounds/sf3";
292 sf2 += ":/usr/share/sounds/sf2";
293 sf2 += ":/usr/share/sounds/sf3";
294 sf2 += ":/usr/share/soundfonts";
295 #endif
298 if (const char *const envSFZ = std::getenv("SFZ_PATH"))
300 sfz = envSFZ;
302 else
304 #if defined(CARLA_OS_WIN)
305 sfz = APPDATA + "\\SFZ";
306 #else
307 sfz = HOME + "/.sounds/sfz";
308 sfz += ":/usr/share/sounds/sfz";
309 #endif
311 #endif // !CARLA_FRONTEND_ONLY_EMBEDDABLE_PLUGINS
313 #ifdef CARLA_OS_WIN
314 if (envPROGRAMFILESx86 != nullptr)
316 const QCarlaString PROGRAMFILESx86(envPROGRAMFILESx86);
317 #ifndef CARLA_FRONTEND_ONLY_EMBEDDABLE_PLUGINS
318 ladspa += ";" + PROGRAMFILESx86 + "\\LADSPA";
319 dssi += ";" + PROGRAMFILESx86 + "\\DSSI";
320 #endif
321 vst2 += ";" + PROGRAMFILESx86 + "\\VSTPlugins";
322 vst2 += ";" + PROGRAMFILESx86 + "\\Steinberg\\VSTPlugins";
325 if (envCOMMONPROGRAMFILESx86 != nullptr)
327 const QCarlaString COMMONPROGRAMFILESx86(envCOMMONPROGRAMFILESx86);
328 vst3 += COMMONPROGRAMFILESx86 + "\\VST3";
329 clap += COMMONPROGRAMFILESx86 + "\\CLAP";
331 #elif !defined(CARLA_FRONTEND_ONLY_EMBEDDABLE_PLUGINS)
332 QCarlaString winePrefix;
334 if (const char* const envWINEPREFIX = std::getenv("WINEPREFIX"))
335 winePrefix = envWINEPREFIX;
337 if (winePrefix.isEmpty())
338 winePrefix = HOME + "/.wine";
340 if (QDir(winePrefix).exists())
342 vst2 += ":" + winePrefix + "/drive_c/Program Files/VstPlugins";
343 vst2 += ":" + winePrefix + "/drive_c/Program Files/Steinberg/VstPlugins";
344 vst2 += ":" + winePrefix + "/drive_c/Program Files/Common Files/VST2";
345 vst3 += ":" + winePrefix + "/drive_c/Program Files/Common Files/VST3";
346 clap += ":" + winePrefix + "/drive_c/Program Files/Common Files/CLAP";
348 #ifdef CARLA_OS_64BIT
349 if (QDir(winePrefix + "/drive_c/Program Files (x86)").exists())
351 vst2 += ":" + winePrefix + "/drive_c/Program Files (x86)/VstPlugins";
352 vst2 += ":" + winePrefix + "/drive_c/Program Files (x86)/Steinberg/VstPlugins";
353 vst2 += ":" + winePrefix + "/drive_c/Program Files (x86)/Common Files/VST2";
354 vst3 += ":" + winePrefix + "/drive_c/Program Files (x86)/Common Files/VST3";
355 clap += ":" + winePrefix + "/drive_c/Program Files (x86)/Common Files/CLAP";
357 #endif
359 #endif
363 // --------------------------------------------------------------------------------------------------------------------
364 // Backwards-compatible horizontalAdvance/width call, depending on Qt version
366 static inline
367 int fontMetricsHorizontalAdvance(const QFontMetrics& fontMetrics, const QString& string)
369 #if QT_VERSION >= 0x50b00
370 return fontMetrics.horizontalAdvance(string);
371 #else
372 return fontMetrics.width(string);
373 #endif
376 // --------------------------------------------------------------------------------------------------------------------
377 // Qt-compatible plugin info
379 // base details, nicely packed and POD-only so we can directly use as binary
380 struct PluginInfoHeader {
381 uint16_t build;
382 uint16_t type;
383 uint32_t hints;
384 uint64_t uniqueId;
385 uint16_t audioIns;
386 uint16_t audioOuts;
387 uint16_t cvIns;
388 uint16_t cvOuts;
389 uint16_t midiIns;
390 uint16_t midiOuts;
391 uint16_t parameterIns;
392 uint16_t parameterOuts;
395 // full details, now with non-POD types
396 struct PluginInfo : PluginInfoHeader {
397 QString category;
398 QString filename;
399 QString name;
400 QString label;
401 QString maker;
404 // convert PluginInfo to Qt types
405 static QVariant asByteArray(const PluginInfo& info)
407 QByteArray qdata;
409 // start with the POD data, stored as-is
410 qdata.append(reinterpret_cast<const char*>(&info), sizeof(PluginInfoHeader));
412 // then all the strings, with a null terminating byte
414 const QByteArray qcategory(info.category.toUtf8());
415 qdata += qcategory.constData();
416 qdata += '\0';
420 const QByteArray qfilename(info.filename.toUtf8());
421 qdata += qfilename.constData();
422 qdata += '\0';
426 const QByteArray qname(info.name.toUtf8());
427 qdata += qname.constData();
428 qdata += '\0';
432 const QByteArray qlabel(info.label.toUtf8());
433 qdata += qlabel.constData();
434 qdata += '\0';
438 const QByteArray qmaker(info.maker.toUtf8());
439 qdata += qmaker.constData();
440 qdata += '\0';
443 return qdata;
446 static QVariant asVariant(const PluginInfo& info)
448 return QVariant(asByteArray(info));
451 // convert Qt types to PluginInfo
452 static PluginInfo asPluginInfo(const QByteArray &qdata)
454 // make sure data is big enough to fit POD data + 5 strings
455 CARLA_SAFE_ASSERT_RETURN(static_cast<size_t>(qdata.size()) >= sizeof(PluginInfoHeader) + sizeof(char) * 5, {});
457 // read POD data first
458 const PluginInfoHeader* const data = reinterpret_cast<const PluginInfoHeader*>(qdata.constData());
460 PluginInfo info = {};
461 info.build = data->build;
462 info.type = data->type;
463 info.hints = data->hints;
464 info.uniqueId = data->uniqueId;
465 info.audioIns = data->audioIns;
466 info.audioOuts = data->audioOuts;
467 info.cvIns = data->cvIns;
468 info.cvOuts = data->cvOuts;
469 info.midiIns = data->midiIns;
470 info.midiOuts = data->midiOuts;
471 info.parameterIns = data->parameterIns;
472 info.parameterOuts = data->parameterOuts;
474 // then all the strings, keeping the same order as in `asVariant`
475 const char* sdata = reinterpret_cast<const char*>(data + 1);
477 info.category = QString::fromUtf8(sdata);
478 sdata += info.category.size() + 1;
480 info.filename = QString::fromUtf8(sdata);
481 sdata += info.filename.size() + 1;
483 info.name = QString::fromUtf8(sdata);
484 sdata += info.name.size() + 1;
486 info.label = QString::fromUtf8(sdata);
487 sdata += info.label.size() + 1;
489 info.maker = QString::fromUtf8(sdata);
490 sdata += info.maker.size() + 1;
492 return info;
495 static PluginInfo asPluginInfo(const QVariant& var)
497 return asPluginInfo(var.toByteArray());
500 static QList<PluginInfo> asPluginInfoList(const QVariant& var)
502 QCarlaByteArray qdata(var.toByteArray());
504 QList<PluginInfo> plist;
506 while (!qdata.isEmpty())
508 const PluginInfo info = asPluginInfo(qdata);
509 CARLA_SAFE_ASSERT_RETURN(info.build != BINARY_NONE, {});
511 plist.append(info);
512 qdata = qdata.sliced(sizeof(PluginInfoHeader)
513 + info.category.size() + info.filename.size() + info.name.size()
514 + info.label.size() + info.maker.size() + 5);
517 return plist;
520 // --------------------------------------------------------------------------------------------------------------------
521 // Qt-compatible plugin favorite
523 // base details, nicely packed and POD-only so we can directly use as binary
524 struct PluginFavoriteHeader {
525 uint16_t type;
526 uint64_t uniqueId;
529 // full details, now with non-POD types
530 struct PluginFavorite : PluginFavoriteHeader {
531 QString filename;
532 QString label;
534 PluginFavorite()
536 type = PLUGIN_NONE;
537 uniqueId = 0;
540 PluginFavorite(uint16_t t, uint64_t u, const QString& f, const QString& l)
541 : filename(f), label(l)
543 type = t;
544 uniqueId = u;
547 bool operator==(const PluginFavorite& other) const
549 return type == other.type && uniqueId == other.uniqueId && filename == other.filename && label == other.label;
553 // convert PluginFavorite to Qt types
554 static QByteArray asByteArray(const PluginFavorite& fav)
556 QByteArray qdata;
558 // start with the POD data, stored as-is
559 qdata.append(reinterpret_cast<const char*>(&fav), sizeof(PluginFavoriteHeader));
561 // then all the strings, with a null terminating byte
563 const QByteArray qfilename(fav.filename.toUtf8());
564 qdata += qfilename.constData();
565 qdata += '\0';
569 const QByteArray qlabel(fav.label.toUtf8());
570 qdata += qlabel.constData();
571 qdata += '\0';
574 return qdata;
577 static QVariant asVariant(const QList<PluginFavorite>& favlist)
579 QByteArray qdata;
581 for (const PluginFavorite &fav : favlist)
582 qdata += asByteArray(fav);
584 return QVariant(qdata);
587 // convert Qt types to PluginInfo
588 static PluginFavorite asPluginFavorite(const QByteArray& qdata)
590 // make sure data is big enough to fit POD data + 3 strings
591 CARLA_SAFE_ASSERT_RETURN(static_cast<size_t>(qdata.size()) >= sizeof(PluginFavoriteHeader) + sizeof(char) * 3, {});
593 // read POD data first
594 const PluginFavoriteHeader* const data = reinterpret_cast<const PluginFavoriteHeader*>(qdata.constData());
595 PluginFavorite fav = { data->type, data->uniqueId, {}, {} };
597 // then all the strings, keeping the same order as in `asVariant`
598 const char* sdata = reinterpret_cast<const char*>(data + 1);
600 fav.filename = QString::fromUtf8(sdata);
601 sdata += fav.filename.size() + 1;
603 fav.label = QString::fromUtf8(sdata);
604 sdata += fav.label.size() + 1;
606 return fav;
609 static QList<PluginFavorite> asPluginFavoriteList(const QVariant& var)
611 QCarlaByteArray qdata(var.toByteArray());
613 QList<PluginFavorite> favlist;
615 while (!qdata.isEmpty())
617 const PluginFavorite fav = asPluginFavorite(qdata);
618 CARLA_SAFE_ASSERT_RETURN(fav.type != PLUGIN_NONE, {});
620 favlist.append(fav);
621 qdata = qdata.sliced(sizeof(PluginFavoriteHeader) + fav.filename.size() + fav.label.size() + 2);
624 return favlist;
627 // create PluginFavorite from PluginInfo data
628 static PluginFavorite asPluginFavorite(const PluginInfo& info)
630 return PluginFavorite(info.type, info.uniqueId, info.filename, info.label);
633 // --------------------------------------------------------------------------------------------------------------------
634 // discovery callbacks
636 static void discoveryCallback(void* const ptr, const CarlaPluginDiscoveryInfo* const info, const char* const sha1sum)
638 static_cast<PluginListDialog*>(ptr)->addPluginInfo(info, sha1sum);
641 static bool checkCacheCallback(void* const ptr, const char* const filename, const char* const sha1sum)
643 if (sha1sum == nullptr)
644 return false;
646 return static_cast<PluginListDialog*>(ptr)->checkPluginCache(filename, sha1sum);
649 // --------------------------------------------------------------------------------------------------------------------
651 struct PluginListDialog::PrivateData {
652 int lastTableWidgetIndex = 0;
653 int timerId = 0;
654 PluginInfo retPlugin;
656 // To be changed by parent
657 bool hasLoadedLv2Plugins = false;
659 struct Discovery {
660 BinaryType btype = BINARY_NATIVE;
661 PluginType ptype = PLUGIN_NONE;
662 bool firstInit = true;
663 bool ignoreCache = false;
664 bool checkInvalid = false;
665 bool usePluginBridges = false;
666 bool useWineBridges = false;
667 CarlaPluginDiscoveryHandle handle = nullptr;
668 QCarlaString tool;
669 CarlaScopedPointer<PluginRefreshDialog> dialog;
670 Discovery()
672 restart();
675 ~Discovery()
677 if (handle != nullptr)
678 carla_plugin_discovery_stop(handle);
681 #ifndef CARLA_FRONTEND_ONLY_EMBEDDABLE_PLUGINS
682 bool nextTool()
684 if (handle != nullptr)
686 carla_plugin_discovery_stop(handle);
687 handle = nullptr;
690 if (!usePluginBridges)
691 return false;
693 #ifdef CARLA_OS_WIN
694 #ifdef CARLA_OS_WIN64
695 // look for win32 plugins on win64
696 if (btype == BINARY_NATIVE)
698 btype = BINARY_WIN32;
699 ptype = PLUGIN_NONE;
700 tool = carla_get_library_folder();
701 tool += CARLA_OS_SEP_STR "carla-discovery-win32.exe";
703 if (QFile(tool).exists())
704 return true;
706 #endif
708 // no other types to try
709 return false;
710 #else // CARLA_OS_WIN
712 #ifndef CARLA_OS_MAC
713 // try 32bit plugins on 64bit systems, skipping macOS where 32bit is no longer supported
714 if (btype == BINARY_NATIVE)
716 btype = BINARY_POSIX32;
717 ptype = PLUGIN_NONE;
718 tool = carla_get_library_folder();
719 tool += CARLA_OS_SEP_STR "carla-discovery-posix32";
721 if (QFile(tool).exists())
722 return true;
724 #endif
726 if (!useWineBridges)
727 return false;
729 // try wine bridges
730 #ifdef CARLA_OS_64BIT
731 if (btype == BINARY_NATIVE || btype == BINARY_POSIX32)
733 btype = BINARY_WIN64;
734 ptype = PLUGIN_NONE;
735 tool = carla_get_library_folder();
736 tool += CARLA_OS_SEP_STR "carla-discovery-win64.exe";
738 if (QFile(tool).exists())
739 return true;
741 #endif
743 if (btype != BINARY_WIN32)
745 btype = BINARY_WIN32;
746 ptype = PLUGIN_NONE;
747 tool = carla_get_library_folder();
748 tool += CARLA_OS_SEP_STR "carla-discovery-win32.exe";
750 if (QFile(tool).exists())
751 return true;
754 return false;
755 #endif // CARLA_OS_WIN
757 #endif // CARLA_FRONTEND_ONLY_EMBEDDABLE_PLUGINS
759 void restart()
761 btype = BINARY_NATIVE;
762 ptype = PLUGIN_NONE;
763 tool = carla_get_library_folder();
764 tool += CARLA_OS_SEP_STR "carla-discovery-native";
765 #ifdef CARLA_OS_WIN
766 tool += ".exe";
767 #endif
769 } discovery;
771 PluginPaths paths;
773 struct {
774 #ifndef CARLA_FRONTEND_ONLY_EMBEDDABLE_PLUGINS
775 std::vector<PluginInfo> internal;
776 std::vector<PluginInfo> ladspa;
777 std::vector<PluginInfo> dssi;
778 #endif
779 std::vector<PluginInfo> lv2;
780 std::vector<PluginInfo> vst2;
781 std::vector<PluginInfo> vst3;
782 std::vector<PluginInfo> clap;
783 #ifndef CARLA_FRONTEND_ONLY_EMBEDDABLE_PLUGINS
784 #ifdef CARLA_OS_MAC
785 std::vector<PluginInfo> au;
786 #endif
787 std::vector<PluginInfo> jsfx;
788 std::vector<PluginInfo> kits;
789 #endif
790 QMap<QString, QList<PluginInfo>> cache;
791 QList<PluginFavorite> favorites;
793 bool add(const PluginInfo& pinfo)
795 switch (pinfo.type)
797 #ifndef CARLA_FRONTEND_ONLY_EMBEDDABLE_PLUGINS
798 case PLUGIN_INTERNAL: internal.push_back(pinfo); return true;
799 case PLUGIN_LADSPA: ladspa.push_back(pinfo); return true;
800 case PLUGIN_DSSI: dssi.push_back(pinfo); return true;
801 #endif
802 case PLUGIN_LV2: lv2.push_back(pinfo); return true;
803 case PLUGIN_VST2: vst2.push_back(pinfo); return true;
804 case PLUGIN_VST3: vst3.push_back(pinfo); return true;
805 case PLUGIN_CLAP: clap.push_back(pinfo); return true;
806 #ifndef CARLA_FRONTEND_ONLY_EMBEDDABLE_PLUGINS
807 #ifdef CARLA_OS_MAC
808 case PLUGIN_AU: au.push_back(pinfo); return true;
809 #endif
810 case PLUGIN_JSFX: jsfx.push_back(pinfo); return true;
811 case PLUGIN_SF2:
812 case PLUGIN_SFZ: kits.push_back(pinfo); return true;
813 #endif
814 default: return false;
817 } plugins;
820 // --------------------------------------------------------------------------------------------------------------------
821 // Plugin List Dialog
823 PluginListDialog::PluginListDialog(QWidget* const parent, const HostSettings* const hostSettings)
824 : QDialog(parent),
825 p(new PrivateData)
827 ui.setupUi(this);
829 // ----------------------------------------------------------------------------------------------------------------
830 // Set-up global discovery options
832 p->discovery.usePluginBridges = hostSettings->showPluginBridges;
833 p->discovery.useWineBridges = hostSettings->showWineBridges;
835 #ifndef CARLA_OS_WIN
836 carla_plugin_discovery_set_option(ENGINE_OPTION_WINE_AUTO_PREFIX, hostSettings->wineAutoPrefix, nullptr);
837 carla_plugin_discovery_set_option(ENGINE_OPTION_WINE_EXECUTABLE, 0, hostSettings->wineExecutable);
838 carla_plugin_discovery_set_option(ENGINE_OPTION_WINE_FALLBACK_PREFIX, 0, hostSettings->wineFallbackPrefix);
839 #endif
841 // ----------------------------------------------------------------------------------------------------------------
842 // Set-up GUI
844 ui.b_add->setEnabled(false);
846 ui.tab_info->tabBar()->hide();
847 ui.tab_reqs->tabBar()->hide();
849 #ifdef CARLA_FRONTEND_ONLY_EMBEDDABLE_PLUGINS
850 ui.ch_internal->hide();
851 ui.ch_ladspa->hide();
852 ui.ch_dssi->hide();
853 ui.ch_au->hide();
854 ui.ch_jsfx->hide();
855 ui.ch_kits->hide();
856 ui.ch_gui->hide();
857 ui.ch_inline_display->hide();
858 ui.toolBox->setItemEnabled(3, false);
859 #endif
861 // do not resize info frame so much
862 const QLayout *const infoLayout = ui.tw_info->layout();
863 const QMargins infoMargins = infoLayout->contentsMargins();
864 ui.tab_info->setMinimumWidth(infoMargins.left() + infoMargins.right() + infoLayout->spacing() * 3
865 + fontMetricsHorizontalAdvance(ui.la_id->fontMetrics(), "Has Custom GUI: 9999999999"));
867 // start with no plugin selected
868 checkPlugin(-1);
870 // custom action that listens for Ctrl+F shortcut
871 addAction(ui.act_focus_search);
873 #ifdef CARLA_OS_64BIT
874 ui.ch_bridged->setText(tr("Bridged (32bit)"));
875 #else
876 ui.ch_bridged->setChecked(false);
877 ui.ch_bridged->setEnabled(false);
878 #endif
880 #if !(defined(CARLA_OS_LINUX) || defined(CARLA_OS_MAC))
881 ui.ch_bridged_wine->setChecked(false);
882 ui.ch_bridged_wine->setEnabled(false);
883 #endif
885 #ifdef CARLA_OS_MAC
886 setWindowModality(Qt::WindowModal);
887 #else
888 ui.ch_au->setChecked(false);
889 ui.ch_au->setEnabled(false);
890 ui.ch_au->setVisible(false);
891 #endif
893 setWindowFlags(windowFlags() & ~Qt::WindowContextHelpButtonHint);
895 // ----------------------------------------------------------------------------------------------------------------
896 // Load settings
898 loadSettings();
900 // ----------------------------------------------------------------------------------------------------------------
901 // Disable bridges if not enabled in settings
903 #if 0
904 // NOTE: We Assume win32 carla build will not run win64 plugins
905 if (WINDOWS and not kIs64bit) or not host.showPluginBridges:
906 ui.ch_native.setChecked(True)
907 ui.ch_native.setEnabled(False)
908 ui.ch_native.setVisible(True)
909 ui.ch_bridged.setChecked(False)
910 ui.ch_bridged.setEnabled(False)
911 ui.ch_bridged.setVisible(False)
912 ui.ch_bridged_wine.setChecked(False)
913 ui.ch_bridged_wine.setEnabled(False)
914 ui.ch_bridged_wine.setVisible(False)
916 elif not host.showWineBridges:
917 ui.ch_bridged_wine.setChecked(False)
918 ui.ch_bridged_wine.setEnabled(False)
919 ui.ch_bridged_wine.setVisible(False)
920 #endif
922 // ----------------------------------------------------------------------------------------------------------------
923 // Set-up Icons
925 if (hostSettings->useSystemIcons)
927 #if 0
928 ui.b_add.setIcon(getIcon('list-add', 16, 'svgz'))
929 ui.b_cancel.setIcon(getIcon('dialog-cancel', 16, 'svgz'))
930 ui.b_clear_filters.setIcon(getIcon('edit-clear', 16, 'svgz'))
931 ui.b_refresh.setIcon(getIcon('view-refresh', 16, 'svgz'))
932 QTableWidgetItem* const hhi = ui.tableWidget->horizontalHeaderItem(TW_FAVORITE);
933 hhi.setIcon(getIcon('bookmarks', 16, 'svgz'))
934 #endif
937 // ----------------------------------------------------------------------------------------------------------------
938 // Set-up connections
940 QObject::connect(this, &QDialog::finished, this, &PluginListDialog::saveSettings);
941 QObject::connect(ui.b_add, &QPushButton::clicked, this, &QDialog::accept);
942 QObject::connect(ui.b_cancel, &QPushButton::clicked, this, &QDialog::reject);
944 QObject::connect(ui.b_refresh, &QPushButton::clicked, this, &PluginListDialog::refreshPlugins);
945 QObject::connect(ui.b_clear_filters, &QPushButton::clicked, this, &PluginListDialog::clearFilters);
946 QObject::connect(ui.lineEdit, &QLineEdit::textChanged, this, &PluginListDialog::checkFilters);
947 QObject::connect(ui.tableWidget, &QTableWidget::currentCellChanged, this, &PluginListDialog::checkPlugin);
948 QObject::connect(ui.tableWidget, &QTableWidget::cellClicked, this, &PluginListDialog::cellClicked);
949 QObject::connect(ui.tableWidget, &QTableWidget::cellDoubleClicked, this, &PluginListDialog::cellDoubleClicked);
951 QObject::connect(ui.ch_internal, &QCheckBox::clicked, this, &PluginListDialog::checkFilters);
952 QObject::connect(ui.ch_ladspa, &QCheckBox::clicked, this, &PluginListDialog::checkFilters);
953 QObject::connect(ui.ch_dssi, &QCheckBox::clicked, this, &PluginListDialog::checkFilters);
954 QObject::connect(ui.ch_lv2, &QCheckBox::clicked, this, &PluginListDialog::checkFilters);
955 QObject::connect(ui.ch_vst, &QCheckBox::clicked, this, &PluginListDialog::checkFilters);
956 QObject::connect(ui.ch_vst3, &QCheckBox::clicked, this, &PluginListDialog::checkFilters);
957 QObject::connect(ui.ch_clap, &QCheckBox::clicked, this, &PluginListDialog::checkFilters);
958 QObject::connect(ui.ch_au, &QCheckBox::clicked, this, &PluginListDialog::checkFilters);
959 QObject::connect(ui.ch_jsfx, &QCheckBox::clicked, this, &PluginListDialog::checkFilters);
960 QObject::connect(ui.ch_kits, &QCheckBox::clicked, this, &PluginListDialog::checkFilters);
961 QObject::connect(ui.ch_effects, &QCheckBox::clicked, this, &PluginListDialog::checkFilters);
962 QObject::connect(ui.ch_instruments, &QCheckBox::clicked, this, &PluginListDialog::checkFilters);
963 QObject::connect(ui.ch_midi, &QCheckBox::clicked, this, &PluginListDialog::checkFilters);
964 QObject::connect(ui.ch_other, &QCheckBox::clicked, this, &PluginListDialog::checkFilters);
965 QObject::connect(ui.ch_native, &QCheckBox::clicked, this, &PluginListDialog::checkFilters);
966 QObject::connect(ui.ch_bridged, &QCheckBox::clicked, this, &PluginListDialog::checkFilters);
967 QObject::connect(ui.ch_bridged_wine, &QCheckBox::clicked, this, &PluginListDialog::checkFilters);
968 QObject::connect(ui.ch_favorites, &QCheckBox::clicked, this, &PluginListDialog::checkFilters);
969 QObject::connect(ui.ch_rtsafe, &QCheckBox::clicked, this, &PluginListDialog::checkFilters);
970 QObject::connect(ui.ch_cv, &QCheckBox::clicked, this, &PluginListDialog::checkFilters);
971 QObject::connect(ui.ch_gui, &QCheckBox::clicked, this, &PluginListDialog::checkFilters);
972 QObject::connect(ui.ch_inline_display, &QCheckBox::clicked, this, &PluginListDialog::checkFilters);
973 QObject::connect(ui.ch_stereo, &QCheckBox::clicked, this, &PluginListDialog::checkFilters);
974 QObject::connect(ui.ch_cat_all, &QCheckBox::clicked, this, &PluginListDialog::checkFiltersCategoryAll);
975 QObject::connect(ui.ch_cat_delay, &QCheckBox::clicked, this, &PluginListDialog::checkFiltersCategorySpecific);
976 QObject::connect(ui.ch_cat_distortion, &QCheckBox::clicked, this, &PluginListDialog::checkFiltersCategorySpecific);
977 QObject::connect(ui.ch_cat_dynamics, &QCheckBox::clicked, this, &PluginListDialog::checkFiltersCategorySpecific);
978 QObject::connect(ui.ch_cat_eq, &QCheckBox::clicked, this, &PluginListDialog::checkFiltersCategorySpecific);
979 QObject::connect(ui.ch_cat_filter, &QCheckBox::clicked, this, &PluginListDialog::checkFiltersCategorySpecific);
980 QObject::connect(ui.ch_cat_modulator, &QCheckBox::clicked, this, &PluginListDialog::checkFiltersCategorySpecific);
981 QObject::connect(ui.ch_cat_synth, &QCheckBox::clicked, this, &PluginListDialog::checkFiltersCategorySpecific);
982 QObject::connect(ui.ch_cat_utility, &QCheckBox::clicked, this, &PluginListDialog::checkFiltersCategorySpecific);
983 QObject::connect(ui.ch_cat_other, &QCheckBox::clicked, this, &PluginListDialog::checkFiltersCategorySpecific);
985 QObject::connect(ui.act_focus_search, &QAction::triggered, this, &PluginListDialog::focusSearchFieldAndSelectAll);
988 PluginListDialog::~PluginListDialog()
990 if (p->timerId != 0)
991 killTimer(p->timerId);
993 delete p;
996 // --------------------------------------------------------------------------------------------------------------------
997 // public methods
999 const PluginInfo& PluginListDialog::getSelectedPluginInfo() const
1001 return p->retPlugin;
1004 void PluginListDialog::addPluginInfo(const CarlaPluginDiscoveryInfo* const info, const char* const sha1sum)
1006 if (info == nullptr)
1008 if (sha1sum != nullptr)
1010 QSafeSettings settings("falkTX", "CarlaDatabase3");
1011 settings.setValue(QString("PluginCache/%1").arg(sha1sum), QByteArray());
1013 const QString qsha1sum(sha1sum);
1014 p->plugins.cache[qsha1sum] = {};
1016 return;
1019 PluginInfo pinfo = {};
1020 pinfo.build = static_cast<uint16_t>(info->btype);
1021 pinfo.type = static_cast<uint16_t>(info->ptype);
1022 pinfo.hints = info->metadata.hints;
1023 pinfo.uniqueId = info->uniqueId;
1024 pinfo.audioIns = static_cast<uint16_t>(info->io.audioIns);
1025 pinfo.audioOuts = static_cast<uint16_t>(info->io.audioOuts);
1026 pinfo.cvIns = static_cast<uint16_t>(info->io.cvIns);
1027 pinfo.cvOuts = static_cast<uint16_t>(info->io.cvOuts);
1028 pinfo.midiIns = static_cast<uint16_t>(info->io.midiIns);
1029 pinfo.midiOuts = static_cast<uint16_t>(info->io.midiOuts);
1030 pinfo.parameterIns = static_cast<uint16_t>(info->io.parameterIns);
1031 pinfo.parameterOuts = static_cast<uint16_t>(info->io.parameterOuts);
1032 pinfo.category = getPluginCategoryAsString(info->metadata.category);
1033 pinfo.filename = QString::fromUtf8(info->filename);
1034 pinfo.name = QString::fromUtf8(info->metadata.name);
1035 pinfo.label = QString::fromUtf8(info->label);
1036 pinfo.maker = QString::fromUtf8(info->metadata.maker);
1038 if (sha1sum != nullptr)
1040 QSafeSettings settings("falkTX", "CarlaDatabase3");
1041 const QString qsha1sum(sha1sum);
1042 const QString key = QString("PluginCache/%1").arg(sha1sum);
1044 // single sha1sum can contain >1 plugin
1045 QByteArray qdata;
1046 if (p->plugins.cache.contains(qsha1sum))
1047 qdata = settings.valueByteArray(key);
1048 qdata += asVariant(pinfo).toByteArray();
1050 settings.setValue(key, qdata);
1052 p->plugins.cache[qsha1sum].append(pinfo);
1055 #ifdef CARLA_FRONTEND_ONLY_EMBEDDABLE_PLUGINS
1056 if ((pinfo.hints & PLUGIN_HAS_CUSTOM_EMBED_UI) == 0x0)
1057 return;
1058 #endif
1060 p->plugins.add(pinfo);
1063 bool PluginListDialog::checkPluginCache(const char* const filename, const char* const sha1sum)
1065 // sha1sum is always valid for this call
1066 const QString qsha1sum(sha1sum);
1068 if (filename != nullptr)
1069 p->discovery.dialog->progressBar->setFormat(filename);
1071 if (!p->plugins.cache.contains(qsha1sum))
1072 return false;
1074 const QList<PluginInfo>& plist(p->plugins.cache[qsha1sum]);
1076 if (plist.isEmpty())
1077 return p->discovery.ignoreCache || !p->discovery.checkInvalid;
1079 // if filename does not match, abort (hash collision?)
1080 if (filename == nullptr || plist.first().filename != filename)
1082 p->plugins.cache.remove(qsha1sum);
1083 return false;
1086 for (const PluginInfo& info : plist)
1088 #ifdef CARLA_FRONTEND_ONLY_EMBEDDABLE_PLUGINS
1089 if ((info.hints & PLUGIN_HAS_CUSTOM_EMBED_UI) == 0x0)
1090 continue;
1091 #endif
1092 p->plugins.add(info);
1095 return true;
1098 void PluginListDialog::setPluginPath(const PluginType ptype, const char* const path)
1100 switch (ptype)
1102 case PLUGIN_LV2:
1103 p->paths.lv2 = path;
1104 break;
1105 case PLUGIN_VST2:
1106 p->paths.vst2 = path;
1107 break;
1108 case PLUGIN_VST3:
1109 p->paths.vst3 = path;
1110 break;
1111 case PLUGIN_CLAP:
1112 p->paths.clap = path;
1113 break;
1114 #ifndef CARLA_FRONTEND_ONLY_EMBEDDABLE_PLUGINS
1115 case PLUGIN_LADSPA:
1116 p->paths.ladspa = path;
1117 break;
1118 case PLUGIN_DSSI:
1119 p->paths.dssi = path;
1120 break;
1121 case PLUGIN_SF2:
1122 p->paths.sf2 = path;
1123 break;
1124 case PLUGIN_SFZ:
1125 p->paths.sfz = path;
1126 break;
1127 case PLUGIN_JSFX:
1128 p->paths.jsfx = path;
1129 break;
1130 #endif
1131 default:
1132 break;
1136 // --------------------------------------------------------------------------------------------------------------------
1137 // protected methods
1139 void PluginListDialog::done(const int r)
1141 if (r == QDialog::Accepted && ui.tableWidget->currentRow() >= 0)
1143 QTableWidgetItem* const widget = ui.tableWidget->item(ui.tableWidget->currentRow(), TW_NAME);
1144 p->retPlugin = asPluginInfo(widget->data(Qt::UserRole + UR_PLUGIN_INFO));
1146 else
1148 p->retPlugin = {};
1151 QDialog::done(r);
1154 void PluginListDialog::showEvent(QShowEvent* const event)
1156 focusSearchFieldAndSelectAll();
1157 QDialog::showEvent(event);
1159 // Set up initial discovery
1160 if (p->discovery.firstInit)
1162 p->discovery.firstInit = false;
1164 p->discovery.dialog = new PluginRefreshDialog(this);
1165 p->discovery.dialog->b_start->setEnabled(false);
1166 p->discovery.dialog->b_skip->setEnabled(true);
1167 p->discovery.dialog->ch_updated->setChecked(true);
1168 p->discovery.dialog->ch_invalid->setChecked(false);
1169 p->discovery.dialog->group->setEnabled(false);
1170 p->discovery.dialog->progressBar->setFormat("Starting initial discovery...");
1172 QObject::connect(p->discovery.dialog->b_skip, &QPushButton::clicked,
1173 this, &PluginListDialog::refreshPluginsSkip);
1174 QObject::connect(p->discovery.dialog, &QDialog::finished,
1175 this, &PluginListDialog::refreshPluginsStop);
1177 p->timerId = startTimer(0);
1179 QTimer::singleShot(0, p->discovery.dialog, &QDialog::exec);
1183 void PluginListDialog::timerEvent(QTimerEvent* const event)
1185 if (event->timerId() == p->timerId)
1187 do {
1188 // discovery in progress, keep it going
1189 if (p->discovery.handle != nullptr)
1191 if (!carla_plugin_discovery_idle(p->discovery.handle))
1193 carla_plugin_discovery_stop(p->discovery.handle);
1194 p->discovery.handle = nullptr;
1196 break;
1199 // start next discovery
1200 QCarlaString path;
1201 switch (p->discovery.ptype)
1203 case PLUGIN_NONE:
1204 #ifndef CARLA_FRONTEND_ONLY_EMBEDDABLE_PLUGINS
1205 if (p->discovery.btype == BINARY_NATIVE)
1207 ui.label->setText(tr("Discovering internal plugins..."));
1208 p->discovery.ptype = PLUGIN_INTERNAL;
1209 break;
1211 [[fallthrough]];
1212 case PLUGIN_INTERNAL:
1213 ui.label->setText(tr("Discovering LADSPA plugins..."));
1214 path = p->paths.ladspa;
1215 p->discovery.ptype = PLUGIN_LADSPA;
1216 break;
1217 case PLUGIN_LADSPA:
1218 ui.label->setText(tr("Discovering DSSI plugins..."));
1219 path = p->paths.dssi;
1220 p->discovery.ptype = PLUGIN_DSSI;
1221 break;
1222 case PLUGIN_DSSI:
1223 #endif
1224 if (p->discovery.btype == BINARY_NATIVE && p->paths.lv2.isNotEmpty())
1226 ui.label->setText(tr("Discovering LV2 plugins..."));
1227 path = p->paths.lv2;
1228 p->discovery.ptype = PLUGIN_LV2;
1229 break;
1231 [[fallthrough]];
1232 case PLUGIN_LV2:
1233 ui.label->setText(tr("Discovering VST2 plugins..."));
1234 path = p->paths.vst2;
1235 p->discovery.ptype = PLUGIN_VST2;
1236 break;
1237 case PLUGIN_VST2:
1238 ui.label->setText(tr("Discovering VST3 plugins..."));
1239 path = p->paths.vst3;
1240 p->discovery.ptype = PLUGIN_VST3;
1241 break;
1242 case PLUGIN_VST3:
1243 ui.label->setText(tr("Discovering CLAP plugins..."));
1244 path = p->paths.clap;
1245 p->discovery.ptype = PLUGIN_CLAP;
1246 break;
1247 case PLUGIN_CLAP:
1248 #ifndef CARLA_FRONTEND_ONLY_EMBEDDABLE_PLUGINS
1249 #ifdef CARLA_OS_MAC
1250 if (p->discovery.btype == BINARY_POSIX32 || p->discovery.btype == BINARY_POSIX64)
1252 ui.label->setText(tr("Discovering AU plugins..."));
1253 p->discovery.ptype = PLUGIN_AU;
1254 break;
1256 [[fallthrough]];
1257 case PLUGIN_AU:
1258 #endif
1259 if (p->discovery.btype == BINARY_NATIVE && p->paths.jsfx.isNotEmpty())
1261 ui.label->setText(tr("Discovering JSFX plugins..."));
1262 path = p->paths.jsfx;
1263 p->discovery.ptype = PLUGIN_JSFX;
1264 break;
1266 [[fallthrough]];
1267 case PLUGIN_JSFX:
1268 if (p->discovery.btype == BINARY_NATIVE && p->paths.sf2.isNotEmpty())
1270 ui.label->setText(tr("Discovering SF2 kits..."));
1271 path = p->paths.sf2;
1272 p->discovery.ptype = PLUGIN_SF2;
1273 break;
1275 [[fallthrough]];
1276 case PLUGIN_SF2:
1277 if (p->discovery.btype == BINARY_NATIVE && p->paths.sfz.isNotEmpty())
1279 ui.label->setText(tr("Discovering SFZ kits..."));
1280 path = p->paths.sfz;
1281 p->discovery.ptype = PLUGIN_SFZ;
1282 break;
1284 [[fallthrough]];
1285 case PLUGIN_SFZ:
1286 #endif
1287 default:
1288 // discovery complete?
1289 for (;;)
1291 #ifndef CARLA_FRONTEND_ONLY_EMBEDDABLE_PLUGINS
1292 if (p->discovery.nextTool())
1294 // tool has nothing to search, go to next one
1295 if (p->discovery.ptype == PLUGIN_NONE)
1296 continue;
1298 // there is still to do, break out of loop
1299 break;
1301 #endif
1303 refreshPluginsStop();
1304 break;
1308 if (p->timerId == 0)
1309 break;
1311 if (p->discovery.dialog)
1312 p->discovery.dialog->progressBar->setFormat(ui.label->text());
1314 p->discovery.handle = carla_plugin_discovery_start(p->discovery.tool.toUtf8().constData(),
1315 p->discovery.btype,
1316 p->discovery.ptype,
1317 path.toUtf8().constData(),
1318 discoveryCallback, checkCacheCallback, this);
1319 } while (false);
1322 QDialog::timerEvent(event);
1325 // --------------------------------------------------------------------------------------------------------------------
1326 // private methods
1328 void PluginListDialog::addPluginsToTable()
1330 // ----------------------------------------------------------------------------------------------------------------
1331 // sum plugins first, creating all needed rows in advance
1333 ui.tableWidget->setSortingEnabled(false);
1334 ui.tableWidget->clearContents();
1336 #ifdef CARLA_FRONTEND_ONLY_EMBEDDABLE_PLUGINS
1337 ui.tableWidget->setRowCount(
1338 int(p->plugins.lv2.size() + p->plugins.vst2.size() + p->plugins.vst3.size() + p->plugins.clap.size()));
1340 constexpr const char* const txt = "Have %1 LV2, %2 VST2, %3 VST3 and %4 CLAP plugins";
1342 ui.label->setText(tr(txt)
1343 .arg(QString::number(p->plugins.lv2.size()))
1344 .arg(QString::number(p->plugins.vst2.size()))
1345 .arg(QString::number(p->plugins.vst3.size()))
1346 .arg(QString::number(p->plugins.clap.size())));
1347 #else
1348 ui.tableWidget->setRowCount(
1349 int(p->plugins.internal.size() + p->plugins.ladspa.size() + p->plugins.dssi.size() +
1350 p->plugins.lv2.size() + p->plugins.vst2.size() + p->plugins.vst3.size() + p->plugins.clap.size() +
1351 #ifdef CARLA_OS_MAC
1352 p->plugins.au.size() +
1353 #endif
1354 p->plugins.jsfx.size() + p->plugins.kits.size()));
1356 constexpr const char* const txt = "Have %1 Internal, %2 LADSPA, %3 DSSI, %4 LV2, %5 VST2, %6 VST3, %7 CLAP"
1357 #ifdef CARLA_OS_MAC
1358 ", %8 AudioUnit and %9 JSFX plugins, plus %10 Sound Kits"
1359 #endif
1360 " and %8 JSFX plugins, plus %9 Sound Kits";
1362 ui.label->setText(tr(txt)
1363 .arg(QString::number(p->plugins.internal.size()))
1364 .arg(QString::number(p->plugins.ladspa.size()))
1365 .arg(QString::number(p->plugins.dssi.size()))
1366 .arg(QString::number(p->plugins.lv2.size()))
1367 .arg(QString::number(p->plugins.vst2.size()))
1368 .arg(QString::number(p->plugins.vst3.size()))
1369 .arg(QString::number(p->plugins.clap.size()))
1370 #ifdef CARLA_OS_MAC
1371 .arg(QString::number(p->plugins.au.size()))
1372 #endif
1373 .arg(QString::number(p->plugins.jsfx.size()))
1374 .arg(QString::number(p->plugins.kits.size())));
1375 #endif
1377 // ----------------------------------------------------------------------------------------------------------------
1378 // now add all plugins to the table
1380 auto addPluginToTable = [=](const PluginInfo& info) {
1381 const int index = p->lastTableWidgetIndex++;
1382 const bool isFav = p->plugins.favorites.contains(asPluginFavorite(info));
1384 QTableWidgetItem* const itemFav = new QTableWidgetItem;
1385 itemFav->setCheckState(isFav ? Qt::Checked : Qt::Unchecked);
1386 itemFav->setText(isFav ? " " : " ");
1388 const QString pluginText = (info.name + info.label + info.maker + info.filename).toLower();
1389 ui.tableWidget->setItem(index, TW_FAVORITE, itemFav);
1390 ui.tableWidget->setItem(index, TW_NAME, new QTableWidgetItem(info.name));
1391 ui.tableWidget->setItem(index, TW_LABEL, new QTableWidgetItem(info.label));
1392 ui.tableWidget->setItem(index, TW_MAKER, new QTableWidgetItem(info.maker));
1393 ui.tableWidget->setItem(index, TW_BINARY, new QTableWidgetItem(QFileInfo(info.filename).fileName()));
1395 QTableWidgetItem *const itemName = ui.tableWidget->item(index, TW_NAME);
1396 itemName->setData(Qt::UserRole + UR_PLUGIN_INFO, asVariant(info));
1397 itemName->setData(Qt::UserRole + UR_SEARCH_TEXT, pluginText);
1400 p->lastTableWidgetIndex = 0;
1402 #ifndef CARLA_FRONTEND_ONLY_EMBEDDABLE_PLUGINS
1403 for (const PluginInfo &plugin : p->plugins.internal)
1404 addPluginToTable(plugin);
1406 for (const PluginInfo &plugin : p->plugins.ladspa)
1407 addPluginToTable(plugin);
1409 for (const PluginInfo &plugin : p->plugins.dssi)
1410 addPluginToTable(plugin);
1411 #endif
1413 for (const PluginInfo &plugin : p->plugins.lv2)
1414 addPluginToTable(plugin);
1416 for (const PluginInfo &plugin : p->plugins.vst2)
1417 addPluginToTable(plugin);
1419 for (const PluginInfo &plugin : p->plugins.vst3)
1420 addPluginToTable(plugin);
1422 for (const PluginInfo& plugin : p->plugins.clap)
1423 addPluginToTable(plugin);
1425 #ifndef CARLA_FRONTEND_ONLY_EMBEDDABLE_PLUGINS
1426 #ifdef CARLA_OS_MAC
1427 for (const PluginInfo& plugin : p->plugins.au)
1428 addPluginToTable(plugin);
1429 #endif
1431 for (const PluginInfo& plugin : p->plugins.jsfx)
1432 addPluginToTable(plugin);
1434 for (const PluginInfo& plugin : p->plugins.kits)
1435 addPluginToTable(plugin);
1436 #endif
1438 CARLA_SAFE_ASSERT_INT2(p->lastTableWidgetIndex == ui.tableWidget->rowCount(),
1439 p->lastTableWidgetIndex, ui.tableWidget->rowCount());
1441 // ----------------------------------------------------------------------------------------------------------------
1442 // and reenable sorting + filtering
1444 ui.tableWidget->setSortingEnabled(true);
1446 checkFilters();
1447 checkPlugin(ui.tableWidget->currentRow());
1450 void PluginListDialog::loadSettings()
1452 const QSafeSettings settings("falkTX", "CarlaDatabase3");
1454 restoreGeometry(settings.valueByteArray("PluginDatabase/Geometry"));
1455 ui.ch_effects->setChecked(settings.valueBool("PluginDatabase/ShowEffects", true));
1456 ui.ch_instruments->setChecked(settings.valueBool("PluginDatabase/ShowInstruments", true));
1457 ui.ch_midi->setChecked(settings.valueBool("PluginDatabase/ShowMIDI", true));
1458 ui.ch_other->setChecked(settings.valueBool("PluginDatabase/ShowOther", true));
1459 ui.ch_internal->setChecked(settings.valueBool("PluginDatabase/ShowInternal", true));
1460 ui.ch_ladspa->setChecked(settings.valueBool("PluginDatabase/ShowLADSPA", true));
1461 ui.ch_dssi->setChecked(settings.valueBool("PluginDatabase/ShowDSSI", true));
1462 ui.ch_lv2->setChecked(settings.valueBool("PluginDatabase/ShowLV2", true));
1463 ui.ch_vst->setChecked(settings.valueBool("PluginDatabase/ShowVST2", true));
1464 ui.ch_vst3->setChecked(settings.valueBool("PluginDatabase/ShowVST3", true));
1465 ui.ch_clap->setChecked(settings.valueBool("PluginDatabase/ShowCLAP", true));
1466 #ifdef CARLA_OS_MAC
1467 ui.ch_au->setChecked(settings.valueBool("PluginDatabase/ShowAU", true));
1468 #endif
1469 ui.ch_jsfx->setChecked(settings.valueBool("PluginDatabase/ShowJSFX", true));
1470 ui.ch_kits->setChecked(settings.valueBool("PluginDatabase/ShowKits", true));
1471 ui.ch_native->setChecked(settings.valueBool("PluginDatabase/ShowNative", true));
1472 ui.ch_bridged->setChecked(settings.valueBool("PluginDatabase/ShowBridged", true));
1473 ui.ch_bridged_wine->setChecked(settings.valueBool("PluginDatabase/ShowBridgedWine", true));
1474 ui.ch_favorites->setChecked(settings.valueBool("PluginDatabase/ShowFavorites", false));
1475 ui.ch_rtsafe->setChecked(settings.valueBool("PluginDatabase/ShowRtSafe", false));
1476 ui.ch_cv->setChecked(settings.valueBool("PluginDatabase/ShowHasCV", false));
1477 ui.ch_gui->setChecked(settings.valueBool("PluginDatabase/ShowHasGUI", false));
1478 ui.ch_inline_display->setChecked(settings.valueBool("PluginDatabase/ShowHasInlineDisplay", false));
1479 ui.ch_stereo->setChecked(settings.valueBool("PluginDatabase/ShowStereoOnly", false));
1480 ui.lineEdit->setText(settings.valueString("PluginDatabase/SearchText", ""));
1482 const QString categories = settings.valueString("PluginDatabase/ShowCategory", "all");
1483 if (categories == "all" or categories.length() < 2)
1485 ui.ch_cat_all->setChecked(true);
1486 ui.ch_cat_delay->setChecked(false);
1487 ui.ch_cat_distortion->setChecked(false);
1488 ui.ch_cat_dynamics->setChecked(false);
1489 ui.ch_cat_eq->setChecked(false);
1490 ui.ch_cat_filter->setChecked(false);
1491 ui.ch_cat_modulator->setChecked(false);
1492 ui.ch_cat_synth->setChecked(false);
1493 ui.ch_cat_utility->setChecked(false);
1494 ui.ch_cat_other->setChecked(false);
1496 else
1498 ui.ch_cat_all->setChecked(false);
1499 ui.ch_cat_delay->setChecked(categories.contains(":delay:"));
1500 ui.ch_cat_distortion->setChecked(categories.contains(":distortion:"));
1501 ui.ch_cat_dynamics->setChecked(categories.contains(":dynamics:"));
1502 ui.ch_cat_eq->setChecked(categories.contains(":eq:"));
1503 ui.ch_cat_filter->setChecked(categories.contains(":filter:"));
1504 ui.ch_cat_modulator->setChecked(categories.contains(":modulator:"));
1505 ui.ch_cat_synth->setChecked(categories.contains(":synth:"));
1506 ui.ch_cat_utility->setChecked(categories.contains(":utility:"));
1507 ui.ch_cat_other->setChecked(categories.contains(":other:"));
1510 const QByteArray tableGeometry = settings.valueByteArray("PluginDatabase/TableGeometry");
1511 QHeaderView* const horizontalHeader = ui.tableWidget->horizontalHeader();
1512 if (! tableGeometry.isNull())
1514 horizontalHeader->restoreState(tableGeometry);
1516 else
1518 ui.tableWidget->setColumnWidth(TW_NAME, 250);
1519 ui.tableWidget->setColumnWidth(TW_LABEL, 200);
1520 ui.tableWidget->setColumnWidth(TW_MAKER, 150);
1521 ui.tableWidget->sortByColumn(TW_NAME, Qt::AscendingOrder);
1524 horizontalHeader->setSectionResizeMode(TW_FAVORITE, QHeaderView::Fixed);
1525 ui.tableWidget->setColumnWidth(TW_FAVORITE, 24);
1526 ui.tableWidget->setSortingEnabled(true);
1528 p->plugins.favorites = asPluginFavoriteList(settings.valueByteArray("PluginListDialog/Favorites"));
1530 // load entire plugin cache
1531 const QStringList keys = settings.allKeys();
1532 for (const QCarlaString key : keys)
1534 if (!key.startsWith("PluginCache/"))
1535 continue;
1537 const QByteArray data(settings.valueByteArray(key));
1539 if (data.isEmpty())
1540 p->plugins.cache.insert(key.sliced(12), {});
1541 else
1542 p->plugins.cache.insert(key.sliced(12), asPluginInfoList(data));
1545 #ifdef CARLA_FRONTEND_ONLY_EMBEDDABLE_PLUGINS
1546 // these are not visible, force their value
1547 ui.ch_native->setChecked(true);
1548 ui.ch_bridged->setChecked(false);
1549 ui.ch_bridged_wine->setChecked(false);
1550 ui.ch_inline_display->setChecked(false);
1551 #endif
1554 // --------------------------------------------------------------------------------------------------------------------
1555 // private slots
1557 void PluginListDialog::cellClicked(const int row, const int column)
1559 if (column != TW_FAVORITE)
1560 return;
1562 const PluginInfo info = asPluginInfo(ui.tableWidget->item(row, TW_NAME)->data(Qt::UserRole + UR_PLUGIN_INFO));
1563 const PluginFavorite fav = asPluginFavorite(info);
1564 const bool isFavorite = p->plugins.favorites.contains(fav);
1566 if (ui.tableWidget->item(row, TW_FAVORITE)->checkState() == Qt::Checked)
1568 if (!isFavorite)
1569 p->plugins.favorites.append(fav);
1571 else if (isFavorite)
1573 p->plugins.favorites.removeAll(fav);
1576 QSafeSettings settings("falkTX", "CarlaDatabase3");
1577 settings.setValue("PluginListDialog/Favorites", asVariant(p->plugins.favorites));
1580 void PluginListDialog::cellDoubleClicked(int, const int column)
1582 if (column != TW_FAVORITE)
1583 done(QDialog::Accepted);
1586 void PluginListDialog::focusSearchFieldAndSelectAll()
1588 ui.lineEdit->setFocus();
1589 ui.lineEdit->selectAll();
1592 void PluginListDialog::checkFilters()
1594 const QCarlaString text = ui.lineEdit->text().toLower();
1596 const bool hideEffects = !ui.ch_effects->isChecked();
1597 const bool hideInstruments = !ui.ch_instruments->isChecked();
1598 const bool hideMidi = !ui.ch_midi->isChecked();
1599 const bool hideOther = !ui.ch_other->isChecked();
1601 const bool hideInternal = !ui.ch_internal->isChecked();
1602 const bool hideLadspa = !ui.ch_ladspa->isChecked();
1603 const bool hideDSSI = !ui.ch_dssi->isChecked();
1604 const bool hideLV2 = !ui.ch_lv2->isChecked();
1605 const bool hideVST2 = !ui.ch_vst->isChecked();
1606 const bool hideVST3 = !ui.ch_vst3->isChecked();
1607 const bool hideCLAP = !ui.ch_clap->isChecked();
1608 const bool hideAU = !ui.ch_au->isChecked();
1609 const bool hideJSFX = !ui.ch_jsfx->isChecked();
1610 const bool hideKits = !ui.ch_kits->isChecked();
1612 const bool hideNative = !ui.ch_native->isChecked();
1613 const bool hideBridged = !ui.ch_bridged->isChecked();
1614 const bool hideBridgedWine = !ui.ch_bridged_wine->isChecked();
1616 const bool hideNonFavs = ui.ch_favorites->isChecked();
1617 const bool hideNonRtSafe = ui.ch_rtsafe->isChecked();
1618 const bool hideNonCV = ui.ch_cv->isChecked();
1619 const bool hideNonGui = ui.ch_gui->isChecked();
1620 const bool hideNonIDisp = ui.ch_inline_display->isChecked();
1621 const bool hideNonStereo = ui.ch_stereo->isChecked();
1623 #if defined(CARLA_OS_WIN64)
1624 static constexpr const BinaryType nativeBins[2] = { BINARY_WIN32, BINARY_WIN64 };
1625 static constexpr const BinaryType wineBins[2] = { BINARY_NONE, BINARY_NONE };
1626 #elif defined(CARLA_OS_WIN32)
1627 static constexpr const BinaryType nativeBins[2] = { BINARY_WIN32, BINARY_NONE };
1628 static constexpr const BinaryType wineBins[2] = { BINARY_NONE, BINARY_NONE };
1629 #elif defined(CARLA_OS_MAC)
1630 static constexpr const BinaryType nativeBins[2] = { BINARY_POSIX64, BINARY_NONE };
1631 static constexpr const BinaryType wineBins[2] = { BINARY_WIN32, BINARY_WIN64 };
1632 #else
1633 static constexpr const BinaryType nativeBins[2] = { BINARY_POSIX32, BINARY_POSIX64 };
1634 static constexpr const BinaryType wineBins[2] = { BINARY_WIN32, BINARY_WIN64 };
1635 #endif
1637 for (int i=0, c=ui.tableWidget->rowCount(); i<c; ++i)
1639 const PluginInfo info = asPluginInfo(ui.tableWidget->item(i, TW_NAME)->data(Qt::UserRole + UR_PLUGIN_INFO));
1640 const QString ptext = ui.tableWidget->item(i, TW_NAME)->data(Qt::UserRole + UR_SEARCH_TEXT).toString();
1642 const uint16_t aIns = info.audioIns;
1643 const uint16_t aOuts = info.audioOuts;
1644 const uint16_t cvIns = info.cvIns;
1645 const uint16_t cvOuts = info.cvOuts;
1646 const uint16_t mIns = info.midiIns;
1647 const uint16_t mOuts = info.midiOuts;
1648 const uint32_t phints = info.hints;
1649 const uint16_t ptype = info.type;
1650 const QString categ = info.category;
1651 const bool isSynth = phints & PLUGIN_IS_SYNTH;
1652 const bool isEffect = aIns > 0 && aOuts > 0 && !isSynth;
1653 const bool isMidi = aIns == 0 && aOuts == 0 && mIns > 0 && mOuts > 0;
1654 const bool isKit = ptype == PLUGIN_SF2 || ptype == PLUGIN_SFZ;
1655 const bool isOther = !(isEffect || isSynth || isMidi || isKit);
1656 const bool isNative = info.build == BINARY_NATIVE;
1657 const bool isRtSafe = phints & PLUGIN_IS_RTSAFE;
1658 const bool isStereo = (aIns == 2 && aOuts == 2) || (isSynth && aOuts == 2);
1659 const bool hasCV = cvIns + cvOuts > 0;
1660 const bool hasGui = phints & PLUGIN_HAS_CUSTOM_UI;
1661 const bool hasIDisp = phints & PLUGIN_HAS_INLINE_DISPLAY;
1662 const bool isBridged = !isNative && (nativeBins[0] == info.build || nativeBins[1] == info.build);
1663 const bool isBridgedWine = !isNative && (wineBins[0] == info.build || wineBins[1] == info.build);
1665 const auto hasText = [text, ptext]() {
1666 const QStringList textSplit = text.strip().split(' ');
1667 for (const QString& t : textSplit)
1668 if (ptext.contains(t))
1669 return true;
1670 return false;
1673 /**/ if (hideEffects && isEffect)
1674 ui.tableWidget->hideRow(i);
1675 else if (hideInstruments && isSynth)
1676 ui.tableWidget->hideRow(i);
1677 else if (hideMidi && isMidi)
1678 ui.tableWidget->hideRow(i);
1679 else if (hideOther && isOther)
1680 ui.tableWidget->hideRow(i);
1681 else if (hideKits && isKit)
1682 ui.tableWidget->hideRow(i);
1683 else if (hideInternal && ptype == PLUGIN_INTERNAL)
1684 ui.tableWidget->hideRow(i);
1685 else if (hideLadspa && ptype == PLUGIN_LADSPA)
1686 ui.tableWidget->hideRow(i);
1687 else if (hideDSSI && ptype == PLUGIN_DSSI)
1688 ui.tableWidget->hideRow(i);
1689 else if (hideLV2 && ptype == PLUGIN_LV2)
1690 ui.tableWidget->hideRow(i);
1691 else if (hideVST2 && ptype == PLUGIN_VST2)
1692 ui.tableWidget->hideRow(i);
1693 else if (hideVST3 && ptype == PLUGIN_VST3)
1694 ui.tableWidget->hideRow(i);
1695 else if (hideCLAP && ptype == PLUGIN_CLAP)
1696 ui.tableWidget->hideRow(i);
1697 else if (hideAU && ptype == PLUGIN_AU)
1698 ui.tableWidget->hideRow(i);
1699 else if (hideJSFX && ptype == PLUGIN_JSFX)
1700 ui.tableWidget->hideRow(i);
1701 else if (hideNative && isNative)
1702 ui.tableWidget->hideRow(i);
1703 else if (hideBridged && isBridged)
1704 ui.tableWidget->hideRow(i);
1705 else if (hideBridgedWine && isBridgedWine)
1706 ui.tableWidget->hideRow(i);
1707 else if (hideNonRtSafe && not isRtSafe)
1708 ui.tableWidget->hideRow(i);
1709 else if (hideNonCV && not hasCV)
1710 ui.tableWidget->hideRow(i);
1711 else if (hideNonGui && not hasGui)
1712 ui.tableWidget->hideRow(i);
1713 else if (hideNonIDisp && not hasIDisp)
1714 ui.tableWidget->hideRow(i);
1715 else if (hideNonStereo && not isStereo)
1716 ui.tableWidget->hideRow(i);
1717 else if (text.isNotEmpty() && ! hasText())
1718 ui.tableWidget->hideRow(i);
1719 else if (hideNonFavs && !p->plugins.favorites.contains(asPluginFavorite(info)))
1720 ui.tableWidget->hideRow(i);
1721 else if (ui.ch_cat_all->isChecked() or
1722 (ui.ch_cat_delay->isChecked() && categ == "delay") or
1723 (ui.ch_cat_distortion->isChecked() && categ == "distortion") or
1724 (ui.ch_cat_dynamics->isChecked() && categ == "dynamics") or
1725 (ui.ch_cat_eq->isChecked() && categ == "eq") or
1726 (ui.ch_cat_filter->isChecked() && categ == "filter") or
1727 (ui.ch_cat_modulator->isChecked() && categ == "modulator") or
1728 (ui.ch_cat_synth->isChecked() && categ == "synth") or
1729 (ui.ch_cat_utility->isChecked() && categ == "utility") or
1730 (ui.ch_cat_other->isChecked() && categ == "other"))
1731 ui.tableWidget->showRow(i);
1732 else
1733 ui.tableWidget->hideRow(i);
1737 void PluginListDialog::checkFiltersCategoryAll(const bool clicked)
1739 const bool notClicked = !clicked;
1740 ui.ch_cat_delay->setChecked(notClicked);
1741 ui.ch_cat_distortion->setChecked(notClicked);
1742 ui.ch_cat_dynamics->setChecked(notClicked);
1743 ui.ch_cat_eq->setChecked(notClicked);
1744 ui.ch_cat_filter->setChecked(notClicked);
1745 ui.ch_cat_modulator->setChecked(notClicked);
1746 ui.ch_cat_synth->setChecked(notClicked);
1747 ui.ch_cat_utility->setChecked(notClicked);
1748 ui.ch_cat_other->setChecked(notClicked);
1749 checkFilters();
1752 void PluginListDialog::checkFiltersCategorySpecific(bool clicked)
1754 if (clicked)
1756 ui.ch_cat_all->setChecked(false);
1758 else if (! (ui.ch_cat_delay->isChecked() ||
1759 ui.ch_cat_distortion->isChecked() ||
1760 ui.ch_cat_dynamics->isChecked() ||
1761 ui.ch_cat_eq->isChecked() ||
1762 ui.ch_cat_filter->isChecked() ||
1763 ui.ch_cat_modulator->isChecked() ||
1764 ui.ch_cat_synth->isChecked() ||
1765 ui.ch_cat_utility->isChecked() ||
1766 ui.ch_cat_other->isChecked()))
1768 ui.ch_cat_all->setChecked(true);
1770 checkFilters();
1773 void PluginListDialog::clearFilters()
1775 auto setCheckedWithoutSignaling = [](QCheckBox* const w, const bool checked)
1777 w->blockSignals(true);
1778 w->setChecked(checked);
1779 w->blockSignals(false);
1782 setCheckedWithoutSignaling(ui.ch_internal, true);
1783 setCheckedWithoutSignaling(ui.ch_ladspa, true);
1784 setCheckedWithoutSignaling(ui.ch_dssi, true);
1785 setCheckedWithoutSignaling(ui.ch_lv2, true);
1786 setCheckedWithoutSignaling(ui.ch_vst, true);
1787 setCheckedWithoutSignaling(ui.ch_vst3, true);
1788 setCheckedWithoutSignaling(ui.ch_clap, true);
1789 setCheckedWithoutSignaling(ui.ch_jsfx, true);
1790 setCheckedWithoutSignaling(ui.ch_kits, true);
1792 setCheckedWithoutSignaling(ui.ch_instruments, true);
1793 setCheckedWithoutSignaling(ui.ch_effects, true);
1794 setCheckedWithoutSignaling(ui.ch_midi, true);
1795 setCheckedWithoutSignaling(ui.ch_other, true);
1797 setCheckedWithoutSignaling(ui.ch_native, true);
1798 setCheckedWithoutSignaling(ui.ch_bridged, false);
1799 setCheckedWithoutSignaling(ui.ch_bridged_wine, false);
1801 setCheckedWithoutSignaling(ui.ch_favorites, false);
1802 setCheckedWithoutSignaling(ui.ch_rtsafe, false);
1803 setCheckedWithoutSignaling(ui.ch_stereo, false);
1804 setCheckedWithoutSignaling(ui.ch_cv, false);
1805 setCheckedWithoutSignaling(ui.ch_gui, false);
1806 setCheckedWithoutSignaling(ui.ch_inline_display, false);
1808 if (ui.ch_au->isEnabled())
1809 setCheckedWithoutSignaling(ui.ch_au, true);
1811 setCheckedWithoutSignaling(ui.ch_cat_all, true);
1812 setCheckedWithoutSignaling(ui.ch_cat_delay, false);
1813 setCheckedWithoutSignaling(ui.ch_cat_distortion, false);
1814 setCheckedWithoutSignaling(ui.ch_cat_dynamics, false);
1815 setCheckedWithoutSignaling(ui.ch_cat_eq, false);
1816 setCheckedWithoutSignaling(ui.ch_cat_filter, false);
1817 setCheckedWithoutSignaling(ui.ch_cat_modulator, false);
1818 setCheckedWithoutSignaling(ui.ch_cat_synth, false);
1819 setCheckedWithoutSignaling(ui.ch_cat_utility, false);
1820 setCheckedWithoutSignaling(ui.ch_cat_other, false);
1822 ui.lineEdit->blockSignals(true);
1823 ui.lineEdit->clear();
1824 ui.lineEdit->blockSignals(false);
1826 checkFilters();
1829 // --------------------------------------------------------------------------------------------------------------------
1831 void PluginListDialog::checkPlugin(const int row)
1833 if (row >= 0)
1835 ui.b_add->setEnabled(true);
1837 const PluginInfo info = asPluginInfo(ui.tableWidget->item(row, TW_NAME)->data(Qt::UserRole + UR_PLUGIN_INFO));
1839 const bool isSynth = info.hints & PLUGIN_IS_SYNTH;
1840 const bool isEffect = info.audioIns > 0 && info.audioOuts > 0 && !isSynth;
1841 const bool isMidi = info.audioIns == 0 && info.audioOuts == 0 && info.midiIns > 0 && info.midiOuts > 0;
1843 QString ptype;
1844 /**/ if (isSynth)
1845 ptype = "Instrument";
1846 else if (isEffect)
1847 ptype = "Effect";
1848 else if (isMidi)
1849 ptype = "MIDI Plugin";
1850 else
1851 ptype = "Other";
1853 QString parch;
1854 /**/ if (info.build == BINARY_NATIVE)
1855 parch = tr("Native");
1856 else if (info.build == BINARY_POSIX32)
1857 parch = "posix32";
1858 else if (info.build == BINARY_POSIX64)
1859 parch = "posix64";
1860 else if (info.build == BINARY_WIN32)
1861 parch = "win32";
1862 else if (info.build == BINARY_WIN64)
1863 parch = "win64";
1864 else if (info.build == BINARY_OTHER)
1865 parch = tr("Other");
1866 else if (info.build == BINARY_WIN32)
1867 parch = tr("Unknown");
1869 ui.l_format->setText(getPluginTypeAsString(static_cast<PluginType>(info.type)));
1871 ui.l_type->setText(ptype);
1872 ui.l_arch->setText(parch);
1873 ui.l_id->setText(QString::number(info.uniqueId));
1874 ui.l_ains->setText(QString::number(info.audioIns));
1875 ui.l_aouts->setText(QString::number(info.audioOuts));
1876 ui.l_cvins->setText(QString::number(info.cvIns));
1877 ui.l_cvouts->setText(QString::number(info.cvOuts));
1878 ui.l_mins->setText(QString::number(info.midiIns));
1879 ui.l_mouts->setText(QString::number(info.midiOuts));
1880 ui.l_pins->setText(QString::number(info.parameterIns));
1881 ui.l_pouts->setText(QString::number(info.parameterOuts));
1882 ui.l_gui->setText(info.hints & PLUGIN_HAS_CUSTOM_UI ? tr("Yes") : tr("No"));
1883 ui.l_idisp->setText(info.hints & PLUGIN_HAS_INLINE_DISPLAY ? tr("Yes") : tr("No"));
1884 ui.l_bridged->setText(info.hints & PLUGIN_IS_BRIDGE ? tr("Yes") : tr("No"));
1885 ui.l_synth->setText(isSynth ? tr("Yes") : tr("No"));
1887 else
1889 ui.b_add->setEnabled(false);
1890 ui.l_format->setText("---");
1891 ui.l_type->setText("---");
1892 ui.l_arch->setText("---");
1893 ui.l_id->setText("---");
1894 ui.l_ains->setText("---");
1895 ui.l_aouts->setText("---");
1896 ui.l_cvins->setText("---");
1897 ui.l_cvouts->setText("---");
1898 ui.l_mins->setText("---");
1899 ui.l_mouts->setText("---");
1900 ui.l_pins->setText("---");
1901 ui.l_pouts->setText("---");
1902 ui.l_gui->setText("---");
1903 ui.l_idisp->setText("---");
1904 ui.l_bridged->setText("---");
1905 ui.l_synth->setText("---");
1909 // --------------------------------------------------------------------------------------------------------------------
1911 void PluginListDialog::refreshPlugins()
1913 refreshPluginsStop();
1915 p->discovery.dialog = new PluginRefreshDialog(this);
1917 QObject::connect(p->discovery.dialog->b_start, &QPushButton::clicked,
1918 this, &PluginListDialog::refreshPluginsStart);
1919 QObject::connect(p->discovery.dialog->b_skip, &QPushButton::clicked,
1920 this, &PluginListDialog::refreshPluginsSkip);
1921 QObject::connect(p->discovery.dialog, &QDialog::finished,
1922 this, &PluginListDialog::refreshPluginsStop);
1924 p->discovery.dialog->exec();
1927 void PluginListDialog::refreshPluginsStart()
1929 // remove old plugins
1930 #ifndef CARLA_FRONTEND_ONLY_EMBEDDABLE_PLUGINS
1931 p->plugins.internal.clear();
1932 p->plugins.ladspa.clear();
1933 p->plugins.dssi.clear();
1934 #endif
1935 p->plugins.lv2.clear();
1936 p->plugins.vst2.clear();
1937 p->plugins.vst3.clear();
1938 p->plugins.clap.clear();
1939 #ifndef CARLA_FRONTEND_ONLY_EMBEDDABLE_PLUGINS
1940 #ifdef CARLA_OS_MAC
1941 p->plugins.au.clear();
1942 #endif
1943 p->plugins.jsfx.clear();
1944 p->plugins.kits.clear();
1945 #endif
1946 p->discovery.dialog->b_start->setEnabled(false);
1947 p->discovery.dialog->b_skip->setEnabled(true);
1948 p->discovery.ignoreCache = p->discovery.dialog->ch_all->isChecked();
1949 p->discovery.checkInvalid =
1950 p->discovery.dialog->ch_invalid->isChecked();
1951 if (p->discovery.ignoreCache)
1952 p->plugins.cache.clear();
1954 // start discovery again
1955 p->discovery.restart();
1957 if (p->timerId == 0)
1958 p->timerId = startTimer(0);
1961 void PluginListDialog::refreshPluginsStop()
1963 // stop previous discovery if still running
1964 if (p->discovery.handle != nullptr)
1966 carla_plugin_discovery_stop(p->discovery.handle);
1967 p->discovery.handle = nullptr;
1970 if (p->discovery.dialog)
1972 p->discovery.dialog->close();
1973 p->discovery.dialog = nullptr;
1976 if (p->timerId != 0)
1978 killTimer(p->timerId);
1979 p->timerId = 0;
1980 addPluginsToTable();
1984 void PluginListDialog::refreshPluginsSkip()
1986 if (p->discovery.handle != nullptr)
1987 carla_plugin_discovery_skip(p->discovery.handle);
1990 // --------------------------------------------------------------------------------------------------------------------
1992 void PluginListDialog::saveSettings()
1994 QSafeSettings settings("falkTX", "CarlaDatabase3");
1996 settings.setValue("PluginDatabase/Geometry", saveGeometry());
1997 settings.setValue("PluginDatabase/TableGeometry", ui.tableWidget->horizontalHeader()->saveState());
1998 settings.setValue("PluginDatabase/ShowEffects", ui.ch_effects->isChecked());
1999 settings.setValue("PluginDatabase/ShowInstruments", ui.ch_instruments->isChecked());
2000 settings.setValue("PluginDatabase/ShowMIDI", ui.ch_midi->isChecked());
2001 settings.setValue("PluginDatabase/ShowOther", ui.ch_other->isChecked());
2002 settings.setValue("PluginDatabase/ShowInternal", ui.ch_internal->isChecked());
2003 settings.setValue("PluginDatabase/ShowLADSPA", ui.ch_ladspa->isChecked());
2004 settings.setValue("PluginDatabase/ShowDSSI", ui.ch_dssi->isChecked());
2005 settings.setValue("PluginDatabase/ShowLV2", ui.ch_lv2->isChecked());
2006 settings.setValue("PluginDatabase/ShowVST2", ui.ch_vst->isChecked());
2007 settings.setValue("PluginDatabase/ShowVST3", ui.ch_vst3->isChecked());
2008 settings.setValue("PluginDatabase/ShowCLAP", ui.ch_clap->isChecked());
2009 settings.setValue("PluginDatabase/ShowAU", ui.ch_au->isChecked());
2010 settings.setValue("PluginDatabase/ShowJSFX", ui.ch_jsfx->isChecked());
2011 settings.setValue("PluginDatabase/ShowKits", ui.ch_kits->isChecked());
2012 settings.setValue("PluginDatabase/ShowNative", ui.ch_native->isChecked());
2013 settings.setValue("PluginDatabase/ShowBridged", ui.ch_bridged->isChecked());
2014 settings.setValue("PluginDatabase/ShowBridgedWine", ui.ch_bridged_wine->isChecked());
2015 settings.setValue("PluginDatabase/ShowFavorites", ui.ch_favorites->isChecked());
2016 settings.setValue("PluginDatabase/ShowRtSafe", ui.ch_rtsafe->isChecked());
2017 settings.setValue("PluginDatabase/ShowHasCV", ui.ch_cv->isChecked());
2018 settings.setValue("PluginDatabase/ShowHasGUI", ui.ch_gui->isChecked());
2019 settings.setValue("PluginDatabase/ShowHasInlineDisplay", ui.ch_inline_display->isChecked());
2020 settings.setValue("PluginDatabase/ShowStereoOnly", ui.ch_stereo->isChecked());
2021 settings.setValue("PluginDatabase/SearchText", ui.lineEdit->text());
2023 if (ui.ch_cat_all->isChecked())
2025 settings.setValue("PluginDatabase/ShowCategory", "all");
2027 else
2029 QCarlaString categories;
2030 if (ui.ch_cat_delay->isChecked())
2031 categories += ":delay";
2032 if (ui.ch_cat_distortion->isChecked())
2033 categories += ":distortion";
2034 if (ui.ch_cat_dynamics->isChecked())
2035 categories += ":dynamics";
2036 if (ui.ch_cat_eq->isChecked())
2037 categories += ":eq";
2038 if (ui.ch_cat_filter->isChecked())
2039 categories += ":filter";
2040 if (ui.ch_cat_modulator->isChecked())
2041 categories += ":modulator";
2042 if (ui.ch_cat_synth->isChecked())
2043 categories += ":synth";
2044 if (ui.ch_cat_utility->isChecked())
2045 categories += ":utility";
2046 if (ui.ch_cat_other->isChecked())
2047 categories += ":other";
2048 if (categories.isNotEmpty())
2049 categories += ":";
2050 settings.setValue("PluginDatabase/ShowCategory", categories);
2053 settings.setValue("PluginListDialog/Favorites", asVariant(p->plugins.favorites));
2056 // --------------------------------------------------------------------------------------------------------------------
2058 PluginListDialog*
2059 carla_frontend_createPluginListDialog(void* const parent, const HostSettings* const hostSettings)
2061 return new PluginListDialog(reinterpret_cast<QWidget*>(parent), hostSettings);
2064 void
2065 carla_frontend_destroyPluginListDialog(PluginListDialog* const dialog)
2067 dialog->close();
2068 delete dialog;
2071 void
2072 carla_frontend_setPluginListDialogPath(PluginListDialog* const dialog, const int ptype, const char* const path)
2074 dialog->setPluginPath(static_cast<PluginType>(ptype), path);
2077 const PluginListDialogResults*
2078 carla_frontend_execPluginListDialog(PluginListDialog* const dialog)
2080 if (dialog->exec())
2082 static PluginListDialogResults ret;
2083 static CarlaString category;
2084 static CarlaString filename;
2085 static CarlaString name;
2086 static CarlaString label;
2087 static CarlaString maker;
2089 const PluginInfo& plugin(dialog->getSelectedPluginInfo());
2091 category = plugin.category.toUtf8();
2092 filename = plugin.filename.toUtf8();
2093 name = plugin.name.toUtf8();
2094 label = plugin.label.toUtf8();
2095 maker = plugin.maker.toUtf8();
2097 ret.build = plugin.build;
2098 ret.type = plugin.type;
2099 ret.hints = plugin.hints;
2100 ret.category = category;
2101 ret.filename = filename;
2102 ret.name = name;
2103 ret.label = label;
2104 ret.maker = maker;
2105 ret.uniqueId = plugin.uniqueId;
2106 ret.audioIns = plugin.audioIns;
2107 ret.audioOuts = plugin.audioOuts;
2108 ret.cvIns = plugin.cvIns;
2109 ret.cvOuts = plugin.cvOuts;
2110 ret.midiIns = plugin.midiIns;
2111 ret.midiOuts = plugin.midiOuts;
2112 ret.parameterIns = plugin.parameterIns;
2113 ret.parameterOuts = plugin.parameterOuts;
2115 return &ret;
2118 return nullptr;
2121 // --------------------------------------------------------------------------------------------------------------------
2123 // const PluginListDialogResults*
2124 // carla_frontend_createAndExecPluginListDialog(void* const parent, const HostSettings* const hostSettings)
2125 // {
2126 // PluginListDialog gui(reinterpret_cast<QWidget*>(parent), hostSettings);
2128 // return carla_frontend_execPluginListDialog(&gui);
2129 // }
2131 // --------------------------------------------------------------------------------------------------------------------