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/bookmarks/recently_used_folders_combo_model.h"
7 #include "chrome/grit/generated_resources.h"
8 #include "components/bookmarks/browser/bookmark_model.h"
9 #include "components/bookmarks/browser/bookmark_utils.h"
10 #include "content/public/browser/user_metrics.h"
11 #include "ui/base/l10n/l10n_util.h"
12 #include "ui/base/models/combobox_model_observer.h"
14 using bookmarks::BookmarkModel
;
15 using bookmarks::BookmarkNode
;
19 // Max number of most recently used folders.
20 const size_t kMaxMRUFolders
= 5;
24 struct RecentlyUsedFoldersComboModel::Item
{
28 TYPE_CHOOSE_ANOTHER_FOLDER
31 Item(const BookmarkNode
* node
, Type type
);
34 bool operator==(const Item
& item
) const;
36 const BookmarkNode
* node
;
40 RecentlyUsedFoldersComboModel::Item::Item(const BookmarkNode
* node
,
46 RecentlyUsedFoldersComboModel::Item::~Item() {}
48 bool RecentlyUsedFoldersComboModel::Item::operator==(const Item
& item
) const {
49 return item
.node
== node
&& item
.type
== type
;
52 RecentlyUsedFoldersComboModel::RecentlyUsedFoldersComboModel(
54 const BookmarkNode
* node
)
55 : bookmark_model_(model
),
56 node_parent_index_(0) {
57 bookmark_model_
->AddObserver(this);
58 // Use + 2 to account for bookmark bar and other node.
59 std::vector
<const BookmarkNode
*> nodes
=
60 bookmarks::GetMostRecentlyModifiedUserFolders(model
, kMaxMRUFolders
+ 2);
62 for (size_t i
= 0; i
< nodes
.size(); ++i
)
63 items_
.push_back(Item(nodes
[i
], Item::TYPE_NODE
));
65 // We special case the placement of these, so remove them from the list, then
67 RemoveNode(model
->bookmark_bar_node());
68 RemoveNode(model
->mobile_node());
69 RemoveNode(model
->other_node());
70 RemoveNode(node
->parent());
72 // Make the parent the first item, unless it's a permanent node, which is
74 if (!model
->is_permanent_node(node
->parent()))
75 items_
.insert(items_
.begin(), Item(node
->parent(), Item::TYPE_NODE
));
77 // Make sure we only have kMaxMRUFolders in the first chunk.
78 if (items_
.size() > kMaxMRUFolders
)
79 items_
.erase(items_
.begin() + kMaxMRUFolders
, items_
.end());
81 // And put the bookmark bar and other nodes at the end of the list.
82 items_
.push_back(Item(model
->bookmark_bar_node(), Item::TYPE_NODE
));
83 items_
.push_back(Item(model
->other_node(), Item::TYPE_NODE
));
84 if (model
->mobile_node()->IsVisible())
85 items_
.push_back(Item(model
->mobile_node(), Item::TYPE_NODE
));
86 items_
.push_back(Item(NULL
, Item::TYPE_SEPARATOR
));
87 items_
.push_back(Item(NULL
, Item::TYPE_CHOOSE_ANOTHER_FOLDER
));
89 std::vector
<Item
>::iterator it
= std::find(items_
.begin(),
93 node_parent_index_
= static_cast<int>(it
- items_
.begin());
96 RecentlyUsedFoldersComboModel::~RecentlyUsedFoldersComboModel() {
97 bookmark_model_
->RemoveObserver(this);
100 int RecentlyUsedFoldersComboModel::GetItemCount() const {
101 return static_cast<int>(items_
.size());
104 base::string16
RecentlyUsedFoldersComboModel::GetItemAt(int index
) {
105 switch (items_
[index
].type
) {
106 case Item::TYPE_NODE
:
107 return items_
[index
].node
->GetTitle();
108 case Item::TYPE_SEPARATOR
:
109 // This function should not be called for separators.
111 return base::string16();
112 case Item::TYPE_CHOOSE_ANOTHER_FOLDER
:
113 return l10n_util::GetStringUTF16(
114 IDS_BOOKMARK_BUBBLE_CHOOSER_ANOTHER_FOLDER
);
117 return base::string16();
120 bool RecentlyUsedFoldersComboModel::IsItemSeparatorAt(int index
) {
121 return items_
[index
].type
== Item::TYPE_SEPARATOR
;
124 int RecentlyUsedFoldersComboModel::GetDefaultIndex() const {
125 return node_parent_index_
;
128 void RecentlyUsedFoldersComboModel::AddObserver(
129 ui::ComboboxModelObserver
* observer
) {
130 observers_
.AddObserver(observer
);
133 void RecentlyUsedFoldersComboModel::RemoveObserver(
134 ui::ComboboxModelObserver
* observer
) {
135 observers_
.RemoveObserver(observer
);
138 void RecentlyUsedFoldersComboModel::BookmarkModelLoaded(BookmarkModel
* model
,
139 bool ids_reassigned
) {}
141 void RecentlyUsedFoldersComboModel::BookmarkModelBeingDeleted(
142 BookmarkModel
* model
) {
145 void RecentlyUsedFoldersComboModel::BookmarkNodeMoved(
146 BookmarkModel
* model
,
147 const BookmarkNode
* old_parent
,
149 const BookmarkNode
* new_parent
,
153 void RecentlyUsedFoldersComboModel::BookmarkNodeAdded(
154 BookmarkModel
* model
,
155 const BookmarkNode
* parent
,
159 void RecentlyUsedFoldersComboModel::OnWillRemoveBookmarks(
160 BookmarkModel
* model
,
161 const BookmarkNode
* parent
,
163 const BookmarkNode
* node
) {
164 // Changing is rare enough that we don't attempt to readjust the contents.
165 // Update |items_| so we aren't left pointing to a deleted node.
166 bool changed
= false;
167 for (std::vector
<Item
>::iterator i
= items_
.begin();
168 i
!= items_
.end();) {
169 if (i
->type
== Item::TYPE_NODE
&& i
->node
->HasAncestor(node
)) {
177 FOR_EACH_OBSERVER(ui::ComboboxModelObserver
, observers_
,
178 OnComboboxModelChanged(this));
182 void RecentlyUsedFoldersComboModel::BookmarkNodeRemoved(
183 BookmarkModel
* model
,
184 const BookmarkNode
* parent
,
186 const BookmarkNode
* node
,
187 const std::set
<GURL
>& removed_urls
) {}
189 void RecentlyUsedFoldersComboModel::BookmarkNodeChanged(
190 BookmarkModel
* model
,
191 const BookmarkNode
* node
) {
194 void RecentlyUsedFoldersComboModel::BookmarkNodeFaviconChanged(
195 BookmarkModel
* model
,
196 const BookmarkNode
* node
) {
199 void RecentlyUsedFoldersComboModel::BookmarkNodeChildrenReordered(
200 BookmarkModel
* model
,
201 const BookmarkNode
* node
) {
204 void RecentlyUsedFoldersComboModel::BookmarkAllUserNodesRemoved(
205 BookmarkModel
* model
,
206 const std::set
<GURL
>& removed_urls
) {
207 // Changing is rare enough that we don't attempt to readjust the contents.
208 // Update |items_| so we aren't left pointing to a deleted node.
209 bool changed
= false;
210 for (std::vector
<Item
>::iterator i
= items_
.begin();
211 i
!= items_
.end();) {
212 if (i
->type
== Item::TYPE_NODE
&&
213 !bookmark_model_
->is_permanent_node(i
->node
)) {
221 FOR_EACH_OBSERVER(ui::ComboboxModelObserver
, observers_
,
222 OnComboboxModelChanged(this));
226 void RecentlyUsedFoldersComboModel::MaybeChangeParent(
227 const BookmarkNode
* node
,
228 int selected_index
) {
229 if (items_
[selected_index
].type
!= Item::TYPE_NODE
)
232 const BookmarkNode
* new_parent
= GetNodeAt(selected_index
);
233 if (new_parent
!= node
->parent()) {
234 content::RecordAction(
235 base::UserMetricsAction("BookmarkBubble_ChangeParent"));
236 bookmark_model_
->Move(node
, new_parent
, new_parent
->child_count());
240 const BookmarkNode
* RecentlyUsedFoldersComboModel::GetNodeAt(int index
) {
241 if (index
< 0 || index
>= static_cast<int>(items_
.size()))
243 return items_
[index
].node
;
246 void RecentlyUsedFoldersComboModel::RemoveNode(const BookmarkNode
* node
) {
247 std::vector
<Item
>::iterator it
= std::find(items_
.begin(),
249 Item(node
, Item::TYPE_NODE
));
250 if (it
!= items_
.end())