1 #include <stdlib.h>
2 #include <string.h>
3 #include "xglk.h"
4 #include "xg_internal.h"
5 #include "xg_win_textbuf.h"
6 
7 #define stype_Text (0)
8 #define stype_Image (1)
9 #define stype_Break (2)
10 
11 /* ### find and kill: cutwin->font.line{height,off} */
12 
13 typedef struct style_struct {
14   int stype;
15   int imagealign; /* from the Glk constants */
16   glui32 linkid; /* for hyperlinks */
17   glui32 image; /* resource ID */
18   glui32 imagewidth, imageheight; /* scaled bounds */
19   glui32 attr; /* a style number */
20   long pos; /* position this style starts at */
21 } style_t;
22 
23 typedef struct imageword_struct {
24   picture_t *pic;
25   long width, height; /* scaled bounds */
26   long pos; /* inline: distance up from the baseline to put top edge;
27 	       margin: distance in from margin. */
28   int imagealign; /* from the Glk constants */
29 } imageword_t;
30 
31 typedef struct word_struct {
32   int stype;
33   long pos, len;
34   long width; /* in pixels */
35   glui32 attr;
36   glui32 linkid;
37   union {
38     long *letterpos; /* if not NULL, an array[0..len] of pixel offsets
39 			from wordpos; */
40     imageword_t *image;
41   } u;
42 } word_t;
43 
44 #define lineflag_Wrapped (1) /* line is a wrap or split from previous line */
45 #define lineflag_Extra (2) /* the magic extra line on the end */
46 
47 typedef struct lline_struct {
48   long pos; /* line starts here */
49   long posend; /* number of chars. May not be exactly to start of next line,
50 		  because it won't include the newline or space that ends
51 		  the line. */
52   long indent; /* in pixels, from textwin_w (0 or greater) */
53   word_t *wordlist;
54   long numwords;
55   long height; /* in pixels */
56   long off; /* baseline offset from top, in pixels */
57   long leftindent; /* how much flow-object there is on the left margin */
58   long leftbelow; /* how much of that object hangs below this line */
59   long rightindent; /* how much flow-object there is on the right margin */
60   long rightbelow; /* how much of that object hangs below this line */
61   int flags;
62 } lline_t;
63 
64 typedef struct histunit {
65   char *str;
66   int len;
67 } histunit;
68 
69 struct window_textbuffer_struct {
70   window_t *owner;
71   XRectangle bbox;
72 
73   int textwin_x, textwin_y, textwin_w, textwin_h; /* bbox minus margins */
74   /*XRectangle textwin_cursor_box;*/ /* This is the area in which the cursor
75 				    is an ibeam. Assuming we do cursors. */
76   wegscroll_t scrollbar;
77 
78   stylehints_t hints;
79   fontset_t font;
80 
81   char *charbuf;
82   long numchars;
83   long char_size;
84 
85   style_t *stylelist;
86   long numstyles;
87   long styles_size;
88 
89   lline_t *linelist;
90   long numlines;
91   long lines_size;
92 
93   lline_t *tmplinelist;
94   long tmplines_size;
95 
96   long scrollpos; /* character position at top of screen */
97   long scrollline; /* number of line at top of screen, after
98 		      win_textbuffer_layout() */
99   long lastseenline; /* last line read before more stuff was output. If
100 			everything has been read, this is numlines. */
101   long dotpos, dotlen; /* dotpos is in [0..numchars] */
102   long lastdotpos, lastdotlen; /* cached values -- fiddled inside
103 				  win_textbuffer_layout() */
104 
105   long linesonpage; /* this stuff is set by xtext_layout() too. */
106   long lineoffset_size;
107   int *lineoffsetlist; /* In [0..linesonpage], and the first element is
108 			    zero. */
109 
110   long drag_firstbeg, drag_firstend;
111   int drag_inscroll;
112   glui32 drag_firstlink;
113 
114   int isactive; /* is window active? */
115 
116   long dirtybeg, dirtyend; /* mark the limits of what needs to be laid
117 			      out, [) format */
118   long dirtydelta; /* how much the dirty area has grown (or shrunk) */
119 
120   int isclear;
121 
122   int historylength; /* cached in case the pref is changed */
123   int historynum, historypos;
124   histunit *history;
125 
126   /* these are for line input */
127   long buflen;
128   char *buffer;
129   long inputfence;
130   glui32 originalattr;
131   gidispatch_rock_t inarrayrock;
132 };
133 
134 #define collapse_dot(cwin)  (cwin->dotpos += cwin->dotlen, cwin->dotlen = 0)
135 #define SIDEMARGIN (0)
136 #define BARWIDTH (18)
137 #define MACSTYLESCROLL
138 
139 static void redrawtext(window_textbuffer_t *cutwin, long beg,
140   long num, int clearnum);
141 static void win_textbuffer_layout(window_textbuffer_t *cutwin);
142 static void flip_selection(window_textbuffer_t *cutwin, long dpos, long dlen);
143 static long find_loc_by_pos(window_textbuffer_t *cutwin, long pos,
144   int *xposret, int *yposret);
145 static long find_pos_by_loc(window_textbuffer_t *cutwin, int xpos, int ypos,
146   long *truepos);
147 static long find_line_by_pos(window_textbuffer_t *cutwin, long pos,
148   long guessline);
149 static void measure_word(window_textbuffer_t *cutwin, lline_t *curline,
150   word_t *curword);
151 static void win_textbuffer_line_cancel(window_textbuffer_t *cutwin,
152   event_t *ev);
153 static void win_textbuffer_setstyle(window_textbuffer_t *cutwin,
154   long pos, int stype,
155   glui32 attr, glui32 linkid, glui32 image, int imagealign,
156   glui32 imagewidth, glui32 imageheight);
157 static void readd_lineheights(window_textbuffer_t *cutwin, long lx);
158 
159 static long back_to_white(window_textbuffer_t *cutwin, long pos);
160 static long fore_to_white(window_textbuffer_t *cutwin, long pos);
161 static long back_to_nonwhite(window_textbuffer_t *cutwin, long pos);
162 static long fore_to_nonwhite(window_textbuffer_t *cutwin, long pos);
163 static void delete_imageword(imageword_t *iwd);
164 
win_textbuffer_create(window_t * win)165 window_textbuffer_t *win_textbuffer_create(window_t *win)
166 {
167   window_textbuffer_t *res =
168     (window_textbuffer_t *)malloc(sizeof(window_textbuffer_t));
169   if (!res)
170     return NULL;
171 
172   res->owner = win;
173 
174   gli_stylehints_for_window(wintype_TextBuffer, &(res->hints));
175   gli_styles_compute(&(res->font), &(res->hints));
176 
177   res->char_size = 256;
178   res->charbuf = (char *)malloc(sizeof(char) * res->char_size);
179   res->numchars = 0;
180 
181   res->styles_size = 8;
182   res->stylelist = (style_t *)malloc(sizeof(style_t) * res->styles_size);
183   res->numstyles = 1;
184   res->stylelist[0].pos = 0;
185   res->stylelist[0].stype = stype_Text;
186   res->stylelist[0].attr = style_Normal;
187   res->stylelist[0].linkid = 0;
188   res->stylelist[0].image = 0;
189   res->stylelist[0].imagealign = 0;
190 
191   res->lines_size = 8;
192   res->linelist = (lline_t *)malloc(sizeof(lline_t) * res->lines_size);
193   res->numlines = 0;
194 
195   res->tmplines_size = 8;
196   res->tmplinelist = (lline_t *)malloc(sizeof(lline_t) * res->tmplines_size);
197 
198   res->historynum = 0;
199   res->historylength = prefs.historylength;
200   res->history = (histunit *)malloc(res->historylength * sizeof(histunit));
201 
202   res->scrollpos = 0;
203   res->scrollline = 0;
204   xweg_init_scrollbar(&res->scrollbar, win, xgc_scroll, xgc_scrollto);
205 
206   res->dirtybeg = 0;
207   res->dirtyend = 0;
208   res->dirtydelta = 0;
209 
210   res->dotpos = 0;
211   res->dotlen = 0;
212   res->lastdotpos = -1;
213   res->lastdotlen = 0;
214 
215   res->drag_firstbeg = 0;
216   res->drag_firstend = 0;
217   res->drag_firstlink = 0;
218   res->drag_inscroll = FALSE;
219 
220   res->isactive = FALSE;
221 
222   res->lastseenline = 0;
223   res->isclear = TRUE;
224 
225   res->linesonpage = 0;
226   res->lineoffset_size = 80;
227   res->lineoffsetlist = (int *)malloc(res->lineoffset_size * sizeof(int));
228   res->lineoffsetlist[0] = 0;
229   /*### res->charsperline = 0; */
230 
231   res->buffer = NULL;
232   res->buflen = 0;
233   res->inputfence = 0;
234 
235   return res;
236 }
237 
win_textbuffer_destroy(struct window_textbuffer_struct * dwin)238 void win_textbuffer_destroy(struct window_textbuffer_struct *dwin)
239 {
240   int ix;
241 
242   if (dwin->buffer) {
243     if (gli_unregister_arr) {
244       (*gli_unregister_arr)(dwin->buffer, dwin->buflen, "&+#!Cn",
245 	dwin->inarrayrock);
246     }
247     dwin->buffer = NULL;
248   }
249 
250   if (dwin->history) {
251     for (ix=0; ix<dwin->historynum; ix++) {
252       free(dwin->history[ix].str);
253     }
254     free(dwin->history);
255     dwin->history = NULL;
256   }
257 
258   if (dwin->stylelist) {
259     free(dwin->stylelist);
260     dwin->stylelist = NULL;
261   }
262 
263   if (dwin->linelist) {
264     long lx, wx;
265 
266     for (lx=0; lx<dwin->numlines; lx++) {
267       word_t *thisword;
268       for (wx=0, thisword=dwin->linelist[lx].wordlist;
269 	    wx<dwin->linelist[lx].numwords;
270 	   wx++, thisword++) {
271 	if (thisword->stype == stype_Text) {
272 	  if (thisword->u.letterpos) {
273 	    free(thisword->u.letterpos);
274 	    thisword->u.letterpos = NULL;
275 	  }
276 	}
277 	else if (thisword->stype == stype_Image) {
278 	  if (thisword->u.image) {
279 	    delete_imageword(thisword->u.image);
280 	    thisword->u.image = NULL;
281 	  }
282 	}
283       }
284       free(dwin->linelist[lx].wordlist);
285       dwin->linelist[lx].wordlist = NULL;
286     }
287 
288     free(dwin->linelist);
289     dwin->linelist = NULL;
290   }
291 
292   if (dwin->tmplinelist) {
293     /* don't free contents; they're owned by linelist. */
294     free(dwin->tmplinelist);
295     dwin->tmplinelist = NULL;
296   }
297 
298   if (dwin->charbuf) {
299     free(dwin->charbuf);
300     dwin->charbuf = NULL;
301   }
302 
303   free(dwin);
304 }
305 
win_textbuffer_rearrange(window_t * win,XRectangle * box)306 void win_textbuffer_rearrange(window_t *win, XRectangle *box)
307 {
308   window_textbuffer_t *cutwin = win->data;
309 
310   int xpos = box->x;
311   int ypos = box->y;
312   int width = box->width;
313   int height = box->height;
314 
315   cutwin->bbox = *box;
316 
317   cutwin->textwin_x = xpos+SIDEMARGIN+prefs.textbuffer.marginx;
318   cutwin->textwin_y = ypos+prefs.textbuffer.marginy;
319   cutwin->textwin_w = width-2*SIDEMARGIN-BARWIDTH-2*prefs.textbuffer.marginx;
320   cutwin->textwin_h = height-2*prefs.textbuffer.marginy;
321 
322   cutwin->scrollbar.box.y = ypos;
323   cutwin->scrollbar.box.height = height;
324   cutwin->scrollbar.box.width = BARWIDTH;
325   cutwin->scrollbar.box.x = xpos + width - BARWIDTH;
326 
327   /*
328   cutwin->textwin_cursor_box.x = xpos;
329   cutwin->textwin_cursor_box.y = ypos;
330   cutwin->textwin_cursor_box.width = width - BARWIDTH;
331   cutwin->textwin_cursor_box.height = height;
332   */
333 
334   cutwin->dirtybeg = 0;
335   cutwin->dirtyend = cutwin->numchars;
336   cutwin->dirtydelta = 0;
337 
338   cutwin->linesonpage = 0;
339   cutwin->lineoffsetlist[0] = 0;
340   /* This will be set to something real later. */
341 }
342 
win_textbuffer_get_rect(window_t * win)343 XRectangle *win_textbuffer_get_rect(window_t *win)
344 {
345   window_textbuffer_t *dwin = win->data;
346   return &dwin->bbox;
347 }
348 
win_textbuffer_get_size(window_t * win,glui32 * width,glui32 * height)349 void win_textbuffer_get_size(window_t *win, glui32 *width, glui32 *height)
350 {
351   window_textbuffer_t *dwin = win->data;
352   *width = 0; /*###dwin->linesperpage; */
353   *height = 0; /*###dwin->charsperline; */
354 }
355 
win_textbuffer_figure_size(window_t * win,long size,int vertical)356 long win_textbuffer_figure_size(window_t *win, long size, int vertical)
357 {
358   window_textbuffer_t *dwin = win->data;
359 
360   if (vertical) {
361     /* size * charwidth */
362     long textwin_w = size * dwin->font.gc[style_Normal].spacewidth;
363     return textwin_w + 2*SIDEMARGIN + BARWIDTH + 2*prefs.textbuffer.marginx;
364   }
365   else {
366     /* size * lineheight */
367     long textwin_h = size * dwin->font.lineheight;
368     return textwin_h + 2*prefs.textbuffer.marginy;
369   }
370 }
371 
win_textbuffer_get_fontset(window_t * win)372 fontset_t *win_textbuffer_get_fontset(window_t *win)
373 {
374   window_textbuffer_t *dwin = win->data;
375   return &(dwin->font);
376 }
377 
win_textbuffer_get_stylehints(window_t * win)378 stylehints_t *win_textbuffer_get_stylehints(window_t *win)
379 {
380   window_textbuffer_t *dwin = win->data;
381   return &(dwin->hints);
382 }
383 
win_textbuffer_redraw(window_t * win)384 void win_textbuffer_redraw(window_t *win)
385 {
386   window_textbuffer_t *cutwin = win->data;
387 
388   gli_draw_window_outline(&cutwin->bbox);
389 
390   gli_draw_window_margin(&(cutwin->font.backcolor),
391     cutwin->bbox.x, cutwin->bbox.y,
392     cutwin->bbox.width-BARWIDTH, cutwin->bbox.height,
393     cutwin->textwin_x-SIDEMARGIN, cutwin->textwin_y,
394     cutwin->textwin_w+2*SIDEMARGIN, cutwin->textwin_h);
395 
396   xweg_draw_scrollbar(&cutwin->scrollbar);
397 
398   if (cutwin->dirtybeg >= 0 || cutwin->dirtyend >= 0) {
399     /* something is dirty. Go through the whole shebang. */
400     win_textbuffer_layout(cutwin);
401     return;
402   }
403 
404   /* this assumes that an exposure event will not come in
405      between a data update and an win_textbuffer_layout call. */
406   /*flip_selection(cutwin->dotpos, cutwin->dotlen);*/
407   redrawtext(cutwin, 0, -1, -1);
408   flip_selection(cutwin, cutwin->dotpos, cutwin->dotlen);
409 
410   xweg_adjust_scrollbar(&cutwin->scrollbar, cutwin->numlines,
411     cutwin->scrollline, cutwin->linesonpage);
412 }
413 
win_textbuffer_flush(window_t * win)414 void win_textbuffer_flush(window_t *win)
415 {
416   window_textbuffer_t *cutwin = win->data;
417   win_textbuffer_layout(cutwin);
418 }
419 
win_textbuffer_setfocus(window_t * win,int turnon)420 void win_textbuffer_setfocus(window_t *win, int turnon)
421 {
422   window_textbuffer_t *cutwin = win->data;
423 
424   if (turnon) {
425     if (!cutwin->isactive) {
426       cutwin->isactive = TRUE;
427       flip_selection(cutwin, cutwin->dotpos, cutwin->dotlen);
428     }
429   }
430   else {
431     if (cutwin->isactive) {
432       flip_selection(cutwin, cutwin->dotpos, cutwin->dotlen);
433       cutwin->isactive = FALSE;
434     }
435   }
436 }
win_textbuffer_init_line(window_t * win,char * buffer,int buflen,int readpos)437 void win_textbuffer_init_line(window_t *win, char *buffer, int buflen,
438   int readpos)
439 {
440   window_textbuffer_t *cutwin = win->data;
441 
442   cutwin->buflen = buflen;
443   cutwin->buffer = buffer;
444 
445   if (readpos) {
446     /* The terp has to enter the text. */
447     cutwin->inputfence = cutwin->numchars;
448     cutwin->originalattr = cutwin->stylelist[cutwin->numstyles-1].attr;
449     win_textbuffer_set_style_text(cutwin, style_Input);
450     win_textbuffer_replace(cutwin, cutwin->inputfence, 0,
451       cutwin->buffer, readpos);
452     win_textbuffer_layout(cutwin);
453   }
454   else {
455     cutwin->inputfence = cutwin->numchars;
456     cutwin->originalattr = cutwin->stylelist[cutwin->numstyles-1].attr;
457     win_textbuffer_set_style_text(cutwin, style_Input);
458   }
459 
460   cutwin->historypos = cutwin->historynum;
461 
462   if (gli_register_arr) {
463     cutwin->inarrayrock = (*gli_register_arr)(buffer, buflen, "&+#!Cn");
464   }
465 }
466 
win_textbuffer_cancel_line(window_t * win,event_t * ev)467 void win_textbuffer_cancel_line(window_t *win, event_t *ev)
468 {
469   window_textbuffer_t *cutwin = win->data;
470   win_textbuffer_line_cancel(cutwin, ev);
471 }
472 
win_textbuffer_perform_click(window_t * win,int dir,XPoint * pt,int butnum,int clicknum,unsigned int state)473 void win_textbuffer_perform_click(window_t *win, int dir, XPoint *pt,
474   int butnum, int clicknum, unsigned int state)
475 {
476   window_textbuffer_t *cutwin = win->data;
477   long pos, hitpos;
478   long px, px2;
479 
480   if (dir == mouse_Down) {
481     cutwin->drag_firstlink = 0;
482     if (pt->x >= cutwin->scrollbar.box.x)
483       cutwin->drag_inscroll = TRUE;
484     else
485       cutwin->drag_inscroll = FALSE;
486 
487     if (cutwin->drag_inscroll) {
488       xweg_click_scrollbar(&cutwin->scrollbar, dir, pt, butnum,
489 	clicknum, state,
490 	cutwin->numlines, cutwin->scrollline, cutwin->linesonpage);
491     }
492     else {
493       pt->x -= cutwin->textwin_x;
494       pt->y -= cutwin->textwin_y;
495 
496       pos = find_pos_by_loc(cutwin, pt->x, pt->y, &hitpos);
497       if (butnum==1) {
498 	if (!(clicknum & 1)) {
499 	  px = back_to_white(cutwin, pos);
500 	  px2 = fore_to_white(cutwin, pos);
501 	}
502 	else {
503 	  if (cutwin->owner->hyperlink_request) {
504 	    long sx;
505 	    /* find the last style at or before hitpos. */
506 	    for (sx=cutwin->numstyles-1; sx>=0; sx--) {
507 	      if (cutwin->stylelist[sx].pos <= hitpos) {
508 		cutwin->drag_firstlink = cutwin->stylelist[sx].linkid;
509 		break;
510 	      }
511 	    }
512 	  }
513 	  px = pos;
514 	  px2 = pos;
515 	}
516 	cutwin->dotpos = px;
517 	cutwin->dotlen = px2-px;
518 	cutwin->drag_firstbeg = px;
519 	cutwin->drag_firstend = px2;
520       }
521       else {
522 	if (pos < cutwin->dotpos+cutwin->dotlen/2) {
523 	  cutwin->drag_firstbeg = cutwin->dotpos+cutwin->dotlen;
524 	}
525 	else {
526 	  cutwin->drag_firstbeg = cutwin->dotpos;
527 	}
528 	cutwin->drag_firstend = cutwin->drag_firstbeg;
529 	if (pos < cutwin->drag_firstbeg) {
530 	  if (!(clicknum & 1))
531 	    cutwin->dotpos = back_to_white(cutwin, pos);
532 	  else
533 	    cutwin->dotpos = pos;
534 	  cutwin->dotlen = cutwin->drag_firstend-cutwin->dotpos;
535 	}
536 	else if (pos > cutwin->drag_firstend) {
537 	  cutwin->dotpos = cutwin->drag_firstbeg;
538 	  if (!(clicknum & 1))
539 	    cutwin->dotlen = fore_to_white(cutwin, pos)-cutwin->drag_firstbeg;
540 	  else
541 	    cutwin->dotlen = pos-cutwin->drag_firstbeg;
542 	}
543 	else {
544 	  cutwin->dotpos = cutwin->drag_firstbeg;
545 	  cutwin->dotlen = cutwin->drag_firstend-cutwin->drag_firstbeg;
546 	}
547       }
548       win_textbuffer_layout(cutwin);
549     }
550   }
551   else if (dir == mouse_Move) {
552     if (cutwin->drag_inscroll) {
553       xweg_click_scrollbar(&cutwin->scrollbar, dir, pt, butnum,
554 	clicknum, state,
555 	cutwin->numlines, cutwin->scrollline, cutwin->linesonpage);
556     }
557     else {
558       pt->x -= cutwin->textwin_x;
559       pt->y -= cutwin->textwin_y;
560 
561       pos = find_pos_by_loc(cutwin, pt->x, pt->y, &hitpos);
562 
563       if (pos < cutwin->drag_firstbeg) {
564 	if (!(clicknum & 1))
565 	  cutwin->dotpos = back_to_white(cutwin, pos);
566 	else
567 	  cutwin->dotpos = pos;
568 	cutwin->dotlen = cutwin->drag_firstend-cutwin->dotpos;
569       }
570       else if (pos > cutwin->drag_firstend) {
571 	cutwin->dotpos = cutwin->drag_firstbeg;
572 	if (!(clicknum & 1))
573 	  cutwin->dotlen = fore_to_white(cutwin, pos)-cutwin->drag_firstbeg;
574 	else
575 	  cutwin->dotlen = pos-cutwin->drag_firstbeg;
576       }
577       else {
578 	cutwin->dotpos = cutwin->drag_firstbeg;
579 	cutwin->dotlen = cutwin->drag_firstend-cutwin->drag_firstbeg;
580       }
581       win_textbuffer_layout(cutwin);
582     }
583   }
584   else if (dir == mouse_Up) {
585     if (cutwin->drag_inscroll) {
586     }
587     else {
588       pt->x -= cutwin->textwin_x;
589       pt->y -= cutwin->textwin_y;
590       if (cutwin->drag_firstlink && cutwin->owner->hyperlink_request) {
591 	pos = find_pos_by_loc(cutwin, pt->x, pt->y, &hitpos);
592 	if (pos == cutwin->drag_firstbeg) {
593 	  cutwin->owner->hyperlink_request = FALSE;
594 	  eventloop_setevent(evtype_Hyperlink, cutwin->owner,
595 	    cutwin->drag_firstlink, 0);
596 	  /* suppressdblclick = TRUE; */
597 	}
598       }
599     }
600   }
601 }
602 
delete_imageword(imageword_t * iwd)603 static void delete_imageword(imageword_t *iwd)
604 {
605   if (iwd->pic) {
606     picture_release(iwd->pic);
607     iwd->pic = NULL;
608   }
609   free(iwd);
610 }
611 
back_to_white(window_textbuffer_t * cutwin,long pos)612 static long back_to_white(window_textbuffer_t *cutwin, long pos)
613 {
614   char *charbuf = cutwin->charbuf;
615   long inputfence = cutwin->inputfence;
616   while (pos > 0 && charbuf[pos-1] != ' ' && charbuf[pos-1] != '\n' && pos-1 != inputfence-1)
617     pos--;
618   return pos;
619 }
620 
fore_to_white(window_textbuffer_t * cutwin,long pos)621 static long fore_to_white(window_textbuffer_t *cutwin, long pos)
622 {
623   char *charbuf = cutwin->charbuf;
624   long inputfence = cutwin->inputfence;
625   long numchars = cutwin->numchars;
626   while (pos < numchars && charbuf[pos] != ' ' && charbuf[pos] != '\n' && pos != inputfence-1)
627     pos++;
628   return pos;
629 }
630 
back_to_nonwhite(window_textbuffer_t * cutwin,long pos)631 static long back_to_nonwhite(window_textbuffer_t *cutwin, long pos)
632 {
633   char *charbuf = cutwin->charbuf;
634   long inputfence = cutwin->inputfence;
635   while (pos > 0 && (charbuf[pos-1] == ' ' || charbuf[pos-1] == '\n' || pos-1 == inputfence-1))
636     pos--;
637   return pos;
638 }
639 
fore_to_nonwhite(window_textbuffer_t * cutwin,long pos)640 static long fore_to_nonwhite(window_textbuffer_t *cutwin, long pos)
641 {
642   char *charbuf = cutwin->charbuf;
643   long inputfence = cutwin->inputfence;
644   long numchars = cutwin->numchars;
645   while (pos < numchars && (charbuf[pos] == ' ' || charbuf[pos] == '\n' || pos == inputfence-1))
646     pos++;
647   return pos;
648 }
649 
650 /* Coordinates are in screen lines. If num < 0, go to the end. clearnum is the
651    number of lines to clear (may be to a notional line); if 0, don't clear at
652    all; if -1, clear whole window. */
redrawtext(window_textbuffer_t * cutwin,long beg,long num,int clearnum)653 static void redrawtext(window_textbuffer_t *cutwin, long beg, long num,
654   int clearnum)
655 {
656   long lx, wx, end;
657   int ypos, ypos2, xpos;
658   lline_t *thisline;
659   fontref_t *gclist;
660   word_t *thisword;
661   int textwin_x, textwin_y, textwin_w, textwin_h;
662   XRectangle textwinbox;
663 
664   /* cache some much-used values. */
665   gclist = cutwin->font.gc;
666   textwinbox.x = textwin_x = cutwin->textwin_x;
667   textwinbox.y = textwin_y = cutwin->textwin_y;
668   textwinbox.width = textwin_w = cutwin->textwin_w;
669   textwinbox.height = textwin_h = cutwin->textwin_h;
670 
671   if (num<0)
672     end = cutwin->numlines;
673   else {
674     end = beg+num;
675     if (end > cutwin->numlines)
676       end = cutwin->numlines;
677   }
678 
679   if (beg < cutwin->scrollline)
680     beg = cutwin->scrollline;
681 
682   if (clearnum != 0) {
683     if (beg-cutwin->scrollline < 0) {
684       ypos = textwin_y;
685     }
686     else if (beg-cutwin->scrollline >= cutwin->linesonpage) {
687       ypos = textwin_y + cutwin->lineoffsetlist[cutwin->linesonpage];
688     }
689     else {
690       ypos = textwin_y + cutwin->lineoffsetlist[beg-cutwin->scrollline];
691     }
692     if (clearnum > 0) {
693       long clearend = beg+clearnum;
694       if (clearend-cutwin->scrollline < 0) {
695 	ypos2 = textwin_y;
696       }
697       else if (clearend-cutwin->scrollline >= cutwin->linesonpage) {
698 	ypos2 = textwin_y + textwin_h;
699       }
700       else {
701 	ypos2 = textwin_y + cutwin->lineoffsetlist[clearend-cutwin->scrollline];
702       }
703     }
704     else {
705       ypos2 = textwin_y+textwin_h;
706     }
707     if (ypos < ypos2) {
708       xglk_clearfor_string(&(cutwin->font.backcolor),
709 	textwin_x-SIDEMARGIN, ypos,
710 	textwin_w+2*SIDEMARGIN, ypos2-ypos);
711     }
712   }
713 
714   /* Back up and draw any hanging images above. */
715   if (beg < end
716     && (cutwin->linelist[beg].leftindent || cutwin->linelist[beg].rightindent)) {
717     int stillleft = (cutwin->linelist[beg].leftindent != 0);
718     int stillright = (cutwin->linelist[beg].rightindent != 0);
719     ypos = textwin_y + cutwin->lineoffsetlist[beg-cutwin->scrollline];
720     for (lx = beg-1; lx >= 0; lx--) {
721       thisline = (&cutwin->linelist[lx]);
722       if (thisline->leftbelow == 0)
723 	stillleft = FALSE;
724       if (thisline->rightbelow == 0)
725 	stillright = FALSE;
726       if (!stillleft && !stillright)
727 	break;
728       ypos -= thisline->height;
729       for (wx=0; wx<thisline->numwords; wx++) {
730 	thisword = thisline->wordlist+wx;
731 	if (thisword->stype == stype_Image) {
732 	  imageword_t *iwd = thisword->u.image;
733 	  if (iwd) {
734 	    if (iwd->imagealign == imagealign_MarginLeft) {
735 	      if (stillleft)
736 		picture_draw(iwd->pic, xiowin, textwin_x+iwd->pos, ypos,
737 		  iwd->width, iwd->height, &textwinbox);
738 	    }
739 	    else if (iwd->imagealign == imagealign_MarginRight) {
740 	      if (stillright)
741 		picture_draw(iwd->pic, xiowin,
742 		  (textwin_x+textwin_w-iwd->width)-iwd->pos,
743 		  ypos, iwd->width, iwd->height, &textwinbox);
744 	    }
745 	  }
746 	}
747 	else if (thisword->stype == stype_Text) {
748 	  break;
749 	}
750       }
751     }
752   }
753 
754   /* The main line-drawing loop. */
755   for (lx=beg; lx<end; lx++) {
756     thisline = (&cutwin->linelist[lx]);
757     if (lx-cutwin->scrollline >= cutwin->linesonpage) {
758       break;
759     }
760     ypos = textwin_y + cutwin->lineoffsetlist[lx-cutwin->scrollline];
761     xpos = textwin_x + thisline->indent;
762     for (wx=0; wx<thisline->numwords; wx++) {
763       thisword = thisline->wordlist+wx;
764       if (thisword->stype == stype_Image) {
765 	imageword_t *iwd = thisword->u.image;
766 	if (iwd) {
767 	  if (iwd->imagealign == imagealign_MarginLeft) {
768 	    picture_draw(iwd->pic, xiowin, textwin_x+iwd->pos, ypos,
769 	      iwd->width, iwd->height, &textwinbox);
770 	  }
771 	  else if (iwd->imagealign == imagealign_MarginRight) {
772 	    picture_draw(iwd->pic, xiowin,
773 	      (textwin_x+textwin_w-iwd->width)-iwd->pos,
774 	      ypos, iwd->width, iwd->height, &textwinbox);
775 	  }
776 	  else {
777 	    picture_draw(iwd->pic, xiowin,
778 	      xpos, ypos+thisline->off-iwd->pos,
779 	      iwd->width, iwd->height, &textwinbox);
780 	  }
781 	}
782       }
783       else if (thisword->stype == stype_Text) {
784 	if (gclist[thisword->attr].backcolor.pixel
785 	  != cutwin->font.backcolor.pixel) {
786 	  xglk_clearfor_string(&(gclist[thisword->attr].backcolor),
787 	    xpos, ypos, thisword->width, thisline->height);
788 	}
789 	xglk_draw_string(&(gclist[thisword->attr]),
790 	  (thisword->linkid != 0), thisword->width,
791 	  xpos, ypos+thisline->off,
792 	  cutwin->charbuf+thisline->pos+thisword->pos, thisword->len);
793       }
794       xpos += thisword->width;
795     }
796   }
797 }
798 
scroll_to(window_textbuffer_t * cutwin,long newscrollline)799 static void scroll_to(window_textbuffer_t *cutwin, long newscrollline)
800 {
801   long ix, val, total;
802   long oldscrollline;
803 
804 #ifdef MACSTYLESCROLL
805   total = 0;
806   for (val = cutwin->numlines; val > 0; val--) {
807     total += cutwin->linelist[val-1].height;
808     if (total > cutwin->textwin_h)
809       break;
810   }
811   if (newscrollline > val)
812     newscrollline = val;
813   if (newscrollline < 0)
814     newscrollline = 0;
815 #else
816   if (newscrollline > cutwin->numlines-cutwin->linesonpage)
817     newscrollline = cutwin->numlines-cutwin->linesonpage;
818   if (newscrollline < 0)
819     newscrollline = 0;
820 #endif
821 
822   if (cutwin->numlines == 0)
823     cutwin->scrollpos = 0;
824   else
825     cutwin->scrollpos = cutwin->linelist[newscrollline].pos;
826 
827   if (cutwin->scrollline != newscrollline) {
828     oldscrollline = cutwin->scrollline;
829     if (!xiobackstore
830       || oldscrollline + cutwin->linesonpage <= newscrollline
831       || newscrollline + cutwin->linesonpage <= oldscrollline) {
832       /* scroll to an entirely new place. */
833       cutwin->scrollline = newscrollline;
834       readd_lineheights(cutwin, cutwin->scrollline);
835       redrawtext(cutwin, cutwin->scrollline, -1, -1);
836       flip_selection(cutwin, cutwin->dotpos, cutwin->dotlen);
837     }
838     else {
839       int ypos1, ypos2, yhgt;
840       long oldlop, diff;
841       flip_selection(cutwin, cutwin->dotpos, cutwin->dotlen);
842       cutwin->scrollline = newscrollline;
843       if (oldscrollline < newscrollline) {
844 	/* scroll down -- things move up */
845 	ypos1 = cutwin->textwin_y + cutwin->lineoffsetlist[newscrollline-oldscrollline];
846 	ypos2 = cutwin->textwin_y;
847 	yhgt = cutwin->textwin_y + cutwin->textwin_h - ypos1;
848 	XCopyArea(xiodpy, xiowin, xiowin, gcfore,
849 	  cutwin->textwin_x-SIDEMARGIN, ypos1,
850 	  cutwin->textwin_w+2*SIDEMARGIN, yhgt,
851 	  cutwin->textwin_x-SIDEMARGIN, ypos2);
852 	oldlop = cutwin->linesonpage;
853 	readd_lineheights(cutwin, cutwin->scrollline);
854 	diff = (cutwin->scrollline + cutwin->linesonpage) - (oldscrollline + oldlop);
855 	redrawtext(cutwin, oldscrollline + oldlop, diff, diff);
856       }
857       else {
858 	/* scroll up -- things move down */
859 	ypos2 = cutwin->textwin_y;
860 	for (ix = newscrollline; ix < oldscrollline; ix++)
861 	  ypos2 += cutwin->linelist[ix].height;
862 	ypos1 = cutwin->textwin_y;
863 	yhgt = cutwin->textwin_y + cutwin->textwin_h - ypos2;
864 	XCopyArea(xiodpy, xiowin, xiowin, gcfore,
865 	  cutwin->textwin_x-SIDEMARGIN, ypos1,
866 	  cutwin->textwin_w+2*SIDEMARGIN, yhgt,
867 	  cutwin->textwin_x-SIDEMARGIN, ypos2);
868 	readd_lineheights(cutwin, cutwin->scrollline);
869 	redrawtext(cutwin, newscrollline, (oldscrollline-newscrollline),
870 	  (oldscrollline-newscrollline));
871       }
872       flip_selection(cutwin, cutwin->dotpos, cutwin->dotlen);
873     }
874     xweg_adjust_scrollbar(&cutwin->scrollbar, cutwin->numlines,
875       cutwin->scrollline, cutwin->linesonpage);
876   }
877 
878   if (cutwin->lastseenline < cutwin->scrollline) {
879     cutwin->lastseenline = cutwin->scrollline;
880   }
881   if (cutwin->lastseenline >= cutwin->numlines - cutwin->linesonpage) {
882     /* this is wrong -- linesonpage doesn't correctly measure lines at
883        the end. */
884     cutwin->lastseenline = cutwin->numlines;
885   }
886 }
887 
refiddle_selection(window_textbuffer_t * cutwin,long oldpos,long oldlen,long newpos,long newlen)888 static void refiddle_selection(window_textbuffer_t *cutwin,
889   long oldpos, long oldlen, long newpos, long newlen)
890 {
891   if (oldlen==0 || newlen==0 || oldpos<0 || newpos<0) {
892     flip_selection(cutwin, oldpos, oldlen);
893     flip_selection(cutwin, newpos, newlen);
894     return;
895   }
896 
897   if (oldpos == newpos) {
898     /* start at same place */
899     if (oldlen < newlen) {
900       flip_selection(cutwin, oldpos+oldlen, newlen-oldlen);
901     }
902     else if (newlen < oldlen) {
903       flip_selection(cutwin, oldpos+newlen, oldlen-newlen);
904     }
905     return;
906   }
907   if (oldpos+oldlen == newpos+newlen) {
908     /* end at same place */
909     if (oldpos < newpos) {
910       flip_selection(cutwin, oldpos, newpos-oldpos);
911     }
912     else if (newpos < oldpos) {
913       flip_selection(cutwin, newpos, oldpos-newpos);
914     }
915     return;
916   }
917 
918   flip_selection(cutwin, oldpos, oldlen);
919   flip_selection(cutwin, newpos, newlen);
920 }
921 
flip_selection(window_textbuffer_t * cutwin,long dpos,long dlen)922 static void flip_selection(window_textbuffer_t *cutwin, long dpos, long dlen)
923 {
924   int xpos, ypos;
925   int xpos2, ypos2;
926   int off, height;
927   long ybody, ybody2;
928   long lx, lx2;
929 
930   if (!cutwin->isactive) {
931     return; /* not the front window */
932   }
933 
934   if (dpos < 0) {
935     return; /* dot hidden */
936   }
937 
938   if (dlen==0) {
939     lx = find_loc_by_pos(cutwin, dpos, &xpos, &ypos);
940     if (lx-cutwin->scrollline < 0
941       || lx-cutwin->scrollline >= cutwin->linesonpage)
942       return;
943     height = cutwin->linelist[lx].height;
944     xglk_draw_dot(cutwin->textwin_x + xpos,
945       cutwin->textwin_y + ypos + height, height);
946   }
947   else {
948     lx = find_loc_by_pos(cutwin, dpos, &xpos, &ypos);
949     lx2 = find_loc_by_pos(cutwin, dpos+dlen, &xpos2, &ypos2);
950     if (ypos==ypos2) {
951       /* within one line */
952       if (lx-cutwin->scrollline < 0
953 	|| lx-cutwin->scrollline >= cutwin->linesonpage)
954 	return;
955       height = cutwin->linelist[lx].height;
956       if (xpos < xpos2) {
957 	XFillRectangle(xiodpy, xiowin, gcflip,
958 	  xpos+cutwin->textwin_x, ypos+cutwin->textwin_y,
959 	  xpos2-xpos, height);
960       }
961     }
962     else {
963       if (lx-cutwin->scrollline >= cutwin->linesonpage)
964 	return;
965       if (lx2-cutwin->scrollline < 0)
966 	return;
967       if (lx-cutwin->scrollline >= 0) {
968 	height = cutwin->linelist[lx].height;
969 	if (xpos < cutwin->textwin_w) {
970 	  /* first partial line */
971 	  XFillRectangle(xiodpy, xiowin, gcflip,
972 	    xpos+cutwin->textwin_x, ypos+cutwin->textwin_y,
973 	    cutwin->textwin_w-xpos, height);
974 	}
975 	ybody = ypos + cutwin->linelist[lx].height;
976 	if (ybody < 0)
977 	  ybody = 0;
978       }
979       else {
980 	ybody = 0;
981       }
982       if (lx2-cutwin->scrollline < cutwin->linesonpage) {
983 	ybody2 = ypos2;
984 	height = cutwin->linelist[lx2].height;
985       }
986       else {
987 	ybody2 = cutwin->textwin_h;
988 	height = 0;
989       }
990       if (ybody < ybody2) {
991 	/* main body */
992 	XFillRectangle(xiodpy, xiowin, gcflip,
993 	  cutwin->textwin_x, ybody+cutwin->textwin_y,
994 	  cutwin->textwin_w, ybody2-ybody);
995       }
996       if (xpos2 > 0 && height) {
997 	/* last partial line */
998 	XFillRectangle(xiodpy, xiowin, gcflip,
999 	  cutwin->textwin_x, ypos2+cutwin->textwin_y,
1000 	  xpos2, height);
1001       }
1002     }
1003   }
1004 }
1005 
1006 /* push lines from tmplinelist[0..newnum) in place of
1007    linelist[oldbeg..oldend) */
slapover(window_textbuffer_t * cutwin,long newnum,long oldbeg,long oldend)1008 static void slapover(window_textbuffer_t *cutwin, long newnum,
1009   long oldbeg, long oldend)
1010 {
1011   long wx, lx;
1012   long newnumlines;
1013 
1014   newnumlines = cutwin->numlines-(oldend-oldbeg)+newnum;
1015   if (newnumlines >= cutwin->lines_size) {
1016     while (newnumlines >= cutwin->lines_size)
1017       cutwin->lines_size *= 2;
1018     cutwin->linelist = (lline_t *)realloc(cutwin->linelist,
1019       sizeof(lline_t) * cutwin->lines_size);
1020   }
1021 
1022   /* clobber old */
1023   for (lx=oldbeg; lx<oldend; lx++) {
1024     word_t *thisword;
1025     /* --- finalize word structure --- */
1026     for (wx=0, thisword=cutwin->linelist[lx].wordlist;
1027 	 wx<cutwin->linelist[lx].numwords;
1028 	 wx++, thisword++) {
1029       if (thisword->stype == stype_Text) {
1030 	if (thisword->u.letterpos) {
1031 	  free(thisword->u.letterpos);
1032 	  thisword->u.letterpos = NULL;
1033 	}
1034       }
1035       else if (thisword->stype == stype_Image) {
1036 	if (thisword->u.image) {
1037 	  delete_imageword(thisword->u.image);
1038 	  thisword->u.image = NULL;
1039 	}
1040       }
1041     }
1042     free(cutwin->linelist[lx].wordlist);
1043     cutwin->linelist[lx].wordlist = NULL;
1044   }
1045 
1046   if (oldend < cutwin->numlines && newnumlines != cutwin->numlines) {
1047     memmove(&cutwin->linelist[oldend+(newnumlines-cutwin->numlines)],
1048       &cutwin->linelist[oldend],
1049       sizeof(lline_t) * (cutwin->numlines-oldend));
1050   }
1051   cutwin->numlines = newnumlines;
1052 
1053   if (newnum) {
1054     memcpy(&cutwin->linelist[oldbeg],
1055          &cutwin->tmplinelist[0],
1056          sizeof(lline_t) * (newnum));
1057   }
1058 }
1059 
1060 /* xpos, ypos are relative to textwin origin */
find_pos_by_loc(window_textbuffer_t * cutwin,int xpos,int ypos,long * truepos)1061 static long find_pos_by_loc(window_textbuffer_t *cutwin, int xpos, int ypos,
1062   long *truepos)
1063 {
1064   int ix, jx;
1065   long linenum;
1066   long wx, atpos, newpos;
1067   lline_t *curline;
1068   word_t *curword;
1069 
1070   *truepos = -1;
1071 
1072   if (ypos < 0)
1073     linenum = (-1) - ((-1)-ypos / cutwin->font.lineheight);
1074   else if (ypos >= cutwin->lineoffsetlist[cutwin->linesonpage])
1075     linenum = cutwin->linesonpage
1076       + (ypos - cutwin->lineoffsetlist[cutwin->linesonpage])
1077       / cutwin->font.lineheight;
1078   else {
1079     long min = 0;
1080     long max = cutwin->linesonpage;
1081     while (min+1 < max) {
1082       linenum = (min+max) / 2;
1083       if (ypos < cutwin->lineoffsetlist[linenum])
1084 	max = linenum;
1085       else if (ypos >= cutwin->lineoffsetlist[linenum+1])
1086 	min = linenum+1;
1087       else {
1088 	min = linenum;
1089 	break;
1090       }
1091     }
1092     linenum = min;
1093   }
1094 
1095   linenum += cutwin->scrollline;
1096 
1097   if (linenum < 0)
1098     return 0;
1099   if (linenum >= cutwin->numlines)
1100     return cutwin->numchars;
1101 
1102   curline = (&cutwin->linelist[linenum]);
1103 
1104   if (curline->leftindent > 0 && xpos < curline->leftindent) {
1105     long lx;
1106     int ylinepos = cutwin->lineoffsetlist[linenum-cutwin->scrollline];
1107     for (lx = linenum; lx >= 0; lx--) {
1108       lline_t *thisline = (&cutwin->linelist[lx]);
1109       if (thisline->leftbelow == 0 && lx < linenum)
1110 	break;
1111       for (wx=0; wx<thisline->numwords; wx++) {
1112 	curword = thisline->wordlist+wx;
1113 	if (curword->stype == stype_Image) {
1114 	  imageword_t *iwd = curword->u.image;
1115 	  if (iwd) {
1116 	    if (iwd->imagealign == imagealign_MarginLeft
1117 	      && xpos >= iwd->pos && xpos < iwd->pos+iwd->width
1118 	      && ypos >= ylinepos && ypos < ylinepos+iwd->height) {
1119 	      *truepos = thisline->pos + curword->pos;
1120 	      break;
1121 	    }
1122 	  }
1123 	}
1124 	else if (curword->stype == stype_Text) {
1125 	  break;
1126 	}
1127       }
1128       ylinepos -= thisline->height;
1129     }
1130   }
1131 
1132   if (curline->rightindent > 0 && xpos >= cutwin->textwin_w - curline->rightindent) {
1133     long lx;
1134     int ylinepos = cutwin->lineoffsetlist[linenum-cutwin->scrollline];
1135     for (lx = linenum; lx >= 0; lx--) {
1136       lline_t *thisline = (&cutwin->linelist[lx]);
1137       if (thisline->rightbelow == 0 && lx < linenum)
1138 	break;
1139       for (wx=0; wx<thisline->numwords; wx++) {
1140 	curword = thisline->wordlist+wx;
1141 	if (curword->stype == stype_Image) {
1142 	  imageword_t *iwd = curword->u.image;
1143 	  if (iwd) {
1144 	    if (iwd->imagealign == imagealign_MarginRight
1145 	      && xpos >= (cutwin->textwin_w-iwd->pos)-iwd->width
1146 	      && xpos < cutwin->textwin_w-iwd->pos
1147 	      && ypos >= ylinepos && ypos < ylinepos+iwd->height) {
1148 	      *truepos = thisline->pos + curword->pos;
1149 	      break;
1150 	    }
1151 	  }
1152 	}
1153 	else if (curword->stype == stype_Text) {
1154 	  break;
1155 	}
1156       }
1157       ylinepos -= thisline->height;
1158     }
1159   }
1160 
1161   xpos -= curline->indent; /* now xpos is relative to line beginning */
1162   if (xpos < 0) {
1163     return curline->pos; /* beginning of line */
1164   }
1165   atpos = 0;
1166   for (wx=0; wx<curline->numwords; wx++) {
1167     newpos = atpos + curline->wordlist[wx].width;
1168     if (xpos < newpos)
1169       break;
1170     atpos = newpos;
1171   }
1172   if (wx==curline->numwords) {
1173     return curline->posend; /* end of line */
1174   }
1175 
1176   xpos -= atpos; /* now xpos is relative to word beginning */
1177   curword = (&curline->wordlist[wx]);
1178 
1179   if (curword->stype == stype_Text) {
1180     if (!curword->u.letterpos)
1181       measure_word(cutwin, curline, curword);
1182 
1183     for (ix=0; ix<curword->len; ix++) {
1184       if (xpos <= (curword->u.letterpos[ix]+curword->u.letterpos[ix+1])/2) {
1185 	jx = ix;
1186 	/* if (curword->pos+ix && xpos <= curword->u.letterpos[ix])
1187 	   jx--;*/ /* doesn't work right */
1188 	break;
1189       }
1190     }
1191     *truepos = curline->pos + curword->pos + ix; /* jx */
1192     return curline->pos + curword->pos + ix;
1193   }
1194   else if (curword->stype == stype_Image) {
1195     imageword_t *iwd = curword->u.image;
1196     if (iwd) {
1197       if (iwd->imagealign != imagealign_MarginLeft
1198 	&& iwd->imagealign != imagealign_MarginRight) {
1199 	/* Just hit the beginning or end of the word. */
1200 	*truepos = curline->pos + curword->pos;
1201 	ix = 0;
1202 	if (xpos > iwd->width/2)
1203 	  ix++;
1204 	return curline->pos + curword->pos + ix;
1205       }
1206     }
1207   }
1208 
1209   /* Just the beginning. */
1210   *truepos = curline->pos + curword->pos;
1211   return curline->pos + curword->pos;
1212 }
1213 
1214 /* returns the last line such that pos >= line.pos. guessline is a guess
1215    to start searching at; -1 means end of file. Can return -1 if pos is
1216    before the start of the layout. */
find_line_by_pos(window_textbuffer_t * cutwin,long pos,long guessline)1217 static long find_line_by_pos(window_textbuffer_t *cutwin, long pos,
1218   long guessline)
1219 {
1220   long lx;
1221 
1222   if (guessline < 0 || guessline >= cutwin->numlines)
1223     guessline = cutwin->numlines-1;
1224 
1225   if (guessline < cutwin->numlines-1
1226     && cutwin->linelist[guessline].pos <= pos) {
1227     for (lx=guessline; lx<cutwin->numlines; lx++) {
1228       if (cutwin->linelist[lx].pos > pos)
1229 	break;
1230     }
1231     lx--;
1232   }
1233   else {
1234     for (lx=guessline; lx>=0; lx--) {
1235       if (cutwin->linelist[lx].pos <= pos)
1236 	break;
1237     }
1238   }
1239 
1240   return lx;
1241 }
1242 
1243 /* returns values relative to textwin origin, at top of line. */
find_loc_by_pos(window_textbuffer_t * cutwin,long pos,int * xposret,int * yposret)1244 static long find_loc_by_pos(window_textbuffer_t *cutwin, long pos,
1245   int *xposret, int *yposret)
1246 {
1247   long lx, lx2;
1248   long wx, atpos;
1249   lline_t *curline;
1250   word_t *curword;
1251 
1252   lx = find_line_by_pos(cutwin, pos, -1);
1253   lx2 = lx - cutwin->scrollline;
1254   if (lx < 0 || lx2 < 0) {
1255     /* somehow before first line laid out */
1256     *xposret = 0;
1257     *yposret = (-cutwin->scrollline) * cutwin->font.lineheight;
1258     return lx;
1259   }
1260   if (lx2 >= cutwin->linesonpage) {
1261     /* or after */
1262     *xposret = 0;
1263     *yposret = cutwin->lineoffsetlist[cutwin->linesonpage]
1264       + (lx2 - cutwin->linesonpage) * cutwin->font.lineheight;
1265     return lx;
1266   }
1267   curline = (&cutwin->linelist[lx]);
1268   *yposret = cutwin->lineoffsetlist[lx2];
1269 
1270   atpos = curline->indent;
1271   for (wx=0; wx<curline->numwords; wx++) {
1272     if (curline->pos+curline->wordlist[wx].pos+curline->wordlist[wx].len >= pos)
1273       break;
1274     atpos += curline->wordlist[wx].width;
1275   }
1276   if (wx==curline->numwords) {
1277     *xposret = atpos;
1278     return lx;
1279   }
1280 
1281   curword = (&curline->wordlist[wx]);
1282   if (curword->stype == stype_Text) {
1283     if (!curword->u.letterpos)
1284       measure_word(cutwin, curline, curword);
1285     atpos += curword->u.letterpos[pos - (curline->pos+curword->pos)];
1286   }
1287   else if (curword->stype == stype_Image) {
1288     if (curword->u.image && pos > (curline->pos+curword->pos))
1289       atpos += curword->u.image->width;
1290   }
1291   else if (curword->stype == stype_Break) {
1292     /* leave it */
1293   }
1294 
1295   *xposret = atpos;
1296   return lx;
1297 }
1298 
readd_lineheights(window_textbuffer_t * cutwin,long lx)1299 static void readd_lineheights(window_textbuffer_t *cutwin, long lx)
1300 {
1301   long jx, lx2;
1302 
1303   lx2 = lx-cutwin->scrollline;
1304   if (lx2 > cutwin->linesonpage)
1305     return;
1306 
1307   for (; lx < cutwin->numlines; lx++, lx2++) {
1308     jx = cutwin->lineoffsetlist[lx2] + cutwin->linelist[lx].height;
1309     if (jx > cutwin->textwin_h)
1310       break;
1311     if (lx2+1 >= cutwin->lineoffset_size) {
1312       cutwin->lineoffset_size = cutwin->lineoffset_size * 2 + lx2 + 1;
1313       cutwin->lineoffsetlist = (int *)realloc(cutwin->lineoffsetlist,
1314 	cutwin->lineoffset_size * sizeof(int));
1315     }
1316     cutwin->lineoffsetlist[lx2+1] = jx;
1317   }
1318   cutwin->linesonpage = lx2;
1319 }
1320 
1321 /* Text words only, please. */
measure_word(window_textbuffer_t * cutwin,lline_t * curline,word_t * curword)1322 static void measure_word(window_textbuffer_t *cutwin, lline_t *curline,
1323   word_t *curword)
1324 {
1325   int cx;
1326   char *buf;
1327   int direction;
1328   int letterwid;
1329   long *arr;
1330 
1331   if (curword->u.letterpos) {
1332     free(curword->u.letterpos);
1333     curword->u.letterpos = NULL;
1334   }
1335 
1336   arr = (long *)malloc(sizeof(long) * (curword->len+1));
1337 
1338   buf = cutwin->charbuf+curline->pos+curword->pos;
1339   arr[0] = 0;
1340   for (cx=0; cx<curword->len-1; cx++) {
1341     letterwid = XTextWidth(cutwin->font.gc[curword->attr].fontstr, buf+cx, 1);
1342     /* attend to curword->linkid? */
1343     arr[cx+1] = arr[cx] + letterwid;
1344   }
1345   arr[cx+1] = curword->width;
1346 
1347   curword->u.letterpos = arr;
1348 }
1349 
win_textbuffer_clear_window(window_textbuffer_t * cutwin)1350 void win_textbuffer_clear_window(window_textbuffer_t *cutwin)
1351 {
1352   int ix;
1353 
1354   if (1) {
1355     /* normal clear */
1356     win_textbuffer_delete_start(cutwin, cutwin->numlines);
1357   }
1358   else {
1359     /* bonzo scrolling clear. This doesn't actually work, since lines can
1360        be of different heights. */
1361     if (!cutwin->isclear) {
1362       for (ix=0; ix<cutwin->linesonpage; ix++)
1363 	win_textbuffer_add(cutwin, '\n', -1);
1364     }
1365   }
1366 
1367   cutwin->isclear = TRUE;
1368 }
1369 
1370 /* pos < 0 means add at end.
1371    all this is grotesquely inefficient if adding anywhere but the end. */
win_textbuffer_add(window_textbuffer_t * cutwin,char ch,long pos)1372 void win_textbuffer_add(window_textbuffer_t *cutwin, char ch, long pos)
1373 {
1374   if (ch != '\n')
1375     cutwin->isclear = FALSE;
1376   if (pos<0)
1377     pos = cutwin->numchars;
1378   win_textbuffer_replace(cutwin, pos, 0, &ch, 1);
1379 }
1380 
1381 /* update data, adjusting dot and styles as necessary. */
win_textbuffer_replace(window_textbuffer_t * cutwin,long pos,long oldlen,char * buf,long newlen)1382 void win_textbuffer_replace(window_textbuffer_t *cutwin, long pos, long oldlen,
1383   char *buf, long newlen)
1384 {
1385   long newnumchars;
1386 
1387   newnumchars = cutwin->numchars-oldlen+newlen;
1388   if (newnumchars >= cutwin->char_size) {
1389     while (newnumchars >= cutwin->char_size)
1390       cutwin->char_size *= 2;
1391     cutwin->charbuf = (char *)realloc(cutwin->charbuf, sizeof(char) * cutwin->char_size);
1392   }
1393 
1394   if (pos < cutwin->dirtybeg || cutwin->dirtybeg < 0)
1395     cutwin->dirtybeg = pos;
1396 
1397   if (newlen != oldlen) {
1398     if (pos+oldlen != cutwin->numchars) {
1399       memmove(cutwin->charbuf+pos+newlen, cutwin->charbuf+pos+oldlen, sizeof(char) * (cutwin->numchars-(pos+oldlen)));
1400     }
1401     if (cutwin->numchars >= cutwin->dirtyend)
1402       cutwin->dirtyend = cutwin->numchars+1;
1403     cutwin->dirtydelta += (newlen-oldlen);
1404   }
1405   else {
1406     if (pos+newlen >= cutwin->dirtyend)
1407       cutwin->dirtyend = pos+newlen+1;
1408     cutwin->dirtydelta += (newlen-oldlen);
1409   }
1410 
1411   /* copy in the new stuff */
1412   if (newlen)
1413     memmove(cutwin->charbuf+pos, buf, sizeof(char) * newlen);
1414 
1415   /* diddle the dot */
1416   if (cutwin->dotpos >= pos+oldlen) {
1417     /* starts after changed region */
1418     cutwin->dotpos += (newlen-oldlen);
1419   }
1420   else if (cutwin->dotpos >= pos) {
1421     /* starts inside changed region */
1422     if (cutwin->dotpos+cutwin->dotlen >= pos+oldlen) {
1423       /* ...but ends after it */
1424       cutwin->dotlen = (cutwin->dotpos+cutwin->dotlen)-(pos+oldlen);
1425       cutwin->dotpos = pos+newlen;
1426     }
1427     else {
1428       /* ...and ends inside it */
1429       cutwin->dotpos = pos+newlen;
1430       cutwin->dotlen = 0;
1431     }
1432   }
1433   else {
1434     /* starts before changed region */
1435     if (cutwin->dotpos+cutwin->dotlen >= pos+oldlen) {
1436       /* ...but ends after it */
1437       cutwin->dotlen += (newlen-oldlen);
1438     }
1439     else if (cutwin->dotpos+cutwin->dotlen >= pos) {
1440       /* ...but ends inside it */
1441       cutwin->dotlen = (pos+newlen) - cutwin->dotpos;
1442     }
1443   }
1444 
1445   cutwin->numchars = newnumchars;
1446 }
1447 
win_textbuffer_set_style_text(window_textbuffer_t * cutwin,glui32 attr)1448 void win_textbuffer_set_style_text(window_textbuffer_t *cutwin, glui32 attr)
1449 {
1450   style_t *sty = &(cutwin->stylelist[cutwin->numstyles-1]);
1451   if (attr == 0xFFFFFFFF)
1452     attr = sty->attr;
1453   win_textbuffer_setstyle(cutwin, -1, stype_Text, attr, sty->linkid,
1454     0, 0, 0, 0);
1455 }
1456 
win_textbuffer_set_style_image(window_textbuffer_t * cutwin,glui32 image,int imagealign,glui32 imagewidth,glui32 imageheight)1457 void win_textbuffer_set_style_image(window_textbuffer_t *cutwin,
1458   glui32 image, int imagealign,
1459   glui32 imagewidth, glui32 imageheight)
1460 {
1461   style_t *sty = &(cutwin->stylelist[cutwin->numstyles-1]);
1462   win_textbuffer_setstyle(cutwin, -1, stype_Image, sty->attr, sty->linkid,
1463     image, imagealign, imagewidth, imageheight);
1464 }
1465 
win_textbuffer_set_style_break(window_textbuffer_t * cutwin)1466 void win_textbuffer_set_style_break(window_textbuffer_t *cutwin)
1467 {
1468   style_t *sty = &(cutwin->stylelist[cutwin->numstyles-1]);
1469   win_textbuffer_setstyle(cutwin, -1, stype_Break, sty->attr, sty->linkid,
1470     0, 0, 0, 0);
1471 }
1472 
win_textbuffer_set_style_link(window_textbuffer_t * cutwin,glui32 linkid)1473 void win_textbuffer_set_style_link(window_textbuffer_t *cutwin,
1474   glui32 linkid)
1475 {
1476   style_t *sty = &(cutwin->stylelist[cutwin->numstyles-1]);
1477   win_textbuffer_setstyle(cutwin, -1, sty->stype, sty->attr, linkid,
1478     sty->image, sty->imagealign, sty->imagewidth, sty->imageheight);
1479 }
1480 
win_textbuffer_setstyle(window_textbuffer_t * cutwin,long pos,int stype,glui32 attr,glui32 linkid,glui32 image,int imagealign,glui32 imagewidth,glui32 imageheight)1481 static void win_textbuffer_setstyle(window_textbuffer_t *cutwin,
1482   long pos, int stype,
1483   glui32 attr, glui32 linkid, glui32 image, int imagealign,
1484   glui32 imagewidth, glui32 imageheight)
1485 {
1486   long sx;
1487 
1488   if (pos < 0)
1489     pos = cutwin->numchars;
1490 
1491   /* find the last style at or before pos. */
1492   for (sx=cutwin->numstyles-1; sx>=0; sx--) {
1493     if (cutwin->stylelist[sx].pos <= pos) {
1494       break;
1495     }
1496   }
1497   if (sx < 0) {
1498     /*printf("oops, went back behind style 0\n");*/
1499     return;
1500   }
1501 
1502   if (cutwin->stylelist[sx].pos == pos) {
1503     /* if sx is *at* pos, just change it. */
1504   }
1505   else {
1506     /* if before, insert a new style after it. */
1507     sx++;
1508     if (cutwin->numstyles+1 >= cutwin->styles_size) {
1509       cutwin->styles_size *= 2;
1510       cutwin->stylelist = (style_t *)realloc(cutwin->stylelist,
1511 	sizeof(style_t) * cutwin->styles_size);
1512     }
1513     cutwin->numstyles++;
1514     if (sx+1 < cutwin->numstyles) {
1515       memmove(&cutwin->stylelist[sx+1], &cutwin->stylelist[sx],
1516 	sizeof(style_t) * (cutwin->numstyles-sx));
1517     }
1518     cutwin->stylelist[sx].pos = pos;
1519   }
1520 
1521   cutwin->stylelist[sx].stype = stype;
1522   cutwin->stylelist[sx].linkid = linkid;
1523   cutwin->stylelist[sx].attr = attr;
1524   cutwin->stylelist[sx].image = image;
1525   cutwin->stylelist[sx].imagealign = imagealign;
1526   cutwin->stylelist[sx].imagewidth = imagewidth;
1527   cutwin->stylelist[sx].imageheight = imageheight;
1528 
1529   if (pos != cutwin->numchars) {
1530     /* really, should only go to next style_t */
1531     cutwin->dirtybeg = pos;
1532     cutwin->dirtyend = cutwin->numchars;
1533     cutwin->dirtydelta = 0;
1534     win_textbuffer_layout(cutwin);
1535   }
1536 }
1537 
win_textbuffer_end_visible(window_textbuffer_t * cutwin)1538 void win_textbuffer_end_visible(window_textbuffer_t *cutwin)
1539 {
1540   if (cutwin->scrollline < cutwin->numlines-cutwin->linesonpage) {
1541     /* this is wrong -- linesonpage doesn't correctly measure lines
1542        at the end. */
1543     scroll_to(cutwin, cutwin->numlines-cutwin->linesonpage);
1544   }
1545 }
1546 
win_textbuffer_set_paging(window_textbuffer_t * cutwin,int forcetoend)1547 void win_textbuffer_set_paging(window_textbuffer_t *cutwin, int forcetoend)
1548 {
1549   long val;
1550   long total;
1551 
1552   if (cutwin->lastseenline == cutwin->numlines)
1553     return;
1554 
1555   if (!forcetoend
1556     && cutwin->lastseenline - 0 < cutwin->numlines - cutwin->linesonpage) {
1557     /* this is wrong -- linesonpage doesn't correctly measure lines
1558        at the end. */
1559     /* scroll lastseenline to top, stick there */
1560     val = cutwin->lastseenline;
1561     if (val > cutwin->scrollline)
1562       val--;
1563   }
1564   else {
1565     /* scroll to bottom, set lastseenline to end. */
1566     total = 0;
1567     for (val = cutwin->numlines; val > 0; val--) {
1568       total += cutwin->linelist[val-1].height;
1569       if (total > cutwin->textwin_h)
1570 	break;
1571     }
1572 
1573     cutwin->lastseenline = cutwin->numlines;
1574   }
1575 
1576   scroll_to(cutwin, val);
1577 }
1578 
win_textbuffer_is_paging(window_t * win)1579 int win_textbuffer_is_paging(window_t *win)
1580 {
1581   window_textbuffer_t *dwin = win->data;
1582 
1583   if (dwin->lastseenline < dwin->numlines - dwin->linesonpage) {
1584     /* this is wrong -- linesonpage doesn't correctly measure lines
1585        at the end. */
1586     return TRUE;
1587   }
1588   return FALSE;
1589 }
1590 
1591 /* delete num lines from the top */
win_textbuffer_delete_start(window_textbuffer_t * cutwin,long num)1592 void win_textbuffer_delete_start(window_textbuffer_t *cutwin, long num)
1593 {
1594   long delchars;
1595   long lx, sx, sx2;
1596   glui32 origattr;
1597 
1598   if (num > cutwin->numlines)
1599     num = cutwin->numlines;
1600   if (num < 0)
1601     num = 0;
1602 
1603   if (cutwin->numlines==0)
1604     return;
1605 
1606   if (num < cutwin->numlines)
1607     delchars = cutwin->linelist[num].pos;
1608   else
1609     delchars = cutwin->numchars;
1610 
1611   if (!delchars)
1612     return;
1613 
1614   /* lines */
1615   slapover(cutwin, 0, 0, num);
1616   for (lx=0; lx<cutwin->numlines; lx++) {
1617     cutwin->linelist[lx].pos -= delchars;
1618     cutwin->linelist[lx].posend -= delchars;
1619   }
1620 
1621   /* styles */
1622   for (sx=0; sx<cutwin->numstyles; sx++) {
1623     if (cutwin->stylelist[sx].pos > delchars)
1624       break;
1625   }
1626   if (sx>0) {
1627     origattr = cutwin->stylelist[sx-1].attr;
1628     cutwin->stylelist[0].pos = 0;
1629     cutwin->stylelist[0].attr = origattr;
1630     for (sx2=1; sx<cutwin->numstyles; sx++, sx2++) {
1631       cutwin->stylelist[sx2].pos = cutwin->stylelist[sx].pos - delchars;
1632       cutwin->stylelist[sx2].attr = cutwin->stylelist[sx].attr;
1633     }
1634     cutwin->numstyles = sx2;
1635   }
1636 
1637   /* chars */
1638   if (cutwin->numchars > delchars)
1639     memmove(&cutwin->charbuf[0], &cutwin->charbuf[delchars], sizeof(char) * (cutwin->numchars-delchars));
1640   cutwin->numchars -= delchars;
1641 
1642   /* adjust, I mean, everything */
1643   if (cutwin->dirtybeg != (-1)) {
1644     cutwin->dirtybeg -= delchars;
1645     cutwin->dirtyend -= delchars;
1646     if (cutwin->dirtyend < 0) {
1647       cutwin->dirtybeg = (-1);
1648       cutwin->dirtyend = (-1);
1649     }
1650     else if (cutwin->dirtybeg < 0) {
1651       cutwin->dirtybeg = 0;
1652     }
1653   }
1654 
1655   cutwin->dotpos -= delchars;
1656   if (cutwin->dotpos < 0) {
1657     if (cutwin->dotpos+cutwin->dotlen < 0) {
1658       cutwin->dotpos = 0;
1659       cutwin->dotlen = 0;
1660     }
1661     else {
1662       cutwin->dotlen += cutwin->dotpos;
1663       cutwin->dotpos = 0;
1664     }
1665   }
1666   cutwin->lastdotpos -= delchars;
1667   if (cutwin->lastdotpos < 0) {
1668     if (cutwin->lastdotpos+cutwin->lastdotlen < 0) {
1669       cutwin->lastdotpos = 0;
1670       cutwin->lastdotlen = 0;
1671     }
1672     else {
1673       cutwin->lastdotlen += cutwin->lastdotpos;
1674       cutwin->lastdotpos = 0;
1675     }
1676   }
1677   cutwin->inputfence -= delchars;
1678   if (cutwin->inputfence < 0)
1679     cutwin->inputfence = 0;
1680 
1681   cutwin->lastseenline -= num;
1682   if (cutwin->lastseenline < 0)
1683     cutwin->lastseenline = 0;
1684 
1685   cutwin->scrollline -= num;
1686   cutwin->scrollpos -= delchars;
1687   if (cutwin->scrollline < 0 || cutwin->scrollpos < 0) {
1688     cutwin->scrollline = 0;
1689     cutwin->scrollpos = 0;
1690     readd_lineheights(cutwin, cutwin->scrollline);
1691     redrawtext(cutwin, 0, -1, -1);
1692     flip_selection(cutwin, cutwin->dotpos, cutwin->dotlen);
1693     xweg_adjust_scrollbar(&cutwin->scrollbar, cutwin->numlines,
1694       cutwin->scrollline, cutwin->linesonpage);
1695   }
1696   else {
1697     xweg_adjust_scrollbar(&cutwin->scrollbar, cutwin->numlines,
1698       cutwin->scrollline, cutwin->linesonpage);
1699   }
1700 }
1701 
win_textbuffer_layout(window_textbuffer_t * cutwin)1702 static void win_textbuffer_layout(window_textbuffer_t *cutwin)
1703 {
1704   long ix, jx, ejx, lx;
1705   long styx, nextstylepos;
1706   style_t *curattr;
1707   long overline, overlineend;
1708   long tmpl, startpos;
1709   int prevflags;
1710   int needwholeredraw;
1711   int textwin_w = cutwin->textwin_w; /* cache */
1712   int textwin_h = cutwin->textwin_h; /* cache */
1713   long leftindent, leftbelow, rightindent, rightbelow;
1714 
1715   int direction;
1716   int wordwidth;
1717 
1718   static long lastline = 0; /* last line dirtied */
1719 
1720   /* Shut up compiler warnings */
1721   ejx = 0;
1722   jx = 0;
1723 
1724   if (cutwin->dirtybeg < 0 || cutwin->dirtyend < 0) {
1725     if (cutwin->lastdotpos != cutwin->dotpos
1726       || cutwin->lastdotlen != cutwin->dotlen) {
1727       refiddle_selection(cutwin, cutwin->lastdotpos,
1728 	cutwin->lastdotlen, cutwin->dotpos, cutwin->dotlen);
1729       /*flip_selection(cutwin, cutwin->lastdotpos, cutwin->lastdotlen);*/
1730       cutwin->lastdotpos = cutwin->dotpos;
1731       cutwin->lastdotlen = cutwin->dotlen;
1732       /*flip_selection(cutwin, cutwin->lastdotpos, cutwin->lastdotlen);*/
1733     }
1734     return;
1735   }
1736 
1737   /* we start by turning off the selection. */
1738   flip_selection(cutwin, cutwin->lastdotpos, cutwin->lastdotlen);
1739   cutwin->lastdotpos = cutwin->dotpos;
1740   cutwin->lastdotlen = cutwin->dotlen;
1741 
1742   if (cutwin->numlines==0) {
1743     overline = 0;
1744     startpos = 0;
1745   }
1746   else {
1747     lx = find_line_by_pos(cutwin, cutwin->dirtybeg, lastline);
1748     /* now lx is the line containing dirtybeg */
1749 
1750     if (lx>0 && lx<cutwin->numlines && (cutwin->linelist[lx].flags & lineflag_Wrapped)) {
1751       /* do layout from previous line, in case a word from the changed area pops back there. */
1752       lx--;
1753     }
1754     overline = lx;
1755     startpos = cutwin->linelist[overline].pos;
1756   }
1757 
1758   /* get any margin info left from previous lines */
1759   if (overline > 0 && cutwin->linelist[overline-1].leftbelow) {
1760     leftbelow = cutwin->linelist[overline-1].leftbelow;
1761     leftindent = cutwin->linelist[overline-1].leftindent;
1762   }
1763   else {
1764     leftbelow = 0;
1765     leftindent = 0;
1766   }
1767   if (overline > 0 && cutwin->linelist[overline-1].rightbelow) {
1768     rightbelow = cutwin->linelist[overline-1].rightbelow;
1769     rightindent = cutwin->linelist[overline-1].rightindent;
1770   }
1771   else {
1772     rightbelow = 0;
1773     rightindent = 0;
1774   }
1775 
1776   /* get the first relevant style_t */
1777   for (styx=cutwin->numstyles-1; styx>0; styx--)
1778     if (cutwin->stylelist[styx].pos <= startpos)
1779       break;
1780   if (styx==cutwin->numstyles-1)
1781     nextstylepos = cutwin->numchars+10;
1782   else
1783     nextstylepos = cutwin->stylelist[styx+1].pos;
1784   curattr = &(cutwin->stylelist[styx]);
1785 
1786   /* start a-layin' */
1787   tmpl = 0;
1788   prevflags = 0;
1789 
1790   while (startpos<cutwin->numchars
1791     && !(startpos >= cutwin->dirtyend && cutwin->charbuf[startpos]=='\n')) {
1792     lline_t *thisline;
1793     long tmpw, tmpwords_size;
1794     long widthsofar, spaceswidth=0;
1795     int lineattrknown, anyimages;
1796     glui32 lineattr;
1797     long ascent, descent, superscent;
1798 
1799     if (tmpl+1 >= cutwin->tmplines_size) {
1800       /* the +1 allows the extra blank line at the end */
1801       cutwin->tmplines_size *= 2;
1802       cutwin->tmplinelist = (lline_t *)realloc(cutwin->tmplinelist,
1803 	sizeof(lline_t) * cutwin->tmplines_size);
1804     }
1805     thisline = (&cutwin->tmplinelist[tmpl]);
1806     thisline->flags = prevflags;
1807     thisline->height = 64; /* initially silly values */
1808     thisline->off = 48;
1809     thisline->indent = cutwin->font.baseindent + leftindent;
1810     /* indent will be added to when lineattr is known. */
1811     lineattrknown = FALSE;
1812     lineattr = style_Normal;
1813     tmpwords_size = 8;
1814     thisline->wordlist = (word_t *)malloc(tmpwords_size * sizeof(word_t));
1815     tmpw = 0;
1816 
1817     /*printf("laying tmpline %d, from charpos %d\n", tmpl, startpos);*/
1818     tmpl++;
1819 
1820     ix = startpos;
1821     widthsofar = thisline->indent; /* will be set when lineattr is known. */
1822     prevflags = 0;
1823 
1824     while (ix<cutwin->numchars && cutwin->charbuf[ix]!='\n') {
1825       word_t *thisword;
1826       int sidebar;
1827 
1828       while (ix >= nextstylepos) {
1829 	/* ahead one style_t */
1830 	styx++;
1831 	if (styx==cutwin->numstyles-1)
1832 	  nextstylepos = cutwin->numchars+10;
1833 	else
1834 	  nextstylepos = cutwin->stylelist[styx+1].pos;
1835 	curattr = &(cutwin->stylelist[styx]);
1836       }
1837 
1838       if (!lineattrknown) {
1839 	lineattr = curattr->attr;
1840 	thisline->indent += cutwin->font.gc[lineattr].indent;
1841 	if (!(thisline->flags & lineflag_Wrapped)) {
1842 	  thisline->indent += cutwin->font.gc[lineattr].parindent;
1843 	}
1844 	widthsofar = thisline->indent;
1845 	lineattrknown = TRUE;
1846       }
1847 
1848       if (tmpw >= tmpwords_size) {
1849 	tmpwords_size *= 2;
1850 	thisline->wordlist = (word_t *)realloc(thisline->wordlist,
1851 	  tmpwords_size * sizeof(word_t));
1852       }
1853       thisword = (&thisline->wordlist[tmpw]);
1854       /* --- initialize word structure --- */
1855 
1856       sidebar = FALSE;
1857       wordwidth = 0;
1858 
1859       if (curattr->stype == stype_Text) {
1860 	thisword->stype = stype_Text;
1861 	thisword->u.letterpos = NULL;
1862 	for (jx=ix;
1863 	     jx<cutwin->numchars && jx<nextstylepos
1864 	       && cutwin->charbuf[jx]!=' '
1865 	       && cutwin->charbuf[jx]!='\n';
1866 	     jx++);
1867 	wordwidth = XTextWidth(cutwin->font.gc[curattr->attr].fontstr,
1868 	  cutwin->charbuf+ix, jx-ix);
1869 	/* attend to curattr->linkid? */
1870       }
1871       else if (curattr->stype == stype_Break) {
1872 	thisword->stype = stype_Break;
1873 	thisword->u.letterpos = NULL;
1874 	jx = ix+1;
1875 	if (leftindent || rightindent)
1876 	  wordwidth = textwin_w + 10;
1877 	else
1878 	  wordwidth = 0;
1879       }
1880       else if (curattr->stype == stype_Image) {
1881 	thisword->stype = stype_Image;
1882 	jx = ix+1;
1883 	thisword->u.image = (imageword_t *)malloc(sizeof(imageword_t));
1884 	thisword->u.image->pic = picture_find(curattr->image);
1885 	if (!thisword->u.image->pic) {
1886 	  delete_imageword(thisword->u.image);
1887 	  thisword->u.image = NULL;
1888 	  wordwidth = 0;
1889 	}
1890 	else {
1891 	  thisword->u.image->width = curattr->imagewidth;
1892 	  thisword->u.image->height = curattr->imageheight;
1893 	  thisword->u.image->imagealign = curattr->imagealign;
1894 	  thisword->u.image->pos = 0;
1895 	  wordwidth = thisword->u.image->width;
1896 
1897 	  if (curattr->imagealign == imagealign_MarginLeft
1898 	    || curattr->imagealign == imagealign_MarginRight) {
1899 	    int px, pcount;
1900 	    wordwidth = 0;
1901 	    sidebar = TRUE;
1902 	    pcount = 0;
1903 	    for (px=0; !pcount && px<tmpw; px++) {
1904 	      if (thisline->wordlist[px].stype == stype_Text)
1905 		pcount++;
1906 	    }
1907 	    if (pcount) {
1908 	      delete_imageword(thisword->u.image);
1909 	      thisword->u.image = NULL;
1910 	    }
1911 	    else {
1912 	      if (curattr->imagealign == imagealign_MarginLeft) {
1913 		long oldleftindent = leftindent;
1914 		leftindent += thisword->u.image->width;
1915 		if (!oldleftindent) {
1916 		  leftindent += prefs.textbuffer.marginx;
1917 		  thisword->u.image->pos = 0;
1918 		}
1919 		else {
1920 		  thisword->u.image->pos = oldleftindent
1921 		    - prefs.textbuffer.marginx;
1922 		}
1923 		if (leftbelow < thisword->u.image->height)
1924 		  leftbelow = thisword->u.image->height;
1925 		thisline->indent += (leftindent - oldleftindent);
1926 		widthsofar += (leftindent - oldleftindent);
1927 	      }
1928 	      else {
1929 		long oldrightindent = rightindent;
1930 		rightindent += thisword->u.image->width;
1931 		if (!oldrightindent) {
1932 		  rightindent += prefs.textbuffer.marginx;
1933 		  thisword->u.image->pos = 0;
1934 		}
1935 		else {
1936 		  thisword->u.image->pos = oldrightindent
1937 		    - prefs.textbuffer.marginx;
1938 		}
1939 		if (rightbelow < thisword->u.image->height)
1940 		  rightbelow = thisword->u.image->height;
1941 	      }
1942 	    }
1943 	  }
1944 	}
1945       }
1946 
1947       if (widthsofar + wordwidth > textwin_w - rightindent && !sidebar) {
1948 	prevflags = lineflag_Wrapped;
1949 	/* the word overflows, so throw it away -- unless it's the first
1950 	   word on a line. (That would lead to an infinite loop.)
1951 	   Of course break-words are always thrown away. */
1952 	if (tmpw == 0 && !(curattr->stype == stype_Break && wordwidth)
1953 	  /*###&& (leftindent == 0 && rightindent == 0)*/) {
1954 	  if (curattr->stype == stype_Text) {
1955 	    /* do something clever -- split the word, put first part in tmplist.
1956 	       but be sure to take at least one letter. */
1957 	    long letx;
1958 	    long wordwidthsofar = 0;
1959 	    for (letx=ix; letx<jx; letx++) {
1960 	      wordwidth = XTextWidth(cutwin->font.gc[curattr->attr].fontstr,
1961 		cutwin->charbuf+letx, 1);
1962 	      /* attend to curattr->linkid? */
1963 	      if (letx > ix
1964 		&& widthsofar + wordwidthsofar+wordwidth
1965 		> (textwin_w - rightindent)) {
1966 		break;
1967 	      }
1968 	      wordwidthsofar += wordwidth;
1969 	    }
1970 	    jx = letx;
1971 	    wordwidth = wordwidthsofar;
1972 	    /* spaceswidth and ejx will be 0 */
1973 	  }
1974 	  /* don't break */
1975 	}
1976 	else {
1977 	  /* pitch this word. */
1978 	  if (thisword->stype == stype_Image) {
1979 	    if (thisword->u.image) {
1980 	      delete_imageword(thisword->u.image);
1981 	      thisword->u.image = NULL;
1982 	    }
1983 	  }
1984 	  /* ejx and spaceswidth are properly set from last word,
1985 	     trim them off. */
1986 	  thisword--;
1987 	  thisword->len -= ejx;
1988 	  thisword->width -= spaceswidth;
1989 	  break; /* line over. */
1990 	}
1991       }
1992 
1993       /* figure out trailing whitespace ### for images too? */
1994       if (!sidebar) {
1995 	ejx = 0;
1996 	while (jx+ejx<cutwin->numchars
1997 	  && jx+ejx<nextstylepos && cutwin->charbuf[jx+ejx]==' ') {
1998 	  ejx++;
1999 	}
2000 	spaceswidth = ejx * cutwin->font.gc[curattr->attr].spacewidth;
2001       }
2002       else {
2003 	ejx = 0;
2004 	spaceswidth = 0;
2005       }
2006 
2007       /* put the word in tmplist */
2008       thisword->pos = ix-startpos;
2009       thisword->len = jx+ejx-ix;
2010       thisword->attr = curattr->attr;
2011       thisword->linkid = curattr->linkid;
2012       thisword->width = wordwidth+spaceswidth;
2013       widthsofar += thisword->width;
2014       tmpw++;
2015 
2016       ix = jx+ejx;
2017     }
2018     thisline->pos = startpos;
2019     if (tmpw) {
2020       word_t *thisword = (&thisline->wordlist[tmpw-1]);
2021       thisline->posend = startpos + thisword->pos + thisword->len;
2022     }
2023     else {
2024       thisline->posend = startpos;
2025     }
2026 
2027     if (ix<cutwin->numchars && cutwin->charbuf[ix]=='\n')
2028       ix++;
2029 
2030     thisline->numwords = tmpw;
2031 
2032     superscent = 0;
2033     ascent = 0;
2034     descent = 0;
2035     anyimages = FALSE;
2036     for (jx=0; jx<tmpw; jx++) {
2037       word_t *thisword = (&thisline->wordlist[jx]);
2038       if (thisword->stype == stype_Text) {
2039 	fontref_t *font = &cutwin->font.gc[thisword->attr];
2040 	if (font->ascent > ascent)
2041 	  ascent = font->ascent;
2042 	if (font->descent > descent)
2043 	  descent = font->descent;
2044       }
2045       else {
2046 	anyimages = TRUE;
2047       }
2048     }
2049     if (anyimages) {
2050       int diff;
2051       for (jx=0; jx<tmpw; jx++) {
2052 	word_t *thisword = (&thisline->wordlist[jx]);
2053 	if (thisword->stype == stype_Image) {
2054 	  imageword_t *iwd = thisword->u.image;
2055 	  if (!iwd)
2056 	    continue;
2057 	  switch (iwd->imagealign) {
2058 	  case imagealign_InlineUp:
2059 	    if (superscent < iwd->height - ascent)
2060 	      superscent = iwd->height - ascent;
2061 	    iwd->pos = iwd->height;
2062 	    break;
2063 	  case imagealign_InlineCenter:
2064 	    diff = iwd->height - ascent;
2065 	    diff = (diff+1) / 2;
2066 	    if (superscent < diff)
2067 	      superscent = diff;
2068 	    if (descent < diff)
2069 	      descent = diff;
2070 	    iwd->pos = ascent + diff;
2071 	    break;
2072 	  case imagealign_InlineDown:
2073 	    if (descent < iwd->height - ascent)
2074 	      descent = iwd->height - ascent;
2075 	    iwd->pos = ascent;
2076 	    break;
2077 	  case imagealign_MarginLeft:
2078 	  case imagealign_MarginRight:
2079 	    /* iwd->pos already set */
2080 	    break;
2081 	  }
2082 	}
2083       }
2084     }
2085     if (superscent+ascent+descent == 0) {
2086       fontref_t *font = &cutwin->font.gc[lineattr];
2087       ascent = font->ascent;
2088       descent = font->descent;
2089     }
2090     thisline->height = superscent+ascent+descent;
2091     thisline->off = superscent+ascent;
2092 
2093     switch (cutwin->font.gc[lineattr].justify) {
2094     case stylehint_just_LeftRight:
2095       if (prevflags==lineflag_Wrapped && tmpw>1) {
2096 	/* full-justify (but only wrapped lines) */
2097 	long extraspace, each;
2098 	extraspace = (textwin_w - rightindent) - widthsofar;
2099 	each = extraspace / (tmpw-1);
2100 	extraspace -= (each*(tmpw-1));
2101 	for (jx=0; jx<extraspace; jx++) {
2102 	  thisline->wordlist[jx].width += (each+1);
2103 	}
2104 	for (; jx<tmpw-1; jx++) {
2105 	  thisline->wordlist[jx].width += each;
2106 	}
2107       }
2108       break;
2109     case stylehint_just_Centered:
2110       {
2111 	long extraspace;
2112 	extraspace = (textwin_w - rightindent) - widthsofar;
2113 	thisline->indent += extraspace / 2;
2114       }
2115       break;
2116     case stylehint_just_RightFlush:
2117       {
2118 	long extraspace;
2119 	extraspace = (textwin_w - rightindent) - widthsofar;
2120 	thisline->indent += extraspace;
2121       }
2122       break;
2123     case stylehint_just_LeftFlush:
2124     default:
2125       break;
2126     }
2127 
2128     thisline->leftindent = leftindent;
2129     if (leftindent && thisline->height < leftbelow) {
2130       leftbelow -= thisline->height;
2131       thisline->leftbelow = leftbelow;
2132     }
2133     else {
2134       leftbelow = 0;
2135       leftindent = 0;
2136       thisline->leftbelow = 0;
2137     }
2138     thisline->rightindent = rightindent;
2139     if (rightindent && thisline->height < rightbelow) {
2140       rightbelow -= thisline->height;
2141       thisline->rightbelow = rightbelow;
2142     }
2143     else {
2144       rightbelow = 0;
2145       rightindent = 0;
2146       thisline->rightbelow = 0;
2147     }
2148 
2149     startpos = ix;
2150   } /* done laying tmp lines */
2151 
2152   if (startpos == cutwin->numchars
2153     && (cutwin->numchars==0 || cutwin->charbuf[cutwin->numchars-1]=='\n')) {
2154     /* lay one more line! */
2155     lline_t *thisline;
2156     fontref_t *font;
2157 
2158     curattr = &(cutwin->stylelist[cutwin->numstyles-1]);
2159     font = &(cutwin->font.gc[curattr->attr]);
2160 
2161     thisline = (&cutwin->tmplinelist[tmpl]);
2162     thisline->flags = lineflag_Extra;
2163     thisline->height = font->ascent + font->descent;
2164     thisline->off = font->ascent;
2165     thisline->indent = cutwin->font.baseindent;
2166     tmpl++;
2167 
2168     thisline->leftindent = leftindent;
2169     if (leftindent && thisline->height < leftbelow) {
2170       leftbelow -= thisline->height;
2171       thisline->leftbelow = leftbelow;
2172     }
2173     else {
2174       thisline->leftbelow = 0;
2175     }
2176     thisline->rightindent = rightindent;
2177     if (rightindent && thisline->height < rightbelow) {
2178       rightbelow -= thisline->height;
2179       thisline->rightbelow = rightbelow;
2180     }
2181     else {
2182       thisline->rightbelow = 0;
2183     }
2184 
2185     thisline->wordlist = (word_t *)malloc(sizeof(word_t));
2186     thisline->numwords = 0;
2187     thisline->pos = startpos;
2188     thisline->posend = startpos;
2189   }
2190 
2191   /*printf("laid %d tmplines, and startpos now %d (delta %d)\n", tmpl, startpos, dirtydelta);*/
2192 
2193   for (lx=overline; lx<cutwin->numlines && cutwin->linelist[lx].pos < startpos-cutwin->dirtydelta; lx++);
2194   if (lx==cutwin->numlines-1 && (cutwin->linelist[lx].flags & lineflag_Extra)) {
2195     /* account for the extra line */
2196     lx++;
2197   }
2198   overlineend = lx;
2199 
2200   /*printf("overwrite area is lines [%d..%d) (of %d); replacing with %d lines\n", overline, overlineend, numlines, tmpl);*/
2201 
2202   slapover(cutwin, tmpl, overline, overlineend);
2203 
2204   lastline = overline+tmpl; /* re-cache value */
2205   needwholeredraw = FALSE;
2206 
2207   /* diddle scroll stuff */
2208   if (cutwin->scrollpos <= cutwin->dirtybeg) {
2209     /* disturbance is at or below the screen-top -- do nothing */
2210   }
2211   else if (cutwin->scrollpos >= startpos-cutwin->dirtydelta) {
2212     /* disturbance is off top of screen -- adjust so that no difference
2213        is visible. */
2214     cutwin->scrollpos += cutwin->dirtydelta;
2215     cutwin->scrollline += (overline-overlineend) - tmpl;
2216     /* the lineoffsetlist therefore doesn't need to be changed,
2217        theoretically. */
2218   }
2219   else {
2220     /* The disturbance spans the screen-top, which is annoying. */
2221     cutwin->scrollpos += cutwin->dirtydelta; /* kind of strange, but
2222 						shouldn't cause trouble */
2223     if (cutwin->scrollpos >= cutwin->numchars)
2224       cutwin->scrollpos = cutwin->numchars-1;
2225     if (cutwin->scrollpos < 0)
2226       cutwin->scrollpos = 0;
2227     cutwin->scrollline = find_line_by_pos(cutwin, cutwin->scrollpos,
2228       cutwin->scrollline);
2229     needwholeredraw = TRUE;
2230   }
2231 
2232   /* rebuild lineoffsetlist */
2233   if (needwholeredraw) {
2234     lx = cutwin->scrollline;
2235   }
2236   else {
2237     lx = overline;
2238     if (lx < cutwin->scrollline)
2239       lx = cutwin->scrollline;
2240   }
2241   readd_lineheights(cutwin, lx);
2242 
2243   /* clean up afterwards. */
2244   cutwin->dirtybeg = -1;
2245   cutwin->dirtyend = -1;
2246   cutwin->dirtydelta = 0;
2247 
2248   if (needwholeredraw) {
2249     redrawtext(cutwin, cutwin->scrollline, -1, -1);
2250   }
2251   else if (tmpl == overlineend-overline) {
2252     redrawtext(cutwin, overline, tmpl, tmpl);
2253   }
2254   else {
2255     if (overlineend > cutwin->numlines)
2256       redrawtext(cutwin, overline, -1, overlineend-overline);
2257     else
2258       redrawtext(cutwin, overline, -1, cutwin->numlines-overline);
2259   }
2260 
2261   flip_selection(cutwin, cutwin->lastdotpos, cutwin->lastdotlen);
2262 
2263   xweg_adjust_scrollbar(&cutwin->scrollbar, cutwin->numlines,
2264     cutwin->scrollline, cutwin->linesonpage);
2265 }
2266 
xgc_buf_scrollto(window_textbuffer_t * cutwin,int op)2267 void xgc_buf_scrollto(window_textbuffer_t *cutwin, int op)
2268 {
2269   scroll_to(cutwin, op);
2270 }
2271 
xgc_buf_scroll(window_textbuffer_t * cutwin,int op)2272 void xgc_buf_scroll(window_textbuffer_t *cutwin, int op)
2273 {
2274   long total, val;
2275 
2276   switch (op) {
2277   case op_UpLine:
2278     scroll_to(cutwin, cutwin->scrollline-1);
2279     break;
2280   case op_DownLine:
2281     scroll_to(cutwin, cutwin->scrollline+1);
2282     break;
2283   case op_UpPage:
2284     total = 0;
2285     for (val = cutwin->scrollline; val > 0; val--) {
2286       total += cutwin->linelist[val-1].height;
2287       if (total > cutwin->textwin_h) {
2288 	val++;
2289 	break;
2290       }
2291     }
2292     scroll_to(cutwin, val);
2293     break;
2294   case op_DownPage:
2295     scroll_to(cutwin, cutwin->scrollline+(cutwin->linesonpage-1));
2296     break;
2297   case op_ToTop:
2298     scroll_to(cutwin, 0);
2299     break;
2300   case op_ToBottom:
2301     scroll_to(cutwin, cutwin->numlines);
2302     break;
2303   }
2304 }
2305 
xgc_buf_movecursor(window_textbuffer_t * cutwin,int op)2306 void xgc_buf_movecursor(window_textbuffer_t *cutwin, int op)
2307 {
2308   long pos;
2309 
2310   switch (op) {
2311   case op_BackChar:
2312     if (cutwin->dotlen) {
2313       cutwin->dotlen = 0;
2314     }
2315     else {
2316       collapse_dot(cutwin);
2317       if (cutwin->dotpos > 0)
2318 	cutwin->dotpos--;
2319     }
2320     break;
2321   case op_ForeChar:
2322     if (cutwin->dotlen) {
2323       collapse_dot(cutwin);
2324     }
2325     else {
2326       collapse_dot(cutwin);
2327       if (cutwin->dotpos < cutwin->numchars)
2328 	cutwin->dotpos++;
2329     }
2330     break;
2331   case op_BackWord:
2332     collapse_dot(cutwin);
2333     cutwin->dotpos = back_to_nonwhite(cutwin, cutwin->dotpos);
2334     cutwin->dotpos = back_to_white(cutwin, cutwin->dotpos);
2335     break;
2336   case op_ForeWord:
2337     collapse_dot(cutwin);
2338     cutwin->dotpos = fore_to_nonwhite(cutwin, cutwin->dotpos);
2339     cutwin->dotpos = fore_to_white(cutwin, cutwin->dotpos);
2340     break;
2341   case op_BeginLine:
2342     if (cutwin->dotlen) {
2343       cutwin->dotlen = 0;
2344     }
2345     else {
2346       if (cutwin->buffer && cutwin->dotpos >= cutwin->inputfence)
2347 	cutwin->dotpos = cutwin->inputfence;
2348       else {
2349 	pos = cutwin->dotpos;
2350 	while (pos > 0 && cutwin->charbuf[pos-1] != '\n')
2351 	  pos--;
2352 	cutwin->dotpos = pos;
2353       }
2354     }
2355     break;
2356   case op_EndLine:
2357     if (cutwin->dotlen) {
2358       collapse_dot(cutwin);
2359     }
2360     else {
2361       if (cutwin->buffer && cutwin->dotpos >= cutwin->inputfence)
2362 	cutwin->dotpos = cutwin->numchars;
2363       else {
2364 	pos = cutwin->dotpos;
2365 	while (pos < cutwin->numchars && cutwin->charbuf[pos] != '\n')
2366 	  pos++;
2367 	cutwin->dotpos = pos;
2368       }
2369     }
2370     break;
2371   }
2372   win_textbuffer_layout(cutwin);
2373 }
2374 
xgc_buf_getchar(window_textbuffer_t * cutwin,int ch)2375 void xgc_buf_getchar(window_textbuffer_t *cutwin, int ch)
2376 {
2377   if (cutwin->owner->char_request) {
2378     glui32 key = ch;
2379     eventloop_setevent(evtype_CharInput, cutwin->owner, key, 0);
2380     cutwin->owner->char_request = FALSE;
2381   }
2382 }
2383 
xgc_buf_insert(window_textbuffer_t * cutwin,int ch)2384 void xgc_buf_insert(window_textbuffer_t *cutwin, int ch)
2385 {
2386   char realch;
2387 
2388   /* ###### not perfect -- should be all typable chars */
2389   if (ch < 32 || ch >= 127)
2390     ch = ' ';
2391 
2392   realch = ch;
2393 
2394   if (cutwin->dotpos < cutwin->inputfence) {
2395     cutwin->dotpos = cutwin->numchars;
2396     cutwin->dotlen = 0;
2397     win_textbuffer_add(cutwin, ch, cutwin->dotpos);
2398   }
2399   else {
2400     win_textbuffer_replace(cutwin, cutwin->dotpos, cutwin->dotlen, &realch, 1);
2401   }
2402 
2403   win_textbuffer_layout(cutwin);
2404   win_textbuffer_end_visible(cutwin);
2405 }
2406 
xgc_buf_delete(window_textbuffer_t * cutwin,int op)2407 void xgc_buf_delete(window_textbuffer_t *cutwin, int op)
2408 {
2409   long pos;
2410 
2411   if (cutwin->dotpos < cutwin->inputfence)
2412     return;
2413 
2414   if (cutwin->dotlen != 0 && (op == op_BackChar || op == op_ForeChar)) {
2415     win_textbuffer_replace(cutwin, cutwin->dotpos, cutwin->dotlen, "", 0);
2416     win_textbuffer_layout(cutwin);
2417     return;
2418   }
2419 
2420   collapse_dot(cutwin);
2421 
2422   switch (op) {
2423   case op_BackChar:
2424     if (cutwin->dotpos <= cutwin->inputfence)
2425       return;
2426     win_textbuffer_replace(cutwin, cutwin->dotpos-1, 1, "", 0);
2427     break;
2428   case op_ForeChar:
2429     if (cutwin->dotpos < cutwin->inputfence || cutwin->dotpos >= cutwin->numchars)
2430       return;
2431     win_textbuffer_replace(cutwin, cutwin->dotpos, 1, "", 0);
2432     break;
2433   case op_BackWord:
2434     pos = back_to_nonwhite(cutwin, cutwin->dotpos);
2435     pos = back_to_white(cutwin, pos);
2436     if (pos < cutwin->inputfence)
2437       pos = cutwin->inputfence;
2438     if (pos >= cutwin->dotpos)
2439       return;
2440     win_textbuffer_replace(cutwin, pos, cutwin->dotpos-pos, "", 0);
2441     break;
2442   case op_ForeWord:
2443     pos = fore_to_nonwhite(cutwin, cutwin->dotpos);
2444     pos = fore_to_white(cutwin, pos);
2445     if (pos < cutwin->inputfence)
2446       pos = cutwin->inputfence;
2447     if (pos <= cutwin->dotpos)
2448       return;
2449     win_textbuffer_replace(cutwin, cutwin->dotpos, pos-cutwin->dotpos, "", 0);
2450     break;
2451   }
2452   win_textbuffer_layout(cutwin);
2453 }
2454 
xgc_buf_cutbuf(window_textbuffer_t * cutwin,int op)2455 void xgc_buf_cutbuf(window_textbuffer_t *cutwin, int op)
2456 {
2457   char *cx;
2458   long num;
2459   long tmppos;
2460 
2461   if (op != op_Copy) {
2462     if (!cutwin->buffer) {
2463       xmsg_set_message("You are not editing input in this window.", FALSE);
2464       return;
2465     }
2466   }
2467 
2468   switch (op) {
2469   case op_Copy:
2470     if (cutwin->dotlen) {
2471       xglk_store_scrap(cutwin->charbuf+cutwin->dotpos, cutwin->dotlen);
2472     }
2473     break;
2474   case op_Wipe:
2475     if (cutwin->dotlen) {
2476       xglk_store_scrap(cutwin->charbuf+cutwin->dotpos, cutwin->dotlen);
2477       if (cutwin->dotpos >= cutwin->inputfence) {
2478 	win_textbuffer_replace(cutwin, cutwin->dotpos, cutwin->dotlen, "", 0);
2479 	win_textbuffer_layout(cutwin);
2480       }
2481     }
2482     break;
2483   case op_Erase:
2484     if (cutwin->dotlen) {
2485       if (cutwin->dotpos >= cutwin->inputfence) {
2486 	win_textbuffer_replace(cutwin, cutwin->dotpos, cutwin->dotlen, "", 0);
2487 	win_textbuffer_layout(cutwin);
2488       }
2489     }
2490     break;
2491   case op_Kill:
2492     if (cutwin->dotpos < cutwin->inputfence) {
2493       /* maybe extend to end-of-line and copy? */
2494       break;
2495     }
2496     cutwin->dotlen = cutwin->numchars-cutwin->dotpos;
2497     xglk_store_scrap(cutwin->charbuf+cutwin->dotpos, cutwin->dotlen);
2498     win_textbuffer_replace(cutwin, cutwin->dotpos, cutwin->dotlen, "", 0);
2499     win_textbuffer_layout(cutwin);
2500     break;
2501   case op_Yank:
2502     collapse_dot(cutwin);
2503     if (cutwin->dotpos < cutwin->inputfence)
2504       cutwin->dotpos = cutwin->numchars;
2505     xglk_fetch_scrap(&cx, &num);
2506     xglk_strip_garbage(cx, num);
2507     if (cx && num) {
2508       tmppos = cutwin->dotpos;
2509       win_textbuffer_replace(cutwin, tmppos, 0, cx, num);
2510       cutwin->dotpos = tmppos+num;
2511       cutwin->dotlen = 0;
2512       free(cx);
2513     }
2514     win_textbuffer_layout(cutwin);
2515     break;
2516   case op_YankReplace:
2517     xglk_fetch_scrap(&cx, &num);
2518     xglk_strip_garbage(cx, num);
2519     if (cx && num) {
2520       if (cutwin->dotpos < cutwin->inputfence) {
2521 	cutwin->dotpos = cutwin->numchars;
2522 	cutwin->dotlen = 0;
2523       }
2524       tmppos = cutwin->dotpos;
2525       win_textbuffer_replace(cutwin, tmppos, cutwin->dotlen, cx, num);
2526       cutwin->dotpos = tmppos+num;
2527       cutwin->dotlen = 0;
2528       free(cx);
2529     }
2530     win_textbuffer_layout(cutwin);
2531     break;
2532   case op_Untype:
2533     if (cutwin->numchars == cutwin->inputfence)
2534       break;
2535     cutwin->dotpos = cutwin->inputfence;
2536     cutwin->dotlen = cutwin->numchars-cutwin->inputfence;
2537     xglk_store_scrap(cutwin->charbuf+cutwin->dotpos, cutwin->dotlen);
2538     win_textbuffer_replace(cutwin, cutwin->dotpos, cutwin->dotlen, "", 0);
2539     win_textbuffer_layout(cutwin);
2540     break;
2541 
2542   }
2543 }
2544 
xgc_buf_history(window_textbuffer_t * cutwin,int op)2545 void xgc_buf_history(window_textbuffer_t *cutwin, int op)
2546 {
2547   long pos, len;
2548 
2549   switch (op) {
2550   case op_BackLine:
2551     if (cutwin->historypos > 0) {
2552 #ifdef OLDSTYLEHISTORY
2553       if (cutwin->dotpos < cutwin->inputfence) {
2554 	cutwin->dotpos = cutwin->numchars;
2555 	cutwin->dotlen = 0;
2556       }
2557       cutwin->historypos--;
2558       pos = cutwin->dotpos;
2559       win_textbuffer_replace(cutwin, pos, cutwin->dotlen, cutwin->history[cutwin->historypos].str, cutwin->history[cutwin->historypos].len);
2560       cutwin->dotpos = pos;
2561       cutwin->dotlen = cutwin->history[cutwin->historypos].len;
2562 #else
2563       cutwin->historypos--;
2564       win_textbuffer_replace(cutwin, cutwin->inputfence, cutwin->numchars-cutwin->inputfence,
2565 	cutwin->history[cutwin->historypos].str, cutwin->history[cutwin->historypos].len);
2566       cutwin->dotpos = cutwin->inputfence + cutwin->history[cutwin->historypos].len;
2567       cutwin->dotlen = 0;
2568 #endif
2569       win_textbuffer_layout(cutwin);
2570     }
2571     break;
2572   case op_ForeLine:
2573     if (cutwin->historypos < cutwin->historynum) {
2574 #ifdef OLDSTYLEHISTORY
2575       if (cutwin->dotpos < cutwin->inputfence) {
2576 	cutwin->dotpos = cutwin->numchars;
2577 	cutwin->dotlen = 0;
2578       }
2579       cutwin->historypos++;
2580       if (cutwin->historypos < cutwin->historynum) {
2581 	pos = cutwin->dotpos;
2582 	win_textbuffer_replace(cutwin, cutwin->dotpos, cutwin->dotlen, cutwin->history[cutwin->historypos].str, cutwin->history[cutwin->historypos].len);
2583 	cutwin->dotpos = pos;
2584 	cutwin->dotlen = cutwin->history[cutwin->historypos].len;
2585       }
2586       else {
2587 	pos = cutwin->dotpos;
2588 	win_textbuffer_replace(cutwin, cutwin->dotpos, cutwin->dotlen, "", 0);
2589 	cutwin->dotpos = pos;
2590 	cutwin->dotlen = 0;
2591       }
2592 #else
2593       cutwin->historypos++;
2594       if (cutwin->historypos < cutwin->historynum) {
2595 	win_textbuffer_replace(cutwin, cutwin->inputfence, cutwin->numchars-cutwin->inputfence,
2596 	  cutwin->history[cutwin->historypos].str, cutwin->history[cutwin->historypos].len);
2597 	cutwin->dotpos = cutwin->inputfence + cutwin->history[cutwin->historypos].len;
2598 	cutwin->dotlen = 0;
2599       }
2600       else {
2601 	win_textbuffer_replace(cutwin, cutwin->inputfence, cutwin->numchars-cutwin->inputfence, "", 0);
2602 	cutwin->dotpos = cutwin->inputfence;
2603 	cutwin->dotlen = 0;
2604       }
2605 #endif
2606       win_textbuffer_layout(cutwin);
2607     }
2608   }
2609 }
2610 
xgc_buf_enter(window_textbuffer_t * cutwin,int op)2611 void xgc_buf_enter(window_textbuffer_t *cutwin, int op)
2612 {
2613   long ix, len, len2;
2614   long buflen;
2615   char *buffer;
2616   gidispatch_rock_t inarrayrock;
2617 
2618   if (op != op_Enter)
2619     return;
2620 
2621   if (!cutwin->buffer)
2622     return;
2623 
2624   buffer = cutwin->buffer;
2625   buflen = cutwin->buflen;
2626   inarrayrock = cutwin->inarrayrock;
2627 
2628   win_textbuffer_set_style_text(cutwin, cutwin->originalattr);
2629 
2630   len = cutwin->numchars - cutwin->inputfence;
2631   len2 = 0;
2632   for (ix=0; ix < len && len2 < buflen; ix++) {
2633     unsigned char ch = cutwin->charbuf[cutwin->inputfence+ix];
2634     buffer[len2] = ch;
2635     len2++;
2636   }
2637 
2638   len = cutwin->numchars - cutwin->inputfence;
2639   if (len) {
2640     /* add to history */
2641     if (cutwin->historynum==cutwin->historylength) {
2642       free(cutwin->history[0].str);
2643       memmove(&cutwin->history[0], &cutwin->history[1], (cutwin->historylength-1) * (sizeof(histunit)));
2644     }
2645     else
2646       cutwin->historynum++;
2647     cutwin->history[cutwin->historynum-1].str = malloc(len*sizeof(char));
2648     memmove(cutwin->history[cutwin->historynum-1].str, cutwin->charbuf+cutwin->inputfence, len*sizeof(char));
2649     cutwin->history[cutwin->historynum-1].len = len;
2650   }
2651 
2652   if (cutwin->owner->echostr) {
2653     window_t *oldwin = cutwin->owner;
2654     /*gli_stream_echo_line(cutwin->owner->echostr,
2655       cutwin->charbuf+cutwin->inputfence, len*sizeof(char));*/
2656     gli_stream_echo_line(cutwin->owner->echostr,
2657       buffer, len2*sizeof(char));
2658   }
2659 
2660   win_textbuffer_add(cutwin, '\n', -1);
2661   cutwin->dotpos = cutwin->numchars;
2662   cutwin->dotlen = 0;
2663   cutwin->inputfence = 0;
2664   win_textbuffer_layout(cutwin);
2665 
2666   eventloop_setevent(evtype_LineInput, cutwin->owner, len2, 0);
2667   cutwin->owner->line_request = FALSE;
2668   cutwin->buffer = NULL;
2669   cutwin->buflen = 0;
2670 
2671   if (gli_unregister_arr) {
2672     (*gli_unregister_arr)(buffer, buflen, "&+#!Cn", inarrayrock);
2673   }
2674 }
2675 
win_textbuffer_line_cancel(window_textbuffer_t * cutwin,event_t * ev)2676 static void win_textbuffer_line_cancel(window_textbuffer_t *cutwin,
2677   event_t *ev)
2678 {
2679   long ix, len, len2;
2680   long buflen;
2681   char *buffer;
2682   gidispatch_rock_t inarrayrock;
2683 
2684   /* same as xgc_buf_enter(), but skip the unnecessary stuff.
2685       We don't need to add to history, collapse the dot, win_textbuffer_layout,
2686       trim the buffer, or shrink the status window. */
2687 
2688   if (!cutwin->buffer)
2689     return;
2690 
2691   buffer = cutwin->buffer;
2692   buflen = cutwin->buflen;
2693   inarrayrock = cutwin->inarrayrock;
2694 
2695   len = cutwin->numchars - cutwin->inputfence;
2696   len2 = 0;
2697   for (ix=0; ix < len && len2 < buflen; ix++) {
2698     unsigned char ch = cutwin->charbuf[cutwin->inputfence+ix];
2699     buffer[len2] = ch;
2700     len2++;
2701   }
2702 
2703   len = cutwin->numchars - cutwin->inputfence;
2704   /*if (len) {
2705     win_textbuffer_replace(cutwin->inputfence, len, "", 0);
2706     cutwin->dotpos = cutwin->numchars;
2707     cutwin->dotlen = 0;
2708     win_textbuffer_layout(cutwin);
2709     }*/
2710 
2711   win_textbuffer_set_style_text(cutwin, cutwin->originalattr);
2712 
2713   if (cutwin->owner->echostr) {
2714     window_t *oldwin = cutwin->owner;
2715     /*gli_stream_echo_line(cutwin->owner->echostr,
2716       cutwin->charbuf+cutwin->inputfence, len*sizeof(char));*/
2717     gli_stream_echo_line(cutwin->owner->echostr,
2718       buffer, len2*sizeof(char));
2719   }
2720 
2721   win_textbuffer_add(cutwin, '\n', -1);
2722   cutwin->dotpos = cutwin->numchars;
2723   cutwin->dotlen = 0;
2724   cutwin->inputfence = 0;
2725   win_textbuffer_layout(cutwin);
2726 
2727   /* create event, and set everything blank. */
2728   ev->type = evtype_LineInput;
2729   ev->val1 = len2;
2730   ev->val2 = 0;
2731   ev->win = cutwin->owner;
2732   cutwin->owner->line_request = FALSE;
2733   cutwin->buffer = NULL;
2734   cutwin->buflen = 0;
2735 
2736   if (gli_unregister_arr) {
2737     (*gli_unregister_arr)(buffer, buflen, "&+#!Cn", inarrayrock);
2738   }
2739 }
2740 
win_textbuffer_trim_buffer(window_textbuffer_t * cutwin)2741 void win_textbuffer_trim_buffer(window_textbuffer_t *cutwin)
2742 {
2743   if (cutwin->numchars > prefs.buffersize + prefs.bufferslack) {
2744     long lx;
2745     for (lx=0; lx<cutwin->numlines; lx++)
2746       if (cutwin->linelist[lx].pos > (cutwin->numchars-prefs.buffersize))
2747 	break;
2748     if (lx) {
2749       win_textbuffer_delete_start(cutwin, lx);
2750     }
2751   }
2752 }
2753 
2754