bookmark_drag_drop_views.cc revision 5f1c94371a64b3196d4be9466099bb892df9b88e
1// Copyright 2012 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "chrome/browser/ui/views/bookmarks/bookmark_drag_drop_views.h"
6
7#include "base/message_loop/message_loop.h"
8#include "base/prefs/pref_service.h"
9#include "chrome/browser/bookmarks/bookmark_model_factory.h"
10#include "chrome/browser/profiles/profile.h"
11#include "chrome/browser/ui/bookmarks/bookmark_drag_drop.h"
12#include "chrome/common/pref_names.h"
13#include "components/bookmarks/browser/bookmark_model.h"
14#include "components/bookmarks/browser/bookmark_node_data.h"
15#include "components/bookmarks/browser/bookmark_utils.h"
16#include "components/user_prefs/user_prefs.h"
17#include "ui/base/dragdrop/drag_drop_types.h"
18#include "ui/base/dragdrop/os_exchange_data.h"
19#include "ui/events/event.h"
20#include "ui/views/drag_utils.h"
21#include "ui/views/widget/widget.h"
22
23using bookmarks::BookmarkNodeData;
24
25namespace chrome {
26
27void DragBookmarks(Profile* profile,
28                   const std::vector<const BookmarkNode*>& nodes,
29                   gfx::NativeView view,
30                   ui::DragDropTypes::DragEventSource source) {
31  DCHECK(!nodes.empty());
32
33  // Set up our OLE machinery.
34  ui::OSExchangeData data;
35  BookmarkNodeData drag_data(nodes);
36  drag_data.Write(profile->GetPath(), &data);
37
38  // Allow nested message loop so we get DnD events as we drag this around.
39  bool was_nested = base::MessageLoop::current()->IsNested();
40  base::MessageLoop::current()->SetNestableTasksAllowed(true);
41
42  int operation = ui::DragDropTypes::DRAG_COPY | ui::DragDropTypes::DRAG_LINK;
43  BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile);
44  if (bookmarks::CanAllBeEditedByUser(model->client(), nodes))
45    operation |= ui::DragDropTypes::DRAG_MOVE;
46
47  views::Widget* widget = views::Widget::GetWidgetForNativeView(view);
48
49  if (widget) {
50    widget->RunShellDrag(NULL, data, gfx::Point(), operation, source);
51  } else {
52    // We hit this case when we're using WebContentsViewWin or
53    // WebContentsViewAura, instead of WebContentsViewViews.
54    views::RunShellDrag(view, data, gfx::Point(), operation, source);
55  }
56
57  base::MessageLoop::current()->SetNestableTasksAllowed(was_nested);
58}
59
60int GetBookmarkDragOperation(content::BrowserContext* browser_context,
61                             const BookmarkNode* node) {
62  PrefService* prefs = user_prefs::UserPrefs::Get(browser_context);
63  Profile* profile = Profile::FromBrowserContext(browser_context);
64  BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile);
65
66  int move = ui::DragDropTypes::DRAG_MOVE;
67  if (!prefs->GetBoolean(prefs::kEditBookmarksEnabled) ||
68      !model->client()->CanBeEditedByUser(node)) {
69    move = ui::DragDropTypes::DRAG_NONE;
70  }
71  if (node->is_url())
72    return ui::DragDropTypes::DRAG_COPY | ui::DragDropTypes::DRAG_LINK | move;
73  return ui::DragDropTypes::DRAG_COPY | move;
74}
75
76int GetPreferredBookmarkDropOperation(int source_operations, int operations) {
77  int common_ops = (source_operations & operations);
78  if (!common_ops)
79    return ui::DragDropTypes::DRAG_NONE;
80  if (ui::DragDropTypes::DRAG_COPY & common_ops)
81    return ui::DragDropTypes::DRAG_COPY;
82  if (ui::DragDropTypes::DRAG_LINK & common_ops)
83    return ui::DragDropTypes::DRAG_LINK;
84  if (ui::DragDropTypes::DRAG_MOVE & common_ops)
85    return ui::DragDropTypes::DRAG_MOVE;
86  return ui::DragDropTypes::DRAG_NONE;
87}
88
89int GetBookmarkDropOperation(Profile* profile,
90                             const ui::DropTargetEvent& event,
91                             const BookmarkNodeData& data,
92                             const BookmarkNode* parent,
93                             int index) {
94  const base::FilePath& profile_path = profile->GetPath();
95
96  if (data.IsFromProfilePath(profile_path) && data.size() > 1)
97    // Currently only accept one dragged node at a time.
98    return ui::DragDropTypes::DRAG_NONE;
99
100  if (!IsValidBookmarkDropLocation(profile, data, parent, index))
101    return ui::DragDropTypes::DRAG_NONE;
102
103  BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile);
104  if (!model->client()->CanBeEditedByUser(parent))
105    return ui::DragDropTypes::DRAG_NONE;
106
107  const BookmarkNode* dragged_node =
108      data.GetFirstNode(model, profile->GetPath());
109  if (dragged_node) {
110    // User is dragging from this profile.
111    if (!model->client()->CanBeEditedByUser(dragged_node)) {
112      // Do a copy instead of a move when dragging bookmarks that the user can't
113      // modify.
114      return ui::DragDropTypes::DRAG_COPY;
115    }
116    return ui::DragDropTypes::DRAG_MOVE;
117  }
118
119  // User is dragging from another app, copy.
120  return GetPreferredBookmarkDropOperation(event.source_operations(),
121      ui::DragDropTypes::DRAG_COPY | ui::DragDropTypes::DRAG_LINK);
122}
123
124bool IsValidBookmarkDropLocation(Profile* profile,
125                                 const BookmarkNodeData& data,
126                                 const BookmarkNode* drop_parent,
127                                 int index) {
128  if (!drop_parent->is_folder()) {
129    NOTREACHED();
130    return false;
131  }
132
133  if (!data.is_valid())
134    return false;
135
136  BookmarkModel* model = BookmarkModelFactory::GetForProfile(profile);
137  if (!model->client()->CanBeEditedByUser(drop_parent))
138    return false;
139
140  const base::FilePath& profile_path = profile->GetPath();
141  if (data.IsFromProfilePath(profile_path)) {
142    std::vector<const BookmarkNode*> nodes = data.GetNodes(model, profile_path);
143    for (size_t i = 0; i < nodes.size(); ++i) {
144      // Don't allow the drop if the user is attempting to drop on one of the
145      // nodes being dragged.
146      const BookmarkNode* node = nodes[i];
147      int node_index = (drop_parent == node->parent()) ?
148          drop_parent->GetIndexOf(nodes[i]) : -1;
149      if (node_index != -1 && (index == node_index || index == node_index + 1))
150        return false;
151
152      // drop_parent can't accept a child that is an ancestor.
153      if (drop_parent->HasAncestor(node))
154        return false;
155    }
156    return true;
157  }
158  // From another profile, always accept.
159  return true;
160}
161
162}  // namespace chrome
163