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" |
(...skipping 99 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
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<AccessibilityEventRouterGtk*>(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 } | 118 } |
119 | 119 |
| 120 gboolean OnTextBufferChanged(GSignalInvocationHint *ihint, |
| 121 guint n_param_values, |
| 122 const GValue* param_values, |
| 123 gpointer user_data) { |
| 124 // The text hasn't changed yet, so defer calling |
| 125 // DispatchAccessibilityNotification. |
| 126 reinterpret_cast<AccessibilityEventRouterGtk*>(user_data)-> |
| 127 PostDispatchAccessibilityNotification( |
| 128 NULL, NotificationType::ACCESSIBILITY_TEXT_CHANGED); |
| 129 return TRUE; |
| 130 } |
| 131 |
| 132 gboolean OnTextViewChanged(GSignalInvocationHint *ihint, |
| 133 guint n_param_values, |
| 134 const GValue* param_values, |
| 135 gpointer user_data) { |
| 136 GtkWidget* widget = GTK_WIDGET(g_value_get_object(param_values)); |
| 137 if (!GTK_IS_TEXT_VIEW(widget)) { |
| 138 return TRUE; |
| 139 } |
| 140 // The text hasn't changed yet, so defer calling |
| 141 // DispatchAccessibilityNotification. |
| 142 reinterpret_cast<AccessibilityEventRouterGtk*>(user_data)-> |
| 143 PostDispatchAccessibilityNotification( |
| 144 widget, NotificationType::ACCESSIBILITY_TEXT_CHANGED); |
| 145 return TRUE; |
| 146 } |
| 147 |
120 gboolean OnMenuMoveCurrent(GSignalInvocationHint *ihint, | 148 gboolean OnMenuMoveCurrent(GSignalInvocationHint *ihint, |
121 guint n_param_values, | 149 guint n_param_values, |
122 const GValue* param_values, | 150 const GValue* param_values, |
123 gpointer user_data) { | 151 gpointer user_data) { |
124 // Get the widget (the GtkMenu). | 152 // Get the widget (the GtkMenu). |
125 GtkWidget* widget = GTK_WIDGET(g_value_get_object(param_values)); | 153 GtkWidget* widget = GTK_WIDGET(g_value_get_object(param_values)); |
126 | 154 |
127 // Moving may move us into or out of a submenu, so after the menu | 155 // 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, | 156 // item moves, |widget| may not be valid anymore. To be safe, then, |
129 // find the topmost ancestor of this menu and post the notification | 157 // find the topmost ancestor of this menu and post the notification |
130 // dispatch on that menu. Then the dispatcher will recurse into submenus | 158 // dispatch on that menu. Then the dispatcher will recurse into submenus |
131 // as necessary to figure out which item is focused. | 159 // as necessary to figure out which item is focused. |
132 while (GTK_MENU_SHELL(widget)->parent_menu_shell) | 160 while (GTK_MENU_SHELL(widget)->parent_menu_shell) |
133 widget = GTK_MENU_SHELL(widget)->parent_menu_shell; | 161 widget = GTK_MENU_SHELL(widget)->parent_menu_shell; |
134 | 162 |
135 // The menu item hasn't moved yet, so we want to defer calling | 163 // The menu item hasn't moved yet, so we want to defer calling |
136 // DispatchAccessibilityNotification until after it does. | 164 // DispatchAccessibilityNotification until after it does. |
137 reinterpret_cast<AccessibilityEventRouterGtk*>(user_data)-> | 165 reinterpret_cast<AccessibilityEventRouterGtk*>(user_data)-> |
138 PostDispatchAccessibilityNotification( | 166 PostDispatchAccessibilityNotification( |
139 widget, NotificationType::ACCESSIBILITY_CONTROL_FOCUSED); | 167 widget, NotificationType::ACCESSIBILITY_CONTROL_FOCUSED); |
140 return TRUE; | 168 return TRUE; |
141 } | 169 } |
142 | 170 |
143 } // anonymous namespace | 171 } // anonymous namespace |
144 | 172 |
145 AccessibilityEventRouterGtk::AccessibilityEventRouterGtk() | 173 AccessibilityEventRouterGtk::AccessibilityEventRouterGtk() |
146 : listening_(false), | 174 : listening_(false), |
147 most_recent_profile_(NULL), | 175 most_recent_profile_(NULL), |
| 176 most_recent_widget_(NULL), |
148 method_factory_(this) { | 177 method_factory_(this) { |
149 // We don't want our event listeners to be installed if accessibility is | 178 // We don't want our event listeners to be installed if accessibility is |
150 // disabled. Install listeners so we can install and uninstall them as | 179 // disabled. Install listeners so we can install and uninstall them as |
151 // needed, then install them now if it's currently enabled. | 180 // needed, then install them now if it's currently enabled. |
152 ExtensionAccessibilityEventRouter *extension_event_router = | 181 ExtensionAccessibilityEventRouter *extension_event_router = |
153 ExtensionAccessibilityEventRouter::GetInstance(); | 182 ExtensionAccessibilityEventRouter::GetInstance(); |
154 extension_event_router->AddOnEnabledListener( | 183 extension_event_router->AddOnEnabledListener( |
155 NewCallback(this, | 184 NewCallback(this, |
156 &AccessibilityEventRouterGtk::InstallEventListeners)); | 185 &AccessibilityEventRouterGtk::InstallEventListeners)); |
157 extension_event_router->AddOnDisabledListener( | 186 extension_event_router->AddOnDisabledListener( |
(...skipping 25 matching lines...) Expand all Loading... |
183 | 212 |
184 void AccessibilityEventRouterGtk::InstallEventListeners() { | 213 void AccessibilityEventRouterGtk::InstallEventListeners() { |
185 // Create and destroy each type of widget we need signals for, | 214 // Create and destroy each type of widget we need signals for, |
186 // to ensure their modules are loaded, otherwise g_signal_lookup | 215 // to ensure their modules are loaded, otherwise g_signal_lookup |
187 // might fail. | 216 // might fail. |
188 g_object_unref(g_object_ref_sink(gtk_combo_box_new())); | 217 g_object_unref(g_object_ref_sink(gtk_combo_box_new())); |
189 g_object_unref(g_object_ref_sink(gtk_entry_new())); | 218 g_object_unref(g_object_ref_sink(gtk_entry_new())); |
190 g_object_unref(g_object_ref_sink(gtk_notebook_new())); | 219 g_object_unref(g_object_ref_sink(gtk_notebook_new())); |
191 g_object_unref(g_object_ref_sink(gtk_toggle_button_new())); | 220 g_object_unref(g_object_ref_sink(gtk_toggle_button_new())); |
192 g_object_unref(g_object_ref_sink(gtk_tree_view_new())); | 221 g_object_unref(g_object_ref_sink(gtk_tree_view_new())); |
| 222 g_object_unref(g_object_ref_sink(gtk_text_view_new())); |
| 223 g_object_unref(g_object_ref_sink(gtk_text_buffer_new(NULL))); |
193 | 224 |
194 // Add signal emission hooks for the events we're interested in. | 225 // Add signal emission hooks for the events we're interested in. |
195 InstallEventListener("clicked", GTK_TYPE_BUTTON, OnButtonClicked); | 226 InstallEventListener("clicked", GTK_TYPE_BUTTON, OnButtonClicked); |
196 InstallEventListener("changed", GTK_TYPE_COMBO_BOX, OnComboBoxChanged); | 227 InstallEventListener("changed", GTK_TYPE_COMBO_BOX, OnComboBoxChanged); |
197 InstallEventListener("cursor-changed", GTK_TYPE_TREE_VIEW, | 228 InstallEventListener("cursor-changed", GTK_TYPE_TREE_VIEW, |
198 OnTreeViewCursorChanged); | 229 OnTreeViewCursorChanged); |
199 InstallEventListener("changed", GTK_TYPE_ENTRY, OnEntryChanged); | 230 InstallEventListener("changed", GTK_TYPE_ENTRY, OnEntryChanged); |
200 InstallEventListener("insert-text", GTK_TYPE_ENTRY, OnEntryChanged); | 231 InstallEventListener("insert-text", GTK_TYPE_ENTRY, OnEntryChanged); |
201 InstallEventListener("delete-text", GTK_TYPE_ENTRY, OnEntryChanged); | 232 InstallEventListener("delete-text", GTK_TYPE_ENTRY, OnEntryChanged); |
202 InstallEventListener("move-cursor", GTK_TYPE_ENTRY, OnEntryChanged); | 233 InstallEventListener("move-cursor", GTK_TYPE_ENTRY, OnEntryChanged); |
203 InstallEventListener("focus-in-event", GTK_TYPE_WIDGET, OnWidgetFocused); | 234 InstallEventListener("focus-in-event", GTK_TYPE_WIDGET, OnWidgetFocused); |
204 InstallEventListener("switch-page", GTK_TYPE_NOTEBOOK, OnPageSwitched); | 235 InstallEventListener("switch-page", GTK_TYPE_NOTEBOOK, OnPageSwitched); |
205 InstallEventListener("toggled", GTK_TYPE_TOGGLE_BUTTON, OnButtonToggled); | 236 InstallEventListener("toggled", GTK_TYPE_TOGGLE_BUTTON, OnButtonToggled); |
206 InstallEventListener("move-current", GTK_TYPE_MENU, OnMenuMoveCurrent); | 237 InstallEventListener("move-current", GTK_TYPE_MENU, OnMenuMoveCurrent); |
| 238 InstallEventListener("changed", GTK_TYPE_TEXT_BUFFER, OnTextBufferChanged); |
| 239 InstallEventListener("move-cursor", GTK_TYPE_TEXT_VIEW, OnTextViewChanged); |
207 | 240 |
208 listening_ = true; | 241 listening_ = true; |
209 } | 242 } |
210 | 243 |
211 void AccessibilityEventRouterGtk::RemoveEventListeners() { | 244 void AccessibilityEventRouterGtk::RemoveEventListeners() { |
212 for (size_t i = 0; i < installed_hooks_.size(); i++) { | 245 for (size_t i = 0; i < installed_hooks_.size(); i++) { |
213 g_signal_remove_emission_hook( | 246 g_signal_remove_emission_hook( |
214 installed_hooks_[i].signal_id, | 247 installed_hooks_[i].signal_id, |
215 installed_hooks_[i].hook_id); | 248 installed_hooks_[i].hook_id); |
216 } | 249 } |
(...skipping 73 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
290 listening_ = false; | 323 listening_ = false; |
291 } | 324 } |
292 | 325 |
293 void AccessibilityEventRouterGtk::DispatchAccessibilityNotification( | 326 void AccessibilityEventRouterGtk::DispatchAccessibilityNotification( |
294 GtkWidget* widget, NotificationType type) { | 327 GtkWidget* widget, NotificationType type) { |
295 if (!listening_) | 328 if (!listening_) |
296 return; | 329 return; |
297 | 330 |
298 Profile* profile = NULL; | 331 Profile* profile = NULL; |
299 bool is_accessible; | 332 bool is_accessible; |
| 333 |
| 334 // Special case: when we get ACCESSIBILITY_TEXT_CHANGED, we don't get |
| 335 // a pointer to the widget, so we try to retrieve it from the most recent |
| 336 // widget. |
| 337 if (widget == NULL && |
| 338 type == NotificationType::ACCESSIBILITY_TEXT_CHANGED && |
| 339 most_recent_widget_ && |
| 340 GTK_IS_TEXT_VIEW(most_recent_widget_)) { |
| 341 widget = most_recent_widget_; |
| 342 } |
| 343 |
| 344 if (!widget) |
| 345 return; |
| 346 |
| 347 most_recent_widget_ = widget; |
300 FindWidget(widget, &profile, &is_accessible); | 348 FindWidget(widget, &profile, &is_accessible); |
301 if (profile) | 349 if (profile) |
302 most_recent_profile_ = profile; | 350 most_recent_profile_ = profile; |
303 | 351 |
304 // Special case: a GtkMenu isn't associated with any particular | 352 // Special case: a GtkMenu isn't associated with any particular |
305 // toplevel window, so menu events get routed to the profile of | 353 // toplevel window, so menu events get routed to the profile of |
306 // the most recent event that was associated with a window. | 354 // the most recent event that was associated with a window. |
307 if (GTK_IS_MENU_SHELL(widget) && most_recent_profile_) { | 355 if (GTK_IS_MENU_SHELL(widget) && most_recent_profile_) { |
308 SendMenuItemNotification(widget, type, most_recent_profile_); | 356 SendMenuItemNotification(widget, type, most_recent_profile_); |
309 return; | 357 return; |
(...skipping 14 matching lines...) Expand all Loading... |
324 SendComboBoxNotification(widget, type, profile); | 372 SendComboBoxNotification(widget, type, profile); |
325 } else if (parent && GTK_IS_COMBO_BOX(parent)) { | 373 } else if (parent && GTK_IS_COMBO_BOX(parent)) { |
326 SendComboBoxNotification(parent, type, profile); | 374 SendComboBoxNotification(parent, type, profile); |
327 } else if (GTK_IS_RADIO_BUTTON(widget)) { | 375 } else if (GTK_IS_RADIO_BUTTON(widget)) { |
328 SendRadioButtonNotification(widget, type, profile); | 376 SendRadioButtonNotification(widget, type, profile); |
329 } else if (GTK_IS_TOGGLE_BUTTON(widget)) { | 377 } else if (GTK_IS_TOGGLE_BUTTON(widget)) { |
330 SendCheckboxNotification(widget, type, profile); | 378 SendCheckboxNotification(widget, type, profile); |
331 } else if (GTK_IS_BUTTON(widget)) { | 379 } else if (GTK_IS_BUTTON(widget)) { |
332 SendButtonNotification(widget, type, profile); | 380 SendButtonNotification(widget, type, profile); |
333 } else if (GTK_IS_ENTRY(widget)) { | 381 } else if (GTK_IS_ENTRY(widget)) { |
334 SendTextBoxNotification(widget, type, profile); | 382 SendEntryNotification(widget, type, profile); |
| 383 } else if (GTK_IS_TEXT_VIEW(widget)) { |
| 384 SendTextViewNotification(widget, type, profile); |
335 } else if (GTK_IS_NOTEBOOK(widget)) { | 385 } else if (GTK_IS_NOTEBOOK(widget)) { |
336 SendTabNotification(widget, type, profile); | 386 SendTabNotification(widget, type, profile); |
337 } else if (GTK_IS_TREE_VIEW(widget)) { | 387 } else if (GTK_IS_TREE_VIEW(widget)) { |
338 SendListBoxNotification(widget, type, profile); | 388 SendListBoxNotification(widget, type, profile); |
339 } else { | 389 } else { |
340 // If we have no idea what this control is, return and skip the | 390 // If we have no idea what this control is, return and skip the |
341 // temporary pause in event listening. | 391 // temporary pause in event listening. |
342 return; | 392 return; |
343 } | 393 } |
344 | 394 |
(...skipping 58 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
403 | 453 |
404 void AccessibilityEventRouterGtk::SendButtonNotification( | 454 void AccessibilityEventRouterGtk::SendButtonNotification( |
405 GtkWidget* widget, NotificationType type, Profile* profile) { | 455 GtkWidget* widget, NotificationType type, Profile* profile) { |
406 std::string button_name = GetWidgetName(widget); | 456 std::string button_name = GetWidgetName(widget); |
407 if (button_name.empty() && gtk_button_get_label(GTK_BUTTON(widget))) | 457 if (button_name.empty() && gtk_button_get_label(GTK_BUTTON(widget))) |
408 button_name = gtk_button_get_label(GTK_BUTTON(widget)); | 458 button_name = gtk_button_get_label(GTK_BUTTON(widget)); |
409 AccessibilityButtonInfo info(profile, button_name); | 459 AccessibilityButtonInfo info(profile, button_name); |
410 SendAccessibilityNotification(type, &info); | 460 SendAccessibilityNotification(type, &info); |
411 } | 461 } |
412 | 462 |
413 void AccessibilityEventRouterGtk::SendTextBoxNotification( | 463 void AccessibilityEventRouterGtk::SendEntryNotification( |
414 GtkWidget* widget, NotificationType type, Profile* profile) { | 464 GtkWidget* widget, NotificationType type, Profile* profile) { |
415 std::string name = GetWidgetName(widget); | 465 std::string name = GetWidgetName(widget); |
416 std::string value = gtk_entry_get_text(GTK_ENTRY(widget)); | 466 std::string value = gtk_entry_get_text(GTK_ENTRY(widget)); |
417 gint start_pos; | 467 gint start_pos; |
418 gint end_pos; | 468 gint end_pos; |
419 gtk_editable_get_selection_bounds(GTK_EDITABLE(widget), &start_pos, &end_pos); | 469 gtk_editable_get_selection_bounds(GTK_EDITABLE(widget), &start_pos, &end_pos); |
420 AccessibilityTextBoxInfo info(profile, name, false); | 470 AccessibilityTextBoxInfo info(profile, name, false); |
421 info.SetValue(value, start_pos, end_pos); | 471 info.SetValue(value, start_pos, end_pos); |
422 SendAccessibilityNotification(type, &info); | 472 SendAccessibilityNotification(type, &info); |
423 } | 473 } |
424 | 474 |
| 475 void AccessibilityEventRouterGtk::SendTextViewNotification( |
| 476 GtkWidget* widget, NotificationType type, Profile* profile) { |
| 477 std::string name = GetWidgetName(widget); |
| 478 GtkTextBuffer* buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(widget)); |
| 479 GtkTextIter start, end; |
| 480 gtk_text_buffer_get_bounds(buffer, &start, &end); |
| 481 gchar* text = gtk_text_buffer_get_text(buffer, &start, &end, false); |
| 482 std::string value = text; |
| 483 g_free(text); |
| 484 GtkTextIter sel_start, sel_end; |
| 485 gtk_text_buffer_get_selection_bounds(buffer, &sel_start, &sel_end); |
| 486 int start_pos = gtk_text_iter_get_offset(&sel_start); |
| 487 int end_pos = gtk_text_iter_get_offset(&sel_end); |
| 488 AccessibilityTextBoxInfo info(profile, name, false); |
| 489 info.SetValue(value, start_pos, end_pos); |
| 490 SendAccessibilityNotification(type, &info); |
| 491 } |
| 492 |
425 void AccessibilityEventRouterGtk::SendTabNotification( | 493 void AccessibilityEventRouterGtk::SendTabNotification( |
426 GtkWidget* widget, NotificationType type, Profile* profile) { | 494 GtkWidget* widget, NotificationType type, Profile* profile) { |
427 int index = gtk_notebook_get_current_page(GTK_NOTEBOOK(widget)); | 495 int index = gtk_notebook_get_current_page(GTK_NOTEBOOK(widget)); |
428 int page_count = gtk_notebook_get_n_pages(GTK_NOTEBOOK(widget)); | 496 int page_count = gtk_notebook_get_n_pages(GTK_NOTEBOOK(widget)); |
429 GtkWidget* page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(widget), index); | 497 GtkWidget* page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(widget), index); |
430 GtkWidget* label = gtk_notebook_get_tab_label(GTK_NOTEBOOK(widget), page); | 498 GtkWidget* label = gtk_notebook_get_tab_label(GTK_NOTEBOOK(widget), page); |
431 std::string name = GetWidgetName(widget); | 499 std::string name = GetWidgetName(widget); |
432 if (name.empty() && gtk_label_get_text(GTK_LABEL(label))) { | 500 if (name.empty() && gtk_label_get_text(GTK_LABEL(label))) { |
433 name = gtk_label_get_text(GTK_LABEL(label)); | 501 name = gtk_label_get_text(GTK_LABEL(label)); |
434 } | 502 } |
(...skipping 124 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
559 name = gtk_label_get_label(GTK_LABEL(child)); | 627 name = gtk_label_get_label(GTK_LABEL(child)); |
560 break; | 628 break; |
561 } | 629 } |
562 } | 630 } |
563 #endif | 631 #endif |
564 | 632 |
565 // Send the event. | 633 // Send the event. |
566 AccessibilityMenuItemInfo info(profile, name, submenu != NULL, index, count); | 634 AccessibilityMenuItemInfo info(profile, name, submenu != NULL, index, count); |
567 SendAccessibilityNotification(type, &info); | 635 SendAccessibilityNotification(type, &info); |
568 } | 636 } |
OLD | NEW |