1 /*
2  * wmslib/src/but/tbin.c, part of wmslib (Library functions)
3  * Copyright (C) 1994-1996 William Shubert.
4  * See "configure.h.in" for more copyright information.
5  */
6 
7 /*
8  * This is absolutely huge and disgusting.  It is no longer a cute little
9  *   button.  It has grown into a monstrosity.  Some day I will break it off,
10  *   make it into its own module, that reads configuration files, etc.
11  * Then it will be cool!  Then it will be its own work processor, and I
12  *   will just have to wrap a few extra buttons around it to have my very
13  *   own beautiful wysiwyg word processor/editor!  Yay!
14  */
15 
16 #include <configure.h>
17 
18 #ifdef  X11_DISP
19 
20 #ifdef  STDC_HEADERS
21 #include <stdlib.h>
22 #include <unistd.h>
23 #endif  /* STDC_HEADERS */
24 #include <ctype.h>
25 #include <stdio.h>
26 #include <X11/Xlib.h>
27 #include <X11/Xutil.h>
28 #include <X11/cursorfont.h>
29 #include <X11/Xatom.h>
30 #include <X11/keysym.h>
31 #include <sys/time.h>
32 #include <wms.h>
33 #include <but/but.h>
34 #include <but/tbin.h>
35 #include <but/timer.h>
36 
37 
38 /**********************************************************************
39  * Constants
40  **********************************************************************/
41 #define  MAX_PASTE  (64*1024)
42 
43 
44 /**********************************************************************
45  * Data types
46  **********************************************************************/
47 typedef enum  {
48   insert_none, insert_ok, insert_cr, insert_bad
49 } Insert;
50 
51 
52 typedef enum  {
53   break_loCur, break_eoLoCur, break_eol
54 } BreakType;
55 
56 
57 typedef struct  Loc_struct  {
58   int  tin, index;
59 } Loc;
60 
61 
62 typedef struct  Tin_struct  {
63   int  start, len;
64   int  width;  /* In pixels. */
65 } Tin;
66 
67 
68 typedef enum  {
69   tbin_loCur, tbin_hiCur, tbin_mouse, tbin_press
70 } TbinLoc;
71 #define  tbin_numLocs  (tbin_press + 1)
72 #define  tbinLoc_iter(i)  for (i = 0;  i < tbin_numLocs;  ++i)
73 
74 
75 typedef struct  Tbin_struct  {
76   ButTimer  *cTimer;
77 
78   int  mouseX;
79   Loc  locs[tbin_numLocs];
80   bool  mouseInBut;
81   int  clicks, prevClickNum;
82 
83   bool  cursorVisible;
84   bool  readOnly;
85   int  lMargin, rMargin;
86 
87   int  maxLines;  /* If you exceed this, chop off the top. */
88   int  numTins, maxTins, loTinBreak, hiTinBreak;
89   Tin  *tins;
90 
91   int  bufLen, loBreak, hiBreak;
92   char  *buf;
93 
94   void  (*offWinCallback)(But *but, int activeLine, int passiveLine,
95 			  int mouseY);
96 
97   MAGIC_STRUCT
98 } Tbin;
99 
100 
101 static But  *cut_butnum = NULL;
102 static But  *paste_butnum = NULL;
103 
104 /**********************************************************************
105  * Forward declarations
106  **********************************************************************/
107 static int  locateMouse(XFontStruct *fs, const char *text, int textLen, int x,
108 			bool *rightSide);
109 static void  ti_cursor(But *but);
110 static void  ti_redrawCursor(But *but);
111 static ButOut  mmove(But *but, int x, int y);
112 static ButOut  mleave(But *but);
113 static ButOut  mpress(But *but, int butnum, int x, int y);
114 static ButOut  mrelease(But *but, int butnum, int x, int y);
115 static ButOut  kpress(But *but, const char *keystr, KeySym sym);
116 static void  draw(But *but, int x,int y, int w,int h);
117 static ButOut  destroy(But *but);
118 static Insert  insert(ButEnv *env, const char *src, Tbin *but,
119 		      int butW, bool *changePrev);
120 static void  cut(ButEnv *env, But *but);
121 static void  paste(ButEnv *env, But *but);
122 static void  enableCTimer(But *but);
123 static void  disableCTimer(But *but);
124 
125 static bool  sreq(ButEnv *env, XSelectionRequestEvent *xsre);
126 static int   sclear(ButEnv *env);
127 static int   snotify(ButEnv *env, XSelectionEvent *xsre);
128 static void  flags(But *but, uint flags);
129 
130 static void  wordsel_adjust(Tbin *tbin);
131 
132 static void  tin_draw(But *but, Tbin *tb, int tinNum,
133 		      int x, int y, int w, int h);
134 static int  calcTinNum(But *but, Tbin *tbin, int y);
135 static void  setMouseX(But *but, Tbin *tbin, int tin, int loc);
136 static void  checkOffWin(But *but, int mouseY, bool force);
137 #define  tinNum_next(tn, tb)  (((tn)+1 == (tb)->loTinBreak) ? \
138 			        (tb)->hiTinBreak:(tn)+1)
139 #define  tinNum_prev(tn, tb)  (((tn) == (tb)->hiTinBreak) ? \
140 			        (tb)->loTinBreak-1:(tn)-1)
141 #define  tinNum_line(tn, tb)  ((tn) >= (tb)->loTinBreak ? \
142 			       (tn) + (tb)->loTinBreak - (tb)->hiTinBreak : \
143 			       (tn))
144 #define  tinNum_line2TinNum(l, tb)             \
145   ((l) >= (tb)->loTinBreak ?                   \
146    (l) + (tb)->hiTinBreak - (tb)->loTinBreak : \
147    (l))
148 #define  tinNum_valid(tn, tb)  (((tn) >= 0) && ((tn) < (tb)->maxTins) && \
149 				(((tn) < (tb)->loTinBreak) || \
150 				 ((tn) >= (tb)->hiTinBreak)))
151 #define  loc_valid(l, tb)  (tinNum_valid((l).tin, (tb)) && \
152 			    ((l).index >= 0) && \
153 			    ((l).index <= (tb)->tins[(l).tin].len))
154 #define  loc_eq(a, b)  (((a).index == (b).index) && ((a).tin == (b).tin))
155 static void  adjustBreak(Tbin *tb, BreakType bType, int breakTin);
156 static void  addMoreBufferSpace(Tbin *tb);
157 static void  addMoreTins(Tbin *tb);
158 static void  breakLine(Tbin *tb, int tinNum, ButEnv *env, int butW);
159 static int  calcTinWidth(Tin *tin, const char *buf, ButEnv *env);
160 static bool  tryJoinLines(Tbin *tb, int tinNum, ButEnv *env, int butW);
161 static bool  resize(But *but, int oldX, int oldY, int oldW, int oldH);
162 static void  killTopLines(Tbin *tb, int lines);
163 static void  setLoc(Tbin *tb, TbinLoc locNum, int position);
164 
165 
166 /**********************************************************************
167  * Global variables
168  **********************************************************************/
169 
170 
171 static ButAction  action = {
172   mmove, mleave, mpress, mrelease,
173   kpress, NULL, draw, destroy, flags, NULL, resize};
174 
175 
butTbin_create(ButWin * win,int layer,int flags,const char * text)176 But  *butTbin_create(ButWin *win, int layer, int flags,
177 		     const char *text)  {
178   But  *but;
179   Tbin  *tb;
180   TbinLoc  tbinLoc;
181 
182   tb = wms_malloc(sizeof(Tbin));
183   MAGIC_SET(tb);
184   but = but_create(win, tb, &action);
185   but->layer = layer;
186   but->flags = flags;
187 
188   if (text == NULL)
189     text = "";
190   tb->cTimer = NULL;
191   tb->mouseX = -1;
192   tbinLoc_iter(tbinLoc)  {
193     tb->locs[tbinLoc].tin = 0;
194     tb->locs[tbinLoc].index = 0;
195   }
196   tb->mouseInBut = FALSE;
197   tb->clicks = tb->prevClickNum = 0;
198 
199   tb->cursorVisible = FALSE;
200   tb->readOnly = FALSE;
201   tb->lMargin = tb->rMargin = butEnv_stdBw(win->env);
202 
203   tb->maxLines = -1;
204   tb->numTins = 1;
205   tb->maxTins = 2;
206   tb->tins = wms_malloc(tb->maxTins * sizeof(Tin));
207 
208   tb->bufLen = 4 * 1024;
209   tb->loBreak = 0;
210   tb->hiBreak = tb->bufLen;
211   tb->buf = wms_malloc(tb->bufLen);
212 
213   butTbin_set(but, text);
214 
215   tb->offWinCallback = NULL;
216   return(but);
217 }
218 
219 
butTbin_get(But * but)220 const char  *butTbin_get(But *but)  {
221   Tbin  *tb = but->iPacket;
222 
223   assert(but->action == &action);
224   assert(MAGIC(tb));
225   adjustBreak(tb, break_eol, tinNum_prev(tb->maxTins, tb));
226   if (tb->loBreak == tb->hiBreak)
227     addMoreBufferSpace(tb);
228   tb->buf[tb->loBreak] = '\0';
229   return(tb->buf);
230 }
231 
232 
butTbin_numLines(But * but)233 int  butTbin_numLines(But *but)  {
234   Tbin  *tbin = but->iPacket;
235 
236   assert(MAGIC(tbin));
237   return(tbin->numTins);
238 }
239 
240 
butTbin_setOffWinCallback(But * but,void (* func)(But * but,int activeLine,int passiveLine,int mouseY))241 void  butTbin_setOffWinCallback(But *but,
242 				void (*func)(But *but, int activeLine,
243 					     int passiveLine,
244 					     int mouseY))  {
245   Tbin  *tbin;
246 
247   assert(MAGIC(but));
248   tbin = but->iPacket;
249   assert(MAGIC(tbin));
250   tbin->offWinCallback = func;
251 }
252 
253 
destroy(But * but)254 static ButOut  destroy(But *but)  {
255   Tbin  *tb = but->iPacket;
256   ButEnv  *env = but->win->env;
257 
258   assert(but->action == &action);
259   assert(MAGIC(tb));
260   if (cut_butnum == but)  {
261     if (env->sClear == sclear)
262       env->sClear = NULL;
263     if (env->sReq == sreq)
264       env->sReq = NULL;
265     cut_butnum = NULL;
266   }
267   if (paste_butnum == but)  {
268     if (env->sNotify == snotify)
269       env->sNotify = NULL;
270     paste_butnum = NULL;
271   }
272   wms_free(tb->tins);
273   wms_free(tb->buf);
274   MAGIC_UNSET(tb);
275   wms_free(tb);
276   return(0);
277 }
278 
279 
draw(But * but,int x,int y,int w,int h)280 static void  draw(But *but, int x,int y, int w,int h)  {
281   Tbin  *tb = but->iPacket;
282   ButEnv  *env = but->win->env;
283   int  fontH, i, startLine;
284   int  tinY;
285 
286   assert(but->action == &action);
287   assert(MAGIC(tb));
288   fontH = butEnv_fontH(env, 0);
289   XSetFont(env->dpy, env->gc, env->fonts[0]->fid);
290   startLine = (y - fontH + 1 - but->y) / fontH;
291   if (startLine < 0)
292     startLine = 0;
293   tinY = but->y + startLine * fontH;
294   for (i = tinNum_line2TinNum(startLine, tb);
295        (i < tb->maxTins) && (tinY < y + h);
296        i = tinNum_next(i, tb))  {
297     tin_draw(but, tb, i, but->x, tinY, but->w, fontH);
298     tinY += fontH;
299   }
300 }
301 
302 
tin_draw(But * but,Tbin * tb,int tinNum,int x,int y,int w,int h)303 static void  tin_draw(But *but, Tbin *tb, int tinNum,
304 		      int x, int y, int w, int h)  {
305   ButWin  *win = but->win;
306   ButEnv  *env = win->env;
307   Tin  *ti = &tb->tins[tinNum];
308   XFontStruct  *fs = env->fonts[0];
309   int  loCur, hiCur;
310   int  blockL, blockW;
311 
312   butEnv_setXFg(env, BUT_FG);
313   if (tb->locs[tbin_loCur].tin < tinNum)  {
314     loCur = -1;
315   } else if (tb->locs[tbin_loCur].tin == tinNum)  {
316     loCur = tb->locs[tbin_loCur].index;
317   } else  {
318     loCur = ti->len + 1;
319   }
320   if (tb->locs[tbin_hiCur].tin < tinNum)  {
321     hiCur = -1;
322   } else if (tb->locs[tbin_hiCur].tin == tinNum)  {
323     hiCur = tb->locs[tbin_hiCur].index;
324   } else  {
325     hiCur = ti->len + 1;
326   }
327   if (loCur != hiCur)  {
328     blockW = 0;
329     assert(hiCur >= 0);
330     if (loCur == -1)  {
331       if (hiCur == 0)
332 	blockL = x + tb->lMargin;
333       else  {
334 	blockL = x;
335 	blockW += tb->lMargin;
336       }
337     } else  {
338       assert((loCur >= 0) && (loCur <= ti->len));
339       blockL = x + tb->lMargin + XTextWidth(fs, tb->buf + ti->start, loCur);
340     }
341     if (hiCur <= ti->len)  {
342       blockW += XTextWidth(fs, tb->buf + ti->start + loCur, hiCur - loCur);
343     } else  {
344       assert(hiCur == ti->len + 1);
345       if (loCur == 0)
346 	blockL = x;
347       blockW = w - blockL;
348     }
349     butEnv_setXFg(env, BUT_SELBG);
350     XFillRectangle(env->dpy, win->win, env->gc, blockL-win->xOff, y-win->yOff,
351 		   blockW, h);
352     butEnv_setXFg(env, BUT_FG);
353   }
354   XDrawString(env->dpy, win->win, env->gc,
355 	      x + tb->lMargin - win->xOff, y + fs->ascent - win->yOff,
356 	      tb->buf + ti->start, ti->len);
357   if (!(but->flags & BUT_PRESSABLE))  {
358     butEnv_stdFill2(env);
359   }
360   if ((tb->locs[tbin_loCur].tin == tinNum) && (loCur == hiCur) && tb->cursorVisible &&
361       (but->flags & BUT_PRESSABLE))
362     ti_cursor(but);
363 }
364 
365 
mmove(But * but,int x,int y)366 static ButOut  mmove(But *but, int x, int y)  {
367   Tbin  *tb = but->iPacket;
368   Tin  *ti;
369   int  tinNum;
370   ButWin  *win = but->win;
371   ButEnv  *env = win->env;
372   int  newMousePress;
373   bool  rightSide;
374   int  oldLoTin = tb->locs[tbin_loCur].tin, oldLo = tb->locs[tbin_loCur].index;
375   int  oldHiTin = tb->locs[tbin_hiCur].tin, oldHi = tb->locs[tbin_hiCur].index;
376 
377   if (!(but->flags & BUT_PRESSABLE))
378     return(BUTOUT_CAUGHT);
379   if (!tb->mouseInBut)  {
380     tb->mouseInBut = TRUE;
381     butEnv_setCursor(env, but, butCur_text);
382   }
383   tinNum = calcTinNum(but, tb, y);
384   if ((tinNum >= 0) && (tinNum < tb->maxTins))
385     ti = &tb->tins[tinNum];
386   else
387     ti = NULL;
388   switch(tb->clicks)  {
389   case(0):
390     break;
391   case(1):
392   case(2):
393     if (ti)  {
394       if (y < but->y)  {
395 	newMousePress = 0;
396 	rightSide = FALSE;
397       } else
398 	newMousePress = locateMouse(env->fonts[0], tb->buf + ti->start,
399 				    ti->len,
400 				    x - (but->x+tb->lMargin), &rightSide);
401       if (rightSide && (newMousePress == ti->len) &&
402 	  ((tinNum > tb->locs[tbin_press].tin) ||
403 	   ((tinNum == tb->locs[tbin_press].tin) &&
404 	    (tb->locs[tbin_press].index < newMousePress))))  {
405 	tinNum = tinNum_next(tinNum, tb);
406 	newMousePress = 0;
407 	if (tinNum >= tb->maxTins)  {
408 	  tinNum = tinNum_prev(tb->maxTins, tb);
409 	  ti = NULL;
410 	} else  {
411 	  ti = &tb->tins[tinNum];
412 	}
413       }
414     } else  {
415       newMousePress = 0;
416       rightSide = FALSE;
417     }
418     if (ti && (tb->clicks == 2))  {
419       if (rightSide &&
420 	  ((tinNum > tb->locs[tbin_press].tin) ||
421 	   ((tinNum == tb->locs[tbin_press].tin) &&
422 	    (newMousePress >= tb->locs[tbin_press].index))) &&
423 	  (newMousePress < ti->len) && (newMousePress > 0))
424 	++newMousePress;
425       else if (!rightSide &&
426 	       ((tinNum < tb->locs[tbin_press].tin) ||
427 		((tinNum == tb->locs[tbin_press].tin) &&
428 		 (newMousePress <= tb->locs[tbin_press].index))) &&
429 	       (newMousePress > 0))
430 	--newMousePress;
431     }
432     if ((newMousePress != tb->locs[tbin_mouse].index) ||
433 	(tinNum != tb->locs[tbin_mouse].tin))  {
434       tb->locs[tbin_mouse].index = newMousePress;
435       tb->locs[tbin_mouse].tin = tinNum;
436       assert(loc_valid(tb->locs[tbin_mouse], tb));
437       if ((tb->locs[tbin_mouse].tin < tb->locs[tbin_press].tin) ||
438 	  ((tb->locs[tbin_mouse].tin == tb->locs[tbin_press].tin) &&
439 	   (tb->locs[tbin_mouse].index < tb->locs[tbin_press].index)))  {
440 	tb->locs[tbin_loCur] = tb->locs[tbin_mouse];
441 	tb->locs[tbin_hiCur] = tb->locs[tbin_press];
442       } else  {
443 	tb->locs[tbin_loCur] = tb->locs[tbin_press];
444 	tb->locs[tbin_hiCur] = tb->locs[tbin_mouse];
445       }
446       if (tb->clicks == 2)  {
447 	wordsel_adjust(tb);
448       }
449     }
450     break;
451   case(3):
452     /* Triple-click. */
453     tb->locs[tbin_mouse].tin = tinNum;
454     tb->locs[tbin_mouse].index = 0;
455     tb->locs[tbin_loCur].index = tb->locs[tbin_hiCur].index = 0;
456     if (tb->locs[tbin_mouse].tin < tb->locs[tbin_press].tin)  {
457       tb->locs[tbin_loCur].tin = tb->locs[tbin_mouse].tin;
458       tb->locs[tbin_hiCur].tin = tb->locs[tbin_press].tin;
459     } else  {
460       tb->locs[tbin_loCur].tin = tb->locs[tbin_press].tin;
461       tb->locs[tbin_hiCur].tin = tb->locs[tbin_mouse].tin;
462     }
463     if (tinNum_next(tb->locs[tbin_hiCur].tin, tb) < tb->maxTins)  {
464       tb->locs[tbin_hiCur].tin = tinNum_next(tb->locs[tbin_hiCur].tin, tb);
465     } else
466       tb->locs[tbin_hiCur].index = tb->tins[tb->locs[tbin_hiCur].tin].len;
467     assert(loc_valid(tb->locs[tbin_mouse], tb));
468     break;
469   default:
470     assert(tb->clicks == 4);
471     tb->locs[tbin_mouse].tin = tinNum;
472     assert(loc_valid(tb->locs[tbin_mouse], tb));
473     break;
474   }
475   if (tb->clicks)  {
476     /*
477      * We cannot call this if no clicks.  Why?  Because with no clicks, we
478      *   _could_ be doing a resize (due to a new line being added).
479      *   Resize calls mmove, which would call checkOffWin, which calls
480      *   resize, etc...boom!
481      * Even this is a bit hairy.
482      */
483     checkOffWin(but, y, TRUE);
484   }
485   if ((tb->locs[tbin_loCur].tin != oldLoTin) ||
486       (tb->locs[tbin_loCur].index != oldLo) ||
487       (tb->locs[tbin_hiCur].tin != oldHiTin) ||
488       (tb->locs[tbin_hiCur].index != oldHi))
489     but_draw(but);
490   return(BUTOUT_CAUGHT);
491 }
492 
493 
mleave(But * but)494 static ButOut  mleave(But *but)  {
495   Tbin  *tb = but->iPacket;
496 
497   if (tb->mouseInBut)  {
498     tb->mouseInBut = FALSE;
499     butEnv_setCursor(but->win->env, but, butCur_idle);
500   }
501   return(BUTOUT_CAUGHT);
502 }
503 
504 
mpress(But * but,int butnum,int x,int y)505 static ButOut  mpress(But *but, int butnum, int x, int y)  {
506   Tbin  *tb = but->iPacket;
507   Tin  *ti;
508   ButEnv  *env = but->win->env;
509   static Time  lastPressTime = -1;
510   static int   lastPressNum = -2;
511   int  rightSide;
512 
513   if (tb->cTimer)  {
514     butTimer_reset(tb->cTimer);
515     tb->cursorVisible = TRUE;
516   }
517   tb->locs[tbin_mouse].tin = calcTinNum(but, tb, y);
518   if ((tb->locs[tbin_mouse].tin >= 0) && (tb->locs[tbin_mouse].tin < tb->maxTins))  {
519     ti = &tb->tins[tb->locs[tbin_mouse].tin];
520     if (y < but->y)  {
521       tb->locs[tbin_mouse].index = 0;
522       rightSide = FALSE;
523     } else
524       tb->locs[tbin_mouse].index = locateMouse(env->fonts[0],
525 					       tb->buf + ti->start,
526 					       ti->len,
527 					       x - (but->x+tb->lMargin),
528 					       &rightSide);
529   } else  {
530     ti = NULL;
531     tb->locs[tbin_mouse].index = 0;
532   }
533   switch (butnum)  {
534   case 1:
535     /* Button 1 selects, drags, etc. */
536     assert(loc_valid(tb->locs[tbin_mouse], tb));
537     tb->locs[tbin_loCur] = tb->locs[tbin_hiCur] = tb->locs[tbin_press] =
538       tb->locs[tbin_mouse];
539     if (!tb->readOnly && !(but->flags & BUT_KEYED))
540       but_newFlags(but, but->flags | BUT_KEYED);
541     but_newFlags(but, but->flags | BUT_LOCKED);
542     if ((lastPressTime + BUT_DCLICK > env->eventTime) &&
543 	(lastPressNum + 1 == env->eventNum))  {
544       tb->clicks = tb->prevClickNum;
545       if (tb->clicks == 1)  {
546 	/* It's a double click! */
547 	++tb->clicks;
548 	if (rightSide)
549 	  ++tb->locs[tbin_hiCur].index;
550 	else
551 	  --tb->locs[tbin_loCur].index;
552 	wordsel_adjust(tb);
553       } else if (tb->clicks == 2)  {
554 	/* It's a triple click! */
555 	++tb->clicks;
556 	tb->locs[tbin_loCur].index = 0;
557 	tb->locs[tbin_hiCur].index = 0;
558 	if (tinNum_next(tb->locs[tbin_mouse].tin, tb) < tb->maxTins)
559 	  tb->locs[tbin_hiCur].tin = tinNum_next(tb->locs[tbin_hiCur].tin, tb);
560       } else if (tb->clicks == 3)  {
561 	/* Quadruple click!  Mark everything! */
562 	++tb->clicks;
563 	tb->locs[tbin_loCur].tin = 0;
564 	tb->locs[tbin_hiCur].tin = tinNum_prev(tb->maxTins, tb);
565 	tb->locs[tbin_loCur].index = 0;
566 	tb->locs[tbin_hiCur].index = tb->tins[tb->locs[tbin_hiCur].tin].len;
567       } else  {
568 	/* Pentuple click!  Restart! */
569 	tb->clicks = 1;
570       }
571     } else  {
572       tb->clicks = 1;
573     }
574     lastPressTime = env->eventTime;
575     lastPressNum = env->eventNum;
576     but_draw(but);
577     break;
578   case 2:
579     /* Paste! */
580     if (tb->readOnly)
581       return(BUTOUT_ERR);
582     else
583       paste(env, but);
584     break;
585   default:
586     /*
587      * Weird.  I don't know what to do with mice with more than 3 buttons
588      *   so I guess I might as well beep.  I like beeping.
589      */
590     return(BUTOUT_ERR);
591     break;
592   }
593   checkOffWin(but, y, TRUE);
594   return(BUTOUT_CAUGHT);
595 }
596 
597 
mrelease(But * but,int butnum,int x,int y)598 static ButOut  mrelease(But *but, int butnum, int x, int y)  {
599   Tbin  *tb = but->iPacket;
600 
601   if (tb->cTimer)  {
602     butTimer_reset(tb->cTimer);
603     tb->cursorVisible = TRUE;
604   }
605   if ((butnum == 1) && tb->clicks)  {
606     tb->prevClickNum = tb->clicks;
607     tb->clicks = 0;
608     if ((tb->locs[tbin_loCur].tin != tb->locs[tbin_hiCur].tin) ||
609 	(tb->locs[tbin_loCur].index != tb->locs[tbin_hiCur].index))  {
610       cut(but->win->env, but);
611     }
612     but_newFlags(but, but->flags & ~BUT_LOCKED);
613   }
614   checkOffWin(but, y, TRUE);
615   return(BUTOUT_CAUGHT);
616 }
617 
618 
kpress(But * but,const char * newtext,KeySym keysym)619 static ButOut  kpress(But *but, const char *newtext, KeySym keysym)  {
620   Tbin  *tb = but->iPacket;
621   ButEnv  *env = but->win->env;
622   bool  need_draw = FALSE;
623   ButOut  result = 0;
624   bool  clearMouseX = TRUE;
625   int  drawLo = tb->locs[tbin_loCur].tin, drawHi = tb->locs[tbin_hiCur].tin;
626   Insert  insertResult = insert_none;
627   bool  changePrev = FALSE;
628 
629   assert(but->flags & BUT_KEYED);
630   butTimer_reset(tb->cTimer);
631   tb->cursorVisible = TRUE;
632   if (((keysym >= XK_KP_Space) && (keysym <= XK_KP_9)) ||
633       ((keysym >= XK_space) && (keysym <= XK_ydiaeresis)))  {
634     need_draw = TRUE;
635     switch(newtext[0])  {
636     case '\001':  /* Ctrl-A: Beginning of line. */
637       tb->locs[tbin_loCur].index = 0;
638       tb->locs[tbin_hiCur] = tb->locs[tbin_loCur];
639       break;
640     case '\020':  /* Ctrl-P: Up a line. */
641       clearMouseX = FALSE;
642       if (tb->locs[tbin_loCur].tin == 0)  {
643 	if (tb->locs[tbin_loCur].index || tb->locs[tbin_hiCur].index ||
644 	    tb->locs[tbin_hiCur].tin)  {
645 	  tb->locs[tbin_loCur].index = 0;
646 	  tb->locs[tbin_hiCur] = tb->locs[tbin_loCur];
647 	} else  {
648 	  need_draw = FALSE;
649 	  result |= BUTOUT_ERR;
650 	}
651       } else  {
652 	if (tb->mouseX == -1)
653 	  setMouseX(but, tb, tb->locs[tbin_loCur].tin,
654 		    tb->locs[tbin_loCur].index);
655 	tb->locs[tbin_loCur].tin = tinNum_prev(tb->locs[tbin_loCur].tin, tb);
656 	tb->locs[tbin_loCur].index =
657 	  locateMouse(env->fonts[0],
658 		      tb->buf + tb->tins[tb->locs[tbin_loCur].tin].start,
659 		      tb->tins[tb->locs[tbin_loCur].tin].len,
660 		      tb->mouseX, NULL);
661 	tb->locs[tbin_hiCur] = tb->locs[tbin_loCur];
662       }
663       break;
664     case '\016':  /* Ctrl-N: Down a line. */
665       clearMouseX = FALSE;
666       if (tinNum_next(tb->locs[tbin_hiCur].tin, tb) == tb->maxTins)  {
667 	if (tb->locs[tbin_loCur].tin != tb->locs[tbin_hiCur].tin)  {
668 	  tb->locs[tbin_loCur] = tb->locs[tbin_hiCur];
669 	} else  {
670 	  need_draw = FALSE;
671 	  result |= BUTOUT_ERR;
672 	}
673       } else  {
674 	if (tb->mouseX == -1)
675 	  setMouseX(but, tb, tb->locs[tbin_hiCur].tin,
676 		    tb->locs[tbin_hiCur].index);
677 	tb->locs[tbin_hiCur].tin = tinNum_next(tb->locs[tbin_hiCur].tin, tb);
678 	tb->locs[tbin_hiCur].index =
679 	  locateMouse(env->fonts[0],
680 		      tb->buf + tb->tins[tb->locs[tbin_hiCur].tin].start,
681 		      tb->tins[tb->locs[tbin_hiCur].tin].len,
682 		      tb->mouseX, NULL);
683 	tb->locs[tbin_loCur] = tb->locs[tbin_hiCur];
684       }
685       break;
686     case '\002':  /* Ctrl-B: Back a character. */
687       if ((tb->locs[tbin_loCur].tin != tb->locs[tbin_hiCur].tin) ||
688 	  (tb->locs[tbin_loCur].index != tb->locs[tbin_hiCur].index))  {
689 	tb->locs[tbin_hiCur] = tb->locs[tbin_loCur];
690 	need_draw = TRUE;
691       } else if (tb->locs[tbin_loCur].index)  {
692 	tb->locs[tbin_hiCur].index = --tb->locs[tbin_loCur].index;
693       } else if (tb->locs[tbin_loCur].tin)  {
694 	tb->locs[tbin_loCur].tin = tinNum_prev(tb->locs[tbin_loCur].tin, tb);
695 	tb->locs[tbin_loCur].index = tb->tins[tb->locs[tbin_loCur].tin].len;
696 	tb->locs[tbin_hiCur] = tb->locs[tbin_loCur];
697       } else  {
698 	need_draw = FALSE;
699 	result |= BUTOUT_ERR;
700       }
701       break;
702     case '\004':  /* Ctrl-D: Delete right. */
703       if ((tb->locs[tbin_loCur].index != tb->locs[tbin_hiCur].index) ||
704 	  (tb->locs[tbin_loCur].tin != tb->locs[tbin_hiCur].tin))  {
705 	insertResult = insert(env, "", tb, but->w, &changePrev);
706 	need_draw = TRUE;
707       } else if (tinNum_next(tb->locs[tbin_loCur].tin, tb) < tb->maxTins)  {
708 	if (tb->locs[tbin_loCur].index <
709 	    tb->tins[tb->locs[tbin_loCur].tin].len)
710 	  ++tb->locs[tbin_hiCur].index;
711 	else if (tb->buf[tb->loBreak - 1] == '\n')  {
712 	  tb->locs[tbin_hiCur].tin = tinNum_next(tb->locs[tbin_hiCur].tin, tb);
713 	  tb->locs[tbin_hiCur].index = 0;
714 	} else  {
715 	  tb->locs[tbin_hiCur].tin = tinNum_next(tb->locs[tbin_hiCur].tin, tb);
716 	  tb->locs[tbin_hiCur].index = 1;
717 	}
718 	insertResult = insert(env, "", tb, but->w, &changePrev);
719 	need_draw = TRUE;
720       } else
721 	result |= BUTOUT_ERR;
722       break;
723     case '\005':  /* Ctrl-E: End of line. */
724       tb->locs[tbin_hiCur].index = tb->tins[tb->locs[tbin_hiCur].tin].len;
725       tb->locs[tbin_loCur] = tb->locs[tbin_hiCur];
726       break;
727     case '\006':  /* Ctrl-F: Forward a character. */
728       need_draw = TRUE;
729       if ((tb->locs[tbin_loCur].tin != tb->locs[tbin_hiCur].tin) ||
730 	  (tb->locs[tbin_loCur].index != tb->locs[tbin_hiCur].index))  {
731 	tb->locs[tbin_loCur] = tb->locs[tbin_hiCur];
732       } else if (tb->locs[tbin_loCur].index <
733 		 tb->tins[tb->locs[tbin_loCur].tin].len)  {
734 	tb->locs[tbin_hiCur].index = ++tb->locs[tbin_loCur].index;
735       } else if (tinNum_next(tb->locs[tbin_loCur].tin, tb) < tb->maxTins)  {
736 	tb->locs[tbin_loCur].tin = tinNum_next(tb->locs[tbin_loCur].tin, tb);
737 	tb->locs[tbin_loCur].index = 0;
738 	tb->locs[tbin_hiCur] = tb->locs[tbin_loCur];
739       } else  {
740 	result |= BUTOUT_ERR;
741 	need_draw = FALSE;
742       }
743       break;
744     case '\013':  /* Ctrl-K: Kill to end of line. */
745       need_draw = TRUE;
746       if ((tb->locs[tbin_loCur].index != tb->locs[tbin_hiCur].index) ||
747 	  (tb->locs[tbin_loCur].tin != tb->locs[tbin_hiCur].tin))
748 	insertResult = insert(env, "", tb, but->w, &changePrev);
749       else if (tinNum_next(tb->locs[tbin_loCur].tin, tb) < tb->maxTins)  {
750 	if (tb->locs[tbin_loCur].index ==
751 	    tb->tins[tb->locs[tbin_loCur].tin].len)  {
752 	  tb->locs[tbin_hiCur].tin = tinNum_next(tb->locs[tbin_loCur].tin, tb);
753 	  tb->locs[tbin_hiCur].index = 0;
754 	} else  {
755 	  tb->locs[tbin_hiCur].tin = tb->locs[tbin_loCur].tin;
756 	  tb->locs[tbin_hiCur].index = tb->tins[tb->locs[tbin_loCur].tin].len;
757 	}
758 	insertResult = insert(env, "", tb, but->w, &changePrev);
759       } else  {
760 	result |= BUTOUT_ERR;
761 	need_draw = FALSE;
762       }
763       break;
764     case '\025':  /* Ctrl-U: Kill from beginning of line. */
765       if ((tb->locs[tbin_loCur].index != tb->locs[tbin_hiCur].index) ||
766 	  (tb->locs[tbin_loCur].tin != tb->locs[tbin_hiCur].tin))
767 	insertResult = insert(env, "", tb, but->w, &changePrev);
768       else if (tb->locs[tbin_loCur].index > 0)  {
769 	tb->locs[tbin_hiCur].index = 0;
770 	tb->locs[tbin_hiCur].tin = tb->locs[tbin_loCur].tin;
771 	insertResult = insert(env, "", tb, but->w, &changePrev);
772       } else  {
773 	result |= BUTOUT_ERR;
774 	need_draw = FALSE;
775       }
776       break;
777     default:
778       insertResult = insert(env, newtext, tb, but->w, &changePrev);
779       break;
780     }
781   } else if ((keysym >= XK_Shift_L) && (keysym <= XK_Hyper_R))  {
782   } else if ((keysym >= XK_F1) && (keysym <= XK_F35))  {
783     insertResult = insert(env, newtext, tb, but->w, &changePrev);
784   } else if ((keysym == XK_BackSpace) || (keysym == XK_Delete))  {
785     if ((tb->locs[tbin_loCur].index != tb->locs[tbin_hiCur].index) ||
786 	(tb->locs[tbin_loCur].tin != tb->locs[tbin_hiCur].tin))  {
787       insertResult = insert(env, "", tb, but->w, &changePrev);
788       need_draw = TRUE;
789     } else if (tb->locs[tbin_loCur].index > 0)  {
790       --tb->locs[tbin_loCur].index;
791       assert((drawHi < tb->loTinBreak) || (drawHi >= tb->hiTinBreak));
792       drawHi = tinNum_line(drawHi, tb);
793       insertResult = insert(env, "", tb, but->w, &changePrev);
794       drawHi = tinNum_line2TinNum(drawHi, tb);
795       assert((drawHi < tb->loTinBreak) || (drawHi >= tb->hiTinBreak));
796       need_draw = TRUE;
797     } else if (tb->locs[tbin_loCur].tin > 0)  {
798       if (tb->buf[tb->tins[tb->locs[tbin_loCur].tin].start - 1] == '\n')  {
799 	tb->locs[tbin_loCur].tin = tinNum_prev(tb->locs[tbin_loCur].tin, tb);
800 	tb->locs[tbin_loCur].index = tb->tins[tb->locs[tbin_loCur].tin].len;
801       } else  {
802 	tb->locs[tbin_loCur].tin = tinNum_prev(tb->locs[tbin_loCur].tin, tb);
803 	tb->locs[tbin_loCur].index = tb->tins[tb->locs[tbin_loCur].tin].len - 1;
804       }
805       insertResult = insert(env, "", tb, but->w, &changePrev);
806       need_draw = TRUE;
807     } else {
808       result |= BUTOUT_ERR;
809     }
810 #if 0  /* People don't like my delete.  :-( */
811   } else if (keysym == XK_Delete)  {
812     if ((tb->locs[tbin_loCur].index != tb->locs[tbin_hiCur].index) ||
813 	(tb->locs[tbin_loCur].tin != tb->locs[tbin_hiCur].tin))  {
814       insertResult = insert(env, "", tb, but->w, &changePrev);
815       need_draw = TRUE;
816     } else if (tinNum_next(tb->locs[tbin_loCur].tin, tb) < tb->maxTins)  {
817       if (tb->locs[tbin_loCur].index < tb->tins[tb->locs[tbin_loCur].tin].len)
818 	++tb->locs[tbin_hiCur].index;
819       else if (tb->buf[tb->loBreak - 1] == '\n')  {
820 	tb->locs[tbin_hiCur].tin = tinNum_next(tb->locs[tbin_hiCur].tin, tb);
821 	tb->locs[tbin_hiCur].index = 0;
822       } else  {
823 	tb->locs[tbin_hiCur].tin = tinNum_next(tb->locs[tbin_hiCur].tin, tb);
824 	tb->locs[tbin_hiCur].index = 1;
825       }
826       insertResult = insert(env, "", tb, but->w, &changePrev);
827       need_draw = TRUE;
828     } else
829       result |= BUTOUT_ERR;
830 #endif
831   } else if (keysym == XK_Left)  {
832     need_draw = TRUE;
833     if (!loc_eq(tb->locs[tbin_loCur], tb->locs[tbin_hiCur]))  {
834       tb->locs[tbin_hiCur] = tb->locs[tbin_loCur];
835     } else if (tb->locs[tbin_loCur].index)  {
836       tb->locs[tbin_hiCur].index = --tb->locs[tbin_loCur].index;
837     } else if (tb->locs[tbin_loCur].tin)  {
838       tb->locs[tbin_loCur].tin = tinNum_prev(tb->locs[tbin_loCur].tin, tb);
839       tb->locs[tbin_loCur].index = tb->tins[tb->locs[tbin_loCur].tin].len;
840       tb->locs[tbin_hiCur] = tb->locs[tbin_loCur];
841     } else
842       result |= BUTOUT_ERR;
843   } else if (keysym == XK_Right)  {
844     need_draw = TRUE;
845     if (!loc_eq(tb->locs[tbin_loCur], tb->locs[tbin_hiCur]))  {
846       tb->locs[tbin_loCur] = tb->locs[tbin_hiCur];
847     } else if (tb->locs[tbin_loCur].index <
848 	       tb->tins[tb->locs[tbin_loCur].tin].len)  {
849       tb->locs[tbin_hiCur].index = ++tb->locs[tbin_loCur].index;
850     } else if (tinNum_next(tb->locs[tbin_loCur].tin, tb) < tb->maxTins)  {
851       tb->locs[tbin_loCur].tin = tinNum_next(tb->locs[tbin_loCur].tin, tb);
852       tb->locs[tbin_loCur].index = 0;
853       tb->locs[tbin_hiCur] = tb->locs[tbin_loCur];
854     } else
855       result |= BUTOUT_ERR;
856   } else if (keysym == XK_Up)  {
857     clearMouseX = FALSE;
858     if (tb->locs[tbin_loCur].tin == 0)  {
859       if (!loc_eq(tb->locs[tbin_loCur], tb->locs[tbin_hiCur]))  {
860 	tb->locs[tbin_hiCur] = tb->locs[tbin_loCur];
861 	need_draw = TRUE;
862       } else
863 	result |= BUTOUT_ERR;
864     } else  {
865       if (tb->mouseX == -1)
866 	setMouseX(but, tb, tb->locs[tbin_loCur].tin,
867 		  tb->locs[tbin_loCur].index);
868       tb->locs[tbin_loCur].tin = tinNum_prev(tb->locs[tbin_loCur].tin, tb);
869       tb->locs[tbin_loCur].index = locateMouse(env->fonts[0],
870 					       tb->buf +
871 					       tb->tins[tb->locs[tbin_loCur].
872 							tin].start,
873 					       tb->tins[tb->locs[tbin_loCur].
874 							tin].len,
875 					       tb->mouseX, NULL);
876       tb->locs[tbin_hiCur] = tb->locs[tbin_loCur];
877       need_draw = TRUE;
878     }
879   } else if (keysym == XK_Down)  {
880     clearMouseX = FALSE;
881     assert(loc_valid(tb->locs[tbin_loCur], tb));
882     assert(loc_valid(tb->locs[tbin_hiCur], tb));
883     if (tinNum_next(tb->locs[tbin_hiCur].tin, tb) == tb->maxTins)  {
884       if (loc_eq(tb->locs[tbin_loCur], tb->locs[tbin_hiCur]))
885 	result |= BUTOUT_ERR;
886       else  {
887 	tb->locs[tbin_loCur] = tb->locs[tbin_hiCur];
888       }
889     } else  {
890       if (tb->mouseX == -1)
891 	setMouseX(but, tb, tb->locs[tbin_hiCur].tin,
892 		  tb->locs[tbin_hiCur].index);
893       tb->locs[tbin_hiCur].tin = tinNum_next(tb->locs[tbin_hiCur].tin, tb);
894       tb->locs[tbin_hiCur].index =
895 	locateMouse(env->fonts[0],
896 		    tb->buf + tb->tins[tb->locs[tbin_hiCur].tin].start,
897 		    tb->tins[tb->locs[tbin_hiCur].tin].len,
898 		    tb->mouseX, NULL);
899       tb->locs[tbin_loCur] = tb->locs[tbin_hiCur];
900     }
901     need_draw = TRUE;
902   } else if ((keysym == XK_Return) || (keysym == XK_Linefeed) ||
903 	     (keysym == XK_KP_Enter))  {
904     insertResult = insert(env, "\n", tb, but->w, &changePrev);
905   } else {
906     result |= BUTOUT_ERR;
907   }
908   /* Void all mouse movement until it is pressed again. */
909   if (tb->clicks)  {
910     tb->clicks = 0;
911     if (but->flags & BUT_LOCKED)
912       but_newFlags(but, but->flags & ~BUT_LOCKED);
913   }
914   if (need_draw || (insertResult != insert_none))  {
915     result |= BUTOUT_CAUGHT;
916     if (tb->locs[tbin_loCur].tin < drawLo)
917       drawLo = tb->locs[tbin_loCur].tin;
918     if (tb->locs[tbin_hiCur].tin > drawHi) {
919       drawHi = tb->locs[tbin_hiCur].tin;
920       assert((drawHi < tb->loTinBreak) || (drawHi >= tb->hiTinBreak));
921     }
922     if (changePrev && drawLo)  {
923       drawLo = tinNum_prev(drawLo, tb);
924     }
925     assert(drawHi >= drawLo);
926     drawLo = tinNum_line(drawLo, tb) * butEnv_fontH(env, 0);
927     if (insertResult == insert_cr) {
928       drawHi = but->h;
929     } else {
930       assert((drawHi < tb->loTinBreak) || (drawHi >= tb->hiTinBreak));
931       drawHi = (tinNum_line(drawHi, tb) + 1) * butEnv_fontH(env, 0);
932     }
933     if (drawLo < but->h)  {
934       assert(drawHi >= drawLo);
935       if (drawHi > but->h) {
936 	drawHi = but->h;
937 	assert(drawHi > drawLo);
938       }
939       butWin_redraw(but->win, but->x,but->y+drawLo, but->w,drawHi - drawLo);
940     }
941   }
942   if (clearMouseX)
943     tb->mouseX = -1;
944   if (insertResult == insert_bad)
945     result |= BUTOUT_ERR;
946   checkOffWin(but, 0, FALSE);
947   return(result);
948 }
949 
950 
insert(ButEnv * env,const char * src,Tbin * tb,int butW,bool * drawLo)951 static Insert  insert(ButEnv *env, const char *src, Tbin *tb, int butW,
952 		      bool *drawLo)  {
953   int  i, len, loTinWidthCheck;
954   Insert  result = insert_ok;
955   bool  lineBroken, dummy;
956 
957   if (drawLo == NULL)
958     drawLo = &dummy;
959   butW -= tb->lMargin + tb->rMargin;
960   assert(tb->loTinBreak <= tb->hiTinBreak);
961   assert(loc_valid(tb->locs[tbin_loCur], tb));
962   assert(loc_valid(tb->locs[tbin_hiCur], tb));
963   assert(tb->maxTins == tb->numTins + tb->hiTinBreak - tb->loTinBreak);
964   adjustBreak(tb, break_loCur, 0);
965   assert(tb->tins[tb->locs[tbin_loCur].tin].start +
966 	 tb->locs[tbin_loCur].index == tb->loBreak);
967   assert(loc_valid(tb->locs[tbin_loCur], tb));
968   assert(loc_valid(tb->locs[tbin_hiCur], tb));
969   assert(tb->maxTins == tb->numTins + tb->hiTinBreak - tb->loTinBreak);
970   if (!loc_eq(tb->locs[tbin_loCur], tb->locs[tbin_hiCur]))  {
971     /*
972      * Cut out any deleted text.
973      */
974     assert(loc_valid(tb->locs[tbin_mouse], tb));
975     assert(loc_valid(tb->locs[tbin_press], tb));
976     if (tb->locs[tbin_hiCur].tin != tb->locs[tbin_loCur].tin)  {
977       result = insert_cr;
978       i = tb->locs[tbin_hiCur].tin + 1 - tb->hiTinBreak;
979       tb->numTins -= i,
980       tb->hiTinBreak += i;
981     }
982     tb->tins[tb->locs[tbin_loCur].tin].len = tb->locs[tbin_loCur].index +
983       tb->tins[tb->locs[tbin_hiCur].tin].len - tb->locs[tbin_hiCur].index;
984     if (tb->locs[tbin_loCur].tin == tb->locs[tbin_hiCur].tin)  {
985       tb->hiBreak += tb->locs[tbin_hiCur].index - tb->locs[tbin_loCur].index;
986     } else
987       tb->hiBreak = tb->tins[tb->locs[tbin_hiCur].tin].start +
988 	tb->locs[tbin_hiCur].index;
989     tb->locs[tbin_hiCur] = tb->locs[tbin_loCur];
990     if (!loc_valid(tb->locs[tbin_mouse], tb))
991       tb->locs[tbin_mouse] = tb->locs[tbin_loCur];
992     if (!loc_valid(tb->locs[tbin_press], tb))
993       tb->locs[tbin_press] = tb->locs[tbin_loCur];
994   }
995   assert(tb->tins[tb->locs[tbin_loCur].tin].start +
996 	 tb->locs[tbin_loCur].index == tb->loBreak);
997   assert(tb->loTinBreak <= tb->hiTinBreak);
998   assert(tb->maxTins == tb->numTins + tb->hiTinBreak - tb->loTinBreak);
999   if (src == NULL)  {
1000     adjustBreak(tb, break_eoLoCur, 0);
1001     return(insert_bad);
1002   }
1003   len = strlen(src);
1004   /*
1005    * We must use <= in this next condition because we may add an extra \n to
1006    *   the buffer at the end.
1007    */
1008   while (tb->hiBreak - tb->loBreak <= len)  {
1009     addMoreBufferSpace(tb);
1010   }
1011   assert(tb->tins[tb->locs[tbin_loCur].tin].start >= 0);
1012   loTinWidthCheck = tinNum_line(tb->locs[tbin_loCur].tin, tb);
1013   for (i = 0;  i < len;  ++i)  {
1014     assert(tb->loTinBreak <= tb->hiTinBreak);
1015     assert(tb->tins[tb->locs[tbin_loCur].tin].start +
1016 	   tb->locs[tbin_loCur].index == tb->loBreak);
1017     tb->buf[tb->loBreak++] = src[i];
1018     if (src[i] == '\n')  {
1019       /*
1020        * If the only line left is the mandatory blank line at the end, then
1021        *   don't bother inserting the final '\n'.
1022        */
1023       if ((i + 1 == len) &&
1024 	  (tinNum_next(tb->locs[tbin_loCur].tin, tb) ==
1025 	   tinNum_prev(tb->maxTins, tb)) &&
1026 	  (tb->locs[tbin_loCur].index ==
1027 	   tb->tins[tb->locs[tbin_loCur].tin].len))  {
1028 	--tb->loBreak;
1029 	tb->locs[tbin_loCur].tin = tinNum_next(tb->locs[tbin_loCur].tin, tb);
1030 	tb->locs[tbin_loCur].index = 0;
1031 	break;
1032       }
1033       result = insert_cr;
1034       if (tb->numTins == tb->maxTins)
1035 	addMoreTins(tb);
1036       tb->tins[tb->locs[tbin_loCur].tin + 1].start = tb->loBreak;
1037       tb->tins[tb->locs[tbin_loCur].tin + 1].len =
1038 	tb->tins[tb->locs[tbin_loCur].tin].len - tb->locs[tbin_loCur].index;
1039       tb->tins[tb->locs[tbin_loCur].tin].len = tb->locs[tbin_loCur].index;
1040       ++tb->numTins;
1041       ++tb->loTinBreak;
1042       ++tb->locs[tbin_loCur].tin;
1043       tb->locs[tbin_loCur].index = 0;
1044     } else  {
1045       ++tb->locs[tbin_loCur].index;
1046       ++tb->tins[tb->locs[tbin_loCur].tin].len;
1047     }
1048   }
1049   assert(tb->tins[tb->locs[tbin_loCur].tin].start >= 0);
1050   assert(tb->maxTins == tb->numTins + tb->hiTinBreak - tb->loTinBreak);
1051   if ((tb->hiBreak == tb->bufLen) && (tb->tins[tb->loTinBreak - 1].len > 0) &&
1052       !tb->readOnly)  {
1053     /*
1054      * Add an extra '\n' to make sure that there's always a blank line at
1055      *   the end of the buffer.
1056      */
1057     tb->buf[tb->loBreak++] = '\n';
1058     result = insert_cr;
1059     if (tb->numTins == tb->maxTins)
1060       addMoreTins(tb);
1061     assert(tb->tins[tb->locs[tbin_loCur].tin].start >= 0);
1062     tb->tins[tb->locs[tbin_loCur].tin + 1].start = tb->loBreak;
1063     tb->tins[tb->locs[tbin_loCur].tin + 1].len = 0;
1064     tb->tins[tb->locs[tbin_loCur].tin + 1].width = 0;
1065     ++tb->numTins;
1066     ++tb->loTinBreak;
1067   }
1068   tb->locs[tbin_hiCur] = tb->locs[tbin_loCur];
1069   adjustBreak(tb, break_eoLoCur, 0);
1070   assert(loc_valid(tb->locs[tbin_loCur], tb));
1071   assert(loc_valid(tb->locs[tbin_hiCur], tb));
1072   assert(tb->maxTins == tb->numTins + tb->hiTinBreak - tb->loTinBreak);
1073   assert(tb->hiBreak <= tb->bufLen);
1074   assert(tb->loBreak <= tb->bufLen);
1075   assert(tb->hiBreak >= 0);
1076   assert(tb->loBreak >= 0);
1077   assert(tb->tins[tb->locs[tbin_loCur].tin].start >= 0);
1078   /*
1079    * Now that the text is inserted, it is time to adjust the EOLs if we have
1080    *   to.
1081    */
1082   tryJoinLines(tb, tinNum_line2TinNum(loTinWidthCheck, tb), env, butW);
1083   lineBroken = TRUE;
1084   for (i = tinNum_line2TinNum(loTinWidthCheck, tb);
1085        (i < tb->maxTins) && ((i <= tb->locs[tbin_loCur].tin) || lineBroken);
1086        i = tinNum_next(i, tb))  {
1087     if (calcTinWidth(&tb->tins[i], tb->buf, env) > butW)  {
1088       lineBroken = TRUE;
1089       breakLine(tb, i, env, butW);
1090       result = insert_cr;
1091     }
1092   }
1093   if (tryJoinLines(tb, tinNum_next(tb->locs[tbin_loCur].tin, tb), env, butW))
1094     result = insert_cr;
1095   if ((tb->maxLines > 0) && (tb->numTins > tb->maxLines))  {
1096     killTopLines(tb, tb->numTins - tb->maxLines);
1097   }
1098   return(result);
1099 }
1100 
1101 
1102 /*
1103  * This routine attempts to join the line that you specify with the
1104  *   previous line.
1105  * You cannot call this unless the current break is in the middle of neither
1106  *   of these lines.  It is OK to call this if the current break is in between
1107  *   these lines.
1108  * The breakpoint may be changed while in this function.
1109  */
tryJoinLines(Tbin * tb,int tinNum,ButEnv * env,int butW)1110 static bool  tryJoinLines(Tbin *tb, int tinNum, ButEnv *env, int butW)  {
1111   int  prevTinNum, w, curChar;
1112   Tin  *a, *b;
1113   XFontStruct  *fs = env->fonts[0];
1114   TbinLoc  loc;
1115 
1116   if ((tinNum == 0) || (tinNum == tb->maxTins))
1117     return(FALSE);
1118   prevTinNum = tinNum_prev(tinNum, tb);
1119   a = &tb->tins[prevTinNum];
1120   b = &tb->tins[tinNum];
1121   if (tinNum == tb->hiTinBreak)  {
1122     if (b->start != tb->hiBreak)
1123       return(FALSE);
1124   } else  {
1125     if (b->start != a->start + a->len)
1126       return(FALSE);
1127   }
1128   w = a->width;
1129   curChar = a->len - 1;
1130   while ((curChar >= 0) && (tb->buf[a->start + curChar] == ' '))  {
1131     --curChar;
1132     if (fs->per_char == NULL)
1133       w += fs->min_bounds.width;
1134     else
1135       w += fs->per_char[(uchar)tb->buf[a->start + curChar] -
1136 			fs->min_char_or_byte2].width;
1137   }
1138   curChar = 0;
1139   while ((curChar < b->len) && (w <= butW))  {
1140     if (fs->per_char == NULL)
1141       w += fs->min_bounds.width;
1142     else
1143       w += fs->per_char[(uchar)tb->buf[b->start + curChar] -
1144 			fs->min_char_or_byte2].width;
1145     ++curChar;
1146   }
1147   if (w > butW)  {
1148     if (curChar)  {
1149       --curChar;
1150       while (curChar && (tb->buf[b->start + curChar] != ' '))
1151 	--curChar;
1152       while ((curChar < b->len) &&
1153 	     (tb->buf[b->start + curChar] == ' '))
1154 	++curChar;
1155     }
1156   }
1157   if (!curChar)
1158     return(FALSE);
1159   if (curChar == b->len)  {
1160     /* We can suck the whole damn line up. */
1161     adjustBreak(tb, break_eol, tinNum);
1162     tinNum = tb->loTinBreak - 1;
1163     tbinLoc_iter(loc)  {
1164       if (tb->locs[loc].tin == tinNum)  {
1165 	tb->locs[loc].index += tb->tins[tinNum - 1].len;
1166 	--tb->locs[loc].tin;
1167       }
1168     }
1169     tb->tins[tinNum - 1].len += tb->tins[tinNum].len;
1170     calcTinWidth(&tb->tins[tinNum - 1], tb->buf, env);
1171     --tb->numTins;
1172     --tb->loTinBreak;
1173     assert((tb->loTinBreak == 0) ||
1174 	   (tb->tins[tb->loTinBreak - 1].start < tb->loBreak));
1175   } else  {
1176     tbinLoc_iter(loc)  {
1177       if (tb->locs[loc].tin == tinNum)  {
1178 	tb->locs[loc].index -= curChar;
1179 	if (tb->locs[loc].index <= 0)  {
1180 	  tb->locs[loc].tin = prevTinNum;
1181 	  tb->locs[loc].index += a->len + curChar;
1182 	}
1183       }
1184     }
1185     a->len += curChar;
1186     b->len -= curChar;
1187     b->start += curChar;
1188     if (tb->hiTinBreak == tinNum)  {
1189       adjustBreak(tb, break_eol, prevTinNum);
1190       calcTinWidth(&tb->tins[tb->loTinBreak - 1], tb->buf, env);
1191       calcTinWidth(&tb->tins[tb->hiTinBreak], tb->buf, env);
1192     }
1193     assert((tb->loTinBreak == 0) ||
1194 	   (tb->tins[tb->loTinBreak - 1].start < tb->loBreak));
1195   }
1196   return(TRUE);
1197 }
1198 
1199 
breakLine(Tbin * tb,int tinNum,ButEnv * env,int butW)1200 static void  breakLine(Tbin *tb, int tinNum, ButEnv *env, int butW)  {
1201   XFontStruct  *fs = env->fonts[0];
1202   int  breakPoint, w;
1203   TbinLoc  loc;
1204 
1205   assert(tinNum < tb->maxTins);
1206   assert((tinNum < tb->loTinBreak) || (tinNum >= tb->hiTinBreak));
1207   if (butW <= 0)  {
1208     return;
1209   }
1210   assert(loc_valid(tb->locs[tbin_loCur], tb));
1211   adjustBreak(tb, break_eol, tinNum);
1212   /*
1213    * I really should make my mark into a loc, but I'll cheese out instead.
1214    */
1215   tinNum = tb->loTinBreak - 1;
1216   assert(loc_valid(tb->locs[tbin_loCur], tb));
1217   breakPoint = tb->tins[tinNum].len;
1218   w = tb->tins[tinNum].width;
1219   assert(w > butW);
1220   while (tb->buf[tb->tins[tinNum].start + breakPoint - 1] == ' ')  {
1221     --breakPoint;
1222     assert(breakPoint > 0);
1223   }
1224   while (breakPoint > 0)  {
1225     if ((tb->buf[tb->tins[tinNum].start + breakPoint] == ' ') &&
1226 	(tb->buf[tb->tins[tinNum].start + breakPoint - 1] != ' ') &&
1227 	(w <= butW))
1228       break;
1229     --breakPoint;
1230     if (fs->per_char == NULL)
1231       w -= fs->min_bounds.width;
1232     else
1233       w -= fs->per_char[(uchar)tb->buf[tb->tins[tinNum].start + breakPoint] -
1234 			fs->min_char_or_byte2].width;
1235   }
1236   if (breakPoint == 0)  {
1237     adjustBreak(tb, break_eoLoCur, 0);
1238     return;
1239   }
1240   while (tb->buf[tb->tins[tinNum].start + breakPoint] == ' ')
1241     ++breakPoint;
1242   if (tb->numTins == tb->maxTins)
1243     addMoreTins(tb);
1244   assert(tb->loTinBreak == tinNum + 1);
1245   if ((tb->hiTinBreak < tb->maxTins) &&
1246       (tb->tins[tb->hiTinBreak].start == tb->hiBreak))  {
1247     /*
1248      * There is no '\n' here, so we can just shuffle the too-long characters
1249      *   down to the next line.
1250      */
1251     tb->tins[tb->hiTinBreak].len += tb->tins[tinNum].len - breakPoint;
1252     tb->tins[tb->hiTinBreak].start = tb->tins[tinNum].start + breakPoint;
1253     tb->tins[tb->hiTinBreak].width +=
1254       XTextWidth(env->fonts[0], tb->buf + tb->tins[tb->hiTinBreak].start,
1255 		 tb->tins[tinNum].len - breakPoint);
1256     tb->tins[tb->loTinBreak] = tb->tins[tb->hiTinBreak];
1257     ++tb->loTinBreak;
1258     ++tb->hiTinBreak;
1259   } else  {
1260     tb->tins[tinNum + 1].start = tb->tins[tinNum].start + breakPoint;
1261     tb->tins[tinNum + 1].len = tb->tins[tinNum].len - breakPoint;
1262     ++tb->loTinBreak;
1263     ++tb->numTins;
1264   }
1265   tb->tins[tinNum].len = breakPoint;
1266   tb->tins[tinNum].width = w;
1267   tbinLoc_iter(loc)  {
1268     if ((tb->locs[loc].tin == tinNum) &&
1269 	(tb->locs[loc].index > tb->tins[tinNum].len))  {
1270       tb->locs[loc].index -= tb->tins[tinNum].len;
1271       tb->locs[loc].tin = tinNum_next(tb->locs[loc].tin, tb);
1272     }
1273   }
1274   ++tinNum;
1275   calcTinWidth(&tb->tins[tinNum], tb->buf, env);
1276   assert(loc_valid(tb->locs[tbin_loCur], tb));
1277   adjustBreak(tb, break_eoLoCur, 0);
1278   assert(loc_valid(tb->locs[tbin_loCur], tb));
1279 }
1280 
1281 
ti_cursor(But * but)1282 static void  ti_cursor(But *but)  {
1283   Tbin  *tb = but->iPacket;
1284   Tin  *ti = &tb->tins[tb->locs[tbin_loCur].tin];
1285   ButWin  *win = but->win;
1286   ButEnv  *env = win->env;
1287   XFontStruct  *fs = env->fonts[0];
1288   int  x, y;
1289   int  rw, rh;
1290 
1291   if (!loc_eq(tb->locs[tbin_loCur], tb->locs[tbin_hiCur]) || !tb->cursorVisible)
1292     return;
1293   x = but->x + XTextWidth(fs, tb->buf + ti->start,
1294 			  tb->locs[tbin_loCur].index) + tb->lMargin;
1295   y = but->y + tinNum_line(tb->locs[tbin_loCur].tin, tb) * (fs->ascent + fs->descent);
1296   if ((rw = (fs->ascent + fs->descent + 10) / 20) < 1)
1297     rw = 1;
1298   x -= rw/2;
1299   if (x < but->x)  {
1300     rw -= but->x - x;
1301     x = but->x;
1302   } else if (x+rw > but->x + but->w)  {
1303     rw = but->x + but->w - x;
1304   }
1305   rh = fs->ascent + fs->descent;
1306   if (y + rh > but->y + but->h)
1307     rh = but->y + but->h - y;
1308   if ((rw <= 0) || (rh <= 0))
1309     return;
1310   butEnv_setXFg(env, BUT_FG);
1311   XFillRectangle(env->dpy, win->win, env->gc, x-win->xOff, y-win->yOff,
1312 		 rw,rh);
1313 }
1314 
1315 
ti_redrawCursor(But * but)1316 static void  ti_redrawCursor(But *but)  {
1317   Tbin  *tb = but->iPacket;
1318   Tin  *ti = &tb->tins[tb->locs[tbin_loCur].tin];
1319   ButWin  *win = but->win;
1320   ButEnv  *env = win->env;
1321   XFontStruct  *fs = env->fonts[0];
1322   int  fontH = fs->ascent + fs->descent;
1323   int  x, y;
1324   int  rw;
1325 
1326   if (!loc_eq(tb->locs[tbin_loCur], tb->locs[tbin_hiCur]))  {
1327     return;
1328   }
1329   x = but->x + XTextWidth(fs, tb->buf + ti->start,
1330 			  tb->locs[tbin_loCur].index) + tb->lMargin;
1331   y = but->y + tinNum_line(tb->locs[tbin_loCur].tin, tb) * fontH;
1332   if ((rw = (fs->ascent + fs->descent + 10) / 20) < 1)
1333     rw = 1;
1334   x -= rw/2;
1335   if (x < but->x)  {
1336     rw -= (but->x - x);
1337     x = but->x;
1338   } else if (x+rw > but->x + but->w)  {
1339     rw = but->x + but->w - x;
1340   }
1341   if (rw <= 0)
1342     return;
1343   butWin_redraw(win, x,y, rw,fs->ascent + fs->descent);
1344 }
1345 
1346 
cut(ButEnv * env,But * but)1347 static void  cut(ButEnv *env, But *but)  {
1348   Tbin  *tbin = but->iPacket;
1349   int  storeLen;
1350 
1351   adjustBreak(tbin, break_loCur, 0);
1352   if (tbin->locs[tbin_loCur].tin == tbin->locs[tbin_hiCur].tin)
1353     storeLen = tbin->locs[tbin_hiCur].index - tbin->locs[tbin_loCur].index;
1354   else
1355     storeLen = tbin->locs[tbin_hiCur].index + tbin->tins[tbin->locs[tbin_hiCur].tin].start -
1356       tbin->hiBreak;
1357   XStoreBytes(env->dpy, tbin->buf + tbin->hiBreak, storeLen);
1358   adjustBreak(tbin, break_eoLoCur, 0);
1359   if ((env->sReq == NULL) ||
1360       ((env->sReq == sreq) && (cut_butnum == but)))
1361     XSetSelectionOwner(env->dpy, XA_PRIMARY, but->win->physWin, 0);
1362   else
1363     env->sClear(env);
1364   if (XGetSelectionOwner(env->dpy, XA_PRIMARY) == but->win->physWin)  {
1365     env->sReq = sreq;
1366     env->sClear = sclear;
1367     cut_butnum = but;
1368   }
1369 }
1370 
1371 
sreq(ButEnv * env,XSelectionRequestEvent * xsre)1372 static bool  sreq(ButEnv *env, XSelectionRequestEvent *xsre)  {
1373   Tbin  *tbin;
1374   int  storeLen;
1375 
1376   if (xsre->target != XA_STRING)
1377     return(FALSE);
1378   tbin = cut_butnum->iPacket;
1379   adjustBreak(tbin, break_loCur, 0);
1380   if (tbin->locs[tbin_loCur].tin == tbin->locs[tbin_hiCur].tin)
1381     storeLen = tbin->locs[tbin_hiCur].index - tbin->locs[tbin_loCur].index;
1382   else
1383     storeLen = tbin->locs[tbin_hiCur].index + tbin->tins[tbin->locs[tbin_hiCur].tin].start -
1384       tbin->hiBreak;
1385   XChangeProperty(env->dpy, xsre->requestor, xsre->property, xsre->target,
1386 		  8, PropModeReplace, tbin->buf + tbin->hiBreak, storeLen);
1387   adjustBreak(tbin, break_eoLoCur, 0);
1388   return(TRUE);
1389 }
1390 
1391 
sclear(ButEnv * env)1392 static int  sclear(ButEnv *env)  {
1393   Tbin  *tbin = cut_butnum->iPacket;
1394 
1395   tbin->locs[tbin_hiCur] = tbin->locs[tbin_loCur];
1396   but_draw(cut_butnum);
1397   env->sReq = NULL;
1398   env->sClear = NULL;
1399   return(0);
1400 }
1401 
1402 
paste(ButEnv * env,But * but)1403 static void  paste(ButEnv *env, But *but)  {
1404   ButWin  *topWin;
1405 
1406   paste_butnum = but;
1407   env->sNotify = snotify;
1408   topWin = but->win;
1409   while (topWin->parent != NULL)
1410     topWin = topWin->parent;
1411   XConvertSelection(env->dpy, XA_PRIMARY, XA_STRING, env->prop,
1412 		    topWin->physWin, 0);
1413 }
1414 
1415 
snotify(ButEnv * env,XSelectionEvent * xsnot)1416 static int  snotify(ButEnv *env, XSelectionEvent *xsnot)  {
1417   unsigned char  *propbuf;
1418   unsigned long  proplen, propleft;
1419   int  blen;
1420   int  pformat;
1421   Atom  ptype;
1422   Tbin  *tbin;
1423   char  *pb2;
1424 
1425   tbin = paste_butnum->iPacket;
1426   if (xsnot->property != None)  {
1427     XGetWindowProperty(env->dpy, xsnot->requestor,
1428 		       xsnot->property, 0, MAX_PASTE, True, AnyPropertyType,
1429 		       &ptype, &pformat, &proplen, &propleft,
1430 		       &propbuf);
1431     tbin->locs[tbin_loCur] = tbin->locs[tbin_hiCur] = tbin->locs[tbin_mouse];
1432     if (insert(env, (char *)propbuf, tbin, paste_butnum->w, NULL) !=
1433 	insert_bad)
1434       but_draw(paste_butnum);
1435     else
1436       XBell(env->dpy, 0);
1437     XFree(propbuf);
1438   } else  {
1439     tbin->locs[tbin_loCur] = tbin->locs[tbin_hiCur] = tbin->locs[tbin_mouse];
1440     propbuf = (unsigned char *)XFetchBytes(env->dpy, &blen);
1441     if (blen > 0)  {
1442       pb2 = (char *)wms_malloc(blen + 1);
1443       pb2[blen] = '\0';
1444       memcpy(pb2, propbuf, blen);
1445       XFree(propbuf);
1446       if (insert(env, pb2, tbin, paste_butnum->w, NULL))
1447 	but_draw(paste_butnum);
1448       else
1449 	XBell(env->dpy, 0);
1450       wms_free(pb2);
1451     } else
1452       XBell(env->dpy, 0);
1453   }
1454   env->sNotify = NULL;
1455   return(0);
1456 }
1457 
1458 
1459 /*
1460  * This adjusts your loc and your cutEnd for when you're in word select mode.
1461  */
wordsel_adjust(Tbin * tbin)1462 static void  wordsel_adjust(Tbin *tbin)  {
1463   char  *atp, *btp;
1464   int  a, b, at, bt;
1465 
1466   a = tbin->locs[tbin_loCur].index;
1467   at = tbin->locs[tbin_loCur].tin;
1468   b = tbin->locs[tbin_hiCur].index;
1469   bt = tbin->locs[tbin_hiCur].tin;
1470   if ((at >= 0) && (at < tbin->numTins))
1471     atp = tbin->buf + tbin->tins[at].start;
1472   else
1473     atp = NULL;
1474   if ((bt >= 0) && (bt < tbin->numTins))
1475     btp = tbin->buf + tbin->tins[bt].start;
1476   else
1477     btp = NULL;
1478   if (atp)  {
1479     for (;;)  {
1480       if (a <= 0)  {
1481 	a = 0;
1482 	break;
1483       }
1484       if (((isalnum(atp[a]) || (atp[a] == '_')) &&
1485 	   (isalnum(atp[a-1]) || (atp[a-1] == '_'))) ||
1486 	  (atp[a] == atp[a-1]) ||
1487 	  (isdigit(atp[a]) && (atp[a-1] == '.') &&
1488 	   (a >= 2) && isdigit(atp[a-2])) ||
1489 	  (isdigit(atp[a+1]) && (atp[a] == '.') && isdigit(atp[a-1])))
1490 	--a;
1491       else
1492 	break;
1493     }
1494   }
1495   if (btp && (b > 0))  {
1496     for (;;)  {
1497       if (b >= tbin->tins[bt].len)  {
1498 	b = tbin->tins[bt].len;
1499 	break;
1500       }
1501       if (((isalnum(btp[b-1]) || (btp[b-1] == '_')) &&
1502 	   (isalnum(btp[b]) || (btp[b] == '_'))) || (btp[b-1] == btp[b]) ||
1503 	  (isdigit(btp[b-1]) && (btp[b] == '.') && isdigit(btp[b+1])) ||
1504 	  ((b >= 2) && isdigit(btp[b-2]) && (btp[b-1] == '.') &&
1505 	   isdigit(btp[b])))
1506 	++b;
1507       else
1508 	break;
1509     }
1510   }
1511   tbin->locs[tbin_loCur].index = a;
1512   tbin->locs[tbin_hiCur].index = b;
1513 }
1514 
1515 
1516 /* Returns the index of the character just to the right of the cursor. */
locateMouse(XFontStruct * fs,const char * text,int textLen,int x,bool * rightSide)1517 static int  locateMouse(XFontStruct *fs, const char *text, int textLen, int x,
1518 			bool  *rightSide)  {
1519   int  i;
1520   int  prev_x = x;
1521   int  spaceWidth;
1522   bool  dummy;
1523 
1524   if (rightSide == NULL)
1525     rightSide = &dummy;
1526   for (i = 0;  (x > 0) && (i < textLen);  ++i)  {
1527     prev_x = x;
1528     if (fs->per_char == NULL)
1529       /* Monospace font. */
1530       x -= fs->min_bounds.width;
1531     else
1532       x -= fs->per_char[(uchar)text[i] - fs->min_char_or_byte2].width;
1533   }
1534   if (fs->per_char == NULL)
1535     spaceWidth = fs->min_bounds.width;
1536   else
1537     spaceWidth = fs->per_char[' ' - fs->min_char_or_byte2].width;
1538   if (x >= spaceWidth)
1539     *rightSide = TRUE;
1540   else if ((*rightSide = ((i > 0) && (x < 0) && (prev_x < -x))))
1541     --i;
1542   return(i);
1543 }
1544 
1545 
flags(But * but,uint flags)1546 static void  flags(But *but, uint flags)  {
1547   Tbin  *tb = but->iPacket;
1548 
1549   if ((but->flags & BUT_KEYED) != (flags & BUT_KEYED))  {
1550     if (flags & BUT_KEYED)  {
1551       enableCTimer(but);
1552       tb->cursorVisible = TRUE;
1553     } else  {
1554       tb->cursorVisible = FALSE;
1555       disableCTimer(but);
1556     }
1557   }
1558   assert((but->flags & BUT_PRESSABLE) || !(but->flags & BUT_KEYED));
1559   but->flags = flags;
1560   but_draw(but);
1561 }
1562 
1563 
blinkCursor(ButTimer * timer)1564 static ButOut  blinkCursor(ButTimer *timer)  {
1565   But  *but = butTimer_packet(timer);
1566   Tbin  *tb = but->iPacket;
1567 
1568   tb->cursorVisible = !(butTimer_ticks(timer) & 1);
1569   ti_redrawCursor(but);
1570   return(0);
1571 }
1572 
1573 
enableCTimer(But * but)1574 static void  enableCTimer(But *but)  {
1575   Tbin *tb = but->iPacket;
1576   struct timeval  halfSec;
1577 
1578   assert(tb->cTimer == NULL);
1579   halfSec.tv_sec = 0;
1580   halfSec.tv_usec = 500000;
1581   tb->cTimer = butTimer_create(but, but, halfSec, halfSec, TRUE,
1582 			       blinkCursor);
1583 }
1584 
1585 
disableCTimer(But * but)1586 static void  disableCTimer(But *but)  {
1587   Tbin *tb = but->iPacket;
1588 
1589   if (tb->cTimer != NULL)  {
1590     butTimer_destroy(tb->cTimer);
1591     tb->cTimer = NULL;
1592   }
1593 }
1594 
1595 
butTbin_set(But * but,const char * newStr)1596 void  butTbin_set(But *but, const char *newStr)  {
1597   Tbin  *tb = but->iPacket;
1598 
1599   assert(MAGIC(but));
1600   assert(but->action == &action);
1601   if (but->flags & BUT_KEYED)
1602     but_setFlags(but, BUT_NOKEY);
1603   tb->numTins = 1;
1604   tb->loTinBreak = 1;
1605   tb->hiTinBreak = tb->maxTins;
1606   tb->locs[tbin_loCur].index = 0;
1607   tb->locs[tbin_loCur].tin = 0;
1608   tb->locs[tbin_hiCur] = tb->locs[tbin_loCur];
1609   tb->tins[0].len = 0;
1610   tb->tins[0].start = 0;
1611   tb->loBreak = 0;
1612   tb->hiBreak = tb->bufLen;
1613   insert(but->win->env, newStr, tb, but->w, NULL);
1614   but_draw(but);
1615 }
1616 
1617 
calcTinNum(But * but,Tbin * tbin,int y)1618 static int  calcTinNum(But *but, Tbin *tbin, int y)  {
1619   int  n;
1620 
1621   if (y < but->y)
1622     return(0);
1623   n = (y - but->y) / butEnv_fontH(but->win->env, 0);
1624   if (n >= tbin->loTinBreak)
1625     n += tbin->hiTinBreak - tbin->loTinBreak;
1626   if (n >= tbin->maxTins)
1627     n = tinNum_prev(tbin->maxTins, tbin);
1628   return(n);
1629 }
1630 
1631 
setMouseX(But * but,Tbin * tbin,int tin,int loc)1632 static void  setMouseX(But *but, Tbin *tbin, int tin, int loc)  {
1633   assert((tin >= 0) && (tin < tbin->maxTins));
1634   assert((loc >= 0) && (loc <= tbin->tins[tin].len));
1635   tbin->mouseX = XTextWidth(but->win->env->fonts[0],
1636 			    tbin->buf + tbin->tins[tin].start, loc);
1637 }
1638 
1639 
checkOffWin(But * but,int mouseY,bool force)1640 static void  checkOffWin(But *but, int mouseY, bool force)  {
1641   ButWin  *win = but->win;
1642   Tbin  *tbin = but->iPacket;
1643   int  y, h, cy, ch, maxCy, loLineNum, hiLineNum;
1644 
1645   assert(MAGIC(but));
1646   assert(MAGIC(tbin));
1647   assert(tbin->maxTins == tbin->numTins +
1648 	 tbin->hiTinBreak - tbin->loTinBreak);
1649   assert(loc_valid(tbin->locs[tbin_loCur], tbin));
1650   assert(loc_valid(tbin->locs[tbin_hiCur], tbin));
1651   if (!loc_eq(tbin->locs[tbin_loCur], tbin->locs[tbin_hiCur]) &&
1652       !tbin->clicks)  {
1653     /*
1654      * The text is selected, but no mouse is moving around.  Don't scroll
1655      *   around!
1656      * Perhaps we should move around in this case if text is inserted or
1657      *   something.  Tough call, but probably we shouldn't since inserting
1658      *   text will (of necessity) move around the cutEnd and the loc.
1659      */
1660     return;
1661   }
1662   if (tbin->offWinCallback)  {
1663     y = win->yOff;
1664     h = win->h;
1665     ch = butEnv_fontH(win->env, 0);
1666     if (tbin->clicks)  {
1667       assert(loc_valid(tbin->locs[tbin_mouse], tbin));
1668       cy = loLineNum = tinNum_line(tbin->locs[tbin_mouse].tin, tbin);
1669       hiLineNum = tinNum_line(tbin->locs[tbin_press].tin, tbin);
1670     } else  {
1671       cy = loLineNum = hiLineNum = tinNum_line(tbin->locs[tbin_loCur].tin,
1672 					       tbin);
1673       mouseY = 0;
1674     }
1675     cy = but->y + cy * ch;
1676     maxCy = tbin->numTins * ch;
1677     if (force || (cy < y) || (cy + ch > y + h) || (maxCy + ch > but->h))  {
1678       tbin->offWinCallback(but, loLineNum, hiLineNum, mouseY);
1679     }
1680   }
1681 }
1682 
1683 
1684 /**
1685  * Adjust the breakpoint of the text field.
1686  *
1687  * Parameters:
1688  *   tb - The tbin being adjusted.
1689  *   bType = Ony of:
1690  *     break_loCur (move break to the lo cursor)
1691  *     break_eoLoCur (move break to end of the line containing the lo cursor)
1692  *     break_eol (move break to the end of line breakTin).
1693  *   breakTin - Used in break_eol. Zero otherwise.
1694  */
adjustBreak(Tbin * tb,BreakType bType,int breakTin)1695 static void  adjustBreak(Tbin *tb, BreakType bType, int breakTin)  {
1696   int  newLoBreak, breakIndex = 0, newHiBreak;
1697   int  origHiTinBreak, origLoTinBreak;
1698   int  breakDiff;
1699   TbinLoc  loc;
1700 
1701   assert(tb->hiBreak <= tb->bufLen);
1702   assert(tb->loBreak <= tb->bufLen);
1703   assert(tb->hiBreak >= 0);
1704   assert(tb->loBreak >= 0);
1705   switch(bType)  {
1706   case break_loCur:
1707     assert(breakTin == 0);
1708     breakTin = tb->locs[tbin_loCur].tin;
1709     breakIndex = tb->locs[tbin_loCur].index;
1710     break;
1711   case break_eoLoCur:
1712     assert(breakTin == 0);
1713     breakTin = tb->locs[tbin_loCur].tin;
1714     breakIndex = tb->tins[tb->locs[tbin_loCur].tin].len;
1715     break;
1716   case break_eol:
1717     assert(breakTin < tb->maxTins);
1718     assert((breakTin < tb->loTinBreak) || (breakTin >= tb->hiTinBreak));
1719     breakIndex = tb->tins[breakTin].len;
1720     break;
1721   }
1722   newLoBreak = tb->tins[breakTin].start;
1723   assert(newLoBreak >= 0);
1724   if (newLoBreak > tb->loBreak)  {
1725     assert(newLoBreak >= tb->hiBreak);
1726     newLoBreak -= tb->hiBreak - tb->loBreak;
1727   }
1728   newLoBreak += breakIndex;
1729   assert(newLoBreak >= 0);
1730   assert(newLoBreak >= 0);
1731   assert(newLoBreak <= tb->loBreak + tb->bufLen - tb->hiBreak);
1732   breakDiff = tb->hiBreak - tb->loBreak;
1733   newHiBreak = newLoBreak + breakDiff;
1734   assert(newHiBreak <= tb->bufLen);
1735   if (newLoBreak < tb->loBreak)  {
1736     memmove(tb->buf + newHiBreak, tb->buf + newLoBreak,
1737 	    tb->loBreak - newLoBreak);
1738     origHiTinBreak = tb->hiTinBreak;
1739     while (tb->loTinBreak > breakTin + 1)  {
1740       --tb->loTinBreak;
1741       --tb->hiTinBreak;
1742       assert(tb->tins[tb->loTinBreak].start <= tb->loBreak);
1743       tb->tins[tb->hiTinBreak] = tb->tins[tb->loTinBreak];
1744       tb->tins[tb->hiTinBreak].start += tb->hiBreak - tb->loBreak;
1745       if (breakTin == tb->loTinBreak)
1746 	breakTin = tb->hiTinBreak;
1747       assert(tb->tins[tb->loTinBreak].start <= tb->bufLen);
1748     }
1749     assert(newLoBreak >= 0);
1750     tb->loBreak = newLoBreak;
1751     tb->hiBreak = newHiBreak;
1752     tbinLoc_iter(loc)  {
1753       if ((tb->locs[loc].tin < origHiTinBreak) &&
1754 	  (tb->locs[loc].tin >= tb->loTinBreak))
1755 	tb->locs[loc].tin += tb->hiTinBreak - tb->loTinBreak;
1756     }
1757     assert(tb->tins[tb->locs[tbin_loCur].tin].start >= 0);
1758     assert((bType != break_loCur) ||
1759 	   (tb->tins[tb->locs[tbin_loCur].tin].start +
1760 	    tb->locs[tbin_loCur].index == tb->loBreak));
1761   } else /* newLoBreak >= tb->loBreak */  {
1762     if (newLoBreak != tb->loBreak)
1763       memmove(tb->buf + tb->loBreak, tb->buf + tb->hiBreak,
1764 	      newLoBreak - tb->loBreak);
1765     origLoTinBreak = tb->loTinBreak;
1766     while (tb->hiTinBreak <= breakTin)  {
1767       assert(tb->tins[tb->hiTinBreak].start >= tb->hiBreak);
1768       tb->tins[tb->loTinBreak] = tb->tins[tb->hiTinBreak];
1769       tb->tins[tb->loTinBreak].start -= tb->hiBreak - tb->loBreak;
1770       if (breakTin == tb->hiTinBreak)
1771 	breakTin = tb->loTinBreak;
1772       assert(tb->tins[tb->loTinBreak].start >= 0);
1773       ++tb->loTinBreak;
1774       ++tb->hiTinBreak;
1775     }
1776     assert(newLoBreak >= 0);
1777     tb->loBreak = newLoBreak;
1778     tb->hiBreak = newHiBreak;
1779     tbinLoc_iter(loc)  {
1780       if ((tb->locs[loc].tin >= origLoTinBreak) &&
1781 	  (tb->locs[loc].tin < tb->hiTinBreak))
1782 	tb->locs[loc].tin -= tb->hiTinBreak - tb->loTinBreak;
1783     }
1784     assert(tb->tins[tb->locs[tbin_loCur].tin].start >= 0);
1785     assert((bType != break_loCur) ||
1786 	   (tb->tins[tb->locs[tbin_loCur].tin].start +
1787 	    tb->locs[tbin_loCur].index == tb->loBreak));
1788   }
1789   assert(tb->hiBreak <= tb->bufLen);
1790   assert(tb->loBreak <= tb->bufLen);
1791   assert(tb->hiBreak >= 0);
1792   assert(tb->loBreak >= 0);
1793 }
1794 
1795 
addMoreBufferSpace(Tbin * tb)1796 static void  addMoreBufferSpace(Tbin *tb)  {
1797   char  *newBuf;
1798   int  newLen, i;
1799   int  newHiBreak;
1800 
1801   newLen = tb->bufLen * 2;
1802   newBuf = wms_malloc(newLen);
1803   newHiBreak = tb->hiBreak + newLen - tb->bufLen;
1804   memcpy(newBuf, tb->buf, tb->loBreak);
1805   memcpy(newBuf + newHiBreak, tb->buf + tb->hiBreak, newLen - newHiBreak);
1806   for (i = tb->hiTinBreak;  i < tb->maxTins;  ++i)  {
1807     tb->tins[i].start += newLen - tb->bufLen;
1808   }
1809   wms_free(tb->buf);
1810   tb->buf = newBuf;
1811   tb->hiBreak = newHiBreak;
1812   tb->bufLen = newLen;
1813 }
1814 
1815 
addMoreTins(Tbin * tb)1816 static void  addMoreTins(Tbin *tb)  {
1817   Tin  *newTins;
1818   int  newMaxTins, i;
1819   int  newHiBreak, tinDiff;
1820 
1821   newMaxTins = tb->maxTins * 2;
1822   newTins = wms_malloc(newMaxTins * sizeof(Tin));
1823   newHiBreak = tb->hiTinBreak + newMaxTins - tb->maxTins;
1824   for (i = 0;  i < tb->loTinBreak;  ++i)  {
1825     newTins[i] = tb->tins[i];
1826   }
1827   tinDiff = newMaxTins - tb->maxTins;
1828   for (i = tb->hiTinBreak;  i < tb->maxTins;  ++i)  {
1829     newTins[i + tinDiff] = tb->tins[i];
1830   }
1831   if (tb->locs[tbin_mouse].tin >= tb->loTinBreak)
1832     tb->locs[tbin_mouse].tin += tinDiff;
1833   if (tb->locs[tbin_press].tin >= tb->loTinBreak)
1834     tb->locs[tbin_press].tin += tinDiff;
1835   if (tb->locs[tbin_loCur].tin >= tb->loTinBreak)
1836     tb->locs[tbin_loCur].tin += tinDiff;
1837   if (tb->locs[tbin_hiCur].tin >= tb->loTinBreak)
1838     tb->locs[tbin_hiCur].tin += tinDiff;
1839   wms_free(tb->tins);
1840   tb->tins = newTins;
1841   tb->hiTinBreak = newHiBreak;
1842   tb->maxTins = newMaxTins;
1843 }
1844 
1845 
calcTinWidth(Tin * tin,const char * buf,ButEnv * env)1846 static int  calcTinWidth(Tin *tin, const char *buf, ButEnv  *env)  {
1847   int  textLen;
1848 
1849   textLen = tin->len;
1850   while ((textLen > 0) && (buf[tin->start + textLen - 1] == ' '))
1851     --textLen;
1852   tin->width = XTextWidth(env->fonts[0], buf + tin->start, textLen);
1853   return(tin->width);
1854 }
1855 
1856 
resize(But * but,int oldX,int oldY,int oldW,int oldH)1857 static bool  resize(But *but, int oldX, int oldY, int oldW, int oldH)  {
1858   Tbin  *tb;
1859   int  newW, i;
1860 
1861   if (oldW != but->w)  {
1862     tb = but->iPacket;
1863     newW = but->w - (tb->rMargin + tb->lMargin);
1864     for (i = 0;  i < tb->maxTins;  i = tinNum_next(i, tb))  {
1865       if (tb->tins[i].width > newW)
1866 	breakLine(tb, i, but->win->env, newW);
1867     }
1868     return(TRUE);
1869   } else
1870     return((but->x != oldX) || (but->y != oldY) || (but->h != oldH));
1871 }
1872 
1873 
killTopLines(Tbin * tb,int lines)1874 static void  killTopLines(Tbin *tb, int lines)  {
1875   int  charsDead, i;
1876   TbinLoc  loc;
1877 
1878   assert(lines < tb->numTins);
1879   adjustBreak(tb, break_eol, tinNum_prev(tb->maxTins, tb));
1880   charsDead = tb->tins[lines].start;
1881   memmove(tb->buf, tb->buf + charsDead, tb->loBreak - charsDead);
1882   tb->loBreak -= charsDead;
1883   tb->numTins -= lines;
1884   tb->loTinBreak -= lines;
1885   for (i = 0;  i < tb->loTinBreak;  ++i)  {
1886     tb->tins[i].start = tb->tins[i + lines].start - charsDead;
1887     tb->tins[i].len = tb->tins[i + lines].len;
1888     tb->tins[i].width = tb->tins[i + lines].width;
1889   }
1890   tbinLoc_iter(loc)  {
1891     tb->locs[loc].tin -= lines;
1892     if (tb->locs[loc].tin < 0)  {
1893       tb->locs[loc].tin = 0;
1894       tb->locs[loc].index = 0;
1895     }
1896   }
1897 }
1898 
1899 
butTbin_insert(But * but,const char * appText)1900 void  butTbin_insert(But *but, const char *appText)  {
1901   bool  saveOldCurs;
1902   Tbin  *tb;
1903 
1904   assert(MAGIC(but));
1905   tb = but->iPacket;
1906   assert(MAGIC(tb));
1907   saveOldCurs = (!loc_eq(tb->locs[tbin_loCur], tb->locs[tbin_hiCur]) ||
1908 		 (tinNum_next(tb->locs[tbin_loCur].tin, tb) !=
1909 		  tb->maxTins));
1910   if (saveOldCurs)  {
1911     tb->locs[tbin_mouse] = tb->locs[tbin_loCur];
1912     tb->locs[tbin_press] = tb->locs[tbin_hiCur];
1913     tb->locs[tbin_loCur].tin = tinNum_prev(tb->maxTins, tb);
1914     tb->locs[tbin_loCur].index = tb->tins[tb->locs[tbin_loCur].tin].len;
1915     tb->locs[tbin_hiCur] = tb->locs[tbin_loCur];
1916   }
1917   insert(but->win->env, appText, tb,
1918 	 but->w - (tb->lMargin + tb->rMargin), NULL);
1919   checkOffWin(but, 0, TRUE);
1920   if (saveOldCurs && !loc_eq(tb->locs[tbin_mouse], tb->locs[tbin_press]))  {
1921     tb->locs[tbin_loCur] = tb->locs[tbin_mouse];
1922     tb->locs[tbin_hiCur] = tb->locs[tbin_press];
1923   }
1924   but_draw(but);
1925 }
1926 
1927 
butTbin_len(But * but)1928 int  butTbin_len(But *but)  {
1929   Tbin  *tb;
1930 
1931   assert(MAGIC(but));
1932   tb = but->iPacket;
1933   assert(MAGIC(tb));
1934   return(tb->bufLen - (tb->hiBreak - tb->loBreak));
1935 }
1936 
1937 
butTbin_setMaxLines(But * but,int maxLines)1938 void  butTbin_setMaxLines(But *but, int maxLines)  {
1939   Tbin  *tb = but->iPacket;
1940 
1941   assert(MAGIC(tb));
1942   tb->maxLines = maxLines;
1943 }
1944 
1945 
butTbin_setReadOnly(But * but,bool ro)1946 void  butTbin_setReadOnly(But *but, bool ro)  {
1947   Tbin  *tb = but->iPacket;
1948 
1949   assert(MAGIC(tb));
1950   tb->readOnly = ro;
1951 }
1952 
1953 
butTbin_delete(But * but,int delStart,int delLen)1954 void  butTbin_delete(But *but, int delStart, int delLen)  {
1955   Tbin  *tb = but->iPacket;
1956 
1957   assert(but->action == &action);
1958   assert(MAGIC(tb));
1959   tb->locs[tbin_mouse] = tb->locs[tbin_loCur];
1960   tb->locs[tbin_press] = tb->locs[tbin_hiCur];
1961   setLoc(tb, tbin_loCur, delStart);
1962   setLoc(tb, tbin_hiCur, delStart + delLen);
1963   insert(but->win->env, "", tb, but->w, NULL);
1964   tb->locs[tbin_loCur] = tb->locs[tbin_mouse];
1965   tb->locs[tbin_hiCur] = tb->locs[tbin_press];
1966 }
1967 
1968 
setLoc(Tbin * tb,TbinLoc locNum,int position)1969 static void  setLoc(Tbin *tb, TbinLoc locNum, int position)  {
1970   Loc  *l = &tb->locs[locNum];
1971   int  i;
1972 
1973   if (position > tb->loBreak)
1974     position += (tb->hiBreak - tb->loBreak);
1975   i = 0;
1976   while (tb->tins[i].start + tb->tins[i].len < position)  {
1977     i = tinNum_next(i, tb);
1978     assert(i < tb->maxTins);
1979   }
1980   l->tin = i;
1981   l->index = position - tb->tins[i].start;
1982 }
1983 
1984 
1985 #endif  /* X11_DISP */
1986