/* * Modify current screen line 'old' to match desired line 'new'. * The old line is at position ln. Each line is divided into 4 regions: * * nlws, olws - amount of leading white space on new/old line * com_head - length of common head * nchanged, ochanged - length of the parts changed * com_tail - length of a common tail */ /* @(#) _id_char.c: 1.1 10/15/83 (1.20 3/6/83) */ #include "curses.ext" #define min(a,b) (a<b ? a : b) _id_char (old, new, ln) register struct line *old, * new; { register chtype *oc_beg, *nc_beg,/* Beginning of changed part */ *oc_end, *nc_end;/* End of changed part */ chtype *p, *q; /* scratch */ int olws, nlws; /* old/new leading white space */ int com_head, com_tail; /* size of common head/tail */ int ochanged, nchanged; /* size of changed part */ int samelen; /* both lines are same length */ int nbl; /* # blanks in new line replacing nonblanks in old */ int nms; /* # chars same in new and old in middle part */ int cw_idc; /* cost of update with insert/delete character */ int cwo_idc; /* cost of update without insert/delete character */ int i, j, n; /* scratch */ int len, diff; static chtype nullline[] = {0, 0}; #ifdef DEBUG if(outf) fprintf(outf, "_id_char(%x, %x, %d)\n", old, new, ln); if(outf) fprintf(outf, "old: "); if(outf) fprintf(outf, "%8x: ", old); if (old == NULL) { if(outf) fprintf(outf, "()\n"); } else { if(outf) fprintf(outf, "%4d ", old->length); for (j=0; j<old->length; j++) { n = old->body[j]; if (n & A_ATTRIBUTES) putc('\'', outf); n &= 0177; if(outf) fprintf(outf, "%c", n>=' ' ? n : '.'); } if(outf) fprintf(outf, "\n"); } if(outf) fprintf(outf, "new: "); if(outf) fprintf(outf, "%8x: ", new); if (new == NULL) { if(outf) fprintf(outf, "()\n"); } else { if(outf) fprintf(outf, "%4d ", new->length); for (j=0; j<new->length; j++) { n = new->body[j]; if (n & A_ATTRIBUTES) putc('\'', outf); n &= 0177; if(outf) fprintf(outf, "%c", n>=' ' ? n : '.'); } if(outf) fprintf(outf, "\n"); } #endif DEBUG if (old == new) { return; } /* Start at the ends of the lines */ if( old ) { oc_beg = old -> body; oc_end = &old -> body[old -> length]; } else { oc_beg = nullline; oc_end = oc_beg; } if( new ) { nc_beg = new -> body; nc_end = &new -> body[new -> length]; } else { nc_beg = nullline; nc_end = nc_beg; } /* Find leading and trailing blanks */ olws = nlws = com_head = com_tail = 0; while (*--oc_end == ' ' && oc_end >= oc_beg) ; while (*--nc_end == ' ' && nc_end >= nc_beg) ; samelen = (nc_end-nc_beg) == (oc_end-oc_beg) ; while( *oc_beg == ' ' && oc_beg <= oc_end ) { oc_beg++; olws++; } while( *nc_beg == ' ' && nc_beg <= nc_end ) { nc_beg++; nlws++; } /* * Now find common heads and tails (com_head & com_tail). If the * lengths are the same, the change was probably at the beginning, * so count common tail first. This only matters if it could match * both ways, for example, when changing * " ####" * to * "#### ####" */ if( samelen ) { while( *oc_end==*nc_end && oc_beg<=oc_end && nc_beg<=nc_end ) { oc_end--; nc_end--; com_tail++; } while( *oc_beg==*nc_beg && oc_beg<=oc_end && nc_beg<=nc_end ) { oc_beg++; nc_beg++; com_head++; } } else { while( *oc_beg==*nc_beg && oc_beg<=oc_end && nc_beg<=nc_end ) { oc_beg++; nc_beg++; com_head++; } while( *oc_end==*nc_end && oc_beg<=oc_end && nc_beg<=nc_end ) { oc_end--; nc_end--; com_tail++; } } ochanged = oc_end - oc_beg + 1; nchanged = nc_end - nc_beg + 1; /* Optimization: lines are identical, so return now */ if( ochanged==0 && nchanged==0 && nlws==olws ) { #ifdef DEBUG if(outf) fprintf(outf, "identical lines, returning early\n"); #endif return; } #ifdef DEBUG if(outf) fprintf (outf, "Before costs: nlws=%2d olws=%2d\ com_head=%2d nchanged=%2d ochanged=%2d com_tail=%2d, icfixed %d,\ icvar %d/32\n", nlws, olws, com_head, nchanged, ochanged, com_tail, SP->term_costs.icfixed, SP->term_costs.icvar); if(outf) fprintf (outf, "samelen %d, *oc_beg %o *nc_beg %o\ *oc_end %o *nc_end %o\n", samelen, *oc_beg, *nc_beg, *oc_end, *nc_end); #endif DEBUG /* Decide whether to use insert/delete character */ #define costic(nchars) (SP->term_costs.icfixed + \ (((nchars)*SP->term_costs.icvar)>>5) ) /* #define costic(nchars) (fprintf(outf,"costic %d, add %g\n", nchars, SP->term_costs.icfixed + (nchars)*SP->term_costs.icvar),(SP->term_costs.icfixed + (nchars)*SP->term_costs.icvar)) */ cw_idc = cwo_idc = nchanged; if( olws > nlws ) /* delete char */ { cw_idc += costic(olws-nlws); } else { if( nlws > olws ) /* insert char */ { cw_idc += costic(nlws-olws); } } if( ochanged > nchanged ) /* delete char */ { cw_idc += costic(ochanged-nchanged); } else { if( nchanged > ochanged ) /* insert char */ { cw_idc += costic(nchanged-ochanged); } } if( olws != nlws ) { cwo_idc += com_head; } if( olws+ochanged != nlws+nchanged ) { cwo_idc += com_tail; } if( cw_idc > cwo_idc ) { /* * It's cheaper to NOT use insert/delete character! * This may be because the gain from saving the common head * and tail is not worth the cost of doing the insert and * delete chars (some terminals are slow at this, or the * tails may be really short) or because the cost is * infinite (i.e. the terminal does not have ins/del char). * We note this fact by forgetting about the common heads * and tails. */ ochanged += com_head+com_tail; nchanged += com_head+com_tail; oc_end += com_tail; nc_end += com_tail; oc_beg -= com_head; nc_beg -= com_head; com_head = 0; com_tail = 0; } /* * On magic cookie terminals, we have to check for the possibility * that there is a cookie that we must overwrite. This is only * necessary because a "go normal" cookie looks like an ordinary * blank but must compare differently. */ if( magic_cookie_glitch >= 0 && com_tail > 0 && nc_end[1] == ' ' && oc_end[0]&A_ATTRIBUTES && oc_end[1] == ' ' ) { #ifdef DEBUG if (outf) fprintf(outf, "adding one because of magic cookie\n"); #endif oc_end++; nc_end++; ochanged++; nchanged++; com_tail--; } #ifdef DEBUG if(outf) fprintf (outf, "After costs: nlws=%2d olws=%2d com_head=%2d nchanged=%2d ochanged=%2d com_tail=%2d, cost w/idc %d, cost wo/idc %d\n", nlws, olws, com_head, nchanged, ochanged, com_tail, cw_idc, cwo_idc); #endif DEBUG /* * Now actually go fix up the line. * There are several cases to consider. * The most important thing to keep in mind is * that deletions need to be done before insertions * to prevent shifting good text off the end of the line. */ if( com_head == 0 && com_tail == 0 ) { /* No common text - must redraw entire line */ if( ochanged == 0 && nchanged == 0 ) { /* Empty lines - do nothing */ _chk_typeahead(); return; } /* If empty old line, pretend leading blanks */ if( ochanged == 0 && !insert_null_glitch ) { olws = nlws; } /* Make sure changed parts start at same column */ j = nlws - olws; if( j > 0 ) { nc_beg -= j; nchanged += j; nlws = olws; } else { if( j < 0 ) { oc_beg += j; ochanged -= j; olws = nlws; } } for(nbl=nms=0,p=oc_beg,q=nc_beg;p<=oc_end && q<=nc_end;p++,q++) { if( *q==' ' && *p != ' ' ) { nbl++; } if( *q == *p ) { nms++; } #ifdef FULLDEBUG if (outf) fprintf(outf, "*p '%c', *q '%c', nms %d, nbl %d\n", *p, *q, nms, nbl); #endif } if( !clr_eol || insert_null_glitch || nms>=nbl ) { _showstring(ln, min(nlws, olws), nc_beg, nc_end, old); if( nlws + nchanged < olws + ochanged ) { _pos(ln, nlws + nchanged); _clreol(); } } else { if( ochanged > 0 ) { _pos(ln, olws); _clreol(); for (p=oc_beg; p<=oc_end; p++) *p = ' '; } _showstring(ln, nlws, nc_beg, nc_end, old); } _chk_typeahead(); return; } if( com_head==0 ) { /* We have only a common tail. */ if( nchanged == 0 && ochanged == 0 ) { _chk_typeahead(); return; } i = (nlws + nchanged) - (olws + ochanged); /* Simplify things - force olws == nlws */ j = nlws - olws; if( j > 0 ) { nc_beg -= j; nchanged += j; nlws = olws; } else { if( j < 0 ) { oc_beg += j; ochanged -= j; olws = nlws; } } if( i >= 0 ) { _showstring(ln, nlws, nc_beg, nc_end-i, old); if( i > 0 ) { _ins_string(ln, nlws+nchanged-i, nc_end-i+1, nc_end); } } else { _showstring(ln, nlws, nc_beg, nc_end, old); _delchars(-i); } _chk_typeahead(); return; } /* At this point, we know there is a common head (com_head != 0) */ if( nlws < olws ) { /* Do the leading delete chars right away */ _pos(ln, 0); _delchars(diff = olws - nlws); olws = nlws; len = old->length; for( i=0; i<len; i++ ) { old->body[i] = old->body[i]+diff; } } if (com_tail == 0) { if( nchanged == 0 && ochanged == 0 ) { if( nlws > olws ) { _ins_blanks(ln, 0, nlws - olws); } _chk_typeahead(); return; } _showstring(ln, olws+com_head, nc_beg, nc_end, old); if( nchanged < ochanged ) { _pos(ln, olws + com_head + nchanged); _clreol(); } if( nlws > olws ) { _ins_blanks(ln, 0, nlws - olws); } } else { if( nchanged > 0 && ochanged > 0 ) { i = min(nchanged, ochanged); _showstring(ln, olws+com_head, nc_beg, nc_beg+i-1, old); } if( nchanged < ochanged ) { _pos(ln, nlws + com_head + nchanged); _delchars(ochanged - nchanged); } else { if( nchanged > ochanged ) { _ins_string(ln, olws+com_head+ochanged, nc_beg + ochanged, nc_end); } } if( nlws > olws ) { _ins_blanks(ln, 0, nlws-olws); } } _chk_typeahead(); return; } /* * Insert nblanks blanks at position (sline, scol). */ static int _ins_blanks(sline, scol, nblanks) int sline, scol, nblanks; { #ifdef DEBUG if (outf) fprintf(outf, "_ins_blanks at (%d, %d) %d blanks\n", sline, scol, nblanks); #endif _pos(sline, scol); if( nblanks > 1 && parm_ich ) { /* Insert the characters and then draw on the blanks */ _inschars(nblanks); } else { /* * Type the blanks in "insert mode". This includes * having to send insert_character before each character * is output. */ _insmode(1); _blanks(nblanks); } _insmode(0); } /* * Insert the given string on the screen. * This is like _showstring but you know you're in insert mode. */ static _ins_string(sline, scol, first, last) int sline, scol; chtype *first, *last; { int len = last-first+1; #ifdef DEBUG if (outf) fprintf(outf, "_ins_string at (%d, %d) %d chars\n", sline, scol, len); #endif _pos(sline, scol); if( len > 1 && parm_ich ) { /* Insert the characters and then draw on the blanks */ _inschars(len); _showstring(sline, scol, first, last, NULL); } else { /* * Type the characters in "insert mode". This includes * having to send insert_character before each character * is output. */ _insmode(1); _showstring(sline, scol, first, last, NULL); } _insmode(0); }