get normal GTK accelerators working again

This commit is contained in:
Paul Davis 2015-07-08 12:45:57 -04:00
parent af378e7256
commit 98b14ad0e6
8 changed files with 204 additions and 238 deletions

View File

@ -114,6 +114,7 @@ typedef uint64_t microseconds_t;
#include "binding_owners.h"
#include "bundle_manager.h"
#include "duplicate_routes_dialog.h"
#include "debug.h"
#include "engine_dialog.h"
#include "export_video_dialog.h"
#include "export_video_infobox.h"
@ -5087,13 +5088,19 @@ ARDOUR_UI::setup_toplevel_window (Gtk::Window& window, const string& name, void*
}
Gtkmm2ext::WindowTitle title (Glib::get_application_name());
title += name;
if (!name.empty()) {
title += name;
}
window.set_title (title.get_string());
window.set_wmclass (string_compose (X_("%1_%1"), downcase (PROGRAM_NAME), downcase (name)), PROGRAM_NAME);
window.set_flags (CAN_FOCUS);
window.add_events (Gdk::KEY_PRESS_MASK|Gdk::KEY_RELEASE_MASK);
window.add_accel_group (ActionManager::ui_manager->get_accel_group());
window.signal_configure_event().connect (sigc::mem_fun (*this, &ARDOUR_UI::configure_handler));
window.signal_window_state_event().connect (sigc::bind (sigc::mem_fun (*this, &ARDOUR_UI::tabbed_window_state_event_handler), owner));
window.signal_key_press_event().connect (sigc::bind (sigc::mem_fun (*this, &ARDOUR_UI::key_event_handler), &window), false);
@ -5101,69 +5108,207 @@ ARDOUR_UI::setup_toplevel_window (Gtk::Window& window, const string& name, void*
}
bool
ARDOUR_UI::key_event_handler (GdkEventKey* ev, Gtk::Window* window)
ARDOUR_UI::key_event_handler (GdkEventKey* ev, Gtk::Window* event_window)
{
switch (ev->type) {
case GDK_KEY_PRESS:
return key_press_handler (ev, window);
default:
break;
Gtkmm2ext::Bindings* bindings = 0;
Gtk::Window* window = 0;
/* until we get ardour bindings working, we are not handling key
* releases yet.
*/
if (ev->type != GDK_KEY_PRESS) {
return false;
}
if (event_window == &_main_window) {
window = event_window;
/* find current tab contents */
Gtk::Widget* w = _tabs.get_nth_page (_tabs.get_current_page());
/* see if it uses the ardour binding system */
HasBindings* bindable;
if ((bindable = dynamic_cast<HasBindings*> (w)) != 0) {
KeyboardKey k (ev->state, ev->keyval);
bindings = &bindable->bindings();
}
} else if (window != 0) {
window = event_window;
/* see if window uses ardour binding system */
} else {
window = &_main_window;
/* no window supplied, try our own bindings */
bindings = &_global_bindings;
}
return key_release_handler (ev, window);
return key_press_focus_accelerator_handler (*window, ev, bindings);
}
bool
ARDOUR_UI::key_press_handler (GdkEventKey* ev, Gtk::Window* event_window)
ARDOUR_UI::key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev, Gtkmm2ext::Bindings* bindings)
{
if (event_window == &_main_window) {
/* find current tab contents */
GtkWindow* win = window.gobj();
GtkWidget* focus = gtk_window_get_focus (win);
bool special_handling_of_unmodified_accelerators = false;
bool allow_activating = true;
/* consider all relevant modifiers but not LOCK or SHIFT */
const guint mask = (Keyboard::RelevantModifierKeyMask & ~(Gdk::SHIFT_MASK|Gdk::LOCK_MASK));
GdkModifierType modifier = GdkModifierType (ev->state);
modifier = GdkModifierType (modifier & gtk_accelerator_get_default_mod_mask());
Gtkmm2ext::possibly_translate_mod_to_make_legal_accelerator(modifier);
Gtk::Widget* w = _tabs.get_nth_page (_tabs.get_current_page());
if (focus) {
if (GTK_IS_ENTRY(focus) || Keyboard::some_magic_widget_has_focus()) {
special_handling_of_unmodified_accelerators = true;
}
}
/* see if it uses the ardour binding system */
#ifdef GTKOSX
/* at one time this appeared to be necessary. As of July 2012, it does not
appear to be. if it ever is necessar, figure out if it should apply
to all platforms.
*/
#if 0
if (Keyboard::some_magic_widget_has_focus ()) {
allow_activating = false;
}
#endif
#endif
HasBindings* bindable;
if ((bindable = dynamic_cast<HasBindings*> (w)) != 0) {
KeyboardKey k (ev->state, ev->keyval);
return bindable->bindings().activate (k, Bindings::Press);
DEBUG_TRACE (DEBUG::Accelerators, string_compose ("Win = %1 focus = %7 (%8) Key event: code = %2 state = %3 special handling ? %4 magic widget focus ? %5 allow_activation ? %6\n",
win,
ev->keyval,
show_gdk_event_state (ev->state),
special_handling_of_unmodified_accelerators,
Keyboard::some_magic_widget_has_focus(),
allow_activating,
focus,
(focus ? gtk_widget_get_name (focus) : "no focus widget")));
/* This exists to allow us to override the way GTK handles
key events. The normal sequence is:
a) event is delivered to a GtkWindow
b) accelerators/mnemonics are activated
c) if (b) didn't handle the event, propagate to
the focus widget and/or focus chain
The problem with this is that if the accelerators include
keys without modifiers, such as the space bar or the
letter "e", then pressing the key while typing into
a text entry widget results in the accelerator being
activated, instead of the desired letter appearing
in the text entry.
There is no good way of fixing this, but this
represents a compromise. The idea is that
key events involving modifiers (not Shift)
get routed into the activation pathway first, then
get propagated to the focus widget if necessary.
If the key event doesn't involve modifiers,
we deliver to the focus widget first, thus allowing
it to get "normal text" without interference
from acceleration.
Of course, this can also be problematic: if there
is a widget with focus, then it will swallow
all "normal text" accelerators.
*/
if (!special_handling_of_unmodified_accelerators) {
/* XXX note that for a brief moment, the conditional above
* included "|| (ev->state & mask)" so as to enforce the
* implication of special_handling_of_UNMODIFIED_accelerators.
* however, this forces any key that GTK doesn't allow and that
* we have an alternative (see next comment) for to be
* automatically sent through the accel groups activation
* pathway, which prevents individual widgets & canvas items
* from ever seeing it if is used by a key binding.
*
* specifically, this hid Ctrl-down-arrow from MIDI region
* views because it is also bound to an action.
*
* until we have a robust, clean binding system, this
* quirk will have to remain in place.
*/
/* pretend that certain key events that GTK does not allow
to be used as accelerators are actually something that
it does allow. but only where there are no modifiers.
*/
uint32_t fakekey = ev->keyval;
if (Gtkmm2ext::possibly_translate_keyval_to_make_legal_accelerator (fakekey)) {
DEBUG_TRACE (DEBUG::Accelerators, string_compose ("\tactivate (was %1 now %2) without special hanlding of unmodified accels\n",
ev->keyval, fakekey));
DEBUG_TRACE (DEBUG::Accelerators, string_compose ("\tmodified modifier was %1\n", show_gdk_event_state (modifier)));
if (allow_activating && gtk_accel_groups_activate(G_OBJECT(win), fakekey, modifier)) {
DEBUG_TRACE (DEBUG::Accelerators, "\taccel group activated by fakekey\n");
return true;
}
}
}
if (!special_handling_of_unmodified_accelerators || (ev->state & mask)) {
/* no special handling or there are modifiers in effect: accelerate first */
DEBUG_TRACE (DEBUG::Accelerators, "\tactivate, then propagate\n");
DEBUG_TRACE (DEBUG::Accelerators, string_compose ("\tevent send-event:%1 time:%2 length:%3 name %7 string:%4 hardware_keycode:%5 group:%6\n",
ev->send_event, ev->time, ev->length, ev->string, ev->hardware_keycode, ev->group, gdk_keyval_name (ev->keyval)));
if (allow_activating) {
DEBUG_TRACE (DEBUG::Accelerators, "\tsending to window\n");
if (gtk_accel_groups_activate (G_OBJECT(win), ev->keyval, modifier)) {
DEBUG_TRACE (DEBUG::Accelerators, "\t\thandled\n");
return true;
}
} else {
/* no bindings in current tab, use baroque GTK mechanism */
return key_press_focus_accelerator_handler (_main_window, ev);
DEBUG_TRACE (DEBUG::Accelerators, "\tactivation skipped\n");
}
DEBUG_TRACE (DEBUG::Accelerators, "\tnot accelerated, now propagate\n");
return gtk_window_propagate_key_event (win, ev);
}
/* no modifiers, propagate first */
DEBUG_TRACE (DEBUG::Accelerators, "\tpropagate, then activate\n");
if (!gtk_window_propagate_key_event (win, ev)) {
DEBUG_TRACE (DEBUG::Accelerators, "\tpropagation didn't handle, so activate\n");
if (allow_activating) {
return gtk_accel_groups_activate (G_OBJECT(win), ev->keyval, modifier);
} else {
DEBUG_TRACE (DEBUG::Accelerators, "\tactivation skipped\n");
}
} else {
/* no window supplied, try our own bindings */
KeyboardKey k (ev->state, ev->keyval);
return _global_bindings.activate (k, Bindings::Press);
}
}
bool
ARDOUR_UI::key_release_handler (GdkEventKey* ev, Gtk::Window* event_window)
{
if (event_window == &_main_window) {
/* find current tab contents */
Gtk::Widget* w = _tabs.get_nth_page (_tabs.get_current_page());
/* see if it uses the ardour binding system */
HasBindings* bindable;
if ((bindable = dynamic_cast<HasBindings*> (w)) != 0) {
KeyboardKey k (ev->state, ev->keyval);
return bindable->bindings().activate (k, Bindings::Release);
} else {
/* no bindings in current tab, use baroque GTK mechanism */
return key_press_focus_accelerator_handler (_main_window, ev);
}
} else {
/* no window supplied, try our own bindings */
KeyboardKey k (ev->state, ev->keyval);
return _global_bindings.activate (k, Bindings::Release);
>>>>>>> first compilable version of tabbable design.
DEBUG_TRACE (DEBUG::Accelerators, "\thandled by propagate\n");
return true;
}
DEBUG_TRACE (DEBUG::Accelerators, "\tnot handled\n");
return true;
}

View File

@ -336,9 +336,7 @@ class ARDOUR_UI : public Gtkmm2ext::UI, public ARDOUR::SessionHandlePtr
gpointer user_data);
bool tabbed_window_state_event_handler (GdkEventWindowState*, void* object);
bool key_press_handler (GdkEventKey*, Gtk::Window* event_window);
bool key_release_handler (GdkEventKey*, Gtk::Window* event_window);
bool key_event_handler (GdkEventKey*, Gtk::Window* window);
protected:
friend class PublicEditor;
@ -827,7 +825,7 @@ class ARDOUR_UI : public Gtkmm2ext::UI, public ARDOUR::SessionHandlePtr
void grab_focus_after_dialog ();
void tabs_switch (GtkNotebookPage*, guint page_number);
bool key_event_handler (GdkEventKey*, Gtk::Window* window);
bool key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev, Gtkmm2ext::Bindings*);
};
#endif /* __ardour_gui_h__ */

View File

@ -120,7 +120,7 @@ ARDOUR_UI::setup_windows ()
top_packer.pack_start (menu_bar_base, false, false);
#endif
main_vpacker.pack_start (top_packer);
main_vpacker.pack_start (top_packer, false, false);
/* now add the transport frame to the top of main window */

View File

@ -28,7 +28,7 @@ class HasBindings {
public:
HasBindings (Gtkmm2ext::Bindings& b) : _bindings (b) {}
Gtkmm2ext::Bindings bindings() const { return _bindings; }
Gtkmm2ext::Bindings& bindings() const { return _bindings; }
protected:
Gtkmm2ext::Bindings& _bindings;

View File

@ -4286,20 +4286,6 @@ Editor::mapped_clear_playlist (RouteTimeAxisView& atv, uint32_t /*sz*/)
atv.clear_playlist ();
}
bool
Editor::on_key_press_event (GdkEventKey* ev)
{
return key_press_focus_accelerator_handler (*current_toplevel(), ev);
}
bool
Editor::on_key_release_event (GdkEventKey* ev)
{
return false;
// return Gtk::Window::on_key_release_event (ev);
// return key_press_focus_accelerator_handler (*this, ev);
}
double
Editor::get_y_origin () const
{
@ -5957,7 +5943,6 @@ Editor::use_own_window ()
if (win && new_window) {
win->set_name ("EditorWindow");
win->add_accel_group (ActionManager::ui_manager->get_accel_group());
ARDOUR_UI::instance()->setup_toplevel_window (*win, _("Editor"), this);

View File

@ -2099,9 +2099,6 @@ class Editor : public PublicEditor, public PBD::ScopedConnectionList, public ARD
void toggle_gain_envelope_active ();
void reset_region_gain_envelopes ();
bool on_key_press_event (GdkEventKey*);
bool on_key_release_event (GdkEventKey*);
void session_state_saved (std::string);
Glib::RefPtr<Gtk::Action> undo_action;

View File

@ -297,12 +297,7 @@ ARDOUR_UI_UTILS::gdk_color_to_rgba (Gdk::Color const& c)
bool
ARDOUR_UI_UTILS::relay_key_press (GdkEventKey* ev, Gtk::Window* win)
{
switch (ev->type) {
case GDK_KEY_PRESS:
return ARDOUR_UI::instance()->key_press_handler (ev, win);
default:
return ARDOUR_UI::instance()->key_release_handler (ev, win);
}
return ARDOUR_UI::instance()->key_event_handler (ev, win);
}
bool
@ -334,8 +329,8 @@ ARDOUR_UI_UTILS::emulate_key_event (unsigned int keyval)
return relay_key_press(&ev);
}
static string
show_gdk_event_state (int state)
string
ARDOUR_UI_UTILS::show_gdk_event_state (int state)
{
string s;
if (state & GDK_SHIFT_MASK) {
@ -392,161 +387,6 @@ show_gdk_event_state (int state)
return s;
}
bool
ARDOUR_UI_UTILS::key_press_focus_accelerator_handler (Gtk::Window& window, GdkEventKey* ev)
{
GtkWindow* win = window.gobj();
GtkWidget* focus = gtk_window_get_focus (win);
bool special_handling_of_unmodified_accelerators = false;
bool allow_activating = true;
/* consider all relevant modifiers but not LOCK or SHIFT */
const guint mask = (Keyboard::RelevantModifierKeyMask & ~(Gdk::SHIFT_MASK|Gdk::LOCK_MASK));
GdkModifierType modifier = GdkModifierType (ev->state);
modifier = GdkModifierType (modifier & gtk_accelerator_get_default_mod_mask());
Gtkmm2ext::possibly_translate_mod_to_make_legal_accelerator(modifier);
if (focus) {
if (GTK_IS_ENTRY(focus) || Keyboard::some_magic_widget_has_focus()) {
special_handling_of_unmodified_accelerators = true;
}
}
#ifdef GTKOSX
/* at one time this appeared to be necessary. As of July 2012, it does not
appear to be. if it ever is necessar, figure out if it should apply
to all platforms.
*/
#if 0
if (Keyboard::some_magic_widget_has_focus ()) {
allow_activating = false;
}
#endif
#endif
DEBUG_TRACE (DEBUG::Accelerators, string_compose ("Win = %1 focus = %7 (%8) Key event: code = %2 state = %3 special handling ? %4 magic widget focus ? %5 allow_activation ? %6\n",
win,
ev->keyval,
show_gdk_event_state (ev->state),
special_handling_of_unmodified_accelerators,
Keyboard::some_magic_widget_has_focus(),
allow_activating,
focus,
(focus ? gtk_widget_get_name (focus) : "no focus widget")));
/* This exists to allow us to override the way GTK handles
key events. The normal sequence is:
a) event is delivered to a GtkWindow
b) accelerators/mnemonics are activated
c) if (b) didn't handle the event, propagate to
the focus widget and/or focus chain
The problem with this is that if the accelerators include
keys without modifiers, such as the space bar or the
letter "e", then pressing the key while typing into
a text entry widget results in the accelerator being
activated, instead of the desired letter appearing
in the text entry.
There is no good way of fixing this, but this
represents a compromise. The idea is that
key events involving modifiers (not Shift)
get routed into the activation pathway first, then
get propagated to the focus widget if necessary.
If the key event doesn't involve modifiers,
we deliver to the focus widget first, thus allowing
it to get "normal text" without interference
from acceleration.
Of course, this can also be problematic: if there
is a widget with focus, then it will swallow
all "normal text" accelerators.
*/
if (!special_handling_of_unmodified_accelerators) {
/* XXX note that for a brief moment, the conditional above
* included "|| (ev->state & mask)" so as to enforce the
* implication of special_handling_of_UNMODIFIED_accelerators.
* however, this forces any key that GTK doesn't allow and that
* we have an alternative (see next comment) for to be
* automatically sent through the accel groups activation
* pathway, which prevents individual widgets & canvas items
* from ever seeing it if is used by a key binding.
*
* specifically, this hid Ctrl-down-arrow from MIDI region
* views because it is also bound to an action.
*
* until we have a robust, clean binding system, this
* quirk will have to remain in place.
*/
/* pretend that certain key events that GTK does not allow
to be used as accelerators are actually something that
it does allow. but only where there are no modifiers.
*/
uint32_t fakekey = ev->keyval;
if (Gtkmm2ext::possibly_translate_keyval_to_make_legal_accelerator (fakekey)) {
DEBUG_TRACE (DEBUG::Accelerators, string_compose ("\tactivate (was %1 now %2) without special hanlding of unmodified accels\n",
ev->keyval, fakekey));
DEBUG_TRACE (DEBUG::Accelerators, string_compose ("\tmodified modifier was %1\n", show_gdk_event_state (modifier)));
if (allow_activating && gtk_accel_groups_activate(G_OBJECT(win), fakekey, modifier)) {
DEBUG_TRACE (DEBUG::Accelerators, "\taccel group activated by fakekey\n");
return true;
}
}
}
if (!special_handling_of_unmodified_accelerators || (ev->state & mask)) {
/* no special handling or there are modifiers in effect: accelerate first */
DEBUG_TRACE (DEBUG::Accelerators, "\tactivate, then propagate\n");
DEBUG_TRACE (DEBUG::Accelerators, string_compose ("\tevent send-event:%1 time:%2 length:%3 name %7 string:%4 hardware_keycode:%5 group:%6\n",
ev->send_event, ev->time, ev->length, ev->string, ev->hardware_keycode, ev->group, gdk_keyval_name (ev->keyval)));
if (allow_activating) {
DEBUG_TRACE (DEBUG::Accelerators, "\tsending to window\n");
if (gtk_accel_groups_activate (G_OBJECT(win), ev->keyval, modifier)) {
DEBUG_TRACE (DEBUG::Accelerators, "\t\thandled\n");
return true;
}
} else {
DEBUG_TRACE (DEBUG::Accelerators, "\tactivation skipped\n");
}
DEBUG_TRACE (DEBUG::Accelerators, "\tnot accelerated, now propagate\n");
return gtk_window_propagate_key_event (win, ev);
}
/* no modifiers, propagate first */
DEBUG_TRACE (DEBUG::Accelerators, "\tpropagate, then activate\n");
if (!gtk_window_propagate_key_event (win, ev)) {
DEBUG_TRACE (DEBUG::Accelerators, "\tpropagation didn't handle, so activate\n");
if (allow_activating) {
return gtk_accel_groups_activate (G_OBJECT(win), ev->keyval, modifier);
} else {
DEBUG_TRACE (DEBUG::Accelerators, "\tactivation skipped\n");
}
} else {
DEBUG_TRACE (DEBUG::Accelerators, "\thandled by propagate\n");
return true;
}
DEBUG_TRACE (DEBUG::Accelerators, "\tnot handled\n");
return true;
}
Glib::RefPtr<Gdk::Pixbuf>
ARDOUR_UI_UTILS::get_xpm (std::string name)

View File

@ -92,6 +92,7 @@ std::string rate_as_string (float r);
bool windows_overlap (Gtk::Window *a, Gtk::Window *b);
bool overwrite_file_dialog (Gtk::Window& parent, std::string title, std::string text);
std::string show_gdk_event_state (int state);
} // namespace
#endif /* __ardour_gtk_utils_h__ */