- add sources.
[platform/framework/web/crosswalk.git] / src / chrome / browser / ui / gtk / bookmarks / bookmark_sub_menu_model_gtk.cc
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.
4
5 #include "chrome/browser/ui/gtk/bookmarks/bookmark_sub_menu_model_gtk.h"
6
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"
25
26 using content::OpenURLParams;
27 using content::PageNavigator;
28
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;
34
35 BookmarkNodeMenuModel::BookmarkNodeMenuModel(
36     ui::SimpleMenuModel::Delegate* delegate,
37     BookmarkModel* model,
38     const BookmarkNode* node,
39     PageNavigator* page_navigator,
40     Profile* profile)
41     : SimpleMenuModel(delegate),
42       model_(model),
43       node_(node),
44       page_navigator_(page_navigator),
45       profile_(profile) {
46   DCHECK(page_navigator_);
47 }
48
49 BookmarkNodeMenuModel::~BookmarkNodeMenuModel() {
50   Clear();
51 }
52
53 void BookmarkNodeMenuModel::Clear() {
54   SimpleMenuModel::Clear();
55   STLDeleteElements(&submenus_);
56 }
57
58 void BookmarkNodeMenuModel::MenuWillShow() {
59   Clear();
60   PopulateMenu();
61 }
62
63 void BookmarkNodeMenuModel::MenuClosed() {
64   Clear();
65 }
66
67 void BookmarkNodeMenuModel::ActivatedAt(int index) {
68   NavigateToMenuItem(index, CURRENT_TAB);
69 }
70
71 void BookmarkNodeMenuModel::ActivatedAt(int index, int event_flags) {
72   NavigateToMenuItem(index, ui::DispositionFromEventFlags(event_flags));
73 }
74
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);
81     } else {
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 string16 label = 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(
90           child,
91           model_,
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.
96     }
97   }
98 }
99
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 string16 label =
106       UTF8ToUTF16(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);
116 }
117
118 void BookmarkNodeMenuModel::NavigateToMenuItem(
119     int index,
120     WindowOpenDisposition disposition) {
121   const BookmarkNode* node = node_->GetChild(index);
122   DCHECK(node);
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
128 }
129
130 BookmarkSubMenuModel::BookmarkSubMenuModel(
131     ui::SimpleMenuModel::Delegate* delegate,
132     Browser* browser)
133     : BookmarkNodeMenuModel(delegate, NULL, NULL, browser, browser->profile()),
134       browser_(browser),
135       fixed_items_(0),
136       bookmark_end_(0),
137       menu_(NULL),
138       menu_showing_(false) {
139 }
140
141 BookmarkSubMenuModel::~BookmarkSubMenuModel() {
142   if (model())
143     model()->RemoveObserver(this);
144 }
145
146 void BookmarkSubMenuModel::Loaded(BookmarkModel* model, bool ids_reassigned) {
147   // For now, just close the menu when the bookmarks are finished loading.
148   // TODO(mdm): it would be slicker to just populate the menu while it's open.
149   BookmarkModelChanged();
150 }
151
152 void BookmarkSubMenuModel::BookmarkModelChanged() {
153   if (menu_showing_ && menu_)
154     menu_->Cancel();
155 }
156
157 void BookmarkSubMenuModel::BookmarkModelBeingDeleted(
158     BookmarkModel* model) {
159   set_model(NULL);
160   // All our submenus will still have pointers to the model, but this call
161   // should force the menu to close, which will cause them to be deleted.
162   BookmarkModelChanged();
163 }
164
165 void BookmarkSubMenuModel::MenuWillShow() {
166   menu_showing_ = true;
167   Clear();
168   AddCheckItemWithStringId(IDC_SHOW_BOOKMARK_BAR, IDS_SHOW_BOOKMARK_BAR);
169   AddItemWithStringId(IDC_SHOW_BOOKMARK_MANAGER, IDS_BOOKMARK_MANAGER);
170   AddItemWithStringId(IDC_IMPORT_SETTINGS, IDS_IMPORT_SETTINGS_MENU_LABEL);
171   AddSeparator(ui::NORMAL_SEPARATOR);
172   AddItemWithStringId(IDC_BOOKMARK_PAGE, IDS_BOOKMARK_THIS_PAGE);
173   AddItemWithStringId(IDC_BOOKMARK_ALL_TABS, IDS_BOOKMARK_OPEN_PAGES);
174   fixed_items_ = bookmark_end_ = GetItemCount();
175   if (!model()) {
176     set_model(BookmarkModelFactory::GetForProfile(browser_->profile()));
177     if (!model())
178       return;
179     model()->AddObserver(this);
180   }
181   // We can't do anything further if the model isn't loaded yet.
182   if (!model()->loaded())
183     return;
184   // The node count includes the node itself, so 1 means empty.
185   if (model()->bookmark_bar_node()->GetTotalNodeCount() > 1) {
186     AddSeparator(ui::NORMAL_SEPARATOR);
187     fixed_items_ = GetItemCount();
188     if (!node())
189       set_node(model()->bookmark_bar_node());
190     // PopulateMenu() won't clear the items we added above.
191     PopulateMenu();
192   }
193   bookmark_end_ = GetItemCount();
194
195   // We want only one separator after the top-level bookmarks and before the
196   // other node and/or mobile node.
197   AddSeparator(ui::NORMAL_SEPARATOR);
198   if (model()->other_node()->GetTotalNodeCount() > 1)
199     AddSubMenuForNode(model()->other_node());
200   if (model()->mobile_node()->GetTotalNodeCount() > 1)
201     AddSubMenuForNode(model()->mobile_node());
202   RemoveTrailingSeparators();
203 }
204
205 void BookmarkSubMenuModel::MenuClosed() {
206   menu_showing_ = false;
207   BookmarkNodeMenuModel::MenuClosed();
208 }
209
210 void BookmarkSubMenuModel::ActivatedAt(int index) {
211   // Because this is also overridden in BookmarkNodeMenuModel which doesn't know
212   // we might be prepending items, we have to adjust the index for it.
213   if (index >= fixed_items_ && index < bookmark_end_)
214     BookmarkNodeMenuModel::ActivatedAt(index - fixed_items_);
215   else
216     SimpleMenuModel::ActivatedAt(index);
217 }
218
219 void BookmarkSubMenuModel::ActivatedAt(int index, int event_flags) {
220   // Because this is also overridden in BookmarkNodeMenuModel which doesn't know
221   // we might be prepending items, we have to adjust the index for it.
222   if (index >= fixed_items_ && index < bookmark_end_)
223     BookmarkNodeMenuModel::ActivatedAt(index - fixed_items_, event_flags);
224   else
225     SimpleMenuModel::ActivatedAt(index, event_flags);
226 }
227
228 bool BookmarkSubMenuModel::IsEnabledAt(int index) const {
229   // We don't want the delegate interfering with bookmark items.
230   return index >= fixed_items_ || SimpleMenuModel::IsEnabledAt(index);
231 }
232
233 bool BookmarkSubMenuModel::IsVisibleAt(int index) const {
234   // We don't want the delegate interfering with bookmark items.
235   return index >= fixed_items_ || SimpleMenuModel::IsVisibleAt(index);
236 }
237
238 // static
239 bool BookmarkSubMenuModel::IsBookmarkItemCommandId(int command_id) {
240   return command_id == kBookmarkItemCommandId;
241 }