diff options
author | sky@google.com <sky@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-11-04 19:29:08 +0000 |
---|---|---|
committer | sky@google.com <sky@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2008-11-04 19:29:08 +0000 |
commit | f28cbb7246977eb443650f7d2fc9675b573317b2 (patch) | |
tree | dda1262734dd45010df771821e869b687b9d1b6a /chrome/browser | |
parent | 431c16ea953a80b9153ba9a8c0b17b07a96f4a24 (diff) | |
download | chromium_src-f28cbb7246977eb443650f7d2fc9675b573317b2.zip chromium_src-f28cbb7246977eb443650f7d2fc9675b573317b2.tar.gz chromium_src-f28cbb7246977eb443650f7d2fc9675b573317b2.tar.bz2 |
Adds support for autoscrolling on drag to bookmark tree/table.
BUG=674
TEST=none
Review URL: http://codereview.chromium.org/9042
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@4625 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome/browser')
-rw-r--r-- | chrome/browser/bookmarks/bookmark_drop_info.cc | 44 | ||||
-rw-r--r-- | chrome/browser/bookmarks/bookmark_drop_info.h | 85 | ||||
-rw-r--r-- | chrome/browser/bookmarks/bookmark_utils.cc | 5 | ||||
-rw-r--r-- | chrome/browser/bookmarks/bookmark_utils.h | 12 | ||||
-rw-r--r-- | chrome/browser/browser.vcproj | 8 | ||||
-rw-r--r-- | chrome/browser/views/bookmark_bar_view.cc | 12 | ||||
-rw-r--r-- | chrome/browser/views/bookmark_folder_tree_view.cc | 179 | ||||
-rw-r--r-- | chrome/browser/views/bookmark_folder_tree_view.h | 111 | ||||
-rw-r--r-- | chrome/browser/views/bookmark_table_view.cc | 159 | ||||
-rw-r--r-- | chrome/browser/views/bookmark_table_view.h | 87 |
10 files changed, 437 insertions, 265 deletions
diff --git a/chrome/browser/bookmarks/bookmark_drop_info.cc b/chrome/browser/bookmarks/bookmark_drop_info.cc new file mode 100644 index 0000000..646b35b --- /dev/null +++ b/chrome/browser/bookmarks/bookmark_drop_info.cc @@ -0,0 +1,44 @@ +// 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_drop_info.h" + +#include "chrome/views/event.h" +#include "chrome/views/view_constants.h" + +BookmarkDropInfo::BookmarkDropInfo(HWND hwnd, int top_margin) + : source_operations_(0), + is_control_down_(false), + last_y_(0), + drop_operation_(0), + hwnd_(hwnd), + top_margin_(top_margin), + scroll_up_(false) { +} + +void BookmarkDropInfo::Update(const views::DropTargetEvent& event) { + source_operations_ = event.GetSourceOperations(); + is_control_down_ = event.IsControlDown(); + last_y_ = event.y(); + + RECT client_rect; + GetClientRect(hwnd_, &client_rect); + scroll_up_ = (last_y_ <= top_margin_ + views::kAutoscrollSize); + bool scroll_down = (last_y_ >= client_rect.bottom - views::kAutoscrollSize); + if (scroll_up_ || scroll_down) { + if (!scroll_timer_.IsRunning()) { + scroll_timer_.Start( + base::TimeDelta::FromMilliseconds(views::kAutoscrollRowTimerMS), + this, + &BookmarkDropInfo::Scroll); + } + } else { + scroll_timer_.Stop(); + } +} + +void BookmarkDropInfo::Scroll() { + SendMessage(hwnd_, WM_VSCROLL, scroll_up_ ? SB_LINEUP : SB_LINEDOWN, NULL); + Scrolled(); +} diff --git a/chrome/browser/bookmarks/bookmark_drop_info.h b/chrome/browser/bookmarks/bookmark_drop_info.h new file mode 100644 index 0000000..f4daf89 --- /dev/null +++ b/chrome/browser/bookmarks/bookmark_drop_info.h @@ -0,0 +1,85 @@ +// 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_DROP_INFO_H_ +#define CHROME_BROWSER_BOOKMARKS_BOOKMARK_DROP_INFO_H_ + +#include <windows.h> + +#include "base/timer.h" +#include "chrome/browser/bookmarks/bookmark_drag_data.h" + +namespace views { +class DropTargetEvent; +} + +// BookmarkDropInfo is a pure virtual class that provides auto-scrolling +// behavior and a handful of fields used for managing a bookmark drop. +// BookmarkDropInfo is used by both BookmarkTableView and +// BookmarksFolderTreeView. +class BookmarkDropInfo { + public: + BookmarkDropInfo(HWND hwnd, int top_margin); + virtual ~BookmarkDropInfo() {} + + // Invoke this from OnDragUpdated. It resets source_operations, + // is_control_down, last_y and updates the autoscroll timer as necessary. + void Update(const views::DropTargetEvent& event); + + // Data from the drag. + void SetData(const BookmarkDragData& data) { data_ = data; } + BookmarkDragData& data() { return data_; } + + // Value of event.GetSourceOperations when Update was last invoked. + int source_operations() const { return source_operations_; } + + // Whether the control key was down last time Update was invoked. + bool is_control_down() const { return is_control_down_; } + + // Y position of the event last passed to Update. + int last_y() { return last_y_; } + + // The drop operation that should occur. This is not updated by + // BookmarkDropInfo, but provided for subclasses. + void set_drop_operation(int drop_operation) { + drop_operation_ = drop_operation; + } + int drop_operation() const { return drop_operation_; } + + protected: + // Invoked if we autoscroll. When invoked subclasses need to determine + // whether the drop is valid again as what is under the mouse has likely + // scrolled. + virtual void Scrolled() = 0; + + private: + // Invoked from the timer. Scrolls up/down a line. + void Scroll(); + + BookmarkDragData data_; + + int source_operations_; + + bool is_control_down_; + + int last_y_; + + int drop_operation_; + + HWND hwnd_; + + // Margin in addition to views::kAutoscrollSize that the mouse is allowed to + // be over before we autoscroll. + int top_margin_; + + // When autoscrolling this determines if we're scrolling up or down. + bool scroll_up_; + + // Used when autoscrolling. + base::RepeatingTimer<BookmarkDropInfo> scroll_timer_; + + DISALLOW_COPY_AND_ASSIGN(BookmarkDropInfo); +}; + +#endif // CHROME_BROWSER_BOOKMARKS_BOOKMARK_DROP_INFO_H_ diff --git a/chrome/browser/bookmarks/bookmark_utils.cc b/chrome/browser/bookmarks/bookmark_utils.cc index d9b0be1..bf2576f 100644 --- a/chrome/browser/bookmarks/bookmark_utils.cc +++ b/chrome/browser/bookmarks/bookmark_utils.cc @@ -146,9 +146,8 @@ bool ShouldOpenAll(HWND parent, const std::vector<BookmarkNode*>& nodes) { namespace bookmark_utils { -int PreferredDropOperation(const views::DropTargetEvent& event, - int operation) { - int common_ops = (event.GetSourceOperations() & operation); +int PreferredDropOperation(int source_operations, int operations) { + int common_ops = (source_operations & operations); if (!common_ops) return 0; if (DragDropTypes::DRAG_COPY & common_ops) diff --git a/chrome/browser/bookmarks/bookmark_utils.h b/chrome/browser/bookmarks/bookmark_utils.h index 0711638..281cf8c 100644 --- a/chrome/browser/bookmarks/bookmark_utils.h +++ b/chrome/browser/bookmarks/bookmark_utils.h @@ -14,18 +14,14 @@ 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 { -// Calculates the drop operation given the event and supported set of -// operations. This prefers the following ordering: COPY, LINK then MOVE. -int PreferredDropOperation(const views::DropTargetEvent& event, - int operation); +// Calculates the drop operation given |source_operations| and the ideal +// set of drop operations (|operations|). This prefers the following ordering: +// COPY, LINK then MOVE. +int PreferredDropOperation(int source_operations, int operations); // 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 diff --git a/chrome/browser/browser.vcproj b/chrome/browser/browser.vcproj index d9daed9..033b3e3 100644 --- a/chrome/browser/browser.vcproj +++ b/chrome/browser/browser.vcproj @@ -746,6 +746,14 @@ > </File> <File + RelativePath=".\bookmarks\bookmark_drop_info.cc" + > + </File> + <File + RelativePath=".\bookmarks\bookmark_drop_info.h" + > + </File> + <File RelativePath=".\bookmarks\bookmark_folder_tree_model.cc" > </File> diff --git a/chrome/browser/views/bookmark_bar_view.cc b/chrome/browser/views/bookmark_bar_view.cc index 4e2f09f..3e514ba 100644 --- a/chrome/browser/views/bookmark_bar_view.cc +++ b/chrome/browser/views/bookmark_bar_view.cc @@ -44,6 +44,7 @@ #include "chrome/views/container.h" #include "chrome/views/menu_button.h" #include "chrome/views/tooltip_manager.h" +#include "chrome/views/view_constants.h" #include "chrome/views/window.h" #include "generated_resources.h" @@ -1669,7 +1670,9 @@ int BookmarkBarView::CalculateDropOperation(const DropTargetEvent& event, int ops = data.GetFirstNode(profile_) ? DragDropTypes::DRAG_MOVE : DragDropTypes::DRAG_COPY | DragDropTypes::DRAG_LINK; - return bookmark_utils::PreferredDropOperation(event, ops); + return + bookmark_utils::PreferredDropOperation(event.GetSourceOperations(), + ops); } for (int i = 0; i < GetBookmarkButtonCount() && @@ -1681,9 +1684,9 @@ int BookmarkBarView::CalculateDropOperation(const DropTargetEvent& event, found = true; BookmarkNode* node = model_->GetBookmarkBarNode()->GetChild(i); if (node->GetType() != history::StarredEntry::URL) { - if (button_x <= MenuItemView::kDropBetweenPixels) { + if (button_x <= views::kDropBetweenPixels) { *index = i; - } else if (button_x < button_w - MenuItemView::kDropBetweenPixels) { + } else if (button_x < button_w - views::kDropBetweenPixels) { *index = i; *drop_on = true; } else { @@ -1759,7 +1762,8 @@ int BookmarkBarView::CalculateDropOperation(const DropTargetEvent& event, } else { // User is dragging from another app, copy. return bookmark_utils::PreferredDropOperation( - event, DragDropTypes::DRAG_COPY | DragDropTypes::DRAG_LINK); + event.GetSourceOperations(), + DragDropTypes::DRAG_COPY | DragDropTypes::DRAG_LINK); } } diff --git a/chrome/browser/views/bookmark_folder_tree_view.cc b/chrome/browser/views/bookmark_folder_tree_view.cc index c068103..29dbceb 100644 --- a/chrome/browser/views/bookmark_folder_tree_view.cc +++ b/chrome/browser/views/bookmark_folder_tree_view.cc @@ -11,10 +11,14 @@ #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 "chrome/views/view_constants.h" #include "generated_resources.h" +void BookmarkFolderTreeView::DropInfo::Scrolled() { + view_->UpdateDropInfo(); +} + BookmarkFolderTreeView::BookmarkFolderTreeView(Profile* profile, BookmarkFolderTreeModel* model) : views::TreeView(), @@ -30,14 +34,18 @@ bool BookmarkFolderTreeView::CanDrop(const OSExchangeData& data) { if (!profile_->GetBookmarkModel()->IsLoaded()) return false; - drop_info_.reset(new DropInfo()); - if (!drop_info_->drag_data.Read(data)) + BookmarkDragData drag_data; + + if (!drag_data.Read(data)) return false; + drop_info_.reset(new DropInfo(this)); + drop_info_->SetData(drag_data); + // 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; + for (size_t i = 0; i < drop_info_->data().size(); ++i) { + if (drop_info_->data().elements[0].is_url) { + drop_info_->set_only_folders(false); break; } } @@ -50,27 +58,12 @@ void BookmarkFolderTreeView::OnDragEntered( } 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; + drop_info_->Update(event); + return UpdateDropInfo(); } void BookmarkFolderTreeView::OnDragExited() { - SetDropParent(NULL, -1, false); + SetDropPosition(DropPosition()); drop_info_.reset(); } @@ -78,8 +71,8 @@ void BookmarkFolderTreeView::OnDragExited() { int BookmarkFolderTreeView::OnPerformDrop(const views::DropTargetEvent& event) { OnPerformDropImpl(); - int drop_operation = drop_info_->drop_operation; - SetDropParent(NULL, -1, false); + int drop_operation = drop_info_->drop_operation(); + SetDropPosition(DropPosition()); drop_info_.reset(); return drop_operation; } @@ -106,6 +99,19 @@ LRESULT BookmarkFolderTreeView::OnNotify(int w_param, LPNMHDR l_param) { return TreeView::OnNotify(w_param, l_param); } +int BookmarkFolderTreeView::UpdateDropInfo() { + DropPosition position = + CalculateDropPosition(drop_info_->last_y(), drop_info_->only_folders()); + drop_info_->set_drop_operation(CalculateDropOperation(position)); + + if (drop_info_->drop_operation() == DragDropTypes::DRAG_NONE) + position = DropPosition(); + + SetDropPosition(position); + + return drop_info_->drop_operation(); +} + 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 @@ -128,12 +134,8 @@ void BookmarkFolderTreeView::BeginDrag(BookmarkNode* node) { is_dragging_ = false; } -FolderNode* BookmarkFolderTreeView::CalculateDropParent(int y, - bool only_folders, - int* drop_index, - bool* drop_on) { - *drop_on = false; - *drop_index = -1; +BookmarkFolderTreeView::DropPosition BookmarkFolderTreeView:: + CalculateDropPosition(int y, bool only_folders) { HWND hwnd = GetNativeControlHWND(); HTREEITEM item = TreeView_GetFirstVisible(hwnd); while (item) { @@ -144,7 +146,7 @@ FolderNode* BookmarkFolderTreeView::CalculateDropParent(int y, if (folder_model()->GetNodeType(model_node) != BookmarkFolderTreeModel::BOOKMARK) { // Only allow drops on bookmark nodes. - return NULL; + return DropPosition(); } FolderNode* node = folder_model()->AsNode(model_node); @@ -153,59 +155,50 @@ FolderNode* BookmarkFolderTreeView::CalculateDropParent(int y, // 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; + return DropPosition(node, node->GetChildCount(), true); } // 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.top + views::kDropBetweenPixels) { + return DropPosition(node->GetParent(), + node->GetParent()->IndexOfChild(node), false); } - if (y >= bounds.bottom - views::MenuItemView::kDropBetweenPixels) { + if (y >= bounds.bottom - views::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; + return DropPosition(node, 0, false); } - *drop_index = node->GetParent()->IndexOfChild(node) + 1; - return node->GetParent(); + return DropPosition(node->GetParent(), + node->GetParent()->IndexOfChild(node) + 1, false); } - *drop_on = true; - *drop_index = node->GetChildCount(); - return node; + return DropPosition(node, node->GetChildCount(), true); } item = TreeView_GetNextVisible(hwnd, item); } - return NULL; + return DropPosition(); } int BookmarkFolderTreeView::CalculateDropOperation( - const views::DropTargetEvent& event, - FolderNode* drop_parent, - int drop_index, - bool drop_on) { - if (!drop_parent) + const DropPosition& position) { + if (!position.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 (drop_info_->data().IsFromProfile(profile_)) { + int bookmark_model_drop_index = FolderIndexToBookmarkIndex(position); if (!bookmark_utils::IsValidDropLocation( - profile_, drop_info_->drag_data, - TreeNodeAsBookmarkNode(drop_parent), + profile_, drop_info_->data(), + TreeNodeAsBookmarkNode(position.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()) + if (drop_info_->is_control_down()) return DragDropTypes::DRAG_COPY; return DragDropTypes::DRAG_MOVE; @@ -213,25 +206,26 @@ int BookmarkFolderTreeView::CalculateDropOperation( // 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); + drop_info_->source_operations(), + 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); + BookmarkNode* parent_node = + TreeNodeAsBookmarkNode(drop_info_->position().parent); + int drop_index = FolderIndexToBookmarkIndex(drop_info_->position()); 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, + if (!drop_info_->data().IsFromProfile(profile_) || + drop_info_->drop_operation() == DragDropTypes::DRAG_COPY) { + bookmark_utils::CloneDragData(model, drop_info_->data().elements, parent_node, drop_index); return; } // else, move. - std::vector<BookmarkNode*> nodes = drop_info_->drag_data.GetNodes(profile_); + std::vector<BookmarkNode*> nodes = drop_info_->data().GetNodes(profile_); if (nodes.empty()) return; @@ -250,46 +244,36 @@ void BookmarkFolderTreeView::OnPerformDropImpl() { } } -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) { +void BookmarkFolderTreeView::SetDropPosition(const DropPosition& position) { + if (drop_info_->position().equals(position)) return; - } + // Remove the indicator over the previous location. - if (drop_info_->drop_on) { - HTREEITEM item = GetTreeItemForNode(drop_info_->drop_parent); + if (drop_info_->position().on) { + HTREEITEM item = GetTreeItemForNode(drop_info_->position().parent); if (item) TreeView_SetItemState(GetNativeControlHWND(), item, 0, TVIS_DROPHILITED); - } else if (drop_info_->drop_index != -1) { + } else if (drop_info_->position().index != -1) { TreeView_SetInsertMark(GetNativeControlHWND(), NULL, FALSE); } - drop_info_->drop_parent = node; - drop_info_->drop_index = drop_index; - drop_info_->drop_on = drop_on; + drop_info_->set_position(position); // And show the new indicator. - if (drop_info_->drop_on) { - HTREEITEM item = GetTreeItemForNode(drop_info_->drop_parent); + if (position.on) { + HTREEITEM item = GetTreeItemForNode(position.parent); if (item) { TreeView_SetItemState(GetNativeControlHWND(), item, TVIS_DROPHILITED, TVIS_DROPHILITED); } - } else if (drop_info_->drop_index != -1) { + } else if (position.index != -1) { BOOL after = FALSE; - if (folder_model()->GetChildCount(drop_info_->drop_parent) == - drop_info_->drop_index) { + FolderNode* node = position.parent; + if (folder_model()->GetChildCount(position.parent) == position.index) { after = TRUE; - node = - folder_model()->GetChild(drop_info_->drop_parent, - drop_info_->drop_index - 1); + node = folder_model()->GetChild(position.parent, position.index - 1); } else { - node = - folder_model()->GetChild(drop_info_->drop_parent, - drop_info_->drop_index); + node = folder_model()->GetChild(position.parent, position.index); } HTREEITEM item = GetTreeItemForNode(node); if (item) @@ -305,16 +289,15 @@ 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()) +int BookmarkFolderTreeView::FolderIndexToBookmarkIndex( + const DropPosition& position) { + BookmarkNode* parent_node = TreeNodeAsBookmarkNode(position.parent); + if (position.on || position.index == position.parent->GetChildCount()) return parent_node->GetChildCount(); - if (index != 0) { + if (position.index != 0) { return parent_node->IndexOfChild( - TreeNodeAsBookmarkNode(node->GetChild(index))); + TreeNodeAsBookmarkNode(position.parent->GetChild(position.index))); } return 0; diff --git a/chrome/browser/views/bookmark_folder_tree_view.h b/chrome/browser/views/bookmark_folder_tree_view.h index ae12b2c..f024e75 100644 --- a/chrome/browser/views/bookmark_folder_tree_view.h +++ b/chrome/browser/views/bookmark_folder_tree_view.h @@ -5,7 +5,9 @@ #ifndef CHROME_BROWSER_VIEWS_BOOKMARK_FOLDER_TREE_VIEW_H_ #define CHROME_BROWSER_VIEWS_BOOKMARK_FOLDER_TREE_VIEW_H_ +#include "base/timer.h" #include "chrome/browser/bookmarks/bookmark_drag_data.h" +#include "chrome/browser/bookmarks/bookmark_drop_info.h" #include "chrome/browser/bookmarks/bookmark_folder_tree_model.h" #include "chrome/views/tree_view.h" @@ -37,61 +39,77 @@ class BookmarkFolderTreeView : public views::TreeView { virtual LRESULT OnNotify(int w_param, LPNMHDR l_param); private: + // DropPosition identifies where the drop should occur. A DropPosition + // consists of the following: the parent FolderNode the drop is to occur at, + // whether the drop is on the parent, and the index into the parent the drop + // should occur at. + // + // WARNING: the index is in terms of the BookmarkFolderTreeModel, which is + // not the same as the BookmarkModel. + struct DropPosition { + DropPosition() : parent(NULL), index(-1), on(false) {} + DropPosition(FolderNode* parent, int index, bool on) + : parent(parent), + index(index), + on(on) {} + + // Returns true if |position| equals this. + bool equals(const DropPosition& position) const { + return (position.parent == parent && position.index == index && + position.on == on); + } + + FolderNode* parent; + int index; + bool on; + }; + // 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; + class DropInfo : public BookmarkDropInfo { + public: + explicit DropInfo(BookmarkFolderTreeView* view) + : BookmarkDropInfo(view->GetNativeControlHWND(), 0), + view_(view), + only_folders_(true) {} + + virtual void Scrolled(); + + // Does drag_data consists of folders only? + void set_only_folders(bool only_folders) { only_folders_ = only_folders; } + bool only_folders() const { return only_folders_; } + + // Position of the drop. + void set_position(const DropPosition& position) { position_ = position; } + const DropPosition& position() const { return position_; } + + private: + BookmarkFolderTreeView* view_; + DropPosition position_; + bool only_folders_; + + DISALLOW_COPY_AND_ASSIGN(DropInfo); }; + friend class DropInfo; + + // Updates drop info. This is invoked both from OnDragUpdated and when we + // autoscroll during a drop. + int UpdateDropInfo(); // 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); + // Calculates the drop position. + DropPosition CalculateDropPosition(int y, bool only_folders); // 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); + // if the position is not valid. + int CalculateDropOperation(const DropPosition& position); // Performs the drop operation. void OnPerformDropImpl(); - // Sets the parent of the drop operation. - void SetDropParent(FolderNode* node, int drop_index, bool drop_on); + // Sets the drop position. + void SetDropPosition(const DropPosition& position); // Returns the model as a BookmarkFolderTreeModel. BookmarkFolderTreeModel* folder_model() const; @@ -99,9 +117,10 @@ class BookmarkFolderTreeView : public views::TreeView { // 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); + // Converts the position in terms of the BookmarkFolderTreeModel to an index + // in terms of the BookmarkModel. The returned index is the index the drop + // should occur at in terms of the BookmarkModel. + int FolderIndexToBookmarkIndex(const DropPosition& position); Profile* profile_; diff --git a/chrome/browser/views/bookmark_table_view.cc b/chrome/browser/views/bookmark_table_view.cc index ffda218..9433976 100644 --- a/chrome/browser/views/bookmark_table_view.cc +++ b/chrome/browser/views/bookmark_table_view.cc @@ -13,7 +13,7 @@ #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 "chrome/views/view_constants.h" #include "generated_resources.h" @@ -35,6 +35,10 @@ int GetWidthOfColumn(const std::vector<views::TableColumn>& columns, } // namespace +void BookmarkTableView::DropInfo::Scrolled() { + view_->UpdateDropInfo(); +} + BookmarkTableView::BookmarkTableView(Profile* profile, BookmarkTableModel* model) : views::TableView(model, std::vector<views::TableColumn>(), @@ -57,17 +61,20 @@ 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)) + BookmarkDragData drag_data; + if (!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_); + std::vector<BookmarkNode*> nodes = drag_data.GetNodes(profile_); for (size_t i = 0; i < nodes.size(); ++i) { if (parent_node_->HasAncestor(nodes[i])) return false; } + + drop_info_.reset(new DropInfo(this)); + drop_info_->SetData(drag_data); return true; } @@ -75,40 +82,24 @@ void BookmarkTableView::OnDragEntered(const views::DropTargetEvent& event) { } int BookmarkTableView::OnDragUpdated(const views::DropTargetEvent& event) { - if (!parent_node_) + if (!parent_node_ || !drop_info_.get()) { + drop_info_.reset(NULL); 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; + drop_info_->Update(event); + return UpdateDropInfo(); } void BookmarkTableView::OnDragExited() { - SetDropIndex(-1, false); + SetDropPosition(DropPosition()); drop_info_.reset(); } int BookmarkTableView::OnPerformDrop(const views::DropTargetEvent& event) { OnPerformDropImpl(); - int drop_operation = drop_info_->drop_operation; - SetDropIndex(-1, false); + int drop_operation = drop_info_->drop_operation(); + SetDropPosition(DropPosition()); drop_info_.reset(); return drop_operation; } @@ -148,12 +139,12 @@ void BookmarkTableView::SetShowPathColumn(bool show_path_column) { } void BookmarkTableView::PostPaint() { - if (!drop_info_.get() || drop_info_->drop_index == -1 || - drop_info_->drop_on) { + if (!drop_info_.get() || drop_info_->position().index == -1 || + drop_info_->position().on) { return; } - RECT bounds = GetDropBetweenHighlightRect(drop_info_->drop_index); + RECT bounds = GetDropBetweenHighlightRect(drop_info_->position().index); HDC dc = GetDC(GetNativeControlHWND()); HBRUSH brush = CreateSolidBrush(GetSysColor(COLOR_WINDOWTEXT)); FillRect(dc, &bounds, brush); @@ -171,6 +162,19 @@ LRESULT BookmarkTableView::OnNotify(int w_param, LPNMHDR l_param) { return TableView::OnNotify(w_param, l_param); } +int BookmarkTableView::UpdateDropInfo() { + DropPosition position = CalculateDropPosition(drop_info_->last_y()); + + drop_info_->set_drop_operation(CalculateDropOperation(position)); + + if (drop_info_->drop_operation() == DragDropTypes::DRAG_NONE) + position = DropPosition(); + + SetDropPosition(position); + + return drop_info_->drop_operation(); +} + void BookmarkTableView::BeginDrag() { std::vector<BookmarkNode*> nodes_to_drag; for (TableView::iterator i = SelectionBegin(); i != SelectionEnd(); ++i) @@ -186,21 +190,18 @@ void BookmarkTableView::BeginDrag() { 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_)) { +int BookmarkTableView::CalculateDropOperation(const DropPosition& position) { + if (drop_info_->data().IsFromProfile(profile_)) { // Data from the same profile. Prefer move, but do copy if the user wants // that. - if (event.IsControlDown()) + if (drop_info_->is_control_down()) return DragDropTypes::DRAG_COPY; int real_drop_index; - BookmarkNode* drop_parent = GetDropParentAndIndex(drop_index, drop_on, + BookmarkNode* drop_parent = GetDropParentAndIndex(position, &real_drop_index); if (!bookmark_utils::IsValidDropLocation( - profile_, drop_info_->drag_data, drop_parent, real_drop_index)) { + profile_, drop_info_->data(), drop_parent, real_drop_index)) { return DragDropTypes::DRAG_NONE; } return DragDropTypes::DRAG_MOVE; @@ -208,28 +209,29 @@ int BookmarkTableView::CalculateDropOperation( // 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); + drop_info_->source_operations(), + 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); + drop_info_->position(), &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, + if (!drop_info_->data().IsFromProfile(profile_) || + drop_info_->drop_operation() == DragDropTypes::DRAG_COPY) { + bookmark_utils::CloneDragData(model, drop_info_->data().elements, drop_parent, drop_index); min_selection = drop_index; max_selection = drop_index + - static_cast<int>(drop_info_->drag_data.elements.size()); + static_cast<int>(drop_info_->data().elements.size()); } else { // else, move. - std::vector<BookmarkNode*> nodes = drop_info_->drag_data.GetNodes(profile_); + std::vector<BookmarkNode*> nodes = drop_info_->data().GetNodes(profile_); if (nodes.empty()) return; @@ -241,7 +243,12 @@ void BookmarkTableView::OnPerformDropImpl() { min_selection = drop_parent->IndexOfChild(nodes[0]); max_selection = min_selection + static_cast<int>(nodes.size()); } - if (min_selection < RowCount() && max_selection < RowCount()) { + if (drop_info_->position().on) { + // The user dropped on a folder, select it. + int index = parent_node_->IndexOfChild(drop_parent); + if (index != -1) + Select(index); + } else if (min_selection < RowCount() && max_selection <= RowCount()) { // Select the moved/copied rows. Select(min_selection); if (min_selection + 1 < max_selection) { @@ -254,70 +261,69 @@ void BookmarkTableView::OnPerformDropImpl() { } } -void BookmarkTableView::SetDropIndex(int index, bool drop_on) { - if (drop_info_->drop_index == index && drop_info_->drop_on == drop_on) +void BookmarkTableView::SetDropPosition(const DropPosition& position) { + if (drop_info_->position().equals(position)) return; - UpdateDropIndex(drop_info_->drop_index, drop_info_->drop_on, false); + UpdateDropIndicator(drop_info_->position(), false); - drop_info_->drop_index = index; - drop_info_->drop_on = drop_on; + drop_info_->set_position(position); - UpdateDropIndex(drop_info_->drop_index, drop_info_->drop_on, true); + UpdateDropIndicator(drop_info_->position(), true); } -void BookmarkTableView::UpdateDropIndex(int index, bool drop_on, bool turn_on) { - if (index == -1) +void BookmarkTableView::UpdateDropIndicator(const DropPosition& position, + bool turn_on) { + if (position.index == -1) return; - if (drop_on) { - ListView_SetItemState(GetNativeControlHWND(), index, + if (position.on) { + ListView_SetItemState(GetNativeControlHWND(), position.index, turn_on ? LVIS_DROPHILITED : 0, LVIS_DROPHILITED); } else { - RECT bounds = GetDropBetweenHighlightRect(index); + RECT bounds = GetDropBetweenHighlightRect(position.index); InvalidateRect(GetNativeControlHWND(), &bounds, FALSE); } } -int BookmarkTableView::CalculateDropIndex(int y, bool* drop_on) { - *drop_on = false; +BookmarkTableView::DropPosition + BookmarkTableView::CalculateDropPosition(int y) { HWND hwnd = GetNativeControlHWND(); int row_count = RowCount(); int top_index = ListView_GetTopIndex(hwnd); if (row_count == 0 || top_index < 0) - return 0; + return DropPosition(0, false); for (int i = top_index; i < row_count; ++i) { RECT bounds; ListView_GetItemRect(hwnd, i, &bounds, LVIR_BOUNDS); if (y < bounds.top) - return i; + return DropPosition(i, false); 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.top + views::kDropBetweenPixels) + return DropPosition(i, false); + if (y >= bounds.bottom - views::kDropBetweenPixels) + return DropPosition(i + 1, false); + return DropPosition(i, true); } if (y < (bounds.bottom - bounds.top) / 2 + bounds.top) - return i; - return i + 1; + return DropPosition(i, false); + return DropPosition(i + 1, false); } } - return row_count; + return DropPosition(row_count, false); } -BookmarkNode* BookmarkTableView::GetDropParentAndIndex(int visual_drop_index, - bool drop_on, - int* index) { - if (drop_on) { - BookmarkNode* parent = parent_node_->GetChild(visual_drop_index); +BookmarkNode* BookmarkTableView::GetDropParentAndIndex( + const DropPosition& position, + int* index) { + if (position.on) { + BookmarkNode* parent = parent_node_->GetChild(position.index); *index = parent->GetChildCount(); return parent; } - *index = visual_drop_index; + *index = position.index; return parent_node_; } @@ -338,6 +344,7 @@ RECT BookmarkTableView::GetDropBetweenHighlightRect(int index) { bounds.bottom = bounds.top + kDropHighlightHeight; return bounds; } + void BookmarkTableView::UpdateColumns() { PrefService* prefs = profile_->GetPrefs(); views::TableColumn name_column = diff --git a/chrome/browser/views/bookmark_table_view.h b/chrome/browser/views/bookmark_table_view.h index a01f1af..40dc81e 100644 --- a/chrome/browser/views/bookmark_table_view.h +++ b/chrome/browser/views/bookmark_table_view.h @@ -6,6 +6,7 @@ #define CHROME_BROWSER_VIEWS_BOOKMARK_TABLE_VIEW_H_ #include "chrome/browser/bookmarks/bookmark_drag_data.h" +#include "chrome/browser/bookmarks/bookmark_drop_info.h" #include "chrome/views/menu.h" #include "chrome/views/table_view.h" @@ -54,49 +55,75 @@ class BookmarkTableView : public views::TableView { 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; + // DropPosition identifies where the drop should occur. + struct DropPosition { + DropPosition() : index(-1), on(false) {} + DropPosition(int index, bool on) : index(index), on(on) {} + + bool equals(const DropPosition& position) const { + return index == position.index && on == position.on; + } + + // The index into the table model as to where the drop should occur. This + // may == the row count of the table. + int index; + + // Whether drop is on the item at the specified index. If false, the drop + // is at the specified index. + bool on; + }; - // Whether the drop is on drop_index or before it. - bool drop_on; + // Information used when we're the drop target of a drag and drop operation. + class DropInfo : public BookmarkDropInfo { + public: + explicit DropInfo(BookmarkTableView* view) + : BookmarkDropInfo(view->GetNativeControlHWND(), + view->content_offset()), + view_(view) {} + + // Overriden to invoke UpdateDropInfo. + virtual void Scrolled(); + + // The position the drop is to occur at. + void set_position(const DropPosition& position) { + position_ = position; + } + const DropPosition& position() { return position_; } + + private: + DropPosition position_; + BookmarkTableView* view_; + + DISALLOW_COPY_AND_ASSIGN(DropInfo); }; + friend class DropInfo; + + // Updates drop info. This is invoked both from OnDragUpdated and when we + // autoscroll during a drop. + int UpdateDropInfo(); // 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); + // Returns the drop operation for the specified position. + int CalculateDropOperation(const DropPosition& position); // 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); + // Sets the position of the drop. If this differs from the current position + // UpdateDropIndicator is invoked for old and new values. + void SetDropPosition(const DropPosition& position); - // 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); + // Invoked from SetDropPosition to update the visual indicator. |turn_on| + // indicates whether the indicator is to be turned on or off. + void UpdateDropIndicator(const DropPosition& position, bool turn_on); - // Determines the drop index for the specified location. - int CalculateDropIndex(int y, bool* drop_on); + // Determines the drop position for the specified location. + DropPosition CalculateDropPosition(int y); - // 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, + // Returns the BookmarkNode the drop should occur on. + BookmarkNode* GetDropParentAndIndex(const DropPosition& position, int* index); // Returns the bounds of drop indicator shown when the drop is to occur |