diff --git a/ChangeLog b/ChangeLog index ee9db0272..c1707d41a 100644 --- a/ChangeLog +++ b/ChangeLog @@ -1,3 +1,10 @@ +2003-02-18 18:03 Alexander Malmberg + + * Headers/gnustep/gui/GSLayoutManager_internal.h, + Source/GSLayoutManager.m, Source/NSLayoutManager.m: Implement + handling of soft-invalidated line frags. Soft-invalidate layout + information for glyphs after an edited range in NSLayoutManager. + 2003-02-18 01:23 Alexander Malmberg * Headers/gnustep/gui/GSLayoutManager_internal.h, diff --git a/Headers/gnustep/gui/GSLayoutManager_internal.h b/Headers/gnustep/gui/GSLayoutManager_internal.h index c5ae60ca7..11270675b 100644 --- a/Headers/gnustep/gui/GSLayoutManager_internal.h +++ b/Headers/gnustep/gui/GSLayoutManager_internal.h @@ -184,8 +184,14 @@ typedef struct GSLayoutManager_textcontainer_s */ BOOL was_invalidated; + /* + The array actually has num_soft+num_linefrags entries. Only the + num_linefrags first are significant, the rest hold soft invalidated + layout information. + */ linefrag_t *linefrags; int num_linefrags; + int num_soft; } textcontainer_t; diff --git a/Source/GSLayoutManager.m b/Source/GSLayoutManager.m index 46d08dc3a..b377fa9f5 100644 --- a/Source/GSLayoutManager.m +++ b/Source/GSLayoutManager.m @@ -1455,7 +1455,7 @@ places where we switch. tc->started = tc->complete = NO; if (tc->linefrags) { - for (j = 0, lf = tc->linefrags; j < tc->num_linefrags; j++, lf++) + for (j = 0, lf = tc->linefrags; j < tc->num_linefrags + tc->num_soft; j++, lf++) { if (lf->points) free(lf->points); @@ -1466,7 +1466,7 @@ places where we switch. free(tc->linefrags); } tc->linefrags = NULL; - tc->num_linefrags = 0; + tc->num_linefrags = tc->num_soft = 0; tc->pos = tc->length = 0; tc->was_invalidated = YES; } @@ -1524,6 +1524,29 @@ places where we switch. break; } tc->complete = YES; + if (tc->num_soft) + { + /* + If there is any soft invalidated layout information left, remove + it. + */ + int k; + linefrag_t *lf; + for (k = tc->num_linefrags, lf = tc->linefrags + k; k < tc->num_linefrags + tc->num_soft; k++, lf++) + { + if (lf->points) + { + free(lf->points); + lf->points = NULL; + } + if (lf->attachments) + { + free(lf->attachments); + lf->attachments = NULL; + } + } + tc->num_soft = 0; + } if (delegate_responds) { [_delegate layoutManager: self @@ -1592,77 +1615,6 @@ by calling this incorrectly. actualCharacterRange: (NSRange *)actualRange { [self _invalidateLayoutFromContainer: 0]; -#if 0 - /* TODO: need to bring this up to date and fix it */ - unsigned int from_glyph = glyphs->glyph_length; - int i, j; - textcontainer_t *tc; - linefrag_t *lf; - - if (from_glyph) - { - unsigned int chi = [self characterIndexForGlyphAtIndex: from_glyph - 1]; - if (chi > aRange.location) - { - from_glyph = [self glyphRangeForCharacterRange: NSMakeRange(aRange.location, 1) - actualCharacterRange: actualRange].location; - } - else - { - if (actualRange) - actualRange->location = chi; - } - } - else - { - if (actualRange) - actualRange->location = 0; - } - if (actualRange) - actualRange->length = [_textStorage length] - actualRange->location; - - if (layout_glyph <= from_glyph) - return; - - for (i = 0, tc = textcontainers; i < num_textcontainers; i++, tc++) - if (tc->pos + tc->length > from_glyph) - break; - if (i == num_textcontainers) - { - NSLog(@"%s: can't find text container for glyph (internal error)", __PRETTY_FUNCTION__); - return; - } - j = i; - - for (i = 0, lf = tc->linefrags; i < tc->num_linefrags; i++, lf++) - if (lf->pos + lf->length > from_glyph) - break; - if (i == tc->num_linefrags) - { - NSLog(@"%s: can't find line frag rect for glyph (internal error)", __PRETTY_FUNCTION__); - return; - } - - if (i) - { - int idx = i; - for (; i < tc->num_linefrags; i++, lf++) - { - if (lf->points) - free(lf->points); - if (lf->attachments) - free(lf->attachments); - } - tc->num_linefrags = idx; - tc->length = tc->linefrags[idx - 1].pos + tc->linefrags[idx - 1].length - tc->pos; - tc->complete = NO; - [self _invalidateLayoutFromContainer: j + 1]; - } - else - { - [self _invalidateLayoutFromContainer: j]; - } -#endif } @@ -1780,6 +1732,7 @@ by calling this incorrectly. return; } + /* Make sure the given glyph range matches earlier layout. */ if (!tc->num_linefrags) { if (glyphRange.location != tc->pos) @@ -1789,9 +1742,6 @@ by calling this incorrectly. __PRETTY_FUNCTION__]; return; } - tc->linefrags = malloc(sizeof(linefrag_t)); - tc->num_linefrags = 1; - lf = tc->linefrags; } else { @@ -1803,10 +1753,66 @@ by calling this incorrectly. __PRETTY_FUNCTION__]; return; } + } + + if (!(tc->num_linefrags + tc->num_soft)) + { + tc->linefrags = malloc(sizeof(linefrag_t)); + tc->num_linefrags = 1; + lf = tc->linefrags; + } + else if (!tc->num_soft) + { tc->num_linefrags++; tc->linefrags = realloc(tc->linefrags, sizeof(linefrag_t) * tc->num_linefrags); lf = &tc->linefrags[tc->num_linefrags - 1]; } + else + { + int i; + for (i = tc->num_linefrags, lf = tc->linefrags + i; i < tc->num_linefrags + tc->num_soft; i++, lf++) + { + if (lf->pos >= NSMaxRange(glyphRange)) + break; + if (lf->points) + { + free(lf->points); + lf->points = NULL; + } + if (lf->attachments) + { + free(lf->attachments); + lf->attachments = NULL; + } + } + + if (i == tc->num_linefrags) + { + /* + If we should keep all soft frags, we need to enlarge the array + to fit the new line frag. + */ + tc->linefrags = realloc(tc->linefrags, sizeof(linefrag_t) * (tc->num_linefrags + tc->num_soft + 1)); + memmove(&tc->linefrags[tc->num_linefrags + 1], &tc->linefrags[tc->num_linefrags], tc->num_soft * sizeof(linefrag_t)); + } + else if (i > tc->num_linefrags + 1) + { + tc->num_soft -= i - tc->num_linefrags; + memmove(&tc->linefrags[tc->num_linefrags + 1], &tc->linefrags[i], tc->num_soft * sizeof(linefrag_t)); + } + else + { + /* + If i == tc->num_linefrags + 1, we're lucky and everything already + lines up, so no moving is necessary. + */ + tc->num_soft--; + } + + tc->num_linefrags++; + lf = &tc->linefrags[tc->num_linefrags - 1]; + } + memset(lf, 0, sizeof(linefrag_t)); lf->rect = fragmentRect; lf->used_rect = usedRect; diff --git a/Source/NSLayoutManager.m b/Source/NSLayoutManager.m index f613edd19..587574b82 100644 --- a/Source/NSLayoutManager.m +++ b/Source/NSLayoutManager.m @@ -1732,6 +1732,45 @@ TODO: not really clear what these should do } +-(void) _dumpLayout +{ + int i, j, k; + textcontainer_t *tc; + linefrag_t *lf; + linefrag_point_t *lp; + linefrag_attachment_t *la; + + for (i = 0, tc = textcontainers; i < num_textcontainers; i++, tc++) + { + printf("tc %2i, %5i+%5i\n",i,tc->pos,tc->length); + printf(" lfs: (%3i)\n", tc->num_linefrags); + for (j = 0, lf = tc->linefrags; j < tc->num_linefrags; j++, lf++) + { + printf(" %3i : %5i+%5i (%g %g)+(%g %g)\n", + j,lf->pos,lf->length, + lf->rect.origin.x,lf->rect.origin.y, + lf->rect.size.width,lf->rect.size.height); + for (k = 0, lp = lf->points; k < lf->num_points; k++, lp++) + printf(" p%3i : %5i+%5i\n",k,lp->pos,lp->length); + for (k = 0, la = lf->attachments; k < lf->num_attachments; k++, la++) + printf(" a%3i : %5i+%5i\n",k,la->pos,la->length); + } + printf(" softs: (%3i)\n", tc->num_soft); + for (; j < tc->num_linefrags + tc->num_soft; j++, lf++) + { + printf(" %3i : %5i+%5i (%g %g)+(%g %g)\n", + j,lf->pos,lf->length, + lf->rect.origin.x,lf->rect.origin.y, + lf->rect.size.width,lf->rect.size.height); + for (k = 0, lp = lf->points; k < lf->num_points; k++, lp++) + printf(" p%3i : %5i+%5i\n",k,lp->pos,lp->length); + for (k = 0, la = lf->attachments; k < lf->num_attachments; k++, la++) + printf(" a%3i : %5i+%5i\n",k,la->pos,la->length); + } + } +} + + /* We completely override this method and use the extra information we have about layout to do smarter invalidation. The comments at the beginning of @@ -1744,6 +1783,20 @@ this file describes this. invalidatedRange: (NSRange)invalidatedRange { NSRange r; + unsigned int original_last_glyph, new_last_glyph; + int glyph_delta; + +/* printf("\n*** invalidating\n"); + [self _dumpLayout];*/ + + /* + Using -glyphRangeForChara... here would be safer, but we must make + absolutely sure that we don't cause any glyph generation until the + invalidation is done. + + TODO: make sure last_glyph is set as expected + */ + original_last_glyph = layout_glyph; if (!(mask & NSTextStorageEditedCharacters)) lengthChange = 0; @@ -1752,12 +1805,29 @@ this file describes this. changeInLength: lengthChange actualCharacterRange: &r]; - if (r.location <= layout_char) + if (layout_char > r.location) { - unsigned int glyph_index; + layout_char += lengthChange; + if (layout_char < r.location) + layout_char = r.location; + } + + if (layout_char == [_textStorage length]) + new_last_glyph = [self numberOfGlyphs]; + else + new_last_glyph = [self glyphRangeForCharacterRange: NSMakeRange(layout_char, 1) + actualCharacterRange: NULL].location; + + glyph_delta = new_last_glyph - original_last_glyph; +/* printf("original=%i, new=%i, delta %i\n", + original_last_glyph,new_last_glyph,glyph_delta);*/ + + if (r.location < layout_char) + { + unsigned int glyph_index, last_glyph; textcontainer_t *tc; linefrag_t *lf; - int i, j; + int i, j, k; int new_num; NSRange char_range; @@ -1784,75 +1854,212 @@ this file describes this. actualCharacterRange: &char_range].location; } - /* glyph_index is the first index we should invalidate for. */ - for (j = 0, tc = textcontainers; j < num_textcontainers; j++, tc++) - if (tc->pos + tc->length >= glyph_index) + /* + For soft invalidation, we need to know where to stop hard-invalidating. + This will cause immediate glyph generation to fill the gaps the + invalidation caused. + */ + if (NSMaxRange(r) == [_textStorage length]) + { + last_glyph = [self numberOfGlyphs]; + } + else + { + last_glyph = + [self glyphRangeForCharacterRange: NSMakeRange(NSMaxRange(r),1) + actualCharacterRange: NULL].location; + } + last_glyph -= glyph_delta; + + /* glyph_index is the first index we should invalidate for. */ + for (j = 0, tc = textcontainers; j < num_textcontainers; j++, tc++) + if (tc->pos + tc->length >= glyph_index) + break; + + LINEFRAG_FOR_GLYPH(glyph_index); + + /* + We invalidate the entire line containing lf, and the entire + previous line. Thus, we scan backwards to find the first line frag + on the previous line. + */ + while (i > 0 && lf[-1].rect.origin.y == lf->rect.origin.y) + lf--, i--; + /* Now we have the first line frag on this line. */ + if (i > 0) + { + lf--, i--; + } + else + { + /* + The previous line isn't in this text container, so we move + to the previous text container. + */ + if (j > 0) + { + j--; + tc--; + i = tc->num_linefrags - 1; + lf = tc->linefrags + i; + } + } + /* Last line frag on previous line. */ + while (i > 0 && lf[-1].rect.origin.y == lf->rect.origin.y) + lf--, i--; + /* First line frag on previous line. */ + + /* Invalidate all line frags that intersect the invalidated range. */ + new_num = i; + while (1) + { + for (; i < tc->num_linefrags + tc->num_soft; i++, lf++) + { + /* + Since we must invalidate whole lines, we can only stop if + the line frag is beyond the invalidated range, and the line + frag is the first line frag in a line. + */ + if (lf->pos >= last_glyph && + (!i || lf[-1].rect.origin.y != lf->rect.origin.y)) + { + break; + } + if (lf->points) + { + free(lf->points); + lf->points = NULL; + } + if (lf->attachments) + { + free(lf->attachments); + lf->attachments = NULL; + } + } + if (i < tc->num_linefrags + tc->num_soft) + break; + tc->num_linefrags = new_num; + tc->num_soft = 0; + tc->was_invalidated = YES; + tc->complete = NO; + if (new_num) + { + tc->length = tc->linefrags[new_num-1].pos + tc->linefrags[new_num-1].length - tc->pos; + } + else + { + tc->pos = tc->length = 0; + tc->started = NO; + } + + j++, tc++; + if (j == num_textcontainers) break; - LINEFRAG_FOR_GLYPH(glyph_index); + new_num = 0; + i = 0; + lf = tc->linefrags; + } - /* - We invalidate the entire line containing lf, and the entire - previous line. Thus, we scan backwards to find the first line frag - on the previous line. - */ - while (i > 0 && lf[-1].rect.origin.y == lf->rect.origin.y) - lf--, i--; - /* Now we have the first line frag on this line. */ - if (i > 0) - { - lf--, i--; - } - else - { - /* - The previous line isn't in this text container, so we move - to the previous text container. - */ - if (j > 0) - { - j--; - tc--; - i = tc->num_linefrags - 1; - lf = tc->linefrags + i; - } - } - /* Last line frag on previous line. */ - while (i > 0 && lf[-1].rect.origin.y == lf->rect.origin.y) - lf--, i--; - /* First line frag on previous line. */ + if (j == num_textcontainers) + goto no_soft_invalidation; - new_num = i; - for (; i < tc->num_linefrags; i++) - { - if (lf->points) - { - free(lf->points); - lf->points = NULL; - } - if (lf->attachments) - { - free(lf->attachments); - lf->attachments = NULL; - } - } - tc->num_linefrags = new_num; - tc->was_invalidated = YES; - tc->complete = NO; - if (new_num) - { - tc->length = tc->linefrags[new_num-1].pos + tc->linefrags[new_num-1].length - tc->pos; - } - else - { - tc->pos = tc->length = 0; - tc->started = NO; - } + if (new_num != i) + { + /* + There's a gap between the last valid line frag and the first + soft line frag. Compact the linefrags. + */ + memmove(tc->linefrags + new_num, lf, sizeof(linefrag_t) * (tc->num_linefrags + tc->num_soft - i)); + tc->num_linefrags -= i - new_num; + i = new_num; + lf = tc->linefrags + i; + } + tc->num_soft += tc->num_linefrags - new_num; + tc->num_linefrags = new_num; + tc->was_invalidated = YES; + tc->complete = NO; + if (new_num) + { + tc->length = tc->linefrags[new_num-1].pos + tc->linefrags[new_num-1].length - tc->pos; + } + else + { + tc->pos = tc->length = 0; + tc->started = NO; + } - /* This works even if j+1>=num_textcontainers, and it sets - layout_char and layout_glyph for us. */ - [self _invalidateLayoutFromContainer: j + 1]; + /* + Soft invalidate all remaining layout. Update their glyph positions + and set the soft-invalidate markers in the text containers. + */ + while (1) + { + for (; i < tc->num_linefrags + tc->num_soft; i++, lf++) + { + lf->pos += glyph_delta; + for (k = 0; k < lf->num_points; k++) + lf->points[k].pos += glyph_delta; + for (k = 0; k < lf->num_attachments; k++) + lf->attachments[k].pos += glyph_delta; + } + + j++, tc++; + if (j == num_textcontainers) + break; + i = 0; + lf = tc->linefrags; + } + +no_soft_invalidation: + /* Set layout_glyph and layout_char. */ + for (i = num_textcontainers - 1, tc = textcontainers + i; i >= 0; i--, tc--) + { + if (tc->started) + { + layout_glyph = tc->pos + tc->length; + if (layout_glyph == glyphs->glyph_length) + layout_char = glyphs->char_length; + else + layout_char = [self characterIndexForGlyphAtIndex: layout_glyph]; /* TODO? */ + break; + } + } + if (i < 0) + layout_glyph = layout_char = 0; } + else + { + int i, j; + linefrag_t *lf; + textcontainer_t *tc; + /* + TODO: could handle this better, but it should be a rare case, + handling it efficiently is tricky. + + For now, we simply clear out all soft invalidation information. + */ + for (i = 0, tc = textcontainers; i < num_textcontainers; i++, tc++) + { + for (j = 0, lf = tc->linefrags + j; j < tc->num_soft; j++, lf++) + { + if (lf->points) + { + free(lf->points); + lf->points = NULL; + } + if (lf->attachments) + { + free(lf->attachments); + lf->attachments = NULL; + } + } + tc->num_soft = 0; + } + } + +/* [self _dumpLayout]; + printf("*** done\n");*/ [self _didInvalidateLayout];