1999-04-01 06:19:37 +00:00
|
|
|
|
/*
|
1998-08-08 16:09:35 +00:00
|
|
|
|
NSStringDrawing.m
|
|
|
|
|
|
1999-04-01 20:21:05 +00:00
|
|
|
|
Categories which add drawing capabilities to NSAttributedString
|
1998-11-30 09:40:01 +00:00
|
|
|
|
and NSString.
|
1998-08-08 16:09:35 +00:00
|
|
|
|
|
1999-04-01 20:21:05 +00:00
|
|
|
|
Copyright (C) 1999 Free Software Foundation, Inc.
|
1998-08-08 16:09:35 +00:00
|
|
|
|
|
1999-04-01 06:19:37 +00:00
|
|
|
|
Author: Richard Frith-Macdonald <richard@brainstorm.co.uk>
|
|
|
|
|
Date: Mar 1999 - rewrite from scratch
|
|
|
|
|
|
1998-08-08 16:09:35 +00:00
|
|
|
|
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.
|
1999-04-01 06:19:37 +00:00
|
|
|
|
|
1998-08-08 16:09:35 +00:00
|
|
|
|
This library is distributed in the hope that it will be useful,
|
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
|
|
|
|
|
Library General Public License for more details.
|
|
|
|
|
|
|
|
|
|
You should have received a copy of the GNU Library General Public
|
|
|
|
|
License along with this library; see the file COPYING.LIB.
|
|
|
|
|
If not, write to the Free Software Foundation,
|
|
|
|
|
59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
1999-04-01 06:19:37 +00:00
|
|
|
|
*/
|
1998-08-08 16:09:35 +00:00
|
|
|
|
|
1999-04-01 06:19:37 +00:00
|
|
|
|
#include <Foundation/Foundation.h>
|
1998-08-08 16:09:35 +00:00
|
|
|
|
#include <AppKit/NSStringDrawing.h>
|
|
|
|
|
#include <AppKit/AppKit.h>
|
|
|
|
|
|
1999-04-01 06:19:37 +00:00
|
|
|
|
|
1999-04-02 08:04:05 +00:00
|
|
|
|
static NSCharacterSet *whitespace;
|
|
|
|
|
static NSCharacterSet *newlines;
|
|
|
|
|
static NSCharacterSet *separators;
|
1999-04-01 20:21:05 +00:00
|
|
|
|
static NSFont *defFont;
|
|
|
|
|
static NSParagraphStyle *defStyle;
|
|
|
|
|
static NSColor *defFgCol;
|
|
|
|
|
static NSColor *defBgCol;
|
1999-04-02 08:04:05 +00:00
|
|
|
|
static SEL advSel = @selector(advancementForGlyph:);
|
|
|
|
|
|
|
|
|
|
static BOOL (*isSepImp)(NSCharacterSet*, SEL, unichar);
|
1999-04-01 06:19:37 +00:00
|
|
|
|
|
1999-04-01 20:21:05 +00:00
|
|
|
|
/*
|
|
|
|
|
* Thne 'checkInit()' function is called to ensure that any static
|
|
|
|
|
* variables required by the string drawing code are initialised.
|
|
|
|
|
*/
|
|
|
|
|
static void
|
|
|
|
|
checkInit()
|
1999-04-01 06:19:37 +00:00
|
|
|
|
{
|
1999-04-01 20:21:05 +00:00
|
|
|
|
static BOOL beenHere = NO;
|
1999-04-01 06:19:37 +00:00
|
|
|
|
|
1999-04-01 20:21:05 +00:00
|
|
|
|
if (beenHere == NO)
|
1999-04-01 06:19:37 +00:00
|
|
|
|
{
|
1999-04-02 08:04:05 +00:00
|
|
|
|
NSMutableCharacterSet *ms;
|
|
|
|
|
|
|
|
|
|
whitespace = [[NSCharacterSet whitespaceCharacterSet] retain];
|
1999-04-01 06:19:37 +00:00
|
|
|
|
|
1999-04-01 20:21:05 +00:00
|
|
|
|
/*
|
|
|
|
|
* Build a character set containing only newline characters.
|
|
|
|
|
*/
|
1999-04-02 08:04:05 +00:00
|
|
|
|
ms = [[NSCharacterSet whitespaceAndNewlineCharacterSet] mutableCopy];
|
|
|
|
|
[ms formIntersectionWithCharacterSet: [whitespace invertedSet]];
|
|
|
|
|
newlines = [ms copy];
|
|
|
|
|
[ms release];
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Build a character set containing only word separators.
|
|
|
|
|
*/
|
|
|
|
|
#if 0 /* FIXME */
|
|
|
|
|
ms = [[NSCharacterSet punctuationCharacterSet] mutableCopy];
|
|
|
|
|
[ms formUnionWithCharacterSet: whitespace];
|
|
|
|
|
separators = [ms copy];
|
|
|
|
|
[ms release];
|
|
|
|
|
#else
|
|
|
|
|
separators = whitespace;
|
|
|
|
|
#endif
|
|
|
|
|
|
|
|
|
|
isSepImp = (BOOL (*)(NSCharacterSet*, SEL, unichar))
|
|
|
|
|
[separators methodForSelector: @selector(characterIsMember:)];
|
1999-04-01 20:21:05 +00:00
|
|
|
|
|
|
|
|
|
defStyle = [NSParagraphStyle defaultParagraphStyle];
|
|
|
|
|
[defStyle retain];
|
|
|
|
|
defBgCol = nil;
|
|
|
|
|
beenHere = YES;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* These defaults could change during the running of the program if the
|
|
|
|
|
* user defaults are changed.
|
|
|
|
|
*/
|
|
|
|
|
defFont = [NSFont userFontOfSize: 12];
|
|
|
|
|
defFgCol = [NSColor textColor];
|
|
|
|
|
}
|
|
|
|
|
|
1999-04-02 08:04:05 +00:00
|
|
|
|
static inline BOOL
|
|
|
|
|
isSeparator(unichar c)
|
|
|
|
|
{
|
|
|
|
|
return (*isSepImp)(separators, @selector(characterIsMember:), c);
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#define ADVANCEMENT(X) (*advImp)(font, advSel, (X))
|
|
|
|
|
|
1999-04-01 20:21:05 +00:00
|
|
|
|
/*
|
|
|
|
|
* The 'sizeLine()' function is called to determine the size of a single
|
|
|
|
|
* line of text (specified by the 'range' argument) that may be part of
|
|
|
|
|
* a larger attributed string.
|
|
|
|
|
* If will also return the position of the baseline of the text within
|
|
|
|
|
* the bounding rectangle as an offset from the bottom of the rectangle.
|
1999-04-02 08:04:05 +00:00
|
|
|
|
* The 'line' range is shortened to indicate any line wrapping.
|
1999-04-01 20:21:05 +00:00
|
|
|
|
*/
|
|
|
|
|
static NSSize
|
1999-04-02 08:04:05 +00:00
|
|
|
|
sizeLine(NSAttributedString *str,
|
|
|
|
|
NSParagraphStyle *style,
|
|
|
|
|
NSRange *para,
|
|
|
|
|
BOOL first,
|
|
|
|
|
float *baseptr)
|
1999-04-01 20:21:05 +00:00
|
|
|
|
{
|
1999-04-02 08:04:05 +00:00
|
|
|
|
unsigned pos = para->location;
|
|
|
|
|
unsigned end = pos + para->length;
|
|
|
|
|
unsigned lastSepIndex;
|
|
|
|
|
float lastSepOffset;
|
|
|
|
|
NSLineBreakMode lbm;
|
|
|
|
|
NSArray *tabStops = [style tabStops];
|
|
|
|
|
unsigned numTabs = [tabStops count];
|
|
|
|
|
unsigned nextTab = 0;
|
1999-04-01 20:21:05 +00:00
|
|
|
|
NSSize size = NSMakeSize(0, 0);
|
|
|
|
|
float baseline = 0;
|
1999-04-02 08:04:05 +00:00
|
|
|
|
float maxx;
|
|
|
|
|
NSFont *oldFont = nil;
|
|
|
|
|
NSSize (*advImp)(NSFont*, SEL, NSGlyph);
|
|
|
|
|
|
|
|
|
|
if (pos >= end)
|
|
|
|
|
return size;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Perform initial horizontal positioning
|
|
|
|
|
*/
|
|
|
|
|
if (first)
|
|
|
|
|
size.width = [style firstLineHeadIndent];
|
|
|
|
|
else
|
|
|
|
|
size.width = [style headIndent];
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Initialise possible linebreak points.
|
|
|
|
|
*/
|
|
|
|
|
lbm = [style lineBreakMode];
|
|
|
|
|
lastSepIndex = 0;
|
|
|
|
|
lastSepOffset = size.width;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Determine the end of a line - use a very large value if the style does
|
|
|
|
|
* not give us an eol relative to our starting point.
|
|
|
|
|
*/
|
|
|
|
|
maxx = [style tailIndent];
|
|
|
|
|
if (maxx <= 0.0)
|
|
|
|
|
maxx = 1.0E8;
|
1999-04-01 20:21:05 +00:00
|
|
|
|
|
|
|
|
|
while (pos < end)
|
|
|
|
|
{
|
|
|
|
|
NSFont *font;
|
|
|
|
|
int superscript;
|
|
|
|
|
int ligature;
|
|
|
|
|
float base;
|
|
|
|
|
float kern;
|
|
|
|
|
NSNumber *num;
|
|
|
|
|
NSRange maxRange;
|
|
|
|
|
NSRange range;
|
|
|
|
|
float below;
|
|
|
|
|
float above;
|
|
|
|
|
|
|
|
|
|
// Maximum range is up to end of line.
|
|
|
|
|
maxRange = NSMakeRange(pos, end - pos);
|
|
|
|
|
|
|
|
|
|
// Get font and range over which it applies.
|
|
|
|
|
font = (NSFont*)[str attribute: NSFontAttributeName
|
|
|
|
|
atIndex: pos
|
|
|
|
|
effectiveRange: &range];
|
|
|
|
|
if (font == nil)
|
|
|
|
|
font = defFont;
|
1999-04-02 08:04:05 +00:00
|
|
|
|
if (font != oldFont)
|
1999-04-01 20:21:05 +00:00
|
|
|
|
{
|
1999-04-02 08:04:05 +00:00
|
|
|
|
oldFont = font;
|
|
|
|
|
advImp = (NSSize (*)(NSFont*, SEL, NSGlyph))
|
|
|
|
|
[font methodForSelector: advSel];
|
1999-04-01 20:21:05 +00:00
|
|
|
|
}
|
1999-04-02 08:04:05 +00:00
|
|
|
|
maxRange = NSIntersectionRange(maxRange, range);
|
1999-04-01 20:21:05 +00:00
|
|
|
|
|
|
|
|
|
// Get baseline offset and range over which it applies.
|
|
|
|
|
num = (NSNumber*)[str attribute: NSBaselineOffsetAttributeName
|
|
|
|
|
atIndex: pos
|
|
|
|
|
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*)[str attribute: NSKernAttributeName
|
|
|
|
|
atIndex: pos
|
|
|
|
|
effectiveRange: &range];
|
|
|
|
|
if (num == nil)
|
|
|
|
|
kern = 0.0;
|
|
|
|
|
else
|
|
|
|
|
kern = [num floatValue];
|
|
|
|
|
maxRange = NSIntersectionRange(maxRange, range);
|
|
|
|
|
|
|
|
|
|
// Get superscript and range over which it applies.
|
|
|
|
|
num = (NSNumber*)[str attribute: NSSuperscriptAttributeName
|
|
|
|
|
atIndex: pos
|
|
|
|
|
effectiveRange: &range];
|
|
|
|
|
if (num == nil)
|
|
|
|
|
superscript = 0;
|
|
|
|
|
else
|
|
|
|
|
superscript = [num intValue];
|
|
|
|
|
maxRange = NSIntersectionRange(maxRange, range);
|
|
|
|
|
|
|
|
|
|
// Get ligature attribute and range over which it applies.
|
|
|
|
|
num = (NSNumber*)[str attribute: NSLigatureAttributeName
|
|
|
|
|
atIndex: pos
|
|
|
|
|
effectiveRange: &range];
|
|
|
|
|
if (num == nil)
|
|
|
|
|
ligature = 1;
|
|
|
|
|
else
|
|
|
|
|
ligature = [num intValue];
|
|
|
|
|
maxRange = NSIntersectionRange(maxRange, range);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* See if the height of the bounding rectangle needs to grow to fit
|
|
|
|
|
* the font for this text.
|
|
|
|
|
*/
|
|
|
|
|
// FIXME - superscript should have some effect on height.
|
|
|
|
|
|
|
|
|
|
below = [font descender];
|
|
|
|
|
above = [font pointSize] - below;
|
|
|
|
|
if (base > 0)
|
|
|
|
|
above += base; // Character is above baseline.
|
|
|
|
|
else if (base < 0)
|
|
|
|
|
below -= base; // Character is below baseline.
|
|
|
|
|
if (below > baseline)
|
|
|
|
|
baseline = below;
|
|
|
|
|
if (size.height < baseline + above)
|
|
|
|
|
size.height = baseline + above;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Now we add the widths of the characters.
|
|
|
|
|
*/
|
|
|
|
|
// FIXME - ligature should have some effect on width.
|
|
|
|
|
range = maxRange;
|
|
|
|
|
if (range.length > 0)
|
|
|
|
|
{
|
|
|
|
|
unichar chars[range.length];
|
1999-04-02 08:04:05 +00:00
|
|
|
|
unsigned i = 0;
|
|
|
|
|
float width = 0;
|
1999-04-01 20:21:05 +00:00
|
|
|
|
|
|
|
|
|
[[str string] getCharacters: chars range: range];
|
1999-04-02 08:04:05 +00:00
|
|
|
|
while (i < range.length && width < maxx)
|
1999-04-01 20:21:05 +00:00
|
|
|
|
{
|
1999-04-02 08:04:05 +00:00
|
|
|
|
unsigned tabIndex = i;
|
|
|
|
|
|
|
|
|
|
while (tabIndex < range.length)
|
|
|
|
|
{
|
|
|
|
|
if (chars[tabIndex] == '\t')
|
|
|
|
|
break;
|
|
|
|
|
tabIndex++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (tabIndex == i)
|
1999-04-01 20:21:05 +00:00
|
|
|
|
{
|
|
|
|
|
NSTextTab *tab;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Either advance to next tabstop or by a space if
|
|
|
|
|
* there are no more tabstops.
|
|
|
|
|
*/
|
|
|
|
|
while (nextTab < numTabs)
|
|
|
|
|
{
|
|
|
|
|
tab = [tabStops objectAtIndex: nextTab];
|
|
|
|
|
if ([tab location] > size.width)
|
|
|
|
|
break;
|
|
|
|
|
nextTab++;
|
|
|
|
|
}
|
|
|
|
|
if (nextTab < numTabs)
|
1999-04-02 08:04:05 +00:00
|
|
|
|
width = [tab location];
|
1999-04-01 20:21:05 +00:00
|
|
|
|
else
|
1999-04-02 08:04:05 +00:00
|
|
|
|
{
|
|
|
|
|
NSSize adv;
|
|
|
|
|
|
|
|
|
|
adv = ADVANCEMENT(' ');
|
|
|
|
|
width = size.width + adv.width;
|
|
|
|
|
}
|
|
|
|
|
if (width > maxx)
|
|
|
|
|
break;
|
|
|
|
|
/*
|
|
|
|
|
* In case we need to word-wrap, we must record this
|
|
|
|
|
* as a possible linebreak position.
|
|
|
|
|
*/
|
|
|
|
|
if (lbm == NSLineBreakByWordWrapping)
|
|
|
|
|
{
|
|
|
|
|
lastSepIndex = pos + i;
|
|
|
|
|
lastSepOffset = size.width;
|
|
|
|
|
}
|
|
|
|
|
size.width = width;
|
|
|
|
|
i++; // Point to next char.
|
1999-04-01 20:21:05 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
1999-04-02 08:04:05 +00:00
|
|
|
|
while (i < tabIndex)
|
|
|
|
|
{
|
|
|
|
|
NSSize adv;
|
|
|
|
|
|
|
|
|
|
adv = ADVANCEMENT(chars[i]);
|
|
|
|
|
width = size.width + adv.width + kern;
|
|
|
|
|
if (width > maxx)
|
|
|
|
|
break;
|
|
|
|
|
if (lbm == NSLineBreakByWordWrapping
|
|
|
|
|
&& isSeparator(chars[i]))
|
|
|
|
|
{
|
|
|
|
|
lastSepIndex = pos + i;
|
|
|
|
|
lastSepOffset = size.width;
|
|
|
|
|
}
|
|
|
|
|
size.width = width;
|
|
|
|
|
i++;
|
|
|
|
|
}
|
1999-04-01 20:21:05 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
1999-04-02 08:04:05 +00:00
|
|
|
|
|
|
|
|
|
if (width > maxx)
|
|
|
|
|
{
|
|
|
|
|
if (lbm == NSLineBreakByWordWrapping)
|
|
|
|
|
{
|
|
|
|
|
unichar c;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Word wrap - if the words are separated by whitespace
|
|
|
|
|
* we discard the whitespace character - is this right?.
|
|
|
|
|
*/
|
|
|
|
|
pos = lastSepIndex;
|
|
|
|
|
size.width = lastSepOffset;
|
|
|
|
|
c = [[str string] characterAtIndex: pos];
|
|
|
|
|
if ([whitespace characterIsMember: c])
|
|
|
|
|
pos++;
|
|
|
|
|
}
|
|
|
|
|
else if (lbm == NSLineBreakByCharWrapping)
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* Simply do a linebreak at the current character position.
|
|
|
|
|
*/
|
|
|
|
|
pos += i;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
/*
|
|
|
|
|
* Truncate line.
|
|
|
|
|
*/
|
|
|
|
|
size.width = maxx;
|
|
|
|
|
pos = end;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
break;
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
pos = NSMaxRange(range); // Next position in string.
|
|
|
|
|
}
|
1999-04-01 20:21:05 +00:00
|
|
|
|
}
|
1999-04-01 06:19:37 +00:00
|
|
|
|
}
|
1999-04-02 08:04:05 +00:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Adjust the range 'para' to specify the characters in this line.
|
|
|
|
|
*/
|
|
|
|
|
para->length = (pos - para->location);
|
|
|
|
|
|
1999-04-01 20:21:05 +00:00
|
|
|
|
if (baseptr)
|
|
|
|
|
*baseptr = baseline;
|
|
|
|
|
return size;
|
|
|
|
|
}
|
|
|
|
|
|
1999-04-02 08:04:05 +00:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* I know it's severely sub-optimal, but the NSString methods just
|
|
|
|
|
* use NSAttributes string to do the job.
|
|
|
|
|
*/
|
|
|
|
|
@implementation NSString (NSStringDrawing)
|
|
|
|
|
|
|
|
|
|
- (void) drawAtPoint: (NSPoint)point withAttributes: (NSDictionary *)attrs
|
|
|
|
|
{
|
|
|
|
|
NSAttributedString *a;
|
|
|
|
|
|
|
|
|
|
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)
|
|
|
|
|
|
1999-04-01 20:21:05 +00:00
|
|
|
|
- (void) drawAtPoint: (NSPoint)point
|
|
|
|
|
{
|
|
|
|
|
NSGraphicsContext *ctxt = [NSGraphicsContext currentContext];
|
|
|
|
|
NSString *allText = [self string];
|
|
|
|
|
unsigned length = [allText length];
|
1999-04-02 08:04:05 +00:00
|
|
|
|
unsigned paraPos = 0;
|
1999-04-01 20:21:05 +00:00
|
|
|
|
BOOL isFlipped = [[ctxt focusView] isFlipped];
|
|
|
|
|
NSParagraphStyle *style = nil;
|
1999-04-02 08:04:05 +00:00
|
|
|
|
BOOL firstLineOfFirstPara = YES;
|
|
|
|
|
NSFont *oldFont = nil;
|
|
|
|
|
NSSize (*advImp)(NSFont*, SEL, NSGlyph);
|
1999-04-01 20:21:05 +00:00
|
|
|
|
|
|
|
|
|
checkInit();
|
1999-04-01 06:19:37 +00:00
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Now produce output on a per-line basis.
|
|
|
|
|
*/
|
1999-04-02 08:04:05 +00:00
|
|
|
|
while (paraPos < length)
|
1999-04-01 06:19:37 +00:00
|
|
|
|
{
|
1999-04-02 08:04:05 +00:00
|
|
|
|
NSRange para; // Range of current paragraph.
|
1999-04-01 06:19:37 +00:00
|
|
|
|
NSRange line; // Range of current line.
|
1999-04-02 08:04:05 +00:00
|
|
|
|
NSRange eol; // Range of newline character.
|
1999-04-01 06:19:37 +00:00
|
|
|
|
unsigned position; // Position in NSString.
|
1999-04-01 20:21:05 +00:00
|
|
|
|
NSSize lineSize;
|
|
|
|
|
float baseline;
|
|
|
|
|
float xpos = 0;
|
1999-04-02 08:04:05 +00:00
|
|
|
|
NSColor *bg = nil;
|
|
|
|
|
float leading;
|
|
|
|
|
BOOL firstLine = YES;
|
1999-04-01 06:19:37 +00:00
|
|
|
|
|
|
|
|
|
/*
|
1999-04-02 08:04:05 +00:00
|
|
|
|
* Determine the range of the next paragraph of text (in 'para') and set
|
|
|
|
|
* 'paraPos' to point after the terminating newline character (if any).
|
1999-04-01 06:19:37 +00:00
|
|
|
|
*/
|
1999-04-02 08:04:05 +00:00
|
|
|
|
para = NSMakeRange(paraPos, length - paraPos);
|
|
|
|
|
eol = [allText rangeOfCharacterFromSet: newlines
|
1999-04-01 06:19:37 +00:00
|
|
|
|
options: NSLiteralSearch
|
1999-04-02 08:04:05 +00:00
|
|
|
|
range: para];
|
1999-04-01 06:19:37 +00:00
|
|
|
|
|
|
|
|
|
if (eol.length == 0)
|
|
|
|
|
eol.location = length;
|
|
|
|
|
else
|
1999-04-02 08:04:05 +00:00
|
|
|
|
para.length = eol.location - para.location;
|
|
|
|
|
paraPos = NSMaxRange(eol);
|
|
|
|
|
position = para.location;
|
1999-04-01 06:19:37 +00:00
|
|
|
|
|
1999-04-02 08:04:05 +00:00
|
|
|
|
do
|
1999-04-01 20:21:05 +00:00
|
|
|
|
{
|
1999-04-02 08:04:05 +00:00
|
|
|
|
if (firstLine == YES)
|
1999-04-01 20:21:05 +00:00
|
|
|
|
{
|
1999-04-02 08:04:05 +00:00
|
|
|
|
leading = [style paragraphSpacing];
|
|
|
|
|
/*
|
|
|
|
|
* Check to see if the new line begins with the same paragraph
|
|
|
|
|
* styl that the old ended in. This information is used to handle
|
|
|
|
|
* what happens between lines and whether the new line is also a
|
|
|
|
|
* new paragraph.
|
|
|
|
|
*/
|
|
|
|
|
style = (NSParagraphStyle*)[self
|
|
|
|
|
attribute: NSParagraphStyleAttributeName
|
|
|
|
|
atIndex: position
|
|
|
|
|
effectiveRange: 0];
|
1999-04-01 20:21:05 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
1999-04-02 08:04:05 +00:00
|
|
|
|
leading = [style lineSpacing];
|
1999-04-01 20:21:05 +00:00
|
|
|
|
}
|
1999-04-02 08:04:05 +00:00
|
|
|
|
|
|
|
|
|
if (firstLineOfFirstPara == YES)
|
1999-04-01 20:21:05 +00:00
|
|
|
|
{
|
1999-04-02 08:04:05 +00:00
|
|
|
|
firstLineOfFirstPara = NO;
|
1999-04-01 20:21:05 +00:00
|
|
|
|
}
|
1999-04-01 06:19:37 +00:00
|
|
|
|
else
|
1999-04-01 20:21:05 +00:00
|
|
|
|
{
|
1999-04-02 08:04:05 +00:00
|
|
|
|
if (isFlipped)
|
|
|
|
|
point.y -= leading;
|
1999-04-01 20:21:05 +00:00
|
|
|
|
else
|
1999-04-02 08:04:05 +00:00
|
|
|
|
point.y += leading;
|
1999-04-01 20:21:05 +00:00
|
|
|
|
|
1999-04-02 08:04:05 +00:00
|
|
|
|
/*
|
|
|
|
|
* Fill the inter-line/interparagraph space with the background
|
|
|
|
|
* color in use at the end of the last line.
|
|
|
|
|
*/
|
|
|
|
|
bg = (NSColor*)[self attribute: NSBackgroundColorAttributeName
|
|
|
|
|
atIndex: position - 1
|
|
|
|
|
effectiveRange: 0];
|
|
|
|
|
if (bg == nil)
|
|
|
|
|
bg = defBgCol;
|
|
|
|
|
|
|
|
|
|
if (bg != nil)
|
1999-04-01 20:21:05 +00:00
|
|
|
|
{
|
1999-04-02 08:04:05 +00:00
|
|
|
|
NSRect fillrect;
|
|
|
|
|
|
|
|
|
|
fillrect.origin = point;
|
|
|
|
|
fillrect.size.width = lineSize.width;
|
|
|
|
|
fillrect.size.height = leading;
|
1999-04-01 20:21:05 +00:00
|
|
|
|
[bg set];
|
|
|
|
|
if (isFlipped == NO)
|
|
|
|
|
fillrect.origin.y -= fillrect.size.height;
|
|
|
|
|
NSRectFill(fillrect);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
1999-04-01 06:19:37 +00:00
|
|
|
|
/*
|
1999-04-02 08:04:05 +00:00
|
|
|
|
* Calculate sizing information for the entire line.
|
1999-04-01 06:19:37 +00:00
|
|
|
|
*/
|
1999-04-02 08:04:05 +00:00
|
|
|
|
line = para;
|
|
|
|
|
lineSize = sizeLine(self, style, &line, firstLine, &baseline);
|
|
|
|
|
firstLine = NO;
|
|
|
|
|
|
|
|
|
|
while (position < NSMaxRange(line))
|
1999-04-01 06:19:37 +00:00
|
|
|
|
{
|
1999-04-02 08:04:05 +00:00
|
|
|
|
NSAttributedString *subAttr;
|
|
|
|
|
NSString *subString;
|
|
|
|
|
NSSize size;
|
|
|
|
|
NSFont *font;
|
|
|
|
|
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;
|
|
|
|
|
if (font != oldFont)
|
|
|
|
|
{
|
|
|
|
|
oldFont = font;
|
|
|
|
|
advImp = (NSSize (*)(NSFont*, SEL, NSGlyph))
|
|
|
|
|
[font methodForSelector: advSel];
|
|
|
|
|
}
|
|
|
|
|
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);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* If this is a new line - adjust for indentation.
|
|
|
|
|
*/
|
|
|
|
|
if (position == line.location)
|
|
|
|
|
{
|
|
|
|
|
NSRect fillrect;
|
1999-04-01 20:21:05 +00:00
|
|
|
|
|
1999-04-02 08:04:05 +00:00
|
|
|
|
fillrect.origin = point;
|
|
|
|
|
fillrect.size = lineSize;
|
|
|
|
|
|
|
|
|
|
if (firstLine)
|
|
|
|
|
xpos = [style firstLineHeadIndent];
|
|
|
|
|
else
|
|
|
|
|
xpos += [style headIndent];
|
|
|
|
|
|
|
|
|
|
fillrect.size.width = xpos;
|
|
|
|
|
if (bg != nil && fillrect.size.width > 0)
|
|
|
|
|
{
|
|
|
|
|
[bg set];
|
|
|
|
|
if (isFlipped == NO)
|
|
|
|
|
fillrect.origin.y -= fillrect.size.height;
|
|
|
|
|
NSRectFill(fillrect);
|
|
|
|
|
}
|
|
|
|
|
}
|
1999-04-01 20:21:05 +00:00
|
|
|
|
|
|
|
|
|
/*
|
1999-04-02 08:04:05 +00:00
|
|
|
|
* 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, draw it, and update our drawing position.
|
1999-04-01 20:21:05 +00:00
|
|
|
|
*/
|
1999-04-02 08:04:05 +00:00
|
|
|
|
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];
|
|
|
|
|
|
|
|
|
|
if (range.length > 0)
|
1999-04-01 20:21:05 +00:00
|
|
|
|
{
|
1999-04-02 08:04:05 +00:00
|
|
|
|
unichar chars[range.length];
|
|
|
|
|
NSArray *tabStops = [style tabStops];
|
|
|
|
|
unsigned numTabs = [tabStops count];
|
|
|
|
|
unsigned nextTab = 0;
|
|
|
|
|
float width = xpos;
|
|
|
|
|
unsigned i;
|
1999-04-01 20:21:05 +00:00
|
|
|
|
|
1999-04-02 08:04:05 +00:00
|
|
|
|
[[self string] getCharacters: chars range: range];
|
1999-04-01 20:21:05 +00:00
|
|
|
|
|
1999-04-02 08:04:05 +00:00
|
|
|
|
/*
|
|
|
|
|
* If we have a background color set - we fill in the
|
|
|
|
|
* region occupied by this substring.
|
|
|
|
|
*/
|
|
|
|
|
if (bg)
|
1999-04-01 20:21:05 +00:00
|
|
|
|
{
|
1999-04-02 08:04:05 +00:00
|
|
|
|
float oldx = xpos;
|
|
|
|
|
NSRect rect;
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
for (i = 0; i < range.length; i++)
|
|
|
|
|
{
|
|
|
|
|
if (chars[i] == '\t')
|
|
|
|
|
{
|
|
|
|
|
NSTextTab *tab;
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* Either advance to next tabstop or by
|
|
|
|
|
* a space if there are no more tabstops.
|
|
|
|
|
*/
|
|
|
|
|
while (nextTab < numTabs)
|
|
|
|
|
{
|
|
|
|
|
tab = [tabStops objectAtIndex: nextTab];
|
|
|
|
|
if ([tab location] > xpos)
|
|
|
|
|
break;
|
|
|
|
|
nextTab++;
|
|
|
|
|
}
|
|
|
|
|
if (nextTab < numTabs)
|
|
|
|
|
xpos = [tab location];
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
NSSize adv;
|
|
|
|
|
|
|
|
|
|
adv = ADVANCEMENT(' ');
|
|
|
|
|
xpos += adv.width;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
NSSize adv;
|
|
|
|
|
|
|
|
|
|
adv = ADVANCEMENT(chars[i]);
|
|
|
|
|
xpos += adv.width;
|
|
|
|
|
xpos += kern;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
rect.origin.x = point.x + oldx;
|
|
|
|
|
rect.origin.y = point.y;
|
|
|
|
|
rect.size.height = lineSize.height;
|
|
|
|
|
rect.size.width = xpos - oldx;
|
|
|
|
|
xpos = oldx;
|
|
|
|
|
if (isFlipped == NO)
|
|
|
|
|
rect.origin.y -= lineSize.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.
|
|
|
|
|
*/
|
|
|
|
|
[fg set];
|
|
|
|
|
[font set];
|
|
|
|
|
if (isFlipped)
|
|
|
|
|
ypos = point.y + lineSize.height - baseline - base;
|
|
|
|
|
else
|
|
|
|
|
ypos = point.y - lineSize.height + baseline + base;
|
|
|
|
|
|
|
|
|
|
i = 0;
|
|
|
|
|
while (i < range.length)
|
|
|
|
|
{
|
|
|
|
|
unsigned tabIndex = i;
|
|
|
|
|
|
|
|
|
|
while (tabIndex < range.length)
|
|
|
|
|
{
|
|
|
|
|
if (chars[tabIndex] == '\t')
|
|
|
|
|
break;
|
|
|
|
|
tabIndex++;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (tabIndex == i)
|
1999-04-01 20:21:05 +00:00
|
|
|
|
{
|
|
|
|
|
NSTextTab *tab;
|
|
|
|
|
|
|
|
|
|
/*
|
1999-04-02 08:04:05 +00:00
|
|
|
|
* Either advance to next tabstop or by a space if
|
|
|
|
|
* there are no more tabstops.
|
1999-04-01 20:21:05 +00:00
|
|
|
|
*/
|
|
|
|
|
while (nextTab < numTabs)
|
|
|
|
|
{
|
|
|
|
|
tab = [tabStops objectAtIndex: nextTab];
|
1999-04-02 08:04:05 +00:00
|
|
|
|
if ([tab location] > width)
|
1999-04-01 20:21:05 +00:00
|
|
|
|
break;
|
|
|
|
|
nextTab++;
|
|
|
|
|
}
|
|
|
|
|
if (nextTab < numTabs)
|
1999-04-02 08:04:05 +00:00
|
|
|
|
width = [tab location];
|
1999-04-01 20:21:05 +00:00
|
|
|
|
else
|
1999-04-02 08:04:05 +00:00
|
|
|
|
{
|
|
|
|
|
NSSize adv;
|
|
|
|
|
|
|
|
|
|
adv = ADVANCEMENT(' ');
|
|
|
|
|
width += adv.width;
|
|
|
|
|
}
|
|
|
|
|
i++; // Point to next char.
|
1999-04-01 20:21:05 +00:00
|
|
|
|
}
|
1999-04-02 08:04:05 +00:00
|
|
|
|
else if (kern == 0)
|
1999-04-01 20:21:05 +00:00
|
|
|
|
{
|
1999-04-02 08:04:05 +00:00
|
|
|
|
char buf[tabIndex - i + 1];
|
|
|
|
|
unsigned j;
|
|
|
|
|
|
|
|
|
|
for (j = i; j < tabIndex; j++)
|
|
|
|
|
buf[j-i] = chars[j];
|
|
|
|
|
buf[j-i] = '\0';
|
|
|
|
|
DPSmoveto(ctxt, point.x + xpos, ypos);
|
|
|
|
|
DPSshow(ctxt, buf);
|
|
|
|
|
while (i < tabIndex)
|
|
|
|
|
{
|
|
|
|
|
NSSize adv;
|
1999-04-01 20:21:05 +00:00
|
|
|
|
|
1999-04-02 08:04:05 +00:00
|
|
|
|
adv = ADVANCEMENT(chars[i]);
|
|
|
|
|
width += adv.width;
|
|
|
|
|
i++;
|
|
|
|
|
}
|
1999-04-01 20:21:05 +00:00
|
|
|
|
}
|
|
|
|
|
else
|
1999-04-02 08:04:05 +00:00
|
|
|
|
{
|
|
|
|
|
while (i < tabIndex)
|
|
|
|
|
{
|
|
|
|
|
NSSize adv;
|
|
|
|
|
char buf[2];
|
|
|
|
|
|
|
|
|
|
width += kern;
|
|
|
|
|
DPSmoveto(ctxt, point.x + width, ypos);
|
|
|
|
|
buf[0] = chars[i];
|
|
|
|
|
buf[1] = '\0';
|
|
|
|
|
DPSshow(ctxt, buf);
|
|
|
|
|
adv = ADVANCEMENT(chars[i]);
|
|
|
|
|
width += adv.width;
|
|
|
|
|
i++;
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
xpos = width;
|
1999-04-01 20:21:05 +00:00
|
|
|
|
}
|
|
|
|
|
|
1999-04-02 08:04:05 +00:00
|
|
|
|
if (underline == NSSingleUnderlineStyle)
|
|
|
|
|
{
|
1999-04-01 20:21:05 +00:00
|
|
|
|
DPSmoveto(ctxt, point.x + xpos, ypos);
|
1999-04-02 08:04:05 +00:00
|
|
|
|
DPSlineto(ctxt, point.x + width - 1, ypos);
|
1999-04-01 20:21:05 +00:00
|
|
|
|
}
|
1999-04-02 08:04:05 +00:00
|
|
|
|
xpos = width;
|
1999-04-01 20:21:05 +00:00
|
|
|
|
}
|
1999-04-01 06:19:37 +00:00
|
|
|
|
}
|
1999-04-02 08:04:05 +00:00
|
|
|
|
if (isFlipped)
|
|
|
|
|
point.y += lineSize.height;
|
|
|
|
|
else
|
|
|
|
|
point.y -= lineSize.height;
|
|
|
|
|
firstLine = NO;
|
|
|
|
|
para.length -= line.length;
|
|
|
|
|
para.location += line.length;
|
|
|
|
|
} while (para.location < eol.location);
|
1999-04-01 06:19:37 +00:00
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
- (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);
|
1998-08-08 16:09:35 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
@end
|
1999-04-01 06:19:37 +00:00
|
|
|
|
|