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
28struct GlobalMenuBarCommand {
29  int str_id;
30  int command;
31  int tag;
32};
33
34namespace {
35
36const int MENU_SEPARATOR =-1;
37const int MENU_END = -2;
38const int MENU_DISABLED_LABEL = -3;
39
40GlobalMenuBarCommand 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
65GlobalMenuBarCommand 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
81GlobalMenuBarCommand 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
99GlobalMenuBarCommand 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
121GlobalMenuBarCommand 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
140GlobalMenuBarCommand 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
148GlobalMenuBar::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
202GlobalMenuBar::~GlobalMenuBar() {
203  Disable();
204  g_object_unref(dummy_accel_group_);
205  gtk_widget_destroy(menu_bar_);
206  g_object_unref(menu_bar_);
207}
208
209void 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
219void 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
248GtkWidget* 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
285void 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
291void 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
304void 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