2001-12-17 16:51:51 +00:00
|
|
|
/** <title>NSStringAdditions</title>
|
1998-08-08 16:09:35 +00:00
|
|
|
|
2001-12-17 16:51:51 +00:00
|
|
|
<abstract>Categories which add drawing capabilities to NSAttributedString
|
|
|
|
and NSString.</abstract>
|
1998-08-08 16:09:35 +00:00
|
|
|
|
2003-03-24 18:15:56 +00:00
|
|
|
Copyright (C) 1999, 2003 Free Software Foundation, Inc.
|
1998-08-08 16:09:35 +00:00
|
|
|
|
2001-12-17 16:51:51 +00:00
|
|
|
Author: Richard Frith-Macdonald <richard@brainstorm.co.uk>
|
1999-04-01 06:19:37 +00:00
|
|
|
Date: Mar 1999 - rewrite from scratch
|
|
|
|
|
2003-03-24 18:15:56 +00:00
|
|
|
Author: Alexander Malmberg <alexander@malmberg.org>
|
|
|
|
Date: November 2002 - February 2003 (rewrite to use NSLayoutManager et al)
|
|
|
|
|
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
|
|
|
|
2003-03-24 18:15:56 +00:00
|
|
|
|
2003-06-13 15:01:12 +00:00
|
|
|
#include "AppKit/NSLayoutManager.h"
|
|
|
|
#include "AppKit/NSTextContainer.h"
|
|
|
|
#include "AppKit/NSTextStorage.h"
|
|
|
|
#include "AppKit/DPSOperators.h"
|
2001-05-18 21:00:52 +00:00
|
|
|
#include "GSTextStorage.h"
|
|
|
|
|
1999-04-19 14:34:50 +00:00
|
|
|
/*
|
2003-03-24 18:15:56 +00:00
|
|
|
TODO:
|
1999-04-02 08:04:05 +00:00
|
|
|
|
2003-03-24 18:15:56 +00:00
|
|
|
these methods are _not_ reentrant. should they be?
|
1999-04-01 06:19:37 +00:00
|
|
|
|
2003-03-24 18:15:56 +00:00
|
|
|
We could save time by trying to avoid changing the text storage and
|
|
|
|
container size if the new rect and text are the same as the previous.
|
|
|
|
This might be a common case if lots of calls to -size... and -draw...
|
|
|
|
are paired.
|
|
|
|
*/
|
1999-04-01 20:21:05 +00:00
|
|
|
|
2003-03-24 18:15:56 +00:00
|
|
|
static NSTextStorage *textStorage;
|
|
|
|
static NSLayoutManager *layoutManager;
|
|
|
|
static NSTextContainer *textContainer;
|
1999-04-01 20:21:05 +00:00
|
|
|
|
2003-03-24 18:15:56 +00:00
|
|
|
static void init_string_drawing(void)
|
|
|
|
{
|
|
|
|
if (textStorage)
|
|
|
|
return;
|
|
|
|
|
|
|
|
textStorage = [[NSTextStorage alloc] init];
|
|
|
|
layoutManager = [[NSLayoutManager alloc] init];
|
|
|
|
[textStorage addLayoutManager: layoutManager];
|
|
|
|
[layoutManager release];
|
|
|
|
textContainer = [[NSTextContainer alloc]
|
|
|
|
initWithContainerSize: NSMakeSize(10,10)];
|
|
|
|
[layoutManager addTextContainer: textContainer];
|
|
|
|
[textContainer release];
|
1999-04-01 20:21:05 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
/*
|
2003-03-24 18:15:56 +00:00
|
|
|
This is an ugly hack to get text to display correctly in non-flipped views.
|
1999-04-09 15:48:39 +00:00
|
|
|
|
2003-03-24 18:15:56 +00:00
|
|
|
The text system always has positive y down, so we flip the coordinate
|
|
|
|
system when drawing (if the view isn't flipped already). This causes the
|
|
|
|
glyphs to be drawn upside-down, so we need to tell NSFont to flip the fonts.
|
|
|
|
*/
|
|
|
|
@interface NSFont (font_flip_hack)
|
|
|
|
+(void) _setFontFlipHack: (BOOL)flip;
|
|
|
|
@end
|
2001-06-04 16:24:48 +00:00
|
|
|
|
2003-03-24 18:15:56 +00:00
|
|
|
@implementation NSAttributedString (NSStringDrawing)
|
1999-04-09 15:48:39 +00:00
|
|
|
|
2003-03-24 18:15:56 +00:00
|
|
|
- (void) drawAtPoint: (NSPoint)point
|
2001-06-04 16:24:48 +00:00
|
|
|
{
|
2003-03-24 18:15:56 +00:00
|
|
|
NSRange r;
|
|
|
|
NSGraphicsContext *ctxt = GSCurrentContext();
|
2001-06-04 16:24:48 +00:00
|
|
|
|
2003-03-24 18:15:56 +00:00
|
|
|
init_string_drawing();
|
2001-06-04 16:24:48 +00:00
|
|
|
|
2003-03-24 18:15:56 +00:00
|
|
|
[textStorage replaceCharactersInRange: NSMakeRange(0, [textStorage length])
|
|
|
|
withString: @""];
|
2001-06-04 16:24:48 +00:00
|
|
|
|
2003-03-24 18:15:56 +00:00
|
|
|
[textContainer setContainerSize: NSMakeSize(1e8, 1e8)];
|
2001-06-04 16:24:48 +00:00
|
|
|
|
2003-03-24 18:15:56 +00:00
|
|
|
[textStorage replaceCharactersInRange: NSMakeRange(0, 0)
|
|
|
|
withAttributedString: self];
|
1999-04-09 15:48:39 +00:00
|
|
|
|
2003-03-24 18:15:56 +00:00
|
|
|
r = NSMakeRange(0, [layoutManager numberOfGlyphs]);
|
1999-04-09 15:48:39 +00:00
|
|
|
|
2003-03-24 18:15:56 +00:00
|
|
|
if (![[NSView focusView] isFlipped])
|
1999-04-09 15:48:39 +00:00
|
|
|
{
|
2003-03-24 18:15:56 +00:00
|
|
|
DPSscale(ctxt, 1, -1);
|
|
|
|
point.y = -point.y;
|
|
|
|
[NSFont _setFontFlipHack: YES];
|
1999-04-09 15:48:39 +00:00
|
|
|
}
|
|
|
|
|
2003-03-24 18:15:56 +00:00
|
|
|
[layoutManager drawBackgroundForGlyphRange: r
|
|
|
|
atPoint: point];
|
2002-05-11 07:17:33 +00:00
|
|
|
|
2003-03-24 18:15:56 +00:00
|
|
|
[layoutManager drawGlyphsForGlyphRange: r
|
|
|
|
atPoint: point];
|
2000-10-02 17:49:43 +00:00
|
|
|
|
2003-03-24 18:15:56 +00:00
|
|
|
if (![[NSView focusView] isFlipped])
|
1999-04-09 15:48:39 +00:00
|
|
|
{
|
2003-03-24 18:15:56 +00:00
|
|
|
DPSscale(ctxt, 1, -1);
|
|
|
|
[NSFont _setFontFlipHack: NO];
|
1999-04-09 15:48:39 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2003-03-24 18:15:56 +00:00
|
|
|
- (void) drawInRect: (NSRect)rect
|
2001-06-04 16:24:48 +00:00
|
|
|
{
|
2003-03-24 18:15:56 +00:00
|
|
|
NSRange r;
|
|
|
|
BOOL need_clip;
|
|
|
|
NSRect used;
|
|
|
|
NSGraphicsContext *ctxt = GSCurrentContext();
|
2001-06-04 16:24:48 +00:00
|
|
|
|
2003-03-24 18:15:56 +00:00
|
|
|
init_string_drawing();
|
1999-04-09 15:48:39 +00:00
|
|
|
|
2003-03-24 18:15:56 +00:00
|
|
|
[textStorage replaceCharactersInRange: NSMakeRange(0, [textStorage length])
|
|
|
|
withString: @""];
|
1999-04-09 15:48:39 +00:00
|
|
|
|
|
|
|
/*
|
2003-03-24 18:15:56 +00:00
|
|
|
TODO: Use rect.size.heigth instead of 1e8? Should make things faster,
|
|
|
|
since we'll only typeset what fits, but lines that used to fit partially
|
|
|
|
won't fit at all.
|
|
|
|
*/
|
|
|
|
[textContainer setContainerSize: NSMakeSize(rect.size.width, 1e8)];
|
1999-04-09 15:48:39 +00:00
|
|
|
|
2003-03-24 18:15:56 +00:00
|
|
|
[textStorage replaceCharactersInRange: NSMakeRange(0, 0)
|
|
|
|
withAttributedString: self];
|
1999-04-09 15:48:39 +00:00
|
|
|
|
2003-03-24 18:15:56 +00:00
|
|
|
used = [layoutManager usedRectForTextContainer: textContainer];
|
1999-04-09 15:48:39 +00:00
|
|
|
|
|
|
|
/*
|
2003-03-24 18:15:56 +00:00
|
|
|
If the used rect fits completely in the rect we draw in, we save time
|
|
|
|
by avoiding the DPSrectclip (and the state save and restore).
|
|
|
|
|
|
|
|
This isn't completely safe; the used rect isn't guaranteed to contain
|
|
|
|
all parts of all glyphs.
|
|
|
|
*/
|
|
|
|
if (used.origin.x >= 0 && used.origin.y <= 0 &&
|
|
|
|
NSMaxX(used) <= rect.size.width && NSMaxY(used) <= rect.size.height)
|
2000-05-07 22:24:29 +00:00
|
|
|
{
|
2003-03-24 18:15:56 +00:00
|
|
|
need_clip = NO;
|
2000-05-07 22:24:29 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2003-03-24 18:15:56 +00:00
|
|
|
need_clip = YES;
|
|
|
|
DPSgsave(ctxt);
|
|
|
|
DPSrectclip(ctxt, rect.origin.x, rect.origin.y,
|
|
|
|
rect.size.width, rect.size.height);
|
1999-04-09 15:48:39 +00:00
|
|
|
}
|
|
|
|
|
2003-03-24 18:15:56 +00:00
|
|
|
r = [layoutManager
|
|
|
|
glyphRangeForBoundingRect: NSMakeRect(0, 0, rect.size.width, rect.size.height)
|
|
|
|
inTextContainer: textContainer];
|
1999-04-09 15:48:39 +00:00
|
|
|
|
2003-03-24 18:15:56 +00:00
|
|
|
if (![[NSView focusView] isFlipped])
|
1999-04-09 15:48:39 +00:00
|
|
|
{
|
2003-03-24 18:15:56 +00:00
|
|
|
DPSscale(ctxt, 1, -1);
|
|
|
|
rect.origin.y = -NSMaxY(rect);
|
|
|
|
[NSFont _setFontFlipHack: YES];
|
1999-04-09 15:48:39 +00:00
|
|
|
}
|
|
|
|
|
2003-03-24 18:15:56 +00:00
|
|
|
[layoutManager drawBackgroundForGlyphRange: r
|
|
|
|
atPoint: rect.origin];
|
1999-04-09 15:48:39 +00:00
|
|
|
|
2003-03-24 18:15:56 +00:00
|
|
|
[layoutManager drawGlyphsForGlyphRange: r
|
|
|
|
atPoint: rect.origin];
|
1999-04-09 15:48:39 +00:00
|
|
|
|
2003-03-24 18:15:56 +00:00
|
|
|
[NSFont _setFontFlipHack: NO];
|
|
|
|
if (![[NSView focusView] isFlipped])
|
1999-04-09 15:48:39 +00:00
|
|
|
{
|
2003-03-24 18:15:56 +00:00
|
|
|
DPSscale(ctxt, 1, -1);
|
1999-04-09 15:48:39 +00:00
|
|
|
}
|
|
|
|
|
2003-03-24 18:15:56 +00:00
|
|
|
if (need_clip)
|
1999-04-01 06:19:37 +00:00
|
|
|
{
|
2003-03-24 18:15:56 +00:00
|
|
|
/* Restore the original clipping path. */
|
|
|
|
DPSgrestore(ctxt);
|
1999-04-01 06:19:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2001-05-18 21:00:52 +00:00
|
|
|
- (NSSize) size
|
|
|
|
{
|
2003-03-24 18:15:56 +00:00
|
|
|
NSRange r;
|
|
|
|
NSRect rect;
|
2001-05-18 21:00:52 +00:00
|
|
|
|
2003-03-24 18:15:56 +00:00
|
|
|
init_string_drawing();
|
2001-05-18 21:00:52 +00:00
|
|
|
|
2003-03-24 18:15:56 +00:00
|
|
|
[textStorage replaceCharactersInRange: NSMakeRange(0, [textStorage length])
|
|
|
|
withString: @""];
|
2001-05-18 21:00:52 +00:00
|
|
|
|
2003-03-24 18:15:56 +00:00
|
|
|
[textContainer setContainerSize: NSMakeSize(1e8, 1e8)];
|
2001-05-18 21:00:52 +00:00
|
|
|
|
2003-03-24 18:15:56 +00:00
|
|
|
[textStorage replaceCharactersInRange: NSMakeRange(0, 0)
|
|
|
|
withAttributedString: self];
|
2001-05-18 21:00:52 +00:00
|
|
|
|
2003-03-24 18:15:56 +00:00
|
|
|
r = NSMakeRange(0, [layoutManager numberOfGlyphs]);
|
2001-05-18 21:00:52 +00:00
|
|
|
|
2003-03-24 18:15:56 +00:00
|
|
|
rect = [layoutManager usedRectForTextContainer: textContainer];
|
|
|
|
return rect.size;
|
2001-05-18 21:00:52 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
@end
|
|
|
|
|
2000-10-02 17:49:43 +00:00
|
|
|
/*
|
2003-03-24 18:15:56 +00:00
|
|
|
TODO: It's severely sub-optimal, but the NSString methods just use
|
|
|
|
NSAttributedString to do the job.
|
|
|
|
*/
|
2000-10-02 17:49:43 +00:00
|
|
|
@implementation NSString (NSStringDrawing)
|
|
|
|
|
|
|
|
- (void) drawAtPoint: (NSPoint)point withAttributes: (NSDictionary *)attrs
|
2000-03-08 08:43:15 +00:00
|
|
|
{
|
2000-10-02 17:49:43 +00:00
|
|
|
NSAttributedString *a;
|
2000-03-08 08:43:15 +00:00
|
|
|
|
2003-03-24 18:15:56 +00:00
|
|
|
a = [[NSAttributedString allocWithZone: NSDefaultMallocZone()]
|
|
|
|
initWithString: self
|
|
|
|
attributes: attrs];
|
2000-10-02 17:49:43 +00:00
|
|
|
[a drawAtPoint: point];
|
|
|
|
RELEASE(a);
|
2000-03-08 08:43:15 +00:00
|
|
|
}
|
|
|
|
|
2000-10-02 17:49:43 +00:00
|
|
|
- (void) drawInRect: (NSRect)rect withAttributes: (NSDictionary *)attrs
|
2000-03-08 08:43:15 +00:00
|
|
|
{
|
2000-10-02 17:49:43 +00:00
|
|
|
NSAttributedString *a;
|
|
|
|
|
|
|
|
a = [[NSAttributedString allocWithZone: NSDefaultMallocZone()]
|
2003-03-24 18:15:56 +00:00
|
|
|
initWithString: self
|
|
|
|
attributes: attrs];
|
2000-10-02 17:49:43 +00:00
|
|
|
[a drawInRect: rect];
|
|
|
|
RELEASE(a);
|
2000-03-08 08:43:15 +00:00
|
|
|
}
|
|
|
|
|
2000-10-02 17:49:43 +00:00
|
|
|
- (NSSize) sizeWithAttributes: (NSDictionary *)attrs
|
2000-03-08 08:43:15 +00:00
|
|
|
{
|
2000-10-02 17:49:43 +00:00
|
|
|
NSAttributedString *a;
|
|
|
|
NSSize s;
|
2000-03-08 08:43:15 +00:00
|
|
|
|
2000-10-02 17:49:43 +00:00
|
|
|
a = [[NSAttributedString allocWithZone: NSDefaultMallocZone()]
|
2003-03-24 18:15:56 +00:00
|
|
|
initWithString: self
|
|
|
|
attributes: attrs];
|
2000-10-02 17:49:43 +00:00
|
|
|
s = [a size];
|
|
|
|
RELEASE(a);
|
|
|
|
return s;
|
|
|
|
}
|
2000-03-08 08:43:15 +00:00
|
|
|
@end
|
2003-03-24 18:15:56 +00:00
|
|
|
|
|
|
|
|
|
|
|
void GSStringDrawingDummyFunction(void)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|