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 |