From c0866f54f312041d2dc08e5c7599d26a8cd23f13 Mon Sep 17 00:00:00 2001 From: Robin Gareus Date: Sun, 13 Oct 2019 04:09:35 +0200 Subject: [PATCH] Prototype categorized preset browser --- gtk2_ardour/plugin_presets_ui.cc | 245 +++++++++++++++++++++++++------ gtk2_ardour/plugin_presets_ui.h | 41 +++++- 2 files changed, 240 insertions(+), 46 deletions(-) diff --git a/gtk2_ardour/plugin_presets_ui.cc b/gtk2_ardour/plugin_presets_ui.cc index 71523bcdd0..c56af4ff18 100644 --- a/gtk2_ardour/plugin_presets_ui.cc +++ b/gtk2_ardour/plugin_presets_ui.cc @@ -16,6 +16,8 @@ * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include + #include "gtkmm2ext/utils.h" #include "ardour/plugin.h" @@ -26,48 +28,77 @@ #include "pbd/i18n.h" using namespace ARDOUR; +using namespace Gtk; PluginPresetsUI::PluginPresetsUI (boost::shared_ptr insert) : _insert (insert) , _load_button (_("Load")) { - _plugin_preset_model = Gtk::TreeStore::create (_plugin_preset_columns); + _filter_banks_model = TreeStore::create (_filter_banks_columns); + _filter_banks_display.set_model (_filter_banks_model); + _filter_banks_display.set_headers_visible (true); + _filter_banks_display.get_selection ()->set_mode (SELECTION_BROWSE); + _filter_banks_display.get_selection ()->signal_changed ().connect (sigc::mem_fun (*this, &PluginPresetsUI::filter_presets)); + _filter_banks_display.set_sensitive (true); + _filter_banks_display.append_column (_("Bank/Vendor"), _filter_banks_columns.name); + _banks_scroller.set_policy (POLICY_NEVER, POLICY_AUTOMATIC); + _banks_scroller.add (_filter_banks_display); + _banks_scroller.set_no_show_all (true); + + _filter_types_model = TreeStore::create (_filter_types_columns); + _filter_types_display.set_model (_filter_types_model); + _filter_types_display.set_headers_visible (true); + _filter_types_display.get_selection ()->set_mode (SELECTION_BROWSE); + _filter_types_display.get_selection ()->signal_changed ().connect (sigc::mem_fun (*this, &PluginPresetsUI::filter_presets)); + _filter_types_display.set_sensitive (true); + _filter_types_display.append_column (_("Type/Category"), _filter_types_columns.name); + _types_scroller.set_policy (POLICY_NEVER, POLICY_AUTOMATIC); + _types_scroller.add (_filter_types_display); + _types_scroller.set_no_show_all (true); + + _plugin_preset_model = TreeStore::create (_plugin_preset_columns); _plugin_preset_display.set_model (_plugin_preset_model); _plugin_preset_display.set_headers_visible (true); - _plugin_preset_display.get_selection ()->set_mode (Gtk::SELECTION_SINGLE); + _plugin_preset_display.get_selection ()->set_mode (SELECTION_BROWSE); _plugin_preset_display.get_selection ()->signal_changed ().connect (sigc::mem_fun (*this, &PluginPresetsUI::preset_selected)); - _plugin_preset_display.signal_row_activated ().connect (sigc::mem_fun (*this, &PluginPresetsUI::row_activated)); + _plugin_preset_display.signal_row_activated ().connect (sigc::mem_fun (*this, &PluginPresetsUI::preset_row_activated)); _plugin_preset_display.set_sensitive (true); - Gtk::CellRendererText* label_render = Gtk::manage (new Gtk::CellRendererText()); - Gtk::TreeView::Column* label_col = Gtk::manage (new Gtk::TreeView::Column (_("Preset"), *label_render)); + CellRendererText* label_render = manage (new CellRendererText()); + TreeView::Column* label_col = manage (new TreeView::Column (_("Preset"), *label_render)); label_col->add_attribute (label_render->property_markup(), _plugin_preset_columns.name); _plugin_preset_display.append_column (*label_col); _preset_desc.set_editable (false); _preset_desc.set_can_focus (false); - _preset_desc.set_wrap_mode (Gtk::WRAP_WORD); + _preset_desc.set_wrap_mode (WRAP_WORD); _preset_desc.set_size_request (400,200); _preset_desc.set_name (X_("TextOnBackground")); _preset_desc.set_border_width (6); - _preset_scroller.set_policy (Gtk::POLICY_NEVER, Gtk::POLICY_AUTOMATIC); + _preset_scroller.set_policy (POLICY_NEVER, POLICY_AUTOMATIC); _preset_scroller.add (_plugin_preset_display); _load_button.set_name ("generic button"); _load_button.signal_clicked.connect (sigc::mem_fun (*this, &PluginPresetsUI::load_preset)); _load_button.set_sensitive (false); - attach (_preset_scroller, 0, 1, 0, 2, Gtk::FILL, Gtk::EXPAND|Gtk::FILL, 2, 0); - attach (_preset_desc, 1, 2, 0, 1, Gtk::EXPAND|Gtk::FILL, Gtk::FILL, 2, 0); - attach (_load_button, 1, 2, 1, 2, Gtk::FILL, Gtk::SHRINK, 2, 0); + Box* filter_box = manage (new VBox ()); + filter_box->pack_start (_banks_scroller); + filter_box->pack_start (_types_scroller); + + attach (*filter_box, 0, 1, 0, 2, FILL, EXPAND|FILL, 2, 0); + attach (_preset_scroller, 1, 2, 0, 2, FILL, EXPAND|FILL, 2, 0); + attach (_preset_desc, 2, 3, 0, 1, EXPAND|FILL, EXPAND|FILL, 2, 0); + attach (_load_button, 2, 3, 1, 2, FILL, SHRINK, 2, 0); boost::shared_ptr plugin (_insert->plugin ()); plugin->PresetAdded.connect (_preset_connections, invalidator (*this), boost::bind (&PluginPresetsUI::update_preset_list, this), gui_context ()); plugin->PresetRemoved.connect (_preset_connections, invalidator (*this), boost::bind (&PluginPresetsUI::update_preset_list, this), gui_context ()); - plugin->PresetLoaded.connect (_preset_connections, invalidator (*this), boost::bind (&PluginPresetsUI::update_preset_list, this), gui_context ()); - plugin->PresetDirty.connect (_preset_connections, invalidator (*this), boost::bind (&PluginPresetsUI::update_preset_list, this), gui_context ()); + + plugin->PresetLoaded.connect (_preset_connections, invalidator (*this), boost::bind (&PluginPresetsUI::filter_presets, this), gui_context ()); + plugin->PresetDirty.connect (_preset_connections, invalidator (*this), boost::bind (&PluginPresetsUI::filter_presets, this), gui_context ()); update_preset_list (); } @@ -76,52 +107,176 @@ void PluginPresetsUI::update_preset_list () { boost::shared_ptr plugin (_insert->plugin ()); - - Plugin::PresetRecord const& p = plugin->last_preset (); std::vector presets = plugin->get_presets (); + _pps.clear (); + std::map banks; // or vendors + std::map types; // or categories - std::string selected_uri; + for (std::vector::const_iterator i = presets.begin (); i != presets.end (); ++i) { + ++banks[_("-All-")]; + ++types[_("-All-")]; + if (i->user) { + ++banks[_("-User-")]; + _pps.push_back (PluginPreset(*i, _("-User-"))); + continue; + } + + std::string l (i->label); + std::vector cat; + size_t pos = 0; + while ((pos = l.find(" - ")) != std::string::npos) { + cat.push_back (l.substr (0, pos)); + l.erase (0, pos + 3); + if (cat.size() > 1) { + break; + } + } + if (cat.size() > 1) { + ++banks[cat.at (0)]; + ++types[cat.at (1)]; + _pps.push_back (PluginPreset(*i, cat.at (0), cat.at (1))); + } else if (cat.size() > 0) { + ++banks[cat.at (0)]; + _pps.push_back (PluginPreset(*i, cat.at (0))); + } else { + _pps.push_back (PluginPreset(*i)); + } + } + + if (types.size() > 2) { + std::string selected_type; + if (_filter_types_display.get_selection ()->count_selected_rows () == 1) { + TreeIter iter = _filter_types_display.get_selection ()->get_selected (); + selected_type = (*iter)[_filter_types_columns.name]; + } else { + selected_type = Gtkmm2ext::markup_escape_text(_("-All-")); + } + _filter_types_model->clear (); + for (std::map::const_iterator i = types.begin (); i != types.end(); ++i) { + TreeModel::Row row = *(_filter_types_model->append ()); + row[_filter_types_columns.name] = Gtkmm2ext::markup_escape_text (i->first); + row[_filter_types_columns.count] = i->second; + } + TreeModel::Children rows = _filter_types_model->children (); + for (TreeModel::Children::iterator i = rows.begin (); i != rows.end (); ++i) { + std::string const& name ((*i)[_filter_types_columns.name]); + if (selected_type == name) { + _filter_types_display.get_selection ()->select (*i); + break; + } + } + _filter_types_display.show_all (); + _types_scroller.show (); + } else { + _filter_types_model->clear (); + _types_scroller.hide (); + } + + if (banks.size() > 2) { + std::string selected_bank = Gtkmm2ext::markup_escape_text(_("-All-")); + if (_filter_banks_display.get_selection ()->count_selected_rows () == 1) { + TreeIter iter = _filter_banks_display.get_selection ()->get_selected (); + selected_bank = (*iter)[_filter_banks_columns.name]; + } + _filter_banks_model->clear (); + for (std::map::const_iterator i = banks.begin (); i != banks.end(); ++i) { + TreeModel::Row row = *(_filter_banks_model->append ()); + row[_filter_banks_columns.name] = Gtkmm2ext::markup_escape_text (i->first); + row[_filter_banks_columns.count] = i->second; + } + TreeModel::Children rows = _filter_banks_model->children (); + for (TreeModel::Children::iterator i = rows.begin (); i != rows.end (); ++i) { + std::string const& name ((*i)[_filter_banks_columns.name]); + if (selected_bank == name) { + _filter_banks_display.get_selection ()->select (*i); + break; + } + } + _filter_banks_display.show_all (); + _banks_scroller.show (); + } else { + _filter_banks_model->clear (); + _banks_scroller.hide (); + } + + std::sort (_pps.begin(), _pps.end()); + + filter_presets (); +} + +void +PluginPresetsUI::filter_presets () +{ + bool user_only = false; + std::string selected_bank; + if (_filter_banks_display.get_selection ()->count_selected_rows () == 1) { + TreeIter iter = _filter_banks_display.get_selection ()->get_selected (); + selected_bank = (*iter)[_filter_banks_columns.name]; + if (_("-All-") == selected_bank) { + selected_bank = ""; + } + if (_("-User-") == selected_bank) { + selected_bank = ""; + user_only = true; + } + } + + std::string selected_type; + if (_filter_types_display.get_selection ()->count_selected_rows () == 1) { + TreeIter iter = _filter_types_display.get_selection ()->get_selected (); + selected_type = (*iter)[_filter_types_columns.name]; + if (_("-All-") == selected_type) { + selected_type = ""; + } + } + + boost::shared_ptr plugin (_insert->plugin ()); + Plugin::PresetRecord const& p = plugin->last_preset (); + + std::string selected_uri = p.valid ? p.uri : ""; if (_plugin_preset_display.get_selection ()->count_selected_rows () == 1) { - Gtk::TreeIter iter = _plugin_preset_display.get_selection ()->get_selected (); + TreeIter iter = _plugin_preset_display.get_selection ()->get_selected (); ARDOUR::Plugin::PresetRecord const& ppr ((*iter)[_plugin_preset_columns.plugin_preset]); selected_uri = ppr.uri; } _plugin_preset_model->clear (); - - bool found_active = false; - bool const modified = plugin->parameter_changed_since_last_preset (); - for (std::vector::const_iterator i = presets.begin (); i != presets.end (); ++i) { - Gtk::TreeModel::Row row = *(_plugin_preset_model->append ()); - if (p.uri == i->uri && !modified) { - row[_plugin_preset_columns.name] = string_compose ("%1", Gtkmm2ext::markup_escape_text (i->label)); - found_active = true; - } else { - row[_plugin_preset_columns.name] = Gtkmm2ext::markup_escape_text (i->label); + for (std::vector::const_iterator i = _pps.begin (); i != _pps.end (); ++i) { + if (!selected_type.empty() && i->_type != selected_type) { + continue; } - row[_plugin_preset_columns.description] = i->description; - row[_plugin_preset_columns.plugin_preset] = *i; + if (!selected_bank.empty() && i->_bank != selected_bank) { + continue; + } + + ARDOUR::Plugin::PresetRecord const& ppr (i->_preset_record); + + if (user_only && !ppr.user) { + continue; + } + + TreeModel::Row row = *(_plugin_preset_model->append ()); + if (p.uri == ppr.uri && !modified) { + row[_plugin_preset_columns.name] = string_compose ("%1", Gtkmm2ext::markup_escape_text (ppr.label)); + } else { + row[_plugin_preset_columns.name] = Gtkmm2ext::markup_escape_text (ppr.label); + } + row[_plugin_preset_columns.description] = ppr.description; + row[_plugin_preset_columns.plugin_preset] = ppr; } - { - Gtk::TreeModel::Row row = *(_plugin_preset_model->prepend ()); - if (found_active || modified) { - row[_plugin_preset_columns.name] = _("(none)"); - } else { - row[_plugin_preset_columns.name] = string_compose ("%1", _("(none)")); - } - row[_plugin_preset_columns.description] = ""; - row[_plugin_preset_columns.plugin_preset] = Plugin::PresetRecord (); - } - - Gtk::TreeModel::Children rows = _plugin_preset_model->children (); - for (Gtk::TreeModel::Children::iterator i = rows.begin (); i != rows.end (); ++i) { + int path = 0; + TreeModel::Children rows = _plugin_preset_model->children (); + for (TreeModel::Children::iterator i = rows.begin (); i != rows.end (); ++i, ++path) { ARDOUR::Plugin::PresetRecord const& ppr ((*i)[_plugin_preset_columns.plugin_preset]); if (ppr.uri == selected_uri) { _plugin_preset_display.get_selection ()->select (*i); + char row_path[21]; + snprintf(row_path, 21, "%d", path); + _plugin_preset_display.scroll_to_row (Gtk::TreePath(row_path)); break; } } @@ -131,10 +286,12 @@ void PluginPresetsUI::preset_selected () { if (_plugin_preset_display.get_selection ()->count_selected_rows () != 1) { + _preset_desc.get_buffer ()->set_text (""); + _load_button.set_sensitive (false); return; } - Gtk::TreeIter iter = _plugin_preset_display.get_selection ()->get_selected (); + TreeIter iter = _plugin_preset_display.get_selection ()->get_selected (); assert (iter); ARDOUR::Plugin::PresetRecord const& ppr ((*iter)[_plugin_preset_columns.plugin_preset]); @@ -153,7 +310,7 @@ PluginPresetsUI::preset_selected () } void -PluginPresetsUI::row_activated (Gtk::TreeModel::Path, Gtk::TreeViewColumn*) +PluginPresetsUI::preset_row_activated (Gtk::TreeModel::Path, Gtk::TreeViewColumn*) { if (_load_button.get_sensitive ()) { load_preset (); @@ -167,7 +324,7 @@ PluginPresetsUI::load_preset () return; } - Gtk::TreeIter iter = _plugin_preset_display.get_selection ()->get_selected (); + TreeIter iter = _plugin_preset_display.get_selection ()->get_selected (); ARDOUR::Plugin::PresetRecord const& ppr ((*iter)[_plugin_preset_columns.plugin_preset]); if (ppr.valid) { _insert->load_preset (ppr); diff --git a/gtk2_ardour/plugin_presets_ui.h b/gtk2_ardour/plugin_presets_ui.h index e87c92d80c..b40f825901 100644 --- a/gtk2_ardour/plugin_presets_ui.h +++ b/gtk2_ardour/plugin_presets_ui.h @@ -39,13 +39,40 @@ public: private: void update_preset_list (); + void filter_presets (); void preset_selected (); - void row_activated (Gtk::TreeModel::Path, Gtk::TreeViewColumn*); + void preset_row_activated (Gtk::TreeModel::Path, Gtk::TreeViewColumn*); void load_preset (); boost::shared_ptr _insert; PBD::ScopedConnectionList _preset_connections; + struct PluginPreset { + PluginPreset (ARDOUR::Plugin::PresetRecord const& p, std::string const& b = "", std::string const& t = "") + : _preset_record (p) + , _bank (b) + , _type (t) + { } + ARDOUR::Plugin::PresetRecord _preset_record; + std::string _bank; + std::string _type; + + bool operator< (PluginPreset const& o) const { + return _preset_record.label < o._preset_record.label; + } + }; + + std::vector _pps; + + struct TagFilterModelColumns : public Gtk::TreeModel::ColumnRecord { + TagFilterModelColumns () { + add (name); + add (count); + } + Gtk::TreeModelColumn name; + Gtk::TreeModelColumn count; + }; + struct PluginPresetModelColumns : public Gtk::TreeModel::ColumnRecord { PluginPresetModelColumns () { add (name); @@ -58,12 +85,22 @@ private: Gtk::TreeModelColumn plugin_preset; }; - ArdourWidgets::ArdourButton _load_button; + TagFilterModelColumns _filter_banks_columns; + Gtk::TreeView _filter_banks_display; + Glib::RefPtr _filter_banks_model; + Gtk::ScrolledWindow _banks_scroller; + + TagFilterModelColumns _filter_types_columns; + Gtk::TreeView _filter_types_display; + Glib::RefPtr _filter_types_model; + Gtk::ScrolledWindow _types_scroller; PluginPresetModelColumns _plugin_preset_columns; Gtk::TreeView _plugin_preset_display; Glib::RefPtr _plugin_preset_model; Gtk::ScrolledWindow _preset_scroller; + + ArdourWidgets::ArdourButton _load_button; Gtk::TextView _preset_desc; };