1 // Copyright (c) 2009 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.
5 #import "chrome/browser/ui/cocoa/clickhold_button_cell.h"
7 #include "base/logging.h"
9 // Minimum and maximum click-hold timeout.
10 static const NSTimeInterval kMinTimeout = 0.0;
11 static const NSTimeInterval kMaxTimeout = 5.0;
13 // Drag distance threshold to activate click-hold; should be >= 0.
14 static const CGFloat kDragDistThreshold = 2.5;
16 // See |-resetToDefaults| (and header file) for other default values.
18 @interface ClickHoldButtonCell (Private)
19 - (void)resetToDefaults;
20 @end // @interface ClickHoldButtonCell (Private)
22 @implementation ClickHoldButtonCell
24 @synthesize enableClickHold = enableClickHold_;
25 @synthesize clickHoldTimeout = clickHoldTimeout_;
26 @synthesize trackOnlyInRect = trackOnlyInRect_;
27 @synthesize activateOnDrag = activateOnDrag_;
28 @synthesize clickHoldTarget = clickHoldTarget_;
29 @synthesize clickHoldAction = clickHoldAction_;
33 + (BOOL)prefersTrackingUntilMouseUp {
38 if ((self = [super init]))
39 [self resetToDefaults];
43 - (id)initWithCoder:(NSCoder*)decoder {
44 if ((self = [super initWithCoder:decoder]))
45 [self resetToDefaults];
49 - (id)initImageCell:(NSImage*)image {
50 if ((self = [super initImageCell:image]))
51 [self resetToDefaults];
55 - (id)initTextCell:(NSString*)string {
56 if ((self = [super initTextCell:string]))
57 [self resetToDefaults];
61 - (BOOL)startTrackingAt:(NSPoint)startPoint
62 inView:(NSView*)controlView {
63 return enableClickHold_ ? YES :
64 [super startTrackingAt:startPoint
68 - (BOOL)continueTracking:(NSPoint)lastPoint
69 at:(NSPoint)currentPoint
70 inView:(NSView*)controlView {
71 return enableClickHold_ ? YES :
72 [super continueTracking:lastPoint
77 - (BOOL)trackMouse:(NSEvent*)originalEvent
78 inRect:(NSRect)cellFrame
79 ofView:(NSView*)controlView
80 untilMouseUp:(BOOL)untilMouseUp {
81 if (!enableClickHold_) {
82 return [super trackMouse:originalEvent
85 untilMouseUp:untilMouseUp];
88 // If doing click-hold, track the mouse ourselves.
89 NSPoint currPoint = [controlView convertPoint:[originalEvent locationInWindow]
91 NSPoint lastPoint = currPoint;
92 NSPoint firstPoint = currPoint;
93 NSTimeInterval timeout =
94 MAX(MIN(clickHoldTimeout_, kMaxTimeout), kMinTimeout);
95 NSDate* clickHoldBailTime = [NSDate dateWithTimeIntervalSinceNow:timeout];
97 if (![self startTrackingAt:currPoint inView:controlView])
101 kContinueTrack, kStopClickHold, kStopMouseUp, kStopLeftRect, kStopNoContinue
102 } state = kContinueTrack;
104 NSEvent* event = [NSApp nextEventMatchingMask:(NSLeftMouseDraggedMask |
106 untilDate:clickHoldBailTime
107 inMode:NSEventTrackingRunLoopMode
109 currPoint = [controlView convertPoint:[event locationInWindow]
114 state = kStopClickHold;
116 // Drag? (If distance meets threshold.)
117 } else if (activateOnDrag_ && ([event type] == NSLeftMouseDragged)) {
118 CGFloat dx = currPoint.x - firstPoint.x;
119 CGFloat dy = currPoint.y - firstPoint.y;
120 if ((dx*dx + dy*dy) >= (kDragDistThreshold*kDragDistThreshold))
121 state = kStopClickHold;
124 } else if ([event type] == NSLeftMouseUp) {
125 state = kStopMouseUp;
127 // Stop tracking if mouse left frame rectangle (if requested to do so).
128 } else if (trackOnlyInRect_ && ![controlView mouse:currPoint
130 state = kStopLeftRect;
132 // Stop tracking if instructed to.
133 } else if (![self continueTracking:lastPoint
135 inView:controlView]) {
136 state = kStopNoContinue;
139 lastPoint = currPoint;
140 } while (state == kContinueTrack);
142 [self stopTracking:lastPoint
149 if (clickHoldAction_) {
150 [static_cast<NSControl*>(controlView) sendAction:clickHoldAction_
151 to:clickHoldTarget_];
157 [static_cast<NSControl*>(controlView) sendAction:[self action]
163 case kStopNoContinue:
167 NOTREACHED() << "Unknown terminating state!";
173 @end // @implementation ClickHoldButtonCell
175 @implementation ClickHoldButtonCell (Private)
177 // Resets various members to defaults indicated in the header file. (Those
178 // without indicated defaults are *not* touched.) Please keep the values below
179 // in sync with the header file, and please be aware of side-effects on code
180 // which relies on the "published" defaults.
181 - (void)resetToDefaults {
182 [self setEnableClickHold:NO];
183 [self setClickHoldTimeout:0.25];
184 [self setTrackOnlyInRect:NO];
185 [self setActivateOnDrag:YES];
188 @end // @implementation ClickHoldButtonCell (Private)