1 // Copyright 2013 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_button_cell.h" 6 7 @interface HyperlinkButtonCell () 8 - (void)customizeButtonCell; 9 @end 10 11 @implementation HyperlinkButtonCell 12 13 @dynamic textColor; 14 @synthesize underlineOnHover = underlineOnHover_; 15 @synthesize shouldUnderline = shouldUnderline_; 16 17 + (NSColor*)defaultTextColor { 18 return [NSColor blueColor]; 19 } 20 21 + (NSButton*)buttonWithString:(NSString*)string { 22 NSButton* button = [[[NSButton alloc] initWithFrame:NSZeroRect] autorelease]; 23 base::scoped_nsobject<HyperlinkButtonCell> cell( 24 [[HyperlinkButtonCell alloc] initTextCell:string]); 25 [cell setAlignment:NSLeftTextAlignment]; 26 [button setCell:cell.get()]; 27 [button setBezelStyle:NSRegularSquareBezelStyle]; 28 return button; 29 } 30 31 // Designated initializer. 32 - (id)init { 33 if ((self = [super init])) { 34 [self customizeButtonCell]; 35 } 36 return self; 37 } 38 39 // Initializer called when the cell is loaded from the NIB. 40 - (id)initWithCoder:(NSCoder*)aDecoder { 41 if ((self = [super initWithCoder:aDecoder])) { 42 [self customizeButtonCell]; 43 } 44 return self; 45 } 46 47 // Initializer for code-based creation. 48 - (id)initTextCell:(NSString*)title { 49 if ((self = [super initTextCell:title])) { 50 [self customizeButtonCell]; 51 } 52 return self; 53 } 54 55 // Because an NSButtonCell has multiple initializers, this method performs the 56 // common cell customization code. 57 - (void)customizeButtonCell { 58 [self setBordered:NO]; 59 [self setTextColor:[HyperlinkButtonCell defaultTextColor]]; 60 [self setShouldUnderline:YES]; 61 62 CGFloat fontSize = [NSFont systemFontSizeForControlSize:[self controlSize]]; 63 NSFont* font = [NSFont controlContentFontOfSize:fontSize]; 64 [self setFont:font]; 65 66 // Do not change button appearance when we are pushed. 67 [self setHighlightsBy:NSNoCellMask]; 68 69 // We need to set this so that we can override |-mouseEntered:| and 70 // |-mouseExited:| to change the cursor style on hover states. 71 [self setShowsBorderOnlyWhileMouseInside:YES]; 72 } 73 74 - (void)setControlSize:(NSControlSize)size { 75 [super setControlSize:size]; 76 [self customizeButtonCell]; // recompute |font|. 77 } 78 79 // Creates the NSDictionary of attributes for the attributed string. 80 - (NSDictionary*)linkAttributes { 81 NSUInteger underlineMask = NSNoUnderlineStyle; 82 if (shouldUnderline_ && 83 (!underlineOnHover_ || (mouseIsInside_ && [self isEnabled]))) 84 underlineMask = NSUnderlinePatternSolid | NSUnderlineStyleSingle; 85 86 base::scoped_nsobject<NSMutableParagraphStyle> paragraphStyle( 87 [[NSParagraphStyle defaultParagraphStyle] mutableCopy]); 88 [paragraphStyle setAlignment:[self alignment]]; 89 [paragraphStyle setLineBreakMode:[self lineBreakMode]]; 90 91 return [NSDictionary dictionaryWithObjectsAndKeys: 92 [self textColor], NSForegroundColorAttributeName, 93 [NSNumber numberWithInt:underlineMask], NSUnderlineStyleAttributeName, 94 [self font], NSFontAttributeName, 95 [NSCursor pointingHandCursor], NSCursorAttributeName, 96 paragraphStyle.get(), NSParagraphStyleAttributeName, 97 nil 98 ]; 99 } 100 101 // Override the drawing for the cell so that the custom style attributes 102 // can always be applied and so that ellipses will appear when appropriate. 103 - (NSRect)drawTitle:(NSAttributedString*)title 104 withFrame:(NSRect)frame 105 inView:(NSView*)controlView { 106 NSDictionary* linkAttributes = [self linkAttributes]; 107 NSString* plainTitle = [title string]; 108 [plainTitle drawWithRect:frame 109 options:(NSStringDrawingUsesLineFragmentOrigin | 110 NSStringDrawingTruncatesLastVisibleLine) 111 attributes:linkAttributes]; 112 return frame; 113 } 114 115 // Override the default behavior to draw the border. Instead, change the cursor. 116 - (void)mouseEntered:(NSEvent*)event { 117 mouseIsInside_ = YES; 118 if ([self isEnabled]) 119 [[NSCursor pointingHandCursor] push]; 120 else 121 [[NSCursor currentCursor] push]; 122 if (underlineOnHover_) 123 [[self controlView] setNeedsDisplay:YES]; 124 } 125 126 - (void)mouseExited:(NSEvent*)event { 127 mouseIsInside_ = NO; 128 [NSCursor pop]; 129 if (underlineOnHover_) 130 [[self controlView] setNeedsDisplay:YES]; 131 } 132 133 // Setters and getters. 134 - (NSColor*)textColor { 135 if ([self isEnabled]) 136 return textColor_.get(); 137 else 138 return [NSColor disabledControlTextColor]; 139 } 140 141 - (void)setTextColor:(NSColor*)color { 142 textColor_.reset([color retain]); 143 } 144 145 // Override so that |-sizeToFit| works better with this type of cell. 146 - (NSSize)cellSize { 147 NSSize size = [super cellSize]; 148 size.width += 2; 149 return size; 150 } 151 152 @end 153