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