diff options
author | Paul Davis <paul@linuxaudiosystems.com> | 2015-08-05 10:56:36 -0400 |
---|---|---|
committer | Paul Davis <paul@linuxaudiosystems.com> | 2016-02-22 15:31:23 -0500 |
commit | 36023db8aa63d59d475c137e6b394f30e8cde1b7 (patch) | |
tree | 75bacdda3abf7f94f651dc34bbed567c2e140741 /libs | |
parent | 1b5247ebb9ef89b922c3c0b2a8194d510bdd75e1 (diff) |
change/extend/rework Bindings API to allow replacement and provide stub/hook/call to save bindings
Diffstat (limited to 'libs')
-rw-r--r-- | libs/gtkmm2ext/bindings.cc | 156 | ||||
-rw-r--r-- | libs/gtkmm2ext/gtkmm2ext/bindings.h | 22 | ||||
-rw-r--r-- | libs/gtkmm2ext/keyboard.cc | 2 |
3 files changed, 149 insertions, 31 deletions
diff --git a/libs/gtkmm2ext/bindings.cc b/libs/gtkmm2ext/bindings.cc index 8803edc540..260d65101a 100644 --- a/libs/gtkmm2ext/bindings.cc +++ b/libs/gtkmm2ext/bindings.cc @@ -289,8 +289,63 @@ Bindings::activate (KeyboardKey kb, Operation op) return true; } +bool +Bindings::replace (KeyboardKey kb, Operation op, string const & action_name, bool can_save) +{ + if (!action_map) { + return false; + } + + /* We have to search the existing binding map by both action and + * keybinding, because the following are possible: + * + * - key is already used for a different action + * - action has a different binding + * - key is not used + * - action is not bound + */ + + RefPtr<Action> action = action_map->find_action (action_name); + + if (!action) { + return false; + } + + KeybindingMap* kbm = 0; + + switch (op) { + case Press: + kbm = &press_bindings; + break; + case Release: + kbm = &release_bindings; + break; + } + + KeybindingMap::iterator k = kbm->find (kb); + + if (k != kbm->end()) { + kbm->erase (k); + } + + /* now linear search by action */ + + for (k = kbm->begin(); k != kbm->end(); ++k) { + if (k->second == action) { + kbm->erase (k); + break; + } + } + + add (kb, op, action, can_save); + + /* for now, this never fails */ + + return true; +} + void -Bindings::add (KeyboardKey kb, Operation op, RefPtr<Action> what) +Bindings::add (KeyboardKey kb, Operation op, RefPtr<Action> what, bool can_save) { KeybindingMap* kbm = 0; @@ -312,10 +367,20 @@ Bindings::add (KeyboardKey kb, Operation op, RefPtr<Action> what) k->second = what; } + /* GTK has the useful feature of showing key bindings for actions in + * menus. As of August 2015, we have no interest in trying to + * reimplement this functionality, so we will use it even though we no + * longer use GTK accelerators for handling key events. To do this, we + * need to make sure that there is a fully populated GTK AccelMap set + * up with all bindings/actions. + */ + Gtk::AccelKey gtk_key; - /* tweak the binding so that GTK will accept it and display something - * acceptable + /* tweak the modifier used in the binding so that GTK will accept it + * and display something acceptable. The actual keyval should display + * correctly even if it involves a key that GTK would not allow + * as an accelerator. */ uint32_t gtk_legal_keyval = kb.key(); @@ -326,6 +391,18 @@ Bindings::add (KeyboardKey kb, Operation op, RefPtr<Action> what) bool entry_exists = Gtk::AccelMap::lookup_entry (what->get_accel_path(), gtk_key); if (!entry_exists || gtk_key.get_key() == 0) { + + /* there is a trick happening here. It turns out that + * gtk_accel_map_add_entry() performs no validation checks on + * the accelerator keyval. This means we can use it to define + * ANY accelerator, even if they violate GTK's rules + * (e.g. about not using navigation keys). This works ONLY when + * the entry in the GTK accelerator map has not already been + * added. The entries will be added by the GTK UIManager when + * building menus, so this code must be called before that + * happens. + */ + Gtk::AccelMap::add_entry (what->get_accel_path(), gtk_binding.key(), (Gdk::ModifierType) gtk_binding.state()); @@ -336,10 +413,14 @@ Bindings::add (KeyboardKey kb, Operation op, RefPtr<Action> what) if (!Gtk::AccelMap::lookup_entry (what->get_accel_path(), gtk_key) || gtk_key.get_key() == 0) { cerr << "GTK binding using " << gtk_binding << " failed for " << what->get_accel_path() << " existing = " << gtk_key.get_key() << " + " << gtk_key.get_mod() << endl; } + + if (can_save) { + Keyboard::save_keybindings (); + } } void -Bindings::remove (KeyboardKey kb, Operation op) +Bindings::remove (KeyboardKey kb, Operation op, bool can_save) { KeybindingMap* kbm = 0; @@ -357,6 +438,36 @@ Bindings::remove (KeyboardKey kb, Operation op) if (k != kbm->end()) { kbm->erase (k); } + + if (can_save) { + Keyboard::save_keybindings (); + } +} + +void +Bindings::remove (RefPtr<Action> action, Operation op, bool can_save) +{ + KeybindingMap* kbm = 0; + + switch (op) { + case Press: + kbm = &press_bindings; + break; + case Release: + kbm = &release_bindings; + break; + } + + for (KeybindingMap::iterator k = kbm->begin(); k != kbm->end(); ++k) { + if (k->second == action) { + kbm->erase (k); + break; + } + } + + if (can_save) { + Keyboard::save_keybindings (); + } } bool @@ -623,11 +734,11 @@ Bindings::load (const XMLNode& node) } void -Bindings::get_all_actions (std::vector<std::string>& names, - std::vector<std::string>& paths, +Bindings::get_all_actions (std::vector<std::string>& paths, + std::vector<std::string>& labels, std::vector<std::string>& tooltips, std::vector<std::string>& keys, - std::vector<KeyboardKey>& bindings) + std::vector<RefPtr<Action> >& actions) { if (!action_map) { return; @@ -644,30 +755,31 @@ Bindings::get_all_actions (std::vector<std::string>& names, /* get a list of all actions */ - ActionMap::Actions actions; - action_map->get_actions (actions); + ActionMap::Actions all_actions; + action_map->get_actions (all_actions); - for (ActionMap::Actions::const_iterator act = actions.begin(); act != actions.end(); ++act) { - names.push_back ((*act)->get_name()); + for (ActionMap::Actions::const_iterator act = all_actions.begin(); act != all_actions.end(); ++act) { + paths.push_back ((*act)->get_accel_path()); + labels.push_back ((*act)->get_label()); tooltips.push_back ((*act)->get_tooltip()); ReverseMap::iterator r = rmap.find (*act); + if (r != rmap.end()) { keys.push_back (gtk_accelerator_get_label (r->second.key(), (GdkModifierType) r->second.state())); - bindings.push_back (r->second); } else { keys.push_back (string()); - bindings.push_back (KeyboardKey::null_key()); } + + actions.push_back (*act); } } - + void -Bindings::get_all_actions (std::vector<std::string>& groups, +Bindings::get_all_actions (std::vector<std::string>& names, std::vector<std::string>& paths, - std::vector<std::string>& tooltips, - std::vector<KeyboardKey>& bindings) + std::vector<std::string>& keys) { /* build a reverse map from actions to bindings */ @@ -684,15 +796,14 @@ Bindings::get_all_actions (std::vector<std::string>& groups, action_map->get_actions (actions); for (ActionMap::Actions::const_iterator act = actions.begin(); act != actions.end(); ++act) { - groups.push_back ((*act)->get_name()); + names.push_back ((*act)->get_name()); paths.push_back ((*act)->get_accel_path()); - tooltips.push_back ((*act)->get_tooltip()); ReverseMap::iterator r = rmap.find (*act); if (r != rmap.end()) { - bindings.push_back (r->second); + keys.push_back (gtk_accelerator_get_label (r->second.key(), (GdkModifierType) r->second.state())); } else { - bindings.push_back (KeyboardKey::null_key()); + keys.push_back (string()); } } } @@ -856,6 +967,7 @@ Bindings::remove_bindings_for_state (std::string const& name, Bindings& bindings } std::ostream& operator<<(std::ostream& out, Gtkmm2ext::KeyboardKey const & k) { - return out << "Key " << k.key() << " (" << (k.key() > 0 ? gdk_keyval_name (k.key()) : "no-key") << ") state " << k.state(); + char const *gdk_name = gdk_keyval_name (k.key()); + return out << "Key " << k.key() << " (" << (gdk_name ? gdk_name : "no-key") << ") state " << k.state(); } diff --git a/libs/gtkmm2ext/gtkmm2ext/bindings.h b/libs/gtkmm2ext/gtkmm2ext/bindings.h index 93e2ff78c5..af451e10ed 100644 --- a/libs/gtkmm2ext/gtkmm2ext/bindings.h +++ b/libs/gtkmm2ext/gtkmm2ext/bindings.h @@ -120,8 +120,11 @@ class LIBGTKMM2EXT_API Bindings { bool empty_keys () const; bool empty_mouse () const; - void add (KeyboardKey, Operation, Glib::RefPtr<Gtk::Action>); - void remove (KeyboardKey, Operation); + void add (KeyboardKey, Operation, Glib::RefPtr<Gtk::Action>, bool can_save = false); + bool replace (KeyboardKey, Operation, std::string const& action_name, bool can_save = true); + void remove (KeyboardKey, Operation, bool can_save = false); + void remove (Glib::RefPtr<Gtk::Action>, Operation, bool can_save = false); + bool activate (KeyboardKey, Operation); void add (MouseButton, Operation, Glib::RefPtr<Gtk::Action>); @@ -140,16 +143,17 @@ class LIBGTKMM2EXT_API Bindings { } static uint32_t ignored_state() { return _ignored_state; } + /* used to list all actions */ void get_all_actions (std::vector<std::string>& names, std::vector<std::string>& paths, - std::vector<std::string>& tooltips, - std::vector<std::string>& keys, - std::vector<KeyboardKey>& bindings); - - void get_all_actions (std::vector<std::string>& groups, - std::vector<std::string>& paths, + std::vector<std::string>& keys); + + /* used for editing bindings */ + void get_all_actions (std::vector<std::string>& paths, + std::vector<std::string>& labels, std::vector<std::string>& tooltips, - std::vector<KeyboardKey>& bindings); + std::vector<std::string>& keys, + std::vector<Glib::RefPtr<Gtk::Action> >& actions); static std::map<std::string,Bindings*> bindings_for_state; diff --git a/libs/gtkmm2ext/keyboard.cc b/libs/gtkmm2ext/keyboard.cc index 0b7ac47471..efd189ddc1 100644 --- a/libs/gtkmm2ext/keyboard.cc +++ b/libs/gtkmm2ext/keyboard.cc @@ -709,6 +709,8 @@ Keyboard::store_keybindings (string const & path) XMLNode* bnode; int ret = 0; + std::cerr << "Save bindings to " << path << endl; + for (map<string,Bindings*>::const_iterator c = Bindings::bindings_for_state.begin(); c != Bindings::bindings_for_state.end(); ++c) { bnode = new XMLNode (X_("Bindings")); bnode->add_property (X_("name"), c->first); |