Chromium Code Reviews| Index: chrome/browser/ui/gtk/global_bookmarks_menu.cc |
| diff --git a/chrome/browser/ui/gtk/global_bookmarks_menu.cc b/chrome/browser/ui/gtk/global_bookmarks_menu.cc |
| new file mode 100644 |
| index 0000000000000000000000000000000000000000..c1d0de77cff1fffa23bd9e2fc1137e804455d7a5 |
| --- /dev/null |
| +++ b/chrome/browser/ui/gtk/global_bookmarks_menu.cc |
| @@ -0,0 +1,259 @@ |
| +// Copyright (c) 2011 The Chromium Authors. All rights reserved. |
| +// Use of this source code is governed by a BSD-style license that can be |
| +// found in the LICENSE file. |
| + |
| +#include "chrome/browser/ui/gtk/global_bookmarks_menu.h" |
| + |
| +#include <gtk/gtk.h> |
| + |
| +#include "base/logging.h" |
| +#include "base/utf_string_conversions.h" |
| +#include "chrome/browser/bookmarks/bookmark_model.h" |
| +#include "chrome/browser/profiles/profile.h" |
| +#include "chrome/browser/ui/browser.h" |
| +#include "chrome/browser/ui/gtk/global_bookmarks_menu.h" |
| +#include "chrome/browser/ui/gtk/global_menu_bar.h" |
| +#include "chrome/browser/ui/gtk/gtk_theme_service.h" |
| +#include "chrome/browser/ui/gtk/gtk_util.h" |
| +#include "content/common/notification_service.h" |
| +#include "grit/generated_resources.h" |
| +#include "ui/base/l10n/l10n_util.h" |
| +#include "ui/gfx/gtk_util.h" |
| + |
| +namespace { |
| + |
| +const int kMaxChars = 50; |
| + |
| +} // namespace |
| + |
| +GlobalBookmarksMenu::GlobalBookmarksMenu(Browser* browser) |
| + : browser_(browser), |
| + profile_(browser->profile()), |
| + default_favicon_(NULL) { |
| + DCHECK(profile_); |
| + |
| + // Move to Init(GtkWidget* bookmark_menu); |
| + default_favicon_ = GtkThemeService::GetDefaultFavicon(true); |
| + default_folder_ = GtkThemeService::GetFolderIcon(true); |
| + registrar_.Add(this, NotificationType::BROWSER_THEME_CHANGED, |
| + Source<Profile>(profile_)); |
| +} |
| + |
| +GlobalBookmarksMenu::~GlobalBookmarksMenu() { |
| + profile_->GetBookmarkModel()->RemoveObserver(this); |
| +} |
| + |
| +void GlobalBookmarksMenu::Init(GtkWidget* bookmark_menu) { |
| + bookmark_menu_ = bookmark_menu; |
| + |
| + BookmarkModel* model = profile_->GetBookmarkModel(); |
| + model->AddObserver(this); |
| + if (model->IsLoaded()) |
| + Loaded(model); |
| +} |
| + |
| +void GlobalBookmarksMenu::RebuildMenuInFuture() { |
| + menu_build_timer_.Stop(); |
| + menu_build_timer_.Start(base::TimeDelta::FromMilliseconds(10), this, |
| + &GlobalBookmarksMenu::RebuildMenu); |
| +} |
| + |
| +void GlobalBookmarksMenu::RebuildMenu() { |
| + BookmarkModel* model = profile_->GetBookmarkModel(); |
| + DCHECK(model); |
| + DCHECK(model->IsLoaded()); |
| + |
| + ClearBookmarkMenu(); |
| + |
| + const BookmarkNode* bar_node = model->GetBookmarkBarNode(); |
| + if (bar_node->child_count()) { |
| + AddBookmarkMenuItem(bookmark_menu_, gtk_separator_menu_item_new()); |
| + AddNodeToMenu(bar_node, bookmark_menu_); |
| + } |
| + |
| + // Only display the other bookmarks folder in the menu if it has items in it. |
|
Evan Stade
2011/05/10 17:09:18
wouldn't this make it impossible to add thing #1 t
Elliot Glaysher
2011/05/10 18:29:28
"Other Bookmarks" won't be shown when empty for lo
|
| + const BookmarkNode* other_node = model->other_node(); |
| + if (other_node->child_count()) { |
| + GtkWidget* submenu = gtk_menu_new(); |
| + AddNodeToMenu(other_node, submenu); |
| + |
| + AddBookmarkMenuItem(bookmark_menu_, gtk_separator_menu_item_new()); |
| + |
| + GtkWidget* menu_item = gtk_image_menu_item_new_with_label( |
| + l10n_util::GetStringUTF8(IDS_BOOMARK_BAR_OTHER_FOLDER_NAME).c_str()); |
| + gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), submenu); |
| + gtk_image_menu_item_set_image( |
| + GTK_IMAGE_MENU_ITEM(menu_item), |
| + gtk_image_new_from_pixbuf(default_folder_)); |
| + |
| + AddBookmarkMenuItem(bookmark_menu_, menu_item); |
| + } |
| +} |
| + |
| +void GlobalBookmarksMenu::AddBookmarkMenuItem(GtkWidget* menu, |
| + GtkWidget* menu_item) { |
| + g_object_set_data(G_OBJECT(menu_item), "type-tag", |
| + GINT_TO_POINTER(GlobalMenuBar::TAG_BOOKMARK_CLEARABLE)); |
| + gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item); |
| + gtk_widget_show(menu_item); |
| +} |
| + |
| +void GlobalBookmarksMenu::AddNodeToMenu(const BookmarkNode* node, |
| + GtkWidget* menu) { |
| + int child_count = node->child_count(); |
| + if (!child_count) { |
| + GtkWidget* item = gtk_menu_item_new_with_label( |
| + l10n_util::GetStringUTF8(IDS_MENU_EMPTY_SUBMENU).c_str()); |
| + gtk_widget_set_sensitive(item, FALSE); |
| + AddBookmarkMenuItem(menu, item); |
| + } else { |
| + for (int i = 0; i < child_count; i++) { |
| + const BookmarkNode* child = node->GetChild(i); |
| + GtkWidget* item = gtk_image_menu_item_new(); |
| + ConfigureMenuItem(child, item); |
| + bookmark_nodes_[child] = item; |
| + |
| + if (child->is_folder()) { |
| + GtkWidget* submenu = gtk_menu_new(); |
| + AddNodeToMenu(child, submenu); |
| + gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), submenu); |
| + } else { |
| + g_object_set_data(G_OBJECT(item), "bookmark-node", |
| + const_cast<BookmarkNode*>(child)); |
| + g_signal_connect(item, "activate", |
| + G_CALLBACK(OnBookmarkItemActivatedThunk), this); |
| + } |
| + |
| + AddBookmarkMenuItem(menu, item); |
| + } |
| + } |
| +} |
| + |
| +void GlobalBookmarksMenu::ConfigureMenuItem(const BookmarkNode* node, |
| + GtkWidget* menu_item) { |
| + string16 elided_name = l10n_util::TruncateString(node->GetTitle(), kMaxChars); |
| + gtk_menu_item_set_label(GTK_MENU_ITEM(menu_item), |
| + UTF16ToUTF8(elided_name).c_str()); |
| + |
| + if (node->is_url()) { |
| + std::string tooltip = gtk_util::BuildTooltipTitleFor(node->GetTitle(), |
| + node->GetURL()); |
| + gtk_widget_set_tooltip_markup(menu_item, tooltip.c_str()); |
| + } |
| + |
| + const SkBitmap& bitmap = profile_->GetBookmarkModel()->GetFavicon(node); |
| + if (!bitmap.isNull()) { |
| + GdkPixbuf* pixbuf = gfx::GdkPixbufFromSkBitmap(&bitmap); |
| + gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item), |
| + gtk_image_new_from_pixbuf(pixbuf)); |
| + g_object_unref(pixbuf); |
| + } else { |
| + gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item), |
| + gtk_image_new_from_pixbuf( |
| + node->is_url() ? default_favicon_ : |
| + default_folder_)); |
| + } |
| +} |
| + |
| +GtkWidget* GlobalBookmarksMenu::MenuItemForNode(const BookmarkNode* node) { |
| + if (!node) |
| + return NULL; |
| + std::map<const BookmarkNode*, GtkWidget*>::iterator it = |
| + bookmark_nodes_.find(node); |
| + if (it == bookmark_nodes_.end()) |
| + return NULL; |
| + return it->second; |
| +} |
| + |
| +void GlobalBookmarksMenu::ClearBookmarkMenu() { |
| + bookmark_nodes_.clear(); |
| + |
| + gtk_container_foreach(GTK_CONTAINER(bookmark_menu_), |
| + &ClearBookmarkItemCallback, |
| + NULL); |
| +} |
| + |
| +// static |
| +void GlobalBookmarksMenu::ClearBookmarkItemCallback(GtkWidget* menu_item, |
| + void* /*unused*/) { |
|
Evan Stade
2011/05/10 17:09:18
I don't think this is within style guidelines
|
| + int tag = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(menu_item), "type-tag")); |
| + if (tag == GlobalMenuBar::TAG_BOOKMARK_CLEARABLE) |
| + gtk_widget_destroy(menu_item); |
| +} |
| + |
| +void GlobalBookmarksMenu::Observe(NotificationType type, |
| + const NotificationSource& source, |
| + const NotificationDetails& details) { |
| + DCHECK(type.value == NotificationType::BROWSER_THEME_CHANGED); |
| + |
| + // Change the icon and invalidate the menu. |
| + default_favicon_ = GtkThemeService::GetDefaultFavicon(true); |
| + default_folder_ = GtkThemeService::GetFolderIcon(true); |
| + RebuildMenuInFuture(); |
| +} |
| + |
| +void GlobalBookmarksMenu::Loaded(BookmarkModel* model) { |
| + // If we have a Loaded() event, then we need to build the menu immediately |
| + // for the first time. |
| + RebuildMenu(); |
| +} |
| + |
| +void GlobalBookmarksMenu::BookmarkModelBeingDeleted(BookmarkModel* model) { |
| + ClearBookmarkMenu(); |
| +} |
| + |
| +void GlobalBookmarksMenu::BookmarkNodeMoved(BookmarkModel* model, |
| + const BookmarkNode* old_parent, |
| + int old_index, |
| + const BookmarkNode* new_parent, |
| + int new_index) { |
| + RebuildMenuInFuture(); |
|
Evan Stade
2011/05/10 17:09:18
when you get a batch of NodeMoved/Added calls, are
|
| +} |
| + |
| +void GlobalBookmarksMenu::BookmarkNodeAdded(BookmarkModel* model, |
| + const BookmarkNode* parent, |
| + int index) { |
| + RebuildMenuInFuture(); |
| +} |
| + |
| +void GlobalBookmarksMenu::BookmarkNodeRemoved(BookmarkModel* model, |
| + const BookmarkNode* parent, |
| + int old_index, |
| + const BookmarkNode* node) { |
| + GtkWidget* item = MenuItemForNode(node); |
| + if (item) { |
| + gtk_widget_destroy(item); |
| + bookmark_nodes_.erase(node); |
| + } |
| +} |
| + |
| +void GlobalBookmarksMenu::BookmarkNodeChanged(BookmarkModel* model, |
| + const BookmarkNode* node) { |
| + GtkWidget* item = MenuItemForNode(node); |
| + if (item) |
| + ConfigureMenuItem(node, item); |
| +} |
| + |
| +void GlobalBookmarksMenu::BookmarkNodeFaviconLoaded(BookmarkModel* model, |
| + const BookmarkNode* node) { |
| + GtkWidget* item = MenuItemForNode(node); |
| + if (item) |
| + ConfigureMenuItem(node, item); |
| +} |
| + |
| +void GlobalBookmarksMenu::BookmarkNodeChildrenReordered( |
| + BookmarkModel* model, |
| + const BookmarkNode* node) { |
| + RebuildMenuInFuture(); |
| +} |
| + |
| +void GlobalBookmarksMenu::OnBookmarkItemActivated(GtkWidget* menu_item) { |
| + // The actual mouse event that generated this activated event was in a |
| + // different process. Go with something default. |
| + const BookmarkNode* node = static_cast<const BookmarkNode*>( |
| + g_object_get_data(G_OBJECT(menu_item), "bookmark-node")); |
| + |
| + browser_->OpenURL(node->GetURL(), GURL(), NEW_FOREGROUND_TAB, |
| + PageTransition::AUTO_BOOKMARK); |
| +} |
| + |