Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(106)

Side by Side Diff: chrome/browser/gtk/accessibility_event_router_gtk.cc

Issue 402099: Add an accessibility API for events raised outside of the web content. (Closed) Base URL: http://src.chromium.org/svn/trunk/src/
Patch Set: '' Created 10 years, 10 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
Property Changes:
Added: svn:eol-style
+ LF
OLDNEW
(Empty)
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
3 // found in the LICENSE file.
4
5 #include "chrome/browser/gtk/accessibility_event_router_gtk.h"
6
7 #include "base/basictypes.h"
8 #include "base/stl_util-inl.h"
9 #include "chrome/browser/extensions/extension_accessibility_api.h"
10 #include "chrome/browser/gtk/gtk_chrome_link_button.h"
11 #include "chrome/browser/profile.h"
12 #include "chrome/common/notification_type.h"
13
14 namespace {
15
16 //
17 // Callbacks triggered by signals on gtk widgets.
18 //
19
20 gboolean OnWidgetFocused(GSignalInvocationHint *ihint,
21 guint n_param_values,
22 const GValue* param_values,
23 gpointer user_data) {
24 GtkWidget* widget = GTK_WIDGET(g_value_get_object(param_values));
25 reinterpret_cast<AccessibilityEventRouter *>(user_data)
26 ->DispatchAccessibilityNotification(
27 widget, NotificationType::ACCESSIBILITY_CONTROL_FOCUSED);
28 return true;
29 }
30
31 gboolean OnButtonClicked(GSignalInvocationHint *ihint,
32 guint n_param_values,
33 const GValue* param_values,
34 gpointer user_data) {
35 GtkWidget* widget = GTK_WIDGET(g_value_get_object(param_values));
36 // Skip toggle buttons because we're also listening on "toggle" events.
37 if (GTK_IS_TOGGLE_BUTTON(widget))
38 return true;
39 reinterpret_cast<AccessibilityEventRouter *>(user_data)
40 ->DispatchAccessibilityNotification(
41 widget, NotificationType::ACCESSIBILITY_CONTROL_ACTION);
42 return true;
43 }
44
45 gboolean OnButtonToggled(GSignalInvocationHint *ihint,
46 guint n_param_values,
47 const GValue* param_values,
48 gpointer user_data) {
49 GtkWidget* widget = GTK_WIDGET(g_value_get_object(param_values));
50 bool checked = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
51 // Skip propagating an "uncheck" event for a radio button because it's
52 // redundant; there will always be a corresponding "check" event for
53 // a different radio button the group.
54 if (GTK_IS_RADIO_BUTTON(widget) && !checked)
55 return true;
56 reinterpret_cast<AccessibilityEventRouter *>(user_data)
57 ->DispatchAccessibilityNotification(
58 widget, NotificationType::ACCESSIBILITY_CONTROL_ACTION);
59 return true;
60 }
61
62 gboolean OnSwitchPage(GSignalInvocationHint *ihint,
63 guint n_param_values,
64 const GValue* param_values,
65 gpointer user_data) {
66 GtkWidget* widget = GTK_WIDGET(g_value_get_object(param_values));
67 reinterpret_cast<AccessibilityEventRouter *>(user_data)
68 ->DispatchAccessibilityNotification(
69 widget, NotificationType::ACCESSIBILITY_CONTROL_ACTION);
70 return true;
71 }
72
73 } // anonymous namespace
74
75 AccessibilityEventRouter::AccessibilityEventRouter() {
76 // We don't want our event listeners to be installed if accessibility is
77 // disabled. Install listeners so we can install and uninstall them as
78 // needed, then install them now if it's currently enabled.
79 ExtensionAccessibilityEventRouter *accessibility_event_router =
80 ExtensionAccessibilityEventRouter::GetInstance();
81 accessibility_event_router->AddOnEnabledListener(
82 NewCallback(this,
83 &AccessibilityEventRouter::InstallEventListeners));
84 accessibility_event_router->AddOnDisabledListener(
85 NewCallback(this,
86 &AccessibilityEventRouter::RemoveEventListeners));
87 if (accessibility_event_router->IsAccessibilityEnabled()) {
88 InstallEventListeners();
89 }
90 }
91
92 // static
93 AccessibilityEventRouter* AccessibilityEventRouter::GetInstance() {
94 return Singleton<AccessibilityEventRouter>::get();
95 }
96
97 void AccessibilityEventRouter::InstallEventListeners() {
98 // Create and destroy a GtkNotebook to ensure this module is loaded,
99 // otherwise we can't lookup its signals. All of the other modules we
100 // need will already be loaded by the time we get here.
101 g_object_unref(g_object_ref_sink(gtk_notebook_new()));
102
103 // Add signal emission hooks for the events we're interested in.
104 focus_hook_ = g_signal_add_emission_hook(
105 g_signal_lookup("focus-in-event", GTK_TYPE_WIDGET),
106 0, OnWidgetFocused, (gpointer)this, NULL);
107 click_hook_ = g_signal_add_emission_hook(
108 g_signal_lookup("clicked", GTK_TYPE_BUTTON),
109 0, OnButtonClicked, (gpointer)this, NULL);
110 toggle_hook_ = g_signal_add_emission_hook(
111 g_signal_lookup("toggled", GTK_TYPE_TOGGLE_BUTTON),
112 0, OnButtonToggled, (gpointer)this, NULL);
113 switch_page_hook_ = g_signal_add_emission_hook(
114 g_signal_lookup("switch-page", GTK_TYPE_NOTEBOOK),
115 0, OnSwitchPage, (gpointer)this, NULL);
116 }
117
118 void AccessibilityEventRouter::RemoveEventListeners() {
119 g_signal_remove_emission_hook(
120 g_signal_lookup("focus-in-event", GTK_TYPE_WIDGET), focus_hook_);
121 g_signal_remove_emission_hook(
122 g_signal_lookup("clicked", GTK_TYPE_BUTTON), click_hook_);
123 g_signal_remove_emission_hook(
124 g_signal_lookup("toggled", GTK_TYPE_TOGGLE_BUTTON), toggle_hook_);
125 g_signal_remove_emission_hook(
126 g_signal_lookup("switch-page", GTK_TYPE_NOTEBOOK), switch_page_hook_);
127 }
128
129 void AccessibilityEventRouter::AddRootWidget(
130 GtkWidget* root_widget, Profile* profile) {
131 root_widget_profile_map_[root_widget] = profile;
132 }
133
134 void AccessibilityEventRouter::RemoveRootWidget(GtkWidget* root_widget) {
135 DCHECK(root_widget_profile_map_.find(root_widget) !=
136 root_widget_profile_map_.end());
137 root_widget_profile_map_.erase(root_widget);
138 }
139
140 void AccessibilityEventRouter::IgnoreWidget(GtkWidget* widget) {
141 widget_info_map_[widget].ignore = true;
142 }
143
144 void AccessibilityEventRouter::SetWidgetName(
145 GtkWidget* widget, std::string name) {
146 widget_info_map_[widget].name = name;
147 }
148
149 void AccessibilityEventRouter::RemoveWidget(GtkWidget* widget) {
150 DCHECK(widget_info_map_.find(widget) != widget_info_map_.end());
151 widget_info_map_.erase(widget);
152 }
153
154 bool AccessibilityEventRouter::IsWidgetAccessible(
155 GtkWidget* widget, Profile** profile) {
156 // First see if it's a descendant of a root widget.
157 bool is_accessible = false;
158 for (base::hash_map<GtkWidget*, Profile*>::const_iterator iter =
159 root_widget_profile_map_.begin();
160 iter != root_widget_profile_map_.end();
161 ++iter) {
162 if (gtk_widget_is_ancestor(widget, iter->first)) {
163 is_accessible = true;
164 if (profile)
165 *profile = iter->second;
166 break;
167 }
168 }
169 if (!is_accessible)
170 return false;
171
172 // Now make sure it's not marked as a widget to be ignored.
173 base::hash_map<GtkWidget*, WidgetInfo>::const_iterator iter =
174 widget_info_map_.find(widget);
175 if (iter != widget_info_map_.end() && iter->second.ignore) {
176 is_accessible = false;
177 }
178
179 return is_accessible;
180 }
181
182 std::string AccessibilityEventRouter::GetWidgetName(GtkWidget* widget) {
183 base::hash_map<GtkWidget*, WidgetInfo>::const_iterator iter =
184 widget_info_map_.find(widget);
185 if (iter != widget_info_map_.end()) {
186 return iter->second.name;
187 } else {
188 return "";
189 }
190 }
191
192 void AccessibilityEventRouter::DispatchAccessibilityNotification(
193 GtkWidget* widget, NotificationType type) {
194 Profile *profile;
195 if (!IsWidgetAccessible(widget, &profile))
196 return;
197
198 // The order of these checks matters, because, for example, a radio button
199 // is a subclass of button. We need to catch the most specific type that
200 // we can handle for each object.
201 if (GTK_IS_RADIO_BUTTON(widget)) {
202 SendRadioButtonNotification(widget, type, profile);
203 } else if (GTK_IS_TOGGLE_BUTTON(widget)) {
204 SendCheckboxNotification(widget, type, profile);
205 } else if (GTK_IS_BUTTON(widget)) {
206 SendButtonNotification(widget, type, profile);
207 } else if (GTK_IS_ENTRY(widget)) {
208 SendTextBoxNotification(widget, type, profile);
209 } else if (GTK_IS_NOTEBOOK(widget)) {
210 SendTabNotification(widget, type, profile);
211 }
212 }
213
214 void AccessibilityEventRouter::SendRadioButtonNotification(
215 GtkWidget* widget, NotificationType type, Profile* profile) {
216 // Get the radio button name
217 std::string button_name = GetWidgetName(widget);
218 if (button_name.empty() && gtk_button_get_label(GTK_BUTTON(widget)))
219 button_name = gtk_button_get_label(GTK_BUTTON(widget));
220
221 // Get its state
222 bool checked = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
223
224 // Get the index of this radio button and the total number of
225 // radio buttons in the group.
226 int item_count = 0;
227 int item_index = -1;
228 for (GSList* group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(widget));
229 group;
230 group = group->next) {
231 if (group->data == widget) {
232 item_index = item_count;
233 }
234 item_count++;
235 }
236 item_index = item_count - 1 - item_index;
237
238 AccessibilityRadioButtonInfo info(
239 profile, button_name, checked, item_index, item_count);
240 SendAccessibilityNotification(type, &info);
241 }
242
243 void AccessibilityEventRouter::SendCheckboxNotification(
244 GtkWidget* widget, NotificationType type, Profile* profile) {
245 std::string button_name = GetWidgetName(widget);
246 if (button_name.empty() && gtk_button_get_label(GTK_BUTTON(widget)))
247 button_name = gtk_button_get_label(GTK_BUTTON(widget));
248 bool checked = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
249 AccessibilityCheckboxInfo info(profile, button_name, checked);
250 SendAccessibilityNotification(type, &info);
251 }
252
253 void AccessibilityEventRouter::SendButtonNotification(
254 GtkWidget* widget, NotificationType type, Profile* profile) {
255 std::string button_name = GetWidgetName(widget);
256 if (button_name.empty() && gtk_button_get_label(GTK_BUTTON(widget)))
257 button_name = gtk_button_get_label(GTK_BUTTON(widget));
258 AccessibilityButtonInfo info(profile, button_name);
259 SendAccessibilityNotification(type, &info);
260 }
261
262 void AccessibilityEventRouter::SendTextBoxNotification(
263 GtkWidget* widget, NotificationType type, Profile* profile) {
264 std::string name = GetWidgetName(widget);
265 std::string value = gtk_entry_get_text(GTK_ENTRY(widget));
266 gint start_pos;
267 gint end_pos;
268 gtk_editable_get_selection_bounds(GTK_EDITABLE(widget), &start_pos, &end_pos);
269 AccessibilityTextBoxInfo info(profile, name, false);
270 info.SetValue(value, start_pos, end_pos);
271 SendAccessibilityNotification(type, &info);
272 }
273
274 void AccessibilityEventRouter::SendTabNotification(
275 GtkWidget* widget, NotificationType type, Profile* profile) {
276 int index = gtk_notebook_get_current_page(GTK_NOTEBOOK(widget));
277 int page_count = gtk_notebook_get_n_pages(GTK_NOTEBOOK(widget));
278 GtkWidget* page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(widget), index);
279 GtkWidget* label = gtk_notebook_get_tab_label(GTK_NOTEBOOK(widget), page);
280 std::string name = GetWidgetName(widget);
281 if (name.empty() && gtk_label_get_text(GTK_LABEL(label))) {
282 name = gtk_label_get_text(GTK_LABEL(label));
283 }
284 AccessibilityTabInfo info(profile, name, index, page_count);
285 SendAccessibilityNotification(type, &info);
286 }
OLDNEW
« no previous file with comments | « chrome/browser/gtk/accessibility_event_router_gtk.h ('k') | chrome/browser/gtk/accessible_widget_helper_gtk.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698