diff --git a/ChangeLog b/ChangeLog index 26fe61430..6b28acfb3 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,26 @@ +2003-01-31 21:59 Alexander Malmberg + + * 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 * Source/GNUmakefile (NSMenuItem.m_FILE_FLAGS): Use -Wno-protocol diff --git a/Headers/gnustep/gui/GSLayoutManager.h b/Headers/gnustep/gui/GSLayoutManager.h index f2e7a0456..3e7ffa7b4 100644 --- a/Headers/gnustep/gui/GSLayoutManager.h +++ b/Headers/gnustep/gui/GSLayoutManager.h @@ -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; diff --git a/Headers/gnustep/gui/GSLayoutManager_internal.h b/Headers/gnustep/gui/GSLayoutManager_internal.h index ba9709365..f30a91165 100644 --- a/Headers/gnustep/gui/GSLayoutManager_internal.h +++ b/Headers/gnustep/gui/GSLayoutManager_internal.h @@ -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 diff --git a/Headers/gnustep/gui/NSFont.h b/Headers/gnustep/gui/NSFont.h index 3dfee1817..f25599b5a 100644 --- a/Headers/gnustep/gui/NSFont.h +++ b/Headers/gnustep/gui/NSFont.h @@ -41,6 +41,7 @@ typedef unsigned int NSGlyph; enum { NSControlGlyph = 0x00ffffff, + GSAttachmentGlyph = 0x00fffffe, NSNullGlyph = 0x0 }; diff --git a/Headers/gnustep/gui/NSTextAttachment.h b/Headers/gnustep/gui/NSTextAttachment.h index c75ace639..802421dec 100644 --- a/Headers/gnustep/gui/NSTextAttachment.h +++ b/Headers/gnustep/gui/NSTextAttachment.h @@ -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 diff --git a/Source/GSHorizontalTypesetter.m b/Source/GSHorizontalTypesetter.m index 090336c79..b4d74abff 100644 --- a/Source/GSHorizontalTypesetter.m +++ b/Source/GSHorizontalTypesetter.m @@ -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 diff --git a/Source/GSLayoutManager.m b/Source/GSLayoutManager.m index a8865b7d7..53bbd7f15 100644 --- a/Source/GSLayoutManager.m +++ b/Source/GSLayoutManager.m @@ -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 diff --git a/Source/NSLayoutManager.m b/Source/NSLayoutManager.m index 241313a59..49993a890 100644 --- a/Source/NSLayoutManager.m +++ b/Source/NSLayoutManager.m @@ -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 *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 } diff --git a/Source/NSTextAttachment.m b/Source/NSTextAttachment.m index 6051efb8e..19bdaebe9 100644 --- a/Source/NSTextAttachment.m +++ b/Source/NSTextAttachment.m @@ -75,8 +75,7 @@ { NSRect aRect; - // FIXME: We ignore the proposedLineFragment - aRect.origin = position; + aRect.origin = [self cellBaselineOffset]; aRect.size = [self cellSize]; return aRect; }