diff --git a/gtk2_ardour/ardour.menus.in b/gtk2_ardour/ardour.menus.in
index fc6edb58c2..f2ad55c1da 100644
--- a/gtk2_ardour/ardour.menus.in
+++ b/gtk2_ardour/ardour.menus.in
@@ -621,6 +621,7 @@
#if 0
#endif
+
#if 0
diff --git a/gtk2_ardour/ardour_ui.cc b/gtk2_ardour/ardour_ui.cc
index 8e8bedcaa8..68a7a631f1 100644
--- a/gtk2_ardour/ardour_ui.cc
+++ b/gtk2_ardour/ardour_ui.cc
@@ -183,6 +183,7 @@
#include "opts.h"
#include "pingback.h"
#include "plugin_dspload_window.h"
+#include "plugin_manager_ui.h"
#include "processor_box.h"
#include "public_editor.h"
#include "rc_option_editor.h"
@@ -335,6 +336,7 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[], const char* localedir)
, export_video_dialog (X_("video-export"), _("Video Export Dialog"))
, lua_script_window (X_("script-manager"), _("Script Manager"))
, idleometer (X_("idle-o-meter"), _("Idle'o'Meter"))
+ , plugin_manager_ui (X_("plugin-manager"), _("Plugin Manager"))
, plugin_dsp_load_window (X_("plugin-dsp-load"), _("Plugin DSP Load"))
, dsp_statistics_window (X_("dsp-statistics"), _("Performance Meters"))
, transport_masters_window (X_("transport-masters"), _("Transport Masters"))
@@ -505,6 +507,7 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[], const char* localedir)
export_video_dialog.set_state (*ui_xml, 0);
lua_script_window.set_state (*ui_xml, 0);
idleometer.set_state (*ui_xml, 0);
+ plugin_manager_ui.set_state (*ui_xml, 0);
plugin_dsp_load_window.set_state (*ui_xml, 0);
dsp_statistics_window.set_state (*ui_xml, 0);
transport_masters_window.set_state (*ui_xml, 0);
@@ -533,6 +536,7 @@ ARDOUR_UI::ARDOUR_UI (int *argcp, char **argvp[], const char* localedir)
WM::Manager::instance().register_window (&audio_port_matrix);
WM::Manager::instance().register_window (&midi_port_matrix);
WM::Manager::instance().register_window (&idleometer);
+ WM::Manager::instance().register_window (&plugin_manager_ui);
WM::Manager::instance().register_window (&plugin_dsp_load_window);
WM::Manager::instance().register_window (&dsp_statistics_window);
WM::Manager::instance().register_window (&transport_masters_window);
diff --git a/gtk2_ardour/ardour_ui.h b/gtk2_ardour/ardour_ui.h
index fd5d0c8f57..7e6111ce0e 100644
--- a/gtk2_ardour/ardour_ui.h
+++ b/gtk2_ardour/ardour_ui.h
@@ -113,6 +113,7 @@
#include "location_ui.h"
#include "lua_script_manager.h"
#include "plugin_dspload_window.h"
+#include "plugin_manager_ui.h"
#include "rc_option_editor.h"
#include "route_dialogs.h"
#include "route_params_ui.h"
@@ -139,6 +140,7 @@ class SpeakerDialog;
class GlobalPortMatrixWindow;
class IdleOMeter;
class PluginDSPLoadWindow;
+class PluginManagerUI;
class DspStatisticsWindow;
class TransportMastersWindow;
class VirtualKeyboardWindow;
@@ -256,6 +258,7 @@ public:
void toggle_editing_space();
void toggle_mixer_space();
void toggle_keep_tearoffs();
+ void show_plugin_manager();
void reset_focus (Gtk::Widget*);
@@ -725,6 +728,7 @@ private:
WM::Proxy export_video_dialog;
WM::Proxy lua_script_window;
WM::Proxy idleometer;
+ WM::Proxy plugin_manager_ui;
WM::Proxy plugin_dsp_load_window;
WM::Proxy dsp_statistics_window;
WM::Proxy transport_masters_window;
diff --git a/gtk2_ardour/ardour_ui_dialogs.cc b/gtk2_ardour/ardour_ui_dialogs.cc
index f865086fba..f7a61c0891 100644
--- a/gtk2_ardour/ardour_ui_dialogs.cc
+++ b/gtk2_ardour/ardour_ui_dialogs.cc
@@ -71,6 +71,7 @@
#include "mini_timeline.h"
#include "mixer_ui.h"
#include "plugin_dspload_window.h"
+#include "plugin_manager_ui.h"
#include "public_editor.h"
#include "processor_box.h"
#include "rc_option_editor.h"
@@ -161,6 +162,7 @@ ARDOUR_UI::set_session (Session *s)
big_clock->set_session (s);
video_timeline->set_session (s);
lua_script_window->set_session (s);
+ plugin_manager_ui->set_session (s);
plugin_dsp_load_window->set_session (s);
dsp_statistics_window->set_session (s);
transport_masters_window->set_session (s);
@@ -1055,6 +1057,13 @@ ARDOUR_UI::toggle_mixer_space()
}
}
+void
+ARDOUR_UI::show_plugin_manager ()
+{
+ Glib::RefPtr tact = ActionManager::get_toggle_action ("Window", "toggle-plugin-manager");
+ tact->set_active();
+}
+
bool
ARDOUR_UI::timecode_button_press (GdkEventButton* ev)
{
diff --git a/gtk2_ardour/plugin_manager_ui.cc b/gtk2_ardour/plugin_manager_ui.cc
new file mode 100644
index 0000000000..e4b171fceb
--- /dev/null
+++ b/gtk2_ardour/plugin_manager_ui.cc
@@ -0,0 +1,271 @@
+/*
+ * Copyright (C) 2021 Robin Gareus
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifdef WAF_BUILD
+#include "gtk2ardour-config.h"
+#endif
+
+#include "ardour/types_convert.h"
+
+#include "gtkmm2ext/gui_thread.h"
+
+#include "plugin_manager_ui.h"
+
+#include "pbd/i18n.h"
+
+using namespace ARDOUR;
+
+PluginManagerUI::PluginManagerUI ()
+ : ArdourWindow (_("Plugin Manager"))
+ , _btn_rescan_all (_("Re-scan Faulty"))
+ , _btn_rescan_sel (_("Re-scan Selected"))
+ , _btn_clear (_("Clear Stale Scan Log"))
+{
+ //plugin_model = Gtk::TreeStore::create (plugin_columns);
+ plugin_model = Gtk::ListStore::create (plugin_columns);
+
+ plugin_display.append_column (_("Status"), plugin_columns.status);
+ plugin_display.append_column (_("BL"), plugin_columns.blacklisted);
+ plugin_display.append_column (_("Name"), plugin_columns.name);
+ plugin_display.append_column (_("Creator"), plugin_columns.creator);
+ plugin_display.append_column (_("Type"), plugin_columns.type);
+ plugin_display.append_column (_("Path"), plugin_columns.path);
+
+ plugin_display.set_model (plugin_model);
+ plugin_display.set_headers_visible (true);
+ plugin_display.set_headers_clickable (true);
+ plugin_display.set_reorderable (false);
+ plugin_display.set_rules_hint (true);
+
+ for (int i = 0; i < 5; ++i) {
+ Gtk::TreeView::Column* column = plugin_display.get_column(i);
+ if (column) {
+ column->set_sort_column(i);
+ }
+ }
+
+ plugin_model->set_sort_column (plugin_columns.name.index(), Gtk::SORT_ASCENDING);
+ plugin_display.set_name("PluginSelectorDisplay");
+
+ plugin_display.get_selection()->signal_changed().connect (sigc::mem_fun(*this, &PluginManagerUI::selection_changed));
+#if 0
+ plugin_display.get_selection()->set_mode (SELECTION_SINGLE);
+ plugin_display.signal_row_activated().connect_notify (sigc::mem_fun(*this, &PluginManagerUI::row_activated));
+#endif
+
+ _scroller.add (plugin_display);
+ _scroller.set_policy (Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
+
+ _log.set_editable (false);
+ _log.set_wrap_mode (Gtk::WRAP_WORD);
+
+ _log_scroller.set_shadow_type(Gtk::SHADOW_NONE);
+ _log_scroller.set_border_width(0);
+ _log_scroller.add (_log);
+ _log_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC);
+
+ _pane.add (_scroller);
+ _pane.add (_log_scroller);
+ _pane.set_divider (0, .85);
+
+ Gtk::Label* lbl = new Gtk::Label (""); // spacer
+
+ /* top level packing */
+ _top.attach (*lbl, 0, 1, 0, 1, Gtk::SHRINK, Gtk::EXPAND | Gtk::FILL, 4, 0);
+ _top.attach (_btn_clear, 0, 1, 1, 2, Gtk::FILL | Gtk::SHRINK, Gtk::SHRINK, 4, 4);
+ _top.attach (_btn_rescan_sel, 0, 1, 2, 3, Gtk::FILL | Gtk::SHRINK, Gtk::SHRINK, 4, 4);
+ _top.attach (_btn_rescan_all, 0, 1, 3, 4, Gtk::FILL | Gtk::SHRINK, Gtk::SHRINK, 4, 4);
+ _top.attach (_pane, 1, 2, 0, 4, Gtk::EXPAND | Gtk::FILL, Gtk::EXPAND | Gtk::FILL, 4, 0);
+
+ add (_top);
+
+ _log.set_size_request (400, -1);
+ set_size_request (-1, 600);
+
+ /* connect to signals */
+
+ PluginManager::instance ().PluginListChanged.connect (_manager_connection, invalidator (*this), boost::bind (&PluginManagerUI::refill, this), gui_context());
+ _btn_rescan_all.signal_clicked.connect (sigc::mem_fun (*this, &PluginManagerUI::rescan_all));
+ _btn_rescan_sel.signal_clicked.connect (sigc::mem_fun (*this, &PluginManagerUI::rescan_selected));
+ _btn_clear.signal_clicked.connect (sigc::mem_fun (*this, &PluginManagerUI::clear_log));
+}
+
+PluginManagerUI::~PluginManagerUI ()
+{
+}
+
+void
+PluginManagerUI::on_show ()
+{
+ refill (); // XXX -> only once in c'tor?
+ ArdourWindow::on_show ();
+}
+
+static std::string
+status_text (PluginScanLogEntry const& psle)
+{
+ if (!psle.recent ()) {
+ return "Stale";
+ }
+
+ PluginScanLogEntry::PluginScanResult sr = psle.result ();
+ if (sr == PluginScanLogEntry::OK) {
+ return "OK";
+ }
+ // TODO pick most relevant, show others on tooltip only
+ std::string rv;
+ if ((int)sr & PluginScanLogEntry::New) {
+ rv += "New ";
+ }
+ if ((int)sr & PluginScanLogEntry::Updated) {
+ rv += "Updated ";
+ }
+ if ((int)sr & PluginScanLogEntry::Error) {
+ rv += "Error ";
+ }
+ if ((int)sr & PluginScanLogEntry::Incompatible) {
+ rv += "Incompatible ";
+ }
+ return rv;
+}
+
+static bool
+is_blacklisted (PluginScanLogEntry const& psle)
+{
+ return (int) psle.result () & PluginScanLogEntry::Blacklisted;
+}
+
+static std::string
+plugin_type (const PluginType t)
+{
+ /* see also PluginManager::to_generic_vst */
+ switch (t) {
+ case Windows_VST:
+ case LXVST:
+ case MacVST:
+ return "VST2.x";
+ default:
+ return enum_2_string (t);
+ }
+}
+
+void
+PluginManagerUI::refill ()
+{
+ std::vector > psl;
+ PluginManager& mgr (PluginManager::instance ());
+ mgr.scan_log (psl);
+
+ plugin_display.set_model (Glib::RefPtr(0));
+
+ int sort_col;
+ Gtk::SortType sort_type;
+ bool sorted = plugin_model->get_sort_column_id (sort_col, sort_type);
+ plugin_model->set_sort_column (-2, Gtk::SORT_ASCENDING);
+
+ bool have_err = false;
+ bool have_stale = false;
+
+ plugin_model->clear ();
+
+ for (std::vector >::const_iterator i = psl.begin(); i != psl.end(); ++i) {
+ PluginInfoList const& plugs = (*i)->nfo ();
+
+ if (!(*i)->recent ()) {
+ have_stale = true;
+ } else if ((*i)->result () == PluginScanLogEntry::Blacklisted) {
+ // OK, but manually blacklisted
+ } else if ((*i)->result () != PluginScanLogEntry::OK) {
+ have_err = true;
+ }
+
+ // TODO show "hidden" status
+ // PluginManager::PluginStatusType status = manager.get_status (*i);
+ if (plugs.size () == 0) {
+ Gtk::TreeModel::Row newrow = *(plugin_model->append());
+ newrow[plugin_columns.path] = (*i)->path ();
+ newrow[plugin_columns.type] = plugin_type ((*i)->type ());
+ newrow[plugin_columns.name] = "-";
+ newrow[plugin_columns.creator] = "-";
+ newrow[plugin_columns.status] = status_text (**i); // XXX
+ newrow[plugin_columns.blacklisted] = is_blacklisted (**i);
+ newrow[plugin_columns.psle] = *i;
+ } else if (plugs.size () == 1) {
+ Gtk::TreeModel::Row newrow = *(plugin_model->append());
+ newrow[plugin_columns.path] = (*i)->path ();
+ newrow[plugin_columns.type] = plugin_type ((*i)->type ());
+ newrow[plugin_columns.name] = plugs.front()->name;
+ newrow[plugin_columns.creator] = plugs.front()->creator;
+ newrow[plugin_columns.status] = status_text (**i);
+ newrow[plugin_columns.blacklisted] = is_blacklisted (**i);
+ newrow[plugin_columns.psle] = *i;
+ } else {
+ for (PluginInfoList::const_iterator j = plugs.begin(); j != plugs.end(); ++j) {
+ Gtk::TreeModel::Row newrow = *(plugin_model->append());
+ newrow[plugin_columns.path] = (*i)->path ();
+ newrow[plugin_columns.type] = plugin_type ((*i)->type ());
+ newrow[plugin_columns.name] = (*j)->name;
+ newrow[plugin_columns.creator] = (*j)->creator;
+ newrow[plugin_columns.status] = status_text (**i);
+ newrow[plugin_columns.blacklisted] = is_blacklisted (**i);
+ newrow[plugin_columns.psle] = *i;
+ }
+ }
+ }
+ plugin_display.set_model (plugin_model);
+ if (sorted) {
+ plugin_model->set_sort_column (sort_col, sort_type);
+ }
+
+ _btn_clear.set_sensitive (have_stale);
+ _btn_rescan_all.set_sensitive (have_err);
+}
+
+void
+PluginManagerUI::selection_changed ()
+{
+ if (plugin_display.get_selection()->count_selected_rows() != 1) {
+ _log.get_buffer()->set_text ("-");
+ return;
+ }
+ Gtk::TreeIter iter = plugin_display.get_selection ()->get_selected ();
+ boost::shared_ptr const& psle ((*iter)[plugin_columns.psle]);
+ _log.get_buffer()->set_text (psle->log ());
+
+ PluginScanLogEntry::PluginScanResult sr = psle->result ();
+ if (sr == PluginScanLogEntry::OK) {
+ _btn_rescan_sel.set_sensitive (false);
+ } else {
+ _btn_rescan_sel.set_sensitive (true);
+ }
+}
+
+void
+PluginManagerUI::rescan_all ()
+{
+}
+
+void
+PluginManagerUI::rescan_selected ()
+{
+}
+
+void
+PluginManagerUI::clear_log ()
+{
+}
diff --git a/gtk2_ardour/plugin_manager_ui.h b/gtk2_ardour/plugin_manager_ui.h
new file mode 100644
index 0000000000..82fc386cbb
--- /dev/null
+++ b/gtk2_ardour/plugin_manager_ui.h
@@ -0,0 +1,90 @@
+/*
+ * Copyright (C) 2021 Robin Gareus
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+ */
+
+#ifndef _gtkardour_plugin_manager_h_
+#define _gtkardour_plugin_manager_h_
+
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+#include
+
+#include "widgets/ardour_button.h"
+#include "widgets/pane.h"
+
+#include "ardour/plugin_manager.h"
+
+#include "ardour_window.h"
+
+class PluginManagerUI : public ArdourWindow
+{
+public:
+ PluginManagerUI ();
+ ~PluginManagerUI ();
+
+private:
+ void refill ();
+ void on_show ();
+ void selection_changed ();
+
+ void rescan_all ();
+ void rescan_selected ();
+ void clear_log ();
+
+ struct PluginColumns : public Gtk::TreeModel::ColumnRecord {
+ PluginColumns () {
+ add (status);
+ add (blacklisted);
+ add (name);
+ add (creator);
+ add (type);
+ add (path);
+ add (psle);
+ }
+ Gtk::TreeModelColumn status;
+ Gtk::TreeModelColumn blacklisted;
+ Gtk::TreeModelColumn name;
+ Gtk::TreeModelColumn type;
+ Gtk::TreeModelColumn creator;
+ Gtk::TreeModelColumn path;
+ Gtk::TreeModelColumn > psle;
+ };
+
+ PluginColumns plugin_columns;
+ Glib::RefPtr plugin_model;
+ Gtk::TreeView plugin_display;
+ Gtk::ScrolledWindow _scroller;
+ Gtk::TextView _log;
+ Gtk::ScrolledWindow _log_scroller;
+ ArdourWidgets::VPane _pane;
+ ArdourWidgets::ArdourButton _btn_rescan_all;
+ ArdourWidgets::ArdourButton _btn_rescan_sel;
+ ArdourWidgets::ArdourButton _btn_clear;
+
+ Gtk::Table _top;
+
+ PBD::ScopedConnection _manager_connection;
+
+};
+
+#endif // _gtkardour_plugin_manager_h_
diff --git a/gtk2_ardour/plugin_selector.cc b/gtk2_ardour/plugin_selector.cc
index 7577454682..f0903d6b97 100644
--- a/gtk2_ardour/plugin_selector.cc
+++ b/gtk2_ardour/plugin_selector.cc
@@ -54,6 +54,7 @@
#include "ardour_message.h"
#include "plugin_scan_dialog.h"
#include "plugin_selector.h"
+#include "ardour_ui.h"
#include "plugin_utils.h"
#include "gui_thread.h"
#include "ui_config.h"
@@ -1252,6 +1253,7 @@ PluginSelector::show_manager ()
if (scan_now) {
PluginScanDialog psd (false, true);
psd.start ();
+ ARDOUR_UI::instance()->show_plugin_manager ();
}
show_all();
diff --git a/gtk2_ardour/wscript b/gtk2_ardour/wscript
index f5d053385f..5c6e794b9a 100644
--- a/gtk2_ardour/wscript
+++ b/gtk2_ardour/wscript
@@ -205,6 +205,7 @@ gtk2_ardour_sources = [
'playlist_selector.cc',
'plugin_display.cc',
'plugin_eq_gui.cc',
+ 'plugin_manager_ui.cc',
'plugin_pin_dialog.cc',
'plugin_presets_ui.cc',
'plugin_scan_dialog.cc',