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