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