| 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 |