diff --git a/gtk2_ardour/plugin_manager_ui.cc b/gtk2_ardour/plugin_manager_ui.cc index e4b171fceb..d099dd7bb9 100644 --- a/gtk2_ardour/plugin_manager_ui.cc +++ b/gtk2_ardour/plugin_manager_ui.cc @@ -20,11 +20,18 @@ #include "gtk2ardour-config.h" #endif +#include +#include + +#include "pbd/unwind.h" + #include "ardour/types_convert.h" #include "gtkmm2ext/gui_thread.h" +#include "ardour_message.h" #include "plugin_manager_ui.h" +#include "plugin_scan_dialog.h" #include "pbd/i18n.h" @@ -32,27 +39,64 @@ using namespace ARDOUR; PluginManagerUI::PluginManagerUI () : ArdourWindow (_("Plugin Manager")) - , _btn_rescan_all (_("Re-scan Faulty")) + , _btn_rescan_all (_("Re-scan All")) + , _btn_rescan_err (_("Re-scan Faulty")) , _btn_rescan_sel (_("Re-scan Selected")) , _btn_clear (_("Clear Stale Scan Log")) + , _in_row_change (false) { - //plugin_model = Gtk::TreeStore::create (plugin_columns); plugin_model = Gtk::ListStore::create (plugin_columns); + Gtk::CellRendererToggle* cell_blacklist = Gtk::manage (new Gtk::CellRendererToggle ()); + Gtk::TreeViewColumn* column_blacklist = Gtk::manage (new Gtk::TreeViewColumn (_("BL"), *cell_blacklist)); + + cell_blacklist->property_activatable() = true; + cell_blacklist->property_radio() = false; + column_blacklist->add_attribute (cell_blacklist->property_active (), plugin_columns.blacklisted); + column_blacklist->add_attribute (cell_blacklist->property_activatable (), plugin_columns.can_blacklist); + + Gtk::CellRendererToggle* cell_fav = Gtk::manage (new Gtk::CellRendererToggle ()); + Gtk::TreeViewColumn* column_fav = Gtk::manage (new Gtk::TreeViewColumn (_("Fav"), *cell_fav)); + + cell_fav->property_activatable() = true; + cell_fav->property_radio() = true; + column_fav->add_attribute (cell_fav->property_active (), plugin_columns.favorite); + column_fav->add_attribute (cell_fav->property_activatable (), plugin_columns.can_fav_hide); + + Gtk::CellRendererToggle* cell_hidden = Gtk::manage (new Gtk::CellRendererToggle ()); + Gtk::TreeViewColumn* column_hidden = Gtk::manage (new Gtk::TreeViewColumn (_("Hide"), *cell_hidden)); + + cell_hidden->property_activatable() = true; + cell_hidden->property_radio() = true; + column_hidden->add_attribute (cell_hidden->property_active (), plugin_columns.hidden); + column_hidden->add_attribute (cell_hidden->property_activatable (), plugin_columns.can_fav_hide); + plugin_display.append_column (_("Status"), plugin_columns.status); - plugin_display.append_column (_("BL"), plugin_columns.blacklisted); + plugin_display.append_column (*column_blacklist); + plugin_display.append_column (*column_fav); + plugin_display.append_column (*column_hidden); 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.append_column (_("File/ID"), 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); + plugin_display.set_enable_search(true); - for (int i = 0; i < 5; ++i) { + plugin_display.get_column (4)->set_resizable (true); + plugin_display.get_column (5)->set_resizable (true); + +#if 0 + if (UIConfiguration::instance().get_use_tooltips()) { + recent_session_display.set_tooltip_column(1); // plugin_columns.tip + } +#endif + + for (int i = 0; i < 8; ++i) { Gtk::TreeView::Column* column = plugin_display.get_column(i); if (column) { column->set_sort_column(i); @@ -62,11 +106,9 @@ PluginManagerUI::PluginManagerUI () plugin_model->set_sort_column (plugin_columns.name.index(), Gtk::SORT_ASCENDING); plugin_display.set_name("PluginSelectorDisplay"); + plugin_display.get_selection()->set_mode (Gtk::SELECTION_SINGLE); 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 + //plugin_display.signal_row_activated().connect_notify (sigc::mem_fun(*this, &PluginManagerUI::row_activated)); _scroller.add (plugin_display); _scroller.set_policy (Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC); @@ -83,26 +125,51 @@ PluginManagerUI::PluginManagerUI () _pane.add (_log_scroller); _pane.set_divider (0, .85); - Gtk::Label* lbl = new Gtk::Label (""); // spacer + Gtk::Label* lbl = Gtk::manage (new Gtk::Label ("")); // spacer + Gtk::Frame* f_info = Gtk::manage (new Gtk::Frame (_("Plugin Count"))); + Gtk::Frame* f_actions = Gtk::manage (new Gtk::Frame (_("Scan Actions"))); + Gtk::VBox* b_actions = Gtk::manage (new Gtk::VBox ()); + + f_info->add (_tbl_nfo); + f_actions->add (*b_actions); + + _tbl_nfo.set_border_width (4); + + b_actions->pack_start (_btn_clear); + b_actions->pack_start (_btn_rescan_sel); + b_actions->pack_start (_btn_rescan_err); + b_actions->pack_start (_btn_rescan_all); + b_actions->set_spacing (4); + b_actions->set_border_width (4); /* 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); + _top.attach (*lbl, 0, 1, 0, 1, Gtk::SHRINK, Gtk::EXPAND | Gtk::FILL, 4, 0); + _top.attach (*f_info, 0, 1, 1, 2, Gtk::FILL | Gtk::SHRINK, Gtk::SHRINK, 4, 4); + _top.attach (*f_actions, 0, 1, 2, 3, Gtk::FILL | Gtk::SHRINK, Gtk::SHRINK, 4, 4); + _top.attach (_pane, 1, 2, 0, 3, Gtk::EXPAND | Gtk::FILL, Gtk::EXPAND | Gtk::FILL, 4, 0); add (_top); + _top.show_all (); _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()); + PluginManager::instance ().PluginListChanged.connect (_manager_connections, invalidator (*this), boost::bind (&PluginManagerUI::refill, this), gui_context()); + PluginManager::instance ().PluginStatusChanged.connect (_manager_connections, invalidator (*this), boost::bind (&PluginManagerUI::plugin_status_changed, this, _1, _2, _3), gui_context()); + _btn_rescan_all.signal_clicked.connect (sigc::mem_fun (*this, &PluginManagerUI::rescan_all)); + _btn_rescan_err.signal_clicked.connect (sigc::mem_fun (*this, &PluginManagerUI::rescan_faulty)); _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)); + + cell_fav->signal_toggled().connect (sigc::mem_fun (*this, &PluginManagerUI::favorite_changed)); + cell_hidden->signal_toggled().connect (sigc::mem_fun (*this, &PluginManagerUI::hidden_changed)); + cell_blacklist->signal_toggled().connect (sigc::mem_fun (*this, &PluginManagerUI::blacklist_changed)); + + /* populate */ + refill (); } PluginManagerUI::~PluginManagerUI () @@ -112,36 +179,42 @@ PluginManagerUI::~PluginManagerUI () void PluginManagerUI::on_show () { - refill (); // XXX -> only once in c'tor? ArdourWindow::on_show (); } static std::string -status_text (PluginScanLogEntry const& psle) +status_text (PluginScanLogEntry const& psle, PluginManager::PluginStatusType status) { if (!psle.recent ()) { return "Stale"; } PluginScanLogEntry::PluginScanResult sr = psle.result (); - if (sr == PluginScanLogEntry::OK) { - return "OK"; + if (sr == PluginScanLogEntry::OK || sr == PluginScanLogEntry::Blacklisted) { + if (status == PluginManager::Concealed) { + return _("Concealed"); + } else { + return _("OK"); + } + } + + if ((int)sr & PluginScanLogEntry::TimeOut) { + return _("New"); } - // TODO pick most relevant, show others on tooltip only - std::string rv; if ((int)sr & PluginScanLogEntry::New) { - rv += "New "; + return _("New"); } if ((int)sr & PluginScanLogEntry::Updated) { - rv += "Updated "; + return _("Updated"); } if ((int)sr & PluginScanLogEntry::Error) { - rv += "Error "; + return _("Error"); } if ((int)sr & PluginScanLogEntry::Incompatible) { - rv += "Incompatible "; + return _("Incompatible"); } - return rv; + assert (0); + return "?"; } static bool @@ -150,6 +223,15 @@ is_blacklisted (PluginScanLogEntry const& psle) return (int) psle.result () & PluginScanLogEntry::Blacklisted; } +static bool +can_blacklist (PluginScanLogEntry const& psle) +{ + if (psle.type () == LV2 || psle.type () == LADSPA) { + return false; + } + return ((int) psle.result () & ~PluginScanLogEntry::Blacklisted) == PluginScanLogEntry::OK; +} + static std::string plugin_type (const PluginType t) { @@ -167,9 +249,12 @@ plugin_type (const PluginType t) void PluginManagerUI::refill () { - std::vector > psl; - PluginManager& mgr (PluginManager::instance ()); - mgr.scan_log (psl); + /* save selection and sort-column, clear model to speed-up refill */ + Gtk::TreeIter iter = plugin_display.get_selection ()->get_selected (); + boost::shared_ptr sel; + if (iter) { + sel = (*iter)[plugin_columns.psle]; + } plugin_display.set_model (Glib::RefPtr(0)); @@ -177,11 +262,16 @@ PluginManagerUI::refill () 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); + plugin_model->clear (); - bool have_err = false; + bool rescan_err = false; bool have_stale = false; - plugin_model->clear (); + std::map plugin_count; + + std::vector > psl; + PluginManager& manager (PluginManager::instance ()); + manager.scan_log (psl); for (std::vector >::const_iterator i = psl.begin(); i != psl.end(); ++i) { PluginInfoList const& plugs = (*i)->nfo (); @@ -189,51 +279,86 @@ PluginManagerUI::refill () if (!(*i)->recent ()) { have_stale = true; } else if ((*i)->result () == PluginScanLogEntry::Blacklisted) { - // OK, but manually blacklisted + /* OK, but manually blacklisted */ } else if ((*i)->result () != PluginScanLogEntry::OK) { - have_err = true; + if ((*i)->type () != LV2) { + rescan_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.path] = Glib::path_get_basename ((*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.status] = status_text (**i, PluginManager::Normal); // XXX newrow[plugin_columns.blacklisted] = is_blacklisted (**i); newrow[plugin_columns.psle] = *i; + newrow[plugin_columns.plugin] = ARDOUR::PluginInfoPtr (); + newrow[plugin_columns.favorite] = false; + newrow[plugin_columns.hidden] = false; + newrow[plugin_columns.can_blacklist] = can_blacklist (**i); + newrow[plugin_columns.can_fav_hide] = false; + ++plugin_count[(*i)->type ()]; } else { for (PluginInfoList::const_iterator j = plugs.begin(); j != plugs.end(); ++j) { + PluginManager::PluginStatusType status = manager.get_status (*j); Gtk::TreeModel::Row newrow = *(plugin_model->append()); - newrow[plugin_columns.path] = (*i)->path (); + + newrow[plugin_columns.favorite] = status == PluginManager::Favorite; + newrow[plugin_columns.hidden] = status == PluginManager::Hidden; + newrow[plugin_columns.path] = Glib::path_get_basename ((*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.status] = status_text (**i, status); newrow[plugin_columns.blacklisted] = is_blacklisted (**i); newrow[plugin_columns.psle] = *i; + newrow[plugin_columns.plugin] = *j; + newrow[plugin_columns.can_blacklist] = can_blacklist (**i); + newrow[plugin_columns.can_fav_hide] = status != PluginManager::Concealed; + + ++plugin_count[(*i)->type ()]; } } } + plugin_display.set_model (plugin_model); if (sorted) { plugin_model->set_sort_column (sort_col, sort_type); } + if (sel) { + Gtk::TreeModel::Children rows = plugin_model->children (); + for (Gtk::TreeModel::Children::iterator i = rows.begin (); i != rows.end (); ++i) { + boost::shared_ptr const& srow ((*i)[plugin_columns.psle]); + if (*sel == *srow) { + plugin_display.get_selection ()->select (*i); + break; + } + } + } + + plugin_display.set_search_column (4); // Name + + int row = 0; + std::list children = _tbl_nfo.get_children(); + for (std::list::iterator child = children.begin(); child != children.end(); ++child) { + _tbl_nfo.remove (**child); + delete *child; + } + + for (std::map::const_iterator i = plugin_count.begin (); i != plugin_count.end (); ++i, ++row) { + Gtk::Label* lbl_type = new Gtk::Label (plugin_type (i->first), Gtk::ALIGN_LEFT, Gtk::ALIGN_CENTER); + Gtk::Label* lbl_count = new Gtk::Label (string_compose ("%1", i->second), Gtk::ALIGN_RIGHT, Gtk::ALIGN_CENTER); + _tbl_nfo.attach (*lbl_type , 0, 1, row, row + 1, Gtk::EXPAND | Gtk::FILL, Gtk::SHRINK, 2, 2); + _tbl_nfo.attach (*lbl_count, 1, 2, row, row + 1, Gtk::SHRINK | Gtk::FILL, Gtk::SHRINK, 2, 2); + } + _tbl_nfo.show_all (); + _btn_clear.set_sensitive (have_stale); - _btn_rescan_all.set_sensitive (have_err); + _btn_rescan_err.set_sensitive (rescan_err); } void @@ -243,29 +368,149 @@ PluginManagerUI::selection_changed () _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) { + if (sr == PluginScanLogEntry::OK || psle->type () == LV2) { _btn_rescan_sel.set_sensitive (false); } else { _btn_rescan_sel.set_sensitive (true); } } +void +PluginManagerUI::blacklist_changed (std::string const& path) +{ + Gtk::TreeIter iter; + if ((iter = plugin_model->get_iter (path))) { + boost::shared_ptr const& psle ((*iter)[plugin_columns.psle]); + if ((*iter)[plugin_columns.blacklisted]) { + PluginScanDialog psd (false, true, this); + PluginManager::instance ().rescan_plugin (psle->type (), psle->path ()); + } else { + PluginManager::instance ().blacklist (psle->type (), psle->path ()); + } + } +} + void PluginManagerUI::rescan_all () { + ArdourMessageDialog msg (_("Are you sure you want to rescan all plugins?"), false, Gtk::MESSAGE_QUESTION, Gtk::BUTTONS_YES_NO, true); + msg.set_title (_("Rescan Plugins")); + msg.set_secondary_text(_("This starts a fresh scan, dropping all cached plugin data and backlists. Depending on the number if plugins installed this can take a long time.")); + + if (msg.run() != Gtk::RESPONSE_YES) { + return; + } + + msg.hide(); + + ARDOUR::PluginManager& manager (PluginManager::instance ()); + if (true) { + manager.clear_au_blacklist (); + manager.clear_vst_blacklist (); + manager.clear_vst3_blacklist (); + } + + manager.clear_au_cache (); + manager.clear_vst_cache (); + manager.clear_vst3_cache (); + + PluginScanDialog psd (false, true, this); + psd.start (); +} + +void +PluginManagerUI::rescan_faulty () +{ + PluginScanDialog psd (false, true, this); + PluginManager::instance ().rescan_faulty (); } void PluginManagerUI::rescan_selected () { + if (plugin_display.get_selection()->count_selected_rows() != 1) { + return; + } + + Gtk::TreeIter iter = plugin_display.get_selection ()->get_selected (); + boost::shared_ptr const& psle ((*iter)[plugin_columns.psle]); + + PluginScanDialog psd (false, true, this); + PluginManager::instance ().rescan_plugin (psle->type (), psle->path ()); } void PluginManagerUI::clear_log () { + PluginManager::instance ().clear_stale_log (); + refill (); +} + +void +PluginManagerUI::plugin_status_changed (ARDOUR::PluginType t, std::string uid, ARDOUR::PluginManager::PluginStatusType stat) +{ + + Gtk::TreeModel::Children rows = plugin_model->children (); + for (Gtk::TreeModel::Children::iterator i = rows.begin (); i != rows.end (); ++i) { + PluginInfoPtr pp = (*i)[plugin_columns.plugin]; + if (!pp || pp->type != t || pp->unique_id != uid) { + continue; + } + + (*i)[plugin_columns.favorite] = (stat == PluginManager::Favorite) ? true : false; + (*i)[plugin_columns.hidden] = (stat == PluginManager::Hidden) ? true : false; + + break; + } +} + +void +PluginManagerUI::favorite_changed (const std::string& path) +{ + if (_in_row_change) { + return; + } + + PBD::Unwinder uw (_in_row_change, true); + + Gtk::TreeIter iter; + if ((iter = plugin_model->get_iter (path))) { + bool favorite = !(*iter)[plugin_columns.favorite]; + + PluginManager::PluginStatusType status = (favorite ? PluginManager::Favorite : PluginManager::Normal); + PluginInfoPtr pi = (*iter)[plugin_columns.plugin]; + + ARDOUR::PluginManager& manager (PluginManager::instance ()); + manager.set_status (pi->type, pi->unique_id, status); + manager.save_statuses (); + } +} + +void +PluginManagerUI::hidden_changed (const std::string& path) +{ + if (_in_row_change) { + return; + } + + PBD::Unwinder uw (_in_row_change, true); + + Gtk::TreeIter iter; + if ((iter = plugin_model->get_iter (path))) { + bool hidden = !(*iter)[plugin_columns.hidden]; + + PluginManager::PluginStatusType status = (hidden ? PluginManager::Hidden : PluginManager::Normal); + PluginInfoPtr pi = (*iter)[plugin_columns.plugin]; + + ARDOUR::PluginManager& manager (PluginManager::instance ()); + manager.set_status (pi->type, pi->unique_id, status); + manager.save_statuses (); + } } diff --git a/gtk2_ardour/plugin_manager_ui.h b/gtk2_ardour/plugin_manager_ui.h index 82fc386cbb..58c37f39fd 100644 --- a/gtk2_ardour/plugin_manager_ui.h +++ b/gtk2_ardour/plugin_manager_ui.h @@ -46,28 +46,44 @@ private: void refill (); void on_show (); void selection_changed (); + void blacklist_changed (std::string const&); + void favorite_changed (std::string const&); + void hidden_changed (std::string const&); void rescan_all (); + void rescan_faulty (); void rescan_selected (); void clear_log (); + void plugin_status_changed (ARDOUR::PluginType, std::string, ARDOUR::PluginManager::PluginStatusType); + struct PluginColumns : public Gtk::TreeModel::ColumnRecord { PluginColumns () { add (status); add (blacklisted); + add (favorite); + add (hidden); add (name); add (creator); add (type); add (path); add (psle); + add (plugin); + add (can_blacklist); + add (can_fav_hide); } Gtk::TreeModelColumn status; Gtk::TreeModelColumn blacklisted; + Gtk::TreeModelColumn favorite; + Gtk::TreeModelColumn hidden; Gtk::TreeModelColumn name; Gtk::TreeModelColumn type; Gtk::TreeModelColumn creator; Gtk::TreeModelColumn path; Gtk::TreeModelColumn > psle; + Gtk::TreeModelColumn plugin; + Gtk::TreeModelColumn can_blacklist; + Gtk::TreeModelColumn can_fav_hide; }; PluginColumns plugin_columns; @@ -78,12 +94,16 @@ private: Gtk::ScrolledWindow _log_scroller; ArdourWidgets::VPane _pane; ArdourWidgets::ArdourButton _btn_rescan_all; + ArdourWidgets::ArdourButton _btn_rescan_err; ArdourWidgets::ArdourButton _btn_rescan_sel; ArdourWidgets::ArdourButton _btn_clear; + Gtk::Table _tbl_nfo; Gtk::Table _top; - PBD::ScopedConnection _manager_connection; + bool _in_row_change; + + PBD::ScopedConnectionList _manager_connections; };