/** 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 and returns a new NSControl into the rectangle frameRect and create a new associated 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 not nil and 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: [NSCell-isEnabled]

*/ - (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 [NSCell-setEnabled:]

*/ - (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 of the NSControl's selected cell as double.

See Also: -setDoubleValue: [NSCell-doubleValue] -intValue -floatValue doubleValue stringValue

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

Returns the value of the NSControl's selected cell as float.

See Also: -setFloatValue: [NSCell-floatValue] -intValue -stringValue doubleValue

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

Returns the value of the NSControl's selected cell as int.

See Also: -setIntValue: [NSCell-intValue] -floatValue -doubleValue -stringValue

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

Returns the value of the NSControl's selected cell as NSString.

See Also: -setStringValue: [NSCell-stringValue] -intValue -floatValue -doubleValue -stringValue

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

Sets the value of 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:] -setIntValue: -setStringValue: -setFloatValue:

*/ - (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 of 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:] -setIntValue: -setStringValue: -setDoubleValue:

*/ - (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 of 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:] -setDoubleValue: -setFloatValue: -setStringValue:

*/ - (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 of 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:] -setIntValue: -setFloatValue: -setDoubleValue:

*/ - (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]; } /**

Sets the NSControl's selected cell to the sender's double value

See Also: [NSCell-takeDoubleValueFrom:] -takeFloatValueFrom: takeIntValueFrom: takeStringValueFrom:

*/ - (void) takeDoubleValueFrom: (id)sender { [[self selectedCell] takeDoubleValueFrom: sender]; [self setNeedsDisplay: YES]; } /**

Sets the NSControl's selected cell to the sender's float value

See Also: [NSCell-takeDoubleValueFrom:] -takeDoubleValueFrom: takeIntValueFrom: takeStringValueFrom:

*/ - (void) takeFloatValueFrom: (id)sender { [[self selectedCell] takeFloatValueFrom: sender]; [self setNeedsDisplay: YES]; } /**

Sets the NSControl's selected cell to the sender's float int

See Also: [NSCell-takeDoubleValueFrom:] -takeDoubleValueFrom: takeFloatValueFrom: takeStringValueFrom:

*/ - (void) takeIntValueFrom: (id)sender { [[self selectedCell] takeIntValueFrom: sender]; [self setNeedsDisplay: YES]; } - (void) takeObjectValueFrom: (id)sender { [[self selectedCell] takeObjectValueFrom: sender]; [self setNeedsDisplay: YES]; } /**

Sets the NSControl's selected cell to the sender's float int

See Also: [NSCell-takeDoubleValueFrom:] -takeDoubleValueFrom: takeFloatValueFrom: takeIntValueFrom:

*/ - (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 NSTextAlignment for more informations.

See Also: -setAlignment: [NSCell-alignment]

*/ - (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: [NSCell-font]

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

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

See Also: -alignment [NSCell-setAlignment:] -abortEditing

*/ - (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 and the editor object (if exists) to fontObject

See Also: -font [NSCell-setFont:] -currentEditor

*/ - (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; } /**

Returns the NSText object used when editing the NSControl.

*/ - (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]; } /**

Redraws a aCell if it is the NSControl's cell.

See Also: -setCell: [NSCell-drawWithFrame:inView:]

*/ - (void) drawCell: (NSCell *)aCell { if (_cell == aCell) { [_cell drawWithFrame: _bounds inView: self]; } } /**

Redraws a aCell's inside if it is the NSControl's cell.

See Also: -setCell: [NSCell-drawInteriorWithFrame:inView:]

*/ - (void) drawCellInside: (NSCell *)aCell { if (_cell == aCell) { [_cell drawInteriorWithFrame: _bounds inView: self]; } } /**

Sets the aCell's state to NSOnState and marks self for display if it is 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]; } /** * */ - (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]; } /**

Returns wheter multiple clicks are ignored.

See Also: -setIgnoresMultiClick: -mouseDown:

*/ - (BOOL) ignoresMultiClick { return _ignoresMultiClick; } /**

Sets wheter multiple clicks are ignored.

See Also: -ignoresMultiClick -mouseDown:

*/ - (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