* doc/es/mc.1.in: Cleanup. Fix key naming.
[midnight-commander.git] / edit / wordproc.c
blob1518b8f34fd97de01e0032fc49d957e5d8ef73dd
1 /* wordproc.c - word-processor mode for the editor: does dynamic
2 paragraph formatting.
3 Copyright (C) 1996 Paul Sheer
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License as published by
7 the Free Software Foundation; either version 2 of the License, or
8 (at your option) any later version.
10 This program is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
18 02110-1301, USA.
21 #include <config.h>
23 #include <stdio.h>
24 #include <stdarg.h>
25 #include <sys/types.h>
26 #ifdef HAVE_UNISTD_H
27 # include <unistd.h>
28 #endif
29 #include <string.h>
30 #include <ctype.h>
31 #include <errno.h>
32 #include <sys/stat.h>
34 #include <stdlib.h>
36 #include "../src/global.h"
38 #include "edit.h"
39 #include "edit-widget.h"
41 #define tab_width option_tab_spacing
43 #define NO_FORMAT_CHARS_START "-+*\\,.;:&>"
44 #define FONT_MEAN_WIDTH 1
46 static long
47 line_start (WEdit *edit, long line)
49 long p, l;
51 l = edit->curs_line;
52 p = edit->curs1;
54 if (line < l)
55 p = edit_move_backward (edit, p, l - line);
56 else if (line > l)
57 p = edit_move_forward (edit, p, line - l, 0);
59 p = edit_bol (edit, p);
60 while (strchr ("\t ", edit_get_byte (edit, p)))
61 p++;
62 return p;
65 static int bad_line_start (WEdit * edit, long p)
67 int c;
68 c = edit_get_byte (edit, p);
69 if (c == '.') { /* `...' is acceptable */
70 if (edit_get_byte (edit, p + 1) == '.')
71 if (edit_get_byte (edit, p + 2) == '.')
72 return 0;
73 return 1;
75 if (c == '-') {
76 if (edit_get_byte (edit, p + 1) == '-')
77 if (edit_get_byte (edit, p + 2) == '-')
78 return 0; /* `---' is acceptable */
79 return 1;
81 if (strchr (NO_FORMAT_CHARS_START, c))
82 return 1;
83 return 0;
87 * Find the start of the current paragraph for the purpose of formatting.
88 * Return position in the file.
90 static long
91 begin_paragraph (WEdit *edit, int force)
93 int i;
94 for (i = edit->curs_line - 1; i >= 0; i--) {
95 if (line_is_blank (edit, i)) {
96 i++;
97 break;
99 if (force) {
100 if (bad_line_start (edit, line_start (edit, i))) {
101 i++;
102 break;
106 return edit_move_backward (edit, edit_bol (edit, edit->curs1),
107 edit->curs_line - i);
111 * Find the end of the current paragraph for the purpose of formatting.
112 * Return position in the file.
114 static long
115 end_paragraph (WEdit *edit, int force)
117 int i;
118 for (i = edit->curs_line + 1; i <= edit->total_lines; i++) {
119 if (line_is_blank (edit, i)) {
120 i--;
121 break;
123 if (force)
124 if (bad_line_start (edit, line_start (edit, i))) {
125 i--;
126 break;
129 return edit_eol (edit,
130 edit_move_forward (edit, edit_bol (edit, edit->curs1),
131 i - edit->curs_line, 0));
134 static unsigned char *
135 get_paragraph (WEdit *edit, long p, long q, int indent, int *size)
137 unsigned char *s, *t;
138 #if 0
139 t = g_malloc ((q - p) + 2 * (q - p) / option_word_wrap_line_length +
140 10);
141 #else
142 t = g_malloc (2 * (q - p) + 100);
143 #endif
144 if (!t)
145 return 0;
146 for (s = t; p < q; p++, s++) {
147 if (indent)
148 if (edit_get_byte (edit, p - 1) == '\n')
149 while (strchr ("\t ", edit_get_byte (edit, p)))
150 p++;
151 *s = edit_get_byte (edit, p);
153 *size = (unsigned long) s - (unsigned long) t;
154 t[*size] = '\n';
155 return t;
158 static void strip_newlines (unsigned char *t, int size)
160 unsigned char *p = t;
161 while (size--) {
162 *p = *p == '\n' ? ' ' : *p;
163 p++;
168 This is a copy of the function
169 int calc_text_pos (WEdit * edit, long b, long *q, int l)
170 in propfont.c :(
171 It calculates the number of chars in a line specified to length l in pixels
173 static inline int next_tab_pos (int x)
175 return x += tab_width - x % tab_width;
177 static int line_pixel_length (unsigned char *t, long b, int l)
179 int x = 0, c, xn = 0;
180 for (;;) {
181 c = t[b];
182 switch (c) {
183 case '\n':
184 return b;
185 case '\t':
186 xn = next_tab_pos (x);
187 break;
188 default:
189 xn = x + 1;
190 break;
192 if (xn > l)
193 break;
194 x = xn;
195 b++;
197 return b;
200 static int
201 next_word_start (unsigned char *t, int q)
203 int i;
204 for (i = q;; i++) {
205 switch (t[i]) {
206 case '\n':
207 return -1;
208 case '\t':
209 case ' ':
210 for (;; i++) {
211 if (t[i] == '\n')
212 return -1;
213 if (t[i] != ' ' && t[i] != '\t')
214 return i;
216 break;
221 /* find the start of a word */
222 static int
223 word_start (unsigned char *t, int q)
225 int i = q;
226 if (t[q] == ' ' || t[q] == '\t')
227 return next_word_start (t, q);
228 for (;;) {
229 int c;
230 if (!i)
231 return -1;
232 c = t[i - 1];
233 if (c == '\n')
234 return -1;
235 if (c == ' ' || c == '\t')
236 return i;
237 i--;
241 /* replaces ' ' with '\n' to properly format a paragraph */
242 static void format_this (unsigned char *t, int size, int indent)
244 int q = 0, ww;
245 strip_newlines (t, size);
246 ww = option_word_wrap_line_length * FONT_MEAN_WIDTH - indent;
247 if (ww < FONT_MEAN_WIDTH * 2)
248 ww = FONT_MEAN_WIDTH * 2;
249 for (;;) {
250 int p;
251 q = line_pixel_length (t, q, ww);
252 if (q > size)
253 break;
254 if (t[q] == '\n')
255 break;
256 p = word_start (t, q);
257 if (p == -1)
258 q = next_word_start (t, q); /* Return the end of the word if the beginning
259 of the word is at the beginning of a line
260 (i.e. a very long word) */
261 else
262 q = p;
263 if (q == -1) /* end of paragraph */
264 break;
265 if (q)
266 t[q - 1] = '\n';
270 static void replace_at (WEdit * edit, long q, int c)
272 edit_cursor_move (edit, q - edit->curs1);
273 edit_delete (edit);
274 edit_insert_ahead (edit, c);
277 /* replaces a block of text */
278 static void
279 put_paragraph (WEdit * edit, unsigned char *t, long p, int indent, int size)
281 long cursor;
282 int i, c = 0;
283 cursor = edit->curs1;
284 if (indent)
285 while (strchr ("\t ", edit_get_byte (edit, p)))
286 p++;
287 for (i = 0; i < size; i++, p++) {
288 if (i && indent) {
289 if (t[i - 1] == '\n' && c == '\n') {
290 while (strchr ("\t ", edit_get_byte (edit, p)))
291 p++;
292 } else if (t[i - 1] == '\n') {
293 long curs;
294 edit_cursor_move (edit, p - edit->curs1);
295 curs = edit->curs1;
296 edit_insert_indent (edit, indent);
297 if (cursor >= curs)
298 cursor += edit->curs1 - p;
299 p = edit->curs1;
300 } else if (c == '\n') {
301 edit_cursor_move (edit, p - edit->curs1);
302 while (strchr ("\t ", edit_get_byte (edit, p))) {
303 edit_delete (edit);
304 if (cursor > edit->curs1)
305 cursor--;
307 p = edit->curs1;
310 c = edit_get_byte (edit, p);
311 if (c != t[i])
312 replace_at (edit, p, t[i]);
314 edit_cursor_move (edit, cursor - edit->curs1); /* restore cursor position */
317 static int test_indent (WEdit * edit, long p, long q)
319 int indent;
320 indent = edit_indent_width (edit, p++);
321 if (!indent)
322 return 0;
323 for (; p < q; p++)
324 if (edit_get_byte (edit, p - 1) == '\n')
325 if (indent != edit_indent_width (edit, p))
326 return 0;
327 return indent;
330 void
331 format_paragraph (WEdit *edit, int force)
333 long p, q;
334 int size;
335 unsigned char *t;
336 int indent = 0;
337 if (option_word_wrap_line_length < 2)
338 return;
339 if (line_is_blank (edit, edit->curs_line))
340 return;
341 p = begin_paragraph (edit, force);
342 q = end_paragraph (edit, force);
343 indent = test_indent (edit, p, q);
344 t = get_paragraph (edit, p, q, indent, &size);
345 if (!t)
346 return;
347 if (!force) {
348 int i;
349 if (strchr (NO_FORMAT_CHARS_START, *t)) {
350 g_free (t);
351 return;
353 for (i = 0; i < size - 1; i++) {
354 if (t[i] == '\n') {
355 if (strchr (NO_FORMAT_CHARS_START "\t ", t[i + 1])) {
356 g_free (t);
357 return;
362 format_this (t, q - p, indent);
363 put_paragraph (edit, t, p, indent, size);
364 g_free (t);
366 /* Scroll left as much as possible to show the formatted paragraph */
367 edit_scroll_left (edit, -edit->start_col);