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