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