1 /* wordproc.c - word-processor mode for the editor: does dynamic
2 paragraph formatting.
3 Copyright (C) 1996-2017 Paul Sheer
4
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.
9
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.
14
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.
19 */
20
21 #include <config.h>
22 #include "edit.h"
23 #if defined (HAVE_MAD) && ! defined (MIDNIGHT) && ! defined (GTK)
24 #include "mad.h"
25 #endif
26
27 #ifdef MIDNIGHT
28 #define tab_width option_tab_spacing
29 #endif
30
31 int line_is_blank (WEdit * edit, long line);
32
33 #define NO_FORMAT_CHARS_START "-+*\\,.;:&>"
34
line_start(WEdit * edit,long line)35 static long line_start (WEdit * edit, long line)
36 {
37 static long p = -1, l = 0;
38 int c;
39 if (p == -1 || abs (l - line) > abs (edit->curs_line - line)) {
40 l = edit->curs_line;
41 p = edit->curs1;
42 }
43 if (line < l)
44 p = edit_move_backward (edit, p, l - line);
45 else if (line > l)
46 p = edit_move_forward (edit, p, line - l, 0);
47 l = line;
48 p = edit_bol (edit, p);
49 while (strchr ("\t ", c = edit_get_byte (edit, p)))
50 p++;
51 return p;
52 }
53
bad_line_start(WEdit * edit,long p)54 static int bad_line_start (WEdit * edit, long p)
55 {
56 int c;
57 c = edit_get_byte (edit, p);
58 if (c == '.') { /* `...' is acceptable */
59 if (edit_get_byte (edit, p + 1) == '.')
60 if (edit_get_byte (edit, p + 2) == '.')
61 return 0;
62 return 1;
63 }
64 if (c == '-') {
65 if (edit_get_byte (edit, p + 1) == '-')
66 if (edit_get_byte (edit, p + 2) == '-')
67 return 0; /* `---' is acceptable */
68 return 1;
69 }
70 if (strchr (NO_FORMAT_CHARS_START, c))
71 return 1;
72 return 0;
73 }
74
begin_paragraph(WEdit * edit,long p,int force)75 static long begin_paragraph (WEdit * edit, long p, int force)
76 {
77 int i;
78 for (i = edit->curs_line - 1; i > 0; i--) {
79 if (line_is_blank (edit, i)) {
80 i++;
81 break;
82 }
83 if (force) {
84 if (bad_line_start (edit, line_start (edit, i))) {
85 i++;
86 break;
87 }
88 }
89 }
90 return edit_move_backward (edit, edit_bol (edit, edit->curs1), edit->curs_line - i);
91 }
92
end_paragraph(WEdit * edit,long p,int force)93 static long end_paragraph (WEdit * edit, long p, int force)
94 {
95 int i;
96 for (i = edit->curs_line + 1; i < edit->total_lines; i++) {
97 if (line_is_blank (edit, i)) {
98 i--;
99 break;
100 }
101 if (force)
102 if (bad_line_start (edit, line_start (edit, i))) {
103 i--;
104 break;
105 }
106 }
107 return edit_eol (edit, edit_move_forward (edit, edit_bol (edit, edit->curs1), i - edit->curs_line, 0));
108 }
109
get_paragraph(WEdit * edit,long p,long q,int indent,int * size)110 static unsigned char *get_paragraph (WEdit * edit, long p, long q, int indent, int *size)
111 {
112 unsigned char *s, *t;
113 #if 0
114 t = malloc ((q - p) + 2 * (q - p) / option_word_wrap_line_length + 10);
115 #else
116 t = malloc (2 * (q - p) + 1024);
117 #endif
118 if (!t)
119 return 0;
120 for (s = t; p < q; p++, s++) {
121 if (indent)
122 if (edit_get_byte (edit, p - 1) == '\n')
123 while (strchr ("\t ", edit_get_byte (edit, p)))
124 p++;
125 *s = edit_get_byte (edit, p);
126 }
127 *size = (unsigned long) s - (unsigned long) t;
128 t[*size] = '\n';
129 return t;
130 }
131
strip_newlines(unsigned char * t,int size)132 static void strip_newlines (unsigned char *t, int size)
133 {
134 unsigned char *p = t;
135 while (size--) {
136 *p = *p == '\n' ? ' ' : *p;
137 p++;
138 }
139 }
140
141 #ifndef MIDNIGHT
142 int edit_width_of_long_printable (int c);
143 #endif
144 /*
145 This is a copy of the function
146 int calc_text_pos (WEdit * edit, long b, long *q, int l)
147 in propfont.c :(
148 It calculates the number of chars in a line specified to length l in pixels
149 */
150 extern int tab_width;
next_tab_pos(int x)151 static inline int next_tab_pos (int x)
152 {
153 return x += tab_width - x % tab_width;
154 }
line_pixel_length(unsigned char * t,long b,int l)155 static int line_pixel_length (unsigned char *t, long b, int l)
156 {
157 int x = 0, c, xn = 0;
158 for (;;) {
159 c = t[b];
160 switch (c) {
161 case '\n':
162 return b;
163 case '\t':
164 xn = next_tab_pos (x);
165 break;
166 default:
167 #ifdef MIDNIGHT
168 xn = x + 1;
169 #else
170 xn = x + edit_width_of_long_printable (c);
171 #endif
172 break;
173 }
174 if (xn > l)
175 break;
176 x = xn;
177 b++;
178 }
179 return b;
180 }
181
182 /* find the start of a word */
next_word_start(unsigned char * t,int q,int size)183 static int next_word_start (unsigned char *t, int q, int size)
184 {
185 int i;
186 for (i = q;; i++) {
187 switch (t[i]) {
188 case '\n':
189 return -1;
190 case '\t':
191 case ' ':
192 for (;; i++) {
193 if (t[i] == '\n')
194 return -1;
195 if (t[i] != ' ' && t[i] != '\t')
196 return i;
197 }
198 break;
199 }
200 }
201 }
202
203 /* find the start of a word */
word_start(unsigned char * t,int q,int size)204 static int word_start (unsigned char *t, int q, int size)
205 {
206 int i = q;
207 if (t[q] == ' ' || t[q] == '\t')
208 return next_word_start (t, q, size);
209 for (;;) {
210 int c;
211 if (!i)
212 return -1;
213 c = t[i - 1];
214 if (c == '\n')
215 return -1;
216 if (c == ' ' || c == '\t')
217 return i;
218 i--;
219 }
220 }
221
222 /* replaces ' ' with '\n' to properly format a paragraph */
format_this(unsigned char * t,int size,int indent)223 static void format_this (unsigned char *t, int size, int indent)
224 {
225 int q = 0, ww;
226 strip_newlines (t, size);
227 ww = option_word_wrap_line_length * FONT_MEAN_WIDTH - indent;
228 if (ww < FONT_MEAN_WIDTH * 2)
229 ww = FONT_MEAN_WIDTH * 2;
230 for (;;) {
231 int p;
232 q = line_pixel_length (t, q, ww);
233 if (q > size)
234 break;
235 if (t[q] == '\n')
236 break;
237 p = word_start (t, q, size);
238 if (p == -1)
239 q = next_word_start (t, q, size); /* Return the end of the word if the beginning
240 of the word is at the beginning of a line
241 (i.e. a very long word) */
242 else
243 q = p;
244 if (q == -1) /* end of paragraph */
245 break;
246 if (q)
247 t[q - 1] = '\n';
248 }
249 }
250
replace_at(WEdit * edit,long q,int c)251 static void replace_at (WEdit * edit, long q, int c)
252 {
253 edit_cursor_move (edit, q - edit->curs1);
254 edit_delete (edit);
255 edit_insert_ahead (edit, c);
256 }
257
258 void edit_insert_indent (WEdit * edit, int indent);
259
260 /* replaces a block of text */
put_paragraph(WEdit * edit,unsigned char * t,long p,long q,int indent,int size)261 static void put_paragraph (WEdit * edit, unsigned char *t, long p, long q, int indent, int size)
262 {
263 long cursor;
264 int i, c = 0;
265 cursor = edit->curs1;
266 if (indent)
267 while (strchr ("\t ", edit_get_byte (edit, p)))
268 p++;
269 for (i = 0; i < size; i++, p++) {
270 if (i && indent) {
271 if (t[i - 1] == '\n' && c == '\n') {
272 while (strchr ("\t ", edit_get_byte (edit, p)))
273 p++;
274 } else if (t[i - 1] == '\n') {
275 long curs;
276 edit_cursor_move (edit, p - edit->curs1);
277 curs = edit->curs1;
278 edit_insert_indent (edit, indent);
279 if (cursor >= curs)
280 cursor += edit->curs1 - p;
281 p = edit->curs1;
282 } else if (c == '\n') {
283 edit_cursor_move (edit, p - edit->curs1);
284 while (strchr ("\t ", edit_get_byte (edit, p))) {
285 edit_delete (edit);
286 if (cursor > edit->curs1)
287 cursor--;
288 }
289 p = edit->curs1;
290 }
291 }
292 c = edit_get_byte (edit, p);
293 if (c != t[i])
294 replace_at (edit, p, t[i]);
295 }
296 edit_cursor_move (edit, cursor - edit->curs1); /* restore cursor position */
297 }
298
299 int edit_indent_width (WEdit * edit, long p);
300
test_indent(WEdit * edit,long p,long q)301 static int test_indent (WEdit * edit, long p, long q)
302 {
303 int indent;
304 indent = edit_indent_width (edit, p++);
305 if (!indent)
306 return 0;
307 for (; p < q; p++)
308 if (edit_get_byte (edit, p - 1) == '\n')
309 if (indent != edit_indent_width (edit, p))
310 return 0;
311 return indent;
312 }
313
new_behaviour_message(WEdit * edit)314 static void new_behaviour_message (WEdit * edit)
315 {
316 char *filename;
317 int fd;
318 filename = catstrs (home_dir, PARMESS_FILE, NULL);
319 if ((fd = open (filename, O_RDONLY)) < 0) {
320 edit_message_dialog (_(" Format Paragraph "), "\
321 This message will not be displayed again\n\
322 \n\
323 The new \"format paragraph\" command will format\n\
324 text inside the selected region if one is highlighted.\n\
325 Otherwise it would try to find the bounds of the\n\
326 current paragraph using heuristics.");
327 fd = creat (filename, 0400);
328 }
329 close (fd);
330 }
331
format_paragraph(WEdit * edit,int force)332 void format_paragraph (WEdit * edit, int force)
333 {
334 long p, q;
335 int size;
336 unsigned char *t;
337 int indent = 0;
338 if (option_word_wrap_line_length < 2)
339 return;
340 if (force)
341 new_behaviour_message (edit);
342 if (force && !eval_marks (edit, &p, &q)) {
343 p = edit_bol (edit, p);
344 q = edit_eol (edit, q);
345 } else {
346 if (line_is_blank (edit, edit->curs_line))
347 return;
348 p = begin_paragraph (edit, edit->curs1, force);
349 q = end_paragraph (edit, edit->curs1, force);
350 }
351 CPushFont ("editor", 0);
352 indent = test_indent (edit, p, q);
353 t = get_paragraph (edit, p, q, indent, &size);
354 if (!t)
355 return;
356 if (!force) {
357 int i;
358 if (strchr (NO_FORMAT_CHARS_START, *t)) {
359 free (t);
360 return;
361 }
362 for (i = 0; i < size - 1; i++) {
363 if (t[i] == '\n') {
364 if (strchr (NO_FORMAT_CHARS_START "\t ", t[i + 1])) {
365 free (t);
366 return;
367 }
368 }
369 }
370 }
371 format_this (t, q - p, indent);
372 put_paragraph (edit, t, p, q, indent, size);
373 free (t);
374 CPopFont ();
375 }
376
377
378
379
380
381
382
383
384
385
386