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_view.h" 6 7 #include "chrome/browser/bookmarks/bookmark_pasteboard_helper_mac.h" 8 #include "chrome/browser/metrics/user_metrics.h" 9 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_controller.h" 10 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_button.h" 11 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_folder_target.h" 12 #import "chrome/browser/ui/cocoa/themed_window.h" 13 #import "chrome/browser/ui/cocoa/view_id_util.h" 14 #import "chrome/browser/themes/theme_service.h" 15 #import "third_party/mozilla/NSPasteboard+Utils.h" 16 17 @interface BookmarkBarView (Private) 18 - (void)themeDidChangeNotification:(NSNotification*)aNotification; 19 - (void)updateTheme:(ui::ThemeProvider*)themeProvider; 20 @end 21 22 @implementation BookmarkBarView 23 24 @synthesize dropIndicatorShown = dropIndicatorShown_; 25 @synthesize dropIndicatorPosition = dropIndicatorPosition_; 26 @synthesize noItemContainer = noItemContainer_; 27 28 29 - (void)dealloc { 30 [[NSNotificationCenter defaultCenter] removeObserver:self]; 31 // This probably isn't strictly necessary, but can't hurt. 32 [self unregisterDraggedTypes]; 33 [super dealloc]; 34 35 // To be clear, our controller_ is an IBOutlet and owns us, so we 36 // don't deallocate it explicitly. It is owned by the browser 37 // window controller, so gets deleted with a browser window is 38 // closed. 39 } 40 41 - (void)awakeFromNib { 42 NSNotificationCenter* defaultCenter = [NSNotificationCenter defaultCenter]; 43 [defaultCenter addObserver:self 44 selector:@selector(themeDidChangeNotification:) 45 name:kBrowserThemeDidChangeNotification 46 object:nil]; 47 48 DCHECK(controller_) << "Expected this to be hooked up via Interface Builder"; 49 NSArray* types = [NSArray arrayWithObjects: 50 NSStringPboardType, 51 NSHTMLPboardType, 52 NSURLPboardType, 53 kBookmarkButtonDragType, 54 kBookmarkDictionaryListPboardType, 55 nil]; 56 [self registerForDraggedTypes:types]; 57 } 58 59 // We need the theme to color the bookmark buttons properly. But our 60 // controller desn't have access to it until it's placed in the view 61 // hierarchy. This is the spot where we close the loop. 62 - (void)viewWillMoveToWindow:(NSWindow*)window { 63 ui::ThemeProvider* themeProvider = [window themeProvider]; 64 [self updateTheme:themeProvider]; 65 [controller_ updateTheme:themeProvider]; 66 } 67 68 - (void)viewDidMoveToWindow { 69 [controller_ viewDidMoveToWindow]; 70 } 71 72 // Called after the current theme has changed. 73 - (void)themeDidChangeNotification:(NSNotification*)aNotification { 74 ui::ThemeProvider* themeProvider = 75 static_cast<ThemeService*>([[aNotification object] pointerValue]); 76 [self updateTheme:themeProvider]; 77 } 78 79 // Adapt appearance to the current theme. Called after theme changes and before 80 // this is shown for the first time. 81 - (void)updateTheme:(ui::ThemeProvider*)themeProvider { 82 if (!themeProvider) 83 return; 84 85 NSColor* color = 86 themeProvider->GetNSColor(ThemeService::COLOR_BOOKMARK_TEXT, 87 true); 88 [noItemTextfield_ setTextColor:color]; 89 } 90 91 // Mouse down events on the bookmark bar should not allow dragging the parent 92 // window around. 93 - (BOOL)mouseDownCanMoveWindow { 94 return NO; 95 } 96 97 -(NSTextField*)noItemTextfield { 98 return noItemTextfield_; 99 } 100 101 -(NSButton*)importBookmarksButton { 102 return importBookmarksButton_; 103 } 104 105 - (BookmarkBarController*)controller { 106 return controller_; 107 } 108 109 // Internal method, needs to be called whenever a change has been made to 110 // dropIndicatorShown_ or dropIndicatorPosition_ so it can get the controller 111 // to reflect the change by moving buttons around. 112 -(void)dropIndicatorChanged { 113 if (dropIndicatorShown_) 114 [controller_ setDropInsertionPos:dropIndicatorPosition_]; 115 else 116 [controller_ clearDropInsertionPos]; 117 } 118 119 -(void)drawRect:(NSRect)dirtyRect { 120 [super drawRect:dirtyRect]; 121 } 122 123 // Shim function to assist in unit testing. 124 - (BOOL)dragClipboardContainsBookmarks { 125 return bookmark_pasteboard_helper_mac::DragClipboardContainsBookmarks(); 126 } 127 128 // NSDraggingDestination methods 129 130 - (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)info { 131 if (![controller_ draggingAllowed:info]) 132 return NSDragOperationNone; 133 if ([[info draggingPasteboard] dataForType:kBookmarkButtonDragType] || 134 [self dragClipboardContainsBookmarks] || 135 [[info draggingPasteboard] containsURLData]) { 136 // We only show the drop indicator if we're not in a position to 137 // perform a hover-open since it doesn't make sense to do both. 138 BOOL showIt = [controller_ shouldShowIndicatorShownForPoint: 139 [info draggingLocation]]; 140 if (!showIt) { 141 if (dropIndicatorShown_) { 142 dropIndicatorShown_ = NO; 143 [self dropIndicatorChanged]; 144 } 145 } else { 146 CGFloat x = 147 [controller_ indicatorPosForDragToPoint:[info draggingLocation]]; 148 // Need an update if the indicator wasn't previously shown or if it has 149 // moved. 150 if (!dropIndicatorShown_ || dropIndicatorPosition_ != x) { 151 dropIndicatorShown_ = YES; 152 dropIndicatorPosition_ = x; 153 [self dropIndicatorChanged]; 154 } 155 } 156 157 [controller_ draggingEntered:info]; // allow hover-open to work. 158 return [info draggingSource] ? NSDragOperationMove : NSDragOperationCopy; 159 } 160 return NSDragOperationNone; 161 } 162 163 - (void)draggingExited:(id<NSDraggingInfo>)info { 164 // Regardless of the type of dragging which ended, we need to get rid of the 165 // drop indicator if one was shown. 166 if (dropIndicatorShown_) { 167 dropIndicatorShown_ = NO; 168 [self dropIndicatorChanged]; 169 } 170 } 171 172 - (void)draggingEnded:(id<NSDraggingInfo>)info { 173 [[BookmarkButton draggedButton] setHidden:NO]; 174 if (dropIndicatorShown_) { 175 dropIndicatorShown_ = NO; 176 [self dropIndicatorChanged]; 177 } 178 [controller_ draggingEnded:info]; 179 } 180 181 - (BOOL)wantsPeriodicDraggingUpdates { 182 return YES; 183 } 184 185 - (NSDragOperation)draggingUpdated:(id<NSDraggingInfo>)info { 186 // For now it's the same as draggingEntered:. 187 return [self draggingEntered:info]; 188 } 189 190 - (BOOL)prepareForDragOperation:(id<NSDraggingInfo>)info { 191 return YES; 192 } 193 194 // Implement NSDraggingDestination protocol method 195 // performDragOperation: for URLs. 196 - (BOOL)performDragOperationForURL:(id<NSDraggingInfo>)info { 197 NSPasteboard* pboard = [info draggingPasteboard]; 198 DCHECK([pboard containsURLData]); 199 200 NSArray* urls = nil; 201 NSArray* titles = nil; 202 [pboard getURLs:&urls andTitles:&titles convertingFilenames:YES]; 203 204 return [controller_ addURLs:urls 205 withTitles:titles 206 at:[info draggingLocation]]; 207 } 208 209 // Implement NSDraggingDestination protocol method 210 // performDragOperation: for bookmark buttons. 211 - (BOOL)performDragOperationForBookmarkButton:(id<NSDraggingInfo>)info { 212 BOOL rtn = NO; 213 NSData* data = [[info draggingPasteboard] 214 dataForType:kBookmarkButtonDragType]; 215 // [info draggingSource] is nil if not the same application. 216 if (data && [info draggingSource]) { 217 BookmarkButton* button = nil; 218 [data getBytes:&button length:sizeof(button)]; 219 BOOL copy = !([info draggingSourceOperationMask] & NSDragOperationMove); 220 rtn = [controller_ dragButton:button 221 to:[info draggingLocation] 222 copy:copy]; 223 UserMetrics::RecordAction(UserMetricsAction("BookmarkBar_DragEnd")); 224 } 225 return rtn; 226 } 227 228 - (BOOL)performDragOperation:(id<NSDraggingInfo>)info { 229 if ([controller_ dragBookmarkData:info]) 230 return YES; 231 NSPasteboard* pboard = [info draggingPasteboard]; 232 if ([pboard dataForType:kBookmarkButtonDragType]) { 233 if ([self performDragOperationForBookmarkButton:info]) 234 return YES; 235 // Fall through.... 236 } 237 if ([pboard containsURLData]) { 238 if ([self performDragOperationForURL:info]) 239 return YES; 240 } 241 return NO; 242 } 243 244 - (void)setController:(id)controller { 245 controller_ = controller; 246 } 247 248 - (ViewID)viewID { 249 return VIEW_ID_BOOKMARK_BAR; 250 } 251 252 @end // @implementation BookmarkBarView 253