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_view.h"
7 #include "chrome/browser/profiles/profile.h"
8 #import "chrome/browser/themes/theme_properties.h"
9 #import "chrome/browser/themes/theme_service.h"
10 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_controller.h"
11 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_button.h"
12 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_context_menu_cocoa_controller.h"
13 #import "chrome/browser/ui/cocoa/bookmarks/bookmark_folder_target.h"
14 #import "chrome/browser/ui/cocoa/browser_window_controller.h"
15 #import "chrome/browser/ui/cocoa/themed_window.h"
16 #import "chrome/browser/ui/cocoa/view_id_util.h"
17 #include "components/bookmarks/browser/bookmark_pasteboard_helper_mac.h"
18 #include "components/bookmarks/browser/bookmark_utils.h"
19 #include "content/public/browser/user_metrics.h"
20 #import "third_party/mozilla/NSPasteboard+Utils.h"
22 using base::UserMetricsAction;
24 @interface BookmarkBarView (Private)
25 - (void)themeDidChangeNotification:(NSNotification*)aNotification;
26 - (void)updateTheme:(ui::ThemeProvider*)themeProvider;
29 @implementation BookmarkBarView
31 @synthesize dropIndicatorShown = dropIndicatorShown_;
32 @synthesize dropIndicatorPosition = dropIndicatorPosition_;
33 @synthesize noItemContainer = noItemContainer_;
37 [[NSNotificationCenter defaultCenter] removeObserver:self];
38 // This probably isn't strictly necessary, but can't hurt.
39 [self unregisterDraggedTypes];
42 // To be clear, our controller_ is an IBOutlet and owns us, so we
43 // don't deallocate it explicitly. It is owned by the browser
44 // window controller, so gets deleted with a browser window is
48 - (void)awakeFromNib {
49 NSNotificationCenter* defaultCenter = [NSNotificationCenter defaultCenter];
50 [defaultCenter addObserver:self
51 selector:@selector(themeDidChangeNotification:)
52 name:kBrowserThemeDidChangeNotification
55 DCHECK(controller_) << "Expected this to be hooked up via Interface Builder";
56 NSArray* types = [NSArray arrayWithObjects:
60 kBookmarkButtonDragType,
61 kBookmarkDictionaryListPboardType,
63 [self registerForDraggedTypes:types];
66 // We need the theme to color the bookmark buttons properly. But our
67 // controller desn't have access to it until it's placed in the view
68 // hierarchy. This is the spot where we close the loop.
69 - (void)viewWillMoveToWindow:(NSWindow*)window {
70 ui::ThemeProvider* themeProvider = [window themeProvider];
71 [self updateTheme:themeProvider];
72 [controller_ updateTheme:themeProvider];
73 [super viewWillMoveToWindow:window];
76 - (void)viewDidMoveToWindow {
77 [controller_ viewDidMoveToWindow];
80 // Called after a theme change took place, possibly for a different profile.
81 - (void)themeDidChangeNotification:(NSNotification*)notification {
82 [self updateTheme:[[self window] themeProvider]];
85 // Adapt appearance to the current theme. Called after theme changes and before
86 // this is shown for the first time.
87 - (void)updateTheme:(ui::ThemeProvider*)themeProvider {
92 themeProvider->GetNSColor(ThemeProperties::COLOR_BOOKMARK_TEXT);
93 [noItemTextfield_ setTextColor:color];
96 // Mouse down events on the bookmark bar should not allow dragging the parent
98 - (BOOL)mouseDownCanMoveWindow {
102 - (BookmarkBarTextField*)noItemTextfield {
103 return noItemTextfield_;
106 - (NSButton*)importBookmarksButton {
107 return importBookmarksButton_;
110 - (BookmarkBarController*)controller {
114 // Internal method, needs to be called whenever a change has been made to
115 // dropIndicatorShown_ or dropIndicatorPosition_ so it can get the controller
116 // to reflect the change by moving buttons around.
117 - (void)dropIndicatorChanged {
118 if (dropIndicatorShown_)
119 [controller_ setDropInsertionPos:dropIndicatorPosition_];
121 [controller_ clearDropInsertionPos];
124 // NSDraggingDestination methods
126 - (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)info {
127 if (![controller_ draggingAllowed:info])
128 return NSDragOperationNone;
129 if ([[info draggingPasteboard] dataForType:kBookmarkButtonDragType] ||
130 PasteboardContainsBookmarks(ui::CLIPBOARD_TYPE_DRAG) ||
131 [[info draggingPasteboard] containsURLData]) {
132 // We only show the drop indicator if we're not in a position to
133 // perform a hover-open since it doesn't make sense to do both.
134 BOOL showIt = [controller_ shouldShowIndicatorShownForPoint:
135 [info draggingLocation]];
137 if (dropIndicatorShown_) {
138 dropIndicatorShown_ = NO;
139 [self dropIndicatorChanged];
143 [controller_ indicatorPosForDragToPoint:[info draggingLocation]];
144 // Need an update if the indicator wasn't previously shown or if it has
146 if (!dropIndicatorShown_ || dropIndicatorPosition_ != x) {
147 dropIndicatorShown_ = YES;
148 dropIndicatorPosition_ = x;
149 [self dropIndicatorChanged];
153 [controller_ draggingEntered:info]; // allow hover-open to work.
154 return [[info draggingSource] isKindOfClass: [BookmarkButton class]] ?
155 NSDragOperationMove : NSDragOperationCopy;
157 return NSDragOperationNone;
160 - (void)draggingExited:(id<NSDraggingInfo>)info {
161 [controller_ draggingExited:info];
163 // Regardless of the type of dragging which ended, we need to get rid of the
164 // drop indicator if one was shown.
165 if (dropIndicatorShown_) {
166 dropIndicatorShown_ = NO;
167 [self dropIndicatorChanged];
171 - (void)draggingEnded:(id<NSDraggingInfo>)info {
172 [controller_ draggingEnded:info];
174 [[BookmarkButton draggedButton] setHidden:NO];
175 if (dropIndicatorShown_) {
176 dropIndicatorShown_ = NO;
177 [self dropIndicatorChanged];
179 [controller_ draggingEnded:info];
182 - (BOOL)wantsPeriodicDraggingUpdates {
186 - (NSDragOperation)draggingUpdated:(id<NSDraggingInfo>)info {
187 // For now it's the same as draggingEntered:.
188 return [self draggingEntered:info];
191 - (BOOL)prepareForDragOperation:(id<NSDraggingInfo>)info {
195 // Implement NSDraggingDestination protocol method
196 // performDragOperation: for URLs.
197 - (BOOL)performDragOperationForURL:(id<NSDraggingInfo>)info {
198 NSPasteboard* pboard = [info draggingPasteboard];
199 DCHECK([pboard containsURLData]);
202 NSArray* titles = nil;
203 [pboard getURLs:&urls andTitles:&titles convertingFilenames:YES];
205 return [controller_ addURLs:urls
207 at:[info draggingLocation]];
210 // Implement NSDraggingDestination protocol method
211 // performDragOperation: for bookmark buttons.
212 - (BOOL)performDragOperationForBookmarkButton:(id<NSDraggingInfo>)info {
214 NSData* data = [[info draggingPasteboard]
215 dataForType:kBookmarkButtonDragType];
216 // [info draggingSource] is nil if not the same application.
217 if (data && [info draggingSource]) {
218 BookmarkButton* button = nil;
219 [data getBytes:&button length:sizeof(button)];
221 // If we're dragging from one profile to another, disallow moving (only
222 // allow copying). Each profile has its own bookmark model, so one way to
223 // check whether we are dragging across profiles is to see if the
224 // |BookmarkNode| corresponding to |button| exists in this profile. If it
225 // does, we're dragging within a profile; otherwise, we're dragging across
227 const BookmarkModel* const model = [[self controller] bookmarkModel];
228 const BookmarkNode* const source_node = [button bookmarkNode];
229 const BookmarkNode* const target_node =
230 GetBookmarkNodeByID(model, source_node->id());
233 !([info draggingSourceOperationMask] & NSDragOperationMove) ||
234 (source_node != target_node);
235 rtn = [controller_ dragButton:button
236 to:[info draggingLocation]
238 content::RecordAction(UserMetricsAction("BookmarkBar_DragEnd"));
243 - (BOOL)performDragOperation:(id<NSDraggingInfo>)info {
244 if ([controller_ dragBookmarkData:info])
246 NSPasteboard* pboard = [info draggingPasteboard];
247 if ([pboard dataForType:kBookmarkButtonDragType]) {
248 if ([self performDragOperationForBookmarkButton:info])
252 if ([pboard containsURLData]) {
253 if ([self performDragOperationForURL:info])
260 return [[controller_ menuController] menuForBookmarkBar];
263 - (void)setController:(id)controller {
264 controller_ = controller;
268 return VIEW_ID_BOOKMARK_BAR;
271 @end // @implementation BookmarkBarView
273 @implementation BookmarkBarTextField
276 return [barView_ menu];
279 @end // @implementation BookmarkBarTextField
281 @implementation BookmarkBarItemContainer
284 return [barView_ menu];
287 @end // @implementation BookmarkBarItemContainer