(edit_delete): take gboolean instead of int.
[midnight-commander.git] / src / editor / wordproc.c
blobc57834ff13acacf053a5db5beadbb86e7d0af8a4
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 (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 (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;
233 for (;;)
235 c = t[b];
236 switch (c)
238 case '\n':
239 return b;
240 case '\t':
241 xn = next_tab_pos (x);
242 break;
243 default:
244 xn = x + 1;
245 break;
247 if (xn > l)
248 break;
249 x = xn;
250 b++;
252 return b;
255 /* --------------------------------------------------------------------------------------------- */
257 static int
258 next_word_start (unsigned char *t, int q, int size)
260 int i;
261 int saw_ws = 0;
263 for (i = q; i < size; i++)
265 switch (t[i])
267 case '\n':
268 return -1;
269 case '\t':
270 case ' ':
271 saw_ws = 1;
272 break;
273 default:
274 if (saw_ws != 0)
275 return i;
276 break;
279 return -1;
282 /* --------------------------------------------------------------------------------------------- */
283 /** find the start of a word */
285 static inline int
286 word_start (unsigned char *t, int q, int size)
288 int i = q;
289 if (t[q] == ' ' || t[q] == '\t')
290 return next_word_start (t, q, size);
291 for (;;)
293 int c;
294 if (!i)
295 return -1;
296 c = t[i - 1];
297 if (c == '\n')
298 return -1;
299 if (c == ' ' || c == '\t')
300 return i;
301 i--;
305 /* --------------------------------------------------------------------------------------------- */
306 /** replaces ' ' with '\n' to properly format a paragraph */
308 static inline void
309 format_this (unsigned char *t, int size, int indent)
311 int q = 0, ww;
312 strip_newlines (t, size);
313 ww = option_word_wrap_line_length * FONT_MEAN_WIDTH - indent;
314 if (ww < FONT_MEAN_WIDTH * 2)
315 ww = FONT_MEAN_WIDTH * 2;
316 for (;;)
318 int p;
319 q = line_pixel_length (t, q, ww);
320 if (q > size)
321 break;
322 if (t[q] == '\n')
323 break;
324 p = word_start (t, q, size);
325 if (p == -1)
326 q = next_word_start (t, q, size); /* Return the end of the word if the beginning
327 of the word is at the beginning of a line
328 (i.e. a very long word) */
329 else
330 q = p;
331 if (q == -1) /* end of paragraph */
332 break;
333 if (q)
334 t[q - 1] = '\n';
338 /* --------------------------------------------------------------------------------------------- */
340 static inline void
341 replace_at (WEdit * edit, long q, int c)
343 edit_cursor_move (edit, q - edit->curs1);
344 edit_delete (edit, TRUE);
345 edit_insert_ahead (edit, c);
348 /* --------------------------------------------------------------------------------------------- */
349 /** replaces a block of text */
351 static inline void
352 put_paragraph (WEdit * edit, unsigned char *t, off_t p, int indent, int size)
354 long cursor;
355 int i, c = 0;
356 cursor = edit->curs1;
357 if (indent)
358 while (strchr ("\t ", edit_get_byte (edit, p)))
359 p++;
360 for (i = 0; i < size; i++, p++)
362 if (i && indent)
364 if (t[i - 1] == '\n' && c == '\n')
366 while (strchr ("\t ", edit_get_byte (edit, p)))
367 p++;
369 else if (t[i - 1] == '\n')
371 off_t curs;
372 edit_cursor_move (edit, p - edit->curs1);
373 curs = edit->curs1;
374 edit_insert_indent (edit, indent);
375 if (cursor >= curs)
376 cursor += edit->curs1 - p;
377 p = edit->curs1;
379 else if (c == '\n')
381 edit_cursor_move (edit, p - edit->curs1);
382 while (strchr ("\t ", edit_get_byte (edit, p)))
384 edit_delete (edit, TRUE);
385 if (cursor > edit->curs1)
386 cursor--;
388 p = edit->curs1;
391 c = edit_get_byte (edit, p);
392 if (c != t[i])
393 replace_at (edit, p, t[i]);
395 edit_cursor_move (edit, cursor - edit->curs1); /* restore cursor position */
398 /* --------------------------------------------------------------------------------------------- */
400 static inline int
401 test_indent (WEdit * edit, off_t p, off_t q)
403 int indent;
404 indent = edit_indent_width (edit, p++);
405 if (!indent)
406 return 0;
407 for (; p < q; p++)
408 if (edit_get_byte (edit, p - 1) == '\n')
409 if (indent != edit_indent_width (edit, p))
410 return 0;
411 return indent;
414 /* --------------------------------------------------------------------------------------------- */
415 /*** public functions ****************************************************************************/
416 /* --------------------------------------------------------------------------------------------- */
418 void
419 format_paragraph (WEdit * edit, int force)
421 long p, q;
422 int size;
423 unsigned char *t;
424 int indent = 0;
425 if (option_word_wrap_line_length < 2)
426 return;
427 if (line_is_blank (edit, edit->curs_line))
428 return;
429 p = begin_paragraph (edit, force);
430 q = end_paragraph (edit, force);
431 indent = test_indent (edit, p, q);
432 t = get_paragraph (edit, p, q, indent, &size);
433 if (!t)
434 return;
435 if (!force)
437 int i;
438 if (strchr (NO_FORMAT_CHARS_START, *t))
440 g_free (t);
441 return;
443 for (i = 0; i < size - 1; i++)
445 if (t[i] == '\n')
447 if (strchr (NO_FORMAT_CHARS_START "\t ", t[i + 1]))
449 g_free (t);
450 return;
455 format_this (t, q - p, indent);
456 put_paragraph (edit, t, p, indent, size);
457 g_free (t);
459 /* Scroll left as much as possible to show the formatted paragraph */
460 edit_scroll_left (edit, -edit->start_col);
463 /* --------------------------------------------------------------------------------------------- */