1 /* Part of XPCE --- The SWI-Prolog GUI toolkit
2
3 Author: Jan Wielemaker and Anjo Anjewierden
4 E-mail: jan@swi.psy.uva.nl
5 WWW: http://www.swi.psy.uva.nl/projects/xpce/
6 Copyright (c) 1985-2002, University of Amsterdam
7 All rights reserved.
8
9 Redistribution and use in source and binary forms, with or without
10 modification, are permitted provided that the following conditions
11 are met:
12
13 1. Redistributions of source code must retain the above copyright
14 notice, this list of conditions and the following disclaimer.
15
16 2. Redistributions in binary form must reproduce the above copyright
17 notice, this list of conditions and the following disclaimer in
18 the documentation and/or other materials provided with the
19 distribution.
20
21 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22 "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23 LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24 FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25 COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26 INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27 BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28 LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29 CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30 LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31 ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32 POSSIBILITY OF SUCH DAMAGE.
33 */
34
35 #include <h/kernel.h>
36 #include <h/graphics.h>
37 #include <h/text.h>
38
39 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
40 The PCE-3 editor object is now split up into a large number of separate
41 objects to improve modilarity and reusability of various pieces of the
42 editor. One of the most critical ones is the textimage object. It
43 maintains the relation between a text and its graphical representation.
44
45 The textimage object understands the following method:
46
47 ->size Size Resize the image to a new size (pixels)
48 ->start Integer Defines first character to be displayed
49 ->wrap Name none, character or word. Defines wrap mode
50 ->Inserted Start Amount Amount characters have been inserted/
51 deleted at Start (deleted: negative argument)
52 ->ChangedRegion From To Region [From, To) has changed
53
54 The textimage extracts information from the underlying text object using
55 a pointer to a function that returns information on a specific character
56 and its attributes:
57
58 void (*fetch)(Any context, TextChar chr)
59
60 Each textimage contains the bitmap image and an array of text_line
61 structures that describe the current contents of the screen. The
62 insert, delete and change messages are used to maintain a summary of the
63 things that need be checked during the update.
64
65 The update phase does the following:
66
67 *) determine the lines that need to be changed.
68 *) fill a second -class maintained- text_line structure array
69 with the information of the lines to be changed.
70 *) Find for each line a similar line in the current map line.
71 A similar line is an equal line, a line that is equal with
72 the exception of underline and invert attributes or a line
73 that is equal except for few insertions/deletions or a
74 combination of the two.
75 *) Find the correct updating order, such that no information
76 will be overwritten during the copying process.
77 *) Update the lines.
78 *) Swap the text_line structures of the class with those of the
79 text_image to get this one upto date.
80
81 This process maintains an area that is the union of everything that has
82 been changed, so we can forward this to the device's update algorithm.
83 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
84
85
86 #undef INFINITE
87 #define INFINITE 0x3fffffff /* 31-bit int, but will do anyhow */
88 #define Round(n, r) ((((n) + ((r)-1)) / (r)) * (r))
89
90 /********************************
91 * LOCAL TYPES *
92 ********************************/
93
94 #define END_CUT (1) /* Line ends due to wrap: none */
95 #define END_WRAP (2) /* Line ends due to wrap */
96 #define END_EOF (4) /* Line ends due to end-of-buffer */
97 #define END_NL (8) /* Line ends due to newline */
98
99 #define X_RIGHT (-1) /* extend to right margin */
100
101 forwards long do_fill_line(TextImage, TextLine, long);
102 static TextLine line_from_y(TextImage ti, int y);
103 forwards status reinitTextImage(TextImage ti);
104 static int char_from_x(TextLine tl, int x);
105 static void copy_line_attributes(TextLine from, TextLine to);
106 static void copy_line_chars(TextLine from, int start, TextLine to);
107 static void ascent_and_descent_graphical(Graphical gr, int *, int *);
108 static void ascent_and_descent_image(Image im, int *, int *);
109
110
111 /********************************
112 * NEW/ALLOC/UNALLOC *
113 ********************************/
114
115 static status
initialiseTextImage(TextImage ti,Any obj,Int w,Int h)116 initialiseTextImage(TextImage ti, Any obj, Int w, Int h)
117 { initialiseGraphical(ti, ZERO, ZERO, w, h);
118
119 assign(ti, text, obj);
120 assign(ti, start, ZERO);
121 assign(ti, end, ZERO);
122 assign(ti, background, getClassVariableValueObject(ti, NAME_background));
123 assign(ti, wrap, getClassVariableValueObject(ti, NAME_wrap));
124 assign(ti, tab_distance, getClassVariableValueObject(ti, NAME_tabDistance));
125
126 return reinitTextImage(ti);
127 }
128
129
130 static void
unalloc_textline(TextLine l)131 unalloc_textline(TextLine l)
132 { if ( l->chars != NULL )
133 { unalloc(l->allocated * sizeof(struct text_char), l->chars);
134 l->chars = NULL;
135 }
136 }
137
138
139 static void
unalloc_screen(TextScreen s)140 unalloc_screen(TextScreen s)
141 { int i;
142
143 if ( s->lines != NULL )
144 { for(i=0; i<s->allocated; i++)
145 unalloc_textline(&s->lines[i]);
146
147 unalloc(s->allocated * sizeof(struct text_line), s->lines);
148 s->lines = NULL;
149 }
150
151 unalloc(sizeof(struct text_screen), s);
152 }
153
154
155 static status
unlinkTextImage(TextImage ti)156 unlinkTextImage(TextImage ti)
157 { unlinkGraphical((Graphical) ti);
158
159 if ( ti->map != NULL )
160 { unalloc_screen(ti->map);
161 ti->map = NULL;
162 }
163
164 succeed;
165 }
166
167
168 static void
ensure_lines_screen(TextScreen s,int lines)169 ensure_lines_screen(TextScreen s, int lines)
170 { if ( s->allocated < lines )
171 { TextLine new;
172 int chars = (s->allocated > 0 ? s->lines[0].allocated : 80);
173 int n;
174
175 if ( lines > 500 )
176 errorPce(NIL, NAME_tooManyScreenLines);
177
178 lines = Round(lines, 8);
179 new = alloc(lines * sizeof(struct text_line));
180 DEBUG(NAME_allocated, Cprintf("Lines at %p, %ld bytes\n",
181 new,
182 (unsigned long) lines * sizeof(struct text_line)));
183
184 for(n = 0; n < s->allocated; n++) /* copy old lines */
185 new[n] = s->lines[n];
186
187 for( ; n < lines; n++) /* create new ones */
188 { new[n].chars = alloc(chars * sizeof(struct text_char));
189 new[n].allocated = chars;
190 new[n].changed = 0;
191 new[n].start = -1;
192 new[n].y = -1;
193 }
194
195 if ( s->lines )
196 unalloc(s->allocated * sizeof(struct text_line), s->lines);
197 s->lines = new;
198 s->allocated = lines;
199 }
200 }
201
202
203 static void
ensure_chars_line(TextLine l,int chars)204 ensure_chars_line(TextLine l, int chars)
205 { if ( l->allocated < chars )
206 { TextChar new;
207 int n;
208
209 chars = Round(chars, 16);
210 new = alloc(chars * sizeof(struct text_char));
211
212 for(n = 0; n < l->allocated; n++)
213 new[n] = l->chars[n];
214
215 if ( l->chars != NULL )
216 unalloc(l->allocated * sizeof(struct text_char), l->chars);
217 l->allocated = chars;
218 l->chars = new;
219 }
220 }
221
222 /*******************************
223 * LOAD/SAVE *
224 *******************************/
225
226 static status
storeTextImage(TextImage ti,FileObj file)227 storeTextImage(TextImage ti, FileObj file)
228 { return storeSlotsObject(ti, file);
229 }
230
231
232 static status
reinitTextImage(TextImage ti)233 reinitTextImage(TextImage ti)
234 { Any obj = ti->text;
235 Elevation z;
236
237 assign(ti, request_compute, ON);
238
239 ti->w = valInt(ti->area->w);
240 ti->h = valInt(ti->area->h);
241 ti->change_start = 0;
242 ti->change_end = INFINITE;
243 ti->inserted = 0;
244
245 ti->seek = (SeekFunction) get(obj, NAME_SeekFunction, EAV);
246 ti->scan = (ScanFunction) get(obj, NAME_ScanFunction, EAV);
247 ti->fetch = (FetchFunction) get(obj, NAME_FetchFunction, EAV);
248 ti->margin = (MarginFunction) get(obj, NAME_MarginFunction, EAV);
249 ti->rewind = (RewindFunction) get(obj, NAME_RewindFunction, EAV);
250
251 if ( !ti->seek || !ti->scan || !ti->fetch )
252 return errorPce(ti, NAME_noFetchFunction, obj);
253 DEBUG(NAME_SeekFunction, Cprintf("ti->seek = %p\n", ti->seek));
254
255 ti->map = alloc(sizeof(struct text_screen));
256 ti->map->allocated = ti->map->length = ti->map->skip = 0;
257 ti->map->lines = NULL;
258
259 if ( restoreVersion < 17 )
260 { if ( (z = getClassVariableValueObject(ti, NAME_elevation)) && notNil(z) )
261 { assign(ti, elevation, z);
262 assign(ti, pen, absInt(z->height));
263 }
264 }
265
266 return obtainClassVariablesObject(ti);
267 }
268
269
270 static status
loadTextImage(TextImage ti,IOSTREAM * fd,ClassDef def)271 loadTextImage(TextImage ti, IOSTREAM *fd, ClassDef def)
272 { TRY(loadSlotsObject(ti, fd, def));
273
274 return reinitTextImage(ti);
275 }
276
277
278 static status
cloneTextImage(TextImage ti,TextImage clone)279 cloneTextImage(TextImage ti, TextImage clone)
280 { clonePceSlots(ti, clone);
281
282 return reinitTextImage(clone);
283 }
284
285
286
287 /********************************
288 * TRAPPING CHANGES *
289 ********************************/
290
291 static int
update_insert(int v,int w,int a)292 update_insert(int v, int w, int a)
293 { if ( a > 0 )
294 return w < v ? v+a : v;
295 else
296 { a = -a;
297 if ( w + a < v ) return v - a;
298 if ( w > v ) return v;
299 return w;
300 }
301 }
302
303
304 status
InsertTextImage(TextImage ti,Int where,Int amount)305 InsertTextImage(TextImage ti, Int where, Int amount)
306 { int w = valInt(where);
307 int a = valInt(amount);
308 int line;
309
310 assign(ti, start, toInt(update_insert(valInt(ti->start), w, a)));
311 assign(ti, end, toInt(update_insert(valInt(ti->end), w, a)));
312
313 if ( ti->map->lines != NULL )
314 { for(line = 0; line <= ti->map->length; line++) /* Last as well!! */
315 { TextLine tl = &ti->map->lines[line];
316
317 tl->start = update_insert(tl->start, w, a);
318 tl->end = update_insert(tl->end, w, a);
319 }
320 }
321
322 if ( w < ti->change_start )
323 ti->change_start = w;
324 if ( a > 0 )
325 { if ( w+a > ti->change_end )
326 ti->change_end = w+a;
327 } else
328 { if ( w+1 > ti->change_end )
329 ti->change_end = w+1;
330 }
331
332 requestComputeGraphical(ti, DEFAULT);
333
334 succeed;
335 }
336
337
338 status
ChangedRegionTextImage(TextImage ti,Int from,Int to)339 ChangedRegionTextImage(TextImage ti, Int from, Int to)
340 { if ( valInt(from) < ti->change_start )
341 ti->change_start = valInt(from);
342 if ( valInt(to) > ti->change_end )
343 ti->change_end = valInt(to);
344 requestComputeGraphical(ti, DEFAULT);
345
346 succeed;
347 }
348
349
350 status
ChangedEntireTextImage(TextImage ti)351 ChangedEntireTextImage(TextImage ti)
352 { return ChangedRegionTextImage(ti, ZERO, toInt(INFINITE));
353 }
354
355
356 /********************************
357 * FILLING INFO *
358 ********************************/
359
360 static int
tab(TextImage ti,int x)361 tab(TextImage ti, int x)
362 { x -= TXT_X_MARGIN;
363 x++;
364
365 if ( isNil(ti->tab_stops) )
366 { int td = valInt(ti->tab_distance);
367
368 x = Round(x, td);
369 } else
370 { int i;
371
372 for(i=1; i<=valInt(ti->tab_stops->size); i++)
373 { int s = valInt(getElementVector(ti->tab_stops, toInt(i)));
374
375 if ( s >= x )
376 return s + TXT_X_MARGIN;
377 }
378
379 x += 5;
380 }
381
382 x += TXT_X_MARGIN;
383
384 return x;
385 }
386
387
388 static void
fill_dimensions_line(TextLine l)389 fill_dimensions_line(TextLine l)
390 { FontObj f = NULL;
391 int ascent = 0, descent = 0;
392 TextChar tc, te;
393
394 for(tc=l->chars, te=&l->chars[l->length]; tc<te; tc++)
395 { int a, d;
396
397 switch(tc->type)
398 { case CHAR_GRAPHICAL:
399 ascent_and_descent_graphical(tc->value.graphical, &a, &d);
400 ascent = max(ascent, a);
401 descent = max(descent, d);
402 break;
403 case CHAR_IMAGE:
404 ascent_and_descent_image(tc->value.image, &a, &d);
405 ascent = max(ascent, a);
406 descent = max(descent, d);
407 break;
408 case CHAR_ASCII:
409 if ( tc->font != f )
410 { f = tc->font;
411
412 assert(f);
413 a = valInt(getAscentFont(f));
414 d = valInt(getDescentFont(f));
415 ascent = max(ascent, a);
416 descent = max(descent, d);
417 }
418 break;
419 }
420 }
421
422 l->base = ascent;
423 l->h = ascent + descent;
424 }
425
426
427 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
428 The function fill_line() fills a line description, assuming the line
429 starts at index `start' and will be displayed at `y' in the bitmap.
430 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
431
432 static long
do_fill_line(TextImage ti,TextLine l,long index)433 do_fill_line(TextImage ti, TextLine l, long index)
434 { short last_break = -1;
435 int last_is_space = FALSE;
436 TextChar tc;
437 int x, i, left_margin, right_margin;
438 long start;
439
440 l->ends_because = 0;
441 start = l->start = index;
442
443 (*ti->seek)(ti->text, index);
444 if ( ti->margin )
445 (*ti->margin)(ti->text, &left_margin, &right_margin);
446 else
447 left_margin = right_margin = 0;
448
449 x = TXT_X_MARGIN + left_margin;
450 if ( right_margin < 0 )
451 right_margin = ti->w - TXT_X_MARGIN;
452 else
453 right_margin = ti->w - TXT_X_MARGIN - right_margin;
454
455 for( i = 0, tc = l->chars; ; i++, tc++)
456 { if ( l->allocated <= i )
457 { ensure_chars_line(l, i+1);
458 tc = &l->chars[i];
459 }
460
461 index = (*ti->fetch)(ti->text, tc);
462 tc->index -= start;
463 tc->x = x;
464
465 switch(tc->type)
466 { case CHAR_ASCII:
467 switch(tc->value.c)
468 { case EOB:
469 case '\n':
470 x = right_margin;
471 l->ends_because |= END_NL;
472 l->length = ++i;
473 l->end = index;
474 if ( tc->value.c == EOB )
475 { index--;
476 l->ends_because |= END_EOF;
477 }
478 l->w = x;
479 ensure_chars_line(l, i+1);
480 tc = &l->chars[i];
481 tc->x = x;
482 fill_dimensions_line(l);
483 return index;
484 case '\t':
485 x = tab(ti, x);
486 last_is_space = TRUE;
487 break;
488 case ' ':
489 x += c_width((wint_t)tc->value.c, tc->font);
490 last_is_space = TRUE;
491 break;
492 default:
493 x += c_width((wint_t)tc->value.c, tc->font);
494 if ( last_is_space )
495 last_break = i;
496 last_is_space = FALSE;
497 break;
498 }
499 break;
500 case CHAR_GRAPHICAL:
501 ComputeGraphical(tc->value.graphical);
502
503 x += valInt(tc->value.graphical->area->w);
504 if ( last_is_space )
505 last_break = i;
506 last_is_space = FALSE;
507 break;
508 case CHAR_IMAGE:
509 x += valInt(tc->value.image->size->w);
510 if ( last_is_space )
511 last_break = i;
512 last_is_space = FALSE;
513 break;
514 }
515
516 if ( x >= right_margin )
517 { l->ends_because |= END_WRAP;
518
519 if ( ti->wrap == NAME_none )
520 { int eof;
521
522 l->ends_because |= END_CUT;
523 l->length = i;
524 l->w = tc->x = right_margin;
525
526 index = (*ti->scan)(ti->text, index, 1, TEXT_SCAN_FOR, EL, &eof) + 1;
527 l->end = index;
528 if ( eof )
529 l->ends_because |= END_EOF;
530 } else if ( ti->wrap == NAME_character )
531 { as_char:
532 if ( index - l->start <= 1 ) /* make sure at least 1 character */
533 { i++;
534 index++;
535 }
536 l->length = i;
537 index--;
538 l->end = index;
539 l->w = tc->x = x;
540 } else if ( ti->wrap == NAME_word )
541 { if ( last_break > 0 )
542 { int eof;
543
544 l->length = i = last_break;
545 l->w = l->chars[i].x;
546 index = l->start + l->chars[i].index;
547
548 (*ti->seek)(ti->text, index);
549 index = (*ti->scan)(ti->text, index, 1, TEXT_SKIP_OVER, BL, &eof);
550 l->end = index;
551 } else /* doesn't fit on line: as character */
552 { goto as_char;
553 }
554 }
555 break;
556 }
557 }
558
559 fill_dimensions_line(l);
560
561 return index;
562 }
563
564
565 #define equal_text_char(c1, c2) ( (c1)->value.c == (c2)->value.c && \
566 (c1)->font == (c2)->font && \
567 (c1)->colour == (c2)->colour && \
568 (c1)->background == (c2)->background && \
569 (c1)->x == (c2)->x && \
570 (c1)->attributes == (c2)->attributes )
571
572 static long
fill_line(TextImage ti,int line,long int index,short int y)573 fill_line(TextImage ti, int line, long int index, short int y)
574 { TextLine l;
575
576 ensure_lines_screen(ti->map, line+1);
577 l = &ti->map->lines[line];
578
579 if ( l->start == index && l->changed < 0 &&
580 (l->end < ti->change_start || l->start >= ti->change_end) )
581 { if ( l->y != y )
582 { l->y = y;
583 l->changed = 0;
584 }
585 return ti->map->lines[line+1].start;
586 }
587
588 if ( l->y != y )
589 { l->y = y;
590 l->changed = 0;
591
592 return do_fill_line(ti, l, index);
593 } else
594 { static struct text_line tmp;
595 long idx;
596
597 if ( !tmp.chars )
598 { tmp.chars = alloc(80 * sizeof(struct text_char));
599 tmp.allocated = 80;
600 }
601
602 idx = do_fill_line(ti, &tmp, index);
603 l->start = tmp.start;
604 l->end = tmp.end;
605 l->ends_because = tmp.ends_because;
606
607 if ( l->h != tmp.h || l->base != tmp.base )
608 { l->changed = 0;
609 copy_line_attributes(&tmp, l);
610 l->y = y; /* overruled by copy_line_attributes */
611 copy_line_chars(&tmp, 0, l);
612
613 return idx;
614 } else
615 { int i;
616 int n = min(l->length, tmp.length);
617
618 ensure_chars_line(l, tmp.length);
619 for(i=0; i<n; i++)
620 { if ( !equal_text_char(&tmp.chars[i], &l->chars[i]) )
621 { l->changed = i;
622 copy_line_chars(&tmp, i, l);
623 l->length = tmp.length;
624
625 return idx;
626 }
627 }
628 if ( i < tmp.length )
629 { l->changed = i;
630 copy_line_chars(&tmp, i, l);
631 }
632 if ( tmp.length < l->length )
633 l->changed = tmp.length;
634 l->length = tmp.length;
635
636 if ( l->w != tmp.w )
637 { /*Cprintf("Line changed width, ->changed = %d\n", l->changed);*/
638
639 if ( l->chars[l->length].x == l->w )
640 l->chars[l->length].x = tmp.w;
641 l->w = tmp.w;
642 l->changed = l->length-1;
643 }
644
645 return idx;
646 }
647 }
648 }
649
650
651 static status
updateMapTextImage(TextImage ti)652 updateMapTextImage(TextImage ti)
653 { if ( ti->change_end > ti->change_start )
654 { BoolObj eof_in_window = OFF;
655 int line;
656 short y = TXT_Y_MARGIN;
657 long index = valInt(ti->start);
658
659 DEBUG(NAME_text, Cprintf("Updating map from %d to %d ",
660 ti->change_start, ti->change_end));
661
662 if ( ti->rewind )
663 (*ti->rewind)(ti->text);
664
665 for(line = 0; ; line++)
666 { long next_index;
667
668 next_index = fill_line(ti, line, index, y);
669 DEBUG(NAME_text,
670 Cprintf("Line %d %4ld..%4ld (changed = %d, y=%d, h=%d)\n",
671 line, index, next_index, ti->map->lines[line].changed,
672 y, ti->map->lines[line].h));
673 if ( line >= ti->map->skip )
674 y += ti->map->lines[line].h;
675
676 if ( y > ti->h - TXT_Y_MARGIN && line > 0 )
677 { ti->map->length = line - ti->map->skip;
678 assign(ti, end, toInt(index));
679 assign(ti, eof_in_window, eof_in_window);
680 ti->change_start = INFINITE;
681 ti->change_end = 0;
682 DEBUG(NAME_text, Cprintf("ok; eof_in_window = %s\n",
683 pp(eof_in_window)); );
684
685 succeed;
686 }
687
688 index = next_index;
689 if ( ti->map->lines[line].ends_because & END_EOF )
690 eof_in_window = ON;
691 }
692 }
693
694 succeed;
695 }
696
697
698 /********************************
699 * DUMP THE MAP *
700 ********************************/
701
702 static void
dump_map(TextScreen map)703 dump_map(TextScreen map)
704 { int i;
705
706 Cprintf("skip = %d; length = %d, allocated = %d lines\n",
707 map->skip, map->length, map->allocated);
708
709 for(i=0; i<map->skip + map->length; i++)
710 { TextLine l = &map->lines[i];
711 int n;
712 int c;
713
714 if ( i < map->skip )
715 Cprintf("--:");
716 else
717 Cprintf("%2d:", i - map->skip);
718 Cprintf("%4ld-%4ld at y=%3d changed = %d ",
719 l->start, l->start + l->length, l->y, l->changed);
720 Cputchar((l->ends_because & END_EOF) ? 'F' : '-');
721 Cputchar((l->ends_because & END_WRAP) ? 'W' : '-');
722 Cputchar((l->ends_because & END_CUT) ? 'C' : '-');
723 Cputchar((l->ends_because & END_NL) ? 'L' : '-');
724 Cprintf(": \"");
725 for(n=0; n < 5 && n < l->length; n++)
726 { if ( (c = l->chars[n].value.c) == '\n' )
727 Cprintf("\\n");
728 else if ( c == EOB )
729 Cprintf("\\$");
730 else
731 Cputchar(c);
732 }
733 if ( l->length - 5 > n )
734 { Cprintf(" ... ");
735 n = l->length - 5;
736 }
737 for( ; n < l->length; n++ )
738 { if ( (c = l->chars[n].value.c) == '\n' )
739 Cprintf("\\n");
740 else if ( c == EOB )
741 Cprintf("\\$");
742 else
743 Cputchar(c);
744 }
745 Cprintf("\"\n");
746 }
747 }
748
749 static status
dumpMapTextImage(TextImage ti)750 dumpMapTextImage(TextImage ti)
751 { dump_map(ti->map);
752
753 succeed;
754 }
755
756
757 /********************************
758 * PAINTING PRIMITIVES *
759 ********************************/
760
761 static void
t_underline(int x,int y,int w,Colour c)762 t_underline(int x, int y, int w, Colour c)
763 { static int ex = 0, ey = 0, ew = 0;
764 static Colour cc = NIL;
765
766 if ( x == ex+ew && y == ey && c == cc )
767 { ew += w;
768 } else
769 { if ( ew > 0 )
770 { r_colour(cc);
771 r_line(ex, ey, ex+ew, ey);
772 }
773 ex = x, ey = y, ew = w;
774 cc = c;
775 }
776 }
777
778
779 static void
t_invert(int x,int y,int w,int h)780 t_invert(int x, int y, int w, int h)
781 { static int ix=0, iy=0, iw=0, ih=0;
782
783 if ( iw == 0 && ih == 0 )
784 { ix = x, iy = y, iw = w, ih = h;
785 } else
786 { if ( iy == y && ih == h && ix + iw == x )
787 { iw += w;
788 return;
789 }
790 }
791
792 r_complement(ix, iy, iw, ih);
793 ix=0, iy=0, iw=0, ih=0;
794 }
795
796
797 static void
t_grey(int x,int y,int w,int h)798 t_grey(int x, int y, int w, int h)
799 { static int ix=0, iy=0, iw=0, ih=0;
800
801 if ( iw == 0 && ih == 0 )
802 { ix = x, iy = y, iw = w, ih = h;
803 } else
804 { if ( iy == y && ih == h && ix + iw == x )
805 { iw += w;
806 return;
807 }
808 }
809
810 r_and(ix, iy, iw, ih, GREY50_IMAGE);
811 ix=0, iy=0, iw=0, ih=0;
812 }
813
814
815 /*******************************
816 * GRAPHICS PAINTING *
817 *******************************/
818
819 static void
ascent_and_descent_graphical(Graphical gr,int * ascent,int * descent)820 ascent_and_descent_graphical(Graphical gr, int *ascent, int *descent)
821 { Point r;
822
823 if ( instanceOfObject(gr, ClassDialogItem) )
824 { if ( (r = qadGetv(gr, NAME_reference, 0, NULL)) )
825 *ascent = valInt(r->y);
826 else
827 *ascent = valInt(gr->area->h);
828 } else if ( onFlag(gr, F_ATTRIBUTE) &&
829 (r = getAttributeObject(gr, NAME_reference)) )
830 { *ascent = valInt(r->y);
831 } else
832 *ascent = valInt(gr->area->h);
833
834 if ( descent )
835 *descent = valInt(gr->area->h) - *ascent;
836 }
837
838
839 static void
ascent_and_descent_image(Image im,int * ascent,int * descent)840 ascent_and_descent_image(Image im, int *ascent, int *descent)
841 { if ( notNil(im->hot_spot) )
842 *ascent = valInt(im->hot_spot->y);
843 else
844 *ascent = valInt(im->size->h);
845
846 if ( descent )
847 *descent = valInt(im->size->h) - *ascent;
848 }
849
850
851 static void
paint_graphical(TextImage ti,Area a,Graphical gr,int x,int base)852 paint_graphical(TextImage ti, Area a, Graphical gr, int x, int base)
853 { int dx, dy;
854 int asc;
855 Int ox = a->x;
856 Int oy = a->y;
857
858 ascent_and_descent_graphical(gr, &asc, NULL);
859 dx = x - valInt(gr->area->x);
860 dy = base - (valInt(gr->area->y) + asc);
861
862 r_offset(dx, dy);
863 assign(a, x, toInt(valInt(a->x) - dx));
864 assign(a, y, toInt(valInt(a->y) - dy));
865 RedrawArea(gr, a);
866 assign(a, x, ox);
867 assign(a, y, oy);
868 r_offset(-dx, -dy);
869 }
870
871
872 static void
paint_image(TextImage ti,Area a,Image im,int x,int base)873 paint_image(TextImage ti, Area a, Image im, int x, int base)
874 { int asc;
875
876 ascent_and_descent_image(im, &asc, NULL);
877
878 DEBUG(NAME_image, Cprintf("Painting %s at %d, %d\n", pp(im), x, base));
879
880 r_image(im,
881 0, 0,
882 x, base - asc, valInt(im->size->w), valInt(im->size->h),
883 ON);
884 }
885
886
887
888 /********************************
889 * PAINTING *
890 ********************************/
891
892 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
893 Paint a line from index `from' to index `to'.
894 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
895
896 static void
paint_attributes(TextImage ti,TextLine l,int from,int to,Colour c)897 paint_attributes(TextImage ti, TextLine l, int from, int to, Colour c)
898 { unsigned char atts = l->chars[from].attributes;
899
900 if ( atts & TXT_UNDERLINED )
901 { t_underline(l->chars[from].x, l->y + l->h - 1,
902 l->chars[to].x - l->chars[from].x, c);
903 }
904 if ( atts & TXT_HIGHLIGHTED )
905 { int w = (to == l->length ? ti->w - TXT_X_MARGIN : l->chars[to].x);
906
907 t_invert(l->chars[from].x, l->y,
908 w - l->chars[from].x, l->h);
909 }
910 if ( atts & TXT_GREYED )
911 { t_grey(l->chars[from].x, l->y,
912 l->chars[to].x - l->chars[from].x, l->h);
913 }
914 }
915
916
917 #define PutBuf(c) if ( (size_t)((char*)out-(char*)buf) < sizeof(buf) ) *out++ = (c)
918
919 static void
paint_line(TextImage ti,Area a,TextLine l,int from,int to)920 paint_line(TextImage ti, Area a, TextLine l, int from, int to)
921 { charW buf[1000];
922 charW *out;
923 int n, s = from, e;
924 FontObj f;
925 Colour c;
926 Any bg;
927 unsigned char atts;
928 int cx, cw;
929 int pen = valInt(ti->pen);
930 int rmargin = ti->w - TXT_X_MARGIN;
931
932 DEBUG(NAME_text, Cprintf("painting line %p from %d to %d\n",
933 l, from, to));
934
935 cx = (from == 0 ? pen : l->chars[from].x);
936 cw = (to >= l->length ? rmargin : l->chars[to].x) - cx;
937 r_clear(cx, l->y, cw, l->h);
938
939 { TextChar last = &l->chars[to-1];
940
941 if ( last->value.c == EOB )
942 to--;
943 }
944
945 for( s = from; s < to; s = e )
946 { int prt;
947 int chr = l->chars[s].value.c;
948
949 e = s;
950
951 c = l->chars[e].colour;
952 bg = l->chars[e].background;
953
954 switch(l->chars[e].type)
955 { case CHAR_GRAPHICAL:
956 if ( notDefault(bg) && !instanceOfObject(bg, ClassElevation) )
957 { int x = l->chars[s].x;
958 int tx = l->chars[s+1].x;
959 r_fill(x, l->y, tx-x, l->h, bg);
960 }
961 paint_graphical(ti, a,
962 l->chars[e].value.graphical,
963 l->chars[e].x,
964 l->y + l->base);
965 e++;
966 paint_attributes(ti, l, s, e, c);
967 continue;
968 case CHAR_IMAGE:
969 if ( notDefault(bg) && !instanceOfObject(bg, ClassElevation) )
970 { int x = l->chars[s].x;
971 int tx = l->chars[s+1].x;
972 r_fill(x, l->y, tx-x, l->h, bg);
973 }
974 r_colour(c);
975 paint_image(ti, a,
976 l->chars[e].value.image,
977 l->chars[e].x,
978 l->y + l->base);
979 e++;
980 paint_attributes(ti, l, s, e, c);
981 continue;
982 }
983
984 n = 0;
985 f = l->chars[e].font;
986 atts = l->chars[e].attributes;
987 out = buf;
988
989 PutBuf(chr);
990
991 if ( chr == '\t' ) /* print tabs */
992 { prt = FALSE;
993
994 for(n++, e++; e < to; n++, e++)
995 { if ( l->chars[e].type != CHAR_ASCII ||
996 l->chars[e].attributes != atts ||
997 l->chars[e].background != bg ||
998 l->chars[e].value.c != '\t' )
999 break;
1000 }
1001 } else if ( chr == '\n' ) /* newline */
1002 { prt = FALSE;
1003
1004 e++;
1005 } else /* real text */
1006 { prt = TRUE;
1007
1008 for(n++, e++; e < to; n++, e++)
1009 { if ( l->chars[e].font != f ||
1010 l->chars[e].colour != c ||
1011 l->chars[e].background != bg ||
1012 l->chars[e].attributes != atts ||
1013 l->chars[e].value.c == '\t' ||
1014 l->chars[e].value.c == '\n' )
1015 break;
1016
1017 PutBuf(l->chars[e].value.c);
1018 }
1019 }
1020
1021 if ( notDefault(bg) )
1022 { if ( instanceOfObject(bg, ClassElevation) )
1023 { int f, t, x, tx;
1024
1025 for(f=s-1; f>=0 && l->chars[f].background == bg; f--)
1026 ;
1027 f++;
1028 for(t=e; t<l->length && l->chars[t].background == bg; t++)
1029 ;
1030
1031 x = l->chars[f].x;
1032 tx = l->chars[t].x;
1033 r_3d_box(x, l->y, tx-x, l->h, 0, bg, TRUE);
1034 } else
1035 { int x = l->chars[s].x;
1036 int tx = l->chars[e].x;
1037
1038 if ( tx > rmargin ) tx = rmargin;
1039 r_fill(x, l->y, tx-x, l->h, bg);
1040 }
1041 }
1042
1043 if ( prt )
1044 { r_colour(c);
1045
1046 s_printW(buf, e - s, l->chars[s].x, l->y + l->base, f);
1047
1048 if ( atts & TXT_BOLDEN )
1049 { s_printW(buf, e - s, l->chars[s].x+1, l->y + l->base, f);
1050 s_printW(buf, e - s, l->chars[s].x, l->y-1 + l->base, f);
1051 }
1052 }
1053
1054 paint_attributes(ti, l, s, e, c);
1055 }
1056
1057 t_underline(0, 0, 0, NIL);
1058 }
1059
1060
1061 static void
paint_area(TextImage ti,Area a,int x,int y,int w,int h)1062 paint_area(TextImage ti, Area a, int x, int y, int w, int h)
1063 { int p = valInt(ti->pen);
1064
1065 if ( x < ti->w - TXT_X_MARGIN && x+w >= TXT_X_MARGIN &&
1066 y < ti->h + TXT_Y_MARGIN && y+h >= TXT_Y_MARGIN )
1067 { TextLine ml = line_from_y(ti, y);
1068 int line = ml - &ti->map->lines[ti->map->skip];
1069 int ly = 0;
1070
1071 for(line = 0; line < ti->map->length && ml->y < y+h; line++, ml++)
1072 { if ( ml->y + ml->h > y )
1073 { int f, t;
1074
1075 if ( ml->y + ml->h > ti->h - TXT_Y_MARGIN )
1076 break;
1077
1078 f = char_from_x(ml, x);
1079 t = char_from_x(ml, x+w);
1080
1081 paint_line(ti, a, ml, f, t+1); /* TBD: get correct boundaries */
1082 ly = ml->y + ml->h;
1083 }
1084 }
1085
1086 if ( y + h > ly )
1087 r_clear(p, ly, ti->w-2*p, y+h-ly);
1088 }
1089
1090 if ( y < TXT_Y_MARGIN )
1091 r_clear(p, p, ti->w-2*p, TXT_Y_MARGIN-p);
1092 if ( x+w >= ti->w - TXT_X_MARGIN )
1093 r_clear(ti->w - TXT_X_MARGIN, p, TXT_X_MARGIN-p, ti->h-2*p);
1094 }
1095
1096
1097 /********************************
1098 * INDEX <-> POSITION *
1099 ********************************/
1100
1101 static TextLine
line_from_y(TextImage ti,int y)1102 line_from_y(TextImage ti, int y)
1103 { if ( ti->map && ti->map->lines )
1104 { int l = ti->map->skip;
1105 int h = ti->map->length - 1;
1106 int m;
1107 TextLine tl;
1108
1109 if ( y < ti->map->lines[l].y )
1110 return &ti->map->lines[l];
1111 if ( y >= ti->map->lines[h].y + ti->map->lines[h].h )
1112 return &ti->map->lines[h];
1113
1114 for(;;)
1115 { m = (l+h) / 2;
1116 tl = &ti->map->lines[m];
1117
1118 if ( y >= tl->y )
1119 { if ( y < tl->y + tl->h )
1120 return tl;
1121 l = (l == m ? l+1 : m);
1122 } else
1123 h = m;
1124 }
1125 }
1126
1127 return NULL;
1128 }
1129
1130
1131 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1132 Determine the character index from a given X-pixel coordinate.
1133 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1134
1135 static int
char_from_x(TextLine tl,int x)1136 char_from_x(TextLine tl, int x)
1137 { int l = 0;
1138 int h = tl->length - 1;
1139 int m;
1140
1141 if ( x < tl->chars[l].x )
1142 return l;
1143 if ( x >= tl->chars[h+1].x )
1144 return h;
1145
1146 for(;;)
1147 { m = (l+h) / 2;
1148
1149 if ( x >= tl->chars[m].x )
1150 { if ( x < tl->chars[m+1].x )
1151 return m;
1152 l = (l == m ? l+1 : m);
1153 } else
1154 h = m;
1155 }
1156 }
1157
1158
1159 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1160 Determine the X-Y position of character at index `pos'. The top-left
1161 corner is defined to be (1,1).
1162
1163 In the current implementation both the Y- and X-search is linear. These
1164 should be changed to binary searches someday, but this routine is not
1165 uterly time critical.
1166
1167 In X-direction, we first do a quick test hoping the characters in the
1168 line are adjacent (i.e. no characters are hidden).
1169 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1170
1171 static status
get_xy_pos(TextImage ti,Int pos,int * x,int * y)1172 get_xy_pos(TextImage ti, Int pos, int *x, int *y)
1173 { int line;
1174 int index = valInt(pos);
1175 int skip;
1176
1177 ComputeGraphical(ti);
1178 skip = ti->map->skip;
1179
1180 for(line=0; line < ti->map->length; line++)
1181 { TextLine l = &ti->map->lines[skip + line];
1182
1183 /* binary search? */
1184 if ( index >= l->start && index < l->end )
1185 { if ( x )
1186 { int li = index - l->start; /* X-index in line */
1187
1188 if ( li > l->length || l->chars[li].index != li ) /* Quick test */
1189 { if ( l->length > 0 && li > l->chars[l->length-1].index )
1190 li = l->length - 1;
1191 else
1192 { TextChar ch = l->chars;
1193 TextChar lc = &l->chars[l->length];
1194
1195 /* binary search! */
1196 while(ch < lc && ch->index < li)
1197 ch++;
1198
1199 li = ch - l->chars;
1200 }
1201 }
1202
1203 *x = li + 1;
1204 }
1205 if ( y )
1206 *y = line + 1;
1207 succeed;
1208 }
1209 }
1210
1211 fail;
1212 }
1213
1214
1215 status
get_character_box_textimage(TextImage ti,int index,int * x,int * y,int * w,int * h,int * b)1216 get_character_box_textimage(TextImage ti, int index,
1217 int *x, int *y, int *w, int *h, int *b)
1218 { int cx, cy;
1219
1220 if ( get_xy_pos(ti, toInt(index), &cx, &cy) )
1221 { TextLine l = &ti->map->lines[cy-1+ti->map->skip];
1222 TextChar tc = &l->chars[cx-1];
1223
1224 *x = tc->x; *y = l->y, *w = tc[1].x - tc->x; *h = l->h; *b = l->base;
1225
1226 succeed;
1227 }
1228
1229 fail;
1230 }
1231
1232
1233 static Point
getCharacterPositionTextImage(TextImage ti,Int index)1234 getCharacterPositionTextImage(TextImage ti, Int index)
1235 { int x, y, w, h, b;
1236
1237 if ( get_character_box_textimage(ti, valInt(index),
1238 &x, &y, &w, &h, &b) )
1239 answer(answerObject(ClassPoint, toInt(x), toInt(y+b), EAV));
1240
1241 fail;
1242 }
1243
1244
1245 static int
get_index_text_image(TextImage ti,int x,int y)1246 get_index_text_image(TextImage ti, int x, int y)
1247 { int line;
1248 int skip;
1249
1250 ComputeGraphical(ti);
1251 skip = ti->map->skip;
1252
1253 if ( y < TXT_Y_MARGIN )
1254 return valInt(ti->start);
1255
1256 for(line=0; line < ti->map->length; line++)
1257 { TextLine l = &ti->map->lines[line + skip];
1258
1259 if ( y >= l->y && y < l->y + l->h )
1260 { int i;
1261
1262 if ( x < TXT_X_MARGIN )
1263 return l->start;
1264
1265 for(i = 0; i < l->length; i++)
1266 if ( l->chars[i+1].x > x )
1267 return l->start + l->chars[i].index; /* bsearch()! */
1268
1269 return l->start + l->length - 1;
1270 }
1271 }
1272
1273 return valInt(ti->end) - 1;
1274 }
1275
1276
1277 Int
getLinesTextImage(TextImage ti)1278 getLinesTextImage(TextImage ti)
1279 { ComputeGraphical(ti);
1280
1281 answer(toInt(ti->map->length));
1282 }
1283
1284
1285 static Int
getLineTextImage(TextImage ti,Int pos)1286 getLineTextImage(TextImage ti, Int pos)
1287 { int cy;
1288
1289 if ( get_xy_pos(ti, pos, NULL, &cy) )
1290 answer(toInt(cy));
1291
1292 fail;
1293 }
1294
1295 /********************************
1296 * EVENTS *
1297 ********************************/
1298
1299 static status
resetTextImage(TextImage ti)1300 resetTextImage(TextImage ti)
1301 { if ( notNil(ti->pointed) )
1302 { DeviceGraphical(ti->pointed, NIL);
1303 assign(ti, pointed, NIL);
1304 }
1305
1306 succeed;
1307 }
1308
1309
1310 Int
getIndexTextImage(TextImage ti,EventObj ev)1311 getIndexTextImage(TextImage ti, EventObj ev)
1312 { Int X, Y;
1313 int x, y;
1314
1315 get_xy_event(ev, ti, ON, &X, &Y);
1316 x = valInt(X);
1317 y = valInt(Y);
1318
1319 if ( x < 0 || x > ti->w )
1320 fail;
1321
1322 answer(toInt(get_index_text_image(ti, x, y)));
1323 }
1324
1325
1326 static status
updatePointedTextImage(TextImage ti,EventObj ev,long * where)1327 updatePointedTextImage(TextImage ti, EventObj ev, long *where)
1328 { Int x, y;
1329 TextLine tl = NULL;
1330 TextChar tc = NULL;
1331 Graphical gr;
1332
1333 if ( isAEvent(ev, NAME_areaExit) )
1334 { if ( notNil(ti->pointed) )
1335 { PceWindow sw = getWindowGraphical((Graphical) ti->device);
1336
1337 if ( sw )
1338 { if ( sw->focus == ti->pointed )
1339 focusWindow(sw, NIL, NIL, NIL, NIL);
1340 else if ( subGraphical(ti->pointed, sw->keyboard_focus) )
1341 keyboardFocusWindow(sw, NIL);
1342 }
1343 }
1344
1345 gr = NIL;
1346
1347 } else
1348 { get_xy_event(ev, ti, ON, &x, &y);
1349 if ( (tl = line_from_y(ti, valInt(y))) &&
1350 (tc = &tl->chars[char_from_x(tl, valInt(x))]) &&
1351 tc->type == CHAR_GRAPHICAL )
1352 { *where = tl->start + tc->index;
1353 gr = tc->value.graphical;
1354 } else
1355 gr = NIL;
1356 }
1357
1358 if ( gr != ti->pointed )
1359 { Name enter, exit;
1360
1361 if ( allButtonsUpEvent(ev) )
1362 { enter = NAME_areaEnter;
1363 exit = NAME_areaExit;
1364 } else
1365 { enter = NAME_areaResume;
1366 exit = NAME_areaCancel;
1367 }
1368
1369 if ( notNil(ti->pointed) )
1370 generateEventGraphical(ti->pointed, exit);
1371
1372 assign(ti, pointed, gr);
1373
1374 if ( notNil(gr) )
1375 { Int ty, tx = toInt(valInt(ti->area->x) + tc->x);
1376 int asc;
1377
1378 ascent_and_descent_graphical(gr, &asc, NULL);
1379 ty = toInt(valInt(ti->area->y) + tl->y + tl->base - asc);
1380
1381 doSetGraphical(gr, tx, ty, DEFAULT, DEFAULT);
1382
1383 generateEventGraphical(ti->pointed, enter);
1384 }
1385 }
1386
1387 succeed;
1388 }
1389
1390
1391 static CursorObj
getDisplayedCursorTextImage(TextImage ti)1392 getDisplayedCursorTextImage(TextImage ti)
1393 { if ( notNil(ti->pointed) )
1394 { CursorObj c;
1395
1396 if ( notNil(c=qadGetv(ti->pointed, NAME_displayedCursor, 0, NULL)) )
1397 answer(c);
1398
1399 answer(NIL);
1400 }
1401
1402 answer(ti->cursor);
1403 }
1404
1405
1406 static status
eventTextImage(TextImage ti,EventObj ev)1407 eventTextImage(TextImage ti, EventObj ev)
1408 { if ( eventGraphical(ti, ev) )
1409 { succeed;
1410 } else
1411 { long where = 0L;
1412
1413 updatePointedTextImage(ti, ev, &where);
1414
1415 if ( notNil(ti->pointed) )
1416 { Graphical gr = ti->pointed;
1417 status rval;
1418 PceWindow sw = getWindowGraphical((Graphical) ti->device);
1419 Area a = gr->area;
1420 Int ow = a->w, oh = a->h;
1421
1422 DeviceGraphical(gr, ti->device);
1423 DisplayedGraphical(gr, ON);
1424 rval = postEvent(ev, gr, DEFAULT);
1425 if ( sw && (sw->focus == gr || sw->keyboard_focus == gr) )
1426 { DisplayObj d = getDisplayGraphical((Graphical) sw);
1427 TextCursor tc = NIL;
1428 Any tcon = NIL;
1429
1430 if ( sw->keyboard_focus == gr &&
1431 instanceOfObject(ti->device, ClassEditor) )
1432 { Editor e = (Editor) ti->device;
1433 tc = e->text_cursor;
1434 if ( notNil(tc) )
1435 tcon = tc->active;
1436 send(tc, NAME_active, OFF, EAV);
1437 }
1438
1439 while( !onFlag(sw, F_FREED|F_FREEING) &&
1440 (sw->focus == gr || sw->keyboard_focus == gr) )
1441 { if ( dispatchDisplay(d) )
1442 ws_discard_input("Focus on graphical in editor");
1443 }
1444
1445 if ( notNil(tcon) && !onFlag(tc, F_FREED|F_FREEING) )
1446 send(tc, NAME_active, tcon, EAV);
1447 }
1448 if ( !onFlag(gr, F_FREED|F_FREEING) &&
1449 !onFlag(ti, F_FREED|F_FREEING) )
1450 { DeviceGraphical(gr, NIL);
1451 a = gr->area;
1452
1453 if ( ow != a->w || oh != a->h )
1454 { DEBUG(NAME_diagram, Cprintf("%s: Changed %d\n", pp(ti), where));
1455 ChangedRegionTextImage(ti, toInt(where), toInt(where+1));
1456 }
1457 }
1458
1459 return rval;
1460 }
1461 }
1462
1463 fail;
1464 }
1465
1466
1467
1468 /********************************
1469 * REDRAW *
1470 ********************************/
1471 static status
RedrawAreaTextImage(TextImage ti,Area a)1472 RedrawAreaTextImage(TextImage ti, Area a)
1473 { int x, y, w, h;
1474 int bx, by, bw, bh;
1475 int sx, sy;
1476 int p = valInt(ti->pen);
1477 int ox = valInt(ti->area->x);
1478 int oy = valInt(ti->area->y);
1479 Any obg;
1480
1481 initialiseDeviceGraphical(ti, &x, &y, &w, &h);
1482 bx = x, by = y, bw = w, bh = h;
1483
1484 sx = valInt(a->x) - valInt(ti->area->x); if ( sx < p ) sx = p;
1485 sy = valInt(a->y) - valInt(ti->area->y); if ( sy < p ) sy = p;
1486 x += sx, w -= sx + p;
1487 y += sy, h -= sy + p;
1488 if ( w > valInt(a->w) ) w = valInt(a->w);
1489 if ( h > valInt(a->h) ) h = valInt(a->h);
1490
1491 obg = r_background(ti->background);
1492 if ( sx < TXT_X_MARGIN || sx + w > ti->w - TXT_X_MARGIN ||
1493 sy < TXT_Y_MARGIN || sy + h > ti->h - TXT_Y_MARGIN )
1494 { Elevation z = ti->elevation;
1495
1496 if ( z && notNil(z) )
1497 { r_3d_box(bx, by, bw, bh, 0, z, FALSE);
1498 } else
1499 { r_thickness(p);
1500 r_dash(ti->texture);
1501 r_box(bx, by, bw, bh, 0, NIL);
1502 }
1503 }
1504 r_offset(ox, oy);
1505 r_thickness(1); /* default for underlining */
1506 r_dash(NAME_none);
1507 paint_area(ti, a, sx, sy, w, h);
1508 r_offset(-ox, -oy);
1509 r_background(obg);
1510
1511 return RedrawAreaGraphical(ti, a);
1512 }
1513
1514
1515 status
computeTextImage(TextImage ti)1516 computeTextImage(TextImage ti)
1517 { if ( notNil(ti->request_compute) )
1518 { TextLine ml;
1519 int line;
1520 int fy = 0, ty = 0, fx = 100000, tx = ti->w - TXT_X_MARGIN;
1521
1522 updateMapTextImage(ti);
1523
1524 ml = &ti->map->lines[ti->map->skip];
1525 for(line = 0; line < ti->map->length; line++, ml++)
1526 { int cy = ml->y + ml->h;
1527
1528 if ( cy > ti->h - TXT_Y_MARGIN )
1529 { if ( fy != ty )
1530 ty = cy;
1531 break;
1532 }
1533
1534 if ( ml->changed >= 0 )
1535 { int cx;
1536
1537 if ( line == ti->map->length - 1 ) /* last line */
1538 cy = ti->h - valInt(ti->pen);
1539
1540 if ( fy == ty )
1541 { fy = ml->y;
1542 ty = cy;
1543 } else
1544 ty = cy;
1545
1546 if ( ml->changed == 0 )
1547 cx = TXT_X_MARGIN;
1548 else
1549 cx = ml->chars[ml->changed].x;
1550 if ( cx < fx )
1551 fx = cx;
1552
1553 ml->changed = -1;
1554 }
1555 }
1556
1557 DEBUG(NAME_text, Cprintf("changedImageGraphical(%s, %d, %d, %d, %d)\n",
1558 pp(ti), fx, fy, tx-fx, ty-fy));
1559 if ( ty > fy )
1560 changedImageGraphical(ti,
1561 toInt(fx), toInt(fy), toInt(tx-fx), toInt(ty-fy));
1562
1563 assign(ti, request_compute, NIL);
1564 }
1565
1566 succeed;
1567 }
1568
1569
1570 /********************************
1571 * CHANGING PARAMETERS *
1572 ********************************/
1573
1574 status
startTextImage(TextImage ti,Int start,Int skip)1575 startTextImage(TextImage ti, Int start, Int skip)
1576 { TextScreen map = ti->map;
1577
1578 if ( isDefault(skip) )
1579 skip = ZERO;
1580 if ( isDefault(start) )
1581 start = ti->start;
1582
1583 if ( ti->start != start ||
1584 map->skip != valInt(skip) )
1585 { assign(ti, start, start);
1586
1587 if ( map->skip != valInt(skip) )
1588 { int sl = 0;
1589 int el = map->length + map->skip;
1590 short y = TXT_Y_MARGIN;
1591
1592 map->skip = valInt(skip);
1593
1594 for( ; sl < el; sl++ )
1595 { map->lines[sl].y = y;
1596 if ( sl >= map->skip )
1597 y += map->lines[sl].h;
1598 }
1599 }
1600
1601 return ChangedEntireTextImage(ti);
1602 }
1603
1604 succeed;
1605 }
1606
1607
1608 static int
locate_screen_line(TextScreen map,int pos)1609 locate_screen_line(TextScreen map, int pos)
1610 { int i;
1611
1612 for(i=0; i < map->skip + map->length; i++)
1613 { if ( pos >= map->lines[i].start &&
1614 pos < map->lines[i].end )
1615 return i;
1616 }
1617
1618 return -1; /* not in the map */
1619 }
1620
1621
1622 static void
copy_line_attributes(TextLine from,TextLine to)1623 copy_line_attributes(TextLine from, TextLine to)
1624 { to->y = from->y;
1625 to->h = from->h;
1626 to->base = from->base;
1627 to->length = from->length;
1628 to->w = from->w;
1629 }
1630
1631
1632 static void
copy_line_chars(TextLine from,int start,TextLine to)1633 copy_line_chars(TextLine from, int start, TextLine to)
1634 { int end = from->length+1;
1635
1636 ensure_chars_line(to, end);
1637
1638 for( ; start < end; start++ )
1639 to->chars[start] = from->chars[start];
1640 }
1641
1642
1643 static void
copy_line(TextLine from,TextLine to)1644 copy_line(TextLine from, TextLine to)
1645 { copy_line_attributes(from, to);
1646 copy_line_chars(from, 0, to);
1647 }
1648
1649
1650 static long
paragraph_start(TextImage ti,long int pos)1651 paragraph_start(TextImage ti, long int pos)
1652 { int eof;
1653 long index;
1654
1655 index = (*ti->scan)(ti->text, pos-1, -1, TEXT_SCAN_FOR, EL, &eof);
1656
1657 return eof ? index : index + 1;
1658 }
1659
1660
1661 static void
shift_lines_down(TextScreen map,int from,int n)1662 shift_lines_down(TextScreen map, int from, int n)
1663 { int i;
1664
1665 if ( map->skip + map->length + n > map->allocated )
1666 ensure_lines_screen(map, map->skip + map->length + n);
1667
1668 for(i = map->skip + map->length + n - 1; i >= n + from; i--)
1669 { TextLine fl = &map->lines[i-n];
1670 TextLine tl = &map->lines[i];
1671
1672 copy_line(fl, tl);
1673 tl->start = fl->start;
1674 tl->end = fl->end;
1675 tl->w = fl->w;
1676 tl->changed = fl->changed;
1677 }
1678
1679 map->length += n;
1680 }
1681
1682
1683 static status
center_from_screen(TextImage ti,long int pos,int line)1684 center_from_screen(TextImage ti, long int pos, int line)
1685 { TextScreen map = ti->map;
1686 int l;
1687
1688 if ( (l = locate_screen_line(map, pos)) >= 0 &&
1689 l >= line )
1690 { int startline = l - line;
1691 int skip = 0;
1692
1693 while( startline > 0 &&
1694 !(map->lines[startline-1].ends_because & END_NL) )
1695 { startline--;
1696 skip++;
1697 }
1698 DEBUG(NAME_center, Cprintf("Start at %ld; skip = %d\n",
1699 map->lines[startline].start, skip));
1700
1701 startTextImage(ti, toInt(map->lines[startline].start), toInt(skip));
1702
1703 succeed;
1704 }
1705
1706 DEBUG(NAME_center, Cprintf("Out of screen: l = %d\n", l));
1707 fail;
1708 }
1709
1710
1711 status
centerTextImage(TextImage ti,Int position,Int screen_line)1712 centerTextImage(TextImage ti, Int position, Int screen_line)
1713 { int pos = valInt(position);
1714 int line;
1715 TextScreen map = ti->map;
1716
1717 ComputeGraphical(ti);
1718 line = (isDefault(screen_line) ? ti->map->length/2 : valInt(screen_line)-1);
1719 if ( line < 0 )
1720 line = 0;
1721
1722 DEBUG(NAME_center, writef("%s: center %d at line %d\n",
1723 ti, position, toInt(line)));
1724
1725 /* Info on the screen: simple */
1726 if ( center_from_screen(ti, pos, line) )
1727 succeed;
1728 else
1729 { long here = pos;
1730 long start;
1731
1732 map->length = map->skip = 0; /* empty the map */
1733 ChangedEntireTextImage(ti); /* recompute next time */
1734
1735 for( ; (start = paragraph_start(ti, here)) > 0; here = start-1 )
1736 { long idx = start;
1737 int ln = 0;
1738
1739 DEBUG(NAME_center, Cprintf("ParStart = %ld\n", start));
1740 do
1741 { shift_lines_down(map, ln, 1);
1742 idx = fill_line(ti, ln, idx, 0);
1743 DEBUG(NAME_center, Cprintf("Filled line %d to %ld\n", ln-1, idx));
1744 } while ( idx <= here &&
1745 !(ti->map->lines[ln++].ends_because & END_EOF) );
1746
1747 if ( center_from_screen(ti, pos, line) )
1748 succeed;
1749 }
1750
1751 return startTextImage(ti, ZERO, ZERO); /* best we can do */
1752 }
1753
1754 }
1755
1756
1757 Int
getStartTextImage(TextImage ti,Int line)1758 getStartTextImage(TextImage ti, Int line)
1759 { int ln = isDefault(line) ? 1 : valInt(line);
1760 TextScreen map = ti->map;
1761 static struct text_line tl; /* reusable dummy line */
1762
1763 ComputeGraphical(ti);
1764
1765 if ( ln >= 0 )
1766 { ln--;
1767 } else
1768 { ln += map->length;
1769 }
1770
1771 DEBUG(NAME_start, Cprintf("Looking for start of line %d\n", ln));
1772
1773 if ( ln < 0 )
1774 { if ( -ln <= map->skip )
1775 { answer(toInt(map->lines[map->skip + ln].start));
1776 } else
1777 { long here = map->lines[0].start;
1778 long start;
1779
1780 ln = -ln - map->skip; /* lines before idx */
1781 do
1782 { long idx = start = paragraph_start(ti, here-1);
1783 DEBUG(NAME_start, Cprintf("start = %ld; here = %ld\n", start, here));
1784 do
1785 { idx = do_fill_line(ti, &tl, idx);
1786 DEBUG(NAME_start, Cprintf("line to %ld; ln = %d\n", idx, ln));
1787 if ( --ln == 0 )
1788 answer(toInt(idx));
1789 } while( idx < here );
1790 here = start;
1791 } while(start > 0);
1792
1793 answer(ZERO); /* start of buffer */
1794 }
1795 } else if ( ln >= map->length )
1796 { int li = map->skip + map->length - 1;
1797 long idx = (li >= 0 ? map->lines[li].start : 0);
1798
1799 for( ln -= map->length - 1; ln > 0; ln-- )
1800 { DEBUG(NAME_start, Cprintf("ln = %d; idx = %ld\n", ln, idx));
1801
1802 idx = do_fill_line(ti, &tl, idx);
1803 if ( tl.ends_because & END_EOF )
1804 break;
1805 }
1806
1807 answer(toInt(idx));
1808 }
1809
1810 answer(toInt(map->lines[map->skip + ln].start));
1811 }
1812
1813
1814 static status
wrapTextImage(TextImage ti,Name wrap)1815 wrapTextImage(TextImage ti, Name wrap)
1816 { if ( ti->wrap != wrap )
1817 { assign(ti, wrap, wrap);
1818 ChangedEntireTextImage(ti);
1819 }
1820
1821 succeed;
1822 }
1823
1824
1825 status
backgroundTextImage(TextImage ti,Any bg)1826 backgroundTextImage(TextImage ti, Any bg)
1827 { if ( ti->background != bg )
1828 { assign(ti, background, bg);
1829 changedEntireImageGraphical(ti); /* only visual effect */
1830 }
1831
1832 succeed;
1833 }
1834
1835
1836 status
tabDistanceTextImage(TextImage ti,Int tab)1837 tabDistanceTextImage(TextImage ti, Int tab)
1838 { if ( ti->tab_distance != tab )
1839 { assign(ti, tab_distance, tab);
1840 ChangedEntireTextImage(ti);
1841 }
1842
1843 succeed;
1844 }
1845
1846
1847 status
tabStopsTextImage(TextImage ti,Vector v)1848 tabStopsTextImage(TextImage ti, Vector v)
1849 { if ( isNil(v) )
1850 assign(ti, tab_stops, v);
1851 else
1852 { int i;
1853
1854 for(i=1; i<valInt(v->size); i++)
1855 { Int s;
1856
1857 if ( !(s = checkType(getElementVector(v, toInt(i)), TypeInt, NIL)) )
1858 return errorPce(v, NAME_elementType, toInt(i), TypeInt);
1859 elementVector(v, toInt(i), s);
1860 }
1861
1862 assign(ti, tab_stops, v);
1863 }
1864
1865 succeed;
1866 }
1867
1868
1869 static status
geometryTextImage(TextImage ti,Int x,Int y,Int w,Int h)1870 geometryTextImage(TextImage ti, Int x, Int y, Int w, Int h)
1871 {
1872 #define Changed(a) ( notDefault(a) && (a) != ti->area->a )
1873
1874 if ( Changed(w) || Changed(h) ) /* resize */
1875 { geometryGraphical(ti, x, y, w, h);
1876 ti->w = valInt(ti->area->w);
1877 ti->h = valInt(ti->area->h);
1878 ChangedEntireTextImage(ti);
1879 } else
1880 geometryGraphical(ti, x, y, DEFAULT, DEFAULT); /* move only */
1881 #undef Changed
1882
1883 succeed;
1884 }
1885
1886 /********************************
1887 * GET ATTRIBUTES *
1888 ********************************/
1889
1890 static Int
getWidthTextImage(TextImage ti)1891 getWidthTextImage(TextImage ti)
1892 { answer(toInt(ti->w));
1893 }
1894
1895
1896 static Int
getHeightTextImage(TextImage ti)1897 getHeightTextImage(TextImage ti)
1898 { answer(toInt(ti->h));
1899 }
1900
1901
1902 Int
getViewTextImage(TextImage ti)1903 getViewTextImage(TextImage ti)
1904 { answer(sub(ti->end, ti->start));
1905 }
1906
1907
1908 /*******************************
1909 * PRECISE SCROLL SUPPORT *
1910 *******************************/
1911
1912 #define MAXPLINES 1000
1913
1914 typedef struct pline
1915 { int y; /* y of the physical line */
1916 long start; /* start index */
1917 } *PLine;
1918
1919
1920 static TextLine
tmpLine()1921 tmpLine()
1922 { static struct text_line tmp;
1923
1924 if ( !tmp.chars )
1925 { tmp.chars = alloc(80 * sizeof(struct text_char));
1926 tmp.allocated = 80;
1927 }
1928
1929 return &tmp;
1930 }
1931
1932
1933 status
bubbleScrollBarTextImage(TextImage ti,ScrollBar sb)1934 bubbleScrollBarTextImage(TextImage ti, ScrollBar sb)
1935 { TextLine tmp = tmpLine();
1936 long index = 0;
1937 int start = -1;
1938 int view = ti->h - 2*TXT_Y_MARGIN;
1939 int len;
1940 int y=0;
1941
1942 if ( ti->rewind )
1943 (*ti->rewind)(ti->text);
1944
1945 for(;;)
1946 { long next_index;
1947
1948 if ( start < 0 && index >= valInt(ti->start) )
1949 start = y;
1950
1951 next_index = do_fill_line(ti, tmp, index);
1952 y += tmp->h;
1953 index = next_index;
1954
1955 if ( tmp->ends_because & END_EOF )
1956 { len = y;
1957 break;
1958 }
1959 }
1960
1961 return bubbleScrollBar(sb, toInt(len), toInt(start), toInt(view));
1962 }
1963
1964
1965 static status
make_pline_map(TextImage ti,PLine lines,int * size)1966 make_pline_map(TextImage ti, PLine lines, int *size)
1967 { TextLine tmp = tmpLine();
1968 long index = 0;
1969 int y=0;
1970 int mx = *size;
1971 int line;
1972
1973 if ( ti->rewind )
1974 (*ti->rewind)(ti->text);
1975
1976 for(line=0; line < mx-1 ;line++)
1977 { lines[line].y = y;
1978 lines[line].start = index;
1979 index = do_fill_line(ti, tmp, index);
1980 y += tmp->h;
1981
1982 if ( tmp->ends_because & END_EOF )
1983 { *size = ++line;
1984 lines[line].y = y+tmp->h;
1985 succeed;
1986 }
1987 }
1988
1989 fail;
1990 }
1991
1992
1993 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
1994 Find filled line starting <lines> screenlines before <here>
1995 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
1996
1997 static status
backwards_filled_line(TextImage ti,TextLine l,long here,int lines)1998 backwards_filled_line(TextImage ti, TextLine l, long here, int lines)
1999 { while(here > 0)
2000 { int i;
2001 long idx, idx0;
2002
2003 idx0 = paragraph_start(ti, here-1);
2004
2005 for(i=0, idx=idx0; ;)
2006 { idx = do_fill_line(ti, l, idx);
2007 i++;
2008 if ( l->end >= here )
2009 break;
2010 }
2011
2012 if ( i == lines && i == 1) /* common case */
2013 { succeed;
2014 } else if ( i >= lines )
2015 { int n = i+1-lines;
2016
2017 for(idx=idx0; n-- > 0; )
2018 idx = do_fill_line(ti, l, idx);
2019
2020 succeed;
2021 } else
2022 { lines -= i;
2023 here = idx0;
2024 }
2025 }
2026
2027 do_fill_line(ti, l, 0);
2028 fail;
2029 }
2030
2031
2032 static status
backwards_filled_line_from_dy(TextImage ti,TextLine l,long here,int dy)2033 backwards_filled_line_from_dy(TextImage ti, TextLine l, long here, int dy)
2034 { while(here > 0)
2035 { int sy;
2036 long idx, idx0;
2037
2038 idx0 = paragraph_start(ti, here-1);
2039
2040 for(sy=0, idx=idx0; ;) /* Height of the paragraph */
2041 { idx = do_fill_line(ti, l, idx);
2042 sy += l->h;
2043 if ( l->end >= here )
2044 break;
2045 }
2046
2047 if ( sy >= dy ) /* somewhere here */
2048 { int skip = sy-dy;
2049
2050 for(idx=idx0; skip > 0; )
2051 { idx = do_fill_line(ti, l, idx);
2052 skip -= l->h;
2053 }
2054
2055 succeed;
2056 } else
2057 { dy -= sy;
2058 here = idx0;
2059 }
2060 }
2061
2062 do_fill_line(ti, l, 0);
2063 fail;
2064 }
2065
2066
2067 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2068 Ensure there is enough to see. We assume 1/3-th of the screen or 1 line
2069 for very small screens will suffice.
2070 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2071
2072 static long
ensure_enough_visible(TextImage ti,long here)2073 ensure_enough_visible(TextImage ti, long here)
2074 { int minv = (ti->h-2*TXT_Y_MARGIN)/3;
2075 TextLine l = tmpLine();
2076 int v = 0;
2077 long idx = here;
2078
2079 for(;;)
2080 { idx = do_fill_line(ti, l, idx);
2081 v += l->h;
2082
2083 if ( v >= minv )
2084 return here;
2085
2086 if ( l->ends_because & END_EOF )
2087 { long end = l->start + l->length;
2088
2089 backwards_filled_line_from_dy(ti, l, end, minv);
2090 return l->start;
2091 }
2092 }
2093 }
2094
2095
2096 Int
getScrollStartTextImage(TextImage ti,Name dir,Name unit,Int amount)2097 getScrollStartTextImage(TextImage ti, Name dir, Name unit, Int amount)
2098 { if ( unit == NAME_file )
2099 { if ( dir == NAME_goto )
2100 { struct pline lines[MAXPLINES];
2101 int count = MAXPLINES;
2102 int l;
2103 int h, wh = ti->h - 2*TXT_Y_MARGIN;
2104
2105 if ( !make_pline_map(ti, lines, &count) )
2106 fail;
2107 h = lines[count].y;
2108
2109 if ( h>wh )
2110 { int yt = ((h-wh) * valInt(amount))/1000;
2111
2112 for(l=0; l<count; l++)
2113 { if ( lines[l].y >= yt )
2114 break;
2115 }
2116 /*Cprintf("%d promille, h=%d, wh=%d, yt=%d, l=%d\n",
2117 valInt(amount), h, wh, yt, l);*/
2118 } else
2119 return ZERO;
2120
2121 answer(toInt(lines[l].start));
2122 }
2123 } else
2124 { long idx;
2125
2126 if ( unit == NAME_line )
2127 { if ( dir == NAME_forwards )
2128 { int n = valInt(amount);
2129 TextLine l = tmpLine();
2130 idx = valInt(ti->start);
2131
2132 for( ; n-- > 0; )
2133 { idx = do_fill_line(ti, l, idx);
2134 if ( l->ends_because & END_EOF )
2135 break;
2136 }
2137 } else /* scrolling up */
2138 { int n = valInt(amount);
2139 TextLine l = tmpLine();
2140
2141 backwards_filled_line(ti, l, valInt(ti->start), n);
2142 idx = l->start;
2143 }
2144 } else /* if ( unit == NAME_page ) */
2145 { int dy = ((ti->h-2*TXT_Y_MARGIN) * valInt(amount))/1000;
2146 TextLine l = tmpLine();
2147
2148 idx = valInt(ti->start);
2149
2150 if ( dir == NAME_forwards )
2151 { for( ; dy > 0 ; )
2152 { long next = do_fill_line(ti, l, idx);
2153 if ( l->ends_because & END_EOF )
2154 break;
2155 dy -= l->h;
2156 if ( dy <= 0 && idx != valInt(ti->start) )
2157 break;
2158 idx = next;
2159 }
2160 } else
2161 { backwards_filled_line_from_dy(ti, l, idx, dy);
2162 idx = l->start;
2163 }
2164 }
2165
2166 if ( idx < 0 )
2167 idx = 0;
2168 else
2169 idx = ensure_enough_visible(ti, idx);
2170
2171 answer(toInt(idx));
2172 }
2173
2174 fail;
2175 }
2176
2177
2178 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2179 Get desired cursor position for moving a line up or down. This is used
2180 in word-wrap mode to get natural behaviour of the cursor up/down arrow.
2181 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2182
2183 Int
getUpDownColumnTextImage(TextImage ti,Int here)2184 getUpDownColumnTextImage(TextImage ti, Int here)
2185 { int cx, cy;
2186
2187 if ( get_xy_pos(ti, here, &cx, &cy) )
2188 { int ly = cy-1+ti->map->skip;
2189 TextLine l = &ti->map->lines[ly];
2190 TextChar tc = &l->chars[cx-1];
2191
2192 answer(toInt(tc->x));
2193 }
2194
2195 fail;
2196 }
2197
2198
2199 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2200 Support cursor up/down movement for editors using long paragraph and in
2201 word-wrap mode. This is pretty tricky. Unlike the above stuff, we use
2202 this routine independent from the size of the buffer, so we cannot
2203 affort scanning the whole buffer.
2204
2205 We assume the cursor is at this moment on the screen. If not, the editor
2206 will fall back to ->next_line, based on the represented text. If it is,
2207 we deduce the X and Y of the characters and look N lines up/down. If
2208 this happens to be on the screen, we are lucky. Otehrwise we have to
2209 format the bits and pieces just off the screen, to determine the proper
2210 location. Going down, this is easy again. Going up we will go back
2211 paragraph-by-paragraph and count the number of lines before the screen.
2212 If we are far enough back, we go forward to include the proper location.
2213 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2214
2215 Int
getUpDownCursorTextImage(TextImage ti,Int here,Int updown,Int column)2216 getUpDownCursorTextImage(TextImage ti, Int here, Int updown, Int column)
2217 { int cx, cy; /* grid x-y */
2218 int ud = valInt(updown);
2219
2220 if ( get_xy_pos(ti, here, &cx, &cy) )
2221 { int ly = cy-1+ti->map->skip;
2222 TextLine l = &ti->map->lines[ly];
2223 TextChar tc = &l->chars[cx-1];
2224 int x = tc->x; /* pixel-x */
2225 int i;
2226
2227 if ( isDefault(column) )
2228 x = tc->x;
2229 else
2230 x = valInt(column);
2231
2232 ly += ud;
2233 if ( ly < 0 ) /* before the screen */
2234 { long start = ti->map->lines[0].start;
2235 long idx = start;
2236 l = tmpLine();
2237
2238 for(;;)
2239 { int i;
2240 long here;
2241
2242 here = idx = paragraph_start(ti, idx);
2243
2244 for(i=0; here < start; i++) /* count screen-lines */
2245 { here = do_fill_line(ti, l, here);
2246 if ( l->ends_because & END_EOF )
2247 break; /* should not happen */
2248 }
2249
2250 if ( i >= -ly )
2251 { i += ly;
2252
2253 for(here=idx; i-- >= 0; )
2254 here = do_fill_line(ti, l, here);
2255
2256 goto out;
2257 }
2258
2259 if ( --idx < 0 )
2260 break;
2261 }
2262
2263 do_fill_line(ti, l, 0);
2264 } else if ( ly >= ti->map->length ) /* after the screen */
2265 { long idx = valInt(ti->end);
2266 int n = ly-(ti->map->length-1);
2267
2268 l = tmpLine();
2269 while(n-- > 0)
2270 { idx = do_fill_line(ti, l, idx);
2271 if ( l->ends_because & END_EOF )
2272 break;
2273 }
2274 } else /* on the screen */
2275 { l = &ti->map->lines[ly];
2276 }
2277
2278 out:
2279 /* fix the X-location */
2280 for(i = 0; i < l->length; i++)
2281 { if ( l->chars[i+1].x > x )
2282 break;
2283 }
2284
2285 return toInt(l->start + l->chars[i].index);
2286 }
2287
2288 fail;
2289 }
2290
2291
2292 Int
getBeginningOfLineCursorTextImage(TextImage ti,Int here)2293 getBeginningOfLineCursorTextImage(TextImage ti, Int here)
2294 { int cx, cy;
2295
2296 if ( get_xy_pos(ti, here, &cx, &cy) )
2297 { int ly = cy-1+ti->map->skip;
2298 TextLine l = &ti->map->lines[ly];
2299
2300 answer(toInt(l->start));
2301 }
2302
2303 fail;
2304 }
2305
2306
2307 Int
getEndOfLineCursorTextImage(TextImage ti,Int here)2308 getEndOfLineCursorTextImage(TextImage ti, Int here)
2309 { int cx, cy;
2310
2311 if ( get_xy_pos(ti, here, &cx, &cy) )
2312 { int ly = cy-1+ti->map->skip;
2313 TextLine l = &ti->map->lines[ly];
2314
2315 answer(toInt(l->end-1));
2316 }
2317
2318 fail;
2319 }
2320
2321
2322 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
2323 ensureVisibleTextImage()
2324 See whether we can make caret part of the screen by scrolling at most
2325 one screen-line up or down. For up we start filling out the physical
2326 line before the screen. For down we fill the next line. If caret is
2327 on this line we discard the first N-lines to free up enough space for
2328 the line.
2329 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
2330
2331 status
ensureVisibleTextImage(TextImage ti,Int caret)2332 ensureVisibleTextImage(TextImage ti, Int caret)
2333 { long here = valInt(caret);
2334
2335 if ( here < valInt(ti->start) )
2336 { long idx = paragraph_start(ti, valInt(ti->start)-1);
2337
2338 if ( here >= idx )
2339 { TextLine l = tmpLine();
2340 long next;
2341
2342 for(; ; idx=next)
2343 { next = do_fill_line(ti, l, idx);
2344 if ( l->ends_because & END_EOF )
2345 fail; /* should not happen */
2346 if ( here >= idx && here < next )
2347 return startTextImage(ti, toInt(idx), ZERO);
2348 }
2349 }
2350 } else
2351 { ComputeGraphical(ti);
2352
2353 if ( here >= valInt(ti->end) && ti->eof_in_window == OFF )
2354 { TextLine l = tmpLine();
2355 long next;
2356
2357 next = do_fill_line(ti, l, valInt(ti->end));
2358 if ( here < next || l->ends_because & END_EOF ) /* shift one at most */
2359 { TextLine last = &ti->map->lines[ti->map->length-1];
2360 int yt = last->y + last->h + l->h;
2361 int yshift = yt - (ti->h - 2*TXT_Y_MARGIN);
2362 int f;
2363
2364 for(f=ti->map->skip; f<ti->map->length; f++)
2365 { if ( ti->map->lines[f].y >= yshift )
2366 return startTextImage(ti, toInt(ti->map->lines[f].start), ZERO);
2367 }
2368 }
2369 } else
2370 succeed;
2371 }
2372
2373 fail;
2374 }
2375
2376
2377 static status
elevationTextImage(TextImage ti,Elevation z)2378 elevationTextImage(TextImage ti, Elevation z)
2379 { if ( ti->elevation != z )
2380 { assign(ti, elevation, z);
2381 if ( notNil(z) )
2382 assign(ti, pen, absInt(z->height));
2383 }
2384
2385 succeed;
2386 }
2387
2388
2389
2390
2391
2392 /*******************************
2393 * CLASS DECLARATION *
2394 *******************************/
2395
2396 /* Type declarations */
2397
2398 static char *T_center[] =
2399 { "index=int", "line=[int]" };
2400 static char *T_start[] =
2401 { "start=[int]", "skip_lines=[int]" };
2402 static char *T_initialise[] =
2403 { "text=object", "width=int", "height=int" };
2404 static char *T_geometry[] =
2405 { "x=[int]", "y=[int]", "width=[int]", "height=[int]" };
2406
2407 /* Instance Variables */
2408
2409 static vardecl var_textImage[] =
2410 { IV(NAME_text, "object", IV_GET,
2411 NAME_storage, "Source of the text"),
2412 SV(NAME_background, "[colour|pixmap]", IV_GET|IV_STORE, backgroundTextImage,
2413 NAME_appearance, "Background colour"),
2414 IV(NAME_start, "int", IV_NONE,
2415 NAME_scroll, "Index of first character displayed"),
2416 IV(NAME_end, "int", IV_GET,
2417 NAME_scroll, "Index of last character displayed"),
2418 SV(NAME_wrap, "{none,character,word}", IV_GET|IV_STORE, wrapTextImage,
2419 NAME_appearance, "Wrap mode for long lines"),
2420 SV(NAME_tabDistance, "int", IV_GET|IV_STORE, tabDistanceTextImage,
2421 NAME_appearance, "Pixel distance between tab stops"),
2422 SV(NAME_tabStops, "vector*", IV_GET|IV_STORE, tabStopsTextImage,
2423 NAME_appearance, "Vector of tab-stops in pixels"),
2424 IV(NAME_pointed, "graphical*", IV_GET,
2425 NAME_event, "Graphical under the pointer"),
2426 IV(NAME_eofInWindow, "bool", IV_GET,
2427 NAME_repaint, "Is end-of-file inside window?"),
2428 SV(NAME_elevation, "elevation*", IV_GET|IV_STORE, elevationTextImage,
2429 NAME_appearance, "Elevation of the area"),
2430 IV(NAME_width, "alien:int", IV_NONE,
2431 NAME_area, "Width of the image"),
2432 IV(NAME_height, "alien:int", IV_NONE,
2433 NAME_area, "Height of the image"),
2434 IV(NAME_changeStart, "alien:int", IV_NONE,
2435 NAME_repaint, "Start of changes (character index)"),
2436 IV(NAME_changeEnd, "alien:int", IV_NONE,
2437 NAME_repaint, "End of changes (character index)"),
2438 IV(NAME_inserted, "alien:int", IV_NONE,
2439 NAME_repaint, "How much text was inserted/deleted"),
2440 IV(NAME_seek, "alien:SeekFunction", IV_NONE,
2441 NAME_internal, "C-Function to seek to a position"),
2442 IV(NAME_scan, "alien:ScanFunction", IV_NONE,
2443 NAME_internal, "C-Function to scan for a syntactical category"),
2444 IV(NAME_fetch, "alien:FetchFunction", IV_NONE,
2445 NAME_internal, "C-function to fetch next character from source"),
2446 IV(NAME_MarginFunction, "alien:MarginFunction", IV_NONE,
2447 NAME_internal, "C-function to fetch margins from source"),
2448 IV(NAME_RewindFunction, "alien:RewindFunction", IV_NONE,
2449 NAME_internal, "C-function to rewind input"),
2450 IV(NAME_map, "alien:TextScreen", IV_NONE,
2451 NAME_cache, "2-dimensional map of source")
2452 };
2453
2454 /* Send Methods */
2455
2456 static senddecl send_textImage[] =
2457 { SM(NAME_compute, 0, NULL, computeTextImage,
2458 DEFAULT, "Recompute text-image if necessary"),
2459 SM(NAME_reset, 0, NULL, resetTextImage,
2460 DEFAULT, "Reset <-pointed after an abort"),
2461 SM(NAME_event, 1, "event", eventTextImage,
2462 DEFAULT, "Forward event to included graphicals"),
2463 SM(NAME_geometry, 4, T_geometry, geometryTextImage,
2464 DEFAULT, "Change image geometry"),
2465 SM(NAME_initialise, 3, T_initialise, initialiseTextImage,
2466 DEFAULT, "Create from source, width and height"),
2467 SM(NAME_unlink, 0, NULL, unlinkTextImage,
2468 DEFAULT, "Reclaim private allocated data"),
2469 SM(NAME_dumpMap, 0, NULL, dumpMapTextImage,
2470 NAME_debugging, "Dump map of the screen"),
2471 SM(NAME_center, 2, T_center, centerTextImage,
2472 NAME_scroll, "Scroll to place index at given line"),
2473 SM(NAME_start, 2, T_start, startTextImage,
2474 NAME_scroll, "Set start of screen and screenlines to skip")
2475 };
2476
2477 /* Get Methods */
2478
2479 static getdecl get_textImage[] =
2480 { GM(NAME_height, 0, "int", NULL, getHeightTextImage,
2481 NAME_area, "Height of the image"),
2482 GM(NAME_width, 0, "int", NULL, getWidthTextImage,
2483 NAME_area, "Width of the image"),
2484 GM(NAME_index, 1, "int", "event", getIndexTextImage,
2485 NAME_event, "Character index from event (position)"),
2486 GM(NAME_line, 1, "int", "int", getLineTextImage,
2487 NAME_scroll, "Window line (row) from character index"),
2488 GM(NAME_lines, 0, "int", NULL, getLinesTextImage,
2489 NAME_scroll, "Number of lines visible"),
2490 GM(NAME_start, 1, "line=int", "index=[int]", getStartTextImage,
2491 NAME_scroll, "Character index for start of screenline"),
2492 GM(NAME_displayedCursor, 0, "cursor*", NULL, getDisplayedCursorTextImage,
2493 NAME_cursor, "Currently displayed cursor"),
2494 GM(NAME_view, 0, "int", NULL, getViewTextImage,
2495 NAME_scroll, "Number of characters visible"),
2496 GM(NAME_characterPosition, 1, "point", "int", getCharacterPositionTextImage,
2497 NAME_compute, "X,BaseLine of character by index")
2498 };
2499
2500 /* Resources */
2501
2502 static classvardecl rc_textImage[] =
2503 { RC(NAME_background, "[colour|pixmap]",
2504 UXWIN("@default", "win_window"),
2505 "Background colour for the text"),
2506 RC(NAME_elevation, "elevation*", "when(@colour_display, 1, @nil)",
2507 "Elevation from the background"),
2508 RC(NAME_tabDistance, "int", "64",
2509 "Tabstop interval (pixels)"),
2510 RC(NAME_wrap, "{none,character,word}", "character",
2511 "Wrap unit for long lines"),
2512 RC(NAME_colour, RC_REFINE, "black", NULL)
2513 };
2514
2515 /* Class Declaration */
2516
2517 static Name textImage_termnames[] = { NAME_text, NAME_width, NAME_height };
2518
2519 ClassDecl(textImage_decls,
2520 var_textImage, send_textImage, get_textImage, rc_textImage,
2521 3, textImage_termnames,
2522 "$Rev$");
2523
2524
2525 status
makeClassTextImage(Class class)2526 makeClassTextImage(Class class)
2527 { declareClass(class, &textImage_decls);
2528
2529 setCloneFunctionClass(class, cloneTextImage);
2530 setLoadStoreFunctionClass(class, loadTextImage, storeTextImage);
2531 setRedrawFunctionClass(class, RedrawAreaTextImage);
2532 solidClass(class, ON);
2533 cloneStyleVariableClass(class, NAME_image, NAME_nil);
2534 cloneStyleVariableClass(class, NAME_changedArea, NAME_nil);
2535 saveStyleVariableClass(class, NAME_image, NAME_nil);
2536 saveStyleVariableClass(class, NAME_changedArea, NAME_nil);
2537
2538 succeed;
2539 }
2540
2541