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

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

Issue 6251001: Move chrome/browser/gtk/ to chrome/browser/ui/gtk/... (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src/
Patch Set: '' Created 9 years, 11 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
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/callback.h"
9 #include "base/message_loop.h"
10 #include "base/stl_util-inl.h"
11 #include "chrome/browser/extensions/extension_accessibility_api.h"
12 #include "chrome/browser/gtk/gtk_chrome_link_button.h"
13 #include "chrome/browser/profiles/profile.h"
14 #include "chrome/common/notification_type.h"
15
16 #if defined(TOOLKIT_VIEWS)
17 #include "views/controls/textfield/gtk_views_textview.h"
18 #include "views/controls/textfield/gtk_views_entry.h"
19 #include "views/controls/textfield/native_textfield_gtk.h"
20 #endif
21
22 namespace {
23
24 //
25 // Callbacks triggered by signals on gtk widgets.
26 //
27
28 gboolean OnWidgetFocused(GSignalInvocationHint *ihint,
29 guint n_param_values,
30 const GValue* param_values,
31 gpointer user_data) {
32 GtkWidget* widget = GTK_WIDGET(g_value_get_object(param_values));
33 reinterpret_cast<AccessibilityEventRouterGtk*>(user_data)->
34 DispatchAccessibilityNotification(
35 widget, NotificationType::ACCESSIBILITY_CONTROL_FOCUSED);
36 return TRUE;
37 }
38
39 gboolean OnButtonClicked(GSignalInvocationHint *ihint,
40 guint n_param_values,
41 const GValue* param_values,
42 gpointer user_data) {
43 GtkWidget* widget = GTK_WIDGET(g_value_get_object(param_values));
44 // Skip toggle buttons because we're also listening on "toggle" events.
45 if (GTK_IS_TOGGLE_BUTTON(widget))
46 return true;
47 reinterpret_cast<AccessibilityEventRouterGtk*>(user_data)->
48 DispatchAccessibilityNotification(
49 widget, NotificationType::ACCESSIBILITY_CONTROL_ACTION);
50 return TRUE;
51 }
52
53 gboolean OnButtonToggled(GSignalInvocationHint *ihint,
54 guint n_param_values,
55 const GValue* param_values,
56 gpointer user_data) {
57 GtkWidget* widget = GTK_WIDGET(g_value_get_object(param_values));
58 bool checked = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
59 // Skip propagating an "uncheck" event for a radio button because it's
60 // redundant; there will always be a corresponding "check" event for
61 // a different radio button the group.
62 if (GTK_IS_RADIO_BUTTON(widget) && !checked)
63 return true;
64 reinterpret_cast<AccessibilityEventRouterGtk*>(user_data)->
65 DispatchAccessibilityNotification(
66 widget, NotificationType::ACCESSIBILITY_CONTROL_ACTION);
67 return TRUE;
68 }
69
70 gboolean OnPageSwitched(GSignalInvocationHint *ihint,
71 guint n_param_values,
72 const GValue* param_values,
73 gpointer user_data) {
74 GtkWidget* widget = GTK_WIDGET(g_value_get_object(param_values));
75 // The page hasn't switched yet, so defer calling
76 // DispatchAccessibilityNotification.
77 reinterpret_cast<AccessibilityEventRouterGtk*>(user_data)->
78 PostDispatchAccessibilityNotification(
79 widget, NotificationType::ACCESSIBILITY_CONTROL_ACTION);
80 return TRUE;
81 }
82
83 gboolean OnComboBoxChanged(GSignalInvocationHint *ihint,
84 guint n_param_values,
85 const GValue* param_values,
86 gpointer user_data) {
87 GtkWidget* widget = GTK_WIDGET(g_value_get_object(param_values));
88 if (!GTK_IS_COMBO_BOX(widget))
89 return true;
90 reinterpret_cast<AccessibilityEventRouterGtk*>(user_data)->
91 DispatchAccessibilityNotification(
92 widget, NotificationType::ACCESSIBILITY_CONTROL_ACTION);
93 return TRUE;
94 }
95
96 gboolean OnTreeViewCursorChanged(GSignalInvocationHint *ihint,
97 guint n_param_values,
98 const GValue* param_values,
99 gpointer user_data) {
100 GtkWidget* widget = GTK_WIDGET(g_value_get_object(param_values));
101 if (!GTK_IS_TREE_VIEW(widget)) {
102 return true;
103 }
104 reinterpret_cast<AccessibilityEventRouterGtk*>(user_data)->
105 DispatchAccessibilityNotification(
106 widget, NotificationType::ACCESSIBILITY_CONTROL_ACTION);
107 return TRUE;
108 }
109
110 gboolean OnEntryChanged(GSignalInvocationHint *ihint,
111 guint n_param_values,
112 const GValue* param_values,
113 gpointer user_data) {
114 GtkWidget* widget = GTK_WIDGET(g_value_get_object(param_values));
115 if (!GTK_IS_ENTRY(widget)) {
116 return TRUE;
117 }
118 // The text hasn't changed yet, so defer calling
119 // DispatchAccessibilityNotification.
120 reinterpret_cast<AccessibilityEventRouterGtk*>(user_data)->
121 PostDispatchAccessibilityNotification(
122 widget, NotificationType::ACCESSIBILITY_TEXT_CHANGED);
123 return TRUE;
124 }
125
126 gboolean OnTextBufferChanged(GSignalInvocationHint *ihint,
127 guint n_param_values,
128 const GValue* param_values,
129 gpointer user_data) {
130 // The text hasn't changed yet, so defer calling
131 // DispatchAccessibilityNotification.
132 reinterpret_cast<AccessibilityEventRouterGtk*>(user_data)->
133 PostDispatchAccessibilityNotification(
134 NULL, NotificationType::ACCESSIBILITY_TEXT_CHANGED);
135 return TRUE;
136 }
137
138 gboolean OnTextViewChanged(GSignalInvocationHint *ihint,
139 guint n_param_values,
140 const GValue* param_values,
141 gpointer user_data) {
142 GtkWidget* widget = GTK_WIDGET(g_value_get_object(param_values));
143 if (!GTK_IS_TEXT_VIEW(widget)) {
144 return TRUE;
145 }
146 // The text hasn't changed yet, so defer calling
147 // DispatchAccessibilityNotification.
148 reinterpret_cast<AccessibilityEventRouterGtk*>(user_data)->
149 PostDispatchAccessibilityNotification(
150 widget, NotificationType::ACCESSIBILITY_TEXT_CHANGED);
151 return TRUE;
152 }
153
154 gboolean OnMenuMoveCurrent(GSignalInvocationHint *ihint,
155 guint n_param_values,
156 const GValue* param_values,
157 gpointer user_data) {
158 // Get the widget (the GtkMenu).
159 GtkWidget* widget = GTK_WIDGET(g_value_get_object(param_values));
160
161 // Moving may move us into or out of a submenu, so after the menu
162 // item moves, |widget| may not be valid anymore. To be safe, then,
163 // find the topmost ancestor of this menu and post the notification
164 // dispatch on that menu. Then the dispatcher will recurse into submenus
165 // as necessary to figure out which item is focused.
166 while (GTK_MENU_SHELL(widget)->parent_menu_shell)
167 widget = GTK_MENU_SHELL(widget)->parent_menu_shell;
168
169 // The menu item hasn't moved yet, so we want to defer calling
170 // DispatchAccessibilityNotification until after it does.
171 reinterpret_cast<AccessibilityEventRouterGtk*>(user_data)->
172 PostDispatchAccessibilityNotification(
173 widget, NotificationType::ACCESSIBILITY_CONTROL_FOCUSED);
174 return TRUE;
175 }
176
177 } // anonymous namespace
178
179 AccessibilityEventRouterGtk::AccessibilityEventRouterGtk()
180 : listening_(false),
181 most_recent_profile_(NULL),
182 most_recent_widget_(NULL),
183 method_factory_(this) {
184 // We don't want our event listeners to be installed if accessibility is
185 // disabled. Install listeners so we can install and uninstall them as
186 // needed, then install them now if it's currently enabled.
187 ExtensionAccessibilityEventRouter *extension_event_router =
188 ExtensionAccessibilityEventRouter::GetInstance();
189 extension_event_router->AddOnEnabledListener(
190 NewCallback(this,
191 &AccessibilityEventRouterGtk::InstallEventListeners));
192 extension_event_router->AddOnDisabledListener(
193 NewCallback(this,
194 &AccessibilityEventRouterGtk::RemoveEventListeners));
195 if (extension_event_router->IsAccessibilityEnabled()) {
196 InstallEventListeners();
197 }
198 }
199
200 AccessibilityEventRouterGtk::~AccessibilityEventRouterGtk() {
201 RemoveEventListeners();
202 }
203
204 // static
205 AccessibilityEventRouterGtk* AccessibilityEventRouterGtk::GetInstance() {
206 return Singleton<AccessibilityEventRouterGtk>::get();
207 }
208
209 void AccessibilityEventRouterGtk::InstallEventListener(
210 const char* signal_name,
211 GType widget_type,
212 GSignalEmissionHook hook_func) {
213 guint signal_id = g_signal_lookup(signal_name, widget_type);
214 gulong hook_id = g_signal_add_emission_hook(
215 signal_id, 0, hook_func, reinterpret_cast<gpointer>(this), NULL);
216 installed_hooks_.push_back(InstalledHook(signal_id, hook_id));
217 }
218
219 bool AccessibilityEventRouterGtk::IsPassword(GtkWidget* widget) {
220 bool is_password = false;
221 #if defined (TOOLKIT_VIEWS)
222 is_password = (GTK_IS_VIEWS_ENTRY(widget) &&
223 GTK_VIEWS_ENTRY(widget)->host != NULL &&
224 GTK_VIEWS_ENTRY(widget)->host->IsPassword()) ||
225 (GTK_IS_VIEWS_TEXTVIEW(widget) &&
226 GTK_VIEWS_TEXTVIEW(widget)->host != NULL &&
227 GTK_VIEWS_TEXTVIEW(widget)->host->IsPassword());
228 #endif
229 return is_password;
230 }
231
232
233 void AccessibilityEventRouterGtk::InstallEventListeners() {
234 // Create and destroy each type of widget we need signals for,
235 // to ensure their modules are loaded, otherwise g_signal_lookup
236 // might fail.
237 g_object_unref(g_object_ref_sink(gtk_combo_box_new()));
238 g_object_unref(g_object_ref_sink(gtk_entry_new()));
239 g_object_unref(g_object_ref_sink(gtk_notebook_new()));
240 g_object_unref(g_object_ref_sink(gtk_toggle_button_new()));
241 g_object_unref(g_object_ref_sink(gtk_tree_view_new()));
242 g_object_unref(g_object_ref_sink(gtk_text_view_new()));
243 g_object_unref(g_object_ref_sink(gtk_text_buffer_new(NULL)));
244
245 // Add signal emission hooks for the events we're interested in.
246 InstallEventListener("clicked", GTK_TYPE_BUTTON, OnButtonClicked);
247 InstallEventListener("changed", GTK_TYPE_COMBO_BOX, OnComboBoxChanged);
248 InstallEventListener("cursor-changed", GTK_TYPE_TREE_VIEW,
249 OnTreeViewCursorChanged);
250 InstallEventListener("changed", GTK_TYPE_ENTRY, OnEntryChanged);
251 InstallEventListener("insert-text", GTK_TYPE_ENTRY, OnEntryChanged);
252 InstallEventListener("delete-text", GTK_TYPE_ENTRY, OnEntryChanged);
253 InstallEventListener("move-cursor", GTK_TYPE_ENTRY, OnEntryChanged);
254 InstallEventListener("focus-in-event", GTK_TYPE_WIDGET, OnWidgetFocused);
255 InstallEventListener("switch-page", GTK_TYPE_NOTEBOOK, OnPageSwitched);
256 InstallEventListener("toggled", GTK_TYPE_TOGGLE_BUTTON, OnButtonToggled);
257 InstallEventListener("move-current", GTK_TYPE_MENU, OnMenuMoveCurrent);
258 InstallEventListener("changed", GTK_TYPE_TEXT_BUFFER, OnTextBufferChanged);
259 InstallEventListener("move-cursor", GTK_TYPE_TEXT_VIEW, OnTextViewChanged);
260
261 listening_ = true;
262 }
263
264 void AccessibilityEventRouterGtk::RemoveEventListeners() {
265 for (size_t i = 0; i < installed_hooks_.size(); i++) {
266 g_signal_remove_emission_hook(
267 installed_hooks_[i].signal_id,
268 installed_hooks_[i].hook_id);
269 }
270 installed_hooks_.clear();
271
272 listening_ = false;
273 }
274
275 void AccessibilityEventRouterGtk::AddRootWidget(
276 GtkWidget* root_widget, Profile* profile) {
277 root_widget_info_map_[root_widget].refcount++;
278 root_widget_info_map_[root_widget].profile = profile;
279 }
280
281 void AccessibilityEventRouterGtk::RemoveRootWidget(GtkWidget* root_widget) {
282 DCHECK(root_widget_info_map_.find(root_widget) !=
283 root_widget_info_map_.end());
284 root_widget_info_map_[root_widget].refcount--;
285 if (root_widget_info_map_[root_widget].refcount == 0) {
286 root_widget_info_map_.erase(root_widget);
287 }
288 }
289
290 void AccessibilityEventRouterGtk::AddWidgetNameOverride(
291 GtkWidget* widget, std::string name) {
292 widget_info_map_[widget].name = name;
293 widget_info_map_[widget].refcount++;
294 }
295
296 void AccessibilityEventRouterGtk::RemoveWidgetNameOverride(GtkWidget* widget) {
297 DCHECK(widget_info_map_.find(widget) != widget_info_map_.end());
298 widget_info_map_[widget].refcount--;
299 if (widget_info_map_[widget].refcount == 0) {
300 widget_info_map_.erase(widget);
301 }
302 }
303
304 void AccessibilityEventRouterGtk::FindWidget(
305 GtkWidget* widget, Profile** profile, bool* is_accessible) {
306 *is_accessible = false;
307
308 for (base::hash_map<GtkWidget*, RootWidgetInfo>::const_iterator iter =
309 root_widget_info_map_.begin();
310 iter != root_widget_info_map_.end();
311 ++iter) {
312 if (widget == iter->first || gtk_widget_is_ancestor(widget, iter->first)) {
313 *is_accessible = true;
314 if (profile)
315 *profile = iter->second.profile;
316 break;
317 }
318 }
319 }
320
321 std::string AccessibilityEventRouterGtk::GetWidgetName(GtkWidget* widget) {
322 base::hash_map<GtkWidget*, WidgetInfo>::const_iterator iter =
323 widget_info_map_.find(widget);
324 if (iter != widget_info_map_.end()) {
325 return iter->second.name;
326 } else {
327 return "";
328 }
329 }
330
331 void AccessibilityEventRouterGtk::StartListening() {
332 listening_ = true;
333 }
334
335 void AccessibilityEventRouterGtk::StopListening() {
336 listening_ = false;
337 }
338
339 void AccessibilityEventRouterGtk::DispatchAccessibilityNotification(
340 GtkWidget* widget, NotificationType type) {
341 // If there's no message loop, we must be about to shutdown or we're
342 // running inside a test; either way, there's no reason to do any
343 // further processing.
344 if (!MessageLoop::current())
345 return;
346
347 if (!listening_)
348 return;
349
350 Profile* profile = NULL;
351 bool is_accessible;
352
353 // Special case: when we get ACCESSIBILITY_TEXT_CHANGED, we don't get
354 // a pointer to the widget, so we try to retrieve it from the most recent
355 // widget.
356 if (widget == NULL &&
357 type == NotificationType::ACCESSIBILITY_TEXT_CHANGED &&
358 most_recent_widget_ &&
359 GTK_IS_TEXT_VIEW(most_recent_widget_)) {
360 widget = most_recent_widget_;
361 }
362
363 if (!widget)
364 return;
365
366 most_recent_widget_ = widget;
367 FindWidget(widget, &profile, &is_accessible);
368 if (profile)
369 most_recent_profile_ = profile;
370
371 // Special case: a GtkMenu isn't associated with any particular
372 // toplevel window, so menu events get routed to the profile of
373 // the most recent event that was associated with a window.
374 if (GTK_IS_MENU_SHELL(widget) && most_recent_profile_) {
375 SendMenuItemNotification(widget, type, most_recent_profile_);
376 return;
377 }
378
379 // In all other cases, return if this widget wasn't marked as accessible.
380 if (!is_accessible)
381 return;
382
383 // The order of these checks matters, because, for example, a radio button
384 // is a subclass of button, and a combo box is a composite control where
385 // the focus event goes to the button that's a child of the combo box.
386 GtkWidget* parent = gtk_widget_get_parent(widget);
387 if (parent && GTK_IS_BUTTON(widget) && GTK_IS_TREE_VIEW(parent)) {
388 // This is a list box column header. Currently not supported.
389 return;
390 } else if (GTK_IS_COMBO_BOX(widget)) {
391 SendComboBoxNotification(widget, type, profile);
392 } else if (parent && GTK_IS_COMBO_BOX(parent)) {
393 SendComboBoxNotification(parent, type, profile);
394 } else if (GTK_IS_RADIO_BUTTON(widget)) {
395 SendRadioButtonNotification(widget, type, profile);
396 } else if (GTK_IS_TOGGLE_BUTTON(widget)) {
397 SendCheckboxNotification(widget, type, profile);
398 } else if (GTK_IS_BUTTON(widget)) {
399 SendButtonNotification(widget, type, profile);
400 } else if (GTK_IS_ENTRY(widget)) {
401 SendEntryNotification(widget, type, profile);
402 } else if (GTK_IS_TEXT_VIEW(widget)) {
403 SendTextViewNotification(widget, type, profile);
404 } else if (GTK_IS_NOTEBOOK(widget)) {
405 SendTabNotification(widget, type, profile);
406 } else if (GTK_IS_TREE_VIEW(widget)) {
407 SendListBoxNotification(widget, type, profile);
408 } else {
409 // If we have no idea what this control is, return and skip the
410 // temporary pause in event listening.
411 return;
412 }
413
414 // After this method returns, additional signal handlers will run,
415 // which will sometimes generate additional signals. To avoid
416 // generating redundant accessibility notifications for the same
417 // initial event, stop listening to all signals generated from now
418 // until this posted task runs.
419 StopListening();
420 MessageLoop::current()->PostTask(
421 FROM_HERE, method_factory_.NewRunnableMethod(
422 &AccessibilityEventRouterGtk::StartListening));
423 }
424
425 void AccessibilityEventRouterGtk::PostDispatchAccessibilityNotification(
426 GtkWidget* widget, NotificationType type) {
427 if (!MessageLoop::current())
428 return;
429
430 MessageLoop::current()->PostTask(
431 FROM_HERE, method_factory_.NewRunnableMethod(
432 &AccessibilityEventRouterGtk::DispatchAccessibilityNotification,
433 widget,
434 type));
435 }
436
437 void AccessibilityEventRouterGtk::SendRadioButtonNotification(
438 GtkWidget* widget, NotificationType type, Profile* profile) {
439 // Get the radio button name
440 std::string button_name = GetWidgetName(widget);
441 if (button_name.empty() && gtk_button_get_label(GTK_BUTTON(widget)))
442 button_name = gtk_button_get_label(GTK_BUTTON(widget));
443
444 // Get its state
445 bool checked = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
446
447 // Get the index of this radio button and the total number of
448 // radio buttons in the group.
449 int item_count = 0;
450 int item_index = -1;
451 for (GSList* group = gtk_radio_button_get_group(GTK_RADIO_BUTTON(widget));
452 group;
453 group = group->next) {
454 if (group->data == widget) {
455 item_index = item_count;
456 }
457 item_count++;
458 }
459 item_index = item_count - 1 - item_index;
460
461 AccessibilityRadioButtonInfo info(
462 profile, button_name, checked, item_index, item_count);
463 SendAccessibilityNotification(type, &info);
464 }
465
466 void AccessibilityEventRouterGtk::SendCheckboxNotification(
467 GtkWidget* widget, NotificationType type, Profile* profile) {
468 std::string button_name = GetWidgetName(widget);
469 if (button_name.empty() && gtk_button_get_label(GTK_BUTTON(widget)))
470 button_name = gtk_button_get_label(GTK_BUTTON(widget));
471 bool checked = gtk_toggle_button_get_active(GTK_TOGGLE_BUTTON(widget));
472 AccessibilityCheckboxInfo info(profile, button_name, checked);
473 SendAccessibilityNotification(type, &info);
474 }
475
476 void AccessibilityEventRouterGtk::SendButtonNotification(
477 GtkWidget* widget, NotificationType type, Profile* profile) {
478 std::string button_name = GetWidgetName(widget);
479 if (button_name.empty() && gtk_button_get_label(GTK_BUTTON(widget)))
480 button_name = gtk_button_get_label(GTK_BUTTON(widget));
481 AccessibilityButtonInfo info(profile, button_name);
482 SendAccessibilityNotification(type, &info);
483 }
484
485 void AccessibilityEventRouterGtk::SendEntryNotification(
486 GtkWidget* widget, NotificationType type, Profile* profile) {
487 std::string name = GetWidgetName(widget);
488 std::string value = gtk_entry_get_text(GTK_ENTRY(widget));
489 gint start_pos;
490 gint end_pos;
491 gtk_editable_get_selection_bounds(GTK_EDITABLE(widget), &start_pos, &end_pos);
492 AccessibilityTextBoxInfo info(profile, name, IsPassword(widget));
493 info.SetValue(value, start_pos, end_pos);
494 SendAccessibilityNotification(type, &info);
495 }
496
497 void AccessibilityEventRouterGtk::SendTextViewNotification(
498 GtkWidget* widget, NotificationType type, Profile* profile) {
499 std::string name = GetWidgetName(widget);
500 GtkTextBuffer* buffer = gtk_text_view_get_buffer(GTK_TEXT_VIEW(widget));
501 GtkTextIter start, end;
502 gtk_text_buffer_get_bounds(buffer, &start, &end);
503 gchar* text = gtk_text_buffer_get_text(buffer, &start, &end, false);
504 std::string value = text;
505 g_free(text);
506 GtkTextIter sel_start, sel_end;
507 gtk_text_buffer_get_selection_bounds(buffer, &sel_start, &sel_end);
508 int start_pos = gtk_text_iter_get_offset(&sel_start);
509 int end_pos = gtk_text_iter_get_offset(&sel_end);
510 AccessibilityTextBoxInfo info(profile, name, IsPassword(widget));
511 info.SetValue(value, start_pos, end_pos);
512 SendAccessibilityNotification(type, &info);
513 }
514
515 void AccessibilityEventRouterGtk::SendTabNotification(
516 GtkWidget* widget, NotificationType type, Profile* profile) {
517 int index = gtk_notebook_get_current_page(GTK_NOTEBOOK(widget));
518 int page_count = gtk_notebook_get_n_pages(GTK_NOTEBOOK(widget));
519 GtkWidget* page = gtk_notebook_get_nth_page(GTK_NOTEBOOK(widget), index);
520 GtkWidget* label = gtk_notebook_get_tab_label(GTK_NOTEBOOK(widget), page);
521 std::string name = GetWidgetName(widget);
522 if (name.empty() && gtk_label_get_text(GTK_LABEL(label))) {
523 name = gtk_label_get_text(GTK_LABEL(label));
524 }
525 AccessibilityTabInfo info(profile, name, index, page_count);
526 SendAccessibilityNotification(type, &info);
527 }
528
529 void AccessibilityEventRouterGtk::SendComboBoxNotification(
530 GtkWidget* widget, NotificationType type, Profile* profile) {
531 // Get the index of the selected item. Will return -1 if no item is
532 // active, which matches the semantics of the extension API.
533 int index = gtk_combo_box_get_active(GTK_COMBO_BOX(widget));
534
535 // Get the number of items.
536 GtkTreeModel* model = gtk_combo_box_get_model(GTK_COMBO_BOX(widget));
537 int count = gtk_tree_model_iter_n_children(model, NULL);
538
539 // Get the value of the current item, if possible. Note that the
540 // model behind the combo box could be arbitrarily complex in theory,
541 // but this code just handles flat lists where the first string column
542 // contains the display value.
543 std::string value;
544 int string_column_index = -1;
545 for (int i = 0; i < gtk_tree_model_get_n_columns(model); i++) {
546 if (gtk_tree_model_get_column_type(model, i) == G_TYPE_STRING) {
547 string_column_index = i;
548 break;
549 }
550 }
551 if (string_column_index) {
552 GtkTreeIter iter;
553 if (gtk_combo_box_get_active_iter(GTK_COMBO_BOX(widget), &iter)) {
554 GValue gvalue = { 0 };
555 gtk_tree_model_get_value(model, &iter, string_column_index, &gvalue);
556 const char* string_value = g_value_get_string(&gvalue);
557 if (string_value) {
558 value = string_value;
559 }
560 g_value_unset(&gvalue);
561 }
562 } else {
563 // Otherwise this must be a gtk_combo_box_text, in which case this
564 // function will return the value of the current item, instead.
565 value = gtk_combo_box_get_active_text(GTK_COMBO_BOX(widget));
566 }
567
568 // Get the name of this combo box.
569 std::string name = GetWidgetName(widget);
570
571 // Send the notification.
572 AccessibilityComboBoxInfo info(profile, name, value, index, count);
573 SendAccessibilityNotification(type, &info);
574 }
575
576 void AccessibilityEventRouterGtk::SendListBoxNotification(
577 GtkWidget* widget, NotificationType type, Profile* profile) {
578 // Get the number of items.
579 GtkTreeModel* model = gtk_tree_view_get_model(GTK_TREE_VIEW(widget));
580 int count = gtk_tree_model_iter_n_children(model, NULL);
581
582 // Get the current selected index and its value.
583 int index = -1;
584 std::string value;
585 GtkTreePath* path;
586 gtk_tree_view_get_cursor(GTK_TREE_VIEW(widget), &path, NULL);
587 if (path != NULL) {
588 gint* indices = gtk_tree_path_get_indices(path);
589 if (indices)
590 index = indices[0];
591
592 GtkTreeIter iter;
593 if (gtk_tree_model_get_iter(model, &iter, path)) {
594 for (int i = 0; i < gtk_tree_model_get_n_columns(model); i++) {
595 if (gtk_tree_model_get_column_type(model, i) == G_TYPE_STRING) {
596 GValue gvalue = { 0 };
597 gtk_tree_model_get_value(model, &iter, i, &gvalue);
598 const char* string_value = g_value_get_string(&gvalue);
599 if (string_value) {
600 if (!value.empty())
601 value += " ";
602 value += string_value;
603 }
604 g_value_unset(&gvalue);
605 }
606 }
607 }
608
609 gtk_tree_path_free(path);
610 }
611
612 // Get the name of this control.
613 std::string name = GetWidgetName(widget);
614
615 // Send the notification.
616 AccessibilityListBoxInfo info(profile, name, value, index, count);
617 SendAccessibilityNotification(type, &info);
618 }
619
620 void AccessibilityEventRouterGtk::SendMenuItemNotification(
621 GtkWidget* menu, NotificationType type, Profile* profile) {
622 // Find the focused menu item, recursing into submenus as needed.
623 GtkWidget* menu_item = GTK_MENU_SHELL(menu)->active_menu_item;
624 if (!menu_item)
625 return;
626 GtkWidget* submenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(menu_item));
627 while (submenu && GTK_MENU_SHELL(submenu)->active_menu_item) {
628 menu = submenu;
629 menu_item = GTK_MENU_SHELL(menu)->active_menu_item;
630 if (!menu_item)
631 return;
632 submenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(menu_item));
633 }
634
635 // Figure out the item index and total number of items.
636 GList* items = gtk_container_get_children(GTK_CONTAINER(menu));
637 guint count = g_list_length(items);
638 int index = g_list_index(items, static_cast<gconstpointer>(menu_item));
639
640 // Get the menu item's label.
641 std::string name;
642 #if GTK_CHECK_VERSION(2, 16, 0)
643 name = gtk_menu_item_get_label(GTK_MENU_ITEM(menu_item));
644 #else
645 GList* children = gtk_container_get_children(GTK_CONTAINER(menu_item));
646 for (GList* l = g_list_first(children); l != NULL; l = g_list_next(l)) {
647 GtkWidget* child = static_cast<GtkWidget*>(l->data);
648 if (GTK_IS_LABEL(child)) {
649 name = gtk_label_get_label(GTK_LABEL(child));
650 break;
651 }
652 }
653 #endif
654
655 // Send the event.
656 AccessibilityMenuItemInfo info(profile, name, submenu != NULL, index, count);
657 SendAccessibilityNotification(type, &info);
658 }
OLDNEW
« no previous file with comments | « chrome/browser/gtk/accessibility_event_router_gtk.h ('k') | chrome/browser/gtk/accessibility_event_router_gtk_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698