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 #include "chrome/browser/ui/gtk/bookmarks/bookmark_sub_menu_model_gtk.h"
7 #include "base/stl_util.h"
8 #include "base/strings/string16.h"
9 #include "base/strings/utf_string_conversions.h"
10 #include "chrome/app/chrome_command_ids.h"
11 #include "chrome/browser/bookmarks/bookmark_model.h"
12 #include "chrome/browser/bookmarks/bookmark_model_factory.h"
13 #include "chrome/browser/bookmarks/bookmark_stats.h"
14 #include "chrome/browser/profiles/profile.h"
15 #include "chrome/browser/ui/browser.h"
16 #include "chrome/browser/ui/gtk/bookmarks/bookmark_utils_gtk.h"
17 #include "chrome/browser/ui/gtk/gtk_theme_service.h"
18 #include "chrome/browser/ui/gtk/menu_gtk.h"
19 #include "grit/generated_resources.h"
20 #include "grit/theme_resources.h"
21 #include "ui/base/accelerators/menu_label_accelerator_util_linux.h"
22 #include "ui/base/l10n/l10n_util.h"
23 #include "ui/base/resource/resource_bundle.h"
24 #include "ui/base/window_open_disposition.h"
26 using content::OpenURLParams
;
27 using content::PageNavigator
;
29 // Per chrome/app/chrome_command_ids.h, values < 4000 are for "dynamic menu
30 // items". We only use one command id for all the bookmarks, because we handle
31 // bookmark item activations directly. So we pick a suitably large random value
32 // and use that to avoid accidental conflicts with other dynamic items.
33 static const int kBookmarkItemCommandId
= 1759;
35 BookmarkNodeMenuModel::BookmarkNodeMenuModel(
36 ui::SimpleMenuModel::Delegate
* delegate
,
38 const BookmarkNode
* node
,
39 PageNavigator
* page_navigator
,
41 : SimpleMenuModel(delegate
),
44 page_navigator_(page_navigator
),
46 DCHECK(page_navigator_
);
49 BookmarkNodeMenuModel::~BookmarkNodeMenuModel() {
53 void BookmarkNodeMenuModel::Clear() {
54 SimpleMenuModel::Clear();
55 STLDeleteElements(&submenus_
);
58 void BookmarkNodeMenuModel::MenuWillShow() {
63 void BookmarkNodeMenuModel::MenuClosed() {
67 void BookmarkNodeMenuModel::ActivatedAt(int index
) {
68 NavigateToMenuItem(index
, CURRENT_TAB
);
71 void BookmarkNodeMenuModel::ActivatedAt(int index
, int event_flags
) {
72 NavigateToMenuItem(index
, ui::DispositionFromEventFlags(event_flags
));
75 void BookmarkNodeMenuModel::PopulateMenu() {
76 DCHECK(submenus_
.empty());
77 for (int i
= 0; i
< node_
->child_count(); ++i
) {
78 const BookmarkNode
* child
= node_
->GetChild(i
);
79 if (child
->is_folder()) {
80 AddSubMenuForNode(child
);
82 // Ironically the label will end up getting converted back to UTF8 later.
83 // We need to escape any Windows-style "&" characters since they will be
84 // converted in MenuGtk outside of our control here.
85 const base::string16 label
= base::UTF8ToUTF16(
86 ui::EscapeWindowsStyleAccelerators(BuildMenuLabelFor(child
)));
87 // No command id. We override ActivatedAt below to handle activations.
88 AddItem(kBookmarkItemCommandId
, label
);
89 GdkPixbuf
* node_icon
= GetPixbufForNode(
92 GtkThemeService::GetFrom(profile_
)->UsingNativeTheme());
93 SetIcon(GetItemCount() - 1, gfx::Image(node_icon
));
94 // TODO(mdm): set up an observer to watch for icon load events and set
95 // the icons in response.
100 void BookmarkNodeMenuModel::AddSubMenuForNode(const BookmarkNode
* node
) {
101 DCHECK(node
->is_folder());
102 // Ironically the label will end up getting converted back to UTF8 later.
103 // We need to escape any Windows-style "&" characters since they will be
104 // converted in MenuGtk outside of our control here.
105 const base::string16 label
= base::UTF8ToUTF16(
106 ui::EscapeWindowsStyleAccelerators(BuildMenuLabelFor(node
)));
107 // Don't pass in the delegate, if any. Bookmark submenus don't need one.
108 BookmarkNodeMenuModel
* submenu
=
109 new BookmarkNodeMenuModel(NULL
, model_
, node
, page_navigator_
, profile_
);
110 // No command id. Nothing happens if you click on the submenu itself.
111 AddSubMenu(kBookmarkItemCommandId
, label
, submenu
);
112 ui::ResourceBundle
& rb
= ui::ResourceBundle::GetSharedInstance();
113 const gfx::Image
& folder_icon
= rb
.GetImageNamed(IDR_BOOKMARK_BAR_FOLDER
);
114 SetIcon(GetItemCount() - 1, folder_icon
);
115 submenus_
.push_back(submenu
);
118 void BookmarkNodeMenuModel::NavigateToMenuItem(
120 WindowOpenDisposition disposition
) {
121 const BookmarkNode
* node
= node_
->GetChild(index
);
123 RecordBookmarkLaunch(node
, BOOKMARK_LAUNCH_LOCATION_WRENCH_MENU
);
124 page_navigator_
->OpenURL(OpenURLParams(
125 node
->url(), content::Referrer(), disposition
,
126 content::PAGE_TRANSITION_AUTO_BOOKMARK
,
127 false)); // is_renderer_initiated
130 BookmarkSubMenuModel::BookmarkSubMenuModel(
131 ui::SimpleMenuModel::Delegate
* delegate
,
133 : BookmarkNodeMenuModel(delegate
, NULL
, NULL
, browser
, browser
->profile()),
138 menu_showing_(false) {
141 BookmarkSubMenuModel::~BookmarkSubMenuModel() {
143 model()->RemoveObserver(this);
146 void BookmarkSubMenuModel::BookmarkModelLoaded(BookmarkModel
* model
,
147 bool ids_reassigned
) {
148 // For now, just close the menu when the bookmarks are finished loading.
149 // TODO(mdm): it would be slicker to just populate the menu while it's open.
150 BookmarkModelChanged();
153 void BookmarkSubMenuModel::BookmarkModelChanged() {
154 if (menu_showing_
&& menu_
)
158 void BookmarkSubMenuModel::BookmarkModelBeingDeleted(
159 BookmarkModel
* model
) {
161 // All our submenus will still have pointers to the model, but this call
162 // should force the menu to close, which will cause them to be deleted.
163 BookmarkModelChanged();
166 void BookmarkSubMenuModel::MenuWillShow() {
167 menu_showing_
= true;
169 AddCheckItemWithStringId(IDC_SHOW_BOOKMARK_BAR
, IDS_SHOW_BOOKMARK_BAR
);
170 AddItemWithStringId(IDC_SHOW_BOOKMARK_MANAGER
, IDS_BOOKMARK_MANAGER
);
171 AddItemWithStringId(IDC_IMPORT_SETTINGS
, IDS_IMPORT_SETTINGS_MENU_LABEL
);
172 AddSeparator(ui::NORMAL_SEPARATOR
);
173 AddItemWithStringId(IDC_BOOKMARK_PAGE
, IDS_BOOKMARK_THIS_PAGE
);
174 AddItemWithStringId(IDC_BOOKMARK_ALL_TABS
, IDS_BOOKMARK_OPEN_PAGES
);
175 fixed_items_
= bookmark_end_
= GetItemCount();
177 set_model(BookmarkModelFactory::GetForProfile(browser_
->profile()));
180 model()->AddObserver(this);
182 // We can't do anything further if the model isn't loaded yet.
183 if (!model()->loaded())
185 // The node count includes the node itself, so 1 means empty.
186 if (model()->bookmark_bar_node()->GetTotalNodeCount() > 1) {
187 AddSeparator(ui::NORMAL_SEPARATOR
);
188 fixed_items_
= GetItemCount();
190 set_node(model()->bookmark_bar_node());
191 // PopulateMenu() won't clear the items we added above.
194 bookmark_end_
= GetItemCount();
196 // We want only one separator after the top-level bookmarks and before the
197 // other node and/or mobile node.
198 AddSeparator(ui::NORMAL_SEPARATOR
);
199 if (model()->other_node()->GetTotalNodeCount() > 1)
200 AddSubMenuForNode(model()->other_node());
201 if (model()->mobile_node()->GetTotalNodeCount() > 1)
202 AddSubMenuForNode(model()->mobile_node());
203 RemoveTrailingSeparators();
206 void BookmarkSubMenuModel::MenuClosed() {
207 menu_showing_
= false;
208 BookmarkNodeMenuModel::MenuClosed();
211 void BookmarkSubMenuModel::ActivatedAt(int index
) {
212 // Because this is also overridden in BookmarkNodeMenuModel which doesn't know
213 // we might be prepending items, we have to adjust the index for it.
214 if (index
>= fixed_items_
&& index
< bookmark_end_
)
215 BookmarkNodeMenuModel::ActivatedAt(index
- fixed_items_
);
217 SimpleMenuModel::ActivatedAt(index
);
220 void BookmarkSubMenuModel::ActivatedAt(int index
, int event_flags
) {
221 // Because this is also overridden in BookmarkNodeMenuModel which doesn't know
222 // we might be prepending items, we have to adjust the index for it.
223 if (index
>= fixed_items_
&& index
< bookmark_end_
)
224 BookmarkNodeMenuModel::ActivatedAt(index
- fixed_items_
, event_flags
);
226 SimpleMenuModel::ActivatedAt(index
, event_flags
);
229 bool BookmarkSubMenuModel::IsEnabledAt(int index
) const {
230 // We don't want the delegate interfering with bookmark items.
231 return index
>= fixed_items_
|| SimpleMenuModel::IsEnabledAt(index
);
234 bool BookmarkSubMenuModel::IsVisibleAt(int index
) const {
235 // We don't want the delegate interfering with bookmark items.
236 return index
>= fixed_items_
|| SimpleMenuModel::IsVisibleAt(index
);
240 bool BookmarkSubMenuModel::IsBookmarkItemCommandId(int command_id
) {
241 return command_id
== kBookmarkItemCommandId
;