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

Side by Side Diff: ui/views/controls/menu/native_menu_gtk.cc

Issue 9728002: Removing deprecated GTK-Views code. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Created 8 years, 9 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) 2011 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 "ui/views/controls/menu/native_menu_gtk.h"
6
7 #include <algorithm>
8 #include <map>
9 #include <string>
10
11 #include "base/bind.h"
12 #include "base/i18n/rtl.h"
13 #include "base/message_loop.h"
14 #include "base/time.h"
15 #include "base/utf_string_conversions.h"
16 #include "third_party/skia/include/core/SkBitmap.h"
17 #include "ui/base/accelerators/accelerator.h"
18 #include "ui/base/keycodes/keyboard_code_conversion_gtk.h"
19 #include "ui/base/keycodes/keyboard_codes.h"
20 #include "ui/base/models/menu_model.h"
21 #include "ui/gfx/font.h"
22 #include "ui/gfx/gtk_util.h"
23 #include "ui/views/controls/menu/menu_2.h"
24 #include "ui/views/controls/menu/menu_listener.h"
25 #include "ui/views/controls/menu/nested_dispatcher_gtk.h"
26 #include "ui/views/views_delegate.h"
27 #include "ui/views/widget/native_widget_gtk.h"
28
29 namespace {
30
31 const char kPositionString[] = "position";
32 const char kAccelGroupString[] = "accel_group";
33
34 // Key for the property set on the gtk menu that gives the handle to the hosting
35 // NativeMenuGtk.
36 const char kNativeMenuGtkString[] = "native_menu_gtk";
37
38 // Data passed to the MenuPositionFunc from gtk_menu_popup
39 struct Position {
40 // The point to run the menu at.
41 gfx::Point point;
42 // The alignment of the menu at that point.
43 views::Menu2::Alignment alignment;
44 };
45
46 // Returns true if the menu item type specified can be executed as a command.
47 bool MenuTypeCanExecute(ui::MenuModel::ItemType type) {
48 return type == ui::MenuModel::TYPE_COMMAND ||
49 type == ui::MenuModel::TYPE_CHECK ||
50 type == ui::MenuModel::TYPE_RADIO;
51 }
52
53 // A callback to gtk_container_foreach to remove all children.
54 // See |NativeMenuGtk::ResetMenu| for the usage.
55 void RemoveChildWidget(GtkWidget* widget, gpointer data) {
56 GtkWidget* parent = gtk_widget_get_parent(widget);
57 gtk_container_remove(GTK_CONTAINER(parent), widget);
58 }
59
60 } // namespace
61
62 namespace views {
63
64 ////////////////////////////////////////////////////////////////////////////////
65 // NativeMenuGtk, public:
66
67 NativeMenuGtk::NativeMenuGtk(Menu2* menu)
68 : parent_(NULL),
69 model_(menu->model()),
70 menu_(NULL),
71 menu_hidden_(true),
72 suppress_activate_signal_(false),
73 activated_menu_(NULL),
74 activated_index_(-1),
75 activate_factory_(this),
76 host_menu_(menu),
77 destroy_handler_id_(0),
78 expose_handler_id_(0),
79 menu_action_(MENU_ACTION_NONE),
80 nested_dispatcher_(NULL),
81 ignore_button_release_(true) {
82 }
83
84 NativeMenuGtk::~NativeMenuGtk() {
85 if (nested_dispatcher_) {
86 // Menu is destroyed while its in message loop.
87 // Let nested dispatcher know the creator is deleted.
88 nested_dispatcher_->CreatorDestroyed();
89 }
90 if (menu_) {
91 DCHECK(destroy_handler_id_);
92 // Don't call MenuDestroyed because menu2 has already been destroyed.
93 g_signal_handler_disconnect(menu_, destroy_handler_id_);
94 gtk_widget_destroy(menu_);
95 }
96 }
97
98 ////////////////////////////////////////////////////////////////////////////////
99 // NativeMenuGtk, MenuWrapper implementation:
100
101 void NativeMenuGtk::RunMenuAt(const gfx::Point& point, int alignment) {
102 activated_menu_ = NULL;
103 activated_index_ = -1;
104 menu_action_ = MENU_ACTION_NONE;
105 // ignore button release event unless mouse is pressed or moved.
106 ignore_button_release_ = true;
107
108 UpdateStates();
109 // Set the FREEZE UPDATE property to the menu's window so that WM maps
110 // the menu after the menu painted itself.
111 GtkWidget* popup_window = gtk_widget_get_ancestor(menu_, GTK_TYPE_WINDOW);
112 CHECK(popup_window);
113 NativeWidgetGtk::UpdateFreezeUpdatesProperty(GTK_WINDOW(popup_window),
114 true /* add */);
115 expose_handler_id_ = g_signal_connect_after(G_OBJECT(menu_), "expose_event",
116 G_CALLBACK(&OnExposeThunk), this);
117
118 Position position = { point, static_cast<Menu2::Alignment>(alignment) };
119 // TODO(beng): value of '1' will not work for context menus!
120 gtk_menu_popup(GTK_MENU(menu_), NULL, NULL, MenuPositionFunc, &position, 1,
121 gtk_get_current_event_time());
122 DCHECK(menu_hidden_);
123 menu_hidden_ = false;
124
125 FOR_EACH_OBSERVER(MenuListener, listeners_, OnMenuOpened());
126
127 // Listen for "hide" signal so that we know when to return from the blocking
128 // RunMenuAt call.
129 gint hide_handle_id =
130 g_signal_connect(menu_, "hide", G_CALLBACK(OnMenuHiddenThunk), this);
131
132 gint move_handle_id =
133 g_signal_connect(menu_, "move-current",
134 G_CALLBACK(OnMenuMoveCurrentThunk), this);
135 gint after_move_handle_id =
136 g_signal_connect_after(menu_, "move-current",
137 G_CALLBACK(AfterMenuMoveCurrentThunk), this);
138
139 model_->MenuWillShow();
140
141 // Block until menu is no longer shown by running a nested message loop.
142 nested_dispatcher_ = new NestedDispatcherGtk(this, true);
143 bool deleted = nested_dispatcher_->RunAndSelfDestruct();
144 if (deleted) {
145 // The menu was destryed while menu is shown, so return immediately.
146 // Don't touch the instance which is already deleted.
147 return;
148 }
149 nested_dispatcher_ = NULL;
150 if (!menu_hidden_) {
151 // If this happens it means we haven't yet gotten the hide signal and
152 // someone else quit the message loop on us.
153 NOTREACHED();
154 menu_hidden_ = true;
155 }
156
157 g_signal_handler_disconnect(G_OBJECT(menu_), hide_handle_id);
158 g_signal_handler_disconnect(G_OBJECT(menu_), move_handle_id);
159 g_signal_handler_disconnect(G_OBJECT(menu_), after_move_handle_id);
160
161 if (activated_menu_) {
162 MessageLoop::current()->PostTask(FROM_HERE,
163 base::Bind(&NativeMenuGtk::ProcessActivate,
164 activate_factory_.GetWeakPtr()));
165 }
166
167 model_->MenuClosed();
168 }
169
170 void NativeMenuGtk::CancelMenu() {
171 gtk_widget_hide(menu_);
172 }
173
174 void NativeMenuGtk::Rebuild() {
175 activated_menu_ = NULL;
176
177 ResetMenu();
178
179 // Try to retrieve accelerator group as data from menu_; if null, create new
180 // one and store it as data into menu_.
181 // We store it as data so as to use the destroy notifier to get rid of initial
182 // reference count. For some reason, when we unref it ourselves (even in
183 // destructor), it would cause random crashes, depending on when gtk tries to
184 // access it.
185 GtkAccelGroup* accel_group = static_cast<GtkAccelGroup*>(
186 g_object_get_data(G_OBJECT(menu_), kAccelGroupString));
187 if (!accel_group) {
188 accel_group = gtk_accel_group_new();
189 g_object_set_data_full(G_OBJECT(menu_), kAccelGroupString, accel_group,
190 g_object_unref);
191 }
192
193 std::map<int, GtkRadioMenuItem*> radio_groups_;
194 for (int i = 0; i < model_->GetItemCount(); ++i) {
195 ui::MenuModel::ItemType type = model_->GetTypeAt(i);
196 if (type == ui::MenuModel::TYPE_SEPARATOR) {
197 AddSeparatorAt(i);
198 } else if (type == ui::MenuModel::TYPE_RADIO) {
199 const int radio_group_id = model_->GetGroupIdAt(i);
200 std::map<int, GtkRadioMenuItem*>::const_iterator iter
201 = radio_groups_.find(radio_group_id);
202 if (iter == radio_groups_.end()) {
203 GtkWidget* new_menu_item = AddMenuItemAt(i, NULL, accel_group);
204 // |new_menu_item| is the first menu item for |radio_group_id| group.
205 radio_groups_.insert(
206 std::make_pair(radio_group_id, GTK_RADIO_MENU_ITEM(new_menu_item)));
207 } else {
208 AddMenuItemAt(i, iter->second, accel_group);
209 }
210 } else {
211 AddMenuItemAt(i, NULL, accel_group);
212 }
213 }
214 if (!menu_hidden_)
215 gtk_menu_reposition(GTK_MENU(menu_));
216 }
217
218 void NativeMenuGtk::UpdateStates() {
219 gtk_container_foreach(GTK_CONTAINER(menu_), &UpdateStateCallback, this);
220 }
221
222 gfx::NativeMenu NativeMenuGtk::GetNativeMenu() const {
223 return menu_;
224 }
225
226 NativeMenuGtk::MenuAction NativeMenuGtk::GetMenuAction() const {
227 return menu_action_;
228 }
229
230 void NativeMenuGtk::AddMenuListener(MenuListener* listener) {
231 listeners_.AddObserver(listener);
232 }
233
234 void NativeMenuGtk::RemoveMenuListener(MenuListener* listener) {
235 listeners_.RemoveObserver(listener);
236 }
237
238 void NativeMenuGtk::SetMinimumWidth(int width) {
239 gtk_widget_set_size_request(menu_, width, -1);
240 }
241
242 bool NativeMenuGtk::Dispatch(GdkEvent* event) {
243 if (menu_hidden_) {
244 // The menu has been closed but the message loop is still nested. Don't
245 // dispatch a message, otherwise we might spawn another message loop.
246 return false; // Exits the nested message loop.
247 }
248 switch (event->type) {
249 case GDK_BUTTON_PRESS:
250 case GDK_2BUTTON_PRESS:
251 case GDK_3BUTTON_PRESS: {
252 ignore_button_release_ = false;
253 gpointer data = NULL;
254 gdk_window_get_user_data(((GdkEventAny*)event)->window, &data);
255 GtkWidget* widget = reinterpret_cast<GtkWidget*>(data);
256 if (widget) {
257 GtkWidget* root = gtk_widget_get_toplevel(widget);
258 if (!g_object_get_data(G_OBJECT(root), kNativeMenuGtkString)) {
259 // The button event is not targeted at a menu, hide the menu and eat
260 // the event. If we didn't do this the button press is dispatched from
261 // the nested message loop and bad things can happen (like trying to
262 // spawn another menu.
263 gtk_menu_popdown(GTK_MENU(menu_));
264 // In some cases we may not have gotten the hide event, but the menu
265 // will be in the process of hiding so set menu_hidden_ anyway.
266 menu_hidden_ = true;
267 return false; // Exits the nested message loop.
268 }
269 }
270 break;
271 }
272 case GDK_MOTION_NOTIFY: {
273 ignore_button_release_ = false;
274 break;
275 }
276 case GDK_BUTTON_RELEASE: {
277 if (ignore_button_release_) {
278 // Ignore if a release event happened without press event.
279 // Normally, release event is eaten by gtk when menu is opened
280 // in response to mouse press event. Since the renderer opens
281 // the context menu asyncrhonous after press event is handled,
282 // gtk sometimes does not eat it, which causes the menu to be
283 // closed.
284 return true;
285 }
286 break;
287 }
288 default:
289 break;
290 }
291 gtk_main_do_event(event);
292 return true;
293 }
294
295 ////////////////////////////////////////////////////////////////////////////////
296 // NativeMenuGtk, private:
297
298 void NativeMenuGtk::OnMenuHidden(GtkWidget* widget) {
299 if (menu_hidden_) {
300 // The menu has been already hidden by us and we're in the process of
301 // quiting the message loop..
302 return;
303 }
304 // Quit the nested message loop we spawned in RunMenuAt.
305 MessageLoop::current()->Quit();
306
307 // Menu can be closed before the menu is shown.
308 if (expose_handler_id_) {
309 g_signal_handler_disconnect(menu_, expose_handler_id_);
310 expose_handler_id_ = 0;
311 }
312
313 menu_hidden_ = true;
314 }
315
316 void NativeMenuGtk::OnMenuMoveCurrent(GtkWidget* menu_widget,
317 GtkMenuDirectionType focus_direction) {
318 GtkWidget* parent = GTK_MENU_SHELL(menu_widget)->parent_menu_shell;
319 GtkWidget* menu_item = GTK_MENU_SHELL(menu_widget)->active_menu_item;
320 GtkWidget* submenu = NULL;
321 if (menu_item) {
322 submenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(menu_item));
323 }
324 if (focus_direction == GTK_MENU_DIR_CHILD && submenu == NULL) {
325 GetAncestor()->menu_action_ = MENU_ACTION_NEXT;
326 gtk_menu_popdown(GTK_MENU(menu_widget));
327 } else if (focus_direction == GTK_MENU_DIR_PARENT && parent == NULL) {
328 GetAncestor()->menu_action_ = MENU_ACTION_PREVIOUS;
329 gtk_menu_popdown(GTK_MENU(menu_widget));
330 }
331 }
332
333 void NativeMenuGtk::AfterMenuMoveCurrent(GtkWidget* menu_widget,
334 GtkMenuDirectionType focus_direction) {
335 SendAccessibilityEvent();
336 }
337
338 gboolean NativeMenuGtk::OnExpose(GtkWidget* widget, GdkEventExpose* event) {
339 GtkWidget* popup_window = gtk_widget_get_ancestor(menu_, GTK_TYPE_WINDOW);
340 CHECK(popup_window);
341 DCHECK(expose_handler_id_);
342 NativeWidgetGtk::UpdateFreezeUpdatesProperty(GTK_WINDOW(popup_window),
343 false /* remove */);
344 if (expose_handler_id_) {
345 g_signal_handler_disconnect(menu_, expose_handler_id_);
346 expose_handler_id_ = 0;
347 }
348 return false;
349 }
350
351 void NativeMenuGtk::AddSeparatorAt(int index) {
352 GtkWidget* separator = gtk_separator_menu_item_new();
353 gtk_widget_show(separator);
354 gtk_menu_append(menu_, separator);
355 }
356
357 GtkWidget* NativeMenuGtk::AddMenuItemAt(int index,
358 GtkRadioMenuItem* radio_group,
359 GtkAccelGroup* accel_group) {
360 GtkWidget* menu_item = NULL;
361 std::string label = gfx::ConvertAcceleratorsFromWindowsStyle(UTF16ToUTF8(
362 model_->GetLabelAt(index)));
363
364 ui::MenuModel::ItemType type = model_->GetTypeAt(index);
365 switch (type) {
366 case ui::MenuModel::TYPE_CHECK:
367 menu_item = gtk_check_menu_item_new_with_mnemonic(label.c_str());
368 break;
369 case ui::MenuModel::TYPE_RADIO:
370 if (radio_group) {
371 menu_item = gtk_radio_menu_item_new_with_mnemonic_from_widget(
372 radio_group, label.c_str());
373 } else {
374 // The item does not belong to any existing radio button groups.
375 menu_item = gtk_radio_menu_item_new_with_mnemonic(NULL, label.c_str());
376 }
377 break;
378 case ui::MenuModel::TYPE_SUBMENU:
379 case ui::MenuModel::TYPE_COMMAND: {
380 SkBitmap icon;
381 // Create menu item with icon if icon exists.
382 if (model_->HasIcons() && model_->GetIconAt(index, &icon)) {
383 menu_item = gtk_image_menu_item_new_with_mnemonic(label.c_str());
384 GdkPixbuf* pixbuf = gfx::GdkPixbufFromSkBitmap(&icon);
385 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item),
386 gtk_image_new_from_pixbuf(pixbuf));
387 g_object_unref(pixbuf);
388
389 // Show the image even if the "gtk-menu-images" setting is turned off.
390 gtk_image_menu_item_set_always_show_image(
391 GTK_IMAGE_MENU_ITEM(menu_item), TRUE);
392 } else {
393 menu_item = gtk_menu_item_new_with_mnemonic(label.c_str());
394 }
395 break;
396 }
397 default:
398 NOTREACHED();
399 break;
400 }
401
402 // Label font.
403 const gfx::Font* font = model_->GetLabelFontAt(index);
404 if (font) {
405 // The label item is the first child of the menu item.
406 GtkWidget* label_widget = GTK_BIN(menu_item)->child;
407 DCHECK(label_widget && GTK_IS_LABEL(label_widget));
408 PangoFontDescription* pfd = font->GetNativeFont();
409 gtk_widget_modify_font(label_widget, pfd);
410 pango_font_description_free(pfd);
411 }
412
413 if (type == ui::MenuModel::TYPE_SUBMENU) {
414 Menu2* submenu = new Menu2(model_->GetSubmenuModelAt(index));
415 static_cast<NativeMenuGtk*>(submenu->wrapper_.get())->set_parent(this);
416 g_object_set_data(G_OBJECT(menu_item), "submenu", submenu);
417 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item),
418 submenu->GetNativeMenu());
419 g_signal_connect(submenu->GetNativeMenu(), "move-current",
420 G_CALLBACK(OnMenuMoveCurrentThunk), this);
421 }
422
423 ui::Accelerator accelerator(ui::VKEY_UNKNOWN, false, false, false);
424 if (accel_group && model_->GetAcceleratorAt(index, &accelerator)) {
425 int gdk_modifiers = 0;
426 if (accelerator.IsShiftDown())
427 gdk_modifiers |= GDK_SHIFT_MASK;
428 if (accelerator.IsCtrlDown())
429 gdk_modifiers |= GDK_CONTROL_MASK;
430 if (accelerator.IsAltDown())
431 gdk_modifiers |= GDK_MOD1_MASK;
432 gtk_widget_add_accelerator(menu_item, "activate", accel_group,
433 ui::GdkKeyCodeForWindowsKeyCode(accelerator.key_code(), false),
434 static_cast<GdkModifierType>(gdk_modifiers), GTK_ACCEL_VISIBLE);
435 }
436
437 g_object_set_data(G_OBJECT(menu_item), kPositionString,
438 reinterpret_cast<void*>(index));
439 g_signal_connect(menu_item, "activate", G_CALLBACK(CallActivate), this);
440 UpdateMenuItemState(menu_item, false);
441 gtk_widget_show(menu_item);
442 gtk_menu_append(menu_, menu_item);
443
444 return menu_item;
445 }
446
447 void NativeMenuGtk::ResetMenu() {
448 if (!menu_) {
449 menu_ = gtk_menu_new();
450 g_object_set_data(
451 G_OBJECT(GTK_MENU(menu_)->toplevel), kNativeMenuGtkString, this);
452 destroy_handler_id_ = g_signal_connect(
453 menu_, "destroy", G_CALLBACK(NativeMenuGtk::MenuDestroyed), host_menu_);
454 } else {
455 gtk_container_foreach(GTK_CONTAINER(menu_), RemoveChildWidget, NULL);
456 }
457 }
458
459 void NativeMenuGtk::UpdateMenuItemState(GtkWidget* menu_item, bool recurse) {
460 int index = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(menu_item),
461 kPositionString));
462
463 gtk_widget_set_sensitive(menu_item, model_->IsEnabledAt(index));
464 if (GTK_IS_CHECK_MENU_ITEM(menu_item)) {
465 suppress_activate_signal_ = true;
466 gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(menu_item),
467 model_->IsItemCheckedAt(index));
468 suppress_activate_signal_ = false;
469 }
470
471 if (recurse && GTK_IS_MENU_ITEM(menu_item)) {
472 // Recurse into submenus.
473 if (gtk_menu_item_get_submenu(GTK_MENU_ITEM(menu_item))) {
474 Menu2* submenu =
475 reinterpret_cast<Menu2*>(g_object_get_data(G_OBJECT(menu_item),
476 "submenu"));
477 if (submenu)
478 submenu->UpdateStates();
479 }
480 }
481 }
482
483 // static
484 void NativeMenuGtk::UpdateStateCallback(GtkWidget* menu_item, gpointer data) {
485 NativeMenuGtk* menu = reinterpret_cast<NativeMenuGtk*>(data);
486 menu->UpdateMenuItemState(menu_item, true);
487 }
488
489 // static
490 void NativeMenuGtk::MenuPositionFunc(GtkMenu* menu,
491 int* x,
492 int* y,
493 gboolean* push_in,
494 void* data) {
495 Position* position = reinterpret_cast<Position*>(data);
496
497 GtkRequisition menu_req;
498 gtk_widget_size_request(GTK_WIDGET(menu), &menu_req);
499
500 *x = position->point.x();
501 *y = position->point.y();
502 views::Menu2::Alignment alignment = position->alignment;
503 if (base::i18n::IsRTL()) {
504 switch (alignment) {
505 case Menu2::ALIGN_TOPRIGHT:
506 alignment = Menu2::ALIGN_TOPLEFT;
507 break;
508 case Menu2::ALIGN_TOPLEFT:
509 alignment = Menu2::ALIGN_TOPRIGHT;
510 break;
511 default:
512 NOTREACHED();
513 break;
514 }
515 }
516 if (alignment == Menu2::ALIGN_TOPRIGHT)
517 *x -= menu_req.width;
518
519 // Make sure the popup fits on screen.
520 GdkScreen* screen = gtk_widget_get_screen(GTK_WIDGET(menu));
521 *x = std::max(0, std::min(gdk_screen_get_width(screen) - menu_req.width, *x));
522 *y = std::max(0, std::min(gdk_screen_get_height(screen) - menu_req.height,
523 *y));
524
525 *push_in = FALSE;
526 }
527
528 void NativeMenuGtk::OnActivate(GtkMenuItem* menu_item) {
529 if (suppress_activate_signal_)
530 return;
531 int position = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(menu_item),
532 kPositionString));
533 // Ignore the signal if it's sent to an inactive checked radio item.
534 //
535 // Suppose there are three radio items A, B, C, and A is now being
536 // checked. If you click C, "activate" signal will be sent to A and C.
537 // Here, we ignore the signal sent to A.
538 if (GTK_IS_RADIO_MENU_ITEM(menu_item) &&
539 !gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menu_item))) {
540 return;
541 }
542
543 // NOTE: we get activate messages for submenus when first shown.
544 if (model_->IsEnabledAt(position) &&
545 MenuTypeCanExecute(model_->GetTypeAt(position))) {
546 NativeMenuGtk* ancestor = GetAncestor();
547 ancestor->activated_menu_ = this;
548 activated_index_ = position;
549 ancestor->menu_action_ = MENU_ACTION_SELECTED;
550 }
551 }
552
553 // static
554 void NativeMenuGtk::CallActivate(GtkMenuItem* menu_item,
555 NativeMenuGtk* native_menu) {
556 native_menu->OnActivate(menu_item);
557 }
558
559 NativeMenuGtk* NativeMenuGtk::GetAncestor() {
560 NativeMenuGtk* ancestor = this;
561 while (ancestor->parent_)
562 ancestor = ancestor->parent_;
563 return ancestor;
564 }
565
566 void NativeMenuGtk::ProcessActivate() {
567 if (activated_menu_)
568 activated_menu_->Activate();
569 }
570
571 void NativeMenuGtk::Activate() {
572 if (model_->IsEnabledAt(activated_index_) &&
573 MenuTypeCanExecute(model_->GetTypeAt(activated_index_))) {
574 model_->ActivatedAt(activated_index_);
575 }
576 }
577
578 void NativeMenuGtk::SendAccessibilityEvent() {
579 // Find the focused menu item, recursing into submenus as needed.
580 GtkWidget* menu = menu_;
581 GtkWidget* menu_item = GTK_MENU_SHELL(menu_)->active_menu_item;
582 if (!menu_item)
583 return;
584 GtkWidget* submenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(menu_item));
585 while (submenu && GTK_MENU_SHELL(submenu)->active_menu_item) {
586 menu = submenu;
587 menu_item = GTK_MENU_SHELL(menu)->active_menu_item;
588 if (!menu_item)
589 return;
590 submenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(menu_item));
591 }
592
593 // Figure out the item index and total number of items.
594 GList* items = gtk_container_get_children(GTK_CONTAINER(menu));
595 guint count = g_list_length(items);
596 int index = g_list_index(items, static_cast<gconstpointer>(menu_item));
597
598 // Get the menu item's label.
599 std::string name;
600 name = gtk_menu_item_get_label(GTK_MENU_ITEM(menu_item));
601
602 if (ViewsDelegate::views_delegate) {
603 ViewsDelegate::views_delegate->NotifyMenuItemFocused(string16(),
604 UTF8ToUTF16(name),
605 index,
606 count,
607 submenu != NULL);
608 }
609 }
610
611 // static
612 void NativeMenuGtk::MenuDestroyed(GtkWidget* widget, Menu2* menu2) {
613 NativeMenuGtk* native_menu =
614 static_cast<NativeMenuGtk*>(menu2->wrapper_.get());
615 // The native gtk widget has already been destroyed.
616 native_menu->menu_ = NULL;
617 delete menu2;
618 }
619
620 ////////////////////////////////////////////////////////////////////////////////
621 // MenuWrapper, public:
622
623 // static
624 MenuWrapper* MenuWrapper::CreateWrapper(Menu2* menu) {
625 return new NativeMenuGtk(menu);
626 }
627
628 } // namespace views
OLDNEW
« no previous file with comments | « ui/views/controls/menu/native_menu_gtk.h ('k') | ui/views/controls/menu/nested_dispatcher_gtk.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698