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