wordproc.c 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413
  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. #ifndef UTF8
  34. #define NO_FORMAT_CHARS_START "-+*\\,.;:&>"
  35. #else /* UTF8 */
  36. #define NO_FORMAT_CHARS_START L"-+*\\,.;:&>"
  37. #endif /* UTF8 */
  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. #ifndef UTF8
  51. while (strchr ("\t ", edit_get_byte (edit, p)))
  52. #else /* UTF8 */
  53. while (wcschr (L"\t ", edit_get_byte (edit, p)))
  54. #endif /* UTF8 */
  55. p++;
  56. return p;
  57. }
  58. static int bad_line_start (WEdit * edit, long p)
  59. {
  60. mc_wint_t c;
  61. c = edit_get_byte (edit, p);
  62. if (c == '.') { /* `...' is acceptable */
  63. if (edit_get_byte (edit, p + 1) == '.')
  64. if (edit_get_byte (edit, p + 2) == '.')
  65. return 0;
  66. return 1;
  67. }
  68. if (c == '-') {
  69. if (edit_get_byte (edit, p + 1) == '-')
  70. if (edit_get_byte (edit, p + 2) == '-')
  71. return 0; /* `---' is acceptable */
  72. return 1;
  73. }
  74. #ifndef UTF8
  75. if (strchr (NO_FORMAT_CHARS_START, c))
  76. #else /* UTF8 */
  77. if (wcschr (NO_FORMAT_CHARS_START, c))
  78. #endif /* UTF8 */
  79. return 1;
  80. return 0;
  81. }
  82. /*
  83. * Find the start of the current paragraph for the purpose of formatting.
  84. * Return position in the file.
  85. */
  86. static long
  87. begin_paragraph (WEdit *edit, int force)
  88. {
  89. int i;
  90. for (i = edit->curs_line - 1; i >= 0; i--) {
  91. if (line_is_blank (edit, i)) {
  92. i++;
  93. break;
  94. }
  95. if (force) {
  96. if (bad_line_start (edit, line_start (edit, i))) {
  97. i++;
  98. break;
  99. }
  100. }
  101. }
  102. return edit_move_backward (edit, edit_bol (edit, edit->curs1),
  103. edit->curs_line - i);
  104. }
  105. /*
  106. * Find the end of the current paragraph for the purpose of formatting.
  107. * Return position in the file.
  108. */
  109. static long
  110. end_paragraph (WEdit *edit, int force)
  111. {
  112. int i;
  113. for (i = edit->curs_line + 1; i <= edit->total_lines; i++) {
  114. if (line_is_blank (edit, i)) {
  115. i--;
  116. break;
  117. }
  118. if (force)
  119. if (bad_line_start (edit, line_start (edit, i))) {
  120. i--;
  121. break;
  122. }
  123. }
  124. return edit_eol (edit,
  125. edit_move_forward (edit, edit_bol (edit, edit->curs1),
  126. i - edit->curs_line, 0));
  127. }
  128. static mc_wchar_t *
  129. get_paragraph (WEdit *edit, long p, long q, int indent, int *size)
  130. {
  131. mc_wchar_t *s, *t;
  132. #if 0
  133. t = g_malloc (((q - p) + 2 * (q - p) / option_word_wrap_line_length +
  134. 10) * sizeof(mc_wchar_t));
  135. #else
  136. t = g_malloc ((2 * (q - p) + 100) * sizeof(mc_wchar_t));
  137. #endif
  138. if (!t)
  139. return 0;
  140. for (s = t; p < q; p++, s++) {
  141. if (indent)
  142. if (edit_get_byte (edit, p - 1) == '\n')
  143. #ifndef UTF8
  144. while (strchr ("\t ", edit_get_byte (edit, p)))
  145. #else /* UTF8 */
  146. while (wcschr (L"\t ", edit_get_byte (edit, p)))
  147. #endif /* UTF8 */
  148. p++;
  149. *s = edit_get_byte (edit, p);
  150. }
  151. *size = s - t;
  152. t[*size] = '\n';
  153. return t;
  154. }
  155. static void strip_newlines (mc_wchar_t *t, int size)
  156. {
  157. mc_wchar_t *p = t;
  158. while (size--) {
  159. *p = *p == '\n' ? ' ' : *p;
  160. p++;
  161. }
  162. }
  163. /*
  164. This is a copy of the function
  165. int calc_text_pos (WEdit * edit, long b, long *q, int l)
  166. in propfont.c :(
  167. It calculates the number of chars in a line specified to length l in pixels
  168. */
  169. static inline int next_tab_pos (int x)
  170. {
  171. return x += tab_width - x % tab_width;
  172. }
  173. static int line_pixel_length (mc_wchar_t *t, long b, int l)
  174. {
  175. int x = 0, c, xn = 0;
  176. for (;;) {
  177. c = t[b];
  178. switch (c) {
  179. case '\n':
  180. return b;
  181. case '\t':
  182. xn = next_tab_pos (x);
  183. break;
  184. default:
  185. xn = x + 1;
  186. break;
  187. }
  188. if (xn > l)
  189. break;
  190. x = xn;
  191. b++;
  192. }
  193. return b;
  194. }
  195. static int
  196. next_word_start (mc_wchar_t *t, int q, int size)
  197. {
  198. int i;
  199. int saw_ws = 0;
  200. for (i = q; i < size; i++) {
  201. switch (t[i]) {
  202. case '\n':
  203. return -1;
  204. case '\t':
  205. case ' ':
  206. saw_ws = 1;
  207. break;
  208. default:
  209. if (saw_ws != 0)
  210. return i;
  211. break;
  212. }
  213. }
  214. return -1;
  215. }
  216. /* find the start of a word */
  217. static int
  218. word_start (mc_wchar_t *t, int q, int size)
  219. {
  220. int i = q;
  221. if (t[q] == ' ' || t[q] == '\t')
  222. return next_word_start (t, q, size);
  223. for (;;) {
  224. int c;
  225. if (!i)
  226. return -1;
  227. c = t[i - 1];
  228. if (c == '\n')
  229. return -1;
  230. if (c == ' ' || c == '\t')
  231. return i;
  232. i--;
  233. }
  234. }
  235. /* replaces ' ' with '\n' to properly format a paragraph */
  236. static void format_this (mc_wchar_t *t, int size, int indent)
  237. {
  238. int q = 0, ww;
  239. strip_newlines (t, size);
  240. ww = option_word_wrap_line_length * FONT_MEAN_WIDTH - indent;
  241. if (ww < FONT_MEAN_WIDTH * 2)
  242. ww = FONT_MEAN_WIDTH * 2;
  243. for (;;) {
  244. int p;
  245. q = line_pixel_length (t, q, ww);
  246. if (q > size)
  247. break;
  248. if (t[q] == '\n')
  249. break;
  250. p = word_start (t, q, size);
  251. if (p == -1)
  252. q = next_word_start (t, q, size); /* Return the end of the word if the beginning
  253. of the word is at the beginning of a line
  254. (i.e. a very long word) */
  255. else
  256. q = p;
  257. if (q == -1) /* end of paragraph */
  258. break;
  259. if (q)
  260. t[q - 1] = '\n';
  261. }
  262. }
  263. static void replace_at (WEdit * edit, long q, mc_wint_t c)
  264. {
  265. edit_cursor_move (edit, q - edit->curs1);
  266. edit_delete (edit);
  267. edit_insert_ahead (edit, c);
  268. }
  269. /* replaces a block of text */
  270. static void
  271. put_paragraph (WEdit * edit, mc_wchar_t *t, long p, int indent, int size)
  272. {
  273. long cursor;
  274. int i;
  275. mc_wchar_t c = 0;
  276. cursor = edit->curs1;
  277. if (indent)
  278. #ifndef UTF8
  279. while (strchr ("\t ", edit_get_byte (edit, p)))
  280. #else /* UTF8 */
  281. while (wcschr (L"\t ", edit_get_byte (edit, p)))
  282. #endif /* UTF8 */
  283. p++;
  284. for (i = 0; i < size; i++, p++) {
  285. if (i && indent) {
  286. if (t[i - 1] == '\n' && c == '\n') {
  287. #ifndef UTF8
  288. while (strchr ("\t ", edit_get_byte (edit, p)))
  289. #else /* UTF8 */
  290. while (wcschr (L"\t ", edit_get_byte (edit, p)))
  291. #endif /* UTF8 */
  292. p++;
  293. } else if (t[i - 1] == '\n') {
  294. long curs;
  295. edit_cursor_move (edit, p - edit->curs1);
  296. curs = edit->curs1;
  297. edit_insert_indent (edit, indent);
  298. if (cursor >= curs)
  299. cursor += edit->curs1 - p;
  300. p = edit->curs1;
  301. } else if (c == '\n') {
  302. edit_cursor_move (edit, p - edit->curs1);
  303. #ifndef UTF8
  304. while (strchr ("\t ", edit_get_byte (edit, p))) {
  305. #else /* UTF8 */
  306. while (wcschr (L"\t ", edit_get_byte (edit, p))) {
  307. #endif /* UTF8 */
  308. edit_delete (edit);
  309. if (cursor > edit->curs1)
  310. cursor--;
  311. }
  312. p = edit->curs1;
  313. }
  314. }
  315. c = edit_get_byte (edit, p);
  316. if (c != t[i])
  317. replace_at (edit, p, t[i]);
  318. }
  319. edit_cursor_move (edit, cursor - edit->curs1); /* restore cursor position */
  320. }
  321. static int test_indent (WEdit * edit, long p, long q)
  322. {
  323. int indent;
  324. indent = edit_indent_width (edit, p++);
  325. if (!indent)
  326. return 0;
  327. for (; p < q; p++)
  328. if (edit_get_byte (edit, p - 1) == '\n')
  329. if (indent != edit_indent_width (edit, p))
  330. return 0;
  331. return indent;
  332. }
  333. void
  334. format_paragraph (WEdit *edit, int force)
  335. {
  336. long p, q;
  337. int size;
  338. mc_wchar_t *t;
  339. int indent = 0;
  340. if (option_word_wrap_line_length < 2)
  341. return;
  342. if (line_is_blank (edit, edit->curs_line))
  343. return;
  344. p = begin_paragraph (edit, force);
  345. q = end_paragraph (edit, force);
  346. indent = test_indent (edit, p, q);
  347. t = get_paragraph (edit, p, q, indent, &size);
  348. if (!t)
  349. return;
  350. if (!force) {
  351. int i;
  352. #ifndef UTF8
  353. if (strchr (NO_FORMAT_CHARS_START, *t)) {
  354. #else /* UTF8 */
  355. if (wcschr (NO_FORMAT_CHARS_START, *t)) {
  356. #endif /* UTF8 */
  357. g_free (t);
  358. return;
  359. }
  360. for (i = 0; i < size - 1; i++) {
  361. if (t[i] == '\n') {
  362. #ifndef UTF8
  363. if (strchr (NO_FORMAT_CHARS_START "\t ", t[i + 1])) {
  364. #else /* UTF8 */
  365. if (wcschr (NO_FORMAT_CHARS_START "\t", t[i + 1])) {
  366. #endif /* UTF8 */
  367. g_free (t);
  368. return;
  369. }
  370. }
  371. }
  372. }
  373. format_this (t, q - p, indent);
  374. put_paragraph (edit, t, p, indent, size);
  375. g_free (t);
  376. /* Scroll left as much as possible to show the formatted paragraph */
  377. edit_scroll_left (edit, -edit->start_col);
  378. }