1 #include <stdio.h>
2 #include <ctype.h>
3 #include <X11/Xlib.h>
4 #include <X11/Xutil.h>
5 
6 #include "ztypes.h"
7 #include "xio.h"
8 
9 static char *charbuf;
10 static long numchars;
11 static long char_size;
12 
13 typedef struct style_t {
14     int attr; /* flags are REVERSE, BOLD, EMPHASIS, FIXED_FONT */
15     long pos; /* position this style starts at */
16 } style;
17 
18 static style *stylelist;
19 static long numstyles;
20 static long styles_size;
21 
22 typedef struct word_t {
23     long pos, len;
24     long width; /* in pixels */
25     int attr;
26 
27     long *letterpos; /* if not NULL, an array[0..len] of pixel offsets from wordpos; */
28 } word;
29 
30 #define lineflag_Wrapped (1) /* line is a wrap or split from previous line */
31 #define lineflag_Extra (2) /* the magic extra line on the end */
32 
33 typedef struct line_t {
34     long pos; /* line starts here */
35     long posend; /* number of chars. May not be exactly to start of next line, because it won't include the newline or space that ends the line. */
36     word *wordlist;
37     long numwords;
38     int flags;
39 } lline;
40 
41 static lline *linelist;
42 static long numlines;
43 static long lines_size;
44 
45 static lline *tmplinelist;
46 static long tmplines_size;
47 
48 static long scrollpos; /* character position at top of screen */
49 static long scrollline; /* number of line at top of screen, after xtext_layout() */
50 static long lastlineseen; /* last line read before more stuff was output. (-1) to indicate all lines read. */
51 static long dotpos, dotlen; /* dotpos is in [0..numchars] */
52 static long lastdotpos = (-1), lastdotlen = 0; /* cached values -- fiddled inside xtext_layout() */
53 
54 static long dirtybeg, dirtyend; /* mark the limits of what needs to be laid out, [) format */
55 static long dirtydelta; /* how much the dirty area has grown (or shrunk) */
56 static long startlay; /* pos of the char that starts the first laid-out line. */
57 
58 static int textwin_x, textwin_y, textwin_w, textwin_h;
59 static int scrollwin_x, scrollwin_y, scrollwin_w, scrollwin_h;
60 static int scrollel_top, scrollel_bot;
61 
62 typedef struct histunit {
63     char *str;
64     int len;
65 } histunit;
66 static int historynum, historypos;
67 static histunit *history;
68 
69 /* these are for xtext editing */
70 static int buflen;
71 static char *buffer;
72 static int *readpos;
73 static long inputfence;
74 static int *killflag;
75 static int originalattr;
76 
77 #define collapse_dot()  (dotpos += dotlen, dotlen = 0)
78 #define SIDEMARGIN (4)
79 #define BARENDHEIGHT (12)
80 #define BARWIDTH (17)
81 #define BAREXTRA (4)
82 
83 static XPoint polydot[3];
84 static long linesperpage;
85 
86 #ifdef __STDC__
87 static void redrawtext(long beg, long num, int clearnum);
88 static void flip_selection(long dpos, long dlen);
89 static void find_loc_by_pos(long pos, int *xposret, int *yposret);
90 static long find_pos_by_loc(int xpos, int ypos);
91 static long find_line_by_pos(long pos, long guessline);
92 static void measure_word(lline *curline, word *curword);
93 static void adjust_elevator();
94 void xtext_delete_start(long num);
95 #else
96 static void redrawtext();
97 static void flip_selection();
98 static void find_loc_by_pos();
99 static long find_pos_by_loc();
100 static long find_line_by_pos();
101 static void measure_word();
102 static void adjust_elevator();
103 void xtext_delete_start();
104 #endif
105 
106 
107 #ifdef __STDC__
xtext_init()108 void xtext_init()
109 #else
110 void xtext_init()
111 #endif
112 {
113     char_size = 256;
114     charbuf = (char *)malloc(sizeof(char) * char_size);
115     numchars = 0;
116 
117     styles_size = 8;
118     stylelist = (style *)malloc(sizeof(style) * styles_size);
119     numstyles = 1;
120     stylelist[0].pos = 0;
121     stylelist[0].attr = 0; /* NORMAL style */
122 
123     lines_size = 8;
124     linelist = (lline *)malloc(sizeof(lline) * lines_size);
125     numlines = 0;
126 
127     tmplines_size = 8;
128     tmplinelist = (lline *)malloc(sizeof(lline) * tmplines_size);
129 
130     historynum = 0;
131     history = (histunit *)malloc(prefs.historylength * sizeof(histunit));
132 
133     scrollpos = 0;
134     scrollline = 0;
135     startlay = 0; /* not yet used */
136 
137     dirtybeg = 0;
138     dirtyend = 0;
139     dirtydelta = 0;
140 
141     dotpos = 0;
142     dotlen = 0;
143 
144     polydot[0].x = 0;
145     polydot[0].y = 0;
146     polydot[1].x = SIDEMARGIN;
147     polydot[1].y = 5;
148     polydot[2].x = -2*SIDEMARGIN;
149     polydot[2].y = 0;
150 
151     scrollel_top = (-1); /* indicate elevator is not there */
152     scrollel_bot = (-1);
153 
154     lastlineseen = 0;
155 }
156 
157 #ifdef __STDC__
xtext_clear_window()158 void xtext_clear_window()
159 #else
160 void xtext_clear_window()
161 #endif
162 {
163     xtext_delete_start(numlines);
164 }
165 
166 #ifdef __STDC__
xtext_resize(int xpos,int ypos,int width,int height)167 void xtext_resize(int xpos, int ypos, int width, int height)
168 #else
169 void xtext_resize(xpos, ypos, width, height)
170 int xpos;
171 int ypos;
172 int width;
173 int height;
174 #endif
175 {
176     scrollwin_x = xpos;
177     scrollwin_w = BARWIDTH;
178     scrollwin_y = ypos+BARENDHEIGHT;
179     scrollwin_h = height-2*BARENDHEIGHT;
180     textwin_x = xpos+scrollwin_w+SIDEMARGIN+prefs.marginx;
181     textwin_y = ypos;
182     textwin_w = width-2*SIDEMARGIN-scrollwin_w-2*prefs.marginx;
183     textwin_h = height;
184 
185     dirtybeg = 0;
186     dirtyend = numchars;
187     dirtydelta = 0;
188 
189     linesperpage = height / lineheight;
190 
191     xtext_layout();
192 }
193 
194 #ifdef __STDC__
xtext_redraw()195 void xtext_redraw()
196 #else
197 void xtext_redraw()
198 #endif
199 {
200     XPoint poly[3];
201 
202     /* this assumes that an exposure event will not come in between a data update and an xtext_layout call. (unless the exposure event itself forces xtext_layout first?) */
203     /*flip_selection(dotpos, dotlen);*/
204     redrawtext(0, -1, -1);
205     flip_selection(dotpos, dotlen);
206 
207     poly[0].x = scrollwin_x + scrollwin_w/2;
208     poly[0].y = textwin_y + 1;
209     poly[1].x = scrollwin_x + 0;
210     poly[1].y = scrollwin_y - 2;
211     poly[2].x = scrollwin_x + scrollwin_w - 1;
212     poly[2].y = scrollwin_y - 2;
213     XFillPolygon(xiodpy, xiowin, gcgrey, poly, 3, Convex, CoordModeOrigin);
214 
215     poly[0].x = scrollwin_x + scrollwin_w/2;
216     poly[0].y = textwin_y + textwin_h - 1;
217     poly[1].x = scrollwin_x + 0;
218     poly[1].y = scrollwin_y + scrollwin_h + 2;
219     poly[2].x = scrollwin_x + scrollwin_w - 1;
220     poly[2].y = scrollwin_y + scrollwin_h + 2;
221     XFillPolygon(xiodpy, xiowin, gcgrey, poly, 3, Convex, CoordModeOrigin);
222 
223     XDrawLine(xiodpy, xiowin, gcblack, scrollwin_x+scrollwin_w, textwin_y, scrollwin_x+scrollwin_w, textwin_h);
224     scrollel_top = (-1);
225     scrollel_bot = (-1);
226     XFillRectangle(xiodpy, xiowin, gcgrey, scrollwin_x, scrollwin_y, scrollwin_w, scrollwin_h);
227     adjust_elevator();
228 }
229 
230 #ifdef __STDC__
back_to_white(long pos)231 static long back_to_white(long pos)
232 #else
233 static long back_to_white(pos)
234 long pos;
235 #endif
236 {
237     while (pos > 0 && charbuf[pos-1] != ' ' && charbuf[pos-1] != '\n')
238 	pos--;
239     return pos;
240 }
241 
242 #ifdef __STDC__
fore_to_white(long pos)243 static long fore_to_white(long pos)
244 #else
245 static long fore_to_white(pos)
246 long pos;
247 #endif
248 {
249     while (pos < numchars && charbuf[pos] != ' ' && charbuf[pos] != '\n')
250 	pos++;
251     return pos;
252 }
253 
254 #ifdef __STDC__
back_to_nonwhite(long pos)255 static long back_to_nonwhite(long pos)
256 #else
257 static long back_to_nonwhite(pos)
258 long pos;
259 #endif
260 {
261     while (pos > 0 && (charbuf[pos-1] == ' ' || charbuf[pos-1] == '\n'))
262 	pos--;
263     return pos;
264 }
265 
266 #ifdef __STDC__
fore_to_nonwhite(long pos)267 static long fore_to_nonwhite(long pos)
268 #else
269 static long fore_to_nonwhite(pos)
270 long pos;
271 #endif
272 {
273     while (pos < numchars && (charbuf[pos] == ' ' || charbuf[pos] == '\n'))
274 	pos++;
275     return pos;
276 }
277 
278 /* Coordinates are in screen lines. If num < 0, go to the end. clearnum is the number of lines to clear (may be to a notional line); if 0, don't clear at all; if -1, clear whole window. */
279 #ifdef __STDC__
redrawtext(long beg,long num,int clearnum)280 static void redrawtext(long beg, long num, int clearnum)
281 #else
282 static void redrawtext(beg, num, clearnum)
283 long beg;
284 long num;
285 int clearnum;
286 #endif
287 {
288     long lx, wx, end, clearend;
289     int ypos, ypos2, xpos;
290     lline *thisline;
291     word *thisword;
292 
293     if (num<0)
294 	end = numlines;
295     else {
296 	end = beg+num;
297 	if (end > numlines)
298 	    end = numlines;
299     }
300 
301     if (beg < scrollline)
302 	beg = scrollline;
303 
304     if (clearnum > 0) {
305 	clearend = beg+clearnum;
306 	ypos = textwin_y + (beg-scrollline) * lineheight;
307 	ypos2 = textwin_y + (clearend-scrollline) * lineheight;
308 	if (ypos2 > textwin_y+textwin_h) {
309 	    ypos2 = textwin_y+textwin_h;
310 	}
311 	if (ypos != ypos2)
312 	    XClearArea(xiodpy, xiowin, textwin_x-SIDEMARGIN, ypos, textwin_w+2*SIDEMARGIN, ypos2-ypos, FALSE);
313     }
314     else if (clearnum < 0) {
315 	ypos = textwin_y + (beg-scrollline) * lineheight;
316 	ypos2 = textwin_y+textwin_h;
317 	if (ypos != ypos2)
318 	    XClearArea(xiodpy, xiowin, textwin_x-SIDEMARGIN, ypos, textwin_w+2*SIDEMARGIN, ypos2-ypos, FALSE);
319     }
320 
321     for (lx=beg; lx<end; lx++) {
322 	thisline = (&linelist[lx]);
323 	ypos = textwin_y + (lx-scrollline) * lineheight;
324 	if (ypos + lineheight >= textwin_y + textwin_h)
325 	    break;
326 	xpos = textwin_x;
327 	for (wx=0; wx<thisline->numwords; wx++) {
328 	    thisword = thisline->wordlist+wx;
329 	    if (thisword->attr & REVERSE)
330 		XDrawImageString(xiodpy, xiowin, gcfont[thisword->attr], xpos, ypos+lineheightoff, charbuf+thisline->pos+thisword->pos, thisword->len);
331 	    else
332 		XDrawString(xiodpy, xiowin, gcfont[thisword->attr], xpos, ypos+lineheightoff, charbuf+thisline->pos+thisword->pos, thisword->len);
333 	    xpos += thisword->width;
334 	}
335     }
336 }
337 
338 #ifdef __STDC__
adjust_elevator()339 static void adjust_elevator()
340 #else
341 static void adjust_elevator()
342 #endif
343 {
344     long newtop, newbot;
345     int barheight = (scrollwin_h-2*BAREXTRA);
346 
347     if (numlines) {
348 	newtop = ((barheight*scrollline) / numlines) + BAREXTRA;
349 	newbot = ((barheight*(scrollline+linesperpage)) / numlines) + BAREXTRA;
350 	if (newtop < BAREXTRA)
351 	    newtop = BAREXTRA;
352 	if (newbot >= scrollwin_h-BAREXTRA)
353 	    newbot = scrollwin_h-BAREXTRA;
354     }
355     else {
356 	newtop = BAREXTRA;
357 	newbot = scrollwin_h-BAREXTRA;
358     }
359 
360     if (newtop == scrollel_top && newbot==scrollel_bot)
361 	return;
362 
363     if (scrollel_top != (-1)
364 	&& (scrollel_top >= newbot || newtop >= scrollel_bot)) {
365 	/* erase old completely */
366 	XFillRectangle(xiodpy, xiowin, gcgrey, scrollwin_x, scrollwin_y+scrollel_top, scrollwin_w, scrollel_bot-scrollel_top);
367 	scrollel_top = (-1);
368     }
369 
370     if (scrollel_top == (-1)) {
371 	/* redraw new completely */
372 	XDrawRectangle(xiodpy, xiowin, gcblack, scrollwin_x, scrollwin_y+newtop, scrollwin_w-1, (newbot-newtop)-1);
373 	XFillRectangle(xiodpy, xiowin, gcwhite, scrollwin_x+1, scrollwin_y+newtop+1, scrollwin_w-2, (newbot-newtop)-2);
374 	scrollel_top = newtop;
375 	scrollel_bot = newbot;
376 	return;
377     }
378 
379     /* ok, the old and new overlap */
380     if (newtop < scrollel_top) {
381 	XFillRectangle(xiodpy, xiowin, gcwhite, scrollwin_x+1, scrollwin_y+newtop+1, scrollwin_w-2, scrollel_top-newtop);
382     }
383     else if (newtop > scrollel_top) {
384 	XFillRectangle(xiodpy, xiowin, gcgrey, scrollwin_x, scrollwin_y+scrollel_top, scrollwin_w, newtop-scrollel_top);
385     }
386 
387     if (newbot > scrollel_bot) {
388 	XFillRectangle(xiodpy, xiowin, gcwhite, scrollwin_x+1, scrollwin_y+scrollel_bot-1, scrollwin_w-2, newbot-scrollel_bot);
389     }
390     else if (newbot < scrollel_bot) {
391 	XFillRectangle(xiodpy, xiowin, gcgrey, scrollwin_x, scrollwin_y+newbot, scrollwin_w, scrollel_bot-newbot);
392     }
393 
394     XDrawRectangle(xiodpy, xiowin, gcblack, scrollwin_x, scrollwin_y+newtop, scrollwin_w-1, (newbot-newtop)-1);
395     scrollel_top = newtop;
396     scrollel_bot = newbot;
397 }
398 
399 #ifdef __STDC__
scroll_to(long newscrollline)400 static void scroll_to(long newscrollline)
401 #else
402 static void scroll_to(newscrollline)
403 long newscrollline;
404 #endif
405 {
406     long oldscrollline;
407 
408     if (newscrollline > numlines-2)
409 	newscrollline = numlines-2;
410     if (newscrollline < 0)
411 	newscrollline = 0;
412 
413     scrollpos = linelist[newscrollline].pos;
414     if (scrollline != newscrollline) {
415 	oldscrollline = scrollline;
416 	if (!xiobackstore
417 	    || oldscrollline + linesperpage <= newscrollline
418 	    || newscrollline + linesperpage <= oldscrollline) {
419 	    scrollline = newscrollline;
420 	    redrawtext(scrollline, -1, -1);
421 	    flip_selection(dotpos, dotlen);
422 	}
423 	else {
424 	    int ypos1, ypos2, yhgt;
425 	    flip_selection(dotpos, dotlen);
426 	    scrollline = newscrollline;
427 	    if (oldscrollline < newscrollline) {
428 		/* scroll down -- things move up */
429 		ypos1 = textwin_y + (newscrollline-oldscrollline) * lineheight;
430 		ypos2 = textwin_y + (0) * lineheight;
431 		yhgt = (linesperpage-(newscrollline-oldscrollline)) * lineheight;
432 		XCopyArea(xiodpy, xiowin, xiowin, gcblack, textwin_x-SIDEMARGIN, ypos1, textwin_w+2*SIDEMARGIN, yhgt, textwin_x-SIDEMARGIN, ypos2);
433 		redrawtext(linesperpage + oldscrollline, (newscrollline-oldscrollline), (newscrollline-oldscrollline));
434 	    }
435 	    else {
436 		/* scroll up -- things move down */
437 		ypos2 = textwin_y + (oldscrollline-newscrollline) * lineheight;
438 		ypos1 = textwin_y + (0) * lineheight;
439 		yhgt = (linesperpage-(oldscrollline-newscrollline)) * lineheight;
440 		XCopyArea(xiodpy, xiowin, xiowin, gcblack, textwin_x-SIDEMARGIN, ypos1, textwin_w+2*SIDEMARGIN, yhgt, textwin_x-SIDEMARGIN, ypos2);
441 		redrawtext(newscrollline, (oldscrollline-newscrollline), (oldscrollline-newscrollline));
442 	    }
443 	    flip_selection(dotpos, dotlen);
444 	}
445 	adjust_elevator();
446     }
447 }
448 
449 #ifdef __STDC__
refiddle_selection(long oldpos,long oldlen,long newpos,long newlen)450 static void refiddle_selection(long oldpos, long oldlen, long newpos, long newlen)
451 #else
452 static void refiddle_selection(oldpos, oldlen, newpos, newlen)
453 long oldpos;
454 long oldlen;
455 long newpos;
456 long newlen;
457 #endif
458 {
459     if (oldlen==0 || newlen==0 || oldpos<0 || newpos<0) {
460 	flip_selection(oldpos, oldlen);
461 	flip_selection(newpos, newlen);
462 	return;
463     }
464 
465     if (oldpos == newpos) {
466 	/* start at same place */
467 	if (oldlen < newlen) {
468 	    flip_selection(oldpos+oldlen, newlen-oldlen);
469 	}
470 	else if (newlen < oldlen) {
471 	    flip_selection(oldpos+newlen, oldlen-newlen);
472 	}
473 	return;
474     }
475     if (oldpos+oldlen == newpos+newlen) {
476 	/* end at same place */
477 	if (oldpos < newpos) {
478 	    flip_selection(oldpos, newpos-oldpos);
479 	}
480 	else if (newpos < oldpos) {
481 	    flip_selection(newpos, oldpos-newpos);
482 	}
483 	return;
484     }
485 
486     flip_selection(oldpos, oldlen);
487     flip_selection(newpos, newlen);
488 }
489 
490 #ifdef __STDC__
flip_selection(long dpos,long dlen)491 static void flip_selection(long dpos, long dlen)
492 #else
493 static void flip_selection(dpos, dlen)
494 long dpos;
495 long dlen;
496 #endif
497 {
498     int xpos, ypos;
499     int xpos2, ypos2;
500     long ybody, ybody2;
501 
502     if (dpos < 0) {
503 	return; /* dot hidden */
504     }
505 
506     if (dlen==0) {
507 	find_loc_by_pos(dpos, &xpos, &ypos);
508 	if (ypos < 0 || ypos+lineheight >= textwin_h) {
509 	    return;
510 	}
511 	polydot[0].x = textwin_x + xpos;
512 	polydot[0].y = textwin_y + ypos + lineheightoff;
513 	XFillPolygon(xiodpy, xiowin, gcflip, polydot, 3, Convex, CoordModePrevious);
514     }
515     else {
516 	find_loc_by_pos(dpos, &xpos, &ypos);
517 	find_loc_by_pos(dpos+dlen, &xpos2, &ypos2);
518 	if (ypos==ypos2) {
519 	    /* within one line */
520 	    if (xpos!=xpos2 && ypos>=0 && ypos+lineheight<textwin_h) {
521 		XFillRectangle(xiodpy, xiowin, gcflip, xpos+textwin_x, ypos+textwin_y, xpos2-xpos, lineheight);
522 	    }
523 	}
524 	else {
525 	    if (xpos < textwin_w && ypos>=0 && ypos+lineheight<textwin_h) {
526 		/* first partial line */
527 		XFillRectangle(xiodpy, xiowin, gcflip, xpos+textwin_x, ypos+textwin_y, textwin_w-xpos, lineheight);
528 	    }
529 	    ybody = ypos+lineheight;
530 	    ybody2 = ypos2;
531 	    if (ybody < ybody2 && ybody2>=0 && ybody+lineheight<textwin_h) {
532 		if (ybody < 0)
533 		    ybody = 0;
534 		if (ybody2+lineheight >= textwin_h)
535 		    ybody2 = textwin_h;
536 		/* main body */
537 		XFillRectangle(xiodpy, xiowin, gcflip, textwin_x, ybody+textwin_y, textwin_w, ybody2-ybody);
538 	    }
539 	    if (xpos2 && ypos2>=0 && ypos2+lineheight<textwin_h) {
540 		/* last partial line */
541 		XFillRectangle(xiodpy, xiowin, gcflip, textwin_x, ypos2+textwin_y, xpos2, lineheight);
542 	    }
543 	}
544     }
545 }
546 
547 /* push lines from tmplinelist[0..newnum) in place of linelist[oldbeg..oldend) */
548 #ifdef __STDC__
slapover(long newnum,long oldbeg,long oldend)549 static void slapover(long newnum, long oldbeg, long oldend)
550 #else
551 static void slapover(newnum, oldbeg, oldend)
552 long newnum;
553 long oldbeg;
554 long oldend;
555 #endif
556 {
557     long wx, lx;
558     long newnumlines;
559 
560     newnumlines = numlines-(oldend-oldbeg)+newnum;
561     if (newnumlines >= lines_size) {
562 	while (newnumlines >= lines_size)
563 	    lines_size *= 2;
564 	linelist = (lline *)realloc(linelist, sizeof(lline) * lines_size);
565     }
566 
567     /* clobber old */
568     for (lx=oldbeg; lx<oldend; lx++) {
569 	word *thisword;
570 	/* --- finalize word structure --- */
571 	for (wx=0, thisword=linelist[lx].wordlist;
572 	     wx<linelist[lx].numwords;
573 	     wx++, thisword++) {
574 	    if (thisword->letterpos) {
575 		free(thisword->letterpos);
576 	    }
577 	}
578 	free(linelist[lx].wordlist);
579 	linelist[lx].wordlist = NULL;
580     }
581 
582     if (oldend < numlines && newnumlines != numlines) {
583 	memmove(&linelist[oldend+(newnumlines-numlines)],
584 		&linelist[oldend],
585 		sizeof(lline) * (numlines-oldend));
586     }
587     /* ### adjust scrollline by difference too? */
588     numlines = newnumlines;
589 
590     if (newnum) {
591 	memcpy(&linelist[oldbeg],
592 	       &tmplinelist[0],
593 	       sizeof(lline) * (newnum));
594     }
595 }
596 
597 /* xpos, ypos are relative to textwin origin */
598 #ifdef __STDC__
find_pos_by_loc(int xpos,int ypos)599 static long find_pos_by_loc(int xpos, int ypos)
600 #else
601 static long find_pos_by_loc(xpos, ypos)
602 int xpos;
603 int ypos;
604 #endif
605 {
606     int ix;
607     long linenum;
608     long wx, atpos, newpos;
609     lline *curline;
610     word *curword;
611 
612     if (ypos < 0)
613 	linenum = (-1) - ((-1)-ypos / lineheight);
614     else
615 	linenum = ypos / lineheight;
616 
617     linenum += scrollline;
618 
619     if (linenum < 0)
620 	return 0;
621     if (linenum >= numlines)
622 	return numchars;
623 
624     curline = (&linelist[linenum]);
625     if (xpos < 0) {
626 	return curline->pos; /* beginning of line */
627     }
628     atpos = 0;
629     for (wx=0; wx<curline->numwords; wx++) {
630 	newpos = atpos + curline->wordlist[wx].width;
631 	if (xpos < newpos)
632 	    break;
633 	atpos = newpos;
634     }
635     if (wx==curline->numwords) {
636 	return curline->posend; /* end of line */
637     }
638 
639     xpos -= atpos; /* now xpos is relative to word beginning */
640     curword = (&curline->wordlist[wx]);
641     if (!curword->letterpos)
642 	measure_word(curline, curword);
643 
644     for (ix=0; ix<curword->len; ix++) {
645 	if (xpos <= (curword->letterpos[ix]+curword->letterpos[ix+1])/2)
646 	    break;
647     }
648     return curline->pos + curword->pos + ix;
649 }
650 
651 /* returns the last line such that pos >= line.pos. guessline is a guess to start searching at; -1 means end of file. Can return -1 if pos is before the start of the layout. */
652 #ifdef __STDC__
find_line_by_pos(long pos,long guessline)653 static long find_line_by_pos(long pos, long guessline)
654 #else
655 static long find_line_by_pos(pos, guessline)
656 long pos;
657 long guessline;
658 #endif
659 {
660     long lx;
661 
662     if (guessline < 0 || guessline >= numlines)
663 	guessline = numlines-1;
664 
665     if (guessline < numlines-1 && linelist[guessline].pos <= pos) {
666 	for (lx=guessline; lx<numlines; lx++) {
667 	    if (linelist[lx].pos > pos)
668 		break;
669 	}
670 	lx--;
671     }
672     else {
673 	for (lx=guessline; lx>=0; lx--) {
674 	    if (linelist[lx].pos <= pos)
675 		break;
676 	}
677     }
678 
679     return lx;
680 }
681 
682 /* returns values relative to textwin origin, at top of line. */
683 #ifdef __STDC__
find_loc_by_pos(long pos,int * xposret,int * yposret)684 static void find_loc_by_pos(long pos, int *xposret, int *yposret)
685 #else
686 static void find_loc_by_pos(pos, xposret, yposret)
687 long pos;
688 int *xposret;
689 int *yposret;
690 #endif
691 {
692     long lx;
693     long wx, atpos;
694     lline *curline;
695     word *curword;
696 
697     lx = find_line_by_pos(pos, -1);
698     if (lx < 0) {
699 	/* somehow before first line laid out */
700 	*xposret = 0;
701 	*yposret = (-scrollline) * lineheight;
702 	return;
703     }
704     curline = (&linelist[lx]);
705 
706     *yposret = (lx-scrollline) * lineheight;
707     atpos = 0;
708     for (wx=0; wx<curline->numwords; wx++) {
709 	if (curline->pos+curline->wordlist[wx].pos+curline->wordlist[wx].len >= pos)
710 	    break;
711 	atpos += curline->wordlist[wx].width;
712     }
713     if (wx==curline->numwords) {
714 	*xposret = atpos;
715 	return;
716     }
717 
718     curword = (&curline->wordlist[wx]);
719     if (!curword->letterpos)
720 	measure_word(curline, curword);
721 
722     atpos += curword->letterpos[pos - (curline->pos+curword->pos)];
723 
724     *xposret = atpos;
725 }
726 
727 #ifdef __STDC__
measure_word(lline * curline,word * curword)728 static void measure_word(lline *curline, word *curword)
729 #else
730 static void measure_word(curline, curword)
731 lline *curline;
732 word *curword;
733 #endif
734 {
735     int cx;
736     char *buf;
737     int direction;
738     int ascent, descent;
739     XCharStruct overall;
740     long *arr;
741 
742     if (curword->letterpos)
743 	free(curword->letterpos);
744 
745     arr = (long *)malloc(sizeof(long) * (curword->len+1));
746 
747     buf = charbuf+curline->pos+curword->pos;
748     arr[0] = 0;
749     for (cx=0; cx<curword->len-1; cx++) {
750 	XTextExtents(fontstr[curword->attr], buf+cx, 1, &direction, &ascent, &descent, &overall);
751 	arr[cx+1] = arr[cx] + overall.width;
752     }
753     arr[cx+1] = curword->width;
754 
755     curword->letterpos = arr;
756 }
757 
758 #ifdef __STDC__
strip_garbage(char * buf,int len)759 static void strip_garbage(char *buf, int len)
760 #else
761 static void strip_garbage(buf, len)
762 char *buf;
763 int len;
764 #endif
765 {
766     int ix;
767 
768     for (ix=0; ix<len; ix++, buf++) {
769 	if (iscntrl(*buf))
770 	    *buf = ' ';
771     }
772 }
773 
774 /* pos < 0 means add at end.
775  all this is grotesquely inefficient if adding anywhere but the end. */
776 #ifdef __STDC__
xtext_add(char ch,long pos)777 void xtext_add(char ch, long pos)
778 #else
779 void xtext_add(ch, pos)
780 char ch;
781 long pos;
782 #endif
783 {
784     if (pos<0)
785 	pos = numchars;
786     xtext_replace(pos, 0, &ch, 1);
787 }
788 
789 /* update data, adjusting dot and styles as necessary. */
790 #ifdef __STDC__
xtext_replace(long pos,long oldlen,char * buf,long newlen)791 void xtext_replace(long pos, long oldlen, char *buf, long newlen)
792 #else
793 void xtext_replace(pos, oldlen, buf, newlen)
794 long pos;
795 long oldlen;
796 char *buf;
797 long newlen;
798 #endif
799 {
800     long newnumchars;
801 
802     newnumchars = numchars-oldlen+newlen;
803     if (newnumchars >= char_size) {
804 	while (newnumchars >= char_size)
805 	    char_size *= 2;
806 	charbuf = (char *)realloc(charbuf, sizeof(char) * char_size);
807     }
808 
809     if (pos < dirtybeg || dirtybeg < 0)
810 	dirtybeg = pos;
811 
812     if (newlen != oldlen) {
813 	if (pos+oldlen != numchars) {
814 	    memmove(charbuf+pos+newlen, charbuf+pos+oldlen, sizeof(char) * (numchars-(pos+oldlen)));
815 	}
816 	if (numchars >= dirtyend)
817 	    dirtyend = numchars+1;
818 	dirtydelta += (newlen-oldlen);
819     }
820     else {
821 	if (pos+newlen >= dirtyend)
822 	    dirtyend = pos+newlen+1;
823 	dirtydelta += (newlen-oldlen);
824     }
825 
826     /* copy in the new stuff */
827     if (newlen)
828 	memmove(charbuf+pos, buf, sizeof(char) * newlen);
829 
830     /* diddle the dot */
831     if (dotpos >= pos+oldlen) {
832 	/* starts after changed region */
833 	dotpos += (newlen-oldlen);
834     }
835     else if (dotpos >= pos) {
836 	/* starts inside changed region */
837 	if (dotpos+dotlen >= pos+oldlen) {
838 	    /* ...but ends after it */
839 	    dotlen = (dotpos+dotlen)-(pos+oldlen);
840 	    dotpos = pos+newlen;
841 	}
842 	else {
843 	    /* ...and ends inside it */
844 	    dotpos = pos+newlen;
845 	    dotlen = 0;
846 	}
847     }
848     else {
849 	/* starts before changed region */
850 	if (dotpos+dotlen >= pos+oldlen) {
851 	    /* ...but ends after it */
852 	    dotlen += (newlen-oldlen);
853 	}
854 	else if (dotpos+dotlen >= pos) {
855 	    /* ...but ends inside it */
856 	    dotlen = (pos+newlen) - dotpos;
857 	}
858     }
859 
860     numchars = newnumchars;
861 }
862 
863 #ifdef __STDC__
xtext_setstyle(long pos,int attr)864 void xtext_setstyle(long pos, int attr)
865 #else
866 void xtext_setstyle(pos, attr)
867 long pos;
868 int attr;
869 #endif
870 {
871     long sx;
872 
873     if (pos < 0)
874 	pos = numchars;
875 
876     for (sx=numstyles-1; sx>=0; sx--) {
877 	if (stylelist[sx].pos <= pos) {
878 	    break;
879 	}
880     }
881     if (sx < 0) {
882 	printf("### oops, went back behind style 0\n");
883 	return;
884     }
885 
886     if (stylelist[sx].pos == pos) {
887 	stylelist[sx].attr = attr;
888     }
889     else {
890 	/* insert a style after sx */
891 	sx++;
892 	if (numstyles+1 >= styles_size) {
893 	    styles_size *= 2;
894 	    stylelist = (style *)realloc(stylelist, sizeof(style) * styles_size);
895 	}
896 	numstyles++;
897 	if (sx < numstyles) {
898 	    memmove(&stylelist[sx+1], &stylelist[sx], sizeof(style) * (numstyles-sx));
899 	    stylelist[sx].pos = pos;
900 	    stylelist[sx].attr = attr;
901 	}
902     }
903 
904     if (pos != numchars) {
905 	/* ### should only go to next style */
906 	dirtybeg = pos;
907 	dirtyend = numchars;
908 	dirtydelta = 0;
909 	xtext_layout();
910     }
911 }
912 
913 #ifdef __STDC__
xtext_set_lastseen()914 void xtext_set_lastseen()
915 #else
916 void xtext_set_lastseen()
917 #endif
918 {
919     lastlineseen = numlines;
920 }
921 
922 #ifdef __STDC__
xtext_end_visible()923 void xtext_end_visible()
924 #else
925 void xtext_end_visible()
926 #endif
927 {
928     long lx;
929 
930     if (lastlineseen < 0 || lastlineseen >= (numlines-linesperpage)-1) {
931 	/* straight to end */
932 	if (scrollline < numlines-linesperpage) {
933 	    scroll_to(numlines-linesperpage);
934 	}
935     }
936     else {
937 	lx = lastlineseen-1;
938 	while (lx < numlines-linesperpage) {
939 	    scroll_to(lx);
940 	    xmess_set_message("[Hit any key to continue.]", TRUE);
941 	    xio_pause();
942 	    lx += (linesperpage-1);
943 	}
944 	scroll_to(numlines-linesperpage);
945 	xmess_set_message(NULL, TRUE);
946     }
947 
948     lastlineseen = (-1);
949 }
950 
951 /* delete num lines from the top */
952 #ifdef __STDC__
xtext_delete_start(long num)953 void xtext_delete_start(long num)
954 #else
955 void xtext_delete_start(num)
956 long num;
957 #endif
958 {
959     long delchars;
960     long lx, sx, sx2;
961     int origattr;
962 
963     if (num > numlines)
964 	num = numlines;
965     if (num < 0)
966 	num = 0;
967 
968     if (num == numlines)
969       delchars = numchars;
970     else
971       delchars = linelist[num].pos;
972     if (!delchars)
973 	return;
974 
975     /* lines */
976     slapover(0, 0, num);
977     for (lx=0; lx<numlines; lx++) {
978 	linelist[lx].pos -= delchars;
979 	linelist[lx].posend -= delchars;
980     }
981 
982     /* styles */
983     for (sx=0; sx<numstyles; sx++) {
984 	if (stylelist[sx].pos > delchars)
985 	    break;
986     }
987     if (sx>0) {
988 	origattr = stylelist[sx-1].attr;
989 	stylelist[0].pos = 0;
990 	stylelist[0].attr = origattr;
991 	for (sx2=1; sx<numstyles; sx++, sx2++) {
992 	    stylelist[sx2].pos = stylelist[sx].pos - delchars;
993 	    stylelist[sx2].attr = stylelist[sx].attr;
994 	}
995 	numstyles = sx2;
996     }
997 
998     /* chars */
999     if (delchars < numchars)
1000       memmove(&charbuf[0], &charbuf[delchars], sizeof(char) * (numchars-delchars));
1001     numchars -= delchars;
1002 
1003     /* adjust, I mean, everything */
1004     if (dirtybeg != (-1)) {
1005 	dirtybeg -= delchars;
1006 	dirtyend -= delchars;
1007 	if (dirtyend < 0) {
1008 	    dirtybeg = (-1);
1009 	    dirtyend = (-1);
1010 	}
1011 	else if (dirtybeg < 0) {
1012 	    dirtybeg = 0;
1013 	}
1014     }
1015 
1016     dotpos -= delchars;
1017     if (dotpos < 0) {
1018 	if (dotpos+dotlen < 0) {
1019 	    dotpos = 0;
1020 	    dotlen = 0;
1021 	}
1022 	else {
1023 	    dotlen += dotpos;
1024 	    dotpos = 0;
1025 	}
1026     }
1027     lastdotpos -= delchars;
1028     if (lastdotpos < 0) {
1029 	if (lastdotpos+lastdotlen < 0) {
1030 	    lastdotpos = 0;
1031 	    lastdotlen = 0;
1032 	}
1033 	else {
1034 	    lastdotlen += lastdotpos;
1035 	    lastdotpos = 0;
1036 	}
1037     }
1038     inputfence -= delchars;
1039     if (inputfence < 0)
1040 	inputfence = 0;
1041 
1042     if (lastlineseen != (-1)) {
1043 	lastlineseen -= num;
1044 	if (lastlineseen < 0)
1045 	    lastlineseen = (-1);
1046     }
1047 
1048     scrollline -= num;
1049     scrollpos -= delchars;
1050     if (scrollline < 0 || scrollpos < 0) {
1051 	scrollline = 0;
1052 	scrollpos = 0;
1053 	redrawtext(0, -1, -1);
1054 	flip_selection(dotpos, dotlen);
1055 	adjust_elevator();
1056     }
1057     else {
1058 	adjust_elevator();
1059     }
1060 }
1061 
1062 #ifdef __STDC__
xtext_layout()1063 void xtext_layout()
1064 #else
1065 void xtext_layout()
1066 #endif
1067 {
1068     long ix, jx, ejx, lx;
1069     long styx, nextstylepos;
1070     int curstyle;
1071     long overline, overlineend;
1072     long tmpl, startpos;
1073     int prevflags;
1074     int needwholeredraw;
1075 
1076     int direction;
1077     int ascent, descent;
1078     XCharStruct overall;
1079 
1080     static long lastline = 0; /* last line dirtied */
1081 
1082     if (dirtybeg < 0 || dirtyend < 0) {
1083 	if (lastdotpos != dotpos || lastdotlen != dotlen) {
1084 	    refiddle_selection(lastdotpos, lastdotlen, dotpos, dotlen);
1085 	    /*flip_selection(lastdotpos, lastdotlen);*/
1086 	    lastdotpos = dotpos;
1087 	    lastdotlen = dotlen;
1088 	    /*flip_selection(lastdotpos, lastdotlen);*/
1089 	}
1090 	return;
1091     }
1092 
1093     /* if any text diddling is done, we'll just flip automatically */
1094     flip_selection(lastdotpos, lastdotlen);
1095     lastdotpos = dotpos;
1096     lastdotlen = dotlen;
1097 
1098     if (numlines==0) {
1099 	overline = 0;
1100 	startpos = 0;
1101     }
1102     else {
1103 	lx = find_line_by_pos(dirtybeg, lastline);
1104 	/* now lx is the line containing dirtybeg */
1105 
1106 	if (lx>0 && lx<numlines && (linelist[lx].flags & lineflag_Wrapped)) {
1107 	    /* do layout from previous line, in case a word from the changed area pops back there. */
1108 	    lx--;
1109 	}
1110 	overline = lx;
1111 	startpos = linelist[overline].pos;
1112     }
1113 
1114     /* get the first relevant style */
1115     for (styx=numstyles-1; styx>0; styx--)
1116 	if (stylelist[styx].pos <= startpos)
1117 	    break;
1118     if (styx==numstyles-1)
1119 	nextstylepos = numchars+10;
1120     else
1121 	nextstylepos = stylelist[styx+1].pos;
1122     curstyle = stylelist[styx].attr;
1123 
1124     /* start a-layin' */
1125     tmpl = 0;
1126     prevflags = 0;
1127 
1128     while (startpos<numchars && !(startpos >= dirtyend && charbuf[startpos]=='\n')) {
1129 	lline *thisline;
1130 	long tmpw, tmpwords_size;
1131 	long widthsofar, spaceswidth;
1132 
1133 	if (tmpl+1 >= tmplines_size) {
1134 	    /* the +1 allows the extra blank line at the end */
1135 	    tmplines_size *= 2;
1136 	    tmplinelist = (lline *)realloc(tmplinelist, sizeof(lline) * tmplines_size);
1137 	}
1138 	thisline = (&tmplinelist[tmpl]);
1139 	thisline->flags = prevflags;
1140 	tmpwords_size = 8;
1141 	thisline->wordlist = (word *)malloc(tmpwords_size * sizeof(word));
1142 	tmpw = 0;
1143 
1144 	/*printf("### laying tmpline %d, from charpos %d\n", tmpl, startpos);*/
1145 	tmpl++;
1146 
1147 	ix = startpos;
1148 	widthsofar = 0;
1149 	prevflags = 0;
1150 
1151 	while (ix<numchars && charbuf[ix]!='\n') {
1152 	    word *thisword;
1153 
1154 	    while (ix >= nextstylepos) {
1155 		/* ahead one style */
1156 		styx++;
1157 		if (styx==numstyles-1)
1158 		    nextstylepos = numchars+10;
1159 		else
1160 		    nextstylepos = stylelist[styx+1].pos;
1161 		curstyle = stylelist[styx].attr;
1162 	    }
1163 
1164 	    if (tmpw >= tmpwords_size) {
1165 		tmpwords_size *= 2;
1166 		thisline->wordlist = (word *)realloc(thisline->wordlist, tmpwords_size * sizeof(word));
1167 	    }
1168 	    thisword = (&thisline->wordlist[tmpw]);
1169 	    /* --- initialize word structure --- */
1170 
1171 	    thisword->letterpos = NULL;
1172 	    for (jx=ix; jx<numchars && jx<nextstylepos && charbuf[jx]!=' ' && charbuf[jx]!='\n'; jx++);
1173 
1174 	    XTextExtents(fontstr[curstyle], charbuf+ix, jx-ix, &direction, &ascent, &descent, &overall);
1175 	    if (widthsofar + overall.width > textwin_w) {
1176 		prevflags = lineflag_Wrapped;
1177 		if (tmpw == 0) {
1178 		    /* do something clever -- split the word, put first part in tmplist. */
1179 		    int letx;
1180 		    long wordwidthsofar = 0;
1181 		    for (letx=ix; letx<jx; letx++) {
1182 			XTextExtents(fontstr[curstyle], charbuf+letx, 1, &direction, &ascent, &descent, &overall);
1183 			if (widthsofar + wordwidthsofar+overall.width > textwin_w) {
1184 			    break;
1185 			}
1186 			wordwidthsofar += overall.width;
1187 		    }
1188 		    jx = letx;
1189 		    overall.width = wordwidthsofar;
1190 		    /* spaceswidth and ejx will be 0 */
1191 		    /* don't break */
1192 		}
1193 		else {
1194 		    /* ejx and spaceswidth are properly set from last word, trim them off. */
1195 		    thisword--;
1196 		    thisword->len -= ejx;
1197 		    thisword->width -= spaceswidth;
1198 		    break;
1199 		}
1200 	    }
1201 
1202 	    /* figure out trailing whitespace */
1203 	    ejx = 0;
1204 	    while (jx+ejx<numchars && jx+ejx<nextstylepos && charbuf[jx+ejx]==' ') {
1205 		ejx++;
1206 	    }
1207 	    spaceswidth = ejx * spacewidth[curstyle];
1208 
1209 	    /* put the word in tmplist */
1210 	    thisword->pos = ix-startpos;
1211 	    thisword->len = jx+ejx-ix;
1212 	    thisword->attr = curstyle;
1213 	    thisword->width = overall.width+spaceswidth;
1214 	    widthsofar += thisword->width;
1215 	    tmpw++;
1216 
1217 	    ix = jx+ejx;
1218 	}
1219 	thisline->pos = startpos;
1220 	if (tmpw) {
1221 	    word *thisword = (&thisline->wordlist[tmpw-1]);
1222 	    thisline->posend = startpos + thisword->pos + thisword->len;
1223 	}
1224 	else {
1225 	    thisline->posend = startpos;
1226 	}
1227 
1228 	if (ix<numchars && charbuf[ix]=='\n')
1229 	    ix++;
1230 
1231 	thisline->numwords = tmpw;
1232 	if (prefs.fulljustify && prevflags==lineflag_Wrapped && tmpw>1) {
1233 	    /* gonna regret this, I just bet */
1234 	    long extraspace, each;
1235 	    extraspace = textwin_w - widthsofar;
1236 	    each = extraspace / (tmpw-1);
1237 	    extraspace -= (each*(tmpw-1));
1238 	    for (jx=0; jx<extraspace; jx++) {
1239 		thisline->wordlist[jx].width += (each+1);
1240 	    }
1241 	    for (; jx<tmpw-1; jx++) {
1242 		thisline->wordlist[jx].width += each;
1243 	    }
1244 	}
1245 
1246 	startpos = ix;
1247     } /* done laying tmp lines */
1248 
1249     if (startpos == numchars && (numchars==0 || charbuf[numchars-1]=='\n')) {
1250 	/* lay one more line! */
1251 	lline *thisline;
1252 	thisline = (&tmplinelist[tmpl]);
1253 	thisline->flags = lineflag_Extra;
1254 	tmpl++;
1255 
1256 	thisline->wordlist = (word *)malloc(sizeof(word));
1257 	thisline->numwords = 0;
1258 	thisline->pos = startpos;
1259 	thisline->posend = startpos;
1260     }
1261 
1262     /*printf("### laid %d tmplines, and startpos now %d (delta %d)\n", tmpl, startpos, dirtydelta);*/
1263 
1264     for (lx=overline; lx<numlines && linelist[lx].pos < startpos-dirtydelta; lx++);
1265     if (lx==numlines-1 && (linelist[lx].flags & lineflag_Extra)) {
1266 	/* account for the extra line */
1267 	lx++;
1268     }
1269     overlineend = lx;
1270 
1271     /*printf("### overwrite area is lines [%d..%d) (of %d); replacing with %d lines\n", overline, overlineend, numlines, tmpl);*/
1272 
1273     slapover(tmpl, overline, overlineend);
1274 
1275     lastline = overline+tmpl; /* re-cache value */
1276     needwholeredraw = FALSE;
1277 
1278     /* diddle scroll stuff */
1279     if (scrollpos <= dirtybeg) {
1280 	/* disturbance is off bottom of screen -- do nothing */
1281     }
1282     else if (scrollpos >= startpos-dirtydelta) {
1283 	/* disturbance is off top of screen -- adjust so that no difference is visible. */
1284 	scrollpos += dirtydelta;
1285 	scrollline += (overline-overlineend) - tmpl;
1286     }
1287     else {
1288 	scrollpos += dirtydelta; /* kind of strange, but shouldn't cause trouble */
1289 	if (scrollpos >= numchars)
1290 	    scrollpos = numchars-1;
1291 	if (scrollpos < 0)
1292 	    scrollpos = 0;
1293 	scrollline = find_line_by_pos(scrollpos, scrollline);
1294 	needwholeredraw = TRUE;
1295     }
1296 
1297     dirtybeg = -1;
1298     dirtyend = -1;
1299     dirtydelta = 0;
1300 
1301     if (needwholeredraw) {
1302 	redrawtext(scrollline, -1, -1);
1303     }
1304     else if (tmpl == overlineend-overline) {
1305 	redrawtext(overline, tmpl, tmpl);
1306     }
1307     else {
1308 	if (overlineend > numlines)
1309 	    redrawtext(overline, -1, overlineend-overline);
1310 	else
1311 	    redrawtext(overline, -1, numlines-overline);
1312     }
1313 
1314     flip_selection(lastdotpos, lastdotlen);
1315 
1316     adjust_elevator();
1317 }
1318 
1319 static long drag_firstbeg, drag_firstend;
1320 static int drag_inscroll;
1321 static int drag_scrollmode; /* 0 for click in elevator; 1 for dragged in elevator; 2 for endzones; 3 for click in background */
1322 static int drag_hitypos;
1323 static long drag_origline;
1324 
1325 /* got a mouse hit. */
1326 #ifdef __STDC__
xtext_hitdown(int xpos,int ypos,unsigned int button,unsigned int mods,int clicknum)1327 void xtext_hitdown(int xpos, int ypos, unsigned int button, unsigned int mods, int clicknum)
1328 #else
1329 void xtext_hitdown(xpos, ypos, button, mods, clicknum)
1330 int xpos;
1331 int ypos;
1332 unsigned int button;
1333 unsigned int mods;
1334 int clicknum;
1335 #endif
1336 {
1337     long pos;
1338     long px, px2;
1339 
1340     if (xpos < scrollwin_x+scrollwin_w)
1341 	drag_inscroll = TRUE;
1342     else
1343 	drag_inscroll = FALSE;
1344 
1345     if (drag_inscroll) {
1346 	drag_origline = scrollline;
1347 	drag_hitypos = ypos-textwin_y;
1348 	switch (button) { /* scrollbar */
1349 	    case Button1:
1350 		if (ypos < scrollwin_y) {
1351 		    drag_scrollmode = 2;
1352 		    xted_scroll(op_ToTop);
1353 		}
1354 		else if (ypos >= scrollwin_y+scrollwin_h) {
1355 		    drag_scrollmode = 2;
1356 		    xted_scroll(op_ToBottom);
1357 		}
1358 		else {
1359 		    if (ypos >= scrollwin_y+scrollel_top
1360 			&& ypos < scrollwin_y+scrollel_bot)
1361 			drag_scrollmode = 0;
1362 		    else
1363 			drag_scrollmode = 3;
1364 		}
1365 		break;
1366 	    case Button3:
1367 		if (ypos < scrollwin_y) {
1368 		    drag_scrollmode = 2;
1369 		    xted_scroll(op_UpLine);
1370 		}
1371 		else if (ypos >= scrollwin_y+scrollwin_h) {
1372 		    drag_scrollmode = 2;
1373 		    xted_scroll(op_DownLine);
1374 		}
1375 		else {
1376 		    if (ypos >= scrollwin_y+scrollel_top
1377 			&& ypos < scrollwin_y+scrollel_bot)
1378 			drag_scrollmode = 0;
1379 		    else
1380 			drag_scrollmode = 3;
1381 		}
1382 		break;
1383 	}
1384     }
1385     else {
1386 	switch (button) { /* text window */
1387 	    case Button1:
1388 	    case Button3:
1389 		xpos -= textwin_x;
1390 		ypos -= textwin_y;
1391 
1392 		pos = find_pos_by_loc(xpos, ypos);
1393 		if (button==Button1) {
1394 		    if (!(clicknum & 1)) {
1395 			px = back_to_white(pos);
1396 			px2 = fore_to_white(pos);
1397 		    }
1398 		    else {
1399 			px = pos;
1400 			px2 = pos;
1401 		    }
1402 		    dotpos = px;
1403 		    dotlen = px2-px;
1404 		    drag_firstbeg = px;
1405 		    drag_firstend = px2;
1406 		}
1407 		else {
1408 		    if (pos < dotpos+dotlen/2) {
1409 			drag_firstbeg = dotpos+dotlen;
1410 		    }
1411 		    else {
1412 			drag_firstbeg = dotpos;
1413 		    }
1414 		    drag_firstend = drag_firstbeg;
1415 		    if (pos < drag_firstbeg) {
1416 			if (!(clicknum & 1))
1417 			    dotpos = back_to_white(pos);
1418 			else
1419 			    dotpos = pos;
1420 			dotlen = drag_firstend-dotpos;
1421 		    }
1422 		    else if (pos > drag_firstend) {
1423 			dotpos = drag_firstbeg;
1424 			if (!(clicknum & 1))
1425 			    dotlen = fore_to_white(pos)-drag_firstbeg;
1426 			else
1427 			    dotlen = pos-drag_firstbeg;
1428 		    }
1429 		    else {
1430 			dotpos = drag_firstbeg;
1431 			dotlen = drag_firstend-drag_firstbeg;
1432 		    }
1433 		}
1434 		xtext_layout();
1435 		break;
1436 	    default:
1437 		break;
1438 	}
1439     }
1440 }
1441 
1442 #ifdef __STDC__
xtext_hitmove(int xpos,int ypos,unsigned int button,unsigned int mods,int clicknum)1443 void xtext_hitmove(int xpos, int ypos, unsigned int button, unsigned int mods, int clicknum)
1444 #else
1445 void xtext_hitmove(xpos, ypos, button, mods, clicknum)
1446 int xpos;
1447 int ypos;
1448 unsigned int button;
1449 unsigned int mods;
1450 int clicknum;
1451 #endif
1452 {
1453     long pos, px;
1454 
1455     if (drag_inscroll) {
1456 	if (drag_scrollmode==0 || drag_scrollmode==1) {
1457 	    drag_scrollmode = 1;
1458 	    px = ((ypos - drag_hitypos)*numlines) / (scrollwin_h-2*BAREXTRA);
1459 	    scroll_to(drag_origline+px);
1460 	}
1461     }
1462     else {
1463 	xpos -= textwin_x;
1464 	ypos -= textwin_y;
1465 
1466 	switch (button) {
1467 	    case Button1:
1468 	    case Button3:
1469 		pos = find_pos_by_loc(xpos, ypos);
1470 		if (pos < drag_firstbeg) {
1471 		    if (!(clicknum & 1))
1472 			dotpos = back_to_white(pos);
1473 		    else
1474 			dotpos = pos;
1475 		    dotlen = drag_firstend-dotpos;
1476 		}
1477 		else if (pos > drag_firstend) {
1478 		    dotpos = drag_firstbeg;
1479 		    if (!(clicknum & 1))
1480 			dotlen = fore_to_white(pos)-drag_firstbeg;
1481 		    else
1482 			dotlen = pos-drag_firstbeg;
1483 		}
1484 		else {
1485 		    dotpos = drag_firstbeg;
1486 		    dotlen = drag_firstend-drag_firstbeg;
1487 		}
1488 		xtext_layout();
1489 		break;
1490 	    default:
1491 		break;
1492 	}
1493     }
1494 }
1495 
1496 #ifdef __STDC__
xtext_hitup(int xpos,int ypos,unsigned int button,unsigned int mods,int clicknum)1497 void xtext_hitup(int xpos, int ypos, unsigned int button, unsigned int mods, int clicknum)
1498 #else
1499 void xtext_hitup(xpos, ypos, button, mods, clicknum)
1500 int xpos;
1501 int ypos;
1502 unsigned int button;
1503 unsigned int mods;
1504 int clicknum;
1505 #endif
1506 {
1507     int px;
1508 
1509     if (drag_inscroll && (drag_scrollmode==0 || drag_scrollmode==3)) {
1510 	switch (button) { /* scrollbar */
1511 	    case Button1:
1512 		px = (ypos - textwin_y) / lineheight;
1513 		scroll_to(scrollline+px);
1514 		break;
1515 	    case Button3:
1516 		px = (ypos - textwin_y) / lineheight;
1517 		scroll_to(scrollline-px);
1518 		break;
1519 	}
1520     }
1521 }
1522 
1523 /* editing functions... */
1524 
1525 #ifdef __STDC__
xted_init(int vbuflen,char * vbuffer,int * vreadpos,int * vkillflag,int firsttime)1526 void xted_init(int vbuflen, char *vbuffer, int *vreadpos, int *vkillflag,
1527   int firsttime)
1528 #else
1529 void xted_init(vbuflen, vbuffer, vreadpos, vkillflag, firsttime)
1530 int vbuflen;
1531 char *vbuffer;
1532 int *vreadpos;
1533 int *vkillflag;
1534 int firsttime;
1535 #endif
1536 {
1537     killflag = vkillflag;
1538     *killflag = (-1);
1539     buflen = vbuflen;
1540     buffer = vbuffer;
1541     readpos = vreadpos;
1542 
1543     if (*readpos) {
1544       if (firsttime) {
1545 	/* Z-machine has already entered the text into the buffer. */
1546 	inputfence = numchars - (*readpos);
1547 	originalattr = stylelist[numstyles-1].attr;
1548 	xtext_setstyle(inputfence, prefs.inputattr);
1549       }
1550       else {
1551 	/* The terp has to enter the text. */
1552 	inputfence = numchars;
1553 	originalattr = stylelist[numstyles-1].attr;
1554 	xtext_setstyle(-1, prefs.inputattr);
1555 	xtext_replace(dotpos, 0, buffer, *readpos);
1556 	xtext_layout();
1557       }
1558     }
1559     else {
1560 	inputfence = numchars;
1561 	originalattr = stylelist[numstyles-1].attr;
1562 	xtext_setstyle(-1, prefs.inputattr);
1563     }
1564 
1565     historypos = historynum;
1566 }
1567 
1568 #ifdef __STDC__
xted_insert(int ch)1569 void xted_insert(int ch)
1570 #else
1571 void xted_insert(ch)
1572 int ch;
1573 #endif
1574 {
1575     if (iscntrl(ch))
1576 	ch = ' ';
1577 
1578     if (dotpos < inputfence) {
1579 	dotpos = numchars;
1580 	dotlen = 0;
1581     }
1582     else {
1583 	collapse_dot();
1584     }
1585 
1586     xtext_add(ch, dotpos);
1587     xtext_layout();
1588     xtext_end_visible();
1589 }
1590 
1591 #ifdef __STDC__
xted_delete(int op)1592 void xted_delete(int op)
1593 #else
1594 void xted_delete(op)
1595 int op;
1596 #endif
1597 {
1598     long pos;
1599 
1600     if (dotpos < inputfence)
1601 	return;
1602     collapse_dot();
1603 
1604     switch (op) {
1605 	case op_BackChar:
1606 	    if (dotpos <= inputfence)
1607 		return;
1608 	    xtext_replace(dotpos-1, 1, "", 0);
1609 	    break;
1610 	case op_ForeChar:
1611 	    if (dotpos < inputfence || dotpos >= numchars)
1612 		return;
1613 	    xtext_replace(dotpos, 1, "", 0);
1614 	    break;
1615 	case op_BackWord:
1616 	    pos = back_to_nonwhite(dotpos);
1617 	    pos = back_to_white(pos);
1618 	    if (pos < inputfence)
1619 		pos = inputfence;
1620 	    if (pos >= dotpos)
1621 		return;
1622 	    xtext_replace(pos, dotpos-pos, "", 0);
1623 	    break;
1624 	case op_ForeWord:
1625 	    pos = fore_to_nonwhite(dotpos);
1626 	    pos = fore_to_white(pos);
1627 	    if (pos < inputfence)
1628 		pos = inputfence;
1629 	    if (pos <= dotpos)
1630 		return;
1631 	    xtext_replace(dotpos, pos-dotpos, "", 0);
1632 	    break;
1633     }
1634     xtext_layout();
1635 }
1636 
1637 #ifdef __STDC__
xted_enter(int op)1638 void xted_enter(int op)
1639 #else
1640 void xted_enter(op)
1641 int op;
1642 #endif
1643 {
1644     int len;
1645 
1646     if (op != op_Enter)
1647 	return;
1648 
1649     if (killflag)
1650 	*killflag = '\n';
1651     xtext_setstyle(-1, originalattr);
1652 
1653     len = numchars-inputfence;
1654     if (len > buflen)
1655 	len = buflen;
1656     memmove(buffer, charbuf+inputfence, len*sizeof(char));
1657     *readpos = len;
1658 
1659     if (len) {
1660 	/* add to history */
1661 	if (historynum==prefs.historylength) {
1662 	    free(history[0].str);
1663 	    memmove(&history[0], &history[1], (prefs.historylength-1) * (sizeof(histunit)));
1664 	}
1665 	else
1666 	    historynum++;
1667 	history[historynum-1].str = malloc(len*sizeof(char));
1668 	memmove(history[historynum-1].str, charbuf+inputfence, len*sizeof(char));
1669 	history[historynum-1].len = len;
1670     }
1671 
1672     xtext_add('\n', -1);
1673     dotpos = numchars;
1674     dotlen = 0;
1675     xtext_layout();
1676 
1677     /* a somewhat strange place to put the buffer trimmer, but what the heck. The status line shrinker too. */
1678     xstat_reset_window_size(op_Shrink);
1679     if (numchars > prefs.buffersize + prefs.bufferslack) {
1680 	long lx;
1681 	for (lx=0; lx<numlines; lx++)
1682 	    if (linelist[lx].pos > (numchars-prefs.buffersize))
1683 		break;
1684 	if (lx) {
1685 	    xtext_delete_start(lx);
1686 	}
1687     }
1688 }
1689 
1690 #ifdef __STDC__
xtext_line_timeout()1691 void xtext_line_timeout()
1692 #else
1693 void xtext_line_timeout()
1694 #endif
1695 {
1696     int len;
1697 
1698     /* same as xted_enter(), but skip the unnecessary stuff.
1699      We don't need to add to history, collapse the dot, xtext_layout, trim the buffer, or shrink the status window. */
1700 
1701     len = numchars-inputfence;
1702     if (len > buflen)
1703 	len = buflen;
1704     memmove(buffer, charbuf+inputfence, len*sizeof(char));
1705     *readpos = len;
1706 
1707     if (len) {
1708 	xtext_replace(inputfence, len, "", 0);
1709 	dotpos = numchars;
1710 	dotlen = 0;
1711 	xtext_layout();
1712     }
1713 
1714     xtext_setstyle(-1, originalattr);
1715 }
1716 
1717 #ifdef __STDC__
xted_scroll(int op)1718 void xted_scroll(int op)
1719 #else
1720 void xted_scroll(op)
1721 int op;
1722 #endif
1723 {
1724     switch (op) {
1725 	case op_UpLine:
1726 	    scroll_to(scrollline-1);
1727 	    break;
1728 	case op_DownLine:
1729 	    scroll_to(scrollline+1);
1730 	    break;
1731 	case op_UpPage:
1732 	    scroll_to(scrollline-(linesperpage-1));
1733 	    break;
1734 	case op_DownPage:
1735 	    scroll_to(scrollline+(linesperpage-1));
1736 	    break;
1737 	case op_ToTop:
1738 	    scroll_to(0);
1739 	    break;
1740 	case op_ToBottom:
1741 	    scroll_to(numlines);
1742 	    break;
1743     }
1744 }
1745 
1746 #ifdef __STDC__
xted_movecursor(int op)1747 void xted_movecursor(int op)
1748 #else
1749 void xted_movecursor(op)
1750 int op;
1751 #endif
1752 {
1753     long pos;
1754 
1755     switch (op) {
1756 	case op_BackChar:
1757 	    collapse_dot();
1758 	    if (dotpos > 0)
1759 		dotpos--;
1760 	    break;
1761 	case op_ForeChar:
1762 	    collapse_dot();
1763 	    if (dotpos < numchars)
1764 		dotpos++;
1765 	    break;
1766 	case op_BackWord:
1767 	    collapse_dot();
1768 	    dotpos = back_to_nonwhite(dotpos);
1769 	    dotpos = back_to_white(dotpos);
1770 	    break;
1771 	case op_ForeWord:
1772 	    collapse_dot();
1773 	    dotpos = fore_to_nonwhite(dotpos);
1774 	    dotpos = fore_to_white(dotpos);
1775 	    break;
1776 	case op_BeginLine:
1777 	    if (dotlen) {
1778 		dotlen = 0;
1779 	    }
1780 	    else {
1781 		if (dotpos >= inputfence)
1782 		    dotpos = inputfence;
1783 		else {
1784 		    pos = dotpos;
1785 		    while (pos > 0 && charbuf[pos-1] != '\n')
1786 			pos--;
1787 		    dotpos = pos;
1788 		}
1789 	    }
1790 	    break;
1791 	case op_EndLine:
1792 	    if (dotlen) {
1793 		collapse_dot();
1794 	    }
1795 	    else {
1796 		if (dotpos >= inputfence)
1797 		    dotpos = numchars;
1798 		else {
1799 		    pos = dotpos;
1800 		    while (pos < numchars && charbuf[pos] != '\n')
1801 			pos++;
1802 		    dotpos = pos;
1803 		}
1804 	    }
1805 	    break;
1806     }
1807     xtext_layout();
1808 }
1809 
1810 #ifdef __STDC__
xted_cutbuf(int op)1811 void xted_cutbuf(int op)
1812 #else
1813 void xted_cutbuf(op)
1814 int op;
1815 #endif
1816 {
1817     char *cx;
1818     int num;
1819     long tmppos;
1820 
1821     switch (op) {
1822 	case op_Copy:
1823 	    if (dotlen) {
1824 		XRotateBuffers(xiodpy, 1);
1825 		XStoreBytes(xiodpy, charbuf+dotpos, sizeof(char)*dotlen);
1826 	    }
1827 	    break;
1828 	case op_Wipe:
1829 	    if (dotlen) {
1830 		XRotateBuffers(xiodpy, 1);
1831 		XStoreBytes(xiodpy, charbuf+dotpos, sizeof(char)*dotlen);
1832 		if (dotpos >= inputfence) {
1833 		    xtext_replace(dotpos, dotlen, "", 0);
1834 		    xtext_layout();
1835 		}
1836 	    }
1837 	    break;
1838 	case op_Yank:
1839 	    collapse_dot();
1840 	    if (dotpos < inputfence)
1841 		dotpos = numchars;
1842 	    cx = XFetchBytes(xiodpy, &num);
1843 	    strip_garbage(cx, num);
1844 	    if (cx && num) {
1845 		tmppos = dotpos;
1846 		xtext_replace(tmppos, 0, cx, num);
1847 		dotpos = tmppos;
1848 		dotlen = num;
1849 		free(cx);
1850 	    }
1851 	    xtext_layout();
1852 	    break;
1853 	case op_Untype:
1854 	    if (numchars == inputfence)
1855 		break;
1856 	    dotpos = inputfence;
1857 	    dotlen = numchars-inputfence;
1858 	    XRotateBuffers(xiodpy, 1);
1859 	    XStoreBytes(xiodpy, charbuf+dotpos, sizeof(char)*dotlen);
1860 	    xtext_replace(dotpos, dotlen, "", 0);
1861 	    xtext_layout();
1862 	    break;
1863 	case op_Kill:
1864 	    if (dotpos < inputfence) {
1865 		/* maybe extend to end-of-line and copy? */
1866 		break;
1867 	    }
1868 	    dotlen = numchars-dotpos;
1869 	    XRotateBuffers(xiodpy, 1);
1870 	    XStoreBytes(xiodpy, charbuf+dotpos, sizeof(char)*dotlen);
1871 	    xtext_replace(dotpos, dotlen, "", 0);
1872 	    xtext_layout();
1873 	    break;
1874     }
1875 }
1876 
1877 #ifdef __STDC__
xted_history(int op)1878 void xted_history(int op)
1879 #else
1880 void xted_history(op)
1881 int op;
1882 #endif
1883 {
1884     long pos, len;
1885 
1886     switch (op) {
1887 	case op_BackLine:
1888 	    if (historypos > 0) {
1889 		if (dotpos < inputfence) {
1890 		    dotpos = numchars;
1891 		    dotlen = 0;
1892 		}
1893 		historypos--;
1894 		pos = dotpos;
1895 		xtext_replace(pos, dotlen, history[historypos].str, history[historypos].len);
1896 		dotpos = pos;
1897 		dotlen = history[historypos].len;
1898 		xtext_layout();
1899 	    }
1900 	    break;
1901 	case op_ForeLine:
1902 	    if (historypos < historynum) {
1903 		if (dotpos < inputfence) {
1904 		    dotpos = numchars;
1905 		    dotlen = 0;
1906 		}
1907 		historypos++;
1908 		if (historypos < historynum) {
1909 		    pos = dotpos;
1910 		    xtext_replace(dotpos, dotlen, history[historypos].str, history[historypos].len);
1911 		    dotpos = pos;
1912 		    dotlen = history[historypos].len;
1913 		}
1914 		else {
1915 		    pos = dotpos;
1916 		    xtext_replace(dotpos, dotlen, "", 0);
1917 		    dotpos = pos;
1918 		    dotlen = 0;
1919 		}
1920 		xtext_layout();
1921 	    }
1922     }
1923 }
1924 
1925 #ifdef __STDC__
xted_define_macro(int keynum)1926 void xted_define_macro(int keynum)
1927 #else
1928 void xted_define_macro(keynum)
1929 int keynum;
1930 #endif
1931 {
1932     static cmdentry *macrocommand = NULL;
1933     char buf[256];
1934     char *cx, *cx2;
1935 
1936     if (!macrocommand) {
1937 	macrocommand = xkey_find_cmd_by_name("macro");
1938 	if (!macrocommand) {
1939 	    xmess_set_message("Error: unable to find macro command entry.", FALSE);
1940 	    return;
1941 	}
1942     }
1943     if (keycmds[keynum] != macrocommand) {
1944 	cx = xkey_get_key_name(keynum);
1945 	sprintf(buf, "Key <%s> is not bound to the macro command.", cx);
1946 	xmess_set_message(buf, FALSE);
1947 	return;
1948     }
1949 
1950     if (dotlen == 0) {
1951 	xmess_set_message("You must highlight a string to define this macro to.", FALSE);
1952 	return;
1953     }
1954 
1955     cx2 = (char *)malloc(sizeof(char) * (dotlen+1));
1956     memcpy(cx2, charbuf+dotpos, dotlen * sizeof(char));
1957     cx2[dotlen] = '\0';
1958     strip_garbage(cx2, dotlen);
1959 
1960     if (keycmdargs[keynum])
1961 	free(keycmdargs[keynum]);
1962     keycmdargs[keynum] = cx2;
1963     cx = xkey_get_key_name(keynum);
1964     if (!cx2 || !cx2[0])
1965 	sprintf(buf, "Macro <%s> is not defined.", cx);
1966     else if (strlen(cx2) > (sizeof(buf)-64))
1967 	sprintf(buf, "Macro <%s> is defined to something too long to display.", cx);
1968     else
1969 	sprintf(buf, "Macro <%s> defined to \"%s\".", cx, cx2);
1970     xmess_set_message(buf, FALSE);
1971 }
1972 
1973 #ifdef __STDC__
xted_macro(int op)1974 void xted_macro(int op)
1975 #else
1976 void xted_macro(op)
1977 int op;
1978 #endif
1979 {
1980     char *str, *cx;
1981 
1982     str = keycmdargs[op];
1983     if (!str || !str[0]) {
1984 	char buf[128];
1985 	cx = xkey_get_key_name(op);
1986 	sprintf(buf, "Macro <%s> is not defined.", cx);
1987 	xmess_set_message(buf, FALSE);
1988 	return;
1989     }
1990 
1991     if (dotpos < inputfence) {
1992 	dotpos = numchars;
1993 	dotlen = 0;
1994     }
1995     else {
1996 	collapse_dot();
1997     }
1998 
1999     xtext_replace(dotpos, 0, str, strlen(str));
2000 
2001     xtext_layout();
2002     xtext_end_visible();
2003 }
2004 
2005 #ifdef __STDC__
xted_noop(int op)2006 void xted_noop(int op)
2007 #else
2008 void xted_noop(op)
2009 int op;
2010 #endif
2011 {
2012     /* good for debugging */
2013 }
2014