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