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