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