summaryrefslogtreecommitdiffstats
path: root/chrome/browser/views
diff options
context:
space:
mode:
authorsky@google.com <sky@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2008-10-29 23:38:06 +0000
committersky@google.com <sky@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2008-10-29 23:38:06 +0000
commit7f856bee73ffdccdbbbbbab4cb79185290d38359 (patch)
treea837328e08a113abdc70b2fc0ae2f2a4a3f804eb /chrome/browser/views
parent281fe14063dd8fb81cea102f5abb7b82f407c3d1 (diff)
downloadchromium_src-7f856bee73ffdccdbbbbbab4cb79185290d38359.zip
chromium_src-7f856bee73ffdccdbbbbbab4cb79185290d38359.tar.gz
chromium_src-7f856bee73ffdccdbbbbbab4cb79185290d38359.tar.bz2
First cut at the bookmark manager. There are still a fair number of
rough edges, but I'm at a good point where I want to land what I have. Here's what is left: . Flicker on show, likely the result of restoring window placement. . tree flickers when dragging splitter. . table/tree need to autoscroll when drop cursor held at bottom of view. . prompts for deleting. . When you move an item the table snaps to the top, this is because I'm sending out model changed. need a better notification. . Operations in menu to add need to change selection. . Remember split location. I would have preferred to split this up into a couple of reviews, but everything is intertwined now. Sorry. BUG=674 TEST=don't test the bookmark manager yet, but make sure bookmark bar still works. Review URL: http://codereview.chromium.org/8197 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@4191 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/views')
-rw-r--r--chrome/browser/views/bookmark_bar_view.cc87
-rw-r--r--chrome/browser/views/bookmark_bar_view.h11
-rw-r--r--chrome/browser/views/bookmark_bubble_view.cc3
-rw-r--r--chrome/browser/views/bookmark_editor_view.cc149
-rw-r--r--chrome/browser/views/bookmark_editor_view.h27
-rw-r--r--chrome/browser/views/bookmark_editor_view_unittest.cc59
-rw-r--r--chrome/browser/views/bookmark_folder_tree_view.cc321
-rw-r--r--chrome/browser/views/bookmark_folder_tree_view.h117
-rw-r--r--chrome/browser/views/bookmark_manager_view.cc383
-rw-r--r--chrome/browser/views/bookmark_manager_view.h160
-rw-r--r--chrome/browser/views/bookmark_table_view.cc394
-rw-r--r--chrome/browser/views/bookmark_table_view.h121
-rw-r--r--chrome/browser/views/browser_views.vcproj24
-rw-r--r--chrome/browser/views/toolbar_view.cc2
14 files changed, 1753 insertions, 105 deletions
diff --git a/chrome/browser/views/bookmark_bar_view.cc b/chrome/browser/views/bookmark_bar_view.cc
index 70be891..8c08453 100644
--- a/chrome/browser/views/bookmark_bar_view.cc
+++ b/chrome/browser/views/bookmark_bar_view.cc
@@ -9,8 +9,8 @@
#include "base/base_drag_source.h"
#include "base/gfx/skia_utils.h"
#include "chrome/app/theme/theme_resources.h"
-#include "chrome/browser/bookmark_bar_context_menu_controller.h"
-#include "chrome/browser/bookmarks/bookmark_drag_utils.h"
+#include "chrome/browser/bookmarks/bookmark_context_menu.h"
+#include "chrome/browser/bookmarks/bookmark_utils.h"
#include "chrome/browser/browser.h"
#include "chrome/browser/browser_list.h"
#include "chrome/browser/browser_process.h"
@@ -395,8 +395,6 @@ class MenuRunner : public views::MenuDelegate,
}
virtual void ModelChanged() {
- if (context_menu_.get())
- context_menu_->ModelChanged();
menu_.Cancel();
}
@@ -531,8 +529,16 @@ class MenuRunner : public views::MenuDelegate,
int y,
bool is_mouse_gesture) {
DCHECK(menu_id_to_node_map_.find(id) != menu_id_to_node_map_.end());
+ std::vector<BookmarkNode*> nodes;
+ nodes.push_back(menu_id_to_node_map_[id]);
context_menu_.reset(
- new BookmarkBarContextMenuController(view_, menu_id_to_node_map_[id]));
+ new BookmarkContextMenu(view_->GetContainer()->GetHWND(),
+ view_->GetProfile(),
+ view_->browser(),
+ view_->GetPageNavigator(),
+ nodes[0]->GetParent(),
+ nodes,
+ BookmarkContextMenu::BOOKMARK_BAR));
context_menu_->RunMenuAt(x, y);
context_menu_.reset(NULL);
return true;
@@ -581,7 +587,7 @@ class MenuRunner : public views::MenuDelegate,
// Data for the drop.
BookmarkDragData drop_data_;
- scoped_ptr<BookmarkBarContextMenuController> context_menu_;
+ scoped_ptr<BookmarkContextMenu> context_menu_;
DISALLOW_COPY_AND_ASSIGN(MenuRunner);
};
@@ -646,6 +652,22 @@ static const SkBitmap& GetGroupIcon() {
}
// static
+void BookmarkBarView::ToggleWhenVisible(Profile* profile) {
+ PrefService* prefs = profile->GetPrefs();
+ const bool always_show = !prefs->GetBoolean(prefs::kShowBookmarkBar);
+
+ // The user changed when the bookmark bar is shown, update the preferences.
+ prefs->SetBoolean(prefs::kShowBookmarkBar, always_show);
+ prefs->ScheduleSavePersistentPrefs(g_browser_process->file_thread());
+
+ // And notify the notification service.
+ Source<Profile> source(profile);
+ NotificationService::current()->Notify(
+ NOTIFY_BOOKMARK_BAR_VISIBILITY_PREF_CHANGED, source,
+ NotificationService::NoDetails());
+}
+
+// static
void BookmarkBarView::RegisterUserPrefs(PrefService* prefs) {
prefs->RegisterBooleanPref(prefs::kShowBookmarkBar, false);
}
@@ -1079,24 +1101,6 @@ int BookmarkBarView::OnPerformDrop(const DropTargetEvent& event) {
return PerformDropImpl(data, parent_node, index);
}
-void BookmarkBarView::ToggleWhenVisible() {
- PrefService* prefs = profile_->GetPrefs();
- const bool always_show = !prefs->GetBoolean(prefs::kShowBookmarkBar);
-
- // The user changed when the bookmark bar is shown, update the preferences.
- prefs->SetBoolean(prefs::kShowBookmarkBar, always_show);
- prefs->ScheduleSavePersistentPrefs(g_browser_process->file_thread());
-
- // And notify the notification service.
- Source<Profile> source(profile_);
- NotificationService::current()->Notify(
- NOTIFY_BOOKMARK_BAR_VISIBILITY_PREF_CHANGED, source,
- NotificationService::NoDetails());
-
- // May need to redraw the bar with a new style.
- SchedulePaint();
-}
-
bool BookmarkBarView::IsAlwaysShown() {
return profile_->GetPrefs()->GetBoolean(prefs::kShowBookmarkBar);
}
@@ -1422,8 +1426,8 @@ void BookmarkBarView::ButtonPressed(views::BaseButton* sender) {
event_utils::DispositionFromEventFlags(sender->mouse_event_flags()),
PageTransition::AUTO_BOOKMARK);
} else {
- BookmarkBarContextMenuController::OpenAll(
- GetContainer()->GetHWND(), GetPageNavigator(), node,
+ bookmark_utils::OpenAll(
+ GetContainer()->GetHWND(), profile_, GetPageNavigator(), node,
event_utils::DispositionFromEventFlags(sender->mouse_event_flags()));
}
UserMetrics::RecordAction(L"ClickedBookmarkBarURLButton", profile_);
@@ -1438,18 +1442,30 @@ void BookmarkBarView::ShowContextMenu(View* source,
return;
}
- BookmarkNode* node = model_->GetBookmarkBarNode();
+ BookmarkNode* parent = NULL;
+ std::vector<BookmarkNode*> nodes;
if (source == other_bookmarked_button_) {
- node = model_->other_node();
+ parent = model_->other_node();
+ // Do this so the user can open all bookmarks. BookmarkContextMenu makes
+ // sure the user can edit/delete the node in this case.
+ nodes.push_back(parent);
} else if (source != this) {
// User clicked on one of the bookmark buttons, find which one they
// clicked on.
int bookmark_button_index = GetChildIndex(source);
DCHECK(bookmark_button_index != -1 &&
bookmark_button_index < GetBookmarkButtonCount());
- node = model_->GetBookmarkBarNode()->GetChild(bookmark_button_index);
+ BookmarkNode* node =
+ model_->GetBookmarkBarNode()->GetChild(bookmark_button_index);
+ nodes.push_back(node);
+ parent = node->GetParent();
+ } else {
+ parent = model_->GetBookmarkBarNode();
}
- BookmarkBarContextMenuController controller(this, node);
+ BookmarkContextMenu controller(GetContainer()->GetHWND(),
+ GetProfile(), browser(), GetPageNavigator(),
+ parent, nodes,
+ BookmarkContextMenu::BOOKMARK_BAR);
controller.RunMenuAt(x, y);
}
@@ -1492,7 +1508,7 @@ bool BookmarkBarView::IsItemChecked(int id) const {
}
void BookmarkBarView::ExecuteCommand(int id) {
- ToggleWhenVisible();
+ ToggleWhenVisible(profile_);
}
void BookmarkBarView::Observe(NotificationType type,
@@ -1652,7 +1668,7 @@ int BookmarkBarView::CalculateDropOperation(const DropTargetEvent& event,
int ops = data.GetFirstNode(profile_)
? DragDropTypes::DRAG_MOVE
: DragDropTypes::DRAG_COPY | DragDropTypes::DRAG_LINK;
- return bookmark_drag_utils::PreferredDropOperation(event, ops);
+ return bookmark_utils::PreferredDropOperation(event, ops);
}
for (int i = 0; i < GetBookmarkButtonCount() &&
@@ -1733,7 +1749,7 @@ int BookmarkBarView::CalculateDropOperation(const DropTargetEvent& event,
// Currently only accept one dragged node at a time.
return DragDropTypes::DRAG_NONE;
- if (!bookmark_drag_utils::IsValidDropLocation(profile_, data, parent, index))
+ if (!bookmark_utils::IsValidDropLocation(profile_, data, parent, index))
return DragDropTypes::DRAG_NONE;
if (data.GetFirstNode(profile_)) {
@@ -1741,7 +1757,7 @@ int BookmarkBarView::CalculateDropOperation(const DropTargetEvent& event,
return DragDropTypes::DRAG_MOVE;
} else {
// User is dragging from another app, copy.
- return bookmark_drag_utils::PreferredDropOperation(
+ return bookmark_utils::PreferredDropOperation(
event, DragDropTypes::DRAG_COPY | DragDropTypes::DRAG_LINK);
}
}
@@ -1767,8 +1783,7 @@ int BookmarkBarView::PerformDropImpl(const BookmarkDragData& data,
return DragDropTypes::DRAG_COPY | DragDropTypes::DRAG_LINK;
} else {
// Dropping a group from different profile. Always accept.
- bookmark_drag_utils::CloneDragData(model_, data.elements, parent_node,
- index);
+ bookmark_utils::CloneDragData(model_, data.elements, parent_node, index);
return DragDropTypes::DRAG_COPY;
}
}
diff --git a/chrome/browser/views/bookmark_bar_view.h b/chrome/browser/views/bookmark_bar_view.h
index 242a7f2..754d6b6 100644
--- a/chrome/browser/views/bookmark_bar_view.h
+++ b/chrome/browser/views/bookmark_bar_view.h
@@ -72,6 +72,10 @@ class BookmarkBarView : public views::View,
explicit BookmarkBarView(Profile* profile, Browser* browser);
virtual ~BookmarkBarView();
+ // Toggles whether the bookmark bar is shown only on the new tab page or on
+ // all tabs.
+ static void ToggleWhenVisible(Profile* profile);
+
static void RegisterUserPrefs(PrefService* prefs);
// Resets the profile. This removes any buttons for the current profile and
@@ -81,6 +85,9 @@ class BookmarkBarView : public views::View,
// Returns the current profile.
Profile* GetProfile() { return profile_; }
+ // Returns the current browser.
+ Browser* browser() const { return browser_; }
+
// Sets the PageNavigator that is used when the user selects an entry on
// the bookmark bar.
void SetPageNavigator(PageNavigator* navigator);
@@ -122,10 +129,6 @@ class BookmarkBarView : public views::View,
// Returns the model.
BookmarkModel* GetModel() { return model_; }
- // Toggles whether the bookmark bar is shown only on the new tab page or on
- // all tabs.
- void ToggleWhenVisible();
-
// Returns true if the bookmarks bar preference is set to 'always show', we
// use this as a shorthand way of knowing what style of bar to draw (if the
// pref is set to false but we're painting, then we must be on the new tab
diff --git a/chrome/browser/views/bookmark_bubble_view.cc b/chrome/browser/views/bookmark_bubble_view.cc
index 103df07..9136014 100644
--- a/chrome/browser/views/bookmark_bubble_view.cc
+++ b/chrome/browser/views/bookmark_bubble_view.cc
@@ -358,7 +358,8 @@ void BookmarkBubbleView::ShowEditor() {
Close();
if (node)
- BookmarkEditorView::Show(parent, profile_, NULL, node);
+ BookmarkEditorView::Show(parent, profile_, NULL, node,
+ BookmarkEditorView::SHOW_TREE);
}
void BookmarkBubbleView::SetNodeTitleFromTextField() {
diff --git a/chrome/browser/views/bookmark_editor_view.cc b/chrome/browser/views/bookmark_editor_view.cc
index 8eb7979..1179037 100644
--- a/chrome/browser/views/bookmark_editor_view.cc
+++ b/chrome/browser/views/bookmark_editor_view.cc
@@ -44,27 +44,34 @@ static const int kNewGroupButtonID = 1002;
void BookmarkEditorView::Show(HWND parent_hwnd,
Profile* profile,
BookmarkNode* parent,
- BookmarkNode* node) {
+ BookmarkNode* node,
+ Configuration configuration) {
DCHECK(profile);
- BookmarkEditorView* editor = new BookmarkEditorView(profile, parent, node);
+ BookmarkEditorView* editor =
+ new BookmarkEditorView(profile, parent, node, configuration);
editor->Show(parent_hwnd);
}
BookmarkEditorView::BookmarkEditorView(Profile* profile,
BookmarkNode* parent,
- BookmarkNode* node)
+ BookmarkNode* node,
+ Configuration configuration)
: profile_(profile),
-#pragma warning(suppress: 4355) // Okay to pass "this" here.
- new_group_button_(
- l10n_util::GetString(IDS_BOOMARK_EDITOR_NEW_FOLDER_BUTTON)),
+ tree_view_(NULL),
+ new_group_button_(NULL),
parent_(parent),
node_(node),
- running_menu_for_root_(false) {
+ running_menu_for_root_(false),
+ show_tree_(configuration == SHOW_TREE) {
DCHECK(profile);
Init();
}
BookmarkEditorView::~BookmarkEditorView() {
+ // The tree model is deleted before the view. Reset the model otherwise the
+ // tree will reference a deleted model.
+ if (tree_view_)
+ tree_view_->SetModel(NULL);
bb_model_->RemoveObserver(this);
}
@@ -96,7 +103,7 @@ bool BookmarkEditorView::Accept() {
}
bool BookmarkEditorView::AreAcceleratorsEnabled(DialogButton button) {
- return !tree_view_.GetEditingNode();
+ return !show_tree_ || !tree_view_->GetEditingNode();
}
views::View* BookmarkEditorView::GetContentsView() {
@@ -107,16 +114,23 @@ void BookmarkEditorView::Layout() {
// Let the grid layout manager lay out most of the dialog...
GetLayoutManager()->Layout(this);
+ if (!show_tree_)
+ return;
+
// Manually lay out the New Folder button in the same row as the OK/Cancel
// buttons...
gfx::Rect parent_bounds = GetParent()->GetLocalBounds(false);
- gfx::Size prefsize = new_group_button_.GetPreferredSize();
- int button_y = parent_bounds.bottom() - prefsize.height() - kButtonVEdgeMargin;
- new_group_button_.SetBounds(kPanelHorizMargin, button_y, prefsize.width(),
+ gfx::Size prefsize = new_group_button_->GetPreferredSize();
+ int button_y =
+ parent_bounds.bottom() - prefsize.height() - kButtonVEdgeMargin;
+ new_group_button_->SetBounds(kPanelHorizMargin, button_y, prefsize.width(),
prefsize.height());
}
gfx::Size BookmarkEditorView::GetPreferredSize() {
+ if (!show_tree_)
+ return views::View::GetPreferredSize();
+
return gfx::Size(views::Window::GetLocalizedContentsSize(
IDS_EDITBOOKMARK_DIALOG_WIDTH_CHARS,
IDS_EDITBOOKMARK_DIALOG_HEIGHT_LINES));
@@ -125,12 +139,12 @@ gfx::Size BookmarkEditorView::GetPreferredSize() {
void BookmarkEditorView::ViewHierarchyChanged(bool is_add,
views::View* parent,
views::View* child) {
- if (child == this) {
+ if (show_tree_ && child == this) {
// Add and remove the New Folder button from the ClientView's hierarchy.
if (is_add) {
- parent->AddChildView(&new_group_button_);
+ parent->AddChildView(new_group_button_.get());
} else {
- parent->RemoveChildView(&new_group_button_);
+ parent->RemoveChildView(new_group_button_.get());
}
}
}
@@ -164,9 +178,9 @@ void BookmarkEditorView::ButtonPressed(NativeButton* sender) {
}
void BookmarkEditorView::ExecuteCommand(int id) {
- DCHECK(tree_view_.GetSelectedNode());
+ DCHECK(tree_view_->GetSelectedNode());
if (id == IDS_EDIT) {
- tree_view_.StartEditing(tree_view_.GetSelectedNode());
+ tree_view_->StartEditing(tree_view_->GetSelectedNode());
} else {
DCHECK(id == IDS_BOOMARK_EDITOR_NEW_FOLDER_MENU_ITEM);
NewGroup();
@@ -180,7 +194,7 @@ bool BookmarkEditorView::IsCommandEnabled(int id) const {
void BookmarkEditorView::Show(HWND parent_hwnd) {
views::Window::CreateChromeWindow(parent_hwnd, gfx::Rect(), this);
UserInputChanged();
- if (bb_model_->IsLoaded())
+ if (show_tree_ && bb_model_->IsLoaded())
ExpandAndSelect();
window()->Show();
// Select all the text in the name textfield.
@@ -198,11 +212,11 @@ void BookmarkEditorView::ShowContextMenu(View* source,
int x,
int y,
bool is_mouse_gesture) {
- DCHECK(source == &tree_view_);
- if (!tree_view_.GetSelectedNode())
+ DCHECK(source == tree_view_);
+ if (!tree_view_->GetSelectedNode())
return;
running_menu_for_root_ =
- (tree_model_->GetParent(tree_view_.GetSelectedNode()) ==
+ (tree_model_->GetParent(tree_view_->GetSelectedNode()) ==
tree_model_->GetRoot());
context_menu_.reset(new Menu(this, Menu::TOPLEFT,
GetContainer()->GetHWND()));
@@ -215,28 +229,32 @@ void BookmarkEditorView::ShowContextMenu(View* source,
}
void BookmarkEditorView::Init() {
- tree_view_.SetContextMenuController(this);
bb_model_ = profile_->GetBookmarkModel();
DCHECK(bb_model_);
bb_model_->AddObserver(this);
- tree_view_.SetRootShown(false);
- // Tell View not to delete all Views declared by value.
- tree_view_.SetParentOwned(false);
- new_group_button_.SetParentOwned(false);
url_tf_.SetParentOwned(false);
title_tf_.SetParentOwned(false);
- new_group_button_.SetEnabled(false);
- new_group_button_.SetListener(this);
- new_group_button_.SetID(kNewGroupButtonID);
-
title_tf_.SetText(node_ ? node_->GetTitle() : std::wstring());
title_tf_.SetController(this);
url_tf_.SetText(node_ ? UTF8ToWide(node_->GetURL().spec()) : std::wstring());
url_tf_.SetController(this);
+ if (show_tree_) {
+ tree_view_ = new views::TreeView();
+ new_group_button_.reset(new views::NativeButton(
+ l10n_util::GetString(IDS_BOOMARK_EDITOR_NEW_FOLDER_BUTTON)));
+ new_group_button_->SetParentOwned(false);
+ tree_view_->SetContextMenuController(this);
+
+ tree_view_->SetRootShown(false);
+ new_group_button_->SetEnabled(false);
+ new_group_button_->SetListener(this);
+ new_group_button_->SetID(kNewGroupButtonID);
+ }
+
// Yummy layout code.
const int labels_column_set_id = 0;
const int single_column_view_set_id = 1;
@@ -277,14 +295,15 @@ void BookmarkEditorView::Init() {
new Label(l10n_util::GetString(IDS_BOOMARK_EDITOR_URL_LABEL)));
layout->AddView(&url_tf_);
- layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
-
- layout->StartRow(1, single_column_view_set_id);
- layout->AddView(&tree_view_);
+ if (show_tree_) {
+ layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
+ layout->StartRow(1, single_column_view_set_id);
+ layout->AddView(tree_view_);
+ }
layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
- if (bb_model_->IsLoaded())
+ if (!show_tree_ || bb_model_->IsLoaded())
Reset();
}
@@ -304,8 +323,10 @@ void BookmarkEditorView::BookmarkNodeAdded(BookmarkModel* model,
void BookmarkEditorView::BookmarkNodeRemoved(BookmarkModel* model,
BookmarkNode* parent,
- int index) {
- if ((node_ && !node_->GetParent()) || (parent_ && !parent_->GetParent())) {
+ int index,
+ BookmarkNode* node) {
+ if ((node_ && node_->HasAncestor(node)) ||
+ (parent_ && parent_->HasAncestor(node))) {
// The node, or its parent was removed. Close the dialog.
window()->Close();
} else {
@@ -314,25 +335,30 @@ void BookmarkEditorView::BookmarkNodeRemoved(BookmarkModel* model,
}
void BookmarkEditorView::Reset() {
+ if (!show_tree_) {
+ if (GetParent())
+ UserInputChanged();
+ return;
+ }
+
+ new_group_button_->SetEnabled(true);
+
// Do this first, otherwise when we invoke SetModel with the real one
// tree_view will try to invoke something on the model we just deleted.
- tree_view_.SetModel(NULL);
+ tree_view_->SetModel(NULL);
EditorNode* root_node = CreateRootNode();
tree_model_.reset(new EditorTreeModel(root_node));
- tree_view_.SetModel(tree_model_.get());
- tree_view_.SetController(this);
-
- new_group_button_.SetEnabled(true);
+ tree_view_->SetModel(tree_model_.get());
+ tree_view_->SetController(this);
context_menu_.reset();
if (GetParent()) {
ExpandAndSelect();
- UserInputChanged();
} else if (GetParent()) {
- tree_view_.ExpandAll();
+ tree_view_->ExpandAll();
}
}
GURL BookmarkEditorView::GetInputURL() const {
@@ -356,13 +382,13 @@ void BookmarkEditorView::UserInputChanged() {
void BookmarkEditorView::NewGroup() {
// Create a new entry parented to the selected item, or the bookmark
// bar if nothing is selected.
- EditorNode* parent = tree_model_->AsNode(tree_view_.GetSelectedNode());
+ EditorNode* parent = tree_model_->AsNode(tree_view_->GetSelectedNode());
if (!parent) {
NOTREACHED();
return;
}
- tree_view_.StartEditing(AddNewGroup(parent));
+ tree_view_->StartEditing(AddNewGroup(parent));
}
BookmarkEditorView::EditorNode* BookmarkEditorView::AddNewGroup(
@@ -376,7 +402,7 @@ BookmarkEditorView::EditorNode* BookmarkEditorView::AddNewGroup(
}
void BookmarkEditorView::ExpandAndSelect() {
- tree_view_.ExpandAll();
+ tree_view_->ExpandAll();
BookmarkNode* to_select = node_ ? node_->GetParent() : parent_;
int group_id_to_select = to_select->id();
@@ -386,7 +412,7 @@ void BookmarkEditorView::ExpandAndSelect() {
if (!b_node)
b_node = tree_model_->GetRoot()->GetChild(0); // Bookmark bar node.
- tree_view_.SetSelectedNode(b_node);
+ tree_view_->SetSelectedNode(b_node);
}
BookmarkEditorView::EditorNode* BookmarkEditorView::CreateRootNode() {
@@ -429,15 +455,17 @@ BookmarkEditorView::EditorNode* BookmarkEditorView::FindNodeWithID(
void BookmarkEditorView::ApplyEdits() {
DCHECK(bb_model_->IsLoaded());
- if (!tree_view_.GetSelectedNode()) {
+ EditorNode* parent = show_tree_ ?
+ tree_model_->AsNode(tree_view_->GetSelectedNode()) : NULL;
+ if (show_tree_ && !parent) {
NOTREACHED();
return;
}
- ApplyEdits(tree_model_->AsNode(tree_view_.GetSelectedNode()));
+ ApplyEdits(parent);
}
void BookmarkEditorView::ApplyEdits(EditorNode* parent) {
- DCHECK(parent);
+ DCHECK(!show_tree_ || parent);
// We're going to apply edits to the bookmark bar model, which will call us
// back. Normally when a structural edit occurs we reset the tree model.
@@ -450,6 +478,26 @@ void BookmarkEditorView::ApplyEdits(EditorNode* parent) {
BookmarkNode* old_parent = node_ ? node_->GetParent() : NULL;
const int old_index = old_parent ? old_parent->IndexOfChild(node_) : -1;
+ if (!show_tree_ ) {
+ if (!node_) {
+ bb_model_->AddURL(parent_, parent_->GetChildCount(), new_title, new_url);
+ return;
+ }
+ // If we're not showing the tree we only need to modify the node.
+ if (old_index == -1) {
+ NOTREACHED();
+ return;
+ }
+ if (new_url != node_->GetURL()) {
+ bb_model_->AddURLWithCreationTime(old_parent, old_index, new_title,
+ new_url, node_->date_added());
+ bb_model_->Remove(old_parent, old_index + 1);
+ } else {
+ bb_model_->SetTitle(node_, new_title);
+ }
+ return;
+ }
+
// Create the new groups and update the titles.
BookmarkNode* new_parent = NULL;
ApplyNameChangesAndCreateNewGroups(
@@ -523,4 +571,3 @@ void BookmarkEditorView::ApplyNameChangesAndCreateNewGroups(
parent_b_node, parent_bb_node);
}
}
-
diff --git a/chrome/browser/views/bookmark_editor_view.h b/chrome/browser/views/bookmark_editor_view.h
index 796f041..4e6e317 100644
--- a/chrome/browser/views/bookmark_editor_view.h
+++ b/chrome/browser/views/bookmark_editor_view.h
@@ -47,17 +47,28 @@ class BookmarkEditorView : public views::View,
FRIEND_TEST(BookmarkEditorViewTest, ModelsMatch);
FRIEND_TEST(BookmarkEditorViewTest, MoveToNewParent);
FRIEND_TEST(BookmarkEditorViewTest, NewURL);
+ FRIEND_TEST(BookmarkEditorViewTest, ChangeURLNoTree);
+ FRIEND_TEST(BookmarkEditorViewTest, ChangeTitleNoTree);
public:
+ // An enumeration of the possible configurations offered.
+ enum Configuration {
+ SHOW_TREE,
+ NO_TREE
+ };
+
// Shows the BookmarkEditorView editing |node|. If |node| is NULL a new entry
- // is created initially parented to |parent|.
+ // is created initially parented to |parent|. If |show_tree| is false the
+ // tree is not shown.
static void Show(HWND parent_window,
Profile* profile,
BookmarkNode* parent,
- BookmarkNode* node);
+ BookmarkNode* node,
+ Configuration configuration);
BookmarkEditorView(Profile* profile,
BookmarkNode* parent,
- BookmarkNode* node);
+ BookmarkNode* node,
+ Configuration configuration);
virtual ~BookmarkEditorView();
@@ -148,7 +159,8 @@ class BookmarkEditorView : public views::View,
int index);
virtual void BookmarkNodeRemoved(BookmarkModel* model,
BookmarkNode* parent,
- int index);
+ int index,
+ BookmarkNode* node);
virtual void BookmarkNodeChanged(BookmarkModel* model,
BookmarkNode* node) {}
virtual void BookmarkNodeFavIconLoaded(BookmarkModel* model,
@@ -222,10 +234,10 @@ class BookmarkEditorView : public views::View,
scoped_ptr<EditorTreeModel> tree_model_;
// Displays star groups.
- views::TreeView tree_view_;
+ views::TreeView* tree_view_;
// Used to create a new group.
- views::NativeButton new_group_button_;
+ scoped_ptr<views::NativeButton> new_group_button_;
// Used for editing the URL.
views::TextField url_tf_;
@@ -249,6 +261,9 @@ class BookmarkEditorView : public views::View,
// nodes.
bool running_menu_for_root_;
+ // Is the tree shown?
+ bool show_tree_;
+
DISALLOW_COPY_AND_ASSIGN(BookmarkEditorView);
};
diff --git a/chrome/browser/views/bookmark_editor_view_unittest.cc b/chrome/browser/views/bookmark_editor_view_unittest.cc
index 0a5ed68..24fb05e 100644
--- a/chrome/browser/views/bookmark_editor_view_unittest.cc
+++ b/chrome/browser/views/bookmark_editor_view_unittest.cc
@@ -79,7 +79,8 @@ class BookmarkEditorViewTest : public testing::Test {
// Makes sure the tree model matches that of the bookmark bar model.
TEST_F(BookmarkEditorViewTest, ModelsMatch) {
- BookmarkEditorView editor(profile_.get(), NULL, NULL);
+ BookmarkEditorView editor(profile_.get(), NULL, NULL,
+ BookmarkEditorView::SHOW_TREE);
BookmarkEditorView::EditorNode* editor_root = editor.tree_model_->GetRoot();
// The root should have two children, one for the bookmark bar node,
// the other for the 'other bookmarks' folder.
@@ -103,7 +104,8 @@ TEST_F(BookmarkEditorViewTest, ModelsMatch) {
// Changes the title and makes sure parent/visual order doesn't change.
TEST_F(BookmarkEditorViewTest, EditTitleKeepsPosition) {
- BookmarkEditorView editor(profile_.get(), NULL, GetNode("a"));
+ BookmarkEditorView editor(profile_.get(), NULL, GetNode("a"),
+ BookmarkEditorView::SHOW_TREE);
editor.title_tf_.SetText(L"new_a");
editor.ApplyEdits(editor.tree_model_->GetRoot()->GetChild(0));
@@ -118,7 +120,8 @@ TEST_F(BookmarkEditorViewTest, EditTitleKeepsPosition) {
TEST_F(BookmarkEditorViewTest, EditURLKeepsPosition) {
Time node_time = Time::Now() + TimeDelta::FromDays(2);
GetNode("a")->date_added_ = node_time;
- BookmarkEditorView editor(profile_.get(), NULL, GetNode("a"));
+ BookmarkEditorView editor(profile_.get(), NULL, GetNode("a"),
+ BookmarkEditorView::SHOW_TREE);
editor.url_tf_.SetText(UTF8ToWide(GURL(base_path() + "new_a").spec()));
@@ -133,7 +136,8 @@ TEST_F(BookmarkEditorViewTest, EditURLKeepsPosition) {
// Moves 'a' to be a child of the other node.
TEST_F(BookmarkEditorViewTest, ChangeParent) {
- BookmarkEditorView editor(profile_.get(), NULL, GetNode("a"));
+ BookmarkEditorView editor(profile_.get(), NULL, GetNode("a"),
+ BookmarkEditorView::SHOW_TREE);
editor.ApplyEdits(editor.tree_model_->GetRoot()->GetChild(1));
@@ -146,7 +150,8 @@ TEST_F(BookmarkEditorViewTest, ChangeParent) {
TEST_F(BookmarkEditorViewTest, ChangeParentAndURL) {
Time node_time = Time::Now() + TimeDelta::FromDays(2);
GetNode("a")->date_added_ = node_time;
- BookmarkEditorView editor(profile_.get(), NULL, GetNode("a"));
+ BookmarkEditorView editor(profile_.get(), NULL, GetNode("a"),
+ BookmarkEditorView::SHOW_TREE);
editor.url_tf_.SetText(UTF8ToWide(GURL(base_path() + "new_a").spec()));
@@ -160,7 +165,8 @@ TEST_F(BookmarkEditorViewTest, ChangeParentAndURL) {
// Creates a new folder and moves a node to it.
TEST_F(BookmarkEditorViewTest, MoveToNewParent) {
- BookmarkEditorView editor(profile_.get(), NULL, GetNode("a"));
+ BookmarkEditorView editor(profile_.get(), NULL, GetNode("a"),
+ BookmarkEditorView::SHOW_TREE);
// Create two nodes: "F21" as a child of "F2" and "F211" as a child of "F21".
BookmarkEditorView::EditorNode* f2 =
@@ -191,7 +197,8 @@ TEST_F(BookmarkEditorViewTest, MoveToNewParent) {
// Brings up the editor, creating a new URL on the bookmark bar.
TEST_F(BookmarkEditorViewTest, NewURL) {
- BookmarkEditorView editor(profile_.get(), NULL, NULL);
+ BookmarkEditorView editor(profile_.get(), NULL, NULL,
+ BookmarkEditorView::SHOW_TREE);
editor.url_tf_.SetText(UTF8ToWide(GURL(base_path() + "a").spec()));
editor.title_tf_.SetText(L"new_a");
@@ -206,3 +213,41 @@ TEST_F(BookmarkEditorViewTest, NewURL) {
EXPECT_EQ(L"new_a", new_node->GetTitle());
EXPECT_TRUE(GURL(base_path() + "a") == new_node->GetURL());
}
+
+// Brings up the editor with no tree and modifies the url.
+TEST_F(BookmarkEditorViewTest, ChangeURLNoTree) {
+ BookmarkEditorView editor(profile_.get(), NULL,
+ model_->other_node()->GetChild(0),
+ BookmarkEditorView::NO_TREE);
+
+ editor.url_tf_.SetText(UTF8ToWide(GURL(base_path() + "a").spec()));
+ editor.title_tf_.SetText(L"new_a");
+
+ editor.ApplyEdits(NULL);
+
+ BookmarkNode* other_node = profile_->GetBookmarkModel()->other_node();
+ ASSERT_EQ(2, other_node->GetChildCount());
+
+ BookmarkNode* new_node = other_node->GetChild(0);
+
+ EXPECT_EQ(L"new_a", new_node->GetTitle());
+ EXPECT_TRUE(GURL(base_path() + "a") == new_node->GetURL());
+}
+
+// Brings up the editor with no tree and modifies only the title.
+TEST_F(BookmarkEditorViewTest, ChangeTitleNoTree) {
+ BookmarkEditorView editor(profile_.get(), NULL,
+ model_->other_node()->GetChild(0),
+ BookmarkEditorView::NO_TREE);
+
+ editor.title_tf_.SetText(L"new_a");
+
+ editor.ApplyEdits(NULL);
+
+ BookmarkNode* other_node = profile_->GetBookmarkModel()->other_node();
+ ASSERT_EQ(2, other_node->GetChildCount());
+
+ BookmarkNode* new_node = other_node->GetChild(0);
+
+ EXPECT_EQ(L"new_a", new_node->GetTitle());
+}
diff --git a/chrome/browser/views/bookmark_folder_tree_view.cc b/chrome/browser/views/bookmark_folder_tree_view.cc
new file mode 100644
index 0000000..c068103
--- /dev/null
+++ b/chrome/browser/views/bookmark_folder_tree_view.cc
@@ -0,0 +1,321 @@
+// Copyright (c) 2006-2008 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/views/bookmark_folder_tree_view.h"
+
+#include "base/base_drag_source.h"
+#include "chrome/browser/bookmarks/bookmark_utils.h"
+#include "chrome/browser/bookmarks/bookmark_model.h"
+#include "chrome/browser/bookmarks/bookmark_folder_tree_model.h"
+#include "chrome/browser/profile.h"
+#include "chrome/common/drag_drop_types.h"
+#include "chrome/common/os_exchange_data.h"
+#include "chrome/views/chrome_menu.h"
+
+#include "generated_resources.h"
+
+BookmarkFolderTreeView::BookmarkFolderTreeView(Profile* profile,
+ BookmarkFolderTreeModel* model)
+ : views::TreeView(),
+ profile_(profile),
+ is_dragging_(false) {
+ SetModel(model);
+ SetEditable(false);
+ SetRootShown(false);
+ set_drag_enabled(true);
+}
+
+bool BookmarkFolderTreeView::CanDrop(const OSExchangeData& data) {
+ if (!profile_->GetBookmarkModel()->IsLoaded())
+ return false;
+
+ drop_info_.reset(new DropInfo());
+ if (!drop_info_->drag_data.Read(data))
+ return false;
+
+ // See if there are any urls being dropped.
+ for (size_t i = 0; i < drop_info_->drag_data.size(); ++i) {
+ if (drop_info_->drag_data.elements[0].is_url) {
+ drop_info_->only_folders = false;
+ break;
+ }
+ }
+
+ return true;
+}
+
+void BookmarkFolderTreeView::OnDragEntered(
+ const views::DropTargetEvent& event) {
+}
+
+int BookmarkFolderTreeView::OnDragUpdated(const views::DropTargetEvent& event) {
+ int drop_index;
+ bool drop_on;
+ FolderNode* drop_parent =
+ CalculateDropParent(event.y(), drop_info_->only_folders, &drop_index,
+ &drop_on);
+ drop_info_->drop_operation =
+ CalculateDropOperation(event, drop_parent, drop_index, drop_on);
+
+ if (drop_info_->drop_operation == DragDropTypes::DRAG_NONE) {
+ drop_parent = NULL;
+ drop_index = -1;
+ drop_on = false;
+ }
+
+ SetDropParent(drop_parent, drop_index, drop_on);
+
+ return drop_info_->drop_operation;
+}
+
+void BookmarkFolderTreeView::OnDragExited() {
+ SetDropParent(NULL, -1, false);
+
+ drop_info_.reset();
+}
+
+int BookmarkFolderTreeView::OnPerformDrop(const views::DropTargetEvent& event) {
+ OnPerformDropImpl();
+
+ int drop_operation = drop_info_->drop_operation;
+ SetDropParent(NULL, -1, false);
+ drop_info_.reset();
+ return drop_operation;
+}
+
+BookmarkNode* BookmarkFolderTreeView::GetSelectedBookmarkNode() {
+ views::TreeModelNode* selected_node = GetSelectedNode();
+ if (!selected_node)
+ return NULL;
+ return TreeNodeAsBookmarkNode(folder_model()->AsNode(selected_node));
+}
+
+LRESULT BookmarkFolderTreeView::OnNotify(int w_param, LPNMHDR l_param) {
+ switch (l_param->code) {
+ case TVN_BEGINDRAG: {
+ HTREEITEM tree_item =
+ reinterpret_cast<LPNMTREEVIEW>(l_param)->itemNew.hItem;
+ FolderNode* folder_node =
+ folder_model()->AsNode(GetNodeForTreeItem(tree_item));
+ BeginDrag(TreeNodeAsBookmarkNode(folder_node));
+ return 0; // Return value ignored.
+ }
+ }
+
+ return TreeView::OnNotify(w_param, l_param);
+}
+
+void BookmarkFolderTreeView::BeginDrag(BookmarkNode* node) {
+ BookmarkModel* model = profile_->GetBookmarkModel();
+ // Only allow the drag if the user has selected a node of type bookmark and it
+ // isn't the bookmark bar or other bookmarks folders.
+ if (!node || node == model->other_node() ||
+ node == model->GetBookmarkBarNode()) {
+ return;
+ }
+
+ std::vector<BookmarkNode*> nodes_to_drag;
+ nodes_to_drag.push_back(node);
+
+ scoped_refptr<OSExchangeData> data = new OSExchangeData;
+ BookmarkDragData(nodes_to_drag).Write(profile_, data);
+ scoped_refptr<BaseDragSource> drag_source(new BaseDragSource);
+ DWORD effects;
+ is_dragging_ = true;
+ DoDragDrop(data, drag_source,
+ DROPEFFECT_LINK | DROPEFFECT_COPY | DROPEFFECT_MOVE, &effects);
+ is_dragging_ = false;
+}
+
+FolderNode* BookmarkFolderTreeView::CalculateDropParent(int y,
+ bool only_folders,
+ int* drop_index,
+ bool* drop_on) {
+ *drop_on = false;
+ *drop_index = -1;
+ HWND hwnd = GetNativeControlHWND();
+ HTREEITEM item = TreeView_GetFirstVisible(hwnd);
+ while (item) {
+ RECT bounds;
+ TreeView_GetItemRect(hwnd, item, &bounds, TRUE);
+ if (y < bounds.bottom) {
+ views::TreeModelNode* model_node = GetNodeForTreeItem(item);
+ if (folder_model()->GetNodeType(model_node) !=
+ BookmarkFolderTreeModel::BOOKMARK) {
+ // Only allow drops on bookmark nodes.
+ return NULL;
+ }
+
+ FolderNode* node = folder_model()->AsNode(model_node);
+ if (!only_folders || !node->GetParent() ||
+ !node->GetParent()->GetParent()) {
+ // If some of the elements being dropped are urls, then we only allow
+ // dropping on a folder. Similarly you can't drop between the
+ // bookmark bar and other folder nodes.
+ *drop_on = true;
+ *drop_index = node->GetChildCount();
+ return node;
+ }
+
+ // Drop contains all folders, allow them to be dropped between
+ // folders.
+ if (y < bounds.top + views::MenuItemView::kDropBetweenPixels) {
+ *drop_index = node->GetParent()->IndexOfChild(node);
+ return node->GetParent();
+ }
+ if (y >= bounds.bottom - views::MenuItemView::kDropBetweenPixels) {
+ if (IsExpanded(node) && folder_model()->GetChildCount(node) > 0) {
+ // The node is expanded and has children, treat the drop as occurring
+ // as the first child. This is done to avoid the selection highlight
+ // dancing around when dragging over expanded folders. Without this
+ // the highlight jumps past the last expanded child of node.
+ *drop_index = 0;
+ return node;
+ }
+ *drop_index = node->GetParent()->IndexOfChild(node) + 1;
+ return node->GetParent();
+ }
+ *drop_on = true;
+ *drop_index = node->GetChildCount();
+ return node;
+ }
+ item = TreeView_GetNextVisible(hwnd, item);
+ }
+ return NULL;
+}
+
+int BookmarkFolderTreeView::CalculateDropOperation(
+ const views::DropTargetEvent& event,
+ FolderNode* drop_parent,
+ int drop_index,
+ bool drop_on) {
+ if (!drop_parent)
+ return DragDropTypes::DRAG_NONE;
+
+ if (drop_info_->drag_data.IsFromProfile(profile_)) {
+ int bookmark_model_drop_index =
+ FolderIndexToBookmarkIndex(drop_parent, drop_index, drop_on);
+ if (!bookmark_utils::IsValidDropLocation(
+ profile_, drop_info_->drag_data,
+ TreeNodeAsBookmarkNode(drop_parent),
+ bookmark_model_drop_index)) {
+ return DragDropTypes::DRAG_NONE;
+ }
+
+ // Data from the same profile. Prefer move, but do copy if the user wants
+ // that.
+ if (event.IsControlDown())
+ return DragDropTypes::DRAG_COPY;
+
+ return DragDropTypes::DRAG_MOVE;
+ }
+ // We're going to copy, but return an operation compatible with the source
+ // operations so that the user can drop.
+ return bookmark_utils::PreferredDropOperation(
+ event, DragDropTypes::DRAG_COPY | DragDropTypes::DRAG_LINK);
+}
+
+void BookmarkFolderTreeView::OnPerformDropImpl() {
+ BookmarkNode* parent_node = TreeNodeAsBookmarkNode(drop_info_->drop_parent);
+ int drop_index = FolderIndexToBookmarkIndex(
+ drop_info_->drop_parent, drop_info_->drop_index, drop_info_->drop_on);
+ BookmarkModel* model = profile_->GetBookmarkModel();
+ // If the data is not from this profile we return an operation compatible
+ // with the source. As such, we need to need to check the data here too.
+ if (!drop_info_->drag_data.IsFromProfile(profile_) ||
+ drop_info_->drop_operation == DragDropTypes::DRAG_COPY) {
+ bookmark_utils::CloneDragData(model, drop_info_->drag_data.elements,
+ parent_node, drop_index);
+ return;
+ }
+
+ // else, move.
+ std::vector<BookmarkNode*> nodes = drop_info_->drag_data.GetNodes(profile_);
+ if (nodes.empty())
+ return;
+
+ for (size_t i = 0; i < nodes.size(); ++i) {
+ model->Move(nodes[i], parent_node, drop_index);
+ // Reset the drop_index, just in case the index didn't really change.
+ drop_index = parent_node->IndexOfChild(nodes[i]) + 1;
+ if (nodes[i]->is_folder()) {
+ Expand(folder_model()->GetFolderNodeForBookmarkNode(nodes[i]));
+ }
+ }
+
+ if (is_dragging_ && nodes[0]->is_folder()) {
+ // We're the drag source. Select the node that was moved.
+ SetSelectedNode(folder_model()->GetFolderNodeForBookmarkNode(nodes[0]));
+ }
+}
+
+void BookmarkFolderTreeView::SetDropParent(FolderNode* node,
+ int drop_index,
+ bool drop_on) {
+ if (drop_info_->drop_parent == node &&
+ drop_info_->drop_index == drop_index &&
+ drop_info_->drop_on == drop_on) {
+ return;
+ }
+ // Remove the indicator over the previous location.
+ if (drop_info_->drop_on) {
+ HTREEITEM item = GetTreeItemForNode(drop_info_->drop_parent);
+ if (item)
+ TreeView_SetItemState(GetNativeControlHWND(), item, 0, TVIS_DROPHILITED);
+ } else if (drop_info_->drop_index != -1) {
+ TreeView_SetInsertMark(GetNativeControlHWND(), NULL, FALSE);
+ }
+
+ drop_info_->drop_parent = node;
+ drop_info_->drop_index = drop_index;
+ drop_info_->drop_on = drop_on;
+
+ // And show the new indicator.
+ if (drop_info_->drop_on) {
+ HTREEITEM item = GetTreeItemForNode(drop_info_->drop_parent);
+ if (item) {
+ TreeView_SetItemState(GetNativeControlHWND(), item, TVIS_DROPHILITED,
+ TVIS_DROPHILITED);
+ }
+ } else if (drop_info_->drop_index != -1) {
+ BOOL after = FALSE;
+ if (folder_model()->GetChildCount(drop_info_->drop_parent) ==
+ drop_info_->drop_index) {
+ after = TRUE;
+ node =
+ folder_model()->GetChild(drop_info_->drop_parent,
+ drop_info_->drop_index - 1);
+ } else {
+ node =
+ folder_model()->GetChild(drop_info_->drop_parent,
+ drop_info_->drop_index);
+ }
+ HTREEITEM item = GetTreeItemForNode(node);
+ if (item)
+ TreeView_SetInsertMark(GetNativeControlHWND(), item, after);
+ }
+}
+
+BookmarkFolderTreeModel* BookmarkFolderTreeView::folder_model() const {
+ return static_cast<BookmarkFolderTreeModel*>(model());
+}
+
+BookmarkNode* BookmarkFolderTreeView::TreeNodeAsBookmarkNode(FolderNode* node) {
+ return folder_model()->TreeNodeAsBookmarkNode(node);
+}
+
+int BookmarkFolderTreeView::FolderIndexToBookmarkIndex(FolderNode* node,
+ int index,
+ bool drop_on) {
+ BookmarkNode* parent_node = TreeNodeAsBookmarkNode(node);
+ if (drop_on || index == node->GetChildCount())
+ return parent_node->GetChildCount();
+
+ if (index != 0) {
+ return parent_node->IndexOfChild(
+ TreeNodeAsBookmarkNode(node->GetChild(index)));
+ }
+
+ return 0;
+}
diff --git a/chrome/browser/views/bookmark_folder_tree_view.h b/chrome/browser/views/bookmark_folder_tree_view.h
new file mode 100644
index 0000000..ae12b2c
--- /dev/null
+++ b/chrome/browser/views/bookmark_folder_tree_view.h
@@ -0,0 +1,117 @@
+// Copyright (c) 2006-2008 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.
+
+#ifndef CHROME_BROWSER_VIEWS_BOOKMARK_FOLDER_TREE_VIEW_H_
+#define CHROME_BROWSER_VIEWS_BOOKMARK_FOLDER_TREE_VIEW_H_
+
+#include "chrome/browser/bookmarks/bookmark_drag_data.h"
+#include "chrome/browser/bookmarks/bookmark_folder_tree_model.h"
+#include "chrome/views/tree_view.h"
+
+class BookmarkModel;
+class BookmarkNode;
+class OSExchangeData;
+class Profile;
+
+// BookmarkFolderTreeView is used to show the contents of a
+// BookmarkFolderTreeModel and provides drag and drop support.
+class BookmarkFolderTreeView : public views::TreeView {
+ public:
+ BookmarkFolderTreeView(Profile* profile, BookmarkFolderTreeModel* model);
+
+ // Drag and drop methods.
+ virtual bool CanDrop(const OSExchangeData& data);
+ virtual void OnDragEntered(const views::DropTargetEvent& event);
+ virtual int OnDragUpdated(const views::DropTargetEvent& event);
+ virtual void OnDragExited();
+ virtual int OnPerformDrop(const views::DropTargetEvent& event);
+
+ // Returns the selected node as a BookmarkNode. This returns NULL if the
+ // selected node is not of type BookmarkFolderTreeModel::BOOKMARK or
+ // nothing is selected.
+ BookmarkNode* GetSelectedBookmarkNode();
+
+ protected:
+ // Overriden to start a drag.
+ virtual LRESULT OnNotify(int w_param, LPNMHDR l_param);
+
+ private:
+ // Provides information used during a drop.
+ struct DropInfo {
+ DropInfo()
+ : drop_parent(NULL),
+ only_folders(true),
+ drop_index(-1),
+ drop_operation(0),
+ drop_on(false) {}
+
+ // Parent the mouse is over.
+ FolderNode* drop_parent;
+
+ // Drag data.
+ BookmarkDragData drag_data;
+
+ // Does drag_data consists of folders only.
+ bool only_folders;
+
+ // If drop_on is false, this is the index to add the child.
+ // WARNING: this index is in terms of the BookmarkFolderTreeModel, which is
+ // not the same as the BookmarkModel.
+ int drop_index;
+
+ // Operation for the drop.
+ int drop_operation;
+
+ // Is the user dropping on drop_parent? If false, the mouse is positioned
+ // such that the drop should insert the data at position drop_index in
+ // drop_parent.
+ bool drop_on;
+ };
+
+ // Starts a drag operation for the specified node.
+ void BeginDrag(BookmarkNode* node);
+
+ // Calculates the drop parent. Returns NULL if not over a valid drop
+ // location. See DropInfos documentation for a description of |drop_index|
+ // and |drop_on|.
+ FolderNode* CalculateDropParent(int y,
+ bool only_folders,
+ int* drop_index,
+ bool* drop_on);
+
+ // Determines the appropriate drop operation. This returns DRAG_NONE
+ // if the location is not valid.
+ int CalculateDropOperation(const views::DropTargetEvent& event,
+ FolderNode* drop_parent,
+ int drop_index,
+ bool drop_on);
+
+ // Performs the drop operation.
+ void OnPerformDropImpl();
+
+ // Sets the parent of the drop operation.
+ void SetDropParent(FolderNode* node, int drop_index, bool drop_on);
+
+ // Returns the model as a BookmarkFolderTreeModel.
+ BookmarkFolderTreeModel* folder_model() const;
+
+ // Converts FolderNode into a BookmarkNode.
+ BookmarkNode* TreeNodeAsBookmarkNode(FolderNode* node);
+
+ // Converts an index in terms of the BookmarkFolderTreeModel to an index
+ // in terms of the BookmarkModel.
+ int FolderIndexToBookmarkIndex(FolderNode* node, int index, bool drop_on);
+
+ Profile* profile_;
+
+ // Non-null during a drop.
+ scoped_ptr<DropInfo> drop_info_;
+
+ // Did we originate the drag?
+ bool is_dragging_;
+
+ DISALLOW_COPY_AND_ASSIGN(BookmarkFolderTreeView);
+};
+
+#endif // CHROME_BROWSER_VIEWS_BOOKMARK_FOLDER_TREE_VIEW_H_
diff --git a/chrome/browser/views/bookmark_manager_view.cc b/chrome/browser/views/bookmark_manager_view.cc
new file mode 100644
index 0000000..050323a
--- /dev/null
+++ b/chrome/browser/views/bookmark_manager_view.cc
@@ -0,0 +1,383 @@
+// Copyright (c) 2006-2008 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/views/bookmark_manager_view.h"
+
+#include <algorithm>
+
+#include "base/gfx/skia_utils.h"
+#include "chrome/app/locales/locale_settings.h"
+#include "chrome/browser/bookmarks/bookmark_context_menu.h"
+#include "chrome/browser/bookmarks/bookmark_folder_tree_model.h"
+#include "chrome/browser/bookmarks/bookmark_model.h"
+#include "chrome/browser/bookmarks/bookmark_table_model.h"
+#include "chrome/browser/bookmarks/bookmark_utils.h"
+#include "chrome/browser/browser_list.h"
+#include "chrome/browser/browser_process.h"
+#include "chrome/browser/profile.h"
+#include "chrome/browser/views/bookmark_editor_view.h"
+#include "chrome/browser/views/bookmark_folder_tree_view.h"
+#include "chrome/browser/views/bookmark_table_view.h"
+#include "chrome/browser/views/standard_layout.h"
+#include "chrome/common/gfx/chrome_canvas.h"
+#include "chrome/common/gfx/color_utils.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/pref_service.h"
+#include "chrome/views/container_win.h"
+#include "chrome/views/grid_layout.h"
+#include "chrome/views/single_split_view.h"
+#include "chrome/views/window.h"
+
+#include "generated_resources.h"
+
+// If non-null, there is an open editor and this is the window it is contained
+// in it.
+static views::Window* open_window = NULL;
+// And this is the manager contained in it.
+static BookmarkManagerView* manager = NULL;
+
+// Delay, in ms, between when the user types and when we run the search.
+static const int kSearchDelayMS = 200;
+
+BookmarkManagerView::BookmarkManagerView(Profile* profile)
+ : profile_(profile->GetOriginalProfile()),
+ table_view_(NULL),
+ tree_view_(NULL),
+ search_factory_(this) {
+ search_tf_ = new views::TextField();
+ search_tf_->set_default_width_in_chars(40);
+
+ table_view_ = new BookmarkTableView(profile_, NULL);
+ table_view_->SetObserver(this);
+ table_view_->SetContextMenuController(this);
+
+ tree_view_ = new BookmarkFolderTreeView(profile_, NULL);
+ tree_view_->SetController(this);
+ tree_view_->SetContextMenuController(this);
+
+ views::SingleSplitView* split_view =
+ new views::SingleSplitView(tree_view_, table_view_);
+
+ views::GridLayout* layout = new views::GridLayout(this);
+ SetLayoutManager(layout);
+ const int search_cs_id = 1;
+ const int split_cs_id = 2;
+ layout->SetInsets(kPanelVertMargin, 0, 0, 0);
+ views::ColumnSet* column_set = layout->AddColumnSet(search_cs_id);
+ column_set->AddColumn(views::GridLayout::TRAILING, views::GridLayout::CENTER,
+ 1, views::GridLayout::USE_PREF, 0, 0);
+ column_set->AddPaddingColumn(0, kButtonHEdgeMargin);
+
+ column_set = layout->AddColumnSet(split_cs_id);
+ column_set->AddColumn(views::GridLayout::FILL, views::GridLayout::FILL, 1,
+ views::GridLayout::USE_PREF, 0, 0);
+
+ layout->StartRow(0, search_cs_id);
+ layout->AddView(search_tf_);
+
+ layout->AddPaddingRow(0, kRelatedControlVerticalSpacing);
+
+ layout->StartRow(1, split_cs_id);
+ layout->AddView(split_view);
+
+ BookmarkModel* bookmark_model = profile_->GetBookmarkModel();
+ if (!bookmark_model->IsLoaded())
+ bookmark_model->AddObserver(this);
+}
+
+BookmarkManagerView::~BookmarkManagerView() {
+ if (!GetBookmarkModel()->IsLoaded()) {
+ GetBookmarkModel()->RemoveObserver(this);
+ } else {
+ // The models are deleted before the views. Make sure we set the models of
+ // the views to NULL so that they aren't left holding a reference to a
+ // deleted model.
+ table_view_->SetModel(NULL);
+ tree_view_->SetModel(NULL);
+ }
+ manager = NULL;
+ open_window = NULL;
+}
+
+// static
+void BookmarkManagerView::RegisterPrefs(PrefService* prefs) {
+ prefs->RegisterDictionaryPref(prefs::kBookmarkManagerPlacement);
+}
+
+// static
+void BookmarkManagerView::Show(Profile* profile) {
+ if (!profile->GetBookmarkModel())
+ return;
+
+ if (open_window != NULL) {
+ open_window->MoveToFront(true);
+ return;
+ }
+
+ // Both of these will be deleted when the dialog closes.
+ manager = new BookmarkManagerView(profile);
+
+ // Create the window.
+ open_window = views::Window::CreateChromeWindow(NULL, gfx::Rect(), manager);
+ // Let the manager know it's parented.
+ manager->PrepareForShow();
+ // And show it.
+ open_window->Show();
+}
+
+// static
+BookmarkManagerView* BookmarkManagerView::current() {
+ return manager;
+}
+
+void BookmarkManagerView::SelectInTree(BookmarkNode* node) {
+ if (!node)
+ return;
+
+ BookmarkNode* parent = node->is_url() ? node->GetParent() : node;
+ FolderNode* folder_node = tree_model_->GetFolderNodeForBookmarkNode(parent);
+ if (!folder_node) {
+ NOTREACHED();
+ return;
+ }
+
+ tree_view_->SetSelectedNode(folder_node);
+
+ if (node->is_url()) {
+ int index = table_model_->IndexOfNode(node);
+ if (index != -1)
+ table_view_->Select(index);
+ }
+}
+
+
+std::vector<BookmarkNode*> BookmarkManagerView::GetSelectedTableNodes() {
+ std::vector<BookmarkNode*> nodes;
+ for (views::TableView::iterator i = table_view_->SelectionBegin();
+ i != table_view_->SelectionEnd(); ++i) {
+ nodes.push_back(table_model_->GetNodeForRow(*i));
+ }
+ // TableViews iterator iterates in reverse order. Reverse the nodes so they
+ // are opened in visual order.
+ std::reverse(nodes.begin(), nodes.end());
+ return nodes;
+}
+
+void BookmarkManagerView::PaintBackground(ChromeCanvas* canvas) {
+ canvas->drawColor(color_utils::GetSysSkColor(COLOR_3DFACE),
+ SkPorterDuff::kSrc_Mode);
+}
+
+gfx::Size BookmarkManagerView::GetPreferredSize() {
+ return gfx::Size(views::Window::GetLocalizedContentsSize(
+ IDS_BOOKMARK_MANAGER_DIALOG_WIDTH_CHARS,
+ IDS_BOOKMARK_MANAGER_DIALOG_HEIGHT_LINES));
+}
+
+std::wstring BookmarkManagerView::GetWindowTitle() const {
+ return l10n_util::GetString(IDS_BOOKMARK_MANAGER_TITLE);
+}
+
+void BookmarkManagerView::SaveWindowPosition(const CRect& bounds,
+ bool maximized,
+ bool always_on_top) {
+ window()->SaveWindowPositionToPrefService(g_browser_process->local_state(),
+ prefs::kBookmarkManagerPlacement,
+ bounds, maximized, always_on_top);
+}
+
+bool BookmarkManagerView::RestoreWindowPosition(CRect* bounds,
+ bool* maximized,
+ bool* always_on_top) {
+ return window()->RestoreWindowPositionFromPrefService(
+ g_browser_process->local_state(),
+ prefs::kBookmarkManagerPlacement,
+ bounds, maximized, always_on_top);
+}
+
+void BookmarkManagerView::OnDoubleClick() {
+ std::vector<BookmarkNode*> nodes = GetSelectedTableNodes();
+ if (nodes.empty())
+ return;
+ if (nodes.size() == 1 && nodes[0]->is_folder()) {
+ // Double click on a folder descends into the folder.
+ SelectInTree(nodes[0]);
+ return;
+ }
+ // TODO(sky): OnDoubleClick needs a handle to the current mouse event so that
+ // we can use
+ // event_utils::DispositionFromEventFlags(sender->mouse_event_flags()) .
+ bookmark_utils::OpenAll(
+ GetContainer()->GetHWND(), profile_, NULL, nodes, CURRENT_TAB);
+}
+
+void BookmarkManagerView::OnTableViewDelete(views::TableView* table) {
+ std::vector<BookmarkNode*> nodes = GetSelectedTableNodes();
+ if (nodes.empty())
+ return;
+ for (size_t i = 0; i < nodes.size(); ++i) {
+ GetBookmarkModel()->Remove(nodes[i]->GetParent(),
+ nodes[i]->GetParent()->IndexOfChild(nodes[i]));
+ }
+}
+
+void BookmarkManagerView::OnTreeViewSelectionChanged(
+ views::TreeView* tree_view) {
+ views::TreeModelNode* node = tree_view_->GetSelectedNode();
+
+ BookmarkTableModel* new_table_model = NULL;
+ BookmarkNode* table_parent_node = NULL;
+
+ if (node) {
+ switch (tree_model_->GetNodeType(node)) {
+ case BookmarkFolderTreeModel::BOOKMARK:
+ table_parent_node = tree_model_->TreeNodeAsBookmarkNode(node);
+ new_table_model =
+ BookmarkTableModel::CreateBookmarkTableModelForFolder(
+ profile_->GetBookmarkModel(),
+ table_parent_node);
+ break;
+
+ case BookmarkFolderTreeModel::RECENTLY_BOOKMARKED:
+ new_table_model = BookmarkTableModel::CreateRecentlyBookmarkedModel(
+ profile_->GetBookmarkModel());
+ break;
+
+ case BookmarkFolderTreeModel::SEARCH:
+ search_factory_.RevokeAll();
+ new_table_model = CreateSearchTableModel();
+ break;
+
+ default:
+ NOTREACHED();
+ break;
+ }
+ }
+
+ SetTableModel(new_table_model, table_parent_node);
+}
+
+void BookmarkManagerView::Loaded(BookmarkModel* model) {
+ model->RemoveObserver(this);
+ LoadedImpl();
+}
+
+void BookmarkManagerView::ContentsChanged(views::TextField* sender,
+ const std::wstring& new_contents) {
+ search_factory_.RevokeAll();
+ MessageLoop::current()->PostDelayedTask(FROM_HERE,
+ search_factory_.NewRunnableMethod(&BookmarkManagerView::PerformSearch),
+ kSearchDelayMS);
+}
+
+void BookmarkManagerView::HandleKeystroke(views::TextField* sender,
+ UINT message, TCHAR key,
+ UINT repeat_count,
+ UINT flags) {
+ if (key == VK_RETURN) {
+ PerformSearch();
+ search_tf_->SelectAll();
+ }
+}
+
+void BookmarkManagerView::ShowContextMenu(views::View* source,
+ int x,
+ int y,
+ bool is_mouse_gesture) {
+ if (!GetBookmarkModel()->IsLoaded())
+ return;
+
+ if (source == table_view_) {
+ std::vector<BookmarkNode*> nodes = GetSelectedTableNodes();
+ if (nodes.empty())
+ return;
+
+ BookmarkNode* parent = tree_view_->GetSelectedBookmarkNode();
+ BookmarkContextMenu menu(GetContainer()->GetHWND(), profile_, NULL, NULL,
+ parent, nodes,
+ BookmarkContextMenu::BOOKMARK_MANAGER_TABLE);
+ menu.RunMenuAt(x, y);
+ } else if (source == tree_view_) {
+ BookmarkNode* node = tree_view_->GetSelectedBookmarkNode();
+ if (!node)
+ return;
+ std::vector<BookmarkNode*> nodes;
+ nodes.push_back(node);
+ BookmarkContextMenu menu(GetContainer()->GetHWND(), profile_, NULL, NULL,
+ node, nodes,
+ BookmarkContextMenu::BOOKMARK_MANAGER_TREE);
+ menu.RunMenuAt(x, y);
+ }
+}
+
+BookmarkTableModel* BookmarkManagerView::CreateSearchTableModel() {
+ std::wstring search_text = search_tf_->GetText();
+ if (search_text.empty())
+ return NULL;
+ return BookmarkTableModel::CreateSearchTableModel(GetBookmarkModel(),
+ search_text);
+}
+
+void BookmarkManagerView::SetTableModel(BookmarkTableModel* new_table_model,
+ BookmarkNode* parent_node) {
+ // Be sure and reset the model on the view before updating table_model_.
+ // Otherwise the view will attempt to use the deleted model when we set the
+ // new one.
+ table_view_->SetModel(NULL);
+ table_view_->SetShowPathColumn(!parent_node);
+ table_view_->SetModel(new_table_model);
+ table_view_->set_parent_node(parent_node);
+ table_model_.reset(new_table_model);
+}
+
+void BookmarkManagerView::PerformSearch() {
+ search_factory_.RevokeAll();
+ // Reset the controller, otherwise when we change the selection we'll get
+ // notified and update the model twice.
+ tree_view_->SetController(NULL);
+ tree_view_->SetSelectedNode(tree_model_->search_node());
+ tree_view_->SetController(this);
+ SetTableModel(CreateSearchTableModel(), NULL);
+}
+
+void BookmarkManagerView::PrepareForShow() {
+ views::SingleSplitView* split_view =
+ static_cast<views::SingleSplitView*>(table_view_->GetParent());
+ // Give a third of the space to the tree.
+ split_view->set_divider_x(split_view->width() / 3);
+ if (!GetBookmarkModel()->IsLoaded()) {
+ search_tf_->SetReadOnly(true);
+ return;
+ }
+
+ LoadedImpl();
+}
+
+void BookmarkManagerView::LoadedImpl() {
+ BookmarkModel* bookmark_model = GetBookmarkModel();
+ BookmarkNode* bookmark_bar_node = bookmark_model->GetBookmarkBarNode();
+ table_model_.reset(
+ BookmarkTableModel::CreateBookmarkTableModelForFolder(bookmark_model,
+ bookmark_bar_node));
+ table_view_->SetModel(table_model_.get());
+ table_view_->set_parent_node(bookmark_bar_node);
+
+ tree_model_.reset(new BookmarkFolderTreeModel(bookmark_model));
+ tree_view_->SetModel(tree_model_.get());
+
+ tree_view_->ExpandAll();
+
+ tree_view_->SetSelectedNode(
+ tree_model_->GetFolderNodeForBookmarkNode(bookmark_bar_node));
+
+ search_tf_->SetReadOnly(false);
+ search_tf_->SetController(this);
+
+ Layout();
+ SchedulePaint();
+}
+
+BookmarkModel* BookmarkManagerView::GetBookmarkModel() const {
+ return profile_->GetBookmarkModel();
+}
diff --git a/chrome/browser/views/bookmark_manager_view.h b/chrome/browser/views/bookmark_manager_view.h
new file mode 100644
index 0000000..c397b33
--- /dev/null
+++ b/chrome/browser/views/bookmark_manager_view.h
@@ -0,0 +1,160 @@
+// Copyright (c) 2006-2008 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.
+
+#ifndef CHROME_BROWSER_VIEWS_BOOKMARK_MANAGER_VIEW_H_
+#define CHROME_BROWSER_VIEWS_BOOKMARK_MANAGER_VIEW_H_
+
+#include "base/task.h"
+#include "chrome/browser/bookmarks/bookmark_model.h"
+#include "chrome/views/table_view.h"
+#include "chrome/views/text_field.h"
+#include "chrome/views/tree_view.h"
+#include "chrome/views/view.h"
+#include "chrome/views/window_delegate.h"
+#include "webkit/glue/window_open_disposition.h"
+
+class BookmarkFolderTreeModel;
+class BookmarkFolderTreeView;
+class BookmarkTableModel;
+class BookmarkTableView;
+class PrefService;
+class Profile;
+
+// A view that lets the user manage their bookmarks. The bookmark manager
+// shows a tree on the left with a table on the right. The tree shows the
+// folder nodes and the table the contents of the selected tree node. The
+// tree is a BookmarkFolderTreeView and the table a BookmarkTableView. A
+// text field is also provided that allows the user to search the contents
+// of the bookmarks.
+class BookmarkManagerView : public views::View,
+ public views::WindowDelegate,
+ public views::TreeViewController,
+ public views::TableViewObserver,
+ public views::TextField::Controller,
+ public BookmarkModelObserver,
+ public views::ContextMenuController {
+ public:
+ explicit BookmarkManagerView(Profile* profile);
+ virtual ~BookmarkManagerView();
+
+ static void RegisterPrefs(PrefService* prefs);
+
+ // Shows the bookmark manager. Only one bookmark manager exists.
+ static void Show(Profile* profile);
+
+ // Returns the current manager, or NULL if the manager is not showing.
+ static BookmarkManagerView* current();
+
+ // Selects the specified node in the tree. If node is a URL it's parent is
+ // selected and node is selected in the table.
+ void SelectInTree(BookmarkNode* node);
+
+ // Returns the selection of the table.
+ std::vector<BookmarkNode*> GetSelectedTableNodes();
+
+ virtual void PaintBackground(ChromeCanvas* canvas);
+
+ virtual gfx::Size GetPreferredSize();
+
+ // WindowDelegate.
+ virtual bool CanResize() const { return true; }
+ virtual bool CanMaximize() const { return true; }
+ virtual std::wstring GetWindowTitle() const;
+ virtual void SaveWindowPosition(const CRect& bounds,
+ bool maximized,
+ bool always_on_top);
+ virtual bool RestoreWindowPosition(CRect* bounds,
+ bool* maximized,
+ bool* always_on_top);
+ virtual View* GetContentsView() { return this; }
+ // TODO(sky): implement these when we have an icon.
+ //virtual SkBitmap GetWindowIcon();
+ //virtual bool ShouldShowWindowIcon() const { return true; }
+
+ private:
+ // TableViewObserver methods.
+ virtual void OnSelectionChanged() {}
+ // Overriden to open the selected table nodes in the current browser.
+ virtual void OnDoubleClick();
+ virtual void OnTableViewDelete(views::TableView* table);
+
+ // TreeViewController method.
+ virtual void OnTreeViewSelectionChanged(views::TreeView* tree_view);
+
+ // BookmarkModelObserver. We're only installed as an observer until the
+ // bookmarks are loaded.
+ virtual void Loaded(BookmarkModel* model);
+ virtual void BookmarkModelBeingDeleted(BookmarkModel* model) {}
+ virtual void BookmarkNodeMoved(BookmarkModel* model,
+ BookmarkNode* old_parent,
+ int old_index,
+ BookmarkNode* new_parent,
+ int new_index) {}
+ virtual void BookmarkNodeAdded(BookmarkModel* model,
+ BookmarkNode* parent,
+ int index) {}
+ virtual void BookmarkNodeRemoved(BookmarkModel* model,
+ BookmarkNode* parent,
+ int index) {}
+ virtual void BookmarkNodeRemoved(BookmarkModel* model,
+ BookmarkNode* parent,
+ int old_index,
+ BookmarkNode* node) {}
+ virtual void BookmarkNodeChanged(BookmarkModel* model,
+ BookmarkNode* node) {}
+ virtual void BookmarkNodeFavIconLoaded(BookmarkModel* model,
+ BookmarkNode* node) {}
+
+ // TextField::Controller methods.
+ // Starts a timer to search for the search text.
+ virtual void ContentsChanged(views::TextField* sender,
+ const std::wstring& new_contents);
+ // If return has been pressed this performs an immediate search.
+ virtual void HandleKeystroke(views::TextField* sender,
+ UINT message, TCHAR key, UINT repeat_count,
+ UINT flags);
+
+ // ContextMenuController.
+ virtual void ShowContextMenu(views::View* source,
+ int x,
+ int y,
+ bool is_mouse_gesture);
+
+ // Creates the table model to use when searching. This returns NULL if there
+ // is no search text.
+ BookmarkTableModel* CreateSearchTableModel();
+
+ // Sets the model of the table and its parent node.
+ void SetTableModel(BookmarkTableModel* new_table_model,
+ BookmarkNode* parent_node);
+
+ // Sets the table's model to the results of CreateSearchTableModel and selects
+ // the search node in the tree.
+ void PerformSearch();
+
+ // Invoked prior to showing. If the BookmarkModel is loaded this invokes
+ // LoadedImpl.
+ void PrepareForShow();
+
+ // Invoked when we're parented and the BookmarkModel is loaded. Sets the
+ // models of the tree/table appropriately and expands the necessary nodes.
+ void LoadedImpl();
+
+ // Returns the BookmarkModel.
+ BookmarkModel* GetBookmarkModel() const;
+
+ Profile* profile_;
+ BookmarkTableView* table_view_;
+ BookmarkFolderTreeView* tree_view_;
+ scoped_ptr<BookmarkTableModel> table_model_;
+ scoped_ptr<BookmarkFolderTreeModel> tree_model_;
+ views::TextField* search_tf_;
+
+ // Factory used for delaying search.
+ ScopedRunnableMethodFactory<BookmarkManagerView> search_factory_;
+
+ DISALLOW_COPY_AND_ASSIGN(BookmarkManagerView);
+};
+
+#endif // CHROME_BROWSER_VIEWS_BOOKMARK_MANAGER_VIEW_H_
diff --git a/chrome/browser/views/bookmark_table_view.cc b/chrome/browser/views/bookmark_table_view.cc
new file mode 100644
index 0000000..ffda218
--- /dev/null
+++ b/chrome/browser/views/bookmark_table_view.cc
@@ -0,0 +1,394 @@
+// Copyright (c) 2006-2008 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/views/bookmark_table_view.h"
+
+#include "base/base_drag_source.h"
+#include "chrome/browser/bookmarks/bookmark_utils.h"
+#include "chrome/browser/bookmarks/bookmark_model.h"
+#include "chrome/browser/bookmarks/bookmark_table_model.h"
+#include "chrome/browser/profile.h"
+#include "chrome/common/drag_drop_types.h"
+#include "chrome/common/os_exchange_data.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/pref_service.h"
+#include "chrome/views/chrome_menu.h"
+
+#include "generated_resources.h"
+
+namespace {
+
+// Height of the drop indicator used when dropping between rows.
+const int kDropHighlightHeight = 2;
+
+int GetWidthOfColumn(const std::vector<views::TableColumn>& columns,
+ const std::vector<int> widths,
+ int column_id) {
+ for (size_t i = 0; i < columns.size(); ++i) {
+ if (columns[i].id == column_id)
+ return widths[i];
+ }
+ NOTREACHED();
+ return -1;
+}
+
+} // namespace
+
+BookmarkTableView::BookmarkTableView(Profile* profile,
+ BookmarkTableModel* model)
+ : views::TableView(model, std::vector<views::TableColumn>(),
+ views::ICON_AND_TEXT, false, true, true),
+ profile_(profile),
+ show_path_column_(false) {
+ UpdateColumns();
+}
+
+// static
+void BookmarkTableView::RegisterUserPrefs(PrefService* prefs) {
+ prefs->RegisterIntegerPref(prefs::kBookmarkTableNameWidth1, -1);
+ prefs->RegisterIntegerPref(prefs::kBookmarkTableURLWidth1, -1);
+ prefs->RegisterIntegerPref(prefs::kBookmarkTableNameWidth2, -1);
+ prefs->RegisterIntegerPref(prefs::kBookmarkTableURLWidth2, -1);
+ prefs->RegisterIntegerPref(prefs::kBookmarkTablePathWidth, -1);
+}
+
+bool BookmarkTableView::CanDrop(const OSExchangeData& data) {
+ if (!parent_node_ || !profile_->GetBookmarkModel()->IsLoaded())
+ return false;
+
+ drop_info_.reset(new DropInfo());
+ if (!drop_info_->drag_data.Read(data))
+ return false;
+
+ // Don't allow the user to drop an ancestor of the parent node onto the
+ // parent node. This would create a cycle, which is definitely a no-no.
+ std::vector<BookmarkNode*> nodes = drop_info_->drag_data.GetNodes(profile_);
+ for (size_t i = 0; i < nodes.size(); ++i) {
+ if (parent_node_->HasAncestor(nodes[i]))
+ return false;
+ }
+ return true;
+}
+
+void BookmarkTableView::OnDragEntered(const views::DropTargetEvent& event) {
+}
+
+int BookmarkTableView::OnDragUpdated(const views::DropTargetEvent& event) {
+ if (!parent_node_)
+ return false;
+
+ LVHITTESTINFO hit_info = {0};
+ hit_info.pt.x = event.x();
+ hit_info.pt.y = event.y();
+ // TODO(sky): need to support auto-scroll and all that good stuff.
+
+ int drop_index;
+ bool drop_on;
+ drop_index = CalculateDropIndex(event.y(), &drop_on);
+
+ drop_info_->drop_operation =
+ CalculateDropOperation(event, drop_index, drop_on);
+
+ if (drop_info_->drop_operation == DragDropTypes::DRAG_NONE) {
+ drop_index = -1;
+ drop_on = false;
+ }
+
+ SetDropIndex(drop_index, drop_on);
+
+ return drop_info_->drop_operation;
+}
+
+void BookmarkTableView::OnDragExited() {
+ SetDropIndex(-1, false);
+ drop_info_.reset();
+}
+
+int BookmarkTableView::OnPerformDrop(const views::DropTargetEvent& event) {
+ OnPerformDropImpl();
+ int drop_operation = drop_info_->drop_operation;
+ SetDropIndex(-1, false);
+ drop_info_.reset();
+ return drop_operation;
+}
+
+BookmarkTableModel* BookmarkTableView::bookmark_table_model() const {
+ return static_cast<BookmarkTableModel*>(model());
+}
+
+void BookmarkTableView::SaveColumnConfiguration() {
+ PrefService* prefs = profile_->GetPrefs();
+ if (!prefs)
+ return;
+
+ if (show_path_column_) {
+ prefs->SetInteger(prefs::kBookmarkTableNameWidth2,
+ GetColumnWidth(IDS_BOOKMARK_TABLE_TITLE));
+ prefs->SetInteger(prefs::kBookmarkTableURLWidth2,
+ GetColumnWidth(IDS_BOOKMARK_TABLE_URL));
+ prefs->SetInteger(prefs::kBookmarkTablePathWidth,
+ GetColumnWidth(IDS_BOOKMARK_TABLE_PATH));
+ } else {
+ prefs->SetInteger(prefs::kBookmarkTableNameWidth1,
+ GetColumnWidth(IDS_BOOKMARK_TABLE_TITLE));
+ prefs->SetInteger(prefs::kBookmarkTableURLWidth1,
+ GetColumnWidth(IDS_BOOKMARK_TABLE_URL));
+ }
+}
+
+void BookmarkTableView::SetShowPathColumn(bool show_path_column) {
+ if (show_path_column == show_path_column_)
+ return;
+
+ SaveColumnConfiguration();
+
+ show_path_column_ = show_path_column;
+ UpdateColumns();
+}
+
+void BookmarkTableView::PostPaint() {
+ if (!drop_info_.get() || drop_info_->drop_index == -1 ||
+ drop_info_->drop_on) {
+ return;
+ }
+
+ RECT bounds = GetDropBetweenHighlightRect(drop_info_->drop_index);
+ HDC dc = GetDC(GetNativeControlHWND());
+ HBRUSH brush = CreateSolidBrush(GetSysColor(COLOR_WINDOWTEXT));
+ FillRect(dc, &bounds, brush);
+ DeleteObject(brush);
+ ReleaseDC(GetNativeControlHWND(), dc);
+}
+
+LRESULT BookmarkTableView::OnNotify(int w_param, LPNMHDR l_param) {
+ switch(l_param->code) {
+ case LVN_BEGINDRAG:
+ BeginDrag();
+ return 0; // Return value doesn't matter for this message.
+ }
+
+ return TableView::OnNotify(w_param, l_param);
+}
+
+void BookmarkTableView::BeginDrag() {
+ std::vector<BookmarkNode*> nodes_to_drag;
+ for (TableView::iterator i = SelectionBegin(); i != SelectionEnd(); ++i)
+ nodes_to_drag.push_back(bookmark_table_model()->GetNodeForRow(*i));
+ if (nodes_to_drag.empty())
+ return; // Nothing to drag.
+
+ scoped_refptr<OSExchangeData> data = new OSExchangeData;
+ BookmarkDragData(nodes_to_drag).Write(profile_, data);
+ scoped_refptr<BaseDragSource> drag_source(new BaseDragSource);
+ DWORD effects;
+ DoDragDrop(data, drag_source,
+ DROPEFFECT_LINK | DROPEFFECT_COPY | DROPEFFECT_MOVE, &effects);
+}
+
+int BookmarkTableView::CalculateDropOperation(
+ const views::DropTargetEvent& event,
+ int drop_index,
+ bool drop_on) {
+ if (drop_info_->drag_data.IsFromProfile(profile_)) {
+ // Data from the same profile. Prefer move, but do copy if the user wants
+ // that.
+ if (event.IsControlDown())
+ return DragDropTypes::DRAG_COPY;
+
+ int real_drop_index;
+ BookmarkNode* drop_parent = GetDropParentAndIndex(drop_index, drop_on,
+ &real_drop_index);
+ if (!bookmark_utils::IsValidDropLocation(
+ profile_, drop_info_->drag_data, drop_parent, real_drop_index)) {
+ return DragDropTypes::DRAG_NONE;
+ }
+ return DragDropTypes::DRAG_MOVE;
+ }
+ // We're going to copy, but return an operation compatible with the source
+ // operations so that the user can drop.
+ return bookmark_utils::PreferredDropOperation(
+ event, DragDropTypes::DRAG_COPY | DragDropTypes::DRAG_LINK);
+}
+
+void BookmarkTableView::OnPerformDropImpl() {
+ int drop_index;
+ BookmarkNode* drop_parent = GetDropParentAndIndex(
+ drop_info_->drop_index, drop_info_->drop_on, &drop_index);
+ BookmarkModel* model = profile_->GetBookmarkModel();
+ int min_selection;
+ int max_selection;
+ // If the data is not from this profile we return an operation compatible
+ // with the source. As such, we need to need to check the data here too.
+ if (!drop_info_->drag_data.IsFromProfile(profile_) ||
+ drop_info_->drop_operation == DragDropTypes::DRAG_COPY) {
+ bookmark_utils::CloneDragData(model, drop_info_->drag_data.elements,
+ drop_parent, drop_index);
+ min_selection = drop_index;
+ max_selection = drop_index +
+ static_cast<int>(drop_info_->drag_data.elements.size());
+ } else {
+ // else, move.
+ std::vector<BookmarkNode*> nodes = drop_info_->drag_data.GetNodes(profile_);
+ if (nodes.empty())
+ return;
+
+ for (size_t i = 0; i < nodes.size(); ++i) {
+ model->Move(nodes[i], drop_parent, drop_index);
+ // Reset the drop_index, just in case the index didn't really change.
+ drop_index = drop_parent->IndexOfChild(nodes[i]) + 1;
+ }
+ min_selection = drop_parent->IndexOfChild(nodes[0]);
+ max_selection = min_selection + static_cast<int>(nodes.size());
+ }
+ if (min_selection < RowCount() && max_selection < RowCount()) {
+ // Select the moved/copied rows.
+ Select(min_selection);
+ if (min_selection + 1 < max_selection) {
+ // SetSelectedState doesn't send notification, so we manually do it.
+ for (int i = min_selection + 1; i < max_selection; ++i)
+ SetSelectedState(i, true);
+ if (observer())
+ observer()->OnSelectionChanged();
+ }
+ }
+}
+
+void BookmarkTableView::SetDropIndex(int index, bool drop_on) {
+ if (drop_info_->drop_index == index && drop_info_->drop_on == drop_on)
+ return;
+
+ UpdateDropIndex(drop_info_->drop_index, drop_info_->drop_on, false);
+
+ drop_info_->drop_index = index;
+ drop_info_->drop_on = drop_on;
+
+ UpdateDropIndex(drop_info_->drop_index, drop_info_->drop_on, true);
+}
+
+void BookmarkTableView::UpdateDropIndex(int index, bool drop_on, bool turn_on) {
+ if (index == -1)
+ return;
+
+ if (drop_on) {
+ ListView_SetItemState(GetNativeControlHWND(), index,
+ turn_on ? LVIS_DROPHILITED : 0, LVIS_DROPHILITED);
+ } else {
+ RECT bounds = GetDropBetweenHighlightRect(index);
+ InvalidateRect(GetNativeControlHWND(), &bounds, FALSE);
+ }
+}
+
+int BookmarkTableView::CalculateDropIndex(int y, bool* drop_on) {
+ *drop_on = false;
+ HWND hwnd = GetNativeControlHWND();
+ int row_count = RowCount();
+ int top_index = ListView_GetTopIndex(hwnd);
+ if (row_count == 0 || top_index < 0)
+ return 0;
+
+ for (int i = top_index; i < row_count; ++i) {
+ RECT bounds;
+ ListView_GetItemRect(hwnd, i, &bounds, LVIR_BOUNDS);
+ if (y < bounds.top)
+ return i;
+ if (y < bounds.bottom) {
+ if (bookmark_table_model()->GetNodeForRow(i)->is_folder()) {
+ if (y < bounds.top + views::MenuItemView::kDropBetweenPixels)
+ return i;
+ if (y >= bounds.bottom - views::MenuItemView::kDropBetweenPixels)
+ return i + 1;
+ *drop_on = true;
+ return i;
+ }
+ if (y < (bounds.bottom - bounds.top) / 2 + bounds.top)
+ return i;
+ return i + 1;
+ }
+ }
+ return row_count;
+}
+
+BookmarkNode* BookmarkTableView::GetDropParentAndIndex(int visual_drop_index,
+ bool drop_on,
+ int* index) {
+ if (drop_on) {
+ BookmarkNode* parent = parent_node_->GetChild(visual_drop_index);
+ *index = parent->GetChildCount();
+ return parent;
+ }
+ *index = visual_drop_index;
+ return parent_node_;
+}
+
+RECT BookmarkTableView::GetDropBetweenHighlightRect(int index) {
+ RECT bounds = { 0 };
+ if (RowCount() == 0) {
+ bounds.top = content_offset();
+ bounds.left = 0;
+ bounds.right = width();
+ } else if (index >= RowCount()) {
+ ListView_GetItemRect(GetNativeControlHWND(), index - 1, &bounds,
+ LVIR_BOUNDS);
+ bounds.top = bounds.bottom - kDropHighlightHeight / 2;
+ } else {
+ ListView_GetItemRect(GetNativeControlHWND(), index, &bounds, LVIR_BOUNDS);
+ bounds.top -= kDropHighlightHeight / 2;
+ }
+ bounds.bottom = bounds.top + kDropHighlightHeight;
+ return bounds;
+}
+void BookmarkTableView::UpdateColumns() {
+ PrefService* prefs = profile_->GetPrefs();
+ views::TableColumn name_column =
+ views::TableColumn(IDS_BOOKMARK_TABLE_TITLE, views::TableColumn::LEFT,
+ -1);
+ views::TableColumn url_column =
+ views::TableColumn(IDS_BOOKMARK_TABLE_URL, views::TableColumn::LEFT, -1);
+ views::TableColumn path_column =
+ views::TableColumn(IDS_BOOKMARK_TABLE_PATH, views::TableColumn::LEFT, -1);
+
+ std::vector<views::TableColumn> columns;
+ if (show_path_column_) {
+ int name_width = -1;
+ int url_width = -1;
+ int path_width = -1;
+ if (prefs) {
+ name_width = prefs->GetInteger(prefs::kBookmarkTableNameWidth2);
+ url_width = prefs->GetInteger(prefs::kBookmarkTableURLWidth2);
+ path_width = prefs->GetInteger(prefs::kBookmarkTablePathWidth);
+ }
+ if (name_width != -1 && url_width != -1 && path_width != -1) {
+ name_column.width = name_width;
+ url_column.width = url_width;
+ path_column.width = path_width;
+ } else {
+ name_column.percent = .5;
+ url_column.percent = .25;
+ path_column.percent= .25;
+ }
+ columns.push_back(name_column);
+ columns.push_back(url_column);
+ columns.push_back(path_column);
+ } else {
+ int name_width = -1;
+ int url_width = -1;
+ if (prefs) {
+ name_width = prefs->GetInteger(prefs::kBookmarkTableNameWidth1);
+ url_width = prefs->GetInteger(prefs::kBookmarkTableURLWidth1);
+ }
+ if (name_width != -1 && url_width != -1) {
+ name_column.width = name_width;
+ url_column.width = url_width;
+ } else {
+ name_column.percent = .5;
+ url_column.percent = .5;
+ }
+ columns.push_back(name_column);
+ columns.push_back(url_column);
+ }
+ SetColumns(columns);
+ for (size_t i = 0; i < columns.size(); ++i)
+ SetColumnVisibility(columns[i].id, true);
+ OnModelChanged();
+}
diff --git a/chrome/browser/views/bookmark_table_view.h b/chrome/browser/views/bookmark_table_view.h
new file mode 100644
index 0000000..a01f1af
--- /dev/null
+++ b/chrome/browser/views/bookmark_table_view.h
@@ -0,0 +1,121 @@
+// Copyright (c) 2006-2008 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.
+
+#ifndef CHROME_BROWSER_VIEWS_BOOKMARK_TABLE_VIEW_H_
+#define CHROME_BROWSER_VIEWS_BOOKMARK_TABLE_VIEW_H_
+
+#include "chrome/browser/bookmarks/bookmark_drag_data.h"
+#include "chrome/views/menu.h"
+#include "chrome/views/table_view.h"
+
+class BookmarkModel;
+class BookmarkNode;
+class BookmarkTableModel;
+class OSExchangeData;
+class PrefService;
+class Profile;
+
+// A TableView implementation that shows a BookmarkTableModel.
+// BookmarkTableView provides drag and drop support as well as showing a
+// separate set of columns when showing search results.
+class BookmarkTableView : public views::TableView {
+ public:
+ BookmarkTableView(Profile* profile, BookmarkTableModel* model);
+
+ static void RegisterUserPrefs(PrefService* prefs);
+
+ // Drag and drop methods.
+ virtual bool CanDrop(const OSExchangeData& data);
+ virtual void OnDragEntered(const views::DropTargetEvent& event);
+ virtual int OnDragUpdated(const views::DropTargetEvent& event);
+ virtual void OnDragExited();
+ virtual int OnPerformDrop(const views::DropTargetEvent& event);
+
+ // Sets the parent of the nodes being displayed. For search and recently
+ // found results |parent| is NULL.
+ void set_parent_node(BookmarkNode* parent) { parent_node_ = parent; }
+
+ // Sets whether the path column should be shown. The path column is shown
+ // for search results and recently bookmarked.
+ void SetShowPathColumn(bool show_path_column);
+
+ // The model as a BookmarkTableModel.
+ BookmarkTableModel* bookmark_table_model() const;
+
+ // Saves the widths of the table columns.
+ void SaveColumnConfiguration();
+
+ protected:
+ // Overriden to draw a drop indicator when dropping between rows.
+ virtual void PostPaint();
+
+ // Overriden to start a drag.
+ virtual LRESULT OnNotify(int w_param, LPNMHDR l_param);
+
+ private:
+ // Information used when we're the drop target of a drag and drop operation.
+ struct DropInfo {
+ DropInfo() : drop_index(-1), drop_operation(0), drop_on(false) {}
+
+ BookmarkDragData drag_data;
+
+ // Index into the table model of where the drop should occur.
+ int drop_index;
+
+ // The drop operation that should occur.
+ int drop_operation;
+
+ // Whether the drop is on drop_index or before it.
+ bool drop_on;
+ };
+
+ // Starts a drop operation.
+ void BeginDrag();
+
+ // Returns the drop operation for the specified index.
+ int CalculateDropOperation(const views::DropTargetEvent& event,
+ int drop_index,
+ bool drop_on);
+
+ // Performs the drop operation.
+ void OnPerformDropImpl();
+
+ // Sets the drop index. If index differs from the current drop index
+ // UpdateDropIndex is invoked for the old and new values.
+ void SetDropIndex(int index, bool drop_on);
+
+ // Invoked from SetDropIndex to update the state for the specified index
+ // and schedule a paint. If |turn_on| is true the highlight is being turned
+ // on for the specified index, otherwise it is being turned off.
+ void UpdateDropIndex(int index, bool drop_on, bool turn_on);
+
+ // Determines the drop index for the specified location.
+ int CalculateDropIndex(int y, bool* drop_on);
+
+ // Returns the BookmarkNode the drop should occur on, or NULL if not over
+ // a valid location.
+ BookmarkNode* GetDropParentAndIndex(int visual_drop_index,
+ bool drop_on,
+ int* index);
+
+ // Returns the bounds of drop indicator shown when the drop is to occur
+ // between rows (drop_on is false).
+ RECT GetDropBetweenHighlightRect(int index);
+
+ // Resets the columns. BookmarkTableView shows different sets of columns.
+ // See ShowPathColumn for details.
+ void UpdateColumns();
+
+ Profile* profile_;
+
+ BookmarkNode* parent_node_;
+
+ scoped_ptr<DropInfo> drop_info_;
+
+ bool show_path_column_;
+
+ DISALLOW_COPY_AND_ASSIGN(BookmarkTableView);
+};
+
+#endif // CHROME_BROWSER_VIEWS_BOOKMARK_TABLE_VIEW_H_
diff --git a/chrome/browser/views/browser_views.vcproj b/chrome/browser/views/browser_views.vcproj
index cfce335..4a47e9a 100644
--- a/chrome/browser/views/browser_views.vcproj
+++ b/chrome/browser/views/browser_views.vcproj
@@ -470,6 +470,30 @@
>
</File>
<File
+ RelativePath=".\bookmark_folder_tree_view.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\bookmark_folder_tree_view.h"
+ >
+ </File>
+ <File
+ RelativePath=".\bookmark_manager_view.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\bookmark_manager_view.h"
+ >
+ </File>
+ <File
+ RelativePath=".\bookmark_table_view.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\bookmark_table_view.h"
+ >
+ </File>
+ <File
RelativePath=".\bookmark_bubble_view.cc"
>
</File>
diff --git a/chrome/browser/views/toolbar_view.cc b/chrome/browser/views/toolbar_view.cc
index a7f26e3..d87e205 100644
--- a/chrome/browser/views/toolbar_view.cc
+++ b/chrome/browser/views/toolbar_view.cc
@@ -546,6 +546,8 @@ void BrowserToolbarView::RunAppMenu(const CPoint& pt, HWND hwnd) {
menu.AppendSeparator();
menu.AppendMenuItemWithLabel(IDC_SHOW_HISTORY,
l10n_util::GetString(IDS_SHOW_HISTORY));
+ menu.AppendMenuItemWithLabel(IDC_SHOW_BOOKMARK_MANAGER,
+ l10n_util::GetString(IDS_BOOKMARK_MANAGER));
menu.AppendMenuItemWithLabel(IDC_SHOW_DOWNLOADS,
l10n_util::GetString(IDS_SHOW_DOWNLOADS));
menu.AppendSeparator();