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