mc/edit/wordproc.c

369 lines
8.0 KiB
C

/* wordproc.c - word-processor mode for the editor: does dynamic
paragraph formatting.
Copyright (C) 1996 Paul Sheer
This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 2 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
02110-1301, USA.
*/
#include <config.h>
#include <stdio.h>
#include <stdarg.h>
#include <sys/types.h>
#ifdef HAVE_UNISTD_H
# include <unistd.h>
#endif
#include <string.h>
#include <ctype.h>
#include <errno.h>
#include <sys/stat.h>
#include <stdlib.h>
#include "../src/global.h"
#include "edit.h"
#include "edit-widget.h"
#define tab_width option_tab_spacing
#define NO_FORMAT_CHARS_START "-+*\\,.;:&>"
#define FONT_MEAN_WIDTH 1
static long
line_start (WEdit *edit, long line)
{
long p, l;
l = edit->curs_line;
p = edit->curs1;
if (line < l)
p = edit_move_backward (edit, p, l - line);
else if (line > l)
p = edit_move_forward (edit, p, line - l, 0);
p = edit_bol (edit, p);
while (strchr ("\t ", edit_get_byte (edit, p)))
p++;
return p;
}
static int bad_line_start (WEdit * edit, long p)
{
int c;
c = edit_get_byte (edit, p);
if (c == '.') { /* `...' is acceptable */
if (edit_get_byte (edit, p + 1) == '.')
if (edit_get_byte (edit, p + 2) == '.')
return 0;
return 1;
}
if (c == '-') {
if (edit_get_byte (edit, p + 1) == '-')
if (edit_get_byte (edit, p + 2) == '-')
return 0; /* `---' is acceptable */
return 1;
}
if (strchr (NO_FORMAT_CHARS_START, c))
return 1;
return 0;
}
/*
* Find the start of the current paragraph for the purpose of formatting.
* Return position in the file.
*/
static long
begin_paragraph (WEdit *edit, int force)
{
int i;
for (i = edit->curs_line - 1; i >= 0; i--) {
if (line_is_blank (edit, i)) {
i++;
break;
}
if (force) {
if (bad_line_start (edit, line_start (edit, i))) {
i++;
break;
}
}
}
return edit_move_backward (edit, edit_bol (edit, edit->curs1),
edit->curs_line - i);
}
/*
* Find the end of the current paragraph for the purpose of formatting.
* Return position in the file.
*/
static long
end_paragraph (WEdit *edit, int force)
{
int i;
for (i = edit->curs_line + 1; i <= edit->total_lines; i++) {
if (line_is_blank (edit, i)) {
i--;
break;
}
if (force)
if (bad_line_start (edit, line_start (edit, i))) {
i--;
break;
}
}
return edit_eol (edit,
edit_move_forward (edit, edit_bol (edit, edit->curs1),
i - edit->curs_line, 0));
}
static unsigned char *
get_paragraph (WEdit *edit, long p, long q, int indent, int *size)
{
unsigned char *s, *t;
#if 0
t = g_malloc ((q - p) + 2 * (q - p) / option_word_wrap_line_length +
10);
#else
t = g_malloc (2 * (q - p) + 100);
#endif
if (!t)
return 0;
for (s = t; p < q; p++, s++) {
if (indent)
if (edit_get_byte (edit, p - 1) == '\n')
while (strchr ("\t ", edit_get_byte (edit, p)))
p++;
*s = edit_get_byte (edit, p);
}
*size = (unsigned long) s - (unsigned long) t;
t[*size] = '\n';
return t;
}
static void strip_newlines (unsigned char *t, int size)
{
unsigned char *p = t;
while (size--) {
*p = *p == '\n' ? ' ' : *p;
p++;
}
}
/*
This is a copy of the function
int calc_text_pos (WEdit * edit, long b, long *q, int l)
in propfont.c :(
It calculates the number of chars in a line specified to length l in pixels
*/
static inline int next_tab_pos (int x)
{
return x += tab_width - x % tab_width;
}
static int line_pixel_length (unsigned char *t, long b, int l)
{
int x = 0, c, xn = 0;
for (;;) {
c = t[b];
switch (c) {
case '\n':
return b;
case '\t':
xn = next_tab_pos (x);
break;
default:
xn = x + 1;
break;
}
if (xn > l)
break;
x = xn;
b++;
}
return b;
}
static int
next_word_start (unsigned char *t, int q)
{
int i;
for (i = q;; i++) {
switch (t[i]) {
case '\n':
return -1;
case '\t':
case ' ':
for (;; i++) {
if (t[i] == '\n')
return -1;
if (t[i] != ' ' && t[i] != '\t')
return i;
}
break;
}
}
}
/* find the start of a word */
static int
word_start (unsigned char *t, int q)
{
int i = q;
if (t[q] == ' ' || t[q] == '\t')
return next_word_start (t, q);
for (;;) {
int c;
if (!i)
return -1;
c = t[i - 1];
if (c == '\n')
return -1;
if (c == ' ' || c == '\t')
return i;
i--;
}
}
/* replaces ' ' with '\n' to properly format a paragraph */
static void format_this (unsigned char *t, int size, int indent)
{
int q = 0, ww;
strip_newlines (t, size);
ww = option_word_wrap_line_length * FONT_MEAN_WIDTH - indent;
if (ww < FONT_MEAN_WIDTH * 2)
ww = FONT_MEAN_WIDTH * 2;
for (;;) {
int p;
q = line_pixel_length (t, q, ww);
if (q > size)
break;
if (t[q] == '\n')
break;
p = word_start (t, q);
if (p == -1)
q = next_word_start (t, q); /* Return the end of the word if the beginning
of the word is at the beginning of a line
(i.e. a very long word) */
else
q = p;
if (q == -1) /* end of paragraph */
break;
if (q)
t[q - 1] = '\n';
}
}
static void replace_at (WEdit * edit, long q, int c)
{
edit_cursor_move (edit, q - edit->curs1);
edit_delete (edit);
edit_insert_ahead (edit, c);
}
/* replaces a block of text */
static void
put_paragraph (WEdit * edit, unsigned char *t, long p, int indent, int size)
{
long cursor;
int i, c = 0;
cursor = edit->curs1;
if (indent)
while (strchr ("\t ", edit_get_byte (edit, p)))
p++;
for (i = 0; i < size; i++, p++) {
if (i && indent) {
if (t[i - 1] == '\n' && c == '\n') {
while (strchr ("\t ", edit_get_byte (edit, p)))
p++;
} else if (t[i - 1] == '\n') {
long curs;
edit_cursor_move (edit, p - edit->curs1);
curs = edit->curs1;
edit_insert_indent (edit, indent);
if (cursor >= curs)
cursor += edit->curs1 - p;
p = edit->curs1;
} else if (c == '\n') {
edit_cursor_move (edit, p - edit->curs1);
while (strchr ("\t ", edit_get_byte (edit, p))) {
edit_delete (edit);
if (cursor > edit->curs1)
cursor--;
}
p = edit->curs1;
}
}
c = edit_get_byte (edit, p);
if (c != t[i])
replace_at (edit, p, t[i]);
}
edit_cursor_move (edit, cursor - edit->curs1); /* restore cursor position */
}
static int test_indent (WEdit * edit, long p, long q)
{
int indent;
indent = edit_indent_width (edit, p++);
if (!indent)
return 0;
for (; p < q; p++)
if (edit_get_byte (edit, p - 1) == '\n')
if (indent != edit_indent_width (edit, p))
return 0;
return indent;
}
void
format_paragraph (WEdit *edit, int force)
{
long p, q;
int size;
unsigned char *t;
int indent = 0;
if (option_word_wrap_line_length < 2)
return;
if (line_is_blank (edit, edit->curs_line))
return;
p = begin_paragraph (edit, force);
q = end_paragraph (edit, force);
indent = test_indent (edit, p, q);
t = get_paragraph (edit, p, q, indent, &size);
if (!t)
return;
if (!force) {
int i;
if (strchr (NO_FORMAT_CHARS_START, *t)) {
g_free (t);
return;
}
for (i = 0; i < size - 1; i++) {
if (t[i] == '\n') {
if (strchr (NO_FORMAT_CHARS_START "\t ", t[i + 1])) {
g_free (t);
return;
}
}
}
}
format_this (t, q - p, indent);
put_paragraph (edit, t, p, indent, size);
g_free (t);
/* Scroll left as much as possible to show the formatted paragraph */
edit_scroll_left (edit, -edit->start_col);
}