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_cocoa.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"
21 #import "ui/base/cocoa/nsview_additions.h"
23 using base::UserMetricsAction;
24 using bookmarks::BookmarkModel;
25 using bookmarks::BookmarkNode;
27 @interface BookmarkBarView (Private)
28 - (void)themeDidChangeNotification:(NSNotification*)aNotification;
29 - (void)updateTheme:(ui::ThemeProvider*)themeProvider;
32 - (void)setFrameSize:(NSSize)size;
35 @implementation BookmarkBarView
37 @synthesize dropIndicatorShown = dropIndicatorShown_;
38 @synthesize dropIndicatorPosition = dropIndicatorPosition_;
39 @synthesize noItemContainer = noItemContainer_;
41 - (void)setFrameSize:(NSSize)size {
42 NSSize oldFrameSize = [self frame].size;
43 [super setFrameSize:size];
44 // Any time the size of the bookmark bar view changes, the bookmark bar view
45 // buttons needs to be redrawn.
46 // https://code.google.com/p/chromium/issues/detail?id=521025#c7
47 if (!NSEqualSizes(oldFrameSize, size))
48 [self cr_recursivelySetNeedsDisplay:YES];
52 [[NSNotificationCenter defaultCenter] removeObserver:self];
53 // This probably isn't strictly necessary, but can't hurt.
54 [self unregisterDraggedTypes];
57 // To be clear, our controller_ is an IBOutlet and owns us, so we
58 // don't deallocate it explicitly. It is owned by the browser
59 // window controller, so gets deleted with a browser window is
63 - (void)awakeFromNib {
64 NSNotificationCenter* defaultCenter = [NSNotificationCenter defaultCenter];
65 [defaultCenter addObserver:self
66 selector:@selector(themeDidChangeNotification:)
67 name:kBrowserThemeDidChangeNotification
70 DCHECK(controller_) << "Expected this to be hooked up via Interface Builder";
71 NSArray* types = [NSArray arrayWithObjects:
75 kBookmarkButtonDragType,
76 kBookmarkDictionaryListPboardType,
78 [self registerForDraggedTypes:types];
81 // We need the theme to color the bookmark buttons properly. But our
82 // controller desn't have access to it until it's placed in the view
83 // hierarchy. This is the spot where we close the loop.
84 - (void)viewWillMoveToWindow:(NSWindow*)window {
85 ui::ThemeProvider* themeProvider = [window themeProvider];
86 [self updateTheme:themeProvider];
87 [controller_ updateTheme:themeProvider];
88 [super viewWillMoveToWindow:window];
91 - (void)viewDidMoveToWindow {
92 [controller_ viewDidMoveToWindow];
95 // Called after a theme change took place, possibly for a different profile.
96 - (void)themeDidChangeNotification:(NSNotification*)notification {
97 [self updateTheme:[[self window] themeProvider]];
100 // Adapt appearance to the current theme. Called after theme changes and before
101 // this is shown for the first time.
102 - (void)updateTheme:(ui::ThemeProvider*)themeProvider {
107 themeProvider->GetNSColor(ThemeProperties::COLOR_BOOKMARK_TEXT);
108 [noItemTextfield_ setTextColor:color];
111 // Mouse down events on the bookmark bar should not allow dragging the parent
113 - (BOOL)mouseDownCanMoveWindow {
117 - (BookmarkBarTextField*)noItemTextfield {
118 return noItemTextfield_;
121 - (NSButton*)importBookmarksButton {
122 return importBookmarksButton_;
125 - (BookmarkBarController*)controller {
129 // Internal method, needs to be called whenever a change has been made to
130 // dropIndicatorShown_ or dropIndicatorPosition_ so it can get the controller
131 // to reflect the change by moving buttons around.
132 - (void)dropIndicatorChanged {
133 if (dropIndicatorShown_)
134 [controller_ setDropInsertionPos:dropIndicatorPosition_];
136 [controller_ clearDropInsertionPos];
139 // NSDraggingDestination methods
141 - (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)info {
142 if (![controller_ draggingAllowed:info])
143 return NSDragOperationNone;
144 if ([[info draggingPasteboard] dataForType:kBookmarkButtonDragType] ||
145 bookmarks::PasteboardContainsBookmarks(ui::CLIPBOARD_TYPE_DRAG) ||
146 [[info draggingPasteboard] containsURLData]) {
147 // We only show the drop indicator if we're not in a position to
148 // perform a hover-open since it doesn't make sense to do both.
149 BOOL showIt = [controller_ shouldShowIndicatorShownForPoint:
150 [info draggingLocation]];
152 if (dropIndicatorShown_) {
153 dropIndicatorShown_ = NO;
154 [self dropIndicatorChanged];
158 [controller_ indicatorPosForDragToPoint:[info draggingLocation]];
159 // Need an update if the indicator wasn't previously shown or if it has
161 if (!dropIndicatorShown_ || dropIndicatorPosition_ != x) {
162 dropIndicatorShown_ = YES;
163 dropIndicatorPosition_ = x;
164 [self dropIndicatorChanged];
168 [controller_ draggingEntered:info]; // allow hover-open to work.
169 return [[info draggingSource] isKindOfClass: [BookmarkButton class]] ?
170 NSDragOperationMove : NSDragOperationCopy;
172 return NSDragOperationNone;
175 - (void)draggingExited:(id<NSDraggingInfo>)info {
176 [controller_ draggingExited:info];
178 // Regardless of the type of dragging which ended, we need to get rid of the
179 // drop indicator if one was shown.
180 if (dropIndicatorShown_) {
181 dropIndicatorShown_ = NO;
182 [self dropIndicatorChanged];
186 - (void)draggingEnded:(id<NSDraggingInfo>)info {
187 [controller_ draggingEnded:info];
189 [[BookmarkButton draggedButton] setHidden:NO];
190 if (dropIndicatorShown_) {
191 dropIndicatorShown_ = NO;
192 [self dropIndicatorChanged];
194 [controller_ draggingEnded:info];
197 - (BOOL)wantsPeriodicDraggingUpdates {
201 - (NSDragOperation)draggingUpdated:(id<NSDraggingInfo>)info {
202 // For now it's the same as draggingEntered:.
203 return [self draggingEntered:info];
206 - (BOOL)prepareForDragOperation:(id<NSDraggingInfo>)info {
210 // Implement NSDraggingDestination protocol method
211 // performDragOperation: for URLs.
212 - (BOOL)performDragOperationForURL:(id<NSDraggingInfo>)info {
213 NSPasteboard* pboard = [info draggingPasteboard];
214 DCHECK([pboard containsURLData]);
217 NSArray* titles = nil;
218 [pboard getURLs:&urls andTitles:&titles convertingFilenames:YES];
220 return [controller_ addURLs:urls
222 at:[info draggingLocation]];
225 // Implement NSDraggingDestination protocol method
226 // performDragOperation: for bookmark buttons.
227 - (BOOL)performDragOperationForBookmarkButton:(id<NSDraggingInfo>)info {
229 NSData* data = [[info draggingPasteboard]
230 dataForType:kBookmarkButtonDragType];
231 // [info draggingSource] is nil if not the same application.
232 if (data && [info draggingSource]) {
233 BookmarkButton* button = nil;
234 [data getBytes:&button length:sizeof(button)];
236 // If we're dragging from one profile to another, disallow moving (only
237 // allow copying). Each profile has its own bookmark model, so one way to
238 // check whether we are dragging across profiles is to see if the
239 // |BookmarkNode| corresponding to |button| exists in this profile. If it
240 // does, we're dragging within a profile; otherwise, we're dragging across
242 const BookmarkModel* const model = [[self controller] bookmarkModel];
243 const BookmarkNode* const source_node = [button bookmarkNode];
244 const BookmarkNode* const target_node =
245 bookmarks::GetBookmarkNodeByID(model, source_node->id());
248 !([info draggingSourceOperationMask] & NSDragOperationMove) ||
249 (source_node != target_node);
250 rtn = [controller_ dragButton:button
251 to:[info draggingLocation]
253 content::RecordAction(UserMetricsAction("BookmarkBar_DragEnd"));
258 - (BOOL)performDragOperation:(id<NSDraggingInfo>)info {
259 if ([controller_ dragBookmarkData:info])
261 NSPasteboard* pboard = [info draggingPasteboard];
262 if ([pboard dataForType:kBookmarkButtonDragType]) {
263 if ([self performDragOperationForBookmarkButton:info])
267 if ([pboard containsURLData]) {
268 if ([self performDragOperationForURL:info])
275 return [[controller_ menuController] menuForBookmarkBar];
278 - (void)setController:(id)controller {
279 controller_ = controller;
283 return VIEW_ID_BOOKMARK_BAR;
286 @end // @implementation BookmarkBarView
288 @implementation BookmarkBarTextField
291 return [barView_ menu];
294 @end // @implementation BookmarkBarTextField
296 @implementation BookmarkBarItemContainer
299 return [barView_ menu];
302 @end // @implementation BookmarkBarItemContainer