Ticket 1551: Update GPL version from 2 to 3
[midnight-commander.git] / src / editor / wordproc.c
blobf28b0ce014c608b2f14ce631a4aeac9b09e4d741
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 "edit-widget.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 long p, l;
76 l = edit->curs_line;
77 p = edit->curs1;
79 if (line < l)
80 p = edit_move_backward (edit, p, l - line);
81 else if (line > l)
82 p = edit_move_forward (edit, p, line - l, 0);
84 p = edit_bol (edit, p);
85 while (strchr ("\t ", edit_get_byte (edit, p)))
86 p++;
87 return p;
90 /* --------------------------------------------------------------------------------------------- */
92 static int
93 bad_line_start (WEdit * edit, long p)
95 int c;
96 c = edit_get_byte (edit, p);
97 if (c == '.')
98 { /* `...' is acceptable */
99 if (edit_get_byte (edit, p + 1) == '.')
100 if (edit_get_byte (edit, p + 2) == '.')
101 return 0;
102 return 1;
104 if (c == '-')
106 if (edit_get_byte (edit, p + 1) == '-')
107 if (edit_get_byte (edit, p + 2) == '-')
108 return 0; /* `---' is acceptable */
109 return 1;
111 if (strchr (NO_FORMAT_CHARS_START, c))
112 return 1;
113 return 0;
116 /* --------------------------------------------------------------------------------------------- */
118 * Find the start of the current paragraph for the purpose of formatting.
119 * Return position in the file.
122 static long
123 begin_paragraph (WEdit * edit, int force)
125 long i;
126 for (i = edit->curs_line - 1; i >= 0; i--)
128 if (line_is_blank (edit, i))
130 i++;
131 break;
133 if (force)
135 if (bad_line_start (edit, line_start (edit, i)))
137 i++;
138 break;
142 return edit_move_backward (edit, edit_bol (edit, edit->curs1), edit->curs_line - i);
145 /* --------------------------------------------------------------------------------------------- */
147 * Find the end of the current paragraph for the purpose of formatting.
148 * Return position in the file.
151 static long
152 end_paragraph (WEdit * edit, int force)
154 int i;
155 for (i = edit->curs_line + 1; i <= edit->total_lines; i++)
157 if (line_is_blank (edit, i))
159 i--;
160 break;
162 if (force)
163 if (bad_line_start (edit, line_start (edit, i)))
165 i--;
166 break;
169 return edit_eol (edit,
170 edit_move_forward (edit, edit_bol (edit, edit->curs1),
171 i - edit->curs_line, 0));
174 /* --------------------------------------------------------------------------------------------- */
176 static unsigned char *
177 get_paragraph (WEdit * edit, long p, long q, int indent, int *size)
179 unsigned char *s, *t;
180 #if 0
181 t = g_try_malloc ((q - p) + 2 * (q - p) / option_word_wrap_line_length + 10);
182 #else
183 t = g_try_malloc (2 * (q - p) + 100);
184 #endif
185 if (t == NULL)
186 return NULL;
187 for (s = t; p < q; p++, s++)
189 if (indent)
190 if (edit_get_byte (edit, p - 1) == '\n')
191 while (strchr ("\t ", edit_get_byte (edit, p)))
192 p++;
193 *s = edit_get_byte (edit, p);
195 *size = (unsigned long) (s - t);
196 /* FIXME: all variables related to 'size' should be fixed */
197 t[*size] = '\n';
198 return t;
201 /* --------------------------------------------------------------------------------------------- */
203 static inline void
204 strip_newlines (unsigned char *t, int size)
206 unsigned char *p = t;
207 while (size-- != 0)
209 if (*p == '\n')
210 *p = ' ';
211 p++;
215 /* --------------------------------------------------------------------------------------------- */
217 This function calculates the number of chars in a line specified to length l in pixels
220 static inline int
221 next_tab_pos (int x)
223 return x += tab_width - x % tab_width;
226 /* --------------------------------------------------------------------------------------------- */
228 static inline int
229 line_pixel_length (unsigned char *t, long b, int l)
231 int x = 0, c, xn = 0;
232 for (;;)
234 c = t[b];
235 switch (c)
237 case '\n':
238 return b;
239 case '\t':
240 xn = next_tab_pos (x);
241 break;
242 default:
243 xn = x + 1;
244 break;
246 if (xn > l)
247 break;
248 x = xn;
249 b++;
251 return b;
254 /* --------------------------------------------------------------------------------------------- */
256 static int
257 next_word_start (unsigned char *t, int q, int size)
259 int i;
260 int saw_ws = 0;
262 for (i = q; i < size; i++)
264 switch (t[i])
266 case '\n':
267 return -1;
268 case '\t':
269 case ' ':
270 saw_ws = 1;
271 break;
272 default:
273 if (saw_ws != 0)
274 return i;
275 break;
278 return -1;
281 /* --------------------------------------------------------------------------------------------- */
282 /** find the start of a word */
284 static inline int
285 word_start (unsigned char *t, int q, int size)
287 int i = q;
288 if (t[q] == ' ' || t[q] == '\t')
289 return next_word_start (t, q, size);
290 for (;;)
292 int c;
293 if (!i)
294 return -1;
295 c = t[i - 1];
296 if (c == '\n')
297 return -1;
298 if (c == ' ' || c == '\t')
299 return i;
300 i--;
304 /* --------------------------------------------------------------------------------------------- */
305 /** replaces ' ' with '\n' to properly format a paragraph */
307 static inline void
308 format_this (unsigned char *t, int size, int indent)
310 int q = 0, ww;
311 strip_newlines (t, size);
312 ww = option_word_wrap_line_length * FONT_MEAN_WIDTH - indent;
313 if (ww < FONT_MEAN_WIDTH * 2)
314 ww = FONT_MEAN_WIDTH * 2;
315 for (;;)
317 int p;
318 q = line_pixel_length (t, q, ww);
319 if (q > size)
320 break;
321 if (t[q] == '\n')
322 break;
323 p = word_start (t, q, size);
324 if (p == -1)
325 q = next_word_start (t, q, size); /* Return the end of the word if the beginning
326 of the word is at the beginning of a line
327 (i.e. a very long word) */
328 else
329 q = p;
330 if (q == -1) /* end of paragraph */
331 break;
332 if (q)
333 t[q - 1] = '\n';
337 /* --------------------------------------------------------------------------------------------- */
339 static inline void
340 replace_at (WEdit * edit, long q, int c)
342 edit_cursor_move (edit, q - edit->curs1);
343 edit_delete (edit, 1);
344 edit_insert_ahead (edit, c);
347 /* --------------------------------------------------------------------------------------------- */
348 /** replaces a block of text */
350 static inline void
351 put_paragraph (WEdit * edit, unsigned char *t, long p, int indent, int size)
353 long cursor;
354 int i, c = 0;
355 cursor = edit->curs1;
356 if (indent)
357 while (strchr ("\t ", edit_get_byte (edit, p)))
358 p++;
359 for (i = 0; i < size; i++, p++)
361 if (i && indent)
363 if (t[i - 1] == '\n' && c == '\n')
365 while (strchr ("\t ", edit_get_byte (edit, p)))
366 p++;
368 else if (t[i - 1] == '\n')
370 long curs;
371 edit_cursor_move (edit, p - edit->curs1);
372 curs = edit->curs1;
373 edit_insert_indent (edit, indent);
374 if (cursor >= curs)
375 cursor += edit->curs1 - p;
376 p = edit->curs1;
378 else if (c == '\n')
380 edit_cursor_move (edit, p - edit->curs1);
381 while (strchr ("\t ", edit_get_byte (edit, p)))
383 edit_delete (edit, 1);
384 if (cursor > edit->curs1)
385 cursor--;
387 p = edit->curs1;
390 c = edit_get_byte (edit, p);
391 if (c != t[i])
392 replace_at (edit, p, t[i]);
394 edit_cursor_move (edit, cursor - edit->curs1); /* restore cursor position */
397 /* --------------------------------------------------------------------------------------------- */
399 static inline int
400 test_indent (WEdit * edit, long p, long q)
402 int indent;
403 indent = edit_indent_width (edit, p++);
404 if (!indent)
405 return 0;
406 for (; p < q; p++)
407 if (edit_get_byte (edit, p - 1) == '\n')
408 if (indent != edit_indent_width (edit, p))
409 return 0;
410 return indent;
413 /* --------------------------------------------------------------------------------------------- */
414 /*** public functions ****************************************************************************/
415 /* --------------------------------------------------------------------------------------------- */
417 void
418 format_paragraph (WEdit * edit, int force)
420 long p, q;
421 int size;
422 unsigned char *t;
423 int indent = 0;
424 if (option_word_wrap_line_length < 2)
425 return;
426 if (line_is_blank (edit, edit->curs_line))
427 return;
428 p = begin_paragraph (edit, force);
429 q = end_paragraph (edit, force);
430 indent = test_indent (edit, p, q);
431 t = get_paragraph (edit, p, q, indent, &size);
432 if (!t)
433 return;
434 if (!force)
436 int i;
437 if (strchr (NO_FORMAT_CHARS_START, *t))
439 g_free (t);
440 return;
442 for (i = 0; i < size - 1; i++)
444 if (t[i] == '\n')
446 if (strchr (NO_FORMAT_CHARS_START "\t ", t[i + 1]))
448 g_free (t);
449 return;
454 format_this (t, q - p, indent);
455 put_paragraph (edit, t, p, indent, size);
456 g_free (t);
458 /* Scroll left as much as possible to show the formatted paragraph */
459 edit_scroll_left (edit, -edit->start_col);
462 /* --------------------------------------------------------------------------------------------- */