mirror of
https://github.com/gnustep/libs-gui.git
synced 2025-04-24 06:28:54 +00:00
Implement text attachment handling.
git-svn-id: svn+ssh://svn.gna.org/svn/gnustep/libs/gui/trunk@15825 72102866-910b-0410-8b05-ffd578937521
This commit is contained in:
parent
8ec1d6751e
commit
3ea5844deb
9 changed files with 503 additions and 85 deletions
23
ChangeLog
23
ChangeLog
|
@ -1,3 +1,26 @@
|
|||
2003-01-31 21:59 Alexander Malmberg <alexander@malmberg.org>
|
||||
|
||||
* Headers/gnustep/gui/GSLayoutManager.h,
|
||||
Headers/gnustep/gui/GSLayoutManager_internal.h,
|
||||
Source/GSLayoutManager.m: Implement [GSLayoutManager
|
||||
-setAttachmentSize:forGlyphRange:].
|
||||
|
||||
* Source/NSLayoutManager.m (-drawGlyphsForGlyphRange:atPoint:):
|
||||
Implement drawing of attachments.
|
||||
|
||||
* Headers/gnustep/gui/NSFont.h: Add GSAttachmentGlyph to mark
|
||||
attachments in a glyph context.
|
||||
|
||||
* Source/GSHorizontalLayoutManager.m: Cleanups and comments.
|
||||
(-layoutLineNewParagraph:): Rewrite line height and baseline
|
||||
handling. Implement typesetting of attachments.
|
||||
|
||||
* Headers/gnustep/gui/NSTextAttachment.h: Add documentation.
|
||||
|
||||
* Source/NSTextAttachment.m (-cellFrameForTextContainer:
|
||||
proposedLineFragment:glyphPosition:characterIndex:): Return the
|
||||
correct position.
|
||||
|
||||
Thu Jan 30 16:31:16 2003 Nicola Pero <n.pero@mi.flashnet.it>
|
||||
|
||||
* Source/GNUmakefile (NSMenuItem.m_FILE_FLAGS): Use -Wno-protocol
|
||||
|
|
|
@ -203,10 +203,6 @@ subclassing). */
|
|||
forGlyphAtIndex: (unsigned int)glyphIndex;
|
||||
|
||||
|
||||
- (void) setAttachmentSize: (NSSize)attachmentSize
|
||||
forGlyphRange: (NSRange)glyphRange; /* not OPENSTEP */
|
||||
|
||||
|
||||
/* Returns the font actually used for a range of glyphs. This isn't
|
||||
necessarily the font specified by NSFontAttributeName; both the typesetter
|
||||
and the layout manager can substitute a different font (the typesetter might
|
||||
|
@ -258,6 +254,9 @@ manager might be substituting screen fonts. */
|
|||
- (void) setLocation: (NSPoint)location
|
||||
forStartOfGlyphRange: (NSRange)glyphRange;
|
||||
|
||||
- (void) setAttachmentSize: (NSSize)attachmentSize
|
||||
forGlyphRange: (NSRange)glyphRange; /* not OPENSTEP */
|
||||
|
||||
|
||||
- (NSTextContainer *) textContainerForGlyphAtIndex: (unsigned int)glyphIndex
|
||||
effectiveRange: (NSRange *)effectiveRange;
|
||||
|
|
|
@ -56,7 +56,7 @@ typedef struct GSLayoutManager_glyph_run_head_s
|
|||
/* char_length must always be accurate. glyph_length is the number of
|
||||
valid glyphs counting from the start. For a level 0 head, it's the number
|
||||
of glyphs in that run. */
|
||||
int glyph_length,char_length;
|
||||
int glyph_length, char_length;
|
||||
|
||||
/* Glyph generation is complete for all created runs. */
|
||||
unsigned int complete:1;
|
||||
|
@ -135,25 +135,34 @@ typedef struct GSLayoutManager_glyph_run_s
|
|||
/* All positions and lengths in glyphs */
|
||||
typedef struct
|
||||
{
|
||||
unsigned int pos,length;
|
||||
unsigned int pos, length;
|
||||
NSPoint p;
|
||||
} linefrag_point_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
NSRect rect,used_rect;
|
||||
unsigned int pos,length;
|
||||
unsigned int pos, length;
|
||||
NSSize size;
|
||||
} linefrag_attachment_t;
|
||||
|
||||
typedef struct
|
||||
{
|
||||
NSRect rect, used_rect;
|
||||
unsigned int pos, length;
|
||||
|
||||
linefrag_point_t *points;
|
||||
int num_points;
|
||||
|
||||
linefrag_attachment_t *attachments;
|
||||
int num_attachments;
|
||||
} linefrag_t;
|
||||
|
||||
typedef struct GSLayoutManager_textcontainer_s
|
||||
{
|
||||
NSTextContainer *textContainer;
|
||||
|
||||
BOOL started,complete;
|
||||
unsigned int pos,length;
|
||||
BOOL started, complete;
|
||||
unsigned int pos, length;
|
||||
|
||||
linefrag_t *linefrags;
|
||||
int num_linefrags;
|
||||
|
@ -199,7 +208,7 @@ run, i is the glyph index in the run. */
|
|||
|
||||
/* Steps forward to the next glyph. If there is no next glyph, r will be
|
||||
the last run and i==r->head.glyph_length. */
|
||||
#define GLYPH_STEP_FORWARD(r,i,pos,cpos) \
|
||||
#define GLYPH_STEP_FORWARD(r, i, pos, cpos) \
|
||||
{ \
|
||||
i++; \
|
||||
while (i==r->head.glyph_length) \
|
||||
|
@ -219,7 +228,7 @@ the last run and i==r->head.glyph_length. */
|
|||
|
||||
/* Steps backward to the previous glyph. If there is no previous glyph, r
|
||||
will be the first glyph and i==-1. */
|
||||
#define GLYPH_STEP_BACKWARD(r,i,pos,cpos) \
|
||||
#define GLYPH_STEP_BACKWARD(r, i, pos, cpos) \
|
||||
{ \
|
||||
i--; \
|
||||
while (i<0 && r->prev) \
|
||||
|
@ -238,11 +247,11 @@ condition holds. r, i, pos, and cpos must be simple variables. When done, r,
|
|||
i, pos, and cpos will be set for the first glyph for which the condition
|
||||
doesn't hold. If there is no such glyph, r is the last run and
|
||||
i==r->head.glyph_length. */
|
||||
#define GLYPH_SCAN_FORWARD(r,i,pos,cpos,condition) \
|
||||
#define GLYPH_SCAN_FORWARD(r, i, pos, cpos, condition) \
|
||||
{ \
|
||||
while (condition) \
|
||||
{ \
|
||||
GLYPH_STEP_FORWARD(r,i,pos,cpos) \
|
||||
GLYPH_STEP_FORWARD(r, i, pos, cpos) \
|
||||
if (i==r->head.glyph_length) \
|
||||
break; \
|
||||
} \
|
||||
|
@ -251,18 +260,18 @@ i==r->head.glyph_length. */
|
|||
/* Scan backward. r, i, pos, and cpos will be set to the first glyph for
|
||||
which condition doesn't hold. If there is no such glyph, r is the first run
|
||||
and i==-1. */
|
||||
#define GLYPH_SCAN_BACKWARD(r,i,pos,cpos,condition) \
|
||||
#define GLYPH_SCAN_BACKWARD(r, i, pos, cpos, condition) \
|
||||
{ \
|
||||
while (condition) \
|
||||
{ \
|
||||
GLYPH_STEP_BACKWARD(r,i,pos,cpos) \
|
||||
GLYPH_STEP_BACKWARD(r, i, pos, cpos) \
|
||||
if (i==-1) \
|
||||
break; \
|
||||
} \
|
||||
}
|
||||
|
||||
glyph_run_t *GSLayoutManager_run_for_glyph_index(unsigned int glyphIndex,
|
||||
glyph_run_head_t *glyphs,unsigned int *glyph_pos,unsigned int *char_pos);
|
||||
glyph_run_head_t *glyphs, unsigned int *glyph_pos, unsigned int *char_pos);
|
||||
#define run_for_glyph_index GSLayoutManager_run_for_glyph_index
|
||||
|
||||
#endif
|
||||
|
|
|
@ -41,6 +41,7 @@ typedef unsigned int NSGlyph;
|
|||
|
||||
enum {
|
||||
NSControlGlyph = 0x00ffffff,
|
||||
GSAttachmentGlyph = 0x00fffffe,
|
||||
NSNullGlyph = 0x0
|
||||
};
|
||||
|
||||
|
|
|
@ -72,12 +72,47 @@ enum {
|
|||
- (void)highlight:(BOOL)flag
|
||||
withFrame:(NSRect)cellFrame
|
||||
inView:(NSView *)controlView;
|
||||
|
||||
|
||||
/** The old way of placing the cell. The text system will never call
|
||||
these directly (TODO: make sure it doesn't), but other things might. The
|
||||
class implements the new method by calling these, so subclasses can
|
||||
easily change behavior by overriding these. **/
|
||||
|
||||
- (NSSize)cellSize;
|
||||
|
||||
/* Returns the offset from the current point when typesetting to the lower
|
||||
left corner of the cell. The class returns (0,0). Positive y is probably
|
||||
up. (TODO) */
|
||||
- (NSPoint)cellBaselineOffset;
|
||||
- (NSRect)cellFrameForTextContainer:(NSTextContainer *)textContainer
|
||||
proposedLineFragment:(NSRect)lineFrag
|
||||
glyphPosition:(NSPoint)position
|
||||
characterIndex:(unsigned)charIndex;
|
||||
|
||||
|
||||
/** The new way of placing the cell. **/
|
||||
|
||||
/* Returns the rectangle in which the cell should be drawn. The rectangle
|
||||
is relative to the current point when typesetting. Positive y is probably
|
||||
up. (TODO)
|
||||
|
||||
lineFrag is the line frag rect that this cell might be placed in, and
|
||||
position is the current position in that line frag rect (positive y is
|
||||
probably down (TODO)). Note that the line frag rect and glyph position
|
||||
may not be where the cell is actually placed.
|
||||
|
||||
Note that this might be called many times for the same attachment. Eg. if
|
||||
you return a rectangle that won't fit in the proposed line frag rect, the
|
||||
typesetter might try to adjust things so it will fit. It will then send
|
||||
this message with a new proposed line frag rect and glyph position. Thus,
|
||||
great care must be taken when using the line frag rect to calculate the
|
||||
returned rectangle to prevent the typesetting process from getting stuck.
|
||||
|
||||
The class uses -cellSize and -cellBaselineOffset to return a rect.
|
||||
*/
|
||||
-(NSRect) cellFrameForTextContainer: (NSTextContainer *)textContainer
|
||||
proposedLineFragment: (NSRect)lineFrag
|
||||
glyphPosition: (NSPoint)position
|
||||
characterIndex: (unsigned int)charIndex;
|
||||
|
||||
|
||||
- (BOOL)wantsToTrackMouse;
|
||||
- (BOOL)wantsToTrackMouseForEvent:(NSEvent *)theEvent
|
||||
inRect:(NSRect)cellFrame
|
||||
|
|
|
@ -36,8 +36,15 @@
|
|||
#include "AppKit/NSTextStorage.h"
|
||||
#include "AppKit/NSParagraphStyle.h"
|
||||
#include "AppKit/NSTextContainer.h"
|
||||
#include "AppKit/NSTextAttachment.h"
|
||||
|
||||
|
||||
/*
|
||||
Note that unless the user creates extra instances, there will only be one
|
||||
instance of GSHorizontalTypesetter for all text typesetting, so we can
|
||||
cache fairly aggressively without having to worry about memory consumption.
|
||||
*/
|
||||
|
||||
|
||||
@implementation GSHorizontalTypesetter
|
||||
|
||||
|
@ -69,6 +76,7 @@ static GSHorizontalTypesetter *shared;
|
|||
|
||||
typedef struct GSHorizontalTypesetter_glyph_cache_s
|
||||
{
|
||||
/* These fields are filled in by the caching: */
|
||||
NSGlyph g;
|
||||
int char_index;
|
||||
|
||||
|
@ -81,9 +89,10 @@ typedef struct GSHorizontalTypesetter_glyph_cache_s
|
|||
int superscript;
|
||||
} attributes;
|
||||
|
||||
/* These fields are filled in during layout: */
|
||||
BOOL nominal;
|
||||
NSPoint pos; /* relative to the baseline */
|
||||
float width;
|
||||
NSPoint pos; /* relative to the line's baseline */
|
||||
NSSize size; /* height is used only for attachments */
|
||||
BOOL dont_show, outside_line_frag;
|
||||
} glyph_cache_t;
|
||||
|
||||
|
@ -242,11 +251,11 @@ been cached.
|
|||
if (gi > 0)
|
||||
{
|
||||
g->pos = g[-1].pos;
|
||||
g->pos.x += g[-1].width;
|
||||
g->pos.x += g[-1].size.width;
|
||||
}
|
||||
else
|
||||
g->pos = NSMakePoint(0,0);
|
||||
g->width = 0;
|
||||
g->size.width = 0;
|
||||
return gi + 1 + cache_base;
|
||||
}
|
||||
gi--;
|
||||
|
@ -339,8 +348,28 @@ typedef struct
|
|||
|
||||
-(int) layoutLineNewParagraph: (BOOL)newParagraph
|
||||
{
|
||||
NSRect rect,remain;
|
||||
float line_height,max_line_height,baseline;
|
||||
NSRect rect, remain;
|
||||
|
||||
/* Baseline and line height handling. */
|
||||
float line_height; /* Current line height. */
|
||||
float max_line_height; /* Maximum line height (usually from the paragraph style). */
|
||||
float baseline; /* Baseline position (0 is top of line-height, positive is down). */
|
||||
float ascender; /* Amount of space we want above the baseline (always>=0). */
|
||||
float descender; /* Amount of space we want below the baseline (always>=0). */
|
||||
/*
|
||||
These are values for the line as a whole. We start out by initializing
|
||||
for the first glyph on the line and then update these as we add more
|
||||
glyphs.
|
||||
|
||||
If we need to increase the line height, we jump back to 'restart:' and
|
||||
rebuild our array of line frag rects.
|
||||
|
||||
(TODO (optimization): if we're dealing with a "simple rectangular
|
||||
text container", we should try to extend the existing line frag in place
|
||||
before jumping back to do all the expensive checking).
|
||||
|
||||
Normally, baseline==ascender.
|
||||
*/
|
||||
|
||||
line_frag_t *line_frags = NULL;
|
||||
int num_line_frags = 0;
|
||||
|
@ -352,6 +381,7 @@ typedef struct
|
|||
if (!cache_length && at_end)
|
||||
return 2;
|
||||
|
||||
/* Set up our initial baseline info. */
|
||||
{
|
||||
float min = [curParagraphStyle minimumLineHeight];
|
||||
max_line_height = [curParagraphStyle maximumLineHeight];
|
||||
|
@ -361,7 +391,13 @@ typedef struct
|
|||
max_line_height = min;
|
||||
|
||||
line_height = [cache->font defaultLineHeightForFont];
|
||||
baseline = ([cache->font ascender] + [cache->font descender] + line_height) / 2.0;
|
||||
ascender = [cache->font ascender];
|
||||
descender = [cache->font descender];
|
||||
|
||||
#define COMPUTE_BASELINE baseline = ascender
|
||||
|
||||
COMPUTE_BASELINE;
|
||||
|
||||
if (line_height < min)
|
||||
line_height = min;
|
||||
|
||||
|
@ -370,8 +406,23 @@ typedef struct
|
|||
}
|
||||
|
||||
/* If we find out that we need to increase the line height, we have to
|
||||
start over. The increased line height might give _completely_ different
|
||||
line frag rects, so we can't reuse much of the layout information. */
|
||||
start over. The increased line height might give _completely_ different
|
||||
line frag rects, so we can't reuse the layout information. */
|
||||
|
||||
|
||||
#define WANT_LINE_HEIGHT(h) \
|
||||
do { \
|
||||
float __new_height = (h); \
|
||||
if (max_line_height > 0 && __new_height > max_line_height) \
|
||||
__new_height = max_line_height; \
|
||||
if (__new_height > line_height) \
|
||||
{ \
|
||||
line_height = __new_height; \
|
||||
COMPUTE_BASELINE; \
|
||||
goto restart; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
restart:
|
||||
// printf("start: at (%g %g) line_height = %g, baseline = %g\n",curPoint.x,curPoint.y,line_height,baseline);
|
||||
{
|
||||
|
@ -391,6 +442,14 @@ restart:
|
|||
line_height + [curParagraphStyle lineSpacing]);
|
||||
}
|
||||
|
||||
/*
|
||||
Build a list of all line frag rects for this line.
|
||||
|
||||
TODO: it's very convenient to do this in advance, but it might be
|
||||
inefficient, and in theory, we might end up with an insane number of line
|
||||
rects (eg. a text container with "hole"-columns every 100 points and
|
||||
width 1e8)
|
||||
*/
|
||||
num_line_frags = 0;
|
||||
if (line_frags)
|
||||
{
|
||||
|
@ -424,25 +483,42 @@ restart:
|
|||
return 1;
|
||||
}
|
||||
|
||||
|
||||
{
|
||||
unsigned int i = 0;
|
||||
NSFont *f = cache->font;
|
||||
glyph_cache_t *g;
|
||||
|
||||
NSPoint p;
|
||||
|
||||
NSFont *f = cache->font;
|
||||
float f_ascender = [f ascender], f_descender = [f descender];
|
||||
|
||||
NSGlyph last_glyph = NSNullGlyph;
|
||||
NSPoint last_p,p;
|
||||
NSPoint last_p;
|
||||
|
||||
unsigned int first_glyph;
|
||||
line_frag_t *lf = line_frags;
|
||||
int lfi = 0;
|
||||
|
||||
BOOL prev_was_attachment;
|
||||
|
||||
|
||||
last_p = p = NSMakePoint(0,0);
|
||||
|
||||
g = cache;
|
||||
first_glyph = 0;
|
||||
prev_was_attachment = NO;
|
||||
/*
|
||||
Main glyph layout loop.
|
||||
*/
|
||||
while (1)
|
||||
{
|
||||
relayout:
|
||||
/*printf("at %3i+%2i, glyph %08x, char %04x (%i)\n",
|
||||
cache_base,i,
|
||||
g->g,
|
||||
[[curTextStorage string] characterAtIndex: g->char_index],g->char_index);*/
|
||||
|
||||
/* Update the cache. */
|
||||
if (i >= cache_length)
|
||||
{
|
||||
if (at_end)
|
||||
|
@ -452,61 +528,203 @@ restart:
|
|||
break;
|
||||
g = cache + i;
|
||||
}
|
||||
|
||||
/*
|
||||
At this point:
|
||||
|
||||
p is the current point (sortof); the point where a nominally
|
||||
spaced glyph would be placed.
|
||||
|
||||
g is the current glyph. i is the current glyph index, relative to
|
||||
the start of the cache.
|
||||
|
||||
last_p and last_glyph are used for kerning and hold the previous
|
||||
glyph and its position. If there's no previous glyph (for kerning
|
||||
purposes), last_glyph is NSNullGlyph and last_p is undefined.
|
||||
|
||||
lf and lfi track the current line frag rect. first_glyph is the
|
||||
first glyph in the current line frag rect.
|
||||
|
||||
Note that the variables tracking the previous glyph shouldn't be
|
||||
updated until we know that the current glyph will fit in the line
|
||||
frag rect.
|
||||
|
||||
*/
|
||||
|
||||
/* If there's a font change, check if the baseline or line height
|
||||
needs adjusting. We update the ascender and descender too, even
|
||||
though there might not actually be any glyphs for this font.
|
||||
(TODO?) */
|
||||
if (g->font != f)
|
||||
{
|
||||
float new_height;
|
||||
float new_height, new_ascender, new_descender;
|
||||
f = g->font;
|
||||
f_ascender = [f ascender];
|
||||
f_descender = [f descender];
|
||||
last_glyph = NSNullGlyph;
|
||||
|
||||
new_height = [f defaultLineHeightForFont];
|
||||
if (max_line_height > 0 && new_height > max_line_height)
|
||||
new_height = max_line_height;
|
||||
if (new_height > line_height)
|
||||
{
|
||||
line_height = new_height;
|
||||
baseline = ([cache->font ascender] + [cache->font descender] + line_height) / 2.0;
|
||||
goto restart;
|
||||
}
|
||||
new_ascender = [f ascender];
|
||||
new_descender = [f descender];
|
||||
|
||||
if (new_ascender > ascender)
|
||||
ascender = new_ascender;
|
||||
if (new_descender > descender)
|
||||
descender = new_descender;
|
||||
|
||||
COMPUTE_BASELINE;
|
||||
|
||||
WANT_LINE_HEIGHT(new_height);
|
||||
}
|
||||
|
||||
if (g->g == NSControlGlyph)
|
||||
{
|
||||
unichar ch = [[curTextStorage string] characterAtIndex: g->char_index];
|
||||
|
||||
/* TODO: need to handle other control characters */
|
||||
|
||||
g->pos = p;
|
||||
g->width = 0;
|
||||
g->size.width = 0;
|
||||
g->dont_show = YES;
|
||||
g->nominal = YES;
|
||||
g->nominal = !prev_was_attachment;
|
||||
i++;
|
||||
g++;
|
||||
last_glyph = NSNullGlyph;
|
||||
|
||||
prev_was_attachment = NO;
|
||||
|
||||
if (ch == 0xa)
|
||||
break;
|
||||
|
||||
continue;
|
||||
}
|
||||
|
||||
g->nominal = YES;
|
||||
if (g->attributes.explicit_kern)
|
||||
p.x += g->attributes.kern;
|
||||
/* TODO: this is a major bottleneck */
|
||||
/* else if (last_glyph)
|
||||
|
||||
/* Set up glyph information. */
|
||||
|
||||
/*
|
||||
TODO:
|
||||
Currently, the attributes of the attachment character (eg. font)
|
||||
affect the layout. Think hard about this.
|
||||
*/
|
||||
g->nominal = !prev_was_attachment;
|
||||
|
||||
if (g->attributes.explicit_kern &&
|
||||
g->attributes.kern != 0)
|
||||
{
|
||||
p = [f positionOfGlyph: g->g
|
||||
precededByGlyph: last_glyph
|
||||
isNominal: &g->nominal];
|
||||
p.x += last_p.x;
|
||||
p.y += last_p.y;
|
||||
}*/
|
||||
p.x += g->attributes.kern;
|
||||
g->nominal = NO;
|
||||
}
|
||||
|
||||
/* Baseline adjustments. */
|
||||
{
|
||||
float y = 0;
|
||||
|
||||
last_p = g->pos = p;
|
||||
g->width = [f advancementForGlyph: g->g].width;
|
||||
p.x += g->width;
|
||||
/* Attributes are up-side-down in our coordinate system. */
|
||||
if (g->attributes.superscript)
|
||||
{
|
||||
y -= g->attributes.superscript * [f xHeight];
|
||||
}
|
||||
if (g->attributes.baseline_offset)
|
||||
{
|
||||
y -= g->attributes.baseline_offset;
|
||||
}
|
||||
|
||||
if (y != p.y)
|
||||
{
|
||||
p.y = y;
|
||||
g->nominal = NO;
|
||||
}
|
||||
|
||||
/* The y==0 case is taken care of when the font is changed. */
|
||||
if (y < 0 && f_ascender - y > ascender)
|
||||
ascender = f_ascender - y;
|
||||
if (y > 0 && f_descender + y > descender)
|
||||
descender = f_descender + y;
|
||||
|
||||
COMPUTE_BASELINE;
|
||||
WANT_LINE_HEIGHT(ascender + descender);
|
||||
}
|
||||
|
||||
if (g->g == GSAttachmentGlyph)
|
||||
{
|
||||
NSTextAttachment *attach = [curTextStorage attribute: NSAttachmentAttributeName
|
||||
atIndex: g->char_index
|
||||
effectiveRange: NULL];
|
||||
NSTextAttachmentCell *cell = [attach attachmentCell];
|
||||
NSRect r;
|
||||
|
||||
if (!cell)
|
||||
{
|
||||
g->pos = p;
|
||||
g->size = NSMakeSize(0,0);
|
||||
g->dont_show = YES;
|
||||
g->nominal = YES;
|
||||
i++;
|
||||
g++;
|
||||
last_glyph = NSNullGlyph;
|
||||
continue;
|
||||
}
|
||||
|
||||
r = [cell cellFrameForTextContainer: curTextContainer
|
||||
proposedLineFragment: lf->rect
|
||||
glyphPosition: p
|
||||
characterIndex: g->char_index];
|
||||
|
||||
printf("cell at %i, (%g %g) in (%g %g)+(%g %g), got rect (%g %g)+(%g %g)\n",
|
||||
g->char_index,p.x,p.y,
|
||||
lf->rect.origin.x,lf->rect.origin.y,
|
||||
lf->rect.size.width,lf->rect.size.height,
|
||||
r.origin.x,r.origin.y,
|
||||
r.size.width,r.size.height);
|
||||
|
||||
/* For some obscure reason, the rectangle we get is up-side-down
|
||||
compared to everything else here, and has it's origin in p.
|
||||
(Makes sense from the cell's pov, though.) */
|
||||
|
||||
if (-NSMinY(r) > descender)
|
||||
descender = -NSMinY(r);
|
||||
|
||||
if (NSMaxY(r) > ascender)
|
||||
ascender = NSMaxY(r);
|
||||
|
||||
/* Update ascender and descender. Adjust line height and
|
||||
baseline if necessary. */
|
||||
COMPUTE_BASELINE;
|
||||
WANT_LINE_HEIGHT(ascender + descender);
|
||||
|
||||
g->size = r.size;
|
||||
g->pos.x = p.x + r.origin.x;
|
||||
g->pos.y = p.y + r.origin.y;
|
||||
|
||||
p.x = g->pos.x + g->size.width;
|
||||
|
||||
/* An attachment is always in a point range of its own. */
|
||||
g->nominal = NO;
|
||||
}
|
||||
else
|
||||
{
|
||||
/* TODO: this is a major bottleneck */
|
||||
/* if (last_glyph)
|
||||
{
|
||||
p = [f positionOfGlyph: g->g
|
||||
precededByGlyph: last_glyph
|
||||
isNominal: &g->nominal];
|
||||
p.x += last_p.x;
|
||||
p.y += last_p.y;
|
||||
}*/
|
||||
|
||||
last_p = g->pos = p;
|
||||
g->size = [f advancementForGlyph: g->g]; /* only width is used */
|
||||
p.x += g->size.width;
|
||||
}
|
||||
|
||||
/* Did the glyph fit in the line frag rect? */
|
||||
if (p.x > lf->rect.size.width)
|
||||
{
|
||||
/* It didn't. Try to break the line. */
|
||||
switch ([curParagraphStyle lineBreakMode])
|
||||
{
|
||||
{ /* TODO: implement all modes */
|
||||
default:
|
||||
case NSLineBreakByCharWrapping:
|
||||
char_wrapping:
|
||||
|
@ -521,8 +739,8 @@ restart:
|
|||
}
|
||||
|
||||
/* We force at least one glyph into each line frag rect. This
|
||||
ensures that typesetting will never get stuck (ie. if the text
|
||||
container is to narrow to fit even a single glyph). */
|
||||
ensures that typesetting will never get stuck (ie. if the text
|
||||
container is to narrow to fit even a single glyph). */
|
||||
if (lf->last_glyph <= first_glyph)
|
||||
lf->last_glyph = i + 1;
|
||||
|
||||
|
@ -530,23 +748,37 @@ restart:
|
|||
i = lf->last_glyph;
|
||||
g = cache + i;
|
||||
/* The -1 is always valid since there's at least one glyph in the
|
||||
line frag rect (see above). */
|
||||
lf->last_used = g[-1].pos.x + g[-1].width;
|
||||
line frag rect (see above). */
|
||||
lf->last_used = g[-1].pos.x + g[-1].size.width;
|
||||
last_glyph = NSNullGlyph;
|
||||
prev_was_attachment = NO;
|
||||
|
||||
lf++;
|
||||
lfi++;
|
||||
if (lfi == num_line_frags)
|
||||
break;
|
||||
first_glyph = i;
|
||||
goto relayout;
|
||||
}
|
||||
|
||||
last_glyph = g->g;
|
||||
i++;
|
||||
g++;
|
||||
else
|
||||
{
|
||||
/* Move to next glyph. */
|
||||
last_glyph = g->g;
|
||||
if (last_glyph == GSAttachmentGlyph)
|
||||
{
|
||||
last_glyph = NSNullGlyph;
|
||||
prev_was_attachment = YES;
|
||||
}
|
||||
else
|
||||
{
|
||||
prev_was_attachment = NO;
|
||||
}
|
||||
i++;
|
||||
g++;
|
||||
}
|
||||
}
|
||||
/* Basic layout is done. */
|
||||
|
||||
/* Take care of the alignments. */
|
||||
if (lfi != num_line_frags)
|
||||
{
|
||||
lf->last_glyph = i;
|
||||
|
@ -574,17 +806,18 @@ restart:
|
|||
newParagraph = NO;
|
||||
}
|
||||
|
||||
/* Layout is complete. Package it and give it to the layout manager. */
|
||||
[curLayoutManager setTextContainer: curTextContainer
|
||||
forGlyphRange: NSMakeRange(cache_base,i)];
|
||||
forGlyphRange: NSMakeRange(cache_base, i)];
|
||||
curGlyph = i + cache_base;
|
||||
{
|
||||
line_frag_t *lf;
|
||||
NSPoint p;
|
||||
unsigned int i,j;
|
||||
unsigned int i, j;
|
||||
glyph_cache_t *g;
|
||||
NSRect used_rect;
|
||||
|
||||
for (lf = line_frags,i = 0,g = cache;lfi >= 0;lfi--,lf++)
|
||||
for (lf = line_frags, i = 0, g = cache; lfi >= 0; lfi--, lf++)
|
||||
{
|
||||
used_rect.origin.x = g->pos.x + lf->rect.origin.x;
|
||||
used_rect.size.width = lf->last_used - g->pos.x;
|
||||
|
@ -596,6 +829,7 @@ restart:
|
|||
forGlyphRange: NSMakeRange(cache_base + i,lf->last_glyph - i)
|
||||
usedRect: used_rect];
|
||||
p = g->pos;
|
||||
/* TODO: probably don't need to call unless the flags are YES */
|
||||
[curLayoutManager setDrawsOutsideLineFragment: g->outside_line_frag
|
||||
forGlyphAtIndex: cache_base + i];
|
||||
[curLayoutManager setNotShownAttribute: g->dont_show
|
||||
|
@ -607,7 +841,12 @@ restart:
|
|||
if (!g->nominal && i != j)
|
||||
{
|
||||
[curLayoutManager setLocation: p
|
||||
forStartOfGlyphRange: NSMakeRange(cache_base + j,i - j)];
|
||||
forStartOfGlyphRange: NSMakeRange(cache_base + j, i - j)];
|
||||
if (g[-1].g == GSAttachmentGlyph)
|
||||
{
|
||||
[curLayoutManager setAttachmentSize: g[-1].size
|
||||
forGlyphRange: NSMakeRange(cache_base + j, i - j)];
|
||||
}
|
||||
p = g->pos;
|
||||
p.y += baseline;
|
||||
j = i;
|
||||
|
@ -619,6 +858,11 @@ restart:
|
|||
{
|
||||
[curLayoutManager setLocation: p
|
||||
forStartOfGlyphRange: NSMakeRange(cache_base + j,i - j)];
|
||||
if (g[-1].g == GSAttachmentGlyph)
|
||||
{
|
||||
[curLayoutManager setAttachmentSize: g[-1].size
|
||||
forGlyphRange: NSMakeRange(cache_base + j, i - j)];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -632,8 +876,8 @@ restart:
|
|||
line_frags = NULL;
|
||||
}
|
||||
|
||||
/* if we're really at the end, we should probably set the extra line frag
|
||||
stuff here */
|
||||
/* TODO: if we're really at the end, we should probably set the extra
|
||||
line frag stuff here */
|
||||
if (newParagraph)
|
||||
return 3;
|
||||
else
|
||||
|
|
|
@ -1257,13 +1257,6 @@ it should still be safe. might lose opportunities to merge runs, though.
|
|||
return 0;
|
||||
}
|
||||
|
||||
-(void) setAttachmentSize: (NSSize)size
|
||||
forGlyphRange: (NSRange)range
|
||||
{
|
||||
NSLog(@"%s not implemented yet", __PRETTY_FUNCTION__);
|
||||
/* TODO!! */
|
||||
}
|
||||
|
||||
@end
|
||||
|
||||
|
||||
|
@ -1287,8 +1280,13 @@ it should still be safe. might lose opportunities to merge runs, though.
|
|||
if (tc->linefrags)
|
||||
{
|
||||
for (j = 0, lf = tc->linefrags; j < tc->num_linefrags; j++, lf++)
|
||||
if (lf->points)
|
||||
free(lf->points);
|
||||
{
|
||||
if (lf->points)
|
||||
free(lf->points);
|
||||
if (lf->attachments)
|
||||
free(lf->attachments);
|
||||
}
|
||||
|
||||
free(tc->linefrags);
|
||||
}
|
||||
tc->linefrags = NULL;
|
||||
|
@ -1653,6 +1651,53 @@ forStartOfGlyphRange: (NSRange)glyphRange
|
|||
layout_char = [self characterIndexForGlyphAtIndex: layout_glyph];
|
||||
}
|
||||
|
||||
|
||||
-(void) setAttachmentSize: (NSSize)size
|
||||
forGlyphRange: (NSRange)glyphRange
|
||||
{
|
||||
textcontainer_t *tc;
|
||||
int i;
|
||||
linefrag_t *lf;
|
||||
linefrag_attachment_t *la;
|
||||
|
||||
SETUP_STUFF
|
||||
|
||||
for (i = 0, tc = textcontainers; i < num_textcontainers; i++, tc++)
|
||||
{
|
||||
if (tc->pos <= glyphRange.location &&
|
||||
tc->pos + tc->length >= glyphRange.location + glyphRange.length)
|
||||
break;
|
||||
}
|
||||
if (i == num_textcontainers)
|
||||
{
|
||||
NSLog(@"%s: text container not set for range", __PRETTY_FUNCTION__);
|
||||
return;
|
||||
}
|
||||
|
||||
for (i = 0, lf = tc->linefrags; i < tc->num_linefrags; i++, lf++)
|
||||
{
|
||||
if (lf->pos <= glyphRange.location &&
|
||||
lf->pos + lf->length >= glyphRange.location + glyphRange.length)
|
||||
break;
|
||||
}
|
||||
if (i == tc->num_linefrags)
|
||||
{
|
||||
NSLog(@"%s: line fragment rect not set for range", __PRETTY_FUNCTION__);
|
||||
return;
|
||||
}
|
||||
|
||||
/* TODO: we do no sanity checking of attachment size ranges. might want
|
||||
to consider doing it */
|
||||
lf->attachments = realloc(lf->attachments,
|
||||
sizeof(linefrag_attachment_t) * (lf->num_attachments + 1));
|
||||
la = &lf->attachments[lf->num_attachments++];
|
||||
|
||||
memset(la, 0, sizeof(linefrag_attachment_t));
|
||||
la->pos = glyphRange.location;
|
||||
la->length = glyphRange.length;
|
||||
la->size = size;
|
||||
}
|
||||
|
||||
#undef SETUP_STUFF
|
||||
|
||||
|
||||
|
|
|
@ -697,6 +697,9 @@ container */
|
|||
linefrag_t *lf;
|
||||
linefrag_point_t *lp;
|
||||
|
||||
linefrag_attachment_t *la;
|
||||
int la_i;
|
||||
|
||||
NSPoint p;
|
||||
unsigned int g;
|
||||
|
||||
|
@ -715,6 +718,8 @@ container */
|
|||
int gbuf_len;
|
||||
NSPoint gbuf_point;
|
||||
|
||||
NSView *controlView = nil;
|
||||
|
||||
|
||||
if (!range.length)
|
||||
return;
|
||||
|
@ -741,6 +746,9 @@ container */
|
|||
return;
|
||||
}
|
||||
|
||||
la = lf->attachments;
|
||||
la_i = 0;
|
||||
|
||||
j = 0;
|
||||
lp = lf->points;
|
||||
while (lp->pos + lp->length < range.location)
|
||||
|
@ -784,6 +792,8 @@ container */
|
|||
lf++;
|
||||
j = 0;
|
||||
lp = lf->points;
|
||||
la = lf->attachments;
|
||||
la_i = 0;
|
||||
}
|
||||
p = lp->p;
|
||||
p.x += lf->rect.origin.x + containerOrigin.x;
|
||||
|
@ -827,6 +837,58 @@ container */
|
|||
}
|
||||
if (!glyph->isNotShown && glyph->g && glyph->g != NSControlGlyph)
|
||||
{
|
||||
if (glyph->g == GSAttachmentGlyph)
|
||||
{
|
||||
/* Silently ignore if we don't have any size information for
|
||||
it. */
|
||||
if (g >= range.location && la)
|
||||
{
|
||||
unsigned int char_index =
|
||||
[self characterRangeForGlyphRange: NSMakeRange(g,1)
|
||||
actualGlyphRange: NULL].location;
|
||||
NSObject<NSTextAttachmentCell> *cell = [[_textStorage attribute: NSAttachmentAttributeName
|
||||
atIndex: char_index
|
||||
effectiveRange: NULL] attachmentCell];
|
||||
NSRect cellFrame;
|
||||
|
||||
if (!controlView)
|
||||
controlView = [tc->textContainer textView];
|
||||
|
||||
while (la->pos != g && la_i < lf->num_attachments)
|
||||
{
|
||||
la++;
|
||||
la_i++;
|
||||
}
|
||||
if (la_i >= lf->num_attachments)
|
||||
continue;
|
||||
|
||||
cellFrame.origin = p;
|
||||
cellFrame.size = la->size;
|
||||
cellFrame.origin.y -= cellFrame.size.height;
|
||||
|
||||
/* Drawing the cell might mess up our state. */
|
||||
/* TODO:
|
||||
optimize this. probably cheaper to not save and
|
||||
explicitly reset just the stuff we actually use, or to
|
||||
collect attachments and draw them in bunches of eg. 4
|
||||
|
||||
should they really be drawn in our coordinate system?
|
||||
*/
|
||||
[cell drawWithFrame: cellFrame
|
||||
inView: controlView
|
||||
characterIndex: char_index
|
||||
layoutManager: self];
|
||||
[f set];
|
||||
if (color)
|
||||
[color set];
|
||||
else
|
||||
{
|
||||
DPSsetgray(ctxt, 0.0);
|
||||
DPSsetalpha(ctxt, 1.0);
|
||||
}
|
||||
}
|
||||
continue;
|
||||
}
|
||||
if (g >= range.location)
|
||||
{
|
||||
if (!gbuf_len)
|
||||
|
@ -860,6 +922,7 @@ for (i = 0; i < gbuf_len; i++) printf(" %3i : %04x\n", i, gbuf[i]); */
|
|||
GSShowGlyphs(ctxt, gbuf, gbuf_len);
|
||||
DPSnewpath(ctxt);
|
||||
}
|
||||
|
||||
#undef GBUF_SIZE
|
||||
}
|
||||
|
||||
|
|
|
@ -75,8 +75,7 @@
|
|||
{
|
||||
NSRect aRect;
|
||||
|
||||
// FIXME: We ignore the proposedLineFragment
|
||||
aRect.origin = position;
|
||||
aRect.origin = [self cellBaselineOffset];
|
||||
aRect.size = [self cellSize];
|
||||
return aRect;
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue