[Metrics] Make MetricsStateManager take a callback param to check if UMA is enabled.
[chromium-blink-merge.git] / chrome / browser / ui / cocoa / bookmarks / bookmark_menu_bridge.mm
blob70734580fa75e41e9d808b51f914d434ebbbd428
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.
5 #import <AppKit/AppKit.h>
7 #include "base/strings/sys_string_conversions.h"
8 #include "chrome/app/chrome_command_ids.h"
9 #import "chrome/browser/app_controller_mac.h"
10 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
11 #include "chrome/browser/prefs/incognito_mode_prefs.h"
12 #include "chrome/browser/profiles/profile.h"
13 #include "chrome/browser/profiles/profile_manager.h"
14 #include "chrome/browser/ui/browser_list.h"
15 #include "chrome/browser/ui/cocoa/bookmarks/bookmark_menu_bridge.h"
16 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_menu_cocoa_controller.h"
17 #include "components/bookmarks/browser/bookmark_model.h"
18 #include "grit/generated_resources.h"
19 #include "grit/theme_resources.h"
20 #include "grit/ui_resources.h"
21 #include "ui/base/l10n/l10n_util.h"
22 #include "ui/base/resource/resource_bundle.h"
23 #include "ui/gfx/image/image.h"
25 BookmarkMenuBridge::BookmarkMenuBridge(Profile* profile, NSMenu* menu)
26     : menuIsValid_(false),
27       profile_(profile),
28       controller_([[BookmarkMenuCocoaController alloc] initWithBridge:this
29                                                               andMenu:menu]) {
30   if (GetBookmarkModel())
31     ObserveBookmarkModel();
34 BookmarkMenuBridge::~BookmarkMenuBridge() {
35   BookmarkModel* model = GetBookmarkModel();
36   if (model)
37     model->RemoveObserver(this);
38   [controller_ release];
41 NSMenu* BookmarkMenuBridge::BookmarkMenu() {
42   return [controller_ menu];
45 void BookmarkMenuBridge::BookmarkModelLoaded(BookmarkModel* model,
46                                              bool ids_reassigned) {
47   InvalidateMenu();
50 void BookmarkMenuBridge::UpdateMenu(NSMenu* bookmark_menu) {
51   UpdateMenuInternal(bookmark_menu, false);
54 void BookmarkMenuBridge::UpdateSubMenu(NSMenu* bookmark_menu) {
55   UpdateMenuInternal(bookmark_menu, true);
58 void BookmarkMenuBridge::UpdateMenuInternal(NSMenu* bookmark_menu,
59                                             bool is_submenu) {
60   DCHECK(bookmark_menu);
61   if (menuIsValid_)
62     return;
64   BookmarkModel* model = GetBookmarkModel();
65   if (!model || !model->loaded())
66     return;
68   if (!folder_image_) {
69     ResourceBundle& rb = ResourceBundle::GetSharedInstance();
70     folder_image_.reset(
71         rb.GetNativeImageNamed(IDR_BOOKMARK_BAR_FOLDER).CopyNSImage());
72   }
74   ClearBookmarkMenu(bookmark_menu);
76   // Add bookmark bar items, if any.
77   const BookmarkNode* barNode = model->bookmark_bar_node();
78   CHECK(barNode);
79   if (!barNode->empty()) {
80     [bookmark_menu addItem:[NSMenuItem separatorItem]];
81     AddNodeToMenu(barNode, bookmark_menu, !is_submenu);
82   }
84   // If the "Other Bookmarks" folder has any content, make a submenu for it and
85   // fill it in.
86   if (!model->other_node()->empty()) {
87     [bookmark_menu addItem:[NSMenuItem separatorItem]];
88     AddNodeAsSubmenu(bookmark_menu,
89                      model->other_node(),
90                      !is_submenu);
91   }
93   // If the "Mobile Bookmarks" folder has any content, make a submenu for it and
94   // fill it in.
95   if (!model->mobile_node()->empty()) {
96     // Add a separator if we did not already add one due to a non-empty
97     // "Other Bookmarks" folder.
98     if (model->other_node()->empty())
99       [bookmark_menu addItem:[NSMenuItem separatorItem]];
101     AddNodeAsSubmenu(bookmark_menu,
102                      model->mobile_node(),
103                      !is_submenu);
104   }
106   menuIsValid_ = true;
109 void BookmarkMenuBridge::BookmarkModelBeingDeleted(BookmarkModel* model) {
110   NSMenu* bookmark_menu = BookmarkMenu();
111   if (bookmark_menu == nil)
112     return;
114   ClearBookmarkMenu(bookmark_menu);
117 void BookmarkMenuBridge::BookmarkNodeMoved(BookmarkModel* model,
118                                            const BookmarkNode* old_parent,
119                                            int old_index,
120                                            const BookmarkNode* new_parent,
121                                            int new_index) {
122   InvalidateMenu();
125 void BookmarkMenuBridge::BookmarkNodeAdded(BookmarkModel* model,
126                                            const BookmarkNode* parent,
127                                            int index) {
128   InvalidateMenu();
131 void BookmarkMenuBridge::BookmarkNodeRemoved(
132     BookmarkModel* model,
133     const BookmarkNode* parent,
134     int old_index,
135     const BookmarkNode* node,
136     const std::set<GURL>& removed_urls) {
137   InvalidateMenu();
140 void BookmarkMenuBridge::BookmarkAllNodesRemoved(
141     BookmarkModel* model,
142     const std::set<GURL>& removed_urls) {
143   InvalidateMenu();
146 void BookmarkMenuBridge::BookmarkNodeChanged(BookmarkModel* model,
147                                              const BookmarkNode* node) {
148   NSMenuItem* item = MenuItemForNode(node);
149   if (item)
150     ConfigureMenuItem(node, item, true);
153 void BookmarkMenuBridge::BookmarkNodeFaviconChanged(BookmarkModel* model,
154                                                     const BookmarkNode* node) {
155   NSMenuItem* item = MenuItemForNode(node);
156   if (item)
157     ConfigureMenuItem(node, item, false);
160 void BookmarkMenuBridge::BookmarkNodeChildrenReordered(
161     BookmarkModel* model, const BookmarkNode* node) {
162   InvalidateMenu();
165 void BookmarkMenuBridge::ResetMenu() {
166   ClearBookmarkMenu(BookmarkMenu());
169 void BookmarkMenuBridge::BuildMenu() {
170   UpdateMenu(BookmarkMenu());
173 // Watch for changes.
174 void BookmarkMenuBridge::ObserveBookmarkModel() {
175   BookmarkModel* model = GetBookmarkModel();
176   model->AddObserver(this);
177   if (model->loaded())
178     BookmarkModelLoaded(model, false);
181 BookmarkModel* BookmarkMenuBridge::GetBookmarkModel() {
182   if (!profile_)
183     return NULL;
184   return BookmarkModelFactory::GetForProfile(profile_);
187 Profile* BookmarkMenuBridge::GetProfile() {
188   return profile_;
191 void BookmarkMenuBridge::ClearBookmarkMenu(NSMenu* menu) {
192   bookmark_nodes_.clear();
193   // Recursively delete all menus that look like a bookmark. Also delete all
194   // separator items since we explicitly add them back in. This deletes
195   // everything except the first item ("Add Bookmark...").
196   NSArray* items = [menu itemArray];
197   for (NSMenuItem* item in items) {
198     // Convention: items in the bookmark list which are bookmarks have
199     // an action of openBookmarkMenuItem:.  Also, assume all items
200     // with submenus are submenus of bookmarks.
201     if (([item action] == @selector(openBookmarkMenuItem:)) ||
202         ([item action] == @selector(openAllBookmarks:)) ||
203         ([item action] == @selector(openAllBookmarksNewWindow:)) ||
204         ([item action] == @selector(openAllBookmarksIncognitoWindow:)) ||
205         [item hasSubmenu] ||
206         [item isSeparatorItem]) {
207       // This will eventually [obj release] all its kids, if it has
208       // any.
209       [menu removeItem:item];
210     } else {
211       // Leave it alone.
212     }
213   }
216 void BookmarkMenuBridge::AddNodeAsSubmenu(NSMenu* menu,
217                                           const BookmarkNode* node,
218                                           bool add_extra_items) {
219   NSString* title = SysUTF16ToNSString(node->GetTitle());
220   NSMenuItem* items = [[[NSMenuItem alloc]
221                             initWithTitle:title
222                                    action:nil
223                             keyEquivalent:@""] autorelease];
224   [items setImage:folder_image_];
225   [menu addItem:items];
226   NSMenu* submenu = [[[NSMenu alloc] initWithTitle:title] autorelease];
227   [menu setSubmenu:submenu forItem:items];
228   AddNodeToMenu(node, submenu, add_extra_items);
231 // TODO(jrg): limit the number of bookmarks in the menubar?
232 void BookmarkMenuBridge::AddNodeToMenu(const BookmarkNode* node, NSMenu* menu,
233                                        bool add_extra_items) {
234   int child_count = node->child_count();
235   if (!child_count) {
236     NSString* empty_string = l10n_util::GetNSString(IDS_MENU_EMPTY_SUBMENU);
237     NSMenuItem* item =
238         [[[NSMenuItem alloc] initWithTitle:empty_string
239                                     action:nil
240                              keyEquivalent:@""] autorelease];
241     [menu addItem:item];
242   } else for (int i = 0; i < child_count; i++) {
243     const BookmarkNode* child = node->GetChild(i);
244     NSString* title = [BookmarkMenuCocoaController menuTitleForNode:child];
245     NSMenuItem* item =
246         [[[NSMenuItem alloc] initWithTitle:title
247                                     action:nil
248                              keyEquivalent:@""] autorelease];
249     [menu addItem:item];
250     bookmark_nodes_[child] = item;
251     if (child->is_folder()) {
252       [item setImage:folder_image_];
253       NSMenu* submenu = [[[NSMenu alloc] initWithTitle:title] autorelease];
254       [menu setSubmenu:submenu forItem:item];
255       AddNodeToMenu(child, submenu, add_extra_items);  // recursive call
256     } else {
257       ConfigureMenuItem(child, item, false);
258     }
259   }
261   if (add_extra_items) {
262     // Add menus for 'Open All Bookmarks'.
263     [menu addItem:[NSMenuItem separatorItem]];
264     bool enabled = child_count != 0;
266     IncognitoModePrefs::Availability incognito_availability =
267         IncognitoModePrefs::GetAvailability(profile_->GetPrefs());
268     bool incognito_enabled =
269         enabled && incognito_availability != IncognitoModePrefs::DISABLED;
271     AddItemToMenu(IDC_BOOKMARK_BAR_OPEN_ALL,
272                   IDS_BOOKMARK_BAR_OPEN_ALL,
273                   node, menu, enabled);
274     AddItemToMenu(IDC_BOOKMARK_BAR_OPEN_ALL_NEW_WINDOW,
275                   IDS_BOOKMARK_BAR_OPEN_ALL_NEW_WINDOW,
276                   node, menu, enabled);
277     AddItemToMenu(IDC_BOOKMARK_BAR_OPEN_ALL_INCOGNITO,
278                   IDS_BOOKMARK_BAR_OPEN_ALL_INCOGNITO,
279                   node, menu, incognito_enabled);
280   }
283 void BookmarkMenuBridge::AddItemToMenu(int command_id,
284                                        int message_id,
285                                        const BookmarkNode* node,
286                                        NSMenu* menu,
287                                        bool enabled) {
288   NSString* title = l10n_util::GetNSStringWithFixup(message_id);
289   SEL action;
290   if (!enabled) {
291     // A nil action makes a menu item appear disabled. NSMenuItem setEnabled
292     // will not reflect the disabled state until the item title is set again.
293     action = nil;
294   } else if (command_id == IDC_BOOKMARK_BAR_OPEN_ALL) {
295     action = @selector(openAllBookmarks:);
296   } else if (command_id == IDC_BOOKMARK_BAR_OPEN_ALL_NEW_WINDOW) {
297     action = @selector(openAllBookmarksNewWindow:);
298   } else {
299     action = @selector(openAllBookmarksIncognitoWindow:);
300   }
301   NSMenuItem* item = [[[NSMenuItem alloc] initWithTitle:title
302                                                  action:action
303                                           keyEquivalent:@""] autorelease];
304   [item setTarget:controller_];
305   [item setTag:node->id()];
306   [item setEnabled:enabled];
307   [menu addItem:item];
310 void BookmarkMenuBridge::ConfigureMenuItem(const BookmarkNode* node,
311                                            NSMenuItem* item,
312                                            bool set_title) {
313   if (set_title)
314     [item setTitle:[BookmarkMenuCocoaController menuTitleForNode:node]];
315   [item setTarget:controller_];
316   [item setAction:@selector(openBookmarkMenuItem:)];
317   [item setTag:node->id()];
318   if (node->is_url())
319     [item setToolTip:[BookmarkMenuCocoaController tooltipForNode:node]];
320   // Check to see if we have a favicon.
321   NSImage* favicon = nil;
322   BookmarkModel* model = GetBookmarkModel();
323   if (model) {
324     const gfx::Image& image = model->GetFavicon(node);
325     if (!image.IsEmpty())
326       favicon = image.ToNSImage();
327   }
328   // If we do not have a loaded favicon, use the default site image instead.
329   if (!favicon) {
330     ResourceBundle& rb = ResourceBundle::GetSharedInstance();
331     favicon = rb.GetNativeImageNamed(IDR_DEFAULT_FAVICON).ToNSImage();
332   }
333   [item setImage:favicon];
336 NSMenuItem* BookmarkMenuBridge::MenuItemForNode(const BookmarkNode* node) {
337   if (!node)
338     return nil;
339   std::map<const BookmarkNode*, NSMenuItem*>::iterator it =
340       bookmark_nodes_.find(node);
341   if (it == bookmark_nodes_.end())
342     return nil;
343   return it->second;