home *** CD-ROM | disk | FTP | other *** search
/ Atari Mega Archive 1 / Atari Mega Archive - Volume 1.iso / gnu / progutil / stdwin.zoo / textedit / textlow.c < prev   
Encoding:
C/C++ Source or Header  |  1990-03-30  |  12.6 KB  |  666 lines

  1. /* Text Edit, low-level routines */
  2. /* $Header: /userfs3/amoeba/guido/src/stdwin/textedit/RCS/textlow.c,v 1.2 89/07/24 13:52:26 guido Exp Locker: guido $ */
  3.  
  4. /* CONVENTION:
  5.     routines beginning with te... have a first parameter 'tp';
  6.     routines beginning with z... don't, or are macros that
  7.     implicitly use a variable or parameter 'tp'
  8. */
  9.  
  10. #include "text.h"
  11.  
  12. #ifdef __STDC__
  13. # define    P(s) s
  14. #else
  15. # define P(s) ()
  16. #endif
  17.  
  18.  
  19. /* textlow.c */
  20. static void teinvertfocus P((TEXTEDIT *tp ));
  21. static void teinvert P((TEXTEDIT *tp , int f1 , int f2 ));
  22. static void teshift P((TEXTEDIT *tp , int n , bufpos first , bufpos last ));
  23. static void teoffset P((TEXTEDIT *tp , int n , int first , int last ));
  24. static void temoveup P((TEXTEDIT *tp , int n , char *from , char *to ));
  25. static void temovedown P((TEXTEDIT *tp , int n , char *from , char *to ));
  26. #undef P
  27.  
  28. /* Variants on wtextwidth, wtextbreak, wdrawtext taking the gap in account.
  29.    These have two buffer offsets as parameters instead of a text pointer
  30.    and a length; tetextbreak also returns a buffer offset */
  31.  
  32. /* These routines now also take tabs into account.
  33.    They should now only be called with a line start for 'pos' ! */
  34.  
  35. int
  36. tetextwidth(tp, pos, end)
  37.     register TEXTEDIT *tp;
  38.     bufpos pos, end;
  39. {
  40.     char *p= tp->buf + pos;
  41.     register char *e= tp->buf +
  42.         (tp->gap >= pos && tp->gap < end ? tp->gap : end);
  43.     int w= 0;
  44.     register char *k;
  45.     
  46.     zcheckpos(pos);
  47.     zcheckpos(end);
  48.     zassert(pos <= end);
  49.     
  50.     /* This do loop is executed once or twice only! */
  51.     do {
  52.         for (k= p; k < e; ++k) {
  53.             if (*k == '\t') {
  54.                 if (k > p)
  55.                     w += wtextwidth(p, (int)(k-p));
  56.                 w= znexttab(w);
  57.                 p= k+1;
  58.             }
  59.         }
  60.         if (k > p)
  61.             w += wtextwidth(p, (int)(k-p));
  62.         if (tp->gap >= pos) {
  63.             p= tp->buf + zgapend;
  64.             e= tp->buf + end;
  65.         }
  66.     } while (k < e);
  67.     
  68.     return w;
  69. }
  70.  
  71. bufpos
  72. tetextbreak(tp, pos, end, width)
  73.     register TEXTEDIT *tp;
  74.     bufpos pos, end;
  75.     int width;
  76. {
  77.     char *p= tp->buf + pos;
  78.     register char *e= tp->buf +
  79.         (tp->gap >= pos && tp->gap < end ? tp->gap : end);
  80.     int w= 0;
  81.     int nn= pos;
  82.     register char *k;
  83.     
  84.     zcheckpos(pos);
  85.     zcheckpos(end);
  86.     zassert(pos <= end);
  87.     
  88.     /* This do loop is executed once or twice only! */
  89.     do {
  90.         for (k= p; k < e; ++k) {
  91.             if (*k == '\t') {
  92.                 if (k > p) {
  93.                     int n= wtextbreak(p, (int)(k-p),
  94.                         width-w);
  95.                     if (n < k-p)
  96.                         return p - tp->buf + n;
  97.                     w += wtextwidth(p, (int)(k-p));
  98.                 }
  99.                 w= znexttab(w);
  100.                 if (w > width)
  101.                     return k - tp->buf;
  102.                 p= k+1;
  103.             }
  104.         }
  105.         if (k > p) {
  106.             int n= wtextbreak(p, (int)(k-p), width-w);
  107.             if (n < k-p)
  108.                 return p - tp->buf + n;
  109.             w += wtextwidth(p, (int)(k-p));
  110.         }
  111.         if (tp->gap >= pos) {
  112.             p= tp->buf + zgapend;
  113.             e= tp->buf + end;
  114.         }
  115.     } while (k < e);
  116.     
  117.     return end;
  118. }
  119.  
  120. hcoord
  121. tedrawtext(tp, h, v, pos, end)
  122.     register TEXTEDIT *tp;
  123.     int h, v;
  124.     bufpos pos, end;
  125. {
  126.     char *p= tp->buf + pos;
  127.     register char *e= tp->buf +
  128.         (tp->gap >= pos && tp->gap < end ? tp->gap : end);
  129.     int w= 0;
  130.     register char *k;
  131.     
  132.     zcheckpos(pos);
  133.     zcheckpos(end);
  134.     zassert(pos <= end);
  135.     
  136.     /* This do loop is executed once or twice only! */
  137.     do {
  138.         for (k= p; k < e; ++k) {
  139.             if (*k == '\t') {
  140.                 if (k > p)
  141.                     w= wdrawtext(h+w, v, p, (int)(k-p))
  142.                         - h;
  143.                 w= znexttab(w);
  144.                 p= k+1;
  145.             }
  146.         }
  147.         if (k > p)
  148.             w= wdrawtext(h+w, v, p, (int)(k-p)) - h;
  149.         if (tp->gap >= pos) {
  150.             p= tp->buf + zgapend;
  151.             e= tp->buf + end;
  152.         }
  153.     } while (k < e);
  154.     
  155.     return h+w;
  156. }
  157.  
  158. /* Safe memory allocation - these abort instead of returning NULL */
  159.  
  160. char *
  161. zmalloc(n)
  162.     int n;
  163. {
  164.     char *p= malloc((size_t)n);
  165.     
  166.     if (p == NULL) {
  167.         dprintf("zmalloc(%d): out of mem", n);
  168.         exit(1);
  169.     }
  170.     return p;
  171. }
  172.  
  173. char *
  174. zrealloc(p, n)
  175.     char *p;
  176.     int n;
  177. {
  178.     char *q= realloc(p, (size_t)n);
  179.     if (q == NULL) {
  180.         dprintf("zrealloc(0x%lx, %d): out of mem", (long)p, n);
  181.         exit(1);
  182.     }
  183.     return q;
  184. }
  185.  
  186. /* Create/destroy a text-edit record */
  187.  
  188. TEXTEDIT *
  189. tealloc(win, left, top, width)
  190.     WINDOW *win;
  191.        int left, top, width;
  192. {
  193.     return tesetup(win, left, top, left+width, top, TRUE);
  194. }
  195.  
  196. TEXTEDIT *
  197. tecreate(win, left, top, right, bottom)
  198.     WINDOW *win;
  199.     int left, top, right, bottom;
  200. {
  201.     return tesetup(win, left, top, right, bottom, TRUE);
  202. }
  203.  
  204. TEXTEDIT *
  205. tesetup(win, left, top, right, bottom, drawing)
  206.     WINDOW *win;
  207.     int left, top, right, bottom;
  208.     bool drawing;
  209. {
  210.     TEXTEDIT *tp= (TEXTEDIT*) zmalloc((int)(sizeof(TEXTEDIT)));
  211.     TEXTATTR saveattr;
  212.     
  213.     tp->win= win;
  214.     tp->left= left;
  215.     tp->top= top;
  216.     tp->right= right;
  217.     tp->width= right-left;
  218.     
  219.     wgettextattr(&saveattr);
  220.     if (win != NULL) {
  221.         wgetwintextattr(win, &tp->attr);
  222.         wsettextattr(&tp->attr);
  223.     }
  224.     else
  225.         tp->attr = saveattr;
  226.     tp->vspace= wlineheight();
  227.     tp->tabsize= 8*wcharwidth(' ');
  228.     if (win != NULL)
  229.         wsettextattr(&saveattr);
  230.     
  231.     tp->bottom= tp->top + tp->vspace;
  232.     
  233.     tp->foc= tp->foclen= 0;
  234.     
  235.     tp->buflen= 1;
  236.     tp->buf= zmalloc(tp->buflen);
  237.     
  238.     tp->gap= 0;
  239.     tp->gaplen= tp->buflen;
  240.     
  241.     tp->nlines= 1;
  242.     tp->nstart= STARTINCR;
  243.     tp->start= (bufpos*) zmalloc((int)(tp->nstart*(int)sizeof(bufpos)));
  244.     tp->start[0]= tp->start[1]= tp->buflen;
  245.     
  246.     tp->aim= UNDEF;
  247.     tp->focprev= FALSE;
  248.     tp->hilite= FALSE;
  249.     tp->mdown= FALSE;
  250.     tp->drawing= drawing;
  251.     tp->opt_valid= FALSE;
  252.     
  253.     if (drawing)
  254.         tesetcaret(tp);
  255.     
  256.     zcheck();
  257.     
  258.     return tp;
  259. }
  260.  
  261.  
  262. void
  263. tedestroy(tp)
  264.     register TEXTEDIT *tp;
  265. {
  266.     wchange(tp->win, tp->left, tp->top, tp->right, tp->bottom);
  267.     tefree(tp);
  268. }
  269.  
  270. void
  271. tefree(tp)
  272.     register TEXTEDIT *tp;
  273. {
  274.     if (tp->drawing) {
  275.         wnocaret(tp->win);
  276.         tehidefocus(tp);
  277.     }
  278.     if (tp->buf != NULL)
  279.         free(tp->buf);
  280.     if (tp->start != NULL)
  281.         free((char*)tp->start);
  282.     free((char*)tp);
  283. }
  284.  
  285. /* Show/hide the focus highlighting.
  286.    The boolean hilite is set when highlighting is visible.
  287.    teshowfocus ensures the highlighting is visible (if applicable);
  288.    tehidefocus ensures it is invisible.
  289.    teinvertfocus does the hard work (it is also called from zdraw) */
  290.  
  291. void
  292. teshowfocus(tp)
  293.     register TEXTEDIT *tp;
  294. {
  295.     if (!tp->hilite && tp->foclen > 0) {
  296.         wbegindrawing(tp->win);
  297.         teinvertfocus(tp);
  298.         wenddrawing(tp->win);
  299.         tp->hilite= TRUE;
  300.     }
  301. }
  302.  
  303. void
  304. tehidefocus(tp)
  305.     register TEXTEDIT *tp;
  306. {
  307.     if (tp->hilite) {
  308.         wbegindrawing(tp->win);
  309.         teinvertfocus(tp);
  310.         wenddrawing(tp->win);
  311.         tp->hilite= FALSE;
  312.     }
  313. }
  314.  
  315. static void
  316. teinvertfocus(tp)
  317.     register TEXTEDIT *tp;
  318. {
  319.     teinvert(tp, tp->foc, zfocend);
  320. }
  321.  
  322. /* Change to a new focus.
  323.    Sometimes this may keep the focus visible, sometimes not. */
  324.  
  325. void
  326. techangefocus(tp, f1, f2)
  327.     register TEXTEDIT *tp;
  328.     int f1, f2;
  329. {
  330.     if (tp->hilite) {
  331.         wbegindrawing(tp->win);
  332.         if (f1 == tp->foc)
  333.             teinvert(tp, zfocend, f2);
  334.         else if (f2 == zfocend)
  335.             teinvert(tp, f1, tp->foc);
  336.         else {
  337.             teinvert(tp, tp->foc, zfocend);
  338.             tp->hilite= FALSE;
  339.         }
  340.         wenddrawing(tp->win);
  341.     }
  342.     tp->foc= f1;
  343.     tp->foclen= f2-f1;
  344. }
  345.  
  346. /* Low-level interface: invert the area between f1 and f2 */
  347.  
  348. static void
  349. teinvert(tp, f1, f2)
  350.     register TEXTEDIT *tp;
  351.     int f1, f2;
  352. {
  353.     coord h, v, hlast, vlast;
  354.     
  355.     if (f1 == f2)
  356.         return;
  357.     if (f2 < f1) {
  358.         int temp= f1;
  359.         f1= f2;
  360.         f2= temp;
  361.     }
  362.     
  363.     tewhichpoint(tp, f1, &h, &v);
  364.     tewhichpoint(tp, f2, &hlast, &vlast);
  365.     
  366.     if (v == vlast)
  367.         winvert(h, v, hlast, v + tp->vspace);
  368.     else {
  369.         winvert(h, v, tp->right, v + tp->vspace);
  370.         winvert(tp->left, v + tp->vspace, tp->right, vlast);
  371.         winvert(tp->left, vlast, hlast, vlast + tp->vspace);
  372.     }
  373. }
  374.  
  375. /* Draw procedure */
  376.  
  377. void
  378. tedraw(tp)
  379.     register TEXTEDIT *tp;
  380. {
  381.     tedrawnew(tp, tp->left, tp->top, tp->right, tp->bottom);
  382. }
  383.  
  384. void
  385. tedrawnew(tp, left, top, right, bottom)
  386.     register TEXTEDIT *tp;
  387.     coord left, top, right, bottom;
  388. {
  389.     lineno ifirst, ilast, i;
  390.     
  391.     /* Compute first, last line to be drawn */
  392.     ifirst= (top - tp->top)/tp->vspace;
  393.     if (ifirst < 0)
  394.         ifirst= 0;
  395.     ilast= (bottom - tp->top + tp->vspace - 1)/tp->vspace;
  396.     if (ilast > tp->nlines)
  397.         ilast= tp->nlines;
  398.     
  399.     /* Draw lines ifirst...ilast-1 */
  400.     for (i= ifirst; i < ilast; ++i) {
  401.         bufpos pos= tp->start[i];
  402.         bufpos end= tp->start[i+1];
  403.         if (end > pos && zcharbefore(end) == EOL)
  404.             zdecr(&end);
  405.         while (end > pos && zcharbefore(end) == ' ')
  406.             zdecr(&end);
  407.         (void) tedrawtext(tp, tp->left, tp->top + i*tp->vspace,
  408.             pos, end);
  409.     }
  410.     if (tp->hilite)
  411.         teinvertfocus(tp);
  412. }
  413.  
  414. /* Move the gap to a new position */
  415.  
  416. void
  417. temovegapto(tp, newgap)
  418.     register TEXTEDIT *tp;
  419.     bufpos newgap;
  420. {
  421.     zcheck();
  422.     zassert(0<=newgap && newgap+tp->gaplen<=tp->buflen);
  423.     
  424.     if (newgap < tp->gap)
  425.         teshift(tp, tp->gaplen, newgap, tp->gap);
  426.     else if (newgap > tp->gap)
  427.         teshift(tp, -tp->gaplen, zgapend, newgap+tp->gaplen);
  428.     tp->gap= newgap;
  429.     
  430.     zcheck();
  431. }
  432.  
  433. /* Extend the gap */
  434.  
  435. void
  436. tegrowgapby(tp, add)
  437.     register TEXTEDIT *tp;
  438.     int add;
  439. {
  440.     zcheck();
  441.     zassert(add>=0);
  442.     
  443.     tp->buf= zrealloc(tp->buf, tp->buflen + add);
  444.     teshift(tp, add, zgapend, tp->buflen);
  445.     tp->gaplen += add;
  446.     if (tp->start[tp->nlines-1] == tp->buflen)
  447.         tp->start[tp->nlines-1]= tp->buflen+add;
  448.     tp->start[tp->nlines]= (tp->buflen += add);
  449.     
  450.     zcheck();
  451. }
  452.  
  453. /* Shift buf[first..last-1] n bytes (positive right, negative left) */
  454.  
  455. static void
  456. teshift(tp, n, first, last)
  457.     register TEXTEDIT *tp;
  458.     int n;
  459.     bufpos first, last;
  460. {
  461.     teoffset(tp, n, first, last);
  462.     if (n < 0)
  463.         temovedown(tp, last-first, tp->buf+first, tp->buf+first+n);
  464.     else if (n > 0)
  465.         temoveup(tp, last-first, tp->buf+first, tp->buf+first+n);
  466. }
  467.  
  468. static void
  469. teoffset(tp, n, first, last)
  470.     register TEXTEDIT *tp;
  471.     int n;
  472.     int first, last;
  473. {
  474.     int i;
  475.     
  476.     zassert(0<=first&&first<=last&&last<=tp->buflen);
  477.     
  478.     i= 0;
  479.     while (tp->start[i] < first)
  480.         ++i;
  481.     while (tp->start[i] < last) {
  482.         tp->start[i] += n;
  483.         ++i;
  484.     }
  485. }
  486.  
  487. static void
  488. temoveup(tp, n, from, to)
  489.     register TEXTEDIT *tp;
  490.     int n;
  491.     char *from, *to;
  492. {
  493.     zassert(from <= to);
  494.     
  495.     from += n, to += n;
  496.     while (--n >= 0)
  497.         *--to = *--from;
  498. }
  499.  
  500. static void
  501. temovedown(tp, n, from, to)
  502.     register TEXTEDIT *tp;
  503.     int n;
  504.     char *from, *to;
  505. {
  506.     zassert(from >= to);
  507.     
  508.     while (--n >= 0)
  509.         *to++ = *from++;
  510. }
  511.  
  512. /* Make all start entries pointing into the gap point to beyond it
  513.    TO DO: replace by a routine to delete the focus??? */
  514.  
  515. void 
  516. teemptygap(tp)
  517.     register TEXTEDIT *tp;
  518. {
  519.     lineno i;
  520.     
  521.     for (i= 0; tp->start[i] < tp->gap; ++i)
  522.         ;
  523.     for (; tp->start[i] < zgapend; ++i)
  524.         tp->start[i]= zgapend;
  525. }
  526.  
  527. /* Set the caret at the new focus position,
  528.    or display the focus highlighting, if applicable.
  529.    Also call wshow() of the focus.
  530.    As a side effect, the optimization data is invalidated */
  531.  
  532. void
  533. tesetcaret(tp)
  534.     register TEXTEDIT *tp;
  535. {
  536.     coord h, v, hlast, vlast;
  537.     
  538.     tewhichpoint(tp, tp->foc, &h, &v);
  539.     
  540.     if (tp->foclen == 0) {
  541.         wsetcaret(tp->win, h, v);
  542.         hlast= h;
  543.         vlast= v;
  544.     }
  545.     else {
  546.         tewhichpoint(tp, zfocend, &hlast, &vlast);
  547.         wnocaret(tp->win);
  548.         teshowfocus(tp);
  549.     }
  550.     wshow(tp->win, h, v, hlast, vlast + tp->vspace);
  551.     tp->opt_valid= FALSE;
  552. }
  553.  
  554. /* Coordinate transformations.
  555.    The following coordinate systems exist;
  556.    a position in the text can be expressed in any of these:
  557.    
  558.        A) offset in buffer with gap removed (used for focus)
  559.     B) offset in buffer (used for start[] array)
  560.     C) (line number, offset in line taking gap into account)
  561.     D) (h, v) on screen
  562.    
  563.    Conversions exist between successive pairs:
  564.    
  565.        A -> B: pos= zaddgap(foc)
  566.     B -> A: foc= zsubgap(pos)
  567.     
  568.     B -> C: line= zwhichline(pos, prev); offset= pos-start[line]
  569.     C -> B: pos= offset + start[line]
  570.     
  571.     C -> D: v= i*vspace; h= ztextwidth(start[i], start[i]+offset)
  572.     D -> C: i= v/wlh; offset= ztextround(i, h) - start[i]
  573. */
  574.  
  575. /* Find (h, v) corresponding to focus position */
  576.  
  577. void
  578. tewhichpoint(tp, f, h_ret, v_ret)
  579.     TEXTEDIT *tp;
  580.     focpos f;
  581.     coord *h_ret, *v_ret;
  582. {
  583.     bufpos pos= zaddgap(f);
  584.     lineno i= tewhichline(tp, pos, f == tp->foc && tp->focprev);
  585.     hcoord h= tetextwidth(tp, tp->start[i], pos);
  586.     
  587.     *h_ret= h + tp->left;
  588.     *v_ret= i*tp->vspace + tp->top;
  589. }
  590.  
  591. /* To which line does the given buffer position belong? */
  592.  
  593. lineno
  594. tewhichline(tp, pos, prev)
  595.     register TEXTEDIT *tp;
  596.     bufpos pos;
  597.     bool prev; /* Cf. focprev */
  598. {
  599.     lineno i;
  600.     
  601.     for (i= 0; pos > tp->start[i+1]; ++i)
  602.         ;
  603.     if (pos == tp->start[i+1] && i+1 < tp->nlines) {
  604.         ++i;
  605.         if (prev && zcharbefore(tp->start[i]) != EOL)
  606.             --i;
  607.     }
  608.     
  609.     return i;
  610. }
  611.  
  612. /* Convert point in window to buffer position,
  613.    possibly taking double-clicking into account.
  614.    If required, the line number is also returned. */
  615.  
  616. bufpos
  617. tewhereis(tp, h, v, line_return)
  618.     register TEXTEDIT *tp;
  619.     coord h, v;
  620.     int *line_return;
  621. {
  622.     bufpos pos;
  623.     lineno i;
  624.     
  625.     i= (v - tp->top)/tp->vspace;
  626.     if (i >= tp->nlines) {
  627.         i= tp->nlines;
  628.         pos= tp->buflen;
  629.     }
  630.     else if (i < 0) {
  631.         i= 0;
  632.         pos= 0;
  633.     }
  634.     else
  635.         pos= tetextround(tp, i, h);
  636.     if (line_return != NULL)
  637.         *line_return= i;
  638.     return pos;
  639. }
  640.  
  641. /* Find the buffer position nearest to the given h coordinate,
  642.    in the given line */
  643.  
  644. bufpos
  645. tetextround(tp, i, h)
  646.     register TEXTEDIT *tp;
  647.     lineno i;
  648.     hcoord h;
  649. {
  650.     bufpos pos;
  651.     bufpos end= tp->start[i+1];
  652.     
  653.     h -= tp->left;
  654.     if (end > tp->start[i] && zcharbefore(end) == EOL)
  655.         zdecr(&end);
  656.     pos= tetextbreak(tp, tp->start[i], end, h);
  657.     
  658.     if (pos < end) {
  659.         if (h - tetextwidth(tp, tp->start[i], pos) >=
  660.             tetextwidth(tp, tp->start[i], znext(pos)) - h)
  661.             zincr(&pos);
  662.     }
  663.     
  664.     return pos;
  665. }
  666.