mirror of
https://github.com/gnustep/libs-gui.git
synced 2025-04-23 20:01:11 +00:00
3306 lines
89 KiB
Objective-C
3306 lines
89 KiB
Objective-C
/** <title>NSCell</title>
|
|
|
|
<abstract>The abstract cell class</abstract>
|
|
|
|
Copyright (C) 1996-2012,2019 Free Software Foundation, Inc.
|
|
|
|
Author: Scott Christley <scottc@net-community.com>
|
|
Date: 1996
|
|
Modifications: Felipe A. Rodriguez <far@ix.netcom.com>
|
|
Date: August 1998
|
|
Rewrite: Multiple authors
|
|
Date: 1999
|
|
Editing, formatters: Nicola Pero <nicola@brainstorm.co.uk>
|
|
Date: 2000
|
|
|
|
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 Lesser 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
|
|
Lesser General Public License for more details.
|
|
|
|
You should have received a copy of the GNU Lesser General Public
|
|
License along with this library; see the file COPYING.LIB.
|
|
If not, see <http://www.gnu.org/licenses/> or write to the
|
|
Free Software Foundation, 51 Franklin Street, Fifth Floor,
|
|
Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#include <math.h>
|
|
|
|
#import "config.h"
|
|
#import <Foundation/NSCoder.h>
|
|
#import <Foundation/NSDebug.h>
|
|
#import <Foundation/NSException.h>
|
|
#import <Foundation/NSFormatter.h>
|
|
#import <Foundation/NSValue.h>
|
|
#import <Foundation/NSNotification.h>
|
|
#import <Foundation/NSNumberFormatter.h>
|
|
#import <Foundation/NSRunLoop.h>
|
|
#import <Foundation/NSString.h>
|
|
#import <Foundation/NSGeometry.h>
|
|
|
|
#import "AppKit/AppKitExceptions.h"
|
|
#import "AppKit/NSAttributedString.h"
|
|
#import "AppKit/NSApplication.h"
|
|
#import "AppKit/NSControl.h"
|
|
#import "AppKit/NSCell.h"
|
|
#import "AppKit/NSClipView.h"
|
|
#import "AppKit/NSColor.h"
|
|
#import "AppKit/NSCursor.h"
|
|
#import "AppKit/NSEvent.h"
|
|
#import "AppKit/NSFont.h"
|
|
#import "AppKit/NSGraphics.h"
|
|
#import "AppKit/NSImage.h"
|
|
#import "AppKit/NSMenu.h"
|
|
#import "AppKit/NSParagraphStyle.h"
|
|
#import "AppKit/NSStringDrawing.h"
|
|
#import "AppKit/NSTextView.h"
|
|
#import "AppKit/NSTextContainer.h"
|
|
#import "AppKit/NSView.h"
|
|
#import "AppKit/NSWindow.h"
|
|
#import "AppKit/NSKeyValueBinding.h"
|
|
#import "GSBindingHelpers.h"
|
|
#import "GNUstepGUI/GSTheme.h"
|
|
#import "GSGuiPrivate.h"
|
|
|
|
static Class colorClass;
|
|
static Class cellClass;
|
|
static Class fontClass;
|
|
static Class imageClass;
|
|
|
|
static NSColor *txtCol;
|
|
static NSColor *dtxtCol;
|
|
|
|
@interface NSCell (PrivateColor)
|
|
+ (void) _systemColorsChanged: (NSNotification*)n;
|
|
@end
|
|
|
|
|
|
@implementation NSCell (PrivateColor)
|
|
+ (void) _systemColorsChanged: (NSNotification*)n
|
|
{
|
|
ASSIGN (txtCol, [colorClass controlTextColor]);
|
|
ASSIGN (dtxtCol, [colorClass disabledControlTextColor]);
|
|
}
|
|
@end
|
|
|
|
|
|
/**
|
|
*<p> TODO Desctiption</p>
|
|
*/
|
|
|
|
@implementation NSCell
|
|
|
|
/*
|
|
* Class methods
|
|
*/
|
|
+ (void) initialize
|
|
{
|
|
if (self == [NSCell class])
|
|
{
|
|
[self setVersion: 4];
|
|
colorClass = [NSColor class];
|
|
cellClass = [NSCell class];
|
|
fontClass = [NSFont class];
|
|
imageClass = [NSImage class];
|
|
/*
|
|
* Watch for changes to system colors, and simulate an initial change
|
|
* in order to set up our defaults.
|
|
*/
|
|
[[NSNotificationCenter defaultCenter]
|
|
addObserver: self
|
|
selector: @selector(_systemColorsChanged:)
|
|
name: NSSystemColorsDidChangeNotification
|
|
object: nil];
|
|
[self _systemColorsChanged: nil];
|
|
#if OS_API_VERSION(GS_API_MACOSX, GS_API_LATEST)
|
|
[self exposeBinding: NSTitleBinding];
|
|
#endif
|
|
}
|
|
}
|
|
|
|
+ (NSMenu*) defaultMenu
|
|
{
|
|
return nil;
|
|
}
|
|
|
|
+ (NSFocusRingType) defaultFocusRingType
|
|
{
|
|
return NSFocusRingTypeDefault;
|
|
}
|
|
|
|
/**<p>This class method returns NO. This method should be overrided by
|
|
subclasses.</p>
|
|
*/
|
|
+ (BOOL) prefersTrackingUntilMouseUp
|
|
{
|
|
return NO;
|
|
}
|
|
|
|
/*
|
|
* Instance methods
|
|
*/
|
|
- (id) init
|
|
{
|
|
return [self initTextCell: @""];
|
|
}
|
|
|
|
/**<p>Initializes and returns a new NSCell with a NSImage <var>anImage</var>.
|
|
This method sets the image position to <ref type="type"
|
|
id="NSCellImagePosition">NSImageOnly</ref> and the cell's type to
|
|
<ref type="type" id="NSCellType">NSImageCellType</ref>.</p>
|
|
<p>See Also: -initTextCell: </p>
|
|
*/
|
|
- (id) initImageCell: (NSImage*)anImage
|
|
{
|
|
_cell.type = NSImageCellType;
|
|
_cell_image = RETAIN (anImage);
|
|
_cell.image_position = NSImageOnly;
|
|
_font = RETAIN ([fontClass systemFontOfSize: 0]);
|
|
|
|
// Implicitly set by allocation:
|
|
//
|
|
//_font = nil;
|
|
//_cell.contents_is_attributed_string = NO;
|
|
//_cell.is_highlighted = NO;
|
|
//_cell.is_disabled = NO;
|
|
//_cell.is_editable = NO;
|
|
//_cell.is_rich_text = NO;
|
|
//_cell.imports_graphics = NO;
|
|
//_cell.shows_first_responder = NO;
|
|
//_cell.refuses_first_responder = NO;
|
|
//_cell.sends_action_on_end_editing = NO;
|
|
//_cell.is_bordered = NO;
|
|
//_cell.is_bezeled = NO;
|
|
//_cell.is_scrollable = NO;
|
|
//_cell.is_selectable = NO;
|
|
//_cell.state = 0;
|
|
//_cell.line_break_mode = NSLineBreakByWordWrapping;
|
|
_action_mask = NSLeftMouseUpMask;
|
|
_menu = [object_getClass(self) defaultMenu];
|
|
[self setFocusRingType: [object_getClass(self) defaultFocusRingType]];
|
|
|
|
return self;
|
|
}
|
|
/**<p>Initializes and returns a new NSCell with a NSString aString.
|
|
This method sets the cell's type to <ref type="type" id="NSCellType">
|
|
NSTextCellType</ref>.</p>
|
|
<p>See Also: -initImageCell: </p>
|
|
*/
|
|
- (id) initTextCell: (NSString*)aString
|
|
{
|
|
_cell.type = NSTextCellType;
|
|
_contents = RETAIN (aString);
|
|
_font = RETAIN ([fontClass systemFontOfSize: 0]);
|
|
|
|
// Implicitly set by allocation:
|
|
//
|
|
//_cell.contents_is_attributed_string = NO;
|
|
//_cell_image = nil;
|
|
//_cell.image_position = NSNoImage;
|
|
//_cell.is_disabled = NO;
|
|
//_cell.state = 0;
|
|
//_cell.is_highlighted = NO;
|
|
//_cell.is_editable = NO;
|
|
//_cell.is_bordered = NO;
|
|
//_cell.is_bezeled = NO;
|
|
//_cell.is_scrollable = NO;
|
|
//_cell.is_selectable = NO;
|
|
//_cell.line_break_mode = NSLineBreakByWordWrapping;
|
|
_action_mask = NSLeftMouseUpMask;
|
|
_menu = [object_getClass(self) defaultMenu];
|
|
[self setFocusRingType: [object_getClass(self) defaultFocusRingType]];
|
|
|
|
return self;
|
|
}
|
|
|
|
- (void) dealloc
|
|
{
|
|
// Remove all key value bindings for this object.
|
|
[GSKeyValueBinding unbindAllForObject: self];
|
|
TEST_RELEASE (_contents);
|
|
TEST_RELEASE (_cell_image);
|
|
TEST_RELEASE (_font);
|
|
TEST_RELEASE (_represented_object);
|
|
TEST_RELEASE (_object_value);
|
|
TEST_RELEASE (_formatter);
|
|
TEST_RELEASE (_menu);
|
|
|
|
[super dealloc];
|
|
}
|
|
|
|
/*
|
|
* Setting the NSCell's Value
|
|
*/
|
|
- (id) objectValue
|
|
{
|
|
if (_cell.has_valid_object_value)
|
|
{
|
|
return _object_value;
|
|
}
|
|
else
|
|
{
|
|
return nil;
|
|
}
|
|
}
|
|
|
|
- (BOOL) hasValidObjectValue
|
|
{
|
|
return _cell.has_valid_object_value;
|
|
}
|
|
|
|
/**<p>Returns the NSCell's value as a double. </p>
|
|
*<p>See Also: -setDoubleValue: </p>
|
|
*/
|
|
- (double) doubleValue
|
|
{
|
|
if ((_cell.has_valid_object_value == YES) &&
|
|
([_object_value respondsToSelector: @selector(doubleValue)]))
|
|
{
|
|
return [_object_value doubleValue];
|
|
}
|
|
else
|
|
{
|
|
return [[self stringValue] doubleValue];
|
|
}
|
|
}
|
|
|
|
/**<p>Returns the cell's value as a float. </p>
|
|
*<p>See Also: -setFloatValue: </p>
|
|
*/
|
|
- (float) floatValue
|
|
{
|
|
if ((_cell.has_valid_object_value == YES) &&
|
|
([_object_value respondsToSelector: @selector(floatValue)]))
|
|
{
|
|
return [_object_value floatValue];
|
|
}
|
|
else
|
|
{
|
|
return [[self stringValue] floatValue];
|
|
}
|
|
}
|
|
|
|
/**<p>Returns the cell's value as an int. </p>
|
|
*<p>See Also: -setIntValue:</p>
|
|
*/
|
|
- (int) intValue
|
|
{
|
|
if ((_cell.has_valid_object_value == YES) &&
|
|
([_object_value respondsToSelector: @selector(intValue)]))
|
|
{
|
|
return [_object_value intValue];
|
|
}
|
|
else
|
|
{
|
|
return [[self stringValue] intValue];
|
|
}
|
|
}
|
|
|
|
/**<p>Returns the cell's value as an NSInteger. </p>
|
|
*<p>See Also: -setIntegerValue:</p>
|
|
*/
|
|
- (NSInteger) integerValue
|
|
{
|
|
if ((_cell.has_valid_object_value == YES) &&
|
|
([_object_value respondsToSelector: @selector(integerValue)]))
|
|
{
|
|
return [_object_value integerValue];
|
|
}
|
|
else
|
|
{
|
|
return [[self stringValue] integerValue];
|
|
}
|
|
}
|
|
|
|
/**<p>Returns the cell's value as a NSString.</p>
|
|
*<p>See Also: -setStringValue: </p>
|
|
*/
|
|
- (NSString*) stringValue
|
|
{
|
|
if (nil == _contents)
|
|
{
|
|
return @"";
|
|
}
|
|
|
|
if (_cell.contents_is_attributed_string == NO)
|
|
{
|
|
// If we have a formatter this is also the string of the _object_value
|
|
return (NSString *)_contents;
|
|
}
|
|
else
|
|
{
|
|
return [(NSAttributedString *)_contents string];
|
|
}
|
|
}
|
|
|
|
- (void) setObjectValue: (id)object
|
|
{
|
|
id newContents;
|
|
|
|
ASSIGN (_object_value, object);
|
|
if (_formatter == nil)
|
|
{
|
|
if (object == nil || [object isKindOfClass: [NSString class]] == YES)
|
|
{
|
|
newContents = object;
|
|
_cell.contents_is_attributed_string = NO;
|
|
_cell.has_valid_object_value = YES;
|
|
|
|
// If we are in single line mode, trim the new line characters
|
|
if(_cell.uses_single_line_mode == YES)
|
|
{
|
|
newContents = [object stringByTrimmingCharactersInSet:
|
|
[NSCharacterSet newlineCharacterSet]];
|
|
}
|
|
}
|
|
else if ([object isKindOfClass: [NSAttributedString class]] == YES)
|
|
{
|
|
newContents = object;
|
|
_cell.contents_is_attributed_string = YES;
|
|
_cell.has_valid_object_value = YES;
|
|
|
|
// If we are in single line mode, trim the new line characters
|
|
if(_cell.uses_single_line_mode == YES)
|
|
{
|
|
newContents = [object stringByTrimmingCharactersInSet:
|
|
[NSCharacterSet newlineCharacterSet]];
|
|
}
|
|
}
|
|
else if ([_object_value respondsToSelector: @selector(attributedStringValue)])
|
|
{
|
|
newContents = [_object_value attributedStringValue];
|
|
_cell.contents_is_attributed_string = YES;
|
|
_cell.has_valid_object_value = YES;
|
|
}
|
|
else if ([_object_value respondsToSelector: @selector(stringValue)])
|
|
{
|
|
// If the thing that was assigned is not a string, but
|
|
// responds to stringValue then get that.
|
|
newContents = [_object_value stringValue];
|
|
_cell.contents_is_attributed_string = NO;
|
|
_cell.has_valid_object_value = YES;
|
|
}
|
|
else
|
|
{
|
|
newContents = [_object_value description];
|
|
_cell.contents_is_attributed_string = NO;
|
|
_cell.has_valid_object_value = YES;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
newContents = [_formatter stringForObjectValue: _object_value];
|
|
_cell.contents_is_attributed_string = NO;
|
|
if (newContents != nil)
|
|
{
|
|
_cell.has_valid_object_value = YES;
|
|
}
|
|
else
|
|
{
|
|
_cell.has_valid_object_value = NO;
|
|
}
|
|
}
|
|
|
|
ASSIGNCOPY(_contents, newContents);
|
|
}
|
|
|
|
|
|
/**<p>Sets the NSCell's value to aDouble.</p>
|
|
*<p>See Also: -doubleValue</p>
|
|
*/
|
|
- (void) setDoubleValue: (double)aDouble
|
|
{
|
|
NSNumber *number;
|
|
|
|
// NB: GNUstep can set a double value for an image cell
|
|
|
|
number = [NSNumber numberWithDouble: aDouble];
|
|
[self setObjectValue: number];
|
|
}
|
|
|
|
/**
|
|
*<p>Sets the NSCell's value to a aFloat. This used for example in
|
|
NSSliderCell</p>
|
|
*<p>See Also: -floatValue</p>
|
|
*/
|
|
- (void) setFloatValue: (float)aFloat
|
|
{
|
|
NSNumber *number;
|
|
|
|
// NB: GNUstep can set a float value for an image cell.
|
|
// NSSliderCell is an example of it!
|
|
|
|
number = [NSNumber numberWithFloat: aFloat];
|
|
[self setObjectValue: number];
|
|
}
|
|
|
|
|
|
/**
|
|
*<p>Sets the NSCell's value to anInt.</p>
|
|
*<p>See Also: -intValue</p>
|
|
*/
|
|
- (void) setIntValue: (int)anInt
|
|
{
|
|
NSNumber *number;
|
|
|
|
// NB: GNUstep can set an int value for an image cell.
|
|
|
|
number = [NSNumber numberWithInt: anInt];
|
|
[self setObjectValue: number];
|
|
}
|
|
|
|
/**
|
|
*<p>Sets the NSCell's value to anInt.</p>
|
|
*<p>See Also: -integerValue</p>
|
|
*/
|
|
- (void) setIntegerValue: (NSInteger)anInt
|
|
{
|
|
NSNumber *number;
|
|
|
|
// NB: GNUstep can set an int value for an image cell.
|
|
|
|
number = [NSNumber numberWithInteger: anInt];
|
|
[self setObjectValue: number];
|
|
}
|
|
|
|
/**<p>Sets the cell's value to a NSString.
|
|
The NSCell's type is set to NSTextCellType if needed</p>
|
|
<p>See Also: -stringValue</p>
|
|
*/
|
|
- (void) setStringValue: (NSString*)aString
|
|
{
|
|
/* We warn about nil for compatibiliy with MacOS X, which refuses
|
|
nil. */
|
|
if (aString == nil)
|
|
{
|
|
NSDebugMLLog (@"MacOSXCompatibility",
|
|
@"Attempt to use nil as string value");
|
|
}
|
|
|
|
if (_cell.type != NSTextCellType)
|
|
{
|
|
[self setType: NSTextCellType];
|
|
}
|
|
|
|
if (_formatter == nil)
|
|
{
|
|
[self setObjectValue: aString];
|
|
}
|
|
else
|
|
{
|
|
id newObjectValue;
|
|
|
|
if ([_formatter getObjectValue: &newObjectValue
|
|
forString: aString
|
|
errorDescription: NULL])
|
|
{
|
|
[self setObjectValue: newObjectValue];
|
|
}
|
|
else
|
|
{
|
|
ASSIGNCOPY(_contents, aString);
|
|
_cell.contents_is_attributed_string = NO;
|
|
_cell.has_valid_object_value = NO;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**<p>Returns some NSCell's attributes for the specified <ref type="type"
|
|
id="NSCellAttribute">NSCellAttribute</ref></p>
|
|
<p>See Also: -setCellAttribute:to:</p>
|
|
*/
|
|
- (NSInteger) cellAttribute: (NSCellAttribute)aParameter
|
|
{
|
|
switch (aParameter)
|
|
{
|
|
case NSCellDisabled: return _cell.is_disabled;
|
|
case NSCellState: return _cell.state;
|
|
case NSCellEditable: return _cell.is_editable;
|
|
case NSCellHighlighted: return _cell.is_highlighted;
|
|
case NSCellIsBordered: return _cell.is_bordered;
|
|
case NSCellAllowsMixedState: return _cell.allows_mixed_state;
|
|
|
|
/*
|
|
case NSPushInCell: return 0;
|
|
case NSChangeGrayCell: return 0;
|
|
case NSCellLightsByContents: return 0;
|
|
case NSCellLightsByGray: return 0;
|
|
case NSChangeBackgroundCell: return 0;
|
|
case NSCellLightsByBackground: return 0;
|
|
case NSCellChangesContents: return 0;
|
|
case NSCellIsInsetButton: return 0;
|
|
*/
|
|
case NSCellHasOverlappingImage:
|
|
{
|
|
return _cell.image_position == NSImageOverlaps;
|
|
}
|
|
case NSCellHasImageHorizontal:
|
|
{
|
|
return (_cell.image_position == NSImageRight)
|
|
|| (_cell.image_position == NSImageLeft);
|
|
}
|
|
case NSCellHasImageOnLeftOrBottom:
|
|
{
|
|
return (_cell.image_position == NSImageBelow)
|
|
|| (_cell.image_position == NSImageLeft);
|
|
}
|
|
default:
|
|
{
|
|
NSWarnLog (@"cell attribute %d not supported", (int)aParameter);
|
|
break;
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
|
|
/**<p>TODO</p>
|
|
*<p>See Also: -cellAttribute:</p>
|
|
*/
|
|
- (void) setCellAttribute: (NSCellAttribute)aParameter to: (NSInteger)value
|
|
{
|
|
switch (aParameter)
|
|
{
|
|
case NSCellDisabled:
|
|
{
|
|
_cell.is_disabled = value;
|
|
break;
|
|
}
|
|
case NSCellState:
|
|
{
|
|
_cell.state = value;
|
|
break;
|
|
}
|
|
case NSCellEditable:
|
|
{
|
|
_cell.is_editable = value;
|
|
break;
|
|
}
|
|
case NSCellHighlighted:
|
|
{
|
|
_cell.is_highlighted = value;
|
|
break;
|
|
}
|
|
case NSCellHasOverlappingImage:
|
|
{
|
|
if (value)
|
|
{
|
|
_cell.image_position = NSImageOverlaps;
|
|
}
|
|
else
|
|
{
|
|
if (_cell.image_position == NSImageOverlaps)
|
|
{
|
|
_cell.image_position = NSImageLeft;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case NSCellHasImageHorizontal:
|
|
{
|
|
if (value)
|
|
{
|
|
if (_cell.image_position != NSImageLeft
|
|
&& _cell.image_position != NSImageRight)
|
|
{
|
|
_cell.image_position = NSImageLeft;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (_cell.image_position == NSImageLeft)
|
|
{
|
|
_cell.image_position = NSImageAbove;
|
|
}
|
|
else if (_cell.image_position == NSImageRight)
|
|
{
|
|
_cell.image_position = NSImageBelow;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
case NSCellHasImageOnLeftOrBottom:
|
|
{
|
|
if (value)
|
|
{
|
|
if (_cell.image_position == NSImageAbove)
|
|
{
|
|
_cell.image_position = NSImageBelow;
|
|
}
|
|
else
|
|
{
|
|
_cell.image_position = NSImageLeft;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (_cell.image_position == NSImageBelow)
|
|
{
|
|
_cell.image_position = NSImageAbove;
|
|
}
|
|
else
|
|
{
|
|
_cell.image_position = NSImageRight;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
/*
|
|
case NSCellChangesContents:
|
|
_cell. = value;
|
|
break;
|
|
case NSCellIsInsetButton:
|
|
_cell. = value;
|
|
break;
|
|
*/
|
|
case NSCellIsBordered:
|
|
{
|
|
_cell.is_bordered = value;
|
|
break;
|
|
}
|
|
case NSCellAllowsMixedState:
|
|
{
|
|
_cell.allows_mixed_state = value;
|
|
break;
|
|
}
|
|
default:
|
|
{
|
|
NSWarnLog (@"cell attribute %d not supported", (int)aParameter);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**<p>Sets the NSCell's type. See <ref type="type" id="NSCellType">NSCellType
|
|
</ref>.If the cell is set to NSTextCellType, the cell is given
|
|
a default title and is reset to the default system font.</p>
|
|
<p>See Also: -type</p>
|
|
*/
|
|
- (void) setType: (NSCellType)aType
|
|
{
|
|
if (_cell.type == aType)
|
|
{
|
|
return;
|
|
}
|
|
|
|
_cell.type = aType;
|
|
switch (_cell.type)
|
|
{
|
|
case NSTextCellType:
|
|
{
|
|
ASSIGN (_contents, @"title");
|
|
_cell.contents_is_attributed_string = NO;
|
|
/* Doc says we have to reset the font too. */
|
|
ASSIGN (_font, [fontClass systemFontOfSize: 0]);
|
|
break;
|
|
}
|
|
case NSImageCellType:
|
|
{
|
|
TEST_RELEASE (_cell_image);
|
|
_cell_image = nil;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/**<p>Returns the cell's type. Returns NSNullCellType if the
|
|
cell's type flag is set to NSImageCellType and if the cell's image
|
|
is nil. See <ref type="type" id="NSCellType">NSCellType</ref> for more
|
|
information.</p><p>See Also -setType:</p>
|
|
*/
|
|
- (NSCellType) type
|
|
{
|
|
if (_cell.type == NSImageCellType && _cell_image == nil)
|
|
return NSNullCellType;
|
|
|
|
return _cell.type;
|
|
}
|
|
|
|
|
|
/**<p>Returns whether the NSCell can respond to mouse events.</p>
|
|
*<p>See Also: -setEnabled:</p>
|
|
*/
|
|
- (BOOL) isEnabled
|
|
{
|
|
return !_cell.is_disabled;
|
|
}
|
|
|
|
/**<p>Sets whether the NSCell can respond to mouse events</p>
|
|
<p>See Also: -isEnabled</p>
|
|
*/
|
|
- (void) setEnabled: (BOOL)flag
|
|
{
|
|
_cell.is_disabled = !flag;
|
|
}
|
|
|
|
/**<p>Returns whether the NSCell has a bezeled border. By default a NSCell
|
|
has no bezeled border</p><p>See Also: -setBezeled:</p>
|
|
*/
|
|
- (BOOL) isBezeled
|
|
{
|
|
return _cell.is_bezeled;
|
|
}
|
|
|
|
/**<p>Returns whether the NSCell has a border. By default a NSCell has
|
|
border</p><p>See Also: -setBordered: -setBezeled: -isBezeled</p>
|
|
*/
|
|
- (BOOL) isBordered
|
|
{
|
|
return _cell.is_bordered;
|
|
}
|
|
|
|
/**<p>Returns whether the cell is opaque. Return NO by default</p>
|
|
*/
|
|
- (BOOL) isOpaque
|
|
{
|
|
return NO;
|
|
}
|
|
|
|
/**<p>Sets whether the cell has a bezeled border.
|
|
If this method is called, the bordered flag is turn off.
|
|
By default a NSCell has no bezeled border</p>
|
|
<p>See Also: -isBezeled -setBordered: -isBordered</p>
|
|
*/
|
|
- (void) setBezeled: (BOOL)flag
|
|
{
|
|
_cell.is_bezeled = flag;
|
|
_cell.is_bordered = NO;
|
|
}
|
|
|
|
/**<p>Sets whether the cell has a border. If this method is called,
|
|
the bezeled flag is turn off. By default a NSCell has no border</p>
|
|
<p>See Also: -isBordered -setBezeled: -isBezeled</p>
|
|
*/
|
|
- (void) setBordered: (BOOL)flag
|
|
{
|
|
_cell.is_bordered = flag;
|
|
_cell.is_bezeled = NO;
|
|
}
|
|
|
|
- (NSFocusRingType) focusRingType
|
|
{
|
|
return _cell.focus_ring_type;
|
|
}
|
|
|
|
- (void) setFocusRingType: (NSFocusRingType)type
|
|
{
|
|
_cell.focus_ring_type = type;
|
|
}
|
|
|
|
/**<p>Sets the NSCell's state. Please use always symbolic constants when
|
|
calling this method. The integer values could be changed in the this
|
|
implementation. (Currently they match the Cocoa values but they are
|
|
quite strange)</p> <p>See Also: -state</p>
|
|
*/
|
|
- (void) setState: (NSInteger)value
|
|
{
|
|
/* We do exactly as in macosx when value is not NSOnState,
|
|
* NSOffState, NSMixedState, even if their behaviour (value < 0 ==>
|
|
* NSMixedState) is a bit strange. We could decide to do
|
|
* differently in the future, so please use always symbolic
|
|
* constants when calling this method, this way your code won't be
|
|
* broken by changes. */
|
|
if (value > 0 || (value < 0 && _cell.allows_mixed_state == NO))
|
|
{
|
|
_cell.state = NSOnState;
|
|
}
|
|
else if (value == 0)
|
|
{
|
|
_cell.state = NSOffState;
|
|
}
|
|
else
|
|
{
|
|
_cell.state = NSMixedState;
|
|
}
|
|
}
|
|
|
|
/**<p>Returns the NSCell's state</p>
|
|
<p>See Also: -setState: </p>
|
|
*/
|
|
- (NSInteger) state
|
|
{
|
|
return _cell.state;
|
|
}
|
|
|
|
- (BOOL) allowsMixedState
|
|
{
|
|
return _cell.allows_mixed_state;
|
|
}
|
|
|
|
- (void) setAllowsMixedState: (BOOL)flag
|
|
{
|
|
_cell.allows_mixed_state = flag;
|
|
if (!flag && _cell.state == NSMixedState)
|
|
{
|
|
[self setNextState];
|
|
}
|
|
}
|
|
|
|
- (NSInteger) nextState
|
|
{
|
|
switch (_cell.state)
|
|
{
|
|
case NSOnState:
|
|
{
|
|
return NSOffState;
|
|
}
|
|
case NSOffState:
|
|
{
|
|
if (_cell.allows_mixed_state)
|
|
{
|
|
return NSMixedState;
|
|
}
|
|
else
|
|
{
|
|
return NSOnState;
|
|
}
|
|
}
|
|
case NSMixedState:
|
|
default:
|
|
{
|
|
return NSOnState;
|
|
}
|
|
}
|
|
}
|
|
|
|
- (void) setNextState
|
|
{
|
|
[self setState: [self nextState]];
|
|
}
|
|
|
|
/**<p>Returns the alignment of the text used in the NSCell. See
|
|
<ref type="type" id="NSTextAlignment">NSTextAlignment</ref> for more
|
|
informations. By default the text alignment is <ref type="type"
|
|
id="NSTextAlignment">NSJustifiedTextAlignment</ref></p>
|
|
<p>See Also: -setAlignment:</p>
|
|
*/
|
|
- (NSTextAlignment) alignment
|
|
{
|
|
return _cell.text_align;
|
|
}
|
|
|
|
/** <p>Returns the font of the text used in the NSCell</p>
|
|
<p>See Also: -setFont:</p>
|
|
*/
|
|
- (NSFont*) font
|
|
{
|
|
return _font;
|
|
}
|
|
|
|
/**<p>Returns whether the cell is editable.By default a NSCell is not editable.
|
|
</p><p>See Also: -setEditable:</p>
|
|
*/
|
|
- (BOOL) isEditable
|
|
{
|
|
return _cell.is_editable;
|
|
}
|
|
|
|
/**<p>Returns whether the cell is selectable. This method returns YES if
|
|
the cell is selectable or editable. NO otherwise</p>
|
|
<p>See Also: -setSelectable: -isEditable -setEditable: </p>
|
|
*/
|
|
- (BOOL) isSelectable
|
|
{
|
|
return _cell.is_selectable || _cell.is_editable;
|
|
}
|
|
|
|
/**<p>Returns whether the NSCell is scrollable. By default a NSCell is not
|
|
scrollable</p><p>See Also: -setScrollable:</p>
|
|
*/
|
|
- (BOOL) isScrollable
|
|
{
|
|
return _cell.is_scrollable;
|
|
}
|
|
|
|
/**<p>Sets the alignment of the text. See <ref type="type"
|
|
id="NSTextAlignment">NSTextAlignment</ref>.</p><p>See Also: -alignment </p>
|
|
*/
|
|
- (void) setAlignment: (NSTextAlignment)mode
|
|
{
|
|
// This does not have any influence on attributed strings
|
|
_cell.text_align = mode;
|
|
}
|
|
|
|
/**<p>Sets whether the NSCell's text is editable.</p>
|
|
<p>See Also: -isEditable -setSelectable: -isSelectable</p>
|
|
*/
|
|
- (void) setEditable: (BOOL)flag
|
|
{
|
|
/*
|
|
* The cell_editable flag is also checked to see if the cell is
|
|
* selectable so turning edit on also turns selectability on (until
|
|
* edit is turned off again).
|
|
*/
|
|
_cell.is_editable = flag;
|
|
}
|
|
|
|
/**<p>Sets the text font. The NSCell's type is set to NSTextCellType if needed
|
|
</p><p>See Also: -font -setType: -type</p>
|
|
*/
|
|
- (void) setFont: (NSFont*)fontObject
|
|
{
|
|
if (_cell.type != NSTextCellType)
|
|
{
|
|
[self setType: NSTextCellType];
|
|
}
|
|
|
|
// This does not have any influence on attributed strings
|
|
ASSIGN (_font, fontObject);
|
|
}
|
|
|
|
/**<p>Sets whether the cell selectable. Making a cell unselectable also
|
|
* makes it uneditable until a -setEditable: re-enables it.</p>
|
|
*<p>See Also: -isSelectable -setEditable: -isEditable</p>
|
|
*/
|
|
- (void) setSelectable: (BOOL)flag
|
|
{
|
|
_cell.is_selectable = flag;
|
|
|
|
if (!flag)
|
|
_cell.is_editable = NO;
|
|
}
|
|
|
|
/**<p>Sets whether the NCell is scrollable. By default a NSCell is not
|
|
scrollable</p><p>See Also: -isSelectable</p>
|
|
*/
|
|
- (void) setScrollable: (BOOL)flag
|
|
{
|
|
_cell.is_scrollable = flag;
|
|
if (flag)
|
|
{
|
|
[self setWraps: NO];
|
|
}
|
|
}
|
|
|
|
- (void) setWraps: (BOOL)flag
|
|
{
|
|
if (flag)
|
|
{
|
|
if (![self wraps])
|
|
[self setLineBreakMode: NSLineBreakByWordWrapping];
|
|
}
|
|
else
|
|
{
|
|
if ([self wraps])
|
|
[self setLineBreakMode: NSLineBreakByClipping];
|
|
}
|
|
}
|
|
|
|
- (BOOL) wraps
|
|
{
|
|
return _cell.line_break_mode == NSLineBreakByWordWrapping
|
|
|| _cell.line_break_mode == NSLineBreakByCharWrapping;
|
|
}
|
|
|
|
- (void) setAttributedStringValue: (NSAttributedString*)attribStr
|
|
{
|
|
/* Hmm. FIXME. Not sure what to do here. */
|
|
if (_formatter != nil)
|
|
{
|
|
id newObjectValue;
|
|
|
|
if ([_formatter getObjectValue: &newObjectValue
|
|
forString: [attribStr string]
|
|
errorDescription: NULL] == YES)
|
|
{
|
|
[self setObjectValue: newObjectValue];
|
|
/* What about the attributed string ? We are loosing it. */
|
|
return;
|
|
}
|
|
_cell.has_valid_object_value = NO;
|
|
}
|
|
else
|
|
{
|
|
_cell.has_valid_object_value = YES;
|
|
ASSIGN (_object_value, attribStr);
|
|
}
|
|
|
|
ASSIGN (_contents, attribStr);
|
|
_cell.contents_is_attributed_string = YES;
|
|
}
|
|
|
|
- (NSAttributedString*) attributedStringValue
|
|
{
|
|
if (_formatter != nil)
|
|
{
|
|
NSDictionary *attributes;
|
|
NSAttributedString *attrStr;
|
|
|
|
attributes = [self _nonAutoreleasedTypingAttributes];
|
|
attrStr = [_formatter attributedStringForObjectValue: _object_value
|
|
withDefaultAttributes: attributes];
|
|
RELEASE(attributes);
|
|
if (attrStr != nil)
|
|
{
|
|
return attrStr;
|
|
}
|
|
}
|
|
|
|
/* In all other cases */
|
|
if (_cell.contents_is_attributed_string &&
|
|
nil != _contents)
|
|
{
|
|
return (NSAttributedString *)_contents;
|
|
}
|
|
else
|
|
{
|
|
NSDictionary *dict;
|
|
NSAttributedString *attrStr;
|
|
|
|
dict = [self _nonAutoreleasedTypingAttributes];
|
|
attrStr = [[NSAttributedString alloc] initWithString: [self stringValue]
|
|
attributes: dict];
|
|
RELEASE(dict);
|
|
return AUTORELEASE(attrStr);
|
|
}
|
|
}
|
|
|
|
- (void) setAllowsEditingTextAttributes: (BOOL)flag
|
|
{
|
|
_cell.is_rich_text = flag;
|
|
if (!flag)
|
|
_cell.imports_graphics = NO;
|
|
}
|
|
|
|
- (BOOL) allowsEditingTextAttributes
|
|
{
|
|
return _cell.is_rich_text;
|
|
}
|
|
|
|
- (void) setImportsGraphics: (BOOL)flag
|
|
{
|
|
_cell.imports_graphics = flag;
|
|
if (flag)
|
|
_cell.is_rich_text = YES;
|
|
}
|
|
|
|
- (BOOL) importsGraphics
|
|
{
|
|
return _cell.imports_graphics;
|
|
}
|
|
|
|
- (NSString*) title
|
|
{
|
|
return [self stringValue];
|
|
}
|
|
|
|
- (void) setTitle: (NSString*)aString
|
|
{
|
|
[self setStringValue: aString];
|
|
}
|
|
|
|
- (NSLineBreakMode) lineBreakMode
|
|
{
|
|
return _cell.line_break_mode;
|
|
}
|
|
|
|
- (void) setLineBreakMode: (NSLineBreakMode)mode
|
|
{
|
|
if (mode == NSLineBreakByCharWrapping || mode == NSLineBreakByWordWrapping)
|
|
{
|
|
_cell.is_scrollable = NO;
|
|
}
|
|
_cell.line_break_mode = mode;
|
|
}
|
|
|
|
- (NSWritingDirection) baseWritingDirection
|
|
{
|
|
return _cell.base_writing_direction;
|
|
}
|
|
|
|
- (void) setBaseWritingDirection: (NSWritingDirection)direction
|
|
{
|
|
_cell.base_writing_direction = direction;
|
|
}
|
|
|
|
/**<p>Implemented by subclasses to return the action method.
|
|
The NSCell implementaiton returns NULL.</p>
|
|
<p>See Also: -setAction: -setTarget: -target</p>
|
|
*/
|
|
- (SEL) action
|
|
{
|
|
return NULL;
|
|
}
|
|
|
|
/** <p>Implemented by subclasses to set the action method.
|
|
The NSCell implementation raises a NSInternalInconsistencyException</p>
|
|
<p>See Also: -action -setTarget: -target</p>
|
|
*/
|
|
- (void) setAction: (SEL)aSelector
|
|
{
|
|
[NSException raise: NSInternalInconsistencyException
|
|
format: @"attempt to set an action in an NSCell"];
|
|
}
|
|
|
|
/**<p>Implemented by subclasses to set the target object.
|
|
The NSCell implementation raises a NSInternalInconsistencyException</p>
|
|
<p>See Also: -target -setAction: -action</p>
|
|
*/
|
|
- (void) setTarget: (id)anObject
|
|
{
|
|
[NSException raise: NSInternalInconsistencyException
|
|
format: @"attempt to set a target in an NSCell"];
|
|
}
|
|
|
|
/**<p>Implemented by subclass to return the target object.
|
|
The NSCell implementation returns nil</p>
|
|
<p>See Also: -setTarget: -setAction: -action</p>
|
|
*/
|
|
- (id) target
|
|
{
|
|
return nil;
|
|
}
|
|
|
|
/**<p>Returns whether the cell can continuously send its action messages.</p>
|
|
<p>See Also: -setContinuous:</p>
|
|
*/
|
|
- (BOOL) isContinuous
|
|
{
|
|
// Some subclasses should redefine this with NSLeftMouseDraggedMask
|
|
return (_action_mask & NSPeriodicMask) != 0;
|
|
}
|
|
|
|
/**<p>Sets whether the cell can continuously send its action messages.</p>
|
|
*<p>See Also: -isContinuous</p>
|
|
*/
|
|
- (void) setContinuous: (BOOL)flag
|
|
{
|
|
// Some subclasses should redefine this with NSLeftMouseDraggedMask
|
|
if (flag)
|
|
{
|
|
_action_mask |= NSPeriodicMask;
|
|
}
|
|
else
|
|
{
|
|
_action_mask &= ~NSPeriodicMask;
|
|
}
|
|
}
|
|
|
|
/**<p>TODO Explain</p>
|
|
*/
|
|
- (NSInteger) sendActionOn: (NSInteger)mask
|
|
{
|
|
NSUInteger previousMask = _action_mask;
|
|
|
|
_action_mask = mask;
|
|
|
|
return previousMask;
|
|
}
|
|
|
|
/**<p>Returns the NSCell's image if the NSCell's type is <ref type="type"
|
|
id="NSCellType">NSImageCellType</ref>,
|
|
returns nil otherwise.</p>
|
|
<p>See Also: -setImage: -setType: -type</p>
|
|
*/
|
|
- (NSImage*) image
|
|
{
|
|
if (_cell.type == NSImageCellType)
|
|
{
|
|
return _cell_image;
|
|
}
|
|
else
|
|
return nil;
|
|
}
|
|
|
|
/**<p>Sets the NSCell's image to anImage. This method sets the cell's type
|
|
to NSImageCellType if needed. Raises an NSInvalidArgumentException if
|
|
the anImage is not an NSImage (sub)class. The new image is retained and the
|
|
old one is released</p><p>See Also: -image</p>
|
|
*/
|
|
- (void) setImage: (NSImage*)anImage
|
|
{
|
|
if (anImage)
|
|
{
|
|
NSAssert ([anImage isKindOfClass: imageClass],
|
|
NSInvalidArgumentException);
|
|
}
|
|
|
|
if (_cell.type != NSImageCellType)
|
|
{
|
|
[self setType: NSImageCellType];
|
|
}
|
|
|
|
ASSIGN (_cell_image, anImage);
|
|
}
|
|
|
|
/**<p>Implemented by sublclasses to assigns the tag <var>anInt</var>.
|
|
The NSCell implementation raises an NSInvalidArgumentException.</p>
|
|
<p>See Also: -tag</p>
|
|
*/
|
|
- (void) setTag: (NSInteger)anInt
|
|
{
|
|
[NSException raise: NSInternalInconsistencyException
|
|
format: @"attempt to set a tag in an NSCell"];
|
|
}
|
|
|
|
/**<p>Implemented by subclasses to Return the tag.
|
|
The NSCell implementation returns -1 </p><p>See Also: -setTag:</p>
|
|
*/
|
|
- (NSInteger) tag
|
|
{
|
|
return -1;
|
|
}
|
|
|
|
/*
|
|
* Formatting Data
|
|
*/
|
|
- (void) setFloatingPointFormat: (BOOL)autoRange
|
|
left: (NSUInteger)leftDigits
|
|
right: (NSUInteger)rightDigits
|
|
{
|
|
NSNumberFormatter *formatter = [[NSNumberFormatter alloc] init];
|
|
NSMutableString *format = [[NSMutableString alloc] init];
|
|
|
|
if (autoRange)
|
|
{
|
|
NSUInteger fieldWidth = leftDigits + rightDigits + 1;
|
|
|
|
// FIXME: this does not fully match the documentation.
|
|
while (fieldWidth--)
|
|
{
|
|
[format appendString: @"#"];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
while (leftDigits--)
|
|
{
|
|
[format appendString: @"#"];
|
|
}
|
|
[format appendString: @"."];
|
|
while (rightDigits--)
|
|
{
|
|
[format appendString: @"0"];
|
|
}
|
|
}
|
|
|
|
[formatter setFormat: format];
|
|
RELEASE(format);
|
|
[self setFormatter: formatter];
|
|
RELEASE(formatter);
|
|
}
|
|
|
|
- (void) setFormatter: (NSFormatter*)newFormatter
|
|
{
|
|
ASSIGN(_formatter, newFormatter);
|
|
}
|
|
|
|
- (id) formatter
|
|
{
|
|
return _formatter;
|
|
}
|
|
|
|
/**<p> TODO</p>
|
|
*/
|
|
- (NSInteger) entryType
|
|
{
|
|
return _cell.entry_type;
|
|
}
|
|
|
|
/** <p>TODO</p>
|
|
*/
|
|
- (void) setEntryType: (NSInteger)aType
|
|
{
|
|
[self setType: NSTextCellType];
|
|
// TODO: This should select a suitable formatter
|
|
_cell.entry_type = aType;
|
|
}
|
|
|
|
- (BOOL) isEntryAcceptable: (NSString*)aString
|
|
{
|
|
if ((_formatter != nil) && ![aString isEqualToString: @""])
|
|
{
|
|
id newObjectValue;
|
|
|
|
return [_formatter getObjectValue: &newObjectValue
|
|
forString: aString
|
|
errorDescription: NULL];
|
|
}
|
|
else
|
|
{
|
|
return YES;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Menu
|
|
*/
|
|
- (void) setMenu: (NSMenu*)aMenu
|
|
{
|
|
ASSIGN (_menu, aMenu);
|
|
}
|
|
|
|
- (NSMenu*) menu
|
|
{
|
|
return _menu;
|
|
}
|
|
|
|
- (NSMenu*) menuForEvent: (NSEvent*)anEvent
|
|
inRect: (NSRect)cellFrame
|
|
ofView: (NSView*)aView
|
|
{
|
|
return [self menu];
|
|
}
|
|
|
|
/**
|
|
* Compares the reciever to another to another NSCell.
|
|
* The argument must be an NSCell sublclass and have
|
|
* the NSCellType NSTextCellType. Returns the result
|
|
* of the comparison of each cell's stringValue.
|
|
*/
|
|
- (NSComparisonResult) compare: (id)otherCell
|
|
{
|
|
if ([otherCell isKindOfClass: cellClass] == NO)
|
|
{
|
|
[NSException raise: NSBadComparisonException
|
|
format: @"NSCell comparison with non-NSCell"];
|
|
}
|
|
if (_cell.type != NSTextCellType
|
|
|| ((NSCell*)otherCell)->_cell.type != NSTextCellType)
|
|
{
|
|
[NSException raise: NSBadComparisonException
|
|
format: @"Comparison between non-text cells"];
|
|
}
|
|
/* We shouldn't access instance variables directly as subclasses
|
|
may override stringValue to retrieve the value from somewhere else. */
|
|
return [[self stringValue] compare: [(NSCell*)otherCell stringValue]];
|
|
}
|
|
|
|
/*
|
|
* Should this cell respond to keyboard input?
|
|
*/
|
|
- (BOOL) acceptsFirstResponder
|
|
{
|
|
return _cell.is_disabled == NO && _cell.refuses_first_responder == NO;
|
|
}
|
|
|
|
- (void) setShowsFirstResponder: (BOOL)flag
|
|
{
|
|
_cell.shows_first_responder = flag;
|
|
}
|
|
|
|
- (BOOL) showsFirstResponder
|
|
{
|
|
return _cell.shows_first_responder;
|
|
}
|
|
|
|
- (void) setTitleWithMnemonic: (NSString*)aString
|
|
{
|
|
NSRange r = [aString rangeOfString: @"&"];
|
|
|
|
if (r.length > 0)
|
|
{
|
|
NSUInteger location = r.location;
|
|
|
|
[self setTitle: [[aString substringToIndex: location]
|
|
stringByAppendingString:
|
|
[aString substringFromIndex: NSMaxRange(r)]]];
|
|
// TODO: We should underline this character
|
|
[self setMnemonicLocation: location];
|
|
}
|
|
}
|
|
|
|
- (NSString*) mnemonic
|
|
{
|
|
NSUInteger location = [self mnemonicLocation];
|
|
NSString *c = [self title];
|
|
|
|
if ((location == NSNotFound) || location >= [c length])
|
|
return @"";
|
|
|
|
return [c substringWithRange: NSMakeRange (location, 1)];
|
|
}
|
|
|
|
- (void) setMnemonicLocation: (NSUInteger)location
|
|
{
|
|
_cell.mnemonic_location = location;
|
|
}
|
|
|
|
- (NSUInteger) mnemonicLocation
|
|
{
|
|
return _cell.mnemonic_location;
|
|
}
|
|
|
|
- (BOOL) refusesFirstResponder
|
|
{
|
|
return _cell.refuses_first_responder;
|
|
}
|
|
|
|
- (void) setRefusesFirstResponder: (BOOL)flag
|
|
{
|
|
_cell.refuses_first_responder = flag;
|
|
}
|
|
|
|
/**
|
|
* Simulates a single click in the cell (only works with controls which have
|
|
* no more than one cell). This method is deprecated,
|
|
* performClickWithFrame:inView: is the right method to use now.
|
|
*/
|
|
- (void) performClick: (id)sender
|
|
{
|
|
NSView *cv = [self controlView];
|
|
|
|
if (cv != nil)
|
|
[self performClickWithFrame: [cv bounds] inView: cv];
|
|
}
|
|
|
|
/*
|
|
* Helper method used to send actions. Sender normally is [self controlView].
|
|
*/
|
|
- (BOOL) _sendActionFrom: (id)sender
|
|
{
|
|
SEL action = [self action];
|
|
|
|
if ([sender respondsToSelector: @selector(sendAction:to:)])
|
|
{
|
|
return [sender sendAction: action to: [self target]];
|
|
}
|
|
else
|
|
{
|
|
if (sender == nil)
|
|
sender = self;
|
|
|
|
if (action)
|
|
{
|
|
return [NSApp sendAction: action to: [self target] from: sender];
|
|
}
|
|
}
|
|
|
|
return NO;
|
|
}
|
|
|
|
/**
|
|
* Simulates a single click in the cell.
|
|
* The display of the cell with this event
|
|
* occurs in the area delimited by <var>cellFrame</var> within
|
|
* <var>controlView</var>.
|
|
*/
|
|
- (void) performClickWithFrame: (NSRect)cellFrame inView: (NSView *)controlView
|
|
{
|
|
if (_cell.is_disabled == YES)
|
|
{
|
|
return;
|
|
}
|
|
|
|
[self setNextState];
|
|
|
|
if ((controlView != nil) && [controlView canDraw])
|
|
{
|
|
NSWindow *cvWin = [controlView window];
|
|
NSDate *limit = [NSDate dateWithTimeIntervalSinceNow: 0.1];
|
|
|
|
[controlView lockFocus];
|
|
[self highlight: YES withFrame: cellFrame inView: controlView];
|
|
[cvWin flushWindow];
|
|
|
|
// Wait approx 1/10 seconds
|
|
[[NSRunLoop currentRunLoop] runUntilDate: limit];
|
|
|
|
[self highlight: NO withFrame: cellFrame inView: controlView];
|
|
[cvWin flushWindow];
|
|
[controlView unlockFocus];
|
|
}
|
|
|
|
[self _sendActionFrom: controlView];
|
|
}
|
|
|
|
/*
|
|
* Deriving values from other objects (not necessarily cells)
|
|
*/
|
|
- (void) takeObjectValueFrom: (id)sender
|
|
{
|
|
[self setObjectValue: [sender objectValue]];
|
|
}
|
|
|
|
/** <p>Sets the NSCell's double value to sender's double value</p>
|
|
<p>See Also: -setDoubleValue:</p>
|
|
*/
|
|
- (void) takeDoubleValueFrom: (id)sender
|
|
{
|
|
[self setDoubleValue: [sender doubleValue]];
|
|
}
|
|
|
|
/** <p>Sets the NSCell's float value to sender's float value</p>
|
|
<p>See Also: -setFloatValue:</p>
|
|
*/
|
|
- (void) takeFloatValueFrom: (id)sender
|
|
{
|
|
[self setFloatValue: [sender floatValue]];
|
|
}
|
|
|
|
/** <p>Sets the NSCell's int value to sender's int value</p>
|
|
<p>See Also: -setIntValue:</p>
|
|
*/
|
|
- (void) takeIntValueFrom: (id)sender
|
|
{
|
|
[self setIntValue: [sender intValue]];
|
|
}
|
|
|
|
/** <p>Sets the NSCell's NSInteger value to sender's NSInteger value</p>
|
|
<p>See Also: -setIntegerValue:</p>
|
|
*/
|
|
- (void) takeIntegerValueFrom: (id)sender
|
|
{
|
|
[self setIntegerValue: [sender integerValue]];
|
|
}
|
|
|
|
/** <p>Sets the NSCell's NSString value to sender's NSSting value</p>
|
|
<p>See Also: -setStringValue:</p>
|
|
*/
|
|
- (void) takeStringValueFrom: (id)sender
|
|
{
|
|
[self setStringValue: [sender stringValue]];
|
|
}
|
|
|
|
/** <p>Returns the NSCell's represented object</p>
|
|
<p>See Also: -setRepresentedObject:</p>
|
|
*/
|
|
- (id) representedObject
|
|
{
|
|
return _represented_object;
|
|
}
|
|
|
|
/** <p>Sets the NSCell's represented object to <var>anObject</var>.
|
|
anObject will be retain.</p><p>See Also: -representedObject</p>
|
|
*/
|
|
- (void) setRepresentedObject: (id)anObject
|
|
{
|
|
/* Ahm - not nice - the RETAIN here could cause retain cycles - anyway. */
|
|
ASSIGN (_represented_object, anObject);
|
|
}
|
|
|
|
- (NSBackgroundStyle)backgroundStyle
|
|
{
|
|
return(_cell.background_style);
|
|
}
|
|
|
|
- (void)setBackgroundStyle:(NSBackgroundStyle)backgroundStyle
|
|
{
|
|
_cell.background_style = backgroundStyle;
|
|
}
|
|
|
|
/**<p>Returns the mouse flags. This flags are usally sets in
|
|
the -trackMouse:inRect:ofView:untilMouseUp: method</p>
|
|
*/
|
|
- (NSInteger) mouseDownFlags
|
|
{
|
|
return _mouse_down_flags;
|
|
}
|
|
|
|
/**<p>Gets the NSCell's <var>delay</var> and the <var>interval</var>
|
|
parameters used when NSCell sends continouly action messages.
|
|
The NSCell implementation sets <var>delay</var> to 0.2 and <var>interval</var>
|
|
to 0.025.</p>
|
|
<p>See Also: -trackMouse:inRect:ofView:untilMouseUp:</p>
|
|
*/
|
|
- (void) getPeriodicDelay: (float*)delay interval: (float*)interval
|
|
{
|
|
*delay = 0.2;
|
|
*interval = 0.025;
|
|
}
|
|
|
|
/**<p>Returns whether tracking starts. The NSCell implementation
|
|
returns YES when the <var>startPoint</var> is into the control view
|
|
retangle, NO otherwise. This method is call at the early stage of
|
|
-trackMouse:inRect:ofView:untilMouseUp:</p><p>See Also:
|
|
[NSView-mouse:inRect:] -trackMouse:inRect:ofView:untilMouseUp:
|
|
</p>
|
|
*/
|
|
- (BOOL) startTrackingAt: (NSPoint)startPoint inView: (NSView*)controlView
|
|
{
|
|
if ([self isContinuous] || (_action_mask & NSLeftMouseDraggedMask))
|
|
{
|
|
return YES;
|
|
}
|
|
else
|
|
{
|
|
return NO;
|
|
}
|
|
}
|
|
|
|
/** <p>Returns whether the mouse dragging should continue for the cell.
|
|
Subclasses should overrided this method if you want
|
|
stop tracking the mouse. This method is call in the
|
|
-trackMouse:inRect:ofView:untilMouseUp: main loop.</p>
|
|
<p>See Also: -trackMouse:inRect:ofView:untilMouseUp:</p>
|
|
*/
|
|
- (BOOL) continueTracking: (NSPoint)lastPoint
|
|
at: (NSPoint)currentPoint
|
|
inView: (NSView*)controlView
|
|
{
|
|
if ([self isContinuous] || (_action_mask & NSLeftMouseDraggedMask))
|
|
{
|
|
return YES;
|
|
}
|
|
else
|
|
{
|
|
return NO;
|
|
}
|
|
}
|
|
|
|
/**<p>Default implementation of this method in NSCell does nothing.</p>
|
|
*/
|
|
- (void) stopTracking: (NSPoint)lastPoint
|
|
at: (NSPoint)stopPoint
|
|
inView: (NSView*)controlView
|
|
mouseIsUp: (BOOL)flag
|
|
{
|
|
}
|
|
|
|
- (BOOL) trackMouse: (NSEvent*)theEvent
|
|
inRect: (NSRect)cellFrame
|
|
ofView: (NSView*)controlView
|
|
untilMouseUp: (BOOL)flag
|
|
{
|
|
NSApplication *theApp = [NSApplication sharedApplication];
|
|
NSUInteger event_mask = NSLeftMouseDownMask | NSLeftMouseUpMask
|
|
| NSMouseMovedMask | NSLeftMouseDraggedMask | NSOtherMouseDraggedMask
|
|
| NSRightMouseDraggedMask;
|
|
NSPoint location = [theEvent locationInWindow];
|
|
NSPoint point = [controlView convertPoint: location fromView: nil];
|
|
NSPoint last_point = point;
|
|
BOOL mouseWentUp = NO;
|
|
BOOL tracking;
|
|
unsigned periodCount = 0;
|
|
|
|
NSDebugLLog(@"NSCell", @"cell start tracking in rect %@ initial point %f %f",
|
|
NSStringFromRect(cellFrame), point.x, point.y);
|
|
|
|
_mouse_down_flags = [theEvent modifierFlags];
|
|
|
|
if (![self isEnabled])
|
|
{
|
|
return NO;
|
|
}
|
|
|
|
if (![controlView mouse: point inRect: cellFrame])
|
|
{
|
|
// point is not in cell
|
|
return NO;
|
|
}
|
|
|
|
tracking = [self startTrackingAt: point inView: controlView];
|
|
|
|
if (_action_mask & NSEventMaskFromType([theEvent type]))
|
|
{
|
|
[self _sendActionFrom: controlView];
|
|
}
|
|
|
|
if ([self isContinuous])
|
|
{
|
|
float delay;
|
|
float interval;
|
|
|
|
[self getPeriodicDelay: &delay interval: &interval];
|
|
[NSEvent startPeriodicEventsAfterDelay: delay withPeriod: interval];
|
|
event_mask |= NSPeriodicMask;
|
|
}
|
|
|
|
NSDebugLLog(@"NSCell", @"cell get mouse events\n");
|
|
if (theEvent != [NSApp currentEvent])
|
|
{
|
|
theEvent = [NSApp currentEvent];
|
|
}
|
|
else
|
|
{
|
|
theEvent = [theApp nextEventMatchingMask: event_mask
|
|
untilDate: [NSDate distantFuture]
|
|
inMode: NSEventTrackingRunLoopMode
|
|
dequeue: YES];
|
|
}
|
|
|
|
while (YES)
|
|
{
|
|
NSEventType eventType;
|
|
|
|
eventType = [theEvent type];
|
|
|
|
// Did the mouse go up?
|
|
if (eventType == NSLeftMouseUp)
|
|
{
|
|
NSDebugLLog(@"NSCell", @"cell mouse went up\n");
|
|
mouseWentUp = YES;
|
|
break;
|
|
}
|
|
else if (eventType == NSPeriodic)
|
|
{
|
|
NSDebugLLog (@"NSCell", @"cell got a periodic event");
|
|
if (periodCount == 4)
|
|
{
|
|
NSWindow *w = [controlView window];
|
|
|
|
/*
|
|
* Too many periodic events in succession -
|
|
* update the mouse location and reset the counter.
|
|
*/
|
|
location = [w mouseLocationOutsideOfEventStream];
|
|
last_point = point;
|
|
point = [controlView convertPoint: location fromView: nil];
|
|
periodCount = 0;
|
|
}
|
|
else
|
|
{
|
|
periodCount++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
location = [theEvent locationInWindow];
|
|
last_point = point;
|
|
point = [controlView convertPoint: location fromView: nil];
|
|
}
|
|
|
|
if (!flag && ![controlView mouse: point inRect: cellFrame])
|
|
{
|
|
NSDebugLLog(@"NSCell", @"point not in cell frame\n");
|
|
break;
|
|
}
|
|
|
|
if (tracking)
|
|
{
|
|
// should continue tracking?
|
|
tracking = [self continueTracking: last_point
|
|
at: point
|
|
inView: controlView];
|
|
NSDebugLLog(@"NSCell", @"cell continue tracking %d\n", tracking);
|
|
}
|
|
|
|
if (_action_mask & NSEventMaskFromType([theEvent type]))
|
|
{
|
|
[self _sendActionFrom: controlView];
|
|
}
|
|
|
|
theEvent = [theApp nextEventMatchingMask: event_mask
|
|
untilDate: [NSDate distantFuture]
|
|
inMode: NSEventTrackingRunLoopMode
|
|
dequeue: YES];
|
|
}
|
|
|
|
if (tracking)
|
|
{
|
|
// Hook called when stop tracking
|
|
[self stopTracking: last_point
|
|
at: point
|
|
inView: controlView
|
|
mouseIsUp: mouseWentUp];
|
|
}
|
|
|
|
if ([self isContinuous])
|
|
{
|
|
[NSEvent stopPeriodicEvents];
|
|
}
|
|
|
|
if (mouseWentUp)
|
|
{
|
|
[self setNextState];
|
|
if (_action_mask & NSEventMaskFromType([theEvent type]))
|
|
{
|
|
[self _sendActionFrom: controlView];
|
|
}
|
|
}
|
|
|
|
// Return YES only if the mouse went up within the cell or flag was true
|
|
if (mouseWentUp && (flag || [controlView mouse: point inRect: cellFrame]))
|
|
{
|
|
NSDebugLLog(@"NSCell", @"mouse went up in cell\n");
|
|
return YES;
|
|
}
|
|
else
|
|
{
|
|
// Otherwise return NO
|
|
NSDebugLLog(@"NSCell", @"mouse did not go up in cell\n");
|
|
return NO;
|
|
}
|
|
}
|
|
|
|
- (NSUInteger) hitTestForEvent: (NSEvent*)event
|
|
inRect: (NSRect)cellFrame
|
|
ofView: (NSView*)controlView
|
|
{
|
|
if (_cell.type == NSImageCellType)
|
|
{
|
|
if ((_cell_image != nil) &&
|
|
NSMouseInRect([controlView convertPoint: [event locationInWindow]
|
|
fromView: nil],
|
|
[self imageRectForBounds: [controlView bounds]],
|
|
[controlView isFlipped]))
|
|
{
|
|
return NSCellHitContentArea;
|
|
}
|
|
else
|
|
{
|
|
return NSCellHitNone;
|
|
}
|
|
}
|
|
else if (_cell.type == NSTextCellType)
|
|
{
|
|
if (_contents == nil)
|
|
{
|
|
return NSCellHitNone;
|
|
}
|
|
else if (_cell.is_disabled == NO)
|
|
{
|
|
return NSCellHitContentArea | NSCellHitEditableTextArea;
|
|
}
|
|
else
|
|
{
|
|
return NSCellHitContentArea;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (_cell.is_disabled == NO)
|
|
{
|
|
return NSCellHitContentArea | NSCellHitTrackableArea;
|
|
}
|
|
else
|
|
{
|
|
return NSCellHitContentArea;
|
|
}
|
|
}
|
|
}
|
|
|
|
/** <p>TODO</p>
|
|
*/
|
|
- (void) resetCursorRect: (NSRect)cellFrame inView: (NSView*)controlView
|
|
{
|
|
if (_cell.type == NSTextCellType && _cell.is_disabled == NO
|
|
&& (_cell.is_selectable == YES || _cell.is_editable == YES))
|
|
{
|
|
static NSCursor *cursor = nil;
|
|
NSRect rect;
|
|
|
|
if (cursor== nil)
|
|
{
|
|
cursor = RETAIN([NSCursor IBeamCursor]);
|
|
}
|
|
rect = NSIntersectionRect(cellFrame, [controlView visibleRect]);
|
|
/*
|
|
* Here we depend on an undocumented feature of NSCursor which may or
|
|
* may not exist in OPENSTEP or MacOS-X ...
|
|
* If we add a cursor rect to a view and don't set it to be set on
|
|
* either entry to or exit from the view, we push it on entry and
|
|
* pop it from the cursor stack on exit.
|
|
*/
|
|
[controlView addCursorRect: rect cursor: cursor];
|
|
}
|
|
}
|
|
|
|
/**<p>Implemented by subclasses to returns the key equivalent.
|
|
The NSCell implementation returns an empty NSString. </p>
|
|
*/
|
|
- (NSString*) keyEquivalent
|
|
{
|
|
return @"";
|
|
}
|
|
|
|
/**<p>Does nothing. This method is used by subclasses to recalculate sizes</p>
|
|
<p>It is usally called from a NSControl object</p>
|
|
<p>See Also: [NSControl-calcSize]</p>
|
|
*/
|
|
- (void) calcDrawInfo: (NSRect)aRect
|
|
{
|
|
}
|
|
|
|
/**Returns the minimun size needed to display the NSCell.
|
|
This size is calculate by adding :
|
|
<list>
|
|
<item> the borders (plain or bezeled) size</item>
|
|
<item> the spacing between the border and inside the cell</item>
|
|
<item> the TODO ... if the cell is type of NSTextCellType
|
|
or the image size if the cell has a NSImageCellType type.</item>
|
|
</list>
|
|
<p>This method returns NSZeroSize if the cell has a NSNullCellType type
|
|
(Cocoa returns a very big size instead).
|
|
</p>
|
|
*/
|
|
- (NSSize) cellSize
|
|
{
|
|
NSSize borderSize, s;
|
|
NSBorderType aType;
|
|
|
|
// Get border size
|
|
if (_cell.is_bordered)
|
|
aType = NSLineBorder;
|
|
else if (_cell.is_bezeled)
|
|
aType = NSBezelBorder;
|
|
else
|
|
aType = NSNoBorder;
|
|
|
|
borderSize = [[GSTheme theme] sizeForBorderType: aType];
|
|
|
|
// Add spacing between border and inside
|
|
if (_cell.is_bordered || _cell.is_bezeled)
|
|
{
|
|
borderSize.height += 1;
|
|
borderSize.width += 3;
|
|
}
|
|
|
|
// Get Content Size
|
|
switch (_cell.type)
|
|
{
|
|
case NSTextCellType:
|
|
{
|
|
NSAttributedString *attrStr;
|
|
|
|
attrStr = [self attributedStringValue];
|
|
if ([attrStr length] != 0)
|
|
{
|
|
s = [attrStr size];
|
|
}
|
|
else
|
|
{
|
|
s = [self _sizeText: @"A"];
|
|
}
|
|
}
|
|
break;
|
|
|
|
case NSImageCellType:
|
|
if (_cell_image == nil)
|
|
{
|
|
s = NSZeroSize;
|
|
}
|
|
else
|
|
{
|
|
s = [_cell_image size];
|
|
}
|
|
break;
|
|
|
|
default:
|
|
case NSNullCellType:
|
|
// macosx instead returns a 'very big size' here; we return NSZeroSize
|
|
s = NSZeroSize;
|
|
break;
|
|
}
|
|
|
|
// Add in border size
|
|
s.width += 2 * borderSize.width;
|
|
s.height += 2 * borderSize.height;
|
|
|
|
return s;
|
|
}
|
|
|
|
/**<p>TODO. Currently the GNUstep implementation returns -cellSize</p>
|
|
<p>See Also: -cellSize</p>
|
|
*/
|
|
- (NSSize) cellSizeForBounds: (NSRect)aRect
|
|
{
|
|
if (_cell.type == NSTextCellType)
|
|
{
|
|
// TODO: Resize the text to fit
|
|
}
|
|
|
|
return [self cellSize];
|
|
}
|
|
|
|
/**<p>TODO</p>
|
|
*/
|
|
- (NSRect) drawingRectForBounds: (NSRect)theRect
|
|
{
|
|
NSSize borderSize;
|
|
NSBorderType aType;
|
|
|
|
// Get border size
|
|
if (_cell.is_bordered)
|
|
aType = NSLineBorder;
|
|
else if (_cell.is_bezeled)
|
|
aType = NSBezelBorder;
|
|
else
|
|
aType = NSNoBorder;
|
|
|
|
borderSize = [[GSTheme theme] sizeForBorderType: aType];
|
|
return NSInsetRect(theRect, borderSize.width, borderSize.height);
|
|
}
|
|
|
|
/**<p>Frame the image gets drawn in</p>
|
|
*/
|
|
- (NSRect) imageRectForBounds: (NSRect)theRect
|
|
{
|
|
if (_cell.type == NSImageCellType)
|
|
{
|
|
NSRect frame = [self drawingRectForBounds: theRect];
|
|
|
|
// Add spacing between border and inside
|
|
if (_cell.is_bordered || _cell.is_bezeled)
|
|
{
|
|
frame.origin.x += 3;
|
|
frame.size.width -= 6;
|
|
frame.origin.y += 1;
|
|
frame.size.height -= 2;
|
|
}
|
|
return frame;
|
|
}
|
|
else
|
|
{
|
|
return theRect;
|
|
}
|
|
}
|
|
|
|
/** <p>Frame the title gets drawn in</p>
|
|
*/
|
|
- (NSRect) titleRectForBounds: (NSRect)theRect
|
|
{
|
|
if (_cell.type == NSTextCellType)
|
|
{
|
|
NSRect frame = [self drawingRectForBounds: theRect];
|
|
|
|
// Add spacing between border and inside
|
|
if (_cell.is_bordered || _cell.is_bezeled)
|
|
{
|
|
frame.origin.x += 3;
|
|
frame.size.width -= 6;
|
|
frame.origin.y += 1;
|
|
frame.size.height -= 2;
|
|
}
|
|
return frame;
|
|
}
|
|
else
|
|
{
|
|
return theRect;
|
|
}
|
|
}
|
|
|
|
- (void) setControlSize: (NSControlSize)controlSize
|
|
{
|
|
_cell.control_size = controlSize;
|
|
}
|
|
|
|
- (NSControlSize) controlSize
|
|
{
|
|
return _cell.control_size;
|
|
}
|
|
|
|
- (void) setControlTint: (NSControlTint)controlTint
|
|
{
|
|
_cell.control_tint = controlTint;
|
|
}
|
|
|
|
- (NSControlTint) controlTint
|
|
{
|
|
return _cell.control_tint;
|
|
}
|
|
|
|
/**<p>This method is used by subclasses to get the control view.
|
|
This method returns nil.</p>
|
|
*/
|
|
- (NSView*) controlView
|
|
{
|
|
return nil;
|
|
}
|
|
|
|
/**<p>This method is used by subclasses to specify the control view.</p>
|
|
*/
|
|
- (void) setControlView: (NSView*)view
|
|
{
|
|
// Do nothing
|
|
}
|
|
|
|
/** <p>This drawing is minimal and with no background,
|
|
* to make it easier for subclass to customize drawing. </p>
|
|
*/
|
|
- (void) drawInteriorWithFrame: (NSRect)cellFrame inView: (NSView*)controlView
|
|
{
|
|
switch (_cell.type)
|
|
{
|
|
case NSTextCellType:
|
|
if (_cell.in_editing)
|
|
[self _drawEditorWithFrame: cellFrame inView: controlView];
|
|
else
|
|
[self _drawAttributedText: [self _drawAttributedString]
|
|
inFrame: [self titleRectForBounds: cellFrame]];
|
|
break;
|
|
|
|
case NSImageCellType:
|
|
if (_cell_image)
|
|
{
|
|
NSSize size;
|
|
NSPoint position;
|
|
NSRect drawingRect = [self imageRectForBounds: cellFrame];
|
|
NSRect rect;
|
|
|
|
size = [_cell_image size];
|
|
position.x = MAX(NSMidX(drawingRect) - (size.width/2.),0.);
|
|
position.y = MAX(NSMidY(drawingRect) - (size.height/2.),0.);
|
|
rect = NSMakeRect(position.x, position.y, size.width, size.height);
|
|
|
|
if (nil != controlView)
|
|
{
|
|
rect = [controlView centerScanRect: rect];
|
|
}
|
|
|
|
[_cell_image drawInRect: rect
|
|
fromRect: NSZeroRect
|
|
operation: NSCompositeSourceOver
|
|
fraction: 1.0
|
|
respectFlipped: YES
|
|
hints: nil];
|
|
}
|
|
break;
|
|
|
|
case NSNullCellType:
|
|
break;
|
|
}
|
|
|
|
// NB: We don't do any highlighting to make it easier for subclasses
|
|
// to reuse this code while doing their own custom highlighting and
|
|
// prettyfying
|
|
}
|
|
|
|
/**<p>Draws the cell in <var>controlView</var></p>
|
|
*/
|
|
- (void) drawWithFrame: (NSRect)cellFrame inView: (NSView*)controlView
|
|
{
|
|
// do nothing if cell's frame rect is zero
|
|
if (NSIsEmptyRect(cellFrame))
|
|
return;
|
|
|
|
// draw the border if needed
|
|
[self _drawBorderAndBackgroundWithFrame: cellFrame inView: controlView];
|
|
// draw interior
|
|
[self drawInteriorWithFrame: cellFrame inView: controlView];
|
|
// Draw first responder
|
|
[self _drawFocusRingWithFrame: cellFrame inView: controlView];
|
|
}
|
|
|
|
/**<p>Sets whether the NSCell is highlighted.</p>
|
|
<p>See Also: -isHighlighted</p>
|
|
*/
|
|
- (void) setHighlighted: (BOOL) flag
|
|
{
|
|
_cell.is_highlighted = flag;
|
|
}
|
|
|
|
/**<p>Returns whether the cell is highlighted. By default NO</p>
|
|
<p>See Also: -setHighlighted:</p>
|
|
*/
|
|
- (BOOL) isHighlighted
|
|
{
|
|
return _cell.is_highlighted;
|
|
}
|
|
|
|
/**
|
|
*<p>TODO explain</p>
|
|
*/
|
|
|
|
- (void) highlight: (BOOL)lit
|
|
withFrame: (NSRect)cellFrame
|
|
inView: (NSView*)controlView
|
|
{
|
|
if (_cell.is_highlighted != lit)
|
|
{
|
|
_cell.is_highlighted = lit;
|
|
|
|
// Disabling this because when combined with making
|
|
// -[NSButtonCell isOpaque] return NO, it was causing scroller buttons
|
|
// to stay stuck down. --Eric (2013-09-28)
|
|
#if 0
|
|
/*
|
|
* NB: This has a visible effect only if subclasses override
|
|
* drawWithFrame:inView: to draw something special when the
|
|
* cell is highlighted.
|
|
* NSCell simply draws border+text/image and makes no highlighting,
|
|
* for easier subclassing.
|
|
*/
|
|
if ([self isOpaque] == NO)
|
|
{
|
|
/* FIXME - This looks like potentially generating an
|
|
* infinite loop! The control asking the cell to draw
|
|
* itself in the rect, the cell asking the control to draw
|
|
* the rect, the control asking the cell to draw itself in
|
|
* the rect, the cell ...
|
|
*
|
|
* I think we should remove it. The control is responsible
|
|
* for using the cell to draw, not vice versa.
|
|
*/
|
|
[controlView displayRect: cellFrame];
|
|
}
|
|
#endif
|
|
[self drawWithFrame: cellFrame inView: controlView];
|
|
}
|
|
}
|
|
|
|
- (NSColor*) highlightColorWithFrame: (NSRect)cellFrame
|
|
inView: (NSView *)controlView
|
|
{
|
|
return [NSColor selectedControlColor];
|
|
}
|
|
|
|
- (NSText*) setUpFieldEditorAttributes: (NSText*)textObject
|
|
{
|
|
NSDictionary *attr;
|
|
|
|
// Reset the string to have a well defined state. The real string gets set later on.
|
|
[textObject setString: @""];
|
|
|
|
[textObject setTextColor: [self textColor]];
|
|
if ([self isBezeled])
|
|
{
|
|
[textObject setBackgroundColor: [NSColor textBackgroundColor]];
|
|
[textObject setDrawsBackground: YES];
|
|
}
|
|
else
|
|
{
|
|
[textObject setDrawsBackground: NO];
|
|
}
|
|
[textObject setFont: [self font]];
|
|
[textObject setAlignment: [self alignment]];
|
|
// FIXME: Add base writing direction
|
|
|
|
[textObject setEditable: [self isEditable]];
|
|
[textObject setSelectable: [self isSelectable]];
|
|
[textObject setRichText: [self allowsEditingTextAttributes]];
|
|
[textObject setImportsGraphics: [self importsGraphics]];
|
|
[(NSTextView*)textObject setAllowsUndo: [self allowsUndo]];
|
|
attr = [self _nonAutoreleasedTypingAttributes];
|
|
[(NSTextView*)textObject setTypingAttributes: attr];
|
|
RELEASE(attr);
|
|
|
|
return textObject;
|
|
}
|
|
|
|
- (void) _setupTextWithFrame: (NSRect)aRect
|
|
inView: (NSView*)controlView
|
|
editor: (NSText*)textObject
|
|
delegate: (id)anObject
|
|
range: (NSRange)selection
|
|
{
|
|
BOOL needsClipView;
|
|
BOOL wraps = [self wraps];
|
|
NSTextContainer *ct;
|
|
NSSize maxSize;
|
|
NSRect titleRect = [self titleRectForBounds: aRect];
|
|
|
|
/* We always add a clip view if the cell is editable so that the user
|
|
can edit the whole contents even if the cell's contents normally is
|
|
clipped. */
|
|
needsClipView = [self isScrollable] || [self isEditable];
|
|
if (needsClipView)
|
|
{
|
|
NSClipView *cv;
|
|
|
|
cv = [[NSClipView alloc] initWithFrame: titleRect];
|
|
[cv setDocumentView: textObject];
|
|
[controlView addSubview: cv];
|
|
RELEASE(cv);
|
|
}
|
|
else
|
|
[controlView addSubview: textObject];
|
|
|
|
/* Note: The order of statements matters here. We must set the text object's
|
|
horizontallyResizable and verticallyResizable attributes before setting
|
|
its frame size. Otherwise, the text object's width and/or height might
|
|
incorrectly be reduced to zero (since the text object has no contents at
|
|
this point) if the text object was resizable by its text container before.
|
|
Of course we could also have set the text object's minimum size to the
|
|
intended frame size, but then we must update the minimum size whenever
|
|
the field editor's frame is changed in -_drawEditorWithFrame:inView:.
|
|
Note that the minimum size is not relevant when a clip view is used. */
|
|
[textObject setMinSize: NSZeroSize];
|
|
[textObject setMaxSize: NSMakeSize(1e6, 1e6)];
|
|
[textObject setHorizontallyResizable: needsClipView && !wraps];
|
|
[textObject setVerticallyResizable: needsClipView];
|
|
[textObject setFrame: titleRect];
|
|
if (needsClipView)
|
|
[textObject setAutoresizingMask: NSViewWidthSizable + NSViewHeightSizable];
|
|
else
|
|
[textObject setAutoresizingMask: NSViewNotSizable];
|
|
|
|
/* Note: Order of statements matters again. The heightTracksTextView
|
|
and widthTracksTextView container attributes must be set after setting
|
|
the horizontallyResizable and verticallyResizable text view attributes
|
|
because NSTextView's setter methods include "safety code", which
|
|
always updates the container attributes along with the text view
|
|
attributes.
|
|
FIXME Fix NSTextView to only reset the text container attributes, but
|
|
never set them. However note that this may break some sloppily written
|
|
code which forgets to set the text container attributes. */
|
|
/* See comments in NSStringDrawing.m about the choice of maximum size. */
|
|
ct = [(NSTextView*)textObject textContainer];
|
|
if (wraps)
|
|
maxSize = NSMakeSize(NSWidth(titleRect), 1e6);
|
|
else
|
|
maxSize = NSMakeSize(1e6, 1e6);
|
|
[ct setContainerSize: maxSize];
|
|
[ct setWidthTracksTextView: wraps];
|
|
[ct setHeightTracksTextView: NO];
|
|
|
|
[self _updateFieldEditor: textObject];
|
|
|
|
[textObject setSelectedRange: selection];
|
|
[textObject scrollRangeToVisible: selection];
|
|
|
|
[textObject setDelegate: anObject];
|
|
[[controlView window] makeFirstResponder: textObject];
|
|
_cell.in_editing = YES;
|
|
}
|
|
|
|
/**<p>Ends any text editing. This method sets the text object's delegate
|
|
to nil, and remove the NSClipView and the text object used for editing</p>
|
|
<p>See Also: -editWithFrame:inView:editor:delegate:event:</p>
|
|
*/
|
|
- (void) endEditing: (NSText*)textObject
|
|
{
|
|
NSClipView *clipView;
|
|
|
|
_cell.in_editing = NO;
|
|
[textObject setString: @""];
|
|
[textObject setDelegate: nil];
|
|
|
|
clipView = (NSClipView*)[textObject superview];
|
|
if ([clipView isKindOfClass: [NSClipView class]])
|
|
{
|
|
[clipView setDocumentView: nil];
|
|
[clipView removeFromSuperview];
|
|
}
|
|
else
|
|
[textObject removeFromSuperview];
|
|
}
|
|
|
|
/*
|
|
* Editing Text
|
|
*/
|
|
/** <p>.This method does nothing if a the <var>controlView</var> is nil,
|
|
if text object does not exist or if the cell's type is not <ref type="type"
|
|
id="NSCellType">NSTextCellType</ref></p>
|
|
*/
|
|
- (void) editWithFrame: (NSRect)aRect
|
|
inView: (NSView*)controlView
|
|
editor: (NSText*)textObject
|
|
delegate: (id)anObject
|
|
event: (NSEvent*)theEvent
|
|
{
|
|
if (!controlView || !textObject || (_cell.type != NSTextCellType))
|
|
return;
|
|
|
|
[self _setupTextWithFrame: aRect
|
|
inView: controlView
|
|
editor: textObject
|
|
delegate: anObject
|
|
range: NSMakeRange(0, 0)];
|
|
|
|
if ([theEvent type] == NSLeftMouseDown)
|
|
{
|
|
[textObject mouseDown: theEvent];
|
|
}
|
|
}
|
|
|
|
/** <p> This method does nothing if the <var>controlView</var> is nil,
|
|
if text object does not exist or if the cell's type is not <ref type="type"
|
|
id="NSCellType">NSTextCellType</ref> </p>
|
|
*/
|
|
- (void) selectWithFrame: (NSRect)aRect
|
|
inView: (NSView*)controlView
|
|
editor: (NSText*)textObject
|
|
delegate: (id)anObject
|
|
start: (NSInteger)selStart
|
|
length: (NSInteger)selLength
|
|
{
|
|
if (!controlView || !textObject || (_cell.type != NSTextCellType))
|
|
return;
|
|
|
|
[self _setupTextWithFrame: aRect
|
|
inView: controlView
|
|
editor: textObject
|
|
delegate: anObject
|
|
range: NSMakeRange(selStart, selLength)];
|
|
}
|
|
|
|
- (BOOL) sendsActionOnEndEditing
|
|
{
|
|
return _cell.sends_action_on_end_editing;
|
|
}
|
|
|
|
- (void) setSendsActionOnEndEditing: (BOOL)flag
|
|
{
|
|
_cell.sends_action_on_end_editing = flag;
|
|
}
|
|
|
|
- (BOOL) allowsUndo
|
|
{
|
|
return _cell.allows_undo;
|
|
}
|
|
|
|
- (void) setAllowsUndo: (BOOL)flag
|
|
{
|
|
_cell.allows_undo = flag;
|
|
}
|
|
|
|
/*
|
|
* Copying
|
|
*/
|
|
- (id) copyWithZone: (NSZone*)zone
|
|
{
|
|
NSCell *c = (NSCell*)NSCopyObject (self, 0, zone);
|
|
|
|
/* Hmmm. */
|
|
c->_contents = [_contents copyWithZone: zone];
|
|
/* Because of performance issues (and because so the doc says) only
|
|
pointers to the objects are copied. We need to RETAIN them all
|
|
though. */
|
|
_font = TEST_RETAIN (_font);
|
|
_object_value = TEST_RETAIN (_object_value);
|
|
_menu = TEST_RETAIN (_menu);
|
|
_cell_image = TEST_RETAIN (_cell_image);
|
|
_formatter = TEST_RETAIN (_formatter);
|
|
_represented_object = TEST_RETAIN (_represented_object);
|
|
|
|
return c;
|
|
}
|
|
|
|
/*
|
|
* NSCoding protocol
|
|
*/
|
|
- (void) encodeWithCoder: (NSCoder*)aCoder
|
|
{
|
|
if ([aCoder allowsKeyedCoding])
|
|
{
|
|
unsigned long cFlags = 0;
|
|
unsigned int cFlags2 = 0;
|
|
id contents = _contents;
|
|
|
|
// encode contents
|
|
[aCoder encodeObject: contents forKey: @"NSContents"];
|
|
|
|
// flags
|
|
cFlags |= [self focusRingType];
|
|
cFlags |= [self showsFirstResponder] ? 0x4 : 0;
|
|
cFlags |= (_action_mask & NSLeftMouseUpMask) ? 0 : 0x20;
|
|
cFlags |= [self wraps] ? 0x40 : 0;
|
|
cFlags |= (_action_mask & NSLeftMouseDraggedMask) ? 0x100 : 0;
|
|
cFlags |= (_action_mask & NSLeftMouseDownMask) ? 0x40000 : 0;
|
|
cFlags |= [self isContinuous] ? 0x80000 : 0;
|
|
cFlags |= [self isScrollable] ? 0x100000 : 0;
|
|
cFlags |= [self isSelectable] ? 0x200000 : 0;
|
|
cFlags |= [self isBezeled] ? 0x400000 : 0;
|
|
cFlags |= [self isBordered] ? 0x800000 : 0;
|
|
cFlags |= ([self type] << 26);
|
|
cFlags |= [self isEditable] ? 0x10000000 : 0;
|
|
cFlags |= ([self isEnabled] == NO) ? 0x20000000 : 0;
|
|
cFlags |= [self isHighlighted] ? 0x40000000 : 0;
|
|
cFlags |= ([self state] == NSOnState) ? 0x80000000 : 0;
|
|
[aCoder encodeInt: cFlags forKey: @"NSCellFlags"];
|
|
|
|
// flags part 2
|
|
cFlags2 |= ([self usesSingleLineMode] ? 0x40 : 0);
|
|
cFlags2 |= (([self allowsUndo] == NO) ? 0x1000 : 0);
|
|
cFlags2 |= ([self controlTint] << 5);
|
|
cFlags2 |= ([self lineBreakMode] << 9);
|
|
cFlags2 |= ([self controlSize] << 17);
|
|
cFlags2 |= [self sendsActionOnEndEditing] ? 0x400000 : 0;
|
|
cFlags2 |= [self allowsMixedState] ? 0x1000000 : 0;
|
|
cFlags2 |= [self refusesFirstResponder] ? 0x2000000 : 0;
|
|
cFlags2 |= ([self alignment] << 26);
|
|
cFlags2 |= [self importsGraphics] ? 0x20000000 : 0;
|
|
cFlags2 |= [self allowsEditingTextAttributes] ? 0x40000000 : 0;
|
|
[aCoder encodeInt: cFlags2 forKey: @"NSCellFlags2"];
|
|
|
|
if (_cell.type == NSTextCellType)
|
|
{
|
|
// font and formatter.
|
|
if ([self font])
|
|
{
|
|
[aCoder encodeObject: [self font] forKey: @"NSSupport"];
|
|
}
|
|
|
|
if ([self formatter])
|
|
{
|
|
[aCoder encodeObject: [self formatter] forKey: @"NSFormatter"];
|
|
}
|
|
}
|
|
else if ([self image])
|
|
{
|
|
[aCoder encodeObject: [self image] forKey: @"NSSupport"];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
BOOL flag;
|
|
unsigned int tmp_int;
|
|
|
|
[aCoder encodeObject: _contents];
|
|
[aCoder encodeObject: _cell_image];
|
|
[aCoder encodeObject: _font];
|
|
[aCoder encodeObject: _object_value];
|
|
flag = _cell.contents_is_attributed_string;
|
|
[aCoder encodeValueOfObjCType: @encode(BOOL) at: &flag];
|
|
flag = _cell.is_highlighted;
|
|
[aCoder encodeValueOfObjCType: @encode(BOOL) at: &flag];
|
|
flag = _cell.is_disabled;
|
|
[aCoder encodeValueOfObjCType: @encode(BOOL) at: &flag];
|
|
flag = _cell.is_editable;
|
|
[aCoder encodeValueOfObjCType: @encode(BOOL) at: &flag];
|
|
flag = _cell.is_rich_text;
|
|
[aCoder encodeValueOfObjCType: @encode(BOOL) at: &flag];
|
|
flag = _cell.imports_graphics;
|
|
[aCoder encodeValueOfObjCType: @encode(BOOL) at: &flag];
|
|
flag = _cell.shows_first_responder;
|
|
[aCoder encodeValueOfObjCType: @encode(BOOL) at: &flag];
|
|
flag = _cell.refuses_first_responder;
|
|
[aCoder encodeValueOfObjCType: @encode(BOOL) at: &flag];
|
|
flag = _cell.sends_action_on_end_editing;
|
|
[aCoder encodeValueOfObjCType: @encode(BOOL) at: &flag];
|
|
flag = _cell.is_bordered;
|
|
[aCoder encodeValueOfObjCType: @encode(BOOL) at: &flag];
|
|
flag = _cell.is_bezeled;
|
|
[aCoder encodeValueOfObjCType: @encode(BOOL) at: &flag];
|
|
flag = _cell.is_scrollable;
|
|
[aCoder encodeValueOfObjCType: @encode(BOOL) at: &flag];
|
|
flag = _cell.is_selectable;
|
|
[aCoder encodeValueOfObjCType: @encode(BOOL) at: &flag];
|
|
// This used to be is_continuous, which has been replaced.
|
|
/* Ayers 20.03.2003: But we must continue to encode it for backward
|
|
compatibility or current releases will have undefined behavior when
|
|
decoding archives (i.e. .gorm files) encoded by this version. */
|
|
flag = [self isContinuous];
|
|
[aCoder encodeValueOfObjCType: @encode(BOOL) at: &flag];
|
|
flag = _cell.allows_mixed_state;
|
|
[aCoder encodeValueOfObjCType: @encode(BOOL) at: &flag];
|
|
flag = [self wraps];
|
|
[aCoder encodeValueOfObjCType: @encode(BOOL) at: &flag];
|
|
tmp_int = _cell.text_align;
|
|
[aCoder encodeValueOfObjCType: @encode(unsigned int) at: &tmp_int];
|
|
tmp_int = _cell.type;
|
|
[aCoder encodeValueOfObjCType: @encode(unsigned int) at: &tmp_int];
|
|
tmp_int = _cell.image_position;
|
|
[aCoder encodeValueOfObjCType: @encode(unsigned int) at: &tmp_int];
|
|
tmp_int = _cell.entry_type;
|
|
[aCoder encodeValueOfObjCType: @encode(unsigned int) at: &tmp_int];
|
|
// FIXME: State may be -1, why do we encode it as unsigned?
|
|
tmp_int = _cell.state;
|
|
[aCoder encodeValueOfObjCType: @encode(unsigned int) at: &tmp_int];
|
|
tmp_int = _cell.mnemonic_location;
|
|
[aCoder encodeValueOfObjCType: @encode(unsigned int) at: &tmp_int];
|
|
[aCoder encodeValueOfObjCType: @encode(NSUInteger) at: &_mouse_down_flags];
|
|
[aCoder encodeValueOfObjCType: @encode(NSUInteger) at: &_action_mask];
|
|
[aCoder encodeValueOfObjCType: @encode(id) at: &_formatter];
|
|
[aCoder encodeValueOfObjCType: @encode(id) at: &_menu];
|
|
[aCoder encodeValueOfObjCType: @encode(id) at: &_represented_object];
|
|
|
|
tmp_int = _cell.allows_undo;
|
|
[aCoder encodeValueOfObjCType: @encode(unsigned int) at: &tmp_int];
|
|
tmp_int = _cell.line_break_mode;
|
|
[aCoder encodeValueOfObjCType: @encode(unsigned int) at: &tmp_int];
|
|
tmp_int = _cell.control_tint;
|
|
[aCoder encodeValueOfObjCType: @encode(unsigned int) at: &tmp_int];
|
|
tmp_int = _cell.control_size;
|
|
[aCoder encodeValueOfObjCType: @encode(unsigned int) at: &tmp_int];
|
|
tmp_int = _cell.focus_ring_type;
|
|
[aCoder encodeValueOfObjCType: @encode(unsigned int) at: &tmp_int];
|
|
tmp_int = _cell.base_writing_direction;
|
|
[aCoder encodeValueOfObjCType: @encode(unsigned int) at: &tmp_int];
|
|
tmp_int = _cell.uses_single_line_mode;
|
|
[aCoder encodeValueOfObjCType: @encode(unsigned int) at: &tmp_int];
|
|
}
|
|
}
|
|
|
|
- (id) initWithCoder: (NSCoder*)aDecoder
|
|
{
|
|
if ([aDecoder allowsKeyedCoding])
|
|
{
|
|
id contents = [aDecoder decodeObjectForKey: @"NSContents"];
|
|
|
|
// initialize based on content...
|
|
if ([contents isKindOfClass: [NSString class]])
|
|
{
|
|
self = [self initTextCell: contents];
|
|
}
|
|
else if ([contents isKindOfClass: [NSImage class]])
|
|
{
|
|
self = [self initImageCell: contents];
|
|
}
|
|
else
|
|
{
|
|
self = [self init];
|
|
[self setObjectValue: contents];
|
|
}
|
|
|
|
if ([aDecoder containsValueForKey: @"NSCellFlags"])
|
|
{
|
|
unsigned long cFlags;
|
|
NSUInteger mask = 0;
|
|
cFlags = [aDecoder decodeIntForKey: @"NSCellFlags"];
|
|
|
|
[self setFocusRingType: (cFlags & 0x3)];
|
|
[self setShowsFirstResponder: ((cFlags & 0x4) == 0x4)];
|
|
// This bit flag is the other way around!
|
|
if ((cFlags & 0x20) != 0x20)
|
|
mask |= NSLeftMouseUpMask;
|
|
// This bit flag is the other way around!
|
|
[self setWraps: ((cFlags & 0x40) != 0x40)];
|
|
if ((cFlags & 0x100) == 0x100)
|
|
mask |= NSLeftMouseDraggedMask;
|
|
if ((cFlags & 0x40000) == 0x40000)
|
|
mask |= NSLeftMouseDownMask;
|
|
if ((cFlags & 0x80000) == 0x80000)
|
|
mask |= NSPeriodicMask;
|
|
[self sendActionOn: mask];
|
|
[self setScrollable: ((cFlags & 0x100000) == 0x100000)];
|
|
[self setSelectable: ((cFlags & 0x200000) == 0x200000)];
|
|
[self setBezeled: ((cFlags & 0x400000) == 0x400000)];
|
|
[self setBordered: ((cFlags & 0x800000) == 0x800000)];
|
|
[self setType: ((cFlags & 0xC000000) >> 26)];
|
|
[self setEditable: ((cFlags & 0x10000000) == 0x10000000)];
|
|
// This bit flag is the other way around!
|
|
[self setEnabled: ((cFlags & 0x20000000) != 0x20000000)];
|
|
[self setHighlighted: ((cFlags & 0x40000000) == 0x40000000)];
|
|
[self setState: ((cFlags & 0x80000000) == 0x80000000) ? NSOnState : NSOffState];
|
|
}
|
|
if ([aDecoder containsValueForKey: @"NSCellFlags2"])
|
|
{
|
|
int cFlags2;
|
|
|
|
cFlags2 = [aDecoder decodeIntForKey: @"NSCellFlags2"];
|
|
[self setUsesSingleLineMode: (cFlags2 & 0x40)];
|
|
[self setControlTint: ((cFlags2 & 0xE0) >> 5)];
|
|
[self setLineBreakMode: ((cFlags2 & 0xE00) >> 9)];
|
|
[self setControlSize: ((cFlags2 & 0xE0000) >> 17)];
|
|
[self setSendsActionOnEndEditing: ((cFlags2 & 0x400000) == 0x400000)];
|
|
[self setAllowsMixedState: ((cFlags2 & 0x1000000) == 0x1000000)];
|
|
[self setRefusesFirstResponder: ((cFlags2 & 0x2000000) == 0x2000000)];
|
|
[self setAlignment: ((cFlags2 & 0x1C000000) >> 26)];
|
|
[self setImportsGraphics: ((cFlags2 & 0x20000000) == 0x20000000)];
|
|
[self setAllowsEditingTextAttributes: ((cFlags2 & 0x40000000) == 0x40000000)];
|
|
}
|
|
if ([aDecoder containsValueForKey: @"NSSupport"])
|
|
{
|
|
id support = [aDecoder decodeObjectForKey: @"NSSupport"];
|
|
|
|
if ([support isKindOfClass: [NSFont class]])
|
|
{
|
|
[self setFont: support];
|
|
}
|
|
else if ([support isKindOfClass: [NSImage class]])
|
|
{
|
|
[self setImage: support];
|
|
}
|
|
}
|
|
if ([aDecoder containsValueForKey: @"NSFormatter"])
|
|
{
|
|
NSFormatter *formatter = [aDecoder decodeObjectForKey: @"NSFormatter"];
|
|
|
|
[self setFormatter: formatter];
|
|
}
|
|
}
|
|
else
|
|
{
|
|
BOOL flag, wraps;
|
|
unsigned int tmp_int;
|
|
id formatter, menu;
|
|
int version = [aDecoder versionForClassName: @"NSCell"];
|
|
|
|
[aDecoder decodeValueOfObjCType: @encode(id) at: &_contents];
|
|
[aDecoder decodeValueOfObjCType: @encode(id) at: &_cell_image];
|
|
[aDecoder decodeValueOfObjCType: @encode(id) at: &_font];
|
|
[aDecoder decodeValueOfObjCType: @encode(id) at: &_object_value];
|
|
[aDecoder decodeValueOfObjCType: @encode(BOOL) at: &flag];
|
|
_cell.contents_is_attributed_string = flag;
|
|
[aDecoder decodeValueOfObjCType: @encode(BOOL) at: &flag];
|
|
_cell.is_highlighted = flag;
|
|
[aDecoder decodeValueOfObjCType: @encode(BOOL) at: &flag];
|
|
_cell.is_disabled = flag;
|
|
[aDecoder decodeValueOfObjCType: @encode(BOOL) at: &flag];
|
|
_cell.is_editable = flag;
|
|
[aDecoder decodeValueOfObjCType: @encode(BOOL) at: &flag];
|
|
_cell.is_rich_text = flag;
|
|
[aDecoder decodeValueOfObjCType: @encode(BOOL) at: &flag];
|
|
_cell.imports_graphics = flag;
|
|
[aDecoder decodeValueOfObjCType: @encode(BOOL) at: &flag];
|
|
_cell.shows_first_responder = flag;
|
|
[aDecoder decodeValueOfObjCType: @encode(BOOL) at: &flag];
|
|
_cell.refuses_first_responder = flag;
|
|
[aDecoder decodeValueOfObjCType: @encode(BOOL) at: &flag];
|
|
_cell.sends_action_on_end_editing = flag;
|
|
[aDecoder decodeValueOfObjCType: @encode(BOOL) at: &flag];
|
|
_cell.is_bordered = flag;
|
|
[aDecoder decodeValueOfObjCType: @encode(BOOL) at: &flag];
|
|
_cell.is_bezeled = flag;
|
|
[aDecoder decodeValueOfObjCType: @encode(BOOL) at: &flag];
|
|
_cell.is_scrollable = flag;
|
|
[aDecoder decodeValueOfObjCType: @encode(BOOL) at: &flag];
|
|
_cell.is_selectable = flag;
|
|
[aDecoder decodeValueOfObjCType: @encode(BOOL) at: &flag];
|
|
// This used to be is_continuous, which has been replaced.
|
|
//_cell.is_continuous = flag;
|
|
[aDecoder decodeValueOfObjCType: @encode(BOOL) at: &flag];
|
|
_cell.allows_mixed_state = flag;
|
|
[aDecoder decodeValueOfObjCType: @encode(BOOL) at: &flag];
|
|
/* The wraps attribute has been superseded by lineBreakMode. However,
|
|
we may need it to set lineBreakMode when reading old archives. */
|
|
wraps = flag;
|
|
[aDecoder decodeValueOfObjCType: @encode(unsigned int) at: &tmp_int];
|
|
_cell.text_align = tmp_int;
|
|
[aDecoder decodeValueOfObjCType: @encode(unsigned int) at: &tmp_int];
|
|
_cell.type = tmp_int;
|
|
[aDecoder decodeValueOfObjCType: @encode(unsigned int) at: &tmp_int];
|
|
_cell.image_position = tmp_int;
|
|
[aDecoder decodeValueOfObjCType: @encode(unsigned int) at: &tmp_int];
|
|
_cell.entry_type = tmp_int;
|
|
[aDecoder decodeValueOfObjCType: @encode(unsigned int) at: &tmp_int];
|
|
_cell.state = tmp_int;
|
|
[aDecoder decodeValueOfObjCType: @encode(unsigned int) at: &tmp_int];
|
|
_cell.mnemonic_location = tmp_int;
|
|
[aDecoder decodeValueOfObjCType: @encode(NSUInteger)
|
|
at: &_mouse_down_flags];
|
|
[aDecoder decodeValueOfObjCType: @encode(NSUInteger) at: &_action_mask];
|
|
if (version < 3)
|
|
{
|
|
unsigned int mask = 0;
|
|
|
|
// Convert old GNUstep mask value to Cocoa values
|
|
if ((_action_mask & 0x1) == 0x1)
|
|
{
|
|
mask |= NSLeftMouseDownMask;
|
|
}
|
|
if ((_action_mask & 0x2) == 0x2)
|
|
{
|
|
mask |= NSLeftMouseUpMask;
|
|
}
|
|
if ((_action_mask & 0x4) == 0x4)
|
|
{
|
|
mask |= NSOtherMouseDownMask;
|
|
}
|
|
if ((_action_mask & 0x8) == 0x8)
|
|
{
|
|
mask |= NSOtherMouseUpMask;
|
|
}
|
|
if ((_action_mask & 0x10) == 0x10)
|
|
{
|
|
mask |= NSRightMouseDownMask;
|
|
}
|
|
if ((_action_mask & 0x20) == 0x20)
|
|
{
|
|
mask |= NSRightMouseUpMask;
|
|
}
|
|
if ((_action_mask & 0x40) == 0x40)
|
|
{
|
|
mask |= NSMouseMovedMask;
|
|
}
|
|
if ((_action_mask & 0x80) == 0x80)
|
|
{
|
|
mask |= NSLeftMouseDraggedMask;
|
|
}
|
|
if ((_action_mask & 0x100) == 0x100)
|
|
{
|
|
mask |= NSOtherMouseDraggedMask;
|
|
}
|
|
if ((_action_mask & 0x200) == 0x200)
|
|
{
|
|
mask |= NSRightMouseDraggedMask;
|
|
}
|
|
if ((_action_mask & 0x400) == 0x400)
|
|
{
|
|
mask |= NSMouseEnteredMask;
|
|
}
|
|
if ((_action_mask & 0x800) == 0x800)
|
|
{
|
|
mask |= NSMouseExitedMask;
|
|
}
|
|
if ((_action_mask & 0x1000) == 0x1000)
|
|
{
|
|
mask |= NSKeyDownMask;
|
|
}
|
|
if ((_action_mask & 0x2000) == 0x2000)
|
|
{
|
|
mask |= NSKeyUpMask;
|
|
}
|
|
if ((_action_mask & 0x4000) == 0x4000)
|
|
{
|
|
mask |= NSFlagsChangedMask;
|
|
}
|
|
if ((_action_mask & 0x8000) == 0x8000)
|
|
{
|
|
mask |= NSAppKitDefinedMask;
|
|
}
|
|
if ((_action_mask & 0x10000) == 0x10000)
|
|
{
|
|
mask |= NSSystemDefinedMask;
|
|
}
|
|
if ((_action_mask & 0x20000) == 0x20000)
|
|
{
|
|
mask |= NSApplicationDefinedMask;
|
|
}
|
|
if ((_action_mask & 0x40000) == 0x40000)
|
|
{
|
|
mask |= NSPeriodicMask;
|
|
}
|
|
if ((_action_mask & 0x80000) == 0x80000)
|
|
{
|
|
mask |= NSCursorUpdateMask;
|
|
}
|
|
if ((_action_mask & 0x100000) == 0x100000)
|
|
{
|
|
mask |= NSScrollWheelMask;
|
|
}
|
|
_action_mask = mask;
|
|
}
|
|
_action_mask |= NSLeftMouseUpMask;
|
|
[aDecoder decodeValueOfObjCType: @encode(id) at: &formatter];
|
|
[self setFormatter: formatter];
|
|
[aDecoder decodeValueOfObjCType: @encode(id) at: &menu];
|
|
[self setMenu: menu];
|
|
[aDecoder decodeValueOfObjCType: @encode(id) at: &_represented_object];
|
|
|
|
if (_formatter != nil)
|
|
{
|
|
NSString *contents;
|
|
|
|
contents = [_formatter stringForObjectValue: _object_value];
|
|
if (contents != nil)
|
|
{
|
|
_cell.has_valid_object_value = YES;
|
|
ASSIGN (_contents, contents);
|
|
_cell.contents_is_attributed_string = NO;
|
|
}
|
|
}
|
|
|
|
if (version >= 2)
|
|
{
|
|
[aDecoder decodeValueOfObjCType: @encode(unsigned int) at: &tmp_int];
|
|
_cell.allows_undo = tmp_int;
|
|
[aDecoder decodeValueOfObjCType: @encode(unsigned int) at: &tmp_int];
|
|
_cell.line_break_mode = tmp_int;
|
|
[aDecoder decodeValueOfObjCType: @encode(unsigned int) at: &tmp_int];
|
|
_cell.control_tint = tmp_int;
|
|
[aDecoder decodeValueOfObjCType: @encode(unsigned int) at: &tmp_int];
|
|
_cell.control_size = tmp_int;
|
|
[aDecoder decodeValueOfObjCType: @encode(unsigned int) at: &tmp_int];
|
|
_cell.focus_ring_type = tmp_int;
|
|
[aDecoder decodeValueOfObjCType: @encode(unsigned int) at: &tmp_int];
|
|
_cell.base_writing_direction = tmp_int;
|
|
}
|
|
else
|
|
{
|
|
/* Backward compatibility: Derive lineBreakMode from the superseded
|
|
wraps attribute. */
|
|
[self setWraps: wraps];
|
|
}
|
|
|
|
if (version >= 4)
|
|
{
|
|
[aDecoder decodeValueOfObjCType: @encode(unsigned int) at: &tmp_int];
|
|
_cell.uses_single_line_mode = tmp_int;
|
|
}
|
|
|
|
}
|
|
return self;
|
|
}
|
|
|
|
- (NSUserInterfaceLayoutDirection) userInterfaceLayoutDirection
|
|
{
|
|
// FIXME
|
|
return NSUserInterfaceLayoutDirectionLeftToRight;
|
|
}
|
|
|
|
- (void) setUserInterfaceLayoutDirection: (NSUserInterfaceLayoutDirection)dir
|
|
{
|
|
// FIXME: implement this
|
|
return;
|
|
}
|
|
|
|
- (void) setUsesSingleLineMode: (BOOL)flag
|
|
{
|
|
_cell.uses_single_line_mode = flag;
|
|
}
|
|
|
|
- (BOOL) usesSingleLineMode
|
|
{
|
|
return _cell.uses_single_line_mode;
|
|
}
|
|
|
|
@end
|
|
|
|
@implementation NSCell (PrivateMethods)
|
|
|
|
- (NSColor*) textColor
|
|
{
|
|
if (_cell.is_disabled)
|
|
return dtxtCol;
|
|
else
|
|
return txtCol;
|
|
}
|
|
|
|
/* This method is an exception and returns a non-autoreleased
|
|
dictionary, so that calling methods can deallocate it immediately
|
|
using release. Otherwise if many cells are drawn/their size
|
|
computed, we pile up hundreds or thousands of these objects before they
|
|
are deallocated at the end of the run loop. */
|
|
- (NSDictionary*) _nonAutoreleasedTypingAttributes
|
|
{
|
|
NSDictionary *attr;
|
|
NSColor *color;
|
|
NSMutableParagraphStyle *paragraphStyle;
|
|
|
|
color = [self textColor];
|
|
/* Note: There are only a few possible paragraph styles for cells.
|
|
TODO: Cache them and reuse them for the whole app lifetime. */
|
|
paragraphStyle = [[NSParagraphStyle defaultParagraphStyle] mutableCopy];
|
|
[paragraphStyle setLineBreakMode: [self lineBreakMode]];
|
|
[paragraphStyle setBaseWritingDirection: [self baseWritingDirection]];
|
|
[paragraphStyle setAlignment: [self alignment]];
|
|
|
|
attr = [[NSDictionary alloc] initWithObjectsAndKeys:
|
|
_font, NSFontAttributeName,
|
|
color, NSForegroundColorAttributeName,
|
|
paragraphStyle, NSParagraphStyleAttributeName,
|
|
nil];
|
|
RELEASE (paragraphStyle);
|
|
return attr;
|
|
}
|
|
|
|
- (NSSize) _sizeText: (NSString*)title
|
|
{
|
|
NSSize size;
|
|
NSDictionary *dict;
|
|
|
|
if (title == nil)
|
|
{
|
|
return NSMakeSize (0,0);
|
|
}
|
|
|
|
dict = [self _nonAutoreleasedTypingAttributes];
|
|
size = [title sizeWithAttributes: dict];
|
|
RELEASE (dict);
|
|
return size;
|
|
}
|
|
|
|
/**
|
|
* Private internal method, returns an attributed string to display.
|
|
*/
|
|
- (NSAttributedString*) _drawAttributedString
|
|
{
|
|
if (!_cell.is_disabled)
|
|
{
|
|
return [self attributedStringValue];
|
|
}
|
|
else
|
|
{
|
|
NSAttributedString *attrStr = [self attributedStringValue];
|
|
NSDictionary *attribs;
|
|
NSMutableDictionary *newAttribs;
|
|
|
|
attribs = [attrStr attributesAtIndex: 0
|
|
effectiveRange: NULL];
|
|
newAttribs = [NSMutableDictionary dictionaryWithDictionary: attribs];
|
|
[newAttribs setObject: [NSColor disabledControlTextColor]
|
|
forKey: NSForegroundColorAttributeName];
|
|
|
|
return AUTORELEASE([[NSAttributedString alloc]
|
|
initWithString: [attrStr string]
|
|
attributes: newAttribs]);
|
|
}
|
|
}
|
|
|
|
|
|
- (BOOL) _shouldShortenStringForRect: (NSRect)titleRect size: (NSSize)titleSize length: (NSUInteger)length
|
|
{
|
|
NSLineBreakMode mode = [self lineBreakMode];
|
|
|
|
return ((titleSize.width > titleRect.size.width) && (length > 4) &&
|
|
(mode == NSLineBreakByTruncatingHead ||
|
|
mode == NSLineBreakByTruncatingTail ||
|
|
mode == NSLineBreakByTruncatingMiddle));
|
|
}
|
|
|
|
- (NSAttributedString*) _resizeAttributedString: (NSAttributedString*)attrstring forRect: (NSRect)titleRect
|
|
{
|
|
// Redo string based on selected truncation mask...
|
|
NSMutableAttributedString *mutableString = AUTORELEASE([attrstring mutableCopy]);
|
|
NSString *ellipsis = @"...";
|
|
NSLineBreakMode mode = [self lineBreakMode];
|
|
// This code shortens the string one character at a time.
|
|
// To speed it up we start off proportional:
|
|
CGFloat width = [mutableString size].width;
|
|
int cut = MAX(floor([mutableString length] * (width - titleRect.size.width) / width), 4);
|
|
|
|
do
|
|
{
|
|
NSRange replaceRange;
|
|
if (mode == NSLineBreakByTruncatingHead)
|
|
replaceRange = NSMakeRange(0, cut);
|
|
else if (mode == NSLineBreakByTruncatingTail)
|
|
replaceRange = NSMakeRange([mutableString length] - cut, cut);
|
|
else
|
|
replaceRange = NSMakeRange(([mutableString length] / 2) - (cut / 2), cut);
|
|
[mutableString replaceCharactersInRange: replaceRange withString: ellipsis];
|
|
cut = 4;
|
|
} while ([mutableString length] > 4 && [mutableString size].width > titleRect.size.width);
|
|
|
|
// Return the modified attributed string...
|
|
return mutableString;
|
|
}
|
|
|
|
/**
|
|
* Private internal method to display an attributed string.
|
|
*/
|
|
- (void) _drawAttributedText: (NSAttributedString*)aString
|
|
inFrame: (NSRect)aRect
|
|
{
|
|
NSSize titleSize;
|
|
|
|
if (aString == nil)
|
|
return;
|
|
|
|
titleSize = [aString size];
|
|
if ([self _shouldShortenStringForRect: aRect size: titleSize length: [aString length]])
|
|
{
|
|
aString = [self _resizeAttributedString: aString forRect: aRect];
|
|
titleSize = [aString size];
|
|
}
|
|
|
|
/** Important: text should always be vertically centered without
|
|
* considering descender [as if descender did not exist].
|
|
* This is particularly important for single line texts.
|
|
* Please make sure the output remains always correct.
|
|
*/
|
|
aRect.origin.y = NSMidY (aRect) - titleSize.height/2;
|
|
aRect.size.height = titleSize.height;
|
|
|
|
[aString drawInRect: aRect];
|
|
}
|
|
|
|
- (void) _drawText: (NSString*)aString inFrame: (NSRect)cellFrame
|
|
{
|
|
NSSize titleSize;
|
|
NSDictionary *attributes;
|
|
|
|
if (aString == nil)
|
|
return;
|
|
|
|
attributes = [self _nonAutoreleasedTypingAttributes];
|
|
titleSize = [aString sizeWithAttributes: attributes];
|
|
if ([self _shouldShortenStringForRect: cellFrame size: titleSize length: [aString length]])
|
|
{
|
|
NSAttributedString *attrstring = AUTORELEASE([[NSAttributedString alloc] initWithString: aString
|
|
attributes: attributes]);
|
|
return [self _drawAttributedText: attrstring inFrame: cellFrame];
|
|
}
|
|
|
|
/** Important: text should always be vertically centered without
|
|
* considering descender [as if descender did not exist].
|
|
* This is particularly important for single line texts.
|
|
* Please make sure the output remains always correct.
|
|
*/
|
|
cellFrame.origin.y = NSMidY (cellFrame) - titleSize.height/2;
|
|
cellFrame.size.height = titleSize.height;
|
|
|
|
[aString drawInRect: cellFrame withAttributes: attributes];
|
|
RELEASE (attributes);
|
|
}
|
|
|
|
// Private helper method overridden in subclasses
|
|
- (void) _drawBorderAndBackgroundWithFrame: (NSRect)cellFrame
|
|
inView: (NSView*)controlView
|
|
{
|
|
NSBorderType aType;
|
|
|
|
// Get border size
|
|
if (_cell.is_bordered)
|
|
aType = NSLineBorder;
|
|
else if (_cell.is_bezeled)
|
|
aType = NSBezelBorder;
|
|
else
|
|
aType = NSNoBorder;
|
|
|
|
[[GSTheme theme] drawBorderType: aType frame: cellFrame view: controlView];
|
|
}
|
|
|
|
// Private helper method
|
|
- (void) _drawFocusRingWithFrame: (NSRect)cellFrame inView: (NSView*)controlView
|
|
{
|
|
if (_cell.shows_first_responder
|
|
&& [[controlView window] firstResponder] == controlView)
|
|
{
|
|
switch (_cell.focus_ring_type)
|
|
{
|
|
case NSFocusRingTypeDefault:
|
|
[[GSTheme theme] drawFocusFrame: [self drawingRectForBounds:
|
|
cellFrame]
|
|
view: controlView];
|
|
break;
|
|
case NSFocusRingTypeExterior:
|
|
[[GSTheme theme] drawFocusFrame: cellFrame
|
|
view: controlView];
|
|
break;
|
|
case NSFocusRingTypeNone:
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
- (void) _drawEditorWithFrame: (NSRect)cellFrame
|
|
inView: (NSView *)controlView
|
|
{
|
|
/* Look Ma', no drawing here... */
|
|
|
|
if ([controlView isKindOfClass: [NSControl class]])
|
|
{
|
|
/* Adjust the text editor's frame to match cell's frame (w/o border) */
|
|
NSRect titleRect = [self titleRectForBounds: cellFrame];
|
|
NSText *textObject = [(NSControl*)controlView currentEditor];
|
|
NSView *clipView = [textObject superview];
|
|
|
|
if ([clipView isKindOfClass: [NSClipView class]])
|
|
{
|
|
[clipView setFrame: titleRect];
|
|
}
|
|
else
|
|
{
|
|
[textObject setFrame: titleRect];
|
|
}
|
|
}
|
|
}
|
|
|
|
- (BOOL) _sendsActionOn:(NSUInteger)eventTypeMask
|
|
{
|
|
return (_action_mask & eventTypeMask);
|
|
}
|
|
|
|
- (void) _setInEditing: (BOOL)flag
|
|
{
|
|
_cell.in_editing = flag;
|
|
}
|
|
|
|
- (BOOL) _inEditing
|
|
{
|
|
return _cell.in_editing;
|
|
}
|
|
|
|
- (void) _updateFieldEditor: (NSText*)textObject
|
|
{
|
|
if (_formatter != nil)
|
|
{
|
|
NSString *contents;
|
|
|
|
contents = [_formatter editingStringForObjectValue: _object_value];
|
|
if (contents == nil)
|
|
{
|
|
// We cannot call the stringValue method as this will call
|
|
// validateEditing in NSActionCell subclasses
|
|
if (nil == _contents)
|
|
{
|
|
contents = @"";
|
|
}
|
|
else
|
|
{
|
|
if (_cell.contents_is_attributed_string == NO)
|
|
{
|
|
contents = (NSString *)_contents;
|
|
}
|
|
else
|
|
{
|
|
contents = [(NSAttributedString *)_contents string];
|
|
}
|
|
}
|
|
}
|
|
if (![contents isEqualToString: [textObject string]])
|
|
[textObject setText: contents];
|
|
}
|
|
else
|
|
{
|
|
NSString *contents;
|
|
|
|
if (nil == _contents)
|
|
{
|
|
contents = @"";
|
|
}
|
|
else
|
|
{
|
|
if (_cell.contents_is_attributed_string == NO)
|
|
{
|
|
contents = (NSString *)_contents;
|
|
}
|
|
else
|
|
{
|
|
contents = [(NSAttributedString *)_contents string];
|
|
}
|
|
}
|
|
if (![contents isEqualToString: [textObject string]])
|
|
{
|
|
if (_cell.contents_is_attributed_string == NO)
|
|
{
|
|
[textObject setText: contents];
|
|
}
|
|
else
|
|
{
|
|
// FIXME what about attribute changes?
|
|
NSRange range = NSMakeRange(0, [[textObject string] length]);
|
|
[textObject replaceCharactersInRange: range
|
|
withAttributedString: (NSAttributedString *)_contents];
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Private method used by NSImageCell and NSButtonCell for calculating
|
|
* scaled image size
|
|
*/
|
|
|
|
static inline NSSize
|
|
scaleProportionally(NSSize imageSize, NSSize canvasSize, BOOL scaleUpOrDown)
|
|
{
|
|
CGFloat ratio;
|
|
|
|
if (imageSize.width <= 0
|
|
|| imageSize.height <= 0)
|
|
{
|
|
return NSMakeSize(0, 0);
|
|
}
|
|
|
|
/* Get the smaller ratio and scale the image size by it. */
|
|
ratio = MIN(canvasSize.width / imageSize.width,
|
|
canvasSize.height / imageSize.height);
|
|
|
|
/* Only scale down, unless scaleUpOrDown is YES */
|
|
if (ratio < 1.0 || scaleUpOrDown)
|
|
{
|
|
imageSize.width *= ratio;
|
|
imageSize.height *= ratio;
|
|
}
|
|
|
|
return imageSize;
|
|
}
|
|
|
|
- (NSSize) _scaleImageWithSize: (NSSize)imageSize
|
|
toFitInSize: (NSSize)canvasSize
|
|
scalingType: (NSImageScaling)scalingType
|
|
{
|
|
NSSize result;
|
|
switch (scalingType)
|
|
{
|
|
case NSImageScaleProportionallyDown: // == NSScaleProportionally
|
|
{
|
|
result = scaleProportionally (imageSize, canvasSize, NO);
|
|
break;
|
|
}
|
|
case NSImageScaleAxesIndependently: // == NSScaleToFit
|
|
{
|
|
result = canvasSize;
|
|
break;
|
|
}
|
|
default:
|
|
case NSImageScaleNone: // == NSScaleNone
|
|
{
|
|
result = imageSize;
|
|
break;
|
|
}
|
|
case NSImageScaleProportionallyUpOrDown:
|
|
{
|
|
result = scaleProportionally (imageSize, canvasSize, YES);
|
|
break;
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
@end
|