| OLD | NEW |
| 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. | 1 // Copyright (c) 2010 The Chromium Authors. All rights reserved. |
| 2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
| 3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
| 4 | 4 |
| 5 #include <gtk/gtk.h> | 5 #include <gtk/gtk.h> |
| 6 | 6 |
| 7 #include "base/keyboard_code_conversion_gtk.h" | 7 #include "base/keyboard_code_conversion_gtk.h" |
| 8 #include "base/keyboard_codes.h" | 8 #include "base/keyboard_codes.h" |
| 9 #include "views/accelerator.h" | 9 #include "views/accelerator.h" |
| 10 #include "views/focus/accelerator_handler.h" | 10 #include "views/focus/accelerator_handler.h" |
| 11 #include "views/focus/focus_manager.h" | 11 #include "views/focus/focus_manager.h" |
| 12 #include "views/widget/widget_gtk.h" | 12 #include "views/widget/widget_gtk.h" |
| 13 | 13 |
| 14 namespace views { | 14 namespace views { |
| 15 | 15 |
| 16 AcceleratorHandler::AcceleratorHandler() : only_menu_pressed_(false) {} | 16 AcceleratorHandler::AcceleratorHandler() {} |
| 17 | 17 |
| 18 bool AcceleratorHandler::Dispatch(GdkEvent* event) { | 18 bool AcceleratorHandler::Dispatch(GdkEvent* event) { |
| 19 // When focus changes, we may not see a full key-press / key-release | 19 // The logic for handling keyboard accelerators has been moved into |
| 20 // pair, so clear the set of keys we think were pressed. | 20 // WidgetGtk::OnKeyEvent handler (views/widget/widget_gtk.cc). |
| 21 if (event->type == GDK_FOCUS_CHANGE) { | |
| 22 pressed_hardware_keys_.clear(); | |
| 23 consumed_vkeys_.clear(); | |
| 24 only_menu_pressed_ = false; | |
| 25 } | |
| 26 | |
| 27 // Skip accelerator handling for non key events. | |
| 28 bool skip = event->type != GDK_KEY_PRESS && event->type != GDK_KEY_RELEASE; | |
| 29 | |
| 30 // Skip if there is a grabbed widget and it is not a window. This is because | |
| 31 // of the following two reasons: | |
| 32 // 1. Widget such as pop up from GtkComboBox is a grabbed widget and use ESC | |
| 33 // as its accelerator key to dismiss itself. We will break Gtk's code if | |
| 34 // we eat this key. See http://crosbug.com/2355 | |
| 35 // 2. Modal dialogs in Gtk are grabbed windows and we want to have our | |
| 36 // accelerator key processing in this case. See http://crogbug.com/3701 | |
| 37 if (!skip) { | |
| 38 GtkWidget* grabbed_widget = gtk_grab_get_current(); | |
| 39 skip = grabbed_widget && !GTK_IS_WINDOW(grabbed_widget); | |
| 40 } | |
| 41 | |
| 42 if (skip) { | |
| 43 // Let Gtk processes the event. | |
| 44 gtk_main_do_event(event); | |
| 45 return true; | |
| 46 } | |
| 47 | |
| 48 GdkEventKey* key_event = reinterpret_cast<GdkEventKey*>(event); | |
| 49 // Let's retrieve the focus manager for the GdkWindow. | |
| 50 GdkWindow* window = gdk_window_get_toplevel(key_event->window); | |
| 51 gpointer ptr; | |
| 52 gdk_window_get_user_data(window, &ptr); | |
| 53 if (!ptr && !gdk_window_is_visible(window)) { | |
| 54 // The window is destroyed while we're handling key events. | |
| 55 gtk_main_do_event(event); | |
| 56 return true; | |
| 57 } | |
| 58 | |
| 59 DCHECK(ptr); | |
| 60 | |
| 61 // The top-level window or window widget is expected to always be associated | |
| 62 // with the top-level gtk widget. | |
| 63 WidgetGtk* widget = | |
| 64 WidgetGtk::GetViewForNative(reinterpret_cast<GtkWidget*>(ptr)); | |
| 65 if (!widget) { | |
| 66 // During dnd we get events for windows we don't control (such as the | |
| 67 // window being dragged). | |
| 68 gtk_main_do_event(event); | |
| 69 return true; | |
| 70 } | |
| 71 FocusManager* focus_manager = widget->GetFocusManager(); | |
| 72 if (!focus_manager) { | |
| 73 NOTREACHED(); | |
| 74 return true; | |
| 75 } | |
| 76 | |
| 77 KeyEvent view_key_event(key_event); | |
| 78 int key_code = view_key_event.GetKeyCode(); | |
| 79 int hardware_keycode = key_event->hardware_keycode; | |
| 80 if (event->type == GDK_KEY_PRESS) { | |
| 81 pressed_hardware_keys_.insert(hardware_keycode); | |
| 82 | |
| 83 // If it's the Alt key, don't send it to the focus manager until release | |
| 84 // (to handle focusing the menu bar). | |
| 85 if (key_code == base::VKEY_MENU && pressed_hardware_keys_.size() == 1) { | |
| 86 only_menu_pressed_ = true; | |
| 87 return true; | |
| 88 } | |
| 89 | |
| 90 if (pressed_hardware_keys_.size() != 1) | |
| 91 only_menu_pressed_ = false; | |
| 92 | |
| 93 // FocusManager::OnKeyEvent will return false if this message has been | |
| 94 // consumed and should not be propagated further. | |
| 95 if (!focus_manager->OnKeyEvent(view_key_event)) { | |
| 96 consumed_vkeys_.insert(key_code); | |
| 97 return true; | |
| 98 } | |
| 99 } | |
| 100 | |
| 101 if (event->type == GDK_KEY_RELEASE) { | |
| 102 pressed_hardware_keys_.erase(hardware_keycode); | |
| 103 | |
| 104 // Make sure to filter out the key release for a key press consumed | |
| 105 // as an accelerator, to avoid calling gtk_main_do_event with an | |
| 106 // unpaired key release. | |
| 107 if (consumed_vkeys_.find(key_code) != consumed_vkeys_.end()) { | |
| 108 consumed_vkeys_.erase(key_code); | |
| 109 return true; | |
| 110 } | |
| 111 | |
| 112 // Special case: the Alt key can trigger an accelerator on release | |
| 113 // rather than on press, but only if no other keys were pressed. | |
| 114 if (key_code == base::VKEY_MENU && only_menu_pressed_) { | |
| 115 Accelerator accelerator(base::VKEY_MENU, false, false, false); | |
| 116 focus_manager->ProcessAccelerator(accelerator); | |
| 117 return true; | |
| 118 } | |
| 119 } | |
| 120 | |
| 121 // Pass the event to gtk if we didn't consume it above. | |
| 122 gtk_main_do_event(event); | 21 gtk_main_do_event(event); |
| 123 return true; | 22 return true; |
| 124 } | 23 } |
| 125 | 24 |
| 126 } // namespace views | 25 } // namespace views |
| OLD | NEW |