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