Merge branch '2123_crash_while_copy'
[midnight-commander.git] / src / editor / wordproc.c
blobe4ba63412bf50d3f7de0476f0bd7f7344633f9b8
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 #define tab_width option_tab_spacing
49 #define NO_FORMAT_CHARS_START "-+*\\,.;:&>"
50 #define FONT_MEAN_WIDTH 1
52 static long
53 line_start (WEdit *edit, long line)
55 long p, l;
57 l = edit->curs_line;
58 p = edit->curs1;
60 if (line < l)
61 p = edit_move_backward (edit, p, l - line);
62 else if (line > l)
63 p = edit_move_forward (edit, p, line - l, 0);
65 p = edit_bol (edit, p);
66 while (strchr ("\t ", edit_get_byte (edit, p)))
67 p++;
68 return p;
71 static int bad_line_start (WEdit * edit, long p)
73 int c;
74 c = edit_get_byte (edit, p);
75 if (c == '.') { /* `...' is acceptable */
76 if (edit_get_byte (edit, p + 1) == '.')
77 if (edit_get_byte (edit, p + 2) == '.')
78 return 0;
79 return 1;
81 if (c == '-') {
82 if (edit_get_byte (edit, p + 1) == '-')
83 if (edit_get_byte (edit, p + 2) == '-')
84 return 0; /* `---' is acceptable */
85 return 1;
87 if (strchr (NO_FORMAT_CHARS_START, c))
88 return 1;
89 return 0;
93 * Find the start of the current paragraph for the purpose of formatting.
94 * Return position in the file.
96 static long
97 begin_paragraph (WEdit *edit, int force)
99 long i;
100 for (i = edit->curs_line - 1; i >= 0; i--) {
101 if (line_is_blank (edit, i)) {
102 i++;
103 break;
105 if (force) {
106 if (bad_line_start (edit, line_start (edit, i))) {
107 i++;
108 break;
112 return edit_move_backward (edit, edit_bol (edit, edit->curs1),
113 edit->curs_line - i);
117 * Find the end of the current paragraph for the purpose of formatting.
118 * Return position in the file.
120 static long
121 end_paragraph (WEdit *edit, int force)
123 int i;
124 for (i = edit->curs_line + 1; i <= edit->total_lines; i++) {
125 if (line_is_blank (edit, i)) {
126 i--;
127 break;
129 if (force)
130 if (bad_line_start (edit, line_start (edit, i))) {
131 i--;
132 break;
135 return edit_eol (edit,
136 edit_move_forward (edit, edit_bol (edit, edit->curs1),
137 i - edit->curs_line, 0));
140 static unsigned char *
141 get_paragraph (WEdit *edit, long p, long q, int indent, int *size)
143 unsigned char *s, *t;
144 #if 0
145 t = g_try_malloc ((q - p) + 2 * (q - p) / option_word_wrap_line_length +
146 10);
147 #else
148 t = g_try_malloc (2 * (q - p) + 100);
149 #endif
150 if (t == NULL)
151 return NULL;
152 for (s = t; p < q; p++, s++) {
153 if (indent)
154 if (edit_get_byte (edit, p - 1) == '\n')
155 while (strchr ("\t ", edit_get_byte (edit, p)))
156 p++;
157 *s = edit_get_byte (edit, p);
159 *size = (unsigned long) s - (unsigned long) t;
160 t[*size] = '\n';
161 return t;
164 static inline void
165 strip_newlines (unsigned char *t, int size)
167 unsigned char *p = t;
168 while (size-- != 0) {
169 if (*p == '\n')
170 *p = ' ';
171 p++;
176 This function calculates the number of chars in a line specified to length l in pixels
178 static inline int
179 next_tab_pos (int x)
181 return x += tab_width - x % tab_width;
184 static inline int
185 line_pixel_length (unsigned char *t, long b, int l)
187 int x = 0, c, xn = 0;
188 for (;;) {
189 c = t[b];
190 switch (c) {
191 case '\n':
192 return b;
193 case '\t':
194 xn = next_tab_pos (x);
195 break;
196 default:
197 xn = x + 1;
198 break;
200 if (xn > l)
201 break;
202 x = xn;
203 b++;
205 return b;
208 static int
209 next_word_start (unsigned char *t, int q, int size)
211 int i;
212 int saw_ws = 0;
214 for (i = q; i < size; i++) {
215 switch (t[i]) {
216 case '\n':
217 return -1;
218 case '\t':
219 case ' ':
220 saw_ws = 1;
221 break;
222 default:
223 if (saw_ws != 0)
224 return i;
225 break;
228 return -1;
231 /* find the start of a word */
232 static inline int
233 word_start (unsigned char *t, int q, int size)
235 int i = q;
236 if (t[q] == ' ' || t[q] == '\t')
237 return next_word_start (t, q, size);
238 for (;;) {
239 int c;
240 if (!i)
241 return -1;
242 c = t[i - 1];
243 if (c == '\n')
244 return -1;
245 if (c == ' ' || c == '\t')
246 return i;
247 i--;
251 /* replaces ' ' with '\n' to properly format a paragraph */
252 static inline void
253 format_this (unsigned char *t, int size, int indent)
255 int q = 0, ww;
256 strip_newlines (t, size);
257 ww = option_word_wrap_line_length * FONT_MEAN_WIDTH - indent;
258 if (ww < FONT_MEAN_WIDTH * 2)
259 ww = FONT_MEAN_WIDTH * 2;
260 for (;;) {
261 int p;
262 q = line_pixel_length (t, q, ww);
263 if (q > size)
264 break;
265 if (t[q] == '\n')
266 break;
267 p = word_start (t, q, size);
268 if (p == -1)
269 q = next_word_start (t, q, size); /* Return the end of the word if the beginning
270 of the word is at the beginning of a line
271 (i.e. a very long word) */
272 else
273 q = p;
274 if (q == -1) /* end of paragraph */
275 break;
276 if (q)
277 t[q - 1] = '\n';
281 static inline void
282 replace_at (WEdit * edit, long q, int c)
284 edit_cursor_move (edit, q - edit->curs1);
285 edit_delete (edit, 1);
286 edit_insert_ahead (edit, c);
289 /* replaces a block of text */
290 static inline void
291 put_paragraph (WEdit * edit, unsigned char *t, long p, int indent, int size)
293 long cursor;
294 int i, c = 0;
295 cursor = edit->curs1;
296 if (indent)
297 while (strchr ("\t ", edit_get_byte (edit, p)))
298 p++;
299 for (i = 0; i < size; i++, p++) {
300 if (i && indent) {
301 if (t[i - 1] == '\n' && c == '\n') {
302 while (strchr ("\t ", edit_get_byte (edit, p)))
303 p++;
304 } else if (t[i - 1] == '\n') {
305 long curs;
306 edit_cursor_move (edit, p - edit->curs1);
307 curs = edit->curs1;
308 edit_insert_indent (edit, indent);
309 if (cursor >= curs)
310 cursor += edit->curs1 - p;
311 p = edit->curs1;
312 } else if (c == '\n') {
313 edit_cursor_move (edit, p - edit->curs1);
314 while (strchr ("\t ", edit_get_byte (edit, p))) {
315 edit_delete (edit, 1);
316 if (cursor > edit->curs1)
317 cursor--;
319 p = edit->curs1;
322 c = edit_get_byte (edit, p);
323 if (c != t[i])
324 replace_at (edit, p, t[i]);
326 edit_cursor_move (edit, cursor - edit->curs1); /* restore cursor position */
329 static inline int
330 test_indent (WEdit * edit, long p, long q)
332 int indent;
333 indent = edit_indent_width (edit, p++);
334 if (!indent)
335 return 0;
336 for (; p < q; p++)
337 if (edit_get_byte (edit, p - 1) == '\n')
338 if (indent != edit_indent_width (edit, p))
339 return 0;
340 return indent;
343 void
344 format_paragraph (WEdit *edit, int force)
346 long p, q;
347 int size;
348 unsigned char *t;
349 int indent = 0;
350 if (option_word_wrap_line_length < 2)
351 return;
352 if (line_is_blank (edit, edit->curs_line))
353 return;
354 p = begin_paragraph (edit, force);
355 q = end_paragraph (edit, force);
356 indent = test_indent (edit, p, q);
357 t = get_paragraph (edit, p, q, indent, &size);
358 if (!t)
359 return;
360 if (!force) {
361 int i;
362 if (strchr (NO_FORMAT_CHARS_START, *t)) {
363 g_free (t);
364 return;
366 for (i = 0; i < size - 1; i++) {
367 if (t[i] == '\n') {
368 if (strchr (NO_FORMAT_CHARS_START "\t ", t[i + 1])) {
369 g_free (t);
370 return;
375 format_this (t, q - p, indent);
376 put_paragraph (edit, t, p, indent, size);
377 g_free (t);
379 /* Scroll left as much as possible to show the formatted paragraph */
380 edit_scroll_left (edit, -edit->start_col);