X-Git-Url: https://main.carlh.net/gitweb/?a=blobdiff_plain;f=libs%2Fgtkmm2ext%2Fbindings.cc;h=8803edc540e2adb938b2e6904ee052591f49eb73;hb=04a9ce757c018f8db7f2e4f2a293bc693fa5c118;hp=f96bd586d985ec6d7cbcb35957291885df535b08;hpb=2ba58dfe65bb0c5ba7d5eb18a1566fa79eeb6993;p=ardour.git diff --git a/libs/gtkmm2ext/bindings.cc b/libs/gtkmm2ext/bindings.cc index f96bd586d9..8803edc540 100644 --- a/libs/gtkmm2ext/bindings.cc +++ b/libs/gtkmm2ext/bindings.cc @@ -1,5 +1,5 @@ /* - 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 @@ -19,14 +19,20 @@ #include -#include +#include "pbd/gstdio_compat.h" +#include +#include -#include "pbd/xml++.h" #include "pbd/convert.h" +#include "pbd/debug.h" +#include "pbd/error.h" +#include "pbd/xml++.h" #include "gtkmm2ext/actions.h" #include "gtkmm2ext/bindings.h" +#include "gtkmm2ext/debug.h" #include "gtkmm2ext/keyboard.h" +#include "gtkmm2ext/utils.h" #include "i18n.h" @@ -34,13 +40,15 @@ using namespace std; using namespace Glib; using namespace Gtk; using namespace Gtkmm2ext; +using namespace PBD; uint32_t Bindings::_ignored_state = 0; +map Bindings::bindings_for_state; MouseButton::MouseButton (uint32_t state, uint32_t keycode) { uint32_t ignore = Bindings::ignored_state(); - + if (gdk_keyval_is_upper (keycode) && gdk_keyval_is_lower (keycode)) { /* key is not subject to case, so ignore SHIFT */ @@ -75,7 +83,7 @@ MouseButton::make_button (const string& str, MouseButton& b) string::size_type lastmod = str.find_last_of ('-'); uint32_t button_number; - + if (lastmod == string::npos) { button_number = PBD::atoi (str); } else { @@ -90,12 +98,12 @@ string MouseButton::name () const { int s = state(); - + string str; if (s & Keyboard::PrimaryModifier) { str += "Primary"; - } + } if (s & Keyboard::SecondaryModifier) { if (!str.empty()) { str += '-'; @@ -107,14 +115,14 @@ MouseButton::name () const str += '-'; } str += "Tertiary"; - } + } if (s & Keyboard::Level4Modifier) { if (!str.empty()) { str += '-'; } str += "Level4"; } - + if (!str.empty()) { str += '-'; } @@ -129,12 +137,6 @@ MouseButton::name () const KeyboardKey::KeyboardKey (uint32_t state, uint32_t keycode) { uint32_t ignore = Bindings::ignored_state(); - - 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; @@ -146,12 +148,12 @@ string KeyboardKey::name () const { int s = state(); - + string str; if (s & Keyboard::PrimaryModifier) { str += "Primary"; - } + } if (s & Keyboard::SecondaryModifier) { if (!str.empty()) { str += '-'; @@ -163,14 +165,14 @@ KeyboardKey::name () const str += '-'; } str += "Tertiary"; - } + } if (s & Keyboard::Level4Modifier) { if (!str.empty()) { str += '-'; } str += "Level4"; } - + if (!str.empty()) { str += '-'; } @@ -203,7 +205,7 @@ KeyboardKey::make_key (const string& str, KeyboardKey& k) string::size_type lastmod = str.find_last_of ('-'); guint keyval; - + if (lastmod == string::npos) { keyval = gdk_keyval_from_name (str.c_str()); } else { @@ -211,10 +213,11 @@ KeyboardKey::make_key (const string& str, KeyboardKey& k) } if (keyval == GDK_VoidSymbol) { - return false; + return false; } k = KeyboardKey (s, keyval); + return true; } @@ -225,6 +228,27 @@ Bindings::Bindings () Bindings::~Bindings() { + if (!_name.empty()) { + remove_bindings_for_state (_name, *this); + } +} + +bool +Bindings::empty_keys() const +{ + return press_bindings.empty() && release_bindings.empty(); +} + +bool +Bindings::empty_mouse () const +{ + return button_press_bindings.empty() && button_release_bindings.empty(); +} + +bool +Bindings::empty() const +{ + return empty_keys() && empty_mouse (); } void @@ -248,16 +272,19 @@ Bindings::activate (KeyboardKey kb, Operation op) kbm = &release_bindings; break; } - + KeybindingMap::iterator k = kbm->find (kb); if (k == kbm->end()) { /* no entry for this key in the state map */ - return false; + DEBUG_TRACE (DEBUG::Bindings, string_compose ("no binding for %1\n", kb)); + return false; } /* lets do it ... */ + DEBUG_TRACE (DEBUG::Bindings, string_compose ("binding for %1: %2\n", kb, k->second->get_name())); + k->second->activate (); return true; } @@ -269,7 +296,7 @@ Bindings::add (KeyboardKey kb, Operation op, RefPtr what) switch (op) { case Press: - kbm = &press_bindings; + kbm = &press_bindings; break; case Release: kbm = &release_bindings; @@ -279,12 +306,36 @@ Bindings::add (KeyboardKey kb, Operation op, RefPtr what) KeybindingMap::iterator k = kbm->find (kb); if (k == kbm->end()) { - pair > newpair (kb, what); + pair > newpair (kb, what); kbm->insert (newpair); - // cerr << "Bindings added " << kb.key() << " w/ " << kb.state() << " => " << what->get_name() << endl; } else { k->second = what; } + + Gtk::AccelKey gtk_key; + + /* tweak the binding so that GTK will accept it and display something + * acceptable + */ + + 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); + + + bool entry_exists = Gtk::AccelMap::lookup_entry (what->get_accel_path(), gtk_key); + + if (!entry_exists || gtk_key.get_key() == 0) { + Gtk::AccelMap::add_entry (what->get_accel_path(), + gtk_binding.key(), + (Gdk::ModifierType) gtk_binding.state()); + } else { + warning << string_compose (_("There is more than one key binding defined for %1. Both will work, but only the first will be visible in menus"), what->get_accel_path()) << endmsg; + } + + 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; + } } void @@ -323,7 +374,7 @@ Bindings::activate (MouseButton bb, Operation op) } MouseButtonBindingMap::iterator b = bbm->find (bb); - + if (b == bbm->end()) { /* no entry for this key in the state map */ return false; @@ -373,7 +424,7 @@ Bindings::remove (MouseButton bb, Operation op) bbm = &button_release_bindings; break; } - + MouseButtonBindingMap::iterator b = bbm->find (bb); if (b != bbm->end()) { @@ -387,7 +438,7 @@ Bindings::save (const string& path) XMLTree tree; XMLNode* root = new XMLNode (X_("Bindings")); tree.set_root (root); - + save (*root); if (!tree.write (path)) { @@ -446,7 +497,7 @@ Bindings::save (XMLNode& root) } bool -Bindings::load (const string& path) +Bindings::load (string const & name) { XMLTree tree; @@ -454,20 +505,55 @@ Bindings::load (const string& path) return false; } - if (!tree.read (path)) { - return false; + XMLNode const * node = Keyboard::bindings_node(); + + if (!node) { + error << string_compose (_("No keyboard binding information when loading bindings for \"%1\""), name) << endmsg; + return false; + } + + if (!_name.empty()) { + remove_bindings_for_state (_name, *this); } + const XMLNodeList& children (node->children()); + bool found = false; + + for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) { + + if ((*i)->name() == X_("Bindings")) { + XMLProperty const * prop = (*i)->property (X_("name")); + + if (!prop) { + continue; + } + + if (prop->value() == name) { + found = true; + node = *i; + break; + } + } + } + + if (!found) { + error << string_compose (_("Bindings for \"%1\" not found in keyboard binding node\n"), name) << endmsg; + return false; + } + press_bindings.clear (); release_bindings.clear (); - XMLNode& root (*tree.root()); - const XMLNodeList& children (root.children()); + const XMLNodeList& bindings (node->children()); - for (XMLNodeList::const_iterator i = children.begin(); i != children.end(); ++i) { - load (**i); + for (XMLNodeList::const_iterator i = bindings.begin(); i != bindings.end(); ++i) { + /* each node could be Press or Release */ + load (**i); } + add_bindings_for_state (_name, *this); + _name = name; + return true; } @@ -475,27 +561,27 @@ void Bindings::load (const XMLNode& 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; } @@ -503,7 +589,7 @@ Bindings::load (const XMLNode& node) RefPtr act; if (action_map) { - act = action_map->find_action (ap->value()); + act = action_map->find_action (ap->value()); } if (!act) { @@ -514,11 +600,11 @@ Bindings::load (const XMLNode& node) act = ActionManager::get_action (group.c_str(), action.c_str()); } } - + if (!act) { continue; } - + if (kp) { KeyboardKey k; if (!KeyboardKey::make_key (kp->value(), k)) { @@ -534,7 +620,90 @@ Bindings::load (const XMLNode& node) } } } -} +} + +void +Bindings::get_all_actions (std::vector& names, + std::vector& paths, + std::vector& tooltips, + std::vector& keys, + std::vector& bindings) +{ + if (!action_map) { + return; + } + + /* build a reverse map from actions to bindings */ + + typedef map,KeyboardKey> ReverseMap; + ReverseMap rmap; + + for (KeybindingMap::const_iterator k = press_bindings.begin(); k != press_bindings.end(); ++k) { + rmap.insert (make_pair (k->second, k->first)); + } + + /* get a list of all actions */ + + ActionMap::Actions actions; + action_map->get_actions (actions); + + for (ActionMap::Actions::const_iterator act = actions.begin(); act != actions.end(); ++act) { + 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()) { + 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()); + } + } +} + +void +Bindings::get_all_actions (std::vector& groups, + std::vector& paths, + std::vector& tooltips, + std::vector& bindings) +{ + /* build a reverse map from actions to bindings */ + + typedef map,KeyboardKey> ReverseMap; + ReverseMap rmap; + + for (KeybindingMap::const_iterator k = press_bindings.begin(); k != press_bindings.end(); ++k) { + rmap.insert (make_pair (k->second, k->first)); + } + + /* get a list of all actions */ + + ActionMap::Actions actions; + action_map->get_actions (actions); + + for (ActionMap::Actions::const_iterator act = actions.begin(); act != actions.end(); ++act) { + groups.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); + } else { + bindings.push_back (KeyboardKey::null_key()); + } + } +} + +void +ActionMap::get_actions (ActionMap::Actions& acts) +{ + for (_ActionMap::iterator a = actions.begin(); a != actions.end(); ++a) { + acts.push_back (a->second); + } +} RefPtr ActionMap::find_action (const string& name) @@ -548,26 +717,87 @@ ActionMap::find_action (const string& name) return RefPtr(); } +RefPtr +ActionMap::create_action_group (const string& name) +{ + RefPtr g = ActionGroup::create (name); + return g; +} + +void +ActionMap::install_action_group (RefPtr group) +{ + ActionManager::ui_manager->insert_action_group (group); +} + +RefPtr +ActionMap::register_action (RefPtr group, const char* name, const char* label) +{ + string fullpath; + + RefPtr act = Action::create (name, label); + + fullpath = group->get_name(); + fullpath += '/'; + fullpath += name; + + if (actions.insert (_ActionMap::value_type (fullpath, act)).second) { + group->add (act); + return act; + } + + /* already registered */ + return RefPtr (); +} + RefPtr -ActionMap::register_action (const char* path, +ActionMap::register_action (RefPtr group, const char* name, const char* label, sigc::slot sl) { string fullpath; RefPtr act = Action::create (name, label); - act->signal_activate().connect (sl); + fullpath = group->get_name(); + fullpath += '/'; + fullpath += name; + + if (actions.insert (_ActionMap::value_type (fullpath, act)).second) { + group->add (act, sl); + return act; + } + + /* already registered */ + return RefPtr(); +} + +RefPtr +ActionMap::register_radio_action (RefPtr group, + Gtk::RadioAction::Group& rgroup, + const char* name, const char* label, + sigc::slot sl) +{ + string fullpath; - fullpath = path; + RefPtr act = RadioAction::create (rgroup, name, label); + RefPtr ract = RefPtr::cast_dynamic(act); + + fullpath = group->get_name(); fullpath += '/'; fullpath += name; - actions.insert (_ActionMap::value_type (fullpath, act)); - return act; + if (actions.insert (_ActionMap::value_type (fullpath, act)).second) { + group->add (act, sl); + return act; + } + + /* already registered */ + return RefPtr(); } RefPtr -ActionMap::register_radio_action (const char* path, Gtk::RadioAction::Group& rgroup, +ActionMap::register_radio_action (RefPtr group, + Gtk::RadioAction::Group& rgroup, const char* name, const char* label, sigc::slot sl, int value) @@ -578,30 +808,54 @@ ActionMap::register_radio_action (const char* path, Gtk::RadioAction::Group& rgr RefPtr ract = RefPtr::cast_dynamic(act); ract->property_value() = value; - act->signal_activate().connect (sigc::bind (sl, act->gobj())); - - fullpath = path; + fullpath = group->get_name(); fullpath += '/'; fullpath += name; - actions.insert (_ActionMap::value_type (fullpath, act)); - return act; + if (actions.insert (_ActionMap::value_type (fullpath, act)).second) { + group->add (act, sigc::bind (sl, act->gobj())); + return act; + } + + /* already registered */ + + return RefPtr(); } RefPtr -ActionMap::register_toggle_action (const char* path, +ActionMap::register_toggle_action (RefPtr group, const char* name, const char* label, sigc::slot sl) { string fullpath; + fullpath = group->get_name(); + fullpath += '/'; + fullpath += name; + RefPtr act = ToggleAction::create (name, label); - act->signal_activate().connect (sl); + if (actions.insert (_ActionMap::value_type (fullpath, act)).second) { + group->add (act, sl); + return act; + } - fullpath = path; - fullpath += '/'; - fullpath += name; + /* already registered */ + return RefPtr(); +} - actions.insert (_ActionMap::value_type (fullpath, act)); - return act; +void +Bindings::add_bindings_for_state (std::string const& name, Bindings& bindings) +{ + bindings_for_state.insert (make_pair (name, &bindings)); } + +void +Bindings::remove_bindings_for_state (std::string const& name, Bindings& bindings) +{ + bindings_for_state.erase (name); +} + +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(); +} +