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