X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=gtk2_ardour%2Fkeyeditor.cc;h=aea8185f3bc5758456fbbadf5baf37ba816e5fbc;hb=9e981367e2cf27cf0d19914e03f55a8c405c7188;hp=37d923a18fda43d9ff3fb04af869e7db2dbba49c;hpb=b4ead1dda62bb690a76719a184cb7d1c6b8f54be;p=ardour.git diff --git a/gtk2_ardour/keyeditor.cc b/gtk2_ardour/keyeditor.cc index 37d923a18f..aea8185f3b 100644 --- a/gtk2_ardour/keyeditor.cc +++ b/gtk2_ardour/keyeditor.cc @@ -22,6 +22,13 @@ #endif #include +#include +#include + +#include + +#include +#include #include #include @@ -32,6 +39,8 @@ #include "gtkmm2ext/bindings.h" #include "gtkmm2ext/utils.h" +#include "pbd/error.h" +#include "pbd/openuri.h" #include "pbd/strsplit.h" #include "ardour/filesystem_paths.h" @@ -41,7 +50,7 @@ #include "keyboard.h" #include "keyeditor.h" -#include "i18n.h" +#include "pbd/i18n.h" using namespace std; using namespace Gtk; @@ -51,6 +60,8 @@ using namespace PBD; using Gtkmm2ext::Keyboard; using Gtkmm2ext::Bindings; +sigc::signal KeyEditor::UpdateBindings; + void bindings_collision_dialog (Gtk::Window& parent) { ArdourDialog dialog (parent, _("Colliding keybindings"), true); @@ -66,16 +77,24 @@ KeyEditor::KeyEditor () : ArdourWindow (_("Key Bindings")) , unbind_button (_("Remove shortcut")) , unbind_box (BUTTONBOX_END) + , filter_entry (_("Search..."), true) + , filter_string("") + , print_button (_("Print")) , sort_column(0) , sort_type(Gtk::SORT_ASCENDING) { - last_keyval = 0; notebook.signal_switch_page ().connect (sigc::mem_fun (*this, &KeyEditor::page_change)); vpacker.pack_start (notebook, true, true); - Label* hint = manage (new Label (_("Select an action, then press the key(s) to (re)set its shortcut"))); + Glib::RefPtr icon = ARDOUR_UI_UTILS::get_icon ("search"); + filter_entry.set_icon_from_pixbuf (icon); + filter_entry.set_icon_tooltip_text (_("Click to reset search string")); + filter_entry.signal_search_string_updated ().connect (sigc::mem_fun (*this, &KeyEditor::search_string_updated)); + vpacker.pack_start (filter_entry, false, false); + + Label* hint = manage (new Label (_("To remove a shortcut select an action then press this: "))); hint->show (); unbind_box.set_spacing (6); unbind_box.pack_start (*hint, false, true); @@ -89,27 +108,58 @@ KeyEditor::KeyEditor () reset_button.add (reset_label); reset_label.set_markup (string_compose ("%1", _("Reset Bindings to Defaults"))); + print_button.signal_clicked().connect (sigc::mem_fun (*this, &KeyEditor::print)); + reset_box.pack_start (reset_button, true, false); + reset_box.pack_start (print_button, true, false); reset_box.show (); reset_button.show (); reset_label.show (); + print_button.show (); reset_button.signal_clicked().connect (sigc::mem_fun (*this, &KeyEditor::reset)); vpacker.pack_start (reset_box, false, false); add (vpacker); unbind_button.set_sensitive (false); + _refresh_connection = UpdateBindings.connect (sigc::mem_fun (*this, &KeyEditor::refresh)); } void KeyEditor::add_tab (string const & name, Bindings& bindings) { Tab* t = new Tab (*this, name, &bindings); - t->populate (); + + if (t->populate () == 0) { + /* no bindings */ + delete t; + return; + } + + tabs.push_back (t); t->show_all (); notebook.append_page (*t, name); } + +void +KeyEditor::remove_tab (string const &name) +{ + guint npages = notebook.get_n_pages (); + + for (guint n = 0; n < npages; ++n) { + Widget* w = notebook.get_nth_page (n); + Tab* tab = dynamic_cast (w); + if (tab) { + if (tab->name == name) { + notebook.remove_page (*w); + return; + } + } + } + cerr << "Removed " << name << endl; +} + void KeyEditor::unbind () { @@ -124,8 +174,12 @@ KeyEditor::page_change (GtkNotebookPage*, guint) } bool -KeyEditor::on_key_press_event (GdkEventKey* ev) +KeyEditor::Tab::key_press_event (GdkEventKey* ev) { + if (view.get_selection()->count_selected_rows() != 1) { + return false; + } + if (!ev->is_modifier) { last_keyval = ev->keyval; } @@ -139,13 +193,17 @@ KeyEditor::on_key_press_event (GdkEventKey* ev) } bool -KeyEditor::on_key_release_event (GdkEventKey* ev) +KeyEditor::Tab::key_release_event (GdkEventKey* ev) { + if (view.get_selection()->count_selected_rows() != 1) { + return false; + } + if (last_keyval == 0) { return false; } - current_tab()->bind (ev, last_keyval); + owner.current_tab()->bind (ev, last_keyval); last_keyval = 0; return true; @@ -155,10 +213,17 @@ KeyEditor::Tab::Tab (KeyEditor& ke, string const & str, Bindings* b) : owner (ke) , name (str) , bindings (b) + , last_keyval (0) { - model = TreeStore::create(columns); + data_model = TreeStore::create(columns); + populate (); + + filter = TreeModelFilter::create(data_model); + filter->set_visible_func (sigc::mem_fun (*this, &Tab::visible_func)); + + sorted_filter = TreeModelSort::create(filter); - view.set_model (model); + view.set_model (sorted_filter); view.append_column (_("Action"), columns.name); view.append_column (_("Shortcut"), columns.binding); view.set_headers_visible (true); @@ -170,12 +235,14 @@ KeyEditor::Tab::Tab (KeyEditor& ke, string const & str, Bindings* b) view.set_rules_hint (true); view.set_name (X_("KeyEditorTree")); - view.get_selection()->signal_changed().connect (sigc::mem_fun (*this, &Tab::action_selected)); + view.signal_cursor_changed().connect (sigc::mem_fun (*this, &Tab::action_selected)); + view.signal_key_press_event().connect (sigc::mem_fun (*this, &Tab::key_press_event), false); + view.signal_key_release_event().connect (sigc::mem_fun (*this, &Tab::key_release_event), false); view.get_column(0)->set_sort_column (columns.name); view.get_column(1)->set_sort_column (columns.binding); - model->set_sort_column (owner.sort_column, owner.sort_type); - model->signal_sort_column_changed().connect (sigc::mem_fun (*this, &Tab::sort_column_changed)); + data_model->set_sort_column (owner.sort_column, owner.sort_type); + data_model->signal_sort_column_changed().connect (sigc::mem_fun (*this, &Tab::sort_column_changed)); signal_map().connect (sigc::mem_fun (*this, &Tab::tab_mapped)); @@ -194,57 +261,54 @@ KeyEditor::Tab::action_selected () return; } - TreeModel::iterator i = view.get_selection()->get_selected(); + TreeModel::const_iterator it = view.get_selection()->get_selected(); - owner.unbind_button.set_sensitive (false); - - if (i != model->children().end()) { - - string path = (*i)[columns.path]; + if (!it) { + return; + } - if (!(*i)[columns.bindable]) { - return; - } + if (!(*it)[columns.bindable]) { + owner.unbind_button.set_sensitive (false); + return; + } - string binding = (*i)[columns.binding]; + const string& binding = (*it)[columns.binding]; - if (!binding.empty()) { - owner.unbind_button.set_sensitive (true); - } + if (!binding.empty()) { + owner.unbind_button.set_sensitive (true); } } void KeyEditor::Tab::unbind () { - TreeModel::iterator i = view.get_selection()->get_selected(); + const std::string& action_path = (*view.get_selection()->get_selected())[columns.path]; - owner.unbind_button.set_sensitive (false); + TreeModel::iterator it = find_action_path (data_model->children().begin(), data_model->children().end(), action_path); - if (i != model->children().end()) { - if (!(*i)[columns.bindable]) { - return; - } + if (!it || !(*it)[columns.bindable]) { + return; + } - const std::string& action_path = (*i)[columns.path]; + bindings->remove (Gtkmm2ext::Bindings::Press, action_path , true); + (*it)[columns.binding] = string (); - bindings->remove (Gtkmm2ext::Bindings::Press, action_path , true); - (*i)[columns.binding] = string (); - } + owner.unbind_button.set_sensitive (false); } void KeyEditor::Tab::bind (GdkEventKey* release_event, guint pressed_key) { - TreeModel::iterator i = view.get_selection()->get_selected(); + const std::string& action_path = (*view.get_selection()->get_selected())[columns.path]; + TreeModel::iterator it = find_action_path (data_model->children().begin(), data_model->children().end(), action_path); - if (i == model->children().end()) { - return; - } + /* pressed key could be upper case if Shift was used. We want all + single keys stored as their lower-case version, so ensure this + */ - string action_path = (*i)[columns.path]; + pressed_key = gdk_keyval_to_lower (pressed_key); - if (!(*i)[columns.bindable]) { + if (!it || !(*it)[columns.bindable]) { return; } @@ -259,12 +323,12 @@ KeyEditor::Tab::bind (GdkEventKey* release_event, guint pressed_key) bool result = bindings->replace (new_binding, Gtkmm2ext::Bindings::Press, action_path); if (result) { - (*i)[columns.binding] = gtk_accelerator_get_label (new_binding.key(), (GdkModifierType) new_binding.state()); + (*it)[columns.binding] = gtk_accelerator_get_label (new_binding.key(), (GdkModifierType) new_binding.state()); owner.unbind_button.set_sensitive (true); } } -void +uint32_t KeyEditor::Tab::populate () { vector paths; @@ -284,7 +348,7 @@ KeyEditor::Tab::populate () vector::iterator l; vector >::iterator a; - model->clear (); + data_model->clear (); for (a = actions.begin(), l = labels.begin(), k = keys.begin(), p = paths.begin(), t = tooltips.begin(); l != labels.end(); ++k, ++p, ++t, ++l, ++a) { @@ -313,7 +377,7 @@ KeyEditor::Tab::populate () TreeIter rowp; TreeModel::Row parent; - rowp = model->append(); + rowp = data_model->append(); nodes[category] = rowp; parent = *(rowp); parent[columns.name] = category; @@ -324,13 +388,13 @@ KeyEditor::Tab::populate () * out with information */ - row = *(model->append (parent.children())); + row = *(data_model->append (parent.children())); } else { /* category/group is present, so just add the child row */ - row = *(model->append ((*r->second)->children())); + row = *(data_model->append ((*r->second)->children())); } @@ -354,6 +418,8 @@ KeyEditor::Tab::populate () } row[columns.action] = *a; } + + return data_model->children().size(); } void @@ -361,7 +427,7 @@ KeyEditor::Tab::sort_column_changed () { int column; SortType type; - if (model->get_sort_column_id (column, type)) { + if (data_model->get_sort_column_id (column, type)) { owner.sort_column = column; owner.sort_type = type; } @@ -370,14 +436,71 @@ KeyEditor::Tab::sort_column_changed () void KeyEditor::Tab::tab_mapped () { - model->set_sort_column (owner.sort_column, owner.sort_type); + data_model->set_sort_column (owner.sort_column, owner.sort_type); + filter->refilter (); +} + +bool +KeyEditor::Tab::visible_func(const Gtk::TreeModel::const_iterator& iter) const +{ + if (!iter) { + return false; + } + + // never filter when search string is empty or item is a category + if (owner.filter_string.empty () || !(*iter)[columns.bindable]) { + return true; + } + + // search name + std::string name = (*iter)[columns.name]; + boost::to_lower (name); + if (name.find (owner.filter_string) != std::string::npos) { + return true; + } + + // search binding + std::string binding = (*iter)[columns.binding]; + boost::to_lower (binding); + if (binding.find (owner.filter_string) != std::string::npos) { + return true; + } + + return false; +} + +TreeModel::iterator +KeyEditor::Tab::find_action_path (TreeModel::const_iterator begin, TreeModel::const_iterator end, const std::string& action_path) const +{ + if (!begin) { + return end; + } + + for (TreeModel::iterator it = begin; it != end; ++it) { + if (it->children()) { + TreeModel::iterator jt = find_action_path (it->children().begin(), it->children().end(), action_path); + if (jt != it->children().end()) { + return jt; + } + } + const std::string& path = (*it)[columns.path]; + if (action_path.compare(path) == 0) { + return it; + } + } + return end; } void KeyEditor::reset () { Keyboard::the_keyboard().reset_bindings (); + refresh (); +} +void +KeyEditor::refresh () +{ for (Tabs::iterator t = tabs.begin(); t != tabs.end(); ++t) { (*t)->view.get_selection()->unselect_all (); (*t)->populate (); @@ -389,3 +512,61 @@ KeyEditor::current_tab () { return dynamic_cast (notebook.get_nth_page (notebook.get_current_page())); } + +void +KeyEditor::search_string_updated (const std::string& filter) +{ + filter_string = boost::to_lower_copy(filter); + KeyEditor::Tab* tab = current_tab (); + if (tab) { + tab->filter->refilter (); + } +} + +void +KeyEditor::print () const +{ + stringstream sstr; + Bindings::save_all_bindings_as_html (sstr); + + if (sstr.str().empty()) { + return; + } + + + gchar* file_name; + GError *err = NULL; + gint fd; + + if ((fd = g_file_open_tmp ("akprintXXXXXX.html", &file_name, &err)) < 0) { + if (err) { + error << string_compose (_("Could not open temporary file to print bindings (%1)"), err->message) << endmsg; + g_error_free (err); + } + return; + } + +#ifdef PLATFORM_WINDOWS + ::close (fd); +#endif + + err = NULL; + + if (!g_file_set_contents (file_name, sstr.str().c_str(), sstr.str().size(), &err)) { +#ifndef PLATFORM_WINDOWS + ::close (fd); +#endif + g_unlink (file_name); + if (err) { + error << string_compose (_("Could not save bindings to file (%1)"), err->message) << endmsg; + g_error_free (err); + } + return; + } + +#ifndef PLATFORM_WINDOWS + ::close (fd); +#endif + + PBD::open_uri (string_compose ("file:///%1", file_name)); +}