/* Copyright (C) 2002-2003, Jeffrey D. Argast. The authors make NO WARRANTY or representation, either express or implied, with respect to this software, its quality, accuracy, merchantability, or fitness for a particular purpose. This software is provided "AS IS", and you, its user, assume the entire risk as to its quality and accuracy. Permission is hereby granted to use, copy, modify, and distribute this software or portions thereof for any purpose, without fee, subject to these conditions: (1) If any part of the source code is distributed, then this statement must be included, with this copyright and no-warranty notice unaltered. (2) Permission for use of this software is granted only if the user accepts full responsibility for any undesirable consequences; the authors accept NO LIABILITY for damages of any kind. */ #import "TextControlWithClear.h" #import "AppUtils.h" @interface TextControlWithClear (PrivateMethods) - (void) resetCache; - (void) resetIconPosition; - (void) resetTextCellFrame; - (void) activateFieldEditor; - (void) setDrawsFocus:(BOOL)drawFocus; - (void) setNeedsDisplayFocusRing; @end // // // @implementation TextControlWithClear + (Class) cellClass { return [NSTextFieldCell class]; } - (id)initWithFrame:(NSRect)frame { self = [super initWithFrame:frame]; if (self) { fResetCache = YES; fDrawFocusRing = NO; fFocusPath = [[NSBezierPath alloc] init]; fClearEditIcon = [[NSImage imageNamed:@"ClearEditIcon"] retain]; [self resetTextCellFrame]; [self resetIconPosition]; NSTextFieldCell* myCell = [self cell]; [myCell setSelectable:YES]; [myCell setEditable:YES]; [myCell setWraps:NO]; [myCell setScrollable:YES]; [myCell setBezeled:NO]; [myCell setBordered:NO]; [myCell setDrawsBackground:YES]; [myCell setStringValue:@""]; } return self; } - (void) dealloc { [fFocusPath release]; [fClearEditIcon release]; [fCachedBackground release]; NSNotificationCenter* center = [NSNotificationCenter defaultCenter]; [center removeObserver:self]; [super dealloc]; } - (void) resetCursorRects { [self addCursorRect:fTextCellFrame cursor:[NSCursor IBeamCursor]]; [self addCursorRect:fIconHitRect cursor:[NSCursor arrowCursor]]; } - (BOOL) acceptsFirstResponder { // We don't become first responder if our text cell // is using the field editor if ( fFieldEditor ) { return NO; } return YES; } - (void)setDelegate:(id)anObject { delegate = anObject; } - (id)delegate { return delegate; } - (void) setFrame:(NSRect)rect { fResetCache = YES; [super setFrame:rect]; [self resetTextCellFrame]; [self resetIconPosition]; } - (void) setBounds:(NSRect)rect { fResetCache = YES; [super setBounds:rect]; [self resetTextCellFrame]; [self resetIconPosition]; } - (void)viewWillStartLiveResize { [[self window] endEditingFor:self]; } - (void) setNeedsDisplay:(BOOL)flag { [super setNeedsDisplay:flag]; if ( flag && fDrawFocusRing ) { [self setNeedsDisplayFocusRing]; } } - (void) setNeedsDisplayInRect:(NSRect)rect { [super setNeedsDisplayInRect:rect]; if ( fDrawFocusRing ) { [self setNeedsDisplayFocusRing]; } } - (void)drawRect:(NSRect)rect { if ( fResetCache ) { [self resetCache]; } [fCachedBackground compositeToPoint:[self bounds].origin operation:NSCompositeSourceOver]; NSString* stringValue = [[self cell] stringValue]; if ( stringValue && ([stringValue length] > 0) ) { [fClearEditIcon compositeToPoint:fIconPosition operation:NSCompositeSourceOver]; } if ( fDrawFocusRing ) { [NSGraphicsContext saveGraphicsState]; NSSetFocusRingStyle(NSFocusRingOnly); [fFocusPath stroke]; [NSGraphicsContext restoreGraphicsState]; } NSTextFieldCell* myCell = [self cell]; [myCell setShowsFirstResponder:NO]; [myCell drawWithFrame:fTextCellFrame inView:self]; } - (void) mouseDown:(NSEvent*)theEvent { NSPoint mousePt = [theEvent locationInWindow]; mousePt = [self convertPoint:mousePt fromView:nil]; BOOL isHit = NSMouseInRect (mousePt, fIconHitRect, NO); if ( isHit ) { [self setStringValue:@""]; if ( !fDrawFocusRing ) { [self textDidClear:self]; } } else { [self activateFieldEditor]; } } - (void) setStringValue:(NSString*) stringValue { [[self cell] setStringValue:stringValue]; [self setNeedsDisplay:YES]; } - (NSString*) stringValue { return [[self cell] stringValue]; } - (void) selectText:(id)sender { if ( fFieldEditor ) { [fFieldEditor selectAll:self]; } else { [self activateFieldEditor]; } } - (BOOL)textShouldBeginEditing:(NSText *)textObject { if ( delegate && [delegate respondsToSelector:@selector(control:textShouldBeginEditing:)] ) { return [delegate control:self textShouldBeginEditing:fFieldEditor]; } return YES; } - (BOOL)textShouldEndEditing:(NSText *)textObject { if ( delegate && [delegate respondsToSelector:@selector(control:textShouldEndEditing:)] ) { return [delegate control:self textShouldEndEditing:fFieldEditor]; } return YES; } - (void)textDidBeginEditing:(NSNotification *)notification { [self setDrawsFocus:YES]; if ( delegate && [delegate respondsToSelector:@selector(controlTextDidBeginEditing:)] ) { [delegate controlTextDidBeginEditing:notification]; } } - (void)textDidEndEditing:(NSNotification *)notification { [[self window] endEditingFor:self]; fFieldEditor = nil; [self setDrawsFocus:NO]; if ( delegate && [delegate respondsToSelector:@selector(controlTextDidEndEditing:)] ) { [delegate controlTextDidEndEditing:notification]; } } - (void)textDidChange:(NSNotification *)notification { if ( delegate && [delegate respondsToSelector:@selector(controlTextDidChange:)] ) { [delegate controlTextDidChange:notification]; } } - (void) textDidClear:(NSControl*)sender { if ( delegate && [delegate respondsToSelector:@selector(textDidClear:)] ) { [delegate textDidClear:self]; } } // // // - (unsigned int) draggingEntered:(id )sender { if ( [sender draggingSource] != self ) { NSPasteboard* pb = [sender draggingPasteboard]; NSString* type = [pb availableTypeFromArray:[NSArray arrayWithObject:NSStringPboardType]]; if ( type ) { NSCell* textFieldCell = [self cell]; [textFieldCell setHighlighted:YES]; [self setNeedsDisplay:YES]; return NSDragOperationCopy; } } return NSDragOperationNone; } - (void) draggingExited:(id )sender { NSCell* textFieldCell = [self cell]; [textFieldCell setHighlighted:NO]; [self setNeedsDisplay:YES]; } - (BOOL) prepareForDragOperation:(id )sender { return YES; } - (BOOL) performDragOperation:(id )sender { NSPasteboard* pb = [sender draggingPasteboard]; NSString* value = ReadStringFromPasteboard (pb); if ( value ) { // read up to the first break NSScanner* scanner = [NSScanner scannerWithString:value]; NSString* resultString = nil; if ( [scanner scanUpToString:@"\n" intoString:&resultString] == YES ) { [self setStringValue:resultString]; [self selectText:self]; } return YES; } return NO; } - (void) concludeDragOperation:(id )sender { NSCell* textFieldCell = [self cell]; [textFieldCell setHighlighted:NO]; [self setNeedsDisplay:YES]; } @end // ///////////////////////////////////////// // // PRIVATE METHODS // ///////////////////////////////////////// // @implementation TextControlWithClear (PrivateMethods) - (void) resetCache { NSRect rect = [self bounds]; NSPoint leftpt, rightpt, leftpt2, rightpt2, leftpt3, rightpt3; float magicOffset = rect.size.height / 2.0; leftpt.x = rect.origin.x + magicOffset; leftpt.y = floor(rect.origin.y + rect.size.height / 2.0); rightpt.x = rect.origin.x + rect.size.width - magicOffset;; rightpt.y = leftpt.y; leftpt2.x = leftpt.x; leftpt2.y = leftpt.y - 1; rightpt2.x = rightpt.x; rightpt2.y = leftpt2.y; leftpt3.x = leftpt.x - 3; leftpt3.y = rect.origin.y; rightpt3.x = rightpt.x + 3; rightpt3.y = leftpt3.y; NSBezierPath* backgroundPath = [NSBezierPath bezierPath]; NSBezierPath* middlegroundPath = [NSBezierPath bezierPath]; NSBezierPath* foregroundPath = [NSBezierPath bezierPath]; NSBezierPath* shadowlinePath = [NSBezierPath bezierPath]; [backgroundPath setLineWidth:rect.size.height ]; [backgroundPath setLineCapStyle:NSRoundLineCapStyle]; [backgroundPath moveToPoint:leftpt]; [backgroundPath lineToPoint:rightpt]; [middlegroundPath setLineWidth:rect.size.height ]; [middlegroundPath setLineCapStyle:NSRoundLineCapStyle]; [middlegroundPath moveToPoint:leftpt2]; [middlegroundPath lineToPoint:rightpt2]; [foregroundPath setLineWidth:rect.size.height - 2]; [foregroundPath setLineCapStyle:NSRoundLineCapStyle]; [foregroundPath moveToPoint:leftpt2]; [foregroundPath lineToPoint:rightpt2]; [shadowlinePath setLineWidth:0]; [shadowlinePath moveToPoint:leftpt3]; [shadowlinePath lineToPoint:rightpt3]; [fFocusPath removeAllPoints]; [fFocusPath setLineWidth:rect.size.height ]; [fFocusPath setLineCapStyle:NSRoundLineCapStyle]; [fFocusPath moveToPoint:leftpt]; [fFocusPath lineToPoint:rightpt]; // icon [self resetIconPosition]; [self resetTextCellFrame]; [fCachedBackground release]; fCachedBackground = [[NSImage alloc] initWithSize:rect.size]; [fCachedBackground lockFocus]; [NSGraphicsContext saveGraphicsState]; [[NSColor windowBackgroundColor] set]; [NSBezierPath fillRect:rect]; [[NSColor colorWithDeviceRed:0.3529 green:0.3529 blue:0.3529 alpha:1.0] set]; //[[[NSColor whiteColor] shadowWithLevel:0.7] set]; [backgroundPath stroke]; [[NSColor colorWithDeviceRed:0.6902 green:0.6902 blue:0.6902 alpha:1.0] set]; //[[NSColor controlShadowColor] set]; [middlegroundPath stroke]; [[NSColor whiteColor] set]; //[[NSColor windowBackgroundColor] set]; [foregroundPath stroke]; [[NSColor colorWithDeviceRed:0.6902 green:0.6902 blue:0.6902 alpha:1.0] set]; //[[NSColor controlShadowColor] set]; [shadowlinePath stroke]; [NSGraphicsContext restoreGraphicsState]; [fCachedBackground unlockFocus]; fResetCache = NO; } - (void) resetIconPosition { NSRect bounds = [self bounds]; NSSize iconSize = [fClearEditIcon size]; // icon fIconPosition.x = bounds.origin.x + bounds.size.width - iconSize.width - 3; fIconPosition.y = bounds.origin.y + ((bounds.size.height - iconSize.height) / 2.0); fIconHitRect.origin = fIconPosition; fIconHitRect.size = iconSize; } - (void) resetTextCellFrame { // text cell NSRect bounds = [self bounds]; float magicOffset = bounds.size.height / 2.0; NSSize iconSize = [fClearEditIcon size]; bounds = NSInsetRect (bounds, magicOffset, 2); bounds.size.width -= (iconSize.width - magicOffset + 4); //bounds.origin.y += (floor(magicOffset / 2) - 3); fTextCellFrame = bounds; } - (void) activateFieldEditor { if ( fFieldEditor ) return; NSWindow* myWindow = [self window]; // the approved way to get the field editor if ( ![myWindow makeFirstResponder:myWindow] ) { [myWindow endEditingFor:nil]; } int length = 0; NSTextFieldCell* myCell = [self cell]; NSString* content = [myCell stringValue]; if ( content ) { length = [content length]; } fFieldEditor = [myWindow fieldEditor:YES forObject:self]; [myCell selectWithFrame:fTextCellFrame inView:self editor:fFieldEditor delegate:self start:0 length:length]; [self setDrawsFocus:YES]; } - (void) setDrawsFocus:(BOOL)drawFocus { if ( fDrawFocusRing ) { [self setNeedsDisplayFocusRing]; } fDrawFocusRing = drawFocus; if ( fDrawFocusRing ) { [self setNeedsDisplayFocusRing]; } } - (void) setNeedsDisplayFocusRing { NSRect myBounds = [self bounds]; [super setKeyboardFocusRingNeedsDisplayInRect:myBounds]; } @end