| OLD | NEW |
| (Empty) | |
| 1 // Copyright (c) 2011 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_bookmark_menu.h" |
| 6 |
| 7 #include <gtk/gtk.h> |
| 8 |
| 9 #include "base/logging.h" |
| 10 #include "base/utf_string_conversions.h" |
| 11 #include "chrome/browser/bookmarks/bookmark_model.h" |
| 12 #include "chrome/browser/profiles/profile.h" |
| 13 #include "chrome/browser/ui/browser.h" |
| 14 #include "chrome/browser/ui/gtk/global_bookmark_menu.h" |
| 15 #include "chrome/browser/ui/gtk/global_menu_bar.h" |
| 16 #include "chrome/browser/ui/gtk/gtk_theme_service.h" |
| 17 #include "chrome/browser/ui/gtk/gtk_util.h" |
| 18 #include "content/common/notification_service.h" |
| 19 #include "grit/generated_resources.h" |
| 20 #include "ui/base/l10n/l10n_util.h" |
| 21 #include "ui/gfx/gtk_util.h" |
| 22 |
| 23 namespace { |
| 24 |
| 25 const int kMaxChars = 50; |
| 26 |
| 27 } // namespace |
| 28 |
| 29 GlobalBookmarkMenu::GlobalBookmarkMenu(Browser* browser) |
| 30 : browser_(browser), |
| 31 profile_(browser->profile()), |
| 32 default_favicon_(NULL), |
| 33 ALLOW_THIS_IN_INITIALIZER_LIST(method_factory_(this)) { |
| 34 DCHECK(profile_); |
| 35 |
| 36 default_favicon_ = GtkThemeService::GetDefaultFavicon(true); |
| 37 default_folder_ = GtkThemeService::GetFolderIcon(true); |
| 38 registrar_.Add(this, NotificationType::BROWSER_THEME_CHANGED, |
| 39 Source<Profile>(profile_)); |
| 40 } |
| 41 |
| 42 GlobalBookmarkMenu::~GlobalBookmarkMenu() { |
| 43 profile_->GetBookmarkModel()->RemoveObserver(this); |
| 44 } |
| 45 |
| 46 void GlobalBookmarkMenu::Init(GtkWidget* bookmark_menu) { |
| 47 bookmark_menu_ = bookmark_menu; |
| 48 |
| 49 BookmarkModel* model = profile_->GetBookmarkModel(); |
| 50 model->AddObserver(this); |
| 51 if (model->IsLoaded()) |
| 52 Loaded(model); |
| 53 } |
| 54 |
| 55 void GlobalBookmarkMenu::RebuildMenuInFuture() { |
| 56 method_factory_.RevokeAll(); |
| 57 MessageLoop::current()->PostTask( |
| 58 FROM_HERE, |
| 59 method_factory_.NewRunnableMethod(&GlobalBookmarkMenu::RebuildMenu)); |
| 60 } |
| 61 |
| 62 void GlobalBookmarkMenu::RebuildMenu() { |
| 63 BookmarkModel* model = profile_->GetBookmarkModel(); |
| 64 DCHECK(model); |
| 65 DCHECK(model->IsLoaded()); |
| 66 |
| 67 ClearBookmarkMenu(); |
| 68 |
| 69 const BookmarkNode* bar_node = model->GetBookmarkBarNode(); |
| 70 if (bar_node->child_count()) { |
| 71 AddBookmarkMenuItem(bookmark_menu_, gtk_separator_menu_item_new()); |
| 72 AddNodeToMenu(bar_node, bookmark_menu_); |
| 73 } |
| 74 |
| 75 // Only display the other bookmarks folder in the menu if it has items in it. |
| 76 const BookmarkNode* other_node = model->other_node(); |
| 77 if (other_node->child_count()) { |
| 78 GtkWidget* submenu = gtk_menu_new(); |
| 79 AddNodeToMenu(other_node, submenu); |
| 80 |
| 81 AddBookmarkMenuItem(bookmark_menu_, gtk_separator_menu_item_new()); |
| 82 |
| 83 GtkWidget* menu_item = gtk_image_menu_item_new_with_label( |
| 84 l10n_util::GetStringUTF8(IDS_BOOMARK_BAR_OTHER_FOLDER_NAME).c_str()); |
| 85 gtk_menu_item_set_submenu(GTK_MENU_ITEM(menu_item), submenu); |
| 86 gtk_image_menu_item_set_image( |
| 87 GTK_IMAGE_MENU_ITEM(menu_item), |
| 88 gtk_image_new_from_pixbuf(default_folder_)); |
| 89 |
| 90 AddBookmarkMenuItem(bookmark_menu_, menu_item); |
| 91 } |
| 92 } |
| 93 |
| 94 void GlobalBookmarkMenu::AddBookmarkMenuItem(GtkWidget* menu, |
| 95 GtkWidget* menu_item) { |
| 96 g_object_set_data(G_OBJECT(menu_item), "type-tag", |
| 97 GINT_TO_POINTER(GlobalMenuBar::TAG_BOOKMARK_CLEARABLE)); |
| 98 gtk_menu_shell_append(GTK_MENU_SHELL(menu), menu_item); |
| 99 gtk_widget_show(menu_item); |
| 100 } |
| 101 |
| 102 void GlobalBookmarkMenu::AddNodeToMenu(const BookmarkNode* node, |
| 103 GtkWidget* menu) { |
| 104 int child_count = node->child_count(); |
| 105 if (!child_count) { |
| 106 GtkWidget* item = gtk_menu_item_new_with_label( |
| 107 l10n_util::GetStringUTF8(IDS_MENU_EMPTY_SUBMENU).c_str()); |
| 108 gtk_widget_set_sensitive(item, FALSE); |
| 109 AddBookmarkMenuItem(menu, item); |
| 110 } else { |
| 111 for (int i = 0; i < child_count; i++) { |
| 112 const BookmarkNode* child = node->GetChild(i); |
| 113 GtkWidget* item = gtk_image_menu_item_new(); |
| 114 ConfigureMenuItem(child, item); |
| 115 bookmark_nodes_[child] = item; |
| 116 |
| 117 if (child->is_folder()) { |
| 118 GtkWidget* submenu = gtk_menu_new(); |
| 119 AddNodeToMenu(child, submenu); |
| 120 gtk_menu_item_set_submenu(GTK_MENU_ITEM(item), submenu); |
| 121 } else { |
| 122 g_object_set_data(G_OBJECT(item), "bookmark-node", |
| 123 const_cast<BookmarkNode*>(child)); |
| 124 g_signal_connect(item, "activate", |
| 125 G_CALLBACK(OnBookmarkItemActivatedThunk), this); |
| 126 } |
| 127 |
| 128 AddBookmarkMenuItem(menu, item); |
| 129 } |
| 130 } |
| 131 } |
| 132 |
| 133 void GlobalBookmarkMenu::ConfigureMenuItem(const BookmarkNode* node, |
| 134 GtkWidget* menu_item) { |
| 135 string16 elided_name = l10n_util::TruncateString(node->GetTitle(), kMaxChars); |
| 136 gtk_menu_item_set_label(GTK_MENU_ITEM(menu_item), |
| 137 UTF16ToUTF8(elided_name).c_str()); |
| 138 |
| 139 if (node->is_url()) { |
| 140 std::string tooltip = gtk_util::BuildTooltipTitleFor(node->GetTitle(), |
| 141 node->GetURL()); |
| 142 gtk_widget_set_tooltip_markup(menu_item, tooltip.c_str()); |
| 143 } |
| 144 |
| 145 const SkBitmap& bitmap = profile_->GetBookmarkModel()->GetFavicon(node); |
| 146 if (!bitmap.isNull()) { |
| 147 GdkPixbuf* pixbuf = gfx::GdkPixbufFromSkBitmap(&bitmap); |
| 148 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item), |
| 149 gtk_image_new_from_pixbuf(pixbuf)); |
| 150 g_object_unref(pixbuf); |
| 151 } else { |
| 152 gtk_image_menu_item_set_image(GTK_IMAGE_MENU_ITEM(menu_item), |
| 153 gtk_image_new_from_pixbuf( |
| 154 node->is_url() ? default_favicon_ : |
| 155 default_folder_)); |
| 156 } |
| 157 } |
| 158 |
| 159 GtkWidget* GlobalBookmarkMenu::MenuItemForNode(const BookmarkNode* node) { |
| 160 if (!node) |
| 161 return NULL; |
| 162 std::map<const BookmarkNode*, GtkWidget*>::iterator it = |
| 163 bookmark_nodes_.find(node); |
| 164 if (it == bookmark_nodes_.end()) |
| 165 return NULL; |
| 166 return it->second; |
| 167 } |
| 168 |
| 169 void GlobalBookmarkMenu::ClearBookmarkMenu() { |
| 170 bookmark_nodes_.clear(); |
| 171 |
| 172 gtk_container_foreach(GTK_CONTAINER(bookmark_menu_), |
| 173 &ClearBookmarkItemCallback, |
| 174 NULL); |
| 175 } |
| 176 |
| 177 // static |
| 178 void GlobalBookmarkMenu::ClearBookmarkItemCallback(GtkWidget* menu_item, |
| 179 void* unused) { |
| 180 int tag = GPOINTER_TO_INT(g_object_get_data(G_OBJECT(menu_item), "type-tag")); |
| 181 if (tag == GlobalMenuBar::TAG_BOOKMARK_CLEARABLE) |
| 182 gtk_widget_destroy(menu_item); |
| 183 } |
| 184 |
| 185 void GlobalBookmarkMenu::Observe(NotificationType type, |
| 186 const NotificationSource& source, |
| 187 const NotificationDetails& details) { |
| 188 DCHECK(type.value == NotificationType::BROWSER_THEME_CHANGED); |
| 189 |
| 190 // Change the icon and invalidate the menu. |
| 191 default_favicon_ = GtkThemeService::GetDefaultFavicon(true); |
| 192 default_folder_ = GtkThemeService::GetFolderIcon(true); |
| 193 RebuildMenuInFuture(); |
| 194 } |
| 195 |
| 196 void GlobalBookmarkMenu::Loaded(BookmarkModel* model) { |
| 197 // If we have a Loaded() event, then we need to build the menu immediately |
| 198 // for the first time. |
| 199 RebuildMenu(); |
| 200 } |
| 201 |
| 202 void GlobalBookmarkMenu::BookmarkModelBeingDeleted(BookmarkModel* model) { |
| 203 ClearBookmarkMenu(); |
| 204 } |
| 205 |
| 206 void GlobalBookmarkMenu::BookmarkNodeMoved(BookmarkModel* model, |
| 207 const BookmarkNode* old_parent, |
| 208 int old_index, |
| 209 const BookmarkNode* new_parent, |
| 210 int new_index) { |
| 211 RebuildMenuInFuture(); |
| 212 } |
| 213 |
| 214 void GlobalBookmarkMenu::BookmarkNodeAdded(BookmarkModel* model, |
| 215 const BookmarkNode* parent, |
| 216 int index) { |
| 217 RebuildMenuInFuture(); |
| 218 } |
| 219 |
| 220 void GlobalBookmarkMenu::BookmarkNodeRemoved(BookmarkModel* model, |
| 221 const BookmarkNode* parent, |
| 222 int old_index, |
| 223 const BookmarkNode* node) { |
| 224 GtkWidget* item = MenuItemForNode(node); |
| 225 if (item) { |
| 226 gtk_widget_destroy(item); |
| 227 bookmark_nodes_.erase(node); |
| 228 } |
| 229 } |
| 230 |
| 231 void GlobalBookmarkMenu::BookmarkNodeChanged(BookmarkModel* model, |
| 232 const BookmarkNode* node) { |
| 233 GtkWidget* item = MenuItemForNode(node); |
| 234 if (item) |
| 235 ConfigureMenuItem(node, item); |
| 236 } |
| 237 |
| 238 void GlobalBookmarkMenu::BookmarkNodeFaviconLoaded(BookmarkModel* model, |
| 239 const BookmarkNode* node) { |
| 240 GtkWidget* item = MenuItemForNode(node); |
| 241 if (item) |
| 242 ConfigureMenuItem(node, item); |
| 243 } |
| 244 |
| 245 void GlobalBookmarkMenu::BookmarkNodeChildrenReordered( |
| 246 BookmarkModel* model, |
| 247 const BookmarkNode* node) { |
| 248 RebuildMenuInFuture(); |
| 249 } |
| 250 |
| 251 void GlobalBookmarkMenu::OnBookmarkItemActivated(GtkWidget* menu_item) { |
| 252 // The actual mouse event that generated this activated event was in a |
| 253 // different process. Go with something default. |
| 254 const BookmarkNode* node = static_cast<const BookmarkNode*>( |
| 255 g_object_get_data(G_OBJECT(menu_item), "bookmark-node")); |
| 256 |
| 257 browser_->OpenURL(node->GetURL(), GURL(), NEW_FOREGROUND_TAB, |
| 258 PageTransition::AUTO_BOOKMARK); |
| 259 } |
| 260 |
| OLD | NEW |