PageRenderTime 59ms CodeModel.GetById 25ms RepoModel.GetById 1ms app.codeStats 0ms

/blender-2.63a/source/blender/editors/space_text/text_draw.c

#
C | 1928 lines | 1415 code | 319 blank | 194 comment | 519 complexity | 49bfb6a10022f902e2a070ef55a02a3c MD5 | raw file
Possible License(s): GPL-3.0, GPL-2.0, BSD-3-Clause, LGPL-3.0, BSD-2-Clause, Apache-2.0, AGPL-1.0
  1. /*
  2. * ***** BEGIN GPL LICENSE BLOCK *****
  3. *
  4. * This program is free software; you can redistribute it and/or
  5. * modify it under the terms of the GNU General Public License
  6. * as published by the Free Software Foundation; either version 2
  7. * of the License, or (at your option) any later version.
  8. *
  9. * This program is distributed in the hope that it will be useful,
  10. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. * GNU General Public License for more details.
  12. *
  13. * You should have received a copy of the GNU General Public License
  14. * along with this program; if not, write to the Free Software Foundation,
  15. * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
  16. *
  17. * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
  18. * All rights reserved.
  19. *
  20. * The Original Code is: all of this file.
  21. *
  22. * Contributor(s): none yet.
  23. *
  24. * ***** END GPL LICENSE BLOCK *****
  25. */
  26. /** \file blender/editors/space_text/text_draw.c
  27. * \ingroup sptext
  28. */
  29. #include <math.h>
  30. #include <stdlib.h>
  31. #include <string.h>
  32. #include <sys/stat.h>
  33. #include "MEM_guardedalloc.h"
  34. #include "BLF_api.h"
  35. #include "BLI_blenlib.h"
  36. #include "BLI_utildefines.h"
  37. #include "DNA_text_types.h"
  38. #include "DNA_space_types.h"
  39. #include "DNA_screen_types.h"
  40. #include "DNA_userdef_types.h"
  41. #include "BKE_context.h"
  42. #include "BKE_suggestions.h"
  43. #include "BKE_text.h"
  44. #include "BIF_gl.h"
  45. #include "ED_datafiles.h"
  46. #include "UI_interface.h"
  47. #include "UI_resources.h"
  48. #include "text_intern.h"
  49. /******************** text font drawing ******************/
  50. // XXX, fixme
  51. #define mono blf_mono_font
  52. static void text_font_begin(SpaceText *st)
  53. {
  54. BLF_size(mono, st->lheight, 72);
  55. }
  56. static void text_font_end(SpaceText *UNUSED(st))
  57. {
  58. }
  59. static int text_font_draw(SpaceText *UNUSED(st), int x, int y, const char *str)
  60. {
  61. BLF_position(mono, x, y, 0);
  62. BLF_draw(mono, str, BLF_DRAW_STR_DUMMY_MAX);
  63. return BLF_width(mono, str);
  64. }
  65. static int text_font_draw_character(SpaceText *st, int x, int y, char c)
  66. {
  67. char str[2];
  68. str[0] = c;
  69. str[1] = '\0';
  70. BLF_position(mono, x, y, 0);
  71. BLF_draw(mono, str, 1);
  72. return st->cwidth;
  73. }
  74. static int text_font_draw_character_utf8(SpaceText *st, int x, int y, const char *c)
  75. {
  76. char str[BLI_UTF8_MAX + 1];
  77. size_t len = BLI_str_utf8_size(c);
  78. memcpy(str, c, len);
  79. str[len] = '\0';
  80. BLF_position(mono, x, y, 0);
  81. BLF_draw(mono, str, len);
  82. return st->cwidth;
  83. }
  84. /****************** flatten string **********************/
  85. static void flatten_string_append(FlattenString *fs, const char *c, int accum, int len)
  86. {
  87. int i;
  88. if (fs->pos + len > fs->len) {
  89. char *nbuf; int *naccum;
  90. fs->len *= 2;
  91. nbuf = MEM_callocN(sizeof(*fs->buf) * fs->len, "fs->buf");
  92. naccum = MEM_callocN(sizeof(*fs->accum) * fs->len, "fs->accum");
  93. memcpy(nbuf, fs->buf, fs->pos * sizeof(*fs->buf));
  94. memcpy(naccum, fs->accum, fs->pos * sizeof(*fs->accum));
  95. if (fs->buf != fs->fixedbuf) {
  96. MEM_freeN(fs->buf);
  97. MEM_freeN(fs->accum);
  98. }
  99. fs->buf = nbuf;
  100. fs->accum = naccum;
  101. }
  102. for (i = 0; i < len; i++)
  103. {
  104. fs->buf[fs->pos + i] = c[i];
  105. fs->accum[fs->pos + i] = accum;
  106. }
  107. fs->pos += len;
  108. }
  109. int flatten_string(SpaceText *st, FlattenString *fs, const char *in)
  110. {
  111. int r, i, total = 0;
  112. memset(fs, 0, sizeof(FlattenString));
  113. fs->buf = fs->fixedbuf;
  114. fs->accum = fs->fixedaccum;
  115. fs->len = sizeof(fs->fixedbuf);
  116. for (r = 0, i = 0; *in; r++) {
  117. if (*in == '\t') {
  118. i = st->tabnumber - (total % st->tabnumber);
  119. total += i;
  120. while (i--)
  121. flatten_string_append(fs, " ", r, 1);
  122. in++;
  123. }
  124. else {
  125. size_t len = BLI_str_utf8_size(in);
  126. flatten_string_append(fs, in, r, len);
  127. in += len;
  128. total++;
  129. }
  130. }
  131. flatten_string_append(fs, "\0", r, 1);
  132. return total;
  133. }
  134. void flatten_string_free(FlattenString *fs)
  135. {
  136. if (fs->buf != fs->fixedbuf)
  137. MEM_freeN(fs->buf);
  138. if (fs->accum != fs->fixedaccum)
  139. MEM_freeN(fs->accum);
  140. }
  141. /* Checks the specified source string for a Python built-in function name. This
  142. * name must start at the beginning of the source string and must be followed by
  143. * a non-identifier (see text_check_identifier(char)) or null character.
  144. *
  145. * If a built-in function is found, the length of the matching name is returned.
  146. * Otherwise, -1 is returned.
  147. *
  148. * See:
  149. * http://docs.python.org/py3k/reference/lexical_analysis.html#keywords
  150. */
  151. static int find_builtinfunc(char *string)
  152. {
  153. int a, i;
  154. const char *builtinfuncs[] = {
  155. /* "False", "None", "True", */ /* see find_bool() */
  156. "and", "as", "assert", "break",
  157. "class", "continue", "def", "del", "elif", "else", "except",
  158. "finally", "for", "from", "global", "if", "import", "in",
  159. "is", "lambda", "nonlocal", "not", "or", "pass", "raise",
  160. "return", "try", "while", "with", "yield",
  161. };
  162. for (a = 0; a < sizeof(builtinfuncs) / sizeof(builtinfuncs[0]); a++) {
  163. i = 0;
  164. while (1) {
  165. /* If we hit the end of a keyword... (eg. "def") */
  166. if (builtinfuncs[a][i] == '\0') {
  167. /* If we still have identifier chars in the source (eg. "definate") */
  168. if (text_check_identifier(string[i]))
  169. i = -1; /* No match */
  170. break; /* Next keyword if no match, otherwise we're done */
  171. /* If chars mismatch, move on to next keyword */
  172. }
  173. else if (string[i] != builtinfuncs[a][i]) {
  174. i = -1;
  175. break; /* Break inner loop, start next keyword */
  176. }
  177. i++;
  178. }
  179. if (i > 0) break; /* If we have a match, we're done */
  180. }
  181. return i;
  182. }
  183. /* Checks the specified source string for a Python special name. This name must
  184. * start at the beginning of the source string and must be followed by a non-
  185. * identifier (see text_check_identifier(char)) or null character.
  186. *
  187. * If a special name is found, the length of the matching name is returned.
  188. * Otherwise, -1 is returned. */
  189. static int find_specialvar(char *string)
  190. {
  191. int i = 0;
  192. /* Check for "def" */
  193. if (string[0] == 'd' && string[1] == 'e' && string[2] == 'f')
  194. i = 3;
  195. /* Check for "class" */
  196. else if (string[0] == 'c' && string[1] == 'l' && string[2] == 'a' && string[3] == 's' && string[4] == 's')
  197. i = 5;
  198. /* If next source char is an identifier (eg. 'i' in "definate") no match */
  199. if (i == 0 || text_check_identifier(string[i]))
  200. return -1;
  201. return i;
  202. }
  203. static int find_decorator(char *string)
  204. {
  205. if (string[0] == '@') {
  206. int i = 1;
  207. while (text_check_identifier(string[i])) {
  208. i++;
  209. }
  210. return i;
  211. }
  212. return -1;
  213. }
  214. static int find_bool(char *string)
  215. {
  216. int i = 0;
  217. /* Check for "False" */
  218. if (string[0] == 'F' && string[1] == 'a' && string[2] == 'l' && string[3] == 's' && string[4] == 'e')
  219. i = 5;
  220. /* Check for "True" */
  221. else if (string[0] == 'T' && string[1] == 'r' && string[2] == 'u' && string[3] == 'e')
  222. i = 4;
  223. /* Check for "None" */
  224. else if (string[0] == 'N' && string[1] == 'o' && string[2] == 'n' && string[3] == 'e')
  225. i = 4;
  226. /* If next source char is an identifier (eg. 'i' in "definate") no match */
  227. if (i == 0 || text_check_identifier(string[i]))
  228. return -1;
  229. return i;
  230. }
  231. /* Ensures the format string for the given line is long enough, reallocating
  232. * as needed. Allocation is done here, alone, to ensure consistency. */
  233. static int text_check_format_len(TextLine *line, unsigned int len)
  234. {
  235. if (line->format) {
  236. if (strlen(line->format) < len) {
  237. MEM_freeN(line->format);
  238. line->format = MEM_mallocN(len + 2, "SyntaxFormat");
  239. if (!line->format) return 0;
  240. }
  241. }
  242. else {
  243. line->format = MEM_mallocN(len + 2, "SyntaxFormat");
  244. if (!line->format) return 0;
  245. }
  246. return 1;
  247. }
  248. /* Formats the specified line. If do_next is set, the process will move on to
  249. * the succeeding line if it is affected (eg. multiline strings). Format strings
  250. * may contain any of the following characters:
  251. * '_' Whitespace
  252. * '#' Comment text
  253. * '!' Punctuation and other symbols
  254. * 'n' Numerals
  255. * 'l' String letters
  256. * 'v' Special variables (class, def)
  257. * 'b' Built-in names (print, for, etc.)
  258. * 'q' Other text (identifiers, etc.)
  259. * It is terminated with a null-terminator '\0' followed by a continuation
  260. * flag indicating whether the line is part of a multi-line string. */
  261. static void txt_format_line(SpaceText *st, TextLine *line, int do_next)
  262. {
  263. FlattenString fs;
  264. char *str, *fmt, orig, cont, find, prev = ' ';
  265. int len, i;
  266. /* Get continuation from previous line */
  267. if (line->prev && line->prev->format != NULL) {
  268. fmt = line->prev->format;
  269. cont = fmt[strlen(fmt) + 1]; /* Just after the null-terminator */
  270. }
  271. else cont = 0;
  272. /* Get original continuation from this line */
  273. if (line->format != NULL) {
  274. fmt = line->format;
  275. orig = fmt[strlen(fmt) + 1]; /* Just after the null-terminator */
  276. }
  277. else orig = 0xFF;
  278. len = flatten_string(st, &fs, line->line);
  279. str = fs.buf;
  280. if (!text_check_format_len(line, len)) {
  281. flatten_string_free(&fs);
  282. return;
  283. }
  284. fmt = line->format;
  285. while (*str) {
  286. /* Handle escape sequences by skipping both \ and next char */
  287. if (*str == '\\') {
  288. *fmt = prev; fmt++; str++;
  289. if (*str == '\0') break;
  290. *fmt = prev; fmt++; str += BLI_str_utf8_size(str);
  291. continue;
  292. }
  293. /* Handle continuations */
  294. else if (cont) {
  295. /* Triple strings ("""...""" or '''...''') */
  296. if (cont & TXT_TRISTR) {
  297. find = (cont & TXT_DBLQUOTSTR) ? '"' : '\'';
  298. if (*str == find && *(str + 1) == find && *(str + 2) == find) {
  299. *fmt = 'l'; fmt++; str++;
  300. *fmt = 'l'; fmt++; str++;
  301. cont = 0;
  302. }
  303. /* Handle other strings */
  304. }
  305. else {
  306. find = (cont & TXT_DBLQUOTSTR) ? '"' : '\'';
  307. if (*str == find) cont = 0;
  308. }
  309. *fmt = 'l';
  310. str += BLI_str_utf8_size(str) - 1;
  311. }
  312. /* Not in a string... */
  313. else {
  314. /* Deal with comments first */
  315. if (prev == '#' || *str == '#') {
  316. *fmt = '#';
  317. str += BLI_str_utf8_size(str) - 1;
  318. }
  319. else if (*str == '"' || *str == '\'') {
  320. /* Strings */
  321. find = *str;
  322. cont = (*str == '"') ? TXT_DBLQUOTSTR : TXT_SNGQUOTSTR;
  323. if (*(str + 1) == find && *(str + 2) == find) {
  324. *fmt = 'l'; fmt++; str++;
  325. *fmt = 'l'; fmt++; str++;
  326. cont |= TXT_TRISTR;
  327. }
  328. *fmt = 'l';
  329. }
  330. /* Whitespace (all ws. has been converted to spaces) */
  331. else if (*str == ' ')
  332. *fmt = '_';
  333. /* Numbers (digits not part of an identifier and periods followed by digits) */
  334. else if ((prev != 'q' && text_check_digit(*str)) || (*str == '.' && text_check_digit(*(str + 1))))
  335. *fmt = 'n';
  336. /* Booleans */
  337. else if (prev != 'q' && (i = find_bool(str)) != -1)
  338. if (i > 0) {
  339. while (i > 1) {
  340. *fmt = 'n'; fmt++; str++;
  341. i--;
  342. }
  343. *fmt = 'n';
  344. }
  345. else {
  346. str += BLI_str_utf8_size(str) - 1;
  347. *fmt = 'q';
  348. }
  349. /* Punctuation */
  350. else if (text_check_delim(*str))
  351. *fmt = '!';
  352. /* Identifiers and other text (no previous ws. or delims. so text continues) */
  353. else if (prev == 'q') {
  354. str += BLI_str_utf8_size(str) - 1;
  355. *fmt = 'q';
  356. }
  357. /* Not ws, a digit, punct, or continuing text. Must be new, check for special words */
  358. else {
  359. /* Special vars(v) or built-in keywords(b) */
  360. if ((i = find_specialvar(str)) != -1)
  361. prev = 'v';
  362. else if ((i = find_builtinfunc(str)) != -1)
  363. prev = 'b';
  364. else if ((i = find_decorator(str)) != -1)
  365. prev = 'v'; /* could have a new color for this */
  366. if (i > 0) {
  367. while (i > 1) {
  368. *fmt = prev; fmt++; str++;
  369. i--;
  370. }
  371. *fmt = prev;
  372. }
  373. else {
  374. str += BLI_str_utf8_size(str) - 1;
  375. *fmt = 'q';
  376. }
  377. }
  378. }
  379. prev = *fmt;
  380. fmt++;
  381. str++;
  382. }
  383. /* Terminate and add continuation char */
  384. *fmt = '\0'; fmt++;
  385. *fmt = cont;
  386. /* If continuation has changed and we're allowed, process the next line */
  387. if (cont != orig && do_next && line->next) {
  388. txt_format_line(st, line->next, do_next);
  389. }
  390. flatten_string_free(&fs);
  391. }
  392. #if 0
  393. /* Formats every line of the current text */
  394. static void txt_format_text(SpaceText *st)
  395. {
  396. TextLine *linep;
  397. if (!st->text) return;
  398. for (linep = st->text->lines.first; linep; linep = linep->next)
  399. txt_format_line(st, linep, 0);
  400. }
  401. #endif
  402. /* Sets the current drawing color based on the format character specified */
  403. static void format_draw_color(char formatchar)
  404. {
  405. switch (formatchar) {
  406. case '_': /* Whitespace */
  407. break;
  408. case '!': /* Symbols */
  409. UI_ThemeColorBlend(TH_TEXT, TH_BACK, 0.5f);
  410. break;
  411. case '#': /* Comments */
  412. UI_ThemeColor(TH_SYNTAX_C);
  413. break;
  414. case 'n': /* Numerals */
  415. UI_ThemeColor(TH_SYNTAX_N);
  416. break;
  417. case 'l': /* Strings */
  418. UI_ThemeColor(TH_SYNTAX_L);
  419. break;
  420. case 'v': /* Specials: class, def */
  421. UI_ThemeColor(TH_SYNTAX_V);
  422. break;
  423. case 'b': /* Keywords: for, print, etc. */
  424. UI_ThemeColor(TH_SYNTAX_B);
  425. break;
  426. case 'q': /* Other text (identifiers) */
  427. default:
  428. UI_ThemeColor(TH_TEXT);
  429. break;
  430. }
  431. }
  432. /************************** draw text *****************************/
  433. /* Notes on word-wrap
  434. * --
  435. * All word-wrap functions follow the algorithm below to maintain consistency.
  436. * line The line to wrap (tabs converted to spaces)
  437. * view_width The maximum number of characters displayable in the region
  438. * This equals region_width/font_width for the region
  439. * wrap_chars Characters that allow wrapping. This equals [' ', '\t', '-']
  440. *
  441. * def wrap(line, view_width, wrap_chars):
  442. * draw_start = 0
  443. * draw_end = view_width
  444. * pos = 0
  445. * for c in line:
  446. * if pos-draw_start >= view_width:
  447. * print line[draw_start:draw_end]
  448. * draw_start = draw_end
  449. * draw_end += view_width
  450. * elif c in wrap_chars:
  451. * draw_end = pos+1
  452. * pos += 1
  453. * print line[draw_start:]
  454. *
  455. */
  456. int wrap_width(SpaceText *st, ARegion *ar)
  457. {
  458. int winx = ar->winx - TXT_SCROLL_WIDTH;
  459. int x, max;
  460. x = st->showlinenrs ? TXT_OFFSET + TEXTXLOC : TXT_OFFSET;
  461. max = st->cwidth ? (winx - x) / st->cwidth : 0;
  462. return max > 8 ? max : 8;
  463. }
  464. /* Sets (offl, offc) for transforming (line, curs) to its wrapped position */
  465. void wrap_offset(SpaceText *st, ARegion *ar, TextLine *linein, int cursin, int *offl, int *offc)
  466. {
  467. Text *text;
  468. TextLine *linep;
  469. int i, j, start, end, max, chop;
  470. char ch;
  471. *offl = *offc = 0;
  472. if (!st->text) return;
  473. if (!st->wordwrap) return;
  474. text = st->text;
  475. /* Move pointer to first visible line (top) */
  476. linep = text->lines.first;
  477. i = st->top;
  478. while (i > 0 && linep) {
  479. int lines = text_get_visible_lines(st, ar, linep->line);
  480. /* Line before top */
  481. if (linep == linein) {
  482. if (lines <= i)
  483. /* no visible part of line */
  484. return;
  485. }
  486. if (i - lines < 0) {
  487. break;
  488. }
  489. else {
  490. linep = linep->next;
  491. (*offl) += lines - 1;
  492. i -= lines;
  493. }
  494. }
  495. max = wrap_width(st, ar);
  496. cursin = txt_utf8_offset_to_index(linein->line, cursin);
  497. while (linep) {
  498. start = 0;
  499. end = max;
  500. chop = 1;
  501. *offc = 0;
  502. for (i = 0, j = 0; linep->line[j]; j += BLI_str_utf8_size(linep->line + j)) {
  503. int chars;
  504. /* Mimic replacement of tabs */
  505. ch = linep->line[j];
  506. if (ch == '\t') {
  507. chars = st->tabnumber - i % st->tabnumber;
  508. if (linep == linein && i < cursin) cursin += chars - 1;
  509. ch = ' ';
  510. }
  511. else {
  512. chars = 1;
  513. }
  514. while (chars--) {
  515. if (i - start >= max) {
  516. if (chop && linep == linein && i >= cursin) {
  517. if (i == cursin) {
  518. (*offl)++;
  519. *offc -= end - start;
  520. }
  521. return;
  522. }
  523. (*offl)++;
  524. *offc -= end - start;
  525. start = end;
  526. end += max;
  527. chop = 1;
  528. }
  529. else if (ch == ' ' || ch == '-') {
  530. end = i + 1;
  531. chop = 0;
  532. if (linep == linein && i >= cursin)
  533. return;
  534. }
  535. i++;
  536. }
  537. }
  538. if (linep == linein) break;
  539. linep = linep->next;
  540. }
  541. }
  542. /* cursin - mem, offc - view */
  543. void wrap_offset_in_line(SpaceText *st, ARegion *ar, TextLine *linein, int cursin, int *offl, int *offc)
  544. {
  545. int i, j, start, end, chars, max, chop;
  546. char ch;
  547. *offl = *offc = 0;
  548. if (!st->text) return;
  549. if (!st->wordwrap) return;
  550. max = wrap_width(st, ar);
  551. start = 0;
  552. end = max;
  553. chop = 1;
  554. *offc = 0;
  555. cursin = txt_utf8_offset_to_index(linein->line, cursin);
  556. for (i = 0, j = 0; linein->line[j]; j += BLI_str_utf8_size(linein->line + j)) {
  557. /* Mimic replacement of tabs */
  558. ch = linein->line[j];
  559. if (ch == '\t') {
  560. chars = st->tabnumber - i % st->tabnumber;
  561. if (i < cursin) cursin += chars - 1;
  562. ch = ' ';
  563. }
  564. else
  565. chars = 1;
  566. while (chars--) {
  567. if (i - start >= max) {
  568. if (chop && i >= cursin) {
  569. if (i == cursin) {
  570. (*offl)++;
  571. *offc -= end - start;
  572. }
  573. return;
  574. }
  575. (*offl)++;
  576. *offc -= end - start;
  577. start = end;
  578. end += max;
  579. chop = 1;
  580. }
  581. else if (ch == ' ' || ch == '-') {
  582. end = i + 1;
  583. chop = 0;
  584. if (i >= cursin)
  585. return;
  586. }
  587. i++;
  588. }
  589. }
  590. }
  591. int text_get_char_pos(SpaceText *st, const char *line, int cur)
  592. {
  593. int a = 0, i;
  594. for (i = 0; i < cur && line[i]; i += BLI_str_utf8_size(line + i)) {
  595. if (line[i] == '\t')
  596. a += st->tabnumber - a % st->tabnumber;
  597. else
  598. a++;
  599. }
  600. return a;
  601. }
  602. static const char *txt_utf8_get_nth(const char *str, int n)
  603. {
  604. int pos = 0;
  605. while (str[pos] && n--) {
  606. pos += BLI_str_utf8_size(str + pos);
  607. }
  608. return str + pos;
  609. }
  610. static int text_draw_wrapped(SpaceText *st, const char *str, int x, int y, int w, const char *format, int skip)
  611. {
  612. FlattenString fs;
  613. int basex, i, a, start, end, max, lines; /* view */
  614. int mi, ma, mstart, mend; /* mem */
  615. flatten_string(st, &fs, str);
  616. str = fs.buf;
  617. max = w / st->cwidth;
  618. if (max < 8) max = 8;
  619. basex = x;
  620. lines = 1;
  621. start = 0; mstart = 0;
  622. end = max; mend = txt_utf8_get_nth(str, max) - str;
  623. for (i = 0, mi = 0; str[mi]; i++, mi += BLI_str_utf8_size(str + mi)) {
  624. if (i - start >= max) {
  625. /* skip hidden part of line */
  626. if (skip) {
  627. skip--;
  628. start = end; mstart = mend;
  629. end += max; mend = txt_utf8_get_nth(str + mend, max) - str;
  630. continue;
  631. }
  632. /* Draw the visible portion of text on the overshot line */
  633. for (a = start, ma = mstart; a < end; a++, ma += BLI_str_utf8_size(str + ma)) {
  634. if (st->showsyntax && format) format_draw_color(format[a]);
  635. x += text_font_draw_character_utf8(st, x, y, str + ma);
  636. }
  637. y -= st->lheight;
  638. x = basex;
  639. lines++;
  640. start = end; mstart = mend;
  641. end += max; mend = txt_utf8_get_nth(str + mend, max) - str;
  642. if (y <= 0) break;
  643. }
  644. else if (str[mi] == ' ' || str[mi] == '-') {
  645. end = i + 1; mend = mi + 1;
  646. }
  647. }
  648. /* Draw the remaining text */
  649. for (a = start, ma = mstart; str[ma] && y > 0; a++, ma += BLI_str_utf8_size(str + ma)) {
  650. if (st->showsyntax && format)
  651. format_draw_color(format[a]);
  652. x += text_font_draw_character_utf8(st, x, y, str + ma);
  653. }
  654. flatten_string_free(&fs);
  655. return lines;
  656. }
  657. static int text_draw(SpaceText *st, char *str, int cshift, int maxwidth, int draw, int x, int y, const char *format)
  658. {
  659. FlattenString fs;
  660. int *acc, r = 0;
  661. const char *in;
  662. int w = flatten_string(st, &fs, str);
  663. if (w < cshift) {
  664. flatten_string_free(&fs);
  665. return 0; /* String is shorter than shift */
  666. }
  667. in = txt_utf8_get_nth(fs.buf, cshift);
  668. acc = fs.accum + cshift;
  669. w = w - cshift;
  670. if (draw) {
  671. int amount = maxwidth ? MIN2(w, maxwidth) : w;
  672. if (st->showsyntax && format) {
  673. int a, str_shift = 0;
  674. format = format + cshift;
  675. for (a = 0; a < amount; a++) {
  676. format_draw_color(format[a]);
  677. x += text_font_draw_character_utf8(st, x, y, in + str_shift);
  678. str_shift += BLI_str_utf8_size(in + str_shift);
  679. }
  680. }
  681. else text_font_draw(st, x, y, in);
  682. }
  683. else {
  684. while (w-- && *acc++ < maxwidth)
  685. r += st->cwidth;
  686. }
  687. flatten_string_free(&fs);
  688. if (cshift && r == 0)
  689. return 0;
  690. else if (st->showlinenrs)
  691. return r + TXT_OFFSET + TEXTXLOC;
  692. else
  693. return r + TXT_OFFSET;
  694. }
  695. /************************ cache utilities *****************************/
  696. typedef struct DrawCache {
  697. int *line_height;
  698. int total_lines, nlines;
  699. /* this is needed to check cache relevance */
  700. int winx, wordwrap, showlinenrs, tabnumber;
  701. short lheight;
  702. char cwidth;
  703. char text_id[MAX_ID_NAME];
  704. /* for partial lines recalculation */
  705. short update_flag;
  706. int valid_head, valid_tail; /* amount of unchanged lines */
  707. } DrawCache;
  708. static void text_drawcache_init(SpaceText *st)
  709. {
  710. DrawCache *drawcache = MEM_callocN(sizeof (DrawCache), "text draw cache");
  711. drawcache->winx = -1;
  712. drawcache->nlines = BLI_countlist(&st->text->lines);
  713. drawcache->text_id[0] = '\0';
  714. st->drawcache = drawcache;
  715. }
  716. static void text_update_drawcache(SpaceText *st, ARegion *ar)
  717. {
  718. DrawCache *drawcache;
  719. int full_update = 0, nlines = 0;
  720. Text *txt = st->text;
  721. if (!st->drawcache) text_drawcache_init(st);
  722. text_update_character_width(st);
  723. drawcache = (DrawCache *)st->drawcache;
  724. nlines = drawcache->nlines;
  725. /* check if full cache update is needed */
  726. full_update |= drawcache->winx != ar->winx; /* area was resized */
  727. full_update |= drawcache->wordwrap != st->wordwrap; /* word-wrapping option was toggled */
  728. full_update |= drawcache->showlinenrs != st->showlinenrs; /* word-wrapping option was toggled */
  729. full_update |= drawcache->tabnumber != st->tabnumber; /* word-wrapping option was toggled */
  730. full_update |= drawcache->lheight != st->lheight; /* word-wrapping option was toggled */
  731. full_update |= drawcache->cwidth != st->cwidth; /* word-wrapping option was toggled */
  732. full_update |= strncmp(drawcache->text_id, txt->id.name, MAX_ID_NAME); /* text datablock was changed */
  733. if (st->wordwrap) {
  734. /* update line heights */
  735. if (full_update || !drawcache->line_height) {
  736. drawcache->valid_head = 0;
  737. drawcache->valid_tail = 0;
  738. drawcache->update_flag = 1;
  739. }
  740. if (drawcache->update_flag) {
  741. TextLine *line = st->text->lines.first;
  742. int lineno = 0, size, lines_count;
  743. int *fp = drawcache->line_height, *new_tail, *old_tail;
  744. nlines = BLI_countlist(&txt->lines);
  745. size = sizeof(int) * nlines;
  746. if (fp) fp = MEM_reallocN(fp, size);
  747. else fp = MEM_callocN(size, "text drawcache line_height");
  748. drawcache->valid_tail = drawcache->valid_head = 0;
  749. old_tail = fp + drawcache->nlines - drawcache->valid_tail;
  750. new_tail = fp + nlines - drawcache->valid_tail;
  751. memmove(new_tail, old_tail, drawcache->valid_tail);
  752. drawcache->total_lines = 0;
  753. if (st->showlinenrs)
  754. st->linenrs_tot = (int)floor(log10((float)nlines)) + 1;
  755. while (line) {
  756. if (drawcache->valid_head) { /* we're inside valid head lines */
  757. lines_count = fp[lineno];
  758. drawcache->valid_head--;
  759. }
  760. else if (lineno > new_tail - fp) { /* we-re inside valid tail lines */
  761. lines_count = fp[lineno];
  762. }
  763. else {
  764. lines_count = text_get_visible_lines(st, ar, line->line);
  765. }
  766. fp[lineno] = lines_count;
  767. line = line->next;
  768. lineno++;
  769. drawcache->total_lines += lines_count;
  770. }
  771. drawcache->line_height = fp;
  772. }
  773. }
  774. else {
  775. if (drawcache->line_height) {
  776. MEM_freeN(drawcache->line_height);
  777. drawcache->line_height = NULL;
  778. }
  779. if (full_update || drawcache->update_flag) {
  780. nlines = BLI_countlist(&txt->lines);
  781. if (st->showlinenrs)
  782. st->linenrs_tot = (int)floor(log10((float)nlines)) + 1;
  783. }
  784. drawcache->total_lines = nlines;
  785. }
  786. drawcache->nlines = nlines;
  787. /* store settings */
  788. drawcache->winx = ar->winx;
  789. drawcache->wordwrap = st->wordwrap;
  790. drawcache->lheight = st->lheight;
  791. drawcache->cwidth = st->cwidth;
  792. drawcache->showlinenrs = st->showlinenrs;
  793. drawcache->tabnumber = st->tabnumber;
  794. strncpy(drawcache->text_id, txt->id.name, MAX_ID_NAME);
  795. /* clear update flag */
  796. drawcache->update_flag = 0;
  797. drawcache->valid_head = 0;
  798. drawcache->valid_tail = 0;
  799. }
  800. void text_drawcache_tag_update(SpaceText *st, int full)
  801. {
  802. /* this happens if text editor ops are caled from python */
  803. if (st == NULL)
  804. return;
  805. if (st->drawcache) {
  806. DrawCache *drawcache = (DrawCache *)st->drawcache;
  807. Text *txt = st->text;
  808. if (drawcache->update_flag) {
  809. /* happens when tagging update from space listener */
  810. /* should do nothing to prevent locally tagged cache be fully recalculated */
  811. return;
  812. }
  813. if (!full) {
  814. int sellno = BLI_findindex(&txt->lines, txt->sell);
  815. int curlno = BLI_findindex(&txt->lines, txt->curl);
  816. if (curlno < sellno) {
  817. drawcache->valid_head = curlno;
  818. drawcache->valid_tail = drawcache->nlines - sellno - 1;
  819. }
  820. else {
  821. drawcache->valid_head = sellno;
  822. drawcache->valid_tail = drawcache->nlines - curlno - 1;
  823. }
  824. /* quick cache recalculation is also used in delete operator,
  825. * which could merge lines which are adjacent to current selection lines
  826. * expand recalculate area to this lines */
  827. if (drawcache->valid_head > 0) drawcache->valid_head--;
  828. if (drawcache->valid_tail > 0) drawcache->valid_tail--;
  829. }
  830. else {
  831. drawcache->valid_head = 0;
  832. drawcache->valid_tail = 0;
  833. }
  834. drawcache->update_flag = 1;
  835. }
  836. }
  837. void text_free_caches(SpaceText *st)
  838. {
  839. DrawCache *drawcache = (DrawCache *)st->drawcache;
  840. if (drawcache) {
  841. if (drawcache->line_height)
  842. MEM_freeN(drawcache->line_height);
  843. MEM_freeN(drawcache);
  844. }
  845. }
  846. /************************ word-wrap utilities *****************************/
  847. /* cache should be updated in caller */
  848. static int text_get_visible_lines_no(SpaceText *st, int lineno)
  849. {
  850. DrawCache *drawcache = (DrawCache *)st->drawcache;
  851. return drawcache->line_height[lineno];
  852. }
  853. int text_get_visible_lines(SpaceText *st, ARegion *ar, const char *str)
  854. {
  855. int i, j, start, end, max, lines, chars;
  856. char ch;
  857. max = wrap_width(st, ar);
  858. lines = 1;
  859. start = 0;
  860. end = max;
  861. for (i = 0, j = 0; str[j]; j += BLI_str_utf8_size(str + j)) {
  862. /* Mimic replacement of tabs */
  863. ch = str[j];
  864. if (ch == '\t') {
  865. chars = st->tabnumber - i % st->tabnumber;
  866. ch = ' ';
  867. }
  868. else chars = 1;
  869. while (chars--) {
  870. if (i - start >= max) {
  871. lines++;
  872. start = end;
  873. end += max;
  874. }
  875. else if (ch == ' ' || ch == '-') {
  876. end = i + 1;
  877. }
  878. i++;
  879. }
  880. }
  881. return lines;
  882. }
  883. int text_get_span_wrap(SpaceText *st, ARegion *ar, TextLine *from, TextLine *to)
  884. {
  885. if (st->wordwrap) {
  886. int ret = 0;
  887. TextLine *tmp = from;
  888. /* Look forwards */
  889. while (tmp) {
  890. if (tmp == to) return ret;
  891. ret += text_get_visible_lines(st, ar, tmp->line);
  892. tmp = tmp->next;
  893. }
  894. return ret;
  895. }
  896. else return txt_get_span(from, to);
  897. }
  898. int text_get_total_lines(SpaceText *st, ARegion *ar)
  899. {
  900. DrawCache *drawcache;
  901. text_update_drawcache(st, ar);
  902. drawcache = (DrawCache *)st->drawcache;
  903. return drawcache->total_lines;
  904. }
  905. /* Move pointer to first visible line (top) */
  906. static TextLine *first_visible_line(SpaceText *st, ARegion *ar, int *wrap_top)
  907. {
  908. Text *text = st->text;
  909. TextLine *pline = text->lines.first;
  910. int i = st->top, lineno = 0;
  911. text_update_drawcache(st, ar);
  912. if (wrap_top) *wrap_top = 0;
  913. if (st->wordwrap) {
  914. while (i > 0 && pline) {
  915. int lines = text_get_visible_lines_no(st, lineno);
  916. if (i - lines < 0) {
  917. if (wrap_top) *wrap_top = i;
  918. break;
  919. }
  920. else {
  921. pline = pline->next;
  922. i -= lines;
  923. lineno++;
  924. }
  925. }
  926. }
  927. else {
  928. for (i = st->top; pline->next && i > 0; i--)
  929. pline = pline->next;
  930. }
  931. return pline;
  932. }
  933. /************************ draw scrollbar *****************************/
  934. static void calc_text_rcts(SpaceText *st, ARegion *ar, rcti *scroll, rcti *back)
  935. {
  936. int lhlstart, lhlend, ltexth, sell_off, curl_off;
  937. short barheight, barstart, hlstart, hlend, blank_lines;
  938. short pix_available, pix_top_margin, pix_bottom_margin, pix_bardiff;
  939. pix_top_margin = 8;
  940. pix_bottom_margin = 4;
  941. pix_available = ar->winy - pix_top_margin - pix_bottom_margin;
  942. ltexth = text_get_total_lines(st, ar);
  943. blank_lines = st->viewlines / 2;
  944. /* nicer code: use scroll rect for entire bar */
  945. back->xmin = ar->winx - 18;
  946. back->xmax = ar->winx;
  947. back->ymin = 0;
  948. back->ymax = ar->winy;
  949. scroll->xmin = ar->winx - 17;
  950. scroll->xmax = ar->winx - 5;
  951. scroll->ymin = 4;
  952. scroll->ymax = 4 + pix_available;
  953. /* when re-sizing a view-port with the bar at the bottom to a greater height more blank lines will be added */
  954. if (ltexth + blank_lines < st->top + st->viewlines) {
  955. blank_lines = st->top + st->viewlines - ltexth;
  956. }
  957. ltexth += blank_lines;
  958. barheight = (ltexth > 0) ? (st->viewlines * pix_available) / ltexth : 0;
  959. pix_bardiff = 0;
  960. if (barheight < 20) {
  961. pix_bardiff = 20 - barheight; /* take into account the now non-linear sizing of the bar */
  962. barheight = 20;
  963. }
  964. barstart = (ltexth > 0) ? ((pix_available - pix_bardiff) * st->top) / ltexth : 0;
  965. st->txtbar = *scroll;
  966. st->txtbar.ymax -= barstart;
  967. st->txtbar.ymin = st->txtbar.ymax - barheight;
  968. CLAMP(st->txtbar.ymin, pix_bottom_margin, ar->winy - pix_top_margin);
  969. CLAMP(st->txtbar.ymax, pix_bottom_margin, ar->winy - pix_top_margin);
  970. st->pix_per_line = (pix_available > 0) ? (float) ltexth / pix_available : 0;
  971. if (st->pix_per_line < 0.1f) st->pix_per_line = 0.1f;
  972. curl_off = text_get_span_wrap(st, ar, st->text->lines.first, st->text->curl);
  973. sell_off = text_get_span_wrap(st, ar, st->text->lines.first, st->text->sell);
  974. lhlstart = MIN2(curl_off, sell_off);
  975. lhlend = MAX2(curl_off, sell_off);
  976. if (ltexth > 0) {
  977. hlstart = (lhlstart * pix_available) / ltexth;
  978. hlend = (lhlend * pix_available) / ltexth;
  979. /* the scrollbar is non-linear sized */
  980. if (pix_bardiff > 0) {
  981. /* the start of the highlight is in the current viewport */
  982. if (ltexth && st->viewlines && lhlstart >= st->top && lhlstart <= st->top + st->viewlines) {
  983. /* speed the progresion of the start of the highlight through the scrollbar */
  984. hlstart = ( ( (pix_available - pix_bardiff) * lhlstart) / ltexth) + (pix_bardiff * (lhlstart - st->top) / st->viewlines);
  985. }
  986. else if (lhlstart > st->top + st->viewlines && hlstart < barstart + barheight && hlstart > barstart) {
  987. /* push hl start down */
  988. hlstart = barstart + barheight;
  989. }
  990. else if (lhlend > st->top && lhlstart < st->top && hlstart > barstart) {
  991. /*fill out start */
  992. hlstart = barstart;
  993. }
  994. if (hlend <= hlstart) {
  995. hlend = hlstart + 2;
  996. }
  997. /* the end of the highlight is in the current viewport */
  998. if (ltexth && st->viewlines && lhlend >= st->top && lhlend <= st->top + st->viewlines) {
  999. /* speed the progresion of the end of the highlight through the scrollbar */
  1000. hlend = (((pix_available - pix_bardiff) * lhlend) / ltexth) + (pix_bardiff * (lhlend - st->top) / st->viewlines);
  1001. }
  1002. else if (lhlend < st->top && hlend >= barstart - 2 && hlend < barstart + barheight) {
  1003. /* push hl end up */
  1004. hlend = barstart;
  1005. }
  1006. else if (lhlend > st->top + st->viewlines && lhlstart < st->top + st->viewlines && hlend < barstart + barheight) {
  1007. /* fill out end */
  1008. hlend = barstart + barheight;
  1009. }
  1010. if (hlend <= hlstart) {
  1011. hlstart = hlend - 2;
  1012. }
  1013. }
  1014. }
  1015. else {
  1016. hlstart = 0;
  1017. hlend = 0;
  1018. }
  1019. if (hlend - hlstart < 2) {
  1020. hlend = hlstart + 2;
  1021. }
  1022. st->txtscroll = *scroll;
  1023. st->txtscroll.ymax = ar->winy - pix_top_margin - hlstart;
  1024. st->txtscroll.ymin = ar->winy - pix_top_margin - hlend;
  1025. CLAMP(st->txtscroll.ymin, pix_bottom_margin, ar->winy - pix_top_margin);
  1026. CLAMP(st->txtscroll.ymax, pix_bottom_margin, ar->winy - pix_top_margin);
  1027. }
  1028. static void draw_textscroll(SpaceText *st, rcti *scroll, rcti *back)
  1029. {
  1030. bTheme *btheme = UI_GetTheme();
  1031. uiWidgetColors wcol = btheme->tui.wcol_scroll;
  1032. unsigned char col[4];
  1033. float rad;
  1034. UI_ThemeColor(TH_BACK);
  1035. glRecti(back->xmin, back->ymin, back->xmax, back->ymax);
  1036. uiWidgetScrollDraw(&wcol, scroll, &st->txtbar, (st->flags & ST_SCROLL_SELECT) ? UI_SCROLL_PRESSED : 0);
  1037. uiSetRoundBox(UI_CNR_ALL);
  1038. rad = 0.4f * MIN2(st->txtscroll.xmax - st->txtscroll.xmin, st->txtscroll.ymax - st->txtscroll.ymin);
  1039. UI_GetThemeColor3ubv(TH_HILITE, col);
  1040. col[3] = 48;
  1041. glColor4ubv(col);
  1042. glEnable(GL_BLEND);
  1043. uiRoundBox(st->txtscroll.xmin + 1, st->txtscroll.ymin, st->txtscroll.xmax - 1, st->txtscroll.ymax, rad);
  1044. glDisable(GL_BLEND);
  1045. }
  1046. /************************** draw markers **************************/
  1047. static void draw_markers(SpaceText *st, ARegion *ar)
  1048. {
  1049. Text *text = st->text;
  1050. TextMarker *marker, *next;
  1051. TextLine *top, *line;
  1052. int offl, offc, i, x1, x2, y1, y2, x, y;
  1053. int topi, topy;
  1054. /* Move pointer to first visible line (top) */
  1055. top = first_visible_line(st, ar, NULL);
  1056. topi = BLI_findindex(&text->lines, top);
  1057. topy = txt_get_span(text->lines.first, top);
  1058. for (marker = text->markers.first; marker; marker = next) {
  1059. next = marker->next;
  1060. /* invisible line (before top) */
  1061. if (marker->lineno < topi) continue;
  1062. line = BLI_findlink(&text->lines, marker->lineno);
  1063. /* Remove broken markers */
  1064. if (marker->end > line->len || marker->start > marker->end) {
  1065. BLI_freelinkN(&text->markers, marker);
  1066. continue;
  1067. }
  1068. wrap_offset(st, ar, line, marker->start, &offl, &offc);
  1069. y1 = txt_get_span(top, line) - st->top + offl + topy;
  1070. x1 = text_get_char_pos(st, line->line, marker->start) - st->left + offc;
  1071. wrap_offset(st, ar, line, marker->end, &offl, &offc);
  1072. y2 = txt_get_span(top, line) - st->top + offl + topy;
  1073. x2 = text_get_char_pos(st, line->line, marker->end) - st->left + offc;
  1074. /* invisible part of line (before top, after last visible line) */
  1075. if (y2 < 0 || y1 > st->top + st->viewlines) continue;
  1076. glColor3ubv(marker->color);
  1077. x = st->showlinenrs ? TXT_OFFSET + TEXTXLOC : TXT_OFFSET;
  1078. y = ar->winy - 3;
  1079. if (y1 == y2) {
  1080. y -= y1 * st->lheight;
  1081. glBegin(GL_LINE_LOOP);
  1082. glVertex2i(x + x2 * st->cwidth + 1, y);
  1083. glVertex2i(x + x1 * st->cwidth - 2, y);
  1084. glVertex2i(x + x1 * st->cwidth - 2, y - st->lheight);
  1085. glVertex2i(x + x2 * st->cwidth + 1, y - st->lheight);
  1086. glEnd();
  1087. }
  1088. else {
  1089. y -= y1 * st->lheight;
  1090. glBegin(GL_LINE_STRIP);
  1091. glVertex2i(ar->winx, y);
  1092. glVertex2i(x + x1 * st->cwidth - 2, y);
  1093. glVertex2i(x + x1 * st->cwidth - 2, y - st->lheight);
  1094. glVertex2i(ar->winx, y - st->lheight);
  1095. glEnd();
  1096. y -= st->lheight;
  1097. for (i = y1 + 1; i < y2; i++) {
  1098. glBegin(GL_LINES);
  1099. glVertex2i(x, y);
  1100. glVertex2i(ar->winx, y);
  1101. glVertex2i(x, y - st->lheight);
  1102. glVertex2i(ar->winx, y - st->lheight);
  1103. glEnd();
  1104. y -= st->lheight;
  1105. }
  1106. glBegin(GL_LINE_STRIP);
  1107. glVertex2i(x, y);
  1108. glVertex2i(x + x2 * st->cwidth + 1, y);
  1109. glVertex2i(x + x2 * st->cwidth + 1, y - st->lheight);
  1110. glVertex2i(x, y - st->lheight);
  1111. glEnd();
  1112. }
  1113. }
  1114. }
  1115. /*********************** draw documentation *******************************/
  1116. static void draw_documentation(SpaceText *st, ARegion *ar)
  1117. {
  1118. TextLine *tmp;
  1119. char *docs, buf[DOC_WIDTH + 1], *p;
  1120. int i, br, lines;
  1121. int boxw, boxh, l, x, y /* , top */ /* UNUSED */;
  1122. if (!st || !st->text) return;
  1123. if (!texttool_text_is_active(st->text)) return;
  1124. docs = texttool_docs_get();
  1125. if (!docs) return;
  1126. /* Count the visible lines to the cursor */
  1127. for (tmp = st->text->curl, l = -st->top; tmp; tmp = tmp->prev, l++) ;
  1128. if (l < 0) return;
  1129. if (st->showlinenrs) {
  1130. x = st->cwidth * (st->text->curc - st->left) + TXT_OFFSET + TEXTXLOC - 4;
  1131. }
  1132. else {
  1133. x = st->cwidth * (st->text->curc - st->left) + TXT_OFFSET - 4;
  1134. }
  1135. if (texttool_suggest_first()) {
  1136. x += SUGG_LIST_WIDTH * st->cwidth + 50;
  1137. }
  1138. /* top= */ /* UNUSED */ y = ar->winy - st->lheight * l - 2;
  1139. boxw = DOC_WIDTH * st->cwidth + 20;
  1140. boxh = (DOC_HEIGHT + 1) * st->lheight;
  1141. /* Draw panel */
  1142. UI_ThemeColor(TH_BACK);
  1143. glRecti(x, y, x + boxw, y - boxh);
  1144. UI_ThemeColor(TH_SHADE1);
  1145. glBegin(GL_LINE_LOOP);
  1146. glVertex2i(x, y);
  1147. glVertex2i(x + boxw, y);
  1148. glVertex2i(x + boxw, y - boxh);
  1149. glVertex2i(x, y - boxh);
  1150. glEnd();
  1151. glBegin(GL_LINE_LOOP);
  1152. glVertex2i(x + boxw - 10, y - 7);
  1153. glVertex2i(x + boxw - 4, y - 7);
  1154. glVertex2i(x + boxw - 7, y - 2);
  1155. glEnd();
  1156. glBegin(GL_LINE_LOOP);
  1157. glVertex2i(x + boxw - 10, y - boxh + 7);
  1158. glVertex2i(x + boxw - 4, y - boxh + 7);
  1159. glVertex2i(x + boxw - 7, y - boxh + 2);
  1160. glEnd();
  1161. UI_ThemeColor(TH_TEXT);
  1162. i = 0; br = DOC_WIDTH; lines = 0; // XXX -doc_scroll;
  1163. for (p = docs; *p; p++) {
  1164. if (*p == '\r' && *(++p) != '\n') *(--p) = '\n'; /* Fix line endings */
  1165. if (*p == ' ' || *p == '\t')
  1166. br = i;
  1167. else if (*p == '\n') {
  1168. buf[i] = '\0';
  1169. if (lines >= 0) {
  1170. y -= st->lheight;
  1171. text_draw(st, buf, 0, 0, 1, x + 4, y - 3, NULL);
  1172. }
  1173. i = 0; br = DOC_WIDTH; lines++;
  1174. }
  1175. buf[i++] = *p;
  1176. if (i == DOC_WIDTH) { /* Reached the width, go to last break and wrap there */
  1177. buf[br] = '\0';
  1178. if (lines >= 0) {
  1179. y -= st->lheight;
  1180. text_draw(st, buf, 0, 0, 1, x + 4, y - 3, NULL);
  1181. }
  1182. p -= i - br - 1; /* Rewind pointer to last break */
  1183. i = 0; br = DOC_WIDTH; lines++;
  1184. }
  1185. if (lines >= DOC_HEIGHT) break;
  1186. }
  1187. if (0 /* XXX doc_scroll*/ > 0 && lines < DOC_HEIGHT) {
  1188. // XXX doc_scroll--;
  1189. draw_documentation(st, ar);
  1190. }
  1191. }
  1192. /*********************** draw suggestion list *******************************/
  1193. static void draw_suggestion_list(SpaceText *st, ARegion *ar)
  1194. {
  1195. SuggItem *item, *first, *last, *sel;
  1196. TextLine *tmp;
  1197. char str[SUGG_LIST_WIDTH + 1];
  1198. int w, boxw = 0, boxh, i, l, x, y, b, *top;
  1199. if (!st || !st->text) return;
  1200. if (!texttool_text_is_active(st->text)) return;
  1201. first = texttool_suggest_first();
  1202. last = texttool_suggest_last();
  1203. if (!first || !last) return;
  1204. text_pop_suggest_list();
  1205. sel = texttool_suggest_selected();
  1206. top = texttool_suggest_top();
  1207. /* Count the visible lines to the cursor */
  1208. for (tmp = st->text->curl, l = -st->top; tmp; tmp = tmp->prev, l++) ;
  1209. if (l < 0) return;
  1210. if (st->showlinenrs) {
  1211. x = st->cwidth * (st->text->curc - st->left) + TXT_OFFSET + TEXTXLOC - 4;
  1212. }
  1213. else {
  1214. x = st->cwidth * (st->text->curc - st->left) + TXT_OFFSET - 4;
  1215. }
  1216. y = ar->winy - st->lheight * l - 2;
  1217. boxw = SUGG_LIST_WIDTH * st->cwidth + 20;
  1218. boxh = SUGG_LIST_SIZE * st->lheight + 8;
  1219. UI_ThemeColor(TH_SHADE1);
  1220. glRecti(x - 1, y + 1, x + boxw + 1, y - boxh - 1);
  1221. UI_ThemeColor(TH_BACK);
  1222. glRecti(x, y, x + boxw, y - boxh);
  1223. /* Set the top 'item' of the visible list */
  1224. for (i = 0, item = first; i < *top && item->next; i++, item = item->next) ;
  1225. for (i = 0; i < SUGG_LIST_SIZE && item; i++, item = item->next) {
  1226. y -= st->lheight;
  1227. BLI_strncpy(str, item->name, SUGG_LIST_WIDTH);
  1228. w = BLF_width(mono, str);
  1229. if (item == sel) {
  1230. UI_ThemeColor(TH_SHADE2);
  1231. glRecti(x + 16, y - 3, x + 16 + w, y + st->lheight - 3);
  1232. }
  1233. b = 1; /* b=1 color block, text is default. b=0 no block, color text */
  1234. switch (item->type) {
  1235. case 'k': UI_ThemeColor(TH_SYNTAX_B); b = 0; break;
  1236. case 'm': UI_ThemeColor(TH_TEXT); break;
  1237. case 'f': UI_ThemeColor(TH_SYNTAX_L); break;
  1238. case 'v': UI_ThemeColor(TH_SYNTAX_N); break;
  1239. case '?': UI_ThemeColor(TH_TEXT); b = 0; break;
  1240. }
  1241. if (b) {
  1242. glRecti(x + 8, y + 2, x + 11, y + 5);
  1243. UI_ThemeColor(TH_TEXT);
  1244. }
  1245. text_draw(st, str, 0, 0, 1, x + 16, y - 1, NULL);
  1246. if (item == last) break;
  1247. }
  1248. }
  1249. /*********************** draw cursor ************************/
  1250. static void draw_cursor(SpaceText *st, ARegion *ar)
  1251. {
  1252. Text *text = st->text;
  1253. int vcurl, vcurc, vsell, vselc, hidden = 0;
  1254. int x, y, w, i;
  1255. /* Draw the selection */
  1256. if (text->curl != text->sell || text->curc != text->selc) {
  1257. int offl, offc;
  1258. /* Convert all to view space character coordinates */
  1259. wrap_offset(st, ar, text->curl, text->curc, &offl, &offc);
  1260. vcurl = txt_get_span(text->lines.first, text->curl) - st->top + offl;
  1261. vcurc = text_get_char_pos(st, text->curl->line, text->curc) - st->left + offc;
  1262. wrap_offset(st, ar, text->sell, text->selc, &offl, &offc);
  1263. vsell = txt_get_span(text->lines.first, text->sell) - st->top + offl;
  1264. vselc = text_get_char_pos(st, text->sell->line, text->selc) - st->left + offc;
  1265. if (vcurc < 0) vcurc = 0;
  1266. if (vselc < 0) vselc = 0, hidden = 1;
  1267. UI_ThemeColor(TH_SHADE2);
  1268. x = st->showlinenrs ? TXT_OFFSET + TEXTXLOC : TXT_OFFSET;
  1269. y = ar->winy - 2;
  1270. if (vcurl == vsell) {
  1271. y -= vcurl * st->lheight;
  1272. if (vcurc < vselc)
  1273. glRecti(x + vcurc * st->cwidth - 1, y, x + vselc * st->cwidth, y - st->lheight);
  1274. else
  1275. glRecti(x + vselc * st->cwidth - 1, y, x + vcurc * st->cwidth, y - st->lheight);
  1276. }
  1277. else {
  1278. int froml, fromc, tol, toc;
  1279. if (vcurl < vsell) {
  1280. froml = vcurl; tol = vsell;
  1281. fromc = vcurc; toc = vselc;
  1282. }
  1283. else {
  1284. froml = vsell; tol = vcurl;
  1285. fromc = vselc; toc = vcurc;
  1286. }
  1287. y -= froml * st->lheight;
  1288. glRecti(x + fromc * st->cwidth - 1, y, ar->winx, y - st->lheight); y -= st->lheight;
  1289. for (i = froml + 1; i < tol; i++)
  1290. glRecti(x - 4, y, ar->winx, y - st->lheight), y -= st->lheight;
  1291. glRecti(x - 4, y, x + toc * st->cwidth, y - st->lheight); y -= st->lheight;
  1292. }
  1293. }
  1294. else {
  1295. int offl, offc;
  1296. wrap_offset(st, ar, text->sell, text->selc, &offl, &offc);
  1297. vsell = txt_get_span(text->lines.first, text->sell) - st->top + offl;
  1298. vselc = text_get_char_pos(st, text->sell->line, text->selc) - st->left + offc;
  1299. if (vselc < 0) {
  1300. vselc = 0;
  1301. hidden = 1;
  1302. }
  1303. }
  1304. if (st->line_hlight) {
  1305. int x1, x2, y1, y2;
  1306. if (st->wordwrap) {
  1307. int visible_lines = text_get_visible_lines(st, ar, text->sell->line);
  1308. int offl, offc;
  1309. wrap_offset_in_line(st, ar, text->sell, text->selc, &offl, &offc);
  1310. y1 = ar->winy - 2 - (vsell - offl) * st->lheight;
  1311. y2 = y1 - st->lheight * visible_lines + 1;
  1312. }
  1313. else {
  1314. y1 = ar->winy - 2 - vsell * st->lheight;
  1315. y2 = y1 - st->lheight + 1;
  1316. }
  1317. if (!(y1 < 0 || y2 > ar->winy)) { /* check we need to draw */
  1318. x1 = st->showlinenrs ? TXT_OFFSET + TEXTXLOC : TXT_OFFSET;
  1319. x2 = x1 + ar->winx;
  1320. glColor4ub(255, 255, 255, 32);
  1321. glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
  1322. glEnable(GL_BLEND);
  1323. glRecti(x1 - 4, y1, x2, y2);
  1324. glDisable(GL_BLEND);
  1325. }
  1326. }
  1327. if (!hidden) {
  1328. /* Draw the cursor itself (we draw the sel. cursor as this is the leading edge) */
  1329. x = st->showlinenrs ? TXT_OFFSET + TEXTXLOC : TXT_OFFSET;
  1330. x += vselc * st->cwidth;
  1331. y = ar->winy - 2 - vsell * st->lheight;
  1332. if (st->overwrite) {
  1333. char ch = text->sell->line[text->selc];
  1334. w = st->cwidth;
  1335. if (ch == '\t') w *= st->tabnumber - (vselc + st->left) % st->tabnumber;
  1336. UI_ThemeColor(TH_HILITE);
  1337. glRecti(x, y - st->lheight - 1, x + w, y - st->lheight + 1);
  1338. }
  1339. else {
  1340. UI_ThemeColor(TH_HILITE);
  1341. glRecti(x - 1, y, x + 1, y - st->lheight);
  1342. }
  1343. }
  1344. }
  1345. /******************* draw matching brackets *********************/
  1346. static void draw_brackets(SpaceText *st, ARegion *ar)
  1347. {
  1348. TextLine *startl, *endl, *linep;
  1349. Text *text = st->text;
  1350. int b, fc, find, stack, viewc, viewl, offl, offc, x, y;
  1351. int startc, endc, c;
  1352. char ch;
  1353. // showsyntax must be on or else the format string will be null
  1354. if (!text->curl || !st->showsyntax) return;
  1355. startl = text->curl;
  1356. startc = text->curc;
  1357. b = text_check_bracket(startl->line[startc]);
  1358. if (b == 0 && startc > 0) b = text_check_bracket(startl->line[--startc]);
  1359. if (b == 0) return;
  1360. linep = startl;
  1361. c = startc;
  1362. fc = txt_utf8_offset_to_index(linep->line, startc);
  1363. endl = NULL;
  1364. endc = -1;
  1365. find = -b;
  1366. stack = 0;
  1367. /* Don't highlight backets if syntax HL is off or bracket in string or comment. */
  1368. if (!linep->format || linep->format[fc] == 'l' || linep->format[fc] == '#')
  1369. return;
  1370. if (b > 0) {
  1371. /* opening bracket, search forward for close */
  1372. fc++;
  1373. c += BLI_str_utf8_size(linep->line + c);
  1374. while (linep) {
  1375. while (c < linep->len) {
  1376. if (linep->format && linep->format[fc] != 'l' && linep->format[fc] != '#') {
  1377. b = text_check_bracket(linep->line[c]);
  1378. if (b == find) {
  1379. if (stack == 0) {
  1380. endl = linep;
  1381. endc = c;
  1382. break;
  1383. }
  1384. stack--;
  1385. }
  1386. else if (b == -find) {
  1387. stack++;
  1388. }
  1389. }
  1390. fc++;
  1391. c += BLI_str_utf8_size(linep->line + c);
  1392. }
  1393. if (endl) break;
  1394. linep = linep->next;
  1395. c = 0;
  1396. fc = 0;
  1397. }
  1398. }
  1399. else {
  1400. /* closing bracket, search backward for open */
  1401. fc--;
  1402. if (c > 0) c -= linep->line + c - BLI_str_prev_char_utf8(linep->line + c);
  1403. while (linep) {
  1404. while (fc >= 0) {
  1405. if (linep->format && linep->format[fc] != 'l' && linep->format[fc] != '#') {
  1406. b = text_check_bracket(linep->line[c]);
  1407. if (b == find) {
  1408. if (stack == 0) {
  1409. endl = linep;
  1410. endc = c;
  1411. break;
  1412. }
  1413. stack--;
  1414. }
  1415. else if (b == -find) {
  1416. stack++;
  1417. }
  1418. }
  1419. fc--;
  1420. if (c > 0) c -= linep->line + c - BLI_str_prev_char_utf8(linep->line + c);
  1421. }
  1422. if (endl) break;
  1423. linep = linep->prev;
  1424. if (linep) {
  1425. if (linep->format) fc = strlen(linep->format) - 1;
  1426. else fc = -1;
  1427. if (linep->len) c = BLI_str_prev_char_utf8(linep->line + linep->len) - linep->line;
  1428. else fc = -1;
  1429. }
  1430. }
  1431. }
  1432. if (!endl || endc == -1)
  1433. return;
  1434. UI_ThemeColor(TH_HILITE);
  1435. x = st->showlinenrs ? TXT_OFFSET + TEXTXLOC : TXT_OFFSET;
  1436. y = ar->winy - st->lheight;
  1437. /* draw opening bracket */
  1438. ch = startl->line[startc];
  1439. wrap_offset(st, ar, startl, startc, &offl, &offc);
  1440. viewc = text_get_char_pos(st, startl->line, startc) - st->left + offc;
  1441. if (viewc >= 0) {
  1442. viewl = txt_get_span(text->lines.first, startl) - st->top + offl;
  1443. text_font_draw_character(st, x + viewc * st->cwidth, y - viewl * st->lheight, ch);
  1444. text_font_draw_character(st, x + viewc * st->cwidth + 1, y - viewl * st->lheight, ch);
  1445. }
  1446. /* draw closing bracket */
  1447. ch = endl->line[endc];
  1448. wrap_offset(st, ar, endl, endc, &offl, &offc);
  1449. viewc = text_get_char_pos(st, endl->line, endc) - st->left + offc;
  1450. if (viewc >= 0) {
  1451. viewl = txt_get_span(text->lines.first, endl) - st->top + offl;
  1452. text_font_draw_character(st, x + viewc * st->cwidth, y - viewl * st->lheight, ch);
  1453. text_font_draw_character(st, x + viewc * st->cwidth + 1, y - viewl * st->lheight, ch);
  1454. }
  1455. }
  1456. /*********************** main area drawing *************************/
  1457. void draw_text_main(SpaceText *st, ARegion *ar)
  1458. {
  1459. Text *text = st->text;
  1460. TextLine *tmp;
  1461. rcti scroll, back;
  1462. char linenr[12];
  1463. int i, x, y, winx, linecount = 0, lineno = 0;
  1464. int wraplinecount = 0, wrap_skip = 0;
  1465. if (st->lheight) st->viewlines = (int)ar->winy / st->lheight;
  1466. else st->viewlines = 0;
  1467. /* if no text, nothing to do */
  1468. if (!text)
  1469. return;
  1470. text_update_drawcache(st, ar);
  1471. /* make sure all the positional pointers exist */
  1472. if (!text->curl || !text->sell || !text->lines.first || !text->lines.last)
  1473. txt_clean_text(text);
  1474. /* update rects for scroll */
  1475. calc_text_rcts(st, ar, &scroll, &back); /* scroll will hold the entire bar size */
  1476. /* update syntax formatting if needed */
  1477. tmp = text->lines.first;
  1478. lineno = 0;
  1479. for (i = 0; i < st->top && tmp; i++) {
  1480. if (st->showsyntax && !tmp->format)
  1481. txt_format_line(st, tmp, 0);
  1482. if (st->wordwrap) {
  1483. int lines = text_get_visible_lines_no(st, lineno);
  1484. if (wraplinecount + lines > st->top) {
  1485. wrap_skip = st->top - wraplinecount;
  1486. break;
  1487. }
  1488. else {
  1489. wraplinecount += lines;
  1490. tmp = tmp->next;
  1491. linecount++;
  1492. }
  1493. }
  1494. else {
  1495. tmp = tmp->next;
  1496. linecount++;
  1497. }
  1498. lineno++;
  1499. }
  1500. text_font_begin(st);
  1501. st->cwidth = BLF_fixed_width(mono);
  1502. st->cwidth = MAX2(st->cwidth, 1);
  1503. /* draw line numbers background */
  1504. if (st->showlinenrs) {
  1505. x = TXT_OFFSET + TEXTXLOC;
  1506. UI_ThemeColor(TH_GRID);
  1507. glRecti((TXT_OFFSET - 12), 0, (TXT_OFFSET - 5) + TEXTXLOC, ar->winy - 2);
  1508. }
  1509. else {
  1510. st->linenrs_tot = 0; /* not used */
  1511. x = TXT_OFFSET;
  1512. }
  1513. y = ar->winy - st->lheight;
  1514. winx = ar->winx - TXT_SCROLL_WIDTH;
  1515. /* draw cursor */
  1516. draw_cursor(st, ar);
  1517. /* draw the text */
  1518. UI_ThemeColor(TH_TEXT);
  1519. for (i = 0; y > 0 && i < st->viewlines && tmp; i++, tmp = tmp->next) {
  1520. if (st->showsyntax && !tmp->format)
  1521. txt_format_line(st, tmp, 0);
  1522. if (st->showlinenrs && !wrap_skip) {
  1523. /* draw line number */
  1524. if (tmp == text->curl)
  1525. UI_ThemeColor(TH_HILITE);
  1526. else
  1527. UI_ThemeColor(TH_TEXT);
  1528. BLI_snprintf(linenr, sizeof(linenr), "%*d", st->linenrs_tot, i + linecount + 1);
  1529. /* itoa(i + linecount + 1, linenr, 10); */ /* not ansi-c :/ */
  1530. text_font_draw(st, TXT_OFFSET - 7, y, linenr);
  1531. UI_ThemeColor(TH_TEXT);
  1532. }
  1533. if (st->wordwrap) {
  1534. /* draw word wrapped text */
  1535. int lines = text_draw_wrapped(st, tmp->line, x, y, winx - x, tmp->format, wrap_skip);
  1536. y -= lines * st->lheight;
  1537. }
  1538. else {
  1539. /* draw unwrapped text */
  1540. text_draw(st, tmp->line, st->left, ar->winx / st->cwidth, 1, x, y, tmp->format);
  1541. y -= st->lheight;
  1542. }
  1543. wrap_skip = 0;
  1544. }
  1545. if (st->flags & ST_SHOW_MARGIN) {
  1546. UI_ThemeColor(TH_HILITE);
  1547. glBegin(GL_LINES);
  1548. glVertex2i(x + st->cwidth * st->margin_column, 0);
  1549. glVertex2i(x + st->cwidth * st->margin_column, ar->winy - 2);
  1550. glEnd();
  1551. }
  1552. /* draw other stuff */
  1553. draw_brackets(st, ar);
  1554. draw_markers(st, ar);
  1555. glTranslatef(0.375f, 0.375f, 0.0f); /* XXX scroll requires exact pixel space */
  1556. draw_textscroll(st, &scroll, &back);
  1557. draw_documentation(st, ar);
  1558. draw_suggestion_list(st, ar);
  1559. text_font_end(st);
  1560. }
  1561. /************************** update ***************************/
  1562. void text_update_character_width(SpaceText *st)
  1563. {
  1564. text_font_begin(st);
  1565. st->cwidth = BLF_fixed_width(mono);
  1566. st->cwidth = MAX2(st->cwidth, 1);
  1567. text_font_end(st);
  1568. }
  1569. /* Moves the view to the cursor location,
  1570. * also used to make sure the view isn't outside the file */
  1571. void text_scroll_to_cursor(SpaceText *st, ScrArea *sa)
  1572. {
  1573. Text *text;
  1574. ARegion *ar = NULL;
  1575. int i, x, winx = 0;
  1576. if (ELEM3(NULL, st, st->text, st->text->curl)) return;
  1577. text = st->text;
  1578. for (ar = sa->regionbase.first; ar; ar = ar->next)
  1579. if (ar->regiontype == RGN_TYPE_WINDOW) {
  1580. winx = ar->winx;
  1581. break;
  1582. }
  1583. winx -= TXT_SCROLL_WIDTH;
  1584. text_update_character_width(st);
  1585. i = txt_get_span(text->lines.first, text->sell);
  1586. if (st->wordwrap) {
  1587. int offl, offc;
  1588. wrap_offset(st, ar, text->sell, text->selc, &offl, &offc);
  1589. i += offl;
  1590. }
  1591. if (st->top + st->viewlines <= i || st->top > i)
  1592. st->top = i - st->viewlines / 2;
  1593. if (st->wordwrap) {
  1594. st->left = 0;
  1595. }
  1596. else {
  1597. x = text_draw(st, text->sell->line, st->left, text->selc, 0, 0, 0, NULL);
  1598. if (x == 0 || x > winx)
  1599. st->left = text->curc - 0.5 * winx / st->cwidth;
  1600. }
  1601. if (st->top < 0) st->top = 0;
  1602. if (st->left < 0) st->left = 0;
  1603. }
  1604. void text_update_cursor_moved(bContext *C)
  1605. {
  1606. ScrArea *sa = CTX_wm_area(C);
  1607. SpaceText *st = CTX_wm_space_text(C);
  1608. text_scroll_to_cursor(st, sa);
  1609. }