| // 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/browser/bookmarks/bookmark_model.h" |
| #include "chrome/browser/bookmarks/bookmark_utils.h" |
| #include "content/public/browser/user_metrics.h" |
| #include "grit/generated_resources.h" |
| #include "ui/base/l10n/l10n_util.h" |
| #include "ui/base/models/combobox_model_observer.h" |
| |
| 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<const BookmarkNode*> nodes = |
| bookmark_utils::GetMostRecentlyModifiedFolders(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)); |
| #if defined(USE_AURA) || defined(TOOLKIT_GTK) |
| items_.push_back(Item(NULL, Item::TYPE_SEPARATOR)); |
| #endif |
| items_.push_back(Item(NULL, Item::TYPE_CHOOSE_ANOTHER_FOLDER)); |
| |
| std::vector<Item>::iterator it = std::find(items_.begin(), |
| items_.end(), |
| Item(node->parent(), |
| Item::TYPE_NODE)); |
| node_parent_index_ = static_cast<int>(it - items_.begin()); |
| } |
| |
| RecentlyUsedFoldersComboModel::~RecentlyUsedFoldersComboModel() { |
| bookmark_model_->RemoveObserver(this); |
| } |
| |
| int RecentlyUsedFoldersComboModel::GetItemCount() const { |
| return static_cast<int>(items_.size()); |
| } |
| |
| 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::Loaded(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<Item>::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_, OnModelChanged()); |
| } |
| |
| void RecentlyUsedFoldersComboModel::BookmarkNodeRemoved( |
| BookmarkModel* model, |
| const BookmarkNode* parent, |
| int old_index, |
| const BookmarkNode* node) { |
| } |
| |
| 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::BookmarkAllNodesRemoved( |
| BookmarkModel* model) { |
| // 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<Item>::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_, OnModelChanged()); |
| } |
| |
| 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( |
| content::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<int>(items_.size())) |
| return NULL; |
| return items_[index].node; |
| } |
| |
| void RecentlyUsedFoldersComboModel::RemoveNode(const BookmarkNode* node) { |
| std::vector<Item>::iterator it = std::find(items_.begin(), |
| items_.end(), |
| Item(node, Item::TYPE_NODE)); |
| if (it != items_.end()) |
| items_.erase(it); |
| } |