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