/[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.37 - (show annotations) (download)
Mon Sep 29 21:25:46 2003 UTC (20 years, 6 months ago) by FredKiefer
Branch: MAIN
CVS Tags: gui_0_9_2, gui-0_9_0, gui-0_9_1, alex_latest_semistable
Branch point for: kazunobu_input_management
Changes since 1.36: +14 -0 lines
Added missing method [isValidGlyphIndex:].

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 - (BOOL) isValidGlyphIndex: (unsigned int)glyphIndex
723 {
724 if (glyphIndex == (unsigned int)-1)
725 return NO;
726
727 if (glyphs->glyph_length <= glyphIndex)
728 {
729 return NO;
730 }
731 else
732 {
733 return YES;
734 }
735 }
736
737 - (unsigned int) getGlyphs: (NSGlyph *)glyphArray
738 range: (NSRange)glyphRange
739 {
740 glyph_run_t *r;
741 NSGlyph *g;
742 unsigned int pos;
743 unsigned int num;
744 unsigned int i, j, k;
745
746 if (glyphRange.length == 0)
747 {
748 return 0;
749 }
750
751 pos = NSMaxRange(glyphRange) - 1;
752 if (glyphs->glyph_length <= pos)
753 {
754 [self _generateGlyphsUpToGlyph: pos];
755 if (glyphs->glyph_length <= pos)
756 {
757 [NSException raise: NSRangeException
758 format: @"%s glyph range out of range", __PRETTY_FUNCTION__];
759 return 0;
760 }
761 }
762
763 r = run_for_glyph_index(glyphRange.location, glyphs, &pos, NULL);
764 if (!r)
765 { /* shouldn't happen */
766 [NSException raise: NSRangeException
767 format: @"%s glyph range out of range", __PRETTY_FUNCTION__];
768 return 0;
769 }
770
771 g = glyphArray;
772 num = 0;
773
774 while (1)
775 {
776 if (pos < glyphRange.location)
777 j = glyphRange.location - pos;
778 else
779 j = 0;
780
781 k = NSMaxRange(glyphRange) - pos;
782 if (k > r->head.glyph_length)
783 k = r->head.glyph_length;
784 if (k <= j)
785 break;
786
787 /* TODO? only "displayed" glyphs */
788 for (i = j; i < k; i++)
789 {
790 *g++=r->glyphs[i].g;
791 num++;
792 }
793
794 pos += r->head.glyph_length;
795 r = (glyph_run_t *)r->head.next;
796 if (!r)
797 break;
798 }
799
800 return num;
801 }
802
803 - (unsigned int) characterIndexForGlyphAtIndex: (unsigned int)glyphIndex
804 {
805 glyph_run_t *r;
806 int pos, cpos;
807
808 if (glyphs->glyph_length <= glyphIndex)
809 {
810 [self _generateGlyphsUpToGlyph: glyphIndex];
811 if (glyphs->glyph_length <= glyphIndex)
812 {
813 [NSException raise: NSRangeException
814 format: @"%s glyph index out of range", __PRETTY_FUNCTION__];
815 return 0;
816 }
817 }
818
819 r = run_for_glyph_index(glyphIndex, glyphs, &pos, &cpos);
820 if (!r)
821 {
822 [NSException raise: NSRangeException
823 format: @"%s glyph index out of range", __PRETTY_FUNCTION__];
824 return 0;
825 }
826
827 return cpos + r->glyphs[glyphIndex - pos].char_offset;
828 }
829
830
831 - (NSRange) characterRangeForGlyphRange: (NSRange)glyphRange
832 actualGlyphRange: (NSRange *)actualGlyphRange
833 {
834 glyph_run_t *r;
835 NSRange real_range, char_range;
836 unsigned int cpos, pos;
837 unsigned j;
838
839 if (NSMaxRange(glyphRange) == 0)
840 {
841 if (actualGlyphRange)
842 *actualGlyphRange = glyphRange;
843 return NSMakeRange(0, 0);
844 }
845
846 pos = NSMaxRange(glyphRange) - 1;
847 if (glyphs->glyph_length <= pos)
848 {
849 [self _generateGlyphsUpToGlyph: pos];
850 if (glyphs->glyph_length <= pos)
851 {
852 [NSException raise: NSRangeException
853 format: @"%s glyph range out of range", __PRETTY_FUNCTION__];
854 return NSMakeRange(0, 0);
855 }
856 }
857
858 r = run_for_glyph_index(glyphRange.location, glyphs, &pos, &cpos);
859 if (!r)
860 {
861 [NSException raise: NSRangeException
862 format: @"%s glyph range out of range", __PRETTY_FUNCTION__];
863 return NSMakeRange(0, 0);
864 }
865
866 j = cpos + r->glyphs[glyphRange.location - pos].char_offset;
867 char_range.location = j;
868
869 /* scan backwards to find the real first glyph */
870 {
871 glyph_run_t *r2;
872 unsigned int adj, cadj;
873 int i;
874
875 i = glyphRange.location - pos;
876 r2 = r;
877 adj = pos;
878 cadj = cpos;
879 while (r2->glyphs[i].char_offset + cadj == j)
880 {
881 i--;
882 while (i < 0)
883 {
884 if (!r2->prev)
885 break;
886 r2 = (glyph_run_t *)r2->prev;
887 i = r2->head.glyph_length - 1;
888 adj -= r2->head.glyph_length;
889 cadj -= r2->head.char_length;
890 }
891 if (i < 0)
892 break;
893 }
894 real_range.location = i + 1 + adj;
895 }
896
897 /* the range is likely short, so we can do better then a completely new
898 search */
899 r = run_for_glyph_index(glyphRange.location + glyphRange.length - 1,
900 glyphs, &pos, &cpos);
901 if (!r)
902 {
903 [NSException raise: NSRangeException
904 format: @"%s glyph range out of range", __PRETTY_FUNCTION__];
905 return NSMakeRange(0, 0);
906 }
907
908 j = cpos + r->glyphs[glyphRange.location + glyphRange.length - 1 - pos].char_offset;
909
910 /* scan forwards to find the real last glyph */
911 {
912 glyph_run_t *r2;
913 unsigned int adj, cadj;
914 unsigned int last = 0;
915 unsigned int i;
916
917 i = glyphRange.location + glyphRange.length - 1 - pos;
918 r2 = r;
919 adj = pos;
920 cadj = cpos;
921 while (r2->glyphs[i].char_offset + cadj == j)
922 {
923 GLYPH_STEP_FORWARD(r2,i,adj,cadj)
924 if (i==r2->head.glyph_length)
925 {
926 last = cadj + r2->head.char_length;
927 goto found;
928 }
929 }
930 last = r2->glyphs[i].char_offset + cadj;
931 found:
932 real_range.length = i + adj - real_range.location;
933 char_range.length = last - char_range.location;
934 }
935
936 if (actualGlyphRange)
937 *actualGlyphRange = real_range;
938 return char_range;
939 }
940
941
942 - (NSRange) glyphRangeForCharacterRange: (NSRange)charRange
943 actualCharacterRange: (NSRange *)actualCharRange
944 {
945 NSRange char_range, glyph_range;
946 glyph_run_t *r;
947 unsigned int cpos, pos;
948 unsigned int i, target;
949
950 /* TODO: should this really be valid?
951
952 This is causing all kinds of problems when border glyph ranges are passed
953 to other functions. Better to keep the layout manager clean of all this and
954 let NSTextView deal with it.
955 */
956 #if 1
957 if (charRange.length == 0 && charRange.location == [[_textStorage string] length])
958 {
959 if (actualCharRange)
960 *actualCharRange = NSMakeRange([[_textStorage string] length], 0);
961 return NSMakeRange([self numberOfGlyphs], 0);
962 }
963 #endif
964 /* TODO: this case is also dubious, but it makes sense to return like this,
965 so it's mostly the caller's fault */
966 if (charRange.length == 0)
967 {
968 NSLog(@"Warning: %s called with zero-length range", __PRETTY_FUNCTION__);
969 if (actualCharRange)
970 *actualCharRange = NSMakeRange(0, 0);
971 return NSMakeRange(0, 0);
972 }
973
974 pos = NSMaxRange(charRange) - 1;
975 [self _generateGlyphsUpToCharacter: pos];
976 if (glyphs->char_length <= pos)
977 {
978 [NSException raise: NSRangeException
979 format: @"%s character range out of range", __PRETTY_FUNCTION__];
980 return NSMakeRange(0, 0);
981 }
982
983 target = charRange.location;
984 r = [self _glyphForCharacter: target
985 index: &i
986 positions: &pos : &cpos];
987 glyph_range.location = i + pos;
988 char_range.location = r->glyphs[i].char_offset + cpos;
989
990 target = NSMaxRange(charRange) - 1;
991 r = [self _glyphForCharacter: target
992 index: &i
993 positions: &pos : &cpos];
994
995 GLYPH_SCAN_FORWARD(r, i, pos, cpos, r->glyphs[i].char_offset + cpos <= target)
996
997 glyph_range.length = i + pos - glyph_range.location;
998 if (i == r->head.glyph_length)
999 char_range.length = glyphs->char_length - char_range.location;
1000 else
1001 char_range.length = r->glyphs[i].char_offset + cpos - char_range.location;
1002
1003 if (actualCharRange)
1004 *actualCharRange = char_range;
1005 return glyph_range;
1006 }
1007
1008
1009 /*
1010 TODO? this might currently lead to continued runs not being marked as
1011 continued runs. this will only happen at safe break spots, though, so
1012 it should still be safe. might lose opportunities to merge runs, though.
1013 */
1014
1015 /*
1016 This is hairy.
1017
1018 The ranges passed in and out of this method are ranges _after_ the change.
1019 Internally, we switch between before- and after-indices. Comments mark the
1020 places where we switch.
1021 */
1022 - (void) invalidateGlyphsForCharacterRange: (NSRange)range
1023 changeInLength: (int)lengthChange
1024 actualCharacterRange: (NSRange *)actualRange
1025 {
1026 BOOL trailing;
1027 glyph_run_head_t *context[SKIP_LIST_DEPTH];
1028 glyph_run_head_t *h;
1029 glyph_run_t *r;
1030 NSRange rng;
1031 int position[SKIP_LIST_DEPTH];
1032 unsigned int cpos;
1033 unsigned int ts_length;
1034 int gap;
1035 int level;
1036 unsigned int ch;
1037
1038
1039 /*
1040 We always clear out the cached run information to be safe.
1041 */
1042 cached_run = NULL;
1043
1044 /* Set it now for early returns. */
1045 if (actualRange)
1046 *actualRange = range;
1047
1048
1049 // printf("\n +++ range=(%i+%i) lengthChange=%i\n", range.location, range.length, lengthChange);
1050 [self _sanityChecks];
1051 // [self _glyphDumpRuns];
1052
1053
1054 /* Switch to before-indices. */
1055 range.length -= lengthChange;
1056 // printf("invalidate %i+%i=%i\n", range.location, range.length, range.location+range.length);
1057
1058 ts_length = [_textStorage length];
1059
1060
1061 /*
1062 Find out what range we actually need to invalidate. This depends on how
1063 context affects glyph generation.
1064 */
1065 ch = range.location;
1066 if (ch > 0)
1067 {
1068 ch = [self _findSafeBreakMovingBackwardFrom: ch];
1069 range.length += range.location - ch;
1070 range.location = ch;
1071 }
1072
1073 ch = ch + range.length + lengthChange;
1074 if (ch < ts_length)
1075 {
1076 ch = [self _findSafeBreakMovingForwardFrom: ch];
1077
1078 ch -= lengthChange;
1079 range.length = ch - range.location;
1080 }
1081
1082 /*
1083 We now have the range to invalidate in 'range' (indices before the
1084 change).
1085 */
1086 // printf("adjusted to %i+%i\n", range.location, range.length);
1087
1088 ch = range.location;
1089
1090 /*
1091 Find the first run (and context) for the range.
1092 */
1093 h = glyphs;
1094 cpos = 0;
1095 for (level = SKIP_LIST_DEPTH - 1; level >= 0; level--)
1096 {
1097 while (cpos + h->char_length <= ch)
1098 {
1099 cpos += h->char_length;
1100 h = h->next;
1101 if (!h)
1102 {
1103 /*
1104 No runs have been created for the range, so there's nothing
1105 to invalidate.
1106 */
1107 // printf("no runs created yet\n");
1108 return;
1109 }
1110 }
1111 context[level] = h;
1112 position[level] = cpos;
1113 h++;
1114 }
1115 h--;
1116 r = (glyph_run_t *)h;
1117
1118 /*
1119 Now we have the first run that intersects the range we're invalidating
1120 in 'r' (and context in 'context' and 'position').
1121 */
1122
1123 // printf("split if %i+%i > %i+%i\n", cpos, r->head.char_length, ch, range.length);
1124 /*
1125 If 'r' extends beyond the invalidated range, split off the trailing, valid
1126 part to a new run. The reason we need to do this is that we must have runs
1127 for the first glyph not invalidated or the deletion loop below will fail.
1128 */
1129 if (cpos + r->head.char_length > ch + range.length && ch != cpos)
1130 {
1131 glyph_run_t *new;
1132 glyph_run_head_t *hn;
1133 int i;
1134
1135 new = run_insert(context);
1136 new->head.char_length = cpos + r->head.char_length - (ch + range.length);
1137 [self _run_copy_attributes: new : r];
1138 /* OPT: keep valid glyphs
1139 this seems to be a fairly rare case
1140 */
1141 hn = &new->head;
1142 hn--;
1143 for (i = 1; i <= new->level; i++, hn--)
1144 run_fix_head(hn);
1145
1146 if (new->head.next)
1147 ((glyph_run_t *)new->head.next)->prev = (glyph_run_head_t *)new;
1148
1149 r->head.char_length -= new->head.char_length;
1150 }
1151
1152 /*
1153 Set things up. We want 'r' to be the last run we want to keep.
1154 */
1155 if (ch == cpos)
1156 {
1157 /*
1158 This run begins exactly at the beginning of the invalidated range.
1159 Since we want 'r' to be the last run to keep, we actually want it
1160 to be the previous run. Thus, we step backwards in the skip list
1161 to get the previous run.
1162 */
1163 glyph_run_head_t *h2;
1164
1165 h2 = h - r->level;
1166 h = context[r->level + 1];
1167 cpos = position[r->level + 1];
1168 h++;
1169 for (level = r->level; level >= 0; level--)
1170 {
1171 while (h->next != h2)
1172 {
1173 cpos += h->char_length;
1174 h = h->next;
1175 }
1176 position[level] = cpos;
1177 context[level] = h;
1178 h++;
1179 h2++;
1180 }
1181 h--;
1182 r = (glyph_run_t *)h;
1183 gap = 0;
1184 }
1185 else
1186 {
1187 /*
1188 This run begins before the invalidated range. Resize it so it ends
1189 just before it.
1190 */
1191 gap = r->head.char_length + cpos - ch;
1192 r->head.char_length = ch - cpos;
1193 /* OPT!!! keep valid glyphs */
1194 if (r->head.complete)
1195 {
1196 r->head.glyph_length = 0;
1197 r->head.complete = 0;
1198 free(r->glyphs);
1199 r->glyphs = NULL;
1200 }
1201 }
1202
1203 /*
1204 'r' is the last run we should keep, 'context' and 'position' are set up
1205 for it, 'gap' is the number of characters already deleted.
1206
1207 Now we delete all runs completely invalidated.
1208 */
1209 {
1210 glyph_run_t *next;
1211 unsigned int max = range.location + range.length;
1212 int i;
1213
1214 cpos += gap + r->head.char_length;
1215 while (1)
1216 {
1217 next = (glyph_run_t *)r->head.next;
1218
1219 /* We reached the end of all created runs. */
1220 if (!next)
1221 break;
1222
1223 NSAssert(max >= cpos,
1224 @"no run for first glyph beyond invalidated range");
1225
1226 /* Clean cut, just stop. */
1227 if (max == cpos)
1228 break;
1229
1230 /*
1231 Part of this run extends beyond the invalidated range. Resize it
1232 so it's completely beyond the invalidated range and stop.
1233 */
1234 if (max < cpos + next->head.char_length)
1235 {
1236 glyph_run_head_t *hn;
1237 /* adjust final run */
1238 /* OPT!!! keep valid glyphs */
1239 if (next->head.complete)
1240 {
1241 next->head.complete = 0;
1242 next->head.glyph_length = 0;
1243 free(next->glyphs);
1244 next->glyphs = NULL;
1245 }
1246 next->head.char_length -= max - cpos;
1247
1248 hn = &next->head;
1249 hn--;
1250 for (i = 1; i <= next->level; i++, hn--)
1251 run_fix_head(hn);
1252
1253 break;
1254 }
1255
1256 cpos += next->head.char_length;
1257
1258 /*
1259 This run is completely inside the invalidated range. Remove it.
1260 The context run heads will be adjusted later.
1261 */
1262 if (next->head.next)
1263 ((glyph_run_t *)next->head.next)->prev = &r->head;
1264
1265 for (i = 0; i <= next->level; i++)
1266 context[i]->next = context[i]->next->next;
1267 h = &next->head;
1268 if (h->complete)
1269 free(next->glyphs);
1270 [self _run_free_attributes: next];
1271 h -= next->level;
1272 free(h);
1273 h = NULL;
1274 }
1275 trailing = !next;
1276 }
1277
1278 /* printf("deleted\n");
1279 [self _glyphDumpRuns];*/
1280
1281 /* Switch back to after-indices. */
1282 range.length += lengthChange;
1283
1284 /*
1285 'r' is the last run we want to keep, and the next run is the next
1286 uninvalidated run. We need to insert new runs for invalidated range
1287 after 'r'.
1288
1289 As we create new runs, we move the context forward. When we do this, we
1290 adjust their heads with updated information. When we're done, we update
1291 all the remaining heads.
1292 */
1293 // printf("create runs for %i+%i\n", range.location, range.length);
1294 { /* OPT: this is creating more runs than it needs to */
1295 NSDictionary *attributes;
1296 glyph_run_t *new;
1297 unsigned int max = range.location + range.length;
1298 int i;
1299
1300 ch = range.location;
1301 while (ch < max)
1302 {
1303 attributes = [_textStorage attributesAtIndex: ch
1304 longestEffectiveRange: &rng
1305 inRange: NSMakeRange(0, [_textStorage length])];
1306
1307 /* printf("at %i, max=%i, effective range (%i+%i)\n",
1308 ch, max, rng.location, rng.length);*/
1309
1310 /*
1311 Catch a common case. If the new run would be a continuation of the
1312 previous run, and the previous run is short, we resize the previous
1313 run instead of creating a new run.
1314
1315 (Note that we must make sure that we don't merge with the dummy runs
1316 at the very front.)
1317
1318 This happens a lot with repeated single-character insertions, aka.
1319 typing in a text view.
1320 */
1321 if (rng.location < ch && context[0]->char_length &&
1322 context[0]->char_length < 16)
1323 {
1324 rng.length -= ch - rng.location;
1325 rng.location = ch;
1326 if (ch + rng.length > max)
1327 {
1328 rng.length = max - ch;
1329 }
1330 new = (glyph_run_t *)context[0];
1331 if (new->head.complete)
1332 {
1333 free(new->glyphs);
1334 new->glyphs = NULL;
1335 new->head.glyph_length = 0;
1336 new->head.complete = 0;
1337 }
1338 new->head.char_length += rng.length;
1339 ch = NSMaxRange(rng);
1340 continue;
1341 }
1342
1343 new = run_insert(context);
1344
1345 /*
1346 We have the longest range the attributes allow us to create a run
1347 for. Since this might overlap the previous and next runs, we might
1348 need to adjust the location and length of the range we create a
1349 run for.
1350
1351 OPT: If the overlapped run is short, we might want to clear out
1352 its glyphs and extend it to cover our range. This should result
1353 in fewer runs being created for large sequences of single character
1354 adds.
1355 */
1356 if (rng.location < ch)
1357 {
1358 /*
1359 The new run has the same attributes as the previous run, so we
1360 mark it is as a continued run.
1361 */
1362 new->continued = 1;
1363 rng.length -= ch - rng.location;
1364 rng.location = ch;
1365 }
1366 if (ch + rng.length > max)
1367 {
1368 /*
1369 The new run has the same attributes as the next run, so we mark
1370 the next run as a continued run.
1371 */
1372 if (new->head.next)
1373 ((glyph_run_t *)new->head.next)->continued = 1;
1374 rng.length = max - ch;
1375 }
1376
1377 /* See comment in -_generateRunsToCharacter:. */
1378 if (rng.length > MAX_RUN_LENGTH)
1379 {
1380 unsigned int safe_break = rng.location + MAX_RUN_LENGTH;
1381 safe_break = [self _findSafeBreakMovingForwardFrom: safe_break];
1382 if (safe_break < NSMaxRange(rng))
1383 rng.length = safe_break - rng.location;
1384 }
1385
1386 // printf("adjusted length: %i\n", rng.length);
1387 new->head.char_length = rng.length;
1388
1389 [self _run_cache_attributes: new : attributes];
1390
1391 h = &new->head;
1392 for (i = 0; i <= new->level; i++, h--)
1393 {
1394 if (i)
1395 run_fix_head(context[i]);
1396 context[i] = h;
1397 }
1398 ch += new->head.char_length;
1399 }
1400
1401 if (context[0]->next)
1402 ((glyph_run_t *)context[0]->next)->prev = context[0];
1403 }
1404
1405 /* Fix up the remaining context run heads. */
1406 {
1407 int i;
1408 for (i = 1; i < SKIP_LIST_DEPTH; i++)
1409 {
1410 run_fix_head(context[i]);
1411 }
1412 }
1413
1414 if (actualRange)
1415 *actualRange = range;
1416
1417 // [self _glyphDumpRuns];
1418 [self _sanityChecks];
1419 }
1420
1421
1422 #define GET_GLYPH \
1423 glyph_run_t *r; \
1424 int pos, cpos; \
1425 \
1426 if (glyphs->glyph_length <= idx) \
1427 { \
1428 [self _generateGlyphsUpToGlyph: idx]; \
1429 if (glyphs->glyph_length <= idx) \
1430 { \
1431 [NSException raise: NSRangeException \
1432 format: @"%s glyph range out of range", __PRETTY_FUNCTION__]; \
1433 } \
1434 } \
1435 \
1436 r = run_for_glyph_index(idx, glyphs, &pos, &cpos); \
1437 if (!r) \
1438 { \
1439 [NSException raise: NSRangeException \
1440 format: @"%s glyph range out of range", __PRETTY_FUNCTION__]; \
1441 } \
1442 idx -= pos;
1443
1444 - (void) setDrawsOutsideLineFragment: (BOOL)flag
1445 forGlyphAtIndex: (unsigned int)idx
1446 {
1447 GET_GLYPH
1448 r->glyphs[idx].drawsOutsideLineFragment = !!flag;
1449 }
1450 - (BOOL) drawsOutsideLineFragmentForGlyphAtIndex: (unsigned int)idx
1451 {
1452 GET_GLYPH
1453 return r->glyphs[idx].drawsOutsideLineFragment;
1454 }
1455
1456 - (void) setNotShownAttribute: (BOOL)flag
1457 forGlyphAtIndex: (unsigned int)idx
1458 {
1459 GET_GLYPH
1460 r->glyphs[idx].isNotShown = !!flag;
1461 }
1462 - (BOOL) notShownAttributeForGlyphAtIndex: (unsigned int)idx
1463 {
1464 GET_GLYPH
1465 return r->glyphs[idx].isNotShown;
1466 }
1467
1468
1469 - (NSFont *) effectiveFontForGlyphAtIndex: (unsigned int)idx
1470 range: (NSRange *)range
1471 {
1472 GET_GLYPH
1473 if (range)
1474 *range = NSMakeRange(pos, r->head.glyph_length);
1475 return r->font;
1476 }
1477
1478
1479 - (void) insertGlyph: (NSGlyph)aGlyph
1480 atGlyphIndex: (unsigned int)glyphIndex
1481 characterIndex: (unsigned int)charIndex
1482 {
1483 NSLog(@"Internal method %s called", __PRETTY_FUNCTION__);
1484 }
1485 - (void) replaceGlyphAtIndex: (unsigned int)glyphIndex
1486 withGlyph: (NSGlyph)newGlyph
1487 {
1488 NSLog(@"Internal method %s called", __PRETTY_FUNCTION__);
1489 }
1490 - (void) deleteGlyphsInRange: (NSRange)aRange
1491 {
1492 NSLog(@"Internal method %s called", __PRETTY_FUNCTION__);
1493 }
1494 - (void) setCharacterIndex: (unsigned int)charIndex
1495 forGlyphAtIndex: (unsigned int)glyphIndex
1496 {
1497 NSLog(@"Internal method %s called", __PRETTY_FUNCTION__);
1498 }
1499
1500
1501 - (void) setIntAttribute: (int)attributeTag
1502 value: (int)anInt
1503 forGlyphAtIndex: (unsigned int)glyphIndex
1504 {
1505 [self subclassResponsibility: _cmd];
1506 }
1507 - (int) intAttribute: (int)attributeTag
1508 forGlyphAtIndex: (unsigned int)glyphIndex
1509 {
1510 [self subclassResponsibility: _cmd];
1511 return 0;
1512 }
1513
1514 @end
1515
1516
1517
1518 /***** Layout handling *****/
1519
1520 @implementation GSLayoutManager (layout_helpers)
1521
1522 -(void) _invalidateLayoutFromContainer: (int)idx
1523 {
1524 int i, j;
1525 textcontainer_t *tc;
1526 linefrag_t *lf;
1527
1528 extra_textcontainer = nil;
1529
1530 for (i = idx, tc = textcontainers + idx; i < num_textcontainers; i++, tc++)
1531 {
1532 tc->complete = NO;
1533 if (tc->linefrags)
1534 {
1535 for (j = 0, lf = tc->linefrags; j < tc->num_linefrags + tc->num_soft; j++, lf++)
1536 {
1537 if (lf->points)
1538 free(lf->points);
1539 if (lf->attachments)
1540 free(lf->attachments);
1541 }
1542
1543 free(tc->linefrags);
1544 }
1545 tc->linefrags = NULL;
1546 tc->num_linefrags = tc->num_soft = 0;
1547 tc->size_linefrags = 0;
1548 tc->pos = tc->length = 0;
1549 tc->was_invalidated = YES;
1550 }
1551 for (i = idx - 1, tc = textcontainers + idx - 1; i >= 0; i--, tc--)
1552 {
1553 if (tc->num_linefrags)
1554 {
1555 layout_glyph = tc->pos + tc->length;
1556 if (layout_glyph == glyphs->glyph_length)
1557 layout_char = glyphs->char_length;
1558 else
1559 layout_char = [self characterIndexForGlyphAtIndex: layout_glyph]; /* TODO? */
1560 return;
1561 }
1562 }
1563 layout_glyph = layout_char = 0;
1564 }
1565
1566 -(void) _freeLayout
1567 {
1568 [self _invalidateLayoutFromContainer: 0];
1569 }
1570
1571
1572 -(void) _doLayout
1573 {
1574 int i, j;
1575 textcontainer_t *tc;
1576 unsigned int next;
1577 NSRect prev;
1578 BOOL delegate_responds;
1579
1580 delegate_responds = [_delegate respondsToSelector:
1581 @selector(layoutManager:didCompleteLayoutForTextContainer:atEnd:)];
1582
1583 next = layout_glyph;
1584 for (i = 0, tc = textcontainers; i < num_textcontainers; i++, tc++)
1585 {
1586 if (tc->complete)
1587 continue;
1588
1589 while (1)
1590 {
1591 if (tc->num_linefrags)
1592 prev = tc->linefrags[tc->num_linefrags - 1].rect;
1593 else
1594 prev = NSZeroRect;
1595 j = [typesetter layoutGlyphsInLayoutManager: self
1596 inTextContainer: tc->textContainer
1597 startingAtGlyphIndex: next
1598 previousLineFragmentRect: prev
1599 nextGlyphIndex: &next
1600 numberOfLineFragments: 0];
1601 if (j)
1602 break;
1603 }
1604 tc->complete = YES;
1605 if (tc->num_soft)
1606 {
1607 /*
1608 If there is any soft invalidated layout information left, remove
1609 it.
1610 */
1611 int k;
1612 linefrag_t *lf;
1613 for (k = tc->num_linefrags, lf = tc->linefrags + k; k < tc->num_linefrags + tc->num_soft; k++, lf++)
1614 {
1615 if (lf->points)
1616 {
1617 free(lf->points);
1618 lf->points = NULL;
1619 }
1620 if (lf->attachments)
1621 {
1622 free(lf->attachments);
1623 lf->attachments = NULL;
1624 }
1625 }
1626 tc->num_soft = 0;
1627 }
1628 if (delegate_responds)
1629 {
1630 [_delegate layoutManager: self
1631 didCompleteLayoutForTextContainer: tc->textContainer
1632 atEnd: j == 2];
1633 /* The call might have resulted in more text containers being
1634 added, so 'textcontainers' might have moved. */
1635 tc = textcontainers + i;
1636 }
1637 if (j == 2)
1638 {
1639 break;
1640 }
1641 if (i == num_textcontainers && delegate_responds)
1642 {
1643 [_delegate layoutManager: self
1644 didCompleteLayoutForTextContainer: nil
1645 atEnd: NO];
1646 }
1647 }
1648 }
1649
1650
1651 -(void) _doLayoutToGlyph: (unsigned int)glyphIndex
1652 {
1653 [self _doLayout];
1654 }
1655
1656 -(void) _doLayoutToContainer: (int)cindex
1657 {
1658 [self _doLayout];
1659 }
1660
1661
1662 -(void) _didInvalidateLayout
1663 {
1664 int i;
1665 textcontainer_t *tc;
1666
1667 for (tc = textcontainers, i = 0; i < num_textcontainers; i++, tc++)
1668 {
1669 tc->was_invalidated = NO;
1670 }
1671 }
1672
1673 @end
1674
1675
1676 @implementation GSLayoutManager (layout)
1677
1678
1679 /*
1680 In the general case, we can't make any assumptions about how layout might
1681 interact between line frag rects. To be safe in all cases, we must
1682 invalidate all layout information.
1683
1684 TODO:
1685 We could handle this by assuming that whoever calls this knows exactly what
1686 needs to be invalidated. We won't be using it internally, anyway, so it
1687 doesn't matter much to us, and it would make more advanced things possible
1688 for external callers. On the other hand, it would be easy to break things
1689 by calling this incorrectly.
1690 */
1691 - (void) invalidateLayoutForCharacterRange: (NSRange)aRange
1692 isSoft: (BOOL)flag
1693 actualCharacterRange: (NSRange *)actualRange
1694 {
1695 [self _invalidateLayoutFromContainer: 0];
1696 }
1697
1698
1699 #define SETUP_STUFF \
1700 unsigned int max = glyphRange.location + glyphRange.length; \
1701 \
1702 [self _generateGlyphsUpToGlyph: max - 1]; \
1703 if (glyphs->glyph_length < max) \
1704 { \
1705 [NSException raise: NSRangeException \
1706 format: @"%s: glyph range out of range", __PRETTY_FUNCTION__]; \
1707 return; \
1708 }
1709
1710 - (void) setTextContainer: (NSTextContainer *)aTextContainer
1711 forGlyphRange: (NSRange)glyphRange
1712 {
1713 textcontainer_t *tc;
1714 int i;
1715 SETUP_STUFF
1716
1717 for (i = 0, tc = textcontainers; i < num_textcontainers; i++, tc++)
1718 if (tc->textContainer == aTextContainer)
1719 break;
1720 if (i == num_textcontainers)
1721 {
1722 NSLog(@"%s: doesn't own text container", __PRETTY_FUNCTION__);
1723 return;
1724 }
1725
1726 /* Assume that no line frags means that layout hasn't started yet. */
1727 if (tc->num_linefrags)
1728 {
1729 if (glyphRange.location != tc->pos + tc->length)
1730 {
1731 [NSException raise: NSRangeException
1732 format: @"%s: glyph range not consistent with existing layout",
1733 __PRETTY_FUNCTION__];
1734 return;
1735 }
1736 tc->length += glyphRange.length;
1737 }
1738 else if (!i)
1739 {
1740 if (glyphRange.location)
1741 {
1742 [NSException raise: NSRangeException
1743 format: @"%s: glyph range not consistent with existing layout",
1744 __PRETTY_FUNCTION__];
1745 return;
1746 }
1747 tc->pos = 0;
1748 tc->length = glyphRange.length;
1749 }
1750 else
1751 {
1752 if (tc[-1].pos + tc[-1].length != glyphRange.location)
1753 {
1754 [NSException raise: NSRangeException
1755 format: @"%s: glyph range not consistent with existing layout",
1756 __PRETTY_FUNCTION__];
1757 return;
1758 }
1759 tc->pos = glyphRange.location;
1760 tc->length = glyphRange.length;
1761 }
1762
1763 {
1764 unsigned int gpos;
1765 unsigned int g;
1766 glyph_t *glyph;
1767 glyph_run_t *run = run_for_glyph_index(glyphRange.location, glyphs, &gpos, NULL);
1768
1769 g = glyphRange.location;
1770 glyph = &run->glyphs[g - gpos];
1771 while (g < glyphRange.location + glyphRange.length)
1772 {
1773 if (g == gpos + run->head.glyph_length)
1774 {
1775 gpos += run->head.glyph_length;
1776 run = (glyph_run_t *)run->head.next;
1777 glyph = run->glyphs;
1778 }
1779
1780 glyph->isNotShown = NO;
1781 glyph->drawsOutsideLineFragment = NO;
1782 g++;
1783 glyph++;
1784 }
1785 }
1786
1787 layout_glyph = tc->pos + tc->length;
1788 if (layout_glyph == glyphs->glyph_length)
1789 layout_char = glyphs->char_length;
1790 else
1791 layout_char = [self characterIndexForGlyphAtIndex: layout_glyph];
1792 }
1793
1794 - (void) setLineFragmentRect: (NSRect)fragmentRect
1795 forGlyphRange: (NSRange)glyphRange
1796 usedRect: (NSRect)usedRect
1797 {
1798 textcontainer_t *tc;
1799 int i;
1800 linefrag_t *lf;
1801
1802 SETUP_STUFF
1803
1804 for (i = 0, tc = textcontainers; i < num_textcontainers; i++, tc++)
1805 {
1806 if (tc->pos <= glyphRange.location &&
1807 tc->pos + tc->length >= glyphRange.location + glyphRange.length)
1808 break;
1809 }
1810 if (i == num_textcontainers)
1811 {
1812 [NSException raise: NSRangeException
1813 format: @"%s: glyph range not consistent with existing layout",
1814 __PRETTY_FUNCTION__];
1815 return;
1816 }
1817
1818 /* Make sure the given glyph range matches earlier layout. */
1819 if (!tc->num_linefrags)
1820 {
1821 if (glyphRange.location != tc->pos)
1822 {
1823 [NSException raise: NSRangeException
1824 format: @"%s: glyph range not consistent with existing layout",
1825 __PRETTY_FUNCTION__];
1826 return;
1827 }
1828 }
1829 else
1830 {
1831 lf = &tc->linefrags[tc->num_linefrags - 1];
1832 if (lf->pos + lf->length != glyphRange.location)
1833 {
1834 [NSException raise: NSRangeException
1835 format: @"%s: glyph range not consistent with existing layout",
1836 __PRETTY_FUNCTION__];
1837 return;
1838 }
1839 }
1840
1841 if (!(tc->num_linefrags + tc->num_soft))
1842 {
1843 if (!tc->size_linefrags)
1844 {
1845 tc->size_linefrags = 16;
1846 tc->linefrags = malloc(sizeof(linefrag_t) * tc->size_linefrags);
1847 }
1848 tc->num_linefrags = 1;
1849 lf = tc->linefrags;
1850 }
1851 else if (!tc->num_soft)
1852 {
1853 if (tc->size_linefrags <= tc->num_linefrags)
1854 {
1855 tc->size_linefrags += tc->size_linefrags / 2;
1856 tc->linefrags = realloc(tc->linefrags, sizeof(linefrag_t) * tc->size_linefrags);
1857 }
1858 tc->num_linefrags++;
1859 lf = &tc->linefrags[tc->num_linefrags - 1];
1860 }
1861 else
1862 {
1863 int i;
1864 for (i = tc->num_linefrags, lf = tc->linefrags + i; i < tc->num_linefrags + tc->num_soft; i++, lf++)
1865 {
1866 if (lf->pos >= NSMaxRange(glyphRange))
1867 break;
1868 if (lf->points)
1869 {
1870 free(lf->points);
1871 lf->points = NULL;
1872 }
1873 if (lf->attachments)
1874 {
1875 free(lf->attachments);
1876 lf->attachments = NULL;
1877 }
1878 }
1879
1880 if (i == tc->num_linefrags)
1881 {
1882 /*
1883 If we should keep all soft frags, we need to enlarge the array
1884 to fit the new line frag.
1885 */
1886 if (tc->size_linefrags <= tc->num_linefrags + tc->num_soft)
1887 {
1888 tc->size_linefrags += tc->size_linefrags / 2;
1889 tc->linefrags = realloc(tc->linefrags, sizeof(linefrag_t) * tc->size_linefrags);
1890 }
1891 memmove(&tc->linefrags[tc->num_linefrags + 1], &tc->linefrags[tc->num_linefrags], tc->num_soft * sizeof(linefrag_t));
1892 }
1893 else if (i > tc->num_linefrags + 1)
1894 {
1895 tc->num_soft -= i - tc->num_linefrags;
1896 memmove(&tc->linefrags[tc->num_linefrags + 1], &tc->linefrags[i], tc->num_soft * sizeof(linefrag_t));
1897 }
1898 else
1899 {
1900 /*
1901 If i == tc->num_linefrags + 1, we're lucky and everything already
1902 lines up, so no moving is necessary.
1903 */
1904 tc->num_soft--;
1905 }
1906
1907 tc->num_linefrags++;
1908 lf = &tc->linefrags[tc->num_linefrags - 1];
1909 }
1910
1911 memset(lf, 0, sizeof(linefrag_t));
1912 lf->rect = fragmentRect;
1913 lf->used_rect = usedRect;
1914 lf->pos = glyphRange.location;
1915 lf->length = glyphRange.length;
1916 }
1917
1918 - (void) setLocation: (NSPoint)location
1919 forStartOfGlyphRange: (NSRange)glyphRange
1920 {
1921 textcontainer_t *tc;
1922 int i;
1923 linefrag_t *lf;
1924 linefrag_point_t *lp;
1925
1926 SETUP_STUFF
1927
1928 for (i = 0, tc = textcontainers; i < num_textcontainers; i++, tc++)
1929 {
1930 if (tc->pos <= glyphRange.location &&
1931 tc->pos + tc->length >= glyphRange.location + glyphRange.length)
1932 break;
1933 }
1934 if (i == num_textcontainers)
1935 {
1936 [NSException raise: NSRangeException
1937 format: @"%s: glyph range not consistent with existing layout",
1938 __PRETTY_FUNCTION__];
1939 return;
1940 }
1941
1942 for (i = tc->num_linefrags - 1, lf = tc->linefrags + i; i >= 0; i--, lf--)
1943 {
1944 if (lf->pos <= glyphRange.location &&
1945 lf->pos + lf->length >= glyphRange.location + glyphRange.length)
1946 break;
1947 }
1948 if (i < 0)
1949 {
1950 [NSException raise: NSRangeException
1951 format: @"%s: glyph range not consistent with existing layout",
1952 __PRETTY_FUNCTION__];
1953 return;
1954 }
1955
1956 if (!lf->num_points)
1957 {
1958 if (glyphRange.location != lf->pos)
1959 {
1960 [NSException raise: NSRangeException
1961 format: @"%s: glyph range not consistent with existing layout",
1962 __PRETTY_FUNCTION__];
1963 return;
1964 }
1965 lp = lf->points = malloc(sizeof(linefrag_point_t));
1966 lf->num_points++;
1967 }
1968 else
1969 {
1970 lp = &lf->points[lf->num_points - 1];
1971 if (lp->pos + lp->length != glyphRange.location)
1972 {
1973 [NSException raise: NSRangeException
1974 format: @"%s: glyph range not consistent with existing layout",
1975 __PRETTY_FUNCTION__];
1976 return;
1977 }
1978 lf->num_points++;
1979 lf->points = realloc(lf->points, sizeof(linefrag_point_t) * lf->num_points);
1980 lp = &lf->points[lf->num_points - 1];
1981 }
1982 lp->pos = glyphRange.location;
1983 lp->length = glyphRange.length;
1984 lp->p = location;
1985 }
1986
1987
1988 -(void) setAttachmentSize: (NSSize)size
1989 forGlyphRange: (NSRange)glyphRange
1990 {
1991 textcontainer_t *tc;
1992 int i;
1993 linefrag_t *lf;
1994 linefrag_attachment_t *la;
1995
1996 SETUP_STUFF
1997
1998 for (i = 0, tc = textcontainers; i < num_textcontainers; i++, tc++)
1999 {
2000 if (tc->pos <= glyphRange.location &&
2001 tc->pos + tc->length >= glyphRange.location + glyphRange.length)
2002 break;
2003 }
2004 if (i == num_textcontainers)
2005 {
2006 [NSException raise: NSRangeException
2007 format: @"%s: glyph range not consistent with existing layout",
2008 __PRETTY_FUNCTION__];
2009 return;
2010 }
2011
2012 for (i = 0, lf = tc->linefrags; i < tc->num_linefrags; i++, lf++)
2013 {
2014 if (lf->pos <= glyphRange.location &&
2015 lf->pos + lf->length >= glyphRange.location + glyphRange.length)
2016 break;
2017 }
2018 if (i == tc->num_linefrags)
2019 {
2020 [NSException raise: NSRangeException
2021 format: @"%s: glyph range not consistent with existing layout",
2022 __PRETTY_FUNCTION__];
2023 return;
2024 }
2025
2026 /* TODO: we do no sanity checking of attachment size ranges. might want
2027 to consider doing it */
2028 lf->attachments = realloc(lf->attachments,
2029 sizeof(linefrag_attachment_t) * (lf->num_attachments + 1));
2030 la = &lf->attachments[lf->num_attachments++];
2031
2032 memset(la, 0, sizeof(linefrag_attachment_t));
2033 la->pos = glyphRange.location;
2034 la->length = glyphRange.length;
2035 la->size = size;
2036 }
2037
2038 #undef SETUP_STUFF
2039
2040
2041 - (NSTextContainer *) textContainerForGlyphAtIndex: (unsigned int)glyphIndex
2042 effectiveRange: (NSRange *)effectiveRange
2043 {
2044 textcontainer_t *tc;
2045 int i;
2046
2047 [self _doLayoutToGlyph: glyphIndex];
2048 for (i = 0, tc = textcontainers; i < num_textcontainers; i++, tc++)
2049 if (tc->pos + tc->length > glyphIndex)
2050 break;
2051 if (i == num_textcontainers)
2052 {
2053 NSLog(@"%s: can't find text container for glyph (internal error)", __PRETTY_FUNCTION__);
2054 return nil;
2055 }
2056
2057 if (effectiveRange)
2058 {
2059 [self _doLayoutToContainer: i];
2060 tc = textcontainers + i;
2061 *effectiveRange = NSMakeRange(tc->pos, tc->length);
2062 }
2063 return tc->textContainer;
2064 }
2065
2066 - (NSRect) lineFragmentRectForGlyphAtIndex: (unsigned int)glyphIndex
2067 effectiveRange: (NSRange *)effectiveGlyphRange
2068 {
2069 int i;
2070 textcontainer_t *tc;
2071 linefrag_t *lf;
2072
2073 [self _doLayoutToGlyph: glyphIndex];
2074 for (i = 0, tc = textcontainers; i < num_textcontainers; i++, tc++)
2075 if (tc->pos + tc->length > glyphIndex)
2076 break;
2077 if (i == num_textcontainers)
2078 {
2079 NSLog(@"%s: can't find text container for glyph (internal error)", __PRETTY_FUNCTION__);
2080 return NSZeroRect;
2081 }
2082
2083 for (i = 0, lf = tc->linefrags; i < tc->num_linefrags; i++, lf++)
2084 if (lf->pos + lf->length > glyphIndex)
2085 break;
2086 if (i == tc->num_linefrags)
2087 {
2088 NSLog(@"%s: can't find line frag rect for glyph (internal error)", __PRETTY_FUNCTION__);
2089 return NSZeroRect;
2090 }
2091
2092 if (effectiveGlyphRange)
2093 {
2094 *effectiveGlyphRange = NSMakeRange(lf->pos, lf->length);
2095 }
2096 return lf->rect;
2097 }
2098
2099 - (NSRect) lineFragmentUsedRectForGlyphAtIndex: (unsigned int)glyphIndex
2100 effectiveRange: (NSRange *)effectiveGlyphRange
2101 {
2102 int i;
2103 textcontainer_t *tc;
2104 linefrag_t *lf;
2105
2106 [self _doLayoutToGlyph: glyphIndex];
2107 for (i = 0, tc = textcontainers; i < num_textcontainers; i++, tc++)
2108 if (tc->pos + tc->length > glyphIndex)
2109 break;
2110 if (i == num_textcontainers)
2111 {
2112 NSLog(@"%s: can't find text container for glyph (internal error)", __PRETTY_FUNCTION__);
2113 return NSMakeRect(0, 0, 0, 0);
2114 }
2115
2116 for (i = 0, lf = tc->linefrags; i < tc->num_linefrags; i++, lf++)
2117 if (lf->pos + lf->length > glyphIndex)
2118 break;
2119 if (i == tc->num_linefrags)
2120 {
2121 NSLog(@"%s: can't find line frag rect for glyph (internal error)", __PRETTY_FUNCTION__);
2122 return NSMakeRect(0, 0, 0, 0);
2123 }
2124
2125 if (effectiveGlyphRange)
2126 {
2127 *effectiveGlyphRange = NSMakeRange(lf->pos, lf->length);
2128 }
2129 return lf->used_rect;
2130 }
2131
2132 - (NSRange) rangeOfNominallySpacedGlyphsContainingIndex: (unsigned int)glyphIndex
2133 startLocation: (NSPoint *)p
2134 {
2135 int i;
2136 textcontainer_t *tc;
2137 linefrag_t *lf;
2138 linefrag_point_t *lp;
2139
2140 [self _doLayoutToGlyph: glyphIndex];
2141 for (i = 0, tc = textcontainers; i < num_textcontainers; i++, tc++)
2142 if (tc->pos + tc->length > glyphIndex)
2143 break;
2144 if (i == num_textcontainers)
2145 {
2146 NSLog(@"%s: can't find text container for glyph (internal error)", __PRETTY_FUNCTION__);
2147 return NSMakeRange(NSNotFound, 0);
2148 }
2149
2150 for (i = 0, lf = tc->linefrags; i < tc->num_linefrags; i++, lf++)
2151 if (lf->pos + lf->length > glyphIndex)
2152 break;
2153 if (i == tc->num_linefrags)
2154 {
2155 NSLog(@"%s: can't find line frag rect for glyph (internal error)", __PRETTY_FUNCTION__);
2156 return NSMakeRange(NSNotFound, 0);
2157 }
2158
2159 for (i = 0, lp = lf->points; i < lf->num_points; i++, lp++)
2160 if (lp->pos + lp->length > glyphIndex)
2161 break;
2162 if (i == lf->num_points)
2163 {
2164 NSLog(@"%s: can't find location for glyph (internal error)", __PRETTY_FUNCTION__);
2165 return NSMakeRange(NSNotFound, 0);
2166 }
2167
2168 if (p)
2169 *p = lp->p;
2170 return NSMakeRange(lp->pos, lp->length);
2171 }
2172
2173
2174 - (NSRange) rangeOfNominallySpacedGlyphsContainingIndex:(unsigned int)glyphIndex
2175 {
2176 return [self rangeOfNominallySpacedGlyphsContainingIndex: glyphIndex
2177 startLocation: NULL];
2178 }
2179
2180
2181 /* The union of all line frag rects' used rects. */
2182 - (NSRect) usedRectForTextContainer: (NSTextContainer *)container
2183 {
2184 textcontainer_t *tc;
2185 linefrag_t *lf;
2186 int i;
2187 NSRect used;
2188
2189 for (i = 0, tc = textcontainers; i < num_textcontainers; i++, tc++)
2190 if (tc->textContainer == container)
2191 break;
2192 if (i == num_textcontainers)
2193 {
2194 NSLog(@"%s: doesn't own text container", __PRETTY_FUNCTION__);
2195 return NSMakeRect(0, 0, 0, 0);
2196 }
2197
2198 [self _doLayoutToContainer: i];
2199 tc = textcontainers + i;
2200 used = NSZeroRect;
2201 for (i = 0, lf = tc->linefrags; i < tc->num_linefrags; i++, lf++)
2202 used = NSUnionRect(used, lf->used_rect);
2203
2204 return used;
2205 }
2206
2207 - (NSRange) glyphRangeForTextContainer: (NSTextContainer *)container
2208 {
2209 textcontainer_t *tc;
2210 int i;
2211
2212 for (i = 0, tc = textcontainers; i < num_textcontainers; i++, tc++)
2213 if (tc->textContainer == container)
2214 break;
2215 if (i == num_textcontainers)
2216 {
2217 NSLog(@"%s: doesn't own text container", __PRETTY_FUNCTION__);
2218 return NSMakeRange(NSNotFound, 0);
2219 }
2220
2221 [self _doLayoutToContainer: i];
2222 tc = textcontainers + i;
2223 return NSMakeRange(tc->pos, tc->length);
2224 }
2225
2226
2227
2228 /* TODO: make more efficient */
2229 - (NSArray *) textContainers
2230 {
2231 NSMutableArray *ma;
2232 int i;
2233
2234 ma = [[NSMutableArray alloc] initWithCapacity: num_textcontainers];
2235 for (i = 0; i < num_textcontainers; i++)
2236 [ma addObject: textcontainers[i].textContainer];
2237 return [ma autorelease];
2238 }
2239
2240 - (void) addTextContainer: (NSTextContainer *)container
2241 {
2242 [self insertTextContainer: container
2243 atIndex: num_textcontainers];
2244 }
2245
2246 - (void) insertTextContainer: (NSTextContainer *)aTextContainer
2247 atIndex: (unsigned int)index
2248 {
2249 unsigned int i;
2250
2251 if (index < num_textcontainers)
2252 [self _invalidateLayoutFromContainer: index];
2253
2254 num_textcontainers++;
2255 textcontainers = realloc(textcontainers,
2256 sizeof(textcontainer_t) * num_textcontainers);
2257
2258 for (i = num_textcontainers - 1; i > index; i--)
2259 textcontainers[i] = textcontainers[i - 1];
2260
2261 memset(&textcontainers[i], 0, sizeof(textcontainer_t));
2262 textcontainers[i].textContainer = [aTextContainer retain];
2263
2264 [aTextContainer setLayoutManager: self];
2265
2266 [self _didInvalidateLayout];
2267 }
2268
2269 - (void) removeTextContainerAtIndex: (unsigned int)index
2270 {
2271 int i;
2272 textcontainer_t *tc = &textcontainers[index];
2273
2274 [self _invalidateLayoutFromContainer: index];
2275 [tc->textContainer setLayoutManager: nil];
2276 [tc->textContainer release];
2277
2278 num_textcontainers--;
2279 for (i = index; i < num_textcontainers; i++)
2280 textcontainers[i] = textcontainers[i + 1];
2281
2282 if (num_textcontainers)
2283 textcontainers = realloc(textcontainers,
2284 sizeof(textcontainer_t) * num_textcontainers);
2285 else
2286 {
2287 free(textcontainers);
2288 textcontainers = NULL;
2289 }
2290
2291 [self _didInvalidateLayout];
2292 }
2293
2294 - (void) textContainerChangedGeometry: (NSTextContainer *)aContainer
2295 {
2296 int i;
2297 for (i = 0; i < num_textcontainers; i++)
2298 if (textcontainers[i].textContainer == aContainer)
2299 break;
2300 if (i == num_textcontainers)
2301 {
2302 NSLog(@"%s: does not own text container", __PRETTY_FUNCTION__);
2303 return;
2304 }
2305 [self _invalidateLayoutFromContainer: i];
2306 [self _didInvalidateLayout];
2307 }
2308
2309
2310 - (unsigned int) firstUnlaidCharacterIndex
2311 {
2312 return layout_char;
2313 }
2314 - (unsigned int) firstUnlaidGlyphIndex
2315 {
2316 return layout_glyph;
2317 }
2318 -(void) getFirstUnlaidCharacterIndex: (unsigned int *)cindex
2319 glyphIndex: (unsigned int *)gindex
2320 {
2321 if (cindex)
2322 *cindex = [self firstUnlaidCharacterIndex];
2323 if (gindex)
2324 *gindex = [self firstUnlaidGlyphIndex];
2325 }
2326
2327
2328 -(void) setExtraLineFragmentRect: (NSRect)linefrag
2329 usedRect: (NSRect)used
2330 textContainer: (NSTextContainer *)tc
2331 {
2332 extra_rect = linefrag;
2333 extra_used_rect = used;
2334 extra_textcontainer = tc;
2335 }
2336
2337 -(NSRect) extraLineFragmentRect
2338 {
2339 return extra_rect;
2340 }
2341
2342 -(NSRect) extraLineFragmentUsedRect
2343 {
2344 return extra_used_rect;
2345 }
2346
2347 -(NSTextContainer *) extraLineFragmentTextContainer
2348 {
2349 return extra_textcontainer;
2350 }
2351
2352
2353 -(void) _softInvalidateUseLineFrags: (int)num
2354 withShift: (NSSize)shift
2355 inTextContainer: (NSTextContainer *)textContainer
2356 {
2357 int i;
2358 textcontainer_t *tc;
2359 linefrag_t *lf;
2360 for (i = 0, tc = textcontainers; i < num_textcontainers; i++, tc++)
2361 if (tc->textContainer == textContainer)
2362 break;
2363 if (i == num_textcontainers)
2364 {
2365 NSLog(@"(%s): does not own text container", __PRETTY_FUNCTION__);
2366 return;
2367 }
2368
2369 if (shift.width || shift.height)
2370 {
2371 for (i = 0, lf = &tc->linefrags[tc->num_linefrags]; i < num; i++, lf++)
2372 {
2373 lf->rect.origin.x += shift.width;
2374 lf->rect.origin.y += shift.height;
2375 lf->used_rect.origin.x += shift.width;
2376 lf->used_rect.origin.y += shift.height;
2377 }
2378 }
2379 tc->num_soft -= num;
2380 tc->num_linefrags += num;
2381 lf = &tc->linefrags[tc->num_linefrags - 1];
2382 tc->length = lf->pos + lf->length - tc->pos;
2383
2384 layout_glyph = tc->pos + tc->length;
2385 /*
2386 We must have glyphs beyond all the soft-invalidated line frags,
2387 so comparing with glyphs->glyph_length is ok.
2388 */
2389 if (layout_glyph == glyphs->glyph_length)
2390 layout_char = glyphs->char_length;
2391 else
2392 layout_char = [self characterIndexForGlyphAtIndex: layout_glyph]; /* TODO? */
2393 }
2394
2395 -(NSRect) _softInvalidateLineFragRect: (int)index
2396 firstGlyph: (unsigned int *)first_glyph
2397 nextGlyph: (unsigned int *)next_glyph
2398 inTextContainer: (NSTextContainer *)textContainer
2399 {
2400 int i;
2401 textcontainer_t *tc;
2402 linefrag_t *lf;
2403 for (i = 0, tc = textcontainers; i < num_textcontainers; i++, tc++)
2404 if (tc->textContainer == textContainer)
2405 break;
2406 if (i == num_textcontainers)
2407 {
2408 NSLog(@"(%s): does not own text container", __PRETTY_FUNCTION__);
2409 return NSZeroRect;
2410 }
2411
2412 if (index >= tc->num_soft)
2413 return NSZeroRect;
2414
2415 lf = &tc->linefrags[tc->num_linefrags + index];
2416 *first_glyph = lf->pos;
2417 *next_glyph = lf->pos + lf->length;
2418 return lf->rect;
2419 }
2420
2421 -(unsigned int) _softInvalidateFirstGlyphInTextContainer: (NSTextContainer *)textContainer
2422 {
2423 int i;
2424 textcontainer_t *tc;
2425 for (i = 0, tc = textcontainers; i < num_textcontainers; i++, tc++)
2426 if (tc->textContainer == textContainer)
2427 break;
2428 if (i == num_textcontainers)
2429 {
2430 NSLog(@"(%s): does not own text container", __PRETTY_FUNCTION__);
2431 return (unsigned int)-1;
2432 }
2433 if (tc->num_soft)
2434 return tc->linefrags[tc->num_linefrags].pos;
2435 else
2436 return (unsigned int)-1;
2437 }
2438
2439 -(unsigned int) _softInvalidateNumberOfLineFragsInTextContainer: (NSTextContainer *)textContainer
2440 {
2441 int i;
2442 textcontainer_t *tc;
2443 for (i = 0, tc = textcontainers; i < num_textcontainers; i++, tc++)
2444 if (tc->textContainer == textContainer)
2445 break;
2446 if (i == num_textcontainers)
2447 {
2448 NSLog(@"(%s): does not own text container", __PRETTY_FUNCTION__);
2449 return (unsigned int)-1;
2450 }
2451 return tc->num_soft;
2452 }
2453
2454 @end
2455
2456
2457 /***** The rest *****/
2458
2459 @implementation GSLayoutManager
2460
2461 - init
2462 {
2463 if (!(self = [super init]))
2464 return nil;
2465
2466 [self _initGlyphs];
2467
2468 typesetter = [[GSTypesetter sharedSystemTypesetter] retain];
2469
2470 usesScreenFonts = YES;
2471
2472 return self;
2473 }
2474
2475
2476 -(void) dealloc
2477 {
2478 int i;
2479 textcontainer_t *tc;
2480
2481 free(rect_array);
2482 rect_array_size = 0;
2483 rect_array = NULL;
2484
2485 [self _freeLayout];
2486 for (i = 0, tc = textcontainers; i < num_textcontainers; i++, tc++)
2487 {
2488 [tc->textContainer release];
2489 }
2490 free(textcontainers);
2491 textcontainers = NULL;
2492
2493 [self _freeGlyphs];
2494
2495 DESTROY(typesetter);
2496
2497 [super dealloc];
2498 }
2499
2500
2501 -(void) _invalidateEverything
2502 {
2503 [self _freeLayout];
2504
2505 [self _freeGlyphs];
2506 [self _initGlyphs];
2507 }
2508
2509
2510 /**
2511 * Sets the text storage for the layout manager.
2512 * Use -replaceTextStorage: instead as a rule. - this method is really
2513 * more for internal use by the text system.
2514 * Invalidates the entire layout (should it??)
2515 */
2516 /*
2517 See [NSTextView -setTextContainer:] for more information about these calls.
2518 */
2519 - (void) setTextStorage: (NSTextStorage *)aTextStorage
2520 {
2521 int i;
2522 textcontainer_t *tc;
2523
2524 [self _invalidateEverything];
2525
2526 /*
2527 * Make a note of the new text storage object, but don't retain it.
2528 * The text storage is owning us - it retains us.
2529 */
2530 _textStorage = aTextStorage;
2531
2532 /*
2533 We send this message to all text containers so they can respond to the
2534 change (most importantly to let them tell their text views).
2535 */
2536 for (i = 0, tc = textcontainers; i < num_textcontainers; i++, tc++)
2537 {
2538 [tc->textContainer setLayoutManager: self];
2539 }
2540 [self _didInvalidateLayout];
2541 }
2542
2543 /**
2544 * Returns the text storage for this layout manager.
2545 */
2546 - (NSTextStorage *) textStorage
2547 {
2548 return _textStorage;
2549 }
2550
2551 /**
2552 * Replaces the text storage with a new one.<br />
2553 * Takes care (since layout managers are owned by text storages)
2554 * not to get self deallocated.
2555 */
2556 - (void) replaceTextStorage: (NSTextStorage *)newTextStorage
2557 {
2558 NSArray *layoutManagers = [_textStorage layoutManagers];
2559 NSEnumerator *enumerator = [layoutManagers objectEnumerator];
2560 GSLayoutManager *object;
2561
2562 /* Remove layout managers from old NSTextStorage object and add them to the
2563 new one. NSTextStorage's addLayoutManager invokes GSLayoutManager's
2564 setTextStorage method automatically, and that includes self. */
2565
2566 while ((object = (GSLayoutManager*)[enumerator nextObject]) != nil)
2567 {
2568 RETAIN(object);
2569 [_textStorage removeLayoutManager: object];
2570 [newTextStorage addLayoutManager: object];
2571 RELEASE(object);
2572 }
2573 }
2574
2575
2576 - (id) delegate
2577 {
2578 return _delegate;
2579 }
2580 - (void) setDelegate: (id)aDelegate
2581 {
2582 _delegate = aDelegate;
2583 }
2584
2585
2586 -(GSTypesetter *) typesetter
2587 {
2588 return typesetter;
2589 }
2590 -(void) setTypesetter: (GSTypesetter *)a_typesetter
2591 {
2592 ASSIGN(typesetter, a_typesetter);
2593 }
2594
2595
2596 - (BOOL) usesScreenFonts
2597 {
2598 return usesScreenFonts;
2599 }
2600 - (void) setUsesScreenFonts: (BOOL)flag
2601 {
2602 flag = !!flag;
2603 if (flag == usesScreenFonts)
2604 return;
2605 usesScreenFonts = flag;
2606 [self _invalidateEverything];
2607 [self _didInvalidateLayout];
2608 }
2609
2610 - (NSFont *) substituteFontForFont: (NSFont *)originalFont
2611 {
2612 NSFont *f;
2613 if (usesScreenFonts)
2614 {
2615 f = [originalFont screenFont];
2616 if (f)
2617 return f;
2618 }
2619 return originalFont;
2620 }
2621
2622
2623 - (void) setBackgroundLayoutEnabled: (BOOL)flag
2624 {
2625 flag = !!flag;
2626 if (flag == backgroundLayoutEnabled)
2627 return;
2628 backgroundLayoutEnabled = flag;
2629 /* TODO */
2630 }
2631 - (BOOL) backgroundLayoutEnabled
2632 {
2633 return backgroundLayoutEnabled;
2634 }
2635
2636 - (void) setShowsInvisibleCharacters: (BOOL)flag
2637 {
2638 flag = !!flag;
2639 if (flag == showsInvisibleCharacters)
2640 return;
2641
2642 showsInvisibleCharacters = flag;
2643 [self _invalidateEverything];
2644 [self _didInvalidateLayout];
2645 }
2646 - (BOOL) showsInvisibleCharacters
2647 {
2648 return showsInvisibleCharacters;
2649 }
2650
2651 - (void) setShowsControlCharacters: (BOOL)flag
2652 {
2653 flag = !!flag;
2654 if (flag == showsControlCharacters)
2655 return;
2656 showsControlCharacters = flag;
2657 [self _invalidateEverything];
2658 [self _didInvalidateLayout];
2659 }
2660 - (BOOL) showsControlCharacters
2661 {
2662 return showsControlCharacters;
2663 }
2664
2665
2666 /*
2667 Note that NSLayoutManager completely overrides this (to perform more
2668 intelligent invalidation of layout using the constraints on layout it
2669 has).
2670 */
2671 - (void) textStorage: (NSTextStorage *)aTextStorage
2672 edited: (unsigned int)mask
2673 range: (NSRange)range
2674 changeInLength: (int)lengthChange
2675 invalidatedRange: (NSRange)invalidatedRange
2676 {
2677 NSRange r;
2678
2679 if (!(mask & NSTextStorageEditedCharacters))
2680 lengthChange = 0;
2681
2682 [self invalidateGlyphsForCharacterRange: invalidatedRange
2683 changeInLength: lengthChange
2684 actualCharacterRange: &r];
2685
2686 /*
2687 See the comments above -invalidateLayoutForCharacterRange:isSoft:
2688 actualCharacterRange: for information on why we invalidate everything
2689 here.
2690 */
2691 [self _invalidateLayoutFromContainer: 0];
2692
2693 [self _didInvalidateLayout];
2694 }
2695
2696
2697 /* These must be in the main implementation so backends can override them
2698 in a category safely. */
2699
2700 /* These three methods should be implemented in the backend, but there's
2701 a dummy here. It maps each character to a glyph with the glyph id==unicode
2702 index, except control characters, which are mapped to NSControlGlyph. */
2703
2704 -(unsigned int) _findSafeBreakMovingBackwardFrom: (unsigned int)ch
2705 {
2706 return ch;
2707 }
2708
2709 -(unsigned int) _findSafeBreakMovingForwardFrom: (unsigned int)ch
2710 {
2711 return ch;
2712 }
2713
2714
2715 /* TODO2: put good control code handling here in a way that makes it easy
2716 for the backends to use it */
2717 -(void) _generateGlyphsForRun: (glyph_run_t *)run at: (unsigned int)pos
2718 {
2719 int i, c = run->head.char_length;
2720 unsigned int ch;
2721 unichar buf[c];
2722
2723 glyph_t *g;
2724
2725 NSCharacterSet *cs = [NSCharacterSet controlCharacterSet];
2726 IMP characterIsMember = [cs methodForSelector: @selector(characterIsMember:)];
2727
2728 run->head.glyph_length = c;
2729 run->glyphs = malloc(sizeof(glyph_t) * c);
2730 memset(run->glyphs, 0, sizeof(glyph_t) * c);
2731
2732 [[_textStorage string] getCharacters: buf
2733 range: NSMakeRange(pos, c)];
2734
2735 g = run->glyphs;
2736 for (i = 0; i < c; i++)
2737 {
2738 ch = buf[i];
2739 g->char_offset = i;
2740 if (characterIsMember(cs, @selector(characterIsMember:), ch))
2741 g->g = NSControlGlyph;
2742 else if (ch == NSAttachmentCharacter)
2743 g->g = GSAttachmentGlyph;
2744 else
2745 g->g = ch;
2746 g++;
2747 }
2748 }
2749
2750 @end
2751

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