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