Chromium Code Reviews| OLD | NEW |
|---|---|
| (Empty) | |
| 1 // Copyright (c) 2013 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/ui/libgtk2ui/menu_util.h" | |
| 6 | |
| 7 #include "base/strings/utf_string_conversions.h" | |
| 8 #include "chrome/app/chrome_command_ids.h" | |
| 9 #include "chrome/browser/ui/libgtk2ui/gtk2_util.h" | |
| 10 #include "ui/base/accelerators/accelerator.h" | |
| 11 #include "ui/base/models/menu_model.h" | |
| 12 | |
| 13 namespace libgtk2ui { | |
| 14 | |
| 15 GtkWidget* BuildMenuItemWithImage(const std::string& label, GtkWidget* image) { | |
| 16 GtkWidget* menu_item = gtk_image_menu_item_new_with_mnemonic(label.c_str()); | |
| 17 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item), image); | |
| 18 return menu_item; | |
| 19 } | |
| 20 | |
| 21 GtkWidget* BuildMenuItemWithImage(const std::string& label, | |
| 22 const gfx::Image& icon) { | |
| 23 GdkPixbuf* pixbuf = GdkPixbufFromSkBitmap(*icon.ToSkBitmap()); | |
| 24 | |
| 25 GtkWidget* menu_item = | |
| 26 BuildMenuItemWithImage(label, gtk_image_new_from_pixbuf(pixbuf)); | |
| 27 g_object_unref(pixbuf); | |
| 28 return menu_item; | |
| 29 } | |
| 30 | |
| 31 GtkWidget* BuildMenuItemWithLabel(const std::string& label, int command_id) { | |
| 32 GtkWidget* img = GetDefaultImageForCommandId(command_id); | |
| 33 return img ? BuildMenuItemWithImage(label, img) | |
| 34 : gtk_menu_item_new_with_mnemonic(label.c_str()); | |
| 35 } | |
| 36 | |
| 37 ui::MenuModel* ModelForMenuItem(GtkMenuItem* menu_item) { | |
| 38 return reinterpret_cast<ui::MenuModel*>( | |
| 39 g_object_get_data(G_OBJECT(menu_item), "model")); | |
| 40 } | |
| 41 | |
| 42 GtkWidget* AppendMenuItemToMenu(int index, | |
| 43 ui::MenuModel* model, | |
| 44 GtkWidget* menu_item, | |
| 45 GtkWidget* menu, | |
| 46 bool connect_to_activate, | |
| 47 GCallback item_activated_cb, | |
| 48 void* this_ptr) { | |
| 49 // Set the ID of a menu item. | |
| 50 // Add 1 to the menu_id to avoid setting zero (null) to "menu-id". | |
| 51 g_object_set_data(G_OBJECT(menu_item), "menu-id", GINT_TO_POINTER(index + 1)); | |
| 52 | |
| 53 // Native menu items do their own thing, so only selectively listen for the | |
| 54 // activate signal. | |
| 55 if (connect_to_activate) { | |
| 56 g_signal_connect(menu_item, "activate", item_activated_cb, this_ptr); | |
| 57 } | |
| 58 | |
| 59 // AppendMenuItemToMenu is used both internally when we control menu creation | |
| 60 // from a model (where the model can choose to hide certain menu items), and | |
| 61 // with immediate commands which don't provide the option. | |
| 62 if (model) { | |
| 63 if (model->IsVisibleAt(index)) | |
| 64 gtk_widget_show(menu_item); | |
| 65 } else { | |
| 66 gtk_widget_show(menu_item); | |
| 67 } | |
| 68 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item); | |
| 69 return menu_item; | |
| 70 } | |
| 71 | |
| 72 bool GetMenuItemID(GtkWidget* menu_item, int* menu_id) { | |
| 73 gpointer id_ptr = g_object_get_data(G_OBJECT(menu_item), "menu-id"); | |
| 74 if (id_ptr != NULL) { | |
| 75 *menu_id = GPOINTER_TO_INT(id_ptr) - 1; | |
| 76 return true; | |
| 77 } | |
| 78 | |
| 79 return false; | |
| 80 } | |
| 81 | |
| 82 void ExecuteCommand(ui::MenuModel* model, int id) { | |
| 83 GdkEvent* event = gtk_get_current_event(); | |
| 84 int event_flags = 0; | |
| 85 | |
| 86 if (event && event->type == GDK_BUTTON_RELEASE) | |
| 87 event_flags = EventFlagsFromGdkState(event->button.state); | |
| 88 model->ActivatedAt(id, event_flags); | |
| 89 | |
| 90 if (event) | |
| 91 gdk_event_free(event); | |
| 92 } | |
| 93 | |
| 94 GtkWidget* GetDefaultImageForCommandId(int command_id) { | |
|
Elliot Glaysher
2013/07/10 22:55:03
As I don't see any of these commands in an appindi
sidharthms
2013/07/11 05:15:12
Done.
| |
| 95 const char* stock; | |
| 96 switch (command_id) { | |
| 97 case IDC_NEW_TAB: | |
| 98 case IDC_CONTENT_CONTEXT_OPENIMAGENEWTAB: | |
| 99 case IDC_CONTENT_CONTEXT_OPENLINKNEWTAB: | |
| 100 case IDC_CONTENT_CONTEXT_OPENAVNEWTAB: | |
| 101 stock = GTK_STOCK_NEW; | |
| 102 break; | |
| 103 | |
| 104 case IDC_CLOSE_TAB: | |
| 105 stock = GTK_STOCK_CLOSE; | |
| 106 break; | |
| 107 | |
| 108 case IDC_CONTENT_CONTEXT_SAVEIMAGEAS: | |
| 109 case IDC_CONTENT_CONTEXT_SAVEAVAS: | |
| 110 case IDC_CONTENT_CONTEXT_SAVELINKAS: | |
| 111 stock = GTK_STOCK_SAVE_AS; | |
| 112 break; | |
| 113 | |
| 114 case IDC_SAVE_PAGE: | |
| 115 stock = GTK_STOCK_SAVE; | |
| 116 break; | |
| 117 | |
| 118 case IDC_COPY: | |
| 119 case IDC_CONTENT_CONTEXT_COPYIMAGELOCATION: | |
| 120 case IDC_CONTENT_CONTEXT_COPYLINKLOCATION: | |
| 121 case IDC_CONTENT_CONTEXT_COPYAVLOCATION: | |
| 122 case IDC_CONTENT_CONTEXT_COPYEMAILADDRESS: | |
| 123 case IDC_CONTENT_CONTEXT_COPY: | |
| 124 stock = GTK_STOCK_COPY; | |
| 125 break; | |
| 126 | |
| 127 case IDC_CUT: | |
| 128 case IDC_CONTENT_CONTEXT_CUT: | |
| 129 stock = GTK_STOCK_CUT; | |
| 130 break; | |
| 131 | |
| 132 case IDC_PASTE: | |
| 133 case IDC_CONTENT_CONTEXT_PASTE: | |
| 134 stock = GTK_STOCK_PASTE; | |
| 135 break; | |
| 136 | |
| 137 case IDC_CONTENT_CONTEXT_DELETE: | |
| 138 case IDC_BOOKMARK_BAR_REMOVE: | |
| 139 stock = GTK_STOCK_DELETE; | |
| 140 break; | |
| 141 | |
| 142 case IDC_CONTENT_CONTEXT_UNDO: | |
| 143 stock = GTK_STOCK_UNDO; | |
| 144 break; | |
| 145 | |
| 146 case IDC_CONTENT_CONTEXT_REDO: | |
| 147 stock = GTK_STOCK_REDO; | |
| 148 break; | |
| 149 | |
| 150 case IDC_SEARCH: | |
| 151 case IDC_FIND: | |
| 152 case IDC_CONTENT_CONTEXT_SEARCHWEBFOR: | |
| 153 stock = GTK_STOCK_FIND; | |
| 154 break; | |
| 155 | |
| 156 case IDC_CONTENT_CONTEXT_SELECTALL: | |
| 157 stock = GTK_STOCK_SELECT_ALL; | |
| 158 break; | |
| 159 | |
| 160 case IDC_CLEAR_BROWSING_DATA: | |
| 161 stock = GTK_STOCK_CLEAR; | |
| 162 break; | |
| 163 | |
| 164 case IDC_BACK: | |
| 165 stock = GTK_STOCK_GO_BACK; | |
| 166 break; | |
| 167 | |
| 168 case IDC_RELOAD: | |
| 169 stock = GTK_STOCK_REFRESH; | |
| 170 break; | |
| 171 | |
| 172 case IDC_FORWARD: | |
| 173 stock = GTK_STOCK_GO_FORWARD; | |
| 174 break; | |
| 175 | |
| 176 case IDC_PRINT: | |
| 177 stock = GTK_STOCK_PRINT; | |
| 178 break; | |
| 179 | |
| 180 case IDC_CONTENT_CONTEXT_VIEWPAGEINFO: | |
| 181 stock = GTK_STOCK_INFO; | |
| 182 break; | |
| 183 | |
| 184 case IDC_SPELLCHECK_MENU: | |
| 185 stock = GTK_STOCK_SPELL_CHECK; | |
| 186 break; | |
| 187 | |
| 188 case IDC_RESTORE_TAB: | |
| 189 stock = GTK_STOCK_UNDELETE; | |
| 190 break; | |
| 191 | |
| 192 case IDC_HOME: | |
| 193 stock = GTK_STOCK_HOME; | |
| 194 break; | |
| 195 | |
| 196 case IDC_STOP: | |
| 197 stock = GTK_STOCK_STOP; | |
| 198 break; | |
| 199 | |
| 200 case IDC_ABOUT: | |
| 201 stock = GTK_STOCK_ABOUT; | |
| 202 break; | |
| 203 | |
| 204 case IDC_EXIT: | |
| 205 stock = GTK_STOCK_QUIT; | |
| 206 break; | |
| 207 | |
| 208 case IDC_HELP_PAGE_VIA_MENU: | |
| 209 stock = GTK_STOCK_HELP; | |
| 210 break; | |
| 211 | |
| 212 case IDC_OPTIONS: | |
| 213 stock = GTK_STOCK_PREFERENCES; | |
| 214 break; | |
| 215 | |
| 216 case IDC_CONTENT_CONTEXT_GOTOURL: | |
| 217 stock = GTK_STOCK_JUMP_TO; | |
| 218 break; | |
| 219 | |
| 220 case IDC_DEV_TOOLS_INSPECT: | |
| 221 case IDC_CONTENT_CONTEXT_INSPECTELEMENT: | |
| 222 stock = GTK_STOCK_PROPERTIES; | |
| 223 break; | |
| 224 | |
| 225 case IDC_BOOKMARK_BAR_ADD_NEW_BOOKMARK: | |
| 226 stock = GTK_STOCK_ADD; | |
| 227 break; | |
| 228 | |
| 229 case IDC_BOOKMARK_BAR_RENAME_FOLDER: | |
| 230 case IDC_BOOKMARK_BAR_EDIT: | |
| 231 stock = GTK_STOCK_EDIT; | |
| 232 break; | |
| 233 | |
| 234 case IDC_BOOKMARK_BAR_NEW_FOLDER: | |
| 235 stock = GTK_STOCK_DIRECTORY; | |
| 236 break; | |
| 237 | |
| 238 case IDC_BOOKMARK_BAR_OPEN_ALL: | |
| 239 stock = GTK_STOCK_OPEN; | |
| 240 break; | |
| 241 | |
| 242 default: | |
| 243 stock = NULL; | |
| 244 } | |
| 245 | |
| 246 return stock ? gtk_image_new_from_stock(stock, GTK_ICON_SIZE_MENU) : NULL; | |
| 247 } | |
| 248 | |
| 249 void BuildSubmenuFromModel(ui::MenuModel* model, | |
| 250 GtkWidget* menu, | |
| 251 GCallback item_activated_cb, | |
| 252 bool* block_activation, | |
| 253 void* this_ptr) { | |
| 254 std::map<int, GtkWidget*> radio_groups; | |
| 255 GtkWidget* menu_item = NULL; | |
| 256 for (int i = 0; i < model->GetItemCount(); ++i) { | |
| 257 gfx::Image icon; | |
| 258 std::string label = | |
| 259 ConvertAcceleratorsFromWindowsStyle(UTF16ToUTF8(model->GetLabelAt(i))); | |
| 260 | |
| 261 bool connect_to_activate = true; | |
| 262 | |
| 263 switch (model->GetTypeAt(i)) { | |
| 264 case ui::MenuModel::TYPE_SEPARATOR: | |
| 265 menu_item = gtk_separator_menu_item_new(); | |
| 266 break; | |
| 267 | |
| 268 case ui::MenuModel::TYPE_CHECK: | |
| 269 menu_item = gtk_check_menu_item_new_with_mnemonic(label.c_str()); | |
| 270 break; | |
| 271 | |
| 272 case ui::MenuModel::TYPE_RADIO: { | |
| 273 std::map<int, GtkWidget*>::iterator iter = | |
| 274 radio_groups.find(model->GetGroupIdAt(i)); | |
| 275 | |
| 276 if (iter == radio_groups.end()) { | |
| 277 menu_item = | |
| 278 gtk_radio_menu_item_new_with_mnemonic(NULL, label.c_str()); | |
| 279 radio_groups[model->GetGroupIdAt(i)] = menu_item; | |
| 280 } else { | |
| 281 menu_item = gtk_radio_menu_item_new_with_mnemonic_from_widget( | |
| 282 GTK_RADIO_MENU_ITEM(iter->second), label.c_str()); | |
| 283 } | |
| 284 break; | |
| 285 } | |
| 286 case ui::MenuModel::TYPE_BUTTON_ITEM: { | |
| 287 NOTIMPLEMENTED(); | |
| 288 break; | |
| 289 } | |
| 290 case ui::MenuModel::TYPE_SUBMENU: | |
| 291 case ui::MenuModel::TYPE_COMMAND: { | |
| 292 int command_id = model->GetCommandIdAt(i); | |
| 293 if (model->GetIconAt(i, &icon)) | |
| 294 menu_item = BuildMenuItemWithImage(label, icon); | |
| 295 else | |
| 296 menu_item = BuildMenuItemWithLabel(label, command_id); | |
| 297 if (GTK_IS_IMAGE_MENU_ITEM(menu_item)) { | |
| 298 SetAlwaysShowImage(menu_item); | |
| 299 } | |
| 300 break; | |
| 301 } | |
| 302 | |
| 303 default: | |
| 304 NOTREACHED(); | |
| 305 } | |
| 306 | |
| 307 if (model->GetTypeAt(i) == ui::MenuModel::TYPE_SUBMENU) { | |
| 308 GtkWidget* submenu = gtk_menu_new(); | |
| 309 ui::MenuModel* submenu_model = model->GetSubmenuModelAt(i); | |
| 310 BuildSubmenuFromModel(submenu_model, | |
| 311 submenu, | |
| 312 item_activated_cb, | |
| 313 block_activation, | |
| 314 this_ptr); | |
| 315 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), submenu); | |
| 316 | |
| 317 // Update all the menu item info in the newly-generated menu. | |
| 318 gtk_container_foreach( | |
| 319 GTK_CONTAINER(submenu), SetMenuItemInfo, block_activation); | |
| 320 submenu_model->MenuWillShow(); | |
| 321 connect_to_activate = false; | |
| 322 } | |
| 323 | |
| 324 ui::Accelerator accelerator; | |
| 325 if (model->GetAcceleratorAt(i, &accelerator)) { | |
| 326 gtk_widget_add_accelerator(menu_item, | |
| 327 "activate", | |
| 328 NULL, | |
| 329 GetGdkKeyCodeForAccelerator(accelerator), | |
| 330 GetGdkModifierForAccelerator(accelerator), | |
| 331 GTK_ACCEL_VISIBLE); | |
| 332 } | |
| 333 | |
| 334 g_object_set_data(G_OBJECT(menu_item), "model", model); | |
| 335 AppendMenuItemToMenu(i, | |
| 336 model, | |
| 337 menu_item, | |
| 338 menu, | |
| 339 connect_to_activate, | |
| 340 item_activated_cb, | |
| 341 this_ptr); | |
| 342 | |
| 343 menu_item = NULL; | |
| 344 } | |
| 345 } | |
| 346 | |
| 347 void SetMenuItemInfo(GtkWidget* widget, void* block_activation_ptr) { | |
| 348 if (GTK_IS_SEPARATOR_MENU_ITEM(widget)) { | |
| 349 // We need to explicitly handle this case because otherwise we'll ask the | |
| 350 // menu delegate about something with an invalid id. | |
| 351 return; | |
| 352 } | |
| 353 | |
| 354 int id; | |
| 355 if (!GetMenuItemID(widget, &id)) | |
| 356 return; | |
| 357 | |
| 358 ui::MenuModel* model = ModelForMenuItem(GTK_MENU_ITEM(widget)); | |
| 359 if (!model) { | |
| 360 // If we're not providing the sub menu, then there's no model. For | |
| 361 // example, the IME submenu doesn't have a model. | |
| 362 return; | |
| 363 } | |
| 364 bool* block_activation = static_cast<bool*>(block_activation_ptr); | |
| 365 | |
| 366 if (GTK_IS_CHECK_MENU_ITEM(widget)) { | |
| 367 GtkCheckMenuItem* item = GTK_CHECK_MENU_ITEM(widget); | |
| 368 | |
| 369 // gtk_check_menu_item_set_active() will send the activate signal. Touching | |
| 370 // the underlying "active" property will also call the "activate" handler | |
| 371 // for this menu item. So we prevent the "activate" handler from | |
| 372 // being called while we set the checkbox. | |
| 373 // Why not use one of the glib signal-blocking functions? Because when we | |
| 374 // toggle a radio button, it will deactivate one of the other radio buttons, | |
| 375 // which we don't have a pointer to. | |
| 376 *block_activation = true; | |
| 377 gtk_check_menu_item_set_active(item, model->IsItemCheckedAt(id)); | |
| 378 *block_activation = false; | |
| 379 } | |
| 380 | |
| 381 if (GTK_IS_MENU_ITEM(widget)) { | |
| 382 gtk_widget_set_sensitive(widget, model->IsEnabledAt(id)); | |
| 383 | |
| 384 if (model->IsVisibleAt(id)) { | |
| 385 // Update the menu item label if it is dynamic. | |
| 386 if (model->IsItemDynamicAt(id)) { | |
| 387 std::string label = ConvertAcceleratorsFromWindowsStyle( | |
| 388 UTF16ToUTF8(model->GetLabelAt(id))); | |
| 389 | |
| 390 gtk_menu_item_set_label(GTK_MENU_ITEM(widget), label.c_str()); | |
| 391 if (GTK_IS_IMAGE_MENU_ITEM(widget)) { | |
| 392 gfx::Image icon; | |
| 393 if (model->GetIconAt(id, &icon)) { | |
| 394 gtk_image_menu_item_set_image( | |
| 395 GTK_IMAGE_MENU_ITEM(widget), | |
| 396 gtk_image_new_from_pixbuf( | |
| 397 GdkPixbufFromSkBitmap(*icon.ToSkBitmap()))); | |
| 398 } else { | |
| 399 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(widget), NULL); | |
| 400 } | |
| 401 } | |
| 402 } | |
| 403 | |
| 404 gtk_widget_show(widget); | |
| 405 } else { | |
| 406 gtk_widget_hide(widget); | |
| 407 } | |
| 408 | |
| 409 GtkWidget* submenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(widget)); | |
| 410 if (submenu) { | |
| 411 gtk_container_foreach( | |
| 412 GTK_CONTAINER(submenu), &SetMenuItemInfo, block_activation_ptr); | |
| 413 } | |
| 414 } | |
| 415 } | |
| 416 | |
| 417 } // namespace libgtk2ui | |
| OLD | NEW |