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