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',