home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 7 / Apprentice-Release7.iso / Source Code / C / Applications / Moscow ML 1.42 / ANSIshellƒ / os_mac_eKeys.c < prev    next >
Encoding:
C/C++ Source or Header  |  1997-05-18  |  19.7 KB  |  761 lines  |  [TEXT/R*ch]

  1. /* os_mac_eKeys.c
  2.  * 3May92  e
  3.  */
  4.  
  5. #include "os_mac.h"
  6.  
  7. /* extensions to an editor for text files by e
  8.    questions/comments via Internet <e@Flavors.COM> */
  9. /* Copyright © e, 1989,1992. All rights reserved.
  10.     Developed using THINK C 5.0.1 for use with Gambit Scheme.
  11.     This code may be freely distributed as long as this notice remains.
  12.    thanks to Marc Feeley.
  13. */
  14.  
  15. #if LISPmodeP
  16.  
  17. #define graphic_char(c) (!(((c)>=0) && ((c)<=32)))
  18.  
  19. static long whsp_fwd_guts( eRec **hE, long offs )
  20.   register char *text = *((**hE).hText);
  21.   register char *p = text + offs;
  22.   register char *end = text + eTeTextLength( hE );
  23.   register char c;
  24.  
  25.   while( p < end )
  26.   { c = *p++;
  27.     if( graphic_char(c) )
  28.     { if( c != ';' )
  29.           return p - text - 1;
  30.       else
  31.         while ( p < end && *p++ != '\r' )  ;
  32.     }
  33.   }
  34.   return end - text;
  35. }
  36.  
  37. static long whsp_fwd( eRec **hE )
  38.   long offs = eTeChPosToOffset( hE, (**hE).selActive ? (**hE).selEnd : (**hE).caretChPos );
  39.   return whsp_fwd_guts( hE, offs );
  40. }
  41.  
  42. static long whsp_bwd( eRec **hE )
  43. {
  44.   long offs = eTeChPosToOffset( hE, (**hE).selActive ? (**hE).selStart : (**hE).caretChPos );
  45.   register char *text = *((**hE).hText);
  46.   register char *p = text + offs - 1;
  47.   register char c;
  48.  
  49.   while( p >= text )
  50.   { if( graphic_char( *p ) )
  51.         return p - text + 1;
  52.     else
  53.       p--;
  54.   }
  55.   return 0;
  56. }
  57.  
  58. long balance_fwd_guts( eRec **hE, long offs, int bal )
  59.   register char *text = *((**hE).hText);
  60.   register char *p = text + offs;
  61.   register char *end = text + eTeTextLength( hE );
  62.   register int b = bal;
  63.   register char c;
  64.  
  65.   while( p < end && !graphic_char(*p) ) p++;
  66.  
  67.   while( p < end )
  68.   { c = *p++;
  69.     if( graphic_char(c) )
  70.       switch (c)
  71.       { case '(':
  72.           b++;
  73.           break;
  74.         case ')':
  75.           b--;
  76.           if (b <= 0) goto scan_done;
  77.           break;
  78.         case ';':
  79.           while ( p < end && *p++ != '\r' )  ;
  80.           break;
  81.         case '"':
  82.         { while (p<end)
  83.           { c = *p++;
  84.               if( c == '"' ) goto breake;
  85.               if( c == '\\' ) p++;
  86.           }
  87.           return -1;
  88.           breake:
  89.           if( b <= 0 ) goto scan_done;
  90.           break;
  91.         }
  92.         case '#':
  93.           if( *p == '\\' ) p += 2;
  94.           break;
  95.         case ',':
  96.           if( *p == '@' ) p++;
  97.           break;
  98.         case '\'':
  99.         case '`':
  100.           break;
  101.         default:
  102.         { while ((p<=end) && graphic_char(c) && (c!='(') && (c!=')') && (c!='"'))
  103.             c = *p++;
  104.           p--;
  105.           if (b <= 0) goto scan_done;
  106.           break;
  107.         }
  108.      }
  109.   }
  110.   return -1;
  111.  
  112.   scan_done:
  113.   return p-text;
  114. }
  115.  
  116. long balance_fwd( eRec **hE, int bal )
  117.   long offs = eTeChPosToOffset( hE, (**hE).selActive ? (**hE).selEnd : (**hE).caretChPos );
  118.   return balance_fwd_guts( hE, offs, bal );
  119. }
  120.  
  121. long balance_bwd_guts( eRec **hE, long offs, int bal, int prefix )
  122. {
  123.   register char *text = *((**hE).hText);
  124.   register char *p = text + offs;
  125.   register int b = bal;
  126.  
  127.   while ((p>text) && !graphic_char(p[-1])) p--;
  128.  
  129.   while (p>text)
  130.     if ((p>=text+3) && (p[-2]=='\\') && (p[-3]=='#'))
  131.     { p -= 3;
  132.       if (b <= 0) goto scan_prefix;
  133.     }
  134.     else
  135.     { register char c = *--p;
  136.       if (graphic_char(c))
  137.         switch (c)
  138.         { case '(':
  139.             b--;
  140.             if (b <= 0) goto scan_prefix;
  141.             break;
  142.           case ')':
  143.             b++;
  144.             break;
  145.           case '"':
  146.           { while (p>text)
  147.             { register char *q = --p;
  148.               while ((q>text) && (q[-1]=='\\')) q--;
  149.               if ((p-q)&1L)
  150.                 p = q;
  151.               else if (*p=='"')
  152.                 if (b <= 0) goto scan_prefix; else goto brak;
  153.             }
  154.             return -1;
  155.             brak:
  156.             break;
  157.           }
  158.           default:
  159.           { while ((p>=text) && graphic_char(c) &&
  160.                    (c!='(') && (c!=')') && (c!='"'))
  161.               c = *--p;
  162.             p++;
  163.             if (b <= 0) goto scan_prefix;
  164.             break;
  165.           }
  166.       }
  167.   }
  168.   return -1;
  169.  
  170.   scan_prefix:
  171.   if (prefix)
  172.     while (p>text)
  173.     { register char c = *--p;
  174.       if ((c!='\'') && (c!='`') && (c!=',') && (c!='@') && (c!='#'))
  175.       { p++; break; }
  176.     }
  177.   return p-text;
  178. }
  179.  
  180. long balance_bwd( eRec **hE, int bal, int prefix )
  181. {
  182.   long offs = eTeChPosToOffset( hE, (**hE).selActive ? (**hE).selStart : (**hE).caretChPos );
  183.   return balance_bwd_guts( hE, offs, bal, prefix );
  184. }
  185.  
  186. static int rangeBalance( register char *beg, register char *end )
  187. {
  188.     register int n = 0;
  189.     
  190.     while(beg < end)
  191.     {    if (*beg == '(')        n++;
  192.         else if (*beg == ')')    n--;
  193.         beg++;
  194.     }
  195.     return(n);
  196. }
  197.  
  198. /* 23Jul92  e  */
  199.  
  200. extern int check_TEInsert( char *ptr, long len, eRec **hE, int bold );
  201.  
  202. #if 0
  203.  
  204. /* 17Aug92  e  */
  205.  
  206. void eTabCommand( eRec **hE, short modifiers, short style )
  207. { long open_paren;
  208.   long selStart, selEnd, selLen;
  209.   ChPos solcp, eolcp;
  210.   char *p;
  211.   long sol, eol;
  212.   long adj = 0;
  213.   Boolean selActive;
  214.  
  215.   if( ( selActive = (**hE).selActive ) )
  216.   {    selStart = eTeChPosToOffset( hE, (**hE).selStart );
  217.     selEnd   = eTeChPosToOffset( hE, (**hE).selEnd );
  218.     selLen   = selEnd - selStart;
  219.     eTeSetSelect( hE, selStart, selStart );
  220.     solcp = (**hE).selStart;
  221.   }
  222.   else
  223.   {    selStart = eTeChPosToOffset( hE, (**hE).caretChPos );
  224.     selLen = 0;
  225.     solcp = (**hE).caretChPos;
  226.   }
  227.   eolcp.h = solcp.h = 0;    /* start of line */
  228.   eolcp.v = solcp.v + 1;
  229.   eol = eTeChPosToOffset( hE, eolcp );    /* next line */
  230.   sol = eTeChPosToOffset( hE, solcp );    /* this line */
  231.   p = *((**hE).hText) + sol;
  232.   while( ! graphic_char( *p ) && sol < eol && *p != RETURN ) { p++; sol++; solcp.h++; }
  233.   if( sol != selStart ) eTeSetSelect( hE, sol, sol );
  234.   if( sol < selStart )  adj = selStart - sol;
  235.   
  236.   open_paren = balance_bwd( hE, 0, 1 );
  237.   eolcp = eTeOffsetToChPos( hE, open_paren );
  238.   if( solcp.h > eolcp.h )
  239.   { eolcp.v = solcp.v;
  240.     eol = eTeChPosToOffset( hE, eolcp );
  241.     eTeSetSelect( hE, eol, eol );
  242.     eTeKillTo( hE, solcp );
  243.   }
  244.   else while (solcp.h < eolcp.h)
  245.   { long len = (eolcp.h - solcp.h < 80) ? eolcp.h - solcp.h : 80;
  246.     if (check_TEInsert( "                                                                                ",
  247.                         len, hE, style )) goto err;
  248.     solcp.h += len;
  249.   }
  250.   err:
  251.   if( sol < selStart || selActive )
  252.   { selStart = eTeChPosToOffset( hE, (**hE).caretChPos ) + adj;
  253.     eTeSetSelect( hE, selStart, selStart + selLen );
  254.   }
  255.   eTeShowCaret( hE );
  256. }
  257.  
  258. #else
  259.  
  260. /* 14Oct92  e  */
  261.  
  262. static char dncase( char c )
  263. { if( c >= 'A' &&  c <= 'Z' ) return c + 0x20;
  264.   else return c;
  265. }
  266.  
  267. static Boolean eq_subfs( char *p, char *q )
  268. { char c;
  269.   while( c = *p++ )
  270.     if( c != dncase( *q++ ) )
  271.       return ( ( c == '•' ) ? TRUE : FALSE ); /* was always FALSE -- 05Jan95 e */
  272.   c = *q;
  273.   return( ( graphic_char(c) && c != '(' && c != '"' ) ? FALSE : TRUE );
  274. }
  275.  
  276. /* subforms of forms whose car is in this table are indented specially...
  277.   call the subform to be indented "the target":
  278.   if there are more than cnt subforms between the car and the target,
  279.     then the last subform before the target determines the postion of the target,
  280.   otherwise the target is indented one more space than the car
  281. */ 
  282.  
  283. struct _spec_subfs { char *str; char cnt; };
  284.  
  285. static struct _spec_subfs spec_subfs_data[] =
  286.     { { "if", 1 },
  287.       { "do", 2 },
  288.       { "do*", 2 },
  289.       { "let", 18 },    /* special case for named let */
  290.       { "let*", 1 },
  291.       { "case", 1 },
  292.       { "begin", 0 },
  293.       { "lambda", 1 },
  294.       { "letrec", 1 },
  295.       { "define", 1 },
  296.       { "##declare", 0 },
  297.       { "##define-macro", 1 },
  298.       { "syntax-rules", 1 },       /* s48 05Jan95 e */
  299.       { "define-structure", 2 },   /* s48 05Jan95 e */
  300.       { "define-record-type", 2 }, /* s48 05Jan95 e */
  301.       { "define-•", 1 },           /* s48 05Jan95 e -- handles many, e.g., { "define-syntax", 1 } */
  302.       { "with-•", 1 },             /* s48 05Jan95 e -- handles many */
  303.       { "call-with-•", 1 },        /* s48 05Jan95 e -- handles many */
  304.       { "", -1 }
  305.     };
  306.  
  307. static long spec_subfs( char *p )
  308. { char c;
  309.   long res = -1;
  310.   struct _spec_subfs *s = spec_subfs_data;
  311.   if( (c = dncase(*p)) >= 'a' && c <= 'z' )
  312.     while( (res = (*s).cnt) >= 0 )
  313.       if( eq_subfs( (*s).str, p ) )
  314.         break;
  315.       else
  316.         s++;
  317.   return res;
  318. }
  319.  
  320. void eTabCommand( eRec **hE, short modifiers, short style )
  321. { long open_paren, upen_paren;
  322.   long selStart, selEnd, selLen;
  323.   ChPos solcp, eolcp;
  324.   char *p;
  325.   long sol, eol;
  326.   long adj = 0;
  327.   Boolean selActive;
  328.   
  329.   if( ( selActive = (**hE).selActive ) )
  330.   {    selStart = eTeChPosToOffset( hE, (**hE).selStart );
  331.     selEnd   = eTeChPosToOffset( hE, (**hE).selEnd );
  332.     selLen   = selEnd - selStart;
  333.     eTeSetSelect( hE, selStart, selStart );
  334.     solcp = (**hE).selStart;
  335.   }
  336.   else
  337.   {    selStart = eTeChPosToOffset( hE, (**hE).caretChPos );
  338.     selLen = 0;
  339.     solcp = (**hE).caretChPos;
  340.   }
  341.   eolcp.h = solcp.h = 0;    /* start of line */
  342.   eolcp.v = solcp.v + 1;
  343.   eol = eTeChPosToOffset( hE, eolcp );    /* next line */
  344.   sol = eTeChPosToOffset( hE, solcp );    /* this line */
  345.   p = *((**hE).hText) + sol;
  346.   while( ! graphic_char( *p ) && sol < eol && *p != RETURN ) { p++; sol++; solcp.h++; }
  347.   if( sol != selStart ) eTeSetSelect( hE, sol, sol );
  348.   if( sol < selStart )  adj = selStart - sol;
  349.   
  350.   open_paren = balance_bwd_guts( hE, sol, 0, 1 );
  351.   /*  14Oct92  e  */
  352.   if( open_paren > 0 )
  353.   { upen_paren = balance_bwd_guts( hE, sol, 1, 1 );
  354.     if( upen_paren < 0 )
  355.     { /* a toplevel form should be at column zero !? */
  356.       eolcp.h = 0;
  357.       goto justdotab;
  358.     }
  359.     else
  360.     { long rover, prevr, first;
  361.       long count = 0;
  362.       char *text = *((**hE).hText);
  363.       p = text + upen_paren;
  364.       while( *p != '(' ) p++;    /* there must be a '(' since upen_paren >= 0 */ 
  365.       upen_paren = p - text;    /* the offset of the '(' */
  366.       prevr = upen_paren + 1;
  367.       rover = first = whsp_fwd_guts( hE, prevr );
  368.       while( rover > 0 && rover < open_paren )
  369.       { prevr = rover;
  370.           count++;
  371.         rover = balance_fwd_guts( hE, rover, 0 );
  372.         if( rover > 0 ) rover = whsp_fwd_guts( hE, rover );
  373.       }
  374.       /* now...
  375.         upen_paren = start of enclosing form
  376.         open_paren = start of preceeding form
  377.         first = first sub-form of enclosing form
  378.         prevr = last sub-form of enclosing form <= open_paren, or upen_paren+1 if none
  379.         rover = last sub-form of enclosing form >= open_paren, or -1 if none
  380.         count = position of rover in subforms of enclosing form
  381.       */ /* this catches subforms inside comments... */
  382.       if( rover != open_paren ) { open_paren = prevr; count--; }
  383.       /* this handles special indentation for special forms... */
  384.       if( first < sol
  385.           && (rover = spec_subfs( text + first ) ) >= 0
  386.           && ( count == 0 || count == rover || ( rover > 15 && count <= (rover&15)) ) )
  387.       { eolcp = eTeOffsetToChPos( hE, first );
  388.           eolcp.h += 1;
  389.           goto justdotab;
  390.       }
  391.     }
  392.   }
  393.   /* */
  394.   eolcp = eTeOffsetToChPos( hE, open_paren );
  395. justdotab:
  396.   if( solcp.h > eolcp.h )
  397.   { eolcp.v = solcp.v;
  398.     eol = eTeChPosToOffset( hE, eolcp );
  399.     /* 27Jan93  e  -- this mungs the scrap...
  400.     eTeSetSelect( hE, eol, eol );
  401.     eTeKillTo( hE, solcp );
  402.     instead... */
  403.     eTeSetSelect( hE, eol, sol );
  404.     eTeDelete( hE );
  405.   }
  406.   else while (solcp.h < eolcp.h)
  407.   { long len = (eolcp.h - solcp.h < 80) ? eolcp.h - solcp.h : 80;
  408.     if (check_TEInsert( "                                                                                ",
  409.                         len, hE, style )) goto err;
  410.     solcp.h += len;
  411.   }
  412.   err:
  413.   if( sol < selStart || selActive )
  414.   { selStart = eTeChPosToOffset( hE, (**hE).caretChPos ) + adj;
  415.     eTeSetSelect( hE, selStart, selStart + selLen );
  416.   }
  417.   eTeShowCaret( hE );
  418. }
  419.  
  420. #endif
  421.  
  422. #define mCheck(n, ch) ((rover[n] == ch) && (n >= 0) && (n < teLength))
  423.  
  424. void eEditCommand( eRec **hE, char ch, short modifiers, short style )
  425. {
  426.     register char *rover;
  427.     register long needLeft, needRight;
  428.     long selStart, selEnd, teLength;
  429.     short wanted;    /* -1 at start, 0 range, 1 at end */
  430.     Boolean selActive;
  431.     short optioned = modifiers & optionKey;
  432.     short shifted  = modifiers & shiftKey;
  433.     
  434.     ch += 0x40;    /* undo control key */
  435.     modifiers &= ~controlKey;
  436.     
  437.     if( ( selActive = (**hE).selActive ) )
  438.     {    selStart = eTeChPosToOffset( hE, (**hE).selStart );
  439.         selEnd =   eTeChPosToOffset( hE, (**hE).selEnd );
  440.     }
  441.     else
  442.     {    selEnd = selStart = eTeChPosToOffset( hE, (**hE).caretChPos );
  443.     }
  444.     teLength = eTeTextLength( hE );
  445.     
  446.     rover = (char *)*(**hE).hText;
  447.     wanted = 0;                        /* always range; someday use mark */
  448.     needLeft = needRight = 0;
  449.  
  450.     switch( ch )
  451.     {
  452.         case 'T':            /* Transpose */        /* 22Jul92  e  */
  453.         case 't':
  454.         case '†':    /* option-t */
  455.         case 'ˇ':    /* option-shift-t */
  456.             if( selActive )
  457.               SysBeep(6);
  458.             else if( optioned )
  459.             { needLeft  = balance_bwd( hE, 0, 1 );
  460.               needRight = balance_fwd( hE, 0 );
  461.               if( needLeft >= 0 && needRight >= 0 )
  462.                 eTeTranspose( hE, needLeft, whsp_bwd( hE ), whsp_fwd( hE ), needRight );
  463.               else
  464.                 SysBeep(6); /* can't be done! */
  465.             }
  466.             else
  467.               eTeTranspose( hE, selStart - 1, selStart, selStart, selStart + 1 );
  468.             return;
  469.  
  470.         case 'K':            /* Kill */
  471.         case 'k':
  472.         case '':    /* option-k */
  473.         case '˚':    /* option-shift-k */
  474.             if( ! selActive && optioned )
  475.             { /* c-m-k (del-fwd-sexp) */
  476.               selEnd = balance_fwd( hE, 0 );
  477.               if( selEnd >= 0 )
  478.               { eTeKillTo( hE, eTeOffsetToChPos( hE, selEnd ) );
  479.               }
  480.               else SysBeep( 3 );
  481.             }
  482.             else /* Kill line */
  483.               eTeKey( hE, 0x7f, KeyDel, optionKey, style);
  484.             return;
  485.  
  486.         case 'D':            /* Delete */
  487.         case 'd':
  488.             eTeKey( hE, 0x7f, KeyDel, 0, style);
  489.             return;
  490.  
  491.         case 'O':            /* Open line */
  492.         case 'o':
  493.         case 'ø':    /* option-o */
  494.         case 'Ø':    /* option-shift-o */
  495.             if( selActive )
  496.                 eTeSetSelect( hE, selStart, selStart );
  497.             eTeKey( hE, '\r', 0, 0, style);
  498.             eTeSetSelect( hE, selStart, selStart );
  499.             return;
  500.  
  501.         case 'A':
  502.         case 'a':
  503.         case 'å':    /* option-a */
  504.         case 'Å':    /* option-shift-a */
  505.             if( optioned )
  506.             {    wanted = shifted ? 0 : -1;
  507.                 needLeft = 2;
  508.                 /* needRight = 2; */
  509.                 break;
  510.             }
  511.             eTeKey( hE, LEFT_ARROW, 0, modifiers | cmdKey, style );
  512.             return;
  513.  
  514.         case 'E':
  515.         case 'e':
  516.                     /* option-e is a dead key */
  517.         case '´':    /* option-shift-e */
  518.             if( optioned )
  519.             {    wanted = shifted ? 0 : 1;
  520.                 /* needLeft = 2; */
  521.                 needRight = 2;
  522.                 break;
  523.             }
  524.             eTeKey( hE, RIGHT_ARROW, 0, modifiers | cmdKey, style );
  525.             return;
  526.  
  527.         case 'P':                            /* Previous */
  528.         case 'p':
  529.         case 'π':    /* option-p */
  530.         case '∏':    /* option-shift-p */
  531.             eTeKey( hE, UP_ARROW, 0, modifiers, style );
  532.             return;
  533.  
  534.         case 'N':                            /* Next */
  535.         case 'n':
  536.                     /* option-n is a dead key */
  537.         case '˜':    /* option-shift-n */
  538.             eTeKey( hE, DOWN_ARROW, 0, modifiers, style );
  539.             return;
  540.  
  541.         case 'M':                            /* Match (Balance) */
  542.         case 'm':
  543.         case 'µ':    /* option-m */
  544.         case 'Â':    /* option-shift-m */
  545.             if( ! selActive )
  546.             {    /* too smart?  22Jul92  e
  547.                 if( mCheck( selStart, '(' ) )
  548.                 {    selEnd = selStart + 1;
  549.                     needLeft = 0;
  550.                     needRight = 1;
  551.                 }
  552.                 else if( mCheck( selStart - 1, ')' ) )
  553.                 {    selStart = selEnd - 1;
  554.                     needLeft = 1;
  555.                     needRight = 0;
  556.                 }
  557.                 else
  558.                 {    needLeft = 1;
  559.                     needRight = 1;
  560.                 }
  561.                 */
  562.                 needLeft = 2;
  563.                 needRight = 2;
  564.             }
  565.             else
  566.             {    needRight = rangeBalance( &rover[selStart], &rover[selEnd] );
  567.                 /* too smart?  22Jul92  e
  568.                 if( needRight == 0 )
  569.                 {    needLeft = 1;
  570.                     needRight = 1;
  571.                 }
  572.                 else if( needRight < 0 )
  573.                 {    needLeft = -needRight;
  574.                     if( mCheck(selEnd - 1, ')' ) )
  575.                         needRight = 0;
  576.                     else
  577.                     {    needRight = 1;
  578.                         needLeft += 1;
  579.                     }
  580.                 }
  581.                 else
  582.                 {    if( mCheck( selStart, '(' ) )
  583.                         needLeft = 0;
  584.                     else
  585.                     {    needLeft = 1;
  586.                         needRight += 1;
  587.                     }
  588.                 }
  589.                 */
  590.                 if( needRight == 0 )
  591.                 {    needLeft = 2;
  592.                     needRight = 2;
  593.                 }
  594.                 else if( needRight < 0 )
  595.                 {    needLeft = -needRight;
  596.                     needRight = 2;
  597.                     needLeft += 2;
  598.                 }
  599.                 else
  600.                 {    needLeft = 2;
  601.                     needRight += 2;
  602.                 }
  603.             }
  604.             break;
  605.  
  606.         case 'ƒ':    /* option f */                /* Forward */
  607.         case 'f':
  608.         case 'Ï':    /* option shift f */
  609.         case 'F':
  610.             wanted = shifted ? 0 : 1;
  611.             if( ! selActive || shifted )
  612.             {    if( optioned ) needRight = 1;
  613.                 else
  614.                 {    eTeKey( hE, RIGHT_ARROW, 0, modifiers, style );
  615.                     return;
  616.                 }
  617.             }
  618.             /* else    selection range case automatic */
  619.             break;
  620.  
  621.         case '∫':    /* option b */                /* Back */
  622.         case 'b':
  623.         case 'ı':    /* option shift b */
  624.         case 'B':
  625.             wanted = shifted ? 0 : -1;
  626.             if( ! selActive || shifted )
  627.             {    if( optioned ) needLeft = 1;
  628.                 else
  629.                 {    eTeKey( hE, LEFT_ARROW, 0, modifiers, style );
  630.                     return;
  631.                 }
  632.             }
  633.             /* else    selection range case automatic */
  634.             break;
  635.  
  636.         default:
  637.             return;
  638.     }
  639.     if( needRight != 0 )
  640.     {     selEnd   = balance_fwd( hE, needRight - 1 );
  641.         if( selEnd   < 0 ) { SysBeep(6); return; } /* can't be done! */
  642.     }
  643.     if( needLeft  != 0 )
  644.     {     selStart = balance_bwd( hE, needLeft  - 1, 1 );
  645.         if( selStart < 0 ) { SysBeep(6); return; } /* can't be done! */
  646.     }
  647.     eTeSetSelect( hE,
  648.                   (wanted ==  1) ? selEnd : selStart,
  649.                   (wanted == -1) ? selStart : selEnd  );
  650.     eTeShowCaret( hE );
  651. }
  652.  
  653. #else
  654.  
  655. #define whsp_char(c) (((c)>=0) && ((c)<=32))
  656.  
  657. /* 16Dec92  e  -- miniature version of os_mac_eKeys.c for ML */
  658.  
  659. /* <tab> documentation
  660.  * C1 is the position of the first graphic character on the current line
  661.  * P1 is the position of the first graphic character on the previous line
  662.  * <option-tab>
  663.  *   inserts or removes space on the current line to align C1 with P1
  664.  * <tab>
  665.  *   if C1 < P1 then same as <option-tab>
  666.  *              else insert sufficient space on the current line
  667.  *                   to move C1 right to next tab stop
  668.  * <shift-tab>
  669.  *   if C1 > P1 then same as <option-tab>
  670.  *              else remove sufficient space on the current line
  671.  *                   to move C1 left to previous tab stop
  672.  * Notes: 
  673.  *  <tab> does not move the insert point or selection relative to the text
  674.  *  <tab> characters are never inserted into the text
  675.  */
  676.  
  677. extern short check_TEInsert( char *ptr, long len, eRec **hE, short bold );
  678.  
  679. static char *eighty = "                                                                                ";
  680.  
  681. void eTabCommand( eRec **hE, short modifiers, short style )
  682. { long selStart, selEnd, selLen;
  683.   ChPos plcp, tlcp, nlcp;
  684.   char *p;
  685.   long pl, tl, nl;
  686.   long adj = 0;
  687.   Boolean selActive;
  688.  
  689.   if( ( selActive = (**hE).selActive ) != 0 )
  690.   {    selStart = eTeChPosToOffset( hE, (**hE).selStart );
  691.     selEnd   = eTeChPosToOffset( hE, (**hE).selEnd );
  692.     selLen   = selEnd - selStart;
  693.     eTeSetSelect( hE, selStart, selStart );
  694.     tlcp = (**hE).selStart;
  695.   }
  696.   else
  697.   {    selStart = eTeChPosToOffset( hE, (**hE).caretChPos );
  698.     selLen = 0;
  699.     tlcp = (**hE).caretChPos;
  700.   }
  701.   plcp.h = tlcp.h = nlcp.h = 0;    /* start of {prev,this,next} line */
  702.   plcp.v = tlcp.v - 1;
  703.   nlcp.v = tlcp.v + 1;
  704.   nl = eTeChPosToOffset( hE, nlcp );    /* next line */
  705.   tl = eTeChPosToOffset( hE, tlcp );    /* this line */
  706.   if (plcp.v < 0)
  707.   {    pl = 0;
  708.       plcp.v = 0;
  709.   }
  710.   else
  711.   { pl = eTeChPosToOffset( hE, plcp );
  712.     p = *((**hE).hText) + pl;
  713.     while( whsp_char( *p ) && pl < tl && *p != RETURN ) { p++; pl++; plcp.h++; }
  714.   }
  715.   p = *((**hE).hText) + tl;
  716.   while( whsp_char( *p ) && tl < nl && *p != RETURN ) { p++; tl++; tlcp.h++; }
  717.  
  718.   if( tl != selStart ) eTeSetSelect( hE, tl, tl );
  719.   if( tl < selStart )  adj = selStart - tl;
  720.   
  721.   if ( ! ( modifiers & optionKey ) )
  722.   {    short tabStops = (**hE).tabStops;
  723.     if( modifiers & shiftKey )
  724.     { if( tlcp.h <= plcp.h )
  725.         if( tlcp.h > 0 && tabStops > 0 )
  726.             plcp.h = ( (tlcp.h - 1) / tabStops ) * tabStops;
  727.         else
  728.             plcp.h = 0;
  729.     }
  730.     else
  731.     { if( tlcp.h >= plcp.h && tabStops > 0 )
  732.         plcp.h = tlcp.h + tabStops - tlcp.h % tabStops;
  733.     }
  734.   }
  735.   if( tlcp.h > plcp.h )
  736.   { plcp.v = tlcp.v;
  737.     pl = eTeChPosToOffset( hE, plcp );
  738.     eTeSetSelect( hE, pl, pl );
  739.     eTeKillTo( hE, tlcp );
  740.   }
  741.   else while (tlcp.h < plcp.h)
  742.   { long len = (plcp.h - tlcp.h < 80) ? plcp.h - tlcp.h : 80;
  743.     if (check_TEInsert( eighty, len, hE, style )) goto err;
  744.     tlcp.h += len;
  745.   }
  746.   err:
  747.   if( tl < selStart || selActive )
  748.   { selStart = eTeChPosToOffset( hE, (**hE).caretChPos ) + adj;
  749.     eTeSetSelect( hE, selStart, selStart + selLen );
  750.   }
  751.   eTeShowCaret( hE );
  752. }
  753.  
  754. #endif
  755.  
  756. /* end of os_mac_eKeys.c */
  757.