Cleanup: style, use braces for blenkernel
[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  * The st->top determines at what line the top of the text is displayed.
83  * If the user moves the cursor the st containing that cursor should
84  * be popped ... other st's retain their own top location.
85  *
86  * Undo
87  * --
88  * Undo/Redo works by storing
89  * events in a queue, and a pointer
90  * to the current position in the
91  * queue...
92  *
93  * Events are stored using an
94  * arbitrary op-code system
95  * to keep track of
96  * a) the two cursors (normal and selected)
97  * b) input (visible and control (ie backspace))
98  *
99  * input data is stored as its
100  * ASCII value, the opcodes are
101  * then selected to not conflict.
102  *
103  * opcodes with data in between are
104  * written at the beginning and end
105  * of the data to allow undo and redo
106  * to simply check the code at the current
107  * undo position
108  */
109
110 /* Undo opcodes */
111
112 enum {
113   /* Complex editing */
114   /* 1 - opcode is followed by 1 byte for ascii character and opcode (repeat)) */
115   /* 2 - opcode is followed by 2 bytes for utf-8 character and opcode (repeat)) */
116   /* 3 - opcode is followed by 3 bytes for utf-8 character and opcode (repeat)) */
117   /* 4 - opcode is followed by 4 bytes for unicode character and opcode (repeat)) */
118   UNDO_INSERT_1 = 013,
119   UNDO_INSERT_2 = 014,
120   UNDO_INSERT_3 = 015,
121   UNDO_INSERT_4 = 016,
122
123   UNDO_BS_1 = 017,
124   UNDO_BS_2 = 020,
125   UNDO_BS_3 = 021,
126   UNDO_BS_4 = 022,
127
128   UNDO_DEL_1 = 023,
129   UNDO_DEL_2 = 024,
130   UNDO_DEL_3 = 025,
131   UNDO_DEL_4 = 026,
132
133   /* Text block (opcode is followed
134    * by 4 character length ID + the text
135    * block itself + the 4 character length
136    * ID (repeat) and opcode (repeat)) */
137   UNDO_DBLOCK = 027, /* Delete block */
138   UNDO_IBLOCK = 030, /* Insert block */
139
140   /* Misc */
141   UNDO_INDENT = 032,
142   UNDO_UNINDENT = 033,
143   UNDO_COMMENT = 034,
144   UNDO_UNCOMMENT = 035,
145
146   UNDO_MOVE_LINES_UP = 036,
147   UNDO_MOVE_LINES_DOWN = 037,
148
149   UNDO_DUPLICATE = 040,
150 };
151
152 /***/
153
154 static void txt_pop_first(Text *text);
155 static void txt_pop_last(Text *text);
156 static void txt_undo_add_blockop(Text *text, TextUndoBuf *utxt, int op, const char *buf);
157 static void txt_delete_line(Text *text, TextLine *line);
158 static void txt_delete_sel(Text *text, TextUndoBuf *utxt);
159 static void txt_make_dirty(Text *text);
160
161 /***/
162
163 /**
164  * Set to true when undoing (so we don't generate undo steps while undoing).
165  *
166  * Also use to disable undo entirely.
167  */
168 static bool undoing;
169
170 /**
171  * \note caller must handle `undo_buf` and `compiled` members.
172  */
173 void BKE_text_free_lines(Text *text)
174 {
175   for (TextLine *tmp = text->lines.first, *tmp_next; tmp; tmp = tmp_next) {
176     tmp_next = tmp->next;
177     MEM_freeN(tmp->line);
178     if (tmp->format) {
179       MEM_freeN(tmp->format);
180     }
181     MEM_freeN(tmp);
182   }
183
184   BLI_listbase_clear(&text->lines);
185
186   text->curl = text->sell = NULL;
187 }
188
189 /** Free (or release) any data used by this text (does not free the text itself). */
190 void BKE_text_free(Text *text)
191 {
192   /* No animdata here. */
193
194   BKE_text_free_lines(text);
195
196   MEM_SAFE_FREE(text->name);
197 #ifdef WITH_PYTHON
198   BPY_text_free_code(text);
199 #endif
200 }
201
202 void BKE_text_init(Text *ta)
203 {
204   TextLine *tmp;
205
206   BLI_assert(MEMCMP_STRUCT_AFTER_IS_ZERO(ta, id));
207
208   ta->name = NULL;
209
210   ta->nlines = 1;
211   ta->flags = TXT_ISDIRTY | TXT_ISMEM;
212   if ((U.flag & USER_TXT_TABSTOSPACES_DISABLE) == 0) {
213     ta->flags |= TXT_TABSTOSPACES;
214   }
215
216   BLI_listbase_clear(&ta->lines);
217
218   tmp = (TextLine *)MEM_mallocN(sizeof(TextLine), "textline");
219   tmp->line = (char *)MEM_mallocN(1, "textline_string");
220   tmp->format = NULL;
221
222   tmp->line[0] = 0;
223   tmp->len = 0;
224
225   tmp->next = NULL;
226   tmp->prev = NULL;
227
228   BLI_addhead(&ta->lines, tmp);
229
230   ta->curl = ta->lines.first;
231   ta->curc = 0;
232   ta->sell = ta->lines.first;
233   ta->selc = 0;
234 }
235
236 Text *BKE_text_add(Main *bmain, const char *name)
237 {
238   Text *ta;
239
240   ta = BKE_libblock_alloc(bmain, ID_TXT, name, 0);
241
242   BKE_text_init(ta);
243
244   return ta;
245 }
246
247 /* this function replaces extended ascii characters */
248 /* to a valid utf-8 sequences */
249 int txt_extended_ascii_as_utf8(char **str)
250 {
251   ptrdiff_t bad_char, i = 0;
252   const ptrdiff_t length = (ptrdiff_t)strlen(*str);
253   int added = 0;
254
255   while ((*str)[i]) {
256     if ((bad_char = BLI_utf8_invalid_byte(*str + i, length - i)) == -1) {
257       break;
258     }
259
260     added++;
261     i += bad_char + 1;
262   }
263
264   if (added != 0) {
265     char *newstr = MEM_mallocN(length + added + 1, "text_line");
266     ptrdiff_t mi = 0;
267     i = 0;
268
269     while ((*str)[i]) {
270       if ((bad_char = BLI_utf8_invalid_byte((*str) + i, length - i)) == -1) {
271         memcpy(newstr + mi, (*str) + i, length - i + 1);
272         break;
273       }
274
275       memcpy(newstr + mi, (*str) + i, bad_char);
276
277       BLI_str_utf8_from_unicode((*str)[i + bad_char], newstr + mi + bad_char);
278       i += bad_char + 1;
279       mi += bad_char + 2;
280     }
281     newstr[length + added] = '\0';
282     MEM_freeN(*str);
283     *str = newstr;
284   }
285
286   return added;
287 }
288
289 // this function removes any control characters from
290 // a textline and fixes invalid utf-8 sequences
291
292 static void cleanup_textline(TextLine *tl)
293 {
294   int i;
295
296   for (i = 0; i < tl->len; i++) {
297     if (tl->line[i] < ' ' && tl->line[i] != '\t') {
298       memmove(tl->line + i, tl->line + i + 1, tl->len - i);
299       tl->len--;
300       i--;
301     }
302   }
303   tl->len += txt_extended_ascii_as_utf8(&tl->line);
304 }
305
306 /**
307  * used for load and reload (unlike txt_insert_buf)
308  * assumes all fields are empty
309  */
310 static void text_from_buf(Text *text, const unsigned char *buffer, const int len)
311 {
312   int i, llen;
313
314   BLI_assert(BLI_listbase_is_empty(&text->lines));
315
316   text->nlines = 0;
317   llen = 0;
318   for (i = 0; i < len; i++) {
319     if (buffer[i] == '\n') {
320       TextLine *tmp;
321
322       tmp = (TextLine *)MEM_mallocN(sizeof(TextLine), "textline");
323       tmp->line = (char *)MEM_mallocN(llen + 1, "textline_string");
324       tmp->format = NULL;
325
326       if (llen) {
327         memcpy(tmp->line, &buffer[i - llen], llen);
328       }
329       tmp->line[llen] = 0;
330       tmp->len = llen;
331
332       cleanup_textline(tmp);
333
334       BLI_addtail(&text->lines, tmp);
335       text->nlines++;
336
337       llen = 0;
338       continue;
339     }
340     llen++;
341   }
342
343   /* create new line in cases:
344    * - rest of line (if last line in file hasn't got \n terminator).
345    *   in this case content of such line would be used to fill text line buffer
346    * - file is empty. in this case new line is needed to start editing from.
347    * - last character in buffer is \n. in this case new line is needed to
348    *   deal with newline at end of file. (see [#28087]) (sergey) */
349   if (llen != 0 || text->nlines == 0 || buffer[len - 1] == '\n') {
350     TextLine *tmp;
351
352     tmp = (TextLine *)MEM_mallocN(sizeof(TextLine), "textline");
353     tmp->line = (char *)MEM_mallocN(llen + 1, "textline_string");
354     tmp->format = NULL;
355
356     if (llen) {
357       memcpy(tmp->line, &buffer[i - llen], llen);
358     }
359
360     tmp->line[llen] = 0;
361     tmp->len = llen;
362
363     cleanup_textline(tmp);
364
365     BLI_addtail(&text->lines, tmp);
366     text->nlines++;
367   }
368
369   text->curl = text->sell = text->lines.first;
370   text->curc = text->selc = 0;
371 }
372
373 bool BKE_text_reload(Text *text)
374 {
375   unsigned char *buffer;
376   size_t buffer_len;
377   char filepath_abs[FILE_MAX];
378   BLI_stat_t st;
379
380   if (!text->name) {
381     return false;
382   }
383
384   BLI_strncpy(filepath_abs, text->name, FILE_MAX);
385   BLI_path_abs(filepath_abs, BKE_main_blendfile_path_from_global());
386
387   buffer = BLI_file_read_text_as_mem(filepath_abs, 0, &buffer_len);
388   if (buffer == NULL) {
389     return false;
390   }
391
392   /* free memory: */
393   BKE_text_free_lines(text);
394   txt_make_dirty(text);
395
396   /* clear undo buffer */
397   if (BLI_stat(filepath_abs, &st) != -1) {
398     text->mtime = st.st_mtime;
399   }
400   else {
401     text->mtime = 0;
402   }
403
404   text_from_buf(text, buffer, buffer_len);
405
406   MEM_freeN(buffer);
407   return true;
408 }
409
410 Text *BKE_text_load_ex(Main *bmain, const char *file, const char *relpath, const bool is_internal)
411 {
412   unsigned char *buffer;
413   size_t buffer_len;
414   Text *ta;
415   char filepath_abs[FILE_MAX];
416   BLI_stat_t st;
417
418   BLI_strncpy(filepath_abs, file, FILE_MAX);
419   if (relpath) { /* can be NULL (bg mode) */
420     BLI_path_abs(filepath_abs, relpath);
421   }
422
423   buffer = BLI_file_read_text_as_mem(filepath_abs, 0, &buffer_len);
424   if (buffer == NULL) {
425     return false;
426   }
427
428   ta = BKE_libblock_alloc(bmain, ID_TXT, BLI_path_basename(filepath_abs), 0);
429   ta->id.us = 0;
430
431   BLI_listbase_clear(&ta->lines);
432   ta->curl = ta->sell = NULL;
433
434   if ((U.flag & USER_TXT_TABSTOSPACES_DISABLE) == 0) {
435     ta->flags = TXT_TABSTOSPACES;
436   }
437
438   if (is_internal == false) {
439     ta->name = MEM_mallocN(strlen(file) + 1, "text_name");
440     strcpy(ta->name, file);
441   }
442   else {
443     ta->flags |= TXT_ISMEM | TXT_ISDIRTY;
444   }
445
446   /* clear undo buffer */
447   if (BLI_stat(filepath_abs, &st) != -1) {
448     ta->mtime = st.st_mtime;
449   }
450   else {
451     ta->mtime = 0;
452   }
453
454   text_from_buf(ta, buffer, buffer_len);
455
456   MEM_freeN(buffer);
457
458   return ta;
459 }
460
461 Text *BKE_text_load(Main *bmain, const char *file, const char *relpath)
462 {
463   return BKE_text_load_ex(bmain, file, relpath, false);
464 }
465
466 /**
467  * Only copy internal data of Text ID from source to already allocated/initialized destination.
468  * You probably never want to use that directly, use BKE_id_copy or BKE_id_copy_ex for typical needs.
469  *
470  * WARNING! This function will not handle ID user count!
471  *
472  * \param flag: Copying options (see BKE_library.h's LIB_ID_COPY_... flags for more).
473  */
474 void BKE_text_copy_data(Main *UNUSED(bmain),
475                         Text *ta_dst,
476                         const Text *ta_src,
477                         const int UNUSED(flag))
478 {
479   /* file name can be NULL */
480   if (ta_src->name) {
481     ta_dst->name = BLI_strdup(ta_src->name);
482   }
483
484   ta_dst->flags |= TXT_ISDIRTY;
485
486   BLI_listbase_clear(&ta_dst->lines);
487   ta_dst->curl = ta_dst->sell = NULL;
488   ta_dst->compiled = NULL;
489
490   /* Walk down, reconstructing */
491   for (TextLine *line_src = ta_src->lines.first; line_src; line_src = line_src->next) {
492     TextLine *line_dst = MEM_mallocN(sizeof(*line_dst), __func__);
493
494     line_dst->line = BLI_strdup(line_src->line);
495     line_dst->format = NULL;
496     line_dst->len = line_src->len;
497
498     BLI_addtail(&ta_dst->lines, line_dst);
499   }
500
501   ta_dst->curl = ta_dst->sell = ta_dst->lines.first;
502   ta_dst->curc = ta_dst->selc = 0;
503 }
504
505 Text *BKE_text_copy(Main *bmain, const Text *ta)
506 {
507   Text *ta_copy;
508   BKE_id_copy(bmain, &ta->id, (ID **)&ta_copy);
509   return ta_copy;
510 }
511
512 void BKE_text_make_local(Main *bmain, Text *text, const bool lib_local)
513 {
514   BKE_id_make_local_generic(bmain, &text->id, true, lib_local);
515 }
516
517 void BKE_text_clear(Text *text, TextUndoBuf *utxt) /* called directly from rna */
518 {
519   const bool undoing_orig = undoing;
520   undoing = (utxt == NULL);
521
522   txt_sel_all(text);
523   txt_delete_sel(text, utxt);
524
525   undoing = undoing_orig;
526
527   txt_make_dirty(text);
528 }
529
530 void BKE_text_write(Text *text, TextUndoBuf *utxt, const char *str) /* called directly from rna */
531 {
532   const bool undoing_orig = undoing;
533   undoing = (utxt == NULL);
534
535   txt_insert_buf(text, utxt, str);
536   txt_move_eof(text, 0);
537
538   undoing = undoing_orig;
539
540   txt_make_dirty(text);
541 }
542
543 /* returns 0 if file on disk is the same or Text is in memory only
544  * returns 1 if file has been modified on disk since last local edit
545  * returns 2 if file on disk has been deleted
546  * -1 is returned if an error occurs */
547
548 int BKE_text_file_modified_check(Text *text)
549 {
550   BLI_stat_t st;
551   int result;
552   char file[FILE_MAX];
553
554   if (!text->name) {
555     return 0;
556   }
557
558   BLI_strncpy(file, text->name, FILE_MAX);
559   BLI_path_abs(file, BKE_main_blendfile_path_from_global());
560
561   if (!BLI_exists(file)) {
562     return 2;
563   }
564
565   result = BLI_stat(file, &st);
566
567   if (result == -1) {
568     return -1;
569   }
570
571   if ((st.st_mode & S_IFMT) != S_IFREG) {
572     return -1;
573   }
574
575   if (st.st_mtime > text->mtime) {
576     return 1;
577   }
578
579   return 0;
580 }
581
582 void BKE_text_file_modified_ignore(Text *text)
583 {
584   BLI_stat_t st;
585   int result;
586   char file[FILE_MAX];
587
588   if (!text->name) {
589     return;
590   }
591
592   BLI_strncpy(file, text->name, FILE_MAX);
593   BLI_path_abs(file, BKE_main_blendfile_path_from_global());
594
595   if (!BLI_exists(file)) {
596     return;
597   }
598
599   result = BLI_stat(file, &st);
600
601   if (result == -1 || (st.st_mode & S_IFMT) != S_IFREG) {
602     return;
603   }
604
605   text->mtime = st.st_mtime;
606 }
607
608 /*****************************/
609 /* Editing utility functions */
610 /*****************************/
611
612 static void make_new_line(TextLine *line, char *newline)
613 {
614   if (line->line) {
615     MEM_freeN(line->line);
616   }
617   if (line->format) {
618     MEM_freeN(line->format);
619   }
620
621   line->line = newline;
622   line->len = strlen(newline);
623   line->format = NULL;
624 }
625
626 static TextLine *txt_new_line(const char *str)
627 {
628   TextLine *tmp;
629
630   if (!str) {
631     str = "";
632   }
633
634   tmp = (TextLine *)MEM_mallocN(sizeof(TextLine), "textline");
635   tmp->line = MEM_mallocN(strlen(str) + 1, "textline_string");
636   tmp->format = NULL;
637
638   strcpy(tmp->line, str);
639
640   tmp->len = strlen(str);
641   tmp->next = tmp->prev = NULL;
642
643   return tmp;
644 }
645
646 static TextLine *txt_new_linen(const char *str, int n)
647 {
648   TextLine *tmp;
649
650   tmp = (TextLine *)MEM_mallocN(sizeof(TextLine), "textline");
651   tmp->line = MEM_mallocN(n + 1, "textline_string");
652   tmp->format = NULL;
653
654   BLI_strncpy(tmp->line, (str) ? str : "", n + 1);
655
656   tmp->len = strlen(tmp->line);
657   tmp->next = tmp->prev = NULL;
658
659   return tmp;
660 }
661
662 void txt_clean_text(Text *text)
663 {
664   TextLine **top, **bot;
665
666   if (!text->lines.first) {
667     if (text->lines.last) {
668       text->lines.first = text->lines.last;
669     }
670     else {
671       text->lines.first = text->lines.last = txt_new_line(NULL);
672     }
673   }
674
675   if (!text->lines.last) {
676     text->lines.last = text->lines.first;
677   }
678
679   top = (TextLine **)&text->lines.first;
680   bot = (TextLine **)&text->lines.last;
681
682   while ((*top)->prev) {
683     *top = (*top)->prev;
684   }
685   while ((*bot)->next) {
686     *bot = (*bot)->next;
687   }
688
689   if (!text->curl) {
690     if (text->sell) {
691       text->curl = text->sell;
692     }
693     else {
694       text->curl = text->lines.first;
695     }
696     text->curc = 0;
697   }
698
699   if (!text->sell) {
700     text->sell = text->curl;
701     text->selc = 0;
702   }
703 }
704
705 int txt_get_span(TextLine *from, TextLine *to)
706 {
707   int ret = 0;
708   TextLine *tmp = from;
709
710   if (!to || !from) {
711     return 0;
712   }
713   if (from == to) {
714     return 0;
715   }
716
717   /* Look forwards */
718   while (tmp) {
719     if (tmp == to) {
720       return ret;
721     }
722     ret++;
723     tmp = tmp->next;
724   }
725
726   /* Look backwards */
727   if (!tmp) {
728     tmp = from;
729     ret = 0;
730     while (tmp) {
731       if (tmp == to) {
732         break;
733       }
734       ret--;
735       tmp = tmp->prev;
736     }
737     if (!tmp) {
738       ret = 0;
739     }
740   }
741
742   return ret;
743 }
744
745 static void txt_make_dirty(Text *text)
746 {
747   text->flags |= TXT_ISDIRTY;
748 #ifdef WITH_PYTHON
749   if (text->compiled) {
750     BPY_text_free_code(text);
751   }
752 #endif
753 }
754
755 /****************************/
756 /* Cursor utility functions */
757 /****************************/
758
759 static void txt_curs_cur(Text *text, TextLine ***linep, int **charp)
760 {
761   *linep = &text->curl;
762   *charp = &text->curc;
763 }
764
765 static void txt_curs_sel(Text *text, TextLine ***linep, int **charp)
766 {
767   *linep = &text->sell;
768   *charp = &text->selc;
769 }
770
771 bool txt_cursor_is_line_start(Text *text)
772 {
773   return (text->selc == 0);
774 }
775
776 bool txt_cursor_is_line_end(Text *text)
777 {
778   return (text->selc == text->sell->len);
779 }
780
781 /*****************************/
782 /* Cursor movement functions */
783 /*****************************/
784
785 int txt_utf8_offset_to_index(const char *str, int offset)
786 {
787   int index = 0, pos = 0;
788   while (pos != offset) {
789     pos += BLI_str_utf8_size(str + pos);
790     index++;
791   }
792   return index;
793 }
794
795 int txt_utf8_index_to_offset(const char *str, int index)
796 {
797   int offset = 0, pos = 0;
798   while (pos != index) {
799     offset += BLI_str_utf8_size(str + offset);
800     pos++;
801   }
802   return offset;
803 }
804
805 int txt_utf8_offset_to_column(const char *str, int offset)
806 {
807   int column = 0, pos = 0;
808   while (pos < offset) {
809     column += BLI_str_utf8_char_width_safe(str + pos);
810     pos += BLI_str_utf8_size_safe(str + pos);
811   }
812   return column;
813 }
814
815 int txt_utf8_column_to_offset(const char *str, int column)
816 {
817   int offset = 0, pos = 0, col;
818   while (*(str + offset) && pos < column) {
819     col = BLI_str_utf8_char_width_safe(str + offset);
820     if (pos + col > column) {
821       break;
822     }
823     offset += BLI_str_utf8_size_safe(str + offset);
824     pos += col;
825   }
826   return offset;
827 }
828
829 void txt_move_up(Text *text, const bool sel)
830 {
831   TextLine **linep;
832   int *charp;
833
834   if (sel) {
835     txt_curs_sel(text, &linep, &charp);
836   }
837   else {
838     txt_pop_first(text);
839     txt_curs_cur(text, &linep, &charp);
840   }
841   if (!*linep) {
842     return;
843   }
844
845   if ((*linep)->prev) {
846     int column = txt_utf8_offset_to_column((*linep)->line, *charp);
847     *linep = (*linep)->prev;
848     *charp = txt_utf8_column_to_offset((*linep)->line, column);
849   }
850   else {
851     txt_move_bol(text, sel);
852   }
853
854   if (!sel) {
855     txt_pop_sel(text);
856   }
857 }
858
859 void txt_move_down(Text *text, const bool sel)
860 {
861   TextLine **linep;
862   int *charp;
863
864   if (sel) {
865     txt_curs_sel(text, &linep, &charp);
866   }
867   else {
868     txt_pop_last(text);
869     txt_curs_cur(text, &linep, &charp);
870   }
871   if (!*linep) {
872     return;
873   }
874
875   if ((*linep)->next) {
876     int column = txt_utf8_offset_to_column((*linep)->line, *charp);
877     *linep = (*linep)->next;
878     *charp = txt_utf8_column_to_offset((*linep)->line, column);
879   }
880   else {
881     txt_move_eol(text, sel);
882   }
883
884   if (!sel) {
885     txt_pop_sel(text);
886   }
887 }
888
889 int txt_calc_tab_left(TextLine *tl, int ch)
890 {
891   /* do nice left only if there are only spaces */
892
893   int tabsize = (ch < TXT_TABSIZE) ? ch : TXT_TABSIZE;
894
895   for (int i = 0; i < ch; i++) {
896     if (tl->line[i] != ' ') {
897       tabsize = 0;
898       break;
899     }
900   }
901
902   /* if in the middle of the space-tab */
903   if (tabsize && ch % TXT_TABSIZE != 0) {
904     tabsize = (ch % TXT_TABSIZE);
905   }
906   return tabsize;
907 }
908
909 int txt_calc_tab_right(TextLine *tl, int ch)
910 {
911   if (tl->line[ch] == ' ') {
912     int i;
913     for (i = 0; i < ch; i++) {
914       if (tl->line[i] != ' ') {
915         return 0;
916       }
917     }
918
919     int tabsize = (ch) % TXT_TABSIZE + 1;
920     for (i = ch + 1; tl->line[i] == ' ' && tabsize < TXT_TABSIZE; i++) {
921       tabsize++;
922     }
923
924     return i - ch;
925   }
926   else {
927     return 0;
928   }
929 }
930
931 void txt_move_left(Text *text, const bool sel)
932 {
933   TextLine **linep;
934   int *charp;
935   int tabsize = 0;
936
937   if (sel) {
938     txt_curs_sel(text, &linep, &charp);
939   }
940   else {
941     txt_pop_first(text);
942     txt_curs_cur(text, &linep, &charp);
943   }
944   if (!*linep) {
945     return;
946   }
947
948   if (*charp == 0) {
949     if ((*linep)->prev) {
950       txt_move_up(text, sel);
951       *charp = (*linep)->len;
952     }
953   }
954   else {
955     /* do nice left only if there are only spaces */
956     // TXT_TABSIZE hardcoded in DNA_text_types.h
957     if (text->flags & TXT_TABSTOSPACES) {
958       tabsize = txt_calc_tab_left(*linep, *charp);
959     }
960
961     if (tabsize) {
962       (*charp) -= tabsize;
963     }
964     else {
965       const char *prev = BLI_str_prev_char_utf8((*linep)->line + *charp);
966       *charp = prev - (*linep)->line;
967     }
968   }
969
970   if (!sel) {
971     txt_pop_sel(text);
972   }
973 }
974
975 void txt_move_right(Text *text, const bool sel)
976 {
977   TextLine **linep;
978   int *charp;
979
980   if (sel) {
981     txt_curs_sel(text, &linep, &charp);
982   }
983   else {
984     txt_pop_last(text);
985     txt_curs_cur(text, &linep, &charp);
986   }
987   if (!*linep) {
988     return;
989   }
990
991   if (*charp == (*linep)->len) {
992     if ((*linep)->next) {
993       txt_move_down(text, sel);
994       *charp = 0;
995     }
996   }
997   else {
998     /* do nice right only if there are only spaces */
999     /* spaces hardcoded in DNA_text_types.h */
1000     int tabsize = 0;
1001
1002     if (text->flags & TXT_TABSTOSPACES) {
1003       tabsize = txt_calc_tab_right(*linep, *charp);
1004     }
1005
1006     if (tabsize) {
1007       (*charp) += tabsize;
1008     }
1009     else {
1010       (*charp) += BLI_str_utf8_size((*linep)->line + *charp);
1011     }
1012   }
1013
1014   if (!sel) {
1015     txt_pop_sel(text);
1016   }
1017 }
1018
1019 void txt_jump_left(Text *text, const bool sel, const bool use_init_step)
1020 {
1021   TextLine **linep;
1022   int *charp;
1023
1024   if (sel) {
1025     txt_curs_sel(text, &linep, &charp);
1026   }
1027   else {
1028     txt_pop_first(text);
1029     txt_curs_cur(text, &linep, &charp);
1030   }
1031   if (!*linep) {
1032     return;
1033   }
1034
1035   BLI_str_cursor_step_utf8(
1036       (*linep)->line, (*linep)->len, charp, STRCUR_DIR_PREV, STRCUR_JUMP_DELIM, use_init_step);
1037
1038   if (!sel) {
1039     txt_pop_sel(text);
1040   }
1041 }
1042
1043 void txt_jump_right(Text *text, const bool sel, const bool use_init_step)
1044 {
1045   TextLine **linep;
1046   int *charp;
1047
1048   if (sel) {
1049     txt_curs_sel(text, &linep, &charp);
1050   }
1051   else {
1052     txt_pop_last(text);
1053     txt_curs_cur(text, &linep, &charp);
1054   }
1055   if (!*linep) {
1056     return;
1057   }
1058
1059   BLI_str_cursor_step_utf8(
1060       (*linep)->line, (*linep)->len, charp, STRCUR_DIR_NEXT, STRCUR_JUMP_DELIM, use_init_step);
1061
1062   if (!sel) {
1063     txt_pop_sel(text);
1064   }
1065 }
1066
1067 void txt_move_bol(Text *text, const bool sel)
1068 {
1069   TextLine **linep;
1070   int *charp;
1071
1072   if (sel) {
1073     txt_curs_sel(text, &linep, &charp);
1074   }
1075   else {
1076     txt_curs_cur(text, &linep, &charp);
1077   }
1078   if (!*linep) {
1079     return;
1080   }
1081
1082   *charp = 0;
1083
1084   if (!sel) {
1085     txt_pop_sel(text);
1086   }
1087 }
1088
1089 void txt_move_eol(Text *text, const bool sel)
1090 {
1091   TextLine **linep;
1092   int *charp;
1093
1094   if (sel) {
1095     txt_curs_sel(text, &linep, &charp);
1096   }
1097   else {
1098     txt_curs_cur(text, &linep, &charp);
1099   }
1100   if (!*linep) {
1101     return;
1102   }
1103
1104   *charp = (*linep)->len;
1105
1106   if (!sel) {
1107     txt_pop_sel(text);
1108   }
1109 }
1110
1111 void txt_move_bof(Text *text, const bool sel)
1112 {
1113   TextLine **linep;
1114   int *charp;
1115
1116   if (sel) {
1117     txt_curs_sel(text, &linep, &charp);
1118   }
1119   else {
1120     txt_curs_cur(text, &linep, &charp);
1121   }
1122   if (!*linep) {
1123     return;
1124   }
1125
1126   *linep = text->lines.first;
1127   *charp = 0;
1128
1129   if (!sel) {
1130     txt_pop_sel(text);
1131   }
1132 }
1133
1134 void txt_move_eof(Text *text, const bool sel)
1135 {
1136   TextLine **linep;
1137   int *charp;
1138
1139   if (sel) {
1140     txt_curs_sel(text, &linep, &charp);
1141   }
1142   else {
1143     txt_curs_cur(text, &linep, &charp);
1144   }
1145   if (!*linep) {
1146     return;
1147   }
1148
1149   *linep = text->lines.last;
1150   *charp = (*linep)->len;
1151
1152   if (!sel) {
1153     txt_pop_sel(text);
1154   }
1155 }
1156
1157 void txt_move_toline(Text *text, unsigned int line, const bool sel)
1158 {
1159   txt_move_to(text, line, 0, sel);
1160 }
1161
1162 /* Moves to a certain byte in a line, not a certain utf8-character! */
1163 void txt_move_to(Text *text, unsigned int line, unsigned int ch, const bool sel)
1164 {
1165   TextLine **linep;
1166   int *charp;
1167   unsigned int i;
1168
1169   if (sel) {
1170     txt_curs_sel(text, &linep, &charp);
1171   }
1172   else {
1173     txt_curs_cur(text, &linep, &charp);
1174   }
1175   if (!*linep) {
1176     return;
1177   }
1178
1179   *linep = text->lines.first;
1180   for (i = 0; i < line; i++) {
1181     if ((*linep)->next) {
1182       *linep = (*linep)->next;
1183     }
1184     else {
1185       break;
1186     }
1187   }
1188   if (ch > (unsigned int)((*linep)->len)) {
1189     ch = (unsigned int)((*linep)->len);
1190   }
1191   *charp = ch;
1192
1193   if (!sel) {
1194     txt_pop_sel(text);
1195   }
1196 }
1197
1198 /****************************/
1199 /* Text selection functions */
1200 /****************************/
1201
1202 static void txt_curs_swap(Text *text)
1203 {
1204   TextLine *tmpl;
1205   int tmpc;
1206
1207   tmpl = text->curl;
1208   text->curl = text->sell;
1209   text->sell = tmpl;
1210
1211   tmpc = text->curc;
1212   text->curc = text->selc;
1213   text->selc = tmpc;
1214 }
1215
1216 static void txt_pop_first(Text *text)
1217 {
1218   if (txt_get_span(text->curl, text->sell) < 0 ||
1219       (text->curl == text->sell && text->curc > text->selc)) {
1220     txt_curs_swap(text);
1221   }
1222
1223   txt_pop_sel(text);
1224 }
1225
1226 static void txt_pop_last(Text *text)
1227 {
1228   if (txt_get_span(text->curl, text->sell) > 0 ||
1229       (text->curl == text->sell && text->curc < text->selc)) {
1230     txt_curs_swap(text);
1231   }
1232
1233   txt_pop_sel(text);
1234 }
1235
1236 void txt_pop_sel(Text *text)
1237 {
1238   text->sell = text->curl;
1239   text->selc = text->curc;
1240 }
1241
1242 void txt_order_cursors(Text *text, const bool reverse)
1243 {
1244   if (!text->curl) {
1245     return;
1246   }
1247   if (!text->sell) {
1248     return;
1249   }
1250
1251   /* Flip so text->curl is before/after text->sell */
1252   if (reverse == false) {
1253     if ((txt_get_span(text->curl, text->sell) < 0) ||
1254         (text->curl == text->sell && text->curc > text->selc)) {
1255       txt_curs_swap(text);
1256     }
1257   }
1258   else {
1259     if ((txt_get_span(text->curl, text->sell) > 0) ||
1260         (text->curl == text->sell && text->curc < text->selc)) {
1261       txt_curs_swap(text);
1262     }
1263   }
1264 }
1265
1266 bool txt_has_sel(Text *text)
1267 {
1268   return ((text->curl != text->sell) || (text->curc != text->selc));
1269 }
1270
1271 static void txt_delete_sel(Text *text, TextUndoBuf *utxt)
1272 {
1273   TextLine *tmpl;
1274   char *buf;
1275
1276   if (!text->curl) {
1277     return;
1278   }
1279   if (!text->sell) {
1280     return;
1281   }
1282
1283   if (!txt_has_sel(text)) {
1284     return;
1285   }
1286
1287   txt_order_cursors(text, false);
1288
1289   if (!undoing) {
1290     buf = txt_sel_to_buf(text);
1291     txt_undo_add_blockop(text, utxt, UNDO_DBLOCK, buf);
1292     MEM_freeN(buf);
1293   }
1294
1295   buf = MEM_mallocN(text->curc + (text->sell->len - text->selc) + 1, "textline_string");
1296
1297   strncpy(buf, text->curl->line, text->curc);
1298   strcpy(buf + text->curc, text->sell->line + text->selc);
1299   buf[text->curc + (text->sell->len - text->selc)] = 0;
1300
1301   make_new_line(text->curl, buf);
1302
1303   tmpl = text->sell;
1304   while (tmpl != text->curl) {
1305     tmpl = tmpl->prev;
1306     if (!tmpl) {
1307       break;
1308     }
1309
1310     txt_delete_line(text, tmpl->next);
1311   }
1312
1313   text->sell = text->curl;
1314   text->selc = text->curc;
1315 }
1316
1317 void txt_sel_all(Text *text)
1318 {
1319   text->curl = text->lines.first;
1320   text->curc = 0;
1321
1322   text->sell = text->lines.last;
1323   text->selc = text->sell->len;
1324 }
1325
1326 /**
1327  * Reverse of #txt_pop_sel
1328  * Clears the selection and ensures the cursor is located
1329  * at the selection (where the cursor is visually while editing).
1330  */
1331 void txt_sel_clear(Text *text)
1332 {
1333   if (text->sell) {
1334     text->curl = text->sell;
1335     text->curc = text->selc;
1336   }
1337 }
1338
1339 void txt_sel_line(Text *text)
1340 {
1341   if (!text->curl) {
1342     return;
1343   }
1344
1345   text->curc = 0;
1346   text->sell = text->curl;
1347   text->selc = text->sell->len;
1348 }
1349
1350 /***************************/
1351 /* Cut and paste functions */
1352 /***************************/
1353
1354 char *txt_to_buf(Text *text)
1355 {
1356   int length;
1357   TextLine *tmp, *linef, *linel;
1358   int charf, charl;
1359   char *buf;
1360
1361   if (!text->curl) {
1362     return NULL;
1363   }
1364   if (!text->sell) {
1365     return NULL;
1366   }
1367   if (!text->lines.first) {
1368     return NULL;
1369   }
1370
1371   linef = text->lines.first;
1372   charf = 0;
1373
1374   linel = text->lines.last;
1375   charl = linel->len;
1376
1377   if (linef == text->lines.last) {
1378     length = charl - charf;
1379
1380     buf = MEM_mallocN(length + 2, "text buffer");
1381
1382     BLI_strncpy(buf, linef->line + charf, length + 1);
1383     buf[length] = 0;
1384   }
1385   else {
1386     length = linef->len - charf;
1387     length += charl;
1388     length += 2; /* For the 2 '\n' */
1389
1390     tmp = linef->next;
1391     while (tmp && tmp != linel) {
1392       length += tmp->len + 1;
1393       tmp = tmp->next;
1394     }
1395
1396     buf = MEM_mallocN(length + 1, "cut buffer");
1397
1398     strncpy(buf, linef->line + charf, linef->len - charf);
1399     length = linef->len - charf;
1400
1401     buf[length++] = '\n';
1402
1403     tmp = linef->next;
1404     while (tmp && tmp != linel) {
1405       strncpy(buf + length, tmp->line, tmp->len);
1406       length += tmp->len;
1407
1408       buf[length++] = '\n';
1409
1410       tmp = tmp->next;
1411     }
1412     strncpy(buf + length, linel->line, charl);
1413     length += charl;
1414
1415     /* python compiler wants an empty end line */
1416     buf[length++] = '\n';
1417     buf[length] = 0;
1418   }
1419
1420   return buf;
1421 }
1422
1423 int txt_find_string(Text *text, const char *findstr, int wrap, int match_case)
1424 {
1425   TextLine *tl, *startl;
1426   const char *s = NULL;
1427
1428   if (!text->curl || !text->sell) {
1429     return 0;
1430   }
1431
1432   txt_order_cursors(text, false);
1433
1434   tl = startl = text->sell;
1435
1436   if (match_case) {
1437     s = strstr(&tl->line[text->selc], findstr);
1438   }
1439   else {
1440     s = BLI_strcasestr(&tl->line[text->selc], findstr);
1441   }
1442   while (!s) {
1443     tl = tl->next;
1444     if (!tl) {
1445       if (wrap) {
1446         tl = text->lines.first;
1447       }
1448       else {
1449         break;
1450       }
1451     }
1452
1453     if (match_case) {
1454       s = strstr(tl->line, findstr);
1455     }
1456     else {
1457       s = BLI_strcasestr(tl->line, findstr);
1458     }
1459     if (tl == startl) {
1460       break;
1461     }
1462   }
1463
1464   if (s) {
1465     int newl = txt_get_span(text->lines.first, tl);
1466     int newc = (int)(s - tl->line);
1467     txt_move_to(text, newl, newc, 0);
1468     txt_move_to(text, newl, newc + strlen(findstr), 1);
1469     return 1;
1470   }
1471   else {
1472     return 0;
1473   }
1474 }
1475
1476 char *txt_sel_to_buf(Text *text)
1477 {
1478   char *buf;
1479   int length = 0;
1480   TextLine *tmp, *linef, *linel;
1481   int charf, charl;
1482
1483   if (!text->curl) {
1484     return NULL;
1485   }
1486   if (!text->sell) {
1487     return NULL;
1488   }
1489
1490   if (text->curl == text->sell) {
1491     linef = linel = text->curl;
1492
1493     if (text->curc < text->selc) {
1494       charf = text->curc;
1495       charl = text->selc;
1496     }
1497     else {
1498       charf = text->selc;
1499       charl = text->curc;
1500     }
1501   }
1502   else if (txt_get_span(text->curl, text->sell) < 0) {
1503     linef = text->sell;
1504     linel = text->curl;
1505
1506     charf = text->selc;
1507     charl = text->curc;
1508   }
1509   else {
1510     linef = text->curl;
1511     linel = text->sell;
1512
1513     charf = text->curc;
1514     charl = text->selc;
1515   }
1516
1517   if (linef == linel) {
1518     length = charl - charf;
1519
1520     buf = MEM_mallocN(length + 1, "sel buffer");
1521
1522     BLI_strncpy(buf, linef->line + charf, length + 1);
1523   }
1524   else {
1525     length += linef->len - charf;
1526     length += charl;
1527     length++; /* For the '\n' */
1528
1529     tmp = linef->next;
1530     while (tmp && tmp != linel) {
1531       length += tmp->len + 1;
1532       tmp = tmp->next;
1533     }
1534
1535     buf = MEM_mallocN(length + 1, "sel buffer");
1536
1537     strncpy(buf, linef->line + charf, linef->len - charf);
1538     length = linef->len - charf;
1539
1540     buf[length++] = '\n';
1541
1542     tmp = linef->next;
1543     while (tmp && tmp != linel) {
1544       strncpy(buf + length, tmp->line, tmp->len);
1545       length += tmp->len;
1546
1547       buf[length++] = '\n';
1548
1549       tmp = tmp->next;
1550     }
1551     strncpy(buf + length, linel->line, charl);
1552     length += charl;
1553
1554     buf[length] = 0;
1555   }
1556
1557   return buf;
1558 }
1559
1560 void txt_insert_buf(Text *text, TextUndoBuf *utxt, const char *in_buffer)
1561 {
1562   const bool undoing_orig = undoing;
1563   int l = 0, len;
1564   size_t i = 0, j;
1565   TextLine *add;
1566   char *buffer;
1567
1568   if (!in_buffer) {
1569     return;
1570   }
1571
1572   txt_delete_sel(text, utxt);
1573
1574   len = strlen(in_buffer);
1575   buffer = BLI_strdupn(in_buffer, len);
1576   len += txt_extended_ascii_as_utf8(&buffer);
1577
1578   if (!undoing) {
1579     txt_undo_add_blockop(text, utxt, UNDO_IBLOCK, buffer);
1580   }
1581   undoing = true;
1582
1583   /* Read the first line (or as close as possible */
1584   while (buffer[i] && buffer[i] != '\n') {
1585     txt_add_raw_char(text, utxt, BLI_str_utf8_as_unicode_step(buffer, &i));
1586   }
1587
1588   if (buffer[i] == '\n') {
1589     txt_split_curline(text, utxt);
1590     i++;
1591
1592     while (i < len) {
1593       l = 0;
1594
1595       while (buffer[i] && buffer[i] != '\n') {
1596         i++;
1597         l++;
1598       }
1599
1600       if (buffer[i] == '\n') {
1601         add = txt_new_linen(buffer + (i - l), l);
1602         BLI_insertlinkbefore(&text->lines, text->curl, add);
1603         i++;
1604       }
1605       else {
1606         for (j = i - l; j < i && j < len;) {
1607           txt_add_raw_char(text, utxt, BLI_str_utf8_as_unicode_step(buffer, &j));
1608         }
1609         break;
1610       }
1611     }
1612   }
1613
1614   MEM_freeN(buffer);
1615   undoing = undoing_orig;
1616 }
1617
1618 /******************/
1619 /* Undo functions */
1620 /******************/
1621
1622 static bool max_undo_test(TextUndoBuf *utxt, int x)
1623 {
1624   /* Normally over-allocating is preferred,
1625    * however in this case the buffer is small enough and re-allocation
1626    * fast enough for each undo step that it's not a problem to allocate each time.
1627    * This also saves on some memory when we have many text buffers
1628    * that would have an empty undo memory allocated.
1629    */
1630
1631   /* Add one for the null terminator. */
1632   utxt->len = utxt->pos + x + 1;
1633   if (utxt->len > TXT_MAX_UNDO) {
1634     /* XXX error("Undo limit reached, buffer cleared\n"); */
1635     MEM_freeN(utxt->buf);
1636     return false;
1637   }
1638   else {
1639     /* Small reallocations on each undo step is fine. */
1640     utxt->buf = MEM_recallocN(utxt->buf, utxt->len);
1641   }
1642   return true;
1643 }
1644
1645 static void txt_undo_end(Text *UNUSED(text), TextUndoBuf *utxt)
1646 {
1647   int undo_pos_end = utxt->pos + 1;
1648   BLI_assert(undo_pos_end + 1 == utxt->len);
1649   utxt->buf[undo_pos_end] = '\0';
1650 }
1651
1652 /* Call once undo is done. */
1653 #ifndef NDEBUG
1654
1655 #endif
1656
1657 #if 0 /* UNUSED */
1658 static void dump_buffer(TextUndoBuf *utxt)
1659 {
1660   int i = 0;
1661
1662   while (i++ < utxt->undo_pos)
1663     printf("%d: %d %c\n", i, utxt->buf[i], utxt->buf[i]);
1664 }
1665
1666 /* Note: this function is outdated and must be updated if needed for future use */
1667 void txt_print_undo(Text *text)
1668 {
1669   int i = 0;
1670   int op;
1671   const char *ops;
1672   int linep, charp;
1673
1674   dump_buffer(text);
1675
1676   printf("---< Undo Buffer >---\n");
1677
1678   printf("UndoPosition is %d\n", utxt->pos);
1679
1680   while (i <= utxt->pos) {
1681     op = utxt->buf[i];
1682
1683     if (op == UNDO_INSERT_1) {
1684       ops = "Insert ascii ";
1685     }
1686     else if (op == UNDO_INSERT_2) {
1687       ops = "Insert 2 bytes ";
1688     }
1689     else if (op == UNDO_INSERT_3) {
1690       ops = "Insert 3 bytes ";
1691     }
1692     else if (op == UNDO_INSERT_4) {
1693       ops = "Insert unicode ";
1694     }
1695     else if (op == UNDO_BS_1) {
1696       ops = "Backspace for ascii ";
1697     }
1698     else if (op == UNDO_BS_2) {
1699       ops = "Backspace for 2 bytes ";
1700     }
1701     else if (op == UNDO_BS_3) {
1702       ops = "Backspace for 3 bytes ";
1703     }
1704     else if (op == UNDO_BS_4) {
1705       ops = "Backspace for unicode ";
1706     }
1707     else if (op == UNDO_DEL_1) {
1708       ops = "Delete ascii ";
1709     }
1710     else if (op == UNDO_DEL_2) {
1711       ops = "Delete 2 bytes ";
1712     }
1713     else if (op == UNDO_DEL_3) {
1714       ops = "Delete 3 bytes ";
1715     }
1716     else if (op == UNDO_DEL_4) {
1717       ops = "Delete unicode ";
1718     }
1719     else if (op == UNDO_DBLOCK) {
1720       ops = "Delete text block";
1721     }
1722     else if (op == UNDO_IBLOCK) {
1723       ops = "Insert text block";
1724     }
1725     else if (op == UNDO_INDENT) {
1726       ops = "Indent ";
1727     }
1728     else if (op == UNDO_UNINDENT) {
1729       ops = "Unindent ";
1730     }
1731     else if (op == UNDO_COMMENT) {
1732       ops = "Comment ";
1733     }
1734     else if (op == UNDO_UNCOMMENT) {
1735       ops = "Uncomment ";
1736     }
1737     else {
1738       ops = "Unknown";
1739     }
1740
1741     printf("Op (%o) at %d = %s", op, i, ops);
1742     if (op >= UNDO_INSERT_1 && op <= UNDO_DEL_4) {
1743       i++;
1744       printf(" - Char is ");
1745       switch (op) {
1746         case UNDO_INSERT_1:
1747         case UNDO_BS_1:
1748         case UNDO_DEL_1:
1749           printf("%c", utxt->buf[i]);
1750           i++;
1751           break;
1752         case UNDO_INSERT_2:
1753         case UNDO_BS_2:
1754         case UNDO_DEL_2:
1755           printf("%c%c", utxt->buf[i], utxt->buf[i + 1]);
1756           i += 2;
1757           break;
1758         case UNDO_INSERT_3:
1759         case UNDO_BS_3:
1760         case UNDO_DEL_3:
1761           printf("%c%c%c", utxt->buf[i], utxt->buf[i + 1], utxt->buf[i + 2]);
1762           i += 3;
1763           break;
1764         case UNDO_INSERT_4:
1765         case UNDO_BS_4:
1766         case UNDO_DEL_4: {
1767           unsigned int uc;
1768           char c[BLI_UTF8_MAX + 1];
1769           size_t c_len;
1770           uc = utxt->buf[i];
1771           i++;
1772           uc = uc + (utxt->buf[i] << 8);
1773           i++;
1774           uc = uc + (utxt->buf[i] << 16);
1775           i++;
1776           uc = uc + (utxt->buf[i] << 24);
1777           i++;
1778           c_len = BLI_str_utf8_from_unicode(uc, c);
1779           c[c_len] = '\0';
1780           puts(c);
1781           break;
1782         }
1783       }
1784     }
1785     else if (op == UNDO_DBLOCK || op == UNDO_IBLOCK) {
1786       i++;
1787
1788       linep = utxt->buf[i];
1789       i++;
1790       linep = linep + (utxt->buf[i] << 8);
1791       i++;
1792       linep = linep + (utxt->buf[i] << 16);
1793       i++;
1794       linep = linep + (utxt->buf[i] << 24);
1795       i++;
1796
1797       printf(" (length %d) <", linep);
1798
1799       while (linep > 0) {
1800         putchar(utxt->buf[i]);
1801         linep--;
1802         i++;
1803       }
1804
1805       linep = utxt->buf[i];
1806       i++;
1807       linep = linep + (utxt->buf[i] << 8);
1808       i++;
1809       linep = linep + (utxt->buf[i] << 16);
1810       i++;
1811       linep = linep + (utxt->buf[i] << 24);
1812       i++;
1813       printf("> (%d)", linep);
1814     }
1815     else if (op == UNDO_INDENT || op == UNDO_UNINDENT) {
1816       i++;
1817
1818       charp = utxt->buf[i];
1819       i++;
1820       charp = charp + (utxt->buf[i] << 8);
1821       i++;
1822
1823       linep = utxt->buf[i];
1824       i++;
1825       linep = linep + (utxt->buf[i] << 8);
1826       i++;
1827       linep = linep + (utxt->buf[i] << 16);
1828       i++;
1829       linep = linep + (utxt->buf[i] << 24);
1830       i++;
1831
1832       printf("to <%d, %d> ", linep, charp);
1833
1834       charp = utxt->buf[i];
1835       i++;
1836       charp = charp + (utxt->buf[i] << 8);
1837       i++;
1838
1839       linep = utxt->buf[i];
1840       i++;
1841       linep = linep + (utxt->buf[i] << 8);
1842       i++;
1843       linep = linep + (utxt->buf[i] << 16);
1844       i++;
1845       linep = linep + (utxt->buf[i] << 24);
1846       i++;
1847
1848       printf("from <%d, %d>", linep, charp);
1849     }
1850
1851     printf(" %d\n", i);
1852     i++;
1853   }
1854 }
1855 #endif
1856
1857 static void txt_undo_store_uint16(char *undo_buf, int *undo_pos, unsigned short value)
1858 {
1859   undo_buf[*undo_pos] = (value)&0xff;
1860   (*undo_pos)++;
1861   undo_buf[*undo_pos] = (value >> 8) & 0xff;
1862   (*undo_pos)++;
1863 }
1864
1865 static void txt_undo_store_uint32(char *undo_buf, int *undo_pos, unsigned int value)
1866 {
1867   undo_buf[*undo_pos] = (value)&0xff;
1868   (*undo_pos)++;
1869   undo_buf[*undo_pos] = (value >> 8) & 0xff;
1870   (*undo_pos)++;
1871   undo_buf[*undo_pos] = (value >> 16) & 0xff;
1872   (*undo_pos)++;
1873   undo_buf[*undo_pos] = (value >> 24) & 0xff;
1874   (*undo_pos)++;
1875 }
1876
1877 /* store the cur cursor to the undo buffer (6 bytes)*/
1878 static void txt_undo_store_cur(Text *text, TextUndoBuf *utxt)
1879 {
1880   txt_undo_store_uint16(utxt->buf, &utxt->pos, text->curc);
1881   txt_undo_store_uint32(utxt->buf, &utxt->pos, txt_get_span(text->lines.first, text->curl));
1882 }
1883
1884 /* store the sel cursor to the undo buffer (6 bytes) */
1885 static void txt_undo_store_sel(Text *text, TextUndoBuf *utxt)
1886 {
1887   txt_undo_store_uint16(utxt->buf, &utxt->pos, text->selc);
1888   txt_undo_store_uint32(utxt->buf, &utxt->pos, txt_get_span(text->lines.first, text->sell));
1889 }
1890
1891 /* store both cursors to the undo buffer (12 bytes) */
1892 static void txt_undo_store_cursors(Text *text, TextUndoBuf *utxt)
1893 {
1894   txt_undo_store_cur(text, utxt);
1895   txt_undo_store_sel(text, utxt);
1896 }
1897
1898 /* store an operator along with a block of data */
1899 static void txt_undo_add_blockop(Text *text, TextUndoBuf *utxt, int op, const char *buf)
1900 {
1901   unsigned int length = strlen(buf);
1902
1903   if (!max_undo_test(utxt, 2 + 12 + 4 + length + 4 + 1)) {
1904     return;
1905   }
1906   /* 2 bytes */
1907   utxt->pos++;
1908   utxt->buf[utxt->pos] = op;
1909   utxt->pos++;
1910   /* 12 bytes */
1911   txt_undo_store_cursors(text, utxt);
1912   /* 4 bytes */
1913   txt_undo_store_uint32(utxt->buf, &utxt->pos, length);
1914   /* 'length' bytes */
1915   memcpy(utxt->buf + utxt->pos, buf, length);
1916   utxt->pos += length;
1917   /* 4 bytes */
1918   txt_undo_store_uint32(utxt->buf, &utxt->pos, length);
1919   /* 1 byte */
1920   utxt->buf[utxt->pos] = op;
1921
1922   txt_undo_end(text, utxt);
1923 }
1924
1925 /* store a regular operator */
1926 void txt_undo_add_op(Text *text, TextUndoBuf *utxt, int op)
1927 {
1928   if (!max_undo_test(utxt, 2 + 12 + 1)) {
1929     return;
1930   }
1931
1932   /* 2 bytes */
1933   utxt->pos++;
1934   utxt->buf[utxt->pos] = op;
1935   utxt->pos++;
1936   /* 12 bytes */
1937   txt_undo_store_cursors(text, utxt);
1938   /* 1 byte */
1939   utxt->buf[utxt->pos] = op;
1940
1941   txt_undo_end(text, utxt);
1942 }
1943
1944 /* store an operator for a single character */
1945 static void txt_undo_add_charop(Text *text, TextUndoBuf *utxt, int op_start, unsigned int c)
1946 {
1947   char utf8[BLI_UTF8_MAX];
1948   size_t i, utf8_size = BLI_str_utf8_from_unicode(c, utf8);
1949
1950   if (utf8_size < 4 && 0) {
1951     if (!max_undo_test(utxt, 2 + 6 + utf8_size + 1)) {
1952       return;
1953     }
1954     /* 2 bytes */
1955     utxt->pos++;
1956     utxt->buf[utxt->pos] = op_start + utf8_size - 1;
1957     utxt->pos++;
1958     /* 6 bytes */
1959     txt_undo_store_cur(text, utxt);
1960     /* 'utf8_size' bytes */
1961     for (i = 0; i < utf8_size; i++) {
1962       utxt->buf[utxt->pos] = utf8[i];
1963       utxt->pos++;
1964     }
1965     /* 1 byte */
1966     utxt->buf[utxt->pos] = op_start + utf8_size - 1;
1967   }
1968   else {
1969     if (!max_undo_test(utxt, 2 + 6 + 4 + 1)) {
1970       return;
1971     }
1972     /* 2 bytes */
1973     utxt->pos++;
1974     utxt->buf[utxt->pos] = op_start + 3;
1975     utxt->pos++;
1976     /* 6 bytes */
1977     txt_undo_store_cur(text, utxt);
1978     /* 4 bytes */
1979     txt_undo_store_uint32(utxt->buf, &utxt->pos, c);
1980     /* 1 byte */
1981     utxt->buf[utxt->pos] = op_start + 3;
1982   }
1983
1984   txt_undo_end(text, utxt);
1985 }
1986
1987 /* extends Link */
1988 struct LinkInt {
1989   struct LinkInt *next, *prev;
1990   int value;
1991 };
1992
1993 /**
1994  * UnindentLines points to a #ListBase composed of #LinkInt elements, listing the numbers
1995  * of the lines that should not be indented back.
1996  */
1997 static void txt_undo_add_unprefix_op(Text *text,
1998                                      TextUndoBuf *utxt,
1999                                      char undo_op,
2000                                      const ListBase *line_index_mask,
2001                                      const int line_index_mask_len)
2002 {
2003   struct LinkInt *idata;
2004
2005   BLI_assert(BLI_listbase_count(line_index_mask) == line_index_mask_len);
2006
2007   /* OP byte + UInt32 count + counted UInt32 line numbers + UInt32 count + 12-bytes selection + OP byte */
2008   if (!max_undo_test(utxt, 2 + 4 + (line_index_mask_len * 4) + 4 + 12 + 1)) {
2009     return;
2010   }
2011
2012   /* 2 bytes */
2013   utxt->pos++;
2014   utxt->buf[utxt->pos] = undo_op;
2015   utxt->pos++;
2016   /* Adding number of line numbers to read
2017    * 4 bytes */
2018   txt_undo_store_uint32(utxt->buf, &utxt->pos, line_index_mask_len);
2019
2020   /* Adding linenumbers of lines that shall not be indented if undoing.
2021    * 'line_index_mask_len * 4' bytes */
2022   for (idata = line_index_mask->first; idata; idata = idata->next) {
2023     txt_undo_store_uint32(utxt->buf, &utxt->pos, idata->value);
2024   }
2025
2026   /* Adding number of line numbers to read again.
2027    * 4 bytes */
2028   txt_undo_store_uint32(utxt->buf, &utxt->pos, line_index_mask_len);
2029   /* Adding current selection.
2030    * 12 bytes */
2031   txt_undo_store_cursors(text, utxt);
2032   /* Closing with OP (same as above).
2033    * 1 byte */
2034   utxt->buf[utxt->pos] = undo_op;
2035   /* Marking as last undo operation */
2036   txt_undo_end(text, utxt);
2037 }
2038
2039 static unsigned short txt_undo_read_uint16(const char *undo_buf, int *undo_pos)
2040 {
2041   unsigned short val;
2042   val = undo_buf[*undo_pos];
2043   (*undo_pos)--;
2044   val = (val << 8) + undo_buf[*undo_pos];
2045   (*undo_pos)--;
2046   return val;
2047 }
2048
2049 static unsigned int txt_undo_read_uint32(const char *undo_buf, int *undo_pos)
2050 {
2051   unsigned int val;
2052   val = undo_buf[*undo_pos];
2053   (*undo_pos)--;
2054   val = (val << 8) + undo_buf[*undo_pos];
2055   (*undo_pos)--;
2056   val = (val << 8) + undo_buf[*undo_pos];
2057   (*undo_pos)--;
2058   val = (val << 8) + undo_buf[*undo_pos];
2059   (*undo_pos)--;
2060   return val;
2061 }
2062
2063 /* read the cur cursor from the undo buffer */
2064 static void txt_undo_read_cur(const char *undo_buf,
2065                               int *undo_pos,
2066                               unsigned int *curln,
2067                               unsigned short *curc)
2068 {
2069   *curln = txt_undo_read_uint32(undo_buf, undo_pos);
2070   *curc = txt_undo_read_uint16(undo_buf, undo_pos);
2071 }
2072
2073 /* read the sel cursor from the undo buffer */
2074 static void txt_undo_read_sel(const char *undo_buf,
2075                               int *undo_pos,
2076                               unsigned int *selln,
2077                               unsigned short *selc)
2078 {
2079   *selln = txt_undo_read_uint32(undo_buf, undo_pos);
2080   *selc = txt_undo_read_uint16(undo_buf, undo_pos);
2081 }
2082
2083 /* read both cursors from the undo buffer */
2084 static void txt_undo_read_cursors(const char *undo_buf,
2085                                   int *undo_pos,
2086                                   unsigned int *curln,
2087                                   unsigned short *curc,
2088                                   unsigned int *selln,
2089                                   unsigned short *selc)
2090 {
2091   txt_undo_read_sel(undo_buf, undo_pos, selln, selc);
2092   txt_undo_read_cur(undo_buf, undo_pos, curln, curc);
2093 }
2094
2095 static unsigned int txt_undo_read_unicode(const char *undo_buf, int *undo_pos, short bytes)
2096 {
2097   unsigned int unicode;
2098   char utf8[BLI_UTF8_MAX + 1];
2099
2100   switch (bytes) {
2101     case 1: /* ascii */
2102       unicode = undo_buf[*undo_pos];
2103       (*undo_pos)--;
2104       break;
2105     case 2: /* 2-byte symbol */
2106       utf8[2] = '\0';
2107       utf8[1] = undo_buf[*undo_pos];
2108       (*undo_pos)--;
2109       utf8[0] = undo_buf[*undo_pos];
2110       (*undo_pos)--;
2111       unicode = BLI_str_utf8_as_unicode(utf8);
2112       break;
2113     case 3: /* 3-byte symbol */
2114       utf8[3] = '\0';
2115       utf8[2] = undo_buf[*undo_pos];
2116       (*undo_pos)--;
2117       utf8[1] = undo_buf[*undo_pos];
2118       (*undo_pos)--;
2119       utf8[0] = undo_buf[*undo_pos];
2120       (*undo_pos)--;
2121       unicode = BLI_str_utf8_as_unicode(utf8);
2122       break;
2123     case 4: /* 32-bit unicode symbol */
2124       unicode = txt_undo_read_uint32(undo_buf, undo_pos);
2125       break;
2126     default:
2127       /* should never happen */
2128       BLI_assert(0);
2129       unicode = 0;
2130       break;
2131   }
2132
2133   return unicode;
2134 }
2135
2136 static unsigned short txt_redo_read_uint16(const char *undo_buf, int *undo_pos)
2137 {
2138   unsigned short val;
2139   val = undo_buf[*undo_pos];
2140   (*undo_pos)++;
2141   val = val + (undo_buf[*undo_pos] << 8);
2142   (*undo_pos)++;
2143   return val;
2144 }
2145
2146 static unsigned int txt_redo_read_uint32(const char *undo_buf, int *undo_pos)
2147 {
2148   unsigned int val;
2149   val = undo_buf[*undo_pos];
2150   (*undo_pos)++;
2151   val = val + (undo_buf[*undo_pos] << 8);
2152   (*undo_pos)++;
2153   val = val + (undo_buf[*undo_pos] << 16);
2154   (*undo_pos)++;
2155   val = val + (undo_buf[*undo_pos] << 24);
2156   (*undo_pos)++;
2157   return val;
2158 }
2159
2160 /* redo read cur cursor from the undo buffer */
2161 static void txt_redo_read_cur(const char *undo_buf,
2162                               int *undo_pos,
2163                               unsigned int *curln,
2164                               unsigned short *curc)
2165 {
2166   *curc = txt_redo_read_uint16(undo_buf, undo_pos);
2167   *curln = txt_redo_read_uint32(undo_buf, undo_pos);
2168 }
2169
2170 /* redo read sel cursor from the undo buffer */
2171 static void txt_redo_read_sel(const char *undo_buf,
2172                               int *undo_pos,
2173                               unsigned int *selln,
2174                               unsigned short *selc)
2175 {
2176   *selc = txt_redo_read_uint16(undo_buf, undo_pos);
2177   *selln = txt_redo_read_uint32(undo_buf, undo_pos);
2178 }
2179
2180 /* redo read both cursors from the undo buffer */
2181 static void txt_redo_read_cursors(const char *undo_buf,
2182                                   int *undo_pos,
2183                                   unsigned int *curln,
2184                                   unsigned short *curc,
2185                                   unsigned int *selln,
2186                                   unsigned short *selc)
2187 {
2188   txt_redo_read_cur(undo_buf, undo_pos, curln, curc);
2189   txt_redo_read_sel(undo_buf, undo_pos, selln, selc);
2190 }
2191
2192 static unsigned int txt_redo_read_unicode(const char *undo_buf, int *undo_pos, short bytes)
2193 {
2194   unsigned int unicode;
2195   char utf8[BLI_UTF8_MAX + 1];
2196
2197   switch (bytes) {
2198     case 1: /* ascii */
2199       unicode = undo_buf[*undo_pos];
2200       (*undo_pos)++;
2201       break;
2202     case 2: /* 2-byte symbol */
2203       utf8[0] = undo_buf[*undo_pos];
2204       (*undo_pos)++;
2205       utf8[1] = undo_buf[*undo_pos];
2206       (*undo_pos)++;
2207       utf8[2] = '\0';
2208       unicode = BLI_str_utf8_as_unicode(utf8);
2209       break;
2210     case 3: /* 3-byte symbol */
2211       utf8[0] = undo_buf[*undo_pos];
2212       (*undo_pos)++;
2213       utf8[1] = undo_buf[*undo_pos];
2214       (*undo_pos)++;
2215       utf8[2] = undo_buf[*undo_pos];
2216       (*undo_pos)++;
2217       utf8[3] = '\0';
2218       unicode = BLI_str_utf8_as_unicode(utf8);
2219       break;
2220     case 4: /* 32-bit unicode symbol */
2221       unicode = txt_redo_read_uint32(undo_buf, undo_pos);
2222       break;
2223     default:
2224       /* should never happen */
2225       BLI_assert(0);
2226       unicode = 0;
2227       break;
2228   }
2229
2230   return unicode;
2231 }
2232
2233 void txt_do_undo(Text *text, TextUndoBuf *utxt)
2234 {
2235   int op = utxt->buf[utxt->pos];
2236   int prev_flags;
2237   unsigned int linep;
2238   unsigned int uni_char;
2239   unsigned int curln, selln;
2240   unsigned short curc, selc;
2241   unsigned short charp;
2242   char *buf;
2243
2244   if (utxt->pos < 0) {
2245     return;
2246   }
2247
2248   utxt->pos--;
2249
2250   undoing = 1;
2251
2252   switch (op) {
2253     case UNDO_INSERT_1:
2254     case UNDO_INSERT_2:
2255     case UNDO_INSERT_3:
2256     case UNDO_INSERT_4:
2257       utxt->pos -= op - UNDO_INSERT_1 + 1;
2258
2259       /* get and restore the cursors */
2260       txt_undo_read_cur(utxt->buf, &utxt->pos, &curln, &curc);
2261       txt_move_to(text, curln, curc, 0);
2262       txt_move_to(text, curln, curc, 1);
2263
2264       txt_delete_char(text, utxt);
2265
2266       utxt->pos--;
2267       break;
2268
2269     case UNDO_BS_1:
2270     case UNDO_BS_2:
2271     case UNDO_BS_3:
2272     case UNDO_BS_4:
2273       charp = op - UNDO_BS_1 + 1;
2274       uni_char = txt_undo_read_unicode(utxt->buf, &utxt->pos, charp);
2275
2276       /* get and restore the cursors */
2277       txt_undo_read_cur(utxt->buf, &utxt->pos, &curln, &curc);
2278       txt_move_to(text, curln, curc, 0);
2279       txt_move_to(text, curln, curc, 1);
2280
2281       txt_add_char(text, utxt, uni_char);
2282
2283       utxt->pos--;
2284       break;
2285
2286     case UNDO_DEL_1:
2287     case UNDO_DEL_2:
2288     case UNDO_DEL_3:
2289     case UNDO_DEL_4:
2290       charp = op - UNDO_DEL_1 + 1;
2291       uni_char = txt_undo_read_unicode(utxt->buf, &utxt->pos, charp);
2292
2293       /* get and restore the cursors */
2294       txt_undo_read_cur(utxt->buf, &utxt->pos, &curln, &curc);
2295       txt_move_to(text, curln, curc, 0);
2296       txt_move_to(text, curln, curc, 1);
2297
2298       txt_add_char(text, utxt, uni_char);
2299
2300       txt_move_left(text, 0);
2301
2302       utxt->pos--;
2303       break;
2304
2305     case UNDO_DBLOCK: {
2306       int i;
2307       /* length of the string in the buffer */
2308       linep = txt_undo_read_uint32(utxt->buf, &utxt->pos);
2309
2310       buf = MEM_mallocN(linep + 1, "dblock buffer");
2311       for (i = 0; i < linep; i++) {
2312         buf[(linep - 1) - i] = utxt->buf[utxt->pos];
2313         utxt->pos--;
2314       }
2315       buf[i] = 0;
2316
2317       /* skip over the length that was stored again */
2318       utxt->pos -= 4;
2319
2320       /* Get the cursor positions */
2321       txt_undo_read_cursors(utxt->buf, &utxt->pos, &curln, &curc, &selln, &selc);
2322
2323       /* move cur to location that needs buff inserted */
2324       txt_move_to(text, curln, curc, 0);
2325
2326       txt_insert_buf(text, utxt, buf);
2327       MEM_freeN(buf);
2328
2329       /* restore the cursors */
2330       txt_move_to(text, curln, curc, 0);
2331       txt_move_to(text, selln, selc, 1);
2332
2333       utxt->pos--;
2334
2335       break;
2336     }
2337     case UNDO_IBLOCK: {
2338       int i;
2339       /* length of the string in the buffer */
2340       linep = txt_undo_read_uint32(utxt->buf, &utxt->pos);
2341
2342       /* txt_backspace_char removes utf8-characters, not bytes */
2343       buf = MEM_mallocN(linep + 1, "iblock buffer");
2344       for (i = 0; i < linep; i++) {
2345         buf[(linep - 1) - i] = utxt->buf[utxt->pos];
2346         utxt->pos--;
2347       }
2348       buf[i] = 0;
2349       linep = BLI_strlen_utf8(buf);
2350       MEM_freeN(buf);
2351
2352       /* skip over the length that was stored again */
2353       utxt->pos -= 4;
2354
2355       /* get and restore the cursors */
2356       txt_undo_read_cursors(utxt->buf, &utxt->pos, &curln, &curc, &selln, &selc);
2357
2358       txt_move_to(text, curln, curc, 0);
2359       txt_move_to(text, selln, selc, 1);
2360
2361       if ((curln == selln) && (curc == selc)) {
2362         /* disable tabs to spaces since moving right may involve skipping multiple spaces */
2363         prev_flags = text->flags;
2364         text->flags &= ~TXT_TABSTOSPACES;
2365
2366         for (i = 0; i < linep; i++) {
2367           txt_move_right(text, 1);
2368         }
2369
2370         text->flags = prev_flags;
2371       }
2372
2373       txt_delete_selected(text, utxt);
2374
2375       utxt->pos--;
2376       break;
2377     }
2378     case UNDO_INDENT:
2379     case UNDO_COMMENT:
2380     case UNDO_DUPLICATE:
2381     case UNDO_MOVE_LINES_UP:
2382     case UNDO_MOVE_LINES_DOWN:
2383       /* get and restore the cursors */
2384       txt_undo_read_cursors(utxt->buf, &utxt->pos, &curln, &curc, &selln, &selc);
2385       txt_move_to(text, curln, curc, 0);
2386       txt_move_to(text, selln, selc, 1);
2387
2388       if (op == UNDO_INDENT) {
2389         txt_unindent(text, utxt);
2390       }
2391       else if (op == UNDO_COMMENT) {
2392         txt_uncomment(text, utxt);
2393       }
2394       else if (op == UNDO_DUPLICATE) {
2395         txt_delete_line(text, text->curl->next);
2396       }
2397       else if (op == UNDO_MOVE_LINES_UP) {
2398         txt_move_lines(text, utxt, TXT_MOVE_LINE_DOWN);
2399       }
2400       else if (op == UNDO_MOVE_LINES_DOWN) {
2401         txt_move_lines(text, utxt, TXT_MOVE_LINE_UP);
2402       }
2403
2404       utxt->pos--;
2405       break;
2406     case UNDO_UNINDENT:
2407     case UNDO_UNCOMMENT: {
2408       void (*txt_prefix_fn)(Text *, TextUndoBuf *);
2409       void (*txt_unprefix_fn)(Text *, TextUndoBuf *);
2410       int count;
2411       int i;
2412       /* Get and restore the cursors */
2413       txt_undo_read_cursors(utxt->buf, &utxt->pos, &curln, &curc, &selln, &selc);
2414       txt_move_to(text, curln, curc, 0);
2415       txt_move_to(text, selln, selc, 1);
2416
2417       /* Un-unindent */
2418       if (op == UNDO_UNINDENT) {
2419         txt_prefix_fn = txt_indent;
2420         txt_unprefix_fn = txt_unindent;
2421       }
2422       else {
2423         txt_prefix_fn = txt_comment;
2424         txt_unprefix_fn = txt_uncomment;
2425       }
2426
2427       txt_prefix_fn(text, utxt);
2428
2429       /* Get the count */
2430       count = txt_undo_read_uint32(utxt->buf, &utxt->pos);
2431       /* Iterate! */
2432       txt_pop_sel(text);
2433
2434       for (i = 0; i < count; i++) {
2435         txt_move_to(text, txt_undo_read_uint32(utxt->buf, &utxt->pos), 0, 0);
2436         /* Un-un-unindent/comment */
2437         txt_unprefix_fn(text, utxt);
2438       }
2439       /* Restore selection */
2440       txt_move_to(text, curln, curc, 0);
2441       txt_move_to(text, selln, selc, 1);
2442       /* Jumo over count */
2443       txt_undo_read_uint32(utxt->buf, &utxt->pos);
2444       /* Jump over closing OP byte */
2445       utxt->pos--;
2446       break;
2447     }
2448     default:
2449       //XXX error("Undo buffer error - resetting");
2450       utxt->pos = -1;
2451
2452       break;
2453   }
2454
2455   undoing = 0;
2456 }
2457
2458 void txt_do_redo(Text *text, TextUndoBuf *utxt)
2459 {
2460   char op;
2461   char *buf;
2462   unsigned int linep;
2463   unsigned short charp;
2464   unsigned int uni_uchar;
2465   unsigned int curln, selln;
2466   unsigned short curc, selc;
2467
2468   utxt->pos++;
2469   op = utxt->buf[utxt->pos];
2470
2471   if (!op) {
2472     utxt->pos--;
2473     return;
2474   }
2475
2476   undoing = 1;
2477
2478   switch (op) {
2479     case UNDO_INSERT_1:
2480     case UNDO_INSERT_2:
2481     case UNDO_INSERT_3:
2482     case UNDO_INSERT_4:
2483       utxt->pos++;
2484
2485       /* get and restore the cursors */
2486       txt_redo_read_cur(utxt->buf, &utxt->pos, &curln, &curc);
2487       txt_move_to(text, curln, curc, 0);
2488       txt_move_to(text, curln, curc, 1);
2489
2490       charp = op - UNDO_INSERT_1 + 1;
2491       uni_uchar = txt_redo_read_unicode(utxt->buf, &utxt->pos, charp);
2492
2493       txt_add_char(text, utxt, uni_uchar);
2494       break;
2495
2496     case UNDO_BS_1:
2497     case UNDO_BS_2:
2498     case UNDO_BS_3:
2499     case UNDO_BS_4:
2500       utxt->pos++;
2501
2502       /* get and restore the cursors */
2503       txt_redo_read_cur(utxt->buf, &utxt->pos, &curln, &curc);
2504       txt_move_to(text, curln, curc, 0);
2505       txt_move_to(text, curln, curc, 1);
2506
2507       utxt->pos += op - UNDO_BS_1 + 1;
2508
2509       /* move right so we backspace the correct char */
2510       txt_move_right(text, 0);
2511       txt_backspace_char(text, utxt);
2512
2513       break;
2514
2515     case UNDO_DEL_1:
2516     case UNDO_DEL_2:
2517     case UNDO_DEL_3:
2518     case UNDO_DEL_4:
2519       utxt->pos++;
2520
2521       /* get and restore the cursors */
2522       txt_redo_read_cur(utxt->buf, &utxt->pos, &curln, &curc);
2523       txt_move_to(text, curln, curc, 0);
2524       txt_move_to(text, curln, curc, 1);
2525
2526       utxt->pos += op - UNDO_DEL_1 + 1;
2527
2528       txt_delete_char(text, utxt);
2529
2530       break;
2531
2532     case UNDO_DBLOCK:
2533       utxt->pos++;
2534
2535       /* get and restore the cursors */
2536       txt_redo_read_cursors(utxt->buf, &utxt->pos, &curln, &curc, &selln, &selc);
2537       txt_move_to(text, curln, curc, 0);
2538       txt_move_to(text, selln, selc, 1);
2539
2540       /* length of the block */
2541       linep = txt_redo_read_uint32(utxt->buf, &utxt->pos);
2542
2543       utxt->pos += linep;
2544
2545       /* skip over the length that was stored again */
2546       utxt->pos += 4;
2547
2548       txt_delete_sel(text, utxt);
2549
2550       break;
2551
2552     case UNDO_IBLOCK:
2553       utxt->pos++;
2554
2555       /* get and restore the cursors */
2556       txt_redo_read_cursors(utxt->buf, &utxt->pos, &curln, &curc, &selln, &selc);
2557       txt_move_to(text, curln, curc, 0);
2558       txt_move_to(text, curln, curc, 1);
2559
2560       /* length of the block */
2561       linep = txt_redo_read_uint32(utxt->buf, &utxt->pos);
2562
2563       buf = MEM_mallocN(linep + 1, "iblock buffer");
2564       memcpy(buf, &utxt->buf[utxt->pos], linep);
2565       utxt->pos += linep;
2566       buf[linep] = 0;
2567
2568       txt_insert_buf(text, utxt, buf);
2569       MEM_freeN(buf);
2570
2571       /* skip over the length that was stored again */
2572       utxt->pos += 4;
2573
2574       break;
2575
2576     case UNDO_INDENT:
2577     case UNDO_COMMENT:
2578     case UNDO_UNCOMMENT:
2579     case UNDO_DUPLICATE:
2580     case UNDO_MOVE_LINES_UP:
2581     case UNDO_MOVE_LINES_DOWN:
2582       utxt->pos++;
2583
2584       /* get and restore the cursors */
2585       txt_redo_read_cursors(utxt->buf, &utxt->pos, &curln, &curc, &selln, &selc);
2586       txt_move_to(text, curln, curc, 0);
2587       txt_move_to(text, selln, selc, 1);
2588
2589       if (op == UNDO_INDENT) {
2590         txt_indent(text, utxt);
2591       }
2592       else if (op == UNDO_COMMENT) {
2593         txt_comment(text, utxt);
2594       }
2595       else if (op == UNDO_UNCOMMENT) {
2596         txt_uncomment(text, utxt);
2597       }
2598       else if (op == UNDO_DUPLICATE) {
2599         txt_duplicate_line(text, utxt);
2600       }
2601       else if (op == UNDO_MOVE_LINES_UP) {
2602         /* offset the cursor by + 1 */
2603         txt_move_to(text, curln + 1, curc, 0);
2604         txt_move_to(text, selln + 1, selc, 1);
2605
2606         txt_move_lines(text, utxt, TXT_MOVE_LINE_UP);
2607       }
2608       else if (op == UNDO_MOVE_LINES_DOWN) {
2609         /* offset the cursor by - 1 */
2610         txt_move_to(text, curln - 1, curc, 0);
2611         txt_move_to(text, selln - 1, selc, 1);
2612
2613         txt_move_lines(text, utxt, TXT_MOVE_LINE_DOWN);
2614       }
2615
2616       /* re-restore the cursors since they got moved when redoing */
2617       txt_move_to(text, curln, curc, 0);
2618       txt_move_to(text, selln, selc, 1);
2619
2620       break;
2621     case UNDO_UNINDENT: {
2622       int count;
2623       int i;
2624
2625       utxt->pos++;
2626       /* Scan all the stuff described in txt_undo_add_unindent_op */
2627       count = txt_redo_read_uint32(utxt->buf, &utxt->pos);
2628       for (i = 0; i < count; i++) {
2629         txt_redo_read_uint32(utxt->buf, &utxt->pos);
2630       }
2631       /* Count again */
2632       txt_redo_read_uint32(utxt->buf, &utxt->pos);
2633       /* Get the selection and re-unindent */
2634       txt_redo_read_cursors(utxt->buf, &utxt->pos, &curln, &curc, &selln, &selc);
2635       txt_move_to(text, curln, curc, 0);
2636       txt_move_to(text, selln, selc, 1);
2637       txt_unindent(text, utxt);
2638       break;
2639     }
2640     default:
2641       //XXX error("Undo buffer error - resetting");
2642       utxt->pos = -1;
2643
2644       break;
2645   }
2646
2647   undoing = 0;
2648 }
2649
2650 /**************************/
2651 /* Line editing functions */
2652 /**************************/
2653
2654 void txt_split_curline(Text *text, TextUndoBuf *utxt)
2655 {
2656   TextLine *ins;
2657   char *left, *right;
2658
2659   if (!text->curl) {
2660     return;
2661   }
2662
2663   txt_delete_sel(text, utxt);
2664
2665   if (!undoing) {
2666     txt_undo_add_charop(text, utxt, UNDO_INSERT_1, '\n');
2667   }
2668
2669   /* Make the two half strings */
2670
2671   left = MEM_mallocN(text->curc + 1, "textline_string");
2672   if (text->curc) {
2673     memcpy(left, text->curl->line, text->curc);
2674   }
2675   left[text->curc] = 0;
2676
2677   right = MEM_mallocN(text->curl->len - text->curc + 1, "textline_string");
2678   memcpy(right, text->curl->line + text->curc, text->curl->len - text->curc + 1);
2679
2680   MEM_freeN(text->curl->line);
2681   if (text->curl->format) {
2682     MEM_freeN(text->curl->format);
2683   }
2684
2685   /* Make the new TextLine */
2686
2687   ins = MEM_mallocN(sizeof(TextLine), "textline");
2688   ins->line = left;
2689   ins->format = NULL;
2690   ins->len = text->curc;
2691
2692   text->curl->line = right;
2693   text->curl->format = NULL;
2694   text->curl->len = text->curl->len - text->curc;
2695
2696   BLI_insertlinkbefore(&text->lines, text->curl, ins);
2697
2698   text->curc = 0;
2699
2700   txt_make_dirty(text);
2701   txt_clean_text(text);
2702
2703   txt_pop_sel(text);
2704 }
2705
2706 static void txt_delete_line(Text *text, TextLine *line)
2707 {
2708   if (!text->curl) {
2709     return;
2710   }
2711
2712   BLI_remlink(&text->lines, line);
2713
2714   if (line->line) {
2715     MEM_freeN(line->line);
2716   }
2717   if (line->format) {
2718     MEM_freeN(line->format);
2719   }
2720
2721   MEM_freeN(line);
2722
2723   txt_make_dirty(text);
2724   txt_clean_text(text);
2725 }
2726
2727 static void txt_combine_lines(Text *text, TextLine *linea, TextLine *lineb)
2728 {
2729   char *tmp, *s;
2730
2731   if (!linea || !lineb) {
2732     return;
2733   }
2734
2735   tmp = MEM_mallocN(linea->len + lineb->len + 1, "textline_string");
2736
2737   s = tmp;
2738   s += BLI_strcpy_rlen(s, linea->line);
2739   s += BLI_strcpy_rlen(s, lineb->line);
2740   (void)s;
2741
2742   make_new_line(linea, tmp);
2743
2744   txt_delete_line(text, lineb);
2745
2746   txt_make_dirty(text);
2747   txt_clean_text(text);
2748 }
2749
2750 void txt_duplicate_line(Text *text, TextUndoBuf *utxt)
2751 {
2752   TextLine *textline;
2753
2754   if (!text->curl) {
2755     return;
2756   }
2757
2758   if (text->curl == text->sell) {
2759     textline = txt_new_line(text->curl->line);
2760     BLI_insertlinkafter(&text->lines, text->curl, textline);
2761
2762     txt_make_dirty(text);
2763     txt_clean_text(text);
2764
2765     if (!undoing) {
2766       txt_undo_add_op(text, utxt, UNDO_DUPLICATE);
2767     }
2768   }
2769 }
2770
2771 void txt_delete_char(Text *text, TextUndoBuf *utxt)
2772 {
2773   unsigned int c = '\n';
2774
2775   if (!text->curl) {
2776     return;
2777   }
2778
2779   if (txt_has_sel(text)) { /* deleting a selection */
2780     txt_delete_sel(text, utxt);
2781     txt_make_dirty(text);
2782     return;
2783   }
2784   else if (text->curc == text->curl->len) { /* Appending two lines */
2785     if (text->curl->next) {
2786       txt_combine_lines(text, text->curl, text->curl->next);
2787       txt_pop_sel(text);
2788     }
2789     else {
2790       return;
2791     }
2792   }
2793   else { /* Just deleting a char */
2794     size_t c_len = 0;
2795     c = BLI_str_utf8_as_unicode_and_size(text->curl->line + text->curc, &c_len);
2796
2797     memmove(text->curl->line + text->curc,
2798             text->curl->line + text->curc + c_len,
2799             text->curl->len - text->curc - c_len + 1);
2800
2801     text->curl->len -= c_len;
2802
2803     txt_pop_sel(text);
2804   }
2805
2806   txt_make_dirty(text);
2807   txt_clean_text(text);
2808
2809   if (!undoing) {
2810     txt_undo_add_charop(text, utxt, UNDO_DEL_1, c);
2811   }
2812 }
2813
2814 void txt_delete_word(Text *text, TextUndoBuf *utxt)
2815 {
2816   txt_jump_right(text, true, true);
2817   txt_delete_sel(text, utxt);
2818   txt_make_dirty(text);
2819 }
2820
2821 void txt_backspace_char(Text *text, TextUndoBuf *utxt)
2822 {
2823   unsigned int c = '\n';
2824
2825   if (!text->curl) {
2826     return;
2827   }
2828
2829   if (txt_has_sel(text)) { /* deleting a selection */
2830     txt_delete_sel(text, utxt);
2831     txt_make_dirty(text);
2832     return;
2833   }
2834   else if (text->curc == 0) { /* Appending two lines */
2835     if (!text->curl->prev) {
2836       return;
2837     }
2838
2839     text->curl = text->curl->prev;
2840     text->curc = text->curl->len;
2841
2842     txt_combine_lines(text, text->curl, text->curl->next);
2843     txt_pop_sel(text);
2844   }
2845   else { /* Just backspacing a char */
2846     size_t c_len = 0;
2847     const char *prev = BLI_str_prev_char_utf8(text->curl->line + text->curc);
2848     c = BLI_str_utf8_as_unicode_and_size(prev, &c_len);
2849
2850     /* source and destination overlap, don't use memcpy() */
2851     memmove(text->curl->line + text->curc - c_len,
2852             text->curl->line + text->curc,
2853             text->curl->len - text->curc + 1);
2854
2855     text->curl->len -= c_len;
2856     text->curc -= c_len;
2857
2858     txt_pop_sel(text);
2859   }
2860
2861   txt_make_dirty(text);
2862   txt_clean_text(text);
2863
2864   if (!undoing) {
2865     txt_undo_add_charop(text, utxt, UNDO_BS_1, c);
2866   }
2867 }
2868
2869 void txt_backspace_word(Text *text, TextUndoBuf *utxt)
2870 {
2871   txt_jump_left(text, true, true);
2872   txt_delete_sel(text, utxt);
2873   txt_make_dirty(text);
2874 }
2875
2876 /* Max spaces to replace a tab with, currently hardcoded to TXT_TABSIZE = 4.
2877  * Used by txt_convert_tab_to_spaces, indent and unindent.
2878  * Remember to change this string according to max tab size */
2879 static char tab_to_spaces[] = "    ";
2880
2881 static void txt_convert_tab_to_spaces(Text *text, TextUndoBuf *utxt)
2882 {
2883   /* sb aims to pad adjust the tab-width needed so that the right number of spaces
2884    * is added so that the indention of the line is the right width (i.e. aligned
2885    * to multiples of TXT_TABSIZE)
2886    */
2887   const char *sb = &tab_to_spaces[text->curc % TXT_TABSIZE];
2888   txt_insert_buf(text, utxt, sb);
2889 }
2890
2891 static bool txt_add_char_intern(Text *text, TextUndoBuf *utxt, unsigned int add, bool replace_tabs)
2892 {
2893   char *tmp, ch[BLI_UTF8_MAX];
2894   size_t add_len;
2895
2896   if (!text->curl) {
2897     return 0;
2898   }
2899
2900   if (add == '\n') {
2901     txt_split_curline(text, utxt);
2902     return true;
2903   }
2904
2905   /* insert spaces rather than tabs */
2906   if (add == '\t' && replace_tabs) {
2907     txt_convert_tab_to_spaces(text, utxt);
2908     return true;
2909   }
2910
2911   txt_delete_sel(text, utxt);
2912
2913   if (!undoing) {
2914     txt_undo_add_charop(text, utxt, UNDO_INSERT_1, add);
2915   }
2916
2917   add_len = BLI_str_utf8_from_unicode(add, ch);
2918
2919   tmp = MEM_mallocN(text->curl->len + add_len + 1, "textline_string");
2920
2921   memcpy(tmp, text->curl->line, text->curc);
2922   memcpy(tmp + text->curc, ch, add_len);
2923   memcpy(
2924       tmp + text->curc + add_len, text->curl->line + text->curc, text->curl->len - text->curc + 1);
2925
2926   make_new_line(text->curl, tmp);
2927
2928   text->curc += add_len;
2929
2930   txt_pop_sel(text);
2931
2932   txt_make_dirty(text);
2933   txt_clean_text(text);
2934
2935   return 1;
2936 }
2937
2938 bool txt_add_char(Text *text, TextUndoBuf *utxt, unsigned int add)
2939 {
2940   return txt_add_char_intern(text, utxt, add, (text->flags & TXT_TABSTOSPACES) != 0);
2941 }
2942
2943 bool txt_add_raw_char(Text *text, TextUndoBuf *utxt, unsigned int add)
2944 {
2945   return txt_add_char_intern(text, utxt, add, 0);
2946 }
2947
2948 void txt_delete_selected(Text *text, TextUndoBuf *utxt)
2949 {
2950   txt_delete_sel(text, utxt);
2951   txt_make_dirty(text);
2952 }
2953
2954 bool txt_replace_char(Text *text, TextUndoBuf *utxt, unsigned int add)
2955 {
2956   unsigned int del;
2957   size_t del_size = 0, add_size;
2958   char ch[BLI_UTF8_MAX];
2959
2960   if (!text->curl) {
2961     return false;
2962   }
2963
2964   /* If text is selected or we're at the end of the line just use txt_add_char */
2965   if (text->curc == text->curl->len || txt_has_sel(text) || add == '\n') {
2966     return txt_add_char(text, utxt, add);
2967   }
2968
2969   del = BLI_str_utf8_as_unicode_and_size(text->curl->line + text->curc, &del_size);
2970   add_size = BLI_str_utf8_from_unicode(add, ch);
2971
2972   if (add_size > del_size) {
2973     char *tmp = MEM_mallocN(text->curl->len + add_size - del_size + 1, "textline_string");
2974     memcpy(tmp, text->curl->line, text->curc);
2975     memcpy(tmp + text->curc + add_size,
2976            text->curl->line + text->curc + del_size,
2977            text->curl->len - text->curc - del_size + 1);
2978     MEM_freeN(text->curl->line);
2979     text->curl->line = tmp;
2980   }
2981   else if (add_size < del_size) {
2982     char *tmp = text->curl->line;
2983     memmove(tmp + text->curc + add_size,
2984             tmp + text->curc + del_size,
2985             text->curl->len - text->curc - del_size + 1);
2986   }
2987
2988   memcpy(text->curl->line + text->curc, ch, add_size);
2989   text->curc += add_size;
2990   text->curl->len += add_size - del_size;
2991
2992   txt_pop_sel(text);
2993   txt_make_dirty(text);
2994   txt_clean_text(text);
2995
2996   /* Should probably create a new op for this */
2997   if (!undoing) {
2998     txt_undo_add_charop(text, utxt, UNDO_INSERT_1, add);
2999     text->curc -= add_size;
3000     txt_pop_sel(text);
3001     txt_undo_add_charop(text, utxt, UNDO_DEL_1, del);
3002     text->curc += add_size;
3003     txt_pop_sel(text);
3004   }
3005   return true;
3006 }
3007
3008 /**
3009  * Generic prefix operation, use for comment & indent.
3010  *
3011  * \note caller must handle undo.
3012  */
3013 static void txt_select_prefix(Text *text, const char *add)
3014 {
3015   int len, num, curc_old;
3016   char *tmp;
3017
3018   const int indentlen = strlen(add);
3019
3020   BLI_assert(!ELEM(NULL, text->curl, text->sell));
3021
3022   curc_old = text->curc;
3023
3024   num = 0;
3025   while (true) {
3026
3027     /* don't indent blank lines */
3028     if (text->curl->len != 0) {
3029       tmp = MEM_mallocN(text->curl->len + indentlen + 1, "textline_string");
3030
3031       text->curc = 0;
3032       if (text->curc) {
3033         memcpy(tmp, text->curl->line, text->curc); /* XXX never true, check prev line */
3034       }
3035       memcpy(tmp + text->curc, add, indentlen);
3036
3037       len = text->curl->len - text->curc;
3038       if (len > 0) {
3039         memcpy(tmp + text->curc + indentlen, text->curl->line + text->curc, len);
3040       }
3041       tmp[text->curl->len + indentlen] = 0;
3042
3043       make_new_line(text->curl, tmp);
3044
3045       text->curc += indentlen;
3046
3047       txt_make_dirty(text);
3048       txt_clean_text(text);
3049     }
3050
3051     if (text->curl == text->sell) {
3052       text->selc += indentlen;
3053       break;
3054     }
3055     else {
3056       text->curl = text->curl->next;
3057       num++;
3058     }
3059   }
3060   if (!curc_old) {
3061     text->curc = 0;
3062   }
3063   else {
3064     text->curc = curc_old + indentlen;
3065   }
3066
3067   while (num > 0) {
3068     text->curl = text->curl->prev;
3069     num--;
3070   }
3071
3072   /* caller must handle undo */
3073 }
3074
3075 /**
3076  * Generic un-prefix operation, use for comment & indent.
3077  *
3078  * \param r_line_index_mask: List of lines that are already at indent level 0,
3079  * to store them later into the undo buffer.
3080  *
3081  * \note caller must handle undo.
3082  */
3083 static void txt_select_unprefix(Text *text,
3084                                 const char *remove,
3085                                 ListBase *r_line_index_mask,
3086                                 int *r_line_index_mask_len)
3087 {
3088   int num = 0;
3089   const int indentlen = strlen(remove);
3090   bool unindented_first = false;
3091
3092   int curl_span_init = 0;
3093
3094   BLI_assert(!ELEM(NULL, text->curl, text->sell));
3095
3096   BLI_listbase_clear(r_line_index_mask);
3097   *r_line_index_mask_len = 0;
3098
3099   if (!undoing) {
3100     curl_span_init = txt_get_span(text->lines.first, text->curl);
3101   }
3102
3103   while (true) {
3104     bool changed = false;
3105     if (STREQLEN(text->curl->line, remove, indentlen)) {
3106       if (num == 0) {
3107         unindented_first = true;
3108       }
3109       text->curl->len -= indentlen;
3110       memmove(text->curl->line, text->curl->line + indentlen, text->curl->len + 1);
3111       changed = true;
3112     }
3113     else {
3114       if (!undoing) {
3115         /* Create list element for 0 indent line */
3116         struct LinkInt *idata = MEM_mallocN(sizeof(struct LinkInt), __func__);
3117         idata->value = curl_span_init + num;
3118         BLI_assert(idata->value == txt_get_span(text->lines.first, text->curl));
3119         BLI_addtail(r_line_index_mask, idata);
3120         (*r_line_index_mask_len) += 1;
3121       }
3122     }
3123
3124     txt_make_dirty(text);
3125     txt_clean_text(text);
3126
3127     if (text->curl == text->sell) {
3128       if (changed) {
3129         text->selc = MAX2(text->selc - indentlen, 0);
3130       }
3131       break;
3132     }
3133     else {
3134       text->curl = text->curl->next;
3135       num++;
3136     }
3137   }
3138
3139   if (unindented_first) {
3140     text->curc = MAX2(text->curc - indentlen, 0);
3141   }
3142
3143   while (num > 0) {
3144     text->curl = text->curl->prev;
3145     num--;
3146   }
3147
3148   /* caller must handle undo */
3149 }
3150
3151 void txt_comment(Text *text, TextUndoBuf *utxt)
3152 {
3153   const char *prefix = "#";
3154
3155   if (ELEM(NULL, text->curl, text->sell)) {
3156     return;
3157   }
3158
3159   txt_select_prefix(text, prefix);
3160
3161   if (!undoing) {
3162     txt_undo_add_op(text, utxt, UNDO_COMMENT);
3163   }
3164 }
3165
3166 void txt_uncomment(Text *text, TextUndoBuf *utxt)
3167 {
3168   const char *prefix = "#";
3169   ListBase line_index_mask;
3170   int line_index_mask_len;
3171
3172   if (ELEM(NULL, text->curl, text->sell)) {
3173     return;
3174   }
3175
3176   txt_select_unprefix(text, prefix, &line_index_mask, &line_index_mask_len);
3177
3178   if (!undoing) {
3179     txt_undo_add_unprefix_op(text, utxt, UNDO_UNCOMMENT, &line_index_mask, line_index_mask_len);
3180   }
3181
3182   BLI_freelistN(&line_index_mask);
3183 }
3184
3185 void txt_indent(Text *text, TextUndoBuf *utxt)
3186 {
3187   const char *prefix = (text->flags & TXT_TABSTOSPACES) ? tab_to_spaces : "\t";
3188
3189   if (ELEM(NULL, text->curl, text->sell)) {
3190     return;
3191   }
3192
3193   txt_select_prefix(text, prefix);
3194
3195   if (!undoing) {
3196     txt_undo_add_op(text, utxt, UNDO_INDENT);
3197   }
3198 }
3199
3200 void txt_unindent(Text *text, TextUndoBuf *utxt)
3201 {
3202   const char *prefix = (text->flags & TXT_TABSTOSPACES) ? tab_to_spaces : "\t";
3203   ListBase line_index_mask;
3204   int line_index_mask_len;
3205
3206   if (ELEM(NULL, text->curl, text->sell)) {
3207     return;
3208   }
3209
3210   txt_select_unprefix(text, prefix, &line_index_mask, &line_index_mask_len);
3211
3212   if (!undoing) {
3213     txt_undo_add_unprefix_op(text, utxt, UNDO_UNINDENT, &line_index_mask, line_index_mask_len);
3214   }
3215
3216   BLI_freelistN(&line_index_mask);
3217 }
3218
3219 void txt_move_lines(struct Text *text, TextUndoBuf *utxt, const int direction)
3220 {
3221   TextLine *line_other;
3222
3223   BLI_assert(ELEM(direction, TXT_MOVE_LINE_UP, TXT_MOVE_LINE_DOWN));
3224
3225   if (!text->curl || !text->sell) {
3226     return;
3227   }
3228
3229   txt_order_cursors(text, false);
3230
3231   line_other = (direction == TXT_MOVE_LINE_DOWN) ? text->sell->next : text->curl->prev;
3232
3233   if (!line_other) {
3234     return;
3235   }
3236
3237   BLI_remlink(&text->lines, line_other);
3238
3239   if (direction == TXT_MOVE_LINE_DOWN) {
3240     BLI_insertlinkbefore(&text->lines, text->curl, line_other);
3241   }
3242   else {
3243     BLI_insertlinkafter(&text->lines, text->sell, line_other);
3244   }
3245
3246   txt_make_dirty(text);
3247   txt_clean_text(text);
3248
3249   if (!undoing) {
3250     txt_undo_add_op(
3251         text, utxt, (direction == TXT_MOVE_LINE_DOWN) ? UNDO_MOVE_LINES_DOWN : UNDO_MOVE_LINES_UP);
3252   }
3253 }
3254
3255 int txt_setcurr_tab_spaces(Text *text, int space)
3256 {
3257   int i = 0;
3258   int test = 0;
3259   const char *word = ":";
3260   const char *comm = "#";
3261   const char indent = (text->flags & TXT_TABSTOSPACES) ? ' ' : '\t';
3262   static const char *back_words[] = {"return", "break", "continue", "pass", "yield", NULL};
3263
3264   if (!text->curl) {
3265     return 0;
3266   }
3267
3268   while (text->curl->line[i] == indent) {
3269     //we only count those tabs/spaces that are before any text or before the curs;
3270     if (i == text->curc) {
3271       return i;
3272     }
3273     else {
3274       i++;
3275     }
3276   }
3277   if (strstr(text->curl->line, word)) {
3278     /* if we find a ':' on this line, then add a tab but not if it is:
3279      * 1) in a comment
3280      * 2) within an identifier
3281      * 3) after the cursor (text->curc), i.e. when creating space before a function def [#25414]
3282      */
3283     int a;
3284     bool is_indent = false;
3285     for (a = 0; (a < text->curc) && (text->curl->line[a] != '\0'); a++) {
3286       char ch = text->curl->line[a];
3287       if (ch == '#') {
3288         break;
3289       }
3290       else if (ch == ':') {
3291         is_indent = 1;
3292       }
3293       else if (ch != ' ' && ch != '\t') {
3294         is_indent = 0;
3295       }
3296     }
3297     if (is_indent) {
3298       i += space;
3299     }
3300   }
3301
3302   for (test = 0; back_words[test]; test++) {
3303     /* if there are these key words then remove a tab because we are done with the block */
3304     if (strstr(text->curl->line, back_words[test]) && i > 0) {
3305       if (strcspn(text->curl->line, back_words[test]) < strcspn(text->curl->line, comm)) {
3306         i -= space;
3307       }
3308     }
3309   }
3310   return i;
3311 }
3312
3313 /*******************************/
3314 /* Character utility functions */
3315 /*******************************/
3316
3317 int text_check_bracket(const char ch)
3318 {
3319   int a;
3320   char opens[] = "([{";
3321   char close[] = ")]}";
3322
3323   for (a = 0; a < (sizeof(opens) - 1); a++) {
3324     if (ch == opens[a]) {
3325       return a + 1;
3326     }
3327     else if (ch == close[a]) {
3328       return -(a + 1);
3329     }
3330   }
3331   return 0;
3332 }
3333
3334 /* TODO, have a function for operators - http://docs.python.org/py3k/reference/lexical_analysis.html#operators */
3335 bool text_check_delim(const char ch)
3336 {
3337   int a;
3338   char delims[] = "():\"\' ~!%^&*-+=[]{};/<>|.#\t,@";
3339
3340   for (a = 0; a < (sizeof(delims) - 1); a++) {
3341     if (ch == delims[a]) {
3342       return true;
3343     }
3344   }
3345   return false;
3346 }
3347
3348 bool text_check_digit(const char ch)
3349 {
3350   if (ch < '0') {
3351     return false;
3352   }
3353   if (ch <= '9') {
3354     return true;
3355   }
3356   return false;
3357 }
3358
3359 bool text_check_identifier(const char ch)
3360 {
3361   if (ch < '0') {
3362     return false;
3363   }
3364   if (ch <= '9') {
3365     return true;
3366   }
3367   if (ch < 'A') {
3368     return false;
3369   }
3370   if (ch <= 'Z' || ch == '_') {
3371     return true;
3372   }
3373   if (ch < 'a') {
3374     return false;
3375   }
3376   if (ch <= 'z') {
3377     return true;
3378   }
3379   return false;
3380 }
3381
3382 bool text_check_identifier_nodigit(const char ch)
3383 {
3384   if (ch <= '9') {
3385     return false;
3386   }
3387   if (ch < 'A') {
3388     return false;
3389   }
3390   if (ch <= 'Z' || ch == '_') {
3391     return true;
3392   }
3393   if (ch < 'a') {
3394     return false;
3395   }
3396   if (ch <= 'z') {
3397     return true;
3398   }
3399   return false;
3400 }
3401
3402 #ifndef WITH_PYTHON
3403 int text_check_identifier_unicode(const unsigned int ch)
3404 {
3405   return (ch < 255 && text_check_identifier((unsigned int)ch));
3406 }
3407
3408 int text_check_identifier_nodigit_unicode(const unsigned int ch)
3409 {
3410   return (ch < 255 && text_check_identifier_nodigit((char)ch));
3411 }
3412 #endif /* WITH_PYTHON */
3413
3414 bool text_check_whitespace(const char ch)
3415 {
3416   if (ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n') {
3417     return true;
3418   }
3419   return false;
3420 }
3421
3422 int text_find_identifier_start(const char *str, int i)
3423 {
3424   if (UNLIKELY(i <= 0)) {
3425     return 0;
3426   }
3427
3428   while (i--) {
3429     if (!text_check_identifier(str[i])) {
3430       break;
3431     }
3432   }
3433   i++;
3434   return i;
3435 }