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/bookmarks/bookmark_pasteboard_helper_mac.h"
8 #include "chrome/browser/profiles/profile.h"
9 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_controller.h"
10 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_folder_target.h"
11 #import "chrome/browser/ui/cocoa/browser_window_controller.h"
12 #include "content/public/browser/user_metrics.h"
14 using base::UserMetricsAction;
16 #import "third_party/mozilla/NSPasteboard+Utils.h"
18 @interface BookmarkBarFolderView()
20 @property(readonly, nonatomic) id<BookmarkButtonControllerProtocol> controller;
22 - (void)setDropIndicatorShown:(BOOL)flag;
26 @implementation BookmarkBarFolderView
28 - (void)awakeFromNib {
29 NSArray* types = [NSArray arrayWithObjects:
33 kBookmarkButtonDragType,
34 kBookmarkDictionaryListPboardType,
36 [self registerForDraggedTypes:types];
40 [self unregisterDraggedTypes];
44 - (id<BookmarkButtonControllerProtocol>)controller {
45 // When needed for testing, set the local data member |controller_| to
46 // the test controller.
47 return controller_ ? controller_ : [[self window] windowController];
50 // Virtually identical to [BookmarkBarView draggingEntered:].
51 // TODO(jrg): find a way to share code. Lack of multiple inheritance
52 // makes things more of a pain but there should be no excuse for laziness.
53 // http://crbug.com/35966
54 - (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)info {
56 if (![[self controller] draggingAllowed:info])
57 return NSDragOperationNone;
58 if ([[info draggingPasteboard] dataForType:kBookmarkButtonDragType] ||
59 PasteboardContainsBookmarks(ui::CLIPBOARD_TYPE_DRAG) ||
60 [[info draggingPasteboard] containsURLData]) {
61 // Find the position of the drop indicator.
62 BOOL showIt = [[self controller]
63 shouldShowIndicatorShownForPoint:[info draggingLocation]];
65 [self setDropIndicatorShown:NO];
67 [self setDropIndicatorShown:YES];
69 CGFloat y = [[self controller]
70 indicatorPosForDragToPoint:[info draggingLocation]];
71 NSRect frame = [dropIndicator_ frame];
72 if (NSMinY(frame) != y) {
74 [dropIndicator_ setFrame:frame];
78 [[self controller] draggingEntered:info]; // allow hover-open to work
79 return [info draggingSource] ? NSDragOperationMove : NSDragOperationCopy;
81 return NSDragOperationNone;
84 - (void)draggingExited:(id<NSDraggingInfo>)info {
85 [[self controller] draggingExited:info];
87 // Regardless of the type of dragging which ended, we need to get rid of the
88 // drop indicator if one was shown.
89 [self setDropIndicatorShown:NO];
92 - (void)draggingEnded:(id<NSDraggingInfo>)info {
93 // Awkwardness since views open and close out from under us.
98 [self draggingExited:info];
101 - (BOOL)wantsPeriodicDraggingUpdates {
102 // TODO(jrg): The controller should slide the existing bookmark buttons
103 // interactively to the side to make room for the about-to-be-dropped
105 // http://crbug.com/35968
107 // The bookmark_bar_folder_hover_state expects continuous callbacks.
111 - (NSDragOperation)draggingUpdated:(id<NSDraggingInfo>)info {
112 // For now it's the same as draggingEntered:.
113 // TODO(jrg): once we return YES for wantsPeriodicDraggingUpdates,
114 // this should ping the [self controller] to perform animations.
115 // http://crbug.com/35968
116 return [self draggingEntered:info];
119 - (BOOL)prepareForDragOperation:(id<NSDraggingInfo>)info {
123 // This code is practically identical to the same function in BookmarkBarView
124 // with the only difference being how the controller is retrieved.
125 // TODO(mrossetti,jrg): http://crbug.com/35966
126 // Implement NSDraggingDestination protocol method
127 // performDragOperation: for URLs.
128 - (BOOL)performDragOperationForURL:(id<NSDraggingInfo>)info {
129 NSPasteboard* pboard = [info draggingPasteboard];
130 DCHECK([pboard containsURLData]);
133 NSArray* titles = nil;
134 [pboard getURLs:&urls andTitles:&titles convertingFilenames:YES];
136 return [[self controller] addURLs:urls
138 at:[info draggingLocation]];
141 // This code is practically identical to the same function in BookmarkBarView
142 // with the only difference being how the controller is retrieved.
143 // http://crbug.com/35966
144 // Implement NSDraggingDestination protocol method
145 // performDragOperation: for bookmark buttons.
146 - (BOOL)performDragOperationForBookmarkButton:(id<NSDraggingInfo>)info {
148 NSData* data = [[info draggingPasteboard]
149 dataForType:kBookmarkButtonDragType];
150 // [info draggingSource] is nil if not the same application.
151 if (data && [info draggingSource]) {
152 BookmarkButton* button = nil;
153 [data getBytes:&button length:sizeof(button)];
155 // If we're dragging from one profile to another, disallow moving (only
156 // allow copying). Each profile has its own bookmark model, so one way to
157 // check whether we are dragging across profiles is to see if the
158 // |BookmarkNode| corresponding to |button| exists in this profile. If it
159 // does, we're dragging within a profile; otherwise, we're dragging across
161 const BookmarkModel* const model = [[self controller] bookmarkModel];
162 const BookmarkNode* const source_node = [button bookmarkNode];
163 const BookmarkNode* const target_node =
164 model->GetNodeByID(source_node->id());
167 !([info draggingSourceOperationMask] & NSDragOperationMove) ||
168 (source_node != target_node);
169 doDrag = [[self controller] dragButton:button
170 to:[info draggingLocation]
172 content::RecordAction(UserMetricsAction("BookmarkBarFolder_DragEnd"));
177 - (BOOL)performDragOperation:(id<NSDraggingInfo>)info {
178 if ([[self controller] dragBookmarkData:info])
180 NSPasteboard* pboard = [info draggingPasteboard];
181 if ([pboard dataForType:kBookmarkButtonDragType] &&
182 [self performDragOperationForBookmarkButton:info])
184 if ([pboard containsURLData] && [self performDragOperationForURL:info])
189 - (void)setDropIndicatorShown:(BOOL)flag {
190 if (dropIndicatorShown_ == flag)
193 dropIndicatorShown_ = flag;
194 if (dropIndicatorShown_) {
195 NSRect frame = NSInsetRect([self bounds], 4, 0);
196 frame.size.height = 1;
197 dropIndicator_.reset([[NSBox alloc] initWithFrame:frame]);
198 [dropIndicator_ setBoxType:NSBoxSeparator];
199 [dropIndicator_ setBorderType:NSLineBorder];
200 [dropIndicator_ setAlphaValue:0.85];
201 [self addSubview:dropIndicator_];
203 [dropIndicator_ removeFromSuperview];
204 dropIndicator_.reset();