wordproc.c 8.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370
  1. /* wordproc.c - word-processor mode for the editor: does dynamic
  2. paragraph formatting.
  3. Copyright (C) 1996 Paul Sheer
  4. This program is free software; you can redistribute it and/or modify
  5. it under the terms of the GNU General Public License as published by
  6. the Free Software Foundation; either version 2 of the License, or
  7. (at your option) any later version.
  8. This program is distributed in the hope that it will be useful,
  9. but WITHOUT ANY WARRANTY; without even the implied warranty of
  10. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  11. GNU General Public License for more details.
  12. You should have received a copy of the GNU General Public License
  13. along with this program; if not, write to the Free Software
  14. Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
  15. 02110-1301, USA.
  16. */
  17. #include <config.h>
  18. #include <stdio.h>
  19. #include <stdarg.h>
  20. #include <sys/types.h>
  21. #ifdef HAVE_UNISTD_H
  22. # include <unistd.h>
  23. #endif
  24. #include <string.h>
  25. #include <ctype.h>
  26. #include <errno.h>
  27. #include <sys/stat.h>
  28. #include <stdlib.h>
  29. #include "../src/global.h"
  30. #include "edit.h"
  31. #include "edit-widget.h"
  32. #define tab_width option_tab_spacing
  33. #define NO_FORMAT_CHARS_START "-+*\\,.;:&>"
  34. #define FONT_MEAN_WIDTH 1
  35. static long
  36. line_start (WEdit *edit, long line)
  37. {
  38. long p, l;
  39. l = edit->curs_line;
  40. p = edit->curs1;
  41. if (line < l)
  42. p = edit_move_backward (edit, p, l - line);
  43. else if (line > l)
  44. p = edit_move_forward (edit, p, line - l, 0);
  45. p = edit_bol (edit, p);
  46. while (strchr ("\t ", edit_get_byte (edit, p)))
  47. p++;
  48. return p;
  49. }
  50. static int bad_line_start (WEdit * edit, long p)
  51. {
  52. int c;
  53. c = edit_get_byte (edit, p);
  54. if (c == '.') { /* `...' is acceptable */
  55. if (edit_get_byte (edit, p + 1) == '.')
  56. if (edit_get_byte (edit, p + 2) == '.')
  57. return 0;
  58. return 1;
  59. }
  60. if (c == '-') {
  61. if (edit_get_byte (edit, p + 1) == '-')
  62. if (edit_get_byte (edit, p + 2) == '-')
  63. return 0; /* `---' is acceptable */
  64. return 1;
  65. }
  66. if (strchr (NO_FORMAT_CHARS_START, c))
  67. return 1;
  68. return 0;
  69. }
  70. /*
  71. * Find the start of the current paragraph for the purpose of formatting.
  72. * Return position in the file.
  73. */
  74. static long
  75. begin_paragraph (WEdit *edit, 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),
  91. edit->curs_line - i);
  92. }
  93. /*
  94. * Find the end of the current paragraph for the purpose of formatting.
  95. * Return position in the file.
  96. */
  97. static long
  98. end_paragraph (WEdit *edit, int force)
  99. {
  100. int i;
  101. for (i = edit->curs_line + 1; i <= edit->total_lines; i++) {
  102. if (line_is_blank (edit, i)) {
  103. i--;
  104. break;
  105. }
  106. if (force)
  107. if (bad_line_start (edit, line_start (edit, i))) {
  108. i--;
  109. break;
  110. }
  111. }
  112. return edit_eol (edit,
  113. edit_move_forward (edit, edit_bol (edit, edit->curs1),
  114. i - edit->curs_line, 0));
  115. }
  116. static unsigned char *
  117. get_paragraph (WEdit *edit, long p, long q, int indent, int *size)
  118. {
  119. unsigned char *s, *t;
  120. #if 0
  121. t = g_malloc ((q - p) + 2 * (q - p) / option_word_wrap_line_length +
  122. 10);
  123. #else
  124. t = g_malloc (2 * (q - p) + 100);
  125. #endif
  126. if (!t)
  127. return 0;
  128. for (s = t; p < q; p++, s++) {
  129. if (indent)
  130. if (edit_get_byte (edit, p - 1) == '\n')
  131. while (strchr ("\t ", edit_get_byte (edit, p)))
  132. p++;
  133. *s = edit_get_byte (edit, p);
  134. }
  135. *size = (unsigned long) s - (unsigned long) t;
  136. t[*size] = '\n';
  137. return t;
  138. }
  139. static void strip_newlines (unsigned char *t, int size)
  140. {
  141. unsigned char *p = t;
  142. while (size--) {
  143. *p = *p == '\n' ? ' ' : *p;
  144. p++;
  145. }
  146. }
  147. /*
  148. This is a copy of the function
  149. int calc_text_pos (WEdit * edit, long b, long *q, int l)
  150. in propfont.c :(
  151. It calculates the number of chars in a line specified to length l in pixels
  152. */
  153. static inline int next_tab_pos (int x)
  154. {
  155. return x += tab_width - x % tab_width;
  156. }
  157. static int line_pixel_length (unsigned char *t, long b, int l)
  158. {
  159. int x = 0, c, xn = 0;
  160. for (;;) {
  161. c = t[b];
  162. switch (c) {
  163. case '\n':
  164. return b;
  165. case '\t':
  166. xn = next_tab_pos (x);
  167. break;
  168. default:
  169. xn = x + 1;
  170. break;
  171. }
  172. if (xn > l)
  173. break;
  174. x = xn;
  175. b++;
  176. }
  177. return b;
  178. }
  179. static int
  180. next_word_start (unsigned char *t, int q, int size)
  181. {
  182. int i;
  183. int saw_ws = 0;
  184. for (i = q; i < size; i++) {
  185. switch (t[i]) {
  186. case '\n':
  187. return -1;
  188. case '\t':
  189. case ' ':
  190. saw_ws = 1;
  191. break;
  192. default:
  193. if (saw_ws != 0)
  194. return i;
  195. break;
  196. }
  197. }
  198. return -1;
  199. }
  200. /* find the start of a word */
  201. static int
  202. word_start (unsigned char *t, int q, int size)
  203. {
  204. int i = q;
  205. if (t[q] == ' ' || t[q] == '\t')
  206. return next_word_start (t, q, size);
  207. for (;;) {
  208. int c;
  209. if (!i)
  210. return -1;
  211. c = t[i - 1];
  212. if (c == '\n')
  213. return -1;
  214. if (c == ' ' || c == '\t')
  215. return i;
  216. i--;
  217. }
  218. }
  219. /* replaces ' ' with '\n' to properly format a paragraph */
  220. static void format_this (unsigned char *t, int size, int indent)
  221. {
  222. int q = 0, ww;
  223. strip_newlines (t, size);
  224. ww = option_word_wrap_line_length * FONT_MEAN_WIDTH - indent;
  225. if (ww < FONT_MEAN_WIDTH * 2)
  226. ww = FONT_MEAN_WIDTH * 2;
  227. for (;;) {
  228. int p;
  229. q = line_pixel_length (t, q, ww);
  230. if (q > size)
  231. break;
  232. if (t[q] == '\n')
  233. break;
  234. p = word_start (t, q, size);
  235. if (p == -1)
  236. q = next_word_start (t, q, size); /* Return the end of the word if the beginning
  237. of the word is at the beginning of a line
  238. (i.e. a very long word) */
  239. else
  240. q = p;
  241. if (q == -1) /* end of paragraph */
  242. break;
  243. if (q)
  244. t[q - 1] = '\n';
  245. }
  246. }
  247. static void replace_at (WEdit * edit, long q, int c)
  248. {
  249. edit_cursor_move (edit, q - edit->curs1);
  250. edit_delete (edit);
  251. edit_insert_ahead (edit, c);
  252. }
  253. /* replaces a block of text */
  254. static void
  255. put_paragraph (WEdit * edit, unsigned char *t, long p, int indent, int size)
  256. {
  257. long cursor;
  258. int i, c = 0;
  259. cursor = edit->curs1;
  260. if (indent)
  261. while (strchr ("\t ", edit_get_byte (edit, p)))
  262. p++;
  263. for (i = 0; i < size; i++, p++) {
  264. if (i && indent) {
  265. if (t[i - 1] == '\n' && c == '\n') {
  266. while (strchr ("\t ", edit_get_byte (edit, p)))
  267. p++;
  268. } else if (t[i - 1] == '\n') {
  269. long curs;
  270. edit_cursor_move (edit, p - edit->curs1);
  271. curs = edit->curs1;
  272. edit_insert_indent (edit, indent);
  273. if (cursor >= curs)
  274. cursor += edit->curs1 - p;
  275. p = edit->curs1;
  276. } else if (c == '\n') {
  277. edit_cursor_move (edit, p - edit->curs1);
  278. while (strchr ("\t ", edit_get_byte (edit, p))) {
  279. edit_delete (edit);
  280. if (cursor > edit->curs1)
  281. cursor--;
  282. }
  283. p = edit->curs1;
  284. }
  285. }
  286. c = edit_get_byte (edit, p);
  287. if (c != t[i])
  288. replace_at (edit, p, t[i]);
  289. }
  290. edit_cursor_move (edit, cursor - edit->curs1); /* restore cursor position */
  291. }
  292. static int test_indent (WEdit * edit, long p, long q)
  293. {
  294. int indent;
  295. indent = edit_indent_width (edit, p++);
  296. if (!indent)
  297. return 0;
  298. for (; p < q; p++)
  299. if (edit_get_byte (edit, p - 1) == '\n')
  300. if (indent != edit_indent_width (edit, p))
  301. return 0;
  302. return indent;
  303. }
  304. void
  305. format_paragraph (WEdit *edit, int force)
  306. {
  307. long p, q;
  308. int size;
  309. unsigned char *t;
  310. int indent = 0;
  311. if (option_word_wrap_line_length < 2)
  312. return;
  313. if (line_is_blank (edit, edit->curs_line))
  314. return;
  315. p = begin_paragraph (edit, force);
  316. q = end_paragraph (edit, force);
  317. indent = test_indent (edit, p, q);
  318. t = get_paragraph (edit, p, q, indent, &size);
  319. if (!t)
  320. return;
  321. if (!force) {
  322. int i;
  323. if (strchr (NO_FORMAT_CHARS_START, *t)) {
  324. g_free (t);
  325. return;
  326. }
  327. for (i = 0; i < size - 1; i++) {
  328. if (t[i] == '\n') {
  329. if (strchr (NO_FORMAT_CHARS_START "\t ", t[i + 1])) {
  330. g_free (t);
  331. return;
  332. }
  333. }
  334. }
  335. }
  336. format_this (t, q - p, indent);
  337. put_paragraph (edit, t, p, indent, size);
  338. g_free (t);
  339. /* Scroll left as much as possible to show the formatted paragraph */
  340. edit_scroll_left (edit, -edit->start_col);
  341. }