/* NSTextField.m Text field control and cell classes Copyright (C) 2000 Free Software Foundation, Inc. Author: Felipe A. Rodriguez Date: June 2000 This file is part of the mySTEP Library and is provided under the terms of the GNU Library General Public License. */ #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import #import "NSAppKitPrivate.h" #define CONTROL(notif_name) NSControl##notif_name##Notification //***************************************************************************** // // NSTextFieldCell // //***************************************************************************** @implementation NSTextFieldCell - (id) initTextCell:(NSString *)aString { self=[super initTextCell:aString]; if(self) { _c.editable = YES; _c.selectable = YES; _c.bordered = NO; _c.bezeled = YES; _c.scrollable = YES; [self setAlignment:NSLeftTextAlignment]; _c.drawsBackground = NO; // default to no background ASSIGN(_backgroundColor, [NSColor textBackgroundColor]); [self _setTextColor:[NSColor textColor]]; } return self; } - (void) dealloc { [_backgroundColor release]; [_placeholderString release]; // if any [super dealloc]; } - (id) copyWithZone:(NSZone *) zone; { NSTextFieldCell *c = [super copyWithZone:zone]; [c setBackgroundColor: _backgroundColor]; [c setPlaceholderString: _placeholderString]; [c _setTextColor: [self _textColor]]; return c; } - (NSSize) cellSize { NSFont *f; NSSize borderSize, s; if ([self isBordered]) // Determine border size borderSize = ([self isBezeled]) ? (NSSize){2,2} : (NSSize){1,1}; else borderSize = NSZeroSize; f = [self font]; // Get size of text with a little buffer space s=[[self stringValue] sizeWithAttributes:[NSDictionary dictionaryWithObjectsAndKeys:f, NSFontAttributeName, nil]]; s.width += 4 + 2 * borderSize.width; // Add in border size s.height += 2 + 2 * borderSize.height; return s; } - (BOOL) isOpaque { return _c.drawsBackground;} - (BOOL) drawsBackground { return _c.drawsBackground; } - (NSTextFieldBezelStyle) bezelStyle; { return _bezelStyle; } - (void) setDrawsBackground:(BOOL)flag { _c.drawsBackground = flag; } - (void) setBackgroundColor:(NSColor*)color { ASSIGN(_backgroundColor, color); } - (void) setBezelStyle:(NSTextFieldBezelStyle)style; { _bezelStyle=style; } - (void) setTextColor:(NSColor*)aColor { [super _setTextColor:aColor]; } - (NSColor *) backgroundColor { return _backgroundColor; } - (NSColor *) textColor { return [super _textColor]; } - (NSString *) placeholderString; { return ([_placeholderString isKindOfClass:[NSString class]])?_placeholderString:nil; } - (void) setPlaceholderString:(NSString *) string; { ASSIGN(_placeholderString, string); } - (NSAttributedString *) placeholderAttributedString; { return ([_placeholderString isKindOfClass:[NSAttributedString class]])?_placeholderString:nil; } - (void) setPlaceholderAttributedString:(NSAttributedString *) string; { ASSIGN(_placeholderString, string); } - (void) drawInteriorWithFrame:(NSRect)cellFrame inView:(NSView*)controlView { if(_c.bezeled || _c.bordered) { if(_bezelStyle == NSTextFieldRoundedBezel) cellFrame = NSInsetRect(cellFrame, 6, 2); else cellFrame = NSInsetRect(cellFrame, 1, 1); } #if 1 NSLog(@"-[super drawInteriorWithFrame:%@", NSStringFromRect(cellFrame)); #endif [super drawInteriorWithFrame:cellFrame inView:controlView]; // default (formatted) drawing method of NSCell } - (void) drawWithFrame:(NSRect)cellFrame inView:(NSView*)controlView { #if 0 NSLog(@"%@ drawWithFrame:%@", NSStringFromClass(isa), NSStringFromRect(cellFrame)); NSLog(@"editable=%@", _c.editable?@"YES":@"NO"); NSLog(@"editing=%@", _c.editing?@"YES":@"NO"); NSLog(@"bezeled=%@", _c.bezeled?@"YES":@"NO"); NSLog(@"bordered=%@", _c.bordered?@"YES":@"NO"); NSLog(@"drawsBackground=%@", _c.drawsBackground?@"YES":@"NO"); NSLog(@"_backgroundColor=%@", _backgroundColor); #endif if([self showsFirstResponder]) { // button is a first responder cell NSColor *y = [NSColor selectedControlColor]; NSColor *c[] = {y, y, y, y}; NSRect cellRing=NSInsetRect(cellFrame, -1, -1); // draw around NSDrawColorTiledRects(cellRing,cellRing,[controlView isFlipped] ? BEZEL_EDGES_FLIPPED : BEZEL_EDGES_NORMAL,c,4); // NSSetFocusRingStyle(); // enlarges clipping area and sets focus ring style // NSFrameRect(cellFrame); // fill } if(_c.bezeled) { if(_bezelStyle == NSTextFieldRoundedBezel) { NSGraphicsContext *ctxt=[NSGraphicsContext currentContext]; NSBezierPath *p=[NSBezierPath _bezierPathWithRoundedBezelInRect:cellFrame vertical:NO]; // box with halfcircular rounded ends if(_c.drawsBackground) { [ctxt saveGraphicsState]; [p addClip]; // clip to contour [_backgroundColor setFill]; [p fill]; // fill with background color [ctxt restoreGraphicsState]; } [[NSColor blackColor] setStroke]; [p stroke]; // fill border } else { float grays[] = { NSWhite, NSWhite, NSDarkGray, NSDarkGray, NSLightGray, NSLightGray, NSBlack, NSBlack }; NSRectEdge *edges = BEZEL_EDGES_NORMAL; if(_c.drawsBackground) { [_backgroundColor set]; NSRectFill(cellFrame); // fill } NSDrawTiledRects(cellFrame, cellFrame, edges, grays, 8); } } else { // not bezeled if(_c.drawsBackground) { #if 0 NSLog(@"_backgroundColor=%@", _backgroundColor); #endif [_backgroundColor set]; NSRectFill(cellFrame); // fill } if(_c.bordered) { // but draw cell border if needed. [[NSColor blackColor] set]; // black frame NSFrameRect(cellFrame); } } if(_c.editing) return; // use editor to draw [self drawInteriorWithFrame:cellFrame inView:controlView]; } - (BOOL) trackMouse:(NSEvent *)event inRect:(NSRect)cellFrame ofView:(NSView *)controlView untilMouseUp:(BOOL)untilMouseUp { #if 1 NSLog(@"NSTextFieldCell trackMouse:"); #endif if([self isSelectable]) { // start editing // how does this work if we have no controlView like DOMHTMLInputElements in SWK? // FIXME: delegate:[self target]? // for making work in SWK [self editWithFrame:[self drawingRectForBounds:cellFrame] inView:controlView editor:[[controlView window] fieldEditor:YES forObject:self] delegate:controlView // make our controlView receive text notifications event:event]; return YES; // done } return [super trackMouse:event inRect:cellFrame ofView:controlView untilMouseUp:untilMouseUp]; // standard tracking } - (void) encodeWithCoder:(NSCoder *)aCoder { [super encodeWithCoder:aCoder]; [aCoder encodeObject: _backgroundColor]; [aCoder encodeObject: [self textColor]]; } - (id) initWithCoder:(NSCoder *)aDecoder { #if 0 NSLog(@"%@ -[NSTextFieldCell initWithCoder:] %@", self, aDecoder); #endif self=[super initWithCoder:aDecoder]; if([aDecoder allowsKeyedCoding]) { // done in NSCell: _c.drawsBackground = [aDecoder decodeBoolObjectForKey:@"NSDrawsBackground"]; [self setTextColor:[aDecoder decodeObjectForKey:@"NSTextColor"]]; _backgroundColor = [[aDecoder decodeObjectForKey:@"NSBackgroundColor"] retain]; _bezelStyle = [aDecoder decodeIntForKey:@"NSTextBezelStyle"]; _delegate = [aDecoder decodeObjectForKey:@"NSDelegate"]; #if 0 NSLog(@"editable=%@", _c.editable?@"YES":@"NO"); NSLog(@"editing=%@", _c.editing?@"YES":@"NO"); NSLog(@"bezeled=%@", _c.bezeled?@"YES":@"NO"); NSLog(@"bordered=%@", _c.bordered?@"YES":@"NO"); NSLog(@"drawsBackground=%@", _c.drawsBackground?@"YES":@"NO"); NSLog(@"_backgroundColor=%@", _backgroundColor); #endif return self; } _backgroundColor = [[aDecoder decodeObject] retain]; [self setTextColor:[aDecoder decodeObject]]; return self; } @end /* NSTextFieldCell */ //***************************************************************************** // // NSTextField // //***************************************************************************** // class variables static Class __textFieldCellClass = Nil; @implementation NSTextField + (void) initialize { __textFieldCellClass = [NSTextFieldCell class]; } + (Class) cellClass { return __textFieldCellClass?__textFieldCellClass:[super cellClass]; } + (void) setCellClass:(Class)class { __textFieldCellClass = class; } - (id) initWithFrame:(NSRect)frameRect { self=[super initWithFrame:frameRect]; if(self) { // [self setCell:[[[[self class] cellClass] new] autorelease]]; // allows to redefine cellClass in subclasses [_cell setState:1]; // FIXME: what is this good for??? } return self; } - (BOOL) acceptsFirstMouse:(NSEvent *)theEvent { return [_cell isEditable]; } // yes, respond immediately on activation - (BOOL) acceptsFirstResponder { return [_cell isSelectable] && [super acceptsFirstResponder]; } - (BOOL) needsPanelToBecomeKey { return [_cell isEditable]; } - (BOOL) isFlipped { return YES; } - (BOOL) isEditable { return [_cell isEditable]; } - (BOOL) isSelectable { return [_cell isSelectable]; } - (void) setEditable:(BOOL)flag { [_cell setEditable:flag]; } - (void) setSelectable:(BOOL)flag { [_cell setSelectable:flag]; } - (void) selectText:(id)sender { NSText *t; if(!_window) return; [_cell selectWithFrame:[_cell drawingRectForBounds:_bounds] inView:self editor:(t = [_window fieldEditor:YES forObject:_cell]) delegate:self start:(int)0 length:[[_cell stringValue] length]]; // [window makeFirstResponder: t]; } - (NSColor*) textColor { return [_cell textColor]; } - (NSColor*) backgroundColor { return [_cell backgroundColor]; } - (void) setTextColor:(NSColor*)aColor { [_cell setTextColor:aColor]; } - (void) setBackgroundColor:(NSColor*)clr { [_cell setBackgroundColor:clr];} - (void) setDrawsBackground:(BOOL)flag { [_cell setDrawsBackground:flag];} - (BOOL) drawsBackground { return [_cell drawsBackground]; } - (BOOL) isBezeled { return [_cell isBezeled]; } - (BOOL) isBordered { return [_cell isBordered]; } - (BOOL) isOpaque { return [_cell isOpaque]; } - (void) setBezeled:(BOOL)flag { [_cell setBezeled:flag]; } - (void) setBordered:(BOOL)flag { [_cell setBordered:flag]; } - (id) delegate { return _delegate; } - (void) setDelegate:(id)anObject { [super setDelegate:anObject]; } // Field editor's delegate methods called when editing - delegate is registered to the notification center - (void) textDidBeginEditing:(NSNotification *)aNotification { #if 1 NSLog(@" NSTextField %@ %@", NSStringFromSelector(_cmd), aNotification); #endif [[NSNotificationCenter defaultCenter] postNotificationName:CONTROL(TextDidBeginEditing) object: self]; #if 1 NSLog(@" NSTextField %@ posted", CONTROL(TextDidBeginEditing)); #endif } - (void) textDidChange:(NSNotification *)aNotification { #if 1 NSLog(@" NSTextField %@ %@", NSStringFromSelector(_cmd), aNotification); #endif [[NSNotificationCenter defaultCenter] postNotificationName:CONTROL(TextDidChange) object: self]; #if 1 NSLog(@" NSTextField %@ posted", CONTROL(TextDidChange)); #endif } - (void) textDidEndEditing:(NSNotification *)aNotification { // FIXME: do the left/right arrow keys always send this notification with NSLeftTextMovement etc. // FIXME: can it be ignored? i.e. by NOT calling endEditing? And does the cursor move in this case? NSNumber *code; #if 1 NSLog(@" NSTextField %@ %@", NSStringFromSelector(_cmd), aNotification); #endif // if(![_cell isEntryAcceptable: [aTextObject string]]) // return; // ignore #if 1 NSLog(@" NSTextField will post %@", CONTROL(TextDidEndEditing)); #endif [[NSNotificationCenter defaultCenter] postNotificationName:CONTROL(TextDidEndEditing) object: self]; #if 1 NSLog(@" NSTextField %@ posted", CONTROL(TextDidEndEditing)); #endif // end editing of cell (should validate and set new cell value) [_cell endEditing:[aNotification object]]; if((code = [[aNotification userInfo] objectForKey:NSTextMovement])) { switch([code intValue]) { case NSReturnTextMovement: if(![self sendAction:[self action] to:[self target]]) { // if this fails: // [self performKeyEquivalent:event] --- ??? // if this fails: // select text } break; case NSTabTextMovement: [_window selectKeyViewFollowingView:self]; break; case NSBacktabTextMovement: [_window selectKeyViewPrecedingView:self]; break; case NSIllegalTextMovement: break; } } } - (BOOL) textShouldBeginEditing:(NSText *)textObject { #if 1 NSLog(@" NSTextField %@ %@", NSStringFromSelector(_cmd), textObject); #endif if(![self isEditable]) return NO; #if 1 NSLog(@" NSTextField delegate=%@", _delegate); #endif if([_delegate respondsToSelector:@selector(control:textShouldBeginEditing:)]) { #if 1 NSLog(@" NSTextField delegate responds to control:textShouldBeginEditing:"); #endif return [_delegate control:self textShouldBeginEditing:textObject]; } return YES; } - (BOOL) textShouldEndEditing:(NSText*)aTextObject { // handle validation #if 1 NSLog(@" NSTextField %@ %@", NSStringFromSelector(_cmd), aTextObject); #endif if(![_window isKeyWindow]) return NO; if(_cell && [_cell isEntryAcceptable: [aTextObject string]]) { if (_delegate && [_delegate respondsToSelector:@selector(control:textShouldEndEditing:)]) { if(![_delegate control:self textShouldEndEditing:aTextObject]) { NSBeep(); return NO; } } return YES; } NSBeep(); // entry is not valid // [[_cell target] performSelector:_errorAction withObject:self]; [aTextObject setString:[_cell stringValue]]; // reset cell to original string return NO; } - (void) resetCursorRects // Manage the cursor { if([self isSelectable]) [self addCursorRect:_bounds cursor:[NSCursor IBeamCursor]]; } - (void) encodeWithCoder:(NSCoder *)aCoder // NSCoding protocol { [super encodeWithCoder:aCoder]; [aCoder encodeConditionalObject:_delegate]; // [aCoder encodeValueOfObjCType:@encode(SEL) at:&_errorAction]; } - (id) initWithCoder:(NSCoder *)aDecoder { self=[super initWithCoder:aDecoder]; if([aDecoder allowsKeyedCoding]) { // delegate? return self; } _delegate = [[aDecoder decodeObject] retain]; // [aDecoder decodeValueOfObjCType:@encode(SEL) at:&_errorAction]; return self; } // - (BOOL) shouldBeTreatedAsInkEvent:(NSEvent *) theEvent; { return [self isEditable]; } // start inking only if editable @end /* NSTextField */