1 /*
2 
3 Copyright (C) 2015-2018 Night Dive Studios, LLC.
4 
5 This program is free software: you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation, either version 3 of the License, or
8 (at your option) any later version.
9 
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 GNU General Public License for more details.
14 
15 You should have received a copy of the GNU General Public License
16 along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 
18 */
19 /*
20  * the texttool, a generic text editor shell sort of thing
21  * only infinitely lamer
22  */
23 
24 
25 #include <string.h>
26 #include <stdio.h>
27 #include <ctype.h>
28 
29 #include "lg.h"
30 //#include <mprintf.h>
31 #include "texttool.h"
32 #include <stdlib.h> // malloc(), free()
33 
34 #define _tt_top(tid) if (tid==NULL) tid=cur_tt; _tt=tid;
35 
36 #define StrDraw  _tt->lfont->s_draw
37 #define CharDraw _tt->lfont->c_draw
38 #define StrWid   _tt->lfont->s_wid
39 #define CharWid  _tt->lfont->c_wid
40 #define LenClr   _tt->lfont->l_clr
41 #define CharClr  _tt->lfont->c_clr
42 #define StrClr   _tt->lfont->s_clr
43 #define LGCursor   _tt->lfont->cursor
44 
45 #define LineExist(_lt,lnum) ((lnum>=0)&&(lnum<_lt->max_h)&&(_lt->lines[lnum]!=NULL))
46 #define LastChar(_lt,lnum)  (_lt->lines[lnum][_lt->line_info[lnum].stl-1])
47 
48 // internal prototypes
49 void _tt_build_cheat(long line_num);
50 void _tt_new_line(long line_num);
51 //TextTool *tt_full_build(TTRect *pos, TTState *es, TTFontInfo *ttf, void *output_data, char *keymap, void *(d_func)(void *, LGRect *));
52 void tt_resize(TextTool *tt, int wid, int height);
53 void _tt_display_line(long line_num, long p_left, long p_right);
54 void _tt_show_line(long line_num, long p_left, long p_right);
55 int _tt_word_len(int which_word, char *s, long pos);
56 void _tt_resize_line(long line_num, long new_len);
57 void _tt_break_line(long line_num, long break_pt);
58 void _tt_rem_front(long line_num, long rem_pos);
59 void _tt_rem_mid(long line_num, long left_c, long right_c);
60 uchar _tt_wrap_check(long *line_num, long *cur_pos);
61 uchar _tt_add_char(long *line_num, long *cur_pos, char c);
62 uchar _tt_del_chars(long *line_num, long *cur_pos, int cnt);
63 uchar _tt_chg_line(int how);
64 uchar _tt_chg_colu(int how);
65 void _tt_return(void);
66 ulong _tt_check_cursor_position(void);
67 int _tt_do_event(long tt_event);
68 
69 // static local coolness
70 static TextTool *cur_tt=NULL, *_tt=NULL;
71 
72 // Must choose one of these font headers to actually use....  and then, of course
73 // make sure you link appropriately also.  Is there a more graceful way to deal?
74 // #include <monofont.h>
75 // #include <realfont.h>
76 #include "tngfont.h"
77 #include "fakefont.h"
78 
79 static TTRect     TTDefRect  = {{0,0},80,24};
80 static TTState    TTDefState = {0,66,0,TTS_WRAP|TTS_FULL,1,TTEV_NULL};
81 //static TTFontInfo TTDefFont  =
82 //       {mono_s_wid, mono_s_draw, mono_c_wid, mono_c_draw, mono_s_clr, mono_l_clr, mono_c_clr, mono_cursor, 1, 1, TTFI_FIXED };
83 //static TTKeyMap   TTDefKMap;
84 
85 TTFontInfo TTDefFont  =
86        {fnt_s_width, fnt_s_draw, fnt_c_width, fnt_c_draw, fnt_s_clr, fnt_l_clr, fnt_c_clr, fnt_cursor, 7, 7, TTFI_PROP|TTFI_SPACE };
87 
88 
89 
90 // public useful stuff
tt_full_build(TTRect * pos,TTState * es,TTFontInfo * ttf,void * output_data,char * keymap,void (* d_func)(void *,LGRect *))91 TextTool *tt_full_build(TTRect *pos, TTState *es, TTFontInfo *ttf, void *output_data, char *keymap, void (*d_func)(void *, LGRect *))
92 {
93    TextTool *new_tt;
94    char *dummy;
95    dummy = keymap;
96 
97    new_tt=(TextTool *)malloc(sizeof(TextTool));
98    _tt_top(new_tt);
99    _tt->max_w=_tt->max_h=_tt->cur_w=_tt->cur_h=0;
100    if (pos!=NULL) _tt->scr_loc=*pos;
101    else           _tt->scr_loc=TTDefRect;
102    _tt->disp_x=_tt->disp_y=0;                // focus on upper left corner
103    if (ttf!=NULL) _tt->lfont=ttf;
104    else           _tt->lfont=&TTDefFont;
105    _tt->output_data = output_data;
106    _tt->display_func = d_func;
107    _tt->lines=(char **)malloc(1);
108    _tt->line_info=(TTCheats *)malloc(1);
109    _tt->disp_rows=(_tt->scr_loc.h+_tt->lfont->height-1)/_tt->lfont->height;
110    _tt_new_line(0);
111    if (es!=NULL)  _tt->es=*es;
112    else           _tt->es=TTDefState;
113    return new_tt;
114 }
115 
tt_toast(TextTool * old_tt)116 uchar tt_toast(TextTool *old_tt)
117 {
118    int i;
119    for (i=0; i<old_tt->max_h; i++)
120       free(old_tt->lines[i]);
121    free(*(old_tt->lines));
122    free(old_tt->line_info);
123    return TRUE;
124 }
125 
126 // set this tt to be the default one
tt_set(TextTool * def_tt)127 uchar tt_set(TextTool *def_tt)
128 {
129    cur_tt=def_tt;
130    return TRUE;
131 }
132 
tt_move(TextTool * tt,int xoff,int yoff)133 void tt_move(TextTool *tt, int xoff, int yoff)
134 {
135    _tt_top(tt);
136    _tt->scr_loc.crn.pt.x=xoff; _tt->scr_loc.crn.pt.y=yoff;
137 }
138 
tt_resize(TextTool * tt,int wid,int height)139 void tt_resize(TextTool *tt, int wid, int height)
140 {
141    _tt_top(tt);
142    _tt->scr_loc.w=wid; _tt->scr_loc.h=height;
143    // do stuff here... recompute the universe, etc...
144 }
145 
146 // move to a new line
147 // delete a line
148 
149 // build a cheat for one line
_tt_build_cheat(long line_num)150 void _tt_build_cheat(long line_num)
151 {
152    TTCheats *loc=&(_tt->line_info[line_num]);
153    int x_pix=0, x_chr=0, a_wid;
154    char *sb=_tt->lines[line_num], *s;
155 
156    // recheck carefully for proportional font stuff...
157    // should probably modularize out the selector for optimal expose events
158 
159    a_wid=strlen(s=sb);
160    loc->stl=a_wid;
161    // find the left edge
162    while ((x_pix<_tt->disp_x)&&(x_chr<loc->stl))
163     { x_pix+=CharWid(*s++); x_chr++; }
164    if (x_chr==loc->stl)
165     { loc->chr[0]=TTC_NOTHING; loc->chr[1]=0; loc->pix[0]=0; loc->pix[1]=0; return; }
166    if (x_pix>_tt->disp_x)
167     { x_pix-=CharWid(*--s); --x_chr; }
168    loc->chr[0]=x_chr; loc->pix[0]=x_pix;
169    // find right edge
170 
171 // bug if string width is 1, basically
172 
173    x_pix+=CharWid(*s++); x_chr++;
174    while ((x_pix<_tt->disp_x+_tt->scr_loc.w)&&(x_chr<loc->stl))
175     { x_pix+=CharWid(*s++); x_chr++; }
176    if (x_chr==loc->stl)
177     { x_pix-=CharWid(*--s); --x_chr; }
178    loc->chr[1]=x_chr; loc->pix[1]=x_pix;
179 
180 // mprintf("line %d (%s): cheat %d %d, %d %d\n",line_num,sb,loc->chr[0],loc->pix[0],loc->chr[1],loc->pix[1]);
181 }
182 
183 // inserts a new line at line_num, inserts appropriately if necessary
_tt_new_line(long line_num)184 void _tt_new_line(long line_num)
185 {
186    _tt->max_h++;
187 //¥¥¥   _tt->lines=(char **)Realloc(_tt->lines,sizeof(char *)*_tt->max_h);
188 //¥¥¥   _tt->line_info=(TTCheats *)Realloc(_tt->line_info,sizeof(TTCheats)*_tt->max_h);
189    free(_tt->lines);
190    _tt->lines = (char **)malloc(sizeof(char *)*_tt->max_h);
191    free(_tt->line_info);
192    _tt->line_info = (TTCheats *)malloc(sizeof(TTCheats)*_tt->max_h);
193    if (line_num<_tt->max_h-1)                 /* insert case */
194    {
195       LG_memmove(&_tt->line_info[line_num+1],&_tt->line_info[line_num],sizeof(TTCheats)*(_tt->max_h-line_num-1));
196       LG_memmove(&_tt->lines[line_num+1],&_tt->lines[line_num],sizeof(char *)*(_tt->max_h-line_num-1));
197    }
198    _tt->line_info[line_num].wid=TTL_INIT;     		/* totally empty at first */
199    _tt->line_info[line_num].flg|=TTC_FLG_RET; 		/* should be a return there */
200    _tt->lines[line_num]=(char *)malloc(TTL_INIT);   /* and it is empty (\0) */
201    _tt->lines[line_num][0]='\0';
202    _tt_build_cheat(line_num);                 /* so we need a cheat for it */
203 }
204 
205 #ifdef MOO
206 // returns whether there are characters on the line in the specified range
207 // sets c_l and c_r to the character counts for the pix counts in p_l and p_r
208 // based on the string in l_n
_tt_pix_cnv(long l_n,long p_l,long p_r,long * c_l,long * c_r)209 uchar _tt_pix_cnv(long l_n, long p_l, long p_r, long *c_l, long *c_r)
210 {
211 
212 
213 }
214 #endif
215 
216 // rebuild a range of cheats
217 
218 // display one line starting at some coordinate
219 // display a rectangle of the area
220 
221 // do appropriate gadgety things to display the line.
_tt_display_line(long line_num,long p_left,long p_right)222 void _tt_display_line(long line_num, long p_left, long p_right)
223 {
224    LGRect disp_rect;
225    int llin;
226 
227    long dummy;
228    dummy = p_left;
229    dummy = p_right;
230 
231    llin=line_num-_tt->disp_y;
232    disp_rect.ul.x = _tt->scr_loc.crn.pt.x+_tt->line_info[line_num].pix[0];
233    disp_rect.ul.y = _tt->scr_loc.crn.pt.y+llin*_tt->lfont->height;
234    disp_rect.lr.x = _tt->scr_loc.w - 1;
235    disp_rect.lr.y = disp_rect.ul.y + _tt->lfont->height;
236 //   mprintf ("disp_rect = (%d, %d)(%d, %d)\n",disp_rect.ul.x, disp_rect.ul.y, disp_rect.lr.x, disp_rect.lr.y);
237    tt_display_all(_tt, &disp_rect);
238 }
239 
240 // for real, display a partial (or full) line
_tt_show_line(long line_num,long p_left,long p_right)241 void _tt_show_line(long line_num, long p_left, long p_right)
242 {
243    int llin=line_num-_tt->disp_y, cy=line_num;
244 //   long c_left, c_right;
245    uchar blnk_line=TRUE;
246 
247    if ((line_num<_tt->disp_y)||(line_num>=_tt->disp_y+_tt->disp_rows)) return;
248    if (p_left==-1) p_left=0; if (p_right==-1) p_right=_tt->scr_loc.w-1;
249    if (LineExist(_tt,cy))
250    {
251       if (_tt->line_info[cy].chr[0]==TTC_REBUILD)
252          _tt_build_cheat(cy);
253 #ifdef LOUD
254       mono_setxy(60,llin);
255       mprintf("%2.2d %2.2d %2.2d %2.2d          ",
256          _tt->line_info[cy].chr[0],_tt->line_info[cy].chr[1],_tt->line_info[cy].wid,_tt->line_info[cy].stl);
257 #endif
258 //    _tt_pix_cnv(_tt->lines[cy],p_left,p_right,&c_left,&c_right);
259       if (_tt->line_info[cy].chr[0]!=TTC_NOTHING)
260       {
261          char *st_base, *s, c;
262          long wid;
263 
264   	      st_base=_tt->lines[cy];
265   	      s=st_base+_tt->line_info[cy].chr[1]+1;
266   	      c=*s; *s='\0';
267          wid=StrDraw(st_base+_tt->line_info[cy].chr[0],
268                      _tt->scr_loc.crn.pt.x+_tt->line_info[cy].pix[0],
269                      _tt->scr_loc.crn.pt.y+llin*_tt->lfont->height);
270          *s=c; blnk_line=FALSE;
271          if (p_left+wid<p_right)
272             LenClr(p_right-p_left-wid,_tt->scr_loc.crn.pt.x+wid,_tt->scr_loc.crn.pt.y+llin*_tt->lfont->height);
273       }
274    }
275    if (blnk_line) LenClr(p_right-p_left,_tt->scr_loc.crn.pt.x,_tt->scr_loc.crn.pt.y+llin*_tt->lfont->height);
276 }
277 
278 // do appropriate UI things to display the whole thing
tt_display_all(TextTool * tt,LGRect * r)279 void tt_display_all(TextTool *tt, LGRect *r)
280 {
281    if (tt->display_func != NULL)
282       tt->display_func(tt->output_data, r);
283 }
284 
285 // fpr real, send full screen to output
tt_show_all(TextTool * tt)286 void tt_show_all(TextTool *tt)
287 {
288    long ay;
289    _tt_top(tt);
290    for (ay=0; ay<_tt->disp_rows; ay++)
291       _tt_show_line(ay+_tt->disp_y,-1,-1);
292    LGCursor(_tt->scr_loc.crn.pt.x+_tt->cur_w,_tt->scr_loc.crn.pt.y+_tt->cur_h-_tt->disp_y);
293 }
294 
tt_dump(TextTool * tt)295 void tt_dump(TextTool *tt)
296 {
297    long ay;
298    _tt_top(tt);
299    printf("DUMP:\n");
300    for (ay=0; ay<_tt->max_h; ay++)
301       printf("%5.5ld %s\n", ay, _tt->lines[ay]);
302 }
303 
304 // return the length of which_word (see defines TTWL_?) in char *s
_tt_word_len(int which_word,char * s,long pos)305 int _tt_word_len(int which_word, char *s, long pos)
306 {
307    char *p, *q, sw;
308    int val=-1;                         /* so we can see if this is acting way zany */
309 
310    switch (which_word)
311    {
312    case TTWL_FIRST: p=strchr(s,' '); if (p==NULL) val=strlen(s); else val=p-s; break;
313    case TTWL_LAST:  p=strrchr(s,' '); if (p==NULL) val=strlen(s); else val=s-p; break;
314    case TTWL_CUR:   q=strchr(s+pos,' '); sw=*(s+pos); *(s+pos)='\0'; p=strrchr(s,' '); *(s+pos)=sw;
315                     if ((q==NULL)&&(p==NULL)) val=strlen(s);
316                     else if (q==NULL) val=strlen(s)-(p-s);
317                     else if (p==NULL) val=q-s; else val=q-p; break;
318    }
319    return val;
320 }
321 
_tt_resize_line(long line_num,long new_len)322 void _tt_resize_line(long line_num, long new_len)
323 {
324    long cur_len=_tt->line_info[line_num].wid;
325    long n_num, new_targ;
326 
327    new_len+=2;                          // \0 and pad space
328    n_num=(new_len-TTL_INIT)/TTL_BASE;   // num of chunks
329    if (((new_len-TTL_INIT)%TTL_BASE)!=0) n_num++;
330    new_targ=n_num*TTL_BASE+TTL_INIT;
331    if (new_targ==cur_len) return;
332    _tt->line_info[line_num].wid=new_targ;
333 //¥¥¥   _tt->lines[line_num]=Realloc(_tt->lines[line_num],new_targ);
334    //SetPtrSize((Ptr)_tt->lines[line_num],new_targ);
335 }
336 
337 // fills line_num with s
tt_fill_line(TextTool * tt,int how,long line_num,char * s)338 void tt_fill_line(TextTool *tt, int how, long line_num, char *s)
339 {
340    long r_len=strlen(s), loop;
341    _tt_top(tt);
342    if (line_num>=_tt->max_h)
343       for (loop=_tt->max_h; loop<=line_num; loop++)
344          _tt_new_line(loop);
345    else
346       if (how==TTF_INSWHOLE) _tt_new_line(line_num); /* insert a new line */
347    if ((how==TTF_INSEND)||(how==TTF_INSFRONT))
348    {
349       if (_tt->line_info[line_num].stl>0)
350 		   r_len+=_tt->line_info[line_num].stl+2;      /* +2 for the space and the \0 */
351       else
352          how=TTF_INSWHOLE;                           /* if nothing there, simply replace */
353    }
354    _tt_resize_line(line_num,r_len);
355    switch (how)
356    {
357    case TTF_REPLACE:
358    case TTF_INSWHOLE: strcpy(_tt->lines[line_num],s); break;
359    case TTF_INSEND:   strcat(_tt->lines[line_num],s); break;
360    case TTF_INSFRONT:
361       LG_memmove(_tt->lines[line_num]+strlen(s)+1,_tt->lines[line_num],_tt->line_info[line_num].stl+1);
362       LG_memmove(_tt->lines[line_num],s,strlen(s));     /* dont need this \0 */
363       _tt->lines[line_num][strlen(s)]=' ';
364       break;
365    }
366    _tt_build_cheat(line_num);
367    _tt_display_line(line_num,-1,-1);
368 
369    // should get gnosis of returns
370 }
371 
372 // breaks off the first break_pt characters of line_num
_tt_break_line(long line_num,long break_pt)373 void _tt_break_line(long line_num, long break_pt)
374 {
375    if (break_pt>=_tt->line_info[line_num].stl) return;
376    _tt->lines[line_num][break_pt]='\0';
377    _tt_resize_line(line_num,break_pt);
378    _tt_build_cheat(line_num);
379    _tt_display_line(line_num,-1,-1);
380 }
381 
_tt_rem_front(long line_num,long rem_pos)382 void _tt_rem_front(long line_num, long rem_pos)
383 {
384    int lin_len=_tt->line_info[line_num].stl;
385    if (rem_pos>=lin_len) return;
386    LG_memmove(_tt->lines[line_num],_tt->lines[line_num]+rem_pos, lin_len-rem_pos);
387    _tt_resize_line(line_num,lin_len-rem_pos+1);
388    _tt_build_cheat(line_num);
389    _tt_display_line(line_num,-1,-1);
390 }
391 
_tt_rem_mid(long line_num,long left_c,long right_c)392 void _tt_rem_mid(long line_num, long left_c, long right_c)
393 {
394    int lin_len=_tt->line_info[line_num].stl, rem_cnt=right_c-left_c+1;
395    if ((rem_cnt>=lin_len)||(left_c>right_c)) return;
396    LG_memmove(_tt->lines[line_num]+left_c,_tt->lines[line_num]+right_c, lin_len-right_c);
397    _tt_resize_line(line_num,lin_len-rem_cnt);
398    _tt_build_cheat(line_num);
399    _tt_display_line(line_num,-1,-1);
400 }
401 
402 // given that only line line_num changed, do wrap checks
_tt_wrap_check(long * line_num,long * cur_pos)403 uchar _tt_wrap_check(long *line_num, long *cur_pos)
404 {
405    char *s, *p, sw=0;                   /* pointers for manipulation, sw is the swap character */
406    long ln=*line_num;
407    uchar wr=FALSE;
408    // first, do we wrap back to the last line (should use pixwid, not stl)
409    if (LineExist(_tt,ln-1)&&((_tt->line_info[ln-1].flg&TTC_FLG_RET)==0))
410       if (_tt->line_info[ln-1].stl+1+_tt_word_len(TTWL_FIRST,_tt->lines[ln],0)<_tt->es.right_m)
411          printf("Wrap back - note %ld %d\n",_tt->line_info[ln-1].stl,_tt_word_len(TTWL_FIRST,_tt->lines[ln],0));
412    // now, does our line wrap
413    if (_tt->line_info[ln].stl>=_tt->es.right_m)
414    {
415       char *base=_tt->lines[ln];
416       s=strrchr(base,' ');
417       while ((s>base)&&(s-base>=_tt->es.right_m))
418       {
419          if (sw!=0) *p=sw;               /* put the savechar back */
420          p=s; sw=*p; *p='\0'; s=strrchr(base,' ');   /* go back another word */
421       }
422       if (sw!=0) *p=sw;                  /* put any punted characters back */
423       if (s>base)
424       {                                  /* do the wrap */
425          int brk=(++s)-base;             /* skip the space, figure out where to break */
426          printf("Want to wrap -%s-...",s);
427          tt_fill_line(NULL,TTF_INSFRONT,ln+1,s);
428          _tt_break_line(ln,brk-1);
429          _tt->line_info[ln].flg&=(~TTC_FLG_RET); /* punt the return */
430          printf("Note strlen %lu and stl %ld for .%s./.%s.\n",strlen(s),_tt->line_info[(*line_num)+1].stl,s,_tt->lines[(*line_num)+1]);
431          if ((*cur_pos)>brk)
432           { (*line_num)++; (*cur_pos)-=brk; }
433          wr=TRUE;
434       }
435    }
436    // now, should we bring something in from the next line, and propagate down
437    // send back ok error code
438    return wr;
439 }
440 
441 // currently returns whether the line wrapped? why? who knows.
_tt_add_char(long * line_num,long * cur_pos,char c)442 uchar _tt_add_char(long *line_num, long *cur_pos, char c)
443 {
444    long new_stl=_tt->line_info[*line_num].stl, new_pos=*cur_pos;
445    char *s=_tt->lines[*line_num];
446    uchar add_at_end=TRUE;
447 
448    if (new_stl>*cur_pos) {                       // insert
449       if ((_tt->es.mode&TTS_OVER)==0)            // actually have to insert
450          LG_memmove(&s[*(cur_pos)+1],&s[*cur_pos],new_stl-*cur_pos);
451       else
452          add_at_end=FALSE;                       // just a punch in
453    }
454    if (add_at_end&&(_tt->es.max_w>0)&&(new_stl>=_tt->es.max_w))
455       new_stl--;                                 // punt final character, we are out of space
456    if (add_at_end)
457   	  { s[*cur_pos]=c; c=0; new_pos=_tt->line_info[*line_num].stl=++new_stl; }
458    if (new_stl>_tt->line_info[*line_num].wid-2)  // have to get more memory
459       _tt_resize_line(*line_num,new_stl);
460    if (new_stl>_tt->max_w) _tt->max_w=new_stl;
461    _tt->lines[*line_num][new_pos]=c;             // put us down, R2
462    (*cur_pos)++;                                 // go to next character
463    _tt_build_cheat(*line_num);                   // we can do incremental cheats later
464    if (_tt->es.mode&TTS_WRAP)
465       if (_tt_wrap_check(line_num,cur_pos)) return TRUE;
466    _tt_display_line(*line_num,-1,-1);
467    return FALSE;
468 }
469 
470 // learn this about return flag
_tt_del_chars(long * line_num,long * cur_pos,int cnt)471 uchar _tt_del_chars(long *line_num, long *cur_pos, int cnt)
472 {
473    int lin_wid=_tt->line_info[*line_num].stl, nlpos=*cur_pos, nrpos=*cur_pos;
474 //   int dir;
475    char *s=_tt->lines[*line_num];
476 
477    if (cnt<0) nlpos+=cnt; else nrpos+=cnt;
478    if ((nlpos>=0)&&(nrpos<=lin_wid))
479    {                                   /* easy case... all on the line */
480       LG_memmove(s+nlpos,s+nrpos,lin_wid-nrpos+1);
481       _tt->line_info[*line_num].stl-=cnt;
482       *cur_pos=nlpos;
483       _tt_display_line(*line_num,-1,-1);
484       _tt_build_cheat(*line_num);
485    }
486    else
487    {                                   /* ugly scene, multi-line thingy */
488       printf("Unhappy %d %d.. %d f %d\n",nlpos,nrpos,lin_wid,cnt);
489 
490 //      cnt+=_tt->cur_w;
491 //      if (cnt>0) { cnt-=lin_wid; dir=1; } else dir=-1;
492 //      _tt_chg_line(dir);
493 //      if (cnt>0) _tt->cur_w=0; else _tt->cur_w=_tt->line_info[_tt->cur_h].stl;
494 //      _tt_chg_colu(cnt-dir);           /* extra bonus wrap character there... */
495    }
496    return TRUE;
497 }
498 
499 // return TRUE if we are out of space
_tt_chg_line(int how)500 uchar _tt_chg_line(int how)
501 {
502    uchar edge=FALSE;                    /* edge of available space */
503    how+=_tt->cur_h;
504    if (how<0) { _tt->cur_h=0; edge=TRUE; }
505    else if (how>=_tt->max_h)
506    {
507       if (_tt->es.mode&TTS_CGROW)
508       {
509 	      for (_tt->cur_h=_tt->max_h; _tt->cur_h<=how; _tt->cur_h++)
510    	      _tt_new_line(_tt->cur_h);
511 	      _tt->cur_w=0; _tt->cur_h--;      // back to the last line
512       }
513       else
514        { edge=TRUE; _tt->cur_h=_tt->max_h-1; }
515    }
516    else
517    {
518       _tt->cur_h=how;
519       if (_tt->cur_w>=_tt->line_info[how].stl)
520          _tt->cur_w=_tt->line_info[how].stl;
521    }
522    return edge;
523 }
524 
525 // returns if it hit the edge
_tt_chg_colu(int how)526 uchar _tt_chg_colu(int how)
527 {
528    int dir, ncpos=_tt->cur_w+how, lin_wid=_tt->line_info[_tt->cur_h].stl;
529 
530    if ((ncpos>=0)&&(ncpos<=lin_wid))
531       _tt->cur_w=ncpos;
532    else
533    {
534       how+=_tt->cur_w;
535       if (how>0) { how-=lin_wid; dir=1; } else dir=-1;
536       if (_tt_chg_line(dir))
537        { if (dir>0) _tt->cur_w=_tt->line_info[_tt->cur_h].stl; else _tt->cur_w=0; return TRUE; }
538       if (how>0) _tt->cur_w=0; else _tt->cur_w=_tt->line_info[_tt->cur_h].stl;
539       _tt_chg_colu(how-dir);           /* extra bonus wrap character there... */
540    }
541    return FALSE;
542 }
543 
544 // should wrap to the next line
_tt_return(void)545 void _tt_return(void)
546 {
547    uchar line_gen=FALSE;
548 
549 
550    switch (_tt->es.mode&TTS_MODE)
551    {
552    case TTS_FULL:
553 	   _tt->line_info[_tt->cur_h].flg|=TTC_FLG_RET;
554 	   if (_tt->cur_w<_tt->line_info[_tt->cur_h].stl)
555    	{
556 	      tt_fill_line(NULL,TTF_INSWHOLE,_tt->cur_h+1,_tt->lines[_tt->cur_h]+_tt->cur_w);
557 	      _tt_break_line(_tt->cur_h,_tt->cur_w);
558 	      line_gen=TRUE;
559 	   }
560 	   _tt->cur_h++; _tt->cur_w=0;         /* go to beginning of next line */
561 	   if ((!line_gen)&&((_tt->es.mode&(TTS_OVER|TTS_READONLY))==0))
562 	      _tt_new_line(_tt->cur_h);
563       break;
564    case TTS_SINGLE:
565    case TTS_LINES:
566    case TTS_READONLY:
567       break;
568    }
569 }
570 
571 // returns changes based on _tt->cur_w and h in terms of scrolling and such
_tt_check_cursor_position(void)572 ulong _tt_check_cursor_position(void)
573 {
574    ulong changed=0;
575    if (_tt->cur_h<_tt->disp_y)                          /* cursor off top of screen */
576     { _tt->disp_y=_tt->cur_h; changed|=TTCHG_REDRAW; }  /* focus up there */
577    if (_tt->cur_h>=_tt->disp_y+_tt->disp_rows)
578     { _tt->disp_y=_tt->cur_h-_tt->disp_rows+1; changed|=TTCHG_REDRAW; }
579    changed|=TTCHG_CURSOR;     // since mono_font has to keep moving it around all the time
580    return changed;
581 }
582 
583 // actually does things.  This takes events, which can come out of script files, macros, or keystroke parses
_tt_do_event(long tt_event)584 int _tt_do_event(long tt_event)
585 {
586 //   int old_w=_tt->cur_w, old_h=_tt->cur_h;
587    int changed=0;
588 
589    if (tt_event&_ALL_M)
590    {
591       if ((tt_event&(_tt->es.mode&_ALL_M))==0)    /* is not an event in this mode */
592          tt_event=TTEV_NULL;
593 
594       switch (tt_event)
595       {
596       case TTEV_RET:    _tt_return(); break;
597       case TTEV_F_CHAR: _tt_chg_colu( _tt->es.r_cnt); break;
598       case TTEV_B_CHAR: _tt_chg_colu(-_tt->es.r_cnt); break;
599       case TTEV_F_LINE: _tt_chg_line( _tt->es.r_cnt); break;
600       case TTEV_B_LINE: _tt_chg_line(-_tt->es.r_cnt); break;
601       case TTEV_REPEAT: _tt->es.r_cnt*=4; break;
602       case TTEV_DEL:    _tt_del_chars(&_tt->cur_h, &_tt->cur_w, _tt->es.r_cnt); break;
603       case TTEV_BACKSP: _tt_del_chars(&_tt->cur_h, &_tt->cur_w,-_tt->es.r_cnt); break;
604       case TTEV_KILL:   _tt_break_line(_tt->cur_h, _tt->cur_w); break;
605       case TTEV_HOME:
606       case TTEV_BOL:    _tt->cur_w=0; break;
607       case TTEV_END:
608       case TTEV_EOL:    _tt->cur_w=_tt->line_info[_tt->cur_h].stl; break;
609       case TTEV_OVER:   if (_tt->es.mode&TTS_OVER) _tt->es.mode-=TTS_OVER; else _tt->es.mode+=TTS_OVER; break;
610       case TTEV_WRAP:   if (_tt->es.mode&TTS_WRAP) _tt->es.mode-=TTS_WRAP; else _tt->es.mode+=TTS_WRAP; break;
611       case TTEV_NULL:   break;
612       }       // life is short and love is always over in the morning black wind come carry me far away
613    }
614    else                                                      /* normal key */
615    {
616       if (_tt->es.mode&TTS_READONLY) return TTCHG_NOCHANGE;  /* readonly mode, ignore non-event keys */
617 	   _tt_add_char(&_tt->cur_h,&_tt->cur_w,tt_event);
618 //        _tt_chg_line(1);
619 //     else
620 //        _tt_chg_colu(1);
621    }
622    if ((_tt->es.r_cnt!=1)&&(tt_event!=TTEV_REPEAT))
623       _tt->es.r_cnt=1;                                       /* reset extra count */
624 //   if ((old_w!=_tt->cur_w)||(old_h!=_tt->cur_h)) changed|=TTCHG_CURSOR;
625    changed|=_tt_check_cursor_position();
626    _tt->es.last_ev=tt_event;
627    return changed;
628 }
629 
630 // converts an input character into an event for the tt system
631 // returns -1 or the line selected (w/return)
tt_parse_char(TextTool * tt,ushort key_code)632 long tt_parse_char(TextTool *tt, ushort key_code)
633 {
634    char c = (key_code & 0xFF);
635    int ret;
636    int event = TTEV_NULL;
637    _tt_top(tt);
638 
639    if (key_code & TEXTTOOL_KB_FLAG_CTRL)
640    {
641       switch (c)
642       {
643       case 'a': event=TTEV_BOL; break;
644       case 'b': event=TTEV_B_CHAR; break;
645       case 'd': event=TTEV_DEL; break;
646       case 'e': event=TTEV_EOL; break;
647       case 'f': event=TTEV_F_CHAR; break;
648       case 'g': event=TTEV_NULL; break;
649       case 'h': event=TTEV_BACKSP; break;
650       case 'k': event=TTEV_KILL; break;
651       case 'm': event=TTEV_RET; break;
652       case 'n': event=TTEV_F_LINE; break;
653       case 'o': event=TTEV_OVER; break;
654       case 'p': event=TTEV_B_LINE; break;
655       case 'u': event=TTEV_REPEAT; break;
656       case 'w': event=TTEV_WRAP; break;
657       }
658    }
659    else if (key_code & TEXTTOOL_KB_FLAG_SPECIAL)
660    {
661       switch(c)
662       {
663          case 0x48: event = TTEV_B_LINE; break;
664          case 0x50: event = TTEV_F_LINE; break;
665          case 0x4b: event = TTEV_B_CHAR; break;
666          case 0x4d: event = TTEV_F_CHAR; break;
667       }
668    }
669    else
670    {
671       switch(c)
672       {
673          case 0xd: event = TTEV_RET; break;
674          case 0x8: event = TTEV_BACKSP; break;
675       }
676    }
677 //   mprintf ("event = %d\n  TTEV_RET = %d  TTEV_NULL = %d\n",event,TTEV_RET,TTEV_NULL);
678    if (event == TTEV_NULL)
679    {
680       if (isprint(c)) event=c;
681    }
682    ret=_tt_do_event(event);
683    if (ret&TTCHG_CURSOR) LGCursor(_tt->scr_loc.crn.pt.x+_tt->cur_w,_tt->scr_loc.crn.pt.y+_tt->cur_h-_tt->disp_y);
684    if (ret&TTCHG_REDRAW) tt_display_all(_tt,NULL);
685    return (event==TTEV_RET)?_tt->cur_h:-1;
686 }
687 
tt_parse_string(TextTool * tt,char * st)688 long tt_parse_string(TextTool *tt, char *st)
689 {
690    char c;
691    int ret, i;
692    int event;
693    _tt_top(tt);
694 
695    for (i=0; i<strlen(st); i++)
696    {
697       c = st[i];
698       event = TTEV_NULL;
699       if (c == '\n')
700       { event = TTEV_RET; }
701       else
702       { if (isprint(c)) event=c; }
703       ret=_tt_do_event(event);
704       if (ret&TTCHG_CURSOR) LGCursor(_tt->scr_loc.crn.pt.x+_tt->cur_w,_tt->scr_loc.crn.pt.y+_tt->cur_h-_tt->disp_y);
705       if (ret&TTCHG_REDRAW) tt_display_all(_tt,NULL);
706    }
707    return (event==TTEV_RET)?_tt->cur_h:-1;
708 }
709 
tt_get(TextTool * tt,long line_num)710 char *tt_get(TextTool *tt, long line_num)
711 {
712    _tt_top(tt);
713    return (line_num>=_tt->max_h)?NULL:_tt->lines[line_num];
714 }
715 
716 
717 
718 
719 
720 
721 // i received a phone call lying in my bed, the other night
722 // i recognized her voice, she asked me out, i said all right
723 // i pulled up along the curb, she walked in front of my car
724 // she was looking nice, she was looking nice, and i said
725 // it was interesting watching her face as it was pressed against my windshield
726 // it was interesting watching her face as it was pressed against my windshield
727 //                                                        against my windshield
728 
729 // feel things going through my head, troubling me and making me nervous
730 // feel things going through my head, troubling me and making me nervous
731 
732 //                          - "Windshield", Green Magnet School, _Blood Music_
733