/[gnustep]/gnustep/core/gui/Source/GSLayoutManager.m
ViewVC logotype

Contents of /gnustep/core/gui/Source/GSLayoutManager.m

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1.36 - (show annotations) (download)
Thu Jul 31 23:52:09 2003 UTC (20 years, 9 months ago) by ayers
Branch: MAIN
CVS Tags: gui-0_8_9
Changes since 1.35: +2 -2 lines
        Header reorganization - Please refer to ChangeLog

1 /*
2 GSLayoutManager.m
3
4 Copyright (C) 2002, 2003 Free Software Foundation, Inc.
5
6 Author: Alexander Malmberg <alexander@malmberg.org>
7 Date: November 2002 - February 2003
8
9 This file is part of the GNUstep GUI Library.
10
11 This library is free software; you can redistribute it and/or
12 modify it under the terms of the GNU Library General Public
13 License as published by the Free Software Foundation; either
14 version 2 of the License, or (at your option) any later version.
15
16 This library is distributed in the hope that it will be useful,
17 but WITHOUT ANY WARRANTY; without even the implied warranty of
18 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 Library General Public License for more details.
20
21 You should have received a copy of the GNU Library General Public
22 License along with this library; see the file COPYING.LIB.
23 If not, write to the Free Software Foundation,
24 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
25 */
26
27 #include <Foundation/NSCharacterSet.h>
28 #include <Foundation/NSDebug.h>
29 #include <Foundation/NSException.h>
30 #include <Foundation/NSValue.h>
31
32 #include "AppKit/NSTextStorage.h"
33 #include "AppKit/NSTextContainer.h"
34
35 /* just for NSAttachmentCharacter */
36 #include "AppKit/NSTextAttachment.h"
37
38 #include "GNUstepGUI/GSTypesetter.h"
39 #include "GNUstepGUI/GSLayoutManager_internal.h"
40
41
42
43 /* TODO: is using rand() here ok? */
44 static int random_level(void)
45 {
46 int i;
47 for (i = 0; i < SKIP_LIST_DEPTH - 2; i++)
48 if ((rand() % SKIP_LIST_LEVEL_PROBABILITY) != 0)
49 break;
50 return i;
51 }
52
53
54 /*
55 Private method used internally by GSLayoutManager for sanity checking.
56 */
57 @interface NSTextStorage (GSLayoutManager_sanity_checking)
58 -(unsigned int) _editCount;
59 @end
60 @implementation NSTextStorage (GSLayoutManager_sanity_checking)
61 -(unsigned int) _editCount;
62 {
63 return _editCount;
64 }
65 @end
66
67
68 /***** Glyph handling *****/
69
70 @implementation GSLayoutManager (glyphs_helpers)
71
72
73 -(void) _run_cache_attributes: (glyph_run_t *)r : (NSDictionary *)attributes
74 {
75 /* set up attributes for this run */
76 NSNumber *n;
77
78 r->explicit_kern = !![attributes objectForKey: NSKernAttributeName];
79
80 n = [attributes objectForKey: NSLigatureAttributeName];
81 if (n)
82 r->ligature = [n intValue];
83 else
84 r->ligature = 1;
85
86 r->font = [typesetter fontForCharactersWithAttributes: attributes];
87 /* TODO: it might be useful to change this slightly:
88 Returning a nil font from -fontForCharactersWithAttributes: causes those
89 characters to not be displayed (ie. no glyphs are generated).
90
91 How would glyph<->char mapping be handled? Map the entire run to one
92 NSNullGlyph?
93 */
94 if (!r->font)
95 r->font = [NSFont userFontOfSize: 0];
96 r->font = [self substituteFontForFont: r->font];
97 r->font = [r->font retain];
98 }
99
100 -(void ) _run_free_attributes: (glyph_run_t *)r
101 {
102 [r->font release];
103 }
104
105 -(void) _run_copy_attributes: (glyph_run_t *)dst : (const glyph_run_t *)src
106 {
107 dst->font = [src->font retain];
108 dst->ligature = src->ligature;
109 dst->explicit_kern = src->explicit_kern;
110 }
111
112
113 -(void) _freeGlyphs
114 {
115 glyph_run_t *cur, *next;
116 glyph_run_head_t *h;
117
118 if (!glyphs)
119 return;
120
121 h = glyphs;
122 h += SKIP_LIST_DEPTH - 1;
123
124 for (cur = (glyph_run_t *)h->next; cur; cur = next)
125 {
126 next = (glyph_run_t *)cur->head.next;
127 if (cur->glyphs)
128 free(cur->glyphs);
129 [self _run_free_attributes: cur];
130 h = &cur->head;
131 h -= cur->level;
132 free(h);
133 }
134
135 free(glyphs);
136 glyphs = NULL;
137 }
138
139 -(void) _initGlyphs
140 {
141 int i, size;
142 glyph_run_head_t *h;
143
144 size = sizeof(glyph_run_head_t) * (SKIP_LIST_DEPTH - 1) + sizeof(glyph_run_t);
145 glyphs = malloc(size);
146 memset(glyphs, 0, size);
147 for (h = glyphs, i = SKIP_LIST_DEPTH; i; i--, h++)
148 h->complete = 1;
149 }
150
151 -(void) _glyphDumpRuns
152 {
153 printf("--- dumping runs\n");
154 {
155 glyph_run_t *h;
156 unsigned int cpos = 0;
157 h = (glyph_run_t *)(glyphs + SKIP_LIST_DEPTH - 1)->next;
158 for (; h; h = (glyph_run_t *)h->head.next)
159 {
160 printf("%08x %i chars, %i glyphs, %i complete, prev %08x next %08x\n",
161 (int)h, h->head.char_length, h->head.glyph_length, h->head.complete,
162 (int)h->prev, (int)h->head.next);
163 printf(" level %i, continued %i\n", h->level, h->continued);
164 if (h->head.complete)
165 {
166 unsigned int i;
167 printf("glyphs:\n");
168 for (i = 0;i < h->head.glyph_length;i++)
169 printf("%5i %04x u%04x ",
170 h->glyphs[i].char_offset,h->glyphs[i].g,
171 [[_textStorage string] characterAtIndex: cpos+h->glyphs[i].char_offset]);
172 printf("\n");
173 }
174 cpos += h->head.char_length;
175 }
176 }
177 printf("- structure\n");
178 {
179 glyph_run_head_t *h, *g;
180 int i;
181
182 printf(" head: ");
183 for (i = 0, h = glyphs + SKIP_LIST_DEPTH - 1; i < SKIP_LIST_DEPTH; i++, h--)
184 printf("%8x %i %3i %3i|", (int)h->next, h->complete, h->char_length, h->glyph_length);
185 printf("\n");
186 h = (glyphs + SKIP_LIST_DEPTH - 1)->next;
187 for (; h; h = h->next)
188 {
189 printf("%8x: ", (int)h);
190 for (g = h, i = ((glyph_run_t *)h)->level; i >= 0; i--, g--)
191 printf("%8x %i %3i %3i|", (int)g->next, g->complete, g->char_length, g->glyph_length);
192 printf("\n");
193 }
194 }
195 printf("--- done\n");
196 fflush(stdout);
197 }
198
199
200 -(void) _sanityChecks
201 {
202 glyph_run_t *g;
203
204 g = (glyph_run_t *)&glyphs[SKIP_LIST_DEPTH - 1];
205 while (g->head.next)
206 {
207 NSAssert((glyph_run_t *)((glyph_run_t *)g->head.next)->prev == g,
208 @"glyph structure corrupted: g->next->prev!=g");
209 g = (glyph_run_t *)g->head.next;
210 }
211 }
212
213
214 -(glyph_run_t *)run_for_glyph_index: (unsigned int)glyphIndex
215 : (unsigned int *)glyph_pos
216 : (unsigned int *)char_pos
217 {
218 int level;
219 glyph_run_head_t *h;
220 int pos, cpos;
221
222 if (glyphs->glyph_length <= glyphIndex)
223 return NULL;
224
225 if (cached_run)
226 {
227 if (glyphIndex >= cached_pos &&
228 glyphIndex < cached_pos + cached_run->head.glyph_length)
229 {
230 if (glyph_pos)
231 *glyph_pos = cached_pos;
232 if (char_pos)
233 *char_pos = cached_cpos;
234 return cached_run;
235 }
236 }
237
238 pos = cpos = 0;
239 level = SKIP_LIST_DEPTH;
240 h = glyphs;
241 while (1)
242 {
243 if (!h->complete)
244 {
245 h++;
246 level--;
247 if (!level)
248 return NULL;
249 continue;
250 }
251 if (glyphIndex >= pos + h->glyph_length)
252 {
253 pos += h->glyph_length;
254 cpos += h->char_length;
255 h = h->next;
256 if (!h)
257 return NULL;
258 continue;
259 }
260 if (level > 1)
261 {
262 h++;
263 level--;
264 continue;
265 }
266
267 *glyph_pos = pos;
268 if (char_pos)
269 *char_pos = cpos;
270
271 cached_run = (glyph_run_t *)h;
272 cached_pos = pos;
273 cached_cpos = cpos;
274
275 return (glyph_run_t *)h;
276 }
277 }
278
279 static glyph_run_t *run_for_character_index(unsigned int charIndex,
280 glyph_run_head_t *glyphs, unsigned int *glyph_pos,
281 unsigned int *char_pos)
282 {
283 int level;
284 glyph_run_head_t *h;
285 int pos, cpos;
286
287 if (glyphs->char_length <= charIndex)
288 return NULL;
289
290 pos = cpos = 0;
291 level = SKIP_LIST_DEPTH;
292 h = glyphs;
293 while (1)
294 {
295 if (!h->complete)
296 {
297 h++;
298 level--;
299 if (!level)
300 return NULL;
301 continue;
302 }
303 if (charIndex >= cpos + h->char_length)
304 {
305 pos += h->glyph_length;
306 cpos += h->char_length;
307 h = h->next;
308 if (!h)
309 return NULL;
310 continue;
311 }
312 if (level > 1)
313 {
314 h++;
315 level--;
316 continue;
317 }
318
319 *glyph_pos = pos;
320 if (char_pos)
321 *char_pos = cpos;
322 return (glyph_run_t *)h;
323 }
324 }
325
326
327 /* Recalculates char_length, glyph_length, and complete for a
328 glyph_run_head_t. All "children" of this head must have valid values. */
329 static void run_fix_head(glyph_run_head_t *h)
330 {
331 glyph_run_head_t *h2, *next;
332 next = h->next;
333 if (next)
334 next++;
335 h2 = h + 1;
336 h->complete = 1;
337 h->glyph_length = 0;
338 h->char_length = 0;
339 while (h2 != next)
340 {
341 if (h->complete)
342 h->glyph_length += h2->glyph_length;
343 h->char_length += h2->char_length;
344 if (!h2->complete)
345 h->complete = 0;
346 h2 = h2->next;
347 }
348 }
349
350 static glyph_run_t *run_insert(glyph_run_head_t **context)
351 {
352 glyph_run_head_t *h;
353 glyph_run_t *r;
354 int level;
355 int i;
356
357 level = random_level();
358 h = malloc(sizeof(glyph_run_head_t) * level + sizeof(glyph_run_t));
359 memset(h, 0, sizeof(glyph_run_head_t) * level + sizeof(glyph_run_t));
360
361 for (i = level; i >= 0; i--)
362 {
363 h->next = context[i]->next;
364 context[i]->next = h;
365 h++;
366 }
367 h--;
368
369 r = (glyph_run_t *)h;
370 r->level = level;
371 r->prev = context[0];
372 return r;
373 }
374
375
376 -(void) _generateRunsToCharacter: (unsigned int)last
377 {
378 glyph_run_head_t *context[SKIP_LIST_DEPTH];
379 int positions[SKIP_LIST_DEPTH];
380 glyph_run_head_t *h;
381 unsigned int pos;
382 unsigned int length;
383 int level;
384
385
386 length = [_textStorage length];
387 if (last >= length)
388 last = length - 1;
389
390 h = glyphs;
391 pos = 0;
392 if (h->char_length > last)
393 return;
394
395 /* We haven't created any run for that character. Find the last run. */
396 for (level = SKIP_LIST_DEPTH; level; level--)
397 {
398 while (h->next) pos += h->char_length, h = h->next;
399 context[level - 1] = h;
400 positions[level - 1] = pos;
401 h++;
402 }
403 h--;
404 pos += h->char_length;
405
406 /* Create runs and add them to the skip list until we're past our
407 target. */
408 while (pos <= last)
409 {
410 NSRange maxRange;
411 NSRange curRange;
412 NSDictionary *attributes;
413
414 glyph_run_head_t *new_head;
415 glyph_run_t *new;
416 int new_level;
417
418 int i;
419
420 maxRange = NSMakeRange(pos, length - pos);
421 if (pos > 0)
422 {
423 maxRange.location--;
424 maxRange.length++;
425 }
426
427 attributes = [_textStorage attributesAtIndex: pos
428 longestEffectiveRange: &curRange
429 inRange: maxRange];
430
431 /*
432 Optimize run structure by merging with the previous run under
433 some circumstances. See the comments in
434 -invalidateGlyphsForCharacterRange:changeInLength:actualCharacterRange:
435 for more information.
436 */
437 if (curRange.location < pos && context[0]->char_length &&
438 context[0]->char_length < 16)
439 {
440 curRange.length -= pos - curRange.location;
441 curRange.location = pos;
442 new = (glyph_run_t *)context[0];
443 if (new->head.complete)
444 {
445 free(new->glyphs);
446 new->glyphs = NULL;
447 new->head.glyph_length = 0;
448 new->head.complete = 0;
449 }
450 new->head.char_length += curRange.length;
451 for (i = 1; i < SKIP_LIST_DEPTH; i++)
452 {
453 run_fix_head(context[i]);
454 }
455 pos = NSMaxRange(curRange);
456 continue;
457 }
458
459 if (curRange.location < pos)
460 {
461 curRange.length -= pos - curRange.location;
462 curRange.location = pos;
463 }
464
465 /*
466 TODO: this shouldn't really be necessary if all searches inside runs
467 were binary. but for now, it helps performance, and it keeps things
468 more balanced when there are long runs of text.
469 */
470 if (curRange.length > MAX_RUN_LENGTH)
471 {
472 unsigned int ch = curRange.location + MAX_RUN_LENGTH;
473 ch = [self _findSafeBreakMovingForwardFrom: ch];
474 if (ch < NSMaxRange(curRange))
475 curRange.length = ch - curRange.location;
476 }
477
478 new_level = random_level();
479
480 /* Since we'll be creating these in order, we can be smart about
481 picking new levels. */
482 {
483 int i;
484 glyph_num_end_runs++;
485 for (i=0; i < SKIP_LIST_DEPTH - 2; i++)
486 if (glyph_num_end_runs & (1 << i))
487 break;
488 new_level = i;
489 }
490
491 new_head = malloc(sizeof(glyph_run_t) + sizeof(glyph_run_head_t) * new_level);
492 memset(new_head, 0, sizeof(glyph_run_t) + sizeof(glyph_run_head_t) * new_level);
493 new = (glyph_run_t *)(new_head + new_level);
494
495 new->level = new_level;
496 new->head.char_length = curRange.length;
497 new->prev = context[0];
498
499 [self _run_cache_attributes: new : attributes];
500
501 h = &new->head;
502 for (i = 0; i <= new_level; i++, h--)
503 {
504 h->char_length = new->head.char_length;
505 context[i]->next = h;
506 context[i] = h;
507 }
508 for (; i < SKIP_LIST_DEPTH; i++)
509 {
510 context[i]->char_length += new->head.char_length;
511 context[i]->complete = 0;
512 }
513
514 pos += new->head.char_length;
515 }
516
517 [self _sanityChecks];
518 }
519
520
521 /*
522 Returns number of valid glyphs under h after generating up to last (sortof,
523 not completely accurate).
524 */
525 -(unsigned int) _generateGlyphs_char_r: (unsigned int)last : (unsigned int)pos
526 : (int)level
527 : (glyph_run_head_t *)h : (glyph_run_head_t *)stop
528 : (BOOL *)all_complete
529 {
530 int total = 0, sub_total;
531 BOOL c;
532
533 *all_complete = YES;
534 while (h != stop && (pos <= last || *all_complete))
535 {
536 if (h->complete)
537 {
538 total += h->glyph_length;
539 pos += h->char_length;
540 h = h->next;
541 continue;
542 }
543
544 if (pos > last)
545 break;
546
547 if (level)
548 {
549 if (h->next)
550 sub_total = [self _generateGlyphs_char_r: last : pos : level - 1: h + 1: h->next + 1: &c];
551 else
552 sub_total = [self _generateGlyphs_char_r: last : pos : level - 1: h + 1: NULL : &c];
553 if (!c)
554 *all_complete = NO;
555 else
556 h->complete = 1;
557 h->glyph_length = sub_total;
558 total += sub_total;
559 }
560 else
561 {
562 [self _generateGlyphsForRun: (glyph_run_t *)h at: pos];
563 h->complete = 1;
564 total += h->glyph_length;
565 }
566 pos += h->char_length;
567 h = h->next;
568 }
569 if (h != stop)
570 *all_complete = NO;
571 return total;
572 }
573
574 -(void) _generateGlyphsUpToCharacter: (unsigned int)last
575 {
576 unsigned int length;
577 BOOL dummy;
578
579 /*
580 Trying to do anything here while the text storage has unprocessed edits
581 (ie. an edit count>0) breaks things badly, and in strange ways. Thus, we
582 detect this and raise an exception.
583 */
584 if ([_textStorage _editCount])
585 {
586 [NSException raise: NSGenericException
587 format: @"Glyph generation was triggered for a layout manager "
588 @"while the text storage it was attached to had "
589 @"unprocessed editing. This is not allowed. Glyph "
590 @"generation may be triggered only at points where "
591 @"calls to -beginEditing and -endEditing are "
592 @"balanced."];
593 }
594
595 if (!_textStorage)
596 return;
597 length = [_textStorage length];
598 if (!length)
599 return;
600 if (last >= length)
601 last = length - 1;
602
603 if (glyphs->char_length <= last)
604 [self _generateRunsToCharacter: last];
605
606 // [self _glyphDumpRuns];
607 if ([self _generateGlyphs_char_r: last : 0 : SKIP_LIST_DEPTH - 1: glyphs : NULL : &dummy])
608 /*[self _glyphDumpRuns]*/;
609 }
610
611 -(void) _generateGlyphsUpToGlyph: (unsigned int)last
612 {
613 unsigned int length;
614
615 if (!_textStorage)
616 return;
617 length = [_textStorage length];
618
619 /* OPT: this can be done __much__ more efficiently */
620 while (glyphs->glyph_length <= last && (glyphs->char_length < length || !glyphs->complete))
621 {
622 [self _generateGlyphsUpToCharacter: glyphs->char_length];
623 }
624 }
625
626
627 -(glyph_run_t *) _glyphForCharacter: (unsigned int)target
628 index: (unsigned int *)rindex
629 positions: (unsigned int *)rpos : (unsigned int *)rcpos
630 {
631 glyph_run_t *r;
632 unsigned int pos, cpos;
633 int lo, hi, mid, i;
634
635 r = run_for_character_index(target, glyphs, &pos, &cpos);
636 if (!r)
637 return NULL;
638
639 target -= cpos;
640
641 lo = 0;
642 hi = r->head.glyph_length - 1;
643 while (lo < hi)
644 {
645 mid = (lo + hi) / 2;
646 if (r->glyphs[mid].char_offset > target)
647 hi = mid - 1;
648 else if (r->glyphs[mid].char_offset < target)
649 lo = mid + 1;
650 else
651 hi = lo = mid;
652 }
653 i = lo;
654 while (r->glyphs[i].char_offset > target)
655 i--;
656 while (i > 0 && r->glyphs[i - 1].char_offset == r->glyphs[i].char_offset)
657 i--;
658
659 *rindex = i;
660 *rpos = pos;
661 *rcpos = cpos;
662 return r;
663 }
664
665 @end
666
667
668 @implementation GSLayoutManager (glyphs)
669
670 - (unsigned int) numberOfGlyphs
671 {
672 [self _generateGlyphsUpToCharacter: -1];
673 return glyphs->glyph_length;
674 }
675
676
677 - (NSGlyph) glyphAtIndex: (unsigned int)glyphIndex
678 {
679 BOOL valid;
680 NSGlyph g;
681 g = [self glyphAtIndex: glyphIndex isValidIndex: &valid];
682 if (valid)
683 return g;
684 [NSException raise: NSRangeException
685 format: @"%s glyph index out of range", __PRETTY_FUNCTION__];
686 return 0;
687 }
688
689
690 - (NSGlyph) glyphAtIndex: (unsigned int)glyphIndex
691 isValidIndex: (BOOL *)isValidIndex
692 {
693 glyph_run_t *r;
694 int pos;
695
696 *isValidIndex = NO;
697
698 /* glyph '-1' is returned in other places as an "invalid" marker; this
699 way, we can say that it isn't valid without building all glyphs */
700 /* TODO: check if this is really safe or smart. if it isn't, some other
701 methods will need to be changed so they can return "no glyph index" in
702 some other way. */
703 if (glyphIndex == (unsigned int)-1)
704 return 0;
705
706 if (glyphs->glyph_length <= glyphIndex)
707 {
708 [self _generateGlyphsUpToGlyph: glyphIndex];
709 if (glyphs->glyph_length <= glyphIndex)
710 return 0;
711 }
712
713 r = run_for_glyph_index(glyphIndex, glyphs, &pos, NULL);
714 if (!r) /* shouldn't happen */
715 return 0;
716
717 glyphIndex -= pos;
718 *isValidIndex = YES;
719 return r->glyphs[glyphIndex].g;
720 }
721
722
723 - (unsigned int) getGlyphs: (NSGlyph *)glyphArray
724 range: (NSRange)glyphRange
725 {
726 glyph_run_t *r;
727 NSGlyph *g;
728 unsigned int pos;
729 unsigned int num;
730 unsigned int i, j, k;
731
732 if (glyphRange.length == 0)
733 {
734 return 0;
735 }
736
737 pos = NSMaxRange(glyphRange) - 1;
738 if (glyphs->glyph_length <= pos)
739 {
740 [self _generateGlyphsUpToGlyph: pos];
741 if (glyphs->glyph_length <= pos)
742 {
743 [NSException raise: NSRangeException
744 format: @"%s glyph range out of range", __PRETTY_FUNCTION__];
745 return 0;
746 }
747 }
748
749 r = run_for_glyph_index(glyphRange.location, glyphs, &pos, NULL);
750 if (!r)
751 { /* shouldn't happen */
752 [NSException raise: NSRangeException
753 format: @"%s glyph range out of range", __PRETTY_FUNCTION__];
754 return 0;
755 }
756
757 g = glyphArray;
758 num = 0;
759
760 while (1)
761 {
762 if (pos < glyphRange.location)
763 j = glyphRange.location - pos;
764 else
765 j = 0;
766
767 k = NSMaxRange(glyphRange) - pos;
768 if (k > r->head.glyph_length)
769 k = r->head.glyph_length;
770 if (k <= j)
771 break;
772
773 /* TODO? only "displayed" glyphs */
774 for (i = j; i < k; i++)
775 {
776 *g++=r->glyphs[i].g;
777 num++;
778 }
779
780 pos += r->head.glyph_length;
781 r = (glyph_run_t *)r->head.next;
782 if (!r)
783 break;
784 }
785
786 return num;
787 }
788
789 - (unsigned int) characterIndexForGlyphAtIndex: (unsigned int)glyphIndex
790 {
791 glyph_run_t *r;
792 int pos, cpos;
793
794 if (glyphs->glyph_length <= glyphIndex)
795 {
796 [self _generateGlyphsUpToGlyph: glyphIndex];
797 if (glyphs->glyph_length <= glyphIndex)
798 {
799 [NSException raise: NSRangeException
800 format: @"%s glyph index out of range", __PRETTY_FUNCTION__];
801 return 0;
802 }
803 }
804
805 r = run_for_glyph_index(glyphIndex, glyphs, &pos, &cpos);
806 if (!r)
807 {
808 [NSException raise: NSRangeException
809 format: @"%s glyph index out of range", __PRETTY_FUNCTION__];
810 return 0;
811 }
812
813 return cpos + r->glyphs[glyphIndex - pos].char_offset;
814 }
815
816
817 - (NSRange) characterRangeForGlyphRange: (NSRange)glyphRange
818 actualGlyphRange: (NSRange *)actualGlyphRange
819 {
820 glyph_run_t *r;
821 NSRange real_range, char_range;
822 unsigned int cpos, pos;
823 unsigned j;
824
825 if (NSMaxRange(glyphRange) == 0)
826 {
827 if (actualGlyphRange)
828 *actualGlyphRange = glyphRange;
829 return NSMakeRange(0, 0);
830 }
831
832 pos = NSMaxRange(glyphRange) - 1;
833 if (glyphs->glyph_length <= pos)
834 {
835 [self _generateGlyphsUpToGlyph: pos];
836 if (glyphs->glyph_length <= pos)
837 {
838 [NSException raise: NSRangeException
839 format: @"%s glyph range out of range", __PRETTY_FUNCTION__];
840 return NSMakeRange(0, 0);
841 }
842 }
843
844 r = run_for_glyph_index(glyphRange.location, glyphs, &pos, &cpos);
845 if (!r)
846 {
847 [NSException raise: NSRangeException
848 format: @"%s glyph range out of range", __PRETTY_FUNCTION__];
849 return NSMakeRange(0, 0);
850 }
851
852 j = cpos + r->glyphs[glyphRange.location - pos].char_offset;
853 char_range.location = j;
854
855 /* scan backwards to find the real first glyph */
856 {
857 glyph_run_t *r2;
858 unsigned int adj, cadj;
859 int i;
860
861 i = glyphRange.location - pos;
862 r2 = r;
863 adj = pos;
864 cadj = cpos;
865 while (r2->glyphs[i].char_offset + cadj == j)
866 {
867 i--;
868 while (i < 0)
869 {
870 if (!r2->prev)
871 break;
872 r2 = (glyph_run_t *)r2->prev;
873 i = r2->head.glyph_length - 1;
874 adj -= r2->head.glyph_length;
875 cadj -= r2->head.char_length;
876 }
877 if (i < 0)
878 break;
879 }
880 real_range.location = i + 1 + adj;
881 }
882
883 /* the range is likely short, so we can do better then a completely new
884 search */
885 r = run_for_glyph_index(glyphRange.location + glyphRange.length - 1,
886 glyphs, &pos, &cpos);
887 if (!r)
888 {
889 [NSException raise: NSRangeException
890 format: @"%s glyph range out of range", __PRETTY_FUNCTION__];
891 return NSMakeRange(0, 0);
892 }
893
894 j = cpos + r->glyphs[glyphRange.location + glyphRange.length - 1 - pos].char_offset;
895
896 /* scan forwards to find the real last glyph */
897 {
898 glyph_run_t *r2;
899 unsigned int adj, cadj;
900 unsigned int last = 0;
901 unsigned int i;
902
903 i = glyphRange.location + glyphRange.length - 1 - pos;
904 r2 = r;
905 adj = pos;
906 cadj = cpos;
907 while (r2->glyphs[i].char_offset + cadj == j)
908 {
909 GLYPH_STEP_FORWARD(r2,i,adj,cadj)
910 if (i==r2->head.glyph_length)
911 {
912 last = cadj + r2->head.char_length;
913 goto found;
914 }
915 }
916 last = r2->glyphs[i].char_offset + cadj;
917 found:
918 real_range.length = i + adj - real_range.location;
919 char_range.length = last - char_range.location;
920 }
921
922 if (actualGlyphRange)
923 *actualGlyphRange = real_range;
924 return char_range;
925 }
926
927
928 - (NSRange) glyphRangeForCharacterRange: (NSRange)charRange
929 actualCharacterRange: (NSRange *)actualCharRange
930 {
931 NSRange char_range, glyph_range;
932 glyph_run_t *r;
933 unsigned int cpos, pos;
934 unsigned int i, target;
935
936 /* TODO: should this really be valid?
937
938 This is causing all kinds of problems when border glyph ranges are passed
939 to other functions. Better to keep the layout manager clean of all this and
940 let NSTextView deal with it.
941 */
942 #if 1
943 if (charRange.length == 0 && charRange.location == [[_textStorage string] length])
944 {
945 if (actualCharRange)
946 *actualCharRange = NSMakeRange([[_textStorage string] length], 0);
947 return NSMakeRange([self numberOfGlyphs], 0);
948 }
949 #endif
950 /* TODO: this case is also dubious, but it makes sense to return like this,
951 so it's mostly the caller's fault */
952 if (charRange.length == 0)
953 {
954 NSLog(@"Warning: %s called with zero-length range", __PRETTY_FUNCTION__);
955 if (actualCharRange)
956 *actualCharRange = NSMakeRange(0, 0);
957 return NSMakeRange(0, 0);
958 }
959
960 pos = NSMaxRange(charRange) - 1;
961 [self _generateGlyphsUpToCharacter: pos];
962 if (glyphs->char_length <= pos)
963 {
964 [NSException raise: NSRangeException
965 format: @"%s character range out of range", __PRETTY_FUNCTION__];
966 return NSMakeRange(0, 0);
967 }
968
969 target = charRange.location;
970 r = [self _glyphForCharacter: target
971 index: &i
972 positions: &pos : &cpos];
973 glyph_range.location = i + pos;
974 char_range.location = r->glyphs[i].char_offset + cpos;
975
976 target = NSMaxRange(charRange) - 1;
977 r = [self _glyphForCharacter: target
978 index: &i
979 positions: &pos : &cpos];
980
981 GLYPH_SCAN_FORWARD(r, i, pos, cpos, r->glyphs[i].char_offset + cpos <= target)
982
983 glyph_range.length = i + pos - glyph_range.location;
984 if (i == r->head.glyph_length)
985 char_range.length = glyphs->char_length - char_range.location;
986 else
987 char_range.length = r->glyphs[i].char_offset + cpos - char_range.location;
988
989 if (actualCharRange)
990 *actualCharRange = char_range;
991 return glyph_range;
992 }
993
994
995 /*
996 TODO? this might currently lead to continued runs not being marked as
997 continued runs. this will only happen at safe break spots, though, so
998 it should still be safe. might lose opportunities to merge runs, though.
999 */
1000
1001 /*
1002 This is hairy.
1003
1004 The ranges passed in and out of this method are ranges _after_ the change.
1005 Internally, we switch between before- and after-indices. Comments mark the
1006 places where we switch.
1007 */
1008 - (void) invalidateGlyphsForCharacterRange: (NSRange)range
1009 changeInLength: (int)lengthChange
1010 actualCharacterRange: (NSRange *)actualRange
1011 {
1012 BOOL trailing;
1013 glyph_run_head_t *context[SKIP_LIST_DEPTH];
1014 glyph_run_head_t *h;
1015 glyph_run_t *r;
1016 NSRange rng;
1017 int position[SKIP_LIST_DEPTH];
1018 unsigned int cpos;
1019 unsigned int ts_length;
1020 int gap;
1021 int level;
1022 unsigned int ch;
1023
1024
1025 /*
1026 We always clear out the cached run information to be safe.
1027 */
1028 cached_run = NULL;
1029
1030 /* Set it now for early returns. */
1031 if (actualRange)
1032 *actualRange = range;
1033
1034
1035 // printf("\n +++ range=(%i+%i) lengthChange=%i\n", range.location, range.length, lengthChange);
1036 [self _sanityChecks];
1037 // [self _glyphDumpRuns];
1038
1039
1040 /* Switch to before-indices. */
1041 range.length -= lengthChange;
1042 // printf("invalidate %i+%i=%i\n", range.location, range.length, range.location+range.length);
1043
1044 ts_length = [_textStorage length];
1045
1046
1047 /*
1048 Find out what range we actually need to invalidate. This depends on how
1049 context affects glyph generation.
1050 */
1051 ch = range.location;
1052 if (ch > 0)
1053 {
1054 ch = [self _findSafeBreakMovingBackwardFrom: ch];
1055 range.length += range.location - ch;
1056 range.location = ch;
1057 }
1058
1059 ch = ch + range.length + lengthChange;
1060 if (ch < ts_length)
1061 {
1062 ch = [self _findSafeBreakMovingForwardFrom: ch];
1063
1064 ch -= lengthChange;
1065 range.length = ch - range.location;
1066 }
1067
1068 /*
1069 We now have the range to invalidate in 'range' (indices before the
1070 change).
1071 */
1072 // printf("adjusted to %i+%i\n", range.location, range.length);
1073
1074 ch = range.location;
1075
1076 /*
1077 Find the first run (and context) for the range.
1078 */
1079 h = glyphs;
1080 cpos = 0;
1081 for (level = SKIP_LIST_DEPTH - 1; level >= 0; level--)
1082 {
1083 while (cpos + h->char_length <= ch)
1084 {
1085 cpos += h->char_length;
1086 h = h->next;
1087 if (!h)
1088 {
1089 /*
1090 No runs have been created for the range, so there's nothing
1091 to invalidate.
1092 */
1093 // printf("no runs created yet\n");
1094 return;
1095 }
1096 }
1097 context[level] = h;
1098 position[level] = cpos;
1099 h++;
1100 }
1101 h--;
1102 r = (glyph_run_t *)h;
1103
1104 /*
1105 Now we have the first run that intersects the range we're invalidating
1106 in 'r' (and context in 'context' and 'position').
1107 */
1108
1109 // printf("split if %i+%i > %i+%i\n", cpos, r->head.char_length, ch, range.length);
1110 /*
1111 If 'r' extends beyond the invalidated range, split off the trailing, valid
1112 part to a new run. The reason we need to do this is that we must have runs
1113 for the first glyph not invalidated or the deletion loop below will fail.
1114 */
1115 if (cpos + r->head.char_length > ch + range.length && ch != cpos)
1116 {
1117 glyph_run_t *new;
1118 glyph_run_head_t *hn;
1119 int i;
1120
1121 new = run_insert(context);
1122 new->head.char_length = cpos + r->head.char_length - (ch + range.length);
1123 [self _run_copy_attributes: new : r];
1124 /* OPT: keep valid glyphs
1125 this seems to be a fairly rare case
1126 */
1127 hn = &new->head;
1128 hn--;
1129 for (i = 1; i <= new->level; i++, hn--)
1130 run_fix_head(hn);
1131
1132 if (new->head.next)
1133 ((glyph_run_t *)new->head.next)->prev = (glyph_run_head_t *)new;
1134
1135 r->head.char_length -= new->head.char_length;
1136 }
1137
1138 /*
1139 Set things up. We want 'r' to be the last run we want to keep.
1140 */
1141 if (ch == cpos)
1142 {
1143 /*
1144 This run begins exactly at the beginning of the invalidated range.
1145 Since we want 'r' to be the last run to keep, we actually want it
1146 to be the previous run. Thus, we step backwards in the skip list
1147 to get the previous run.
1148 */
1149 glyph_run_head_t *h2;
1150
1151 h2 = h - r->level;
1152 h = context[r->level + 1];
1153 cpos = position[r->level + 1];
1154 h++;
1155 for (level = r->level; level >= 0; level--)
1156 {
1157 while (h->next != h2)
1158 {
1159 cpos += h->char_length;
1160 h = h->next;
1161 }
1162 position[level] = cpos;
1163 context[level] = h;
1164 h++;
1165 h2++;
1166 }
1167 h--;
1168 r = (glyph_run_t *)h;
1169 gap = 0;
1170 }
1171 else
1172 {
1173 /*
1174 This run begins before the invalidated range. Resize it so it ends
1175 just before it.
1176 */
1177 gap = r->head.char_length + cpos - ch;
1178 r->head.char_length = ch - cpos;
1179 /* OPT!!! keep valid glyphs */
1180 if (r->head.complete)
1181 {
1182 r->head.glyph_length = 0;
1183 r->head.complete = 0;
1184 free(r->glyphs);
1185 r->glyphs = NULL;
1186 }
1187 }
1188
1189 /*
1190 'r' is the last run we should keep, 'context' and 'position' are set up
1191 for it, 'gap' is the number of characters already deleted.
1192
1193 Now we delete all runs completely invalidated.
1194 */
1195 {
1196 glyph_run_t *next;
1197 unsigned int max = range.location + range.length;
1198 int i;
1199
1200 cpos += gap + r->head.char_length;
1201 while (1)
1202 {
1203 next = (glyph_run_t *)r->head.next;
1204
1205 /* We reached the end of all created runs. */
1206 if (!next)
1207 break;
1208
1209 NSAssert(max >= cpos,
1210 @"no run for first glyph beyond invalidated range");
1211
1212 /* Clean cut, just stop. */
1213 if (max == cpos)
1214 break;
1215
1216 /*
1217 Part of this run extends beyond the invalidated range. Resize it
1218 so it's completely beyond the invalidated range and stop.
1219 */
1220 if (max < cpos + next->head.char_length)
1221 {
1222 glyph_run_head_t *hn;
1223 /* adjust final run */
1224 /* OPT!!! keep valid glyphs */
1225 if (next->head.complete)
1226 {
1227 next->head.complete = 0;
1228 next->head.glyph_length = 0;
1229 free(next->glyphs);
1230 next->glyphs = NULL;
1231 }
1232 next->head.char_length -= max - cpos;
1233
1234 hn = &next->head;
1235 hn--;
1236 for (i = 1; i <= next->level; i++, hn--)
1237 run_fix_head(hn);
1238
1239 break;
1240 }
1241
1242 cpos += next->head.char_length;
1243
1244 /*
1245 This run is completely inside the invalidated range. Remove it.
1246 The context run heads will be adjusted later.
1247 */
1248 if (next->head.next)
1249 ((glyph_run_t *)next->head.next)->prev = &r->head;
1250
1251 for (i = 0; i <= next->level; i++)
1252 context[i]->next = context[i]->next->next;
1253 h = &next->head;
1254 if (h->complete)
1255 free(next->glyphs);
1256 [self _run_free_attributes: next];
1257 h -= next->level;
1258 free(h);
1259 h = NULL;
1260 }
1261 trailing = !next;
1262 }
1263
1264 /* printf("deleted\n");
1265 [self _glyphDumpRuns];*/
1266
1267 /* Switch back to after-indices. */
1268 range.length += lengthChange;
1269
1270 /*
1271 'r' is the last run we want to keep, and the next run is the next
1272 uninvalidated run. We need to insert new runs for invalidated range
1273 after 'r'.
1274
1275 As we create new runs, we move the context forward. When we do this, we
1276 adjust their heads with updated information. When we're done, we update
1277 all the remaining heads.
1278 */
1279 // printf("create runs for %i+%i\n", range.location, range.length);
1280 { /* OPT: this is creating more runs than it needs to */
1281 NSDictionary *attributes;
1282 glyph_run_t *new;
1283 unsigned int max = range.location + range.length;
1284 int i;
1285
1286 ch = range.location;
1287 while (ch < max)
1288 {
1289 attributes = [_textStorage attributesAtIndex: ch
1290 longestEffectiveRange: &rng
1291 inRange: NSMakeRange(0, [_textStorage length])];
1292
1293 /* printf("at %i, max=%i, effective range (%i+%i)\n",
1294 ch, max, rng.location, rng.length);*/
1295
1296 /*
1297 Catch a common case. If the new run would be a continuation of the
1298 previous run, and the previous run is short, we resize the previous
1299 run instead of creating a new run.
1300
1301 (Note that we must make sure that we don't merge with the dummy runs
1302 at the very front.)
1303
1304 This happens a lot with repeated single-character insertions, aka.
1305 typing in a text view.
1306 */
1307 if (rng.location < ch && context[0]->char_length &&
1308 context[0]->char_length < 16)
1309 {
1310 rng.length -= ch - rng.location;
1311 rng.location = ch;
1312 if (ch + rng.length > max)
1313 {
1314 rng.length = max - ch;
1315 }
1316 new = (glyph_run_t *)context[0];
1317 if (new->head.complete)
1318 {
1319 free(new->glyphs);
1320 new->glyphs = NULL;
1321 new->head.glyph_length = 0;
1322 new->head.complete = 0;
1323 }
1324 new->head.char_length += rng.length;
1325 ch = NSMaxRange(rng);
1326 continue;
1327 }
1328
1329 new = run_insert(context);
1330
1331 /*
1332 We have the longest range the attributes allow us to create a run
1333 for. Since this might overlap the previous and next runs, we might
1334 need to adjust the location and length of the range we create a
1335 run for.
1336
1337 OPT: If the overlapped run is short, we might want to clear out
1338 its glyphs and extend it to cover our range. This should result
1339 in fewer runs being created for large sequences of single character
1340 adds.
1341 */
1342 if (rng.location < ch)
1343 {
1344 /*
1345 The new run has the same attributes as the previous run, so we
1346 mark it is as a continued run.
1347 */
1348 new->continued = 1;
1349 rng.length -= ch - rng.location;
1350 rng.location = ch;
1351 }
1352 if (ch + rng.length > max)
1353 {
1354 /*
1355 The new run has the same attributes as the next run, so we mark
1356 the next run as a continued run.
1357 */
1358 if (new->head.next)
1359 ((glyph_run_t *)new->head.next)->continued = 1;
1360 rng.length = max - ch;
1361 }
1362
1363 /* See comment in -_generateRunsToCharacter:. */
1364 if (rng.length > MAX_RUN_LENGTH)
1365 {
1366 unsigned int safe_break = rng.location + MAX_RUN_LENGTH;
1367 safe_break = [self _findSafeBreakMovingForwardFrom: safe_break];
1368 if (safe_break < NSMaxRange(rng))
1369 rng.length = safe_break - rng.location;
1370 }
1371
1372 // printf("adjusted length: %i\n", rng.length);
1373 new->head.char_length = rng.length;
1374
1375 [self _run_cache_attributes: new : attributes];
1376
1377 h = &new->head;
1378 for (i = 0; i <= new->level; i++, h--)
1379 {
1380 if (i)
1381 run_fix_head(context[i]);
1382 context[i] = h;
1383 }
1384 ch += new->head.char_length;
1385 }
1386
1387 if (context[0]->next)
1388 ((glyph_run_t *)context[0]->next)->prev = context[0];
1389 }
1390
1391 /* Fix up the remaining context run heads. */
1392 {
1393 int i;
1394 for (i = 1; i < SKIP_LIST_DEPTH; i++)
1395 {
1396 run_fix_head(context[i]);
1397 }
1398 }
1399
1400 if (actualRange)
1401 *actualRange = range;
1402
1403 // [self _glyphDumpRuns];
1404 [self _sanityChecks];
1405 }
1406
1407
1408 #define GET_GLYPH \
1409 glyph_run_t *r; \
1410 int pos, cpos; \
1411 \
1412 if (glyphs->glyph_length <= idx) \
1413 { \
1414 [self _generateGlyphsUpToGlyph: idx]; \
1415 if (glyphs->glyph_length <= idx) \
1416 { \
1417 [NSException raise: NSRangeException \
1418 format: @"%s glyph range out of range", __PRETTY_FUNCTION__]; \
1419 } \
1420 } \
1421 \
1422 r = run_for_glyph_index(idx, glyphs, &pos, &cpos); \
1423 if (!r) \
1424 { \
1425 [NSException raise: NSRangeException \
1426 format: @"%s glyph range out of range", __PRETTY_FUNCTION__]; \
1427 } \
1428 idx -= pos;
1429
1430 - (void) setDrawsOutsideLineFragment: (BOOL)flag
1431 forGlyphAtIndex: (unsigned int)idx
1432 {
1433 GET_GLYPH
1434 r->glyphs[idx].drawsOutsideLineFragment = !!flag;
1435 }
1436 - (BOOL) drawsOutsideLineFragmentForGlyphAtIndex: (unsigned int)idx
1437 {
1438 GET_GLYPH
1439 return r->glyphs[idx].drawsOutsideLineFragment;
1440 }
1441
1442 - (void) setNotShownAttribute: (BOOL)flag
1443 forGlyphAtIndex: (unsigned int)idx
1444 {
1445 GET_GLYPH
1446 r->glyphs[idx].isNotShown = !!flag;
1447 }
1448 - (BOOL) notShownAttributeForGlyphAtIndex: (unsigned int)idx
1449 {
1450 GET_GLYPH
1451 return r->glyphs[idx].isNotShown;
1452 }
1453
1454
1455 - (NSFont *) effectiveFontForGlyphAtIndex: (unsigned int)idx
1456 range: (NSRange *)range
1457 {
1458 GET_GLYPH
1459 if (range)
1460 *range = NSMakeRange(pos, r->head.glyph_length);
1461 return r->font;
1462 }
1463
1464
1465 - (void) insertGlyph: (NSGlyph)aGlyph
1466 atGlyphIndex: (unsigned int)glyphIndex
1467 characterIndex: (unsigned int)charIndex
1468 {
1469 NSLog(@"Internal method %s called", __PRETTY_FUNCTION__);
1470 }
1471 - (void) replaceGlyphAtIndex: (unsigned int)glyphIndex
1472 withGlyph: (NSGlyph)newGlyph
1473 {
1474 NSLog(@"Internal method %s called", __PRETTY_FUNCTION__);
1475 }
1476 - (void) deleteGlyphsInRange: (NSRange)aRange
1477 {
1478 NSLog(@"Internal method %s called", __PRETTY_FUNCTION__);
1479 }
1480 - (void) setCharacterIndex: (unsigned int)charIndex
1481 forGlyphAtIndex: (unsigned int)glyphIndex
1482 {
1483 NSLog(@"Internal method %s called", __PRETTY_FUNCTION__);
1484 }
1485
1486
1487 - (void) setIntAttribute: (int)attributeTag
1488 value: (int)anInt
1489 forGlyphAtIndex: (unsigned int)glyphIndex
1490 {
1491 [self subclassResponsibility: _cmd];
1492 }
1493 - (int) intAttribute: (int)attributeTag
1494 forGlyphAtIndex: (unsigned int)glyphIndex
1495 {
1496 [self subclassResponsibility: _cmd];
1497 return 0;
1498 }
1499
1500 @end
1501
1502
1503
1504 /***** Layout handling *****/
1505
1506 @implementation GSLayoutManager (layout_helpers)
1507
1508 -(void) _invalidateLayoutFromContainer: (int)idx
1509 {
1510 int i, j;
1511 textcontainer_t *tc;
1512 linefrag_t *lf;
1513
1514 extra_textcontainer = nil;
1515
1516 for (i = idx, tc = textcontainers + idx; i < num_textcontainers; i++, tc++)
1517 {
1518 tc->complete = NO;
1519 if (tc->linefrags)
1520 {
1521 for (j = 0, lf = tc->linefrags; j < tc->num_linefrags + tc->num_soft; j++, lf++)
1522 {
1523 if (lf->points)
1524 free(lf->points);
1525 if (lf->attachments)
1526 free(lf->attachments);
1527 }
1528
1529 free(tc->linefrags);
1530 }
1531 tc->linefrags = NULL;
1532 tc->num_linefrags = tc->num_soft = 0;
1533 tc->size_linefrags = 0;
1534 tc->pos = tc->length = 0;
1535 tc->was_invalidated = YES;
1536 }
1537 for (i = idx - 1, tc = textcontainers + idx - 1; i >= 0; i--, tc--)
1538 {
1539 if (tc->num_linefrags)
1540 {
1541 layout_glyph = tc->pos + tc->length;
1542 if (layout_glyph == glyphs->glyph_length)
1543 layout_char = glyphs->char_length;
1544 else
1545 layout_char = [self characterIndexForGlyphAtIndex: layout_glyph]; /* TODO? */
1546 return;
1547 }
1548 }
1549 layout_glyph = layout_char = 0;
1550 }
1551
1552 -(void) _freeLayout
1553 {
1554 [self _invalidateLayoutFromContainer: 0];
1555 }
1556
1557
1558 -(void) _doLayout
1559 {
1560 int i, j;
1561 textcontainer_t *tc;
1562 unsigned int next;
1563 NSRect prev;
1564 BOOL delegate_responds;
1565
1566 delegate_responds = [_delegate respondsToSelector:
1567 @selector(layoutManager:didCompleteLayoutForTextContainer:atEnd:)];
1568
1569 next = layout_glyph;
1570 for (i = 0, tc = textcontainers; i < num_textcontainers; i++, tc++)
1571 {
1572 if (tc->complete)
1573 continue;
1574
1575 while (1)
1576 {
1577 if (tc->num_linefrags)
1578 prev = tc->linefrags[tc->num_linefrags - 1].rect;
1579 else
1580 prev = NSZeroRect;
1581 j = [typesetter layoutGlyphsInLayoutManager: self
1582 inTextContainer: tc->textContainer
1583 startingAtGlyphIndex: next
1584 previousLineFragmentRect: prev
1585 nextGlyphIndex: &next
1586 numberOfLineFragments: 0];
1587 if (j)
1588 break;
1589 }
1590 tc->complete = YES;
1591 if (tc->num_soft)
1592 {
1593 /*
1594 If there is any soft invalidated layout information left, remove
1595 it.
1596 */
1597 int k;
1598 linefrag_t *lf;
1599 for (k = tc->num_linefrags, lf = tc->linefrags + k; k < tc->num_linefrags + tc->num_soft; k++, lf++)
1600 {
1601 if (lf->points)
1602 {
1603 free(lf->points);
1604 lf->points = NULL;
1605 }
1606 if (lf->attachments)
1607 {
1608 free(lf->attachments);
1609 lf->attachments = NULL;
1610 }
1611 }
1612 tc->num_soft = 0;
1613 }
1614 if (delegate_responds)
1615 {
1616 [_delegate layoutManager: self
1617 didCompleteLayoutForTextContainer: tc->textContainer
1618 atEnd: j == 2];
1619 /* The call might have resulted in more text containers being
1620 added, so 'textcontainers' might have moved. */
1621 tc = textcontainers + i;
1622 }
1623 if (j == 2)
1624 {
1625 break;
1626 }
1627 if (i == num_textcontainers && delegate_responds)
1628 {
1629 [_delegate layoutManager: self
1630 didCompleteLayoutForTextContainer: nil
1631 atEnd: NO];
1632 }
1633 }
1634 }
1635
1636
1637 -(void) _doLayoutToGlyph: (unsigned int)glyphIndex
1638 {
1639 [self _doLayout];
1640 }
1641
1642 -(void) _doLayoutToContainer: (int)cindex
1643 {
1644 [self _doLayout];
1645 }
1646
1647
1648 -(void) _didInvalidateLayout
1649 {
1650 int i;
1651 textcontainer_t *tc;
1652
1653 for (tc = textcontainers, i = 0; i < num_textcontainers; i++, tc++)
1654 {
1655 tc->was_invalidated = NO;
1656 }
1657 }
1658
1659 @end
1660
1661
1662 @implementation GSLayoutManager (layout)
1663
1664
1665 /*
1666 In the general case, we can't make any assumptions about how layout might
1667 interact between line frag rects. To be safe in all cases, we must
1668 invalidate all layout information.
1669
1670 TODO:
1671 We could handle this by assuming that whoever calls this knows exactly what
1672 needs to be invalidated. We won't be using it internally, anyway, so it
1673 doesn't matter much to us, and it would make more advanced things possible
1674 for external callers. On the other hand, it would be easy to break things
1675 by calling this incorrectly.
1676 */
1677 - (void) invalidateLayoutForCharacterRange: (NSRange)aRange
1678 isSoft: (BOOL)flag
1679 actualCharacterRange: (NSRange *)actualRange
1680 {
1681 [self _invalidateLayoutFromContainer: 0];
1682 }
1683
1684
1685 #define SETUP_STUFF \
1686 unsigned int max = glyphRange.location + glyphRange.length; \
1687 \
1688 [self _generateGlyphsUpToGlyph: max - 1]; \
1689 if (glyphs->glyph_length < max) \
1690 { \
1691 [NSException raise: NSRangeException \
1692 format: @"%s: glyph range out of range", __PRETTY_FUNCTION__]; \
1693 return; \
1694 }
1695
1696 - (void) setTextContainer: (NSTextContainer *)aTextContainer
1697 forGlyphRange: (NSRange)glyphRange
1698 {
1699 textcontainer_t *tc;
1700 int i;
1701 SETUP_STUFF
1702
1703 for (i = 0, tc = textcontainers; i < num_textcontainers; i++, tc++)
1704 if (tc->textContainer == aTextContainer)
1705 break;
1706 if (i == num_textcontainers)
1707 {
1708 NSLog(@"%s: doesn't own text container", __PRETTY_FUNCTION__);
1709 return;
1710 }
1711
1712 /* Assume that no line frags means that layout hasn't started yet. */
1713 if (tc->num_linefrags)
1714 {
1715 if (glyphRange.location != tc->pos + tc->length)
1716 {
1717 [NSException raise: NSRangeException
1718 format: @"%s: glyph range not consistent with existing layout",
1719 __PRETTY_FUNCTION__];
1720 return;
1721 }
1722 tc->length += glyphRange.length;
1723 }
1724 else if (!i)
1725 {
1726 if (glyphRange.location)
1727 {
1728 [NSException raise: NSRangeException
1729 format: @"%s: glyph range not consistent with existing layout",
1730 __PRETTY_FUNCTION__];
1731 return;
1732 }
1733 tc->pos = 0;
1734 tc->length = glyphRange.length;
1735 }
1736 else
1737 {
1738 if (tc[-1].pos + tc[-1].length != glyphRange.location)
1739 {
1740 [NSException raise: NSRangeException
1741 format: @"%s: glyph range not consistent with existing layout",
1742 __PRETTY_FUNCTION__];
1743 return;
1744 }
1745 tc->pos = glyphRange.location;
1746 tc->length = glyphRange.length;
1747 }
1748
1749 {
1750 unsigned int gpos;
1751 unsigned int g;
1752 glyph_t *glyph;
1753 glyph_run_t *run = run_for_glyph_index(glyphRange.location, glyphs, &gpos, NULL);
1754
1755 g = glyphRange.location;
1756 glyph = &run->glyphs[g - gpos];
1757 while (g < glyphRange.location + glyphRange.length)
1758 {
1759 if (g == gpos + run->head.glyph_length)
1760 {
1761 gpos += run->head.glyph_length;
1762 run = (glyph_run_t *)run->head.next;
1763 glyph = run->glyphs;
1764 }
1765
1766 glyph->isNotShown = NO;
1767 glyph->drawsOutsideLineFragment = NO;
1768 g++;
1769 glyph++;
1770 }
1771 }
1772
1773 layout_glyph = tc->pos + tc->length;
1774 if (layout_glyph == glyphs->glyph_length)
1775 layout_char = glyphs->char_length;
1776 else
1777 layout_char = [self characterIndexForGlyphAtIndex: layout_glyph];
1778 }
1779
1780 - (void) setLineFragmentRect: (NSRect)fragmentRect
1781 forGlyphRange: (NSRange)glyphRange
1782 usedRect: (NSRect)usedRect
1783 {
1784 textcontainer_t *tc;
1785 int i;
1786 linefrag_t *lf;
1787
1788 SETUP_STUFF
1789
1790 for (i = 0, tc = textcontainers; i < num_textcontainers; i++, tc++)
1791 {
1792 if (tc->pos <= glyphRange.location &&
1793 tc->pos + tc->length >= glyphRange.location + glyphRange.length)
1794 break;
1795 }
1796 if (i == num_textcontainers)
1797 {
1798 [NSException raise: NSRangeException
1799 format: @"%s: glyph range not consistent with existing layout",
1800 __PRETTY_FUNCTION__];
1801 return;
1802 }
1803
1804 /* Make sure the given glyph range matches earlier layout. */
1805 if (!tc->num_linefrags)
1806 {
1807 if (glyphRange.location != tc->pos)
1808 {
1809 [NSException raise: NSRangeException
1810 format: @"%s: glyph range not consistent with existing layout",
1811 __PRETTY_FUNCTION__];
1812 return;
1813 }
1814 }
1815 else
1816 {
1817 lf = &tc->linefrags[tc->num_linefrags - 1];
1818 if (lf->pos + lf->length != glyphRange.location)
1819 {
1820 [NSException raise: NSRangeException
1821 format: @"%s: glyph range not consistent with existing layout",
1822 __PRETTY_FUNCTION__];
1823 return;
1824 }
1825 }
1826
1827 if (!(tc->num_linefrags + tc->num_soft))
1828 {
1829 if (!tc->size_linefrags)
1830 {
1831 tc->size_linefrags = 16;
1832 tc->linefrags = malloc(sizeof(linefrag_t) * tc->size_linefrags);
1833 }
1834 tc->num_linefrags = 1;
1835 lf = tc->linefrags;
1836 }
1837 else if (!tc->num_soft)
1838 {
1839 if (tc->size_linefrags <= tc->num_linefrags)
1840 {
1841 tc->size_linefrags += tc->size_linefrags / 2;
1842 tc->linefrags = realloc(tc->linefrags, sizeof(linefrag_t) * tc->size_linefrags);
1843 }
1844 tc->num_linefrags++;
1845 lf = &tc->linefrags[tc->num_linefrags - 1];
1846 }
1847 else
1848 {
1849 int i;
1850 for (i = tc->num_linefrags, lf = tc->linefrags + i; i < tc->num_linefrags + tc->num_soft; i++, lf++)
1851 {
1852 if (lf->pos >= NSMaxRange(glyphRange))
1853 break;
1854 if (lf->points)
1855 {
1856 free(lf->points);
1857 lf->points = NULL;
1858 }
1859 if (lf->attachments)
1860 {
1861 free(lf->attachments);
1862 lf->attachments = NULL;
1863 }
1864 }
1865
1866 if (i == tc->num_linefrags)
1867 {
1868 /*
1869 If we should keep all soft frags, we need to enlarge the array
1870 to fit the new line frag.
1871 */
1872 if (tc->size_linefrags <= tc->num_linefrags + tc->num_soft)
1873 {
1874 tc->size_linefrags += tc->size_linefrags / 2;
1875 tc->linefrags = realloc(tc->linefrags, sizeof(linefrag_t) * tc->size_linefrags);
1876 }
1877 memmove(&tc->linefrags[tc->num_linefrags + 1], &tc->linefrags[tc->num_linefrags], tc->num_soft * sizeof(linefrag_t));
1878 }
1879 else if (i > tc->num_linefrags + 1)
1880 {
1881 tc->num_soft -= i - tc->num_linefrags;
1882 memmove(&tc->linefrags[tc->num_linefrags + 1], &tc->linefrags[i], tc->num_soft * sizeof(linefrag_t));
1883 }
1884 else
1885 {
1886 /*
1887 If i == tc->num_linefrags + 1, we're lucky and everything already
1888 lines up, so no moving is necessary.
1889 */
1890 tc->num_soft--;
1891 }
1892
1893 tc->num_linefrags++;
1894 lf = &tc->linefrags[tc->num_linefrags - 1];
1895 }
1896
1897 memset(lf, 0, sizeof(linefrag_t));
1898 lf->rect = fragmentRect;
1899 lf->used_rect = usedRect;
1900 lf->pos = glyphRange.location;
1901 lf->length = glyphRange.length;
1902 }
1903
1904 - (void) setLocation: (NSPoint)location
1905 forStartOfGlyphRange: (NSRange)glyphRange
1906 {
1907 textcontainer_t *tc;
1908 int i;
1909 linefrag_t *lf;
1910 linefrag_point_t *lp;
1911
1912 SETUP_STUFF
1913
1914 for (i = 0, tc = textcontainers; i < num_textcontainers; i++, tc++)
1915 {
1916 if (tc->pos <= glyphRange.location &&
1917 tc->pos + tc->length >= glyphRange.location + glyphRange.length)
1918 break;
1919 }
1920 if (i == num_textcontainers)
1921 {
1922 [NSException raise: NSRangeException
1923 format: @"%s: glyph range not consistent with existing layout",
1924 __PRETTY_FUNCTION__];
1925 return;
1926 }
1927
1928 for (i = tc->num_linefrags - 1, lf = tc->linefrags + i; i >= 0; i--, lf--)
1929 {
1930 if (lf->pos <= glyphRange.location &&
1931 lf->pos + lf->length >= glyphRange.location + glyphRange.length)
1932 break;
1933 }
1934 if (i < 0)
1935 {
1936 [NSException raise: NSRangeException
1937 format: @"%s: glyph range not consistent with existing layout",
1938 __PRETTY_FUNCTION__];
1939 return;
1940 }
1941
1942 if (!lf->num_points)
1943 {
1944 if (glyphRange.location != lf->pos)
1945 {
1946 [NSException raise: NSRangeException
1947 format: @"%s: glyph range not consistent with existing layout",
1948 __PRETTY_FUNCTION__];
1949 return;
1950 }
1951 lp = lf->points = malloc(sizeof(linefrag_point_t));
1952 lf->num_points++;
1953 }
1954 else
1955 {
1956 lp = &lf->points[lf->num_points - 1];
1957 if (lp->pos + lp->length != glyphRange.location)
1958 {
1959 [NSException raise: NSRangeException
1960 format: @"%s: glyph range not consistent with existing layout",
1961 __PRETTY_FUNCTION__];
1962 return;
1963 }
1964 lf->num_points++;
1965 lf->points = realloc(lf->points, sizeof(linefrag_point_t) * lf->num_points);
1966 lp = &lf->points[lf->num_points - 1];
1967 }
1968 lp->pos = glyphRange.location;
1969 lp->length = glyphRange.length;
1970 lp->p = location;
1971 }
1972
1973
1974 -(void) setAttachmentSize: (NSSize)size
1975 forGlyphRange: (NSRange)glyphRange
1976 {
1977 textcontainer_t *tc;
1978 int i;
1979 linefrag_t *lf;
1980 linefrag_attachment_t *la;
1981
1982 SETUP_STUFF
1983
1984 for (i = 0, tc = textcontainers; i < num_textcontainers; i++, tc++)
1985 {
1986 if (tc->pos <= glyphRange.location &&
1987 tc->pos + tc->length >= glyphRange.location + glyphRange.length)
1988 break;
1989 }
1990 if (i == num_textcontainers)
1991 {
1992 [NSException raise: NSRangeException
1993 format: @"%s: glyph range not consistent with existing layout",
1994 __PRETTY_FUNCTION__];
1995 return;
1996 }
1997
1998 for (i = 0, lf = tc->linefrags; i < tc->num_linefrags; i++, lf++)
1999 {
2000 if (lf->pos <= glyphRange.location &&
2001 lf->pos + lf->length >= glyphRange.location + glyphRange.length)
2002 break;
2003 }
2004 if (i == tc->num_linefrags)
2005 {
2006 [NSException raise: NSRangeException
2007 format: @"%s: glyph range not consistent with existing layout",
2008 __PRETTY_FUNCTION__];
2009 return;
2010 }
2011
2012 /* TODO: we do no sanity checking of attachment size ranges. might want
2013 to consider doing it */
2014 lf->attachments = realloc(lf->attachments,
2015 sizeof(linefrag_attachment_t) * (lf->num_attachments + 1));
2016 la = &lf->attachments[lf->num_attachments++];
2017
2018 memset(la, 0, sizeof(linefrag_attachment_t));
2019 la->pos = glyphRange.location;
2020 la->length = glyphRange.length;
2021 la->size = size;
2022 }
2023
2024 #undef SETUP_STUFF
2025
2026
2027 - (NSTextContainer *) textContainerForGlyphAtIndex: (unsigned int)glyphIndex
2028 effectiveRange: (NSRange *)effectiveRange
2029 {
2030 textcontainer_t *tc;
2031 int i;
2032
2033 [self _doLayoutToGlyph: glyphIndex];
2034 for (i = 0, tc = textcontainers; i < num_textcontainers; i++, tc++)
2035 if (tc->pos + tc->length > glyphIndex)
2036 break;
2037 if (i == num_textcontainers)
2038 {
2039 NSLog(@"%s: can't find text container for glyph (internal error)", __PRETTY_FUNCTION__);
2040 return nil;
2041 }
2042
2043 if (effectiveRange)
2044 {
2045 [self _doLayoutToContainer: i];
2046 tc = textcontainers + i;
2047 *effectiveRange = NSMakeRange(tc->pos, tc->length);
2048 }
2049 return tc->textContainer;
2050 }
2051
2052 - (NSRect) lineFragmentRectForGlyphAtIndex: (unsigned int)glyphIndex
2053 effectiveRange: (NSRange *)effectiveGlyphRange
2054 {
2055 int i;
2056 textcontainer_t *tc;
2057 linefrag_t *lf;
2058
2059 [self _doLayoutToGlyph: glyphIndex];
2060 for (i = 0, tc = textcontainers; i < num_textcontainers; i++, tc++)
2061 if (tc->pos + tc->length > glyphIndex)
2062 break;
2063 if (i == num_textcontainers)
2064 {
2065 NSLog(@"%s: can't find text container for glyph (internal error)", __PRETTY_FUNCTION__);
2066 return NSZeroRect;
2067 }
2068
2069 for (i = 0, lf = tc->linefrags; i < tc->num_linefrags; i++, lf++)
2070 if (lf->pos + lf->length > glyphIndex)
2071 break;
2072 if (i == tc->num_linefrags)
2073 {
2074 NSLog(@"%s: can't find line frag rect for glyph (internal error)", __PRETTY_FUNCTION__);
2075 return NSZeroRect;
2076 }
2077
2078 if (effectiveGlyphRange)
2079 {
2080 *effectiveGlyphRange = NSMakeRange(lf->pos, lf->length);
2081 }
2082 return lf->rect;
2083 }
2084
2085 - (NSRect) lineFragmentUsedRectForGlyphAtIndex: (unsigned int)glyphIndex
2086 effectiveRange: (NSRange *)effectiveGlyphRange
2087 {
2088 int i;
2089 textcontainer_t *tc;
2090 linefrag_t *lf;
2091
2092 [self _doLayoutToGlyph: glyphIndex];
2093 for (i = 0, tc = textcontainers; i < num_textcontainers; i++, tc++)
2094 if (tc->pos + tc->length > glyphIndex)
2095 break;
2096 if (i == num_textcontainers)
2097 {
2098 NSLog(@"%s: can't find text container for glyph (internal error)", __PRETTY_FUNCTION__);
2099 return NSMakeRect(0, 0, 0, 0);
2100 }
2101
2102 for (i = 0, lf = tc->linefrags; i < tc->num_linefrags; i++, lf++)
2103 if (lf->pos + lf->length > glyphIndex)
2104 break;
2105 if (i == tc->num_linefrags)
2106 {
2107 NSLog(@"%s: can't find line frag rect for glyph (internal error)", __PRETTY_FUNCTION__);
2108 return NSMakeRect(0, 0, 0, 0);
2109 }
2110
2111 if (effectiveGlyphRange)
2112 {
2113 *effectiveGlyphRange = NSMakeRange(lf->pos, lf->length);
2114 }
2115 return lf->used_rect;
2116 }
2117
2118 - (NSRange) rangeOfNominallySpacedGlyphsContainingIndex: (unsigned int)glyphIndex
2119 startLocation: (NSPoint *)p
2120 {
2121 int i;
2122 textcontainer_t *tc;
2123 linefrag_t *lf;
2124 linefrag_point_t *lp;
2125
2126 [self _doLayoutToGlyph: glyphIndex];
2127 for (i = 0, tc = textcontainers; i < num_textcontainers; i++, tc++)
2128 if (tc->pos + tc->length > glyphIndex)
2129 break;
2130 if (i == num_textcontainers)
2131 {
2132 NSLog(@"%s: can't find text container for glyph (internal error)", __PRETTY_FUNCTION__);
2133 return NSMakeRange(NSNotFound, 0);
2134 }
2135
2136 for (i = 0, lf = tc->linefrags; i < tc->num_linefrags; i++, lf++)
2137 if (lf->pos + lf->length > glyphIndex)
2138 break;
2139 if (i == tc->num_linefrags)
2140 {
2141 NSLog(@"%s: can't find line frag rect for glyph (internal error)", __PRETTY_FUNCTION__);
2142 return NSMakeRange(NSNotFound, 0);
2143 }
2144
2145 for (i = 0, lp = lf->points; i < lf->num_points; i++, lp++)
2146 if (lp->pos + lp->length > glyphIndex)
2147 break;
2148 if (i == lf->num_points)
2149 {
2150 NSLog(@"%s: can't find location for glyph (internal error)", __PRETTY_FUNCTION__);
2151 return NSMakeRange(NSNotFound, 0);
2152 }
2153
2154 if (p)
2155 *p = lp->p;
2156 return NSMakeRange(lp->pos, lp->length);
2157 }
2158
2159
2160 - (NSRange) rangeOfNominallySpacedGlyphsContainingIndex:(unsigned int)glyphIndex
2161 {
2162 return [self rangeOfNominallySpacedGlyphsContainingIndex: glyphIndex
2163 startLocation: NULL];
2164 }
2165
2166
2167 /* The union of all line frag rects' used rects. */
2168 - (NSRect) usedRectForTextContainer: (NSTextContainer *)container
2169 {
2170 textcontainer_t *tc;
2171 linefrag_t *lf;
2172 int i;
2173 NSRect used;
2174
2175 for (i = 0, tc = textcontainers; i < num_textcontainers; i++, tc++)
2176 if (tc->textContainer == container)
2177 break;
2178 if (i == num_textcontainers)
2179 {
2180 NSLog(@"%s: doesn't own text container", __PRETTY_FUNCTION__);
2181 return NSMakeRect(0, 0, 0, 0);
2182 }
2183
2184 [self _doLayoutToContainer: i];
2185 tc = textcontainers + i;
2186 used = NSZeroRect;
2187 for (i = 0, lf = tc->linefrags; i < tc->num_linefrags; i++, lf++)
2188 used = NSUnionRect(used, lf->used_rect);
2189
2190 return used;
2191 }
2192
2193 - (NSRange) glyphRangeForTextContainer: (NSTextContainer *)container
2194 {
2195 textcontainer_t *tc;
2196 int i;
2197
2198 for (i = 0, tc = textcontainers; i < num_textcontainers; i++, tc++)
2199 if (tc->textContainer == container)
2200 break;
2201 if (i == num_textcontainers)
2202 {
2203 NSLog(@"%s: doesn't own text container", __PRETTY_FUNCTION__);
2204 return NSMakeRange(NSNotFound, 0);
2205 }
2206
2207 [self _doLayoutToContainer: i];
2208 tc = textcontainers + i;
2209 return NSMakeRange(tc->pos, tc->length);
2210 }
2211
2212
2213
2214 /* TODO: make more efficient */
2215 - (NSArray *) textContainers
2216 {
2217 NSMutableArray *ma;
2218 int i;
2219
2220 ma = [[NSMutableArray alloc] initWithCapacity: num_textcontainers];
2221 for (i = 0; i < num_textcontainers; i++)
2222 [ma addObject: textcontainers[i].textContainer];
2223 return [ma autorelease];
2224 }
2225
2226 - (void) addTextContainer: (NSTextContainer *)container
2227 {
2228 [self insertTextContainer: container
2229 atIndex: num_textcontainers];
2230 }
2231
2232 - (void) insertTextContainer: (NSTextContainer *)aTextContainer
2233 atIndex: (unsigned int)index
2234 {
2235 unsigned int i;
2236
2237 if (index < num_textcontainers)
2238 [self _invalidateLayoutFromContainer: index];
2239
2240 num_textcontainers++;
2241 textcontainers = realloc(textcontainers,
2242 sizeof(textcontainer_t) * num_textcontainers);
2243
2244 for (i = num_textcontainers - 1; i > index; i--)
2245 textcontainers[i] = textcontainers[i - 1];
2246
2247 memset(&textcontainers[i], 0, sizeof(textcontainer_t));
2248 textcontainers[i].textContainer = [aTextContainer retain];
2249
2250 [aTextContainer setLayoutManager: self];
2251
2252 [self _didInvalidateLayout];
2253 }
2254
2255 - (void) removeTextContainerAtIndex: (unsigned int)index
2256 {
2257 int i;
2258 textcontainer_t *tc = &textcontainers[index];
2259
2260 [self _invalidateLayoutFromContainer: index];
2261 [tc->textContainer setLayoutManager: nil];
2262 [tc->textContainer release];
2263
2264 num_textcontainers--;
2265 for (i = index; i < num_textcontainers; i++)
2266 textcontainers[i] = textcontainers[i + 1];
2267
2268 if (num_textcontainers)
2269 textcontainers = realloc(textcontainers,
2270 sizeof(textcontainer_t) * num_textcontainers);
2271 else
2272 {
2273 free(textcontainers);
2274 textcontainers = NULL;
2275 }
2276
2277 [self _didInvalidateLayout];
2278 }
2279
2280 - (void) textContainerChangedGeometry: (NSTextContainer *)aContainer
2281 {
2282 int i;
2283 for (i = 0; i < num_textcontainers; i++)
2284 if (textcontainers[i].textContainer == aContainer)
2285 break;
2286 if (i == num_textcontainers)
2287 {
2288 NSLog(@"%s: does not own text container", __PRETTY_FUNCTION__);
2289 return;
2290 }
2291 [self _invalidateLayoutFromContainer: i];
2292 [self _didInvalidateLayout];
2293 }
2294
2295
2296 - (unsigned int) firstUnlaidCharacterIndex
2297 {
2298 return layout_char;
2299 }
2300 - (unsigned int) firstUnlaidGlyphIndex
2301 {
2302 return layout_glyph;
2303 }
2304 -(void) getFirstUnlaidCharacterIndex: (unsigned int *)cindex
2305 glyphIndex: (unsigned int *)gindex
2306 {
2307 if (cindex)
2308 *cindex = [self firstUnlaidCharacterIndex];
2309 if (gindex)
2310 *gindex = [self firstUnlaidGlyphIndex];
2311 }
2312
2313
2314 -(void) setExtraLineFragmentRect: (NSRect)linefrag
2315 usedRect: (NSRect)used
2316 textContainer: (NSTextContainer *)tc
2317 {
2318 extra_rect = linefrag;
2319 extra_used_rect = used;
2320 extra_textcontainer = tc;
2321 }
2322
2323 -(NSRect) extraLineFragmentRect
2324 {
2325 return extra_rect;
2326 }
2327
2328 -(NSRect) extraLineFragmentUsedRect
2329 {
2330 return extra_used_rect;
2331 }
2332
2333 -(NSTextContainer *) extraLineFragmentTextContainer
2334 {
2335 return extra_textcontainer;
2336 }
2337
2338
2339 -(void) _softInvalidateUseLineFrags: (int)num
2340 withShift: (NSSize)shift
2341 inTextContainer: (NSTextContainer *)textContainer
2342 {
2343 int i;
2344 textcontainer_t *tc;
2345 linefrag_t *lf;
2346 for (i = 0, tc = textcontainers; i < num_textcontainers; i++, tc++)
2347 if (tc->textContainer == textContainer)
2348 break;
2349 if (i == num_textcontainers)
2350 {
2351 NSLog(@"(%s): does not own text container", __PRETTY_FUNCTION__);
2352 return;
2353 }
2354
2355 if (shift.width || shift.height)
2356 {
2357 for (i = 0, lf = &tc->linefrags[tc->num_linefrags]; i < num; i++, lf++)
2358 {
2359 lf->rect.origin.x += shift.width;
2360 lf->rect.origin.y += shift.height;
2361 lf->used_rect.origin.x += shift.width;
2362 lf->used_rect.origin.y += shift.height;
2363 }
2364 }
2365 tc->num_soft -= num;
2366 tc->num_linefrags += num;
2367 lf = &tc->linefrags[tc->num_linefrags - 1];
2368 tc->length = lf->pos + lf->length - tc->pos;
2369
2370 layout_glyph = tc->pos + tc->length;
2371 /*
2372 We must have glyphs beyond all the soft-invalidated line frags,
2373 so comparing with glyphs->glyph_length is ok.
2374 */
2375 if (layout_glyph == glyphs->glyph_length)
2376 layout_char = glyphs->char_length;
2377 else
2378 layout_char = [self characterIndexForGlyphAtIndex: layout_glyph]; /* TODO? */
2379 }
2380
2381 -(NSRect) _softInvalidateLineFragRect: (int)index
2382 firstGlyph: (unsigned int *)first_glyph
2383 nextGlyph: (unsigned int *)next_glyph
2384 inTextContainer: (NSTextContainer *)textContainer
2385 {
2386 int i;
2387 textcontainer_t *tc;
2388 linefrag_t *lf;
2389 for (i = 0, tc = textcontainers; i < num_textcontainers; i++, tc++)
2390 if (tc->textContainer == textContainer)
2391 break;
2392 if (i == num_textcontainers)
2393 {
2394 NSLog(@"(%s): does not own text container", __PRETTY_FUNCTION__);
2395 return NSZeroRect;
2396 }
2397
2398 if (index >= tc->num_soft)
2399 return NSZeroRect;
2400
2401 lf = &tc->linefrags[tc->num_linefrags + index];
2402 *first_glyph = lf->pos;
2403 *next_glyph = lf->pos + lf->length;
2404 return lf->rect;
2405 }
2406
2407 -(unsigned int) _softInvalidateFirstGlyphInTextContainer: (NSTextContainer *)textContainer
2408 {
2409 int i;
2410 textcontainer_t *tc;
2411 for (i = 0, tc = textcontainers; i < num_textcontainers; i++, tc++)
2412 if (tc->textContainer == textContainer)
2413 break;
2414 if (i == num_textcontainers)
2415 {
2416 NSLog(@"(%s): does not own text container", __PRETTY_FUNCTION__);
2417 return (unsigned int)-1;
2418 }
2419 if (tc->num_soft)
2420 return tc->linefrags[tc->num_linefrags].pos;
2421 else
2422 return (unsigned int)-1;
2423 }
2424
2425 -(unsigned int) _softInvalidateNumberOfLineFragsInTextContainer: (NSTextContainer *)textContainer
2426 {
2427 int i;
2428 textcontainer_t *tc;
2429 for (i = 0, tc = textcontainers; i < num_textcontainers; i++, tc++)
2430 if (tc->textContainer == textContainer)
2431 break;
2432 if (i == num_textcontainers)
2433 {
2434 NSLog(@"(%s): does not own text container", __PRETTY_FUNCTION__);
2435 return (unsigned int)-1;
2436 }
2437 return tc->num_soft;
2438 }
2439
2440 @end
2441
2442
2443 /***** The rest *****/
2444
2445 @implementation GSLayoutManager
2446
2447 - init
2448 {
2449 if (!(self = [super init]))
2450 return nil;
2451
2452 [self _initGlyphs];
2453
2454 typesetter = [[GSTypesetter sharedSystemTypesetter] retain];
2455
2456 usesScreenFonts = YES;
2457
2458 return self;
2459 }
2460
2461
2462 -(void) dealloc
2463 {
2464 int i;
2465 textcontainer_t *tc;
2466
2467 free(rect_array);
2468 rect_array_size = 0;
2469 rect_array = NULL;
2470
2471 [self _freeLayout];
2472 for (i = 0, tc = textcontainers; i < num_textcontainers; i++, tc++)
2473 {
2474 [tc->textContainer release];
2475 }
2476 free(textcontainers);
2477 textcontainers = NULL;
2478
2479 [self _freeGlyphs];
2480
2481 DESTROY(typesetter);
2482
2483 [super dealloc];
2484 }
2485
2486
2487 -(void) _invalidateEverything
2488 {
2489 [self _freeLayout];
2490
2491 [self _freeGlyphs];
2492 [self _initGlyphs];
2493 }
2494
2495
2496 /**
2497 * Sets the text storage for the layout manager.
2498 * Use -replaceTextStorage: instead as a rule. - this method is really
2499 * more for internal use by the text system.
2500 * Invalidates the entire layout (should it??)
2501 */
2502 /*
2503 See [NSTextView -setTextContainer:] for more information about these calls.
2504 */
2505 - (void) setTextStorage: (NSTextStorage *)aTextStorage
2506 {
2507 int i;
2508 textcontainer_t *tc;
2509
2510 [self _invalidateEverything];
2511
2512 /*
2513 * Make a note of the new text storage object, but don't retain it.
2514 * The text storage is owning us - it retains us.
2515 */
2516 _textStorage = aTextStorage;
2517
2518 /*
2519 We send this message to all text containers so they can respond to the
2520 change (most importantly to let them tell their text views).
2521 */
2522 for (i = 0, tc = textcontainers; i < num_textcontainers; i++, tc++)
2523 {
2524 [tc->textContainer setLayoutManager: self];
2525 }
2526 [self _didInvalidateLayout];
2527 }
2528
2529 /**
2530 * Returns the text storage for this layout manager.
2531 */
2532 - (NSTextStorage *) textStorage
2533 {
2534 return _textStorage;
2535 }
2536
2537 /**
2538 * Replaces the text storage with a new one.<br />
2539 * Takes care (since layout managers are owned by text storages)
2540 * not to get self deallocated.
2541 */
2542 - (void) replaceTextStorage: (NSTextStorage *)newTextStorage
2543 {
2544 NSArray *layoutManagers = [_textStorage layoutManagers];
2545 NSEnumerator *enumerator = [layoutManagers objectEnumerator];
2546 GSLayoutManager *object;
2547
2548 /* Remove layout managers from old NSTextStorage object and add them to the
2549 new one. NSTextStorage's addLayoutManager invokes GSLayoutManager's
2550 setTextStorage method automatically, and that includes self. */
2551
2552 while ((object = (GSLayoutManager*)[enumerator nextObject]) != nil)
2553 {
2554 RETAIN(object);
2555 [_textStorage removeLayoutManager: object];
2556 [newTextStorage addLayoutManager: object];
2557 RELEASE(object);
2558 }
2559 }
2560
2561
2562 - (id) delegate
2563 {
2564 return _delegate;
2565 }
2566 - (void) setDelegate: (id)aDelegate
2567 {
2568 _delegate = aDelegate;
2569 }
2570
2571
2572 -(GSTypesetter *) typesetter
2573 {
2574 return typesetter;
2575 }
2576 -(void) setTypesetter: (GSTypesetter *)a_typesetter
2577 {
2578 ASSIGN(typesetter, a_typesetter);
2579 }
2580
2581
2582 - (BOOL) usesScreenFonts
2583 {
2584 return usesScreenFonts;
2585 }
2586 - (void) setUsesScreenFonts: (BOOL)flag
2587 {
2588 flag = !!flag;
2589 if (flag == usesScreenFonts)
2590 return;
2591 usesScreenFonts = flag;
2592 [self _invalidateEverything];
2593 [self _didInvalidateLayout];
2594 }
2595
2596 - (NSFont *) substituteFontForFont: (NSFont *)originalFont
2597 {
2598 NSFont *f;
2599 if (usesScreenFonts)
2600 {
2601 f = [originalFont screenFont];
2602 if (f)
2603 return f;
2604 }
2605 return originalFont;
2606 }
2607
2608
2609 - (void) setBackgroundLayoutEnabled: (BOOL)flag
2610 {
2611 flag = !!flag;
2612 if (flag == backgroundLayoutEnabled)
2613 return;
2614 backgroundLayoutEnabled = flag;
2615 /* TODO */
2616 }
2617 - (BOOL) backgroundLayoutEnabled
2618 {
2619 return backgroundLayoutEnabled;
2620 }
2621
2622 - (void) setShowsInvisibleCharacters: (BOOL)flag
2623 {
2624 flag = !!flag;
2625 if (flag == showsInvisibleCharacters)
2626 return;
2627
2628 showsInvisibleCharacters = flag;
2629 [self _invalidateEverything];
2630 [self _didInvalidateLayout];
2631 }
2632 - (BOOL) showsInvisibleCharacters
2633 {
2634 return showsInvisibleCharacters;
2635 }
2636
2637 - (void) setShowsControlCharacters: (BOOL)flag
2638 {
2639 flag = !!flag;
2640 if (flag == showsControlCharacters)
2641 return;
2642 showsControlCharacters = flag;
2643 [self _invalidateEverything];
2644 [self _didInvalidateLayout];
2645 }
2646 - (BOOL) showsControlCharacters
2647 {
2648 return showsControlCharacters;
2649 }
2650
2651
2652 /*
2653 Note that NSLayoutManager completely overrides this (to perform more
2654 intelligent invalidation of layout using the constraints on layout it
2655 has).
2656 */
2657 - (void) textStorage: (NSTextStorage *)aTextStorage
2658 edited: (unsigned int)mask
2659 range: (NSRange)range
2660 changeInLength: (int)lengthChange
2661 invalidatedRange: (NSRange)invalidatedRange
2662 {
2663 NSRange r;
2664
2665 if (!(mask & NSTextStorageEditedCharacters))
2666 lengthChange = 0;
2667
2668 [self invalidateGlyphsForCharacterRange: invalidatedRange
2669 changeInLength: lengthChange
2670 actualCharacterRange: &r];
2671
2672 /*
2673 See the comments above -invalidateLayoutForCharacterRange:isSoft:
2674 actualCharacterRange: for information on why we invalidate everything
2675 here.
2676 */
2677 [self _invalidateLayoutFromContainer: 0];
2678
2679 [self _didInvalidateLayout];
2680 }
2681
2682
2683 /* These must be in the main implementation so backends can override them
2684 in a category safely. */
2685
2686 /* These three methods should be implemented in the backend, but there's
2687 a dummy here. It maps each character to a glyph with the glyph id==unicode
2688 index, except control characters, which are mapped to NSControlGlyph. */
2689
2690 -(unsigned int) _findSafeBreakMovingBackwardFrom: (unsigned int)ch
2691 {
2692 return ch;
2693 }
2694
2695 -(unsigned int) _findSafeBreakMovingForwardFrom: (unsigned int)ch
2696 {
2697 return ch;
2698 }
2699
2700
2701 /* TODO2: put good control code handling here in a way that makes it easy
2702 for the backends to use it */
2703 -(void) _generateGlyphsForRun: (glyph_run_t *)run at: (unsigned int)pos
2704 {
2705 int i, c = run->head.char_length;
2706 unsigned int ch;
2707 unichar buf[c];
2708
2709 glyph_t *g;
2710
2711 NSCharacterSet *cs = [NSCharacterSet controlCharacterSet];
2712 IMP characterIsMember = [cs methodForSelector: @selector(characterIsMember:)];
2713
2714 run->head.glyph_length = c;
2715 run->glyphs = malloc(sizeof(glyph_t) * c);
2716 memset(run->glyphs, 0, sizeof(glyph_t) * c);
2717
2718 [[_textStorage string] getCharacters: buf
2719 range: NSMakeRange(pos, c)];
2720
2721 g = run->glyphs;
2722 for (i = 0; i < c; i++)
2723 {
2724 ch = buf[i];
2725 g->char_offset = i;
2726 if (characterIsMember(cs, @selector(characterIsMember:), ch))
2727 g->g = NSControlGlyph;
2728 else if (ch == NSAttachmentCharacter)
2729 g->g = GSAttachmentGlyph;
2730 else
2731 g->g = ch;
2732 g++;
2733 }
2734 }
2735
2736 @end
2737

savannah-hackers-public@gnu.org
ViewVC Help
Powered by ViewVC 1.1.26