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