Chromium Code Reviews| Index: chrome/browser/ui/gtk/global_menu_bar.cc |
| diff --git a/chrome/browser/ui/gtk/global_menu_bar.cc b/chrome/browser/ui/gtk/global_menu_bar.cc |
| index 6442e41dce300efbc8a7f7549a03538f1812bba8..cd2c924953eaae2ecbfabb9c647307589bcd2068 100644 |
| --- a/chrome/browser/ui/gtk/global_menu_bar.cc |
| +++ b/chrome/browser/ui/gtk/global_menu_bar.cc |
| @@ -6,26 +6,63 @@ |
| #include <gtk/gtk.h> |
| +#include "base/utf_string_conversions.h" |
| +#include "base/stl_util-inl.h" |
| +#include "base/string_number_conversions.h" |
| #include "chrome/app/chrome_command_ids.h" |
| +#include "chrome/browser/favicon_service.h" |
| #include "chrome/browser/prefs/pref_service.h" |
| #include "chrome/browser/profiles/profile.h" |
| #include "chrome/browser/ui/browser.h" |
| +#include "chrome/browser/ui/browser_tab_restore_service_delegate.h" |
| #include "chrome/browser/ui/gtk/accelerators_gtk.h" |
| +#include "chrome/browser/ui/gtk/gtk_util.h" |
| +#include "chrome/browser/ui/gtk/gtk_theme_service.h" |
| +#include "chrome/browser/ui/gtk/owned_widget_gtk.h" |
| #include "chrome/common/pref_names.h" |
| +#include "chrome/common/url_constants.h" |
| #include "content/common/notification_service.h" |
| #include "grit/generated_resources.h" |
| #include "ui/base/l10n/l10n_util.h" |
| +#include "ui/base/text/text_elider.h" |
| +#include "ui/gfx/codec/png_codec.h" |
| #include "ui/gfx/gtk_util.h" |
| struct GlobalMenuBarCommand { |
| int str_id; |
| int command; |
| + int tag; |
| +}; |
| + |
| +struct GlobalMenuBar::ClearMenuClosure { |
| + GtkWidget* container; |
| + GlobalMenuBar* menu_bar; |
| + int tag; |
| +}; |
| + |
| +struct GlobalMenuBar::GetIndexClosure { |
| + bool found; |
| + int current_index; |
| + int tag; |
| }; |
| namespace { |
| const int MENU_SEPARATOR =-1; |
| const int MENU_END = -2; |
| +const int MENU_DISABLED_LABEL = -3; |
| + |
| +const int TAG_NORMAL = 0; |
| +const int TAG_MOST_VISITED = 1; |
| +const int TAG_RECENTLY_CLOSED = 2; |
| +const int TAG_MOST_VISITED_HEADER = 3; |
| +const int TAG_RECENTLY_CLOSED_HEADER = 4; |
| + |
| +// The number of recently closed items to get. |
| +const unsigned int kRecentlyClosedCount = 10; |
| + |
| +// Menus more than this many chars long will get trimmed. |
| +const int kMaximumMenuWidthInChars = 50; |
| GlobalMenuBarCommand file_menu[] = { |
| { IDS_NEW_TAB, IDC_NEW_TAB }, |
| @@ -99,6 +136,45 @@ GlobalMenuBarCommand view_menu[] = { |
| { MENU_END, MENU_END } |
| }; |
| +GlobalMenuBarCommand history_menu[] = { |
| + { IDS_HISTORY_HOME_LINUX, IDC_HOME }, |
| + { IDS_HISTORY_BACK_LINUX, IDC_BACK }, |
| + { IDS_HISTORY_FORWARD_LINUX, IDC_FORWARD }, |
| + |
| + { MENU_SEPARATOR, MENU_SEPARATOR }, |
| + |
| + { IDS_HISTORY_VISITED_LINUX, MENU_DISABLED_LABEL, TAG_MOST_VISITED_HEADER }, |
| + |
| + { MENU_SEPARATOR, MENU_SEPARATOR }, |
| + |
| + { IDS_HISTORY_CLOSED_LINUX, MENU_DISABLED_LABEL, TAG_RECENTLY_CLOSED_HEADER }, |
| + |
| + { MENU_SEPARATOR, MENU_SEPARATOR }, |
| + |
| + { IDS_SHOWFULLHISTORY_LINK, IDC_SHOW_HISTORY }, |
| + |
| + { MENU_END, MENU_END } |
| +}; |
| + |
| +GlobalMenuBarCommand bookmark_menu[] = { |
| + { IDS_BOOKMARK_MANAGER, IDC_SHOW_BOOKMARK_MANAGER }, |
| + { IDS_BOOKMARK_CURRENT_PAGE_LINUX, IDC_BOOKMARK_PAGE }, |
| + { IDS_BOOKMARK_ALL_TABS_LINUX, IDC_BOOKMARK_ALL_TABS }, |
| + |
| + { MENU_SEPARATOR, MENU_SEPARATOR }, |
| + // TODO(erg): Real implementation of bookmark bar bookmarks! |
| + { MENU_SEPARATOR, MENU_SEPARATOR }, |
| + |
| + { IDS_BOOMARK_BAR_OPEN_ALL, IDC_BOOKMARK_BAR_OPEN_ALL }, |
| + { IDS_BOOMARK_BAR_OPEN_ALL_NEW_WINDOW, IDC_BOOKMARK_BAR_OPEN_ALL_NEW_WINDOW }, |
| + { IDS_BOOMARK_BAR_OPEN_ALL_INCOGNITO, IDC_BOOKMARK_BAR_OPEN_ALL_INCOGNITO }, |
| + |
| + { MENU_SEPARATOR, MENU_SEPARATOR }, |
| + // TODO(erg): "Other bookmarks" bookmarks |
| + |
| + { MENU_END, MENU_END } |
| +}; |
| + |
| GlobalMenuBarCommand tools_menu[] = { |
| { IDS_SHOW_DOWNLOADS, IDC_SHOW_DOWNLOADS }, |
| { IDS_SHOW_HISTORY, IDC_SHOW_HISTORY }, |
| @@ -126,28 +202,77 @@ GlobalMenuBarCommand help_menu[] = { |
| } // namespace |
| -GlobalMenuBar::GlobalMenuBar(Browser* browser, |
| - BrowserWindowGtk* window) |
| +class GlobalMenuBar::HistoryItem { |
| + public: |
| + HistoryItem() |
| + : icon_requested(false), |
| + menu_item(NULL), |
| + session_id(0) {} |
| + |
| + // The title for the menu item. |
| + string16 title; |
| + // The URL that will be navigated to if the user selects this item. |
| + GURL url; |
| + |
| + // If the icon is being requested from the FaviconService, |icon_requested| |
| + // will be true and |icon_handle| will be non-NULL. If this is false, then |
| + // |icon_handle| will be NULL. |
| + bool icon_requested; |
| + // The Handle given to us by the FaviconService for the icon fetch request. |
| + FaviconService::Handle icon_handle; |
| + |
| + // The icon as a GtkImage for inclusion in a GtkImageMenuItem. |
| + OwnedWidgetGtk icon_image; |
| + |
| + // A pointer to the menu_item. This is a weak reference in the GTK+ version |
| + // because the GtkMenu must sink the reference. |
| + GtkWidget* menu_item; |
| + |
| + // This ID is unique for a browser session and can be passed to the |
| + // TabRestoreService to re-open the closed window or tab that this |
| + // references. A non-0 session ID indicates that this is an entry can be |
| + // restored that way. Otherwise, the URL will be used to open the item and |
| + // this ID will be 0. |
| + SessionID::id_type session_id; |
| + |
| + // If the HistoryItem is a window, this will be the vector of tabs. Note |
| + // that this is a list of weak references. The |menu_item_map_| is the owner |
| + // of all items. If it is not a window, then the entry is a single page and |
| + // the vector will be empty. |
| + std::vector<HistoryItem*> tabs; |
| + |
| + private: |
| + DISALLOW_COPY_AND_ASSIGN(HistoryItem); |
| +}; |
| + |
| + |
|
Evan Stade
2011/04/15 19:00:41
extra line
|
| +GlobalMenuBar::GlobalMenuBar(Browser* browser) |
| : browser_(browser), |
| - browser_window_(window), |
| + profile_(browser_->profile()), |
| + default_favicon_(NULL), |
| menu_bar_(gtk_menu_bar_new()), |
| dummy_accel_group_(gtk_accel_group_new()), |
| - block_activation_(false) { |
| + block_activation_(false), |
| + history_menu_(NULL), |
| + tab_restore_service_(NULL) { |
| // The global menu bar should never actually be shown in the app; it should |
| // instead remain in our widget hierarchy simply to be noticed by third party |
| // components. |
| - gtk_widget_set_no_show_all(menu_bar_, TRUE); |
| + gtk_widget_set_no_show_all(menu_bar_.get(), TRUE); |
| // Set a nice name so it shows up in gtkparasite and others. |
| - gtk_widget_set_name(menu_bar_, "chrome-hidden-global-menubar"); |
| + gtk_widget_set_name(menu_bar_.get(), "chrome-hidden-global-menubar"); |
| BuildGtkMenuFrom(IDS_FILE_MENU_LINUX, &id_to_menu_item_, file_menu); |
| BuildGtkMenuFrom(IDS_EDIT_MENU_LINUX, &id_to_menu_item_, edit_menu); |
| BuildGtkMenuFrom(IDS_VIEW_MENU_LINUX, &id_to_menu_item_, view_menu); |
| + history_menu_ = BuildGtkMenuFrom(IDS_HISTORY_MENU_LINUX, &id_to_menu_item_, |
| + history_menu); |
| + BuildGtkMenuFrom(IDS_BOOKMARKS_MENU_LINUX, &id_to_menu_item_, bookmark_menu); |
| BuildGtkMenuFrom(IDS_TOOLS_MENU_LINUX, &id_to_menu_item_, tools_menu); |
| BuildGtkMenuFrom(IDS_HELP_MENU_LINUX, &id_to_menu_item_, help_menu); |
| - for (IDMenuItemMap::const_iterator it = id_to_menu_item_.begin(); |
| + for (CommandIDMenuItemMap::const_iterator it = id_to_menu_item_.begin(); |
| it != id_to_menu_item_.end(); ++it) { |
| // Get the starting enabled state. |
| gtk_widget_set_sensitive( |
| @@ -170,6 +295,24 @@ GlobalMenuBar::GlobalMenuBar(Browser* browser, |
| browser_->command_updater()->AddCommandObserver(it->first, this); |
| } |
| + default_favicon_ = GtkThemeService::GetDefaultFavicon(true); |
| + |
| + if (profile_) { |
| + tab_restore_service_ = profile_->GetTabRestoreService(); |
| + if (tab_restore_service_) { |
| + tab_restore_service_->LoadTabsFromLastSession(); |
| + tab_restore_service_->AddObserver(this); |
| + |
| + // If LoadTabsFromLastSession doesn't load tabs, it won't call |
| + // TabRestoreServiceChanged(). This ensures that all new windows after |
| + // the first one will have their menus populated correctly. |
| + TabRestoreServiceChanged(tab_restore_service_); |
| + } |
| + |
| + registrar_.Add(this, NotificationType::BROWSER_THEME_CHANGED, |
| + Source<Profile>(profile_)); |
| + } |
| + |
| // Listen for bookmark bar visibility changes and set the initial state. |
| registrar_.Add(this, NotificationType::BOOKMARK_BAR_VISIBILITY_PREF_CHANGED, |
| NotificationService::AllSources()); |
| @@ -179,56 +322,265 @@ GlobalMenuBar::GlobalMenuBar(Browser* browser, |
| } |
| GlobalMenuBar::~GlobalMenuBar() { |
| - for (IDMenuItemMap::const_iterator it = id_to_menu_item_.begin(); |
| + for (CommandIDMenuItemMap::const_iterator it = id_to_menu_item_.begin(); |
| it != id_to_menu_item_.end(); ++it) { |
| browser_->command_updater()->RemoveCommandObserver(it->first, this); |
| } |
| + if (tab_restore_service_) |
| + tab_restore_service_->RemoveObserver(this); |
| + |
| + STLDeleteContainerPairSecondPointers(menu_item_history_map_.begin(), |
| + menu_item_history_map_.end()); |
| + menu_item_history_map_.clear(); |
| + |
| g_object_unref(dummy_accel_group_); |
| } |
| -void GlobalMenuBar::BuildGtkMenuFrom(int menu_str_id, |
| - std::map<int, GtkWidget*>* id_to_menu_item, |
| - GlobalMenuBarCommand* commands) { |
| +GtkWidget* GlobalMenuBar::BuildGtkMenuFrom( |
| + int menu_str_id, |
| + std::map<int, GtkWidget*>* id_to_menu_item, |
| + GlobalMenuBarCommand* commands) { |
| GtkWidget* menu = gtk_menu_new(); |
| for (int i = 0; commands[i].str_id != MENU_END; ++i) { |
| - GtkWidget* menu_item = NULL; |
| - if (commands[i].str_id == MENU_SEPARATOR) { |
| - menu_item = gtk_separator_menu_item_new(); |
| - } else { |
| - int command_id = commands[i].command; |
| - std::string label = |
| - gfx::ConvertAcceleratorsFromWindowsStyle( |
| - l10n_util::GetStringUTF8(commands[i].str_id)); |
| + GtkWidget* menu_item = BuildMenuItem( |
| + commands[i].str_id, commands[i].command, commands[i].tag, |
| + id_to_menu_item, menu); |
| + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item); |
| + } |
| + |
| + gtk_widget_show(menu); |
| + |
| + GtkWidget* menu_item = gtk_menu_item_new_with_mnemonic( |
| + gfx::ConvertAcceleratorsFromWindowsStyle( |
| + l10n_util::GetStringUTF8(menu_str_id)).c_str()); |
| + gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), menu); |
| + gtk_widget_show(menu_item); |
| + gtk_menu_shell_append(GTK_MENU_SHELL(menu_bar_.get()), menu_item); |
| - if (command_id == IDC_SHOW_BOOKMARK_BAR) |
| - menu_item = gtk_check_menu_item_new_with_mnemonic(label.c_str()); |
| - else |
| - menu_item = gtk_menu_item_new_with_mnemonic(label.c_str()); |
| + return menu; |
| +} |
| +GtkWidget* GlobalMenuBar::BuildMenuItem( |
| + int string_id, |
| + int command_id, |
| + int tag_id, |
| + std::map<int, GtkWidget*>* id_to_menu_item, |
| + GtkWidget* menu_to_add_to) { |
| + GtkWidget* menu_item = NULL; |
| + if (string_id == MENU_SEPARATOR) { |
| + menu_item = gtk_separator_menu_item_new(); |
| + } else { |
| + std::string label = |
| + gfx::ConvertAcceleratorsFromWindowsStyle( |
| + l10n_util::GetStringUTF8(string_id)); |
| + |
| + if (command_id == IDC_SHOW_BOOKMARK_BAR) |
| + menu_item = gtk_check_menu_item_new_with_mnemonic(label.c_str()); |
| + else |
| + menu_item = gtk_menu_item_new_with_mnemonic(label.c_str()); |
| + |
| + if (tag_id) { |
| + g_object_set_data(G_OBJECT(menu_item), "type-tag", |
| + GINT_TO_POINTER(tag_id)); |
| + } |
| + |
| + if (command_id == MENU_DISABLED_LABEL) { |
| + gtk_widget_set_sensitive(menu_item, FALSE); |
| + } else { |
| id_to_menu_item->insert(std::make_pair(command_id, menu_item)); |
| g_object_set_data(G_OBJECT(menu_item), "command-id", |
| GINT_TO_POINTER(command_id)); |
| g_signal_connect(menu_item, "activate", |
| G_CALLBACK(OnItemActivatedThunk), this); |
| } |
| - gtk_widget_show(menu_item); |
| - gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item); |
| } |
| + gtk_widget_show(menu_item); |
| + return menu_item; |
| +} |
| - gtk_widget_show(menu); |
| +GlobalMenuBar::HistoryItem* GlobalMenuBar::HistoryItemForMenuItem( |
| + GtkWidget* menu_item) { |
| + MenuItemToHistoryMap::iterator it = menu_item_history_map_.find(menu_item); |
| + if (it != menu_item_history_map_.end()) { |
|
Evan Stade
2011/04/15 19:00:41
nit: return it != menu_item_history_map_.end() ? i
|
| + return it->second; |
| + } |
| + return NULL; |
| +} |
| - GtkWidget* menu_item = gtk_menu_item_new_with_mnemonic( |
| - gfx::ConvertAcceleratorsFromWindowsStyle( |
| - l10n_util::GetStringUTF8(menu_str_id)).c_str()); |
| - gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), menu); |
| +GlobalMenuBar::HistoryItem* GlobalMenuBar::HistoryItemForTab( |
| + const TabRestoreService::Tab& entry) { |
| + if (entry.navigations.empty()) |
| + return NULL; |
| + |
| + const TabNavigation& current_navigation = |
| + entry.navigations.at(entry.current_navigation_index); |
| + if (current_navigation.virtual_url() == GURL(chrome::kChromeUINewTabURL)) |
| + return NULL; |
| + |
| + HistoryItem* item = new HistoryItem(); |
| + item->title = current_navigation.title(); |
| + item->url = current_navigation.virtual_url(); |
| + item->session_id = entry.id; |
| + |
| + // Tab navigations don't come with icons, so we always have to request them. |
| + GetFaviconForHistoryItem(item); |
| + |
| + return item; |
| + |
| +} |
| + |
| +GtkWidget* GlobalMenuBar::AddHistoryItemToMenu(HistoryItem* item, |
| + GtkWidget* menu, |
| + int tag, |
| + int index) { |
| + string16 title = item->title; |
| + std::string url_string = item->url.possibly_invalid_spec(); |
| + |
| + if (title.empty()) |
| + title = UTF8ToUTF16(url_string); |
| + ui::ElideString(title, kMaximumMenuWidthInChars, &title); |
| + |
| + GtkWidget* menu_item = gtk_image_menu_item_new_with_label( |
| + UTF16ToUTF8(title).c_str()); |
| + gtk_image_menu_item_set_always_show_image(GTK_IMAGE_MENU_ITEM(menu_item), |
| + TRUE); |
| + item->menu_item = menu_item; |
| gtk_widget_show(menu_item); |
| + g_object_set_data(G_OBJECT(menu_item), "type-tag", GINT_TO_POINTER(tag)); |
| + g_signal_connect(menu_item, "activate", |
| + G_CALLBACK(OnRecentlyClosedItemActivatedThunk), this); |
| + if (item->icon_image.get()) { |
| + gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item), |
| + item->icon_image.get()); |
| + } else if (!item->tabs.size()) { |
| + gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item), |
| + gtk_image_new_from_pixbuf(default_favicon_)); |
| + } |
| + |
| + std::string tooltip = gtk_util::BuildTooltipTitleFor(item->title, item->url); |
| + gtk_widget_set_tooltip_markup(menu_item, tooltip.c_str()); |
| + |
| + menu_item_history_map_.insert(std::make_pair(menu_item, item)); |
| + gtk_menu_shell_insert(GTK_MENU_SHELL(menu), menu_item, index); |
| + |
| + return menu_item; |
| +} |
| + |
| +void GlobalMenuBar::GetFaviconForHistoryItem(HistoryItem* item) { |
| + FaviconService* service = |
| + profile_->GetFaviconService(Profile::EXPLICIT_ACCESS); |
| + FaviconService::Handle handle = service->GetFaviconForURL( |
| + item->url, |
| + history::FAVICON, |
| + &favicon_consumer_, |
| + NewCallback(this, &GlobalMenuBar::GotFaviconData)); |
| + favicon_consumer_.SetClientData(service, handle, item); |
| + item->icon_handle = handle; |
| + item->icon_requested = true; |
| +} |
| - gtk_menu_shell_append(GTK_MENU_SHELL(menu_bar_), menu_item); |
| +void GlobalMenuBar::GotFaviconData(FaviconService::Handle handle, |
| + history::FaviconData favicon) { |
| + HistoryItem* item = |
| + favicon_consumer_.GetClientData( |
| + profile_->GetFaviconService(Profile::EXPLICIT_ACCESS), handle); |
| + DCHECK(item); |
| + item->icon_requested = false; |
| + item->icon_handle = NULL; |
| + |
| + SkBitmap icon; |
| + if (favicon.is_valid() && |
| + gfx::PNGCodec::Decode(favicon.image_data->front(), |
| + favicon.image_data->size(), &icon)) { |
| + GdkPixbuf* pixbuf = gfx::GdkPixbufFromSkBitmap(&icon); |
| + if (pixbuf) { |
| + item->icon_image.Own(gtk_image_new_from_pixbuf(pixbuf)); |
| + g_object_unref(pixbuf); |
| + |
| + if (item->menu_item) { |
| + gtk_image_menu_item_set_image( |
| + GTK_IMAGE_MENU_ITEM(item->menu_item), |
| + item->icon_image.get()); |
| + } |
| + } |
| + } |
| +} |
| + |
| +void GlobalMenuBar::CancelFaviconRequest(HistoryItem* item) { |
| + DCHECK(item); |
| + if (item->icon_requested) { |
| + FaviconService* service = |
| + profile_->GetFaviconService(Profile::EXPLICIT_ACCESS); |
| + service->CancelRequest(item->icon_handle); |
| + item->icon_requested = false; |
| + item->icon_handle = NULL; |
| + } |
| +} |
| + |
| +int GlobalMenuBar::GetIndexOfMenuItemWithTag(GtkWidget* menu, int tag_id) { |
| + GetIndexClosure closure; |
| + closure.found = false; |
| + closure.current_index = 0; |
| + closure.tag = tag_id; |
| + |
| + gtk_container_foreach( |
| + GTK_CONTAINER(menu), |
| + reinterpret_cast<void (*)(GtkWidget*, void*)>(GetIndexCallback), |
| + &closure); |
| + |
| + return closure.current_index; |
| +} |
| + |
| +void GlobalMenuBar::ClearMenuSection(GtkWidget* menu, int tag) { |
| + ClearMenuClosure closure; |
| + closure.container = menu; |
| + closure.menu_bar = this; |
| + closure.tag = tag; |
| + |
| + gtk_container_foreach( |
| + GTK_CONTAINER(menu), |
| + reinterpret_cast<void (*)(GtkWidget*, void*)>(ClearMenuCallback), |
| + &closure); |
| +} |
| + |
| +// static |
| +void GlobalMenuBar::GetIndexCallback(GtkWidget* menu_item, |
| + GetIndexClosure* closure) { |
| + int tag = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(menu_item), "type-tag")); |
| + if (tag == closure->tag) |
| + closure->found = true; |
| + |
| + if (!closure->found) |
| + closure->current_index++; |
| +} |
| + |
| +// static |
| +void GlobalMenuBar::ClearMenuCallback(GtkWidget* menu_item, |
| + ClearMenuClosure* closure) { |
| + DCHECK_NE(closure->tag, 0); |
| + |
| + int tag = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(menu_item), "type-tag")); |
| + if (closure->tag == tag) { |
| + HistoryItem* item = closure->menu_bar->HistoryItemForMenuItem(menu_item); |
| + |
| + if (item) { |
| + closure->menu_bar->CancelFaviconRequest(item); |
| + closure->menu_bar->menu_item_history_map_.erase(menu_item); |
| + delete item; |
| + } |
| + |
| + GtkWidget* submenu = gtk_menu_item_get_submenu(GTK_MENU_ITEM(menu_item)); |
| + if (submenu) { |
|
Evan Stade
2011/04/15 19:00:41
nit: no curlies
|
| + closure->menu_bar->ClearMenuSection(submenu, closure->tag); |
| + } |
| + |
| + gtk_container_remove(GTK_CONTAINER(closure->container), menu_item); |
| + } |
| } |
| void GlobalMenuBar::EnabledStateChangedForCommand(int id, bool enabled) { |
| - IDMenuItemMap::iterator it = id_to_menu_item_.find(id); |
| + CommandIDMenuItemMap::iterator it = id_to_menu_item_.find(id); |
| if (it != id_to_menu_item_.end()) |
| gtk_widget_set_sensitive(it->second, enabled); |
| } |
| @@ -236,19 +588,140 @@ void GlobalMenuBar::EnabledStateChangedForCommand(int id, bool enabled) { |
| void GlobalMenuBar::Observe(NotificationType type, |
| const NotificationSource& source, |
| const NotificationDetails& details) { |
| - DCHECK(type.value == NotificationType::BOOKMARK_BAR_VISIBILITY_PREF_CHANGED); |
| - |
| - IDMenuItemMap::iterator it = id_to_menu_item_.find(IDC_SHOW_BOOKMARK_BAR); |
| - if (it != id_to_menu_item_.end()) { |
| - PrefService* prefs = browser_->profile()->GetPrefs(); |
| + if (type.value == NotificationType::BOOKMARK_BAR_VISIBILITY_PREF_CHANGED) { |
| + CommandIDMenuItemMap::iterator it = |
| + id_to_menu_item_.find(IDC_SHOW_BOOKMARK_BAR); |
| + if (it != id_to_menu_item_.end()) { |
| + PrefService* prefs = browser_->profile()->GetPrefs(); |
| + |
| + block_activation_ = true; |
| + gtk_check_menu_item_set_active( |
| + GTK_CHECK_MENU_ITEM(it->second), |
| + prefs->GetBoolean(prefs::kShowBookmarkBar)); |
| + block_activation_ = false; |
| + } |
| + } else if (type.value == NotificationType::BROWSER_THEME_CHANGED) { |
| + // Keeping track of which menu items have the default icon is going an |
| + // error-prone pain, so instead just store the new default favicon and |
| + // we'll update on the next menu change event. |
| + default_favicon_ = GtkThemeService::GetDefaultFavicon(true); |
| + } else { |
| + NOTREACHED(); |
| + } |
| +} |
| - block_activation_ = true; |
| - gtk_check_menu_item_set_active(GTK_CHECK_MENU_ITEM(it->second), |
| - prefs->GetBoolean(prefs::kShowBookmarkBar)); |
| - block_activation_ = false; |
| +void GlobalMenuBar::TabRestoreServiceChanged(TabRestoreService* service) { |
| + const TabRestoreService::Entries& entries = service->entries(); |
| + |
| + ClearMenuSection(history_menu_, TAG_RECENTLY_CLOSED); |
| + |
| + // We'll get the index the "Recently Closed" header. (This can vary depending |
| + // on the number of "Most Visited" items. |
| + int index = GetIndexOfMenuItemWithTag(history_menu_, |
| + TAG_RECENTLY_CLOSED_HEADER) + 1; |
| + |
| + unsigned int added_count = 0; |
| + for (TabRestoreService::Entries::const_iterator it = entries.begin(); |
| + it != entries.end() && added_count < kRecentlyClosedCount; ++it) { |
|
Evan Stade
2011/04/15 19:00:41
instead of added_count can we track the size of hi
Elliot Glaysher
2011/04/15 20:29:03
We can in this patch, but we won't be able to once
|
| + TabRestoreService::Entry* entry = *it; |
| + |
| + if (entry->type == TabRestoreService::WINDOW) { |
| + TabRestoreService::Window* entry_win = (TabRestoreService::Window*)entry; |
|
Evan Stade
2011/04/15 19:00:41
C++ style cast
|
| + std::vector<TabRestoreService::Tab>& tabs = entry_win->tabs; |
| + if (!tabs.size()) |
|
Evan Stade
2011/04/15 19:00:41
empty()
|
| + continue; |
| + |
| + // Create the item for the parent/window. Do not set the title yet |
| + // because the actual number of items that are in the menu will not be |
| + // known until things like the NTP are filtered out, which is done when |
| + // the tab items are actually created. |
| + HistoryItem* item = new HistoryItem(); |
| + item->session_id = entry_win->id; |
| + |
| + GtkWidget* submenu = gtk_menu_new(); |
| + |
| + GtkWidget* restore_item = gtk_menu_item_new_with_label( |
| + l10n_util::GetStringUTF8( |
| + IDS_HISTORY_CLOSED_RESTORE_WINDOW_LINUX).c_str()); |
| + g_object_set_data(G_OBJECT(restore_item), "type-tag", |
| + GINT_TO_POINTER(TAG_RECENTLY_CLOSED)); |
| + g_signal_connect(restore_item, "activate", |
| + G_CALLBACK(OnRecentlyClosedItemActivatedThunk), this); |
| + gtk_widget_show(restore_item); |
| + |
| + // The mac version of this code allows the user to click on the parent |
| + // menu item to have the same effect as clicking the restore window |
| + // submenu item. GTK+ helpfully activates a menu item when it shows a |
| + // submenu so toss that feature out. |
| + menu_item_history_map_.insert(std::make_pair(restore_item, item)); |
| + gtk_menu_shell_append(GTK_MENU_SHELL(submenu), restore_item); |
| + |
| + GtkWidget* separator = gtk_separator_menu_item_new(); |
| + gtk_widget_show(separator); |
| + gtk_menu_shell_append(GTK_MENU_SHELL(submenu), separator); |
| + |
| + // Loop over the window's tabs and add them to the submenu. |
| + int subindex = 2; |
| + int subadd_count = 0; |
| + std::vector<TabRestoreService::Tab>::const_iterator it; |
| + for (it = tabs.begin(); it != tabs.end(); ++it) { |
| + TabRestoreService::Tab tab = *it; |
| + HistoryItem* tab_item = HistoryItemForTab(tab); |
| + if (tab_item) { |
| + ++subadd_count; |
| + item->tabs.push_back(tab_item); |
| + AddHistoryItemToMenu(tab_item, submenu, TAG_RECENTLY_CLOSED, |
| + subindex++); |
| + } |
| + } |
| + |
| + // Sometimes it is possible for there to not be any subitems for a given |
| + // window; if that is the case, do not add the entry to the main menu. |
| + if (subadd_count) { |
|
Evan Stade
2011/04/15 19:00:41
use subindex > 2 instead?
|
| + // Now that the number of tabs that has been added is known, set the |
| + // title of the parent menu item. |
| + std::string title = |
| + (item->tabs.size() == 1) ? |
| + l10n_util::GetStringUTF8( |
| + IDS_NEW_TAB_RECENTLY_CLOSED_WINDOW_SINGLE) : |
| + l10n_util::GetStringFUTF8( |
| + IDS_NEW_TAB_RECENTLY_CLOSED_WINDOW_MULTIPLE, |
| + base::IntToString16(item->tabs.size())); |
| + |
| + // Create the menu item parent. Unlike mac, it's can't be activated. |
| + GtkWidget* parent_item = gtk_image_menu_item_new_with_label( |
| + title.c_str()); |
| + gtk_widget_show(parent_item); |
| + g_object_set_data(G_OBJECT(parent_item), "type-tag", |
| + GINT_TO_POINTER(TAG_RECENTLY_CLOSED)); |
| + gtk_menu_item_set_submenu(GTK_MENU_ITEM(parent_item), submenu); |
| + |
| + gtk_menu_shell_insert(GTK_MENU_SHELL(history_menu_), parent_item, |
| + index++); |
| + ++added_count; |
| + } else { |
| + // Clean up after this failed attempt to make a submenu. |
| + menu_item_history_map_.erase(restore_item); |
|
Evan Stade
2011/04/15 19:00:41
can we avoid adding it to the map in the first pla
|
| + g_object_ref_sink(submenu); |
| + g_object_unref(submenu); |
|
Evan Stade
2011/04/15 19:00:41
I think it would be a good idea to gtk_widget_dest
|
| + } |
| + } else if (entry->type == TabRestoreService::TAB) { |
| + TabRestoreService::Tab* tab = |
| + static_cast<TabRestoreService::Tab*>(entry); |
| + HistoryItem* item = HistoryItemForTab(*tab); |
| + if (item) { |
| + AddHistoryItemToMenu(item, history_menu_, TAG_RECENTLY_CLOSED, index++); |
| + ++added_count; |
| + } |
| + } |
| } |
| } |
| +void GlobalMenuBar::TabRestoreServiceDestroyed( |
| + TabRestoreService* service) { |
| + // Intentionally left blank. We hold a weak reference to the service. |
| +} |
| + |
| void GlobalMenuBar::OnItemActivated(GtkWidget* sender) { |
| if (block_activation_) |
| return; |
| @@ -256,3 +729,21 @@ void GlobalMenuBar::OnItemActivated(GtkWidget* sender) { |
| int id = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(sender), "command-id")); |
| browser_->ExecuteCommandIfEnabled(id); |
| } |
| + |
| +void GlobalMenuBar::OnRecentlyClosedItemActivated(GtkWidget* sender) { |
| + WindowOpenDisposition disposition = |
| + gtk_util::DispositionForCurrentButtonPressEvent(); |
| + HistoryItem* item = HistoryItemForMenuItem(sender); |
| + |
| + // If this item can be restored using TabRestoreService, do so. Otherwise, |
| + // just load the URL. |
| + TabRestoreService* service = browser_->profile()->GetTabRestoreService(); |
| + if (item->session_id && service) { |
| + service->RestoreEntryById(browser_->tab_restore_service_delegate(), |
| + item->session_id, false); |
| + } else { |
| + DCHECK(item->url.is_valid()); |
| + browser_->OpenURL(item->url, GURL(), disposition, |
| + PageTransition::AUTO_BOOKMARK); |
| + } |
| +} |