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.
5 #import "chrome/browser/ui/cocoa/hyperlink_text_view.h"
7 #include "base/mac/scoped_nsobject.h"
9 // The baseline shift for text in the NSTextView.
10 const float kTextBaselineShift = -1.0;
12 @interface HyperlinkTextView(Private)
13 // Initialize the NSTextView properties for this subclass.
14 - (void)configureTextView;
16 // Change the current IBeamCursor to an arrowCursor.
20 @implementation HyperlinkTextView
22 - (id)initWithCoder:(NSCoder*)decoder {
23 if ((self = [super initWithCoder:decoder]))
24 [self configureTextView];
28 - (id)initWithFrame:(NSRect)frameRect {
29 if ((self = [super initWithFrame:frameRect]))
30 [self configureTextView];
34 - (BOOL)acceptsFirstResponder {
35 return acceptsFirstResponder_;
38 // Never draw the insertion point (otherwise, it shows up without any user
39 // action if full keyboard accessibility is enabled).
40 - (BOOL)shouldDrawInsertionPoint {
44 - (NSRange)selectionRangeForProposedRange:(NSRange)proposedSelRange
45 granularity:(NSSelectionGranularity)granularity {
46 // Do not allow selections.
47 return NSMakeRange(0, 0);
50 // Convince NSTextView to not show an I-Beam cursor when the cursor is over the
51 // text view but not over actual text.
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
58 - (void)mouseMoved:(NSEvent*)e {
63 - (void)mouseEntered:(NSEvent*)e {
64 [super mouseEntered:e];
68 - (void)cursorUpdate:(NSEvent*)e {
69 [super cursorUpdate:e];
73 - (void)configureTextView {
74 [self setEditable:NO];
75 [self setDrawsBackground:NO];
76 [self setHorizontallyResizable:NO];
77 [self setVerticallyResizable:NO];
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];
85 acceptsFirstResponder_ = YES;
89 if ([[NSCursor currentCursor] isEqual:[NSCursor IBeamCursor]])
90 [[NSCursor arrowCursor] set];
93 - (void)setMessageAndLink:(NSString*)message
94 withLink:(NSString*)link
95 atOffset:(NSUInteger)linkOffset
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])
105 linkColor:linkColor];
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)
120 // Create the attributed string for the message.
121 base::scoped_nsobject<NSAttributedString> attributedMessage(
122 [[NSMutableAttributedString alloc] initWithString:message
123 attributes:attributes]);
125 // Update the text view with the new text.
126 [[self textStorage] setAttributedString:attributedMessage];
129 - (void)addLinkRange:(NSRange)range
131 linkColor:(NSColor*)linkColor {
132 NSDictionary* attributes = @{
133 NSForegroundColorAttributeName : linkColor,
134 NSUnderlineStyleAttributeName : @(YES),
135 NSCursorAttributeName : [NSCursor pointingHandCursor],
136 NSLinkAttributeName : name,
137 NSUnderlineStyleAttributeName : @(NSSingleUnderlineStyle)
140 [[self textStorage] addAttributes:attributes range:range];
143 - (void)setAcceptsFirstResponder:(BOOL)acceptsFirstResponder {
144 acceptsFirstResponder_ = acceptsFirstResponder;