| OLD | NEW | 
|---|
|  | (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/menu_gtk.h" |  | 
| 6 |  | 
| 7 #include <map> |  | 
| 8 |  | 
| 9 #include "app/menus/accelerator_gtk.h" |  | 
| 10 #include "app/menus/button_menu_item_model.h" |  | 
| 11 #include "app/menus/menu_model.h" |  | 
| 12 #include "base/i18n/rtl.h" |  | 
| 13 #include "base/logging.h" |  | 
| 14 #include "base/message_loop.h" |  | 
| 15 #include "base/stl_util-inl.h" |  | 
| 16 #include "base/utf_string_conversions.h" |  | 
| 17 #include "chrome/app/chrome_command_ids.h" |  | 
| 18 #include "chrome/browser/gtk/gtk_custom_menu.h" |  | 
| 19 #include "chrome/browser/gtk/gtk_custom_menu_item.h" |  | 
| 20 #include "chrome/browser/gtk/gtk_util.h" |  | 
| 21 #include "gfx/gtk_util.h" |  | 
| 22 #include "third_party/skia/include/core/SkBitmap.h" |  | 
| 23 #include "webkit/glue/window_open_disposition.h" |  | 
| 24 |  | 
| 25 bool MenuGtk::block_activation_ = false; |  | 
| 26 |  | 
| 27 namespace { |  | 
| 28 |  | 
| 29 // Sets the ID of a menu item. |  | 
| 30 void SetMenuItemID(GtkWidget* menu_item, int menu_id) { |  | 
| 31   DCHECK_GE(menu_id, 0); |  | 
| 32 |  | 
| 33   // Add 1 to the menu_id to avoid setting zero (null) to "menu-id". |  | 
| 34   g_object_set_data(G_OBJECT(menu_item), "menu-id", |  | 
| 35                     GINT_TO_POINTER(menu_id + 1)); |  | 
| 36 } |  | 
| 37 |  | 
| 38 // Gets the ID of a menu item. |  | 
| 39 // Returns true if the menu item has an ID. |  | 
| 40 bool GetMenuItemID(GtkWidget* menu_item, int* menu_id) { |  | 
| 41   gpointer id_ptr = g_object_get_data(G_OBJECT(menu_item), "menu-id"); |  | 
| 42   if (id_ptr != NULL) { |  | 
| 43     *menu_id = GPOINTER_TO_INT(id_ptr) - 1; |  | 
| 44     return true; |  | 
| 45   } |  | 
| 46 |  | 
| 47   return false; |  | 
| 48 } |  | 
| 49 |  | 
| 50 menus::MenuModel* ModelForMenuItem(GtkMenuItem* menu_item) { |  | 
| 51   return reinterpret_cast<menus::MenuModel*>( |  | 
| 52       g_object_get_data(G_OBJECT(menu_item), "model")); |  | 
| 53 } |  | 
| 54 |  | 
| 55 void SetupButtonShowHandler(GtkWidget* button, |  | 
| 56                             menus::ButtonMenuItemModel* model, |  | 
| 57                             int index) { |  | 
| 58   g_object_set_data(G_OBJECT(button), "button-model", |  | 
| 59                     model); |  | 
| 60   g_object_set_data(G_OBJECT(button), "button-model-id", |  | 
| 61                     GINT_TO_POINTER(index)); |  | 
| 62 } |  | 
| 63 |  | 
| 64 void OnSubmenuShowButtonImage(GtkWidget* widget, GtkButton* button) { |  | 
| 65   MenuGtk::Delegate* delegate = reinterpret_cast<MenuGtk::Delegate*>( |  | 
| 66       g_object_get_data(G_OBJECT(button), "menu-gtk-delegate")); |  | 
| 67   int icon_idr = GPOINTER_TO_INT(g_object_get_data( |  | 
| 68       G_OBJECT(button), "button-image-idr")); |  | 
| 69 |  | 
| 70   GtkIconSet* icon_set = delegate->GetIconSetForId(icon_idr); |  | 
| 71   if (icon_set) { |  | 
| 72     gtk_button_set_image( |  | 
| 73         button, gtk_image_new_from_icon_set(icon_set, |  | 
| 74                                             GTK_ICON_SIZE_MENU)); |  | 
| 75   } |  | 
| 76 } |  | 
| 77 |  | 
| 78 void SetupImageIcon(GtkWidget* button, |  | 
| 79                     GtkWidget* menu, |  | 
| 80                     int icon_idr, |  | 
| 81                     MenuGtk::Delegate* menu_gtk_delegate) { |  | 
| 82   g_object_set_data(G_OBJECT(button), "button-image-idr", |  | 
| 83                     GINT_TO_POINTER(icon_idr)); |  | 
| 84   g_object_set_data(G_OBJECT(button), "menu-gtk-delegate", |  | 
| 85                     menu_gtk_delegate); |  | 
| 86 |  | 
| 87   g_signal_connect(menu, "show", G_CALLBACK(OnSubmenuShowButtonImage), button); |  | 
| 88 } |  | 
| 89 |  | 
| 90 // Popup menus may get squished if they open up too close to the bottom of the |  | 
| 91 // screen. This function takes the size of the screen, the size of the menu, |  | 
| 92 // an optional widget, the Y position of the mouse click, and adjusts the popup |  | 
| 93 // menu's Y position to make it fit if it's possible to do so. |  | 
| 94 // Returns the new Y position of the popup menu. |  | 
| 95 int CalculateMenuYPosition(const GdkRectangle* screen_rect, |  | 
| 96                            const GtkRequisition* menu_req, |  | 
| 97                            const GtkWidget* widget, const int y) { |  | 
| 98   CHECK(screen_rect); |  | 
| 99   CHECK(menu_req); |  | 
| 100   // If the menu would run off the bottom of the screen, and there is enough |  | 
| 101   // screen space upwards to accommodate the menu, then pop upwards. If there |  | 
| 102   // is a widget, then also move the anchor point to the top of the widget |  | 
| 103   // rather than the bottom. |  | 
| 104   const int screen_top = screen_rect->y; |  | 
| 105   const int screen_bottom = screen_rect->y + screen_rect->height; |  | 
| 106   const int menu_bottom = y + menu_req->height; |  | 
| 107   int alternate_y = y - menu_req->height; |  | 
| 108   if (widget) |  | 
| 109     alternate_y -= widget->allocation.height; |  | 
| 110   if (menu_bottom >= screen_bottom && alternate_y >= screen_top) |  | 
| 111     return alternate_y; |  | 
| 112   return y; |  | 
| 113 } |  | 
| 114 |  | 
| 115 }  // namespace |  | 
| 116 |  | 
| 117 GtkWidget* MenuGtk::Delegate::GetDefaultImageForCommandId(int command_id) { |  | 
| 118   const char* stock; |  | 
| 119   switch (command_id) { |  | 
| 120     case IDC_NEW_TAB: |  | 
| 121     case IDC_CONTENT_CONTEXT_OPENIMAGENEWTAB: |  | 
| 122     case IDC_CONTENT_CONTEXT_OPENLINKNEWTAB: |  | 
| 123     case IDC_CONTENT_CONTEXT_OPENAVNEWTAB: |  | 
| 124       stock = GTK_STOCK_NEW; |  | 
| 125       break; |  | 
| 126 |  | 
| 127     case IDC_CLOSE_TAB: |  | 
| 128       stock = GTK_STOCK_CLOSE; |  | 
| 129       break; |  | 
| 130 |  | 
| 131     case IDC_CONTENT_CONTEXT_SAVEIMAGEAS: |  | 
| 132     case IDC_CONTENT_CONTEXT_SAVEAVAS: |  | 
| 133     case IDC_CONTENT_CONTEXT_SAVELINKAS: |  | 
| 134       stock = GTK_STOCK_SAVE_AS; |  | 
| 135       break; |  | 
| 136 |  | 
| 137     case IDC_SAVE_PAGE: |  | 
| 138       stock = GTK_STOCK_SAVE; |  | 
| 139       break; |  | 
| 140 |  | 
| 141     case IDC_COPY: |  | 
| 142     case IDC_COPY_URL: |  | 
| 143     case IDC_CONTENT_CONTEXT_COPYIMAGELOCATION: |  | 
| 144     case IDC_CONTENT_CONTEXT_COPYLINKLOCATION: |  | 
| 145     case IDC_CONTENT_CONTEXT_COPYAVLOCATION: |  | 
| 146     case IDC_CONTENT_CONTEXT_COPYEMAILADDRESS: |  | 
| 147     case IDC_CONTENT_CONTEXT_COPY: |  | 
| 148       stock = GTK_STOCK_COPY; |  | 
| 149       break; |  | 
| 150 |  | 
| 151     case IDC_CUT: |  | 
| 152     case IDC_CONTENT_CONTEXT_CUT: |  | 
| 153       stock = GTK_STOCK_CUT; |  | 
| 154       break; |  | 
| 155 |  | 
| 156     case IDC_PASTE: |  | 
| 157     case IDC_CONTENT_CONTEXT_PASTE: |  | 
| 158       stock = GTK_STOCK_PASTE; |  | 
| 159       break; |  | 
| 160 |  | 
| 161     case IDC_CONTENT_CONTEXT_DELETE: |  | 
| 162     case IDC_BOOKMARK_BAR_REMOVE: |  | 
| 163       stock = GTK_STOCK_DELETE; |  | 
| 164       break; |  | 
| 165 |  | 
| 166     case IDC_CONTENT_CONTEXT_UNDO: |  | 
| 167       stock = GTK_STOCK_UNDO; |  | 
| 168       break; |  | 
| 169 |  | 
| 170     case IDC_CONTENT_CONTEXT_REDO: |  | 
| 171       stock = GTK_STOCK_REDO; |  | 
| 172       break; |  | 
| 173 |  | 
| 174     case IDC_SEARCH: |  | 
| 175     case IDC_FIND: |  | 
| 176     case IDC_CONTENT_CONTEXT_SEARCHWEBFOR: |  | 
| 177       stock = GTK_STOCK_FIND; |  | 
| 178       break; |  | 
| 179 |  | 
| 180     case IDC_CONTENT_CONTEXT_SELECTALL: |  | 
| 181       stock = GTK_STOCK_SELECT_ALL; |  | 
| 182       break; |  | 
| 183 |  | 
| 184     case IDC_CLEAR_BROWSING_DATA: |  | 
| 185       stock = GTK_STOCK_CLEAR; |  | 
| 186       break; |  | 
| 187 |  | 
| 188     case IDC_BACK: |  | 
| 189       stock = GTK_STOCK_GO_BACK; |  | 
| 190       break; |  | 
| 191 |  | 
| 192     case IDC_RELOAD: |  | 
| 193       stock = GTK_STOCK_REFRESH; |  | 
| 194       break; |  | 
| 195 |  | 
| 196     case IDC_FORWARD: |  | 
| 197       stock = GTK_STOCK_GO_FORWARD; |  | 
| 198       break; |  | 
| 199 |  | 
| 200     case IDC_PRINT: |  | 
| 201       stock = GTK_STOCK_PRINT; |  | 
| 202       break; |  | 
| 203 |  | 
| 204     case IDC_CONTENT_CONTEXT_VIEWPAGEINFO: |  | 
| 205       stock = GTK_STOCK_INFO; |  | 
| 206       break; |  | 
| 207 |  | 
| 208     case IDC_SPELLCHECK_MENU: |  | 
| 209       stock = GTK_STOCK_SPELL_CHECK; |  | 
| 210       break; |  | 
| 211 |  | 
| 212     case IDC_RESTORE_TAB: |  | 
| 213       stock = GTK_STOCK_UNDELETE; |  | 
| 214       break; |  | 
| 215 |  | 
| 216     case IDC_HOME: |  | 
| 217       stock = GTK_STOCK_HOME; |  | 
| 218       break; |  | 
| 219 |  | 
| 220     case IDC_STOP: |  | 
| 221       stock = GTK_STOCK_STOP; |  | 
| 222       break; |  | 
| 223 |  | 
| 224     case IDC_ABOUT: |  | 
| 225       stock = GTK_STOCK_ABOUT; |  | 
| 226       break; |  | 
| 227 |  | 
| 228     case IDC_EXIT: |  | 
| 229       stock = GTK_STOCK_QUIT; |  | 
| 230       break; |  | 
| 231 |  | 
| 232     case IDC_HELP_PAGE: |  | 
| 233       stock = GTK_STOCK_HELP; |  | 
| 234       break; |  | 
| 235 |  | 
| 236     case IDC_OPTIONS: |  | 
| 237       stock = GTK_STOCK_PREFERENCES; |  | 
| 238       break; |  | 
| 239 |  | 
| 240     case IDC_CONTENT_CONTEXT_GOTOURL: |  | 
| 241       stock = GTK_STOCK_JUMP_TO; |  | 
| 242       break; |  | 
| 243 |  | 
| 244     case IDC_DEV_TOOLS_INSPECT: |  | 
| 245     case IDC_CONTENT_CONTEXT_INSPECTELEMENT: |  | 
| 246       stock = GTK_STOCK_PROPERTIES; |  | 
| 247       break; |  | 
| 248 |  | 
| 249     case IDC_BOOKMARK_BAR_ADD_NEW_BOOKMARK: |  | 
| 250       stock = GTK_STOCK_ADD; |  | 
| 251       break; |  | 
| 252 |  | 
| 253     case IDC_BOOKMARK_BAR_RENAME_FOLDER: |  | 
| 254     case IDC_BOOKMARK_BAR_EDIT: |  | 
| 255       stock = GTK_STOCK_EDIT; |  | 
| 256       break; |  | 
| 257 |  | 
| 258     case IDC_BOOKMARK_BAR_NEW_FOLDER: |  | 
| 259       stock = GTK_STOCK_DIRECTORY; |  | 
| 260       break; |  | 
| 261 |  | 
| 262     case IDC_BOOKMARK_BAR_OPEN_ALL: |  | 
| 263       stock = GTK_STOCK_OPEN; |  | 
| 264       break; |  | 
| 265 |  | 
| 266     default: |  | 
| 267       stock = NULL; |  | 
| 268   } |  | 
| 269 |  | 
| 270   return stock ? gtk_image_new_from_stock(stock, GTK_ICON_SIZE_MENU) : NULL; |  | 
| 271 } |  | 
| 272 |  | 
| 273 GtkWidget* MenuGtk::Delegate::GetImageForCommandId(int command_id) const { |  | 
| 274   return GetDefaultImageForCommandId(command_id); |  | 
| 275 } |  | 
| 276 |  | 
| 277 MenuGtk::MenuGtk(MenuGtk::Delegate* delegate, |  | 
| 278                  menus::MenuModel* model) |  | 
| 279     : delegate_(delegate), |  | 
| 280       model_(model), |  | 
| 281       dummy_accel_group_(gtk_accel_group_new()), |  | 
| 282       menu_(gtk_custom_menu_new()), |  | 
| 283       factory_(this) { |  | 
| 284   DCHECK(model); |  | 
| 285   g_object_ref_sink(menu_); |  | 
| 286   ConnectSignalHandlers(); |  | 
| 287   BuildMenuFromModel(); |  | 
| 288 } |  | 
| 289 |  | 
| 290 MenuGtk::~MenuGtk() { |  | 
| 291   Cancel(); |  | 
| 292 |  | 
| 293   gtk_widget_destroy(menu_); |  | 
| 294   g_object_unref(menu_); |  | 
| 295 |  | 
| 296   STLDeleteContainerPointers(submenus_we_own_.begin(), submenus_we_own_.end()); |  | 
| 297   g_object_unref(dummy_accel_group_); |  | 
| 298 } |  | 
| 299 |  | 
| 300 void MenuGtk::ConnectSignalHandlers() { |  | 
| 301   // We connect afterwards because OnMenuShow calls SetMenuItemInfo, which may |  | 
| 302   // take a long time or even start a nested message loop. |  | 
| 303   g_signal_connect(menu_, "show", G_CALLBACK(OnMenuShowThunk), this); |  | 
| 304   g_signal_connect(menu_, "hide", G_CALLBACK(OnMenuHiddenThunk), this); |  | 
| 305 } |  | 
| 306 |  | 
| 307 GtkWidget* MenuGtk::AppendMenuItemWithLabel(int command_id, |  | 
| 308                                             const std::string& label) { |  | 
| 309   std::string converted_label = gfx::ConvertAcceleratorsFromWindowsStyle(label); |  | 
| 310   GtkWidget* menu_item = BuildMenuItemWithLabel(label, command_id); |  | 
| 311   return AppendMenuItem(command_id, menu_item); |  | 
| 312 } |  | 
| 313 |  | 
| 314 GtkWidget* MenuGtk::AppendMenuItemWithIcon(int command_id, |  | 
| 315                                            const std::string& label, |  | 
| 316                                            const SkBitmap& icon) { |  | 
| 317   std::string converted_label = gfx::ConvertAcceleratorsFromWindowsStyle(label); |  | 
| 318   GtkWidget* menu_item = BuildMenuItemWithImage(converted_label, icon); |  | 
| 319   return AppendMenuItem(command_id, menu_item); |  | 
| 320 } |  | 
| 321 |  | 
| 322 GtkWidget* MenuGtk::AppendCheckMenuItemWithLabel(int command_id, |  | 
| 323                                                  const std::string& label) { |  | 
| 324   std::string converted_label = gfx::ConvertAcceleratorsFromWindowsStyle(label); |  | 
| 325   GtkWidget* menu_item = |  | 
| 326       gtk_check_menu_item_new_with_mnemonic(converted_label.c_str()); |  | 
| 327   return AppendMenuItem(command_id, menu_item); |  | 
| 328 } |  | 
| 329 |  | 
| 330 GtkWidget* MenuGtk::AppendSeparator() { |  | 
| 331   GtkWidget* menu_item = gtk_separator_menu_item_new(); |  | 
| 332   gtk_widget_show(menu_item); |  | 
| 333   gtk_menu_shell_append(GTK_MENU_SHELL(menu_), menu_item); |  | 
| 334   return menu_item; |  | 
| 335 } |  | 
| 336 |  | 
| 337 GtkWidget* MenuGtk::AppendMenuItem(int command_id, GtkWidget* menu_item) { |  | 
| 338   if (delegate_ && delegate_->AlwaysShowIconForCmd(command_id) && |  | 
| 339       GTK_IS_IMAGE_MENU_ITEM(menu_item)) |  | 
| 340     gtk_util::SetAlwaysShowImage(menu_item); |  | 
| 341 |  | 
| 342   return AppendMenuItemToMenu(command_id, NULL, menu_item, menu_, true); |  | 
| 343 } |  | 
| 344 |  | 
| 345 GtkWidget* MenuGtk::AppendMenuItemToMenu(int index, |  | 
| 346                                          menus::MenuModel* model, |  | 
| 347                                          GtkWidget* menu_item, |  | 
| 348                                          GtkWidget* menu, |  | 
| 349                                          bool connect_to_activate) { |  | 
| 350   SetMenuItemID(menu_item, index); |  | 
| 351 |  | 
| 352   // Native menu items do their own thing, so only selectively listen for the |  | 
| 353   // activate signal. |  | 
| 354   if (connect_to_activate) { |  | 
| 355     g_signal_connect(menu_item, "activate", |  | 
| 356                      G_CALLBACK(OnMenuItemActivatedThunk), this); |  | 
| 357   } |  | 
| 358 |  | 
| 359   // AppendMenuItemToMenu is used both internally when we control menu creation |  | 
| 360   // from a model (where the model can choose to hide certain menu items), and |  | 
| 361   // with immediate commands which don't provide the option. |  | 
| 362   if (model) { |  | 
| 363     if (model->IsVisibleAt(index)) |  | 
| 364       gtk_widget_show(menu_item); |  | 
| 365   } else { |  | 
| 366     gtk_widget_show(menu_item); |  | 
| 367   } |  | 
| 368   gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item); |  | 
| 369   return menu_item; |  | 
| 370 } |  | 
| 371 |  | 
| 372 void MenuGtk::Popup(GtkWidget* widget, GdkEvent* event) { |  | 
| 373   DCHECK(event->type == GDK_BUTTON_PRESS) |  | 
| 374       << "Non-button press event sent to RunMenuAt"; |  | 
| 375 |  | 
| 376   Popup(widget, event->button.button, event->button.time); |  | 
| 377 } |  | 
| 378 |  | 
| 379 void MenuGtk::Popup(GtkWidget* widget, gint button_type, guint32 timestamp) { |  | 
| 380   gtk_menu_popup(GTK_MENU(menu_), NULL, NULL, |  | 
| 381                  WidgetMenuPositionFunc, |  | 
| 382                  widget, |  | 
| 383                  button_type, timestamp); |  | 
| 384 } |  | 
| 385 |  | 
| 386 void MenuGtk::PopupAsContext(guint32 event_time) { |  | 
| 387   // TODO(estade): |button| value of 3 (6th argument) is not strictly true, |  | 
| 388   // but does it matter? |  | 
| 389   gtk_menu_popup(GTK_MENU(menu_), NULL, NULL, NULL, NULL, 3, event_time); |  | 
| 390 } |  | 
| 391 |  | 
| 392 void MenuGtk::PopupAsContextAt(guint32 event_time, gfx::Point point) { |  | 
| 393   gtk_menu_popup(GTK_MENU(menu_), NULL, NULL, |  | 
| 394                  PointMenuPositionFunc, &point, 3, event_time); |  | 
| 395 } |  | 
| 396 |  | 
| 397 void MenuGtk::PopupAsContextForStatusIcon(guint32 event_time, guint32 button, |  | 
| 398                                           GtkStatusIcon* icon) { |  | 
| 399   gtk_menu_popup(GTK_MENU(menu_), NULL, NULL, gtk_status_icon_position_menu, |  | 
| 400                  icon, button, event_time); |  | 
| 401 } |  | 
| 402 |  | 
| 403 void MenuGtk::PopupAsFromKeyEvent(GtkWidget* widget) { |  | 
| 404   Popup(widget, 0, gtk_get_current_event_time()); |  | 
| 405   gtk_menu_shell_select_first(GTK_MENU_SHELL(menu_), FALSE); |  | 
| 406 } |  | 
| 407 |  | 
| 408 void MenuGtk::Cancel() { |  | 
| 409   gtk_menu_popdown(GTK_MENU(menu_)); |  | 
| 410 } |  | 
| 411 |  | 
| 412 void MenuGtk::UpdateMenu() { |  | 
| 413   gtk_container_foreach(GTK_CONTAINER(menu_), SetMenuItemInfo, this); |  | 
| 414 } |  | 
| 415 |  | 
| 416 GtkWidget* MenuGtk::BuildMenuItemWithImage(const std::string& label, |  | 
| 417                                   GtkWidget* image) { |  | 
| 418   GtkWidget* menu_item = |  | 
| 419       gtk_image_menu_item_new_with_mnemonic(label.c_str()); |  | 
| 420   gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item), image); |  | 
| 421   return menu_item; |  | 
| 422 } |  | 
| 423 |  | 
| 424 GtkWidget* MenuGtk::BuildMenuItemWithImage(const std::string& label, |  | 
| 425                                            const SkBitmap& icon) { |  | 
| 426   GdkPixbuf* pixbuf = gfx::GdkPixbufFromSkBitmap(&icon); |  | 
| 427   GtkWidget* menu_item = BuildMenuItemWithImage(label, |  | 
| 428       gtk_image_new_from_pixbuf(pixbuf)); |  | 
| 429   g_object_unref(pixbuf); |  | 
| 430   return menu_item; |  | 
| 431 } |  | 
| 432 |  | 
| 433 GtkWidget* MenuGtk::BuildMenuItemWithLabel(const std::string& label, |  | 
| 434                                            int command_id) { |  | 
| 435   GtkWidget* img = |  | 
| 436       delegate_ ? delegate_->GetImageForCommandId(command_id) : |  | 
| 437                   MenuGtk::Delegate::GetDefaultImageForCommandId(command_id); |  | 
| 438   return img ? BuildMenuItemWithImage(label, img) : |  | 
| 439                gtk_menu_item_new_with_mnemonic(label.c_str()); |  | 
| 440 } |  | 
| 441 |  | 
| 442 void MenuGtk::BuildMenuFromModel() { |  | 
| 443   BuildSubmenuFromModel(model_, menu_); |  | 
| 444 } |  | 
| 445 |  | 
| 446 void MenuGtk::BuildSubmenuFromModel(menus::MenuModel* model, GtkWidget* menu) { |  | 
| 447   std::map<int, GtkWidget*> radio_groups; |  | 
| 448   GtkWidget* menu_item = NULL; |  | 
| 449   for (int i = 0; i < model->GetItemCount(); ++i) { |  | 
| 450     SkBitmap icon; |  | 
| 451     std::string label = |  | 
| 452         gfx::ConvertAcceleratorsFromWindowsStyle( |  | 
| 453             UTF16ToUTF8(model->GetLabelAt(i))); |  | 
| 454     bool connect_to_activate = true; |  | 
| 455 |  | 
| 456     switch (model->GetTypeAt(i)) { |  | 
| 457       case menus::MenuModel::TYPE_SEPARATOR: |  | 
| 458         menu_item = gtk_separator_menu_item_new(); |  | 
| 459         break; |  | 
| 460 |  | 
| 461       case menus::MenuModel::TYPE_CHECK: |  | 
| 462         menu_item = gtk_check_menu_item_new_with_mnemonic(label.c_str()); |  | 
| 463         break; |  | 
| 464 |  | 
| 465       case menus::MenuModel::TYPE_RADIO: { |  | 
| 466         std::map<int, GtkWidget*>::iterator iter = |  | 
| 467             radio_groups.find(model->GetGroupIdAt(i)); |  | 
| 468 |  | 
| 469         if (iter == radio_groups.end()) { |  | 
| 470           menu_item = gtk_radio_menu_item_new_with_mnemonic( |  | 
| 471               NULL, label.c_str()); |  | 
| 472           radio_groups[model->GetGroupIdAt(i)] = menu_item; |  | 
| 473         } else { |  | 
| 474           menu_item = gtk_radio_menu_item_new_with_mnemonic_from_widget( |  | 
| 475               GTK_RADIO_MENU_ITEM(iter->second), label.c_str()); |  | 
| 476         } |  | 
| 477         break; |  | 
| 478       } |  | 
| 479       case menus::MenuModel::TYPE_BUTTON_ITEM: { |  | 
| 480         menus::ButtonMenuItemModel* button_menu_item_model = |  | 
| 481             model->GetButtonMenuItemAt(i); |  | 
| 482         menu_item = BuildButtomMenuItem(button_menu_item_model, menu); |  | 
| 483         connect_to_activate = false; |  | 
| 484         break; |  | 
| 485       } |  | 
| 486       case menus::MenuModel::TYPE_SUBMENU: |  | 
| 487       case menus::MenuModel::TYPE_COMMAND: { |  | 
| 488         int command_id = model->GetCommandIdAt(i); |  | 
| 489         if (model->GetIconAt(i, &icon)) |  | 
| 490           menu_item = BuildMenuItemWithImage(label, icon); |  | 
| 491         else |  | 
| 492           menu_item = BuildMenuItemWithLabel(label, command_id); |  | 
| 493         if (delegate_ && delegate_->AlwaysShowIconForCmd(command_id) && |  | 
| 494             GTK_IS_IMAGE_MENU_ITEM(menu_item)) |  | 
| 495           gtk_util::SetAlwaysShowImage(menu_item); |  | 
| 496         break; |  | 
| 497       } |  | 
| 498 |  | 
| 499       default: |  | 
| 500         NOTREACHED(); |  | 
| 501     } |  | 
| 502 |  | 
| 503     if (model->GetTypeAt(i) == menus::MenuModel::TYPE_SUBMENU) { |  | 
| 504       GtkWidget* submenu = gtk_menu_new(); |  | 
| 505       BuildSubmenuFromModel(model->GetSubmenuModelAt(i), submenu); |  | 
| 506       gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), submenu); |  | 
| 507     } |  | 
| 508 |  | 
| 509     menus::AcceleratorGtk accelerator; |  | 
| 510     if (model->GetAcceleratorAt(i, &accelerator)) { |  | 
| 511       gtk_widget_add_accelerator(menu_item, |  | 
| 512                                  "activate", |  | 
| 513                                  dummy_accel_group_, |  | 
| 514                                  accelerator.GetGdkKeyCode(), |  | 
| 515                                  accelerator.gdk_modifier_type(), |  | 
| 516                                  GTK_ACCEL_VISIBLE); |  | 
| 517     } |  | 
| 518 |  | 
| 519     g_object_set_data(G_OBJECT(menu_item), "model", model); |  | 
| 520     AppendMenuItemToMenu(i, model, menu_item, menu, connect_to_activate); |  | 
| 521 |  | 
| 522     menu_item = NULL; |  | 
| 523   } |  | 
| 524 } |  | 
| 525 |  | 
| 526 GtkWidget* MenuGtk::BuildButtomMenuItem(menus::ButtonMenuItemModel* model, |  | 
| 527                                         GtkWidget* menu) { |  | 
| 528   GtkWidget* menu_item = gtk_custom_menu_item_new( |  | 
| 529       gfx::RemoveWindowsStyleAccelerators(UTF16ToUTF8(model->label())).c_str()); |  | 
| 530 |  | 
| 531   // Set up the callback to the model for when it is clicked. |  | 
| 532   g_object_set_data(G_OBJECT(menu_item), "button-model", model); |  | 
| 533   g_signal_connect(menu_item, "button-pushed", |  | 
| 534                    G_CALLBACK(OnMenuButtonPressedThunk), this); |  | 
| 535   g_signal_connect(menu_item, "try-button-pushed", |  | 
| 536                    G_CALLBACK(OnMenuTryButtonPressedThunk), this); |  | 
| 537 |  | 
| 538   GtkSizeGroup* group = NULL; |  | 
| 539   for (int i = 0; i < model->GetItemCount(); ++i) { |  | 
| 540     GtkWidget* button = NULL; |  | 
| 541 |  | 
| 542     switch (model->GetTypeAt(i)) { |  | 
| 543       case menus::ButtonMenuItemModel::TYPE_SPACE: { |  | 
| 544         gtk_custom_menu_item_add_space(GTK_CUSTOM_MENU_ITEM(menu_item)); |  | 
| 545         break; |  | 
| 546       } |  | 
| 547       case menus::ButtonMenuItemModel::TYPE_BUTTON: { |  | 
| 548         button = gtk_custom_menu_item_add_button( |  | 
| 549             GTK_CUSTOM_MENU_ITEM(menu_item), |  | 
| 550             model->GetCommandIdAt(i)); |  | 
| 551 |  | 
| 552         int icon_idr; |  | 
| 553         if (model->GetIconAt(i, &icon_idr)) { |  | 
| 554           SetupImageIcon(button, menu, icon_idr, delegate_); |  | 
| 555         } else { |  | 
| 556           gtk_button_set_label( |  | 
| 557               GTK_BUTTON(button), |  | 
| 558               gfx::RemoveWindowsStyleAccelerators( |  | 
| 559                   UTF16ToUTF8(model->GetLabelAt(i))).c_str()); |  | 
| 560         } |  | 
| 561 |  | 
| 562         SetupButtonShowHandler(button, model, i); |  | 
| 563         break; |  | 
| 564       } |  | 
| 565       case menus::ButtonMenuItemModel::TYPE_BUTTON_LABEL: { |  | 
| 566         button = gtk_custom_menu_item_add_button_label( |  | 
| 567             GTK_CUSTOM_MENU_ITEM(menu_item), |  | 
| 568             model->GetCommandIdAt(i)); |  | 
| 569         gtk_button_set_label( |  | 
| 570             GTK_BUTTON(button), |  | 
| 571             gfx::RemoveWindowsStyleAccelerators( |  | 
| 572                 UTF16ToUTF8(model->GetLabelAt(i))).c_str()); |  | 
| 573         SetupButtonShowHandler(button, model, i); |  | 
| 574         break; |  | 
| 575       } |  | 
| 576     } |  | 
| 577 |  | 
| 578     if (button && model->PartOfGroup(i)) { |  | 
| 579       if (!group) |  | 
| 580         group = gtk_size_group_new(GTK_SIZE_GROUP_HORIZONTAL); |  | 
| 581 |  | 
| 582       gtk_size_group_add_widget(group, button); |  | 
| 583     } |  | 
| 584   } |  | 
| 585 |  | 
| 586   if (group) { |  | 
| 587     g_object_unref(group); |  | 
| 588   } |  | 
| 589 |  | 
| 590   return menu_item; |  | 
| 591 } |  | 
| 592 |  | 
| 593 void MenuGtk::OnMenuItemActivated(GtkWidget* menuitem) { |  | 
| 594   if (block_activation_) |  | 
| 595     return; |  | 
| 596 |  | 
| 597   // We receive activation messages when highlighting a menu that has a |  | 
| 598   // submenu. Ignore them. |  | 
| 599   if (gtk_menu_item_get_submenu(GTK_MENU_ITEM(menuitem))) |  | 
| 600     return; |  | 
| 601 |  | 
| 602   // The activate signal is sent to radio items as they get deselected; |  | 
| 603   // ignore it in this case. |  | 
| 604   if (GTK_IS_RADIO_MENU_ITEM(menuitem) && |  | 
| 605       !gtk_check_menu_item_get_active(GTK_CHECK_MENU_ITEM(menuitem))) { |  | 
| 606     return; |  | 
| 607   } |  | 
| 608 |  | 
| 609   int id; |  | 
| 610   if (!GetMenuItemID(menuitem, &id)) |  | 
| 611     return; |  | 
| 612 |  | 
| 613   menus::MenuModel* model = ModelForMenuItem(GTK_MENU_ITEM(menuitem)); |  | 
| 614 |  | 
| 615   // The menu item can still be activated by hotkeys even if it is disabled. |  | 
| 616   if (model->IsEnabledAt(id)) |  | 
| 617     ExecuteCommand(model, id); |  | 
| 618 } |  | 
| 619 |  | 
| 620 void MenuGtk::OnMenuButtonPressed(GtkWidget* menu_item, int command_id) { |  | 
| 621   menus::ButtonMenuItemModel* model = |  | 
| 622       reinterpret_cast<menus::ButtonMenuItemModel*>( |  | 
| 623           g_object_get_data(G_OBJECT(menu_item), "button-model")); |  | 
| 624   if (model && model->IsCommandIdEnabled(command_id)) { |  | 
| 625     if (delegate_) |  | 
| 626       delegate_->CommandWillBeExecuted(); |  | 
| 627 |  | 
| 628     model->ActivatedCommand(command_id); |  | 
| 629   } |  | 
| 630 } |  | 
| 631 |  | 
| 632 gboolean MenuGtk::OnMenuTryButtonPressed(GtkWidget* menu_item, |  | 
| 633                                          int command_id) { |  | 
| 634   gboolean pressed = FALSE; |  | 
| 635   menus::ButtonMenuItemModel* model = |  | 
| 636       reinterpret_cast<menus::ButtonMenuItemModel*>( |  | 
| 637           g_object_get_data(G_OBJECT(menu_item), "button-model")); |  | 
| 638   if (model && |  | 
| 639       model->IsCommandIdEnabled(command_id) && |  | 
| 640       !model->DoesCommandIdDismissMenu(command_id)) { |  | 
| 641     if (delegate_) |  | 
| 642       delegate_->CommandWillBeExecuted(); |  | 
| 643 |  | 
| 644     model->ActivatedCommand(command_id); |  | 
| 645     pressed = TRUE; |  | 
| 646   } |  | 
| 647 |  | 
| 648   return pressed; |  | 
| 649 } |  | 
| 650 |  | 
| 651 // static |  | 
| 652 void MenuGtk::WidgetMenuPositionFunc(GtkMenu* menu, |  | 
| 653                                      int* x, |  | 
| 654                                      int* y, |  | 
| 655                                      gboolean* push_in, |  | 
| 656                                      void* void_widget) { |  | 
| 657   GtkWidget* widget = GTK_WIDGET(void_widget); |  | 
| 658   GtkRequisition menu_req; |  | 
| 659 |  | 
| 660   gtk_widget_size_request(GTK_WIDGET(menu), &menu_req); |  | 
| 661 |  | 
| 662   gdk_window_get_origin(widget->window, x, y); |  | 
| 663   GdkScreen *screen = gtk_widget_get_screen(widget); |  | 
| 664   gint monitor = gdk_screen_get_monitor_at_point(screen, *x, *y); |  | 
| 665 |  | 
| 666   GdkRectangle screen_rect; |  | 
| 667   gdk_screen_get_monitor_geometry(screen, monitor, |  | 
| 668                                   &screen_rect); |  | 
| 669 |  | 
| 670   if (GTK_WIDGET_NO_WINDOW(widget)) { |  | 
| 671     *x += widget->allocation.x; |  | 
| 672     *y += widget->allocation.y; |  | 
| 673   } |  | 
| 674   *y += widget->allocation.height; |  | 
| 675 |  | 
| 676   bool start_align = |  | 
| 677     !!g_object_get_data(G_OBJECT(widget), "left-align-popup"); |  | 
| 678   if (base::i18n::IsRTL()) |  | 
| 679     start_align = !start_align; |  | 
| 680 |  | 
| 681   if (!start_align) |  | 
| 682     *x += widget->allocation.width - menu_req.width; |  | 
| 683 |  | 
| 684   *y = CalculateMenuYPosition(&screen_rect, &menu_req, widget, *y); |  | 
| 685 |  | 
| 686   *push_in = FALSE; |  | 
| 687 } |  | 
| 688 |  | 
| 689 // static |  | 
| 690 void MenuGtk::PointMenuPositionFunc(GtkMenu* menu, |  | 
| 691                                     int* x, |  | 
| 692                                     int* y, |  | 
| 693                                     gboolean* push_in, |  | 
| 694                                     gpointer userdata) { |  | 
| 695   *push_in = TRUE; |  | 
| 696 |  | 
| 697   gfx::Point* point = reinterpret_cast<gfx::Point*>(userdata); |  | 
| 698   *x = point->x(); |  | 
| 699   *y = point->y(); |  | 
| 700 |  | 
| 701   GtkRequisition menu_req; |  | 
| 702   gtk_widget_size_request(GTK_WIDGET(menu), &menu_req); |  | 
| 703   GdkScreen* screen; |  | 
| 704   gdk_display_get_pointer(gdk_display_get_default(), &screen, NULL, NULL, NULL); |  | 
| 705   gint monitor = gdk_screen_get_monitor_at_point(screen, *x, *y); |  | 
| 706 |  | 
| 707   GdkRectangle screen_rect; |  | 
| 708   gdk_screen_get_monitor_geometry(screen, monitor, &screen_rect); |  | 
| 709 |  | 
| 710   *y = CalculateMenuYPosition(&screen_rect, &menu_req, NULL, *y); |  | 
| 711 } |  | 
| 712 |  | 
| 713 void MenuGtk::ExecuteCommand(menus::MenuModel* model, int id) { |  | 
| 714   if (delegate_) |  | 
| 715     delegate_->CommandWillBeExecuted(); |  | 
| 716 |  | 
| 717   GdkEvent* event = gtk_get_current_event(); |  | 
| 718   if (event && event->type == GDK_BUTTON_RELEASE) { |  | 
| 719     model->ActivatedAtWithDisposition( |  | 
| 720         id, event_utils::DispositionFromEventFlags(event->button.state)); |  | 
| 721   } else { |  | 
| 722     model->ActivatedAt(id); |  | 
| 723   } |  | 
| 724 |  | 
| 725   if (event) |  | 
| 726     gdk_event_free(event); |  | 
| 727 } |  | 
| 728 |  | 
| 729 void MenuGtk::OnMenuShow(GtkWidget* widget) { |  | 
| 730   MessageLoop::current()->PostTask(FROM_HERE, |  | 
| 731       factory_.NewRunnableMethod(&MenuGtk::UpdateMenu)); |  | 
| 732 } |  | 
| 733 |  | 
| 734 void MenuGtk::OnMenuHidden(GtkWidget* widget) { |  | 
| 735   if (delegate_) |  | 
| 736     delegate_->StoppedShowing(); |  | 
| 737 } |  | 
| 738 |  | 
| 739 // static |  | 
| 740 void MenuGtk::SetButtonItemInfo(GtkWidget* button, gpointer userdata) { |  | 
| 741   menus::ButtonMenuItemModel* model = |  | 
| 742       reinterpret_cast<menus::ButtonMenuItemModel*>( |  | 
| 743           g_object_get_data(G_OBJECT(button), "button-model")); |  | 
| 744   int index = GPOINTER_TO_INT(g_object_get_data( |  | 
| 745       G_OBJECT(button), "button-model-id")); |  | 
| 746 |  | 
| 747   if (model->IsItemDynamicAt(index)) { |  | 
| 748     std::string label = |  | 
| 749         gfx::ConvertAcceleratorsFromWindowsStyle( |  | 
| 750             UTF16ToUTF8(model->GetLabelAt(index))); |  | 
| 751     gtk_button_set_label(GTK_BUTTON(button), label.c_str()); |  | 
| 752   } |  | 
| 753 |  | 
| 754   gtk_widget_set_sensitive(GTK_WIDGET(button), model->IsEnabledAt(index)); |  | 
| 755 } |  | 
| 756 |  | 
| 757 // static |  | 
| 758 void MenuGtk::SetMenuItemInfo(GtkWidget* widget, gpointer userdata) { |  | 
| 759   if (GTK_IS_SEPARATOR_MENU_ITEM(widget)) { |  | 
| 760     // We need to explicitly handle this case because otherwise we'll ask the |  | 
| 761     // menu delegate about something with an invalid id. |  | 
| 762     return; |  | 
| 763   } |  | 
| 764 |  | 
| 765   int id; |  | 
| 766   if (!GetMenuItemID(widget, &id)) |  | 
| 767     return; |  | 
| 768 |  | 
| 769   menus::MenuModel* model = ModelForMenuItem(GTK_MENU_ITEM(widget)); |  | 
| 770   if (!model) { |  | 
| 771     // If we're not providing the sub menu, then there's no model.  For |  | 
| 772     // example, the IME submenu doesn't have a model. |  | 
| 773     return; |  | 
| 774   } |  | 
| 775 |  | 
| 776   if (GTK_IS_CHECK_MENU_ITEM(widget)) { |  | 
| 777     GtkCheckMenuItem* item = GTK_CHECK_MENU_ITEM(widget); |  | 
| 778 |  | 
| 779     // gtk_check_menu_item_set_active() will send the activate signal. Touching |  | 
| 780     // the underlying "active" property will also call the "activate" handler |  | 
| 781     // for this menu item. So we prevent the "activate" handler from |  | 
| 782     // being called while we set the checkbox. |  | 
| 783     // Why not use one of the glib signal-blocking functions?  Because when we |  | 
| 784     // toggle a radio button, it will deactivate one of the other radio buttons, |  | 
| 785     // which we don't have a pointer to. |  | 
| 786     // Wny not make this a member variable?  Because "menu" is a pointer to the |  | 
| 787     // root of the MenuGtk and we want to disable *all* MenuGtks, including |  | 
| 788     // submenus. |  | 
| 789     block_activation_ = true; |  | 
| 790     gtk_check_menu_item_set_active(item, model->IsItemCheckedAt(id)); |  | 
| 791     block_activation_ = false; |  | 
| 792   } |  | 
| 793 |  | 
| 794   if (GTK_IS_CUSTOM_MENU_ITEM(widget)) { |  | 
| 795     // Iterate across all the buttons to update their visible properties. |  | 
| 796     gtk_custom_menu_item_foreach_button(GTK_CUSTOM_MENU_ITEM(widget), |  | 
| 797                                         SetButtonItemInfo, |  | 
| 798                                         userdata); |  | 
| 799   } |  | 
| 800 |  | 
| 801   if (GTK_IS_MENU_ITEM(widget)) { |  | 
| 802     gtk_widget_set_sensitive(widget, model->IsEnabledAt(id)); |  | 
| 803 |  | 
| 804     if (model->IsVisibleAt(id)) { |  | 
| 805       // Update the menu item label if it is dynamic. |  | 
| 806       if (model->IsItemDynamicAt(id)) { |  | 
| 807         std::string label = |  | 
| 808             gfx::ConvertAcceleratorsFromWindowsStyle( |  | 
| 809                 UTF16ToUTF8(model->GetLabelAt(id))); |  | 
| 810 |  | 
| 811 #if GTK_CHECK_VERSION(2, 16, 0) |  | 
| 812         gtk_menu_item_set_label(GTK_MENU_ITEM(widget), label.c_str()); |  | 
| 813 #else |  | 
| 814         gtk_label_set_label(GTK_LABEL(GTK_BIN(widget)->child), label.c_str()); |  | 
| 815 #endif |  | 
| 816         if (GTK_IS_IMAGE_MENU_ITEM(widget)) { |  | 
| 817           SkBitmap icon; |  | 
| 818           if (model->GetIconAt(id, &icon)) { |  | 
| 819             GdkPixbuf* pixbuf = gfx::GdkPixbufFromSkBitmap(&icon); |  | 
| 820             gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(widget), |  | 
| 821                                           gtk_image_new_from_pixbuf(pixbuf)); |  | 
| 822             g_object_unref(pixbuf); |  | 
| 823           } else { |  | 
| 824             gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(widget), NULL); |  | 
| 825           } |  | 
| 826         } |  | 
| 827       } |  | 
| 828 |  | 
| 829       gtk_widget_show(widget); |  | 
| 830     } else { |  | 
| 831       gtk_widget_hide(widget); |  | 
| 832     } |  | 
| 833 |  | 
| 834     GtkWidget* submenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(widget)); |  | 
| 835     if (submenu) { |  | 
| 836       gtk_container_foreach(GTK_CONTAINER(submenu), &SetMenuItemInfo, |  | 
| 837                             userdata); |  | 
| 838     } |  | 
| 839   } |  | 
| 840 } |  | 
| OLD | NEW | 
|---|