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 |