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