wordproc.c 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375
  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. /** \file
  18. * \brief Source: word-processor mode for the editor: does dynamic paragraph formatting
  19. * \author Paul Sheer
  20. * \date 1996
  21. */
  22. #include <config.h>
  23. #include <stdio.h>
  24. #include <stdarg.h>
  25. #include <sys/types.h>
  26. #include <unistd.h>
  27. #include <string.h>
  28. #include <ctype.h>
  29. #include <errno.h>
  30. #include <sys/stat.h>
  31. #include <stdlib.h>
  32. #include "../src/global.h"
  33. #include "edit-impl.h"
  34. #include "edit-widget.h"
  35. #include "../src/main.h" /* option_tab_spacing */
  36. #define tab_width option_tab_spacing
  37. #define NO_FORMAT_CHARS_START "-+*\\,.;:&>"
  38. #define FONT_MEAN_WIDTH 1
  39. static long
  40. line_start (WEdit *edit, long line)
  41. {
  42. long p, l;
  43. l = edit->curs_line;
  44. p = edit->curs1;
  45. if (line < l)
  46. p = edit_move_backward (edit, p, l - line);
  47. else if (line > l)
  48. p = edit_move_forward (edit, p, line - l, 0);
  49. p = edit_bol (edit, p);
  50. while (strchr ("\t ", edit_get_byte (edit, p)))
  51. p++;
  52. return p;
  53. }
  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. /*
  75. * Find the start of the current paragraph for the purpose of formatting.
  76. * Return position in the file.
  77. */
  78. static long
  79. begin_paragraph (WEdit *edit, int force)
  80. {
  81. int i;
  82. for (i = edit->curs_line - 1; i >= 0; i--) {
  83. if (line_is_blank (edit, i)) {
  84. i++;
  85. break;
  86. }
  87. if (force) {
  88. if (bad_line_start (edit, line_start (edit, i))) {
  89. i++;
  90. break;
  91. }
  92. }
  93. }
  94. return edit_move_backward (edit, edit_bol (edit, edit->curs1),
  95. edit->curs_line - i);
  96. }
  97. /*
  98. * Find the end of the current paragraph for the purpose of formatting.
  99. * Return position in the file.
  100. */
  101. static long
  102. end_paragraph (WEdit *edit, int force)
  103. {
  104. int i;
  105. for (i = edit->curs_line + 1; i <= edit->total_lines; i++) {
  106. if (line_is_blank (edit, i)) {
  107. i--;
  108. break;
  109. }
  110. if (force)
  111. if (bad_line_start (edit, line_start (edit, i))) {
  112. i--;
  113. break;
  114. }
  115. }
  116. return edit_eol (edit,
  117. edit_move_forward (edit, edit_bol (edit, edit->curs1),
  118. i - edit->curs_line, 0));
  119. }
  120. static unsigned char *
  121. get_paragraph (WEdit *edit, long p, long q, int indent, int *size)
  122. {
  123. unsigned char *s, *t;
  124. #if 0
  125. t = g_malloc ((q - p) + 2 * (q - p) / option_word_wrap_line_length +
  126. 10);
  127. #else
  128. t = g_malloc (2 * (q - p) + 100);
  129. #endif
  130. if (!t)
  131. return 0;
  132. for (s = t; p < q; p++, s++) {
  133. if (indent)
  134. if (edit_get_byte (edit, p - 1) == '\n')
  135. while (strchr ("\t ", edit_get_byte (edit, p)))
  136. p++;
  137. *s = edit_get_byte (edit, p);
  138. }
  139. *size = (unsigned long) s - (unsigned long) t;
  140. t[*size] = '\n';
  141. return t;
  142. }
  143. static void strip_newlines (unsigned char *t, int size)
  144. {
  145. unsigned char *p = t;
  146. while (size--) {
  147. *p = *p == '\n' ? ' ' : *p;
  148. p++;
  149. }
  150. }
  151. /*
  152. This is a copy of the function
  153. int calc_text_pos (WEdit * edit, long b, long *q, int l)
  154. in propfont.c :(
  155. It calculates the number of chars in a line specified to length l in pixels
  156. */
  157. static inline int next_tab_pos (int x)
  158. {
  159. return x += tab_width - x % tab_width;
  160. }
  161. static int line_pixel_length (unsigned char *t, long b, int l)
  162. {
  163. int x = 0, c, xn = 0;
  164. for (;;) {
  165. c = t[b];
  166. switch (c) {
  167. case '\n':
  168. return b;
  169. case '\t':
  170. xn = next_tab_pos (x);
  171. break;
  172. default:
  173. xn = x + 1;
  174. break;
  175. }
  176. if (xn > l)
  177. break;
  178. x = xn;
  179. b++;
  180. }
  181. return b;
  182. }
  183. static int
  184. next_word_start (unsigned char *t, int q, int size)
  185. {
  186. int i;
  187. int saw_ws = 0;
  188. for (i = q; i < size; i++) {
  189. switch (t[i]) {
  190. case '\n':
  191. return -1;
  192. case '\t':
  193. case ' ':
  194. saw_ws = 1;
  195. break;
  196. default:
  197. if (saw_ws != 0)
  198. return i;
  199. break;
  200. }
  201. }
  202. return -1;
  203. }
  204. /* find the start of a word */
  205. static int
  206. word_start (unsigned char *t, int q, int size)
  207. {
  208. int i = q;
  209. if (t[q] == ' ' || t[q] == '\t')
  210. return next_word_start (t, q, size);
  211. for (;;) {
  212. int c;
  213. if (!i)
  214. return -1;
  215. c = t[i - 1];
  216. if (c == '\n')
  217. return -1;
  218. if (c == ' ' || c == '\t')
  219. return i;
  220. i--;
  221. }
  222. }
  223. /* replaces ' ' with '\n' to properly format a paragraph */
  224. static void format_this (unsigned char *t, int size, int indent)
  225. {
  226. int q = 0, ww;
  227. strip_newlines (t, size);
  228. ww = option_word_wrap_line_length * FONT_MEAN_WIDTH - indent;
  229. if (ww < FONT_MEAN_WIDTH * 2)
  230. ww = FONT_MEAN_WIDTH * 2;
  231. for (;;) {
  232. int p;
  233. q = line_pixel_length (t, q, ww);
  234. if (q > size)
  235. break;
  236. if (t[q] == '\n')
  237. break;
  238. p = word_start (t, q, size);
  239. if (p == -1)
  240. q = next_word_start (t, q, size); /* Return the end of the word if the beginning
  241. of the word is at the beginning of a line
  242. (i.e. a very long word) */
  243. else
  244. q = p;
  245. if (q == -1) /* end of paragraph */
  246. break;
  247. if (q)
  248. t[q - 1] = '\n';
  249. }
  250. }
  251. static void replace_at (WEdit * edit, long q, int c)
  252. {
  253. edit_cursor_move (edit, q - edit->curs1);
  254. edit_delete (edit, 1);
  255. edit_insert_ahead (edit, c);
  256. }
  257. /* replaces a block of text */
  258. static void
  259. put_paragraph (WEdit * edit, unsigned char *t, long p, int indent, int size)
  260. {
  261. long cursor;
  262. int i, c = 0;
  263. cursor = edit->curs1;
  264. if (indent)
  265. while (strchr ("\t ", edit_get_byte (edit, p)))
  266. p++;
  267. for (i = 0; i < size; i++, p++) {
  268. if (i && indent) {
  269. if (t[i - 1] == '\n' && c == '\n') {
  270. while (strchr ("\t ", edit_get_byte (edit, p)))
  271. p++;
  272. } else if (t[i - 1] == '\n') {
  273. long curs;
  274. edit_cursor_move (edit, p - edit->curs1);
  275. curs = edit->curs1;
  276. edit_insert_indent (edit, indent);
  277. if (cursor >= curs)
  278. cursor += edit->curs1 - p;
  279. p = edit->curs1;
  280. } else if (c == '\n') {
  281. edit_cursor_move (edit, p - edit->curs1);
  282. while (strchr ("\t ", edit_get_byte (edit, p))) {
  283. edit_delete (edit, 1);
  284. if (cursor > edit->curs1)
  285. cursor--;
  286. }
  287. p = edit->curs1;
  288. }
  289. }
  290. c = edit_get_byte (edit, p);
  291. if (c != t[i])
  292. replace_at (edit, p, t[i]);
  293. }
  294. edit_cursor_move (edit, cursor - edit->curs1); /* restore cursor position */
  295. }
  296. static int test_indent (WEdit * edit, long p, long q)
  297. {
  298. int indent;
  299. indent = edit_indent_width (edit, p++);
  300. if (!indent)
  301. return 0;
  302. for (; p < q; p++)
  303. if (edit_get_byte (edit, p - 1) == '\n')
  304. if (indent != edit_indent_width (edit, p))
  305. return 0;
  306. return indent;
  307. }
  308. void
  309. format_paragraph (WEdit *edit, int force)
  310. {
  311. long p, q;
  312. int size;
  313. unsigned char *t;
  314. int indent = 0;
  315. if (option_word_wrap_line_length < 2)
  316. return;
  317. if (line_is_blank (edit, edit->curs_line))
  318. return;
  319. p = begin_paragraph (edit, force);
  320. q = end_paragraph (edit, force);
  321. indent = test_indent (edit, p, q);
  322. t = get_paragraph (edit, p, q, indent, &size);
  323. if (!t)
  324. return;
  325. if (!force) {
  326. int i;
  327. if (strchr (NO_FORMAT_CHARS_START, *t)) {
  328. g_free (t);
  329. return;
  330. }
  331. for (i = 0; i < size - 1; i++) {
  332. if (t[i] == '\n') {
  333. if (strchr (NO_FORMAT_CHARS_START "\t ", t[i + 1])) {
  334. g_free (t);
  335. return;
  336. }
  337. }
  338. }
  339. }
  340. format_this (t, q - p, indent);
  341. put_paragraph (edit, t, p, indent, size);
  342. g_free (t);
  343. /* Scroll left as much as possible to show the formatted paragraph */
  344. edit_scroll_left (edit, -edit->start_col);
  345. }