summaryrefslogtreecommitdiffstats
path: root/chrome
diff options
context:
space:
mode:
authorjrg@chromium.org <jrg@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-11-18 17:59:10 +0000
committerjrg@chromium.org <jrg@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-11-18 17:59:10 +0000
commit52c71ed5a093463acb72598dc8b07a35fbb49842 (patch)
tree7e1705718bed27cdd1068dacb06176da3fb5b531 /chrome
parent1308459ce571eccb2b344f35ca0e4bf750be9395 (diff)
downloadchromium_src-52c71ed5a093463acb72598dc8b07a35fbb49842.zip
chromium_src-52c71ed5a093463acb72598dc8b07a35fbb49842.tar.gz
chromium_src-52c71ed5a093463acb72598dc8b07a35fbb49842.tar.bz2
Drag and drop of buttons/folders from bar to bar.
Does not include DnD to/from menus. No animations of drop destination yet. BUG=http://crbug.com/17608 TEST=Do some draggin and droppin. Confirm click still works on the marks and folders. Confirm "other bookmarks" canNOT be moved. Confirm NTP / detached bar also works for DnD. Review URL: http://codereview.chromium.org/395031 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@32341 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'chrome')
-rw-r--r--chrome/browser/cocoa/bookmark_bar_controller.h12
-rw-r--r--chrome/browser/cocoa/bookmark_bar_controller.mm59
-rw-r--r--chrome/browser/cocoa/bookmark_bar_controller_unittest.mm33
-rw-r--r--chrome/browser/cocoa/bookmark_bar_view.h6
-rw-r--r--chrome/browser/cocoa/bookmark_bar_view.mm58
-rw-r--r--chrome/browser/cocoa/bookmark_bar_view_unittest.mm70
-rw-r--r--chrome/browser/cocoa/bookmark_button.h23
-rw-r--r--chrome/browser/cocoa/bookmark_button.mm130
-rw-r--r--chrome/browser/cocoa/bookmark_button_unittest.mm49
-rwxr-xr-xchrome/chrome.gyp5
10 files changed, 414 insertions, 31 deletions
diff --git a/chrome/browser/cocoa/bookmark_bar_controller.h b/chrome/browser/cocoa/bookmark_bar_controller.h
index 648a9ad..456f2bc 100644
--- a/chrome/browser/cocoa/bookmark_bar_controller.h
+++ b/chrome/browser/cocoa/bookmark_bar_controller.h
@@ -16,15 +16,16 @@
#include "webkit/glue/window_open_disposition.h"
@class BookmarkBarController;
-class BookmarkModel;
-class BookmarkNode;
@class BookmarkBarView;
+@class BookmarkButton;
+class BookmarkModel;
@class BookmarkMenu;
+class BookmarkNode;
class Browser;
class GURL;
@class MenuButton;
-class Profile;
class PrefService;
+class Profile;
class TabContents;
@class ToolbarController;
@protocol ViewResizer;
@@ -184,6 +185,11 @@ willAnimateFromState:(bookmarks::VisualState)oldState
// Returns true if at least one bookmark was added.
- (BOOL)addURLs:(NSArray*)urls withTitles:(NSArray*)titles at:(NSPoint)point;
+// Complete a drag of a bookmark button to this location on the main bar.
+// TODO(jrg): submenu DnD.
+// Returns YES on success.
+- (BOOL)dragButton:(BookmarkButton*)sourceButton to:(NSPoint)point;
+
// Actions for manipulating bookmarks.
// From a button, ...
- (IBAction)openBookmark:(id)sender;
diff --git a/chrome/browser/cocoa/bookmark_bar_controller.mm b/chrome/browser/cocoa/bookmark_bar_controller.mm
index ffb0ea5..a6ddfd7 100644
--- a/chrome/browser/cocoa/bookmark_bar_controller.mm
+++ b/chrome/browser/cocoa/bookmark_bar_controller.mm
@@ -16,11 +16,12 @@
#import "chrome/browser/cocoa/bookmark_bar_controller.h"
#import "chrome/browser/cocoa/bookmark_bar_toolbar_view.h"
#import "chrome/browser/cocoa/bookmark_bar_view.h"
+#import "chrome/browser/cocoa/bookmark_button.h"
#import "chrome/browser/cocoa/bookmark_button_cell.h"
#import "chrome/browser/cocoa/bookmark_editor_controller.h"
-#import "chrome/browser/cocoa/bookmark_name_folder_controller.h"
#import "chrome/browser/cocoa/bookmark_menu.h"
#import "chrome/browser/cocoa/bookmark_menu_cocoa_controller.h"
+#import "chrome/browser/cocoa/bookmark_name_folder_controller.h"
#import "chrome/browser/cocoa/event_utils.h"
#import "chrome/browser/cocoa/menu_button.h"
#import "chrome/browser/cocoa/toolbar_controller.h"
@@ -108,18 +109,6 @@ const NSTimeInterval kBookmarkBarAnimationDuration = 0.12;
} // namespace
-// Specialization of NSButton that responds to middle-clicks. By default,
-// NSButton ignores them.
-@interface BookmarkButton : NSButton
-@end
-
-@implementation BookmarkButton
-- (void)otherMouseUp:(NSEvent*) event {
- [self performClick:self];
-}
-@end
-
-
@interface BookmarkBarController(Private)
// Determines the appropriate state for the given situation.
+ (bookmarks::VisualState)visualStateToShowNormalBar:(BOOL)showNormalBar
@@ -470,6 +459,47 @@ const NSTimeInterval kBookmarkBarAnimationDuration = 0.12;
return YES;
}
+- (BOOL)dragButton:(BookmarkButton*)sourceButton to:(NSPoint)point {
+ DCHECK([sourceButton isKindOfClass:[BookmarkButton class]]);
+
+ void* pointer = [[[sourceButton cell] representedObject] pointerValue];
+ const BookmarkNode* sourceNode = static_cast<const BookmarkNode*>(pointer);
+ DCHECK(sourceNode);
+
+ // Identify which buttons we are between. For now, assume a button
+ // location is at the center point of its view, and that an exact
+ // match means "place before".
+ // TODO(jrg): revisit position info based on UI team feedback.
+ // dropLocation is in bar local coordinates.
+ NSPoint dropLocation = [[self view] convertPoint:point
+ fromView:[[[self view] window]
+ contentView]];
+ NSButton* buttonToTheRightOfDraggedButton = nil;
+ for (NSButton* button in buttons_.get()) {
+ CGFloat midpoint = NSMidX([button frame]);
+ if (dropLocation.x <= midpoint) {
+ buttonToTheRightOfDraggedButton = button;
+ break;
+ }
+ }
+ if (buttonToTheRightOfDraggedButton) {
+ pointer = [[[buttonToTheRightOfDraggedButton cell]
+ representedObject] pointerValue];
+ const BookmarkNode* afterNode = static_cast<const BookmarkNode*>(pointer);
+ bookmarkModel_->Move(sourceNode, sourceNode->GetParent(),
+ afterNode->GetParent()->IndexOfChild(afterNode));
+ } else {
+ // If nothing is to my right I am at the end!
+ bookmarkModel_->Move(sourceNode, sourceNode->GetParent(),
+ sourceNode->GetParent()->GetChildCount());
+ }
+
+ // Movement of a node triggers observers (like us) to rebuild the
+ // bar so we don't have to do so explicitly.
+
+ return YES;
+}
+
- (int)currentTabContentsHeight {
return browser_->GetSelectedTabContents() ?
browser_->GetSelectedTabContents()->view()->GetContainerSize().height() :
@@ -1017,7 +1047,8 @@ const NSTimeInterval kBookmarkBarAnimationDuration = 0.12;
NSRect frame = [self frameForBookmarkButtonFromCell:cell xOffset:&ignored];
frame.origin.x = [[self buttonView] bounds].size.width - frame.size.width;
frame.origin.x -= bookmarks::kBookmarkHorizontalPadding;
- NSButton* button = [[BookmarkButton alloc] initWithFrame:frame];
+ BookmarkButton* button = [[BookmarkButton alloc] initWithFrame:frame];
+ [button setDraggable:NO];
otherBookmarksButton_.reset(button);
// Peg at right; keep same height as bar.
diff --git a/chrome/browser/cocoa/bookmark_bar_controller_unittest.mm b/chrome/browser/cocoa/bookmark_bar_controller_unittest.mm
index 7433bf2..c025b0e 100644
--- a/chrome/browser/cocoa/bookmark_bar_controller_unittest.mm
+++ b/chrome/browser/cocoa/bookmark_bar_controller_unittest.mm
@@ -63,7 +63,7 @@
// Remember the number of times we've gotten a frameDidChange notification.
@interface BookmarkBarControllerTogglePong : BookmarkBarControllerNoOpen {
-@private
+ @private
int toggles_;
}
@property (readonly) int toggles;
@@ -758,4 +758,35 @@ TEST_F(BookmarkBarControllerTest, TestMenuNodeAndDisable) {
EXPECT_TRUE([bar_ validateUserInterfaceItem:item]);
}
+TEST_F(BookmarkBarControllerTest, TestDragButton) {
+ BookmarkModel* model = helper_.profile()->GetBookmarkModel();
+
+ GURL gurls[] = { GURL("http://www.google.com/a"),
+ GURL("http://www.google.com/b"),
+ GURL("http://www.google.com/c") };
+ std::wstring titles[] = { L"a", L"b", L"c" };
+ for (unsigned i = 0; i < arraysize(titles); i++) {
+ model->SetURLStarred(gurls[i], titles[i], true);
+ }
+
+ EXPECT_EQ([[bar_ buttons] count], arraysize(titles));
+ EXPECT_TRUE([[[[bar_ buttons] objectAtIndex:0] title] isEqual:@"a"]);
+
+ [bar_ dragButton:[[bar_ buttons] objectAtIndex:2] to:NSMakePoint(0, 0)];
+ EXPECT_TRUE([[[[bar_ buttons] objectAtIndex:0] title] isEqual:@"c"]);
+
+ [bar_ dragButton:[[bar_ buttons] objectAtIndex:1] to:NSMakePoint(1000, 0)];
+ EXPECT_TRUE([[[[bar_ buttons] objectAtIndex:0] title] isEqual:@"c"]);
+ EXPECT_TRUE([[[[bar_ buttons] objectAtIndex:1] title] isEqual:@"b"]);
+ EXPECT_TRUE([[[[bar_ buttons] objectAtIndex:2] title] isEqual:@"a"]);
+
+ // Finally, a drop of the 1st between the next 2
+ CGFloat x = NSMinX([[[bar_ buttons] objectAtIndex:2] frame]);
+ x += [[bar_ view] frame].origin.x;
+ [bar_ dragButton:[[bar_ buttons] objectAtIndex:0] to:NSMakePoint(x, 0)];
+ EXPECT_TRUE([[[[bar_ buttons] objectAtIndex:0] title] isEqual:@"b"]);
+ EXPECT_TRUE([[[[bar_ buttons] objectAtIndex:1] title] isEqual:@"c"]);
+ EXPECT_TRUE([[[[bar_ buttons] objectAtIndex:2] title] isEqual:@"a"]);
+}
+
} // namespace
diff --git a/chrome/browser/cocoa/bookmark_bar_view.h b/chrome/browser/cocoa/bookmark_bar_view.h
index 9b81ec4..8a9cc3f 100644
--- a/chrome/browser/cocoa/bookmark_bar_view.h
+++ b/chrome/browser/cocoa/bookmark_bar_view.h
@@ -17,9 +17,11 @@
IBOutlet BookmarkBarController* controller_;
IBOutlet NSTextField* noItemTextfield_;
}
-
- (NSTextField*)noItemTextfield;
+@end
+@interface BookmarkBarView(TestingAPI)
+- (void)setController:(id)controller;
@end
-
+
#endif // CHROME_BROWSER_COCOA_BOOKMARK_BAR_VIEW_H_
diff --git a/chrome/browser/cocoa/bookmark_bar_view.mm b/chrome/browser/cocoa/bookmark_bar_view.mm
index 61cfa67..e6976af 100644
--- a/chrome/browser/cocoa/bookmark_bar_view.mm
+++ b/chrome/browser/cocoa/bookmark_bar_view.mm
@@ -5,6 +5,7 @@
#import "chrome/browser/cocoa/bookmark_bar_view.h"
#import "chrome/browser/cocoa/bookmark_bar_controller.h"
+#import "chrome/browser/cocoa/bookmark_button.h"
#import "third_party/GTM/AppKit/GTMTheme.h"
#import "third_party/mozilla/include/NSPasteboard+Utils.h"
@@ -30,8 +31,12 @@
object:nil];
DCHECK(controller_ && "Expected this to be hooked up via Interface Builder");
- NSArray* types = [NSArray arrayWithObjects:NSStringPboardType,
- NSHTMLPboardType, NSURLPboardType, nil];
+ NSArray* types = [NSArray arrayWithObjects:
+ NSStringPboardType,
+ NSHTMLPboardType,
+ NSURLPboardType,
+ kBookmarkButtonDragType,
+ nil];
[self registerForDraggedTypes:types];
}
@@ -69,6 +74,8 @@
- (NSDragOperation)draggingEntered:(id<NSDraggingInfo>)info {
if ([[info draggingPasteboard] containsURLData])
return NSDragOperationCopy;
+ if ([[info draggingPasteboard] dataForType:kBookmarkButtonDragType])
+ return NSDragOperationMove;
return NSDragOperationNone;
}
@@ -80,16 +87,19 @@
}
- (NSDragOperation)draggingUpdated:(id<NSDraggingInfo>)info {
- if ([[info draggingPasteboard] containsURLData])
- return NSDragOperationCopy;
- return NSDragOperationNone;
+ // For now it's the same as draggingEntered:.
+ // TODO(jrg): once we return YES for wantsPeriodicDraggingUpdates,
+ // this should ping the controller_ to perform animations.
+ return [self draggingEntered:info];
}
- (BOOL)prepareForDragOperation:(id<NSDraggingInfo>)info {
return YES;
}
-- (BOOL)performDragOperation:(id<NSDraggingInfo>)info {
+// Implement NSDraggingDestination protocol method
+// performDragOperation: for URLs.
+- (BOOL)performDragOperationForURL:(id<NSDraggingInfo>)info {
NSPasteboard* pboard = [info draggingPasteboard];
DCHECK([pboard containsURLData]);
@@ -102,4 +112,40 @@
at:[info draggingLocation]];
}
+// Implement NSDraggingDestination protocol method
+// performDragOperation: for bookmarks.
+- (BOOL)performDragOperationForBookmark:(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)];
+ rtn = [controller_ dragButton:button to:[info draggingLocation]];
+ }
+ return rtn;
+}
+
+- (BOOL)performDragOperation:(id<NSDraggingInfo>)info {
+ NSPasteboard* pboard = [info draggingPasteboard];
+ if ([pboard containsURLData]) {
+ return [self performDragOperationForURL:info];
+ } else if ([pboard dataForType:kBookmarkButtonDragType]) {
+ return [self performDragOperationForBookmark:info];
+ } else {
+ NOTREACHED() << "Unknown drop type onto bookmark bar.";
+ return NO;
+ }
+}
+
@end // @implementation BookmarkBarView
+
+
+@implementation BookmarkBarView(TestingAPI)
+
+- (void)setController:(id)controller {
+ controller_ = controller;
+}
+
+@end // @implementation BookmarkBarView(TestingAPI)
diff --git a/chrome/browser/cocoa/bookmark_bar_view_unittest.mm b/chrome/browser/cocoa/bookmark_bar_view_unittest.mm
index 000d7fa..4091168 100644
--- a/chrome/browser/cocoa/bookmark_bar_view_unittest.mm
+++ b/chrome/browser/cocoa/bookmark_bar_view_unittest.mm
@@ -4,23 +4,85 @@
#include "base/scoped_nsobject.h"
#import "chrome/browser/cocoa/bookmark_bar_view.h"
+#import "chrome/browser/cocoa/bookmark_button.h"
#import "chrome/browser/cocoa/cocoa_test_helper.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "testing/platform_test.h"
+// Fake DraggingInfo, fake BookmarkBarController, fake pasteboard...
+@interface FakeBookmarkDraggingInfo : NSObject {
+ scoped_nsobject<NSData> data_;
+ BOOL pong_;
+}
+@end
+
+@implementation FakeBookmarkDraggingInfo
+
+- (id)init {
+ if ((self = [super init])) {
+ data_.reset([[NSData dataWithBytes:&self length:sizeof(self)] retain]);
+ }
+ return self;
+}
+
+// So we can be both info and pasteboard.
+- (id)draggingPasteboard {
+ return self;
+}
+
+// So we can look local.
+- (id)draggingSource {
+ return self;
+}
+
+- (BOOL)containsURLData {
+ return NO;
+}
+
+- (NSPoint)draggingLocation {
+ return NSMakePoint(10, 10);
+}
+
+- (NSData*)dataForType:(NSString*)type {
+ if ([type isEqual:kBookmarkButtonDragType])
+ return data_.get();
+ return nil;
+}
+
+// Fake a controller for callback ponging
+- (BOOL)dragButton:(BookmarkButton*)button to:(NSPoint)point {
+ pong_ = YES;
+ return YES;
+}
+
+// Confirm the pong.
+- (BOOL)dragButtonToPong {
+ return pong_;
+}
+
+@end
+
namespace {
-class BookmarkBarViewTest : public PlatformTest {
+class BookmarkBarViewTest : public CocoaTest {
public:
- CocoaTestHelper cocoa_helper_;
scoped_nsobject<BookmarkBarView> view_;
};
-// This class only needs to do one thing: prevent mouse down events from moving
-// the parent window around.
TEST_F(BookmarkBarViewTest, CanDragWindow) {
view_.reset([[BookmarkBarView alloc] init]);
EXPECT_FALSE([view_.get() mouseDownCanMoveWindow]);
}
+TEST_F(BookmarkBarViewTest, BookmarkButtonDragAndDrop) {
+ view_.reset([[BookmarkBarView alloc] init]);
+ scoped_nsobject<FakeBookmarkDraggingInfo>
+ info([[FakeBookmarkDraggingInfo alloc] init]);
+
+ [view_ setController:info.get()];
+ EXPECT_EQ([view_ draggingEntered:(id)info.get()], NSDragOperationMove);
+ EXPECT_TRUE([view_ performDragOperation:(id)info.get()]);
+ EXPECT_TRUE([info dragButtonToPong]);
+}
+
} // namespace
diff --git a/chrome/browser/cocoa/bookmark_button.h b/chrome/browser/cocoa/bookmark_button.h
new file mode 100644
index 0000000..79c878a
--- /dev/null
+++ b/chrome/browser/cocoa/bookmark_button.h
@@ -0,0 +1,23 @@
+// Copyright (c) 2009 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 <Cocoa/Cocoa.h>
+
+// Class for bookmark bar buttons that can be drag sources.
+@interface BookmarkButton : NSButton {
+ @private
+ BOOL draggable_;
+ BOOL beingDragged_; // are we being dragged?
+
+ // Initial mouse-down to prevent a hair-trigger drag.
+ NSPoint initialMouseDownLocation_;
+}
+
+// Enable or disable dragability for special buttons like "Other Bookmarks".
+@property BOOL draggable;
+
+@end
+
+extern NSString* kBookmarkButtonDragType;
+
diff --git a/chrome/browser/cocoa/bookmark_button.mm b/chrome/browser/cocoa/bookmark_button.mm
new file mode 100644
index 0000000..8ef0682
--- /dev/null
+++ b/chrome/browser/cocoa/bookmark_button.mm
@@ -0,0 +1,130 @@
+// Copyright (c) 2009 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 "base/scoped_nsobject.h"
+#import "chrome/browser/cocoa/bookmark_button.h"
+
+NSString* kBookmarkButtonDragType = @"ChromiumBookmarkButtonDragType";
+
+// http://codereview.chromium.org/180036/diff/3001/3004
+namespace {
+const CGFloat kWebDragStartHysteresisX = 5.0;
+const CGFloat kWebDragStartHysteresisY = 5.0;
+}
+
+@implementation BookmarkButton
+
+@synthesize draggable = draggable_;
+
+- (id)initWithFrame:(NSRect)frame {
+ if ((self = [super initWithFrame:frame])) {
+ draggable_ = YES;
+ }
+ return self;
+}
+
+// By default, NSButton ignores middle-clicks.
+- (void)otherMouseUp:(NSEvent*)event {
+ [self performClick:self];
+}
+
+- (void)beginDrag:(NSEvent*)event {
+ NSSize dragOffset = NSMakeSize(0.0, 0.0);
+ NSPasteboard* pboard = [NSPasteboard pasteboardWithName:NSDragPboard];
+ [pboard declareTypes:[NSArray arrayWithObject:kBookmarkButtonDragType]
+ owner:self];
+
+ // This NSData is no longer referenced once the function ends so
+ // there is no need to retain/release when placing in here as an
+ // opaque pointer.
+ [pboard setData:[NSData dataWithBytes:&self length:sizeof(self)]
+ forType:kBookmarkButtonDragType];
+
+ // This won't work if the source view is clipped. Fortunately, we
+ // don't display clipped bookmark buttons.
+ [[self superview] lockFocus];
+ scoped_nsobject<NSBitmapImageRep>
+ bitmapImage([[NSBitmapImageRep alloc]
+ initWithFocusedViewRect:[self frame]]);
+
+ [[self superview] unlockFocus];
+ scoped_nsobject<NSImage> imageDrag([[NSImage alloc]
+ initWithSize:[bitmapImage size]]);
+ [imageDrag addRepresentation:bitmapImage];
+
+ // At the moment, moving bookmarks causes their buttons (like me!)
+ // to be destroyed and rebuilt. Make sure we don't go away while on
+ // the stack.
+ [self retain];
+
+ CGFloat yAt = [self bounds].size.height;
+ [self dragImage:imageDrag at:NSMakePoint(0, yAt) offset:dragOffset
+ event:event pasteboard:pboard source:self slideBack:YES];
+
+ // And we're done.
+ [self autorelease];
+}
+
+- (void)draggedImage:(NSImage*)anImage
+ endedAt:(NSPoint)aPoint
+ operation:(NSDragOperation)operation {
+ beingDragged_ = NO;
+ [[self cell] setHighlighted:NO];
+}
+
+- (NSDragOperation)draggingSourceOperationMaskForLocal:(BOOL)isLocal {
+ return isLocal ? NSDragOperationMove : NSDragOperationNone;
+}
+
+- (void)mouseUp:(NSEvent*)theEvent {
+ // This conditional is never true (DnD loops in Cocoa eat the mouse
+ // up) but I added it in case future versions of Cocoa do unexpected
+ // things.
+ if (beingDragged_)
+ return [super mouseUp:theEvent];
+
+ // There are non-drag cases where a mouseUp: may happen
+ // (e.g. mouse-down, cmd-tab to another application, move mouse,
+ // mouse-up). So we check.
+ NSPoint viewLocal = [self convertPoint:[theEvent locationInWindow]
+ fromView:[[self window] contentView]];
+ if (NSPointInRect(viewLocal, [self bounds])) {
+ [self performClick:self];
+ } else {
+ [[self cell] setHighlighted:NO];
+ }
+}
+
+// Mimic "begin a click" operation visually. Do NOT follow through
+// with normal button event handling.
+- (void)mouseDown:(NSEvent*)theEvent {
+ [[self cell] setHighlighted:YES];
+ initialMouseDownLocation_ = [theEvent locationInWindow];
+}
+
+// Return YES if we have crossed a threshold of movement after
+// mouse-down when we should begin a drag. Else NO.
+- (BOOL)hasCrossedDragThreshold:(NSEvent*)theEvent {
+ NSPoint currentLocation = [theEvent locationInWindow];
+ if ((abs(currentLocation.x - initialMouseDownLocation_.x) >
+ kWebDragStartHysteresisX) ||
+ (abs(currentLocation.y - initialMouseDownLocation_.y) >
+ kWebDragStartHysteresisY)) {
+ return YES;
+ } else {
+ return NO;
+ }
+}
+
+- (void)mouseDragged:(NSEvent*)theEvent {
+ if (beingDragged_)
+ [super mouseDragged:theEvent];
+ else {
+ if (draggable_ && [self hasCrossedDragThreshold:theEvent]) {
+ [self beginDrag:theEvent];
+ }
+ }
+}
+
+@end
diff --git a/chrome/browser/cocoa/bookmark_button_unittest.mm b/chrome/browser/cocoa/bookmark_button_unittest.mm
new file mode 100644
index 0000000..fcdf528
--- /dev/null
+++ b/chrome/browser/cocoa/bookmark_button_unittest.mm
@@ -0,0 +1,49 @@
+// Copyright (c) 2009 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.
+
+#include "base/scoped_nsobject.h"
+#import "chrome/browser/cocoa/bookmark_button.h"
+#import "chrome/browser/cocoa/cocoa_test_helper.h"
+#include "testing/gtest/include/gtest/gtest.h"
+#include "testing/platform_test.h"
+
+class BookmarkButtonTest : public CocoaTest {
+ public:
+};
+
+NSEvent* Event(const NSPoint point, const NSEventType type) {
+ static NSUInteger eventNumber = 0; // thx shess
+ return [NSEvent mouseEventWithType:type
+ location:point
+ modifierFlags:0
+ timestamp:0
+ windowNumber:183 // picked out of thin air.
+ context:nil
+ eventNumber:eventNumber++
+ clickCount:1
+ pressure:0.0];
+}
+
+// Make sure the basic case of "click" still works.
+TEST_F(BookmarkButtonTest, DownUp) {
+ scoped_nsobject<NSMutableArray> array;
+ array.reset([[NSMutableArray alloc] init]);
+ [array addObject:@"foo"];
+ [array addObject:@"bar"];
+
+ scoped_nsobject<BookmarkButton> button;
+ button.reset([[BookmarkButton alloc] initWithFrame:NSMakeRect(0,0,500,500)]);
+
+ [button setTarget:array.get()];
+ [button setAction:@selector(removeAllObjects)];
+ EXPECT_FALSE([[button cell] isHighlighted]);
+
+ NSEvent* downEvent(Event(NSMakePoint(10,10), NSLeftMouseDown));
+ NSEvent* upEvent(Event(NSMakePoint(10,10), NSLeftMouseDown));
+ [button mouseDown:downEvent];
+ EXPECT_TRUE([[button cell] isHighlighted]);
+ [button mouseUp:upEvent];
+ EXPECT_FALSE([[button cell] isHighlighted]);
+ EXPECT_FALSE([array count]); // confirms target/action fired
+}
diff --git a/chrome/chrome.gyp b/chrome/chrome.gyp
index c4ac3f1..969c303 100755
--- a/chrome/chrome.gyp
+++ b/chrome/chrome.gyp
@@ -1083,6 +1083,8 @@
'browser/cocoa/bookmark_bar_toolbar_view.mm',
'browser/cocoa/bookmark_bubble_controller.h',
'browser/cocoa/bookmark_bubble_controller.mm',
+ 'browser/cocoa/bookmark_button.h',
+ 'browser/cocoa/bookmark_button.mm',
'browser/cocoa/bookmark_button_cell.h',
'browser/cocoa/bookmark_button_cell.mm',
'browser/cocoa/bookmark_editor_base_controller.h',
@@ -3280,7 +3282,7 @@
'../third_party/icu/icu.gyp:icui18n',
'../third_party/icu/icu.gyp:icuuc',
'../third_party/npapi/npapi.gyp:npapi',
- # TODO(yaar): Should only stop depending on webcore and depend
+ # TODO(yaar): Should only stop depending on webcore and depend
# only on webkit.
'../third_party/WebKit/WebCore/WebCore.gyp/WebCore.gyp:webcore',
'../third_party/WebKit/WebKit/chromium/WebKit.gyp:webkit',
@@ -4563,6 +4565,7 @@
'browser/cocoa/bookmark_bar_toolbar_view_unittest.mm',
'browser/cocoa/bookmark_bar_view_unittest.mm',
'browser/cocoa/bookmark_bubble_controller_unittest.mm',
+ 'browser/cocoa/bookmark_button_unittest.mm',
'browser/cocoa/bookmark_button_cell_unittest.mm',
'browser/cocoa/bookmark_editor_base_controller_unittest.mm',
'browser/cocoa/bookmark_editor_controller_unittest.mm',