X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fgtkmm2ext%2Fbindings.cc;h=ff44da4b32c5487d6cf8be2e4f4d7681e4370a33;hb=cf52d6e4b40111eb04b244ec054055a4ec15dbe0;hp=583d3515d2415f2101d1b7dde87aa76d3d295dca;hpb=aa71d60ab990275bddd4a9ae0e378ed36e0c0fd4;p=ardour.git diff --git a/libs/gtkmm2ext/bindings.cc b/libs/gtkmm2ext/bindings.cc index 583d3515d2..ff44da4b32 100644 --- a/libs/gtkmm2ext/bindings.cc +++ b/libs/gtkmm2ext/bindings.cc @@ -1,19 +1,19 @@ /* - Copyright (C) 2012 Paul Davis + Copyright (C) 2012 Paul Davis - 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 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. + 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., 675 Mass Ave, Cambridge, MA 02139, USA. + 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., 675 Mass Ave, Cambridge, MA 02139, USA. */ @@ -26,6 +26,7 @@ #include "pbd/convert.h" #include "pbd/debug.h" #include "pbd/error.h" +#include "pbd/replace_all.h" #include "pbd/xml++.h" #include "gtkmm2ext/actions.h" @@ -34,7 +35,7 @@ #include "gtkmm2ext/keyboard.h" #include "gtkmm2ext/utils.h" -#include "i18n.h" +#include "pbd/i18n.h" using namespace std; using namespace Glib; @@ -46,102 +47,121 @@ list Bindings::bindings; /* global. Gulp */ list ActionMap::action_maps; /* global. Gulp */ PBD::Signal1 Bindings::BindingsChanged; +template +struct ActionNameRegistered +{ + ActionNameRegistered(std::string const& name) + : action_name(name) + {} + + bool operator()(IteratorValueType elem) const { + return elem.second.action_name == action_name; + } + std::string const& action_name; +}; + MouseButton::MouseButton (uint32_t state, uint32_t keycode) { - uint32_t ignore = ~Keyboard::RelevantModifierKeyMask; + uint32_t ignore = ~Keyboard::RelevantModifierKeyMask; - if (gdk_keyval_is_upper (keycode) && gdk_keyval_is_lower (keycode)) { - /* key is not subject to case, so ignore SHIFT - */ - ignore |= GDK_SHIFT_MASK; - } + /* this is a slightly wierd test that relies on + * gdk_keyval_is_{upper,lower}() returning true for keys that have no + * case-sensitivity. This covers mostly non-alphanumeric keys. + */ + + if (gdk_keyval_is_upper (keycode) && gdk_keyval_is_lower (keycode)) { + /* key is not subject to case, so ignore SHIFT + */ + ignore |= GDK_SHIFT_MASK; + } - _val = (state & ~ignore); - _val <<= 32; - _val |= keycode; + _val = (state & ~ignore); + _val <<= 32; + _val |= keycode; }; bool MouseButton::make_button (const string& str, MouseButton& b) { - int s = 0; + int s = 0; - if (str.find ("Primary") != string::npos) { - s |= Keyboard::PrimaryModifier; - } + if (str.find ("Primary") != string::npos) { + s |= Keyboard::PrimaryModifier; + } - if (str.find ("Secondary") != string::npos) { - s |= Keyboard::SecondaryModifier; - } + if (str.find ("Secondary") != string::npos) { + s |= Keyboard::SecondaryModifier; + } - if (str.find ("Tertiary") != string::npos) { - s |= Keyboard::TertiaryModifier; - } + if (str.find ("Tertiary") != string::npos) { + s |= Keyboard::TertiaryModifier; + } - if (str.find ("Level4") != string::npos) { - s |= Keyboard::Level4Modifier; - } + if (str.find ("Level4") != string::npos) { + s |= Keyboard::Level4Modifier; + } - string::size_type lastmod = str.find_last_of ('-'); - uint32_t button_number; + string::size_type lastmod = str.find_last_of ('-'); + uint32_t button_number; - if (lastmod == string::npos) { - button_number = PBD::atoi (str); - } else { - button_number = PBD::atoi (str.substr (lastmod+1)); - } + if (lastmod == string::npos) { + button_number = PBD::atoi (str); + } else { + button_number = PBD::atoi (str.substr (lastmod+1)); + } - b = MouseButton (s, button_number); - return true; + b = MouseButton (s, button_number); + return true; } string MouseButton::name () const { - int s = state(); - - string str; - - if (s & Keyboard::PrimaryModifier) { - str += "Primary"; - } - if (s & Keyboard::SecondaryModifier) { - if (!str.empty()) { - str += '-'; - } - str += "Secondary"; - } - if (s & Keyboard::TertiaryModifier) { - if (!str.empty()) { - str += '-'; - } - str += "Tertiary"; - } - if (s & Keyboard::Level4Modifier) { - if (!str.empty()) { - str += '-'; - } - str += "Level4"; - } - - if (!str.empty()) { - str += '-'; - } - - char buf[16]; - snprintf (buf, sizeof (buf), "%u", button()); - str += buf; - - return str; + int s = state(); + + string str; + + if (s & Keyboard::PrimaryModifier) { + str += "Primary"; + } + if (s & Keyboard::SecondaryModifier) { + if (!str.empty()) { + str += '-'; + } + str += "Secondary"; + } + if (s & Keyboard::TertiaryModifier) { + if (!str.empty()) { + str += '-'; + } + str += "Tertiary"; + } + if (s & Keyboard::Level4Modifier) { + if (!str.empty()) { + str += '-'; + } + str += "Level4"; + } + + if (!str.empty()) { + str += '-'; + } + + char buf[16]; + snprintf (buf, sizeof (buf), "%u", button()); + str += buf; + + return str; } +/*================================ KeyboardKey ================================*/ KeyboardKey::KeyboardKey (uint32_t state, uint32_t keycode) { - uint32_t ignore = ~Keyboard::RelevantModifierKeyMask; + uint32_t ignore = ~Keyboard::RelevantModifierKeyMask; - _val = (state & ~ignore); - _val <<= 32; - _val |= keycode; + _val = (state & ~ignore); + _val <<= 32; + _val |= keycode; } string @@ -155,93 +175,212 @@ KeyboardKey::display_label () const * on all platforms, notably the command key on OS X. */ - return gtk_accelerator_get_label (key(), (GdkModifierType) state()); + uint32_t mod = state(); + +#ifdef __APPLE__ + /* We use both bits (MOD2|META) for Primary on OS X, + * but we don't want MOD2 showing up in listings. + */ + + if (mod & GDK_MOD2_MASK) { + mod &= ~GDK_MOD2_MASK; + } +#endif + + return gtk_accelerator_get_label (key(), (GdkModifierType) mod); } string KeyboardKey::name () const { - int s = state(); - - string str; - - if (s & Keyboard::PrimaryModifier) { - str += "Primary"; - } - if (s & Keyboard::SecondaryModifier) { - if (!str.empty()) { - str += '-'; - } - str += "Secondary"; - } - if (s & Keyboard::TertiaryModifier) { - if (!str.empty()) { - str += '-'; - } - str += "Tertiary"; - } - if (s & Keyboard::Level4Modifier) { - if (!str.empty()) { - str += '-'; - } - str += "Level4"; - } - - if (!str.empty()) { - str += '-'; - } - - char const *gdk_name = gdk_keyval_name (key()); - - if (gdk_name) { - str += gdk_name; - } else { - /* fail! */ - return string(); - } - - return str; + int s = state(); + + string str; + + if (s & Keyboard::PrimaryModifier) { + str += "Primary"; + } + if (s & Keyboard::SecondaryModifier) { + if (!str.empty()) { + str += '-'; + } + str += "Secondary"; + } + if (s & Keyboard::TertiaryModifier) { + if (!str.empty()) { + str += '-'; + } + str += "Tertiary"; + } + if (s & Keyboard::Level4Modifier) { + if (!str.empty()) { + str += '-'; + } + str += "Level4"; + } + + if (!str.empty()) { + str += '-'; + } + + char const *gdk_name = gdk_keyval_name (key()); + + if (gdk_name) { + str += gdk_name; + } else { + /* fail! */ + return string(); + } + + return str; +} + +string +KeyboardKey::native_name () const +{ + int s = state(); + + string str; + + if (s & Keyboard::PrimaryModifier) { + str += Keyboard::primary_modifier_name (); + } + if (s & Keyboard::SecondaryModifier) { + if (!str.empty()) { + str += '-'; + } + str += Keyboard::secondary_modifier_name (); + } + if (s & Keyboard::TertiaryModifier) { + if (!str.empty()) { + str += '-'; + } + str += Keyboard::tertiary_modifier_name (); + } + if (s & Keyboard::Level4Modifier) { + if (!str.empty()) { + str += '-'; + } + str += Keyboard::level4_modifier_name (); + } + + if (!str.empty()) { + str += '-'; + } + + char const *gdk_name = gdk_keyval_name (key()); + + if (gdk_name) { + str += gdk_name; + } else { + /* fail! */ + return string(); + } + + return str; +} + +string +KeyboardKey::native_short_name () const +{ + int s = state(); + + string str; + + if (s & Keyboard::PrimaryModifier) { + str += Keyboard::primary_modifier_short_name (); + } + if (s & Keyboard::SecondaryModifier) { + if (!str.empty()) { + str += '-'; + } + str += Keyboard::secondary_modifier_short_name (); + } + if (s & Keyboard::TertiaryModifier) { + if (!str.empty()) { + str += '-'; + } + str += Keyboard::tertiary_modifier_short_name (); + } + if (s & Keyboard::Level4Modifier) { + if (!str.empty()) { + str += '-'; + } + str += Keyboard::level4_modifier_short_name (); + } + + if (!str.empty()) { + str += '-'; + } + + char const *gdk_name = gdk_keyval_name (key()); + + if (gdk_name) { + str += gdk_name; + } else { + /* fail! */ + return string(); + } + + return str; } bool KeyboardKey::make_key (const string& str, KeyboardKey& k) { - int s = 0; + int s = 0; - if (str.find ("Primary") != string::npos) { - s |= Keyboard::PrimaryModifier; - } + if (str.find ("Primary") != string::npos) { + s |= Keyboard::PrimaryModifier; + } - if (str.find ("Secondary") != string::npos) { - s |= Keyboard::SecondaryModifier; - } + if (str.find ("Secondary") != string::npos) { + s |= Keyboard::SecondaryModifier; + } - if (str.find ("Tertiary") != string::npos) { - s |= Keyboard::TertiaryModifier; - } + if (str.find ("Tertiary") != string::npos) { + s |= Keyboard::TertiaryModifier; + } - if (str.find ("Level4") != string::npos) { - s |= Keyboard::Level4Modifier; - } + if (str.find ("Level4") != string::npos) { + s |= Keyboard::Level4Modifier; + } - string::size_type lastmod = str.find_last_of ('-'); - guint keyval; + /* since all SINGLE key events keycodes are changed to lower case + * before looking them up, make sure we only store lower case here. The + * Shift part will be stored in the modifier part of the KeyboardKey. + * + * And yes Mildred, this doesn't cover CapsLock cases. Oh well. + */ - if (lastmod == string::npos) { - keyval = gdk_keyval_from_name (str.c_str()); - } else { - keyval = gdk_keyval_from_name (str.substr (lastmod+1).c_str()); - } + string actual; - if (keyval == GDK_VoidSymbol || keyval == 0) { - return false; - } + string::size_type lastmod = str.find_last_of ('-'); - k = KeyboardKey (s, keyval); + if (lastmod != string::npos) { + actual = str.substr (lastmod+1); + } + else { + actual = str; + } + + if (actual.size() == 1) { + actual = PBD::downcase (actual); + } - return true; + guint keyval; + keyval = gdk_keyval_from_name (actual.c_str()); + + if (keyval == GDK_VoidSymbol || keyval == 0) { + return false; + } + + k = KeyboardKey (s, keyval); + + return true; } +/*================================= Bindings =================================*/ Bindings::Bindings (std::string const& name) : _name (name) , _action_map (0) @@ -266,49 +405,49 @@ Bindings::get_binding_for_action (RefPtr action, Operation& op) { const string action_name = ardour_action_name (action); - for (KeybindingMap::iterator k = press_bindings.begin(); k != press_bindings.end(); ++k) { + for (KeybindingMap::iterator k = press_bindings.begin(); k != press_bindings.end(); ++k) { - /* option one: action has already been associated with the - * binding - */ + /* option one: action has already been associated with the + * binding + */ - if (k->second.action == action) { - return k->first; - } + if (k->second.action == action) { + return k->first; + } - /* option two: action name matches, so lookup the action, - * setup the association while we're here, and return the binding. - */ + /* option two: action name matches, so lookup the action, + * setup the association while we're here, and return the binding. + */ - if (_action_map && k->second.action_name == action_name) { - k->second.action = _action_map->find_action (action_name); - return k->first; - } + if (_action_map && k->second.action_name == action_name) { + k->second.action = _action_map->find_action (action_name); + return k->first; + } - } + } - for (KeybindingMap::iterator k = release_bindings.begin(); k != release_bindings.end(); ++k) { + for (KeybindingMap::iterator k = release_bindings.begin(); k != release_bindings.end(); ++k) { - /* option one: action has already been associated with the - * binding - */ + /* option one: action has already been associated with the + * binding + */ - if (k->second.action == action) { - return k->first; - } + if (k->second.action == action) { + return k->first; + } - /* option two: action name matches, so lookup the action, - * setup the association while we're here, and return the binding. - */ + /* option two: action name matches, so lookup the action, + * setup the association while we're here, and return the binding. + */ - if (_action_map && k->second.action_name == action_name) { - k->second.action = _action_map->find_action (action_name); - return k->first; - } + if (_action_map && k->second.action_name == action_name) { + k->second.action = _action_map->find_action (action_name); + return k->first; + } - } + } - return KeyboardKey::null_key(); + return KeyboardKey::null_key(); } void @@ -346,44 +485,42 @@ Bindings::empty() const bool Bindings::activate (KeyboardKey kb, Operation op) { - 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()) { - /* no entry for this key in the state map */ - DEBUG_TRACE (DEBUG::Bindings, string_compose ("no binding for %1\n", kb)); - return false; - } - - RefPtr action; - - if (k->second.action) { - action = k->second.action; - } else { - if (_action_map) { - action = _action_map->find_action (k->second.action_name); - } - } - - if (action) { - /* lets do it ... */ - DEBUG_TRACE (DEBUG::Bindings, string_compose ("binding for %1: %2\n", kb, k->second.action_name)); - action->activate (); - } - - /* return true even if the action could not be found */ - - return true; + KeybindingMap& kbm = get_keymap (op); + + /* if shift was pressed, GDK will send us (e.g) 'E' rather than 'e'. + Our bindings all use the lower case character/keyname, so switch + to the lower case before doing the lookup. + */ + + KeyboardKey unshifted (kb.state(), gdk_keyval_to_lower (kb.key())); + + KeybindingMap::iterator k = kbm.find (unshifted); + + if (k == kbm.end()) { + /* no entry for this key in the state map */ + DEBUG_TRACE (DEBUG::Bindings, string_compose ("no binding for %1\n", unshifted)); + return false; + } + + RefPtr action; + + if (k->second.action) { + action = k->second.action; + } else { + if (_action_map) { + action = _action_map->find_action (k->second.action_name); + } + } + + if (action) { + /* lets do it ... */ + DEBUG_TRACE (DEBUG::Bindings, string_compose ("binding for %1: %2\n", unshifted, k->second.action_name)); + action->activate (); + } + + /* return true even if the action could not be found */ + + return true; } void @@ -436,36 +573,32 @@ Bindings::dissociate () void Bindings::push_to_gtk (KeyboardKey kb, RefPtr 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. - */ - - uint32_t gtk_legal_keyval = kb.key(); - possibly_translate_keyval_to_make_legal_accelerator (gtk_legal_keyval); - KeyboardKey gtk_binding (kb.state(), gtk_legal_keyval); - Gtk::AccelKey gtk_key; + /* 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; bool entry_exists = Gtk::AccelMap::lookup_entry (what->get_accel_path(), gtk_key); - if (!entry_exists) { - - /* 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()); - } + if (!entry_exists) { + + /* 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(), kb.key(), (Gdk::ModifierType) kb.state()); + } } bool @@ -475,333 +608,376 @@ Bindings::replace (KeyboardKey kb, Operation op, string const & action_name, boo 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_map->find_action (action_name); + if (is_registered(op, action_name)) { + remove (op, action_name, can_save); + } - if (!action) { - return false; - } + /* XXX need a way to get the old group name */ + add (kb, op, action_name, 0, can_save); - KeybindingMap* kbm = 0; + return true; +} - switch (op) { - case Press: - kbm = &press_bindings; - break; - case Release: - kbm = &release_bindings; - break; - } +bool +Bindings::add (KeyboardKey kb, Operation op, string const& action_name, XMLProperty const* group, bool can_save) +{ + if (is_registered (op, action_name)) { + return false; + } - KeybindingMap::iterator k = kbm->find (kb); + KeybindingMap& kbm = get_keymap (op); + if (group) { + KeybindingMap::value_type new_pair = make_pair (kb, ActionInfo (action_name, group->value())); + kbm.insert (new_pair).first; + } else { + KeybindingMap::value_type new_pair = make_pair (kb, ActionInfo (action_name)); + kbm.insert (new_pair).first; + } - if (k != kbm->end()) { - kbm->erase (k); - } + if (can_save) { + Keyboard::keybindings_changed (); + } - /* now linear search by action */ + BindingsChanged (this); /* EMIT SIGNAL */ + return true; +} - for (k = kbm->begin(); k != kbm->end(); ++k) { - if (k->second.action_name == action_name) { - kbm->erase (k); - break; - } - } +bool +Bindings::remove (Operation op, std::string const& action_name, bool can_save) +{ + bool erased_action = false; + KeybindingMap& kbm = get_keymap (op); + for (KeybindingMap::iterator k = kbm.begin(); k != kbm.end(); ++k) { + if (k->second.action_name == action_name) { + kbm.erase (k); + erased_action = true; + break; + } + } - add (kb, op, action_name, can_save); + if (!erased_action) { + return erased_action; + } - /* for now, this never fails */ + if (can_save) { + Keyboard::keybindings_changed (); + } - return true; + BindingsChanged (this); /* EMIT SIGNAL */ + return erased_action; } -void -Bindings::add (KeyboardKey kb, Operation op, string const& action_name, bool can_save) + +bool +Bindings::activate (MouseButton bb, Operation op) { - KeybindingMap* kbm = 0; + MouseButtonBindingMap& bbm = get_mousemap(op); - switch (op) { - case Press: - kbm = &press_bindings; - break; - case Release: - kbm = &release_bindings; - break; - } + MouseButtonBindingMap::iterator b = bbm.find (bb); - KeybindingMap::iterator k = kbm->find (kb); + if (b == bbm.end()) { + /* no entry for this key in the state map */ + return false; + } - if (k != kbm->end()) { - kbm->erase (k); - } - KeybindingMap::value_type new_pair (kb, ActionInfo (action_name)); + RefPtr action; - kbm->insert (new_pair).first; + if (b->second.action) { + action = b->second.action; + } else { + if (_action_map) { + action = _action_map->find_action (b->second.action_name); + } + } + + if (action) { + /* lets do it ... */ + DEBUG_TRACE (DEBUG::Bindings, string_compose ("activating action %1\n", ardour_action_name (action))); + action->activate (); + } - if (can_save) { - Keyboard::keybindings_changed (); - } + /* return true even if the action could not be found */ - BindingsChanged (this); /* EMIT SIGNAL */ + return true; } void -Bindings::remove (KeyboardKey kb, Operation op, bool can_save) +Bindings::add (MouseButton bb, Operation op, string const& action_name, XMLProperty const* /*group*/) { - KeybindingMap* kbm = 0; + MouseButtonBindingMap& bbm = get_mousemap(op); - 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); - } - - if (can_save) { - Keyboard::keybindings_changed (); - } - - BindingsChanged (this); /* EMIT SIGNAL */ + MouseButtonBindingMap::value_type newpair (bb, ActionInfo (action_name)); + bbm.insert (newpair); } void -Bindings::remove (RefPtr action, Operation op, bool can_save) +Bindings::remove (MouseButton bb, Operation op) { - 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 == action) { - kbm->erase (k); - break; - } - } - - if (can_save) { - Keyboard::keybindings_changed (); - } - - BindingsChanged (this); /* EMIT SIGNAL */ -} + MouseButtonBindingMap& bbm = get_mousemap(op); + MouseButtonBindingMap::iterator b = bbm.find (bb); -bool -Bindings::activate (MouseButton bb, Operation op) -{ - MouseButtonBindingMap* bbm = 0; - - switch (op) { - case Press: - bbm = &button_press_bindings; - break; - case Release: - bbm = &button_release_bindings; - break; - } - - MouseButtonBindingMap::iterator b = bbm->find (bb); - - if (b == bbm->end()) { - /* no entry for this key in the state map */ - return false; - } - - RefPtr action; - - if (b->second.action) { - action = b->second.action; - } else { - if (_action_map) { - action = _action_map->find_action (b->second.action_name); - } - } - - if (action) { - /* lets do it ... */ - DEBUG_TRACE (DEBUG::Bindings, string_compose ("activating action %1\n", ardour_action_name (action))); - action->activate (); - } - - /* return true even if the action could not be found */ - - return true; + if (b != bbm.end()) { + bbm.erase (b); + } } void -Bindings::add (MouseButton bb, Operation op, string const& action_name) +Bindings::save (XMLNode& root) { - MouseButtonBindingMap* bbm = 0; - - switch (op) { - case Press: - bbm = &button_press_bindings; - break; - case Release: - bbm = &button_release_bindings; - break; - } - - MouseButtonBindingMap::value_type newpair (bb, ActionInfo (action_name)); - bbm->insert (newpair); + XMLNode* presses = new XMLNode (X_("Press")); + + for (KeybindingMap::iterator k = press_bindings.begin(); k != press_bindings.end(); ++k) { + XMLNode* child; + + if (k->first.name().empty()) { + continue; + } + + child = new XMLNode (X_("Binding")); + child->add_property (X_("key"), k->first.name()); + child->add_property (X_("action"), k->second.action_name); + presses->add_child_nocopy (*child); + } + + for (MouseButtonBindingMap::iterator k = button_press_bindings.begin(); k != button_press_bindings.end(); ++k) { + XMLNode* child; + child = new XMLNode (X_("Binding")); + child->add_property (X_("button"), k->first.name()); + child->add_property (X_("action"), k->second.action_name); + presses->add_child_nocopy (*child); + } + + XMLNode* releases = new XMLNode (X_("Release")); + + for (KeybindingMap::iterator k = release_bindings.begin(); k != release_bindings.end(); ++k) { + XMLNode* child; + + if (k->first.name().empty()) { + continue; + } + + child = new XMLNode (X_("Binding")); + child->add_property (X_("key"), k->first.name()); + child->add_property (X_("action"), k->second.action_name); + releases->add_child_nocopy (*child); + } + + for (MouseButtonBindingMap::iterator k = button_release_bindings.begin(); k != button_release_bindings.end(); ++k) { + XMLNode* child; + child = new XMLNode (X_("Binding")); + child->add_property (X_("button"), k->first.name()); + child->add_property (X_("action"), k->second.action_name); + releases->add_child_nocopy (*child); + } + + root.add_child_nocopy (*presses); + root.add_child_nocopy (*releases); } void -Bindings::remove (MouseButton bb, Operation op) +Bindings::save_all_bindings_as_html (ostream& ostr) { - MouseButtonBindingMap* bbm = 0; - - switch (op) { - case Press: - bbm = &button_press_bindings; - break; - case Release: - bbm = &button_release_bindings; - break; - } - - MouseButtonBindingMap::iterator b = bbm->find (bb); - - if (b != bbm->end()) { - bbm->erase (b); - } + if (bindings.empty()) { + return; + } + + + ostr << "\n\n"; + ostr << PROGRAM_NAME; + ostr << "\n"; + + + ostr << "\n"; + + ostr << "\n\n"; + + ostr << "
\n"; + + for (list::const_iterator b = bindings.begin(); b != bindings.end(); ++b) { + (*b)->save_as_html (ostr); + } + + ostr << "
\n"; + ostr << "\n"; + ostr << "\n"; } void -Bindings::save (XMLNode& root) +Bindings::save_as_html (ostream& ostr) const { - XMLNode* presses = new XMLNode (X_("Press")); - - for (KeybindingMap::iterator k = press_bindings.begin(); k != press_bindings.end(); ++k) { - XMLNode* child; - - if (k->first.name().empty()) { - continue; - } - - child = new XMLNode (X_("Binding")); - child->add_property (X_("key"), k->first.name()); - child->add_property (X_("action"), k->second.action_name); - presses->add_child_nocopy (*child); - } - - for (MouseButtonBindingMap::iterator k = button_press_bindings.begin(); k != button_press_bindings.end(); ++k) { - XMLNode* child; - child = new XMLNode (X_("Binding")); - child->add_property (X_("button"), k->first.name()); - child->add_property (X_("action"), k->second.action_name); - presses->add_child_nocopy (*child); - } - - XMLNode* releases = new XMLNode (X_("Release")); - - for (KeybindingMap::iterator k = release_bindings.begin(); k != release_bindings.end(); ++k) { - XMLNode* child; - - if (k->first.name().empty()) { - continue; - } - - child = new XMLNode (X_("Binding")); - child->add_property (X_("key"), k->first.name()); - child->add_property (X_("action"), k->second.action_name); - releases->add_child_nocopy (*child); - } - - for (MouseButtonBindingMap::iterator k = button_release_bindings.begin(); k != button_release_bindings.end(); ++k) { - XMLNode* child; - child = new XMLNode (X_("Binding")); - child->add_property (X_("button"), k->first.name()); - child->add_property (X_("action"), k->second.action_name); - releases->add_child_nocopy (*child); - } - - root.add_child_nocopy (*presses); - root.add_child_nocopy (*releases); + + if (!press_bindings.empty()) { + + ostr << "
\n"; + ostr << "

"; + ostr << name(); + ostr << "

\n\n"; + + /* first pass: separate by group */ + + typedef std::map > GroupMap; + GroupMap group_map; + + for (KeybindingMap::const_iterator k = press_bindings.begin(); k != press_bindings.end(); ++k) { + if (k->first.name().empty()) { + continue; + } + + string group_name; + if (!k->second.group_name.empty()) { + group_name = k->second.group_name; + } else { + group_name = X_("nogroup"); + } + + GroupMap::iterator gm = group_map.find (group_name); + if (gm == group_map.end()) { + std::vector li; + li.push_back (k); + group_map.insert (make_pair (group_name,li)); + } else { + gm->second.push_back (k); + } + } + + for (GroupMap::const_iterator gm = group_map.begin(); gm != group_map.end(); ++gm) { + + ostr << "
\n"; + ostr << "
" << gm->first << "
\n"; + + for (vector::const_iterator k = gm->second.begin(); k != gm->second.end(); ++k) { + + if ((*k)->first.name().empty()) { + continue; + } + + RefPtr action; + + if ((*k)->second.action) { + action = (*k)->second.action; + } else { + if (_action_map) { + action = _action_map->find_action ((*k)->second.action_name); + } + } + + if (!action) { + continue; + } + + string key_name = (*k)->first.native_short_name (); + replace_all (key_name, X_("KP_"), X_("Numpad ")); + + string::size_type pos; + + char const *targets[] = { X_("Separator"), X_("Add"), X_("Subtract"), X_("Decimal"), X_("Divide"), + X_("grave"), X_("comma"), X_("period"), X_("asterisk"), X_("backslash"), + X_("apostrophe"), X_("minus"), X_("plus"), X_("slash"), X_("semicolon"), + X_("colon"), X_("equal"), X_("bracketleft"), X_("bracketright"), + X_("ampersand"), X_("numbersign"), X_("parenleft"), X_("parenright"), + X_("quoteright"), X_("quoteleft"), X_("exclam"), X_("quotedbl"), + 0 + }; + + char const *replacements[] = { X_("-"), X_("+"), X_("-"), X_("."), X_("/"), + X_("`"), X_(","), X_("."), X_("*"), X_("\\"), + X_("'"), X_("-"), X_("+"), X_("/"), X_(";"), + X_(":"), X_("="), X_("{"), X_("{"), + X_("&"), X_("#"), X_("("), X_(")"), + X_("`"), X_("'"), X_("!"), X_("\""), + }; + + for (size_t n = 0; targets[n]; ++n) { + if ((pos = key_name.find (targets[n])) != string::npos) { + key_name.replace (pos, strlen (targets[n]), replacements[n]); + } + } + + ostr << "
" << key_name << "
"; + ostr << "
" << action->get_label() << "
\n"; + } + ostr << "
\n\n"; + } + + ostr << "
\n"; + } } bool Bindings::load (XMLNode const& node) { - const XMLNodeList& children (node.children()); + const XMLNodeList& children (node.children()); - press_bindings.clear (); - release_bindings.clear (); + press_bindings.clear (); + release_bindings.clear (); - for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) { - /* each node could be Press or Release */ - load_operation (**i); - } + for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) { + /* each node could be Press or Release */ + load_operation (**i); + } - return true; + return true; } void Bindings::load_operation (XMLNode const& node) { - if (node.name() == X_("Press") || node.name() == X_("Release")) { - - Operation op; - - if (node.name() == X_("Press")) { - op = Press; - } else { - op = Release; - } - - const XMLNodeList& children (node.children()); - - for (XMLNodeList::const_iterator p = children.begin(); p != children.end(); ++p) { - - XMLProperty* ap; - XMLProperty* kp; - XMLProperty* bp; - - ap = (*p)->property ("action"); - kp = (*p)->property ("key"); - bp = (*p)->property ("button"); - - if (!ap || (!kp && !bp)) { - continue; - } - - if (kp) { - KeyboardKey k; - if (!KeyboardKey::make_key (kp->value(), k)) { - continue; - } - add (k, op, ap->value()); - } else { - MouseButton b; - if (!MouseButton::make_button (bp->value(), b)) { - continue; - } - add (b, op, ap->value()); - } - } - } + if (node.name() == X_("Press") || node.name() == X_("Release")) { + + Operation op; + + if (node.name() == X_("Press")) { + op = Press; + } else { + op = Release; + } + + const XMLNodeList& children (node.children()); + + for (XMLNodeList::const_iterator p = children.begin(); p != children.end(); ++p) { + + XMLProperty const * ap; + XMLProperty const * kp; + XMLProperty const * bp; + XMLProperty const * gp; + XMLNode const * child = *p; + + ap = child->property ("action"); + kp = child->property ("key"); + bp = child->property ("button"); + gp = child->property ("group"); + + if (!ap || (!kp && !bp)) { + continue; + } + + if (kp) { + KeyboardKey k; + if (!KeyboardKey::make_key (kp->value(), k)) { + continue; + } + add (k, op, ap->value(), gp); + } else { + MouseButton b; + if (!MouseButton::make_button (bp->value(), b)) { + continue; + } + add (b, op, ap->value(), gp); + } + } + } } void @@ -868,6 +1044,56 @@ Bindings::associate_all () } } +bool +Bindings::is_bound (KeyboardKey const& kb, Operation op) const +{ + const KeybindingMap& km = get_keymap(op); + return km.find(kb) != km.end(); +} + +bool +Bindings::is_registered (Operation op, std::string const& action_name) const +{ + const KeybindingMap& km = get_keymap(op); + return std::find_if(km.begin(), km.end(), ActionNameRegistered(action_name)) != km.end(); +} + +Bindings::KeybindingMap& +Bindings::get_keymap (Operation op) +{ + switch (op) { + case Press: + return press_bindings; + case Release: + default: + return release_bindings; + } +} + +const Bindings::KeybindingMap& +Bindings::get_keymap (Operation op) const +{ + switch (op) { + case Press: + return press_bindings; + case Release: + default: + return release_bindings; + } +} + +Bindings::MouseButtonBindingMap& +Bindings::get_mousemap (Operation op) +{ + switch (op) { + case Press: + return button_press_bindings; + case Release: + default: + return button_release_bindings; + } +} + /*==========================================ACTION MAP =========================================*/ ActionMap::ActionMap (string const & name) @@ -899,13 +1125,13 @@ ActionMap::get_actions (ActionMap::Actions& acts) RefPtr ActionMap::find_action (const string& name) { - _ActionMap::iterator a = _actions.find (name); + _ActionMap::iterator a = _actions.find (name); - if (a != _actions.end()) { - return a->second; - } + if (a != _actions.end()) { + return a->second; + } - return RefPtr(); + return RefPtr(); } RefPtr @@ -930,42 +1156,42 @@ ActionMap::create_action_group (const string& name) RefPtr ActionMap::register_action (RefPtr group, const char* name, const char* label) { - string fullpath; + string fullpath; - RefPtr act = Action::create (name, label); + RefPtr act = Action::create (name, label); - fullpath = group->get_name(); - fullpath += '/'; - fullpath += name; + fullpath = group->get_name(); + fullpath += '/'; + fullpath += name; - if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) { - group->add (act); - return act; - } + if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) { + group->add (act); + return act; + } - /* already registered */ - return RefPtr (); + /* already registered */ + return RefPtr (); } RefPtr ActionMap::register_action (RefPtr group, const char* name, const char* label, sigc::slot sl) { - string fullpath; + string fullpath; - RefPtr act = Action::create (name, label); + RefPtr act = Action::create (name, label); - fullpath = group->get_name(); - fullpath += '/'; - fullpath += name; + fullpath = group->get_name(); + fullpath += '/'; + fullpath += name; - if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) { - group->add (act, sl); - return act; - } + if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) { + group->add (act, sl); + return act; + } - /* already registered */ - return RefPtr(); + /* already registered */ + return RefPtr(); } RefPtr @@ -974,22 +1200,22 @@ ActionMap::register_radio_action (RefPtr group, const char* name, const char* label, sigc::slot sl) { - string fullpath; + string fullpath; - RefPtr act = RadioAction::create (rgroup, name, label); - RefPtr ract = RefPtr::cast_dynamic(act); + RefPtr act = RadioAction::create (rgroup, name, label); + RefPtr ract = RefPtr::cast_dynamic(act); - fullpath = group->get_name(); - fullpath += '/'; - fullpath += name; + fullpath = group->get_name(); + fullpath += '/'; + fullpath += name; - if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) { - group->add (act, sl); - return act; - } + if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) { + group->add (act, sl); + return act; + } - /* already registered */ - return RefPtr(); + /* already registered */ + return RefPtr(); } RefPtr @@ -999,53 +1225,53 @@ ActionMap::register_radio_action (RefPtr group, sigc::slot sl, int value) { - string fullpath; + string fullpath; - RefPtr act = RadioAction::create (rgroup, name, label); - RefPtr ract = RefPtr::cast_dynamic(act); - ract->property_value() = value; + RefPtr act = RadioAction::create (rgroup, name, label); + RefPtr ract = RefPtr::cast_dynamic(act); + ract->property_value() = value; - fullpath = group->get_name(); - fullpath += '/'; - fullpath += name; + fullpath = group->get_name(); + fullpath += '/'; + fullpath += name; - if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) { - group->add (act, sigc::bind (sl, act->gobj())); - return act; - } + if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) { + group->add (act, sigc::bind (sl, act->gobj())); + return act; + } - /* already registered */ + /* already registered */ - return RefPtr(); + return RefPtr(); } RefPtr ActionMap::register_toggle_action (RefPtr group, const char* name, const char* label, sigc::slot sl) { - string fullpath; + string fullpath; - fullpath = group->get_name(); - fullpath += '/'; - fullpath += name; + fullpath = group->get_name(); + fullpath += '/'; + fullpath += name; - RefPtr act = ToggleAction::create (name, label); + RefPtr act = ToggleAction::create (name, label); - if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) { - group->add (act, sl); - return act; - } + if (_actions.insert (_ActionMap::value_type (fullpath, act)).second) { + group->add (act, sl); + return act; + } - /* already registered */ - return RefPtr(); + /* already registered */ + return RefPtr(); } void ActionMap::get_all_actions (std::vector& paths, - std::vector& labels, - std::vector& tooltips, - std::vector& keys, - std::vector >& actions) + std::vector& labels, + std::vector& tooltips, + std::vector& keys, + std::vector >& actions) { for (list::const_iterator map = action_maps.begin(); map != action_maps.end(); ++map) { @@ -1084,6 +1310,5 @@ ActionMap::get_all_actions (std::vector& paths, std::ostream& operator<<(std::ostream& out, Gtkmm2ext::KeyboardKey const & k) { char const *gdk_name = gdk_keyval_name (k.key()); - return out << "Key " << k.key() << " (" << (gdk_name ? gdk_name : "no-key") << ") state " << k.state(); + return out << "Key " << k.key() << " (" << (gdk_name ? gdk_name : "no-key") << ") state " << hex << k.state() << dec; } -