Update po/mc.pot and po/*.po files.
[midnight-commander.git] / src / editor / wordproc.c
blob580deef91c9e41e3a8c955427ef34b6d3fc0ef83
1 /*
2 Word-processor mode for the editor: does dynamic
3 paragraph formatting.
5 Copyright (C) 2011
6 The Free Software Foundation, Inc.
8 Copyright (C) 1996 Paul Sheer
10 Writen by:
11 Paul Sheer, 1996
13 This file is part of the Midnight Commander.
15 The Midnight Commander is free software: you can redistribute it
16 and/or modify it under the terms of the GNU General Public License as
17 published by the Free Software Foundation, either version 3 of the License,
18 or (at your option) any later version.
20 The Midnight Commander is distributed in the hope that it will be useful,
21 but WITHOUT ANY WARRANTY; without even the implied warranty of
22 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
23 GNU General Public License for more details.
25 You should have received a copy of the GNU General Public License
26 along with this program. If not, see <http://www.gnu.org/licenses/>.
29 /** \file
30 * \brief Source: word-processor mode for the editor: does dynamic paragraph formatting
31 * \author Paul Sheer
32 * \date 1996
35 #include <config.h>
37 #include <stdio.h>
38 #include <stdarg.h>
39 #include <sys/types.h>
40 #include <unistd.h>
41 #include <string.h>
42 #include <ctype.h>
43 #include <errno.h>
44 #include <sys/stat.h>
46 #include <stdlib.h>
48 #include "lib/global.h"
50 #include "src/setup.h" /* option_tab_spacing */
52 #include "edit-impl.h"
53 #include "editwidget.h"
55 /*** global variables ****************************************************************************/
57 /*** file scope macro definitions ****************************************************************/
59 #define tab_width option_tab_spacing
61 #define NO_FORMAT_CHARS_START "-+*\\,.;:&>"
62 #define FONT_MEAN_WIDTH 1
64 /*** file scope type declarations ****************************************************************/
66 /*** file scope variables ************************************************************************/
68 /*** file scope functions ************************************************************************/
69 /* --------------------------------------------------------------------------------------------- */
71 static long
72 line_start (WEdit * edit, long line)
74 off_t p;
75 long l;
77 l = edit->curs_line;
78 p = edit->curs1;
80 if (line < l)
81 p = edit_move_backward (edit, p, l - line);
82 else if (line > l)
83 p = edit_move_forward (edit, p, line - l, 0);
85 p = edit_bol (edit, p);
86 while (strchr ("\t ", edit_get_byte (edit, p)))
87 p++;
88 return p;
91 /* --------------------------------------------------------------------------------------------- */
93 static int
94 bad_line_start (WEdit * edit, off_t p)
96 int c;
97 c = edit_get_byte (edit, p);
98 if (c == '.')
99 { /* `...' is acceptable */
100 if (edit_get_byte (edit, p + 1) == '.')
101 if (edit_get_byte (edit, p + 2) == '.')
102 return 0;
103 return 1;
105 if (c == '-')
107 if (edit_get_byte (edit, p + 1) == '-')
108 if (edit_get_byte (edit, p + 2) == '-')
109 return 0; /* `---' is acceptable */
110 return 1;
112 if (strchr (NO_FORMAT_CHARS_START, c))
113 return 1;
114 return 0;
117 /* --------------------------------------------------------------------------------------------- */
119 * Find the start of the current paragraph for the purpose of formatting.
120 * Return position in the file.
123 static long
124 begin_paragraph (WEdit * edit, int force)
126 long i;
127 for (i = edit->curs_line - 1; i >= 0; i--)
129 if (edit_line_is_blank (edit, i))
131 i++;
132 break;
134 if (force)
136 if (bad_line_start (edit, line_start (edit, i)))
138 i++;
139 break;
143 return edit_move_backward (edit, edit_bol (edit, edit->curs1), edit->curs_line - i);
146 /* --------------------------------------------------------------------------------------------- */
148 * Find the end of the current paragraph for the purpose of formatting.
149 * Return position in the file.
152 static long
153 end_paragraph (WEdit * edit, int force)
155 long i;
156 for (i = edit->curs_line + 1; i <= edit->total_lines; i++)
158 if (edit_line_is_blank (edit, i))
160 i--;
161 break;
163 if (force)
164 if (bad_line_start (edit, line_start (edit, i)))
166 i--;
167 break;
170 return edit_eol (edit,
171 edit_move_forward (edit, edit_bol (edit, edit->curs1),
172 i - edit->curs_line, 0));
175 /* --------------------------------------------------------------------------------------------- */
177 static unsigned char *
178 get_paragraph (WEdit * edit, off_t p, off_t q, int indent, int *size)
180 unsigned char *s, *t;
181 #if 0
182 t = g_try_malloc ((q - p) + 2 * (q - p) / option_word_wrap_line_length + 10);
183 #else
184 t = g_try_malloc (2 * (q - p) + 100);
185 #endif
186 if (t == NULL)
187 return NULL;
188 for (s = t; p < q; p++, s++)
190 if (indent)
191 if (edit_get_byte (edit, p - 1) == '\n')
192 while (strchr ("\t ", edit_get_byte (edit, p)))
193 p++;
194 *s = edit_get_byte (edit, p);
196 *size = (unsigned long) (s - t);
197 /* FIXME: all variables related to 'size' should be fixed */
198 t[*size] = '\n';
199 return t;
202 /* --------------------------------------------------------------------------------------------- */
204 static inline void
205 strip_newlines (unsigned char *t, int size)
207 unsigned char *p = t;
208 while (size-- != 0)
210 if (*p == '\n')
211 *p = ' ';
212 p++;
216 /* --------------------------------------------------------------------------------------------- */
218 This function calculates the number of chars in a line specified to length l in pixels
221 static inline int
222 next_tab_pos (int x)
224 return x += tab_width - x % tab_width;
227 /* --------------------------------------------------------------------------------------------- */
229 static inline int
230 line_pixel_length (unsigned char *t, long b, int l)
232 int x = 0, c, xn = 0;
234 while (TRUE)
236 c = t[b];
237 switch (c)
239 case '\n':
240 return b;
241 case '\t':
242 xn = next_tab_pos (x);
243 break;
244 default:
245 xn = x + 1;
246 break;
248 if (xn > l)
249 break;
250 x = xn;
251 b++;
253 return b;
256 /* --------------------------------------------------------------------------------------------- */
258 static int
259 next_word_start (unsigned char *t, int q, int size)
261 int i;
262 int saw_ws = 0;
264 for (i = q; i < size; i++)
266 switch (t[i])
268 case '\n':
269 return -1;
270 case '\t':
271 case ' ':
272 saw_ws = 1;
273 break;
274 default:
275 if (saw_ws != 0)
276 return i;
277 break;
280 return -1;
283 /* --------------------------------------------------------------------------------------------- */
284 /** find the start of a word */
286 static inline int
287 word_start (unsigned char *t, int q, int size)
289 int i = q;
291 if (t[q] == ' ' || t[q] == '\t')
292 return next_word_start (t, q, size);
294 while (TRUE)
296 int c;
298 if (i == 0)
299 return -1;
300 c = t[i - 1];
301 if (c == '\n')
302 return -1;
303 if (c == ' ' || c == '\t')
304 return i;
305 i--;
309 /* --------------------------------------------------------------------------------------------- */
310 /** replaces ' ' with '\n' to properly format a paragraph */
312 static inline void
313 format_this (unsigned char *t, int size, int indent)
315 int q = 0, ww;
317 strip_newlines (t, size);
318 ww = option_word_wrap_line_length * FONT_MEAN_WIDTH - indent;
319 if (ww < FONT_MEAN_WIDTH * 2)
320 ww = FONT_MEAN_WIDTH * 2;
321 while (TRUE)
323 int p;
325 q = line_pixel_length (t, q, ww);
326 if (q > size)
327 break;
328 if (t[q] == '\n')
329 break;
330 p = word_start (t, q, size);
331 if (p == -1)
332 q = next_word_start (t, q, size); /* Return the end of the word if the beginning
333 of the word is at the beginning of a line
334 (i.e. a very long word) */
335 else
336 q = p;
337 if (q == -1) /* end of paragraph */
338 break;
339 if (q != 0)
340 t[q - 1] = '\n';
344 /* --------------------------------------------------------------------------------------------- */
346 static inline void
347 replace_at (WEdit * edit, long q, int c)
349 edit_cursor_move (edit, q - edit->curs1);
350 edit_delete (edit, TRUE);
351 edit_insert_ahead (edit, c);
354 /* --------------------------------------------------------------------------------------------- */
355 /** replaces a block of text */
357 static inline void
358 put_paragraph (WEdit * edit, unsigned char *t, off_t p, int indent, int size)
360 long cursor;
361 int i, c = 0;
363 cursor = edit->curs1;
364 if (indent)
365 while (strchr ("\t ", edit_get_byte (edit, p)))
366 p++;
367 for (i = 0; i < size; i++, p++)
369 if (i && indent)
371 if (t[i - 1] == '\n' && c == '\n')
373 while (strchr ("\t ", edit_get_byte (edit, p)))
374 p++;
376 else if (t[i - 1] == '\n')
378 off_t curs;
379 edit_cursor_move (edit, p - edit->curs1);
380 curs = edit->curs1;
381 edit_insert_indent (edit, indent);
382 if (cursor >= curs)
383 cursor += edit->curs1 - p;
384 p = edit->curs1;
386 else if (c == '\n')
388 edit_cursor_move (edit, p - edit->curs1);
389 while (strchr ("\t ", edit_get_byte (edit, p)))
391 edit_delete (edit, TRUE);
392 if (cursor > edit->curs1)
393 cursor--;
395 p = edit->curs1;
398 c = edit_get_byte (edit, p);
399 if (c != t[i])
400 replace_at (edit, p, t[i]);
402 edit_cursor_move (edit, cursor - edit->curs1); /* restore cursor position */
405 /* --------------------------------------------------------------------------------------------- */
407 static inline int
408 test_indent (const WEdit * edit, off_t p, off_t q)
410 int indent;
412 indent = edit_indent_width (edit, p++);
413 if (indent == 0)
414 return 0;
416 for (; p < q; p++)
417 if (edit_get_byte (edit, p - 1) == '\n')
418 if (indent != edit_indent_width (edit, p))
419 return 0;
420 return indent;
423 /* --------------------------------------------------------------------------------------------- */
424 /*** public functions ****************************************************************************/
425 /* --------------------------------------------------------------------------------------------- */
427 void
428 format_paragraph (WEdit * edit, int force)
430 long p, q;
431 int size;
432 unsigned char *t;
433 int indent = 0;
434 if (option_word_wrap_line_length < 2)
435 return;
436 if (edit_line_is_blank (edit, edit->curs_line))
437 return;
438 p = begin_paragraph (edit, force);
439 q = end_paragraph (edit, force);
440 indent = test_indent (edit, p, q);
441 t = get_paragraph (edit, p, q, indent, &size);
442 if (!t)
443 return;
444 if (!force)
446 int i;
447 if (strchr (NO_FORMAT_CHARS_START, *t))
449 g_free (t);
450 return;
452 for (i = 0; i < size - 1; i++)
454 if (t[i] == '\n')
456 if (strchr (NO_FORMAT_CHARS_START "\t ", t[i + 1]))
458 g_free (t);
459 return;
464 format_this (t, q - p, indent);
465 put_paragraph (edit, t, p, indent, size);
466 g_free (t);
468 /* Scroll left as much as possible to show the formatted paragraph */
469 edit_scroll_left (edit, -edit->start_col);
472 /* --------------------------------------------------------------------------------------------- */