Home | History | Annotate | Download | only in cocoa
      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