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