Blender  V3.3
text.c
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later
2  * Copyright 2001-2002 NaN Holding BV. All rights reserved. */
3 
8 #include <stdlib.h> /* abort */
9 #include <string.h> /* strstr */
10 #include <sys/stat.h>
11 #include <sys/types.h>
12 #include <wctype.h>
13 
14 #include "MEM_guardedalloc.h"
15 
16 #include "BLI_fileops.h"
17 #include "BLI_listbase.h"
18 #include "BLI_path_util.h"
19 #include "BLI_string.h"
20 #include "BLI_string_cursor_utf8.h"
21 #include "BLI_string_utf8.h"
22 #include "BLI_utildefines.h"
23 
24 #include "BLT_translation.h"
25 
26 #include "DNA_constraint_types.h"
27 #include "DNA_material_types.h"
28 #include "DNA_node_types.h"
29 #include "DNA_object_types.h"
30 #include "DNA_scene_types.h"
31 #include "DNA_screen_types.h"
32 #include "DNA_space_types.h"
33 #include "DNA_text_types.h"
34 #include "DNA_userdef_types.h"
35 
36 #include "BKE_bpath.h"
37 #include "BKE_idtype.h"
38 #include "BKE_lib_id.h"
39 #include "BKE_main.h"
40 #include "BKE_node.h"
41 #include "BKE_text.h"
42 
43 #include "BLO_read_write.h"
44 
45 #ifdef WITH_PYTHON
46 # include "BPY_extern.h"
47 #endif
48 
49 /* -------------------------------------------------------------------- */
53 static void txt_pop_first(Text *text);
54 static void txt_pop_last(Text *text);
55 static void txt_delete_line(Text *text, TextLine *line);
56 static void txt_delete_sel(Text *text);
57 static void txt_make_dirty(Text *text);
58 
61 /* -------------------------------------------------------------------- */
65 static void text_init_data(ID *id)
66 {
67  Text *text = (Text *)id;
68  TextLine *tmp;
69 
71 
72  text->filepath = NULL;
73 
74  text->flags = TXT_ISDIRTY | TXT_ISMEM;
75  if ((U.flag & USER_TXT_TABSTOSPACES_DISABLE) == 0) {
76  text->flags |= TXT_TABSTOSPACES;
77  }
78 
79  BLI_listbase_clear(&text->lines);
80 
81  tmp = (TextLine *)MEM_mallocN(sizeof(TextLine), "textline");
82  tmp->line = (char *)MEM_mallocN(1, "textline_string");
83  tmp->format = NULL;
84 
85  tmp->line[0] = 0;
86  tmp->len = 0;
87 
88  tmp->next = NULL;
89  tmp->prev = NULL;
90 
91  BLI_addhead(&text->lines, tmp);
92 
93  text->curl = text->lines.first;
94  text->curc = 0;
95  text->sell = text->lines.first;
96  text->selc = 0;
97 }
98 
109 static void text_copy_data(Main *UNUSED(bmain),
110  ID *id_dst,
111  const ID *id_src,
112  const int UNUSED(flag))
113 {
114  Text *text_dst = (Text *)id_dst;
115  const Text *text_src = (Text *)id_src;
116 
117  /* File name can be NULL. */
118  if (text_src->filepath) {
119  text_dst->filepath = BLI_strdup(text_src->filepath);
120  }
121 
122  text_dst->flags |= TXT_ISDIRTY;
123 
124  BLI_listbase_clear(&text_dst->lines);
125  text_dst->curl = text_dst->sell = NULL;
126  text_dst->compiled = NULL;
127 
128  /* Walk down, reconstructing. */
129  LISTBASE_FOREACH (TextLine *, line_src, &text_src->lines) {
130  TextLine *line_dst = MEM_mallocN(sizeof(*line_dst), __func__);
131 
132  line_dst->line = BLI_strdup(line_src->line);
133  line_dst->format = NULL;
134  line_dst->len = line_src->len;
135 
136  BLI_addtail(&text_dst->lines, line_dst);
137  }
138 
139  text_dst->curl = text_dst->sell = text_dst->lines.first;
140  text_dst->curc = text_dst->selc = 0;
141 }
142 
144 static void text_free_data(ID *id)
145 {
146  /* No animation-data here. */
147  Text *text = (Text *)id;
148 
149  BKE_text_free_lines(text);
150 
151  MEM_SAFE_FREE(text->filepath);
152 #ifdef WITH_PYTHON
153  BPY_text_free_code(text);
154 #endif
155 }
156 
157 static void text_foreach_path(ID *id, BPathForeachPathData *bpath_data)
158 {
159  Text *text = (Text *)id;
160 
161  if (text->filepath != NULL) {
163  }
164 }
165 
166 static void text_blend_write(BlendWriter *writer, ID *id, const void *id_address)
167 {
168  Text *text = (Text *)id;
169 
170  /* NOTE: we are clearing local temp data here, *not* the flag in the actual 'real' ID. */
171  if ((text->flags & TXT_ISMEM) && (text->flags & TXT_ISEXT)) {
172  text->flags &= ~TXT_ISEXT;
173  }
174 
175  /* Clean up, important in undo case to reduce false detection of changed datablocks. */
176  text->compiled = NULL;
177 
178  /* write LibData */
179  BLO_write_id_struct(writer, Text, id_address, &text->id);
180  BKE_id_blend_write(writer, &text->id);
181 
182  if (text->filepath) {
183  BLO_write_string(writer, text->filepath);
184  }
185 
186  if (!(text->flags & TXT_ISEXT)) {
187  /* now write the text data, in two steps for optimization in the readfunction */
188  LISTBASE_FOREACH (TextLine *, tmp, &text->lines) {
189  BLO_write_struct(writer, TextLine, tmp);
190  }
191 
192  LISTBASE_FOREACH (TextLine *, tmp, &text->lines) {
193  BLO_write_raw(writer, tmp->len + 1, tmp->line);
194  }
195  }
196 }
197 
198 static void text_blend_read_data(BlendDataReader *reader, ID *id)
199 {
200  Text *text = (Text *)id;
201  BLO_read_data_address(reader, &text->filepath);
202 
203  text->compiled = NULL;
204 
205 #if 0
206  if (text->flags & TXT_ISEXT) {
207  BKE_text_reload(text);
208  }
209  /* else { */
210 #endif
211 
212  BLO_read_list(reader, &text->lines);
213 
214  BLO_read_data_address(reader, &text->curl);
215  BLO_read_data_address(reader, &text->sell);
216 
217  LISTBASE_FOREACH (TextLine *, ln, &text->lines) {
218  BLO_read_data_address(reader, &ln->line);
219  ln->format = NULL;
220 
221  if (ln->len != (int)strlen(ln->line)) {
222  printf("Error loading text, line lengths differ\n");
223  ln->len = strlen(ln->line);
224  }
225  }
226 
227  text->flags = (text->flags) & ~TXT_ISEXT;
228 }
229 
231  .id_code = ID_TXT,
232  .id_filter = FILTER_ID_TXT,
233  .main_listbase_index = INDEX_ID_TXT,
234  .struct_size = sizeof(Text),
235  .name = "Text",
236  .name_plural = "texts",
237  .translation_context = BLT_I18NCONTEXT_ID_TEXT,
239  .asset_type_info = NULL,
240 
242  .copy_data = text_copy_data,
243  .free_data = text_free_data,
244  .make_local = NULL,
245  .foreach_id = NULL,
246  .foreach_cache = NULL,
247  .foreach_path = text_foreach_path,
248  .owner_get = NULL,
249 
250  .blend_write = text_blend_write,
251  .blend_read_data = text_blend_read_data,
252  .blend_read_lib = NULL,
253  .blend_read_expand = NULL,
254 
255  .blend_read_undo_preserve = NULL,
256 
257  .lib_override_apply_post = NULL,
258 };
259 
262 /* -------------------------------------------------------------------- */
267 {
268  for (TextLine *tmp = text->lines.first, *tmp_next; tmp; tmp = tmp_next) {
269  tmp_next = tmp->next;
270  MEM_freeN(tmp->line);
271  if (tmp->format) {
272  MEM_freeN(tmp->format);
273  }
274  MEM_freeN(tmp);
275  }
276 
277  BLI_listbase_clear(&text->lines);
278 
279  text->curl = text->sell = NULL;
280 }
281 
282 Text *BKE_text_add(Main *bmain, const char *name)
283 {
284  Text *ta;
285 
286  ta = BKE_id_new(bmain, ID_TXT, name);
287  /* Texts have no users by default... Set the fake user flag to ensure that this text block
288  * doesn't get deleted by default when cleaning up data blocks. */
289  id_us_min(&ta->id);
290  id_fake_user_set(&ta->id);
291 
292  return ta;
293 }
294 
296 {
297  ptrdiff_t bad_char, i = 0;
298  const ptrdiff_t length = (ptrdiff_t)strlen(*str);
299  int added = 0;
300 
301  while ((*str)[i]) {
302  if ((bad_char = BLI_str_utf8_invalid_byte(*str + i, length - i)) == -1) {
303  break;
304  }
305 
306  added++;
307  i += bad_char + 1;
308  }
309 
310  if (added != 0) {
311  char *newstr = MEM_mallocN(length + added + 1, "text_line");
312  ptrdiff_t mi = 0;
313  i = 0;
314 
315  while ((*str)[i]) {
316  if ((bad_char = BLI_str_utf8_invalid_byte((*str) + i, length - i)) == -1) {
317  memcpy(newstr + mi, (*str) + i, length - i + 1);
318  break;
319  }
320 
321  memcpy(newstr + mi, (*str) + i, bad_char);
322 
323  const int mofs = mi + bad_char;
324  BLI_str_utf8_from_unicode((*str)[i + bad_char], newstr + mofs, (length + added) - mofs);
325  i += bad_char + 1;
326  mi += bad_char + 2;
327  }
328  newstr[length + added] = '\0';
329  MEM_freeN(*str);
330  *str = newstr;
331  }
332 
333  return added;
334 }
335 
339 static void cleanup_textline(TextLine *tl)
340 {
341  int i;
342 
343  for (i = 0; i < tl->len; i++) {
344  if (tl->line[i] < ' ' && tl->line[i] != '\t') {
345  memmove(tl->line + i, tl->line + i + 1, tl->len - i);
346  tl->len--;
347  i--;
348  }
349  }
350  tl->len += txt_extended_ascii_as_utf8(&tl->line);
351 }
352 
357 static void text_from_buf(Text *text, const unsigned char *buffer, const int len)
358 {
359  int i, llen, lines_count;
360 
362 
363  llen = 0;
364  lines_count = 0;
365  for (i = 0; i < len; i++) {
366  if (buffer[i] == '\n') {
367  TextLine *tmp;
368 
369  tmp = (TextLine *)MEM_mallocN(sizeof(TextLine), "textline");
370  tmp->line = (char *)MEM_mallocN(llen + 1, "textline_string");
371  tmp->format = NULL;
372 
373  if (llen) {
374  memcpy(tmp->line, &buffer[i - llen], llen);
375  }
376  tmp->line[llen] = 0;
377  tmp->len = llen;
378 
379  cleanup_textline(tmp);
380 
381  BLI_addtail(&text->lines, tmp);
382  lines_count += 1;
383 
384  llen = 0;
385  continue;
386  }
387  llen++;
388  }
389 
390  /* create new line in cases:
391  * - rest of line (if last line in file hasn't got \n terminator).
392  * in this case content of such line would be used to fill text line buffer
393  * - file is empty. in this case new line is needed to start editing from.
394  * - last character in buffer is \n. in this case new line is needed to
395  * deal with newline at end of file. (see T28087) (sergey) */
396  if (llen != 0 || lines_count == 0 || buffer[len - 1] == '\n') {
397  TextLine *tmp;
398 
399  tmp = (TextLine *)MEM_mallocN(sizeof(TextLine), "textline");
400  tmp->line = (char *)MEM_mallocN(llen + 1, "textline_string");
401  tmp->format = NULL;
402 
403  if (llen) {
404  memcpy(tmp->line, &buffer[i - llen], llen);
405  }
406 
407  tmp->line[llen] = 0;
408  tmp->len = llen;
409 
410  cleanup_textline(tmp);
411 
412  BLI_addtail(&text->lines, tmp);
413  /* lines_count += 1; */ /* UNUSED */
414  }
415 
416  text->curl = text->sell = text->lines.first;
417  text->curc = text->selc = 0;
418 }
419 
421 {
422  unsigned char *buffer;
423  size_t buffer_len;
424  char filepath_abs[FILE_MAX];
425  BLI_stat_t st;
426 
427  if (!text->filepath) {
428  return false;
429  }
430 
431  BLI_strncpy(filepath_abs, text->filepath, FILE_MAX);
432  BLI_path_abs(filepath_abs, ID_BLEND_PATH_FROM_GLOBAL(&text->id));
433 
434  buffer = BLI_file_read_text_as_mem(filepath_abs, 0, &buffer_len);
435  if (buffer == NULL) {
436  return false;
437  }
438 
439  /* free memory: */
440  BKE_text_free_lines(text);
441  txt_make_dirty(text);
442 
443  /* clear undo buffer */
444  if (BLI_stat(filepath_abs, &st) != -1) {
445  text->mtime = st.st_mtime;
446  }
447  else {
448  text->mtime = 0;
449  }
450 
451  text_from_buf(text, buffer, buffer_len);
452 
453  MEM_freeN(buffer);
454  return true;
455 }
456 
458  const char *filepath,
459  const char *relbase,
460  const bool is_internal)
461 {
462  unsigned char *buffer;
463  size_t buffer_len;
464  Text *ta;
465  char filepath_abs[FILE_MAX];
466  BLI_stat_t st;
467 
468  BLI_strncpy(filepath_abs, filepath, FILE_MAX);
469  BLI_path_abs(filepath_abs, relbase);
470 
471  buffer = BLI_file_read_text_as_mem(filepath_abs, 0, &buffer_len);
472  if (buffer == NULL) {
473  return NULL;
474  }
475 
476  ta = BKE_libblock_alloc(bmain, ID_TXT, BLI_path_basename(filepath_abs), 0);
477  id_us_min(&ta->id);
478  id_fake_user_set(&ta->id);
479 
481  ta->curl = ta->sell = NULL;
482 
483  if ((U.flag & USER_TXT_TABSTOSPACES_DISABLE) == 0) {
484  ta->flags = TXT_TABSTOSPACES;
485  }
486 
487  if (is_internal == false) {
488  const size_t filepath_len = strlen(filepath);
489  ta->filepath = MEM_mallocN(filepath_len + 1, "text_name");
490  memcpy(ta->filepath, filepath, filepath_len + 1);
491  }
492  else {
493  ta->flags |= TXT_ISMEM | TXT_ISDIRTY;
494  }
495 
496  /* clear undo buffer */
497  if (BLI_stat(filepath_abs, &st) != -1) {
498  ta->mtime = st.st_mtime;
499  }
500  else {
501  ta->mtime = 0;
502  }
503 
504  text_from_buf(ta, buffer, buffer_len);
505 
506  MEM_freeN(buffer);
507 
508  return ta;
509 }
510 
511 Text *BKE_text_load(Main *bmain, const char *filepath, const char *relbase)
512 {
513  return BKE_text_load_ex(bmain, filepath, relbase, false);
514 }
515 
516 void BKE_text_clear(Text *text) /* called directly from rna */
517 {
518  txt_sel_all(text);
519  txt_delete_sel(text);
520  txt_make_dirty(text);
521 }
522 
523 void BKE_text_write(Text *text, const char *str, int str_len) /* called directly from rna */
524 {
525  txt_insert_buf(text, str, str_len);
526  txt_move_eof(text, 0);
527  txt_make_dirty(text);
528 }
529 
531 {
532  BLI_stat_t st;
533  int result;
534  char filepath[FILE_MAX];
535 
536  if (!text->filepath) {
537  return 0;
538  }
539 
540  BLI_strncpy(filepath, text->filepath, FILE_MAX);
541  BLI_path_abs(filepath, ID_BLEND_PATH_FROM_GLOBAL(&text->id));
542 
543  if (!BLI_exists(filepath)) {
544  return 2;
545  }
546 
547  result = BLI_stat(filepath, &st);
548 
549  if (result == -1) {
550  return -1;
551  }
552 
553  if ((st.st_mode & S_IFMT) != S_IFREG) {
554  return -1;
555  }
556 
557  if (st.st_mtime > text->mtime) {
558  return 1;
559  }
560 
561  return 0;
562 }
563 
565 {
566  BLI_stat_t st;
567  int result;
568  char filepath[FILE_MAX];
569 
570  if (!text->filepath) {
571  return;
572  }
573 
574  BLI_strncpy(filepath, text->filepath, FILE_MAX);
575  BLI_path_abs(filepath, ID_BLEND_PATH_FROM_GLOBAL(&text->id));
576 
577  if (!BLI_exists(filepath)) {
578  return;
579  }
580 
581  result = BLI_stat(filepath, &st);
582 
583  if (result == -1 || (st.st_mode & S_IFMT) != S_IFREG) {
584  return;
585  }
586 
587  text->mtime = st.st_mtime;
588 }
589 
592 /* -------------------------------------------------------------------- */
596 static void make_new_line(TextLine *line, char *newline)
597 {
598  if (line->line) {
599  MEM_freeN(line->line);
600  }
601  if (line->format) {
602  MEM_freeN(line->format);
603  }
604 
605  line->line = newline;
606  line->len = strlen(newline);
607  line->format = NULL;
608 }
609 
610 static TextLine *txt_new_linen(const char *str, int str_len)
611 {
612  TextLine *tmp;
613 
614  tmp = (TextLine *)MEM_mallocN(sizeof(TextLine), "textline");
615  tmp->line = MEM_mallocN(str_len + 1, "textline_string");
616  tmp->format = NULL;
617 
618  memcpy(tmp->line, str, str_len);
619  tmp->line[str_len] = '\0';
620  tmp->len = str_len;
621  tmp->next = tmp->prev = NULL;
622 
623  BLI_assert(strlen(tmp->line) == str_len);
624 
625  return tmp;
626 }
627 
628 static TextLine *txt_new_line(const char *str)
629 {
630  return txt_new_linen(str, strlen(str));
631 }
632 
633 void txt_clean_text(Text *text)
634 {
635  TextLine **top, **bot;
636 
637  if (!text->lines.first) {
638  if (text->lines.last) {
639  text->lines.first = text->lines.last;
640  }
641  else {
642  text->lines.first = text->lines.last = txt_new_line("");
643  }
644  }
645 
646  if (!text->lines.last) {
647  text->lines.last = text->lines.first;
648  }
649 
650  top = (TextLine **)&text->lines.first;
651  bot = (TextLine **)&text->lines.last;
652 
653  while ((*top)->prev) {
654  *top = (*top)->prev;
655  }
656  while ((*bot)->next) {
657  *bot = (*bot)->next;
658  }
659 
660  if (!text->curl) {
661  if (text->sell) {
662  text->curl = text->sell;
663  }
664  else {
665  text->curl = text->lines.first;
666  }
667  text->curc = 0;
668  }
669 
670  if (!text->sell) {
671  text->sell = text->curl;
672  text->selc = 0;
673  }
674 }
675 
677 {
678  int ret = 0;
679  TextLine *tmp = from;
680 
681  if (!to || !from) {
682  return 0;
683  }
684  if (from == to) {
685  return 0;
686  }
687 
688  /* Look forwards */
689  while (tmp) {
690  if (tmp == to) {
691  return ret;
692  }
693  ret++;
694  tmp = tmp->next;
695  }
696 
697  /* Look backwards */
698  if (!tmp) {
699  tmp = from;
700  ret = 0;
701  while (tmp) {
702  if (tmp == to) {
703  break;
704  }
705  ret--;
706  tmp = tmp->prev;
707  }
708  if (!tmp) {
709  ret = 0;
710  }
711  }
712 
713  return ret;
714 }
715 
716 static void txt_make_dirty(Text *text)
717 {
718  text->flags |= TXT_ISDIRTY;
719 #ifdef WITH_PYTHON
720  if (text->compiled) {
721  BPY_text_free_code(text);
722  }
723 #endif
724 }
725 
728 /* -------------------------------------------------------------------- */
732 static void txt_curs_cur(Text *text, TextLine ***linep, int **charp)
733 {
734  *linep = &text->curl;
735  *charp = &text->curc;
736 }
737 
738 static void txt_curs_sel(Text *text, TextLine ***linep, int **charp)
739 {
740  *linep = &text->sell;
741  *charp = &text->selc;
742 }
743 
745 {
746  return (text->selc == 0);
747 }
748 
749 bool txt_cursor_is_line_end(const Text *text)
750 {
751  return (text->selc == text->sell->len);
752 }
753 
756 /* -------------------------------------------------------------------- */
764 void txt_move_up(Text *text, const bool sel)
765 {
766  TextLine **linep;
767  int *charp;
768 
769  if (sel) {
770  txt_curs_sel(text, &linep, &charp);
771  }
772  else {
773  txt_pop_first(text);
774  txt_curs_cur(text, &linep, &charp);
775  }
776  if (!*linep) {
777  return;
778  }
779 
780  if ((*linep)->prev) {
781  int column = BLI_str_utf8_offset_to_column((*linep)->line, *charp);
782  *linep = (*linep)->prev;
783  *charp = BLI_str_utf8_offset_from_column((*linep)->line, column);
784  }
785  else {
786  txt_move_bol(text, sel);
787  }
788 
789  if (!sel) {
790  txt_pop_sel(text);
791  }
792 }
793 
794 void txt_move_down(Text *text, const bool sel)
795 {
796  TextLine **linep;
797  int *charp;
798 
799  if (sel) {
800  txt_curs_sel(text, &linep, &charp);
801  }
802  else {
803  txt_pop_last(text);
804  txt_curs_cur(text, &linep, &charp);
805  }
806  if (!*linep) {
807  return;
808  }
809 
810  if ((*linep)->next) {
811  int column = BLI_str_utf8_offset_to_column((*linep)->line, *charp);
812  *linep = (*linep)->next;
813  *charp = BLI_str_utf8_offset_from_column((*linep)->line, column);
814  }
815  else {
816  txt_move_eol(text, sel);
817  }
818 
819  if (!sel) {
820  txt_pop_sel(text);
821  }
822 }
823 
824 int txt_calc_tab_left(TextLine *tl, int ch)
825 {
826  /* do nice left only if there are only spaces */
827 
828  int tabsize = (ch < TXT_TABSIZE) ? ch : TXT_TABSIZE;
829 
830  for (int i = 0; i < ch; i++) {
831  if (tl->line[i] != ' ') {
832  tabsize = 0;
833  break;
834  }
835  }
836 
837  /* if in the middle of the space-tab */
838  if (tabsize && ch % TXT_TABSIZE != 0) {
839  tabsize = (ch % TXT_TABSIZE);
840  }
841  return tabsize;
842 }
843 
844 int txt_calc_tab_right(TextLine *tl, int ch)
845 {
846  if (tl->line[ch] == ' ') {
847  int i;
848  for (i = 0; i < ch; i++) {
849  if (tl->line[i] != ' ') {
850  return 0;
851  }
852  }
853 
854  int tabsize = (ch) % TXT_TABSIZE + 1;
855  for (i = ch + 1; tl->line[i] == ' ' && tabsize < TXT_TABSIZE; i++) {
856  tabsize++;
857  }
858 
859  return i - ch;
860  }
861 
862  return 0;
863 }
864 
865 void txt_move_left(Text *text, const bool sel)
866 {
867  TextLine **linep;
868  int *charp;
869  int tabsize = 0;
870 
871  if (sel) {
872  txt_curs_sel(text, &linep, &charp);
873  }
874  else {
875  txt_pop_first(text);
876  txt_curs_cur(text, &linep, &charp);
877  }
878  if (!*linep) {
879  return;
880  }
881 
882  if (*charp == 0) {
883  if ((*linep)->prev) {
884  txt_move_up(text, sel);
885  *charp = (*linep)->len;
886  }
887  }
888  else {
889  /* do nice left only if there are only spaces */
890  /* #TXT_TABSIZE hard-coded in DNA_text_types.h */
891  if (text->flags & TXT_TABSTOSPACES) {
892  tabsize = txt_calc_tab_left(*linep, *charp);
893  }
894 
895  if (tabsize) {
896  (*charp) -= tabsize;
897  }
898  else {
899  const char *prev = BLI_str_find_prev_char_utf8((*linep)->line + *charp, (*linep)->line);
900  *charp = prev - (*linep)->line;
901  }
902  }
903 
904  if (!sel) {
905  txt_pop_sel(text);
906  }
907 }
908 
909 void txt_move_right(Text *text, const bool sel)
910 {
911  TextLine **linep;
912  int *charp;
913 
914  if (sel) {
915  txt_curs_sel(text, &linep, &charp);
916  }
917  else {
918  txt_pop_last(text);
919  txt_curs_cur(text, &linep, &charp);
920  }
921  if (!*linep) {
922  return;
923  }
924 
925  if (*charp == (*linep)->len) {
926  if ((*linep)->next) {
927  txt_move_down(text, sel);
928  *charp = 0;
929  }
930  }
931  else {
932  /* do nice right only if there are only spaces */
933  /* spaces hardcoded in DNA_text_types.h */
934  int tabsize = 0;
935 
936  if (text->flags & TXT_TABSTOSPACES) {
937  tabsize = txt_calc_tab_right(*linep, *charp);
938  }
939 
940  if (tabsize) {
941  (*charp) += tabsize;
942  }
943  else {
944  (*charp) += BLI_str_utf8_size((*linep)->line + *charp);
945  }
946  }
947 
948  if (!sel) {
949  txt_pop_sel(text);
950  }
951 }
952 
953 void txt_jump_left(Text *text, const bool sel, const bool use_init_step)
954 {
955  TextLine **linep;
956  int *charp;
957 
958  if (sel) {
959  txt_curs_sel(text, &linep, &charp);
960  }
961  else {
962  txt_pop_first(text);
963  txt_curs_cur(text, &linep, &charp);
964  }
965  if (!*linep) {
966  return;
967  }
968 
970  (*linep)->line, (*linep)->len, charp, STRCUR_DIR_PREV, STRCUR_JUMP_DELIM, use_init_step);
971 
972  if (!sel) {
973  txt_pop_sel(text);
974  }
975 }
976 
977 void txt_jump_right(Text *text, const bool sel, const bool use_init_step)
978 {
979  TextLine **linep;
980  int *charp;
981 
982  if (sel) {
983  txt_curs_sel(text, &linep, &charp);
984  }
985  else {
986  txt_pop_last(text);
987  txt_curs_cur(text, &linep, &charp);
988  }
989  if (!*linep) {
990  return;
991  }
992 
994  (*linep)->line, (*linep)->len, charp, STRCUR_DIR_NEXT, STRCUR_JUMP_DELIM, use_init_step);
995 
996  if (!sel) {
997  txt_pop_sel(text);
998  }
999 }
1000 
1001 void txt_move_bol(Text *text, const bool sel)
1002 {
1003  TextLine **linep;
1004  int *charp;
1005 
1006  if (sel) {
1007  txt_curs_sel(text, &linep, &charp);
1008  }
1009  else {
1010  txt_curs_cur(text, &linep, &charp);
1011  }
1012  if (!*linep) {
1013  return;
1014  }
1015 
1016  *charp = 0;
1017 
1018  if (!sel) {
1019  txt_pop_sel(text);
1020  }
1021 }
1022 
1023 void txt_move_eol(Text *text, const bool sel)
1024 {
1025  TextLine **linep;
1026  int *charp;
1027 
1028  if (sel) {
1029  txt_curs_sel(text, &linep, &charp);
1030  }
1031  else {
1032  txt_curs_cur(text, &linep, &charp);
1033  }
1034  if (!*linep) {
1035  return;
1036  }
1037 
1038  *charp = (*linep)->len;
1039 
1040  if (!sel) {
1041  txt_pop_sel(text);
1042  }
1043 }
1044 
1045 void txt_move_bof(Text *text, const bool sel)
1046 {
1047  TextLine **linep;
1048  int *charp;
1049 
1050  if (sel) {
1051  txt_curs_sel(text, &linep, &charp);
1052  }
1053  else {
1054  txt_curs_cur(text, &linep, &charp);
1055  }
1056  if (!*linep) {
1057  return;
1058  }
1059 
1060  *linep = text->lines.first;
1061  *charp = 0;
1062 
1063  if (!sel) {
1064  txt_pop_sel(text);
1065  }
1066 }
1067 
1068 void txt_move_eof(Text *text, const bool sel)
1069 {
1070  TextLine **linep;
1071  int *charp;
1072 
1073  if (sel) {
1074  txt_curs_sel(text, &linep, &charp);
1075  }
1076  else {
1077  txt_curs_cur(text, &linep, &charp);
1078  }
1079  if (!*linep) {
1080  return;
1081  }
1082 
1083  *linep = text->lines.last;
1084  *charp = (*linep)->len;
1085 
1086  if (!sel) {
1087  txt_pop_sel(text);
1088  }
1089 }
1090 
1091 void txt_move_toline(Text *text, unsigned int line, const bool sel)
1092 {
1093  txt_move_to(text, line, 0, sel);
1094 }
1095 
1096 void txt_move_to(Text *text, unsigned int line, unsigned int ch, const bool sel)
1097 {
1098  TextLine **linep;
1099  int *charp;
1100  unsigned int i;
1101 
1102  if (sel) {
1103  txt_curs_sel(text, &linep, &charp);
1104  }
1105  else {
1106  txt_curs_cur(text, &linep, &charp);
1107  }
1108  if (!*linep) {
1109  return;
1110  }
1111 
1112  *linep = text->lines.first;
1113  for (i = 0; i < line; i++) {
1114  if ((*linep)->next) {
1115  *linep = (*linep)->next;
1116  }
1117  else {
1118  break;
1119  }
1120  }
1121  if (ch > (unsigned int)((*linep)->len)) {
1122  ch = (unsigned int)((*linep)->len);
1123  }
1124  *charp = ch;
1125 
1126  if (!sel) {
1127  txt_pop_sel(text);
1128  }
1129 }
1130 
1133 /* -------------------------------------------------------------------- */
1137 static void txt_curs_swap(Text *text)
1138 {
1139  TextLine *tmpl;
1140  int tmpc;
1141 
1142  tmpl = text->curl;
1143  text->curl = text->sell;
1144  text->sell = tmpl;
1145 
1146  tmpc = text->curc;
1147  text->curc = text->selc;
1148  text->selc = tmpc;
1149 }
1150 
1151 static void txt_pop_first(Text *text)
1152 {
1153  if (txt_get_span(text->curl, text->sell) < 0 ||
1154  (text->curl == text->sell && text->curc > text->selc)) {
1155  txt_curs_swap(text);
1156  }
1157 
1158  txt_pop_sel(text);
1159 }
1160 
1161 static void txt_pop_last(Text *text)
1162 {
1163  if (txt_get_span(text->curl, text->sell) > 0 ||
1164  (text->curl == text->sell && text->curc < text->selc)) {
1165  txt_curs_swap(text);
1166  }
1167 
1168  txt_pop_sel(text);
1169 }
1170 
1171 void txt_pop_sel(Text *text)
1172 {
1173  text->sell = text->curl;
1174  text->selc = text->curc;
1175 }
1176 
1177 void txt_order_cursors(Text *text, const bool reverse)
1178 {
1179  if (!text->curl) {
1180  return;
1181  }
1182  if (!text->sell) {
1183  return;
1184  }
1185 
1186  /* Flip so text->curl is before/after text->sell */
1187  if (reverse == false) {
1188  if ((txt_get_span(text->curl, text->sell) < 0) ||
1189  (text->curl == text->sell && text->curc > text->selc)) {
1190  txt_curs_swap(text);
1191  }
1192  }
1193  else {
1194  if ((txt_get_span(text->curl, text->sell) > 0) ||
1195  (text->curl == text->sell && text->curc < text->selc)) {
1196  txt_curs_swap(text);
1197  }
1198  }
1199 }
1200 
1201 bool txt_has_sel(const Text *text)
1202 {
1203  return ((text->curl != text->sell) || (text->curc != text->selc));
1204 }
1205 
1206 static void txt_delete_sel(Text *text)
1207 {
1208  TextLine *tmpl;
1209  char *buf;
1210 
1211  if (!text->curl) {
1212  return;
1213  }
1214  if (!text->sell) {
1215  return;
1216  }
1217 
1218  if (!txt_has_sel(text)) {
1219  return;
1220  }
1221 
1222  txt_order_cursors(text, false);
1223 
1224  buf = MEM_mallocN(text->curc + (text->sell->len - text->selc) + 1, "textline_string");
1225 
1226  memcpy(buf, text->curl->line, text->curc);
1227  memcpy(buf + text->curc, text->sell->line + text->selc, text->sell->len - text->selc);
1228  buf[text->curc + (text->sell->len - text->selc)] = 0;
1229 
1230  make_new_line(text->curl, buf);
1231 
1232  tmpl = text->sell;
1233  while (tmpl != text->curl) {
1234  tmpl = tmpl->prev;
1235  if (!tmpl) {
1236  break;
1237  }
1238 
1239  txt_delete_line(text, tmpl->next);
1240  }
1241 
1242  text->sell = text->curl;
1243  text->selc = text->curc;
1244 }
1245 
1246 void txt_sel_all(Text *text)
1247 {
1248  text->curl = text->lines.first;
1249  text->curc = 0;
1250 
1251  text->sell = text->lines.last;
1252  text->selc = text->sell->len;
1253 }
1254 
1255 void txt_sel_clear(Text *text)
1256 {
1257  if (text->sell) {
1258  text->curl = text->sell;
1259  text->curc = text->selc;
1260  }
1261 }
1262 
1263 void txt_sel_line(Text *text)
1264 {
1265  if (!text->curl) {
1266  return;
1267  }
1268 
1269  text->curc = 0;
1270  text->sell = text->curl;
1271  text->selc = text->sell->len;
1272 }
1273 
1274 void txt_sel_set(Text *text, int startl, int startc, int endl, int endc)
1275 {
1276  TextLine *froml, *tol;
1277  int fromllen, tollen;
1278 
1279  /* Support negative indices. */
1280  if (startl < 0 || endl < 0) {
1281  int end = BLI_listbase_count(&text->lines) - 1;
1282  if (startl < 0) {
1283  startl = end + startl + 1;
1284  }
1285  if (endl < 0) {
1286  endl = end + endl + 1;
1287  }
1288  }
1289  CLAMP_MIN(startl, 0);
1290  CLAMP_MIN(endl, 0);
1291 
1292  froml = BLI_findlink(&text->lines, startl);
1293  if (froml == NULL) {
1294  froml = text->lines.last;
1295  }
1296  if (startl == endl) {
1297  tol = froml;
1298  }
1299  else {
1300  tol = BLI_findlink(&text->lines, endl);
1301  if (tol == NULL) {
1302  tol = text->lines.last;
1303  }
1304  }
1305 
1306  fromllen = BLI_strlen_utf8(froml->line);
1307  tollen = BLI_strlen_utf8(tol->line);
1308 
1309  /* Support negative indices. */
1310  if (startc < 0) {
1311  startc = fromllen + startc + 1;
1312  }
1313  if (endc < 0) {
1314  endc = tollen + endc + 1;
1315  }
1316 
1317  CLAMP(startc, 0, fromllen);
1318  CLAMP(endc, 0, tollen);
1319 
1320  text->curl = froml;
1321  text->curc = BLI_str_utf8_offset_from_index(froml->line, startc);
1322  text->sell = tol;
1323  text->selc = BLI_str_utf8_offset_from_index(tol->line, endc);
1324 }
1325 
1328 /* -------------------------------------------------------------------- */
1341 char *txt_to_buf_for_undo(Text *text, size_t *r_buf_len)
1342 {
1343  int buf_len = 0;
1344  LISTBASE_FOREACH (const TextLine *, l, &text->lines) {
1345  buf_len += l->len + 1;
1346  }
1347  char *buf = MEM_mallocN(buf_len, __func__);
1348  char *buf_step = buf;
1349  LISTBASE_FOREACH (const TextLine *, l, &text->lines) {
1350  memcpy(buf_step, l->line, l->len);
1351  buf_step += l->len;
1352  *buf_step++ = '\n';
1353  }
1354  *r_buf_len = buf_len;
1355  return buf;
1356 }
1357 
1358 void txt_from_buf_for_undo(Text *text, const char *buf, size_t buf_len)
1359 {
1360  const char *buf_end = buf + buf_len;
1361  const char *buf_step = buf;
1362 
1363  /* First re-use existing lines.
1364  * Good for undo since it means in practice many operations re-use all
1365  * except for the modified line. */
1366  TextLine *l_src = text->lines.first;
1367  BLI_listbase_clear(&text->lines);
1368  while (buf_step != buf_end && l_src) {
1369  /* New lines are ensured by #txt_to_buf_for_undo. */
1370  const char *buf_step_next = strchr(buf_step, '\n');
1371  const int len = buf_step_next - buf_step;
1372 
1373  TextLine *l = l_src;
1374  l_src = l_src->next;
1375  if (l->len != len) {
1376  l->line = MEM_reallocN(l->line, len + 1);
1377  l->len = len;
1378  }
1379  MEM_SAFE_FREE(l->format);
1380 
1381  memcpy(l->line, buf_step, len);
1382  l->line[len] = '\0';
1383  BLI_addtail(&text->lines, l);
1384  buf_step = buf_step_next + 1;
1385  }
1386 
1387  /* If we have extra lines. */
1388  while (l_src != NULL) {
1389  TextLine *l_src_next = l_src->next;
1390  MEM_freeN(l_src->line);
1391  if (l_src->format) {
1392  MEM_freeN(l_src->format);
1393  }
1394  MEM_freeN(l_src);
1395  l_src = l_src_next;
1396  }
1397 
1398  while (buf_step != buf_end) {
1399  /* New lines are ensured by #txt_to_buf_for_undo. */
1400  const char *buf_step_next = strchr(buf_step, '\n');
1401  const int len = buf_step_next - buf_step;
1402 
1403  TextLine *l = MEM_mallocN(sizeof(TextLine), "textline");
1404  l->line = MEM_mallocN(len + 1, "textline_string");
1405  l->len = len;
1406  l->format = NULL;
1407 
1408  memcpy(l->line, buf_step, len);
1409  l->line[len] = '\0';
1410  BLI_addtail(&text->lines, l);
1411  buf_step = buf_step_next + 1;
1412  }
1413 
1414  text->curl = text->sell = text->lines.first;
1415  text->curc = text->selc = 0;
1416 
1417  txt_make_dirty(text);
1418 }
1419 
1422 /* -------------------------------------------------------------------- */
1426 char *txt_to_buf(Text *text, size_t *r_buf_strlen)
1427 {
1428  const bool has_data = !BLI_listbase_is_empty(&text->lines);
1429  /* Identical to #txt_to_buf_for_undo except that the string is nil terminated. */
1430  size_t buf_len = 0;
1431  LISTBASE_FOREACH (const TextLine *, l, &text->lines) {
1432  buf_len += l->len + 1;
1433  }
1434  if (has_data) {
1435  buf_len -= 1;
1436  }
1437  char *buf = MEM_mallocN(buf_len + 1, __func__);
1438  char *buf_step = buf;
1439  LISTBASE_FOREACH (const TextLine *, l, &text->lines) {
1440  memcpy(buf_step, l->line, l->len);
1441  buf_step += l->len;
1442  *buf_step++ = '\n';
1443  }
1444  /* Remove the trailing new-line so a round-trip doesn't add a newline:
1445  * Python for e.g. `text.from_string(text.as_string())`. */
1446  if (has_data) {
1447  buf_step--;
1448  }
1449  *buf_step = '\0';
1450  *r_buf_strlen = buf_len;
1451  return buf;
1452 }
1453 
1454 char *txt_sel_to_buf(Text *text, size_t *r_buf_strlen)
1455 {
1456  char *buf;
1457  size_t length = 0;
1458  TextLine *tmp, *linef, *linel;
1459  int charf, charl;
1460 
1461  if (r_buf_strlen) {
1462  *r_buf_strlen = 0;
1463  }
1464 
1465  if (!text->curl) {
1466  return NULL;
1467  }
1468  if (!text->sell) {
1469  return NULL;
1470  }
1471 
1472  if (text->curl == text->sell) {
1473  linef = linel = text->curl;
1474 
1475  if (text->curc < text->selc) {
1476  charf = text->curc;
1477  charl = text->selc;
1478  }
1479  else {
1480  charf = text->selc;
1481  charl = text->curc;
1482  }
1483  }
1484  else if (txt_get_span(text->curl, text->sell) < 0) {
1485  linef = text->sell;
1486  linel = text->curl;
1487 
1488  charf = text->selc;
1489  charl = text->curc;
1490  }
1491  else {
1492  linef = text->curl;
1493  linel = text->sell;
1494 
1495  charf = text->curc;
1496  charl = text->selc;
1497  }
1498 
1499  if (linef == linel) {
1500  length = charl - charf;
1501  buf = MEM_mallocN(length + 1, "sel buffer");
1502  memcpy(buf, linef->line + charf, length);
1503  buf[length] = '\0';
1504  }
1505  else {
1506  /* Add 1 for the '\n' */
1507  length = (linef->len - charf) + charl + 1;
1508 
1509  for (tmp = linef->next; tmp && tmp != linel; tmp = tmp->next) {
1510  length += tmp->len + 1;
1511  }
1512 
1513  buf = MEM_mallocN(length + 1, "sel buffer");
1514 
1515  memcpy(buf, linef->line + charf, linef->len - charf);
1516  length = linef->len - charf;
1517  buf[length++] = '\n';
1518 
1519  for (tmp = linef->next; tmp && tmp != linel; tmp = tmp->next) {
1520  memcpy(buf + length, tmp->line, tmp->len);
1521  length += tmp->len;
1522  buf[length++] = '\n';
1523  }
1524 
1525  memcpy(buf + length, linel->line, charl);
1526  length += charl;
1527  buf[length] = '\0';
1528  }
1529 
1530  if (r_buf_strlen) {
1531  *r_buf_strlen = length;
1532  }
1533 
1534  return buf;
1535 }
1536 
1537 void txt_insert_buf(Text *text, const char *in_buffer, int in_buffer_len)
1538 {
1539  BLI_assert(in_buffer_len == strlen(in_buffer));
1540 
1541  int l = 0;
1542  size_t i = 0, j;
1543  TextLine *add;
1544  char *buffer;
1545 
1546  txt_delete_sel(text);
1547 
1548  buffer = BLI_strdupn(in_buffer, in_buffer_len);
1549  in_buffer_len += txt_extended_ascii_as_utf8(&buffer);
1550 
1551  /* Read the first line (or as close as possible */
1552  while (buffer[i] && buffer[i] != '\n') {
1553  txt_add_raw_char(text, BLI_str_utf8_as_unicode_step(buffer, in_buffer_len, &i));
1554  }
1555 
1556  if (buffer[i] == '\n') {
1557  txt_split_curline(text);
1558  i++;
1559 
1560  while (i < in_buffer_len) {
1561  l = 0;
1562 
1563  while (buffer[i] && buffer[i] != '\n') {
1564  i++;
1565  l++;
1566  }
1567 
1568  if (buffer[i] == '\n') {
1569  add = txt_new_linen(buffer + (i - l), l);
1570  BLI_insertlinkbefore(&text->lines, text->curl, add);
1571  i++;
1572  }
1573  else {
1574  for (j = i - l; j < i && j < in_buffer_len;) {
1575  txt_add_raw_char(text, BLI_str_utf8_as_unicode_step(buffer, in_buffer_len, &j));
1576  }
1577  break;
1578  }
1579  }
1580  }
1581 
1582  MEM_freeN(buffer);
1583 }
1584 
1587 /* -------------------------------------------------------------------- */
1591 int txt_find_string(Text *text, const char *findstr, int wrap, int match_case)
1592 {
1593  TextLine *tl, *startl;
1594  const char *s = NULL;
1595 
1596  if (!text->curl || !text->sell) {
1597  return 0;
1598  }
1599 
1600  txt_order_cursors(text, false);
1601 
1602  tl = startl = text->sell;
1603 
1604  if (match_case) {
1605  s = strstr(&tl->line[text->selc], findstr);
1606  }
1607  else {
1608  s = BLI_strcasestr(&tl->line[text->selc], findstr);
1609  }
1610  while (!s) {
1611  tl = tl->next;
1612  if (!tl) {
1613  if (wrap) {
1614  tl = text->lines.first;
1615  }
1616  else {
1617  break;
1618  }
1619  }
1620 
1621  if (match_case) {
1622  s = strstr(tl->line, findstr);
1623  }
1624  else {
1625  s = BLI_strcasestr(tl->line, findstr);
1626  }
1627  if (tl == startl) {
1628  break;
1629  }
1630  }
1631 
1632  if (s) {
1633  int newl = txt_get_span(text->lines.first, tl);
1634  int newc = (int)(s - tl->line);
1635  txt_move_to(text, newl, newc, 0);
1636  txt_move_to(text, newl, newc + strlen(findstr), 1);
1637  return 1;
1638  }
1639 
1640  return 0;
1641 }
1642 
1645 /* -------------------------------------------------------------------- */
1650 {
1651  TextLine *ins;
1652  char *left, *right;
1653 
1654  if (!text->curl) {
1655  return;
1656  }
1657 
1658  txt_delete_sel(text);
1659 
1660  /* Make the two half strings */
1661 
1662  left = MEM_mallocN(text->curc + 1, "textline_string");
1663  if (text->curc) {
1664  memcpy(left, text->curl->line, text->curc);
1665  }
1666  left[text->curc] = 0;
1667 
1668  right = MEM_mallocN(text->curl->len - text->curc + 1, "textline_string");
1669  memcpy(right, text->curl->line + text->curc, text->curl->len - text->curc + 1);
1670 
1671  MEM_freeN(text->curl->line);
1672  if (text->curl->format) {
1673  MEM_freeN(text->curl->format);
1674  }
1675 
1676  /* Make the new TextLine */
1677 
1678  ins = MEM_mallocN(sizeof(TextLine), "textline");
1679  ins->line = left;
1680  ins->format = NULL;
1681  ins->len = text->curc;
1682 
1683  text->curl->line = right;
1684  text->curl->format = NULL;
1685  text->curl->len = text->curl->len - text->curc;
1686 
1687  BLI_insertlinkbefore(&text->lines, text->curl, ins);
1688 
1689  text->curc = 0;
1690 
1691  txt_make_dirty(text);
1692  txt_clean_text(text);
1693 
1694  txt_pop_sel(text);
1695 }
1696 
1697 static void txt_delete_line(Text *text, TextLine *line)
1698 {
1699  if (!text->curl) {
1700  return;
1701  }
1702 
1703  BLI_remlink(&text->lines, line);
1704 
1705  if (line->line) {
1706  MEM_freeN(line->line);
1707  }
1708  if (line->format) {
1709  MEM_freeN(line->format);
1710  }
1711 
1712  MEM_freeN(line);
1713 
1714  txt_make_dirty(text);
1715  txt_clean_text(text);
1716 }
1717 
1718 static void txt_combine_lines(Text *text, TextLine *linea, TextLine *lineb)
1719 {
1720  char *tmp, *s;
1721 
1722  if (!linea || !lineb) {
1723  return;
1724  }
1725 
1726  tmp = MEM_mallocN(linea->len + lineb->len + 1, "textline_string");
1727 
1728  s = tmp;
1729  s += BLI_strcpy_rlen(s, linea->line);
1730  s += BLI_strcpy_rlen(s, lineb->line);
1731  (void)s;
1732 
1733  make_new_line(linea, tmp);
1734 
1735  txt_delete_line(text, lineb);
1736 
1737  txt_make_dirty(text);
1738  txt_clean_text(text);
1739 }
1740 
1742 {
1743  TextLine *textline;
1744 
1745  if (!text->curl) {
1746  return;
1747  }
1748 
1749  if (text->curl == text->sell) {
1750  textline = txt_new_line(text->curl->line);
1751  BLI_insertlinkafter(&text->lines, text->curl, textline);
1752 
1753  txt_make_dirty(text);
1754  txt_clean_text(text);
1755  }
1756 }
1757 
1759 {
1760  unsigned int c = '\n';
1761 
1762  if (!text->curl) {
1763  return;
1764  }
1765 
1766  if (txt_has_sel(text)) { /* deleting a selection */
1767  txt_delete_sel(text);
1768  txt_make_dirty(text);
1769  return;
1770  }
1771  if (text->curc == text->curl->len) { /* Appending two lines */
1772  if (text->curl->next) {
1773  txt_combine_lines(text, text->curl, text->curl->next);
1774  txt_pop_sel(text);
1775  }
1776  else {
1777  return;
1778  }
1779  }
1780  else { /* Just deleting a char */
1781  size_t c_len = text->curc;
1782  c = BLI_str_utf8_as_unicode_step(text->curl->line, text->curl->len, &c_len);
1783  c_len -= text->curc;
1784  UNUSED_VARS(c);
1785 
1786  memmove(text->curl->line + text->curc,
1787  text->curl->line + text->curc + c_len,
1788  text->curl->len - text->curc - c_len + 1);
1789 
1790  text->curl->len -= c_len;
1791 
1792  txt_pop_sel(text);
1793  }
1794 
1795  txt_make_dirty(text);
1796  txt_clean_text(text);
1797 }
1798 
1800 {
1801  txt_jump_right(text, true, true);
1802  txt_delete_sel(text);
1803  txt_make_dirty(text);
1804 }
1805 
1807 {
1808  unsigned int c = '\n';
1809 
1810  if (!text->curl) {
1811  return;
1812  }
1813 
1814  if (txt_has_sel(text)) { /* deleting a selection */
1815  txt_delete_sel(text);
1816  txt_make_dirty(text);
1817  return;
1818  }
1819  if (text->curc == 0) { /* Appending two lines */
1820  if (!text->curl->prev) {
1821  return;
1822  }
1823 
1824  text->curl = text->curl->prev;
1825  text->curc = text->curl->len;
1826 
1827  txt_combine_lines(text, text->curl, text->curl->next);
1828  txt_pop_sel(text);
1829  }
1830  else { /* Just backspacing a char */
1831  const char *prev = BLI_str_find_prev_char_utf8(text->curl->line + text->curc,
1832  text->curl->line);
1833  size_t c_len = prev - text->curl->line;
1834  c = BLI_str_utf8_as_unicode_step(text->curl->line, text->curl->len, &c_len);
1835  c_len -= prev - text->curl->line;
1836 
1837  UNUSED_VARS(c);
1838 
1839  /* source and destination overlap, don't use memcpy() */
1840  memmove(text->curl->line + text->curc - c_len,
1841  text->curl->line + text->curc,
1842  text->curl->len - text->curc + 1);
1843 
1844  text->curl->len -= c_len;
1845  text->curc -= c_len;
1846 
1847  txt_pop_sel(text);
1848  }
1849 
1850  txt_make_dirty(text);
1851  txt_clean_text(text);
1852 }
1853 
1855 {
1856  txt_jump_left(text, true, true);
1857  txt_delete_sel(text);
1858  txt_make_dirty(text);
1859 }
1860 
1861 /* Max spaces to replace a tab with, currently hardcoded to TXT_TABSIZE = 4.
1862  * Used by txt_convert_tab_to_spaces, indent and unindent.
1863  * Remember to change this string according to max tab size */
1864 static char tab_to_spaces[] = " ";
1865 
1867 {
1868  /* sb aims to pad adjust the tab-width needed so that the right number of spaces
1869  * is added so that the indentation of the line is the right width (i.e. aligned
1870  * to multiples of TXT_TABSIZE)
1871  */
1872  const char *sb = &tab_to_spaces[text->curc % TXT_TABSIZE];
1873  txt_insert_buf(text, sb, strlen(sb));
1874 }
1875 
1876 static bool txt_add_char_intern(Text *text, unsigned int add, bool replace_tabs)
1877 {
1878  char *tmp, ch[BLI_UTF8_MAX];
1879  size_t add_len;
1880 
1881  if (!text->curl) {
1882  return 0;
1883  }
1884 
1885  if (add == '\n') {
1886  txt_split_curline(text);
1887  return true;
1888  }
1889 
1890  /* insert spaces rather than tabs */
1891  if (add == '\t' && replace_tabs) {
1893  return true;
1894  }
1895 
1896  txt_delete_sel(text);
1897 
1898  add_len = BLI_str_utf8_from_unicode(add, ch, sizeof(ch));
1899 
1900  tmp = MEM_mallocN(text->curl->len + add_len + 1, "textline_string");
1901 
1902  memcpy(tmp, text->curl->line, text->curc);
1903  memcpy(tmp + text->curc, ch, add_len);
1904  memcpy(
1905  tmp + text->curc + add_len, text->curl->line + text->curc, text->curl->len - text->curc + 1);
1906 
1907  make_new_line(text->curl, tmp);
1908 
1909  text->curc += add_len;
1910 
1911  txt_pop_sel(text);
1912 
1913  txt_make_dirty(text);
1914  txt_clean_text(text);
1915 
1916  return 1;
1917 }
1918 
1919 bool txt_add_char(Text *text, unsigned int add)
1920 {
1921  return txt_add_char_intern(text, add, (text->flags & TXT_TABSTOSPACES) != 0);
1922 }
1923 
1924 bool txt_add_raw_char(Text *text, unsigned int add)
1925 {
1926  return txt_add_char_intern(text, add, 0);
1927 }
1928 
1930 {
1931  txt_delete_sel(text);
1932  txt_make_dirty(text);
1933 }
1934 
1935 bool txt_replace_char(Text *text, unsigned int add)
1936 {
1937  unsigned int del;
1938  size_t del_size = 0, add_size;
1939  char ch[BLI_UTF8_MAX];
1940 
1941  if (!text->curl) {
1942  return false;
1943  }
1944 
1945  /* If text is selected or we're at the end of the line just use txt_add_char */
1946  if (text->curc == text->curl->len || txt_has_sel(text) || add == '\n') {
1947  return txt_add_char(text, add);
1948  }
1949 
1950  del_size = text->curc;
1951  del = BLI_str_utf8_as_unicode_step(text->curl->line, text->curl->len, &del_size);
1952  del_size -= text->curc;
1953  UNUSED_VARS(del);
1954  add_size = BLI_str_utf8_from_unicode(add, ch, sizeof(ch));
1955 
1956  if (add_size > del_size) {
1957  char *tmp = MEM_mallocN(text->curl->len + add_size - del_size + 1, "textline_string");
1958  memcpy(tmp, text->curl->line, text->curc);
1959  memcpy(tmp + text->curc + add_size,
1960  text->curl->line + text->curc + del_size,
1961  text->curl->len - text->curc - del_size + 1);
1962  MEM_freeN(text->curl->line);
1963  text->curl->line = tmp;
1964  }
1965  else if (add_size < del_size) {
1966  char *tmp = text->curl->line;
1967  memmove(tmp + text->curc + add_size,
1968  tmp + text->curc + del_size,
1969  text->curl->len - text->curc - del_size + 1);
1970  }
1971 
1972  memcpy(text->curl->line + text->curc, ch, add_size);
1973  text->curc += add_size;
1974  text->curl->len += add_size - del_size;
1975 
1976  txt_pop_sel(text);
1977  txt_make_dirty(text);
1978  txt_clean_text(text);
1979  return true;
1980 }
1981 
1987 static void txt_select_prefix(Text *text, const char *add, bool skip_blank_lines)
1988 {
1989  int len, num, curc_old, selc_old;
1990  char *tmp;
1991 
1992  const int indentlen = strlen(add);
1993 
1994  BLI_assert(!ELEM(NULL, text->curl, text->sell));
1995 
1996  curc_old = text->curc;
1997  selc_old = text->selc;
1998 
1999  num = 0;
2000  while (true) {
2001 
2002  /* don't indent blank lines */
2003  if ((text->curl->len != 0) || (skip_blank_lines == 0)) {
2004  tmp = MEM_mallocN(text->curl->len + indentlen + 1, "textline_string");
2005 
2006  text->curc = 0;
2007  if (text->curc) {
2008  memcpy(tmp, text->curl->line, text->curc); /* XXX never true, check prev line */
2009  }
2010  memcpy(tmp + text->curc, add, indentlen);
2011 
2012  len = text->curl->len - text->curc;
2013  if (len > 0) {
2014  memcpy(tmp + text->curc + indentlen, text->curl->line + text->curc, len);
2015  }
2016  tmp[text->curl->len + indentlen] = 0;
2017 
2018  make_new_line(text->curl, tmp);
2019 
2020  text->curc += indentlen;
2021 
2022  txt_make_dirty(text);
2023  txt_clean_text(text);
2024  }
2025 
2026  if (text->curl == text->sell) {
2027  if (text->curl->len != 0) {
2028  text->selc += indentlen;
2029  }
2030  break;
2031  }
2032 
2033  text->curl = text->curl->next;
2034  num++;
2035  }
2036 
2037  while (num > 0) {
2038  text->curl = text->curl->prev;
2039  num--;
2040  }
2041 
2042  /* Keep the cursor left aligned if we don't have a selection. */
2043  if (curc_old == 0 && !(text->curl == text->sell && curc_old == selc_old)) {
2044  if (text->curl == text->sell) {
2045  if (text->curc == text->selc) {
2046  text->selc = 0;
2047  }
2048  }
2049  text->curc = 0;
2050  }
2051  else {
2052  if (text->curl->len != 0) {
2053  text->curc = curc_old + indentlen;
2054  }
2055  }
2056 }
2057 
2066 static bool txt_select_unprefix(Text *text, const char *remove, const bool require_all)
2067 {
2068  int num = 0;
2069  const int indentlen = strlen(remove);
2070  bool unindented_first = false;
2071  bool changed_any = false;
2072 
2073  BLI_assert(!ELEM(NULL, text->curl, text->sell));
2074 
2075  if (require_all) {
2076  /* Check all non-empty lines use this 'remove',
2077  * so the operation is applied equally or not at all. */
2078  TextLine *l = text->curl;
2079  while (true) {
2080  if (STREQLEN(l->line, remove, indentlen)) {
2081  /* pass */
2082  }
2083  else {
2084  /* Blank lines or whitespace can be skipped. */
2085  for (int i = 0; i < l->len; i++) {
2086  if (!ELEM(l->line[i], '\t', ' ')) {
2087  return false;
2088  }
2089  }
2090  }
2091  if (l == text->sell) {
2092  break;
2093  }
2094  l = l->next;
2095  }
2096  }
2097 
2098  while (true) {
2099  bool changed = false;
2100  if (STREQLEN(text->curl->line, remove, indentlen)) {
2101  if (num == 0) {
2102  unindented_first = true;
2103  }
2104  text->curl->len -= indentlen;
2105  memmove(text->curl->line, text->curl->line + indentlen, text->curl->len + 1);
2106  changed = true;
2107  changed_any = true;
2108  }
2109 
2110  txt_make_dirty(text);
2111  txt_clean_text(text);
2112 
2113  if (text->curl == text->sell) {
2114  if (changed) {
2115  text->selc = MAX2(text->selc - indentlen, 0);
2116  }
2117  break;
2118  }
2119 
2120  text->curl = text->curl->next;
2121  num++;
2122  }
2123 
2124  if (unindented_first) {
2125  text->curc = MAX2(text->curc - indentlen, 0);
2126  }
2127 
2128  while (num > 0) {
2129  text->curl = text->curl->prev;
2130  num--;
2131  }
2132 
2133  /* caller must handle undo */
2134  return changed_any;
2135 }
2136 
2137 void txt_comment(Text *text)
2138 {
2139  const char *prefix = "#";
2140 
2141  if (ELEM(NULL, text->curl, text->sell)) {
2142  return;
2143  }
2144 
2145  const bool skip_blank_lines = txt_has_sel(text);
2146  txt_select_prefix(text, prefix, skip_blank_lines);
2147 }
2148 
2149 bool txt_uncomment(Text *text)
2150 {
2151  const char *prefix = "#";
2152 
2153  if (ELEM(NULL, text->curl, text->sell)) {
2154  return false;
2155  }
2156 
2157  return txt_select_unprefix(text, prefix, true);
2158 }
2159 
2160 void txt_indent(Text *text)
2161 {
2162  const char *prefix = (text->flags & TXT_TABSTOSPACES) ? tab_to_spaces : "\t";
2163 
2164  if (ELEM(NULL, text->curl, text->sell)) {
2165  return;
2166  }
2167 
2168  txt_select_prefix(text, prefix, true);
2169 }
2170 
2171 bool txt_unindent(Text *text)
2172 {
2173  const char *prefix = (text->flags & TXT_TABSTOSPACES) ? tab_to_spaces : "\t";
2174 
2175  if (ELEM(NULL, text->curl, text->sell)) {
2176  return false;
2177  }
2178 
2179  return txt_select_unprefix(text, prefix, false);
2180 }
2181 
2182 void txt_move_lines(struct Text *text, const int direction)
2183 {
2184  TextLine *line_other;
2185 
2187 
2188  if (!text->curl || !text->sell) {
2189  return;
2190  }
2191 
2192  txt_order_cursors(text, false);
2193 
2194  line_other = (direction == TXT_MOVE_LINE_DOWN) ? text->sell->next : text->curl->prev;
2195 
2196  if (!line_other) {
2197  return;
2198  }
2199 
2200  BLI_remlink(&text->lines, line_other);
2201 
2202  if (direction == TXT_MOVE_LINE_DOWN) {
2203  BLI_insertlinkbefore(&text->lines, text->curl, line_other);
2204  }
2205  else {
2206  BLI_insertlinkafter(&text->lines, text->sell, line_other);
2207  }
2208 
2209  txt_make_dirty(text);
2210  txt_clean_text(text);
2211 }
2212 
2213 int txt_setcurr_tab_spaces(Text *text, int space)
2214 {
2215  int i = 0;
2216  int test = 0;
2217  const char *word = ":";
2218  const char *comm = "#";
2219  const char indent = (text->flags & TXT_TABSTOSPACES) ? ' ' : '\t';
2220  static const char *back_words[] = {"return", "break", "continue", "pass", "yield", NULL};
2221 
2222  if (!text->curl) {
2223  return 0;
2224  }
2225 
2226  while (text->curl->line[i] == indent) {
2227  /* We only count those tabs/spaces that are before any text or before the curs; */
2228  if (i == text->curc) {
2229  return i;
2230  }
2231 
2232  i++;
2233  }
2234  if (strstr(text->curl->line, word)) {
2235  /* if we find a ':' on this line, then add a tab but not if it is:
2236  * 1) in a comment
2237  * 2) within an identifier
2238  * 3) after the cursor (text->curc), i.e. when creating space before a function def T25414.
2239  */
2240  int a;
2241  bool is_indent = false;
2242  for (a = 0; (a < text->curc) && (text->curl->line[a] != '\0'); a++) {
2243  char ch = text->curl->line[a];
2244  if (ch == '#') {
2245  break;
2246  }
2247  if (ch == ':') {
2248  is_indent = 1;
2249  }
2250  else if (!ELEM(ch, ' ', '\t')) {
2251  is_indent = 0;
2252  }
2253  }
2254  if (is_indent) {
2255  i += space;
2256  }
2257  }
2258 
2259  for (test = 0; back_words[test]; test++) {
2260  /* if there are these key words then remove a tab because we are done with the block */
2261  if (strstr(text->curl->line, back_words[test]) && i > 0) {
2262  if (strcspn(text->curl->line, back_words[test]) < strcspn(text->curl->line, comm)) {
2263  i -= space;
2264  }
2265  }
2266  }
2267  return i;
2268 }
2269 
2272 /* -------------------------------------------------------------------- */
2276 int text_check_bracket(const char ch)
2277 {
2278  int a;
2279  char opens[] = "([{";
2280  char close[] = ")]}";
2281 
2282  for (a = 0; a < (sizeof(opens) - 1); a++) {
2283  if (ch == opens[a]) {
2284  return a + 1;
2285  }
2286  if (ch == close[a]) {
2287  return -(a + 1);
2288  }
2289  }
2290  return 0;
2291 }
2292 
2293 bool text_check_delim(const char ch)
2294 {
2295  /* TODO: have a function for operators:
2296  * http://docs.python.org/py3k/reference/lexical_analysis.html#operators */
2297 
2298  int a;
2299  char delims[] = "():\"\' ~!%^&*-+=[]{};/<>|.#\t,@";
2300 
2301  for (a = 0; a < (sizeof(delims) - 1); a++) {
2302  if (ch == delims[a]) {
2303  return true;
2304  }
2305  }
2306  return false;
2307 }
2308 
2309 bool text_check_digit(const char ch)
2310 {
2311  if (ch < '0') {
2312  return false;
2313  }
2314  if (ch <= '9') {
2315  return true;
2316  }
2317  return false;
2318 }
2319 
2320 bool text_check_identifier(const char ch)
2321 {
2322  if (ch < '0') {
2323  return false;
2324  }
2325  if (ch <= '9') {
2326  return true;
2327  }
2328  if (ch < 'A') {
2329  return false;
2330  }
2331  if (ch <= 'Z' || ch == '_') {
2332  return true;
2333  }
2334  if (ch < 'a') {
2335  return false;
2336  }
2337  if (ch <= 'z') {
2338  return true;
2339  }
2340  return false;
2341 }
2342 
2344 {
2345  if (ch <= '9') {
2346  return false;
2347  }
2348  if (ch < 'A') {
2349  return false;
2350  }
2351  if (ch <= 'Z' || ch == '_') {
2352  return true;
2353  }
2354  if (ch < 'a') {
2355  return false;
2356  }
2357  if (ch <= 'z') {
2358  return true;
2359  }
2360  return false;
2361 }
2362 
2363 #ifndef WITH_PYTHON
2364 int text_check_identifier_unicode(const unsigned int ch)
2365 {
2366  return (ch < 255 && text_check_identifier((unsigned int)ch));
2367 }
2368 
2369 int text_check_identifier_nodigit_unicode(const unsigned int ch)
2370 {
2371  return (ch < 255 && text_check_identifier_nodigit((char)ch));
2372 }
2373 #endif /* WITH_PYTHON */
2374 
2375 bool text_check_whitespace(const char ch)
2376 {
2377  if (ELEM(ch, ' ', '\t', '\r', '\n')) {
2378  return true;
2379  }
2380  return false;
2381 }
2382 
2383 int text_find_identifier_start(const char *str, int i)
2384 {
2385  if (UNLIKELY(i <= 0)) {
2386  return 0;
2387  }
2388 
2389  while (i--) {
2390  if (!text_check_identifier(str[i])) {
2391  break;
2392  }
2393  }
2394  i++;
2395  return i;
2396 }
2397 
bool BKE_bpath_foreach_path_allocated_process(struct BPathForeachPathData *bpath_data, char **path)
Definition: bpath.c:175
@ IDTYPE_FLAGS_APPEND_IS_REUSABLE
Definition: BKE_idtype.h:39
@ IDTYPE_FLAGS_NO_ANIMDATA
Definition: BKE_idtype.h:41
void id_us_min(struct ID *id)
Definition: lib_id.c:313
void * BKE_libblock_alloc(struct Main *bmain, short type, const char *name, int flag) ATTR_WARN_UNUSED_RESULT
Definition: lib_id.c:1050
void id_fake_user_set(struct ID *id)
Definition: lib_id.c:343
void BKE_id_blend_write(struct BlendWriter *writer, struct ID *id)
Definition: lib_id.c:2008
void * BKE_id_new(struct Main *bmain, short type, const char *name)
Definition: lib_id.c:1159
@ TXT_MOVE_LINE_UP
Definition: BKE_text.h:134
@ TXT_MOVE_LINE_DOWN
Definition: BKE_text.h:135
#define BLI_assert(a)
Definition: BLI_assert.h:46
File and directory operations.
int BLI_exists(const char *path) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition: storage.c:314
void * BLI_file_read_text_as_mem(const char *filepath, size_t pad_bytes, size_t *r_size)
Definition: storage.c:466
int BLI_stat(const char *path, BLI_stat_t *buffer) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
struct stat BLI_stat_t
Definition: BLI_fileops.h:73
BLI_INLINE bool BLI_listbase_is_empty(const struct ListBase *lb)
Definition: BLI_listbase.h:269
void BLI_addhead(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:60
#define LISTBASE_FOREACH(type, var, list)
Definition: BLI_listbase.h:336
BLI_INLINE void BLI_listbase_clear(struct ListBase *lb)
Definition: BLI_listbase.h:273
void BLI_insertlinkafter(struct ListBase *listbase, void *vprevlink, void *vnewlink) ATTR_NONNULL(1)
Definition: listbase.c:301
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:80
void BLI_remlink(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:100
void BLI_insertlinkbefore(struct ListBase *listbase, void *vnextlink, void *vnewlink) ATTR_NONNULL(1)
Definition: listbase.c:340
void * BLI_findlink(const struct ListBase *listbase, int number) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
int BLI_listbase_count(const struct ListBase *listbase) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
const char * BLI_path_basename(const char *path) ATTR_NONNULL() ATTR_WARN_UNUSED_RESULT
Definition: path_util.c:1653
#define FILE_MAX
bool BLI_path_abs(char *path, const char *basepath) ATTR_NONNULL()
Definition: path_util.c:897
char * BLI_strcasestr(const char *s, const char *find) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition: string.c:538
size_t BLI_strcpy_rlen(char *__restrict dst, const char *__restrict src) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition: string.c:134
char * BLI_strdup(const char *str) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL() ATTR_MALLOC
Definition: string.c:42
char * BLI_strdupn(const char *str, size_t len) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT ATTR_NONNULL()
Definition: string.c:33
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, size_t maxncpy) ATTR_NONNULL()
Definition: string.c:64
void BLI_str_cursor_step_utf8(const char *str, size_t maxlen, int *pos, eStrCursorJumpDirection direction, eStrCursorJumpType jump, bool use_init_step)
@ STRCUR_DIR_NEXT
@ STRCUR_DIR_PREV
@ STRCUR_JUMP_DELIM
int BLI_str_utf8_offset_from_column(const char *str, int column) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition: string_utf8.c:791
int BLI_str_utf8_size(const char *p) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition: string_utf8.c:452
char size_t ptrdiff_t BLI_str_utf8_invalid_byte(const char *str, size_t length) ATTR_NONNULL(1)
Definition: string_utf8.c:46
#define BLI_UTF8_MAX
int BLI_str_utf8_offset_from_index(const char *str, int index) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition: string_utf8.c:771
unsigned int BLI_str_utf8_as_unicode_step(const char *__restrict p, size_t p_len, size_t *__restrict index) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1
int BLI_str_utf8_offset_to_column(const char *str, int offset) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition: string_utf8.c:781
const char * BLI_str_find_prev_char_utf8(const char *p, const char *str_start) ATTR_WARN_UNUSED_RESULT ATTR_RETURNS_NONNULL ATTR_NONNULL(1
size_t BLI_strlen_utf8(const char *strc) ATTR_NONNULL(1) ATTR_WARN_UNUSED_RESULT
Definition: string_utf8.c:317
size_t BLI_str_utf8_from_unicode(unsigned int c, char *outbuf, size_t outbuf_len) ATTR_NONNULL(2)
Definition: string_utf8.c:575
#define UNUSED_VARS(...)
#define STREQLEN(a, b, n)
#define UNUSED(x)
#define MAX2(a, b)
#define UNLIKELY(x)
#define ELEM(...)
#define MEMCMP_STRUCT_AFTER_IS_ZERO(struct_var, member)
#define CLAMP_MIN(a, b)
#define BLO_read_data_address(reader, ptr_p)
#define BLO_write_id_struct(writer, struct_name, id_address, id)
#define BLO_write_struct(writer, struct_name, data_ptr)
void BLO_read_list(BlendDataReader *reader, struct ListBase *list)
Definition: readfile.c:5172
void BLO_write_string(BlendWriter *writer, const char *data_ptr)
Definition: writefile.c:1601
void BLO_write_raw(BlendWriter *writer, size_t size_in_bytes, const void *data_ptr)
Definition: writefile.c:1489
#define BLT_I18NCONTEXT_ID_TEXT
void BPY_text_free_code(struct Text *text)
@ INDEX_ID_TXT
Definition: DNA_ID.h:1001
#define ID_BLEND_PATH_FROM_GLOBAL(_id)
Definition: DNA_ID.h:561
#define FILTER_ID_TXT
Definition: DNA_ID.h:923
@ ID_TXT
Definition: DNA_ID_enums.h:62
Object is a sort of wrapper for general info.
struct Text Text
#define TXT_TABSIZE
@ TXT_TABSTOSPACES
@ TXT_ISDIRTY
@ TXT_ISMEM
@ TXT_ISEXT
@ USER_TXT_TABSTOSPACES_DISABLE
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint GLsizei GLsizei GLenum type _GL_VOID_RET _GL_VOID GLsizei GLenum GLenum const void *pixels _GL_VOID_RET _GL_VOID const void *pointer _GL_VOID_RET _GL_VOID GLdouble v _GL_VOID_RET _GL_VOID GLfloat v _GL_VOID_RET _GL_VOID GLint GLint i2 _GL_VOID_RET _GL_VOID GLint j _GL_VOID_RET _GL_VOID GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLdouble right
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint GLsizei GLsizei GLenum type _GL_VOID_RET _GL_VOID GLsizei GLenum GLenum const void *pixels _GL_VOID_RET _GL_VOID const void *pointer _GL_VOID_RET _GL_VOID GLdouble v _GL_VOID_RET _GL_VOID GLfloat v _GL_VOID_RET _GL_VOID GLint GLint i2 _GL_VOID_RET _GL_VOID GLint j _GL_VOID_RET _GL_VOID GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble top
Read Guarded memory(de)allocation.
#define MEM_SAFE_FREE(v)
#define MEM_reallocN(vmemh, len)
static void init_data(ModifierData *md)
Group Output data from inside of a node group A color picker Mix two input colors RGB to Convert a color s luminance to a grayscale value Generate a normal vector and a dot product Bright Control the brightness and contrast of the input color Vector Map an input vectors to used to fine tune the interpolation of the input Camera Retrieve information about the camera and how it relates to the current shading point s position CLAMP
ATTR_WARN_UNUSED_RESULT const BMLoop * l
unsigned int U
Definition: btGjkEpa3.h:78
StackEntry * from
SyclQueue void void size_t num_bytes void
int len
Definition: draw_manager.c:108
#define str(s)
ccl_global float * buffer
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:27
void *(* MEM_mallocN)(size_t len, const char *str)
Definition: mallocn.c:33
static int left
static unsigned c
Definition: RandGen.cpp:83
static unsigned a[3]
Definition: RandGen.cpp:78
bool add(void *owner, const AttributeIDRef &attribute_id, eAttrDomain domain, eCustomDataType data_type, const AttributeInit &initializer)
bool remove(void *owner, const AttributeIDRef &attribute_id)
static struct PartialUpdateUser * wrap(PartialUpdateUserImpl *user)
T length(const vec_base< T, Size > &a)
SymEdge< T > * prev(const SymEdge< T > *se)
Definition: delaunay_2d.cc:105
static const pxr::TfToken st("st", pxr::TfToken::Immortal)
return ret
struct BMLoop * next
Definition: bmesh_class.h:233
short id_code
Definition: BKE_idtype.h:114
Definition: DNA_ID.h:368
void * last
Definition: DNA_listBase.h:31
void * first
Definition: DNA_listBase.h:31
Definition: BKE_main.h:121
char * format
char * line
struct TextLine * prev
struct TextLine * next
int flags
ListBase lines
TextLine * curl
int selc
double mtime
TextLine * sell
int curc
void * compiled
char * filepath
Text * BKE_text_add(Main *bmain, const char *name)
Definition: text.c:282
bool txt_uncomment(Text *text)
Definition: text.c:2149
void txt_insert_buf(Text *text, const char *in_buffer, int in_buffer_len)
Definition: text.c:1537
static void text_foreach_path(ID *id, BPathForeachPathData *bpath_data)
Definition: text.c:157
static void text_free_data(ID *id)
Definition: text.c:144
int txt_find_string(Text *text, const char *findstr, int wrap, int match_case)
Definition: text.c:1591
static void txt_curs_swap(Text *text)
Definition: text.c:1137
bool BKE_text_reload(Text *text)
Definition: text.c:420
bool txt_unindent(Text *text)
Definition: text.c:2171
static void txt_make_dirty(Text *text)
Definition: text.c:716
void txt_sel_set(Text *text, int startl, int startc, int endl, int endc)
Definition: text.c:1274
void txt_indent(Text *text)
Definition: text.c:2160
void BKE_text_free_lines(Text *text)
Definition: text.c:266
bool text_check_identifier(const char ch)
Definition: text.c:2320
void txt_sel_all(Text *text)
Definition: text.c:1246
static void text_copy_data(Main *UNUSED(bmain), ID *id_dst, const ID *id_src, const int UNUSED(flag))
Definition: text.c:109
void txt_clean_text(Text *text)
Definition: text.c:633
char * txt_sel_to_buf(Text *text, size_t *r_buf_strlen)
Definition: text.c:1454
void BKE_text_write(Text *text, const char *str, int str_len)
Definition: text.c:523
void txt_move_toline(Text *text, unsigned int line, const bool sel)
Definition: text.c:1091
Text * BKE_text_load(Main *bmain, const char *filepath, const char *relbase)
Definition: text.c:511
static void text_blend_read_data(BlendDataReader *reader, ID *id)
Definition: text.c:198
bool txt_cursor_is_line_start(const Text *text)
Definition: text.c:744
void txt_pop_sel(Text *text)
Definition: text.c:1171
static void text_from_buf(Text *text, const unsigned char *buffer, const int len)
Definition: text.c:357
bool txt_cursor_is_line_end(const Text *text)
Definition: text.c:749
static void txt_delete_line(Text *text, TextLine *line)
Definition: text.c:1697
int text_check_identifier_unicode(const unsigned int ch)
Definition: text.c:2364
bool txt_add_char(Text *text, unsigned int add)
Definition: text.c:1919
void txt_move_bof(Text *text, const bool sel)
Definition: text.c:1045
void txt_delete_word(Text *text)
Definition: text.c:1799
void txt_from_buf_for_undo(Text *text, const char *buf, size_t buf_len)
Definition: text.c:1358
static TextLine * txt_new_linen(const char *str, int str_len)
Definition: text.c:610
static void txt_curs_cur(Text *text, TextLine ***linep, int **charp)
Definition: text.c:732
int txt_calc_tab_left(TextLine *tl, int ch)
Definition: text.c:824
void txt_split_curline(Text *text)
Definition: text.c:1649
static void text_init_data(ID *id)
Definition: text.c:65
void txt_jump_right(Text *text, const bool sel, const bool use_init_step)
Definition: text.c:977
void txt_move_right(Text *text, const bool sel)
Definition: text.c:909
bool text_check_digit(const char ch)
Definition: text.c:2309
bool txt_replace_char(Text *text, unsigned int add)
Definition: text.c:1935
bool txt_has_sel(const Text *text)
Definition: text.c:1201
void txt_backspace_char(Text *text)
Definition: text.c:1806
void txt_move_eol(Text *text, const bool sel)
Definition: text.c:1023
static void make_new_line(TextLine *line, char *newline)
Definition: text.c:596
void BKE_text_file_modified_ignore(Text *text)
Definition: text.c:564
void txt_move_lines(struct Text *text, const int direction)
Definition: text.c:2182
int BKE_text_file_modified_check(Text *text)
Definition: text.c:530
int txt_setcurr_tab_spaces(Text *text, int space)
Definition: text.c:2213
int text_check_bracket(const char ch)
Definition: text.c:2276
int text_check_identifier_nodigit_unicode(const unsigned int ch)
Definition: text.c:2369
bool text_check_delim(const char ch)
Definition: text.c:2293
void txt_move_bol(Text *text, const bool sel)
Definition: text.c:1001
int txt_get_span(TextLine *from, TextLine *to)
Definition: text.c:676
static bool txt_select_unprefix(Text *text, const char *remove, const bool require_all)
Definition: text.c:2066
Text * BKE_text_load_ex(Main *bmain, const char *filepath, const char *relbase, const bool is_internal)
Definition: text.c:457
void txt_delete_char(Text *text)
Definition: text.c:1758
static void txt_pop_last(Text *text)
Definition: text.c:1161
char * txt_to_buf_for_undo(Text *text, size_t *r_buf_len)
Definition: text.c:1341
static void txt_delete_sel(Text *text)
Definition: text.c:1206
void txt_move_eof(Text *text, const bool sel)
Definition: text.c:1068
void txt_move_down(Text *text, const bool sel)
Definition: text.c:794
static void cleanup_textline(TextLine *tl)
Definition: text.c:339
char * txt_to_buf(Text *text, size_t *r_buf_strlen)
Definition: text.c:1426
static void text_blend_write(BlendWriter *writer, ID *id, const void *id_address)
Definition: text.c:166
void BKE_text_clear(Text *text)
Definition: text.c:516
void txt_move_to(Text *text, unsigned int line, unsigned int ch, const bool sel)
Definition: text.c:1096
void txt_comment(Text *text)
Definition: text.c:2137
static void txt_pop_first(Text *text)
Definition: text.c:1151
static void txt_convert_tab_to_spaces(Text *text)
Definition: text.c:1866
int txt_calc_tab_right(TextLine *tl, int ch)
Definition: text.c:844
void txt_jump_left(Text *text, const bool sel, const bool use_init_step)
Definition: text.c:953
void txt_move_left(Text *text, const bool sel)
Definition: text.c:865
void txt_duplicate_line(Text *text)
Definition: text.c:1741
bool txt_add_raw_char(Text *text, unsigned int add)
Definition: text.c:1924
static void txt_combine_lines(Text *text, TextLine *linea, TextLine *lineb)
Definition: text.c:1718
void txt_backspace_word(Text *text)
Definition: text.c:1854
static TextLine * txt_new_line(const char *str)
Definition: text.c:628
void txt_order_cursors(Text *text, const bool reverse)
Definition: text.c:1177
void txt_sel_clear(Text *text)
Definition: text.c:1255
static bool txt_add_char_intern(Text *text, unsigned int add, bool replace_tabs)
Definition: text.c:1876
void txt_move_up(Text *text, const bool sel)
Definition: text.c:764
bool text_check_whitespace(const char ch)
Definition: text.c:2375
static void txt_curs_sel(Text *text, TextLine ***linep, int **charp)
Definition: text.c:738
void txt_sel_line(Text *text)
Definition: text.c:1263
int text_find_identifier_start(const char *str, int i)
Definition: text.c:2383
static void txt_select_prefix(Text *text, const char *add, bool skip_blank_lines)
Definition: text.c:1987
void txt_delete_selected(Text *text)
Definition: text.c:1929
IDTypeInfo IDType_ID_TXT
Definition: text.c:230
bool text_check_identifier_nodigit(const char ch)
Definition: text.c:2343
int txt_extended_ascii_as_utf8(char **str)
Definition: text.c:295
static char tab_to_spaces[]
Definition: text.c:1864