/* * This module contains the word wrap and format paragraph functions. * * New editor name: tde, the Thomson-Davis Editor. * Author: Frank Davis * Date: June 5, 1991, version 1.0 * Date: July 29, 1991, version 1.1 * Date: October 5, 1991, version 1.2 * Date: January 20, 1992, version 1.3 * Date: February 17, 1992, version 1.4 * Date: April 1, 1992, version 1.5 * Date: June 5, 1992, version 2.0 * * This code is released into the public domain, Frank Davis. * You may distribute it freely. */ #include "tdestr.h" /* global variables definitions */ #include "common.h" /* external global variable declarations */ #include "define.h" #include "tdefunc.h" /* * Name: find_left_margin * Purpose: find left margin depending on word wrap mode * Date: June 5, 1992 * Passed: text: pointer to current line * Notes: the algorithm used to figure the indent column was yanked out * of the insert_newline( ) function and was made into a more * general algorithm for figuring the left margin irregardless * of word wrap or indent mode. when in the DYNAMIC_WRAP mode, * the user don't have to keep changing the left margin when * special indentation is needed. */ int find_left_margin( text_ptr text, int wrap_mode ) { register int lm; text_ptr source; if (wrap_mode == FIXED_WRAP) { /* * for FIXED_WRAP mode, the left and paragraph margins are determined * from the master mode structure. */ text = cpb( text ); text = find_prev( text ); if (text == NULL) lm = mode.left_margin; else if (linelen( text ) == 0) lm = mode.parg_margin; else lm = mode.left_margin; } else { /* * for Indent and DYNAMIC_WRAP modes, the left margin is determined * from the first non blank line above the cursor. */ source = g_status.copied == TRUE ? g_status.line_buff : text; lm = first_non_blank( source ); if (source[lm] == '\n' || source[lm] == CONTROL_Z) { text = cpb( text ); while ((text = find_prev( text )) != NULL) { lm = first_non_blank( text ); if (text[lm] != '\n') break; } } } return( lm ); } /* * Name: word_wrap * Purpose: make sure lines don't get longer than right margin * Date: November 27, 1991 * Passed: window: pointer to current window * Notes: rcol, lm, rm, pm all start counting at zero. * len (line length) starts counting at 1. * * when we compare margins and line lengths, we either have to * add one to the margins or subtract one from the len. I add * one to the margins. */ void word_wrap( WINDOW *window ) { int c; /* character the user just entered. */ register int len; /* length of current line */ int i; /* padding spaces required */ int line; /* line on screen to save and show prompt */ text_ptr p; /* line above wrapped line */ int rcol; int lm; int rm; register WINDOW *win; /* put window pointer in a register */ win = window; /* * set up a few local variables. */ c = g_status.key_pressed; line = win->bottom_line; rcol = win->rcol; copy_line( win->cursor, line ); /* * when we wrap, we need know where the left margin is. * let's look at the line above to see if this is the first line * in a paragraph. */ win->cursor = cpf( win->cursor ); p = cpb( win->cursor ); p = find_prev( p ); lm = find_left_margin( win->cursor, mode.word_wrap ); rm = mode.right_margin; /* * there two ways that words are pushed onto next line. * 1. if the word being typed goes over the right margin * 2. typing a word in the middle of the line pushes words at end of * line to next line * * if the user enters spaces past the right margin then we don't * word wrap spaces. */ len = linelen( g_status.line_buff ); if (rcol > rm+1 && c != ' ') { /* * if this is the first line in a paragraph then set left margin * to paragraph margin. */ if ((*p == CONTROL_Z || p == NULL || is_line_blank( p )) && first_non_blank( g_status.line_buff ) > rm && mode.word_wrap == FIXED_WRAP) lm = mode.parg_margin; /* * simple word wrap. the cursor goes past the right margin. * find the beginning of the word and put it on a new line. * * Special case - if the word begins at the left margin then * don't wrap it. */ for (i=rcol-1; i > lm && g_status.line_buff[i] != ' '; ) i--; if (i > lm) { i++; win->rcol = i; g_status.command = WordWrap; insert_newline( win ); /* * find out where to place the cursor on the new line. */ win->rcol = lm + rcol - i; check_virtual_col( win, win->rcol, win->rcol ); /* * we just wrapped the word at the eol. now, let's see if * we can combine it with the line below. since just added * a line, set new_line to false - don't add another line. */ len = linelen( win->cursor ); if (len < rm+1) combine_wrap_spill( win, len, rm, FALSE ); } } else if (len > rm+1) { /* * this is the second word wrap case. we are pushing words onto * next line. we need to now what character is in the right margin. * * 1) if the character is not a space, then we need to search backwards * to find the start of the word that is on the right margin. * 2) if the character is a space, then we need to search forward to * find the word that is over the right margin. */ /* * don't wrap spaces past right margin */ if (c == ' ' && rcol > rm) { for (i=rcol; i lm && g_status.line_buff[i] != ' '; ) i--; /* * if we search all the way back to left margin then test for * a special case - see the matching else for more info. */ if (i > lm) { i++; /* * if i > rcol then cursor stays on same line. */ if (i > rcol) { combine_wrap_spill( win, i, rm, TRUE ); /* * split the line at or behind the cursor. almost the * same as when the cursor goes over the right margin. */ } else if (i <= rcol) { win->rcol = i; g_status.command = WordWrap; insert_newline( win ); win->rcol = lm + rcol - i; check_virtual_col( win, win->rcol, win->rcol ); len = linelen( win->cursor ); if (len < rm+1) combine_wrap_spill( win, len, rm, FALSE ); } } /* * if the user changed margins or for some reason there's a long * text line, let's see if there are any words past the right * margin. if we get to this else, we know the current word * begins at least at the left margin. * * now search forwards for a break */ } else { /* * go to the right margin and see if there are any words past * right margin. */ for (i=rm+1; icursor, window, TRUE ); if (!is_line_blank( window->cursor )) { old_ww = mode.word_wrap; if (old_ww == NO_WRAP) mode.word_wrap = FIXED_WRAP; window->cursor = cpf( window->cursor ); dup_window_info( &w, window ); line = window->bottom_line; /* * find the beginning of the paragraph. */ p = w.cursor = cpb( w.cursor ); p = find_prev( p ); if (g_status.command == FormatParagraph) { while (p != NULL && *p != CONTROL_Z && !is_line_blank( cpf( p ) )) { --w.rline; w.cursor = find_prev( w.cursor ); p = find_prev( p ); } pm = mode.parg_margin; /* * if format text, don't find the beginning of the paragraph. * but we need to know if this is the first line in a paragraph. */ } else if (g_status.command == FormatText) { if (p == NULL || *p == CONTROL_Z || is_line_blank( p )) pm = mode.parg_margin; else pm = mode.left_margin; } g_status.command = WordWrap; p = w.cursor = cpf( w.cursor ); if (mode.word_wrap == FIXED_WRAP) lm = mode.left_margin; else { lm = pm = find_left_margin( p, mode.word_wrap ); } rm = mode.right_margin; eop = FALSE; /* * do the paragraph */ for (first_line=TRUE; *p != CONTROL_Z && p != NULL && !is_line_blank( p ) && eop == FALSE && !g_status.control_break;) { /* * find out what margin to use */ if (first_line) { margin = pm; first_line = FALSE; } else margin = lm; /* * line up the margin */ copy_line( w.cursor, line ); rcol = find_word( p, 0 ); if (rcol != ERROR && rcol != margin) { /* * must add spaces to get the indentation right */ if (rcol < margin) { source = g_status.line_buff; spaces = margin - rcol; dest = source + spaces; memmove( dest, source, linelen( source )+2 ); while (spaces--) *source++ = ' '; } else { w.rcol = margin; word_delete( &w ); un_copy_line( p, &w, TRUE ); } } /* * now make sure rest of line is formatted */ source = g_status.line_buff; len = linelen( source ); for (; len < rm+1 && eop == FALSE;) { pp = find_next( p ); if (is_line_blank( pp )) eop = TRUE; else { non_blank = first_non_blank( pp ); control_t = 1; if (*pp == ' ') ++control_t; w.cursor = p; w.rcol = len + 1; if (*(p+len-1) == '.') ++w.rcol; while (control_t--) word_delete( &w ); un_copy_line( p, &w, TRUE ); copy_line( p, line ); len = linelen( source ); } } if (len <= rm+1) { un_copy_line( p, &w, TRUE ); p = find_next( p ); if (is_line_blank( p )) eop = TRUE; else { w.cursor = find_next( w.cursor ); w.rline++; } } else { w.rcol = rm; g_status.key_pressed = *(w.cursor + rm); rline = w.rline; word_wrap( &w ); if (rline == w.rline) { w.cursor = find_next( w.cursor); ++w.rline; } } g_status.copied = FALSE; p = w.cursor = cpf( w.cursor ); } mode.word_wrap = old_ww; g_status.copied = FALSE; w.file_info->dirty = GLOBAL; } return( OK ); } /* * Name: combine_wrap_spill * Purpose: combine word wrap lines so we don't push each word onto a * separate line. * Date: November 27, 1991 * Passed: window: pointer to current window * wrap_col: col to combine next line * rm: right margin * new_line: boolean, should we insert a new line? */ void combine_wrap_spill( WINDOW *window, int wrap_col, int rm, int new_line ) { text_ptr p; /* line we wrapped */ text_ptr pp; /* pointer to next line after wrapped line */ int p_len; /* length of line we just word wrapped */ int non_blank; /* first non-blank column on next line */ int control_t; /* number of times to call word_delete */ WINDOW w; /* scratch window */ dup_window_info( &w, window ); g_status.command = WordWrap; w.rcol = wrap_col; if (new_line) { insert_newline( &w ); p = find_next( window->cursor ); } else p = cpf( window->cursor ); if (p != NULL) { p_len = linelen( p ); pp = find_next( p ); if (pp != NULL) { non_blank = first_non_blank( pp ); if (!is_line_blank( pp ) && p_len + linelen( pp + non_blank ) <= rm) { control_t = 1; if (*pp == ' ') ++control_t; w.cursor = p; w.rcol = p_len + 1; if (*(p+p_len-1) == '.') ++w.rcol; while (control_t--) word_delete( &w ); un_copy_line( w.cursor, &w, TRUE ); } window->file_info->dirty = GLOBAL; } } } /* * Name: find_word * Purpose: find a word on a line * Date: November 29, 1991 * Passed: p: information allowing access to the current window * start_col: col to start the search * Notes: returns the column of the next word or -1 if no more words */ int find_word( text_ptr p, int start_col ) { register int rc; register char c; p = cpf( p ); p += start_col; rc = start_col; while ((c = *p++) == ' ') ++rc; if (c == '\n' || c == CONTROL_Z) rc = ERROR; return( rc ); } /* * Name: left_justify * Purpose: left justify a line according to left margin * Date: November 27, 1991 * Passed: window: pointer to current window */ int left_justify( WINDOW *window ) { int len; /* length of current line */ register int spaces; char *source; char *dest; int rcol; int lm; register WINDOW *win; /* put window pointer in a register */ win = window; copy_line( win->cursor, win->bottom_line ); lm = mode.left_margin; rcol = find_word( g_status.line_buff, 0 ); if (rcol != ERROR && rcol != lm) { /* * must add spaces to get the indentation correct */ if (rcol < lm) { source = g_status.line_buff; spaces = lm - rcol; dest = source + spaces; len = linelen( source ) + 2; if (len + spaces > MAX_LINE_LENGTH) { /* * line would be too long */ error( WARNING, win->bottom_line, ww1 ); return( ERROR ); } else { load_undo_buffer( win->cursor ); memmove( dest, source, len ); while (spaces--) *source++ = ' '; win->file_info->dirty = GLOBAL; } /* * else delete spaces to get the indentation correct */ } else { dest = g_status.line_buff + lm; source = g_status.line_buff + rcol; memmove( dest, source, linelen( source ) + 2 ); win->file_info->dirty = GLOBAL; } show_changed_line( win ); } return( OK ); } /* * Name: right_justify * Purpose: right justify a line according to right margin * Date: November 27, 1991 * Passed: window: pointer to current window */ int right_justify( WINDOW *window ) { int len; /* length of current line */ int i; int spaces; char *source; char *dest; register int rcol; int rm; register WINDOW *win; /* put window pointer in a register */ win = window; copy_line( win->cursor, win->bottom_line ); source = g_status.line_buff; if (!is_line_blank( source )) { rm = mode.right_margin; len = linelen( source ); for (rcol=len-1; rcol>=0 && *(source+rcol) == ' ';) rcol--; if (rcol != rm) { /* * if rcol is less than right margin then we need to add spaces. */ if (rcol < rm) { spaces = rm - rcol; dest = source + spaces; len = linelen( source ) + 2; if (len + spaces > MAX_LINE_LENGTH) { /* * line would be too long */ error( WARNING, win->bottom_line, ww1 ); return( ERROR ); } else { load_undo_buffer( win->cursor ); memmove( dest, source, len ); while (spaces--) *source++ = ' '; win->file_info->dirty = GLOBAL; } /* * if rcol is greater than right margin then we need to sub spaces. */ } else { load_undo_buffer( win->cursor ); rcol = rcol - rm; i = first_non_blank( source ); if (rcol > i) rcol = i; dest = source + rcol; memmove( source, dest, linelen( dest ) + 2 ); win->file_info->dirty = GLOBAL; } show_changed_line( win ); } } return( OK ); } /* * Name: center_justify * Purpose: center a line according to left and right margin * Date: November 27, 1991 * Passed: window: pointer to current window */ int center_justify( WINDOW *window ) { int len; /* length of current line */ char *source; /* temp line buffer pointers */ char *dest; int rm; int lm; register int spaces; /* center of text on current line */ int center; /* center of current margins */ int first; /* column of first char on line */ int last; /* column of last char on line */ register WINDOW *win; /* put window pointer in a register */ win = window; copy_line( win->cursor, win->bottom_line ); source = g_status.line_buff; if (!is_line_blank( source )) { rm = mode.right_margin; lm = mode.left_margin; center = (rm + lm) / 2; first = first_non_blank( source ); len = linelen( source ); for (last=len-1; last>=0 && *(source+last) == ' ';) last--; spaces = last + first - 1; spaces = (spaces / 2) + (spaces & 1); if (spaces != center) { /* * if spaces is less than center margin then we need to add spaces. */ if (spaces < center) { spaces = center - spaces; dest = source + spaces; len = linelen( source ) + 2; if (len + spaces > MAX_LINE_LENGTH) { /* * line would be too long */ error( WARNING, win->bottom_line, ww1 ); return( ERROR ); } else { load_undo_buffer( win->cursor ); memmove( dest, source, len ); while (spaces--) *source++ = ' '; win->file_info->dirty = GLOBAL; } /* * if spaces is greater than center margin then we need to sub spaces. */ } else { load_undo_buffer( win->cursor ); spaces = spaces - center; if (spaces > first) spaces = first; dest = source + spaces; memmove( source, dest, linelen( dest ) + 2 ); win->file_info->dirty = GLOBAL; } show_changed_line( win ); } } return( OK ); }