1 // Copyright (c) 2012 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/gtk/global_menu_bar.h" 6 7 #include <gtk/gtk.h> 8 9 #include "base/command_line.h" 10 #include "base/prefs/pref_service.h" 11 #include "chrome/app/chrome_command_ids.h" 12 #include "chrome/browser/chrome_notification_types.h" 13 #include "chrome/browser/profiles/profile.h" 14 #include "chrome/browser/ui/browser.h" 15 #include "chrome/browser/ui/browser_commands.h" 16 #include "chrome/browser/ui/gtk/accelerators_gtk.h" 17 #include "chrome/browser/ui/gtk/gtk_theme_service.h" 18 #include "chrome/browser/ui/gtk/gtk_util.h" 19 #include "chrome/common/chrome_switches.h" 20 #include "chrome/common/pref_names.h" 21 #include "content/public/browser/notification_details.h" 22 #include "content/public/browser/notification_source.h" 23 #include "grit/generated_resources.h" 24 #include "ui/base/accelerators/menu_label_accelerator_util_linux.h" 25 #include "ui/base/accelerators/platform_accelerator_gtk.h" 26 #include "ui/base/l10n/l10n_util.h" 27 28 struct GlobalMenuBarCommand { 29 int str_id; 30 int command; 31 int tag; 32 }; 33 34 namespace { 35 36 const int MENU_SEPARATOR =-1; 37 const int MENU_END = -2; 38 const int MENU_DISABLED_LABEL = -3; 39 40 GlobalMenuBarCommand file_menu[] = { 41 { IDS_NEW_TAB, IDC_NEW_TAB }, 42 { IDS_NEW_WINDOW, IDC_NEW_WINDOW }, 43 { IDS_NEW_INCOGNITO_WINDOW, IDC_NEW_INCOGNITO_WINDOW }, 44 { IDS_REOPEN_CLOSED_TABS_LINUX, IDC_RESTORE_TAB }, 45 { IDS_OPEN_FILE_LINUX, IDC_OPEN_FILE }, 46 { IDS_OPEN_LOCATION_LINUX, IDC_FOCUS_LOCATION }, 47 48 { MENU_SEPARATOR, MENU_SEPARATOR }, 49 50 { IDS_CREATE_SHORTCUTS, IDC_CREATE_SHORTCUTS }, 51 52 { MENU_SEPARATOR, MENU_SEPARATOR }, 53 54 { IDS_CLOSE_WINDOW_LINUX, IDC_CLOSE_WINDOW }, 55 { IDS_CLOSE_TAB_LINUX, IDC_CLOSE_TAB }, 56 { IDS_SAVE_PAGE, IDC_SAVE_PAGE }, 57 58 { MENU_SEPARATOR, MENU_SEPARATOR }, 59 60 { IDS_PRINT, IDC_PRINT }, 61 62 { MENU_END, MENU_END } 63 }; 64 65 GlobalMenuBarCommand edit_menu[] = { 66 { IDS_CUT, IDC_CUT }, 67 { IDS_COPY, IDC_COPY }, 68 { IDS_PASTE, IDC_PASTE }, 69 70 { MENU_SEPARATOR, MENU_SEPARATOR }, 71 72 { IDS_FIND, IDC_FIND }, 73 74 { MENU_SEPARATOR, MENU_SEPARATOR }, 75 76 { IDS_PREFERENCES, IDC_OPTIONS }, 77 78 { MENU_END, MENU_END } 79 }; 80 81 GlobalMenuBarCommand view_menu[] = { 82 { IDS_SHOW_BOOKMARK_BAR, IDC_SHOW_BOOKMARK_BAR }, 83 84 { MENU_SEPARATOR, MENU_SEPARATOR }, 85 86 { IDS_STOP_MENU_LINUX, IDC_STOP }, 87 { IDS_RELOAD_MENU_LINUX, IDC_RELOAD }, 88 89 { MENU_SEPARATOR, MENU_SEPARATOR }, 90 91 { IDS_FULLSCREEN, IDC_FULLSCREEN }, 92 { IDS_TEXT_DEFAULT_LINUX, IDC_ZOOM_NORMAL }, 93 { IDS_TEXT_BIGGER_LINUX, IDC_ZOOM_PLUS }, 94 { IDS_TEXT_SMALLER_LINUX, IDC_ZOOM_MINUS }, 95 96 { MENU_END, MENU_END } 97 }; 98 99 GlobalMenuBarCommand history_menu[] = { 100 { IDS_HISTORY_HOME_LINUX, IDC_HOME }, 101 { IDS_HISTORY_BACK_LINUX, IDC_BACK }, 102 { IDS_HISTORY_FORWARD_LINUX, IDC_FORWARD }, 103 104 { MENU_SEPARATOR, MENU_SEPARATOR }, 105 106 { IDS_HISTORY_VISITED_LINUX, MENU_DISABLED_LABEL, 107 GlobalMenuBar::TAG_MOST_VISITED_HEADER }, 108 109 { MENU_SEPARATOR, MENU_SEPARATOR }, 110 111 { IDS_HISTORY_CLOSED_LINUX, MENU_DISABLED_LABEL, 112 GlobalMenuBar::TAG_RECENTLY_CLOSED_HEADER }, 113 114 { MENU_SEPARATOR, MENU_SEPARATOR }, 115 116 { IDS_SHOWFULLHISTORY_LINK, IDC_SHOW_HISTORY }, 117 118 { MENU_END, MENU_END } 119 }; 120 121 GlobalMenuBarCommand tools_menu[] = { 122 { IDS_SHOW_DOWNLOADS, IDC_SHOW_DOWNLOADS }, 123 { IDS_SHOW_HISTORY, IDC_SHOW_HISTORY }, 124 { IDS_SHOW_EXTENSIONS, IDC_MANAGE_EXTENSIONS }, 125 126 { MENU_SEPARATOR, MENU_SEPARATOR }, 127 128 { IDS_TASK_MANAGER, IDC_TASK_MANAGER }, 129 { IDS_CLEAR_BROWSING_DATA, IDC_CLEAR_BROWSING_DATA }, 130 131 { MENU_SEPARATOR, MENU_SEPARATOR }, 132 133 { IDS_VIEW_SOURCE, IDC_VIEW_SOURCE }, 134 { IDS_DEV_TOOLS, IDC_DEV_TOOLS }, 135 { IDS_DEV_TOOLS_CONSOLE, IDC_DEV_TOOLS_CONSOLE }, 136 137 { MENU_END, MENU_END } 138 }; 139 140 GlobalMenuBarCommand help_menu[] = { 141 { IDS_FEEDBACK, IDC_FEEDBACK }, 142 { IDS_HELP_PAGE , IDC_HELP_PAGE_VIA_MENU }, 143 { MENU_END, MENU_END } 144 }; 145 146 } // namespace 147 148 GlobalMenuBar::GlobalMenuBar(Browser* browser) 149 : browser_(browser), 150 menu_bar_(gtk_menu_bar_new()), 151 history_menu_(browser_), 152 dummy_accel_group_(gtk_accel_group_new()), 153 block_activation_(false) { 154 // The global menu bar should never actually be shown in the app; it should 155 // instead remain in our widget hierarchy simply to be noticed by third party 156 // components. 157 g_object_ref_sink(menu_bar_); 158 gtk_widget_set_no_show_all(menu_bar_, TRUE); 159 160 // Set a nice name so it shows up in gtkparasite and others. 161 gtk_widget_set_name(menu_bar_, "chrome-hidden-global-menubar"); 162 163 BuildGtkMenuFrom(IDS_FILE_MENU_LINUX, &id_to_menu_item_, file_menu, NULL); 164 BuildGtkMenuFrom(IDS_EDIT_MENU_LINUX, &id_to_menu_item_, edit_menu, NULL); 165 BuildGtkMenuFrom(IDS_VIEW_MENU_LINUX, &id_to_menu_item_, view_menu, NULL); 166 BuildGtkMenuFrom(IDS_HISTORY_MENU_LINUX, &id_to_menu_item_, 167 history_menu, &history_menu_); 168 169 BuildGtkMenuFrom(IDS_TOOLS_MENU_LINUX, &id_to_menu_item_, tools_menu, NULL); 170 BuildGtkMenuFrom(IDS_HELP_MENU_LINUX, &id_to_menu_item_, help_menu, NULL); 171 172 for (CommandIDMenuItemMap::const_iterator it = id_to_menu_item_.begin(); 173 it != id_to_menu_item_.end(); ++it) { 174 // Get the starting enabled state. 175 gtk_widget_set_sensitive(it->second, 176 chrome::IsCommandEnabled(browser_, it->first)); 177 178 // Set the accelerator for each menu item. 179 AcceleratorsGtk* accelerators = AcceleratorsGtk::GetInstance(); 180 const ui::Accelerator* accelerator = 181 accelerators->GetPrimaryAcceleratorForCommand(it->first); 182 if (accelerator) { 183 gtk_widget_add_accelerator(it->second, 184 "activate", 185 dummy_accel_group_, 186 ui::GetGdkKeyCodeForAccelerator(*accelerator), 187 ui::GetGdkModifierForAccelerator(*accelerator), 188 GTK_ACCEL_VISIBLE); 189 } 190 191 chrome::AddCommandObserver(browser_, it->first, this); 192 } 193 194 pref_change_registrar_.Init(browser_->profile()->GetPrefs()); 195 pref_change_registrar_.Add( 196 prefs::kShowBookmarkBar, 197 base::Bind(&GlobalMenuBar::OnBookmarkBarVisibilityChanged, 198 base::Unretained(this))); 199 OnBookmarkBarVisibilityChanged(); 200 } 201 202 GlobalMenuBar::~GlobalMenuBar() { 203 Disable(); 204 g_object_unref(dummy_accel_group_); 205 gtk_widget_destroy(menu_bar_); 206 g_object_unref(menu_bar_); 207 } 208 209 void GlobalMenuBar::Disable() { 210 for (CommandIDMenuItemMap::const_iterator it = id_to_menu_item_.begin(); 211 it != id_to_menu_item_.end(); ++it) { 212 chrome::RemoveCommandObserver(browser_, it->first, this); 213 } 214 id_to_menu_item_.clear(); 215 216 pref_change_registrar_.RemoveAll(); 217 } 218 219 void GlobalMenuBar::BuildGtkMenuFrom( 220 int menu_str_id, 221 std::map<int, GtkWidget*>* id_to_menu_item, 222 GlobalMenuBarCommand* commands, 223 GlobalMenuOwner* owner) { 224 GtkWidget* menu = gtk_menu_new(); 225 for (int i = 0; commands[i].str_id != MENU_END; ++i) { 226 GtkWidget* menu_item = BuildMenuItem( 227 commands[i].str_id, commands[i].command, commands[i].tag, 228 id_to_menu_item, menu); 229 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item); 230 } 231 232 gtk_widget_show(menu); 233 234 GtkWidget* menu_item = gtk_menu_item_new_with_mnemonic( 235 ui::RemoveWindowsStyleAccelerators( 236 l10n_util::GetStringUTF8(menu_str_id)).c_str()); 237 238 // Give the owner a chance to sink the reference before we add it to the menu 239 // bar. 240 if (owner) 241 owner->Init(menu, menu_item); 242 243 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), menu); 244 gtk_widget_show(menu_item); 245 gtk_menu_shell_append(GTK_MENU_SHELL(menu_bar_), menu_item); 246 } 247 248 GtkWidget* GlobalMenuBar::BuildMenuItem( 249 int string_id, 250 int command_id, 251 int tag_id, 252 std::map<int, GtkWidget*>* id_to_menu_item, 253 GtkWidget* menu_to_add_to) { 254 GtkWidget* menu_item = NULL; 255 if (string_id == MENU_SEPARATOR) { 256 menu_item = gtk_separator_menu_item_new(); 257 } else { 258 std::string label = ui::ConvertAcceleratorsFromWindowsStyle( 259 l10n_util::GetStringUTF8(string_id)); 260 261 if (command_id == IDC_SHOW_BOOKMARK_BAR) 262 menu_item = gtk_check_menu_item_new_with_mnemonic(label.c_str()); 263 else 264 menu_item = gtk_menu_item_new_with_mnemonic(label.c_str()); 265 266 if (tag_id) { 267 g_object_set_data(G_OBJECT(menu_item), "type-tag", 268 GINT_TO_POINTER(tag_id)); 269 } 270 271 if (command_id == MENU_DISABLED_LABEL) { 272 gtk_widget_set_sensitive(menu_item, FALSE); 273 } else { 274 id_to_menu_item->insert(std::make_pair(command_id, menu_item)); 275 g_object_set_data(G_OBJECT(menu_item), "command-id", 276 GINT_TO_POINTER(command_id)); 277 g_signal_connect(menu_item, "activate", 278 G_CALLBACK(OnItemActivatedThunk), this); 279 } 280 } 281 gtk_widget_show(menu_item); 282 return menu_item; 283 } 284 285 void GlobalMenuBar::EnabledStateChangedForCommand(int id, bool enabled) { 286 CommandIDMenuItemMap::iterator it = id_to_menu_item_.find(id); 287 if (it != id_to_menu_item_.end()) 288 gtk_widget_set_sensitive(it->second, enabled); 289 } 290 291 void GlobalMenuBar::OnBookmarkBarVisibilityChanged() { 292 CommandIDMenuItemMap::iterator it = 293 id_to_menu_item_.find(IDC_SHOW_BOOKMARK_BAR); 294 if (it != id_to_menu_item_.end()) { 295 PrefService* prefs = browser_->profile()->GetPrefs(); 296 block_activation_ = true; 297 gtk_check_menu_item_set_active( 298 GTK_CHECK_MENU_ITEM(it->second), 299 prefs->GetBoolean(prefs::kShowBookmarkBar)); 300 block_activation_ = false; 301 } 302 } 303 304 void GlobalMenuBar::OnItemActivated(GtkWidget* sender) { 305 if (block_activation_) 306 return; 307 308 int id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(sender), "command-id")); 309 chrome::ExecuteCommand(browser_, id); 310 } 311