/** NSControl The abstract control class Copyright (C) 1996 Free Software Foundation, Inc. Author: Scott Christley Date: 1996 Author: Richard Frith-Macdonald Date: August 1998 This file is part of the GNUstep GUI Library. This library is free software; you can redistribute it and/or modify it under the terms of the GNU Library General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public License for more details. You should have received a copy of the GNU Library General Public License along with this library; see the file COPYING.LIB. If not, write to the Free Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "config.h" #include #include #include "AppKit/NSActionCell.h" #include "AppKit/NSApplication.h" #include "AppKit/NSCell.h" #include "AppKit/NSControl.h" #include "AppKit/NSColor.h" #include "AppKit/NSEvent.h" #include "AppKit/NSTextStorage.h" #include "AppKit/NSTextView.h" #include "AppKit/NSWindow.h" /* * Class variables */ static Class usedCellClass; static Class cellClass; static Class actionCellClass; /**

TODO Description

*/ @implementation NSControl /* * Class methods */ + (void) initialize { if (self == [NSControl class]) { [self setVersion: 1]; cellClass = [NSCell class]; usedCellClass = cellClass; actionCellClass = [NSActionCell class]; } } /* * Setting the Control's Cell */ + (Class) cellClass { return usedCellClass; } + (void) setCellClass: (Class)factoryId { usedCellClass = factoryId ? factoryId : cellClass; } /**

Initializes a new NSControl into the frame frameRect and create a new NSCell

See Also: -setCell:

*/ - (id) initWithFrame: (NSRect)frameRect { NSCell *cell = [[[self class] cellClass] new]; [super initWithFrame: frameRect]; [self setCell: cell]; RELEASE(cell); //_tag = 0; return self; } - (void) dealloc { RELEASE(_cell); [super dealloc]; } /**

Returns the NSControl's cell

See Also: -setCell:

*/ - (id) cell { return _cell; } /**

Sets the NSControl's cell to aCell, Raises an NSInvalidArgumentException exception if aCell is nil or if it is not a cell class

See Also: -cell

*/ - (void) setCell: (NSCell *)aCell { if (aCell != nil && [aCell isKindOfClass: cellClass] == NO) [NSException raise: NSInvalidArgumentException format: @"attempt to set non-cell object for control cell"]; ASSIGN(_cell, aCell); } /**

Returns whether the selected cell of the NSControl is enabled

See Also: -setEnabled:

*/ - (BOOL) isEnabled { return [[self selectedCell] isEnabled]; } /**

Sets whether the NSControl's selected cell is enabled. If flag is NO, this method abort the editing. This method marks self for display

See Also: -isEnabled

*/ - (void) setEnabled: (BOOL)flag { [[self selectedCell] setEnabled: flag]; if (!flag) [self abortEditing]; [self setNeedsDisplay: YES]; } /**

Returns the NSControl's selected cell

*/ - (id) selectedCell { return _cell; } /**

Returns the tag of the NSControl's selected cell (if exists). -1 otherwise

See Also: [NSCell-tag]

*/ - (int) selectedTag { NSCell *selected = [self selectedCell]; if (selected == nil) return -1; else return [selected tag]; } /**

Returns the value if the NSControl's selected cell as double

See Also: [NSCell-doubleValue]

*/ - (double) doubleValue { // The validation is performed by the NSActionCell return [[self selectedCell] doubleValue]; } /**

Returns the value if the NSControl's selected cell as float

See Also: [NSCell-floatValue]

*/ - (float) floatValue { return [[self selectedCell] floatValue]; } /**

Returns the value if the NSControl's selected cell as int

See Also: [NSCell-intValue]

*/ - (int) intValue { return [[self selectedCell] intValue]; } /**

Returns the value if the NSControl's selected cell as NSString

See Also: [NSCell-stringValue]

*/ - (NSString *) stringValue { return [[self selectedCell] stringValue]; } - (id) objectValue { return [[self selectedCell] objectValue]; } /**

Sets the value if the NSControl's selected cell to double. If the selected cell is an action cell, it marks self for display.

See Also: -doubleValue [NSCell-setDoubleValue:]

*/ - (void) setDoubleValue: (double)aDouble { NSCell *selected = [self selectedCell]; BOOL wasEditing = [self abortEditing]; [selected setDoubleValue: aDouble]; if (![selected isKindOfClass: actionCellClass]) [self setNeedsDisplay: YES]; if (wasEditing) { [[self window] makeFirstResponder: self]; } } /**

Sets the value if the NSControl's selected cell to float. If the selected cell is an action cell, it marks self for display.

See Also: -floatValue [NSCell-setFloatValue:]

*/ - (void) setFloatValue: (float)aFloat { NSCell *selected = [self selectedCell]; BOOL wasEditing = [self abortEditing]; [selected setFloatValue: aFloat]; if (![selected isKindOfClass: actionCellClass]) [self setNeedsDisplay: YES]; if (wasEditing) { [[self window] makeFirstResponder: self]; } } /**

Sets the value if the NSControl's selected cell to int. If the selected cell is an action cell, it marks self for display.

See Also: -intValue [NSCell-setIntValue:]

*/ - (void) setIntValue: (int)anInt { NSCell *selected = [self selectedCell]; BOOL wasEditing = [self abortEditing]; [selected setIntValue: anInt]; if (![selected isKindOfClass: actionCellClass]) [self setNeedsDisplay: YES]; if (wasEditing) { [[self window] makeFirstResponder: self]; } } /**

Sets the value if the NSControl's selected cell to NSString. If the selected cell is an action cell, it marks self for display.

See Also: stringValue [NSCell-setStringValue:]

*/ - (void) setStringValue: (NSString *)aString { NSCell *selected = [self selectedCell]; BOOL wasEditing = [self abortEditing]; [selected setStringValue: aString]; if (![selected isKindOfClass: actionCellClass]) [self setNeedsDisplay: YES]; if (wasEditing) { [[self window] makeFirstResponder: self]; } } - (void) setObjectValue: (id)anObject { NSCell *selected = [self selectedCell]; BOOL wasEditing = [self abortEditing]; [selected setObjectValue: anObject]; if (![selected isKindOfClass: actionCellClass]) [self setNeedsDisplay: YES]; if (wasEditing) { [[self window] makeFirstResponder: self]; } } /**

Marks self for display

*/ - (void) setNeedsDisplay { [super setNeedsDisplay: YES]; } /* * Interacting with Other Controls */ - (void) takeDoubleValueFrom: (id)sender { [[self selectedCell] takeDoubleValueFrom: sender]; [self setNeedsDisplay: YES]; } - (void) takeFloatValueFrom: (id)sender { [[self selectedCell] takeFloatValueFrom: sender]; [self setNeedsDisplay: YES]; } - (void) takeIntValueFrom: (id)sender { [[self selectedCell] takeIntValueFrom: sender]; [self setNeedsDisplay: YES]; } - (void) takeObjectValueFrom: (id)sender { [[self selectedCell] takeObjectValueFrom: sender]; [self setNeedsDisplay: YES]; } - (void) takeStringValueFrom: (id)sender { [[self selectedCell] takeStringValueFrom: sender]; [self setNeedsDisplay: YES]; } /**

Returns the alignment of the text in the NSControl's cell. Returns NSNaturalTextAlignment if the cell does not exists

See Also: -setAlignment:

*/ - (NSTextAlignment) alignment { if (_cell) return [_cell alignment]; else return NSNaturalTextAlignment; } /**

Returns the font of the text in the NSControl's cell. Returns nil if the cell does not exists

See Also: -setFont:

*/ - (NSFont *) font { if (_cell) return [_cell font]; else return nil; } /**

Sets the alignment of the text in the NSControl's cell. This method abort the editing and marks self for display if the cell is an NSActionCell

See Also: -alignment

*/ - (void) setAlignment: (NSTextAlignment)mode { if (_cell) { [self abortEditing]; [_cell setAlignment: mode]; if (![_cell isKindOfClass: actionCellClass]) [self setNeedsDisplay: YES]; } } /**

Sets the font of the text in the NSControl's cell.

See Also: -font

*/ - (void) setFont: (NSFont *)fontObject { if (_cell) { NSText *editor = [self currentEditor]; [_cell setFont: fontObject]; if (editor != nil) [editor setFont: fontObject]; } } - (void) setFloatingPointFormat: (BOOL)autoRange left: (unsigned)leftDigits right: (unsigned)rightDigits { [self abortEditing]; [_cell setFloatingPointFormat: autoRange left: leftDigits right: rightDigits]; if (![_cell isKindOfClass: actionCellClass]) [self setNeedsDisplay: YES]; } - (void) setFormatter: (NSFormatter*)newFormatter { if (_cell) { [_cell setFormatter: newFormatter]; if (![_cell isKindOfClass: actionCellClass]) [self setNeedsDisplay: YES]; } } - (id) formatter { return [_cell formatter]; } /**

Sends an [NSCell-endEditing:] message to the current object used to edit the NSControl. Returns NO if the the currentEditor does not exists, YES otherwise.

*/ - (BOOL) abortEditing { NSText *text; text = [self currentEditor]; if (text == nil) { return NO; } [[self selectedCell] endEditing: text]; return YES; } - (NSText *) currentEditor { if (_cell != nil) { NSText *text; text = [_window fieldEditor: NO forObject: self]; if (([text delegate] == self) && ([_window firstResponder] == text)) { return text; } } return nil; } /** */ - (void) validateEditing { NSText *text; text = [self currentEditor]; if (text == nil) { return; } if ([text isRichText]) { NSAttributedString *attr; NSTextStorage *storage; int len; storage = [(NSTextView*)text textStorage]; len = [storage length]; attr = [storage attributedSubstringFromRange: NSMakeRange(0, len)]; [[self selectedCell] setAttributedStringValue: attr]; } else { NSString *string; string = AUTORELEASE([[text string] copy]); [[self selectedCell] setStringValue: string]; } } /**

Recalculates the internal size by sending [NSCell-calcDrawInfo:] to the cell.

*/ - (void) calcSize { [_cell calcDrawInfo: [self bounds]]; } /**

Resizes the NSControl to fits the NSControl's cell size

See Also: [NSCell-cellSize]

*/ - (void) sizeToFit { [self setFrameSize: [_cell cellSize]]; } /**

Returns whether the NSControl's cell is opaque

*/ - (BOOL) isOpaque { return [_cell isOpaque]; } - (void) drawRect: (NSRect)aRect { [self drawCell: _cell]; } - (void) drawCell: (NSCell *)aCell { if (_cell == aCell) { [_cell drawWithFrame: _bounds inView: self]; } } - (void) drawCellInside: (NSCell *)aCell { if (_cell == aCell) { [_cell drawInteriorWithFrame: _bounds inView: self]; } } /**

Selects aCell if it's the NSControl's cell

*/ - (void) selectCell: (NSCell *)aCell { if (_cell == aCell) { [_cell setState: 1]; [self setNeedsDisplay: YES]; } } /**

Marks self for display

*/ - (void) updateCell: (NSCell *)aCell { [self setNeedsDisplay: YES]; } /**

Marks self for display

*/ - (void) updateCellInside: (NSCell *)aCell { [self setNeedsDisplay: YES]; } /**

Returns the NSControl's cell action method

See Also: -setAction: [NSCell-action]

*/ - (SEL) action { return [_cell action]; } /**

Returns whether the NSControl's cell can continuously sends its action message

See Also: -setContinuous: [NSCell-isContinuous]

*/ - (BOOL) isContinuous { return [_cell isContinuous]; } - (BOOL) sendAction: (SEL)theAction to: (id)theTarget { if (theAction) return [NSApp sendAction: theAction to: theTarget from: self]; else return NO; } - (int) sendActionOn: (int)mask { return [_cell sendActionOn: mask]; } /**

Sets the NSControl's cell action method

See Also: -action [NSCell-setAction:]

*/ - (void) setAction: (SEL)aSelector { [_cell setAction: aSelector]; } /**

Sets whether the NSControl's cell can continuously sends its action message

See Also: -isContinuous [NSCell-setContinuous:]

*/ - (void) setContinuous: (BOOL)flag { [_cell setContinuous: flag]; } /**

Sets the target object of the NSControl's cell to anObject

See Also: -target [NSCell-setTarget:]

*/ - (void) setTarget: (id)anObject { [_cell setTarget: anObject]; } /**

Returns the target object of the NSControl's cell

See Also: -setTarget: [NSCell-target]

*/ - (id) target { return [_cell target]; } /* * Attributed string handling */ - (void) setAttributedStringValue: (NSAttributedString*)attribStr { NSCell *selected = [self selectedCell]; [self abortEditing]; [selected setAttributedStringValue: attribStr]; if (![selected isKindOfClass: actionCellClass]) [self setNeedsDisplay: YES]; } - (NSAttributedString*) attributedStringValue { NSCell *selected = [self selectedCell]; if (selected == nil) { return AUTORELEASE([NSAttributedString new]); } // As this mehtod is not defined for NSActionCell, we have // to do the validation here. [self validateEditing]; return [selected attributedStringValue]; } /** * Assigning a Tag */ - (void) setTag: (int)anInt { _tag = anInt; } - (int) tag { return _tag; } /* * Activation */ /** * Simulates a single mouse click on the control. This method calls the cell's * method performClickWithFrame:inView:. Take note that sender is not * used. */ - (void) performClick: (id)sender { [_cell performClickWithFrame: [self bounds] inView: self]; } - (BOOL)refusesFirstResponder { return [[self selectedCell] refusesFirstResponder]; } - (void)setRefusesFirstResponder:(BOOL)flag { [[self selectedCell] setRefusesFirstResponder: flag]; } - (BOOL) acceptsFirstResponder { return [[self selectedCell] acceptsFirstResponder]; } /* * Tracking the Mouse */ - (void) mouseDown: (NSEvent *)theEvent { NSApplication *theApp = [NSApplication sharedApplication]; BOOL mouseUp = NO, done = NO; NSEvent *e; int oldActionMask; NSPoint location; unsigned int event_mask = NSLeftMouseDownMask | NSLeftMouseUpMask | NSMouseMovedMask | NSLeftMouseDraggedMask | NSOtherMouseDraggedMask | NSRightMouseDraggedMask; if (![self isEnabled]) return; if (_ignoresMultiClick && ([theEvent clickCount] > 1)) { [super mouseDown: theEvent]; return; } if ([_cell isContinuous]) { oldActionMask = [_cell sendActionOn: NSPeriodicMask]; } else { oldActionMask = [_cell sendActionOn: 0]; } [_window _captureMouse: self]; e = theEvent; // loop until mouse goes up while (!done) { location = [e locationInWindow]; location = [self convertPoint: location fromView: nil]; // ask the cell to track the mouse only // if the mouse is within the cell if ([self mouse: location inRect: _bounds]) { [_cell setHighlighted: YES]; [self setNeedsDisplay: YES]; if ([_cell trackMouse: e inRect: _bounds ofView: self untilMouseUp: [[_cell class] prefersTrackingUntilMouseUp]]) done = mouseUp = YES; else { [_cell setHighlighted: NO]; [self setNeedsDisplay: YES]; } } if (done) break; e = [theApp nextEventMatchingMask: event_mask untilDate: nil inMode: NSEventTrackingRunLoopMode dequeue: YES]; if ([e type] == NSLeftMouseUp) done = YES; } [_window _releaseMouse: self]; if (mouseUp) { [_cell setHighlighted: NO]; [self setNeedsDisplay: YES]; } [_cell sendActionOn: oldActionMask]; if (mouseUp) [self sendAction: [self action] to: [self target]]; } - (BOOL) shouldBeTreatedAsInkEvent: (NSEvent *)theEvent { return NO; } - (void) resetCursorRects { [_cell resetCursorRect: _bounds inView: self]; } - (BOOL) ignoresMultiClick { return _ignoresMultiClick; } - (void) setIgnoresMultiClick: (BOOL)flag { _ignoresMultiClick = flag; } /* * NSCoding protocol */ - (void) encodeWithCoder: (NSCoder*)aCoder { [super encodeWithCoder: aCoder]; [aCoder encodeValueOfObjCType: @encode(int) at: &_tag]; [aCoder encodeObject: _cell]; [aCoder encodeValueOfObjCType: @encode(BOOL) at: &_ignoresMultiClick]; } - (id) initWithCoder: (NSCoder*)aDecoder { [super initWithCoder: aDecoder]; if ([aDecoder allowsKeyedCoding]) { NSCell *cell = [aDecoder decodeObjectForKey: @"NSCell"]; if (cell != nil) { [self setCell: cell]; } if ([aDecoder containsValueForKey: @"NSEnabled"]) { [self setEnabled: [aDecoder decodeBoolForKey: @"NSEnabled"]]; } } else { [aDecoder decodeValueOfObjCType: @encode(int) at: &_tag]; [aDecoder decodeValueOfObjCType: @encode(id) at: &_cell]; [aDecoder decodeValueOfObjCType: @encode(BOOL) at: &_ignoresMultiClick]; } return self; } @end