| // Copyright 2015 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 "ui/base/cocoa/tool_tip_base_view.h" |
| |
| #include "base/logging.h" |
| |
| // Below is the nasty tooltip stuff -- copied from WebKit's WebHTMLView.mm |
| // with minor modifications for code style and commenting. |
| // |
| // The 'public' interface is -setToolTipAtMousePoint:. This differs from |
| // -setToolTip: in that the updated tooltip takes effect immediately, |
| // without the user's having to move the mouse out of and back into the view. |
| // |
| // Unfortunately, doing this requires sending fake mouseEnter/Exit events to |
| // the view, which in turn requires overriding some internal tracking-rect |
| // methods (to keep track of its owner & userdata, which need to be filled out |
| // in the fake events.) --snej 7/6/09 |
| |
| |
| /* |
| * Copyright (C) 2005, 2006, 2007, 2008, 2009 Apple Inc. All rights reserved. |
| * (C) 2006, 2007 Graham Dennis (graham.dennis@gmail.com) |
| * |
| * Redistribution and use in source and binary forms, with or without |
| * modification, are permitted provided that the following conditions |
| * are met: |
| * |
| * 1. Redistributions of source code must retain the above copyright |
| * notice, this list of conditions and the following disclaimer. |
| * 2. Redistributions in binary form must reproduce the above copyright |
| * notice, this list of conditions and the following disclaimer in the |
| * documentation and/or other materials provided with the distribution. |
| * 3. Neither the name of Apple Computer, Inc. ("Apple") nor the names of |
| * its contributors may be used to endorse or promote products derived |
| * from this software without specific prior written permission. |
| * |
| * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY |
| * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED |
| * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE |
| * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY |
| * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES |
| * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; |
| * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND |
| * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
| * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF |
| * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
| */ |
| |
| // Any non-zero value will do, but using something recognizable might help us |
| // debug some day. |
| const NSTrackingRectTag kTrackingRectTag = 0xBADFACE; |
| |
| @implementation ToolTipBaseView |
| |
| // Override of a public NSView method, replacing the inherited functionality. |
| // See above for rationale. |
| - (NSTrackingRectTag)addTrackingRect:(NSRect)rect |
| owner:(id)owner |
| userData:(void *)data |
| assumeInside:(BOOL)assumeInside { |
| DCHECK(trackingRectOwner_ == nil); |
| trackingRectOwner_ = owner; |
| trackingRectUserData_ = data; |
| return kTrackingRectTag; |
| } |
| |
| // Override of (apparently) a private NSView method(!) See above for rationale. |
| - (NSTrackingRectTag)_addTrackingRect:(NSRect)rect |
| owner:(id)owner |
| userData:(void *)data |
| assumeInside:(BOOL)assumeInside |
| useTrackingNum:(int)tag { |
| DCHECK(tag == 0 || tag == kTrackingRectTag); |
| DCHECK(trackingRectOwner_ == nil); |
| trackingRectOwner_ = owner; |
| trackingRectUserData_ = data; |
| return kTrackingRectTag; |
| } |
| |
| // Override of (apparently) a private NSView method(!) See above for rationale. |
| - (void)_addTrackingRects:(NSRect *)rects |
| owner:(id)owner |
| userDataList:(void **)userDataList |
| assumeInsideList:(BOOL *)assumeInsideList |
| trackingNums:(NSTrackingRectTag *)trackingNums |
| count:(int)count { |
| DCHECK(count == 1); |
| DCHECK(trackingNums[0] == 0 || trackingNums[0] == kTrackingRectTag); |
| DCHECK(trackingRectOwner_ == nil); |
| trackingRectOwner_ = owner; |
| trackingRectUserData_ = userDataList[0]; |
| trackingNums[0] = kTrackingRectTag; |
| } |
| |
| // Override of a public NSView method, replacing the inherited functionality. |
| // See above for rationale. |
| - (void)removeTrackingRect:(NSTrackingRectTag)tag { |
| if (tag == 0) |
| return; |
| |
| if (tag == kTrackingRectTag) { |
| trackingRectOwner_ = nil; |
| return; |
| } |
| |
| if (tag == lastToolTipTag_) { |
| [super removeTrackingRect:tag]; |
| lastToolTipTag_ = 0; |
| return; |
| } |
| |
| // If any other tracking rect is being removed, we don't know how it was |
| // created and it's possible there's a leak involved (see Radar 3500217). |
| NOTREACHED(); |
| } |
| |
| // Override of (apparently) a private NSView method(!) |
| - (void)_removeTrackingRects:(NSTrackingRectTag *)tags count:(int)count { |
| for (int i = 0; i < count; ++i) { |
| int tag = tags[i]; |
| if (tag == 0) |
| continue; |
| DCHECK(tag == kTrackingRectTag); |
| trackingRectOwner_ = nil; |
| } |
| } |
| |
| // Sends a fake NSMouseExited event to the view for its current tracking rect. |
| - (void)_sendToolTipMouseExited { |
| // Nothing matters except window, trackingNumber, and userData. |
| int windowNumber = [[self window] windowNumber]; |
| NSTimeInterval eventTime = [[NSApp currentEvent] timestamp]; |
| NSEvent* fakeEvent = [NSEvent enterExitEventWithType:NSMouseExited |
| location:NSZeroPoint |
| modifierFlags:0 |
| timestamp:eventTime |
| windowNumber:windowNumber |
| context:NULL |
| eventNumber:0 |
| trackingNumber:kTrackingRectTag |
| userData:trackingRectUserData_]; |
| [trackingRectOwner_ mouseExited:fakeEvent]; |
| } |
| |
| // Sends a fake NSMouseEntered event to the view for its current tracking rect. |
| - (void)_sendToolTipMouseEntered { |
| // Nothing matters except window, trackingNumber, and userData. |
| int windowNumber = [[self window] windowNumber]; |
| NSTimeInterval eventTime = [[NSApp currentEvent] timestamp]; |
| NSEvent* fakeEvent = [NSEvent enterExitEventWithType:NSMouseEntered |
| location:NSZeroPoint |
| modifierFlags:0 |
| timestamp:eventTime |
| windowNumber:windowNumber |
| context:NULL |
| eventNumber:0 |
| trackingNumber:kTrackingRectTag |
| userData:trackingRectUserData_]; |
| [trackingRectOwner_ mouseEntered:fakeEvent]; |
| } |
| |
| // Sets the view's current tooltip, to be displayed at the current mouse |
| // location. (This does not make the tooltip appear -- as usual, it only |
| // appears after a delay.) Pass null to remove the tooltip. |
| - (void)setToolTipAtMousePoint:(NSString *)string { |
| NSString *toolTip = [string length] == 0 ? nil : string; |
| if ((toolTip && toolTip_ && [toolTip isEqualToString:toolTip_]) || |
| (!toolTip && !toolTip_)) { |
| return; |
| } |
| |
| if (toolTip_) { |
| [self _sendToolTipMouseExited]; |
| } |
| |
| toolTip_.reset([toolTip copy]); |
| |
| if (toolTip) { |
| // See radar 3500217 for why we remove all tooltips |
| // rather than just the single one we created. |
| [self removeAllToolTips]; |
| NSRect wideOpenRect = NSMakeRect(-100000, -100000, 200000, 200000); |
| lastToolTipTag_ = [self addToolTipRect:wideOpenRect |
| owner:self |
| userData:NULL]; |
| [self _sendToolTipMouseEntered]; |
| } |
| } |
| |
| // NSView calls this to get the text when displaying the tooltip. |
| - (NSString *)view:(NSView *)view |
| stringForToolTip:(NSToolTipTag)tag |
| point:(NSPoint)point |
| userData:(void *)data { |
| return [[toolTip_ copy] autorelease]; |
| } |
| |
| @end |