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. 4 5#import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_folder_view.h" 6 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" 13 14using content::UserMetricsAction; 15 16#import "third_party/mozilla/NSPasteboard+Utils.h" 17 18@interface BookmarkBarFolderView() 19 20@property(readonly, nonatomic) id<BookmarkButtonControllerProtocol> controller; 21 22@end 23 24@implementation BookmarkBarFolderView 25 26- (void)awakeFromNib { 27 NSArray* types = [NSArray arrayWithObjects: 28 NSStringPboardType, 29 NSHTMLPboardType, 30 NSURLPboardType, 31 kBookmarkButtonDragType, 32 kBookmarkDictionaryListPboardType, 33 nil]; 34 [self registerForDraggedTypes:types]; 35} 36 37- (void)dealloc { 38 [self unregisterDraggedTypes]; 39 [super dealloc]; 40} 41 42- (id<BookmarkButtonControllerProtocol>)controller { 43 // When needed for testing, set the local data member |controller_| to 44 // the test controller. 45 return controller_ ? controller_ : [[self window] windowController]; 46} 47 48- (void)drawRect:(NSRect)rect { 49 // TODO(jrg): copied from bookmark_bar_view but orientation changed. 50 // Code dup sucks but I'm not sure I can take 16 lines and make it 51 // generic for horiz vs vertical while keeping things simple. 52 // TODO(jrg): when throwing it all away and using animations, try 53 // hard to make a common routine for both. 54 // http://crbug.com/35966, http://crbug.com/35968 55 56 // Draw the bookmark-button-dragging drop indicator if necessary. 57 if (dropIndicatorShown_) { 58 const CGFloat kBarHeight = 1; 59 const CGFloat kBarHorizPad = 4; 60 const CGFloat kBarOpacity = 0.85; 61 62 NSRect uglyBlackBar = 63 NSMakeRect(kBarHorizPad, dropIndicatorPosition_, 64 NSWidth([self bounds]) - 2*kBarHorizPad, 65 kBarHeight); 66 NSColor* uglyBlackBarColor = [NSColor blackColor]; 67 [[uglyBlackBarColor colorWithAlphaComponent:kBarOpacity] setFill]; 68 [[NSBezierPath bezierPathWithRect:uglyBlackBar] fill]; 69 } 70} 71 72// TODO(mrossetti,jrg): Identical to -[BookmarkBarView 73// dragClipboardContainsBookmarks]. http://crbug.com/35966 74// Shim function to assist in unit testing. 75- (BOOL)dragClipboardContainsBookmarks { 76 return bookmark_pasteboard_helper_mac::PasteboardContainsBookmarks( 77 bookmark_pasteboard_helper_mac::kDragPasteboard); 78} 79 80// Virtually identical to [BookmarkBarView draggingEntered:]. 81// TODO(jrg): find a way to share code. Lack of multiple inheritance 82// makes things more of a pain but there should be no excuse for laziness. 83// http://crbug.com/35966 84- (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)info { 85 inDrag_ = YES; 86 if (![[self controller] draggingAllowed:info]) 87 return NSDragOperationNone; 88 if ([[info draggingPasteboard] dataForType:kBookmarkButtonDragType] || 89 [self dragClipboardContainsBookmarks] || 90 [[info draggingPasteboard] containsURLData]) { 91 // Find the position of the drop indicator. 92 BOOL showIt = [[self controller] 93 shouldShowIndicatorShownForPoint:[info draggingLocation]]; 94 if (!showIt) { 95 if (dropIndicatorShown_) { 96 dropIndicatorShown_ = NO; 97 [self setNeedsDisplay:YES]; 98 } 99 } else { 100 CGFloat y = 101 [[self controller] 102 indicatorPosForDragToPoint:[info draggingLocation]]; 103 104 // Need an update if the indicator wasn't previously shown or if it has 105 // moved. 106 if (!dropIndicatorShown_ || dropIndicatorPosition_ != y) { 107 dropIndicatorShown_ = YES; 108 dropIndicatorPosition_ = y; 109 [self setNeedsDisplay:YES]; 110 } 111 } 112 113 [[self controller] draggingEntered:info]; // allow hover-open to work 114 return [info draggingSource] ? NSDragOperationMove : NSDragOperationCopy; 115 } 116 return NSDragOperationNone; 117} 118 119- (void)draggingExited:(id<NSDraggingInfo>)info { 120 [[self controller] draggingExited:info]; 121 122 // Regardless of the type of dragging which ended, we need to get rid of the 123 // drop indicator if one was shown. 124 if (dropIndicatorShown_) { 125 dropIndicatorShown_ = NO; 126 [self setNeedsDisplay:YES]; 127 } 128} 129 130- (void)draggingEnded:(id<NSDraggingInfo>)info { 131 // Awkwardness since views open and close out from under us. 132 if (inDrag_) { 133 inDrag_ = NO; 134 } 135 136 [self draggingExited:info]; 137} 138 139- (BOOL)wantsPeriodicDraggingUpdates { 140 // TODO(jrg): This should probably return |YES| and the controller should 141 // slide the existing bookmark buttons interactively to the side to make 142 // room for the about-to-be-dropped bookmark. 143 // http://crbug.com/35968 144 return NO; 145} 146 147- (NSDragOperation)draggingUpdated:(id<NSDraggingInfo>)info { 148 // For now it's the same as draggingEntered:. 149 // TODO(jrg): once we return YES for wantsPeriodicDraggingUpdates, 150 // this should ping the [self controller] to perform animations. 151 // http://crbug.com/35968 152 return [self draggingEntered:info]; 153} 154 155- (BOOL)prepareForDragOperation:(id<NSDraggingInfo>)info { 156 return YES; 157} 158 159// This code is practically identical to the same function in BookmarkBarView 160// with the only difference being how the controller is retrieved. 161// TODO(mrossetti,jrg): http://crbug.com/35966 162// Implement NSDraggingDestination protocol method 163// performDragOperation: for URLs. 164- (BOOL)performDragOperationForURL:(id<NSDraggingInfo>)info { 165 NSPasteboard* pboard = [info draggingPasteboard]; 166 DCHECK([pboard containsURLData]); 167 168 NSArray* urls = nil; 169 NSArray* titles = nil; 170 [pboard getURLs:&urls andTitles:&titles convertingFilenames:YES]; 171 172 return [[self controller] addURLs:urls 173 withTitles:titles 174 at:[info draggingLocation]]; 175} 176 177// This code is practically identical to the same function in BookmarkBarView 178// with the only difference being how the controller is retrieved. 179// http://crbug.com/35966 180// Implement NSDraggingDestination protocol method 181// performDragOperation: for bookmark buttons. 182- (BOOL)performDragOperationForBookmarkButton:(id<NSDraggingInfo>)info { 183 BOOL doDrag = NO; 184 NSData* data = [[info draggingPasteboard] 185 dataForType:kBookmarkButtonDragType]; 186 // [info draggingSource] is nil if not the same application. 187 if (data && [info draggingSource]) { 188 BookmarkButton* button = nil; 189 [data getBytes:&button length:sizeof(button)]; 190 191 // If we're dragging from one profile to another, disallow moving (only 192 // allow copying). Each profile has its own bookmark model, so one way to 193 // check whether we are dragging across profiles is to see if the 194 // |BookmarkNode| corresponding to |button| exists in this profile. If it 195 // does, we're dragging within a profile; otherwise, we're dragging across 196 // profiles. 197 const BookmarkModel* const model = [[self controller] bookmarkModel]; 198 const BookmarkNode* const source_node = [button bookmarkNode]; 199 const BookmarkNode* const target_node = 200 model->GetNodeByID(source_node->id()); 201 202 BOOL copy = 203 !([info draggingSourceOperationMask] & NSDragOperationMove) || 204 (source_node != target_node); 205 doDrag = [[self controller] dragButton:button 206 to:[info draggingLocation] 207 copy:copy]; 208 content::RecordAction(UserMetricsAction("BookmarkBarFolder_DragEnd")); 209 } 210 return doDrag; 211} 212 213- (BOOL)performDragOperation:(id<NSDraggingInfo>)info { 214 if ([[self controller] dragBookmarkData:info]) 215 return YES; 216 NSPasteboard* pboard = [info draggingPasteboard]; 217 if ([pboard dataForType:kBookmarkButtonDragType] && 218 [self performDragOperationForBookmarkButton:info]) 219 return YES; 220 if ([pboard containsURLData] && [self performDragOperationForURL:info]) 221 return YES; 222 return NO; 223} 224 225@end 226