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