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,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