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