
/* .page */

int wrap_line_insert ( lines_wrapped )

/*****************************************************************************
 * This function is used by the options command when text wrapping is first  *
 * enabled, and by visual_processor when text wrapping is in progress and a  *
 * new character is being inserted that may exceed the right margin.         *
 *                                                                           *
 * It checks the current line to see if it is longer than the text wrapping  *
 * right margin. If it is, it deletes word(s) on the line until the line is  *
 * shorter than the right margin, and prepends them to the next line,        *
 * creating a line if needed (if we are at the end of the file). It then     *
 * recursively calls itself to handle the case where this newly modified     *
 * line is now itself too long. It will recursively ripple down the file     *
 * like this until all lines in the current paragraph fit within the right   *
 * margin, or to the end of file if need be.                                 * 
 *                                                                           *
 * IMPORTANT: lines_wrapped should be zeroed before calling this routine, as *
 * it is used as recursion depth checker. If this routine ripples down more  *
 * than MAX_WRAP_LINES, this counter will stop it.                           *
 *                                                                           *
 * This function returns TRUE if the wrap succeeds, or FALSE if it does not. *
 *                                                                           *
 *****************************************************************************/

 int *lines_wrapped;       /* recursion depth = total lines wrapped          */

{
   int cx, cy;             /* current screen X and Y at start                */
   int prev_cx;            /* CX before word_left                            */
   int text_size;          /* num chars in end of line words                 */
   int i;                  /* loop counter                                   */
   int wrap_len;           /* length of wrapped words plus left margin       */
   int wrap_succeeded;     /* did wrap succeed? the return value             */
   struct node *next_line; /* the next line in the file                      */

   cx = curr_screen.cx;    /* save current screen co-ordinates               */
   cy = curr_screen.cy;

   wrap_succeeded = TRUE;  /* innocent until proven guilty!                  */

   load_line_buf ();       /* load current line into line buffer for editing */

   /* First check that recursion hasn't run wild. If so, stop it.            */

   if ( *lines_wrapped > MAX_WRAP_LINES ) 
      return ( FALSE );

   if ( line_buf.length >= options.right_margin - 1 ) 
   {
      /* The current line has exceeded the text wrap right margin and must  */
      /* be wrapped. Go to the end of the line, and move to the start of    */
      /* the first word whose end is inside the right margin. From there to */
      /* the line end will be deleted and moved to the next line.           */

      /* NOTE: this code doesn't quite meet the intent. It finds the first  */
      /* word whose start lies within the right margin. Its end may lie     */
      /* outside the right margin. Need a routine to find the length of the */
      /* word, and only terminate the while loop if start+length lies       */
      /* inside the margin. The current code is good enough for now though. */

      *lines_wrapped += 1; /* Increment the recursion count                 */
      ext_end_cmd ();      /* Move to end of line (does update_curr_line)   */

      while ( curr_screen.cx >= options.right_margin - 1 )
      {
         prev_cx = curr_screen.cx;
         word_left_cmd ();

         /* Check for a word_left command that leaves the cursor exactly at  */
         /* the left margin. If this is the case, we have hit solid unbroken */
         /* text that we cannot wrap. Put the cursor back where we started   */
         /* and return an error.                                             */

         if ( curr_screen.cx <= options.left_margin   ||
              curr_screen.cx == prev_cx             )
         {
            *lines_wrapped -= 1;
            curr_screen.cx = cx;
            curr_screen.cy = cy;
            restore_cursor ();
            return ( FALSE );
         }
      }
      
      /* Save the words that exceed the right margin into wrap_bufr prior to */
      /* deleting it/them. Offset by the amount of the left margin, so that  */
      /* ultimately, the new line is at the correct left margin.             */

      for ( i=0; i < options.left_margin; i++ )  /* put offset in wrap_buf   */
         wrap_bufr[i] = ' ';
      wrap_bufr[options.left_margin] = '\0';

      load_line_buf ();
      text_size = line_buf.length - curr_screen.cx;

      /* Create a version of the saved word(s) from the end of the line into */
      /* wrap_bufr2. A second buffer (wrap_bufr2) is used instead of simply  */
      /* appending the saved word(s) to wrap_bufr, so that the saved words   */
      /* can have all unneeded white space compressed out first - needed to  */
      /* retain visual alignment in a situation where the line being wrapped */
      /* has large amounts of embedded spaces.                               */

      for ( i = 0; i < text_size; i++ )
         wrap_bufr2[i] = line_buf.text[curr_screen.cx+i];
      wrap_len = compress_white_space ( wrap_bufr2, text_size );
      wrap_bufr2[wrap_len]   = ' ';
      wrap_bufr2[wrap_len+1] = '\0';

      /* Delete the last words, shortening the line, and kill any trailing  */
      /* white space on the line                                            */

      delete_right_cmd (); 
      update_curr_line ();

      next_line = curr_line->fwd_link;
      text_size = ( next_line == EOL ) ? 0 : next_line->length;

      if ( next_line == EOL || text_size == 0 ) 
      {
         /* We are going to place the deleted text in wrap_bufr at the head */
         /* the next line, but The line we are wrapping is the last line of */
         /* the file or is a zero length line. Either way, we need to open  */
         /* up a new line below to receive the text we have just deleted.   */

         ext_end_cmd ();      /* jump to end of current line                */

         load_line_buf ();
         enter_handler ();    /* create new line, leaves us on it           */

         cursor_up_cmd ();    /* move back up to the line we are wrapping   */
         ext_end_cmd ();      /* jump back to the end of that line          */
      }

      /* Go to next line, get to the front, and insert the deleted word(s)  */
      /* Before doing the insert, compress all white space so that line is  */
      /* indented to zero. Then adding the above generated wrap_bufr to it  */
      /* will result in it being indented to the left margin as expected.   */

      cursor_down_cmd ();
      ext_home_cmd ();

      load_line_buf ();
      line_buf.length = compress_white_space ( &line_buf.text, line_buf.length );
      line_buf.text[line_buf.length] = '\0';
      
      strcat ( wrap_bufr, wrap_bufr2 );    /* Combine indent and saved words */
      strcat ( wrap_bufr, line_buf.text ); /* Append line to saved words     */
      strcpy ( line_buf.text, wrap_bufr ); /* Copy the result back to line   */
      line_buf.length = strlen ( line_buf.text );

      update_curr_line ();
      writeln ( curr_line->text, curr_line->length, 0 ); /* update screen   */

      /* The new line with the previously deleted text now inserted at the  */
      /* front may now *itself* be too long. Recursively call ourselves to  */
      /* check for and handle this condition.                               */

      wrap_succeeded = wrap_line_insert ( lines_wrapped );

      /* Move back up to the line we were on when originally called, and    */
      /* put the cursor back where it belongs. That's it kids! All done!    */

      cursor_up_cmd ();
      ti_move_cursor ( (curr_screen.cx = cx), curr_screen.cy );

   } /* line_buf.length >= options.right_margin */

   return ( wrap_succeeded );

} /* wrap_line_insert */

