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