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