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