// 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]; NSEvent* fakeEvent = [NSEvent enterExitEventWithType:NSMouseExited location:NSZeroPoint modifierFlags:0 timestamp:0 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]; NSEvent* fakeEvent = [NSEvent enterExitEventWithType:NSMouseEntered location:NSZeroPoint modifierFlags:0 timestamp:0 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