keymap files: unification of Fxx keys: move to lower case.
[midnight-commander.git] / src / editor / wordproc.c
blobc17cb5206e3d20b4c1417d640e286a66f2a01214
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 /** \file
22 * \brief Source: word-processor mode for the editor: does dynamic paragraph formatting
23 * \author Paul Sheer
24 * \date 1996
27 #include <config.h>
29 #include <stdio.h>
30 #include <stdarg.h>
31 #include <sys/types.h>
32 #include <unistd.h>
33 #include <string.h>
34 #include <ctype.h>
35 #include <errno.h>
36 #include <sys/stat.h>
38 #include <stdlib.h>
40 #include "lib/global.h"
42 #include "src/setup.h" /* option_tab_spacing */
44 #include "edit-impl.h"
45 #include "edit-widget.h"
47 /*** global variables ****************************************************************************/
49 /*** file scope macro definitions ****************************************************************/
51 #define tab_width option_tab_spacing
53 #define NO_FORMAT_CHARS_START "-+*\\,.;:&>"
54 #define FONT_MEAN_WIDTH 1
56 /*** file scope type declarations ****************************************************************/
58 /*** file scope variables ************************************************************************/
60 /*** file scope functions ************************************************************************/
61 /* --------------------------------------------------------------------------------------------- */
63 static long
64 line_start (WEdit * edit, long line)
66 long p, l;
68 l = edit->curs_line;
69 p = edit->curs1;
71 if (line < l)
72 p = edit_move_backward (edit, p, l - line);
73 else if (line > l)
74 p = edit_move_forward (edit, p, line - l, 0);
76 p = edit_bol (edit, p);
77 while (strchr ("\t ", edit_get_byte (edit, p)))
78 p++;
79 return p;
82 /* --------------------------------------------------------------------------------------------- */
84 static int
85 bad_line_start (WEdit * edit, long p)
87 int c;
88 c = edit_get_byte (edit, p);
89 if (c == '.')
90 { /* `...' is acceptable */
91 if (edit_get_byte (edit, p + 1) == '.')
92 if (edit_get_byte (edit, p + 2) == '.')
93 return 0;
94 return 1;
96 if (c == '-')
98 if (edit_get_byte (edit, p + 1) == '-')
99 if (edit_get_byte (edit, p + 2) == '-')
100 return 0; /* `---' is acceptable */
101 return 1;
103 if (strchr (NO_FORMAT_CHARS_START, c))
104 return 1;
105 return 0;
108 /* --------------------------------------------------------------------------------------------- */
110 * Find the start of the current paragraph for the purpose of formatting.
111 * Return position in the file.
114 static long
115 begin_paragraph (WEdit * edit, int force)
117 long i;
118 for (i = edit->curs_line - 1; i >= 0; i--)
120 if (line_is_blank (edit, i))
122 i++;
123 break;
125 if (force)
127 if (bad_line_start (edit, line_start (edit, i)))
129 i++;
130 break;
134 return edit_move_backward (edit, edit_bol (edit, edit->curs1), edit->curs_line - i);
137 /* --------------------------------------------------------------------------------------------- */
139 * Find the end of the current paragraph for the purpose of formatting.
140 * Return position in the file.
143 static long
144 end_paragraph (WEdit * edit, int force)
146 int i;
147 for (i = edit->curs_line + 1; i <= edit->total_lines; i++)
149 if (line_is_blank (edit, i))
151 i--;
152 break;
154 if (force)
155 if (bad_line_start (edit, line_start (edit, i)))
157 i--;
158 break;
161 return edit_eol (edit,
162 edit_move_forward (edit, edit_bol (edit, edit->curs1),
163 i - edit->curs_line, 0));
166 /* --------------------------------------------------------------------------------------------- */
168 static unsigned char *
169 get_paragraph (WEdit * edit, long p, long q, int indent, int *size)
171 unsigned char *s, *t;
172 #if 0
173 t = g_try_malloc ((q - p) + 2 * (q - p) / option_word_wrap_line_length + 10);
174 #else
175 t = g_try_malloc (2 * (q - p) + 100);
176 #endif
177 if (t == NULL)
178 return NULL;
179 for (s = t; p < q; p++, s++)
181 if (indent)
182 if (edit_get_byte (edit, p - 1) == '\n')
183 while (strchr ("\t ", edit_get_byte (edit, p)))
184 p++;
185 *s = edit_get_byte (edit, p);
187 *size = (unsigned long) s - (unsigned long) t;
188 t[*size] = '\n';
189 return t;
192 /* --------------------------------------------------------------------------------------------- */
194 static inline void
195 strip_newlines (unsigned char *t, int size)
197 unsigned char *p = t;
198 while (size-- != 0)
200 if (*p == '\n')
201 *p = ' ';
202 p++;
206 /* --------------------------------------------------------------------------------------------- */
208 This function calculates the number of chars in a line specified to length l in pixels
211 static inline int
212 next_tab_pos (int x)
214 return x += tab_width - x % tab_width;
217 /* --------------------------------------------------------------------------------------------- */
219 static inline int
220 line_pixel_length (unsigned char *t, long b, int l)
222 int x = 0, c, xn = 0;
223 for (;;)
225 c = t[b];
226 switch (c)
228 case '\n':
229 return b;
230 case '\t':
231 xn = next_tab_pos (x);
232 break;
233 default:
234 xn = x + 1;
235 break;
237 if (xn > l)
238 break;
239 x = xn;
240 b++;
242 return b;
245 /* --------------------------------------------------------------------------------------------- */
247 static int
248 next_word_start (unsigned char *t, int q, int size)
250 int i;
251 int saw_ws = 0;
253 for (i = q; i < size; i++)
255 switch (t[i])
257 case '\n':
258 return -1;
259 case '\t':
260 case ' ':
261 saw_ws = 1;
262 break;
263 default:
264 if (saw_ws != 0)
265 return i;
266 break;
269 return -1;
272 /* --------------------------------------------------------------------------------------------- */
273 /** find the start of a word */
275 static inline int
276 word_start (unsigned char *t, int q, int size)
278 int i = q;
279 if (t[q] == ' ' || t[q] == '\t')
280 return next_word_start (t, q, size);
281 for (;;)
283 int c;
284 if (!i)
285 return -1;
286 c = t[i - 1];
287 if (c == '\n')
288 return -1;
289 if (c == ' ' || c == '\t')
290 return i;
291 i--;
295 /* --------------------------------------------------------------------------------------------- */
296 /** replaces ' ' with '\n' to properly format a paragraph */
298 static inline void
299 format_this (unsigned char *t, int size, int indent)
301 int q = 0, ww;
302 strip_newlines (t, size);
303 ww = option_word_wrap_line_length * FONT_MEAN_WIDTH - indent;
304 if (ww < FONT_MEAN_WIDTH * 2)
305 ww = FONT_MEAN_WIDTH * 2;
306 for (;;)
308 int p;
309 q = line_pixel_length (t, q, ww);
310 if (q > size)
311 break;
312 if (t[q] == '\n')
313 break;
314 p = word_start (t, q, size);
315 if (p == -1)
316 q = next_word_start (t, q, size); /* Return the end of the word if the beginning
317 of the word is at the beginning of a line
318 (i.e. a very long word) */
319 else
320 q = p;
321 if (q == -1) /* end of paragraph */
322 break;
323 if (q)
324 t[q - 1] = '\n';
328 /* --------------------------------------------------------------------------------------------- */
330 static inline void
331 replace_at (WEdit * edit, long q, int c)
333 edit_cursor_move (edit, q - edit->curs1);
334 edit_delete (edit, 1);
335 edit_insert_ahead (edit, c);
338 /* --------------------------------------------------------------------------------------------- */
339 /** replaces a block of text */
341 static inline void
342 put_paragraph (WEdit * edit, unsigned char *t, long p, int indent, int size)
344 long cursor;
345 int i, c = 0;
346 cursor = edit->curs1;
347 if (indent)
348 while (strchr ("\t ", edit_get_byte (edit, p)))
349 p++;
350 for (i = 0; i < size; i++, p++)
352 if (i && indent)
354 if (t[i - 1] == '\n' && c == '\n')
356 while (strchr ("\t ", edit_get_byte (edit, p)))
357 p++;
359 else if (t[i - 1] == '\n')
361 long curs;
362 edit_cursor_move (edit, p - edit->curs1);
363 curs = edit->curs1;
364 edit_insert_indent (edit, indent);
365 if (cursor >= curs)
366 cursor += edit->curs1 - p;
367 p = edit->curs1;
369 else if (c == '\n')
371 edit_cursor_move (edit, p - edit->curs1);
372 while (strchr ("\t ", edit_get_byte (edit, p)))
374 edit_delete (edit, 1);
375 if (cursor > edit->curs1)
376 cursor--;
378 p = edit->curs1;
381 c = edit_get_byte (edit, p);
382 if (c != t[i])
383 replace_at (edit, p, t[i]);
385 edit_cursor_move (edit, cursor - edit->curs1); /* restore cursor position */
388 /* --------------------------------------------------------------------------------------------- */
390 static inline int
391 test_indent (WEdit * edit, long p, long q)
393 int indent;
394 indent = edit_indent_width (edit, p++);
395 if (!indent)
396 return 0;
397 for (; p < q; p++)
398 if (edit_get_byte (edit, p - 1) == '\n')
399 if (indent != edit_indent_width (edit, p))
400 return 0;
401 return indent;
404 /* --------------------------------------------------------------------------------------------- */
405 /*** public functions ****************************************************************************/
406 /* --------------------------------------------------------------------------------------------- */
408 void
409 format_paragraph (WEdit * edit, int force)
411 long p, q;
412 int size;
413 unsigned char *t;
414 int indent = 0;
415 if (option_word_wrap_line_length < 2)
416 return;
417 if (line_is_blank (edit, edit->curs_line))
418 return;
419 p = begin_paragraph (edit, force);
420 q = end_paragraph (edit, force);
421 indent = test_indent (edit, p, q);
422 t = get_paragraph (edit, p, q, indent, &size);
423 if (!t)
424 return;
425 if (!force)
427 int i;
428 if (strchr (NO_FORMAT_CHARS_START, *t))
430 g_free (t);
431 return;
433 for (i = 0; i < size - 1; i++)
435 if (t[i] == '\n')
437 if (strchr (NO_FORMAT_CHARS_START "\t ", t[i + 1]))
439 g_free (t);
440 return;
445 format_this (t, q - p, indent);
446 put_paragraph (edit, t, p, indent, size);
447 g_free (t);
449 /* Scroll left as much as possible to show the formatted paragraph */
450 edit_scroll_left (edit, -edit->start_col);
453 /* --------------------------------------------------------------------------------------------- */