summaryrefslogtreecommitdiffstats
path: root/chrome/browser
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
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')
-rw-r--r--chrome/browser/bookmark_bar_context_menu_controller.cc423
-rw-r--r--chrome/browser/bookmark_bar_context_menu_controller.h59
-rw-r--r--chrome/browser/bookmark_bar_context_menu_controller_test.cc141
-rw-r--r--chrome/browser/bookmarks/bookmark_context_menu.cc458
-rw-r--r--chrome/browser/bookmarks/bookmark_context_menu.h105
-rw-r--r--chrome/browser/bookmarks/bookmark_context_menu_test.cc271
-rw-r--r--chrome/browser/bookmarks/bookmark_drag_utils.cc91
-rw-r--r--chrome/browser/bookmarks/bookmark_folder_tree_model.cc2
-rw-r--r--chrome/browser/bookmarks/bookmark_utils.cc241
-rw-r--r--chrome/browser/bookmarks/bookmark_utils.h (renamed from chrome/browser/bookmarks/bookmark_drag_utils.h)36
-rw-r--r--chrome/browser/browser.vcproj20
-rw-r--r--chrome/browser/browser_commands.cc22
-rw-r--r--chrome/browser/browser_prefs.cc4
-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
27 files changed, 2882 insertions, 849 deletions
diff --git a/chrome/browser/bookmark_bar_context_menu_controller.cc b/chrome/browser/bookmark_bar_context_menu_controller.cc
deleted file mode 100644
index 975a4ec..0000000
--- a/chrome/browser/bookmark_bar_context_menu_controller.cc
+++ /dev/null
@@ -1,423 +0,0 @@
-// 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/bookmark_bar_context_menu_controller.h"
-
-#include "chrome/browser/bookmarks/bookmark_model.h"
-#include "chrome/browser/browser.h"
-#include "chrome/browser/browser_list.h"
-#include "chrome/browser/page_navigator.h"
-#include "chrome/browser/profile.h"
-#include "chrome/browser/tab_contents.h"
-#include "chrome/browser/user_metrics.h"
-#include "chrome/browser/views/bookmark_editor_view.h"
-#include "chrome/browser/views/input_window.h"
-#include "chrome/common/l10n_util.h"
-#include "chrome/common/pref_names.h"
-#include "chrome/common/pref_service.h"
-#include "chrome/views/container.h"
-#include "chrome/views/window.h"
-
-#include "chromium_strings.h"
-#include "generated_resources.h"
-
-namespace {
-
-// Number of bookmarks we'll open before prompting the user to see if they
-// really want to open all.
-const int kNumURLsBeforePrompting = 15;
-
-// Returns true if the specified node is of type URL, or has a descendant
-// of type URL.
-bool NodeHasURLs(BookmarkNode* node) {
- if (node->GetType() == history::StarredEntry::URL)
- return true;
-
- for (int i = 0; i < node->GetChildCount(); ++i) {
- if (NodeHasURLs(node->GetChild(i)))
- return true;
- }
- return false;
-}
-
-// Implementation of OpenAll. Opens all nodes of type URL and recurses for
-// groups.
-void OpenAllImpl(BookmarkNode* node,
- WindowOpenDisposition initial_disposition,
- PageNavigator** navigator,
- bool* opened_url) {
- if (node->GetType() == history::StarredEntry::URL) {
- WindowOpenDisposition disposition;
- if (*opened_url)
- disposition = NEW_BACKGROUND_TAB;
- else
- disposition = initial_disposition;
- (*navigator)->OpenURL(node->GetURL(), GURL(), disposition,
- PageTransition::AUTO_BOOKMARK);
- if (!*opened_url) {
- *opened_url = true;
- // We opened the first URL which may have opened a new window or clobbered
- // the current page, reset the navigator just to be sure.
- Browser* new_browser = BrowserList::GetLastActive();
- if (new_browser) {
- TabContents* current_tab = new_browser->GetSelectedTabContents();
- DCHECK(new_browser && current_tab);
- if (new_browser && current_tab)
- *navigator = current_tab;
- } // else, new_browser == NULL, which happens during testing.
- }
- } else {
- // Group, recurse through children.
- for (int i = 0; i < node->GetChildCount(); ++i) {
- OpenAllImpl(node->GetChild(i), initial_disposition, navigator,
- opened_url);
- }
- }
-}
-
-// Returns the number of descendants of node that are of type url.
-int DescendantURLCount(BookmarkNode* node) {
- int result = 0;
- for (int i = 0; i < node->GetChildCount(); ++i) {
- BookmarkNode* child = node->GetChild(i);
- if (child->is_url())
- result++;
- else
- result += DescendantURLCount(child);
- }
- return result;
-}
-
-bool ShouldOpenAll(HWND parent, BookmarkNode* node) {
- int descendant_count = DescendantURLCount(node);
- if (descendant_count < kNumURLsBeforePrompting)
- return true;
-
- std::wstring message =
- l10n_util::GetStringF(IDS_BOOKMARK_BAR_SHOULD_OPEN_ALL,
- IntToWString(descendant_count));
- return (MessageBox(parent, message.c_str(),
- l10n_util::GetString(IDS_PRODUCT_NAME).c_str(),
- MB_YESNO | MB_ICONWARNING | MB_TOPMOST) == IDYES);
-}
-
-// EditFolderController -------------------------------------------------------
-
-// EditFolderController manages the editing and/or creation of a folder. If the
-// user presses ok, the name change is committed to the database.
-//
-// EditFolderController deletes itself when the window is closed.
-//
-class EditFolderController : public InputWindowDelegate,
- public BookmarkBarView::ModelChangedListener {
- public:
- EditFolderController(BookmarkBarView* view,
- BookmarkNode* node,
- int visual_order,
- bool is_new)
- : view_(view),
- node_(node),
- visual_order_(visual_order),
- is_new_(is_new) {
- DCHECK(is_new_ || node);
- window_ = CreateInputWindow(view->GetContainer()->GetHWND(), this);
- view_->SetModelChangedListener(this);
- }
-
- void Show() {
- window_->Show();
- }
-
- virtual void ModelChanged() {
- window_->Close();
- }
-
- private:
- virtual std::wstring GetTextFieldLabel() {
- return l10n_util::GetString(IDS_BOOMARK_BAR_EDIT_FOLDER_LABEL);
- }
-
- virtual std::wstring GetTextFieldContents() {
- if (is_new_)
- return l10n_util::GetString(IDS_BOOMARK_EDITOR_NEW_FOLDER_NAME);
- return node_->GetTitle();
- }
-
- virtual bool IsValid(const std::wstring& text) {
- return !text.empty();
- }
-
- virtual void InputAccepted(const std::wstring& text) {
- view_->ClearModelChangedListenerIfEquals(this);
- BookmarkModel* model = view_->GetProfile()->GetBookmarkModel();
- if (is_new_)
- model->AddGroup(node_, visual_order_, text);
- else
- model->SetTitle(node_, text);
- }
-
- virtual void InputCanceled() {
- view_->ClearModelChangedListenerIfEquals(this);
- }
-
- virtual void WindowClosing() {
- view_->ClearModelChangedListenerIfEquals(this);
- delete this;
- }
-
- virtual std::wstring GetWindowTitle() const {
- return is_new_ ?
- l10n_util::GetString(IDS_BOOMARK_FOLDER_EDITOR_WINDOW_TITLE_NEW) :
- l10n_util::GetString(IDS_BOOMARK_FOLDER_EDITOR_WINDOW_TITLE);
- }
-
- virtual views::View* GetContentsView() {
- return view_;
- }
-
- BookmarkBarView* view_;
-
- // If is_new is true, this is the parent to create the new node under.
- // Otherwise this is the node to change the title of.
- BookmarkNode* node_;
-
- int visual_order_;
- bool is_new_;
- views::Window* window_;
-
- DISALLOW_EVIL_CONSTRUCTORS(EditFolderController);
-};
-
-} // namespace
-
-// BookmarkBarContextMenuController -------------------------------------------
-
-// static
-void BookmarkBarContextMenuController::OpenAll(
- HWND parent,
- PageNavigator* navigator,
- BookmarkNode* node,
- WindowOpenDisposition initial_disposition) {
- if (!ShouldOpenAll(parent, node))
- return;
-
- PageNavigator* nav = navigator;
- bool opened_url = false;
- OpenAllImpl(node, initial_disposition, &nav, &opened_url);
-}
-
-BookmarkBarContextMenuController::BookmarkBarContextMenuController(
- BookmarkBarView* view,
- BookmarkNode* node)
- : view_(view),
- node_(node),
- menu_(this) {
- if (node->GetType() == history::StarredEntry::URL) {
- menu_.AppendMenuItemWithLabel(
- IDS_BOOMARK_BAR_OPEN_IN_NEW_TAB,
- l10n_util::GetString(IDS_BOOMARK_BAR_OPEN_IN_NEW_TAB));
- menu_.AppendMenuItemWithLabel(
- IDS_BOOMARK_BAR_OPEN_IN_NEW_WINDOW,
- l10n_util::GetString(IDS_BOOMARK_BAR_OPEN_IN_NEW_WINDOW));
- menu_.AppendMenuItemWithLabel(
- IDS_BOOMARK_BAR_OPEN_INCOGNITO,
- l10n_util::GetString(IDS_BOOMARK_BAR_OPEN_INCOGNITO));
- } else {
- menu_.AppendMenuItemWithLabel(
- IDS_BOOMARK_BAR_OPEN_ALL,
- l10n_util::GetString(IDS_BOOMARK_BAR_OPEN_ALL));
- menu_.AppendMenuItemWithLabel(
- IDS_BOOMARK_BAR_OPEN_ALL_NEW_WINDOW,
- l10n_util::GetString(IDS_BOOMARK_BAR_OPEN_ALL_NEW_WINDOW));
- menu_.AppendMenuItemWithLabel(
- IDS_BOOMARK_BAR_OPEN_ALL_INCOGNITO,
- l10n_util::GetString(IDS_BOOMARK_BAR_OPEN_ALL_INCOGNITO));
- }
- menu_.AppendSeparator();
-
- if (node->GetParent() !=
- view->GetProfile()->GetBookmarkModel()->root_node()) {
- menu_.AppendMenuItemWithLabel(IDS_BOOKMARK_BAR_EDIT,
- l10n_util::GetString(IDS_BOOKMARK_BAR_EDIT));
- menu_.AppendMenuItemWithLabel(
- IDS_BOOKMARK_BAR_REMOVE,
- l10n_util::GetString(IDS_BOOKMARK_BAR_REMOVE));
- }
-
- menu_.AppendMenuItemWithLabel(
- IDS_BOOMARK_BAR_ADD_NEW_BOOKMARK,
- l10n_util::GetString(IDS_BOOMARK_BAR_ADD_NEW_BOOKMARK));
- menu_.AppendMenuItemWithLabel(
- IDS_BOOMARK_BAR_NEW_FOLDER,
- l10n_util::GetString(IDS_BOOMARK_BAR_NEW_FOLDER));
- menu_.AppendSeparator();
- menu_.AppendMenuItem(IDS_BOOMARK_BAR_ALWAYS_SHOW,
- l10n_util::GetString(IDS_BOOMARK_BAR_ALWAYS_SHOW),
- views::MenuItemView::CHECKBOX);
-}
-
-void BookmarkBarContextMenuController::RunMenuAt(int x, int y) {
- // Record the current ModelChangedListener. It will be non-null when we're
- // used as the context menu for another menu.
- ModelChangedListener* last_listener = view_->GetModelChangedListener();
-
- view_->SetModelChangedListener(this);
-
- // width/height don't matter here.
- menu_.RunMenuAt(view_->GetContainer()->GetHWND(), gfx::Rect(x, y, 0, 0),
- views::MenuItemView::TOPLEFT, true);
-
- if (view_->GetModelChangedListener() == this)
- view_->SetModelChangedListener(last_listener);
-}
-
-void BookmarkBarContextMenuController::ModelChanged() {
- menu_.Cancel();
-}
-
-void BookmarkBarContextMenuController::ExecuteCommand(int id) {
- Profile* profile = view_->GetProfile();
-
- switch (id) {
- case IDS_BOOMARK_BAR_OPEN_INCOGNITO:
- UserMetrics::RecordAction(L"BookmarkBar_ContextMenu_OpenInIncognito",
- profile);
-
- view_->GetPageNavigator()->OpenURL(node_->GetURL(), GURL(),
- OFF_THE_RECORD,
- PageTransition::AUTO_BOOKMARK);
- break;
-
- case IDS_BOOMARK_BAR_OPEN_IN_NEW_WINDOW:
- UserMetrics::RecordAction(L"BookmarkBar_ContextMenu_OpenInNewWindow",
- profile);
-
- view_->GetPageNavigator()->OpenURL(node_->GetURL(), GURL(), NEW_WINDOW,
- PageTransition::AUTO_BOOKMARK);
- break;
-
- case IDS_BOOMARK_BAR_OPEN_IN_NEW_TAB:
- UserMetrics::RecordAction(L"BookmarkBar_ContextMenu_OpenInNewTab",
- profile);
-
- view_->GetPageNavigator()->OpenURL(node_->GetURL(), GURL(),
- NEW_FOREGROUND_TAB,
- PageTransition::AUTO_BOOKMARK);
- break;
-
- case IDS_BOOMARK_BAR_OPEN_ALL:
- case IDS_BOOMARK_BAR_OPEN_ALL_INCOGNITO:
- case IDS_BOOMARK_BAR_OPEN_ALL_NEW_WINDOW: {
- WindowOpenDisposition initial_disposition;
- if (id == IDS_BOOMARK_BAR_OPEN_ALL) {
- initial_disposition = CURRENT_TAB;
- UserMetrics::RecordAction(L"BookmarkBar_ContextMenu_OpenAll",
- profile);
- } else if (id == IDS_BOOMARK_BAR_OPEN_ALL_NEW_WINDOW) {
- initial_disposition = NEW_WINDOW;
- UserMetrics::RecordAction(
- L"BookmarkBar_ContextMenu_OpenAllInNewWindow", profile);
- } else {
- initial_disposition = OFF_THE_RECORD;
- UserMetrics::RecordAction(
- L"BookmarkBar_ContextMenu_OpenAllIncognito", profile);
- }
-
- // GetContainer is NULL during testing.
- HWND parent_hwnd = view_->GetContainer() ?
- view_->GetContainer()->GetHWND() : 0;
-
- OpenAll(parent_hwnd, view_->GetPageNavigator(), node_,
- initial_disposition);
- break;
- }
-
- case IDS_BOOKMARK_BAR_EDIT:
- UserMetrics::RecordAction(L"BookmarkBar_ContextMenu_Edit", profile);
-
- if (node_->GetType() == history::StarredEntry::URL) {
- BookmarkEditorView::Show(view_->GetContainer()->GetHWND(),
- view_->GetProfile(), NULL, node_);
- } else {
- // Controller deletes itself when done.
- EditFolderController* controller = new EditFolderController(
- view_, node_, -1, false);
- controller->Show();
- }
- break;
-
- case IDS_BOOKMARK_BAR_REMOVE: {
- UserMetrics::RecordAction(L"BookmarkBar_ContextMenu_Remove", profile);
-
- view_->GetModel()->Remove(node_->GetParent(),
- node_->GetParent()->IndexOfChild(node_));
- break;
- }
-
- case IDS_BOOMARK_BAR_ADD_NEW_BOOKMARK: {
- UserMetrics::RecordAction(L"BookmarkBar_ContextMenu_Add", profile);
-
- BookmarkEditorView::Show(view_->GetContainer()->GetHWND(),
- view_->GetProfile(), node_, NULL);
- break;
- }
-
- case IDS_BOOMARK_BAR_NEW_FOLDER: {
- UserMetrics::RecordAction(L"BookmarkBar_ContextMenu_NewFolder",
- profile);
-
- int visual_order;
- BookmarkNode* parent =
- GetParentAndVisualOrderForNewNode(&visual_order);
- GetParentAndVisualOrderForNewNode(&visual_order);
- // Controller deletes itself when done.
- EditFolderController* controller =
- new EditFolderController(view_, parent, visual_order, true);
- controller->Show();
- break;
- }
-
- case IDS_BOOMARK_BAR_ALWAYS_SHOW:
- view_->ToggleWhenVisible();
- break;
-
- default:
- NOTREACHED();
- }
-}
-
-bool BookmarkBarContextMenuController::IsItemChecked(int id) const {
- DCHECK(id == IDS_BOOMARK_BAR_ALWAYS_SHOW);
- return view_->GetProfile()->GetPrefs()->GetBoolean(prefs::kShowBookmarkBar);
-}
-
-bool BookmarkBarContextMenuController::IsCommandEnabled(int id) const {
- switch (id) {
- case IDS_BOOMARK_BAR_OPEN_INCOGNITO:
- return !view_->GetProfile()->IsOffTheRecord();
-
- case IDS_BOOMARK_BAR_OPEN_ALL_INCOGNITO:
- return NodeHasURLs(node_) && !view_->GetProfile()->IsOffTheRecord();
-
- case IDS_BOOMARK_BAR_OPEN_ALL:
- case IDS_BOOMARK_BAR_OPEN_ALL_NEW_WINDOW:
- return NodeHasURLs(node_);
- }
- return true;
-}
-
-// Returns the parent node and visual_order to use when adding new
-// bookmarks/folders.
-BookmarkNode* BookmarkBarContextMenuController::
- GetParentAndVisualOrderForNewNode(int* visual_order) {
- if (node_->GetType() != history::StarredEntry::URL) {
- // Adding to a group always adds to the end.
- *visual_order = node_->GetChildCount();
- return node_;
- } else {
- DCHECK(node_->GetParent());
- *visual_order = node_->GetParent()->IndexOfChild(node_) + 1;
- return node_->GetParent();
- }
-}
diff --git a/chrome/browser/bookmark_bar_context_menu_controller.h b/chrome/browser/bookmark_bar_context_menu_controller.h
deleted file mode 100644
index e6227cd..0000000
--- a/chrome/browser/bookmark_bar_context_menu_controller.h
+++ /dev/null
@@ -1,59 +0,0 @@
-// 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_BOOKMARK_BAR_CONTEXT_MENU_CONTROLLER_H_
-#define CHROME_BROWSER_BOOKMARK_BAR_CONTEXT_MENU_CONTROLLER_H_
-
-#include "chrome/views/chrome_menu.h"
-#include "chrome/browser/views/bookmark_bar_view.h"
-#include "webkit/glue/window_open_disposition.h"
-
-class BookmarkNode;
-class PageNavigator;
-
-// BookmarkBarContextMenuController manages the context menus shown for the
-// bookmark bar, items on the bookmark bar, and submens of folders on the
-// bookmark bar.
-class BookmarkBarContextMenuController : public views::MenuDelegate,
- public BookmarkBarView::ModelChangedListener {
- public:
- // Recursively opens all bookmarks of |node|. |initial_disposition| dictates
- // how the first URL is opened, all subsequent URLs are opened as background
- // tabs.
- static void OpenAll(HWND parent,
- PageNavigator* navigator,
- BookmarkNode* node,
- WindowOpenDisposition initial_disposition);
-
- BookmarkBarContextMenuController(BookmarkBarView* view,
- BookmarkNode* node);
-
- // Shows the menu at the specified place.
- void RunMenuAt(int x, int y);
-
- // ModelChangedListener method, cancels the menu.
- virtual void ModelChanged();
-
- // Returns the menu.
- views::MenuItemView* menu() { return &menu_; }
-
- // Menu::Delegate methods.
- virtual void ExecuteCommand(int id);
- virtual bool IsItemChecked(int id) const;
- virtual bool IsCommandEnabled(int id) const;
-
- private:
- // Returns the parent node and visual_order to use when adding new
- // bookmarks/folders.
- BookmarkNode* GetParentAndVisualOrderForNewNode(int* visual_order);
-
- views::MenuItemView menu_;
- BookmarkBarView* view_;
- BookmarkNode* node_;
-
- DISALLOW_EVIL_CONSTRUCTORS(BookmarkBarContextMenuController);
-};
-
-#endif // CHROME_BROWSER_BOOKMARK_BAR_CONTEXT_MENU_CONTROLLER_H_
-
diff --git a/chrome/browser/bookmark_bar_context_menu_controller_test.cc b/chrome/browser/bookmark_bar_context_menu_controller_test.cc
deleted file mode 100644
index 58e59e3..0000000
--- a/chrome/browser/bookmark_bar_context_menu_controller_test.cc
+++ /dev/null
@@ -1,141 +0,0 @@
-// 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/bookmark_bar_context_menu_controller.h"
-#include "chrome/browser/bookmarks/bookmark_model.h"
-#include "chrome/browser/profile.h"
-#include "chrome/browser/views/bookmark_bar_view.h"
-#include "chrome/common/pref_names.h"
-#include "chrome/common/pref_service.h"
-#include "chrome/browser/page_navigator.h"
-#include "chrome/test/testing_profile.h"
-#include "testing/gtest/include/gtest/gtest.h"
-
-#include "generated_resources.h"
-
-namespace {
-
-// PageNavigator implementation that records the URL.
-class TestingPageNavigator : public PageNavigator {
- public:
- virtual void OpenURL(const GURL& url, const GURL& referrer,
- WindowOpenDisposition disposition,
- PageTransition::Type transition) {
- urls_.push_back(url);
- }
-
- std::vector<GURL> urls_;
-};
-
-} // namespace
-
-class BookmarkBarContextMenuControllerTest : public testing::Test {
- public:
- BookmarkBarContextMenuControllerTest()
- : bb_view_(NULL), model_(NULL) {
- }
-
- virtual void SetUp() {
- BookmarkBarView::testing_ = true;
-
- profile_.reset(new TestingProfile());
- profile_->set_has_history_service(true);
- profile_->CreateBookmarkModel(true);
-
- model_ = profile_->GetBookmarkModel();
-
- bb_view_.reset(new BookmarkBarView(profile_.get(), NULL));
- bb_view_->SetPageNavigator(&navigator_);
-
- AddTestData();
- }
-
- virtual void TearDown() {
- BookmarkBarView::testing_ = false;
-
- // Flush the message loop to make Purify happy.
- message_loop_.RunAllPending();
- }
-
- protected:
- MessageLoopForUI message_loop_;
- scoped_ptr<TestingProfile> profile_;
- BookmarkModel* model_;
- scoped_ptr<BookmarkBarView> bb_view_;
- TestingPageNavigator navigator_;
-
- private:
- // Creates the following structure:
- // a
- // F1
- // f1a
- // F11
- // f11a
- // F2
- void AddTestData() {
- std::string test_base = "file:///c:/tmp/";
-
- model_->AddURL(model_->GetBookmarkBarNode(), 0, L"a",
- GURL(test_base + "a"));
- BookmarkNode* f1 =
- model_->AddGroup(model_->GetBookmarkBarNode(), 1, L"F1");
- model_->AddURL(f1, 0, L"f1a", GURL(test_base + "f1a"));
- BookmarkNode* f11 = model_->AddGroup(f1, 1, L"F11");
- model_->AddURL(f11, 0, L"f11a", GURL(test_base + "f11a"));
- model_->AddGroup(model_->GetBookmarkBarNode(), 2, L"F2");
- }
-};
-
-// Tests Deleting from the menu.
-TEST_F(BookmarkBarContextMenuControllerTest, DeleteURL) {
- BookmarkBarContextMenuController controller(
- bb_view_.get(), model_->GetBookmarkBarNode()->GetChild(0));
- GURL url = model_->GetBookmarkBarNode()->GetChild(0)->GetURL();
- ASSERT_TRUE(controller.IsCommandEnabled(IDS_BOOKMARK_BAR_REMOVE));
- // Delete the URL.
- controller.ExecuteCommand(IDS_BOOKMARK_BAR_REMOVE);
- // Model shouldn't have URL anymore.
- ASSERT_FALSE(model_->IsBookmarked(url));
-}
-
-// Tests open all on a folder with a couple of bookmarks.
-TEST_F(BookmarkBarContextMenuControllerTest, OpenAll) {
- BookmarkNode* folder = model_->GetBookmarkBarNode()->GetChild(1);
- BookmarkBarContextMenuController controller(bb_view_.get(), folder);
- ASSERT_TRUE(controller.IsCommandEnabled(IDS_BOOMARK_BAR_OPEN_ALL));
- ASSERT_TRUE(controller.IsCommandEnabled(IDS_BOOMARK_BAR_OPEN_ALL_INCOGNITO));
- ASSERT_TRUE(controller.IsCommandEnabled(IDS_BOOMARK_BAR_OPEN_ALL_NEW_WINDOW));
- // Open it.
- controller.ExecuteCommand(IDS_BOOMARK_BAR_OPEN_ALL);
- // Should have navigated to F1's children.
- ASSERT_EQ(2, navigator_.urls_.size());
- ASSERT_TRUE(folder->GetChild(0)->GetURL() == navigator_.urls_[0]);
- ASSERT_TRUE(folder->GetChild(1)->GetChild(0)->GetURL() ==
- navigator_.urls_[1]);
-
- // Make sure incognito is disabled when OTR.
- profile_->set_off_the_record(true);
- ASSERT_TRUE(controller.IsCommandEnabled(IDS_BOOMARK_BAR_OPEN_ALL));
- ASSERT_FALSE(controller.IsCommandEnabled(IDS_BOOMARK_BAR_OPEN_ALL_INCOGNITO));
- ASSERT_TRUE(
- controller.IsCommandEnabled(IDS_BOOMARK_BAR_OPEN_ALL_NEW_WINDOW));
-}
-
-// Tests that menus are appropriately disabled for empty folders.
-TEST_F(BookmarkBarContextMenuControllerTest, DisableForEmptyFolder) {
- BookmarkNode* folder = model_->GetBookmarkBarNode()->GetChild(2);
- BookmarkBarContextMenuController controller(bb_view_.get(), folder);
- EXPECT_FALSE(controller.IsCommandEnabled(IDS_BOOMARK_BAR_OPEN_ALL));
- EXPECT_FALSE(
- controller.IsCommandEnabled(IDS_BOOMARK_BAR_OPEN_ALL_NEW_WINDOW));
-}
-
-// Tests the enabled state of open incognito.
-TEST_F(BookmarkBarContextMenuControllerTest, DisableIncognito) {
- BookmarkBarContextMenuController controller(
- bb_view_.get(), model_->GetBookmarkBarNode()->GetChild(0));
- EXPECT_TRUE(controller.IsCommandEnabled(IDS_BOOMARK_BAR_OPEN_INCOGNITO));
- profile_->set_off_the_record(true);
- EXPECT_FALSE(controller.IsCommandEnabled(IDS_BOOMARK_BAR_OPEN_INCOGNITO));
-}
diff --git a/chrome/browser/bookmarks/bookmark_context_menu.cc b/chrome/browser/bookmarks/bookmark_context_menu.cc
new file mode 100644
index 0000000..4f033ac
--- /dev/null
+++ b/chrome/browser/bookmarks/bookmark_context_menu.cc
@@ -0,0 +1,458 @@
+// 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/bookmarks/bookmark_context_menu.h"
+
+#include "chrome/browser/bookmarks/bookmark_model.h"
+#include "chrome/browser/bookmarks/bookmark_utils.h"
+#include "chrome/browser/browser.h"
+#include "chrome/browser/browser_list.h"
+#include "chrome/browser/page_navigator.h"
+#include "chrome/browser/profile.h"
+#include "chrome/browser/tab_contents.h"
+#include "chrome/browser/user_metrics.h"
+#include "chrome/browser/views/bookmark_bar_view.h"
+#include "chrome/browser/views/bookmark_editor_view.h"
+#include "chrome/browser/views/bookmark_manager_view.h"
+#include "chrome/browser/views/input_window.h"
+#include "chrome/common/l10n_util.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/pref_service.h"
+#include "chrome/views/container.h"
+#include "chrome/views/window.h"
+
+#include "generated_resources.h"
+
+namespace {
+
+// Returns true if the specified node is of type URL, or has a descendant
+// of type URL.
+bool NodeHasURLs(BookmarkNode* node) {
+ if (node->is_url())
+ return true;
+
+ for (int i = 0; i < node->GetChildCount(); ++i) {
+ if (NodeHasURLs(node->GetChild(i)))
+ return true;
+ }
+ return false;
+}
+
+// EditFolderController -------------------------------------------------------
+
+// EditFolderController manages the editing and/or creation of a folder. If the
+// user presses ok, the name change is committed to the model.
+//
+// EditFolderController deletes itself when the window is closed.
+class EditFolderController : public InputWindowDelegate,
+ public BookmarkModelObserver {
+ public:
+ virtual ~EditFolderController() {
+ if (model_)
+ model_->RemoveObserver(this);
+ }
+
+ static void Show(Profile* profile,
+ HWND hwnd,
+ BookmarkNode* node,
+ bool is_new) {
+ // EditFolderController deletes itself when done.
+ EditFolderController* controller =
+ new EditFolderController(profile, hwnd, node, is_new);
+ controller->Show();
+ }
+
+ private:
+ EditFolderController(Profile* profile,
+ HWND hwnd,
+ BookmarkNode* node,
+ bool is_new)
+ : profile_(profile),
+ model_(profile->GetBookmarkModel()),
+ node_(node),
+ is_new_(is_new) {
+ DCHECK(is_new_ || node);
+ window_ = CreateInputWindow(hwnd, this);
+ model_->AddObserver(this);
+ }
+
+ void Show() {
+ window_->Show();
+ }
+
+ // InputWindowDelegate methods.
+ virtual std::wstring GetTextFieldLabel() {
+ return l10n_util::GetString(IDS_BOOMARK_BAR_EDIT_FOLDER_LABEL);
+ }
+
+ virtual std::wstring GetTextFieldContents() {
+ if (is_new_)
+ return l10n_util::GetString(IDS_BOOMARK_EDITOR_NEW_FOLDER_NAME);
+ return node_->GetTitle();
+ }
+
+ virtual bool IsValid(const std::wstring& text) {
+ return !text.empty();
+ }
+
+ virtual void InputAccepted(const std::wstring& text) {
+ if (is_new_)
+ model_->AddGroup(node_, node_->GetChildCount(), text);
+ else
+ model_->SetTitle(node_, text);
+ }
+
+ virtual void InputCanceled() {
+ }
+
+ virtual void WindowClosing() {
+ delete this;
+ }
+
+ virtual std::wstring GetWindowTitle() const {
+ return is_new_ ?
+ l10n_util::GetString(IDS_BOOMARK_FOLDER_EDITOR_WINDOW_TITLE_NEW) :
+ l10n_util::GetString(IDS_BOOMARK_FOLDER_EDITOR_WINDOW_TITLE);
+ }
+
+ // BookmarkModelObserver methods, all invoke ModelChanged and close the
+ // dialog.
+ virtual void Loaded(BookmarkModel* model) {}
+ virtual void BookmarkModelBeingDeleted(BookmarkModel* model) {
+ model_->RemoveObserver(this);
+ model_ = NULL;
+ ModelChanged();
+ }
+
+ virtual void BookmarkNodeMoved(BookmarkModel* model,
+ BookmarkNode* old_parent,
+ int old_index,
+ BookmarkNode* new_parent,
+ int new_index) {
+ ModelChanged();
+ }
+
+ virtual void BookmarkNodeAdded(BookmarkModel* model,
+ BookmarkNode* parent,
+ int index) {
+ ModelChanged();
+ }
+
+ virtual void BookmarkNodeRemoved(BookmarkModel* model,
+ BookmarkNode* parent,
+ int index,
+ BookmarkNode* node) {
+ ModelChanged();
+ }
+
+ virtual void BookmarkNodeChanged(BookmarkModel* model, BookmarkNode* node) {
+ ModelChanged();
+ }
+
+ virtual void BookmarkNodeFavIconLoaded(BookmarkModel* model,
+ BookmarkNode* node) {}
+
+ void ModelChanged() {
+ window_->Close();
+ }
+
+ Profile* profile_;
+ BookmarkModel* model_;
+ // If is_new is true, this is the parent to create the new node under.
+ // Otherwise this is the node to change the title of.
+ BookmarkNode* node_;
+
+ bool is_new_;
+ views::Window* window_;
+
+ DISALLOW_COPY_AND_ASSIGN(EditFolderController);
+};
+
+} // namespace
+
+// BookmarkContextMenu -------------------------------------------
+
+BookmarkContextMenu::BookmarkContextMenu(
+ HWND hwnd,
+ Profile* profile,
+ Browser* browser,
+ PageNavigator* navigator,
+ BookmarkNode* parent,
+ const std::vector<BookmarkNode*>& selection,
+ ConfigurationType configuration)
+ : hwnd_(hwnd),
+ profile_(profile),
+ browser_(browser),
+ navigator_(navigator),
+ parent_(parent),
+ selection_(selection),
+ model_(profile->GetBookmarkModel()),
+ configuration_(configuration) {
+ DCHECK(profile_);
+ DCHECK(model_->IsLoaded());
+ menu_.reset(new views::MenuItemView(this));
+ if (selection.size() == 1 && selection[0]->is_url()) {
+ menu_->AppendMenuItemWithLabel(
+ IDS_BOOMARK_BAR_OPEN_ALL,
+ l10n_util::GetString(IDS_BOOMARK_BAR_OPEN_IN_NEW_TAB));
+ menu_->AppendMenuItemWithLabel(
+ IDS_BOOMARK_BAR_OPEN_ALL_NEW_WINDOW,
+ l10n_util::GetString(IDS_BOOMARK_BAR_OPEN_IN_NEW_WINDOW));
+ menu_->AppendMenuItemWithLabel(
+ IDS_BOOMARK_BAR_OPEN_ALL_INCOGNITO,
+ l10n_util::GetString(IDS_BOOMARK_BAR_OPEN_INCOGNITO));
+ } else {
+ menu_->AppendMenuItemWithLabel(
+ IDS_BOOMARK_BAR_OPEN_ALL,
+ l10n_util::GetString(IDS_BOOMARK_BAR_OPEN_ALL));
+ menu_->AppendMenuItemWithLabel(
+ IDS_BOOMARK_BAR_OPEN_ALL_NEW_WINDOW,
+ l10n_util::GetString(IDS_BOOMARK_BAR_OPEN_ALL_NEW_WINDOW));
+ menu_->AppendMenuItemWithLabel(
+ IDS_BOOMARK_BAR_OPEN_ALL_INCOGNITO,
+ l10n_util::GetString(IDS_BOOMARK_BAR_OPEN_ALL_INCOGNITO));
+ }
+ menu_->AppendSeparator();
+
+ menu_->AppendMenuItemWithLabel(IDS_BOOKMARK_BAR_EDIT,
+ l10n_util::GetString(IDS_BOOKMARK_BAR_EDIT));
+ menu_->AppendMenuItemWithLabel(
+ IDS_BOOKMARK_BAR_REMOVE,
+ l10n_util::GetString(IDS_BOOKMARK_BAR_REMOVE));
+
+ if (configuration != BOOKMARK_BAR) {
+ menu_->AppendMenuItemWithLabel(
+ IDS_BOOKMARK_MANAGER_SHOW_IN_FOLDER,
+ l10n_util::GetString(IDS_BOOKMARK_MANAGER_SHOW_IN_FOLDER));
+ }
+
+ menu_->AppendSeparator();
+
+ menu_->AppendMenuItemWithLabel(
+ IDS_BOOMARK_BAR_ADD_NEW_BOOKMARK,
+ l10n_util::GetString(IDS_BOOMARK_BAR_ADD_NEW_BOOKMARK));
+ menu_->AppendMenuItemWithLabel(
+ IDS_BOOMARK_BAR_NEW_FOLDER,
+ l10n_util::GetString(IDS_BOOMARK_BAR_NEW_FOLDER));
+
+ if (configuration == BOOKMARK_BAR) {
+ menu_->AppendSeparator();
+ menu_->AppendMenuItemWithLabel(IDS_BOOKMARK_MANAGER,
+ l10n_util::GetString(IDS_BOOKMARK_MANAGER));
+ menu_->AppendMenuItem(IDS_BOOMARK_BAR_ALWAYS_SHOW,
+ l10n_util::GetString(IDS_BOOMARK_BAR_ALWAYS_SHOW),
+ views::MenuItemView::CHECKBOX);
+ }
+ model_->AddObserver(this);
+}
+
+BookmarkContextMenu::~BookmarkContextMenu() {
+ if (model_)
+ model_->RemoveObserver(this);
+}
+
+void BookmarkContextMenu::RunMenuAt(int x, int y) {
+ if (!model_->IsLoaded()) {
+ NOTREACHED();
+ return;
+ }
+ // width/height don't matter here.
+ menu_->RunMenuAt(hwnd_, gfx::Rect(x, y, 0, 0), views::MenuItemView::TOPLEFT,
+ true);
+}
+
+void BookmarkContextMenu::ExecuteCommand(int id) {
+ switch (id) {
+ case IDS_BOOMARK_BAR_OPEN_ALL:
+ case IDS_BOOMARK_BAR_OPEN_ALL_INCOGNITO:
+ case IDS_BOOMARK_BAR_OPEN_ALL_NEW_WINDOW: {
+ PageNavigator* navigator = browser_ ?
+ browser_->GetSelectedTabContents() : navigator_;
+ WindowOpenDisposition initial_disposition;
+ if (id == IDS_BOOMARK_BAR_OPEN_ALL) {
+ initial_disposition = NEW_FOREGROUND_TAB;
+ UserMetrics::RecordAction(L"BookmarkBar_ContextMenu_OpenAll",
+ profile_);
+ } else if (id == IDS_BOOMARK_BAR_OPEN_ALL_NEW_WINDOW) {
+ initial_disposition = NEW_WINDOW;
+ UserMetrics::RecordAction(
+ L"BookmarkBar_ContextMenu_OpenAllInNewWindow", profile_);
+ } else {
+ initial_disposition = OFF_THE_RECORD;
+ UserMetrics::RecordAction(
+ L"BookmarkBar_ContextMenu_OpenAllIncognito", profile_);
+ }
+
+ bookmark_utils::OpenAll(hwnd_, profile_, navigator, selection_,
+ initial_disposition);
+ break;
+ }
+
+ case IDS_BOOKMARK_BAR_EDIT:
+ UserMetrics::RecordAction(L"BookmarkBar_ContextMenu_Edit", profile_);
+
+ if (selection_.size() != 1) {
+ NOTREACHED();
+ return;
+ }
+
+ if (selection_[0]->is_url()) {
+ BookmarkEditorView::Configuration editor_config;
+ if (configuration_ == BOOKMARK_BAR)
+ editor_config = BookmarkEditorView::SHOW_TREE;
+ else
+ editor_config = BookmarkEditorView::NO_TREE;
+ BookmarkEditorView::Show(hwnd_, profile_, NULL, selection_[0],
+ editor_config);
+ } else {
+ EditFolderController::Show(profile_, hwnd_, selection_[0], false);
+ }
+ break;
+
+ case IDS_BOOKMARK_BAR_REMOVE: {
+ UserMetrics::RecordAction(L"BookmarkBar_ContextMenu_Remove", profile_);
+ BookmarkModel* model = RemoveModelObserver();
+
+ for (size_t i = 0; i < selection_.size(); ++i) {
+ model->Remove(selection_[i]->GetParent(),
+ selection_[i]->GetParent()->IndexOfChild(selection_[i]));
+ }
+ selection_.clear();
+ break;
+ }
+
+ case IDS_BOOMARK_BAR_ADD_NEW_BOOKMARK: {
+ UserMetrics::RecordAction(L"BookmarkBar_ContextMenu_Add", profile_);
+
+ BookmarkEditorView::Configuration editor_config;
+ if (configuration_ == BOOKMARK_BAR)
+ editor_config = BookmarkEditorView::SHOW_TREE;
+ else
+ editor_config = BookmarkEditorView::NO_TREE;
+ BookmarkEditorView::Show(hwnd_, profile_, GetParentForNewNodes(), NULL,
+ editor_config);
+ break;
+ }
+
+ case IDS_BOOMARK_BAR_NEW_FOLDER: {
+ UserMetrics::RecordAction(L"BookmarkBar_ContextMenu_NewFolder",
+ profile_);
+
+ EditFolderController::Show(profile_, hwnd_, GetParentForNewNodes(),
+ true);
+ break;
+ }
+
+ case IDS_BOOMARK_BAR_ALWAYS_SHOW:
+ BookmarkBarView::ToggleWhenVisible(profile_);
+ break;
+
+ case IDS_BOOKMARK_MANAGER_SHOW_IN_FOLDER:
+ UserMetrics::RecordAction(L"BookmarkBar_ContextMenu_ShowInFolder",
+ profile_);
+
+ if (selection_.size() != 1) {
+ NOTREACHED();
+ return;
+ }
+
+ if (BookmarkManagerView::current())
+ BookmarkManagerView::current()->SelectInTree(selection_[0]);
+ break;
+
+ default:
+ NOTREACHED();
+ }
+}
+
+bool BookmarkContextMenu::IsItemChecked(int id) const {
+ DCHECK(id == IDS_BOOMARK_BAR_ALWAYS_SHOW);
+ return profile_->GetPrefs()->GetBoolean(prefs::kShowBookmarkBar);
+}
+
+bool BookmarkContextMenu::IsCommandEnabled(int id) const {
+ bool is_root_node =
+ (selection_.size() == 1 &&
+ selection_[0]->GetParent() == model_->root_node());
+ switch (id) {
+ case IDS_BOOMARK_BAR_OPEN_INCOGNITO:
+ return !profile_->IsOffTheRecord();
+
+ case IDS_BOOMARK_BAR_OPEN_ALL_INCOGNITO:
+ return HasURLs() && !profile_->IsOffTheRecord();
+
+ case IDS_BOOMARK_BAR_OPEN_ALL:
+ case IDS_BOOMARK_BAR_OPEN_ALL_NEW_WINDOW:
+ return HasURLs();
+
+ case IDS_BOOKMARK_BAR_EDIT:
+ return selection_.size() == 1 && !is_root_node;
+
+ case IDS_BOOKMARK_BAR_REMOVE:
+ return !selection_.empty() && !is_root_node;
+
+ case IDS_BOOKMARK_MANAGER_SHOW_IN_FOLDER:
+ return configuration_ == BOOKMARK_MANAGER_TABLE &&
+ selection_.size() == 1;
+
+ case IDS_BOOMARK_BAR_NEW_FOLDER:
+ case IDS_BOOMARK_BAR_ADD_NEW_BOOKMARK:
+ return GetParentForNewNodes() != NULL;
+ }
+ return true;
+}
+
+void BookmarkContextMenu::BookmarkModelBeingDeleted(BookmarkModel* model) {
+ ModelChanged();
+}
+
+void BookmarkContextMenu::BookmarkNodeMoved(BookmarkModel* model,
+ BookmarkNode* old_parent,
+ int old_index,
+ BookmarkNode* new_parent,
+ int new_index) {
+ ModelChanged();
+}
+
+void BookmarkContextMenu::BookmarkNodeAdded(BookmarkModel* model,
+ BookmarkNode* parent,
+ int index) {
+ ModelChanged();
+}
+
+void BookmarkContextMenu::BookmarkNodeRemoved(BookmarkModel* model,
+ BookmarkNode* parent,
+ int index,
+ BookmarkNode* node) {
+ ModelChanged();
+}
+
+void BookmarkContextMenu::BookmarkNodeChanged(BookmarkModel* model,
+ BookmarkNode* node) {
+ ModelChanged();
+}
+
+void BookmarkContextMenu::ModelChanged() {
+ menu_->Cancel();
+}
+
+BookmarkModel* BookmarkContextMenu::RemoveModelObserver() {
+ BookmarkModel* model = model_;
+ model_->RemoveObserver(this);
+ model_ = NULL;
+ return model;
+}
+
+bool BookmarkContextMenu::HasURLs() const {
+ for (size_t i = 0; i < selection_.size(); ++i) {
+ if (NodeHasURLs(selection_[i]))
+ return true;
+ }
+ return false;
+}
+
+BookmarkNode* BookmarkContextMenu::GetParentForNewNodes() const {
+ return (selection_.size() == 1 && selection_[0]->is_folder()) ?
+ selection_[0] : parent_;
+}
diff --git a/chrome/browser/bookmarks/bookmark_context_menu.h b/chrome/browser/bookmarks/bookmark_context_menu.h
new file mode 100644
index 0000000..5b48627
--- /dev/null
+++ b/chrome/browser/bookmarks/bookmark_context_menu.h
@@ -0,0 +1,105 @@
+// 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_BOOKMARKS_BOOKMARK_CONTEXT_MENU_H_
+#define CHROME_BROWSER_BOOKMARKS_BOOKMARK_CONTEXT_MENU_H_
+
+#include <vector>
+
+#include "chrome/browser/bookmarks/bookmark_model.h"
+#include "chrome/views/chrome_menu.h"
+
+class Browser;
+class PageNavigator;
+
+// BookmarkContextMenu manages the context menu shown for the
+// bookmark bar, items on the bookmark bar, submenus of the bookmark bar and
+// the bookmark manager.
+class BookmarkContextMenu : public views::MenuDelegate,
+ public BookmarkModelObserver {
+ public:
+ // Used to configure what the context menu shows.
+ enum ConfigurationType {
+ BOOKMARK_BAR,
+ BOOKMARK_MANAGER_TABLE,
+ BOOKMARK_MANAGER_TREE
+ };
+
+ // Creates the bookmark context menu.
+ // |profile| is used for opening urls as well as enabling 'open incognito'.
+ // |browser| is used to determine the PageNavigator and may be null.
+ // |navigator| is used if |browser| is null, and is provided for testing.
+ // |parent| is the parent for newly created nodes if |selection| is empty.
+ // |selection| is the nodes the context menu operates on and may be empty.
+ // |configuration| determines which items to show.
+ BookmarkContextMenu(HWND hwnd,
+ Profile* profile,
+ Browser* browser,
+ PageNavigator* navigator,
+ BookmarkNode* parent,
+ const std::vector<BookmarkNode*>& selection,
+ ConfigurationType configuration);
+ virtual ~BookmarkContextMenu();
+
+ // Shows the menu at the specified place.
+ void RunMenuAt(int x, int y);
+
+ // Returns the menu.
+ views::MenuItemView* menu() const { return menu_.get(); }
+
+ // Menu::Delegate methods.
+ virtual void ExecuteCommand(int id);
+ virtual bool IsItemChecked(int id) const;
+ virtual bool IsCommandEnabled(int id) const;
+
+ private:
+ // BookmarkModelObserver method. Any change to the model results in closing
+ // the menu.
+ 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,
+ BookmarkNode* node);
+ virtual void BookmarkNodeChanged(BookmarkModel* model,
+ BookmarkNode* node);
+ virtual void BookmarkNodeFavIconLoaded(BookmarkModel* model,
+ BookmarkNode* node) {}
+
+ // Invoked from the various bookmark model observer methods. Closes the menu.
+ void ModelChanged();
+
+ // Removes the observer from the model and NULLs out model_.
+ BookmarkModel* RemoveModelObserver();
+
+ // Returns true if selection_ has at least one bookmark of type url.
+ bool HasURLs() const;
+
+ // Returns the parent for newly created folders/bookmarks. If selection_
+ // has one element and it is a folder, selection_[0] is returned, otherwise
+ // parent_ is returned.
+ BookmarkNode* GetParentForNewNodes() const;
+
+ HWND hwnd_;
+ Profile* profile_;
+ Browser* browser_;
+ PageNavigator* navigator_;
+ BookmarkNode* parent_;
+ std::vector<BookmarkNode*> selection_;
+ scoped_ptr<views::MenuItemView> menu_;
+ BookmarkModel* model_;
+ ConfigurationType configuration_;
+
+ DISALLOW_COPY_AND_ASSIGN(BookmarkContextMenu);
+};
+
+#endif // CHROME_BROWSER_BOOKMARKS_BOOKMARK_CONTEXT_MENU_H_
diff --git a/chrome/browser/bookmarks/bookmark_context_menu_test.cc b/chrome/browser/bookmarks/bookmark_context_menu_test.cc
new file mode 100644
index 0000000..e665058
--- /dev/null
+++ b/chrome/browser/bookmarks/bookmark_context_menu_test.cc
@@ -0,0 +1,271 @@
+// 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/bookmarks/bookmark_context_menu.h"
+#include "chrome/browser/bookmarks/bookmark_model.h"
+#include "chrome/browser/bookmarks/bookmark_utils.h"
+#include "chrome/browser/profile.h"
+#include "chrome/browser/views/bookmark_bar_view.h"
+#include "chrome/common/pref_names.h"
+#include "chrome/common/pref_service.h"
+#include "chrome/browser/page_navigator.h"
+#include "chrome/test/testing_profile.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+#include "generated_resources.h"
+
+namespace {
+
+// PageNavigator implementation that records the URL.
+class TestingPageNavigator : public PageNavigator {
+ public:
+ virtual void OpenURL(const GURL& url,
+ const GURL& referrer,
+ WindowOpenDisposition disposition,
+ PageTransition::Type transition) {
+ urls_.push_back(url);
+ }
+
+ std::vector<GURL> urls_;
+};
+
+} // namespace
+
+class BookmarkContextMenuTest : public testing::Test {
+ public:
+ BookmarkContextMenuTest()
+ : model_(NULL) {
+ }
+
+ virtual void SetUp() {
+ BookmarkBarView::testing_ = true;
+
+ profile_.reset(new TestingProfile());
+ profile_->set_has_history_service(true);
+ profile_->CreateBookmarkModel(true);
+ profile_->BlockUntilBookmarkModelLoaded();
+
+ model_ = profile_->GetBookmarkModel();
+
+ AddTestData();
+ }
+
+ virtual void TearDown() {
+ BookmarkBarView::testing_ = false;
+
+ // Flush the message loop to make Purify happy.
+ message_loop_.RunAllPending();
+ }
+
+ protected:
+ MessageLoopForUI message_loop_;
+ scoped_ptr<TestingProfile> profile_;
+ BookmarkModel* model_;
+ TestingPageNavigator navigator_;
+
+ private:
+ // Creates the following structure:
+ // a
+ // F1
+ // f1a
+ // F11
+ // f11a
+ // F2
+ // F3
+ // F4
+ // f4a
+ void AddTestData() {
+ std::string test_base = "file:///c:/tmp/";
+
+ model_->AddURL(model_->GetBookmarkBarNode(), 0, L"a",
+ GURL(test_base + "a"));
+ BookmarkNode* f1 =
+ model_->AddGroup(model_->GetBookmarkBarNode(), 1, L"F1");
+ model_->AddURL(f1, 0, L"f1a", GURL(test_base + "f1a"));
+ BookmarkNode* f11 = model_->AddGroup(f1, 1, L"F11");
+ model_->AddURL(f11, 0, L"f11a", GURL(test_base + "f11a"));
+ model_->AddGroup(model_->GetBookmarkBarNode(), 2, L"F2");
+ model_->AddGroup(model_->GetBookmarkBarNode(), 3, L"F3");
+ BookmarkNode* f4 =
+ model_->AddGroup(model_->GetBookmarkBarNode(), 4, L"F4");
+ model_->AddURL(f4, 0, L"f4a", GURL(test_base + "f4a"));
+ }
+};
+
+// Tests Deleting from the menu.
+TEST_F(BookmarkContextMenuTest, DeleteURL) {
+ std::vector<BookmarkNode*> nodes;
+ nodes.push_back(model_->GetBookmarkBarNode()->GetChild(0));
+ BookmarkContextMenu controller(
+ NULL, profile_.get(), NULL, NULL, nodes[0]->GetParent(), nodes,
+ BookmarkContextMenu::BOOKMARK_BAR);
+ GURL url = model_->GetBookmarkBarNode()->GetChild(0)->GetURL();
+ ASSERT_TRUE(controller.IsCommandEnabled(IDS_BOOKMARK_BAR_REMOVE));
+ // Delete the URL.
+ controller.ExecuteCommand(IDS_BOOKMARK_BAR_REMOVE);
+ // Model shouldn't have URL anymore.
+ ASSERT_FALSE(model_->IsBookmarked(url));
+}
+
+// Tests open all on a folder with a couple of bookmarks.
+TEST_F(BookmarkContextMenuTest, OpenAll) {
+ BookmarkNode* folder = model_->GetBookmarkBarNode()->GetChild(1);
+ bookmark_utils::OpenAll(
+ NULL, profile_.get(), &navigator_, folder, NEW_FOREGROUND_TAB);
+
+ // Should have navigated to F1's children.
+ ASSERT_EQ(2, navigator_.urls_.size());
+ ASSERT_TRUE(folder->GetChild(0)->GetURL() == navigator_.urls_[0]);
+ ASSERT_TRUE(folder->GetChild(1)->GetChild(0)->GetURL() ==
+ navigator_.urls_[1]);
+}
+
+// Tests the enabled state of the menus when supplied an empty vector.
+TEST_F(BookmarkContextMenuTest, EmptyNodes) {
+ BookmarkContextMenu controller(
+ NULL, profile_.get(), NULL, NULL, model_->other_node(),
+ std::vector<BookmarkNode*>(), BookmarkContextMenu::BOOKMARK_BAR);
+ EXPECT_FALSE(controller.IsCommandEnabled(IDS_BOOMARK_BAR_OPEN_ALL));
+ EXPECT_FALSE(
+ controller.IsCommandEnabled(IDS_BOOMARK_BAR_OPEN_ALL_NEW_WINDOW));
+ EXPECT_FALSE(controller.IsCommandEnabled(IDS_BOOMARK_BAR_OPEN_ALL_INCOGNITO));
+ EXPECT_FALSE(controller.IsCommandEnabled(IDS_BOOKMARK_BAR_REMOVE));
+ EXPECT_FALSE(
+ controller.IsCommandEnabled(IDS_BOOKMARK_MANAGER_SHOW_IN_FOLDER));
+ EXPECT_TRUE(
+ controller.IsCommandEnabled(IDS_BOOMARK_BAR_ADD_NEW_BOOKMARK));
+ EXPECT_TRUE(
+ controller.IsCommandEnabled(IDS_BOOMARK_BAR_NEW_FOLDER));
+}
+
+// Tests the enabled state of the menus when supplied a vector with a single
+// url.
+TEST_F(BookmarkContextMenuTest, SingleURL) {
+ std::vector<BookmarkNode*> nodes;
+ nodes.push_back(model_->GetBookmarkBarNode()->GetChild(0));
+ BookmarkContextMenu controller(
+ NULL, profile_.get(), NULL, NULL, nodes[0]->GetParent(),
+ nodes, BookmarkContextMenu::BOOKMARK_BAR);
+ EXPECT_TRUE(controller.IsCommandEnabled(IDS_BOOMARK_BAR_OPEN_ALL));
+ EXPECT_TRUE(
+ controller.IsCommandEnabled(IDS_BOOMARK_BAR_OPEN_ALL_NEW_WINDOW));
+ EXPECT_TRUE(controller.IsCommandEnabled(IDS_BOOMARK_BAR_OPEN_ALL_INCOGNITO));
+ EXPECT_TRUE(controller.IsCommandEnabled(IDS_BOOKMARK_BAR_REMOVE));
+ EXPECT_FALSE(
+ controller.IsCommandEnabled(IDS_BOOKMARK_MANAGER_SHOW_IN_FOLDER));
+ EXPECT_TRUE(
+ controller.IsCommandEnabled(IDS_BOOMARK_BAR_ADD_NEW_BOOKMARK));
+ EXPECT_TRUE(
+ controller.IsCommandEnabled(IDS_BOOMARK_BAR_NEW_FOLDER));
+}
+
+// Tests the enabled state of the menus when supplied a vector with multiple
+// urls.
+TEST_F(BookmarkContextMenuTest, MultipleURLs) {
+ std::vector<BookmarkNode*> nodes;
+ nodes.push_back(model_->GetBookmarkBarNode()->GetChild(0));
+ nodes.push_back(model_->GetBookmarkBarNode()->GetChild(1)->GetChild(0));
+ BookmarkContextMenu controller(
+ NULL, profile_.get(), NULL, NULL, nodes[0]->GetParent(),
+ nodes, BookmarkContextMenu::BOOKMARK_BAR);
+ EXPECT_TRUE(controller.IsCommandEnabled(IDS_BOOMARK_BAR_OPEN_ALL));
+ EXPECT_TRUE(
+ controller.IsCommandEnabled(IDS_BOOMARK_BAR_OPEN_ALL_NEW_WINDOW));
+ EXPECT_TRUE(controller.IsCommandEnabled(IDS_BOOMARK_BAR_OPEN_ALL_INCOGNITO));
+ EXPECT_TRUE(controller.IsCommandEnabled(IDS_BOOKMARK_BAR_REMOVE));
+ EXPECT_FALSE(
+ controller.IsCommandEnabled(IDS_BOOKMARK_MANAGER_SHOW_IN_FOLDER));
+ EXPECT_TRUE(
+ controller.IsCommandEnabled(IDS_BOOMARK_BAR_ADD_NEW_BOOKMARK));
+ EXPECT_TRUE(
+ controller.IsCommandEnabled(IDS_BOOMARK_BAR_NEW_FOLDER));
+}
+
+// Tests the enabled state of the menus when supplied an vector with a single
+// folder.
+TEST_F(BookmarkContextMenuTest, SingleFolder) {
+ std::vector<BookmarkNode*> nodes;
+ nodes.push_back(model_->GetBookmarkBarNode()->GetChild(2));
+ BookmarkContextMenu controller(
+ NULL, profile_.get(), NULL, NULL, nodes[0]->GetParent(),
+ nodes, BookmarkContextMenu::BOOKMARK_BAR);
+ EXPECT_FALSE(controller.IsCommandEnabled(IDS_BOOMARK_BAR_OPEN_ALL));
+ EXPECT_FALSE(
+ controller.IsCommandEnabled(IDS_BOOMARK_BAR_OPEN_ALL_NEW_WINDOW));
+ EXPECT_FALSE(controller.IsCommandEnabled(IDS_BOOMARK_BAR_OPEN_ALL_INCOGNITO));
+ EXPECT_TRUE(controller.IsCommandEnabled(IDS_BOOKMARK_BAR_REMOVE));
+ EXPECT_FALSE(
+ controller.IsCommandEnabled(IDS_BOOKMARK_MANAGER_SHOW_IN_FOLDER));
+ EXPECT_TRUE(
+ controller.IsCommandEnabled(IDS_BOOMARK_BAR_ADD_NEW_BOOKMARK));
+ EXPECT_TRUE(
+ controller.IsCommandEnabled(IDS_BOOMARK_BAR_NEW_FOLDER));
+}
+
+// Tests the enabled state of the menus when supplied a vector with multiple
+// folders, all of which are empty.
+TEST_F(BookmarkContextMenuTest, MultipleEmptyFolders) {
+ std::vector<BookmarkNode*> nodes;
+ nodes.push_back(model_->GetBookmarkBarNode()->GetChild(2));
+ nodes.push_back(model_->GetBookmarkBarNode()->GetChild(3));
+ BookmarkContextMenu controller(
+ NULL, profile_.get(), NULL, NULL, nodes[0]->GetParent(),
+ nodes, BookmarkContextMenu::BOOKMARK_BAR);
+ EXPECT_FALSE(controller.IsCommandEnabled(IDS_BOOMARK_BAR_OPEN_ALL));
+ EXPECT_FALSE(
+ controller.IsCommandEnabled(IDS_BOOMARK_BAR_OPEN_ALL_NEW_WINDOW));
+ EXPECT_FALSE(controller.IsCommandEnabled(IDS_BOOMARK_BAR_OPEN_ALL_INCOGNITO));
+ EXPECT_TRUE(controller.IsCommandEnabled(IDS_BOOKMARK_BAR_REMOVE));
+ EXPECT_FALSE(
+ controller.IsCommandEnabled(IDS_BOOKMARK_MANAGER_SHOW_IN_FOLDER));
+ EXPECT_TRUE(
+ controller.IsCommandEnabled(IDS_BOOMARK_BAR_ADD_NEW_BOOKMARK));
+ EXPECT_TRUE(
+ controller.IsCommandEnabled(IDS_BOOMARK_BAR_NEW_FOLDER));
+}
+
+// Tests the enabled state of the menus when supplied a vector with multiple
+// folders, some of which contain URLs.
+TEST_F(BookmarkContextMenuTest, MultipleFoldersWithURLs) {
+ std::vector<BookmarkNode*> nodes;
+ nodes.push_back(model_->GetBookmarkBarNode()->GetChild(3));
+ nodes.push_back(model_->GetBookmarkBarNode()->GetChild(4));
+ BookmarkContextMenu controller(
+ NULL, profile_.get(), NULL, NULL, nodes[0]->GetParent(),
+ nodes, BookmarkContextMenu::BOOKMARK_BAR);
+ EXPECT_TRUE(controller.IsCommandEnabled(IDS_BOOMARK_BAR_OPEN_ALL));
+ EXPECT_TRUE(
+ controller.IsCommandEnabled(IDS_BOOMARK_BAR_OPEN_ALL_NEW_WINDOW));
+ EXPECT_TRUE(controller.IsCommandEnabled(IDS_BOOMARK_BAR_OPEN_ALL_INCOGNITO));
+ EXPECT_TRUE(controller.IsCommandEnabled(IDS_BOOKMARK_BAR_REMOVE));
+ EXPECT_FALSE(
+ controller.IsCommandEnabled(IDS_BOOKMARK_MANAGER_SHOW_IN_FOLDER));
+ EXPECT_TRUE(
+ controller.IsCommandEnabled(IDS_BOOMARK_BAR_ADD_NEW_BOOKMARK));
+ EXPECT_TRUE(
+ controller.IsCommandEnabled(IDS_BOOMARK_BAR_NEW_FOLDER));
+}
+
+// Tests the enabled state of open incognito.
+TEST_F(BookmarkContextMenuTest, DisableIncognito) {
+ std::vector<BookmarkNode*> nodes;
+ nodes.push_back(model_->GetBookmarkBarNode()->GetChild(0));
+ BookmarkContextMenu controller(
+ NULL, profile_.get(), NULL, NULL, nodes[0]->GetParent(),
+ nodes, BookmarkContextMenu::BOOKMARK_BAR);
+ profile_->set_off_the_record(true);
+ EXPECT_FALSE(controller.IsCommandEnabled(IDS_BOOMARK_BAR_OPEN_INCOGNITO));
+ EXPECT_FALSE(controller.IsCommandEnabled(IDS_BOOMARK_BAR_OPEN_ALL_INCOGNITO));
+}
+
+// Tests that you can't remove/edit when showing the other node.
+TEST_F(BookmarkContextMenuTest, DisabledItemsWithOtherNode) {
+ std::vector<BookmarkNode*> nodes;
+ nodes.push_back(model_->other_node());
+ BookmarkContextMenu controller(
+ NULL, profile_.get(), NULL, NULL, nodes[0], nodes,
+ BookmarkContextMenu::BOOKMARK_BAR);
+ EXPECT_FALSE(controller.IsCommandEnabled(IDS_BOOKMARK_BAR_EDIT));
+ EXPECT_FALSE(controller.IsCommandEnabled(IDS_BOOKMARK_BAR_REMOVE));
+}
diff --git a/chrome/browser/bookmarks/bookmark_drag_utils.cc b/chrome/browser/bookmarks/bookmark_drag_utils.cc
deleted file mode 100644
index 863369c..0000000
--- a/chrome/browser/bookmarks/bookmark_drag_utils.cc
+++ /dev/null
@@ -1,91 +0,0 @@
-// 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/bookmarks/bookmark_drag_utils.h"
-
-#include "chrome/browser/bookmarks/bookmark_drag_data.h"
-#include "chrome/browser/bookmarks/bookmark_model.h"
-#include "chrome/common/drag_drop_types.h"
-#include "chrome/views/event.h"
-
-namespace {
-
-void CloneDragDataImpl(BookmarkModel* model,
- const BookmarkDragData::Element& element,
- BookmarkNode* parent,
- int index_to_add_at) {
- if (element.is_url) {
- model->AddURL(parent, index_to_add_at, element.title, element.url);
- } else {
- BookmarkNode* new_folder = model->AddGroup(parent, index_to_add_at,
- element.title);
- for (int i = 0; i < static_cast<int>(element.children.size()); ++i)
- CloneDragDataImpl(model, element.children[i], new_folder, i);
- }
-}
-
-} // namespace
-
-namespace bookmark_drag_utils {
-
-int PreferredDropOperation(const views::DropTargetEvent& event,
- int operation) {
- int common_ops = (event.GetSourceOperations() & operation);
- if (!common_ops)
- return 0;
- if (DragDropTypes::DRAG_COPY & common_ops)
- return DragDropTypes::DRAG_COPY;
- if (DragDropTypes::DRAG_LINK & common_ops)
- return DragDropTypes::DRAG_LINK;
- if (DragDropTypes::DRAG_MOVE & common_ops)
- return DragDropTypes::DRAG_MOVE;
- return DragDropTypes::DRAG_NONE;
-}
-
-bool IsValidDropLocation(Profile* profile,
- const BookmarkDragData& data,
- BookmarkNode* drop_parent,
- int index) {
- if (!drop_parent->is_folder()) {
- NOTREACHED();
- return false;
- }
-
- if (!data.is_valid())
- return false;
-
- if (data.IsFromProfile(profile)) {
- std::vector<BookmarkNode*> nodes = data.GetNodes(profile);
- for (size_t i = 0; i < nodes.size(); ++i) {
- // Don't allow the drop if the user is attempting to drop on one of the
- // nodes being dragged.
- BookmarkNode* node = nodes[i];
- int node_index = (drop_parent == node->GetParent()) ?
- drop_parent->IndexOfChild(nodes[i]) : -1;
- if (node_index != -1 && (index == node_index || index == node_index + 1))
- return false;
-
- // drop_parent can't accept a child that is an ancestor.
- if (drop_parent->HasAncestor(node))
- return false;
- }
- return true;
- }
- // From the same profile, always accept.
- return true;
-}
-
-void CloneDragData(BookmarkModel* model,
- const std::vector<BookmarkDragData::Element>& elements,
- BookmarkNode* parent,
- int index_to_add_at) {
- if (!parent->is_folder() || !model) {
- NOTREACHED();
- return;
- }
- for (size_t i = 0; i < elements.size(); ++i)
- CloneDragDataImpl(model, elements[i], parent, index_to_add_at + i);
-}
-
-} // namespace bookmark_drag_utils
diff --git a/chrome/browser/bookmarks/bookmark_folder_tree_model.cc b/chrome/browser/bookmarks/bookmark_folder_tree_model.cc
index f365e76..b1932a4 100644
--- a/chrome/browser/bookmarks/bookmark_folder_tree_model.cc
+++ b/chrome/browser/bookmarks/bookmark_folder_tree_model.cc
@@ -33,6 +33,8 @@ BookmarkFolderTreeModel::NodeType BookmarkFolderTreeModel::GetNodeType(
return RECENTLY_BOOKMARKED;
if (node == search_node_)
return SEARCH;
+ if (node == GetRoot())
+ return NONE;
return BOOKMARK;
}
diff --git a/chrome/browser/bookmarks/bookmark_utils.cc b/chrome/browser/bookmarks/bookmark_utils.cc
new file mode 100644
index 0000000..d9b0be1
--- /dev/null
+++ b/chrome/browser/bookmarks/bookmark_utils.cc
@@ -0,0 +1,241 @@
+// 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/bookmarks/bookmark_utils.h"
+
+#include "chrome/browser/bookmarks/bookmark_drag_data.h"
+#include "chrome/browser/bookmarks/bookmark_model.h"
+#include "chrome/browser/browser.h"
+#include "chrome/browser/browser_list.h"
+#include "chrome/browser/page_navigator.h"
+#include "chrome/browser/tab_contents.h"
+#include "chrome/common/drag_drop_types.h"
+#include "chrome/common/l10n_util.h"
+#include "chrome/views/event.h"
+
+#include "chromium_strings.h"
+#include "generated_resources.h"
+
+namespace {
+
+// Number of bookmarks we'll open before prompting the user to see if they
+// really want to open all.
+const int kNumURLsBeforePrompting = 15;
+
+// A PageNavigator implementation that creates a new Browser. This is used when
+// opening a url and there is no Browser open. The Browser is created the first
+// time the PageNavigator method is invoked.
+class NewBrowserPageNavigator : public PageNavigator {
+ public:
+ explicit NewBrowserPageNavigator(Profile* profile)
+ : profile_(profile),
+ browser_(NULL) {}
+
+ virtual ~NewBrowserPageNavigator() {
+ if (browser_)
+ browser_->Show();
+ }
+
+ Browser* browser() const { return browser_; }
+
+ virtual void OpenURL(const GURL& url,
+ const GURL& referrer,
+ WindowOpenDisposition disposition,
+ PageTransition::Type transition) {
+ if (!browser_) {
+ Profile* profile = (disposition == OFF_THE_RECORD) ?
+ profile_->GetOffTheRecordProfile() : profile_;
+ browser_ = new Browser(gfx::Rect(), SW_SHOW, profile,
+ BrowserType::TABBED_BROWSER, std::wstring());
+ // Always open the first tab in the foreground.
+ disposition = NEW_FOREGROUND_TAB;
+ }
+ browser_->OpenURLFromTab(NULL, url, referrer, NEW_FOREGROUND_TAB, transition);
+ }
+
+ private:
+ Profile* profile_;
+ Browser* browser_;
+
+ DISALLOW_COPY_AND_ASSIGN(NewBrowserPageNavigator);
+};
+
+void CloneDragDataImpl(BookmarkModel* model,
+ const BookmarkDragData::Element& element,
+ BookmarkNode* parent,
+ int index_to_add_at) {
+ if (element.is_url) {
+ model->AddURL(parent, index_to_add_at, element.title, element.url);
+ } else {
+ BookmarkNode* new_folder = model->AddGroup(parent, index_to_add_at,
+ element.title);
+ for (int i = 0; i < static_cast<int>(element.children.size()); ++i)
+ CloneDragDataImpl(model, element.children[i], new_folder, i);
+ }
+}
+
+// Returns the number of descendants of node that are of type url.
+int DescendantURLCount(BookmarkNode* node) {
+ int result = 0;
+ for (int i = 0; i < node->GetChildCount(); ++i) {
+ BookmarkNode* child = node->GetChild(i);
+ if (child->is_url())
+ result++;
+ else
+ result += DescendantURLCount(child);
+ }
+ return result;
+}
+
+// Implementation of OpenAll. Opens all nodes of type URL and recurses for
+// groups. |navigator| is the PageNavigator used to open URLs. After the first
+// url is opened |opened_url| is set to true and |navigator| is set to the
+// PageNavigator of the last active tab. This is done to handle a window
+// disposition of new window, in which case we want subsequent tabs to open in
+// that window.
+void OpenAllImpl(BookmarkNode* node,
+ WindowOpenDisposition initial_disposition,
+ PageNavigator** navigator,
+ bool* opened_url) {
+ if (node->is_url()) {
+ WindowOpenDisposition disposition;
+ if (*opened_url)
+ disposition = NEW_BACKGROUND_TAB;
+ else
+ disposition = initial_disposition;
+ (*navigator)->OpenURL(node->GetURL(), GURL(), disposition,
+ PageTransition::AUTO_BOOKMARK);
+ if (!*opened_url) {
+ *opened_url = true;
+ // We opened the first URL which may have opened a new window or clobbered
+ // the current page, reset the navigator just to be sure.
+ Browser* new_browser = BrowserList::GetLastActive();
+ if (new_browser) {
+ TabContents* current_tab = new_browser->GetSelectedTabContents();
+ DCHECK(new_browser && current_tab);
+ if (new_browser && current_tab)
+ *navigator = current_tab;
+ } // else, new_browser == NULL, which happens during testing.
+ }
+ } else {
+ // Group, recurse through children.
+ for (int i = 0; i < node->GetChildCount(); ++i) {
+ OpenAllImpl(node->GetChild(i), initial_disposition, navigator,
+ opened_url);
+ }
+ }
+}
+
+bool ShouldOpenAll(HWND parent, const std::vector<BookmarkNode*>& nodes) {
+ int descendant_count = 0;
+ for (size_t i = 0; i < nodes.size(); ++i)
+ descendant_count += DescendantURLCount(nodes[i]);
+ if (descendant_count < kNumURLsBeforePrompting)
+ return true;
+
+ std::wstring message =
+ l10n_util::GetStringF(IDS_BOOKMARK_BAR_SHOULD_OPEN_ALL,
+ IntToWString(descendant_count));
+ return MessageBox(parent, message.c_str(),
+ l10n_util::GetString(IDS_PRODUCT_NAME).c_str(),
+ MB_YESNO | MB_ICONWARNING | MB_TOPMOST) == IDYES;
+}
+
+} // namespace
+
+namespace bookmark_utils {
+
+int PreferredDropOperation(const views::DropTargetEvent& event,
+ int operation) {
+ int common_ops = (event.GetSourceOperations() & operation);
+ if (!common_ops)
+ return 0;
+ if (DragDropTypes::DRAG_COPY & common_ops)
+ return DragDropTypes::DRAG_COPY;
+ if (DragDropTypes::DRAG_LINK & common_ops)
+ return DragDropTypes::DRAG_LINK;
+ if (DragDropTypes::DRAG_MOVE & common_ops)
+ return DragDropTypes::DRAG_MOVE;
+ return DragDropTypes::DRAG_NONE;
+}
+
+bool IsValidDropLocation(Profile* profile,
+ const BookmarkDragData& data,
+ BookmarkNode* drop_parent,
+ int index) {
+ if (!drop_parent->is_folder()) {
+ NOTREACHED();
+ return false;
+ }
+
+ if (!data.is_valid())
+ return false;
+
+ if (data.IsFromProfile(profile)) {
+ std::vector<BookmarkNode*> nodes = data.GetNodes(profile);
+ for (size_t i = 0; i < nodes.size(); ++i) {
+ // Don't allow the drop if the user is attempting to drop on one of the
+ // nodes being dragged.
+ BookmarkNode* node = nodes[i];
+ int node_index = (drop_parent == node->GetParent()) ?
+ drop_parent->IndexOfChild(nodes[i]) : -1;
+ if (node_index != -1 && (index == node_index || index == node_index + 1))
+ return false;
+
+ // drop_parent can't accept a child that is an ancestor.
+ if (drop_parent->HasAncestor(node))
+ return false;
+ }
+ return true;
+ }
+ // From the same profile, always accept.
+ return true;
+}
+
+void CloneDragData(BookmarkModel* model,
+ const std::vector<BookmarkDragData::Element>& elements,
+ BookmarkNode* parent,
+ int index_to_add_at) {
+ if (!parent->is_folder() || !model) {
+ NOTREACHED();
+ return;
+ }
+ for (size_t i = 0; i < elements.size(); ++i)
+ CloneDragDataImpl(model, elements[i], parent, index_to_add_at + i);
+}
+
+void OpenAll(HWND parent,
+ Profile* profile,
+ PageNavigator* navigator,
+ const std::vector<BookmarkNode*>& nodes,
+ WindowOpenDisposition initial_disposition) {
+ if (!ShouldOpenAll(parent, nodes))
+ return;
+
+ NewBrowserPageNavigator navigator_impl(profile);
+ if (!navigator) {
+ Browser* browser =
+ BrowserList::FindBrowserWithType(profile, BrowserType::TABBED_BROWSER);
+ if (!browser || !browser->GetSelectedTabContents())
+ navigator = &navigator_impl;
+ else
+ navigator = browser->GetSelectedTabContents();
+ }
+
+ bool opened_url = false;
+ for (size_t i = 0; i < nodes.size(); ++i)
+ OpenAllImpl(nodes[i], initial_disposition, &navigator, &opened_url);
+}
+
+void OpenAll(HWND parent,
+ Profile* profile,
+ PageNavigator* navigator,
+ BookmarkNode* node,
+ WindowOpenDisposition initial_disposition) {
+ std::vector<BookmarkNode*> nodes;
+ nodes.push_back(node);
+ OpenAll(parent, profile, navigator, nodes, initial_disposition);
+}
+
+} // namespace bookmark_utils
diff --git a/chrome/browser/bookmarks/bookmark_drag_utils.h b/chrome/browser/bookmarks/bookmark_utils.h
index 2805311..0711638 100644
--- a/chrome/browser/bookmarks/bookmark_drag_utils.h
+++ b/chrome/browser/bookmarks/bookmark_utils.h
@@ -2,21 +2,25 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#ifndef CHROME_BROWSER_BOOKMARKS_BOOKMARK_DRAG_UTILS_H_
-#define CHROME_BROWSER_BOOKMARKS_BOOKMARK_DRAG_UTILS_H_
+#ifndef CHROME_BROWSER_BOOKMARKS_BOOKMARK_UTILS_H_
+#define CHROME_BROWSER_BOOKMARKS_BOOKMARK_UTILS_H_
+
+#include <vector>
#include "chrome/browser/bookmarks/bookmark_drag_data.h"
+#include "webkit/glue/window_open_disposition.h"
class BookmarkNode;
+class PageNavigator;
class Profile;
namespace views {
class DropTargetEvent;
}
-// Functions used in managing bookmark drag and drop. These functions are
-// used by both the bookmark bar and bookmark manager.
-namespace bookmark_drag_utils {
+// A collection of bookmark utility functions used by various parts of the UI
+// that show bookmarks: bookmark manager, bookmark bar view ...
+namespace bookmark_utils {
// Calculates the drop operation given the event and supported set of
// operations. This prefers the following ordering: COPY, LINK then MOVE.
@@ -40,6 +44,24 @@ void CloneDragData(BookmarkModel* model,
BookmarkNode* parent,
int index_to_add_at);
-}
+// Recursively opens all bookmarks. |initial_disposition| dictates how the
+// first URL is opened, all subsequent URLs are opened as background tabs.
+// |navigator| is used to open the URLs. If |navigator| is NULL the last
+// tabbed browser with the profile |profile| is used. If there is no browser
+// with the specified profile a new one is created.
+void OpenAll(HWND parent,
+ Profile* profile,
+ PageNavigator* navigator,
+ const std::vector<BookmarkNode*>& nodes,
+ WindowOpenDisposition initial_disposition);
+
+// Convenience for opening a single BookmarkNode.
+void OpenAll(HWND parent,
+ Profile* profile,
+ PageNavigator* navigator,
+ BookmarkNode* node,
+ WindowOpenDisposition initial_disposition);
+
+} // namespace bookmark_utils
-#endif // CHROME_BROWSER_BOOKMARKS_BOOKMARK_DRAG_UTILS_H_
+#endif // CHROME_BROWSER_BOOKMARKS_BOOKMARK_UTILS_H_
diff --git a/chrome/browser/browser.vcproj b/chrome/browser/browser.vcproj
index 75c604e..d9daed9 100644
--- a/chrome/browser/browser.vcproj
+++ b/chrome/browser/browser.vcproj
@@ -722,11 +722,11 @@
Name="Bookmarks"
>
<File
- RelativePath=".\bookmark_bar_context_menu_controller.cc"
+ RelativePath=".\bookmarks\bookmark_context_menu.cc"
>
</File>
<File
- RelativePath=".\bookmark_bar_context_menu_controller.h"
+ RelativePath=".\bookmarks\bookmark_context_menu.h"
>
</File>
<File
@@ -746,14 +746,6 @@
>
</File>
<File
- RelativePath=".\bookmarks\bookmark_drag_utils.cc"
- >
- </File>
- <File
- RelativePath=".\bookmarks\bookmark_drag_utils.h"
- >
- </File>
- <File
RelativePath=".\bookmarks\bookmark_folder_tree_model.cc"
>
</File>
@@ -789,6 +781,14 @@
RelativePath=".\bookmarks\bookmark_table_model.h"
>
</File>
+ <File
+ RelativePath=".\bookmarks\bookmark_utils.cc"
+ >
+ </File>
+ <File
+ RelativePath=".\bookmarks\bookmark_utils.h"
+ >
+ </File>
</Filter>
<Filter
Name="Tab Contents"
diff --git a/chrome/browser/browser_commands.cc b/chrome/browser/browser_commands.cc
index 0f01548..a4df1c1 100644
--- a/chrome/browser/browser_commands.cc
+++ b/chrome/browser/browser_commands.cc
@@ -25,6 +25,8 @@
#include "chrome/browser/task_manager.h"
#include "chrome/browser/user_metrics.h"
#include "chrome/browser/views/about_chrome_view.h"
+#include "chrome/browser/views/bookmark_bar_view.h"
+#include "chrome/browser/views/bookmark_manager_view.h"
#include "chrome/browser/views/bug_report_view.h"
#include "chrome/browser/views/clear_browsing_data.h"
#include "chrome/browser/views/importer_view.h"
@@ -104,6 +106,7 @@ void Browser::InitCommandState() {
controller_.UpdateCommandEnabled(IDC_ABOUT, true);
controller_.UpdateCommandEnabled(IDC_SHOW_HISTORY, true);
controller_.UpdateCommandEnabled(IDC_SHOW_BOOKMARKS_BAR, true);
+ controller_.UpdateCommandEnabled(IDC_SHOW_BOOKMARK_MANAGER, true);
controller_.UpdateCommandEnabled(IDC_SHOW_DOWNLOADS, true);
controller_.UpdateCommandEnabled(IDC_ENCODING, true);
controller_.UpdateCommandEnabled(IDC_ENCODING_AUTO_DETECT, true);
@@ -650,22 +653,15 @@ void Browser::ExecuteCommand(int id) {
DuplicateContentsAt(selected_index());
break;
- case IDC_SHOW_BOOKMARKS_BAR: {
+ case IDC_SHOW_BOOKMARKS_BAR:
UserMetrics::RecordAction(L"ShowBookmarksBar", profile_);
+ BookmarkBarView::ToggleWhenVisible(profile_);
+ break;
- // Invert the current pref.
- PrefService* prefs = profile_->GetPrefs();
- prefs->SetBoolean(prefs::kShowBookmarkBar,
- !prefs->GetBoolean(prefs::kShowBookmarkBar));
- 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());
+ case IDC_SHOW_BOOKMARK_MANAGER:
+ UserMetrics::RecordAction(L"ShowBookmarkManager", profile_);
+ BookmarkManagerView::Show(profile_);
break;
- }
case IDC_SHOW_HISTORY:
UserMetrics::RecordAction(L"ShowHistory", profile_);
diff --git a/chrome/browser/browser_prefs.cc b/chrome/browser/browser_prefs.cc
index 9f43083..46deec6 100644
--- a/chrome/browser/browser_prefs.cc
+++ b/chrome/browser/browser_prefs.cc
@@ -21,6 +21,8 @@
#include "chrome/browser/task_manager.h"
#include "chrome/browser/template_url_prepopulate_data.h"
#include "chrome/browser/views/bookmark_bar_view.h"
+#include "chrome/browser/views/bookmark_manager_view.h"
+#include "chrome/browser/views/bookmark_table_view.h"
#include "chrome/browser/views/keyword_editor_view.h"
#include "chrome/browser/views/page_info_window.h"
#include "chrome/browser/web_contents.h"
@@ -29,6 +31,7 @@ namespace browser {
void RegisterAllPrefs(PrefService* user_prefs, PrefService* local_state) {
// Prefs in Local State
+ BookmarkManagerView::RegisterPrefs(local_state);
Browser::RegisterPrefs(local_state);
CacheManagerHost::RegisterPrefs(local_state);
chrome_browser_net::RegisterPrefs(local_state);
@@ -44,6 +47,7 @@ void RegisterAllPrefs(PrefService* user_prefs, PrefService* local_state) {
// User prefs
BookmarkBarView::RegisterUserPrefs(user_prefs);
+ BookmarkTableView::RegisterUserPrefs(user_prefs);
Browser::RegisterUserPrefs(user_prefs);
chrome_browser_net::RegisterUserPrefs(user_prefs);
DownloadManager::RegisterUserPrefs(user_prefs);
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();