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 "chrome/browser/gtk/accessibility_event_router_gtk.h" | 5 #include "chrome/browser/gtk/accessibility_event_router_gtk.h" |
6 | 6 |
7 #include "base/basictypes.h" | 7 #include "base/basictypes.h" |
8 #include "base/callback.h" | 8 #include "base/callback.h" |
9 #include "base/message_loop.h" | 9 #include "base/message_loop.h" |
10 #include "base/stl_util-inl.h" | 10 #include "base/stl_util-inl.h" |
11 #include "chrome/browser/extensions/extension_accessibility_api.h" | 11 #include "chrome/browser/extensions/extension_accessibility_api.h" |
12 #include "chrome/browser/gtk/gtk_chrome_link_button.h" | 12 #include "chrome/browser/gtk/gtk_chrome_link_button.h" |
13 #include "chrome/browser/profile.h" | 13 #include "chrome/browser/profile.h" |
14 #include "chrome/common/notification_type.h" | 14 #include "chrome/common/notification_type.h" |
15 | 15 |
16 namespace { | 16 namespace { |
17 | 17 |
18 // | 18 // |
19 // Callbacks triggered by signals on gtk widgets. | 19 // Callbacks triggered by signals on gtk widgets. |
20 // | 20 // |
21 | 21 |
22 gboolean OnWidgetFocused(GSignalInvocationHint *ihint, | 22 gboolean OnWidgetFocused(GSignalInvocationHint *ihint, |
23 guint n_param_values, | 23 guint n_param_values, |
24 const GValue* param_values, | 24 const GValue* param_values, |
25 gpointer user_data) { | 25 gpointer user_data) { |
26 GtkWidget* widget = GTK_WIDGET(g_value_get_object(param_values)); | 26 GtkWidget* widget = GTK_WIDGET(g_value_get_object(param_values)); |
27 reinterpret_cast<AccessibilityEventRouter *>(user_data)-> | 27 reinterpret_cast<AccessibilityEventRouterGtk*>(user_data)-> |
28 DispatchAccessibilityNotification( | 28 DispatchAccessibilityNotification( |
29 widget, NotificationType::ACCESSIBILITY_CONTROL_FOCUSED); | 29 widget, NotificationType::ACCESSIBILITY_CONTROL_FOCUSED); |
30 return true; | 30 return TRUE; |
31 } | 31 } |
32 | 32 |
33 gboolean OnButtonClicked(GSignalInvocationHint *ihint, | 33 gboolean OnButtonClicked(GSignalInvocationHint *ihint, |
34 guint n_param_values, | 34 guint n_param_values, |
35 const GValue* param_values, | 35 const GValue* param_values, |
36 gpointer user_data) { | 36 gpointer user_data) { |
37 GtkWidget* widget = GTK_WIDGET(g_value_get_object(param_values)); | 37 GtkWidget* widget = GTK_WIDGET(g_value_get_object(param_values)); |
38 // Skip toggle buttons because we're also listening on "toggle" events. | 38 // Skip toggle buttons because we're also listening on "toggle" events. |
39 if (GTK_IS_TOGGLE_BUTTON(widget)) | 39 if (GTK_IS_TOGGLE_BUTTON(widget)) |
40 return true; | 40 return true; |
41 reinterpret_cast<AccessibilityEventRouter *>(user_data)-> | 41 reinterpret_cast<AccessibilityEventRouterGtk*>(user_data)-> |
42 DispatchAccessibilityNotification( | 42 DispatchAccessibilityNotification( |
43 widget, NotificationType::ACCESSIBILITY_CONTROL_ACTION); | 43 widget, NotificationType::ACCESSIBILITY_CONTROL_ACTION); |
44 return true; | 44 return TRUE; |
45 } | 45 } |
46 | 46 |
47 gboolean OnButtonToggled(GSignalInvocationHint *ihint, | 47 gboolean OnButtonToggled(GSignalInvocationHint *ihint, |
48 guint n_param_values, | 48 guint n_param_values, |
49 const GValue* param_values, | 49 const GValue* param_values, |
50 gpointer user_data) { | 50 gpointer user_data) { |
51 GtkWidget* widget = GTK_WIDGET(g_value_get_object(param_values)); | 51 GtkWidget* widget = GTK_WIDGET(g_value_get_object(param_values)); |
52 bool checked = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)); | 52 bool checked = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)); |
53 // Skip propagating an "uncheck" event for a radio button because it's | 53 // Skip propagating an "uncheck" event for a radio button because it's |
54 // redundant; there will always be a corresponding "check" event for | 54 // redundant; there will always be a corresponding "check" event for |
55 // a different radio button the group. | 55 // a different radio button the group. |
56 if (GTK_IS_RADIO_BUTTON(widget) && !checked) | 56 if (GTK_IS_RADIO_BUTTON(widget) && !checked) |
57 return true; | 57 return true; |
58 reinterpret_cast<AccessibilityEventRouter *>(user_data)-> | 58 reinterpret_cast<AccessibilityEventRouterGtk*>(user_data)-> |
59 DispatchAccessibilityNotification( | 59 DispatchAccessibilityNotification( |
60 widget, NotificationType::ACCESSIBILITY_CONTROL_ACTION); | 60 widget, NotificationType::ACCESSIBILITY_CONTROL_ACTION); |
61 return true; | 61 return TRUE; |
62 } | 62 } |
63 | 63 |
64 gboolean OnPageSwitched(GSignalInvocationHint *ihint, | 64 gboolean OnPageSwitched(GSignalInvocationHint *ihint, |
65 guint n_param_values, | 65 guint n_param_values, |
66 const GValue* param_values, | 66 const GValue* param_values, |
67 gpointer user_data) { | 67 gpointer user_data) { |
68 GtkWidget* widget = GTK_WIDGET(g_value_get_object(param_values)); | 68 GtkWidget* widget = GTK_WIDGET(g_value_get_object(param_values)); |
69 // The page hasn't switched yet, so defer calling | 69 // The page hasn't switched yet, so defer calling |
70 // DispatchAccessibilityNotification. | 70 // DispatchAccessibilityNotification. |
71 reinterpret_cast<AccessibilityEventRouter *>(user_data)-> | 71 reinterpret_cast<AccessibilityEventRouterGtk*>(user_data)-> |
72 PostDispatchAccessibilityNotification( | 72 PostDispatchAccessibilityNotification( |
73 widget, NotificationType::ACCESSIBILITY_CONTROL_ACTION); | 73 widget, NotificationType::ACCESSIBILITY_CONTROL_ACTION); |
74 return true; | 74 return TRUE; |
75 } | 75 } |
76 | 76 |
77 gboolean OnComboBoxChanged(GSignalInvocationHint *ihint, | 77 gboolean OnComboBoxChanged(GSignalInvocationHint *ihint, |
78 guint n_param_values, | 78 guint n_param_values, |
79 const GValue* param_values, | 79 const GValue* param_values, |
80 gpointer user_data) { | 80 gpointer user_data) { |
81 GtkWidget* widget = GTK_WIDGET(g_value_get_object(param_values)); | 81 GtkWidget* widget = GTK_WIDGET(g_value_get_object(param_values)); |
82 if (!GTK_IS_COMBO_BOX(widget)) | 82 if (!GTK_IS_COMBO_BOX(widget)) |
83 return true; | 83 return true; |
84 reinterpret_cast<AccessibilityEventRouter *>(user_data)-> | 84 reinterpret_cast<AccessibilityEventRouterGtk*>(user_data)-> |
85 DispatchAccessibilityNotification( | 85 DispatchAccessibilityNotification( |
86 widget, NotificationType::ACCESSIBILITY_CONTROL_ACTION); | 86 widget, NotificationType::ACCESSIBILITY_CONTROL_ACTION); |
87 return true; | 87 return TRUE; |
88 } | 88 } |
89 | 89 |
90 gboolean OnTreeViewCursorChanged(GSignalInvocationHint *ihint, | 90 gboolean OnTreeViewCursorChanged(GSignalInvocationHint *ihint, |
91 guint n_param_values, | 91 guint n_param_values, |
92 const GValue* param_values, | 92 const GValue* param_values, |
93 gpointer user_data) { | 93 gpointer user_data) { |
94 GtkWidget* widget = GTK_WIDGET(g_value_get_object(param_values)); | 94 GtkWidget* widget = GTK_WIDGET(g_value_get_object(param_values)); |
95 if (!GTK_IS_TREE_VIEW(widget)) { | 95 if (!GTK_IS_TREE_VIEW(widget)) { |
96 return true; | 96 return true; |
97 } | 97 } |
98 reinterpret_cast<AccessibilityEventRouter *>(user_data)-> | 98 reinterpret_cast<AccessibilityEventRouterGtk*>(user_data)-> |
99 DispatchAccessibilityNotification( | 99 DispatchAccessibilityNotification( |
100 widget, NotificationType::ACCESSIBILITY_CONTROL_ACTION); | 100 widget, NotificationType::ACCESSIBILITY_CONTROL_ACTION); |
101 return true; | 101 return TRUE; |
102 } | 102 } |
103 | 103 |
104 gboolean OnEntryChanged(GSignalInvocationHint *ihint, | 104 gboolean OnEntryChanged(GSignalInvocationHint *ihint, |
105 guint n_param_values, | 105 guint n_param_values, |
106 const GValue* param_values, | 106 const GValue* param_values, |
107 gpointer user_data) { | 107 gpointer user_data) { |
108 GtkWidget* widget = GTK_WIDGET(g_value_get_object(param_values)); | 108 GtkWidget* widget = GTK_WIDGET(g_value_get_object(param_values)); |
109 if (!GTK_IS_ENTRY(widget)) { | 109 if (!GTK_IS_ENTRY(widget)) { |
110 return true; | 110 return TRUE; |
111 } | 111 } |
112 // The text hasn't changed yet, so defer calling | 112 // The text hasn't changed yet, so defer calling |
113 // DispatchAccessibilityNotification. | 113 // DispatchAccessibilityNotification. |
114 reinterpret_cast<AccessibilityEventRouter *>(user_data)-> | 114 reinterpret_cast<AccessibilityEventRouterGtk*>(user_data)-> |
115 PostDispatchAccessibilityNotification( | 115 PostDispatchAccessibilityNotification( |
116 widget, NotificationType::ACCESSIBILITY_TEXT_CHANGED); | 116 widget, NotificationType::ACCESSIBILITY_TEXT_CHANGED); |
117 return true; | 117 return TRUE; |
| 118 } |
| 119 |
| 120 gboolean OnMenuMoveCurrent(GSignalInvocationHint *ihint, |
| 121 guint n_param_values, |
| 122 const GValue* param_values, |
| 123 gpointer user_data) { |
| 124 // Get the widget (the GtkMenu). |
| 125 GtkWidget* widget = GTK_WIDGET(g_value_get_object(param_values)); |
| 126 |
| 127 // Moving may move us into or out of a submenu, so after the menu |
| 128 // item moves, |widget| may not be valid anymore. To be safe, then, |
| 129 // find the topmost ancestor of this menu and post the notification |
| 130 // dispatch on that menu. Then the dispatcher will recurse into submenus |
| 131 // as necessary to figure out which item is focused. |
| 132 while (GTK_MENU_SHELL(widget)->parent_menu_shell) |
| 133 widget = GTK_MENU_SHELL(widget)->parent_menu_shell; |
| 134 |
| 135 // The menu item hasn't moved yet, so we want to defer calling |
| 136 // DispatchAccessibilityNotification until after it does. |
| 137 reinterpret_cast<AccessibilityEventRouterGtk*>(user_data)-> |
| 138 PostDispatchAccessibilityNotification( |
| 139 widget, NotificationType::ACCESSIBILITY_CONTROL_FOCUSED); |
| 140 return TRUE; |
118 } | 141 } |
119 | 142 |
120 } // anonymous namespace | 143 } // anonymous namespace |
121 | 144 |
122 AccessibilityEventRouter::AccessibilityEventRouter() | 145 AccessibilityEventRouterGtk::AccessibilityEventRouterGtk() |
123 : method_factory_(this) { | 146 : listening_(false), |
| 147 most_recent_profile_(NULL), |
| 148 method_factory_(this) { |
124 // We don't want our event listeners to be installed if accessibility is | 149 // We don't want our event listeners to be installed if accessibility is |
125 // disabled. Install listeners so we can install and uninstall them as | 150 // disabled. Install listeners so we can install and uninstall them as |
126 // needed, then install them now if it's currently enabled. | 151 // needed, then install them now if it's currently enabled. |
127 ExtensionAccessibilityEventRouter *accessibility_event_router = | 152 ExtensionAccessibilityEventRouter *extension_event_router = |
128 ExtensionAccessibilityEventRouter::GetInstance(); | 153 ExtensionAccessibilityEventRouter::GetInstance(); |
129 accessibility_event_router->AddOnEnabledListener( | 154 extension_event_router->AddOnEnabledListener( |
130 NewCallback(this, | 155 NewCallback(this, |
131 &AccessibilityEventRouter::InstallEventListeners)); | 156 &AccessibilityEventRouterGtk::InstallEventListeners)); |
132 accessibility_event_router->AddOnDisabledListener( | 157 extension_event_router->AddOnDisabledListener( |
133 NewCallback(this, | 158 NewCallback(this, |
134 &AccessibilityEventRouter::RemoveEventListeners)); | 159 &AccessibilityEventRouterGtk::RemoveEventListeners)); |
135 if (accessibility_event_router->IsAccessibilityEnabled()) { | 160 if (extension_event_router->IsAccessibilityEnabled()) { |
136 InstallEventListeners(); | 161 InstallEventListeners(); |
137 } | 162 } |
138 } | 163 } |
139 | 164 |
140 AccessibilityEventRouter::~AccessibilityEventRouter() { | 165 AccessibilityEventRouterGtk::~AccessibilityEventRouterGtk() { |
141 RemoveEventListeners(); | 166 RemoveEventListeners(); |
142 } | 167 } |
143 | 168 |
144 // static | 169 // static |
145 AccessibilityEventRouter* AccessibilityEventRouter::GetInstance() { | 170 AccessibilityEventRouterGtk* AccessibilityEventRouterGtk::GetInstance() { |
146 return Singleton<AccessibilityEventRouter>::get(); | 171 return Singleton<AccessibilityEventRouterGtk>::get(); |
147 } | 172 } |
148 | 173 |
149 void AccessibilityEventRouter::InstallEventListener( | 174 void AccessibilityEventRouterGtk::InstallEventListener( |
150 const char* signal_name, | 175 const char* signal_name, |
151 GType widget_type, | 176 GType widget_type, |
152 GSignalEmissionHook hook_func) { | 177 GSignalEmissionHook hook_func) { |
153 guint signal_id = g_signal_lookup(signal_name, widget_type); | 178 guint signal_id = g_signal_lookup(signal_name, widget_type); |
154 gulong hook_id = g_signal_add_emission_hook( | 179 gulong hook_id = g_signal_add_emission_hook( |
155 signal_id, 0, hook_func, reinterpret_cast<gpointer>(this), NULL); | 180 signal_id, 0, hook_func, reinterpret_cast<gpointer>(this), NULL); |
156 installed_hooks_.push_back(InstalledHook(signal_id, hook_id)); | 181 installed_hooks_.push_back(InstalledHook(signal_id, hook_id)); |
157 } | 182 } |
158 | 183 |
159 void AccessibilityEventRouter::InstallEventListeners() { | 184 void AccessibilityEventRouterGtk::InstallEventListeners() { |
160 // Create and destroy each type of widget we need signals for, | 185 // Create and destroy each type of widget we need signals for, |
161 // to ensure their modules are loaded, otherwise g_signal_lookup | 186 // to ensure their modules are loaded, otherwise g_signal_lookup |
162 // might fail. | 187 // might fail. |
163 g_object_unref(g_object_ref_sink(gtk_combo_box_new())); | 188 g_object_unref(g_object_ref_sink(gtk_combo_box_new())); |
164 g_object_unref(g_object_ref_sink(gtk_entry_new())); | 189 g_object_unref(g_object_ref_sink(gtk_entry_new())); |
165 g_object_unref(g_object_ref_sink(gtk_notebook_new())); | 190 g_object_unref(g_object_ref_sink(gtk_notebook_new())); |
166 g_object_unref(g_object_ref_sink(gtk_toggle_button_new())); | 191 g_object_unref(g_object_ref_sink(gtk_toggle_button_new())); |
167 g_object_unref(g_object_ref_sink(gtk_tree_view_new())); | 192 g_object_unref(g_object_ref_sink(gtk_tree_view_new())); |
168 | 193 |
169 // Add signal emission hooks for the events we're interested in. | 194 // Add signal emission hooks for the events we're interested in. |
170 InstallEventListener("clicked", GTK_TYPE_BUTTON, OnButtonClicked); | 195 InstallEventListener("clicked", GTK_TYPE_BUTTON, OnButtonClicked); |
171 InstallEventListener("changed", GTK_TYPE_COMBO_BOX, OnComboBoxChanged); | 196 InstallEventListener("changed", GTK_TYPE_COMBO_BOX, OnComboBoxChanged); |
172 InstallEventListener("cursor-changed", GTK_TYPE_TREE_VIEW, | 197 InstallEventListener("cursor-changed", GTK_TYPE_TREE_VIEW, |
173 OnTreeViewCursorChanged); | 198 OnTreeViewCursorChanged); |
174 InstallEventListener("changed", GTK_TYPE_ENTRY, OnEntryChanged); | 199 InstallEventListener("changed", GTK_TYPE_ENTRY, OnEntryChanged); |
175 InstallEventListener("insert-text", GTK_TYPE_ENTRY, OnEntryChanged); | 200 InstallEventListener("insert-text", GTK_TYPE_ENTRY, OnEntryChanged); |
176 InstallEventListener("delete-text", GTK_TYPE_ENTRY, OnEntryChanged); | 201 InstallEventListener("delete-text", GTK_TYPE_ENTRY, OnEntryChanged); |
177 InstallEventListener("move-cursor", GTK_TYPE_ENTRY, OnEntryChanged); | 202 InstallEventListener("move-cursor", GTK_TYPE_ENTRY, OnEntryChanged); |
178 InstallEventListener("focus-in-event", GTK_TYPE_WIDGET, OnWidgetFocused); | 203 InstallEventListener("focus-in-event", GTK_TYPE_WIDGET, OnWidgetFocused); |
179 InstallEventListener("switch-page", GTK_TYPE_NOTEBOOK, OnPageSwitched); | 204 InstallEventListener("switch-page", GTK_TYPE_NOTEBOOK, OnPageSwitched); |
180 InstallEventListener("toggled", GTK_TYPE_TOGGLE_BUTTON, OnButtonToggled); | 205 InstallEventListener("toggled", GTK_TYPE_TOGGLE_BUTTON, OnButtonToggled); |
| 206 InstallEventListener("move-current", GTK_TYPE_MENU, OnMenuMoveCurrent); |
181 | 207 |
182 listening_ = true; | 208 listening_ = true; |
183 } | 209 } |
184 | 210 |
185 void AccessibilityEventRouter::RemoveEventListeners() { | 211 void AccessibilityEventRouterGtk::RemoveEventListeners() { |
186 for (size_t i = 0; i < installed_hooks_.size(); i++) { | 212 for (size_t i = 0; i < installed_hooks_.size(); i++) { |
187 g_signal_remove_emission_hook( | 213 g_signal_remove_emission_hook( |
188 installed_hooks_[i].signal_id, | 214 installed_hooks_[i].signal_id, |
189 installed_hooks_[i].hook_id); | 215 installed_hooks_[i].hook_id); |
190 } | 216 } |
191 installed_hooks_.clear(); | 217 installed_hooks_.clear(); |
192 | 218 |
193 listening_ = false; | 219 listening_ = false; |
194 } | 220 } |
195 | 221 |
196 void AccessibilityEventRouter::AddRootWidget( | 222 void AccessibilityEventRouterGtk::AddRootWidget( |
197 GtkWidget* root_widget, Profile* profile) { | 223 GtkWidget* root_widget, Profile* profile) { |
198 root_widget_profile_map_[root_widget] = profile; | 224 root_widget_profile_map_[root_widget] = profile; |
199 } | 225 } |
200 | 226 |
201 void AccessibilityEventRouter::RemoveRootWidget(GtkWidget* root_widget) { | 227 void AccessibilityEventRouterGtk::RemoveRootWidget(GtkWidget* root_widget) { |
202 DCHECK(root_widget_profile_map_.find(root_widget) != | 228 DCHECK(root_widget_profile_map_.find(root_widget) != |
203 root_widget_profile_map_.end()); | 229 root_widget_profile_map_.end()); |
204 root_widget_profile_map_.erase(root_widget); | 230 root_widget_profile_map_.erase(root_widget); |
205 } | 231 } |
206 | 232 |
207 void AccessibilityEventRouter::IgnoreWidget(GtkWidget* widget) { | 233 void AccessibilityEventRouterGtk::IgnoreWidget(GtkWidget* widget) { |
208 widget_info_map_[widget].ignore = true; | 234 widget_info_map_[widget].ignore = true; |
209 } | 235 } |
210 | 236 |
211 void AccessibilityEventRouter::SetWidgetName( | 237 void AccessibilityEventRouterGtk::SetWidgetName( |
212 GtkWidget* widget, std::string name) { | 238 GtkWidget* widget, std::string name) { |
213 widget_info_map_[widget].name = name; | 239 widget_info_map_[widget].name = name; |
214 } | 240 } |
215 | 241 |
216 void AccessibilityEventRouter::RemoveWidget(GtkWidget* widget) { | 242 void AccessibilityEventRouterGtk::RemoveWidget(GtkWidget* widget) { |
217 DCHECK(widget_info_map_.find(widget) != widget_info_map_.end()); | 243 DCHECK(widget_info_map_.find(widget) != widget_info_map_.end()); |
218 widget_info_map_.erase(widget); | 244 widget_info_map_.erase(widget); |
219 } | 245 } |
220 | 246 |
221 bool AccessibilityEventRouter::IsWidgetAccessible( | 247 void AccessibilityEventRouterGtk::FindWidget( |
222 GtkWidget* widget, Profile** profile) { | 248 GtkWidget* widget, Profile** profile, bool* is_accessible) { |
| 249 *is_accessible = false; |
| 250 |
223 // First see if it's a descendant of a root widget. | 251 // First see if it's a descendant of a root widget. |
224 bool is_accessible = false; | |
225 for (base::hash_map<GtkWidget*, Profile*>::const_iterator iter = | 252 for (base::hash_map<GtkWidget*, Profile*>::const_iterator iter = |
226 root_widget_profile_map_.begin(); | 253 root_widget_profile_map_.begin(); |
227 iter != root_widget_profile_map_.end(); | 254 iter != root_widget_profile_map_.end(); |
228 ++iter) { | 255 ++iter) { |
229 if (gtk_widget_is_ancestor(widget, iter->first)) { | 256 if (gtk_widget_is_ancestor(widget, iter->first)) { |
230 is_accessible = true; | 257 *is_accessible = true; |
231 if (profile) | 258 if (profile) |
232 *profile = iter->second; | 259 *profile = iter->second; |
233 break; | 260 break; |
234 } | 261 } |
235 } | 262 } |
236 if (!is_accessible) | 263 if (!*is_accessible) |
237 return false; | 264 return; |
238 | 265 |
239 // Now make sure it's not marked as a widget to be ignored. | 266 // Now make sure it's not marked as a widget to be ignored. |
240 base::hash_map<GtkWidget*, WidgetInfo>::const_iterator iter = | 267 base::hash_map<GtkWidget*, WidgetInfo>::const_iterator iter = |
241 widget_info_map_.find(widget); | 268 widget_info_map_.find(widget); |
242 if (iter != widget_info_map_.end() && iter->second.ignore) { | 269 if (iter != widget_info_map_.end() && iter->second.ignore) { |
243 is_accessible = false; | 270 *is_accessible = false; |
| 271 return; |
244 } | 272 } |
245 | |
246 return is_accessible; | |
247 } | 273 } |
248 | 274 |
249 std::string AccessibilityEventRouter::GetWidgetName(GtkWidget* widget) { | 275 std::string AccessibilityEventRouterGtk::GetWidgetName(GtkWidget* widget) { |
250 base::hash_map<GtkWidget*, WidgetInfo>::const_iterator iter = | 276 base::hash_map<GtkWidget*, WidgetInfo>::const_iterator iter = |
251 widget_info_map_.find(widget); | 277 widget_info_map_.find(widget); |
252 if (iter != widget_info_map_.end()) { | 278 if (iter != widget_info_map_.end()) { |
253 return iter->second.name; | 279 return iter->second.name; |
254 } else { | 280 } else { |
255 return ""; | 281 return ""; |
256 } | 282 } |
257 } | 283 } |
258 | 284 |
259 void AccessibilityEventRouter::StartListening() { | 285 void AccessibilityEventRouterGtk::StartListening() { |
260 listening_ = true; | 286 listening_ = true; |
261 } | 287 } |
262 | 288 |
263 void AccessibilityEventRouter::StopListening() { | 289 void AccessibilityEventRouterGtk::StopListening() { |
264 listening_ = false; | 290 listening_ = false; |
265 } | 291 } |
266 | 292 |
267 void AccessibilityEventRouter::DispatchAccessibilityNotification( | 293 void AccessibilityEventRouterGtk::DispatchAccessibilityNotification( |
268 GtkWidget* widget, NotificationType type) { | 294 GtkWidget* widget, NotificationType type) { |
269 if (!listening_) | 295 if (!listening_) |
270 return; | 296 return; |
271 Profile *profile; | 297 |
272 if (!IsWidgetAccessible(widget, &profile)) | 298 Profile* profile = NULL; |
| 299 bool is_accessible; |
| 300 FindWidget(widget, &profile, &is_accessible); |
| 301 if (profile) |
| 302 most_recent_profile_ = profile; |
| 303 |
| 304 // Special case: a GtkMenu isn't associated with any particular |
| 305 // toplevel window, so menu events get routed to the profile of |
| 306 // the most recent event that was associated with a window. |
| 307 if (GTK_IS_MENU_SHELL(widget) && most_recent_profile_) { |
| 308 SendMenuItemNotification(widget, type, most_recent_profile_); |
| 309 return; |
| 310 } |
| 311 |
| 312 // In all other cases, return if this widget wasn't marked as accessible. |
| 313 if (!is_accessible) |
273 return; | 314 return; |
274 | 315 |
275 // The order of these checks matters, because, for example, a radio button | 316 // The order of these checks matters, because, for example, a radio button |
276 // is a subclass of button, and a combo box is a composite control where | 317 // is a subclass of button, and a combo box is a composite control where |
277 // the focus event goes to the button that's a child of the combo box. | 318 // the focus event goes to the button that's a child of the combo box. |
278 GtkWidget* parent = gtk_widget_get_parent(widget); | 319 GtkWidget* parent = gtk_widget_get_parent(widget); |
279 if (parent && GTK_IS_BUTTON(widget) && GTK_IS_TREE_VIEW(parent)) { | 320 if (parent && GTK_IS_BUTTON(widget) && GTK_IS_TREE_VIEW(parent)) { |
280 // This is a list box column header. Currently not supported. | 321 // This is a list box column header. Currently not supported. |
281 return; | 322 return; |
282 } else if (GTK_IS_COMBO_BOX(widget)) { | 323 } else if (GTK_IS_COMBO_BOX(widget)) { |
(...skipping 19 matching lines...) Expand all Loading... |
302 } | 343 } |
303 | 344 |
304 // After this method returns, additional signal handlers will run, | 345 // After this method returns, additional signal handlers will run, |
305 // which will sometimes generate additional signals. To avoid | 346 // which will sometimes generate additional signals. To avoid |
306 // generating redundant accessibility notifications for the same | 347 // generating redundant accessibility notifications for the same |
307 // initial event, stop listening to all signals generated from now | 348 // initial event, stop listening to all signals generated from now |
308 // until this posted task runs. | 349 // until this posted task runs. |
309 StopListening(); | 350 StopListening(); |
310 MessageLoop::current()->PostTask( | 351 MessageLoop::current()->PostTask( |
311 FROM_HERE, method_factory_.NewRunnableMethod( | 352 FROM_HERE, method_factory_.NewRunnableMethod( |
312 &AccessibilityEventRouter::StartListening)); | 353 &AccessibilityEventRouterGtk::StartListening)); |
313 } | 354 } |
314 | 355 |
315 void AccessibilityEventRouter::PostDispatchAccessibilityNotification( | 356 void AccessibilityEventRouterGtk::PostDispatchAccessibilityNotification( |
316 GtkWidget* widget, NotificationType type) { | 357 GtkWidget* widget, NotificationType type) { |
317 MessageLoop::current()->PostTask( | 358 MessageLoop::current()->PostTask( |
318 FROM_HERE, method_factory_.NewRunnableMethod( | 359 FROM_HERE, method_factory_.NewRunnableMethod( |
319 &AccessibilityEventRouter::DispatchAccessibilityNotification, | 360 &AccessibilityEventRouterGtk::DispatchAccessibilityNotification, |
320 widget, | 361 widget, |
321 type)); | 362 type)); |
322 } | 363 } |
323 | 364 |
324 void AccessibilityEventRouter::SendRadioButtonNotification( | 365 void AccessibilityEventRouterGtk::SendRadioButtonNotification( |
325 GtkWidget* widget, NotificationType type, Profile* profile) { | 366 GtkWidget* widget, NotificationType type, Profile* profile) { |
326 // Get the radio button name | 367 // Get the radio button name |
327 std::string button_name = GetWidgetName(widget); | 368 std::string button_name = GetWidgetName(widget); |
328 if (button_name.empty() && gtk_button_get_label(GTK_BUTTON(widget))) | 369 if (button_name.empty() && gtk_button_get_label(GTK_BUTTON(widget))) |
329 button_name = gtk_button_get_label(GTK_BUTTON(widget)); | 370 button_name = gtk_button_get_label(GTK_BUTTON(widget)); |
330 | 371 |
331 // Get its state | 372 // Get its state |
332 bool checked = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)); | 373 bool checked = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)); |
333 | 374 |
334 // Get the index of this radio button and the total number of | 375 // Get the index of this radio button and the total number of |
335 // radio buttons in the group. | 376 // radio buttons in the group. |
336 int item_count = 0; | 377 int item_count = 0; |
337 int item_index = -1; | 378 int item_index = -1; |
338 for (GSList* group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(widget)); | 379 for (GSList* group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(widget)); |
339 group; | 380 group; |
340 group = group->next) { | 381 group = group->next) { |
341 if (group->data == widget) { | 382 if (group->data == widget) { |
342 item_index = item_count; | 383 item_index = item_count; |
343 } | 384 } |
344 item_count++; | 385 item_count++; |
345 } | 386 } |
346 item_index = item_count - 1 - item_index; | 387 item_index = item_count - 1 - item_index; |
347 | 388 |
348 AccessibilityRadioButtonInfo info( | 389 AccessibilityRadioButtonInfo info( |
349 profile, button_name, checked, item_index, item_count); | 390 profile, button_name, checked, item_index, item_count); |
350 SendAccessibilityNotification(type, &info); | 391 SendAccessibilityNotification(type, &info); |
351 } | 392 } |
352 | 393 |
353 void AccessibilityEventRouter::SendCheckboxNotification( | 394 void AccessibilityEventRouterGtk::SendCheckboxNotification( |
354 GtkWidget* widget, NotificationType type, Profile* profile) { | 395 GtkWidget* widget, NotificationType type, Profile* profile) { |
355 std::string button_name = GetWidgetName(widget); | 396 std::string button_name = GetWidgetName(widget); |
356 if (button_name.empty() && gtk_button_get_label(GTK_BUTTON(widget))) | 397 if (button_name.empty() && gtk_button_get_label(GTK_BUTTON(widget))) |
357 button_name = gtk_button_get_label(GTK_BUTTON(widget)); | 398 button_name = gtk_button_get_label(GTK_BUTTON(widget)); |
358 bool checked = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)); | 399 bool checked = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget)); |
359 AccessibilityCheckboxInfo info(profile, button_name, checked); | 400 AccessibilityCheckboxInfo info(profile, button_name, checked); |
360 SendAccessibilityNotification(type, &info); | 401 SendAccessibilityNotification(type, &info); |
361 } | 402 } |
362 | 403 |
363 void AccessibilityEventRouter::SendButtonNotification( | 404 void AccessibilityEventRouterGtk::SendButtonNotification( |
364 GtkWidget* widget, NotificationType type, Profile* profile) { | 405 GtkWidget* widget, NotificationType type, Profile* profile) { |
365 std::string button_name = GetWidgetName(widget); | 406 std::string button_name = GetWidgetName(widget); |
366 if (button_name.empty() && gtk_button_get_label(GTK_BUTTON(widget))) | 407 if (button_name.empty() && gtk_button_get_label(GTK_BUTTON(widget))) |
367 button_name = gtk_button_get_label(GTK_BUTTON(widget)); | 408 button_name = gtk_button_get_label(GTK_BUTTON(widget)); |
368 AccessibilityButtonInfo info(profile, button_name); | 409 AccessibilityButtonInfo info(profile, button_name); |
369 SendAccessibilityNotification(type, &info); | 410 SendAccessibilityNotification(type, &info); |
370 } | 411 } |
371 | 412 |
372 void AccessibilityEventRouter::SendTextBoxNotification( | 413 void AccessibilityEventRouterGtk::SendTextBoxNotification( |
373 GtkWidget* widget, NotificationType type, Profile* profile) { | 414 GtkWidget* widget, NotificationType type, Profile* profile) { |
374 std::string name = GetWidgetName(widget); | 415 std::string name = GetWidgetName(widget); |
375 std::string value = gtk_entry_get_text(GTK_ENTRY(widget)); | 416 std::string value = gtk_entry_get_text(GTK_ENTRY(widget)); |
376 gint start_pos; | 417 gint start_pos; |
377 gint end_pos; | 418 gint end_pos; |
378 gtk_editable_get_selection_bounds(GTK_EDITABLE(widget), &start_pos, &end_pos); | 419 gtk_editable_get_selection_bounds(GTK_EDITABLE(widget), &start_pos, &end_pos); |
379 AccessibilityTextBoxInfo info(profile, name, false); | 420 AccessibilityTextBoxInfo info(profile, name, false); |
380 info.SetValue(value, start_pos, end_pos); | 421 info.SetValue(value, start_pos, end_pos); |
381 SendAccessibilityNotification(type, &info); | 422 SendAccessibilityNotification(type, &info); |
382 } | 423 } |
383 | 424 |
384 void AccessibilityEventRouter::SendTabNotification( | 425 void AccessibilityEventRouterGtk::SendTabNotification( |
385 GtkWidget* widget, NotificationType type, Profile* profile) { | 426 GtkWidget* widget, NotificationType type, Profile* profile) { |
386 int index = gtk_notebook_get_current_page(GTK_NOTEBOOK(widget)); | 427 int index = gtk_notebook_get_current_page(GTK_NOTEBOOK(widget)); |
387 int page_count = gtk_notebook_get_n_pages(GTK_NOTEBOOK(widget)); | 428 int page_count = gtk_notebook_get_n_pages(GTK_NOTEBOOK(widget)); |
388 GtkWidget* page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(widget), index); | 429 GtkWidget* page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(widget), index); |
389 GtkWidget* label = gtk_notebook_get_tab_label(GTK_NOTEBOOK(widget), page); | 430 GtkWidget* label = gtk_notebook_get_tab_label(GTK_NOTEBOOK(widget), page); |
390 std::string name = GetWidgetName(widget); | 431 std::string name = GetWidgetName(widget); |
391 if (name.empty() && gtk_label_get_text(GTK_LABEL(label))) { | 432 if (name.empty() && gtk_label_get_text(GTK_LABEL(label))) { |
392 name = gtk_label_get_text(GTK_LABEL(label)); | 433 name = gtk_label_get_text(GTK_LABEL(label)); |
393 } | 434 } |
394 AccessibilityTabInfo info(profile, name, index, page_count); | 435 AccessibilityTabInfo info(profile, name, index, page_count); |
395 SendAccessibilityNotification(type, &info); | 436 SendAccessibilityNotification(type, &info); |
396 } | 437 } |
397 | 438 |
398 void AccessibilityEventRouter::SendComboBoxNotification( | 439 void AccessibilityEventRouterGtk::SendComboBoxNotification( |
399 GtkWidget* widget, NotificationType type, Profile* profile) { | 440 GtkWidget* widget, NotificationType type, Profile* profile) { |
400 // Get the index of the selected item. Will return -1 if no item is | 441 // Get the index of the selected item. Will return -1 if no item is |
401 // active, which matches the semantics of the extension API. | 442 // active, which matches the semantics of the extension API. |
402 int index = gtk_combo_box_get_active(GTK_COMBO_BOX(widget)); | 443 int index = gtk_combo_box_get_active(GTK_COMBO_BOX(widget)); |
403 | 444 |
404 // Get the number of items. | 445 // Get the number of items. |
405 GtkTreeModel* model = gtk_combo_box_get_model(GTK_COMBO_BOX(widget)); | 446 GtkTreeModel* model = gtk_combo_box_get_model(GTK_COMBO_BOX(widget)); |
406 int count = gtk_tree_model_iter_n_children(model, NULL); | 447 int count = gtk_tree_model_iter_n_children(model, NULL); |
407 | 448 |
408 // Get the value of the current item, if possible. Note that the | 449 // Get the value of the current item, if possible. Note that the |
(...skipping 26 matching lines...) Expand all Loading... |
435 } | 476 } |
436 | 477 |
437 // Get the name of this combo box. | 478 // Get the name of this combo box. |
438 std::string name = GetWidgetName(widget); | 479 std::string name = GetWidgetName(widget); |
439 | 480 |
440 // Send the notification. | 481 // Send the notification. |
441 AccessibilityComboBoxInfo info(profile, name, value, index, count); | 482 AccessibilityComboBoxInfo info(profile, name, value, index, count); |
442 SendAccessibilityNotification(type, &info); | 483 SendAccessibilityNotification(type, &info); |
443 } | 484 } |
444 | 485 |
445 void AccessibilityEventRouter::SendListBoxNotification( | 486 void AccessibilityEventRouterGtk::SendListBoxNotification( |
446 GtkWidget* widget, NotificationType type, Profile* profile) { | 487 GtkWidget* widget, NotificationType type, Profile* profile) { |
447 // Get the number of items. | 488 // Get the number of items. |
448 GtkTreeModel* model = gtk_tree_view_get_model(GTK_TREE_VIEW(widget)); | 489 GtkTreeModel* model = gtk_tree_view_get_model(GTK_TREE_VIEW(widget)); |
449 int count = gtk_tree_model_iter_n_children(model, NULL); | 490 int count = gtk_tree_model_iter_n_children(model, NULL); |
450 | 491 |
451 // Get the current selected index and its value. | 492 // Get the current selected index and its value. |
452 int index = -1; | 493 int index = -1; |
453 std::string value; | 494 std::string value; |
454 GtkTreePath* path; | 495 GtkTreePath* path; |
455 gtk_tree_view_get_cursor(GTK_TREE_VIEW(widget), &path, NULL); | 496 gtk_tree_view_get_cursor(GTK_TREE_VIEW(widget), &path, NULL); |
(...skipping 22 matching lines...) Expand all Loading... |
478 gtk_tree_path_free(path); | 519 gtk_tree_path_free(path); |
479 } | 520 } |
480 | 521 |
481 // Get the name of this control. | 522 // Get the name of this control. |
482 std::string name = GetWidgetName(widget); | 523 std::string name = GetWidgetName(widget); |
483 | 524 |
484 // Send the notification. | 525 // Send the notification. |
485 AccessibilityListBoxInfo info(profile, name, value, index, count); | 526 AccessibilityListBoxInfo info(profile, name, value, index, count); |
486 SendAccessibilityNotification(type, &info); | 527 SendAccessibilityNotification(type, &info); |
487 } | 528 } |
| 529 |
| 530 void AccessibilityEventRouterGtk::SendMenuItemNotification( |
| 531 GtkWidget* menu, NotificationType type, Profile* profile) { |
| 532 // Find the focused menu item, recursing into submenus as needed. |
| 533 GtkWidget* menu_item = GTK_MENU_SHELL(menu)->active_menu_item; |
| 534 if (!menu_item) |
| 535 return; |
| 536 GtkWidget* submenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(menu_item)); |
| 537 while (submenu && GTK_MENU_SHELL(submenu)->active_menu_item) { |
| 538 menu = submenu; |
| 539 menu_item = GTK_MENU_SHELL(menu)->active_menu_item; |
| 540 if (!menu_item) |
| 541 return; |
| 542 submenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(menu_item)); |
| 543 } |
| 544 |
| 545 // Figure out the item index and total number of items. |
| 546 GList* items = gtk_container_get_children(GTK_CONTAINER(menu)); |
| 547 guint count = g_list_length(items); |
| 548 int index = g_list_index(items, static_cast<gconstpointer>(menu_item)); |
| 549 |
| 550 // Get the menu item's label. |
| 551 std::string name; |
| 552 #if GTK_CHECK_VERSION(2, 16, 0) |
| 553 name = gtk_menu_item_get_label(GTK_MENU_ITEM(menu_item)); |
| 554 #else |
| 555 GList* children = gtk_container_get_children(GTK_CONTAINER(menu_item)); |
| 556 for (GList* l = g_list_first(children); l != NULL; l = g_list_next(l)) { |
| 557 GtkWidget* child = static_cast<GtkWidget*>(l->data); |
| 558 if (GTK_IS_LABEL(child)) { |
| 559 name = gtk_label_get_label(GTK_LABEL(child)); |
| 560 break; |
| 561 } |
| 562 } |
| 563 #endif |
| 564 |
| 565 // Send the event. |
| 566 AccessibilityMenuItemInfo info(profile, name, submenu != NULL, index, count); |
| 567 SendAccessibilityNotification(type, &info); |
| 568 } |
OLD | NEW |