1/** <title>NSSecureTextField</title> 2 3 <abstract>Secure Text field control class for hidden text entry</abstract> 4 5 Copyright (C) 1999 Free Software Foundation, Inc. 6 7 Author: Gregory John Casamento <greg.casamento@gmail.com> 8 Date: 2000 9 10 Author: Nicola Pero <nicola@brainstorm.co.uk> 11 Date: October 2002 12 13 This file is part of the GNUstep GUI Library. 14 15 This library is free software; you can redistribute it and/or 16 modify it under the terms of the GNU Lesser General Public 17 License as published by the Free Software Foundation; either 18 version 2 of the License, or (at your option) any later version. 19 20 This library is distributed in the hope that it will be useful, 21 but WITHOUT ANY WARRANTY; without even the implied warranty of 22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU 23 Lesser General Public License for more details. 24 25 You should have received a copy of the GNU Lesser General Public 26 License along with this library; see the file COPYING.LIB. 27 If not, see <http://www.gnu.org/licenses/> or write to the 28 Free Software Foundation, 51 Franklin Street, Fifth Floor, 29 Boston, MA 02110-1301, USA. 30*/ 31 32#import "config.h" 33#import <Foundation/NSException.h> 34 35#import "AppKit/NSAttributedString.h" 36#import "AppKit/NSEvent.h" 37#import "AppKit/NSFont.h" 38#import "AppKit/NSImage.h" 39#import "AppKit/NSGlyphGenerator.h" 40#import "AppKit/NSLayoutManager.h" 41#import "AppKit/NSSecureTextField.h" 42#import "AppKit/NSStringDrawing.h" 43#import "AppKit/NSTextContainer.h" 44#import "AppKit/NSTextView.h" 45#import "AppKit/NSWindow.h" 46#import "GNUstepGUI/GSFontInfo.h" 47 48// the Unicode code point for a bullet 49#define BULLET 0x2022 50 51/* 'Secure' subclasses */ 52@interface NSSecureTextView : NSTextView 53{ 54} 55- (void) setEchosBullets:(BOOL)flag; 56- (BOOL) echosBullets; 57@end 58 59@interface GSSimpleSecureGlyphGenerator : NSGlyphGenerator 60@end 61 62@interface NSGlyphGenerator (Private) 63- (NSFont *) fontForCharactersWithAttributes: (NSDictionary *)attributes; 64@end 65 66@interface GSSimpleSecureLayoutManager : NSLayoutManager 67{ 68 BOOL _echosBullets; 69} 70- (void) setEchosBullets:(BOOL)flag; 71- (BOOL) echosBullets; 72@end 73 74@implementation NSSecureTextField 75 76+ (void) initialize 77{ 78 if (self == [NSSecureTextField class]) 79 { 80 [self setVersion:2]; 81 } 82} 83 84+ (Class) cellClass 85{ 86 /* Hard code here to make sure no other class is used. */ 87 return [NSSecureTextFieldCell class]; 88} 89 90+ (void) setCellClass: (Class)factoryId 91{ 92 /* Ward off interlopers with a stern message. */ 93 [NSException raise: NSInvalidArgumentException 94 format: @"NSSecureTextField only uses NSSecureTextFieldCells."]; 95} 96 97- (id) initWithCoder: (NSCoder *)coder 98{ 99 if((self = [super initWithCoder: coder]) != nil) 100 { 101 [self setEchosBullets: YES]; 102 } 103 return self; 104} 105 106- (id) initWithFrame:(NSRect)frameRect 107{ 108 self = [super initWithFrame: frameRect]; 109 if (nil == self) 110 return nil; 111 112 [self setEchosBullets: YES]; 113 114 return self; 115} 116 117- (void) setEchosBullets: (BOOL)flag 118{ 119 [_cell setEchosBullets: flag]; 120} 121 122- (BOOL) echosBullets 123{ 124 return [_cell echosBullets]; 125} 126@end /* NSSecureTextField */ 127 128@implementation NSSecureTextFieldCell 129 130+ (void)initialize 131{ 132 if (self == [NSSecureTextFieldCell class]) 133 { 134 [self setVersion:2]; 135 } 136} 137 138- (BOOL) echosBullets 139{ 140 return _echosBullets; 141} 142 143/* Functionality not implemented. */ 144- (void) setEchosBullets: (BOOL)flag 145{ 146 _echosBullets = flag; 147} 148 149/* Substitute a fixed-pitch font for correct bullet drawing */ 150- (void) setFont: (NSFont *) f 151{ 152 if (![f isFixedPitch]) 153 { 154 f = [NSFont userFixedPitchFontOfSize: [f pointSize]]; 155 } 156 157 [super setFont: f]; 158} 159 160- (NSAttributedString *)_replacementAttributedString 161{ 162 NSDictionary *attributes; 163 NSMutableString *string; 164 unsigned int length; 165 unsigned int i; 166 unichar *buf; 167 168 length = [[self stringValue] length]; 169 buf = NSZoneMalloc (NSDefaultMallocZone (), length * sizeof (unichar)); 170 for (i = 0; i < length; i++) 171 { 172 buf[i] = BULLET; 173 } 174 175 string = [[NSMutableString alloc] 176 initWithCharactersNoCopy: buf length: length freeWhenDone: YES]; 177 AUTORELEASE (string); 178 179 attributes = [self _nonAutoreleasedTypingAttributes]; 180 return AUTORELEASE([[NSAttributedString alloc] initWithString: string 181 attributes: attributes]); 182} 183 184- (NSAttributedString*) _drawAttributedString 185{ 186 if (_echosBullets) 187 { 188 if (!_cell.is_disabled) 189 { 190 return [self _replacementAttributedString]; 191 } 192 else 193 { 194 NSAttributedString *attrStr = [self _replacementAttributedString]; 195 NSDictionary *attribs; 196 NSMutableDictionary *newAttribs; 197 198 attribs = [attrStr attributesAtIndex: 0 199 effectiveRange: NULL]; 200 newAttribs = [NSMutableDictionary 201 dictionaryWithDictionary: attribs]; 202 [newAttribs setObject: [NSColor disabledControlTextColor] 203 forKey: NSForegroundColorAttributeName]; 204 205 return AUTORELEASE([[NSAttributedString alloc] 206 initWithString: [attrStr string] 207 attributes: newAttribs]); 208 } 209 } 210 else 211 { 212 /* .. do nothing. */ 213 return nil; 214 } 215} 216 217- (NSText *) setUpFieldEditorAttributes: (NSText *)textObject 218{ 219 NSSecureTextView *secureView; 220 221 /* Replace the text object with a secure instance. It's not shared. */ 222 secureView = AUTORELEASE([[NSSecureTextView alloc] init]); 223 224 [secureView setEchosBullets: [self echosBullets]]; 225 return [super setUpFieldEditorAttributes: secureView]; 226} 227 228- (id) initWithCoder: (NSCoder *)decoder 229{ 230 self = [super initWithCoder: decoder]; 231 if (nil == self) 232 return nil; 233 234 if ([decoder allowsKeyedCoding]) 235 { 236 _echosBullets = [decoder decodeBoolForKey: @"GSEchoBullets"]; 237 } 238 else 239 { 240 [decoder decodeValueOfObjCType: @encode(BOOL) at: &_echosBullets]; 241 } 242 243 return self; 244} 245 246- (void) encodeWithCoder: (NSCoder *)coder 247{ 248 [super encodeWithCoder: coder]; 249 if([coder allowsKeyedCoding]) 250 { 251 [coder encodeBool: _echosBullets forKey: @"GSEchoBullets"]; 252 } 253 else 254 { 255 [coder encodeValueOfObjCType: @encode(BOOL) at: &_echosBullets]; 256 } 257} 258@end 259 260@implementation GSSimpleSecureGlyphGenerator 261- (void) generateGlyphsForGlyphStorage: (id <NSGlyphStorage>)storage 262 desiredNumberOfCharacters: (NSUInteger)num 263 glyphIndex: (NSUInteger*)glyph 264 characterIndex: (NSUInteger*)index 265{ 266 NSGlyph glyphs[num]; 267 NSGlyph gl; 268 NSAttributedString *attrstr = [storage attributedString]; 269 GSFontInfo *fi; 270 int i; 271 NSRange maxRange = NSMakeRange(*index, num); 272 NSRange curRange; 273 NSDictionary *attributes; 274 275 attributes = [attrstr attributesAtIndex: *index 276 longestEffectiveRange: &curRange 277 inRange: maxRange]; 278 fi = [[self fontForCharactersWithAttributes: attributes] fontInfo]; 279 if (!fi) 280 { 281 [NSException raise: NSGenericException 282 format: @"Glyph generation with no font."]; 283 return; 284 } 285 286 gl = [fi glyphForCharacter: BULLET]; 287 for (i = 0; i < num; i++) 288 { 289 glyphs[i] = gl; 290 } 291 292 [storage insertGlyphs: glyphs 293 length: num 294 forStartingGlyphAtIndex: *glyph 295 characterIndex: *index]; 296} 297 298@end 299 300@implementation GSSimpleSecureLayoutManager 301 302- (BOOL) echosBullets 303{ 304 return _echosBullets; 305} 306 307- (void) setEchosBullets: (BOOL)flag 308{ 309 _echosBullets = flag; 310} 311 312- (void) drawGlyphsForGlyphRange: (NSRange)glyphRange 313 atPoint: (NSPoint)containerOrigin 314{ 315 if ([self echosBullets]) 316 { 317 [super drawGlyphsForGlyphRange: glyphRange 318 atPoint: containerOrigin]; 319 } 320 else 321 { 322 /* Do nothing. */ 323 } 324} 325 326@end 327 328@implementation NSSecureTextView 329 330- (id) initWithFrame: (NSRect)frameRect 331 textContainer: (NSTextContainer*)aTextContainer 332{ 333 GSSimpleSecureLayoutManager *m; 334 GSSimpleSecureGlyphGenerator *g; 335 336 /* Perform the normal init. */ 337 self = [super initWithFrame: frameRect textContainer: aTextContainer]; 338 if (nil == self) 339 return nil; 340 341 /* Then, replace the layout manager with a 342 * GSSimpleSecureLayoutManager. */ 343 m = [[GSSimpleSecureLayoutManager alloc] init]; 344 g = [[GSSimpleSecureGlyphGenerator alloc] init]; 345 [m setGlyphGenerator: g]; 346 RELEASE(g); 347 [[self textContainer] replaceLayoutManager: m]; 348 RELEASE(m); 349 350 [self setFieldEditor: YES]; 351 352 return self; 353} 354 355- (BOOL) echosBullets 356{ 357 return [(GSSimpleSecureLayoutManager*)[self layoutManager] echosBullets]; 358} 359 360- (void) setEchosBullets: (BOOL)flag 361{ 362 [(GSSimpleSecureLayoutManager*)[self layoutManager] setEchosBullets: flag]; 363} 364 365- (BOOL) writeSelectionToPasteboard: (NSPasteboard*)pboard 366 types: (NSArray*)types 367{ 368 /* Return NO since the selection should never be written to the 369 * pasteboard */ 370 return NO; 371} 372 373- (id) validRequestorForSendType: (NSString*) sendType 374 returnType: (NSString*) returnType 375{ 376 /* Return nil to indicate that no type can be sent to the pasteboard 377 * for an object of this class. */ 378 return nil; 379} 380@end 381