1 // Copyright (c) 2011 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.
5 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_folder_view.h"
7 #include "chrome/browser/profiles/profile.h"
8 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_controller.h"
9 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_folder_target.h"
10 #import "chrome/browser/ui/cocoa/browser_window_controller.h"
11 #include "components/bookmarks/browser/bookmark_pasteboard_helper_mac.h"
12 #include "components/bookmarks/browser/bookmark_utils.h"
13 #include "content/public/browser/user_metrics.h"
15 using base::UserMetricsAction;
17 #import "third_party/mozilla/NSPasteboard+Utils.h"
19 @interface BookmarkBarFolderView()
21 @property(readonly, nonatomic) id<BookmarkButtonControllerProtocol> controller;
23 - (void)setDropIndicatorShown:(BOOL)flag;
27 @implementation BookmarkBarFolderView
29 - (void)awakeFromNib {
30 NSArray* types = [NSArray arrayWithObjects:
34 kBookmarkButtonDragType,
35 kBookmarkDictionaryListPboardType,
37 [self registerForDraggedTypes:types];
41 [self unregisterDraggedTypes];
45 - (id<BookmarkButtonControllerProtocol>)controller {
46 // When needed for testing, set the local data member |controller_| to
47 // the test controller.
48 return controller_ ? controller_ : [[self window] windowController];
51 // Virtually identical to [BookmarkBarView draggingEntered:].
52 // TODO(jrg): find a way to share code. Lack of multiple inheritance
53 // makes things more of a pain but there should be no excuse for laziness.
54 // http://crbug.com/35966
55 - (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)info {
57 if (![[self controller] draggingAllowed:info])
58 return NSDragOperationNone;
59 if ([[info draggingPasteboard] dataForType:kBookmarkButtonDragType] ||
60 PasteboardContainsBookmarks(ui::CLIPBOARD_TYPE_DRAG) ||
61 [[info draggingPasteboard] containsURLData]) {
62 // Find the position of the drop indicator.
63 BOOL showIt = [[self controller]
64 shouldShowIndicatorShownForPoint:[info draggingLocation]];
66 [self setDropIndicatorShown:NO];
68 [self setDropIndicatorShown:YES];
70 CGFloat y = [[self controller]
71 indicatorPosForDragToPoint:[info draggingLocation]];
72 NSRect frame = [dropIndicator_ frame];
73 if (NSMinY(frame) != y) {
75 [dropIndicator_ setFrame:frame];
79 [[self controller] draggingEntered:info]; // allow hover-open to work
80 return [info draggingSource] ? NSDragOperationMove : NSDragOperationCopy;
82 return NSDragOperationNone;
85 - (void)draggingExited:(id<NSDraggingInfo>)info {
86 [[self controller] draggingExited:info];
88 // Regardless of the type of dragging which ended, we need to get rid of the
89 // drop indicator if one was shown.
90 [self setDropIndicatorShown:NO];
93 - (void)draggingEnded:(id<NSDraggingInfo>)info {
94 // Awkwardness since views open and close out from under us.
99 [self draggingExited:info];
102 - (BOOL)wantsPeriodicDraggingUpdates {
103 // TODO(jrg): The controller should slide the existing bookmark buttons
104 // interactively to the side to make room for the about-to-be-dropped
106 // http://crbug.com/35968
108 // The bookmark_bar_folder_hover_state expects continuous callbacks.
112 - (NSDragOperation)draggingUpdated:(id<NSDraggingInfo>)info {
113 // For now it's the same as draggingEntered:.
114 // TODO(jrg): once we return YES for wantsPeriodicDraggingUpdates,
115 // this should ping the [self controller] to perform animations.
116 // http://crbug.com/35968
117 return [self draggingEntered:info];
120 - (BOOL)prepareForDragOperation:(id<NSDraggingInfo>)info {
124 // This code is practically identical to the same function in BookmarkBarView
125 // with the only difference being how the controller is retrieved.
126 // TODO(mrossetti,jrg): http://crbug.com/35966
127 // Implement NSDraggingDestination protocol method
128 // performDragOperation: for URLs.
129 - (BOOL)performDragOperationForURL:(id<NSDraggingInfo>)info {
130 NSPasteboard* pboard = [info draggingPasteboard];
131 DCHECK([pboard containsURLData]);
134 NSArray* titles = nil;
135 [pboard getURLs:&urls andTitles:&titles convertingFilenames:YES];
137 return [[self controller] addURLs:urls
139 at:[info draggingLocation]];
142 // This code is practically identical to the same function in BookmarkBarView
143 // with the only difference being how the controller is retrieved.
144 // http://crbug.com/35966
145 // Implement NSDraggingDestination protocol method
146 // performDragOperation: for bookmark buttons.
147 - (BOOL)performDragOperationForBookmarkButton:(id<NSDraggingInfo>)info {
149 NSData* data = [[info draggingPasteboard]
150 dataForType:kBookmarkButtonDragType];
151 // [info draggingSource] is nil if not the same application.
152 if (data && [info draggingSource]) {
153 BookmarkButton* button = nil;
154 [data getBytes:&button length:sizeof(button)];
156 // If we're dragging from one profile to another, disallow moving (only
157 // allow copying). Each profile has its own bookmark model, so one way to
158 // check whether we are dragging across profiles is to see if the
159 // |BookmarkNode| corresponding to |button| exists in this profile. If it
160 // does, we're dragging within a profile; otherwise, we're dragging across
162 const BookmarkModel* const model = [[self controller] bookmarkModel];
163 const BookmarkNode* const source_node = [button bookmarkNode];
164 const BookmarkNode* const target_node =
165 GetBookmarkNodeByID(model, source_node->id());
168 !([info draggingSourceOperationMask] & NSDragOperationMove) ||
169 (source_node != target_node);
170 doDrag = [[self controller] dragButton:button
171 to:[info draggingLocation]
173 content::RecordAction(UserMetricsAction("BookmarkBarFolder_DragEnd"));
178 - (BOOL)performDragOperation:(id<NSDraggingInfo>)info {
179 if ([[self controller] dragBookmarkData:info])
181 NSPasteboard* pboard = [info draggingPasteboard];
182 if ([pboard dataForType:kBookmarkButtonDragType] &&
183 [self performDragOperationForBookmarkButton:info])
185 if ([pboard containsURLData] && [self performDragOperationForURL:info])
190 - (void)setDropIndicatorShown:(BOOL)flag {
191 if (dropIndicatorShown_ == flag)
194 dropIndicatorShown_ = flag;
195 if (dropIndicatorShown_) {
196 NSRect frame = NSInsetRect([self bounds], 4, 0);
197 frame.size.height = 1;
198 dropIndicator_.reset([[NSBox alloc] initWithFrame:frame]);
199 [dropIndicator_ setBoxType:NSBoxSeparator];
200 [dropIndicator_ setBorderType:NSLineBorder];
201 [dropIndicator_ setAlphaValue:0.85];
202 [self addSubview:dropIndicator_];
204 [dropIndicator_ removeFromSuperview];
205 dropIndicator_.reset();