Multiple drawing fixes

git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/gui/trunk@4006 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
Richard Frith-MacDonald 1999-04-01 06:19:37 +00:00
parent cdd115ed0e
commit e3dbc302ff
6 changed files with 718 additions and 378 deletions

View file

@ -1,3 +1,15 @@
Wed Mar 31 21:06:00 1999 Richard Frith-Macdonald <richard@brainstorm.co.uk>
* Source/NSCell.m: Make bezeled and bordered mutually exclusive
- bug report by Jonathan Gapen
* Headers/AppKit/NSStringDrawing.h: Add all the string drawing stuff
* Source/NSStringDrawing.m: Rewrite from scratch - preliminary work.
* Source/NSButton.m: Removed redundant lock/unlock in drawing ops.
* Source/NSControl.m: tidy.
* Source/NSSplitView.m: avoid compiler warning.
* Source/NSTextFieldCell.m: tidy up, set draws background color in
initialisation, fix border/bezel drawing.
Wed Mar 31 17:32:00 1999 Richard Frith-Macdonald <richard@brainstorm.co.uk>
* Source/NSView: ([-viewWithTag:]) complete rewrite to find nearest

View file

@ -8,6 +8,8 @@
Author: Felipe A. Rodriguez <far@ix.netcom.com>
Date: Aug 1998
Rewrite: Richard Frith-Macdonald <richard@brainstorm.co.uk>
Date: Mar 1999
This file is part of the GNUstep GUI Library.
@ -34,45 +36,42 @@
#include <Foundation/NSAttributedString.h>
#include <Foundation/NSGeometry.h>
// global NSString attribute names used in ascessing
// the respective property in a text attributes
// dictionary. if the key is not in the dictionary
// the default value is assumed
extern NSString *NSFontAttributeName; // NSFont, Helvetica 12
extern NSString *NSParagraphStyleAttributeName; // defaultParagraphStyle
extern NSString *NSForegroundColorAttributeName; // NSColor, blackColor
extern NSString *NSUnderlineStyleAttributeName; // NSNumber int, 0 no line
extern NSString *NSSuperscriptAttributeName; // NSNumber int, 0
extern NSString *NSBackgroundColorAttributeName; // NSColor, nil
extern NSString *NSAttachmentAttributeName; // NSTextAttachment, nil
extern NSString *NSLigatureAttributeName; // NSNumber int, 1
extern NSString *NSBaselineOffsetAttributeName; // NSNumber float, 0 points
extern NSString *NSKernAttributeName; // NSNumber float, 0
//
// Extended definitions:
//
// NSParagraphStyleAttributeName NSParagraphStyle, default is
// defaultParagraphStyle
//
// NSKernAttributeName NSNumber float, offset from
// baseline, amount to modify default
// kerning, if 0 kerning is off
// global NSString attribute names used in accessing
// the respective property in a text attributes
// dictionary. if the key is not in the dictionary
// the default value is assumed
extern NSString *NSFontAttributeName;
extern NSString *NSParagraphStyleAttributeName;
extern NSString *NSForegroundColorAttributeName;
extern NSString *NSUnderlineStyleAttributeName;
extern NSString *NSSuperscriptAttributeName;
extern NSString *NSBackgroundColorAttributeName;
extern NSString *NSAttachmentAttributeName;
extern NSString *NSLigatureAttributeName;
extern NSString *NSBaselineOffsetAttributeName;
extern NSString *NSKernAttributeName;
// Currently supported values for NSUnderlineStyleAttributeName
enum
{ // Currently supported values for
NSSingleUnderlineStyle = 1 // NSUnderlineStyleAttributeName
{
GSNoUnderlineStyle = 0,
NSSingleUnderlineStyle = 1
};
@interface NSString (NSStringDrawing)
- (NSSize)sizeWithAttributes:(NSDictionary *)attrs;
- (void) drawAtPoint: (NSPoint)point withAttributes: (NSDictionary*)attrs;
- (void) drawInRect: (NSRect)rect withAttributes: (NSDictionary*)attrs;
- (NSSize) sizeWithAttributes: (NSDictionary*)attrs;
@end
@interface NSAttributedString (NSStringDrawing)
- (NSSize)size;
- (NSSize) size;
- (void) drawAtPoint: (NSPoint)point;
- (void) drawInRect: (NSRect)rect;
@end

View file

@ -508,11 +508,15 @@
- (void) setBezeled: (BOOL)flag
{
cell_bezeled = flag;
if (cell_bezeled)
cell_bordered = NO;
}
- (void) setBordered: (BOOL)flag
{
cell_bordered = flag;
if (cell_bordered)
cell_bezeled = NO;
}
//
@ -670,14 +674,11 @@ static inline NSPoint centerSizeInRect(NSSize innerSize, NSRect outerRect)
// draw the border if needed
if ([self isBordered])
{
if ([self isBezeled])
{
NSDrawWhiteBezel(cellFrame, cellFrame);
}
else
{
NSFrameRect(cellFrame);
}
NSFrameRect(cellFrame);
}
else if ([self isBezeled])
{
NSDrawWhiteBezel(cellFrame, cellFrame);
}
[self drawInteriorWithFrame: cellFrame inView: controlView];

View file

@ -1,4 +1,4 @@
/*
/*
NSControl.m
The abstract control class
@ -9,14 +9,14 @@
Date: 1996
Author: Felipe A. Rodriguez <far@ix.netcom.com>
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
@ -26,11 +26,13 @@
License along with this library; see the file COPYING.LIB.
If not, write to the Free Software Foundation,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
*/
#include <gnustep/gui/config.h>
#include <Foundation/NSException.h>
#include <AppKit/NSControl.h>
#include <AppKit/NSColor.h>
#include <AppKit/NSEvent.h>
#include <AppKit/NSWindow.h>
#include <AppKit/NSApplication.h>
@ -48,39 +50,47 @@ static id _NSCONTROL_CELL_CLASS = nil;
//
// Class methods
//
+ (void)initialize
+ (void) initialize
{
if (self == [NSControl class])
{
NSDebugLog(@"Initialize NSControl class\n");
[self setVersion:1]; // Initial version
[self setCellClass:[NSCell class]]; // Set cell class
}
if (self == [NSControl class])
{
NSDebugLog(@"Initialize NSControl class\n");
[self setVersion: 1];
}
}
//
// Setting the Control's Cell
// Setting the Control's Cell
//
+ (Class)cellClass { return _NSCONTROL_CELL_CLASS; }
+ (void)setCellClass:(Class)factoryId { _NSCONTROL_CELL_CLASS = factoryId; }
+ (Class) cellClass
{
return _NSCONTROL_CELL_CLASS;
}
+ (void) setCellClass: (Class)factoryId
{
_NSCONTROL_CELL_CLASS = factoryId;
}
//
// Instance methods
//
- (id)initWithFrame:(NSRect)frameRect
- (id) initWithFrame: (NSRect)frameRect
{
[super initWithFrame:frameRect];
// create our cell
[self setCell:[[_NSCONTROL_CELL_CLASS new] autorelease]];
tag = 0;
[super initWithFrame: frameRect];
if (_NSCONTROL_CELL_CLASS)
[self setCell: [[_NSCONTROL_CELL_CLASS new] autorelease]];
else
[self setCell: [[NSCell new] autorelease]];
tag = 0;
return self;
return self;
}
- (void)dealloc
- (void) dealloc
{
[cell release]; // release our cell
[super dealloc];
[cell release];
[super dealloc];
}
//
@ -97,219 +107,259 @@ static id _NSCONTROL_CELL_CLASS = nil;
}
//
// Setting the Control's Cell
// Setting the Control's Cell
//
- (id)cell { return cell; }
- (void)setCell:(NSCell *)aCell
- (id) cell
{
if (![aCell isKindOfClass:[NSCell class]]) // must be a cell
return;
return cell;
}
[cell setControlView:nil];
[aCell setControlView:self];
[aCell retain];
[cell release];
cell = aCell;
- (void) setCell: (NSCell *)aCell
{
if (![aCell isKindOfClass: [NSCell class]])
[NSException raise: NSInvalidArgumentException
format: @"attempt to set silly value for control cell"];
[cell setControlView: nil];
[aCell setControlView: self];
[aCell retain];
[cell release];
cell = aCell;
}
//
// Enabling and Disabling the Control
// Enabling and Disabling the Control
//
- (BOOL)isEnabled { return [[self selectedCell] isEnabled]; }
- (void)setEnabled:(BOOL)flag { [[self selectedCell] setEnabled:flag]; }
//
// Identifying the Selected Cell
//
- (id)selectedCell
- (BOOL) isEnabled
{
if ([cell state])
return cell;
else
return nil;
return [[self selectedCell] isEnabled];
}
- (int)selectedTag
- (void) setEnabled: (BOOL)flag
{
return [[self selectedCell] tag];
[[self selectedCell] setEnabled: flag];
}
//
// Setting the Control's Value
// Identifying the Selected Cell
//
- (double)doubleValue
- (id) selectedCell
{
return [[self selectedCell] doubleValue];
if ([cell state])
return cell;
else
return nil;
}
- (float)floatValue
- (int) selectedTag
{
return [[self selectedCell] floatValue];
}
- (int)intValue
{
return [[self selectedCell] intValue];
}
- (void)setDoubleValue:(double)aDouble
{
[[self selectedCell] setDoubleValue:aDouble];
[self setNeedsDisplay:YES];
}
- (void)setFloatValue:(float)aFloat
{
[[self selectedCell] setFloatValue:aFloat];
[self setNeedsDisplay:YES];
}
- (void)setIntValue:(int)anInt
{
[[self selectedCell] setIntValue:anInt];
[self setNeedsDisplay:YES];
}
- (void)setNeedsDisplay { [super setNeedsDisplay:YES]; }
- (void)setStringValue:(NSString *)aString
{
[[self selectedCell] setStringValue:aString];
[self setNeedsDisplay:YES];
}
- (NSString *)stringValue
{
return [[self selectedCell] stringValue];
return [[self selectedCell] tag];
}
//
// Interacting with Other Controls
// Setting the Control's Value
//
- (void)takeDoubleValueFrom:(id)sender
- (double) doubleValue
{
[[self selectedCell] takeDoubleValueFrom:sender];
[self setNeedsDisplay:YES];
return [[self selectedCell] doubleValue];
}
- (void)takeFloatValueFrom:(id)sender
- (float) floatValue
{
[[self selectedCell] takeFloatValueFrom:sender];
[self setNeedsDisplay:YES];
return [[self selectedCell] floatValue];
}
- (void)takeIntValueFrom:(id)sender
- (int) intValue
{
[[self selectedCell] takeIntValueFrom:sender];
[self setNeedsDisplay:YES];
return [[self selectedCell] intValue];
}
- (void)takeStringValueFrom:(id)sender
- (void) setDoubleValue: (double)aDouble
{
[[self selectedCell] takeStringValueFrom:sender];
[self setNeedsDisplay:YES];
[[self selectedCell] setDoubleValue: aDouble];
[self setNeedsDisplay: YES];
}
- (void) setFloatValue: (float)aFloat
{
[[self selectedCell] setFloatValue: aFloat];
[self setNeedsDisplay: YES];
}
- (void) setIntValue: (int)anInt
{
[[self selectedCell] setIntValue: anInt];
[self setNeedsDisplay: YES];
}
- (void) setNeedsDisplay
{
[super setNeedsDisplay: YES];
}
- (void) setStringValue: (NSString *)aString
{
[[self selectedCell] setStringValue: aString];
[self setNeedsDisplay: YES];
}
- (NSString *) stringValue
{
return [[self selectedCell] stringValue];
}
//
// Formatting Text
// Interacting with Other Controls
//
- (NSTextAlignment)alignment
- (void) takeDoubleValueFrom: (id)sender
{
if (cell)
return [cell alignment];
else
return NSLeftTextAlignment;
[[self selectedCell] takeDoubleValueFrom: sender];
[self setNeedsDisplay: YES];
}
- (NSFont *)font
- (void) takeFloatValueFrom: (id)sender
{
if (cell)
return [cell font];
else
return nil;
[[self selectedCell] takeFloatValueFrom: sender];
[self setNeedsDisplay: YES];
}
- (void)setAlignment:(NSTextAlignment)mode
- (void) takeIntValueFrom: (id)sender
{
if (cell)
{
[cell setAlignment:mode];
[self setNeedsDisplay:YES];
}
[[self selectedCell] takeIntValueFrom: sender];
[self setNeedsDisplay: YES];
}
- (void)setFont:(NSFont *)fontObject
- (void) takeStringValueFrom: (id)sender
{
if (cell)
[cell setFont:fontObject];
[[self selectedCell] takeStringValueFrom: sender];
[self setNeedsDisplay: YES];
}
- (void)setFloatingPointFormat:(BOOL)autoRange
left:(unsigned)leftDigits
right:(unsigned)rightDigits
{}
//
// Managing the Field Editor
// Formatting Text
//
- (BOOL)abortEditing { return NO; }
- (NSText *)currentEditor { return nil; }
- (void)validateEditing {} // FIX ME
//
// Resizing the Control
//
- (void)calcSize {} // FIX ME
- (void)sizeToFit {}
//
// Displaying the Control and Cell
//
- (void)drawCell:(NSCell *)aCell
- (NSTextAlignment) alignment
{
if (cell == aCell)
{
[self lockFocus];
[cell drawWithFrame:bounds inView:self];
[self unlockFocus];
}
if (cell)
return [cell alignment];
else
return NSLeftTextAlignment;
}
- (void)drawCellInside:(NSCell *)aCell
- (NSFont *) font
{
if (cell == aCell)
{
[self lockFocus];
[cell drawInteriorWithFrame:bounds inView:self];
[self unlockFocus];
}
if (cell)
return [cell font];
else
return nil;
}
- (void)selectCell:(NSCell *)aCell
{
if (cell == aCell)
[cell setState:1];
- (void) setAlignment: (NSTextAlignment)mode
{
if (cell)
{
[cell setAlignment: mode];
[self setNeedsDisplay: YES];
}
}
- (void)updateCell:(NSCell *)aCell { [self setNeedsDisplay:YES]; }
- (void)updateCellInside:(NSCell *)aCell { [self setNeedsDisplay:YES]; }
//
// Target and Action
//
- (SEL)action { return [cell action]; }
- (BOOL)isContinuous { return [cell isContinuous]; }
- (BOOL)sendAction:(SEL)theAction to:(id)theTarget
- (void) setFont: (NSFont *)fontObject
{
NSApplication *theApp = [NSApplication sharedApplication];
if (cell)
[cell setFont: fontObject];
}
if (theAction && theTarget)
return [theApp sendAction:theAction to:theTarget from:self];
else
return NO;
- (void) setFloatingPointFormat: (BOOL)autoRange
left: (unsigned)leftDigits
right: (unsigned)rightDigits
{
}
//
// Managing the Field Editor
//
- (BOOL) abortEditing
{
return NO;
}
- (NSText *) currentEditor
{
return nil;
}
- (void) validateEditing
{
} // FIX ME
//
// Resizing the Control
//
- (void) calcSize
{
} // FIX ME
- (void) sizeToFit
{
}
//
// Displaying the Control and Cell
//
- (void) drawCell: (NSCell *)aCell
{
if (cell == aCell)
{
[cell drawWithFrame: bounds inView: self];
}
}
- (void) drawCellInside: (NSCell *)aCell
{
if (cell == aCell)
{
[cell drawInteriorWithFrame: bounds inView: self];
}
}
- (void) selectCell: (NSCell *)aCell
{
if (cell == aCell)
[cell setState: 1];
}
- (void) updateCell: (NSCell *)aCell
{
[self setNeedsDisplay: YES];
}
- (void) updateCellInside: (NSCell *)aCell
{
[self setNeedsDisplay: YES];
}
//
// Target and Action
//
- (SEL) action
{
return [cell action];
}
- (BOOL) isContinuous
{
return [cell isContinuous];
}
- (BOOL) sendAction: (SEL)theAction to: (id)theTarget
{
NSApplication *theApp = [NSApplication sharedApplication];
if (theAction && theTarget)
return [theApp sendAction: theAction to: theTarget from: self];
else
return NO;
}
- (int) sendActionOn: (int)mask
@ -317,124 +367,154 @@ NSApplication *theApp = [NSApplication sharedApplication];
return [cell sendActionOn: mask];
}
- (void)setAction:(SEL)aSelector { [cell setAction:aSelector]; }
- (void)setContinuous:(BOOL)flag { [cell setContinuous:flag]; }
- (void)setTarget:(id)anObject { [cell setTarget:anObject]; }
- (id)target { return [cell target]; }
//
// Assigning a Tag
//
- (void)setTag:(int)anInt { tag = anInt; }
- (int)tag { return tag; }
//
// Tracking the Mouse
//
- (void)mouseDown:(NSEvent *)theEvent
- (void) setAction: (SEL)aSelector
{
NSApplication *theApp = [NSApplication sharedApplication];
BOOL mouseUp = NO, done = NO;
NSEvent *e;
int oldActionMask;
NSPoint location;
unsigned int event_mask = NSLeftMouseDownMask | NSLeftMouseUpMask |
NSMouseMovedMask | NSLeftMouseDraggedMask |
NSRightMouseDraggedMask;
NSDebugLog(@"NSControl mouseDown\n");
if (![self isEnabled]) // If we are not enabled
return; // then ignore the mouse
if ([cell isContinuous]) // Have NSCell send action
oldActionMask = [cell sendActionOn:0]; // only if we're continuous
else
oldActionMask = [cell sendActionOn: NSPeriodicMask];
[window _captureMouse: self]; // capture the mouse
[self lockFocus];
e = theEvent;
while (!done) // loop until mouse goes up
{
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 ((location.x >= 0) && (location.x < bounds.size.width) &&
(location.y >= 0 && location.y < bounds.size.height))
{ // highlight the cell
[cell highlight: YES withFrame: bounds inView: self];
[window flushWindow];
if([cell trackMouse:e inRect:bounds ofView:self untilMouseUp:YES])
done = mouseUp = YES; // YES if the mouse
else // goes up in the cell
{
[cell highlight: NO withFrame: bounds inView: self];
[window flushWindow];
}
}
if (done) // if done break
break; // out of the loop
NSDebugLog(@"NSControl process another event\n");
e = [theApp nextEventMatchingMask:event_mask // get next event
untilDate:nil
inMode:NSEventTrackingRunLoopMode
dequeue:YES];
if ([e type] == NSLeftMouseUp) // If mouse went up
done = YES; // then we are done
}
[window _releaseMouse: self]; // Release mouse
if (mouseUp) // the mouse went up in the button
{
[self lockFocus]; // lockFocus gets released when button is
// drawn presseddown (NSView's displayRect)
// so we call it again, one of these needs
// to be optimized out FAR FIX ME
// [cell setState:![cell state]];
[cell highlight: NO withFrame: bounds inView: self]; // Unhighlight
[window flushWindow];
}
[self unlockFocus];
// Restore the old
[cell sendActionOn:oldActionMask]; // action mask
if (mouseUp) // Have the target
[self sendAction:[self action] to:[self target]]; // perform action
[cell setAction: aSelector];
}
- (BOOL)ignoresMultiClick { return NO; }
- (void)setIgnoresMultiClick:(BOOL)flag {} // FIX ME
- (void) setContinuous: (BOOL)flag
{
[cell setContinuous: flag];
}
- (void) setTarget: (id)anObject
{
[cell setTarget: anObject];
}
- (id) target
{
return [cell target];
}
//
// Assigning a Tag
//
- (void) setTag: (int)anInt
{
tag = anInt;
}
- (int) tag
{
return tag;
}
//
// Tracking the Mouse
//
- (void) mouseDown: (NSEvent *)theEvent
{
NSApplication *theApp = [NSApplication sharedApplication];
BOOL mouseUp = NO, done = NO;
NSEvent *e;
int oldActionMask;
NSPoint location;
unsigned int event_mask = NSLeftMouseDownMask | NSLeftMouseUpMask |
NSMouseMovedMask | NSLeftMouseDraggedMask |
NSRightMouseDraggedMask;
NSDebugLog(@"NSControl mouseDown\n");
if (![self isEnabled])
return;
if ([cell isContinuous])
oldActionMask = [cell sendActionOn: 0];
else
oldActionMask = [cell sendActionOn: NSPeriodicMask];
[window _captureMouse: self];
[self lockFocus];
e = theEvent;
while (!done) // loop until mouse goes up
{
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 ((location.x >= 0) && (location.x < bounds.size.width) &&
(location.y >= 0 && location.y < bounds.size.height))
{
[cell highlight: YES withFrame: bounds inView: self];
[window flushWindow];
if ([cell trackMouse: e
inRect: bounds
ofView: self
untilMouseUp: YES])
done = mouseUp = YES;
else
{
[cell highlight: NO withFrame: bounds inView: self];
[window flushWindow];
}
}
if (done)
break;
NSDebugLog(@"NSControl process another event\n");
e = [theApp nextEventMatchingMask: event_mask
untilDate: nil
inMode: NSEventTrackingRunLoopMode
dequeue: YES];
if ([e type] == NSLeftMouseUp)
done = YES;
}
[window _releaseMouse: self];
if (mouseUp)
{
[self lockFocus];
// [cell setState: ![cell state]];
[cell highlight: NO withFrame: bounds inView: self];
[window flushWindow];
}
[self unlockFocus];
[cell sendActionOn: oldActionMask];
if (mouseUp)
[self sendAction: [self action] to: [self target]];
}
- (BOOL) ignoresMultiClick
{
return NO;
}
- (void) setIgnoresMultiClick: (BOOL)flag
{
}
//
// Methods Implemented by the Delegate
//
- (BOOL)control:(NSControl *)control
textShouldBeginEditing:(NSText *)fieldEditor
- (BOOL) control: (NSControl *)control
textShouldBeginEditing: (NSText *)fieldEditor
{
return NO;
return NO;
}
- (BOOL)control:(NSControl *)control
textShouldEndEditing:(NSText *)fieldEditor
- (BOOL) control: (NSControl *)control
textShouldEndEditing: (NSText *)fieldEditor
{
return NO;
return NO;
}
- (void)controlTextDidBeginEditing:(NSNotification *)aNotification
{}
- (void) controlTextDidBeginEditing: (NSNotification *)aNotification
{
}
- (void)controlTextDidEndEditing:(NSNotification *)aNotification
{}
- (void) controlTextDidEndEditing: (NSNotification *)aNotification
{
}
- (void)controlTextDidChange:(NSNotification *)aNotification
{}
- (void) controlTextDidChange: (NSNotification *)aNotification
{
}
//
// NSCoding protocol
@ -442,7 +522,7 @@ unsigned int event_mask = NSLeftMouseDownMask | NSLeftMouseUpMask |
- (void) encodeWithCoder: (NSCoder*)aCoder
{
[super encodeWithCoder: aCoder];
[aCoder encodeValueOfObjCType: @encode(int) at: &tag];
[aCoder encodeObject: cell];
}
@ -450,7 +530,7 @@ unsigned int event_mask = NSLeftMouseDownMask | NSLeftMouseUpMask |
- (id) initWithCoder: (NSCoder*)aDecoder
{
[super initWithCoder: aDecoder];
[aDecoder decodeValueOfObjCType: @encode(int) at: &tag];
[aDecoder decodeValueOfObjCType: @encode(id) at: &cell];

View file

@ -57,7 +57,7 @@
NSPoint p;
NSEvent *e;
NSRect r, r1, bigRect, vis;
id v, prev = nil;
id v = nil, prev = nil;
float minCoord, maxCoord;
NSArray *subs = [self subviews];
int offset = 0,i,count = [subs count];

View file

@ -1,21 +1,23 @@
/*
/*
NSStringDrawing.m
Categories which add measure capabilities to NSAttributedString
Categories which add measure capabilities to NSAttributedString
and NSString.
Copyright (C) 1997 Free Software Foundation, Inc.
Copyright (C) 1997,1999 Free Software Foundation, Inc.
Author: Felipe A. Rodriguez <far@ix.netcom.com>
Date: Aug 1998
Author: Richard Frith-Macdonald <richard@brainstorm.co.uk>
Date: Mar 1999 - rewrite from scratch
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
@ -25,79 +27,325 @@
License along with this library; see the file COPYING.LIB.
If not, write to the Free Software Foundation,
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
*/
#include <Foundation/Foundation.h>
#include <AppKit/NSStringDrawing.h>
#include <AppKit/AppKit.h>
// by default tabs are measured as one
#define TABWIDTH 3 // char so this value is set to one
// minus the default tab width of 4
/*
* I know it's severely sub-optimal, but the NSString methods just
* use NSAttributes string to do the job.
*/
@implementation NSString (NSStringDrawing)
- (NSSize)sizeWithAttributes:(NSDictionary *)attrs
- (void) drawAtPoint: (NSPoint)point withAttributes: (NSDictionary *)attrs
{
NSFont *font;
const char *str = [self cString];
int i = 0, j = TABWIDTH;
static float tabSize;
float tabSumSize;
static float pointSize;
static NSFont *lastFont = nil;
NSAttributedString *a;
while(*str != '\0') // calc the additional size
{ // to be added for tabs.
if(*str++ == '\t')
{ // j is initialized to the
i += j; // max number of spaces
j = TABWIDTH; // needed per tab. it then
} // varies in order to align
else // tabs to even multiples
j = j-- > 0 ? j : TABWIDTH; // of TABWIDTH + 1.
};
// if font is not
if(!(font = [attrs objectForKey:NSFontAttributeName])) // specified, use
font = [NSFont userFontOfSize:12]; // the default
if(font != lastFont) // update font info
{ // if font changes
tabSize = [font widthOfString:@"\t"];
lastFont = font;
pointSize = [font pointSize];
}
tabSumSize = (float)i * tabSize;
return NSMakeSize(([font widthOfString:self] + tabSumSize), pointSize);
a = [NSAttributedString allocWithZone: NSDefaultMallocZone()];
[a initWithString: self attributes: attrs];
[a drawAtPoint: point];
[a release];
}
- (void) drawInRect: (NSRect)rect withAttributes: (NSDictionary *)attrs
{
NSAttributedString *a;
a = [NSAttributedString allocWithZone: NSDefaultMallocZone()];
[a initWithString: self attributes: attrs];
[a drawInRect: rect];
[a release];
}
- (NSSize) sizeWithAttributes: (NSDictionary *)attrs
{
NSAttributedString *a;
NSSize s;
a = [NSAttributedString allocWithZone: NSDefaultMallocZone()];
[a initWithString: self attributes: attrs];
s = [a size];
[a release];
return s;
}
@end
@implementation NSAttributedString (NSStringDrawing)
- (NSSize)size // this method is
{ // untested FIX ME
NSFont *font;
unsigned int length;
NSRange effectiveRange;
NSString *subString;
float pointSize;
float sumOfCharacterRange = 0;
static NSCharacterSet *nlset = nil;
length = [self length];
effectiveRange = NSMakeRange(0, 0);
/* FIXME completely ignores paragraph style attachments and other layout info */
- (void) drawAtPoint: (NSPoint)point
{
NSGraphicsContext *ctxt = [NSGraphicsContext currentContext];
NSString *allText = [self string];
unsigned length = [allText length];
unsigned linePos = 0;
NSFont *defFont = [NSFont userFontOfSize: 12];
NSParagraphStyle *defStyle = [NSParagraphStyle defaultParagraphStyle];
NSColor *defFgCol = [NSColor textColor];
NSColor *defBgCol = nil;
BOOL isFlipped = [[ctxt focusView] isFlipped];
NSPoint start = point;
while (NSMaxRange(effectiveRange) < length)
{
font = (NSFont*)[self attribute:NSFontAttributeName
atIndex:NSMaxRange(effectiveRange)
effectiveRange:&effectiveRange];
subString = [self attributedSubstringFromRange:effectiveRange];
sumOfCharacterRange += [font widthOfString:subString];
pointSize = MAX([font pointSize], pointSize);
}
return NSMakeSize(sumOfCharacterRange, pointSize);
/*
* Build a character set containing only newline characters if necessary.
*/
if (nlset == nil)
{
NSCharacterSet *not_ws;
NSMutableCharacterSet *new_set;
not_ws = [[NSCharacterSet whitespaceCharacterSet] invertedSet];
new_set = [[NSCharacterSet whitespaceAndNewlineCharacterSet] mutableCopy];
[new_set formIntersectionWithCharacterSet: not_ws];
nlset = [new_set copy];
[new_set release];
}
/*
* Now produce output on a per-line basis.
*/
while (linePos < length)
{
NSRange line; // Range of current line.
NSRange eol; // Rnage of newline character.
float lineHeight; // Height of text in this line.
unsigned position; // Position in NSString.
/*
* Determine the range of the next line of text (in 'line') and set
* 'linePos' to point after the terminating newline character (if any).
*/
line = NSMakeRange(linePos, length - linePos);
eol = [allText rangeOfCharacterFromSet: nlset
options: NSLiteralSearch
range: line];
if (eol.length == 0)
eol.location = length;
else
line.length = eol.location - line.location;
linePos = NSMaxRange(eol);
position = line.location;
while (position < eol.location)
{
NSAttributedString *subAttr;
NSString *subString;
NSSize size;
NSFont *font;
NSParagraphStyle *style;
NSColor *bg;
NSColor *fg;
int underline;
int superscript;
float base;
float kern;
float ypos;
int ligature;
NSNumber *num;
NSRange maxRange;
NSRange range;
// Maximum range is up to end of line.
maxRange = NSMakeRange(position, eol.location - position);
// Get font and range over which it applies.
font = (NSFont*)[self attribute: NSFontAttributeName
atIndex: position
effectiveRange: &range];
if (font == nil)
font = defFont;
maxRange = NSIntersectionRange(maxRange, range);
// Get style and range over which it applies.
style = (NSParagraphStyle*)[self attribute: NSParagraphStyleAttributeName
atIndex: position
effectiveRange: &range];
if (style == nil)
style = defStyle;
maxRange = NSIntersectionRange(maxRange, range);
// Get background color and range over which it applies.
bg = (NSColor*)[self attribute: NSBackgroundColorAttributeName
atIndex: position
effectiveRange: &range];
if (bg == nil)
bg = defBgCol;
maxRange = NSIntersectionRange(maxRange, range);
// Get foreground color and range over which it applies.
fg = (NSColor*)[self attribute: NSForegroundColorAttributeName
atIndex: position
effectiveRange: &range];
if (fg == nil)
fg = defFgCol;
maxRange = NSIntersectionRange(maxRange, range);
// Get underline style and range over which it applies.
num = (NSNumber*)[self attribute: NSUnderlineStyleAttributeName
atIndex: position
effectiveRange: &range];
if (num == nil)
underline = GSNoUnderlineStyle; // No underline
else
underline = [num intValue];
maxRange = NSIntersectionRange(maxRange, range);
// Get superscript and range over which it applies.
num = (NSNumber*)[self attribute: NSSuperscriptAttributeName
atIndex: position
effectiveRange: &range];
if (num == nil)
superscript = 0;
else
superscript = [num intValue];
maxRange = NSIntersectionRange(maxRange, range);
// Get baseline offset and range over which it applies.
num = (NSNumber*)[self attribute: NSBaselineOffsetAttributeName
atIndex: position
effectiveRange: &range];
if (num == nil)
base = 0.0;
else
base = [num floatValue];
maxRange = NSIntersectionRange(maxRange, range);
// Get kern attribute and range over which it applies.
num = (NSNumber*)[self attribute: NSKernAttributeName
atIndex: position
effectiveRange: &range];
if (num == nil)
kern = 0.0;
else
kern = [num floatValue];
maxRange = NSIntersectionRange(maxRange, range);
// Get ligature attribute and range over which it applies.
num = (NSNumber*)[self attribute: NSLigatureAttributeName
atIndex: position
effectiveRange: &range];
if (num == nil)
ligature = 1;
else
ligature = [num intValue];
maxRange = NSIntersectionRange(maxRange, range);
/*
* Now, at last we have all the required text drawing attributes and
* we have a range over which ALL of them apply. We update our
* position to point past this range, then we grab the substring from
* the range, draw it, and update our drawing position.
*/
range = maxRange;
position = NSMaxRange(range); // Next position in string.
subAttr = [self attributedSubstringFromRange: range];
subString = [subAttr string];
size.width = [font widthOfString: subString];
size.height = [font pointSize];
lineHeight = size.height;
/*
* If we have a background color set - we fill in the
* region occupied by this substring.
*/
if (bg)
{
NSRect rect;
rect.origin = point;
rect.size = size;
if (isFlipped == NO)
rect.origin.y -= size.height;
[bg set];
NSRectFill(rect);
}
/*
* Set font and color, then draw the substring.
* NB. Our origin is top-left of the string so we need to
* calculate a vertical coordinate for the baseline of the
* text produced by psshow.
*/
if (isFlipped)
ypos = point.y + size.height - [font descender];
else
ypos = point.y - size.height + [font descender];
[fg set];
[font set];
DPSmoveto(ctxt, point.x, ypos);
DPSshow(ctxt, [subString cString]);
if (underline == NSSingleUnderlineStyle)
{
DPSmoveto(ctxt, point.x, ypos);
DPSlineto(ctxt, point.x + size.width - 1, ypos);
}
point.x += size.width; // Next point to draw from.
}
point.x = start.x;
if (isFlipped)
point.y += lineHeight;
else
point.y -= lineHeight;
}
}
- (void) drawInRect: (NSRect)rect
{
NSPoint point;
NSView *view = [NSView focusView];
NSRectClip(rect);
/*
* Since [-drawAtPoint:] positions the top-left corner of the text at
* the point, we locate the top-left corner of the rectangle to do the
* drawing.
*/
point.x = rect.origin.x;
if ([view isFlipped])
point.y = rect.origin.y;
else
point.y = rect.origin.y + rect.size.height;
[self drawAtPoint: point];
NSRectClip([view bounds]);
}
/* FIXME completely ignores paragraph style attachments and other layout info */
- (NSSize) size
{
unsigned length = [self length];
unsigned position = 0;
float height = 0;
float width = 0;
while (position < length)
{
NSRange range;
NSFont *font;
NSString *subString;
font = (NSFont*)[self attribute: NSFontAttributeName
atIndex: position
effectiveRange: &range];
subString = [[self attributedSubstringFromRange: range] string];
width += [font widthOfString: subString];
height = MAX([font pointSize], height);
position = NSMaxRange(range);
}
return NSMakeSize(width, height);
}
@end