Fix T59713: Armature layer dots not updated on changes
[blender.git] / source / blender / blenkernel / intern / text.c
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
17  * All rights reserved.
18  */
19
20 /** \file
21  * \ingroup bke
22  */
23
24 #include <stdlib.h> /* abort */
25 #include <string.h> /* strstr */
26 #include <sys/types.h>
27 #include <sys/stat.h>
28 #include <wchar.h>
29 #include <wctype.h>
30
31 #include "MEM_guardedalloc.h"
32
33 #include "BLI_utildefines.h"
34 #include "BLI_path_util.h"
35 #include "BLI_string.h"
36 #include "BLI_string_cursor_utf8.h"
37 #include "BLI_string_utf8.h"
38 #include "BLI_listbase.h"
39 #include "BLI_fileops.h"
40
41 #include "DNA_constraint_types.h"
42 #include "DNA_scene_types.h"
43 #include "DNA_screen_types.h"
44 #include "DNA_space_types.h"
45 #include "DNA_text_types.h"
46 #include "DNA_userdef_types.h"
47 #include "DNA_object_types.h"
48 #include "DNA_node_types.h"
49 #include "DNA_material_types.h"
50
51 #include "BKE_library.h"
52 #include "BKE_main.h"
53 #include "BKE_text.h"
54 #include "BKE_node.h"
55
56 #ifdef WITH_PYTHON
57 #  include "BPY_extern.h"
58 #endif
59
60 /*
61  * How Texts should work
62  * --
63  * A text should relate to a file as follows -
64  * (Text *)->name should be the place where the
65  *     file will or has been saved.
66  *
67  * (Text *)->flags has the following bits
68  *     TXT_ISDIRTY - should always be set if the file in mem. differs from
69  *                     the file on disk, or if there is no file on disk.
70  *     TXT_ISMEM - should always be set if the Text has not been mapped to
71  *                     a file, in which case (Text *)->name may be NULL or garbage.
72  *     TXT_ISEXT - should always be set if the Text is not to be written into
73  *                     the .blend
74  *     TXT_ISSCRIPT - should be set if the user has designated the text
75  *                     as a script. (NEW: this was unused, but now it is needed by
76  *                     space handler script links (see header_view3d.c, for example)
77  *
78  * ->>> see also: /makesdna/DNA_text_types.h
79  *
80  * Display
81  * --
82  *
83  * The st->top determines at what line the top of the text is displayed.
84  * If the user moves the cursor the st containing that cursor should
85  * be popped ... other st's retain their own top location.
86  */
87
88 /***/
89
90 static void txt_pop_first(Text *text);
91 static void txt_pop_last(Text *text);
92 static void txt_delete_line(Text *text, TextLine *line);
93 static void txt_delete_sel(Text *text);
94 static void txt_make_dirty(Text *text);
95
96 /***/
97
98 /**
99  * \note caller must handle `compiled` member.
100  */
101 void BKE_text_free_lines(Text *text)
102 {
103   for (TextLine *tmp = text->lines.first, *tmp_next; tmp; tmp = tmp_next) {
104     tmp_next = tmp->next;
105     MEM_freeN(tmp->line);
106     if (tmp->format) {
107       MEM_freeN(tmp->format);
108     }
109     MEM_freeN(tmp);
110   }
111
112   BLI_listbase_clear(&text->lines);
113
114   text->curl = text->sell = NULL;
115 }
116
117 /** Free (or release) any data used by this text (does not free the text itself). */
118 void BKE_text_free(Text *text)
119 {
120   /* No animdata here. */
121
122   BKE_text_free_lines(text);
123
124   MEM_SAFE_FREE(text->name);
125 #ifdef WITH_PYTHON
126   BPY_text_free_code(text);
127 #endif
128 }
129
130 void BKE_text_init(Text *ta)
131 {
132   TextLine *tmp;
133
134   BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(ta, id));
135
136   ta->name = NULL;
137
138   ta->nlines = 1;
139   ta->flags = TXT_ISDIRTY | TXT_ISMEM;
140   if ((U.flag & USER_TXT_TABSTOSPACES_DISABLE) == 0) {
141     ta->flags |= TXT_TABSTOSPACES;
142   }
143
144   BLI_listbase_clear(&ta->lines);
145
146   tmp = (TextLine *)MEM_mallocN(sizeof(TextLine), "textline");
147   tmp->line = (char *)MEM_mallocN(1, "textline_string");
148   tmp->format = NULL;
149
150   tmp->line[0] = 0;
151   tmp->len = 0;
152
153   tmp->next = NULL;
154   tmp->prev = NULL;
155
156   BLI_addhead(&ta->lines, tmp);
157
158   ta->curl = ta->lines.first;
159   ta->curc = 0;
160   ta->sell = ta->lines.first;
161   ta->selc = 0;
162 }
163
164 Text *BKE_text_add(Main *bmain, const char *name)
165 {
166   Text *ta;
167
168   ta = BKE_libblock_alloc(bmain, ID_TXT, name, 0);
169
170   BKE_text_init(ta);
171
172   return ta;
173 }
174
175 /* this function replaces extended ascii characters */
176 /* to a valid utf-8 sequences */
177 int txt_extended_ascii_as_utf8(char **str)
178 {
179   ptrdiff_t bad_char, i = 0;
180   const ptrdiff_t length = (ptrdiff_t)strlen(*str);
181   int added = 0;
182
183   while ((*str)[i]) {
184     if ((bad_char = BLI_utf8_invalid_byte(*str + i, length - i)) == -1) {
185       break;
186     }
187
188     added++;
189     i += bad_char + 1;
190   }
191
192   if (added != 0) {
193     char *newstr = MEM_mallocN(length + added + 1, "text_line");
194     ptrdiff_t mi = 0;
195     i = 0;
196
197     while ((*str)[i]) {
198       if ((bad_char = BLI_utf8_invalid_byte((*str) + i, length - i)) == -1) {
199         memcpy(newstr + mi, (*str) + i, length - i + 1);
200         break;
201       }
202
203       memcpy(newstr + mi, (*str) + i, bad_char);
204
205       BLI_str_utf8_from_unicode((*str)[i + bad_char], newstr + mi + bad_char);
206       i += bad_char + 1;
207       mi += bad_char + 2;
208     }
209     newstr[length + added] = '\0';
210     MEM_freeN(*str);
211     *str = newstr;
212   }
213
214   return added;
215 }
216
217 // this function removes any control characters from
218 // a textline and fixes invalid utf-8 sequences
219
220 static void cleanup_textline(TextLine *tl)
221 {
222   int i;
223
224   for (i = 0; i < tl->len; i++) {
225     if (tl->line[i] < ' ' && tl->line[i] != '\t') {
226       memmove(tl->line + i, tl->line + i + 1, tl->len - i);
227       tl->len--;
228       i--;
229     }
230   }
231   tl->len += txt_extended_ascii_as_utf8(&tl->line);
232 }
233
234 /**
235  * used for load and reload (unlike txt_insert_buf)
236  * assumes all fields are empty
237  */
238 static void text_from_buf(Text *text, const unsigned char *buffer, const int len)
239 {
240   int i, llen;
241
242   BLI_assert(BLI_listbase_is_empty(&text->lines));
243
244   text->nlines = 0;
245   llen = 0;
246   for (i = 0; i < len; i++) {
247     if (buffer[i] == '\n') {
248       TextLine *tmp;
249
250       tmp = (TextLine *)MEM_mallocN(sizeof(TextLine), "textline");
251       tmp->line = (char *)MEM_mallocN(llen + 1, "textline_string");
252       tmp->format = NULL;
253
254       if (llen) {
255         memcpy(tmp->line, &buffer[i - llen], llen);
256       }
257       tmp->line[llen] = 0;
258       tmp->len = llen;
259
260       cleanup_textline(tmp);
261
262       BLI_addtail(&text->lines, tmp);
263       text->nlines++;
264
265       llen = 0;
266       continue;
267     }
268     llen++;
269   }
270
271   /* create new line in cases:
272    * - rest of line (if last line in file hasn't got \n terminator).
273    *   in this case content of such line would be used to fill text line buffer
274    * - file is empty. in this case new line is needed to start editing from.
275    * - last character in buffer is \n. in this case new line is needed to
276    *   deal with newline at end of file. (see [#28087]) (sergey) */
277   if (llen != 0 || text->nlines == 0 || buffer[len - 1] == '\n') {
278     TextLine *tmp;
279
280     tmp = (TextLine *)MEM_mallocN(sizeof(TextLine), "textline");
281     tmp->line = (char *)MEM_mallocN(llen + 1, "textline_string");
282     tmp->format = NULL;
283
284     if (llen) {
285       memcpy(tmp->line, &buffer[i - llen], llen);
286     }
287
288     tmp->line[llen] = 0;
289     tmp->len = llen;
290
291     cleanup_textline(tmp);
292
293     BLI_addtail(&text->lines, tmp);
294     text->nlines++;
295   }
296
297   text->curl = text->sell = text->lines.first;
298   text->curc = text->selc = 0;
299 }
300
301 bool BKE_text_reload(Text *text)
302 {
303   unsigned char *buffer;
304   size_t buffer_len;
305   char filepath_abs[FILE_MAX];
306   BLI_stat_t st;
307
308   if (!text->name) {
309     return false;
310   }
311
312   BLI_strncpy(filepath_abs, text->name, FILE_MAX);
313   BLI_path_abs(filepath_abs, BKE_main_blendfile_path_from_global());
314
315   buffer = BLI_file_read_text_as_mem(filepath_abs, 0, &buffer_len);
316   if (buffer == NULL) {
317     return false;
318   }
319
320   /* free memory: */
321   BKE_text_free_lines(text);
322   txt_make_dirty(text);
323
324   /* clear undo buffer */
325   if (BLI_stat(filepath_abs, &st) != -1) {
326     text->mtime = st.st_mtime;
327   }
328   else {
329     text->mtime = 0;
330   }
331
332   text_from_buf(text, buffer, buffer_len);
333
334   MEM_freeN(buffer);
335   return true;
336 }
337
338 Text *BKE_text_load_ex(Main *bmain, const char *file, const char *relpath, const bool is_internal)
339 {
340   unsigned char *buffer;
341   size_t buffer_len;
342   Text *ta;
343   char filepath_abs[FILE_MAX];
344   BLI_stat_t st;
345
346   BLI_strncpy(filepath_abs, file, FILE_MAX);
347   if (relpath) { /* can be NULL (bg mode) */
348     BLI_path_abs(filepath_abs, relpath);
349   }
350
351   buffer = BLI_file_read_text_as_mem(filepath_abs, 0, &buffer_len);
352   if (buffer == NULL) {
353     return false;
354   }
355
356   ta = BKE_libblock_alloc(bmain, ID_TXT, BLI_path_basename(filepath_abs), 0);
357   ta->id.us = 0;
358
359   BLI_listbase_clear(&ta->lines);
360   ta->curl = ta->sell = NULL;
361
362   if ((U.flag & USER_TXT_TABSTOSPACES_DISABLE) == 0) {
363     ta->flags = TXT_TABSTOSPACES;
364   }
365
366   if (is_internal == false) {
367     ta->name = MEM_mallocN(strlen(file) + 1, "text_name");
368     strcpy(ta->name, file);
369   }
370   else {
371     ta->flags |= TXT_ISMEM | TXT_ISDIRTY;
372   }
373
374   /* clear undo buffer */
375   if (BLI_stat(filepath_abs, &st) != -1) {
376     ta->mtime = st.st_mtime;
377   }
378   else {
379     ta->mtime = 0;
380   }
381
382   text_from_buf(ta, buffer, buffer_len);
383
384   MEM_freeN(buffer);
385
386   return ta;
387 }
388
389 Text *BKE_text_load(Main *bmain, const char *file, const char *relpath)
390 {
391   return BKE_text_load_ex(bmain, file, relpath, false);
392 }
393
394 /**
395  * Only copy internal data of Text ID from source
396  * to already allocated/initialized destination.
397  * You probably never want to use that directly,
398  * use #BKE_id_copy or #BKE_id_copy_ex for typical needs.
399  *
400  * WARNING! This function will not handle ID user count!
401  *
402  * \param flag: Copying options (see BKE_library.h's LIB_ID_COPY_... flags for more).
403  */
404 void BKE_text_copy_data(Main *UNUSED(bmain),
405                         Text *ta_dst,
406                         const Text *ta_src,
407                         const int UNUSED(flag))
408 {
409   /* file name can be NULL */
410   if (ta_src->name) {
411     ta_dst->name = BLI_strdup(ta_src->name);
412   }
413
414   ta_dst->flags |= TXT_ISDIRTY;
415
416   BLI_listbase_clear(&ta_dst->lines);
417   ta_dst->curl = ta_dst->sell = NULL;
418   ta_dst->compiled = NULL;
419
420   /* Walk down, reconstructing */
421   for (TextLine *line_src = ta_src->lines.first; line_src; line_src = line_src->next) {
422     TextLine *line_dst = MEM_mallocN(sizeof(*line_dst), __func__);
423
424     line_dst->line = BLI_strdup(line_src->line);
425     line_dst->format = NULL;
426     line_dst->len = line_src->len;
427
428     BLI_addtail(&ta_dst->lines, line_dst);
429   }
430
431   ta_dst->curl = ta_dst->sell = ta_dst->lines.first;
432   ta_dst->curc = ta_dst->selc = 0;
433 }
434
435 Text *BKE_text_copy(Main *bmain, const Text *ta)
436 {
437   Text *ta_copy;
438   BKE_id_copy(bmain, &ta->id, (ID **)&ta_copy);
439   return ta_copy;
440 }
441
442 void BKE_text_make_local(Main *bmain, Text *text, const bool lib_local)
443 {
444   BKE_id_make_local_generic(bmain, &text->id, true, lib_local);
445 }
446
447 void BKE_text_clear(Text *text) /* called directly from rna */
448 {
449   txt_sel_all(text);
450   txt_delete_sel(text);
451   txt_make_dirty(text);
452 }
453
454 void BKE_text_write(Text *text, const char *str) /* called directly from rna */
455 {
456   txt_insert_buf(text, str);
457   txt_move_eof(text, 0);
458   txt_make_dirty(text);
459 }
460
461 /* returns 0 if file on disk is the same or Text is in memory only
462  * returns 1 if file has been modified on disk since last local edit
463  * returns 2 if file on disk has been deleted
464  * -1 is returned if an error occurs */
465
466 int BKE_text_file_modified_check(Text *text)
467 {
468   BLI_stat_t st;
469   int result;
470   char file[FILE_MAX];
471
472   if (!text->name) {
473     return 0;
474   }
475
476   BLI_strncpy(file, text->name, FILE_MAX);
477   BLI_path_abs(file, BKE_main_blendfile_path_from_global());
478
479   if (!BLI_exists(file)) {
480     return 2;
481   }
482
483   result = BLI_stat(file, &st);
484
485   if (result == -1) {
486     return -1;
487   }
488
489   if ((st.st_mode & S_IFMT) != S_IFREG) {
490     return -1;
491   }
492
493   if (st.st_mtime > text->mtime) {
494     return 1;
495   }
496
497   return 0;
498 }
499
500 void BKE_text_file_modified_ignore(Text *text)
501 {
502   BLI_stat_t st;
503   int result;
504   char file[FILE_MAX];
505
506   if (!text->name) {
507     return;
508   }
509
510   BLI_strncpy(file, text->name, FILE_MAX);
511   BLI_path_abs(file, BKE_main_blendfile_path_from_global());
512
513   if (!BLI_exists(file)) {
514     return;
515   }
516
517   result = BLI_stat(file, &st);
518
519   if (result == -1 || (st.st_mode & S_IFMT) != S_IFREG) {
520     return;
521   }
522
523   text->mtime = st.st_mtime;
524 }
525
526 /*****************************/
527 /* Editing utility functions */
528 /*****************************/
529
530 static void make_new_line(TextLine *line, char *newline)
531 {
532   if (line->line) {
533     MEM_freeN(line->line);
534   }
535   if (line->format) {
536     MEM_freeN(line->format);
537   }
538
539   line->line = newline;
540   line->len = strlen(newline);
541   line->format = NULL;
542 }
543
544 static TextLine *txt_new_line(const char *str)
545 {
546   TextLine *tmp;
547
548   if (!str) {
549     str = "";
550   }
551
552   tmp = (TextLine *)MEM_mallocN(sizeof(TextLine), "textline");
553   tmp->line = MEM_mallocN(strlen(str) + 1, "textline_string");
554   tmp->format = NULL;
555
556   strcpy(tmp->line, str);
557
558   tmp->len = strlen(str);
559   tmp->next = tmp->prev = NULL;
560
561   return tmp;
562 }
563
564 static TextLine *txt_new_linen(const char *str, int n)
565 {
566   TextLine *tmp;
567
568   tmp = (TextLine *)MEM_mallocN(sizeof(TextLine), "textline");
569   tmp->line = MEM_mallocN(n + 1, "textline_string");
570   tmp->format = NULL;
571
572   BLI_strncpy(tmp->line, (str) ? str : "", n + 1);
573
574   tmp->len = strlen(tmp->line);
575   tmp->next = tmp->prev = NULL;
576
577   return tmp;
578 }
579
580 void txt_clean_text(Text *text)
581 {
582   TextLine **top, **bot;
583
584   if (!text->lines.first) {
585     if (text->lines.last) {
586       text->lines.first = text->lines.last;
587     }
588     else {
589       text->lines.first = text->lines.last = txt_new_line(NULL);
590     }
591   }
592
593   if (!text->lines.last) {
594     text->lines.last = text->lines.first;
595   }
596
597   top = (TextLine **)&text->lines.first;
598   bot = (TextLine **)&text->lines.last;
599
600   while ((*top)->prev) {
601     *top = (*top)->prev;
602   }
603   while ((*bot)->next) {
604     *bot = (*bot)->next;
605   }
606
607   if (!text->curl) {
608     if (text->sell) {
609       text->curl = text->sell;
610     }
611     else {
612       text->curl = text->lines.first;
613     }
614     text->curc = 0;
615   }
616
617   if (!text->sell) {
618     text->sell = text->curl;
619     text->selc = 0;
620   }
621 }
622
623 int txt_get_span(TextLine *from, TextLine *to)
624 {
625   int ret = 0;
626   TextLine *tmp = from;
627
628   if (!to || !from) {
629     return 0;
630   }
631   if (from == to) {
632     return 0;
633   }
634
635   /* Look forwards */
636   while (tmp) {
637     if (tmp == to) {
638       return ret;
639     }
640     ret++;
641     tmp = tmp->next;
642   }
643
644   /* Look backwards */
645   if (!tmp) {
646     tmp = from;
647     ret = 0;
648     while (tmp) {
649       if (tmp == to) {
650         break;
651       }
652       ret--;
653       tmp = tmp->prev;
654     }
655     if (!tmp) {
656       ret = 0;
657     }
658   }
659
660   return ret;
661 }
662
663 static void txt_make_dirty(Text *text)
664 {
665   text->flags |= TXT_ISDIRTY;
666 #ifdef WITH_PYTHON
667   if (text->compiled) {
668     BPY_text_free_code(text);
669   }
670 #endif
671 }
672
673 /****************************/
674 /* Cursor utility functions */
675 /****************************/
676
677 static void txt_curs_cur(Text *text, TextLine ***linep, int **charp)
678 {
679   *linep = &text->curl;
680   *charp = &text->curc;
681 }
682
683 static void txt_curs_sel(Text *text, TextLine ***linep, int **charp)
684 {
685   *linep = &text->sell;
686   *charp = &text->selc;
687 }
688
689 bool txt_cursor_is_line_start(Text *text)
690 {
691   return (text->selc == 0);
692 }
693
694 bool txt_cursor_is_line_end(Text *text)
695 {
696   return (text->selc == text->sell->len);
697 }
698
699 /*****************************/
700 /* Cursor movement functions */
701 /*****************************/
702
703 int txt_utf8_offset_to_index(const char *str, int offset)
704 {
705   int index = 0, pos = 0;
706   while (pos != offset) {
707     pos += BLI_str_utf8_size(str + pos);
708     index++;
709   }
710   return index;
711 }
712
713 int txt_utf8_index_to_offset(const char *str, int index)
714 {
715   int offset = 0, pos = 0;
716   while (pos != index) {
717     offset += BLI_str_utf8_size(str + offset);
718     pos++;
719   }
720   return offset;
721 }
722
723 int txt_utf8_offset_to_column(const char *str, int offset)
724 {
725   int column = 0, pos = 0;
726   while (pos < offset) {
727     column += BLI_str_utf8_char_width_safe(str + pos);
728     pos += BLI_str_utf8_size_safe(str + pos);
729   }
730   return column;
731 }
732
733 int txt_utf8_column_to_offset(const char *str, int column)
734 {
735   int offset = 0, pos = 0, col;
736   while (*(str + offset) && pos < column) {
737     col = BLI_str_utf8_char_width_safe(str + offset);
738     if (pos + col > column) {
739       break;
740     }
741     offset += BLI_str_utf8_size_safe(str + offset);
742     pos += col;
743   }
744   return offset;
745 }
746
747 void txt_move_up(Text *text, const bool sel)
748 {
749   TextLine **linep;
750   int *charp;
751
752   if (sel) {
753     txt_curs_sel(text, &linep, &charp);
754   }
755   else {
756     txt_pop_first(text);
757     txt_curs_cur(text, &linep, &charp);
758   }
759   if (!*linep) {
760     return;
761   }
762
763   if ((*linep)->prev) {
764     int column = txt_utf8_offset_to_column((*linep)->line, *charp);
765     *linep = (*linep)->prev;
766     *charp = txt_utf8_column_to_offset((*linep)->line, column);
767   }
768   else {
769     txt_move_bol(text, sel);
770   }
771
772   if (!sel) {
773     txt_pop_sel(text);
774   }
775 }
776
777 void txt_move_down(Text *text, const bool sel)
778 {
779   TextLine **linep;
780   int *charp;
781
782   if (sel) {
783     txt_curs_sel(text, &linep, &charp);
784   }
785   else {
786     txt_pop_last(text);
787     txt_curs_cur(text, &linep, &charp);
788   }
789   if (!*linep) {
790     return;
791   }
792
793   if ((*linep)->next) {
794     int column = txt_utf8_offset_to_column((*linep)->line, *charp);
795     *linep = (*linep)->next;
796     *charp = txt_utf8_column_to_offset((*linep)->line, column);
797   }
798   else {
799     txt_move_eol(text, sel);
800   }
801
802   if (!sel) {
803     txt_pop_sel(text);
804   }
805 }
806
807 int txt_calc_tab_left(TextLine *tl, int ch)
808 {
809   /* do nice left only if there are only spaces */
810
811   int tabsize = (ch < TXT_TABSIZE) ? ch : TXT_TABSIZE;
812
813   for (int i = 0; i < ch; i++) {
814     if (tl->line[i] != ' ') {
815       tabsize = 0;
816       break;
817     }
818   }
819
820   /* if in the middle of the space-tab */
821   if (tabsize && ch % TXT_TABSIZE != 0) {
822     tabsize = (ch % TXT_TABSIZE);
823   }
824   return tabsize;
825 }
826
827 int txt_calc_tab_right(TextLine *tl, int ch)
828 {
829   if (tl->line[ch] == ' ') {
830     int i;
831     for (i = 0; i < ch; i++) {
832       if (tl->line[i] != ' ') {
833         return 0;
834       }
835     }
836
837     int tabsize = (ch) % TXT_TABSIZE + 1;
838     for (i = ch + 1; tl->line[i] == ' ' && tabsize < TXT_TABSIZE; i++) {
839       tabsize++;
840     }
841
842     return i - ch;
843   }
844   else {
845     return 0;
846   }
847 }
848
849 void txt_move_left(Text *text, const bool sel)
850 {
851   TextLine **linep;
852   int *charp;
853   int tabsize = 0;
854
855   if (sel) {
856     txt_curs_sel(text, &linep, &charp);
857   }
858   else {
859     txt_pop_first(text);
860     txt_curs_cur(text, &linep, &charp);
861   }
862   if (!*linep) {
863     return;
864   }
865
866   if (*charp == 0) {
867     if ((*linep)->prev) {
868       txt_move_up(text, sel);
869       *charp = (*linep)->len;
870     }
871   }
872   else {
873     /* do nice left only if there are only spaces */
874     // TXT_TABSIZE hardcoded in DNA_text_types.h
875     if (text->flags & TXT_TABSTOSPACES) {
876       tabsize = txt_calc_tab_left(*linep, *charp);
877     }
878
879     if (tabsize) {
880       (*charp) -= tabsize;
881     }
882     else {
883       const char *prev = BLI_str_prev_char_utf8((*linep)->line + *charp);
884       *charp = prev - (*linep)->line;
885     }
886   }
887
888   if (!sel) {
889     txt_pop_sel(text);
890   }
891 }
892
893 void txt_move_right(Text *text, const bool sel)
894 {
895   TextLine **linep;
896   int *charp;
897
898   if (sel) {
899     txt_curs_sel(text, &linep, &charp);
900   }
901   else {
902     txt_pop_last(text);
903     txt_curs_cur(text, &linep, &charp);
904   }
905   if (!*linep) {
906     return;
907   }
908
909   if (*charp == (*linep)->len) {
910     if ((*linep)->next) {
911       txt_move_down(text, sel);
912       *charp = 0;
913     }
914   }
915   else {
916     /* do nice right only if there are only spaces */
917     /* spaces hardcoded in DNA_text_types.h */
918     int tabsize = 0;
919
920     if (text->flags & TXT_TABSTOSPACES) {
921       tabsize = txt_calc_tab_right(*linep, *charp);
922     }
923
924     if (tabsize) {
925       (*charp) += tabsize;
926     }
927     else {
928       (*charp) += BLI_str_utf8_size((*linep)->line + *charp);
929     }
930   }
931
932   if (!sel) {
933     txt_pop_sel(text);
934   }
935 }
936
937 void txt_jump_left(Text *text, const bool sel, const bool use_init_step)
938 {
939   TextLine **linep;
940   int *charp;
941
942   if (sel) {
943     txt_curs_sel(text, &linep, &charp);
944   }
945   else {
946     txt_pop_first(text);
947     txt_curs_cur(text, &linep, &charp);
948   }
949   if (!*linep) {
950     return;
951   }
952
953   BLI_str_cursor_step_utf8(
954       (*linep)->line, (*linep)->len, charp, STRCUR_DIR_PREV, STRCUR_JUMP_DELIM, use_init_step);
955
956   if (!sel) {
957     txt_pop_sel(text);
958   }
959 }
960
961 void txt_jump_right(Text *text, const bool sel, const bool use_init_step)
962 {
963   TextLine **linep;
964   int *charp;
965
966   if (sel) {
967     txt_curs_sel(text, &linep, &charp);
968   }
969   else {
970     txt_pop_last(text);
971     txt_curs_cur(text, &linep, &charp);
972   }
973   if (!*linep) {
974     return;
975   }
976
977   BLI_str_cursor_step_utf8(
978       (*linep)->line, (*linep)->len, charp, STRCUR_DIR_NEXT, STRCUR_JUMP_DELIM, use_init_step);
979
980   if (!sel) {
981     txt_pop_sel(text);
982   }
983 }
984
985 void txt_move_bol(Text *text, const bool sel)
986 {
987   TextLine **linep;
988   int *charp;
989
990   if (sel) {
991     txt_curs_sel(text, &linep, &charp);
992   }
993   else {
994     txt_curs_cur(text, &linep, &charp);
995   }
996   if (!*linep) {
997     return;
998   }
999
1000   *charp = 0;
1001
1002   if (!sel) {
1003     txt_pop_sel(text);
1004   }
1005 }
1006
1007 void txt_move_eol(Text *text, const bool sel)
1008 {
1009   TextLine **linep;
1010   int *charp;
1011
1012   if (sel) {
1013     txt_curs_sel(text, &linep, &charp);
1014   }
1015   else {
1016     txt_curs_cur(text, &linep, &charp);
1017   }
1018   if (!*linep) {
1019     return;
1020   }
1021
1022   *charp = (*linep)->len;
1023
1024   if (!sel) {
1025     txt_pop_sel(text);
1026   }
1027 }
1028
1029 void txt_move_bof(Text *text, const bool sel)
1030 {
1031   TextLine **linep;
1032   int *charp;
1033
1034   if (sel) {
1035     txt_curs_sel(text, &linep, &charp);
1036   }
1037   else {
1038     txt_curs_cur(text, &linep, &charp);
1039   }
1040   if (!*linep) {
1041     return;
1042   }
1043
1044   *linep = text->lines.first;
1045   *charp = 0;
1046
1047   if (!sel) {
1048     txt_pop_sel(text);
1049   }
1050 }
1051
1052 void txt_move_eof(Text *text, const bool sel)
1053 {
1054   TextLine **linep;
1055   int *charp;
1056
1057   if (sel) {
1058     txt_curs_sel(text, &linep, &charp);
1059   }
1060   else {
1061     txt_curs_cur(text, &linep, &charp);
1062   }
1063   if (!*linep) {
1064     return;
1065   }
1066
1067   *linep = text->lines.last;
1068   *charp = (*linep)->len;
1069
1070   if (!sel) {
1071     txt_pop_sel(text);
1072   }
1073 }
1074
1075 void txt_move_toline(Text *text, unsigned int line, const bool sel)
1076 {
1077   txt_move_to(text, line, 0, sel);
1078 }
1079
1080 /* Moves to a certain byte in a line, not a certain utf8-character! */
1081 void txt_move_to(Text *text, unsigned int line, unsigned int ch, const bool sel)
1082 {
1083   TextLine **linep;
1084   int *charp;
1085   unsigned int i;
1086
1087   if (sel) {
1088     txt_curs_sel(text, &linep, &charp);
1089   }
1090   else {
1091     txt_curs_cur(text, &linep, &charp);
1092   }
1093   if (!*linep) {
1094     return;
1095   }
1096
1097   *linep = text->lines.first;
1098   for (i = 0; i < line; i++) {
1099     if ((*linep)->next) {
1100       *linep = (*linep)->next;
1101     }
1102     else {
1103       break;
1104     }
1105   }
1106   if (ch > (unsigned int)((*linep)->len)) {
1107     ch = (unsigned int)((*linep)->len);
1108   }
1109   *charp = ch;
1110
1111   if (!sel) {
1112     txt_pop_sel(text);
1113   }
1114 }
1115
1116 /****************************/
1117 /* Text selection functions */
1118 /****************************/
1119
1120 static void txt_curs_swap(Text *text)
1121 {
1122   TextLine *tmpl;
1123   int tmpc;
1124
1125   tmpl = text->curl;
1126   text->curl = text->sell;
1127   text->sell = tmpl;
1128
1129   tmpc = text->curc;
1130   text->curc = text->selc;
1131   text->selc = tmpc;
1132 }
1133
1134 static void txt_pop_first(Text *text)
1135 {
1136   if (txt_get_span(text->curl, text->sell) < 0 ||
1137       (text->curl == text->sell && text->curc > text->selc)) {
1138     txt_curs_swap(text);
1139   }
1140
1141   txt_pop_sel(text);
1142 }
1143
1144 static void txt_pop_last(Text *text)
1145 {
1146   if (txt_get_span(text->curl, text->sell) > 0 ||
1147       (text->curl == text->sell && text->curc < text->selc)) {
1148     txt_curs_swap(text);
1149   }
1150
1151   txt_pop_sel(text);
1152 }
1153
1154 void txt_pop_sel(Text *text)
1155 {
1156   text->sell = text->curl;
1157   text->selc = text->curc;
1158 }
1159
1160 void txt_order_cursors(Text *text, const bool reverse)
1161 {
1162   if (!text->curl) {
1163     return;
1164   }
1165   if (!text->sell) {
1166     return;
1167   }
1168
1169   /* Flip so text->curl is before/after text->sell */
1170   if (reverse == false) {
1171     if ((txt_get_span(text->curl, text->sell) < 0) ||
1172         (text->curl == text->sell && text->curc > text->selc)) {
1173       txt_curs_swap(text);
1174     }
1175   }
1176   else {
1177     if ((txt_get_span(text->curl, text->sell) > 0) ||
1178         (text->curl == text->sell && text->curc < text->selc)) {
1179       txt_curs_swap(text);
1180     }
1181   }
1182 }
1183
1184 bool txt_has_sel(Text *text)
1185 {
1186   return ((text->curl != text->sell) || (text->curc != text->selc));
1187 }
1188
1189 static void txt_delete_sel(Text *text)
1190 {
1191   TextLine *tmpl;
1192   char *buf;
1193
1194   if (!text->curl) {
1195     return;
1196   }
1197   if (!text->sell) {
1198     return;
1199   }
1200
1201   if (!txt_has_sel(text)) {
1202     return;
1203   }
1204
1205   txt_order_cursors(text, false);
1206
1207   buf = MEM_mallocN(text->curc + (text->sell->len - text->selc) + 1, "textline_string");
1208
1209   strncpy(buf, text->curl->line, text->curc);
1210   strcpy(buf + text->curc, text->sell->line + text->selc);
1211   buf[text->curc + (text->sell->len - text->selc)] = 0;
1212
1213   make_new_line(text->curl, buf);
1214
1215   tmpl = text->sell;
1216   while (tmpl != text->curl) {
1217     tmpl = tmpl->prev;
1218     if (!tmpl) {
1219       break;
1220     }
1221
1222     txt_delete_line(text, tmpl->next);
1223   }
1224
1225   text->sell = text->curl;
1226   text->selc = text->curc;
1227 }
1228
1229 void txt_sel_all(Text *text)
1230 {
1231   text->curl = text->lines.first;
1232   text->curc = 0;
1233
1234   text->sell = text->lines.last;
1235   text->selc = text->sell->len;
1236 }
1237
1238 /**
1239  * Reverse of #txt_pop_sel
1240  * Clears the selection and ensures the cursor is located
1241  * at the selection (where the cursor is visually while editing).
1242  */
1243 void txt_sel_clear(Text *text)
1244 {
1245   if (text->sell) {
1246     text->curl = text->sell;
1247     text->curc = text->selc;
1248   }
1249 }
1250
1251 void txt_sel_line(Text *text)
1252 {
1253   if (!text->curl) {
1254     return;
1255   }
1256
1257   text->curc = 0;
1258   text->sell = text->curl;
1259   text->selc = text->sell->len;
1260 }
1261
1262 /* -------------------------------------------------------------------- */
1263 /** \name Buffer Conversion for Undo/Redo
1264  *
1265  * Buffer conversion functions that rely on the buffer already being validated.
1266  *
1267  * The only requirement for these functions is that they're reverse-able,
1268  * the undo logic doesn't inspect their content.
1269  *
1270  * Currently buffers:
1271  * - Always ends with a new-line.
1272  * - Are not null terminated.
1273  * \{ */
1274
1275 /**
1276  * Create a buffer, the only requirement is #txt_from_buf_for_undo can decode it.
1277  */
1278 char *txt_to_buf_for_undo(Text *text, int *r_buf_len)
1279 {
1280   int buf_len = 0;
1281   for (const TextLine *l = text->lines.first; l; l = l->next) {
1282     buf_len += l->len + 1;
1283   }
1284   char *buf = MEM_mallocN(buf_len, __func__);
1285   char *buf_step = buf;
1286   for (const TextLine *l = text->lines.first; l; l = l->next) {
1287     memcpy(buf_step, l->line, l->len);
1288     buf_step += l->len;
1289     *buf_step++ = '\n';
1290   }
1291   *r_buf_len = buf_len;
1292   return buf;
1293 }
1294
1295 /**
1296  * Decode a buffer from #txt_to_buf_for_undo.
1297  */
1298 void txt_from_buf_for_undo(Text *text, const char *buf, int buf_len)
1299 {
1300   const char *buf_end = buf + buf_len;
1301   const char *buf_step = buf;
1302
1303   /* First re-use existing lines.
1304    * Good for undo since it means in practice many operations re-use all
1305    * except for the modified line. */
1306   TextLine *l_src = text->lines.first;
1307   BLI_listbase_clear(&text->lines);
1308   while (buf_step != buf_end && l_src) {
1309     /* New lines are ensured by #txt_to_buf_for_undo. */
1310     const char *buf_step_next = strchr(buf_step, '\n');
1311     const int len = buf_step_next - buf_step;
1312
1313     TextLine *l = l_src;
1314     l_src = l_src->next;
1315     if (l->len != len) {
1316       l->line = MEM_reallocN(l->line, len + 1);
1317       l->len = len;
1318     }
1319     MEM_SAFE_FREE(l->format);
1320
1321     memcpy(l->line, buf_step, len);
1322     l->line[len] = '\0';
1323     BLI_addtail(&text->lines, l);
1324     buf_step = buf_step_next + 1;
1325   }
1326
1327   /* If we have extra lines. */
1328   while (l_src != NULL) {
1329     TextLine *l_src_next = l_src->next;
1330     MEM_freeN(l_src->line);
1331     if (l_src->format) {
1332       MEM_freeN(l_src->format);
1333     }
1334     MEM_freeN(l_src);
1335     l_src = l_src_next;
1336   }
1337
1338   while (buf_step != buf_end) {
1339     /* New lines are ensured by #txt_to_buf_for_undo. */
1340     const char *buf_step_next = strchr(buf_step, '\n');
1341     const int len = buf_step_next - buf_step;
1342
1343     TextLine *l = MEM_mallocN(sizeof(TextLine), "textline");
1344     l->line = MEM_mallocN(len + 1, "textline_string");
1345     l->len = len;
1346     l->format = NULL;
1347
1348     memcpy(l->line, buf_step, len);
1349     l->line[len] = '\0';
1350     BLI_addtail(&text->lines, l);
1351     buf_step = buf_step_next + 1;
1352   }
1353
1354   text->curl = text->sell = text->lines.first;
1355   text->curc = text->selc = 0;
1356
1357   txt_make_dirty(text);
1358 }
1359
1360 /** \} */
1361
1362 /***************************/
1363 /* Cut and paste functions */
1364 /***************************/
1365
1366 char *txt_to_buf(Text *text, int *r_buf_strlen)
1367 {
1368   int length;
1369   TextLine *tmp, *linef, *linel;
1370   int charf, charl;
1371   char *buf;
1372
1373   if (r_buf_strlen) {
1374     *r_buf_strlen = 0;
1375   }
1376
1377   if (!text->curl) {
1378     return NULL;
1379   }
1380   if (!text->sell) {
1381     return NULL;
1382   }
1383   if (!text->lines.first) {
1384     return NULL;
1385   }
1386
1387   linef = text->lines.first;
1388   charf = 0;
1389
1390   linel = text->lines.last;
1391   charl = linel->len;
1392
1393   if (linef == text->lines.last) {
1394     length = charl - charf;
1395
1396     buf = MEM_mallocN(length + 2, "text buffer");
1397
1398     BLI_strncpy(buf, linef->line + charf, length + 1);
1399     buf[length] = 0;
1400   }
1401   else {
1402     length = linef->len - charf;
1403     length += charl;
1404     length += 2; /* For the 2 '\n' */
1405
1406     tmp = linef->next;
1407     while (tmp && tmp != linel) {
1408       length += tmp->len + 1;
1409       tmp = tmp->next;
1410     }
1411
1412     buf = MEM_mallocN(length + 1, "cut buffer");
1413
1414     strncpy(buf, linef->line + charf, linef->len - charf);
1415     length = linef->len - charf;
1416
1417     buf[length++] = '\n';
1418
1419     tmp = linef->next;
1420     while (tmp && tmp != linel) {
1421       strncpy(buf + length, tmp->line, tmp->len);
1422       length += tmp->len;
1423
1424       buf[length++] = '\n';
1425
1426       tmp = tmp->next;
1427     }
1428     strncpy(buf + length, linel->line, charl);
1429     length += charl;
1430
1431     /* python compiler wants an empty end line */
1432     buf[length++] = '\n';
1433     buf[length] = 0;
1434   }
1435
1436   if (r_buf_strlen) {
1437     *r_buf_strlen = length;
1438   }
1439
1440   return buf;
1441 }
1442
1443 int txt_find_string(Text *text, const char *findstr, int wrap, int match_case)
1444 {
1445   TextLine *tl, *startl;
1446   const char *s = NULL;
1447
1448   if (!text->curl || !text->sell) {
1449     return 0;
1450   }
1451
1452   txt_order_cursors(text, false);
1453
1454   tl = startl = text->sell;
1455
1456   if (match_case) {
1457     s = strstr(&tl->line[text->selc], findstr);
1458   }
1459   else {
1460     s = BLI_strcasestr(&tl->line[text->selc], findstr);
1461   }
1462   while (!s) {
1463     tl = tl->next;
1464     if (!tl) {
1465       if (wrap) {
1466         tl = text->lines.first;
1467       }
1468       else {
1469         break;
1470       }
1471     }
1472
1473     if (match_case) {
1474       s = strstr(tl->line, findstr);
1475     }
1476     else {
1477       s = BLI_strcasestr(tl->line, findstr);
1478     }
1479     if (tl == startl) {
1480       break;
1481     }
1482   }
1483
1484   if (s) {
1485     int newl = txt_get_span(text->lines.first, tl);
1486     int newc = (int)(s - tl->line);
1487     txt_move_to(text, newl, newc, 0);
1488     txt_move_to(text, newl, newc + strlen(findstr), 1);
1489     return 1;
1490   }
1491   else {
1492     return 0;
1493   }
1494 }
1495
1496 char *txt_sel_to_buf(Text *text, int *r_buf_strlen)
1497 {
1498   char *buf;
1499   int length = 0;
1500   TextLine *tmp, *linef, *linel;
1501   int charf, charl;
1502
1503   if (r_buf_strlen) {
1504     *r_buf_strlen = 0;
1505   }
1506
1507   if (!text->curl) {
1508     return NULL;
1509   }
1510   if (!text->sell) {
1511     return NULL;
1512   }
1513
1514   if (text->curl == text->sell) {
1515     linef = linel = text->curl;
1516
1517     if (text->curc < text->selc) {
1518       charf = text->curc;
1519       charl = text->selc;
1520     }
1521     else {
1522       charf = text->selc;
1523       charl = text->curc;
1524     }
1525   }
1526   else if (txt_get_span(text->curl, text->sell) < 0) {
1527     linef = text->sell;
1528     linel = text->curl;
1529
1530     charf = text->selc;
1531     charl = text->curc;
1532   }
1533   else {
1534     linef = text->curl;
1535     linel = text->sell;
1536
1537     charf = text->curc;
1538     charl = text->selc;
1539   }
1540
1541   if (linef == linel) {
1542     length = charl - charf;
1543
1544     buf = MEM_mallocN(length + 1, "sel buffer");
1545
1546     BLI_strncpy(buf, linef->line + charf, length + 1);
1547   }
1548   else {
1549     length += linef->len - charf;
1550     length += charl;
1551     length++; /* For the '\n' */
1552
1553     tmp = linef->next;
1554     while (tmp && tmp != linel) {
1555       length += tmp->len + 1;
1556       tmp = tmp->next;
1557     }
1558
1559     buf = MEM_mallocN(length + 1, "sel buffer");
1560
1561     strncpy(buf, linef->line + charf, linef->len - charf);
1562     length = linef->len - charf;
1563
1564     buf[length++] = '\n';
1565
1566     tmp = linef->next;
1567     while (tmp && tmp != linel) {
1568       strncpy(buf + length, tmp->line, tmp->len);
1569       length += tmp->len;
1570
1571       buf[length++] = '\n';
1572
1573       tmp = tmp->next;
1574     }
1575     strncpy(buf + length, linel->line, charl);
1576     length += charl;
1577
1578     buf[length] = 0;
1579   }
1580
1581   if (r_buf_strlen) {
1582     *r_buf_strlen = length;
1583   }
1584
1585   return buf;
1586 }
1587
1588 void txt_insert_buf(Text *text, const char *in_buffer)
1589 {
1590   int l = 0, len;
1591   size_t i = 0, j;
1592   TextLine *add;
1593   char *buffer;
1594
1595   if (!in_buffer) {
1596     return;
1597   }
1598
1599   txt_delete_sel(text);
1600
1601   len = strlen(in_buffer);
1602   buffer = BLI_strdupn(in_buffer, len);
1603   len += txt_extended_ascii_as_utf8(&buffer);
1604
1605   /* Read the first line (or as close as possible */
1606   while (buffer[i] && buffer[i] != '\n') {
1607     txt_add_raw_char(text, BLI_str_utf8_as_unicode_step(buffer, &i));
1608   }
1609
1610   if (buffer[i] == '\n') {
1611     txt_split_curline(text);
1612     i++;
1613
1614     while (i < len) {
1615       l = 0;
1616
1617       while (buffer[i] && buffer[i] != '\n') {
1618         i++;
1619         l++;
1620       }
1621
1622       if (buffer[i] == '\n') {
1623         add = txt_new_linen(buffer + (i - l), l);
1624         BLI_insertlinkbefore(&text->lines, text->curl, add);
1625         i++;
1626       }
1627       else {
1628         for (j = i - l; j < i && j < len;) {
1629           txt_add_raw_char(text, BLI_str_utf8_as_unicode_step(buffer, &j));
1630         }
1631         break;
1632       }
1633     }
1634   }
1635
1636   MEM_freeN(buffer);
1637 }
1638
1639 /**************************/
1640 /* Line editing functions */
1641 /**************************/
1642
1643 void txt_split_curline(Text *text)
1644 {
1645   TextLine *ins;
1646   char *left, *right;
1647
1648   if (!text->curl) {
1649     return;
1650   }
1651
1652   txt_delete_sel(text);
1653
1654   /* Make the two half strings */
1655
1656   left = MEM_mallocN(text->curc + 1, "textline_string");
1657   if (text->curc) {
1658     memcpy(left, text->curl->line, text->curc);
1659   }
1660   left[text->curc] = 0;
1661
1662   right = MEM_mallocN(text->curl->len - text->curc + 1, "textline_string");
1663   memcpy(right, text->curl->line + text->curc, text->curl->len - text->curc + 1);
1664
1665   MEM_freeN(text->curl->line);
1666   if (text->curl->format) {
1667     MEM_freeN(text->curl->format);
1668   }
1669
1670   /* Make the new TextLine */
1671
1672   ins = MEM_mallocN(sizeof(TextLine), "textline");
1673   ins->line = left;
1674   ins->format = NULL;
1675   ins->len = text->curc;
1676
1677   text->curl->line = right;
1678   text->curl->format = NULL;
1679   text->curl->len = text->curl->len - text->curc;
1680
1681   BLI_insertlinkbefore(&text->lines, text->curl, ins);
1682
1683   text->curc = 0;
1684
1685   txt_make_dirty(text);
1686   txt_clean_text(text);
1687
1688   txt_pop_sel(text);
1689 }
1690
1691 static void txt_delete_line(Text *text, TextLine *line)
1692 {
1693   if (!text->curl) {
1694     return;
1695   }
1696
1697   BLI_remlink(&text->lines, line);
1698
1699   if (line->line) {
1700     MEM_freeN(line->line);
1701   }
1702   if (line->format) {
1703     MEM_freeN(line->format);
1704   }
1705
1706   MEM_freeN(line);
1707
1708   txt_make_dirty(text);
1709   txt_clean_text(text);
1710 }
1711
1712 static void txt_combine_lines(Text *text, TextLine *linea, TextLine *lineb)
1713 {
1714   char *tmp, *s;
1715
1716   if (!linea || !lineb) {
1717     return;
1718   }
1719
1720   tmp = MEM_mallocN(linea->len + lineb->len + 1, "textline_string");
1721
1722   s = tmp;
1723   s += BLI_strcpy_rlen(s, linea->line);
1724   s += BLI_strcpy_rlen(s, lineb->line);
1725   (void)s;
1726
1727   make_new_line(linea, tmp);
1728
1729   txt_delete_line(text, lineb);
1730
1731   txt_make_dirty(text);
1732   txt_clean_text(text);
1733 }
1734
1735 void txt_duplicate_line(Text *text)
1736 {
1737   TextLine *textline;
1738
1739   if (!text->curl) {
1740     return;
1741   }
1742
1743   if (text->curl == text->sell) {
1744     textline = txt_new_line(text->curl->line);
1745     BLI_insertlinkafter(&text->lines, text->curl, textline);
1746
1747     txt_make_dirty(text);
1748     txt_clean_text(text);
1749   }
1750 }
1751
1752 void txt_delete_char(Text *text)
1753 {
1754   unsigned int c = '\n';
1755
1756   if (!text->curl) {
1757     return;
1758   }
1759
1760   if (txt_has_sel(text)) { /* deleting a selection */
1761     txt_delete_sel(text);
1762     txt_make_dirty(text);
1763     return;
1764   }
1765   else if (text->curc == text->curl->len) { /* Appending two lines */
1766     if (text->curl->next) {
1767       txt_combine_lines(text, text->curl, text->curl->next);
1768       txt_pop_sel(text);
1769     }
1770     else {
1771       return;
1772     }
1773   }
1774   else { /* Just deleting a char */
1775     size_t c_len = 0;
1776     c = BLI_str_utf8_as_unicode_and_size(text->curl->line + text->curc, &c_len);
1777     UNUSED_VARS(c);
1778
1779     memmove(text->curl->line + text->curc,
1780             text->curl->line + text->curc + c_len,
1781             text->curl->len - text->curc - c_len + 1);
1782
1783     text->curl->len -= c_len;
1784
1785     txt_pop_sel(text);
1786   }
1787
1788   txt_make_dirty(text);
1789   txt_clean_text(text);
1790 }
1791
1792 void txt_delete_word(Text *text)
1793 {
1794   txt_jump_right(text, true, true);
1795   txt_delete_sel(text);
1796   txt_make_dirty(text);
1797 }
1798
1799 void txt_backspace_char(Text *text)
1800 {
1801   unsigned int c = '\n';
1802
1803   if (!text->curl) {
1804     return;
1805   }
1806
1807   if (txt_has_sel(text)) { /* deleting a selection */
1808     txt_delete_sel(text);
1809     txt_make_dirty(text);
1810     return;
1811   }
1812   else if (text->curc == 0) { /* Appending two lines */
1813     if (!text->curl->prev) {
1814       return;
1815     }
1816
1817     text->curl = text->curl->prev;
1818     text->curc = text->curl->len;
1819
1820     txt_combine_lines(text, text->curl, text->curl->next);
1821     txt_pop_sel(text);
1822   }
1823   else { /* Just backspacing a char */
1824     size_t c_len = 0;
1825     const char *prev = BLI_str_prev_char_utf8(text->curl->line + text->curc);
1826     c = BLI_str_utf8_as_unicode_and_size(prev, &c_len);
1827     UNUSED_VARS(c);
1828
1829     /* source and destination overlap, don't use memcpy() */
1830     memmove(text->curl->line + text->curc - c_len,
1831             text->curl->line + text->curc,
1832             text->curl->len - text->curc + 1);
1833
1834     text->curl->len -= c_len;
1835     text->curc -= c_len;
1836
1837     txt_pop_sel(text);
1838   }
1839
1840   txt_make_dirty(text);
1841   txt_clean_text(text);
1842 }
1843
1844 void txt_backspace_word(Text *text)
1845 {
1846   txt_jump_left(text, true, true);
1847   txt_delete_sel(text);
1848   txt_make_dirty(text);
1849 }
1850
1851 /* Max spaces to replace a tab with, currently hardcoded to TXT_TABSIZE = 4.
1852  * Used by txt_convert_tab_to_spaces, indent and unindent.
1853  * Remember to change this string according to max tab size */
1854 static char tab_to_spaces[] = "    ";
1855
1856 static void txt_convert_tab_to_spaces(Text *text)
1857 {
1858   /* sb aims to pad adjust the tab-width needed so that the right number of spaces
1859    * is added so that the indention of the line is the right width (i.e. aligned
1860    * to multiples of TXT_TABSIZE)
1861    */
1862   const char *sb = &tab_to_spaces[text->curc % TXT_TABSIZE];
1863   txt_insert_buf(text, sb);
1864 }
1865
1866 static bool txt_add_char_intern(Text *text, unsigned int add, bool replace_tabs)
1867 {
1868   char *tmp, ch[BLI_UTF8_MAX];
1869   size_t add_len;
1870
1871   if (!text->curl) {
1872     return 0;
1873   }
1874
1875   if (add == '\n') {
1876     txt_split_curline(text);
1877     return true;
1878   }
1879
1880   /* insert spaces rather than tabs */
1881   if (add == '\t' && replace_tabs) {
1882     txt_convert_tab_to_spaces(text);
1883     return true;
1884   }
1885
1886   txt_delete_sel(text);
1887
1888   add_len = BLI_str_utf8_from_unicode(add, ch);
1889
1890   tmp = MEM_mallocN(text->curl->len + add_len + 1, "textline_string");
1891
1892   memcpy(tmp, text->curl->line, text->curc);
1893   memcpy(tmp + text->curc, ch, add_len);
1894   memcpy(
1895       tmp + text->curc + add_len, text->curl->line + text->curc, text->curl->len - text->curc + 1);
1896
1897   make_new_line(text->curl, tmp);
1898
1899   text->curc += add_len;
1900
1901   txt_pop_sel(text);
1902
1903   txt_make_dirty(text);
1904   txt_clean_text(text);
1905
1906   return 1;
1907 }
1908
1909 bool txt_add_char(Text *text, unsigned int add)
1910 {
1911   return txt_add_char_intern(text, add, (text->flags & TXT_TABSTOSPACES) != 0);
1912 }
1913
1914 bool txt_add_raw_char(Text *text, unsigned int add)
1915 {
1916   return txt_add_char_intern(text, add, 0);
1917 }
1918
1919 void txt_delete_selected(Text *text)
1920 {
1921   txt_delete_sel(text);
1922   txt_make_dirty(text);
1923 }
1924
1925 bool txt_replace_char(Text *text, unsigned int add)
1926 {
1927   unsigned int del;
1928   size_t del_size = 0, add_size;
1929   char ch[BLI_UTF8_MAX];
1930
1931   if (!text->curl) {
1932     return false;
1933   }
1934
1935   /* If text is selected or we're at the end of the line just use txt_add_char */
1936   if (text->curc == text->curl->len || txt_has_sel(text) || add == '\n') {
1937     return txt_add_char(text, add);
1938   }
1939
1940   del = BLI_str_utf8_as_unicode_and_size(text->curl->line + text->curc, &del_size);
1941   UNUSED_VARS(del);
1942   add_size = BLI_str_utf8_from_unicode(add, ch);
1943
1944   if (add_size > del_size) {
1945     char *tmp = MEM_mallocN(text->curl->len + add_size - del_size + 1, "textline_string");
1946     memcpy(tmp, text->curl->line, text->curc);
1947     memcpy(tmp + text->curc + add_size,
1948            text->curl->line + text->curc + del_size,
1949            text->curl->len - text->curc - del_size + 1);
1950     MEM_freeN(text->curl->line);
1951     text->curl->line = tmp;
1952   }
1953   else if (add_size < del_size) {
1954     char *tmp = text->curl->line;
1955     memmove(tmp + text->curc + add_size,
1956             tmp + text->curc + del_size,
1957             text->curl->len - text->curc - del_size + 1);
1958   }
1959
1960   memcpy(text->curl->line + text->curc, ch, add_size);
1961   text->curc += add_size;
1962   text->curl->len += add_size - del_size;
1963
1964   txt_pop_sel(text);
1965   txt_make_dirty(text);
1966   txt_clean_text(text);
1967   return true;
1968 }
1969
1970 /**
1971  * Generic prefix operation, use for comment & indent.
1972  *
1973  * \note caller must handle undo.
1974  */
1975 static void txt_select_prefix(Text *text, const char *add)
1976 {
1977   int len, num, curc_old;
1978   char *tmp;
1979
1980   const int indentlen = strlen(add);
1981
1982   BLI_assert(!ELEM(NULL, text->curl, text->sell));
1983
1984   curc_old = text->curc;
1985
1986   num = 0;
1987   while (true) {
1988
1989     /* don't indent blank lines */
1990     if (text->curl->len != 0) {
1991       tmp = MEM_mallocN(text->curl->len + indentlen + 1, "textline_string");
1992
1993       text->curc = 0;
1994       if (text->curc) {
1995         memcpy(tmp, text->curl->line, text->curc); /* XXX never true, check prev line */
1996       }
1997       memcpy(tmp + text->curc, add, indentlen);
1998
1999       len = text->curl->len - text->curc;
2000       if (len > 0) {
2001         memcpy(tmp + text->curc + indentlen, text->curl->line + text->curc, len);
2002       }
2003       tmp[text->curl->len + indentlen] = 0;
2004
2005       make_new_line(text->curl, tmp);
2006
2007       text->curc += indentlen;
2008
2009       txt_make_dirty(text);
2010       txt_clean_text(text);
2011     }
2012
2013     if (text->curl == text->sell) {
2014       text->selc += indentlen;
2015       break;
2016     }
2017     else {
2018       text->curl = text->curl->next;
2019       num++;
2020     }
2021   }
2022   if (!curc_old) {
2023     text->curc = 0;
2024   }
2025   else {
2026     text->curc = curc_old + indentlen;
2027   }
2028
2029   while (num > 0) {
2030     text->curl = text->curl->prev;
2031     num--;
2032   }
2033
2034   /* caller must handle undo */
2035 }
2036
2037 /**
2038  * Generic un-prefix operation, use for comment & indent.
2039  *
2040  * \param r_line_index_mask: List of lines that are already at indent level 0,
2041  * to store them later into the undo buffer.
2042  *
2043  * \note caller must handle undo.
2044  */
2045 static void txt_select_unprefix(Text *text, const char *remove)
2046 {
2047   int num = 0;
2048   const int indentlen = strlen(remove);
2049   bool unindented_first = false;
2050
2051   BLI_assert(!ELEM(NULL, text->curl, text->sell));
2052
2053   while (true) {
2054     bool changed = false;
2055     if (STREQLEN(text->curl->line, remove, indentlen)) {
2056       if (num == 0) {
2057         unindented_first = true;
2058       }
2059       text->curl->len -= indentlen;
2060       memmove(text->curl->line, text->curl->line + indentlen, text->curl->len + 1);
2061       changed = true;
2062     }
2063
2064     txt_make_dirty(text);
2065     txt_clean_text(text);
2066
2067     if (text->curl == text->sell) {
2068       if (changed) {
2069         text->selc = MAX2(text->selc - indentlen, 0);
2070       }
2071       break;
2072     }
2073     else {
2074       text->curl = text->curl->next;
2075       num++;
2076     }
2077   }
2078
2079   if (unindented_first) {
2080     text->curc = MAX2(text->curc - indentlen, 0);
2081   }
2082
2083   while (num > 0) {
2084     text->curl = text->curl->prev;
2085     num--;
2086   }
2087
2088   /* caller must handle undo */
2089 }
2090
2091 void txt_comment(Text *text)
2092 {
2093   const char *prefix = "#";
2094
2095   if (ELEM(NULL, text->curl, text->sell)) {
2096     return;
2097   }
2098
2099   txt_select_prefix(text, prefix);
2100 }
2101
2102 void txt_uncomment(Text *text)
2103 {
2104   const char *prefix = "#";
2105
2106   if (ELEM(NULL, text->curl, text->sell)) {
2107     return;
2108   }
2109
2110   txt_select_unprefix(text, prefix);
2111 }
2112
2113 void txt_indent(Text *text)
2114 {
2115   const char *prefix = (text->flags & TXT_TABSTOSPACES) ? tab_to_spaces : "\t";
2116
2117   if (ELEM(NULL, text->curl, text->sell)) {
2118     return;
2119   }
2120
2121   txt_select_prefix(text, prefix);
2122 }
2123
2124 void txt_unindent(Text *text)
2125 {
2126   const char *prefix = (text->flags & TXT_TABSTOSPACES) ? tab_to_spaces : "\t";
2127
2128   if (ELEM(NULL, text->curl, text->sell)) {
2129     return;
2130   }
2131
2132   txt_select_unprefix(text, prefix);
2133 }
2134
2135 void txt_move_lines(struct Text *text, const int direction)
2136 {
2137   TextLine *line_other;
2138
2139   BLI_assert(ELEM(direction, TXT_MOVE_LINE_UP, TXT_MOVE_LINE_DOWN));
2140
2141   if (!text->curl || !text->sell) {
2142     return;
2143   }
2144
2145   txt_order_cursors(text, false);
2146
2147   line_other = (direction == TXT_MOVE_LINE_DOWN) ? text->sell->next : text->curl->prev;
2148
2149   if (!line_other) {
2150     return;
2151   }
2152
2153   BLI_remlink(&text->lines, line_other);
2154
2155   if (direction == TXT_MOVE_LINE_DOWN) {
2156     BLI_insertlinkbefore(&text->lines, text->curl, line_other);
2157   }
2158   else {
2159     BLI_insertlinkafter(&text->lines, text->sell, line_other);
2160   }
2161
2162   txt_make_dirty(text);
2163   txt_clean_text(text);
2164 }
2165
2166 int txt_setcurr_tab_spaces(Text *text, int space)
2167 {
2168   int i = 0;
2169   int test = 0;
2170   const char *word = ":";
2171   const char *comm = "#";
2172   const char indent = (text->flags & TXT_TABSTOSPACES) ? ' ' : '\t';
2173   static const char *back_words[] = {"return", "break", "continue", "pass", "yield", NULL};
2174
2175   if (!text->curl) {
2176     return 0;
2177   }
2178
2179   while (text->curl->line[i] == indent) {
2180     // we only count those tabs/spaces that are before any text or before the curs;
2181     if (i == text->curc) {
2182       return i;
2183     }
2184     else {
2185       i++;
2186     }
2187   }
2188   if (strstr(text->curl->line, word)) {
2189     /* if we find a ':' on this line, then add a tab but not if it is:
2190      * 1) in a comment
2191      * 2) within an identifier
2192      * 3) after the cursor (text->curc), i.e. when creating space before a function def [#25414]
2193      */
2194     int a;
2195     bool is_indent = false;
2196     for (a = 0; (a < text->curc) && (text->curl->line[a] != '\0'); a++) {
2197       char ch = text->curl->line[a];
2198       if (ch == '#') {
2199         break;
2200       }
2201       else if (ch == ':') {
2202         is_indent = 1;
2203       }
2204       else if (ch != ' ' && ch != '\t') {
2205         is_indent = 0;
2206       }
2207     }
2208     if (is_indent) {
2209       i += space;
2210     }
2211   }
2212
2213   for (test = 0; back_words[test]; test++) {
2214     /* if there are these key words then remove a tab because we are done with the block */
2215     if (strstr(text->curl->line, back_words[test]) && i > 0) {
2216       if (strcspn(text->curl->line, back_words[test]) < strcspn(text->curl->line, comm)) {
2217         i -= space;
2218       }
2219     }
2220   }
2221   return i;
2222 }
2223
2224 /*******************************/
2225 /* Character utility functions */
2226 /*******************************/
2227
2228 int text_check_bracket(const char ch)
2229 {
2230   int a;
2231   char opens[] = "([{";
2232   char close[] = ")]}";
2233
2234   for (a = 0; a < (sizeof(opens) - 1); a++) {
2235     if (ch == opens[a]) {
2236       return a + 1;
2237     }
2238     else if (ch == close[a]) {
2239       return -(a + 1);
2240     }
2241   }
2242   return 0;
2243 }
2244
2245 /* TODO, have a function for operators -
2246  * http://docs.python.org/py3k/reference/lexical_analysis.html#operators */
2247 bool text_check_delim(const char ch)
2248 {
2249   int a;
2250   char delims[] = "():\"\' ~!%^&*-+=[]{};/<>|.#\t,@";
2251
2252   for (a = 0; a < (sizeof(delims) - 1); a++) {
2253     if (ch == delims[a]) {
2254       return true;
2255     }
2256   }
2257   return false;
2258 }
2259
2260 bool text_check_digit(const char ch)
2261 {
2262   if (ch < '0') {
2263     return false;
2264   }
2265   if (ch <= '9') {
2266     return true;
2267   }
2268   return false;
2269 }
2270
2271 bool text_check_identifier(const char ch)
2272 {
2273   if (ch < '0') {
2274     return false;
2275   }
2276   if (ch <= '9') {
2277     return true;
2278   }
2279   if (ch < 'A') {
2280     return false;
2281   }
2282   if (ch <= 'Z' || ch == '_') {
2283     return true;
2284   }
2285   if (ch < 'a') {
2286     return false;
2287   }
2288   if (ch <= 'z') {
2289     return true;
2290   }
2291   return false;
2292 }
2293
2294 bool text_check_identifier_nodigit(const char ch)
2295 {
2296   if (ch <= '9') {
2297     return false;
2298   }
2299   if (ch < 'A') {
2300     return false;
2301   }
2302   if (ch <= 'Z' || ch == '_') {
2303     return true;
2304   }
2305   if (ch < 'a') {
2306     return false;
2307   }
2308   if (ch <= 'z') {
2309     return true;
2310   }
2311   return false;
2312 }
2313
2314 #ifndef WITH_PYTHON
2315 int text_check_identifier_unicode(const unsigned int ch)
2316 {
2317   return (ch < 255 && text_check_identifier((unsigned int)ch));
2318 }
2319
2320 int text_check_identifier_nodigit_unicode(const unsigned int ch)
2321 {
2322   return (ch < 255 && text_check_identifier_nodigit((char)ch));
2323 }
2324 #endif /* WITH_PYTHON */
2325
2326 bool text_check_whitespace(const char ch)
2327 {
2328   if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n') {
2329     return true;
2330   }
2331   return false;
2332 }
2333
2334 int text_find_identifier_start(const char *str, int i)
2335 {
2336   if (UNLIKELY(i <= 0)) {
2337     return 0;
2338   }
2339
2340   while (i--) {
2341     if (!text_check_identifier(str[i])) {
2342       break;
2343     }
2344   }
2345   i++;
2346   return i;
2347 }