diff options
author | sky@google.com <sky@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-03-12 19:10:22 +0000 |
---|---|---|
committer | sky@google.com <sky@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-03-12 19:10:22 +0000 |
commit | 7aa4c00e8f6464212974198b3a8a1bd4427fe53d (patch) | |
tree | 1f7b0b358e9e9e225df536ad268536b9e402561d /chrome/browser/bookmarks | |
parent | b3fb80902af2bbb14a755c9a77adb6c21d4987a3 (diff) | |
download | chromium_src-7aa4c00e8f6464212974198b3a8a1bd4427fe53d.zip chromium_src-7aa4c00e8f6464212974198b3a8a1bd4427fe53d.tar.gz chromium_src-7aa4c00e8f6464212974198b3a8a1bd4427fe53d.tar.bz2 |
Refactors code for showing bookmarks menus into a standalone class
that isn't tied to the bookmark bar. Removes some dead code from the
bookmark bar, and fixes a UMR in chrome_menu that I found when running
under purify. There isn't any behavior change in this patch.
BUG=none
TEST=this is a large refactoring of how bookmark menus are
shown. Please thoroughly test menus on the bookmark bar to make sure I
didn't screw anything up.
Review URL: http://codereview.chromium.org/42128
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@11558 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser/bookmarks')
-rw-r--r-- | chrome/browser/bookmarks/base_bookmark_model_observer.h | 57 | ||||
-rw-r--r-- | chrome/browser/bookmarks/bookmark_menu_controller.cc | 232 | ||||
-rw-r--r-- | chrome/browser/bookmarks/bookmark_menu_controller.h | 137 | ||||
-rw-r--r-- | chrome/browser/bookmarks/bookmark_utils.cc | 57 | ||||
-rw-r--r-- | chrome/browser/bookmarks/bookmark_utils.h | 23 |
5 files changed, 506 insertions, 0 deletions
diff --git a/chrome/browser/bookmarks/base_bookmark_model_observer.h b/chrome/browser/bookmarks/base_bookmark_model_observer.h new file mode 100644 index 0000000..34d755f --- /dev/null +++ b/chrome/browser/bookmarks/base_bookmark_model_observer.h @@ -0,0 +1,57 @@ +// Copyright (c) 2009 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_BASE_BOOKMARK_MODEL_OBSERVER_H_ +#define CHROME_BROWSER_BOOKMARKS_BASE_BOOKMARK_MODEL_OBSERVER_H_ + +#include "chrome/browser/bookmarks/bookmark_model.h" + +// Base class for a BookmarkModelObserver implementation. All mutations of the +// model funnel into the method BookmarkModelChanged. +class BaseBookmarkModelObserver : public BookmarkModelObserver { + public: + BaseBookmarkModelObserver() {} + virtual ~BaseBookmarkModelObserver() {} + + virtual void BookmarkModelChanged() = 0; + + virtual void Loaded(BookmarkModel* model) {} + + virtual void BookmarkModelBeingDeleted(BookmarkModel* model) { + BookmarkModelChanged(); + } + virtual void BookmarkNodeMoved(BookmarkModel* model, + BookmarkNode* old_parent, + int old_index, + BookmarkNode* new_parent, + int new_index) { + BookmarkModelChanged(); + } + virtual void BookmarkNodeAdded(BookmarkModel* model, + BookmarkNode* parent, + int index) { + BookmarkModelChanged(); + } + virtual void BookmarkNodeRemoved(BookmarkModel* model, + BookmarkNode* parent, + int index) { + BookmarkModelChanged(); + } + virtual void BookmarkNodeChanged(BookmarkModel* model, + BookmarkNode* node) { + BookmarkModelChanged(); + } + virtual void BookmarkNodeFavIconLoaded(BookmarkModel* model, + BookmarkNode* node) { + } + virtual void BookmarkNodeChildrenReordered(BookmarkModel* model, + BookmarkNode* node) { + BookmarkModelChanged(); + } + + private: + DISALLOW_COPY_AND_ASSIGN(BaseBookmarkModelObserver); +}; + +#endif // CHROME_BROWSER_BOOKMARKS_BASE_BOOKMARK_MODEL_OBSERVER_H_ diff --git a/chrome/browser/bookmarks/bookmark_menu_controller.cc b/chrome/browser/bookmarks/bookmark_menu_controller.cc new file mode 100644 index 0000000..058a2f5 --- /dev/null +++ b/chrome/browser/bookmarks/bookmark_menu_controller.cc @@ -0,0 +1,232 @@ +// Copyright (c) 2009 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_menu_controller.h" + +#include "chrome/browser/bookmarks/bookmark_drag_data.h" +#include "chrome/browser/bookmarks/bookmark_utils.h" +#include "chrome/browser/metrics/user_metrics.h" +#include "chrome/browser/profile.h" +#include "chrome/browser/tab_contents/page_navigator.h" +#include "chrome/browser/views/event_utils.h" +#include "chrome/common/os_exchange_data.h" +#include "chrome/common/page_transition_types.h" +#include "chrome/common/resource_bundle.h" +#include "grit/theme_resources.h" + +BookmarkMenuController::BookmarkMenuController(Browser* browser, + Profile* profile, + PageNavigator* navigator, + HWND hwnd, + BookmarkNode* node, + int start_child_index) + : browser_(browser), + profile_(profile), + page_navigator_(navigator), + hwnd_(hwnd), + node_(node), + observer_(NULL), + for_drop_(false) { + menu_.reset(new views::MenuItemView(this)); + int next_menu_id = 1; + menu_id_to_node_map_[menu_->GetCommand()] = node; + menu_->set_has_icons(true); + BuildMenu(node, start_child_index, menu_.get(), &next_menu_id); +} + +void BookmarkMenuController::RunMenuAt( + const gfx::Rect& bounds, + views::MenuItemView::AnchorPosition position, + bool for_drop) { + for_drop_ = for_drop; + profile_->GetBookmarkModel()->AddObserver(this); + if (for_drop) { + menu_->RunMenuForDropAt(hwnd_, bounds, position); + } else { + menu_->RunMenuAt(hwnd_, bounds, position, false); + delete this; + } +} + +void BookmarkMenuController::Cancel() { + menu_->Cancel(); +} + +bool BookmarkMenuController::IsTriggerableEvent(const views::MouseEvent& e) { + return event_utils::IsPossibleDispositionEvent(e); +} + +void BookmarkMenuController::ExecuteCommand(int id, int mouse_event_flags) { + DCHECK(page_navigator_); + DCHECK(menu_id_to_node_map_.find(id) != menu_id_to_node_map_.end()); + GURL url = menu_id_to_node_map_[id]->GetURL(); + page_navigator_->OpenURL( + url, GURL(), event_utils::DispositionFromEventFlags(mouse_event_flags), + PageTransition::AUTO_BOOKMARK); +} + +bool BookmarkMenuController::CanDrop(views::MenuItemView* menu, + const OSExchangeData& data) { + // Only accept drops of 1 node, which is the case for all data dragged from + // bookmark bar and menus. + + if (!drop_data_.Read(data) || drop_data_.elements.size() != 1) + return false; + + if (drop_data_.has_single_url()) + return true; + + BookmarkNode* drag_node = drop_data_.GetFirstNode(profile_); + if (!drag_node) { + // Dragging a group from another profile, always accept. + return true; + } + + // Drag originated from same profile and is not a URL. Only accept it if + // the dragged node is not a parent of the node menu represents. + BookmarkNode* drop_node = menu_id_to_node_map_[menu->GetCommand()]; + DCHECK(drop_node); + BookmarkNode* node = drop_node; + while (drop_node && drop_node != drag_node) + drop_node = drop_node->GetParent(); + return (drop_node == NULL); +} + +int BookmarkMenuController::GetDropOperation( + views::MenuItemView* item, + const views::DropTargetEvent& event, + DropPosition* position) { + // Should only get here if we have drop data. + DCHECK(drop_data_.is_valid()); + + BookmarkNode* node = menu_id_to_node_map_[item->GetCommand()]; + BookmarkNode* drop_parent = node->GetParent(); + int index_to_drop_at = drop_parent->IndexOfChild(node); + if (*position == DROP_AFTER) { + index_to_drop_at++; + } else if (*position == DROP_ON) { + drop_parent = node; + index_to_drop_at = node->GetChildCount(); + } + DCHECK(drop_parent); + return bookmark_utils::BookmarkDropOperation( + profile_, event, drop_data_, drop_parent, index_to_drop_at); +} + +int BookmarkMenuController::OnPerformDrop(views::MenuItemView* menu, + DropPosition position, + const views::DropTargetEvent& event) { + BookmarkNode* drop_node = menu_id_to_node_map_[menu->GetCommand()]; + DCHECK(drop_node); + BookmarkModel* model = profile_->GetBookmarkModel(); + DCHECK(model); + BookmarkNode* drop_parent = drop_node->GetParent(); + DCHECK(drop_parent); + int index_to_drop_at = drop_parent->IndexOfChild(drop_node); + if (position == DROP_AFTER) { + index_to_drop_at++; + } else if (position == DROP_ON) { + DCHECK(drop_node->is_folder()); + drop_parent = drop_node; + index_to_drop_at = drop_node->GetChildCount(); + } + + int result = bookmark_utils::PerformBookmarkDrop( + profile_, drop_data_, drop_parent, index_to_drop_at); + if (for_drop_) + delete this; + return result; +} + +bool BookmarkMenuController::ShowContextMenu(views::MenuItemView* source, + int id, + int x, + 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 BookmarkContextMenu(hwnd_, + profile_, + browser_, + page_navigator_, + nodes[0]->GetParent(), + nodes, + BookmarkContextMenu::BOOKMARK_BAR)); + context_menu_->RunMenuAt(x, y); + context_menu_.reset(NULL); + return true; +} + +void BookmarkMenuController::DropMenuClosed(views::MenuItemView* menu) { + delete this; +} + +bool BookmarkMenuController::CanDrag(views::MenuItemView* menu) { + return true; +} + +void BookmarkMenuController::WriteDragData(views::MenuItemView* sender, + OSExchangeData* data) { + DCHECK(sender && data); + + UserMetrics::RecordAction(L"BookmarkBar_DragFromFolder", profile_); + + BookmarkDragData drag_data(menu_id_to_node_map_[sender->GetCommand()]); + drag_data.Write(profile_, data); +} + +int BookmarkMenuController::GetDragOperations(views::MenuItemView* sender) { + return bookmark_utils::BookmarkDragOperation( + menu_id_to_node_map_[sender->GetCommand()]); +} + +void BookmarkMenuController::BookmarkModelChanged() { + menu_->Cancel(); +} + +void BookmarkMenuController::BookmarkNodeFavIconLoaded(BookmarkModel* model, + BookmarkNode* node) { + if (node_to_menu_id_map_.find(node) != node_to_menu_id_map_.end()) + menu_->SetIcon(node->GetFavIcon(), node_to_menu_id_map_[node]); +} + +void BookmarkMenuController::BuildMenu(BookmarkNode* parent, + int start_child_index, + views::MenuItemView* menu, + int* next_menu_id) { + DCHECK(!parent->GetChildCount() || + start_child_index < parent->GetChildCount()); + for (int i = start_child_index; i < parent->GetChildCount(); ++i) { + BookmarkNode* node = parent->GetChild(i); + int id = *next_menu_id; + + (*next_menu_id)++; + if (node->is_url()) { + SkBitmap icon = node->GetFavIcon(); + if (icon.width() == 0) { + icon = *ResourceBundle::GetSharedInstance(). + GetBitmapNamed(IDR_DEFAULT_FAVICON); + } + menu->AppendMenuItemWithIcon(id, node->GetTitle(), icon); + node_to_menu_id_map_[node] = id; + } else if (node->is_folder()) { + SkBitmap* folder_icon = ResourceBundle::GetSharedInstance(). + GetBitmapNamed(IDR_BOOKMARK_BAR_FOLDER); + views::MenuItemView* submenu = + menu->AppendSubMenuWithIcon(id, node->GetTitle(), *folder_icon); + BuildMenu(node, 0, submenu, next_menu_id); + } else { + NOTREACHED(); + } + menu_id_to_node_map_[id] = node; + } +} + +BookmarkMenuController::~BookmarkMenuController() { + profile_->GetBookmarkModel()->RemoveObserver(this); + if (observer_) + observer_->BookmarkMenuDeleted(this); +} diff --git a/chrome/browser/bookmarks/bookmark_menu_controller.h b/chrome/browser/bookmarks/bookmark_menu_controller.h new file mode 100644 index 0000000..27636a7 --- /dev/null +++ b/chrome/browser/bookmarks/bookmark_menu_controller.h @@ -0,0 +1,137 @@ +// Copyright (c) 2009 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_MENU_CONTROLLER_H_ +#define CHROME_BROWSER_BOOKMARKS_BOOKMARK_MENU_CONTROLLER_H_ + +#include <map> + +#include "chrome/browser/bookmarks/base_bookmark_model_observer.h" +#include "chrome/browser/bookmarks/bookmark_context_menu.h" +#include "chrome/browser/bookmarks/bookmark_drag_data.h" +#include "chrome/views/chrome_menu.h" + +class BookmarkContextMenu; +class BookmarkNode; +class Browser; +class OSExchangeData; +class PageNavigator; +class Profile; + +// BookmarkMenuController is responsible for showing a menu of bookmarks, +// each item in the menu represents a bookmark. +// BookmarkMenuController deletes itself as necessary, although the menu can +// be explicitly hidden by way of the Cancel method. +class BookmarkMenuController : public BaseBookmarkModelObserver, + public views::MenuDelegate { + public: + // The observer is notified prior to the menu being deleted. + class Observer { + public: + virtual void BookmarkMenuDeleted(BookmarkMenuController* controller) = 0; + }; + + // Creates a BookmarkMenuController showing the children of |node| starting + // at index |start_child_index|. + BookmarkMenuController(Browser* browser, + Profile* profile, + PageNavigator* page_navigator, + HWND hwnd, + BookmarkNode* node, + int start_child_index); + + // Shows the menu. + void RunMenuAt(const gfx::Rect& bounds, + views::MenuItemView::AnchorPosition position, + bool for_drop); + + // Hides the menu. + void Cancel(); + + // Returns the node the menu is showing for. + BookmarkNode* node() const { return node_; } + + // Returns the menu. + views::MenuItemView* menu() const { return menu_.get(); } + + // Returns the context menu, or NULL if the context menu isn't showing. + views::MenuItemView* context_menu() const { + return context_menu_.get() ? context_menu_->menu() : NULL; + } + + void set_observer(Observer* observer) { observer_ = observer; } + + // MenuDelegate methods. + virtual bool IsTriggerableEvent(const views::MouseEvent& e); + virtual void ExecuteCommand(int id, int mouse_event_flags); + virtual bool CanDrop(views::MenuItemView* menu, const OSExchangeData& data); + virtual int GetDropOperation(views::MenuItemView* item, + const views::DropTargetEvent& event, + DropPosition* position); + virtual int OnPerformDrop(views::MenuItemView* menu, + DropPosition position, + const views::DropTargetEvent& event); + virtual bool ShowContextMenu(views::MenuItemView* source, + int id, + int x, + int y, + bool is_mouse_gesture); + virtual void DropMenuClosed(views::MenuItemView* menu); + virtual bool CanDrag(views::MenuItemView* menu); + virtual void WriteDragData(views::MenuItemView* sender, OSExchangeData* data); + virtual int GetDragOperations(views::MenuItemView* sender); + + virtual void BookmarkModelChanged(); + virtual void BookmarkNodeFavIconLoaded(BookmarkModel* model, + BookmarkNode* node); + + private: + // BookmarkMenuController deletes itself as necessary. + ~BookmarkMenuController(); + + // Creates an entry in menu for each child node of |parent| starting at + // |start_child_index|. + void BuildMenu(BookmarkNode* parent, + int start_child_index, + views::MenuItemView* menu, + int* next_menu_id); + + Browser* browser_; + + Profile* profile_; + + PageNavigator* page_navigator_; + + // Parent of menus. + HWND hwnd_; + + // The node we're showing the contents of. + BookmarkNode* node_; + + // Maps from menu id to BookmarkNode. + std::map<int, BookmarkNode*> menu_id_to_node_map_; + + // Mapping from node to menu id. This only contains entries for nodes of type + // URL. + std::map<BookmarkNode*, int> node_to_menu_id_map_; + + // The menu. + scoped_ptr<views::MenuItemView> menu_; + + // Data for the drop. + BookmarkDragData drop_data_; + + // Used when a context menu is shown. + scoped_ptr<BookmarkContextMenu> context_menu_; + + // The observer, may be null. + Observer* observer_; + + // Is the menu being shown for a drop? + bool for_drop_; + + DISALLOW_COPY_AND_ASSIGN(BookmarkMenuController); +}; + +#endif // CHROME_BROWSER_BOOKMARKS_BOOKMARK_MENU_CONTROLLER_H_ diff --git a/chrome/browser/bookmarks/bookmark_utils.cc b/chrome/browser/bookmarks/bookmark_utils.cc index 2d33794..c9214b4 100644 --- a/chrome/browser/bookmarks/bookmark_utils.cc +++ b/chrome/browser/bookmarks/bookmark_utils.cc @@ -201,6 +201,63 @@ int PreferredDropOperation(int source_operations, int operations) { return DragDropTypes::DRAG_NONE; } +int BookmarkDragOperation(BookmarkNode* node) { + if (node->is_url()) { + return DragDropTypes::DRAG_COPY | DragDropTypes::DRAG_MOVE | + DragDropTypes::DRAG_LINK; + } + return DragDropTypes::DRAG_COPY | DragDropTypes::DRAG_MOVE; +} + +int BookmarkDropOperation(Profile* profile, + const views::DropTargetEvent& event, + const BookmarkDragData& data, + BookmarkNode* parent, + int index) { + if (data.IsFromProfile(profile) && data.size() > 1) + // Currently only accept one dragged node at a time. + return DragDropTypes::DRAG_NONE; + + if (!bookmark_utils::IsValidDropLocation(profile, data, parent, index)) + return DragDropTypes::DRAG_NONE; + + if (data.GetFirstNode(profile)) { + // User is dragging from this profile: move. + return DragDropTypes::DRAG_MOVE; + } + // User is dragging from another app, copy. + return PreferredDropOperation(event.GetSourceOperations(), + DragDropTypes::DRAG_COPY | DragDropTypes::DRAG_LINK); +} + +int PerformBookmarkDrop(Profile* profile, + const BookmarkDragData& data, + BookmarkNode* parent_node, + int index) { + BookmarkNode* dragged_node = data.GetFirstNode(profile); + BookmarkModel* model = profile->GetBookmarkModel(); + if (dragged_node) { + // Drag from same profile, do a move. + model->Move(dragged_node, parent_node, index); + return DragDropTypes::DRAG_MOVE; + } else if (data.has_single_url()) { + // New URL, add it at the specified location. + std::wstring title = data.elements[0].title; + if (title.empty()) { + // No title, use the host. + title = UTF8ToWide(data.elements[0].url.host()); + if (title.empty()) + title = l10n_util::GetString(IDS_BOOMARK_BAR_UNKNOWN_DRAG_TITLE); + } + model->AddURL(parent_node, index, title, data.elements[0].url); + return DragDropTypes::DRAG_COPY | DragDropTypes::DRAG_LINK; + } else { + // Dropping a group from different profile. Always accept. + bookmark_utils::CloneDragData(model, data.elements, parent_node, index); + return DragDropTypes::DRAG_COPY; + } +} + bool IsValidDropLocation(Profile* profile, const BookmarkDragData& data, BookmarkNode* drop_parent, diff --git a/chrome/browser/bookmarks/bookmark_utils.h b/chrome/browser/bookmarks/bookmark_utils.h index 9419731..26e60ef 100644 --- a/chrome/browser/bookmarks/bookmark_utils.h +++ b/chrome/browser/bookmarks/bookmark_utils.h @@ -17,6 +17,10 @@ class BookmarkNode; class PageNavigator; class Profile; +namespace views { +class DropTargetEvent; +} + // A collection of bookmark utility functions used by various parts of the UI // that show bookmarks: bookmark manager, bookmark bar view ... namespace bookmark_utils { @@ -26,6 +30,25 @@ namespace bookmark_utils { // COPY, LINK then MOVE. int PreferredDropOperation(int source_operations, int operations); +// Returns the drag operations for the specified node. +int BookmarkDragOperation(BookmarkNode* node); + +// Returns the preferred drop operation on a bookmark menu/bar. +// |parent| is the parent node the drop is to occur on and |index| the index the +// drop is over. +int BookmarkDropOperation(Profile* profile, + const views::DropTargetEvent& event, + const BookmarkDragData& data, + BookmarkNode* parent, + int index); + +// Performs a drop of bookmark data onto |parent_node| at |index|. Returns the +// type of drop the resulted. +int PerformBookmarkDrop(Profile* profile, + const BookmarkDragData& data, + BookmarkNode* parent_node, + int index); + // Returns true if the bookmark data can be dropped on |drop_parent| at // |index|. A drop from a separate profile is always allowed, where as // a drop from the same profile is only allowed if none of the nodes in |