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