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 - (id)copyWithZone:(NSZone*)zone { 56 NSColor* color = textColor_.release(); 57 HyperlinkButtonCell* cell = [super copyWithZone:zone]; 58 cell->textColor_.reset([color copy]); 59 textColor_.reset(color); 60 return cell; 61 } 62 63 // Because an NSButtonCell has multiple initializers, this method performs the 64 // common cell customization code. 65 - (void)customizeButtonCell { 66 [self setBordered:NO]; 67 [self setTextColor:[HyperlinkButtonCell defaultTextColor]]; 68 [self setShouldUnderline:YES]; 69 70 CGFloat fontSize = [NSFont systemFontSizeForControlSize:[self controlSize]]; 71 NSFont* font = [NSFont controlContentFontOfSize:fontSize]; 72 [self setFont:font]; 73 74 // Do not change button appearance when we are pushed. 75 [self setHighlightsBy:NSNoCellMask]; 76 77 // We need to set this so that we can override |-mouseEntered:| and 78 // |-mouseExited:| to change the cursor style on hover states. 79 [self setShowsBorderOnlyWhileMouseInside:YES]; 80 } 81 82 - (void)setControlSize:(NSControlSize)size { 83 [super setControlSize:size]; 84 [self customizeButtonCell]; // recompute |font|. 85 } 86 87 // Creates the NSDictionary of attributes for the attributed string. 88 - (NSDictionary*)linkAttributes { 89 NSUInteger underlineMask = NSNoUnderlineStyle; 90 if (shouldUnderline_ && 91 (!underlineOnHover_ || (mouseIsInside_ && [self isEnabled]))) 92 underlineMask = NSUnderlinePatternSolid | NSUnderlineStyleSingle; 93 94 base::scoped_nsobject<NSMutableParagraphStyle> paragraphStyle( 95 [[NSParagraphStyle defaultParagraphStyle] mutableCopy]); 96 [paragraphStyle setAlignment:[self alignment]]; 97 [paragraphStyle setLineBreakMode:[self lineBreakMode]]; 98 99 return [NSDictionary dictionaryWithObjectsAndKeys: 100 [self textColor], NSForegroundColorAttributeName, 101 [NSNumber numberWithInt:underlineMask], NSUnderlineStyleAttributeName, 102 [self font], NSFontAttributeName, 103 [NSCursor pointingHandCursor], NSCursorAttributeName, 104 paragraphStyle.get(), NSParagraphStyleAttributeName, 105 nil 106 ]; 107 } 108 109 // Override the drawing for the cell so that the custom style attributes 110 // can always be applied and so that ellipses will appear when appropriate. 111 - (NSRect)drawTitle:(NSAttributedString*)title 112 withFrame:(NSRect)frame 113 inView:(NSView*)controlView { 114 NSDictionary* linkAttributes = [self linkAttributes]; 115 NSString* plainTitle = [title string]; 116 [plainTitle drawWithRect:frame 117 options:(NSStringDrawingUsesLineFragmentOrigin | 118 NSStringDrawingTruncatesLastVisibleLine) 119 attributes:linkAttributes]; 120 return frame; 121 } 122 123 // Override the default behavior to draw the border. Instead, change the cursor. 124 - (void)mouseEntered:(NSEvent*)event { 125 mouseIsInside_ = YES; 126 if ([self isEnabled]) 127 [[NSCursor pointingHandCursor] push]; 128 else 129 [[NSCursor currentCursor] push]; 130 if (underlineOnHover_) 131 [[self controlView] setNeedsDisplay:YES]; 132 } 133 134 - (void)mouseExited:(NSEvent*)event { 135 mouseIsInside_ = NO; 136 [NSCursor pop]; 137 if (underlineOnHover_) 138 [[self controlView] setNeedsDisplay:YES]; 139 } 140 141 // Setters and getters. 142 - (NSColor*)textColor { 143 if ([self isEnabled]) 144 return textColor_.get(); 145 else 146 return [NSColor disabledControlTextColor]; 147 } 148 149 - (void)setTextColor:(NSColor*)color { 150 textColor_.reset([color retain]); 151 } 152 153 // Override so that |-sizeToFit| works better with this type of cell. 154 - (NSSize)cellSize { 155 NSSize size = [super cellSize]; 156 size.width += 2; 157 return size; 158 } 159 160 @end 161