recently_used_folders_combo_model.cc revision a1401311d1ab56c4ed0a474bd38c108f75cb0cd9
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/bookmarks/recently_used_folders_combo_model.h"
6
7#include "chrome/browser/bookmarks/bookmark_model.h"
8#include "chrome/browser/bookmarks/bookmark_utils.h"
9#include "content/public/browser/user_metrics.h"
10#include "grit/generated_resources.h"
11#include "ui/base/l10n/l10n_util.h"
12#include "ui/base/models/combobox_model_observer.h"
13
14namespace {
15
16// Max number of most recently used folders.
17const size_t kMaxMRUFolders = 5;
18
19}  // namespace
20
21struct RecentlyUsedFoldersComboModel::Item {
22  enum Type {
23    TYPE_NODE,
24    TYPE_SEPARATOR,
25    TYPE_CHOOSE_ANOTHER_FOLDER
26  };
27
28  Item(const BookmarkNode* node, Type type);
29  ~Item();
30
31  bool operator==(const Item& item) const;
32
33  const BookmarkNode* node;
34  Type type;
35};
36
37RecentlyUsedFoldersComboModel::Item::Item(const BookmarkNode* node,
38                                          Type type)
39    : node(node),
40      type(type) {
41}
42
43RecentlyUsedFoldersComboModel::Item::~Item() {}
44
45bool RecentlyUsedFoldersComboModel::Item::operator==(const Item& item) const {
46  return item.node == node && item.type == type;
47}
48
49RecentlyUsedFoldersComboModel::RecentlyUsedFoldersComboModel(
50    BookmarkModel* model,
51    const BookmarkNode* node)
52    : bookmark_model_(model),
53      node_parent_index_(0) {
54  bookmark_model_->AddObserver(this);
55  // Use + 2 to account for bookmark bar and other node.
56  std::vector<const BookmarkNode*> nodes =
57      bookmark_utils::GetMostRecentlyModifiedFolders(model, kMaxMRUFolders + 2);
58
59  for (size_t i = 0; i < nodes.size(); ++i)
60    items_.push_back(Item(nodes[i], Item::TYPE_NODE));
61
62  // We special case the placement of these, so remove them from the list, then
63  // fix up the order.
64  RemoveNode(model->bookmark_bar_node());
65  RemoveNode(model->mobile_node());
66  RemoveNode(model->other_node());
67  RemoveNode(node->parent());
68
69  // Make the parent the first item, unless it's a permanent node, which is
70  // added below.
71  if (!model->is_permanent_node(node->parent()))
72    items_.insert(items_.begin(), Item(node->parent(), Item::TYPE_NODE));
73
74  // Make sure we only have kMaxMRUFolders in the first chunk.
75  if (items_.size() > kMaxMRUFolders)
76    items_.erase(items_.begin() + kMaxMRUFolders, items_.end());
77
78  // And put the bookmark bar and other nodes at the end of the list.
79  items_.push_back(Item(model->bookmark_bar_node(), Item::TYPE_NODE));
80  items_.push_back(Item(model->other_node(), Item::TYPE_NODE));
81  if (model->mobile_node()->IsVisible())
82    items_.push_back(Item(model->mobile_node(), Item::TYPE_NODE));
83  items_.push_back(Item(NULL, Item::TYPE_SEPARATOR));
84  items_.push_back(Item(NULL, Item::TYPE_CHOOSE_ANOTHER_FOLDER));
85
86  std::vector<Item>::iterator it = std::find(items_.begin(),
87                                             items_.end(),
88                                             Item(node->parent(),
89                                                  Item::TYPE_NODE));
90  node_parent_index_ = static_cast<int>(it - items_.begin());
91}
92
93RecentlyUsedFoldersComboModel::~RecentlyUsedFoldersComboModel() {
94  bookmark_model_->RemoveObserver(this);
95}
96
97int RecentlyUsedFoldersComboModel::GetItemCount() const {
98  return static_cast<int>(items_.size());
99}
100
101base::string16 RecentlyUsedFoldersComboModel::GetItemAt(int index) {
102  switch (items_[index].type) {
103    case Item::TYPE_NODE:
104      return items_[index].node->GetTitle();
105    case Item::TYPE_SEPARATOR:
106      // This function should not be called for separators.
107      NOTREACHED();
108      return base::string16();
109    case Item::TYPE_CHOOSE_ANOTHER_FOLDER:
110      return l10n_util::GetStringUTF16(
111          IDS_BOOKMARK_BUBBLE_CHOOSER_ANOTHER_FOLDER);
112  }
113  NOTREACHED();
114  return base::string16();
115}
116
117bool RecentlyUsedFoldersComboModel::IsItemSeparatorAt(int index) {
118  return items_[index].type == Item::TYPE_SEPARATOR;
119}
120
121int RecentlyUsedFoldersComboModel::GetDefaultIndex() const {
122  return node_parent_index_;
123}
124
125void RecentlyUsedFoldersComboModel::AddObserver(
126    ui::ComboboxModelObserver* observer) {
127  observers_.AddObserver(observer);
128}
129
130void RecentlyUsedFoldersComboModel::RemoveObserver(
131    ui::ComboboxModelObserver* observer) {
132  observers_.RemoveObserver(observer);
133}
134
135void RecentlyUsedFoldersComboModel::BookmarkModelLoaded(BookmarkModel* model,
136                                                        bool ids_reassigned) {}
137
138void RecentlyUsedFoldersComboModel::BookmarkModelBeingDeleted(
139    BookmarkModel* model) {
140}
141
142void RecentlyUsedFoldersComboModel::BookmarkNodeMoved(
143    BookmarkModel* model,
144    const BookmarkNode* old_parent,
145    int old_index,
146    const BookmarkNode* new_parent,
147    int new_index) {
148}
149
150void RecentlyUsedFoldersComboModel::BookmarkNodeAdded(
151    BookmarkModel* model,
152    const BookmarkNode* parent,
153    int index) {
154}
155
156void RecentlyUsedFoldersComboModel::OnWillRemoveBookmarks(
157    BookmarkModel* model,
158    const BookmarkNode* parent,
159    int old_index,
160    const BookmarkNode* node) {
161  // Changing is rare enough that we don't attempt to readjust the contents.
162  // Update |items_| so we aren't left pointing to a deleted node.
163  bool changed = false;
164  for (std::vector<Item>::iterator i = items_.begin();
165       i != items_.end();) {
166    if (i->type == Item::TYPE_NODE && i->node->HasAncestor(node)) {
167      i = items_.erase(i);
168      changed = true;
169    } else {
170      ++i;
171    }
172  }
173  if (changed) {
174    FOR_EACH_OBSERVER(ui::ComboboxModelObserver, observers_,
175                      OnComboboxModelChanged(this));
176  }
177}
178
179void RecentlyUsedFoldersComboModel::BookmarkNodeRemoved(
180    BookmarkModel* model,
181    const BookmarkNode* parent,
182    int old_index,
183    const BookmarkNode* node) {
184}
185
186void RecentlyUsedFoldersComboModel::BookmarkNodeChanged(
187    BookmarkModel* model,
188    const BookmarkNode* node) {
189}
190
191void RecentlyUsedFoldersComboModel::BookmarkNodeFaviconChanged(
192    BookmarkModel* model,
193    const BookmarkNode* node) {
194}
195
196void RecentlyUsedFoldersComboModel::BookmarkNodeChildrenReordered(
197      BookmarkModel* model,
198      const BookmarkNode* node) {
199}
200
201void RecentlyUsedFoldersComboModel::BookmarkAllNodesRemoved(
202    BookmarkModel* model) {
203  // Changing is rare enough that we don't attempt to readjust the contents.
204  // Update |items_| so we aren't left pointing to a deleted node.
205  bool changed = false;
206  for (std::vector<Item>::iterator i = items_.begin();
207       i != items_.end();) {
208    if (i->type == Item::TYPE_NODE &&
209        !bookmark_model_->is_permanent_node(i->node)) {
210      i = items_.erase(i);
211      changed = true;
212    } else {
213      ++i;
214    }
215  }
216  if (changed) {
217    FOR_EACH_OBSERVER(ui::ComboboxModelObserver, observers_,
218                      OnComboboxModelChanged(this));
219  }
220}
221
222void RecentlyUsedFoldersComboModel::MaybeChangeParent(
223    const BookmarkNode* node,
224    int selected_index) {
225  if (items_[selected_index].type != Item::TYPE_NODE)
226    return;
227
228  const BookmarkNode* new_parent = GetNodeAt(selected_index);
229  if (new_parent != node->parent()) {
230    content::RecordAction(
231        base::UserMetricsAction("BookmarkBubble_ChangeParent"));
232    bookmark_model_->Move(node, new_parent, new_parent->child_count());
233  }
234}
235
236const BookmarkNode* RecentlyUsedFoldersComboModel::GetNodeAt(int index) {
237  if (index < 0 || index >= static_cast<int>(items_.size()))
238    return NULL;
239  return items_[index].node;
240}
241
242void RecentlyUsedFoldersComboModel::RemoveNode(const BookmarkNode* node) {
243  std::vector<Item>::iterator it = std::find(items_.begin(),
244                                             items_.end(),
245                                             Item(node, Item::TYPE_NODE));
246  if (it != items_.end())
247    items_.erase(it);
248}
249