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