* global.h: Move fcntl.h inclusion here. Define O_BINARY.
[midnight-commander.git] / edit / wordproc.c
blob9cc656c95c8b7316fc89560f1c8239295f110bdd
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., 59 Temple Place, Suite 330, Boston, MA
18 02111-1307, USA.
21 #include <config.h>
22 #include "edit.h"
24 #define tab_width option_tab_spacing
26 int line_is_blank (WEdit * edit, long line);
28 #define NO_FORMAT_CHARS_START "-+*\\,.;:&>"
30 static long line_start (WEdit * edit, long line)
32 static long p = -1, l = 0;
33 int c;
34 if (p == -1 || abs (l - line) > abs (edit->curs_line - line)) {
35 l = edit->curs_line;
36 p = edit->curs1;
38 if (line < l)
39 p = edit_move_backward (edit, p, l - line);
40 else if (line > l)
41 p = edit_move_forward (edit, p, line - l, 0);
42 l = line;
43 p = edit_bol (edit, p);
44 while (strchr ("\t ", c = edit_get_byte (edit, p)))
45 p++;
46 return p;
49 static int bad_line_start (WEdit * edit, long p)
51 int c;
52 c = edit_get_byte (edit, p);
53 if (c == '.') { /* `...' is acceptable */
54 if (edit_get_byte (edit, p + 1) == '.')
55 if (edit_get_byte (edit, p + 2) == '.')
56 return 0;
57 return 1;
59 if (c == '-') {
60 if (edit_get_byte (edit, p + 1) == '-')
61 if (edit_get_byte (edit, p + 2) == '-')
62 return 0; /* `---' is acceptable */
63 return 1;
65 if (strchr (NO_FORMAT_CHARS_START, c))
66 return 1;
67 return 0;
70 static long begin_paragraph (WEdit * edit, long p, int force)
72 int i;
73 for (i = edit->curs_line - 1; i > 0; i--) {
74 if (line_is_blank (edit, i)) {
75 i++;
76 break;
78 if (force) {
79 if (bad_line_start (edit, line_start (edit, i))) {
80 i++;
81 break;
85 return edit_move_backward (edit, edit_bol (edit, edit->curs1), edit->curs_line - i);
88 static long end_paragraph (WEdit * edit, long p, int force)
90 int i;
91 for (i = edit->curs_line + 1; i < edit->total_lines; i++) {
92 if (line_is_blank (edit, i)) {
93 i--;
94 break;
96 if (force)
97 if (bad_line_start (edit, line_start (edit, i))) {
98 i--;
99 break;
102 return edit_eol (edit, edit_move_forward (edit, edit_bol (edit, edit->curs1), i - edit->curs_line, 0));
105 static unsigned char *get_paragraph (WEdit * edit, long p, long q, int indent, int *size)
107 unsigned char *s, *t;
108 #if 0
109 t = malloc ((q - p) + 2 * (q - p) / option_word_wrap_line_length + 10);
110 #else
111 t = malloc (2 * (q - p) + 100);
112 #endif
113 if (!t)
114 return 0;
115 for (s = t; p < q; p++, s++) {
116 if (indent)
117 if (edit_get_byte (edit, p - 1) == '\n')
118 while (strchr ("\t ", edit_get_byte (edit, p)))
119 p++;
120 *s = edit_get_byte (edit, p);
122 *size = (unsigned long) s - (unsigned long) t;
123 t[*size] = '\n';
124 return t;
127 static void strip_newlines (unsigned char *t, int size)
129 unsigned char *p = t;
130 while (size--) {
131 *p = *p == '\n' ? ' ' : *p;
132 p++;
137 This is a copy of the function
138 int calc_text_pos (WEdit * edit, long b, long *q, int l)
139 in propfont.c :(
140 It calculates the number of chars in a line specified to length l in pixels
142 static inline int next_tab_pos (int x)
144 return x += tab_width - x % tab_width;
146 static int line_pixel_length (unsigned char *t, long b, int l)
148 int x = 0, c, xn = 0;
149 for (;;) {
150 c = t[b];
151 switch (c) {
152 case '\n':
153 return b;
154 case '\t':
155 xn = next_tab_pos (x);
156 break;
157 default:
158 xn = x + 1;
159 break;
161 if (xn > l)
162 break;
163 x = xn;
164 b++;
166 return b;
169 /* find the start of a word */
170 static int next_word_start (unsigned char *t, int q, int size)
172 int i;
173 for (i = q;; i++) {
174 switch (t[i]) {
175 case '\n':
176 return -1;
177 case '\t':
178 case ' ':
179 for (;; i++) {
180 if (t[i] == '\n')
181 return -1;
182 if (t[i] != ' ' && t[i] != '\t')
183 return i;
185 break;
190 /* find the start of a word */
191 static int word_start (unsigned char *t, int q, int size)
193 int i = q;
194 if (t[q] == ' ' || t[q] == '\t')
195 return next_word_start (t, q, size);
196 for (;;) {
197 int c;
198 if (!i)
199 return -1;
200 c = t[i - 1];
201 if (c == '\n')
202 return -1;
203 if (c == ' ' || c == '\t')
204 return i;
205 i--;
209 /* replaces ' ' with '\n' to properly format a paragraph */
210 static void format_this (unsigned char *t, int size, int indent)
212 int q = 0, ww;
213 strip_newlines (t, size);
214 ww = option_word_wrap_line_length * FONT_MEAN_WIDTH - indent;
215 if (ww < FONT_MEAN_WIDTH * 2)
216 ww = FONT_MEAN_WIDTH * 2;
217 for (;;) {
218 int p;
219 q = line_pixel_length (t, q, ww);
220 if (q > size)
221 break;
222 if (t[q] == '\n')
223 break;
224 p = word_start (t, q, size);
225 if (p == -1)
226 q = next_word_start (t, q, size); /* Return the end of the word if the beginning
227 of the word is at the beginning of a line
228 (i.e. a very long word) */
229 else
230 q = p;
231 if (q == -1) /* end of paragraph */
232 break;
233 if (q)
234 t[q - 1] = '\n';
238 static void replace_at (WEdit * edit, long q, int c)
240 edit_cursor_move (edit, q - edit->curs1);
241 edit_delete (edit);
242 edit_insert_ahead (edit, c);
245 void edit_insert_indent (WEdit * edit, int indent);
247 /* replaces a block of text */
248 static void put_paragraph (WEdit * edit, unsigned char *t, long p, long q, int indent, int size)
250 long cursor;
251 int i, c = 0;
252 cursor = edit->curs1;
253 if (indent)
254 while (strchr ("\t ", edit_get_byte (edit, p)))
255 p++;
256 for (i = 0; i < size; i++, p++) {
257 if (i && indent) {
258 if (t[i - 1] == '\n' && c == '\n') {
259 while (strchr ("\t ", edit_get_byte (edit, p)))
260 p++;
261 } else if (t[i - 1] == '\n') {
262 long curs;
263 edit_cursor_move (edit, p - edit->curs1);
264 curs = edit->curs1;
265 edit_insert_indent (edit, indent);
266 if (cursor >= curs)
267 cursor += edit->curs1 - p;
268 p = edit->curs1;
269 } else if (c == '\n') {
270 edit_cursor_move (edit, p - edit->curs1);
271 while (strchr ("\t ", edit_get_byte (edit, p))) {
272 edit_delete (edit);
273 if (cursor > edit->curs1)
274 cursor--;
276 p = edit->curs1;
279 c = edit_get_byte (edit, p);
280 if (c != t[i])
281 replace_at (edit, p, t[i]);
283 edit_cursor_move (edit, cursor - edit->curs1); /* restore cursor position */
286 int edit_indent_width (WEdit * edit, long p);
288 static int test_indent (WEdit * edit, long p, long q)
290 int indent;
291 indent = edit_indent_width (edit, p++);
292 if (!indent)
293 return 0;
294 for (; p < q; p++)
295 if (edit_get_byte (edit, p - 1) == '\n')
296 if (indent != edit_indent_width (edit, p))
297 return 0;
298 return indent;
301 void format_paragraph (WEdit * edit, int force)
303 long p, q;
304 int size;
305 unsigned char *t;
306 int indent = 0;
307 if (option_word_wrap_line_length < 2)
308 return;
309 if (line_is_blank (edit, edit->curs_line))
310 return;
311 p = begin_paragraph (edit, edit->curs1, force);
312 q = end_paragraph (edit, edit->curs1, force);
313 indent = test_indent (edit, p, q);
314 t = get_paragraph (edit, p, q, indent, &size);
315 if (!t)
316 return;
317 if (!force) {
318 int i;
319 if (strchr (NO_FORMAT_CHARS_START, *t)) {
320 free (t);
321 return;
323 for (i = 0; i < size - 1; i++) {
324 if (t[i] == '\n') {
325 if (strchr (NO_FORMAT_CHARS_START "\t ", t[i + 1])) {
326 free (t);
327 return;
332 format_this (t, q - p, indent);
333 put_paragraph (edit, t, p, q, indent, size);
334 free (t);