blob: b468c6fce2484a4c5e751a3838420346a14798bf [file] [log] [blame]
// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_view.h"
#include "chrome/browser/bookmarks/bookmark_pasteboard_helper_mac.h"
#include "chrome/browser/profiles/profile.h"
#import "chrome/browser/themes/theme_properties.h"
#import "chrome/browser/themes/theme_service.h"
#import "chrome/browser/ui/cocoa/bookmarks/bookmark_bar_controller.h"
#import "chrome/browser/ui/cocoa/bookmarks/bookmark_button.h"
#import "chrome/browser/ui/cocoa/bookmarks/bookmark_context_menu_cocoa_controller.h"
#import "chrome/browser/ui/cocoa/bookmarks/bookmark_folder_target.h"
#import "chrome/browser/ui/cocoa/browser_window_controller.h"
#import "chrome/browser/ui/cocoa/themed_window.h"
#import "chrome/browser/ui/cocoa/view_id_util.h"
#include "content/public/browser/user_metrics.h"
#import "third_party/mozilla/NSPasteboard+Utils.h"
using content::UserMetricsAction;
@interface BookmarkBarView (Private)
- (void)themeDidChangeNotification:(NSNotification*)aNotification;
- (void)updateTheme:(ui::ThemeProvider*)themeProvider;
@end
@implementation BookmarkBarView
@synthesize dropIndicatorShown = dropIndicatorShown_;
@synthesize dropIndicatorPosition = dropIndicatorPosition_;
@synthesize noItemContainer = noItemContainer_;
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];
// This probably isn't strictly necessary, but can't hurt.
[self unregisterDraggedTypes];
[super dealloc];
// To be clear, our controller_ is an IBOutlet and owns us, so we
// don't deallocate it explicitly. It is owned by the browser
// window controller, so gets deleted with a browser window is
// closed.
}
- (void)awakeFromNib {
NSNotificationCenter* defaultCenter = [NSNotificationCenter defaultCenter];
[defaultCenter addObserver:self
selector:@selector(themeDidChangeNotification:)
name:kBrowserThemeDidChangeNotification
object:nil];
DCHECK(controller_) << "Expected this to be hooked up via Interface Builder";
NSArray* types = [NSArray arrayWithObjects:
NSStringPboardType,
NSHTMLPboardType,
NSURLPboardType,
kBookmarkButtonDragType,
kBookmarkDictionaryListPboardType,
nil];
[self registerForDraggedTypes:types];
}
// We need the theme to color the bookmark buttons properly. But our
// controller desn't have access to it until it's placed in the view
// hierarchy. This is the spot where we close the loop.
- (void)viewWillMoveToWindow:(NSWindow*)window {
ui::ThemeProvider* themeProvider = [window themeProvider];
[self updateTheme:themeProvider];
[controller_ updateTheme:themeProvider];
[super viewWillMoveToWindow:window];
}
- (void)viewDidMoveToWindow {
[controller_ viewDidMoveToWindow];
}
// Called after a theme change took place, possibly for a different profile.
- (void)themeDidChangeNotification:(NSNotification*)notification {
[self updateTheme:[[self window] themeProvider]];
}
// Adapt appearance to the current theme. Called after theme changes and before
// this is shown for the first time.
- (void)updateTheme:(ui::ThemeProvider*)themeProvider {
if (!themeProvider)
return;
NSColor* color =
themeProvider->GetNSColor(ThemeProperties::COLOR_BOOKMARK_TEXT,
true);
[noItemTextfield_ setTextColor:color];
}
// Mouse down events on the bookmark bar should not allow dragging the parent
// window around.
- (BOOL)mouseDownCanMoveWindow {
return NO;
}
- (BookmarkBarTextField*)noItemTextfield {
return noItemTextfield_;
}
- (NSButton*)importBookmarksButton {
return importBookmarksButton_;
}
- (BookmarkBarController*)controller {
return controller_;
}
// Internal method, needs to be called whenever a change has been made to
// dropIndicatorShown_ or dropIndicatorPosition_ so it can get the controller
// to reflect the change by moving buttons around.
- (void)dropIndicatorChanged {
if (dropIndicatorShown_)
[controller_ setDropInsertionPos:dropIndicatorPosition_];
else
[controller_ clearDropInsertionPos];
}
// Shim function to assist in unit testing.
- (BOOL)dragClipboardContainsBookmarks {
return bookmark_pasteboard_helper_mac::PasteboardContainsBookmarks(
bookmark_pasteboard_helper_mac::kDragPasteboard);
}
// NSDraggingDestination methods
- (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)info {
if (![controller_ draggingAllowed:info])
return NSDragOperationNone;
if ([[info draggingPasteboard] dataForType:kBookmarkButtonDragType] ||
[self dragClipboardContainsBookmarks] ||
[[info draggingPasteboard] containsURLData]) {
// We only show the drop indicator if we're not in a position to
// perform a hover-open since it doesn't make sense to do both.
BOOL showIt = [controller_ shouldShowIndicatorShownForPoint:
[info draggingLocation]];
if (!showIt) {
if (dropIndicatorShown_) {
dropIndicatorShown_ = NO;
[self dropIndicatorChanged];
}
} else {
CGFloat x =
[controller_ indicatorPosForDragToPoint:[info draggingLocation]];
// Need an update if the indicator wasn't previously shown or if it has
// moved.
if (!dropIndicatorShown_ || dropIndicatorPosition_ != x) {
dropIndicatorShown_ = YES;
dropIndicatorPosition_ = x;
[self dropIndicatorChanged];
}
}
[controller_ draggingEntered:info]; // allow hover-open to work.
return [[info draggingSource] isKindOfClass: [BookmarkButton class]] ?
NSDragOperationMove : NSDragOperationCopy;
}
return NSDragOperationNone;
}
- (void)draggingExited:(id<NSDraggingInfo>)info {
[controller_ draggingExited:info];
// Regardless of the type of dragging which ended, we need to get rid of the
// drop indicator if one was shown.
if (dropIndicatorShown_) {
dropIndicatorShown_ = NO;
[self dropIndicatorChanged];
}
}
- (void)draggingEnded:(id<NSDraggingInfo>)info {
[controller_ draggingEnded:info];
[[BookmarkButton draggedButton] setHidden:NO];
if (dropIndicatorShown_) {
dropIndicatorShown_ = NO;
[self dropIndicatorChanged];
}
[controller_ draggingEnded:info];
}
- (BOOL)wantsPeriodicDraggingUpdates {
return YES;
}
- (NSDragOperation)draggingUpdated:(id<NSDraggingInfo>)info {
// For now it's the same as draggingEntered:.
return [self draggingEntered:info];
}
- (BOOL)prepareForDragOperation:(id<NSDraggingInfo>)info {
return YES;
}
// Implement NSDraggingDestination protocol method
// performDragOperation: for URLs.
- (BOOL)performDragOperationForURL:(id<NSDraggingInfo>)info {
NSPasteboard* pboard = [info draggingPasteboard];
DCHECK([pboard containsURLData]);
NSArray* urls = nil;
NSArray* titles = nil;
[pboard getURLs:&urls andTitles:&titles convertingFilenames:YES];
return [controller_ addURLs:urls
withTitles:titles
at:[info draggingLocation]];
}
// Implement NSDraggingDestination protocol method
// performDragOperation: for bookmark buttons.
- (BOOL)performDragOperationForBookmarkButton:(id<NSDraggingInfo>)info {
BOOL rtn = NO;
NSData* data = [[info draggingPasteboard]
dataForType:kBookmarkButtonDragType];
// [info draggingSource] is nil if not the same application.
if (data && [info draggingSource]) {
BookmarkButton* button = nil;
[data getBytes:&button length:sizeof(button)];
// If we're dragging from one profile to another, disallow moving (only
// allow copying). Each profile has its own bookmark model, so one way to
// check whether we are dragging across profiles is to see if the
// |BookmarkNode| corresponding to |button| exists in this profile. If it
// does, we're dragging within a profile; otherwise, we're dragging across
// profiles.
const BookmarkModel* const model = [[self controller] bookmarkModel];
const BookmarkNode* const source_node = [button bookmarkNode];
const BookmarkNode* const target_node =
model->GetNodeByID(source_node->id());
BOOL copy =
!([info draggingSourceOperationMask] & NSDragOperationMove) ||
(source_node != target_node);
rtn = [controller_ dragButton:button
to:[info draggingLocation]
copy:copy];
content::RecordAction(UserMetricsAction("BookmarkBar_DragEnd"));
}
return rtn;
}
- (BOOL)performDragOperation:(id<NSDraggingInfo>)info {
if ([controller_ dragBookmarkData:info])
return YES;
NSPasteboard* pboard = [info draggingPasteboard];
if ([pboard dataForType:kBookmarkButtonDragType]) {
if ([self performDragOperationForBookmarkButton:info])
return YES;
// Fall through....
}
if ([pboard containsURLData]) {
if ([self performDragOperationForURL:info])
return YES;
}
return NO;
}
- (NSMenu*)menu {
return [[controller_ menuController] menuForBookmarkNode:NULL];
}
- (void)setController:(id)controller {
controller_ = controller;
}
- (ViewID)viewID {
return VIEW_ID_BOOKMARK_BAR;
}
@end // @implementation BookmarkBarView
@implementation BookmarkBarTextField
- (NSMenu*)menu {
return [barView_ menu];
}
@end // @implementation BookmarkBarTextField
@implementation BookmarkBarItemContainer
- (NSMenu*)menu {
return [barView_ menu];
}
@end // @implementation BookmarkBarItemContainer