Home | History | Annotate | Download | only in gtk
      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