1 // Copyright (c) 2011 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. 4 5 #import "ui/base/cocoa/controls/hyperlink_text_view.h" 6 7 #include "base/mac/scoped_nsobject.h" 8 #include "ui/base/cocoa/nsview_additions.h" 9 10 // The baseline shift for text in the NSTextView. 11 const float kTextBaselineShift = -1.0; 12 13 @interface HyperlinkTextView(Private) 14 // Initialize the NSTextView properties for this subclass. 15 - (void)configureTextView; 16 17 // Change the current IBeamCursor to an arrowCursor. 18 - (void)fixupCursor; 19 @end 20 21 @implementation HyperlinkTextView 22 23 @synthesize drawsBackgroundUsingSuperview = drawsBackgroundUsingSuperview_; 24 25 - (id)initWithCoder:(NSCoder*)decoder { 26 if ((self = [super initWithCoder:decoder])) 27 [self configureTextView]; 28 return self; 29 } 30 31 - (id)initWithFrame:(NSRect)frameRect { 32 if ((self = [super initWithFrame:frameRect])) 33 [self configureTextView]; 34 return self; 35 } 36 37 - (BOOL)acceptsFirstResponder { 38 return !refusesFirstResponder_; 39 } 40 41 - (void)drawViewBackgroundInRect:(NSRect)rect { 42 if (drawsBackgroundUsingSuperview_) 43 [self cr_drawUsingAncestor:[self superview] inRect:rect]; 44 else 45 [super drawViewBackgroundInRect:rect]; 46 } 47 48 // Never draw the insertion point (otherwise, it shows up without any user 49 // action if full keyboard accessibility is enabled). 50 - (BOOL)shouldDrawInsertionPoint { 51 return NO; 52 } 53 54 - (NSRange)selectionRangeForProposedRange:(NSRange)proposedSelRange 55 granularity:(NSSelectionGranularity)granularity { 56 // Do not allow selections. 57 return NSMakeRange(0, 0); 58 } 59 60 // Convince NSTextView to not show an I-Beam cursor when the cursor is over the 61 // text view but not over actual text. 62 // 63 // http://www.mail-archive.com/cocoa-dev@lists.apple.com/msg10791.html 64 // "NSTextView sets the cursor over itself dynamically, based on considerations 65 // including the text under the cursor. It does so in -mouseEntered:, 66 // -mouseMoved:, and -cursorUpdate:, so those would be points to consider 67 // overriding." 68 - (void)mouseMoved:(NSEvent*)e { 69 [super mouseMoved:e]; 70 [self fixupCursor]; 71 } 72 73 - (void)mouseEntered:(NSEvent*)e { 74 [super mouseEntered:e]; 75 [self fixupCursor]; 76 } 77 78 - (void)cursorUpdate:(NSEvent*)e { 79 [super cursorUpdate:e]; 80 [self fixupCursor]; 81 } 82 83 - (void)configureTextView { 84 [self setEditable:NO]; 85 [self setDrawsBackground:NO]; 86 [self setHorizontallyResizable:NO]; 87 [self setVerticallyResizable:NO]; 88 89 // When text is rendered, linkTextAttributes override anything set via 90 // addAttributes for text that has NSLinkAttributeName. Set to nil to allow 91 // custom attributes to take precedence. 92 [self setLinkTextAttributes:nil]; 93 [self setDisplaysLinkToolTips:NO]; 94 95 refusesFirstResponder_ = NO; 96 drawsBackgroundUsingSuperview_ = NO; 97 } 98 99 - (void)fixupCursor { 100 if ([[NSCursor currentCursor] isEqual:[NSCursor IBeamCursor]]) 101 [[NSCursor arrowCursor] set]; 102 } 103 104 - (void)setMessageAndLink:(NSString*)message 105 withLink:(NSString*)link 106 atOffset:(NSUInteger)linkOffset 107 font:(NSFont*)font 108 messageColor:(NSColor*)messageColor 109 linkColor:(NSColor*)linkColor { 110 NSMutableString* finalMessage = [NSMutableString stringWithString:message]; 111 [finalMessage insertString:link atIndex:linkOffset]; 112 [self setMessage:finalMessage withFont:font messageColor:messageColor]; 113 if ([link length] != 0) { 114 [self addLinkRange:NSMakeRange(linkOffset, [link length]) 115 withName:@"" 116 linkColor:linkColor]; 117 } 118 } 119 120 - (void)setMessage:(NSString*)message 121 withFont:(NSFont*)font 122 messageColor:(NSColor*)messageColor { 123 // Create an attributes dictionary for the message and link. 124 NSDictionary* attributes = @{ 125 NSForegroundColorAttributeName : messageColor, 126 NSCursorAttributeName : [NSCursor arrowCursor], 127 NSFontAttributeName : font, 128 NSBaselineOffsetAttributeName : @(kTextBaselineShift) 129 }; 130 131 // Create the attributed string for the message. 132 base::scoped_nsobject<NSAttributedString> attributedMessage( 133 [[NSMutableAttributedString alloc] initWithString:message 134 attributes:attributes]); 135 136 // Update the text view with the new text. 137 [[self textStorage] setAttributedString:attributedMessage]; 138 } 139 140 - (void)addLinkRange:(NSRange)range 141 withName:(id)name 142 linkColor:(NSColor*)linkColor { 143 NSDictionary* attributes = @{ 144 NSForegroundColorAttributeName : linkColor, 145 NSUnderlineStyleAttributeName : @(YES), 146 NSCursorAttributeName : [NSCursor pointingHandCursor], 147 NSLinkAttributeName : name, 148 NSUnderlineStyleAttributeName : @(NSSingleUnderlineStyle) 149 }; 150 151 [[self textStorage] addAttributes:attributes range:range]; 152 } 153 154 - (void)setRefusesFirstResponder:(BOOL)refusesFirstResponder { 155 refusesFirstResponder_ = refusesFirstResponder; 156 } 157 158 @end 159