// Copyright (c) 2012 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "chrome/browser/ui/bookmarks/recently_used_folders_combo_model.h" #include "chrome/grit/generated_resources.h" #include "components/bookmarks/browser/bookmark_model.h" #include "components/bookmarks/browser/bookmark_utils.h" #include "content/public/browser/user_metrics.h" #include "ui/base/l10n/l10n_util.h" #include "ui/base/models/combobox_model_observer.h" using bookmarks::BookmarkModel; using bookmarks::BookmarkNode; namespace { // Max number of most recently used folders. const size_t kMaxMRUFolders = 5; } // namespace struct RecentlyUsedFoldersComboModel::Item { enum Type { TYPE_NODE, TYPE_SEPARATOR, TYPE_CHOOSE_ANOTHER_FOLDER }; Item(const BookmarkNode* node, Type type); ~Item(); bool operator==(const Item& item) const; const BookmarkNode* node; Type type; }; RecentlyUsedFoldersComboModel::Item::Item(const BookmarkNode* node, Type type) : node(node), type(type) { } RecentlyUsedFoldersComboModel::Item::~Item() {} bool RecentlyUsedFoldersComboModel::Item::operator==(const Item& item) const { return item.node == node && item.type == type; } RecentlyUsedFoldersComboModel::RecentlyUsedFoldersComboModel( BookmarkModel* model, const BookmarkNode* node) : bookmark_model_(model), node_parent_index_(0) { bookmark_model_->AddObserver(this); // Use + 2 to account for bookmark bar and other node. std::vector nodes = bookmarks::GetMostRecentlyModifiedUserFolders(model, kMaxMRUFolders + 2); for (size_t i = 0; i < nodes.size(); ++i) items_.push_back(Item(nodes[i], Item::TYPE_NODE)); // We special case the placement of these, so remove them from the list, then // fix up the order. RemoveNode(model->bookmark_bar_node()); RemoveNode(model->mobile_node()); RemoveNode(model->other_node()); RemoveNode(node->parent()); // Make the parent the first item, unless it's a permanent node, which is // added below. if (!model->is_permanent_node(node->parent())) items_.insert(items_.begin(), Item(node->parent(), Item::TYPE_NODE)); // Make sure we only have kMaxMRUFolders in the first chunk. if (items_.size() > kMaxMRUFolders) items_.erase(items_.begin() + kMaxMRUFolders, items_.end()); // And put the bookmark bar and other nodes at the end of the list. items_.push_back(Item(model->bookmark_bar_node(), Item::TYPE_NODE)); items_.push_back(Item(model->other_node(), Item::TYPE_NODE)); if (model->mobile_node()->IsVisible()) items_.push_back(Item(model->mobile_node(), Item::TYPE_NODE)); items_.push_back(Item(NULL, Item::TYPE_SEPARATOR)); items_.push_back(Item(NULL, Item::TYPE_CHOOSE_ANOTHER_FOLDER)); std::vector::iterator it = std::find(items_.begin(), items_.end(), Item(node->parent(), Item::TYPE_NODE)); node_parent_index_ = static_cast(it - items_.begin()); } RecentlyUsedFoldersComboModel::~RecentlyUsedFoldersComboModel() { bookmark_model_->RemoveObserver(this); } int RecentlyUsedFoldersComboModel::GetItemCount() const { return static_cast(items_.size()); } base::string16 RecentlyUsedFoldersComboModel::GetItemAt(int index) { switch (items_[index].type) { case Item::TYPE_NODE: return items_[index].node->GetTitle(); case Item::TYPE_SEPARATOR: // This function should not be called for separators. NOTREACHED(); return base::string16(); case Item::TYPE_CHOOSE_ANOTHER_FOLDER: return l10n_util::GetStringUTF16( IDS_BOOKMARK_BUBBLE_CHOOSER_ANOTHER_FOLDER); } NOTREACHED(); return base::string16(); } bool RecentlyUsedFoldersComboModel::IsItemSeparatorAt(int index) { return items_[index].type == Item::TYPE_SEPARATOR; } int RecentlyUsedFoldersComboModel::GetDefaultIndex() const { return node_parent_index_; } void RecentlyUsedFoldersComboModel::AddObserver( ui::ComboboxModelObserver* observer) { observers_.AddObserver(observer); } void RecentlyUsedFoldersComboModel::RemoveObserver( ui::ComboboxModelObserver* observer) { observers_.RemoveObserver(observer); } void RecentlyUsedFoldersComboModel::BookmarkModelLoaded(BookmarkModel* model, bool ids_reassigned) {} void RecentlyUsedFoldersComboModel::BookmarkModelBeingDeleted( BookmarkModel* model) { } void RecentlyUsedFoldersComboModel::BookmarkNodeMoved( BookmarkModel* model, const BookmarkNode* old_parent, int old_index, const BookmarkNode* new_parent, int new_index) { } void RecentlyUsedFoldersComboModel::BookmarkNodeAdded( BookmarkModel* model, const BookmarkNode* parent, int index) { } void RecentlyUsedFoldersComboModel::OnWillRemoveBookmarks( BookmarkModel* model, const BookmarkNode* parent, int old_index, const BookmarkNode* node) { // Changing is rare enough that we don't attempt to readjust the contents. // Update |items_| so we aren't left pointing to a deleted node. bool changed = false; for (std::vector::iterator i = items_.begin(); i != items_.end();) { if (i->type == Item::TYPE_NODE && i->node->HasAncestor(node)) { i = items_.erase(i); changed = true; } else { ++i; } } if (changed) { FOR_EACH_OBSERVER(ui::ComboboxModelObserver, observers_, OnComboboxModelChanged(this)); } } void RecentlyUsedFoldersComboModel::BookmarkNodeRemoved( BookmarkModel* model, const BookmarkNode* parent, int old_index, const BookmarkNode* node, const std::set& removed_urls) {} void RecentlyUsedFoldersComboModel::BookmarkNodeChanged( BookmarkModel* model, const BookmarkNode* node) { } void RecentlyUsedFoldersComboModel::BookmarkNodeFaviconChanged( BookmarkModel* model, const BookmarkNode* node) { } void RecentlyUsedFoldersComboModel::BookmarkNodeChildrenReordered( BookmarkModel* model, const BookmarkNode* node) { } void RecentlyUsedFoldersComboModel::BookmarkAllUserNodesRemoved( BookmarkModel* model, const std::set& removed_urls) { // Changing is rare enough that we don't attempt to readjust the contents. // Update |items_| so we aren't left pointing to a deleted node. bool changed = false; for (std::vector::iterator i = items_.begin(); i != items_.end();) { if (i->type == Item::TYPE_NODE && !bookmark_model_->is_permanent_node(i->node)) { i = items_.erase(i); changed = true; } else { ++i; } } if (changed) { FOR_EACH_OBSERVER(ui::ComboboxModelObserver, observers_, OnComboboxModelChanged(this)); } } void RecentlyUsedFoldersComboModel::MaybeChangeParent( const BookmarkNode* node, int selected_index) { if (items_[selected_index].type != Item::TYPE_NODE) return; const BookmarkNode* new_parent = GetNodeAt(selected_index); if (new_parent != node->parent()) { content::RecordAction( base::UserMetricsAction("BookmarkBubble_ChangeParent")); bookmark_model_->Move(node, new_parent, new_parent->child_count()); } } const BookmarkNode* RecentlyUsedFoldersComboModel::GetNodeAt(int index) { if (index < 0 || index >= static_cast(items_.size())) return NULL; return items_[index].node; } void RecentlyUsedFoldersComboModel::RemoveNode(const BookmarkNode* node) { std::vector::iterator it = std::find(items_.begin(), items_.end(), Item(node, Item::TYPE_NODE)); if (it != items_.end()) items_.erase(it); }