home *** CD-ROM | disk | FTP | other *** search
/ Black Box 4 / BlackBox.cdr / editors / tde150.arj / WORDWRAP.C < prev   
C/C++ Source or Header  |  1992-04-01  |  20KB  |  631 lines

  1. /*
  2.  * This module contains the word wrap and format paragraph functions.
  3.  *
  4.  * New editor name:  tde, the Thomson-Davis Editor.
  5.  * Author:           Frank Davis
  6.  * Date:             June 5, 1991, version 1.0
  7.  * Date:             July 29, 1991, version 1.1
  8.  * Date:             October 5, 1991, version 1.2
  9.  * Date:             January 20, 1992, version 1.3
  10.  * Date:             February 17, 1992, version 1.4
  11.  * Date:             April 1, 1992, version 1.5
  12.  *
  13.  * This code is released into the public domain, Frank Davis.
  14.  * You may distribute it freely.
  15.  */
  16.  
  17. #include "tdestr.h"     /* global variables definitions */
  18. #include "common.h"     /* external global variable declarations */
  19. #include "define.h"
  20. #include "tdefunc.h"
  21.  
  22.  
  23. /*
  24.  * Name:    word_wrap
  25.  * Purpose: make sure lines don't get longer than right margin
  26.  * Date:    November 27, 1991
  27.  * Passed:  window:   information allowing access to the current window
  28.  * Notes:   rcol, lm, rm, pm all start counting at zero.
  29.  *          len (line length) starts counting at 1.
  30.  *
  31.  *          when we compare margins and line lengths, we either have to
  32.  *          add one to the margins or subtract one from the len.  I add
  33.  *          one to the margins.
  34.  */
  35. void word_wrap( WINDOW *window )
  36. {
  37. int c;              /* character the user just entered. */
  38. register int len;   /* length of current line */
  39. int i;              /* padding spaces required */
  40. int line;           /* line on screen to save and show prompt */
  41. text_ptr p;         /* line above wrapped line */
  42. int rcol;
  43. int lm;
  44. int rm;
  45. register WINDOW *win;          /* put window pointer in a register */
  46.  
  47.    win = window;
  48.  
  49.    /*
  50.     * set up a few local variables.
  51.     */
  52.    c = g_status.key_pressed;
  53.    line = win->bottom_line;
  54.    rcol = win->rcol;
  55.    copy_line( win->cursor, line );
  56.  
  57.    /*
  58.     * when we wrap, we need know where the left margin is.
  59.     * let's look at the line above to see if this is the first line
  60.     * in a paragraph.
  61.     */
  62.    win->cursor = cpf( win->cursor );
  63.    p = cpb( win->cursor );
  64.    p = find_prev( p );
  65.  
  66.    lm = mode.left_margin;
  67.    rm = mode.right_margin;
  68.  
  69.    /*
  70.     * there two ways that words are pushed onto next line.
  71.     *   1. if the word being typed goes over the right margin
  72.     *   2. typing a word in the middle of the line pushes words at end of
  73.     *      line to next line
  74.     *
  75.     * if the user enters spaces past the right margin then we don't
  76.     * word wrap spaces.
  77.     */
  78.    len = linelen( g_status.line_buff );
  79.    if (rcol > rm+1 && c != ' ') {
  80.  
  81.       /*
  82.        * if this is the first line in a paragraph then set left margin
  83.        * to paragraph margin.
  84.        */
  85.       if ((*p == CONTROL_Z || p == NULL || is_line_blank( p )) &&
  86.            first_non_blank( g_status.line_buff ) > rm)
  87.          lm = mode.parg_margin;
  88.  
  89.       /*
  90.        * simple word wrap.  the cursor goes past the right margin.
  91.        * find the beginning of the word and put it on a new line.
  92.        *
  93.        * Special case - if the word begins at the left margin then
  94.        * don't wrap it.
  95.        */
  96.       for (i=rcol-1; i > lm  &&  g_status.line_buff[i] != ' '; )
  97.          i--;
  98.       if (i > lm) {
  99.          i++;
  100.          win->rcol = i;
  101.          g_status.command = WordWrap;
  102.          insert_newline( win );
  103.  
  104.          /*
  105.           * find out where to place the cursor on the new line.
  106.           */
  107.          win->rcol = lm + rcol - i;
  108.          check_virtual_col( win, win->rcol, win->rcol );
  109.  
  110.          /*
  111.           * we just wrapped the word at the eol.  now, let's see if
  112.           * we can combine it with the line below.  since just added
  113.           * a line, set new_line to false - don't add another line.
  114.           */
  115.          len = linelen( win->cursor );
  116.          if (len < rm+1)
  117.             combine_wrap_spill( win, len, rm, FALSE );
  118.       }
  119.    } else if (len > rm+1) {
  120.  
  121.       /*
  122.        * this is the second word wrap case.  we are pushing words onto
  123.        * next line.  we need to now what character is in the right margin.
  124.        *
  125.        * 1) if the character is not a space, then we need to search backwards
  126.        *    to find the start of the word that is on the right margin.
  127.        * 2) if the character is a space, then we need to search forward to
  128.        *    find the word that is over the right margin.
  129.        */
  130.  
  131.       /*
  132.        * don't wrap spaces past right margin
  133.        */
  134.       if (c == ' ' && rcol > rm) {
  135.          for (i=rcol; i<len && g_status.line_buff[i] == ' ';)
  136.             i++;
  137.  
  138.          /*
  139.           * if i == len then all that's left on line is blanks - don't wrap.
  140.           */
  141.          if (i < len)
  142.             combine_wrap_spill( win, i, rm, TRUE );
  143.  
  144.       } else if (g_status.line_buff[rm+1] != ' ') {
  145.  
  146.          /*
  147.           * search backwards for the word to put on next line.
  148.           */
  149.          for (i=rm+1; i > lm  &&  g_status.line_buff[i] != ' '; )
  150.             i--;
  151.  
  152.          /*
  153.           * if we search all the way back to left margin then test for
  154.           * a special case - see the matching else for more info.
  155.           */
  156.          if (i > lm) {
  157.             i++;
  158.  
  159.             /*
  160.              * if i > rcol then cursor stays on same line.
  161.              */
  162.             if (i > rcol) {
  163.                combine_wrap_spill( win, i, rm, TRUE );
  164.  
  165.             /*
  166.              * split the line at or behind the cursor.  almost the
  167.              * same as when the cursor goes over the right margin.
  168.              */
  169.             } else if (i <= rcol) {
  170.                win->rcol = i;
  171.                g_status.command = WordWrap;
  172.                insert_newline( win );
  173.                win->rcol = lm + rcol - i;
  174.                check_virtual_col( win, win->rcol, win->rcol );
  175.                len = linelen( win->cursor );
  176.                if (len < rm+1)
  177.                   combine_wrap_spill( win, len, rm, FALSE );
  178.             }
  179.          }
  180.  
  181.          /*
  182.           * if the user changed margins or for some reason there's a long
  183.           * text line, let's see if there are any words past the right
  184.           * margin.  if we get to this else, we know the current word
  185.           * begins at least at the left margin.
  186.           *
  187.           * now search forwards for a break
  188.           */
  189.       } else {
  190.  
  191.          /*
  192.           * go to the right margin and see if there are any words past
  193.           *  right margin.
  194.           */
  195.          for (i=rm+1; i<len && g_status.line_buff[i] == ' '; )
  196.             i++;
  197.  
  198.          /*
  199.           * we either found a space or the eol.  test for eol.
  200.           * if i == len then this is one big word - don't wrap it.
  201.           */
  202.          if (i != len)
  203.             combine_wrap_spill( win, i, rm, TRUE );
  204.       }
  205.    }
  206. }
  207.  
  208.  
  209. /*
  210.  * Name:    format_paragraph
  211.  * Purpose: format paragraph using left, right, and paragraph margins.
  212.  * Date:    November 27, 1991
  213.  * Passed:  window:   information allowing access to the current window
  214.  */
  215. void format_paragraph( WINDOW *window )
  216. {
  217. register int len;       /* length of current line */
  218. int first_line;         /* boolean, first line formatted? */
  219. int spaces;             /* no. of spaces to add */
  220. int line;               /* line on screen to save and show prompt */
  221. int control_t;          /* number of times to call WordDelete */
  222. int non_blank;
  223. text_ptr p;             /* scratch text pointers */
  224. text_ptr pp;
  225. char *source;           /* scratch line buffer pointers */
  226. char *dest;
  227. int rcol;               /* scratch cols and margins */
  228. int lm;
  229. int rm;
  230. int pm;
  231. int margin;
  232. int eop;                /* boolean, (e)nd (o)f (p)aragraph? */
  233. int old_ww;             /* save state of word wrap flag */
  234. long rline;
  235. WINDOW w;              /* scratch window */
  236.  
  237.    un_copy_line( window->cursor, window, TRUE );
  238.    if (!is_line_blank( window->cursor )) {
  239.       old_ww = mode.word_wrap;
  240.       mode.word_wrap = TRUE;
  241.       window->cursor = cpf( window->cursor );
  242.       dup_window_info( &w, window );
  243.  
  244.       line = window->bottom_line;
  245.  
  246.       /*
  247.        * find the beginning of the paragraph.
  248.        */
  249.       p = w.cursor = cpb( w.cursor );
  250.       p = find_prev( p );
  251.       if (g_status.command == FormatParagraph) {
  252.          while (p != NULL && *p != CONTROL_Z && !is_line_blank( cpf( p ) )) {
  253.             --w.rline;
  254.             w.cursor = find_prev( w.cursor );
  255.             p = find_prev( p );
  256.          }
  257.          pm = mode.parg_margin;
  258.  
  259.       /*
  260.        * if format text, don't find the beginning of the paragraph.
  261.        * but we need to know if this is the first line in a paragraph.
  262.        */
  263.       } else if (g_status.command == FormatText) {
  264.          if (p == NULL || *p == CONTROL_Z  || is_line_blank( p ))
  265.             pm = mode.parg_margin;
  266.          else
  267.             pm = mode.left_margin;
  268.       }
  269.  
  270.       g_status.command = WordWrap;
  271.       eop = FALSE;
  272.       lm = mode.left_margin;
  273.       rm = mode.right_margin;
  274.       p = w.cursor = cpf( w.cursor );
  275.  
  276.       /*
  277.        * do the paragraph
  278.        */
  279.       for (first_line=TRUE; *p != CONTROL_Z && p != NULL &&
  280.                             !is_line_blank( p ) && eop == FALSE;) {
  281.  
  282.          /*
  283.           * find out what margin to use
  284.           */
  285.          if (first_line) {
  286.             margin = pm;
  287.             first_line = FALSE;
  288.          } else
  289.             margin = lm;
  290.  
  291.          /*
  292.           * line up the margin
  293.           */
  294.          copy_line( w.cursor, line );
  295.          rcol = find_word( p, 0 );
  296.          if (rcol != ERROR && rcol != margin) {
  297.  
  298.             /*
  299.              * must add spaces to get the indentation right
  300.              */
  301.             if (rcol < margin) {
  302.                source = g_status.line_buff;
  303.                spaces = margin - rcol;
  304.                dest = source + spaces;
  305.                memmove( dest, source, linelen( source )+2 );
  306.                while (spaces--)
  307.                   *source++ = ' ';
  308.             } else {
  309.                w.rcol = margin;
  310.                word_delete( &w );
  311.                un_copy_line( p, &w, TRUE );
  312.             }
  313.          }
  314.  
  315.          /*
  316.           * now make sure rest of line is formatted
  317.           */
  318.  
  319.          source = g_status.line_buff;
  320.          len = linelen( source );
  321.          for (; len < rm+1 && eop == FALSE;) {
  322.             pp = find_next( p );
  323.             if (is_line_blank( pp ))
  324.                eop = TRUE;
  325.             else {
  326.                non_blank = first_non_blank( pp );
  327.                control_t = 1;
  328.                if (*pp == ' ')
  329.                   ++control_t;
  330.                w.cursor = p;
  331.                w.rcol = len + 1;
  332.                if (*(p+len-1) == '.')
  333.                   ++w.rcol;
  334.                while (control_t--)
  335.                   word_delete( &w );
  336.                un_copy_line( p, &w, TRUE );
  337.                copy_line( p, line );
  338.                len = linelen( source );
  339.             }
  340.          }
  341.          if (len <= rm+1) {
  342.             un_copy_line( p, &w, TRUE );
  343.             p = find_next( p );
  344.             if (is_line_blank( p ))
  345.                eop = TRUE;
  346.             else {
  347.                w.cursor = find_next( w.cursor );
  348.                w.rline++;
  349.             }
  350.          } else {
  351.             w.rcol = rm;
  352.             g_status.key_pressed = *(w.cursor + rm);
  353.             rline = w.rline;
  354.             word_wrap( &w );
  355.             if (rline == w.rline) {
  356.                w.cursor = find_next( w.cursor);
  357.                ++w.rline;
  358.             }
  359.          }
  360.          g_status.copied = FALSE;
  361.          p = w.cursor = cpf( w.cursor );
  362.       }
  363.       mode.word_wrap = old_ww;
  364.       g_status.copied = FALSE;
  365.       w.file_info->dirty = GLOBAL;
  366.    }
  367. }
  368.  
  369.  
  370. /*
  371.  * Name:    combine_wrap_spill
  372.  * Purpose: combine word wrap lines so we don't push each word onto a
  373.  *          separate line.
  374.  * Date:    November 27, 1991
  375.  * Passed:  window:   information allowing access to the current window
  376.  *          wrap_col: col to combine next line
  377.  *          rm:       right margin
  378.  *          new_line: boolean, should we insert a new line?
  379.  */
  380. void combine_wrap_spill( WINDOW *window, int wrap_col, int rm, int new_line )
  381. {
  382. text_ptr p;             /* line we wrapped */
  383. text_ptr pp;            /* pointer to next line after wrapped line */
  384. int p_len;              /* length of line we just word wrapped */
  385. int non_blank;          /* first non-blank column on next line */
  386. int control_t;          /* number of times to call word_delete */
  387. WINDOW w;              /* scratch window */
  388.  
  389.    dup_window_info( &w, window );
  390.    g_status.command = WordWrap;
  391.    w.rcol = wrap_col;
  392.    if (new_line) {
  393.       insert_newline( &w );
  394.       p = find_next( window->cursor );
  395.    } else
  396.       p = cpf( window->cursor );
  397.    if (p != NULL) {
  398.       p_len = linelen( p );
  399.       pp = find_next( p );
  400.       if (pp != NULL) {
  401.          non_blank = first_non_blank( pp );
  402.          if (!is_line_blank( pp ) && p_len + linelen( pp + non_blank ) <= rm) {
  403.             control_t = 1;
  404.             if (*pp == ' ')
  405.                ++control_t;
  406.             w.cursor = p;
  407.             w.rcol = p_len + 1;
  408.             if (*(p+p_len-1) == '.')
  409.                ++w.rcol;
  410.             while (control_t--)
  411.                word_delete( &w );
  412.             un_copy_line( w.cursor, &w, TRUE );
  413.          }
  414.          window->file_info->dirty = GLOBAL;
  415.       }
  416.    }
  417. }
  418.  
  419.  
  420. /*
  421.  * Name:    find_word
  422.  * Purpose: find a word on a line
  423.  * Date:    November 29, 1991
  424.  * Passed:  p:   information allowing access to the current window
  425.  *          start_col: col to start the search
  426.  * Notes:   returns the column of the next word or -1 if no more words
  427.  */
  428. int find_word( text_ptr p, int start_col )
  429. {
  430. register int rc;
  431. register char c;
  432.  
  433.    p = cpf( p );
  434.    p += start_col;
  435.    rc = start_col;
  436.    while ((c = *p++) == ' ')
  437.       ++rc;
  438.    if (c == '\n' || c == CONTROL_Z)
  439.      rc = ERROR;
  440.    return( rc );
  441. }
  442.  
  443.  
  444. /*
  445.  * Name:    left_justify
  446.  * Purpose: left justify a line according to left margin
  447.  * Date:    November 27, 1991
  448.  * Passed:  window:   information allowing access to the current window
  449.  */
  450. void left_justify( WINDOW *window )
  451. {
  452. int len;   /* length of current line */
  453. register int spaces;
  454. char *source;
  455. char *dest;
  456. int rcol;
  457. int lm;
  458. register WINDOW *win;          /* put window pointer in a register */
  459.  
  460.    win = window;
  461.    copy_line( win->cursor, win->bottom_line );
  462.    lm = mode.left_margin;
  463.    rcol = find_word( g_status.line_buff, 0 );
  464.    if (rcol != -1 && rcol != lm) {
  465.  
  466.       /*
  467.        * must add spaces to get the indentation correct
  468.        */
  469.       if (rcol < lm) {
  470.          source = g_status.line_buff;
  471.          spaces = lm - rcol;
  472.          dest = source + spaces;
  473.          len = linelen( source ) + 2;
  474.          if (len + spaces > MAX_LINE_LENGTH) {
  475.             error( WARNING, win->bottom_line, "line would be too long." );
  476.             return;
  477.          } else {
  478.             load_undo_buffer( win->cursor );
  479.             memmove( dest, source, len );
  480.             while (spaces--)
  481.                *source++ = ' ';
  482.             win->file_info->dirty = GLOBAL;
  483.          }
  484.  
  485.       /*
  486.        * else delete spaces to get the indentation correct
  487.        */
  488.       } else {
  489.          dest = g_status.line_buff + lm;
  490.          source = g_status.line_buff + rcol;
  491.          memmove( dest, source, linelen( source ) + 2 );
  492.          win->file_info->dirty = GLOBAL;
  493.       }
  494.       show_changed_line( win );
  495.    }
  496. }
  497.  
  498.  
  499. /*
  500.  * Name:    right_justify
  501.  * Purpose: right justify a line according to right margin
  502.  * Date:    November 27, 1991
  503.  * Passed:  window:   information allowing access to the current window
  504.  */
  505. void right_justify( WINDOW *window )
  506. {
  507. int len;   /* length of current line */
  508. int i;
  509. int spaces;
  510. char *source;
  511. char *dest;
  512. register int rcol;
  513. int rm;
  514. register WINDOW *win;          /* put window pointer in a register */
  515.  
  516.    win = window;
  517.    copy_line( win->cursor, win->bottom_line );
  518.    source = g_status.line_buff;
  519.    if (!is_line_blank( source )) {
  520.       rm = mode.right_margin;
  521.       len = linelen( source );
  522.       for (rcol=len-1; rcol>=0 && *(source+rcol) == ' ';)
  523.          rcol--;
  524.       if (rcol != rm) {
  525.  
  526.          /*
  527.           * if rcol is less than right margin then we need to add spaces.
  528.           */
  529.          if (rcol < rm) {
  530.             spaces = rm - rcol;
  531.             dest = source + spaces;
  532.             len = linelen( source ) + 2;
  533.             if (len + spaces > MAX_LINE_LENGTH) {
  534.                error( WARNING, win->bottom_line, "line would be too long." );
  535.                return;
  536.             } else {
  537.                load_undo_buffer( win->cursor );
  538.                memmove( dest, source, len );
  539.                while (spaces--)
  540.                   *source++ = ' ';
  541.                win->file_info->dirty = GLOBAL;
  542.             }
  543.  
  544.          /*
  545.           * if rcol is greater than right margin then we need to sub spaces.
  546.           */
  547.          } else {
  548.             load_undo_buffer( win->cursor );
  549.             rcol = rcol - rm;
  550.             i = first_non_blank( source );
  551.             if (rcol > i)
  552.                rcol = i;
  553.             dest = source + rcol;
  554.             memmove( source, dest, linelen( dest ) + 2 );
  555.             win->file_info->dirty = GLOBAL;
  556.          }
  557.          show_changed_line( win );
  558.       }
  559.    }
  560. }
  561.  
  562.  
  563. /*
  564.  * Name:    center_justify
  565.  * Purpose: center a line according to left and right margin
  566.  * Date:    November 27, 1991
  567.  * Passed:  window:   information allowing access to the current window
  568.  */
  569. void center_justify( WINDOW *window )
  570. {
  571. int len;   /* length of current line */
  572. char *source;           /* temp line buffer pointers */
  573. char *dest;
  574. int rm;
  575. int lm;
  576. register int spaces;             /* center of text on current line */
  577. int center;             /* center of current margins */
  578. int first;              /* column of first char on line */
  579. int last;               /* column of last char on line */
  580. register WINDOW *win;  /* put window pointer in a register */
  581.  
  582.    win = window;
  583.    copy_line( win->cursor, win->bottom_line );
  584.    source = g_status.line_buff;
  585.    if (!is_line_blank( source )) {
  586.       rm = mode.right_margin;
  587.       lm = mode.left_margin;
  588.       center = (rm + lm) / 2;
  589.       first = first_non_blank( source );
  590.       len = linelen( source );
  591.       for (last=len-1; last>=0 && *(source+last) == ' ';)
  592.          last--;
  593.       spaces = last + first - 1;
  594.       spaces = (spaces / 2) + (spaces & 1);
  595.       if (spaces != center) {
  596.  
  597.          /*
  598.           * if spaces is less than center margin then we need to add spaces.
  599.           */
  600.          if (spaces < center) {
  601.             spaces = center - spaces;
  602.             dest = source + spaces;
  603.             len = linelen( source ) + 2;
  604.             if (len + spaces > MAX_LINE_LENGTH) {
  605.                error( WARNING, win->bottom_line, "line would be too long." );
  606.                return;
  607.             } else {
  608.                load_undo_buffer( win->cursor );
  609.                memmove( dest, source, len );
  610.                while (spaces--)
  611.                   *source++ = ' ';
  612.                win->file_info->dirty = GLOBAL;
  613.             }
  614.  
  615.          /*
  616.           * if spaces is greater than center margin then we need to sub spaces.
  617.           */
  618.          } else {
  619.             load_undo_buffer( win->cursor );
  620.             spaces = spaces - center;
  621.             if (spaces > first)
  622.                spaces = first;
  623.             dest = source + spaces;
  624.             memmove( source, dest, linelen( dest ) + 2 );
  625.             win->file_info->dirty = GLOBAL;
  626.          }
  627.          show_changed_line( win );
  628.       }
  629.    }
  630. }
  631.