rename and negate DISABLE_PYTHON --> WITH_PYTHON
[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
40 #include "DNA_constraint_types.h"
41 #include "DNA_controller_types.h"
42 #include "DNA_scene_types.h"
43 #include "DNA_screen_types.h"
44 #include "DNA_space_types.h"
45 #include "DNA_text_types.h"
46 #include "DNA_userdef_types.h"
47 #include "DNA_object_types.h"
48
49 #include "BKE_depsgraph.h"
50 #include "BKE_global.h"
51 #include "BKE_library.h"
52 #include "BKE_main.h"
53 #include "BKE_text.h"
54 #include "BKE_utildefines.h"
55
56 #ifdef WITH_PYTHON
57 #include "BPY_extern.h"
58 #endif
59
60 /***************/ /*
61
62 How Texts should work
63 --
64 A text should relate to a file as follows -
65 (Text *)->name should be the place where the 
66         file will or has been saved.
67         
68 (Text *)->flags has the following bits
69         TXT_ISDIRTY - should always be set if the file in mem. differs from
70                                         the file on disk, or if there is no file on disk.
71         TXT_ISMEM - should always be set if the Text has not been mapped to
72                                         a file, in which case (Text *)->name may be NULL or garbage.                    
73         TXT_ISEXT - should always be set if the Text is not to be written into
74                                         the .blend
75         TXT_ISSCRIPT - should be set if the user has designated the text
76                                         as a script. (NEW: this was unused, but now it is needed by
77                                         space handler script links (see header_view3d.c, for example)
78
79 ->>> see also: /makesdna/DNA_text_types.h
80
81 Display
82 --
83 The st->top determines at what line the top of the text is displayed.
84 If the user moves the cursor the st containing that cursor should
85 be popped ... other st's retain their own top location.
86
87 Markers
88 --
89 The mrk->flags define the behaviour and relationships between markers. The
90 upper two bytes are used to hold a group ID, the lower two are normal flags. If
91 TMARK_EDITALL is set the group ID defines which other markers should be edited.
92
93 The mrk->clr field is used to visually group markers where the flags may not
94 match. A template system, for example, may allow editing of repeating tokens
95 (in one group) but include other marked positions (in another group) all in the
96 same template with the same colour.
97
98 Undo
99 --
100 Undo/Redo works by storing
101 events in a queue, and a pointer
102 to the current position in the
103 queue...
104
105 Events are stored using an
106 arbitrary op-code system
107 to keep track of
108 a) the two cursors (normal and selected)
109 b) input (visible and control (ie backspace))
110
111 input data is stored as its
112 ASCII value, the opcodes are
113 then selected to not conflict.
114
115 opcodes with data in between are
116 written at the beginning and end
117 of the data to allow undo and redo
118 to simply check the code at the current
119 undo position
120
121 */ /***************/
122
123 /***/
124
125 static void txt_pop_first(Text *text);
126 static void txt_pop_last(Text *text);
127 static void txt_undo_add_op(Text *text, int op);
128 static void txt_undo_add_block(Text *text, int op, const char *buf);
129 static void txt_delete_line(Text *text, TextLine *line);
130 static void txt_delete_sel (Text *text);
131 static void txt_make_dirty (Text *text);
132
133 /***/
134
135 static unsigned char undoing;
136
137 /* allow to switch off undoing externally */
138 void txt_set_undostate(int u)
139 {
140         undoing = u;
141 }
142
143 int txt_get_undostate(void)
144 {
145         return undoing;
146 }
147
148 static void init_undo_text(Text *text)
149 {
150         text->undo_pos= -1;
151         text->undo_len= TXT_INIT_UNDO;
152         text->undo_buf= MEM_mallocN(text->undo_len, "undo buf");
153 }
154
155 void free_text(Text *text)
156 {
157         TextLine *tmp;
158
159         for (tmp= text->lines.first; tmp; tmp= tmp->next) {
160                 MEM_freeN(tmp->line);
161                 if (tmp->format)
162                   MEM_freeN(tmp->format);
163         }
164         
165         BLI_freelistN(&text->lines);
166         BLI_freelistN(&text->markers);
167
168         if(text->name) MEM_freeN(text->name);
169         MEM_freeN(text->undo_buf);
170 #ifdef WITH_PYTHON
171         if (text->compiled) BPY_free_compiled_text(text);
172 #endif
173 }
174
175 Text *add_empty_text(char *name) 
176 {
177         Main *bmain= G.main;
178         Text *ta;
179         TextLine *tmp;
180         
181         ta= alloc_libblock(&bmain->text, ID_TXT, name);
182         ta->id.us= 1;
183         
184         ta->name= NULL;
185
186         init_undo_text(ta);
187
188         ta->nlines=1;
189         ta->flags= TXT_ISDIRTY | TXT_ISMEM;
190         if((U.flag & USER_TXT_TABSTOSPACES_DISABLE)==0)
191                 ta->flags |= TXT_TABSTOSPACES;
192
193         ta->lines.first= ta->lines.last= NULL;
194         ta->markers.first= ta->markers.last= NULL;
195
196         tmp= (TextLine*) MEM_mallocN(sizeof(TextLine), "textline");
197         tmp->line= (char*) MEM_mallocN(1, "textline_string");
198         tmp->format= NULL;
199         
200         tmp->line[0]=0;
201         tmp->len= 0;
202                                 
203         tmp->next= NULL;
204         tmp->prev= NULL;
205                                 
206         BLI_addhead(&ta->lines, tmp);
207         
208         ta->curl= ta->lines.first;
209         ta->curc= 0;
210         ta->sell= ta->lines.first;
211         ta->selc= 0;
212
213         return ta;
214 }
215
216 // this function removes any control characters from
217 // a textline
218
219 static void cleanup_textline(TextLine * tl)
220 {
221         int i;
222
223         for (i = 0; i < tl->len; i++ ) {
224                 if (tl->line[i] < ' ' && tl->line[i] != '\t') {
225                         memmove(tl->line + i, tl->line + i + 1, tl->len - i);
226                         tl->len--;
227                         i--;
228                 }
229         }
230 }
231
232 int reopen_text(Text *text)
233 {
234         FILE *fp;
235         int i, llen, len, res;
236         unsigned char *buffer;
237         TextLine *tmp;
238         char str[FILE_MAXDIR+FILE_MAXFILE];
239         struct stat st;
240
241         if (!text || !text->name) return 0;
242         
243         BLI_strncpy(str, text->name, FILE_MAXDIR+FILE_MAXFILE);
244         BLI_path_abs(str, G.main->name);
245         
246         fp= fopen(str, "r");
247         if(fp==NULL) return 0;
248
249         /* free memory: */
250
251         for (tmp= text->lines.first; tmp; tmp= tmp->next) {
252                 MEM_freeN(tmp->line);
253                 if (tmp->format) MEM_freeN(tmp->format);
254         }
255         
256         BLI_freelistN(&text->lines);
257
258         text->lines.first= text->lines.last= NULL;
259         text->curl= text->sell= NULL;
260
261         /* clear undo buffer */
262         MEM_freeN(text->undo_buf);
263         init_undo_text(text);
264         
265         fseek(fp, 0L, SEEK_END);
266         len= ftell(fp);
267         fseek(fp, 0L, SEEK_SET);        
268
269         text->undo_pos= -1;
270         
271         buffer= MEM_mallocN(len, "text_buffer");
272         // under windows fread can return less then len bytes because
273         // of CR stripping
274         len = fread(buffer, 1, len, fp);
275
276         fclose(fp);
277
278         res= stat(str, &st);
279         text->mtime= st.st_mtime;
280         
281         text->nlines=0;
282         i=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(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_flush_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, 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(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_free_compiled_text(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, oldcc, oldsc;
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         oldcc= text->curc;
1245         oldsc= text->selc;
1246         
1247         s= strstr(&tl->line[text->selc], findstr);
1248         while (!s) {
1249                 tl= tl->next;
1250                 if (!tl) {
1251                         if (wrap)
1252                                 tl= text->lines.first;
1253                         else
1254                                 break;
1255                 }
1256
1257                 s= strstr(tl->line, findstr);
1258                 if (tl==startl)
1259                         break;
1260         }
1261         
1262         if (s) {
1263                 int newl= txt_get_span(text->lines.first, tl);
1264                 int newc= (int)(s-tl->line);
1265                 txt_move_to(text, newl, newc, 0);
1266                 txt_move_to(text, newl, newc + strlen(findstr), 1);
1267                 return 1;                               
1268         } else
1269                 return 0;
1270 }
1271
1272 char *txt_sel_to_buf (Text *text)
1273 {
1274         char *buf;
1275         int length=0;
1276         TextLine *tmp, *linef, *linel;
1277         int charf, charl;
1278         
1279         if (!text) return NULL;
1280         if (!text->curl) return NULL;
1281         if (!text->sell) return NULL;
1282         
1283         if (text->curl==text->sell) {
1284                 linef= linel= text->curl;
1285                 
1286                 if (text->curc < text->selc) {
1287                         charf= text->curc;
1288                         charl= text->selc;
1289                 } else{
1290                         charf= text->selc;
1291                         charl= text->curc;
1292                 }
1293         } else if (txt_get_span(text->curl, text->sell)<0) {
1294                 linef= text->sell;
1295                 linel= text->curl;
1296
1297                 charf= text->selc;              
1298                 charl= text->curc;
1299         } else {
1300                 linef= text->curl;
1301                 linel= text->sell;
1302                 
1303                 charf= text->curc;
1304                 charl= text->selc;
1305         }
1306
1307         if (linef == linel) {
1308                 length= charl-charf;
1309
1310                 buf= MEM_mallocN(length+1, "sel buffer");
1311                 
1312                 BLI_strncpy(buf, linef->line + charf, length+1);
1313         } else {
1314                 length+= linef->len - charf;
1315                 length+= charl;
1316                 length++; /* For the '\n' */
1317                 
1318                 tmp= linef->next;
1319                 while (tmp && tmp!= linel) {
1320                         length+= tmp->len+1;
1321                         tmp= tmp->next;
1322                 }
1323                 
1324                 buf= MEM_mallocN(length+1, "sel buffer");
1325                 
1326                 strncpy(buf, linef->line+ charf, linef->len-charf);
1327                 length= linef->len-charf;
1328                 
1329                 buf[length++]='\n';
1330                 
1331                 tmp= linef->next;
1332                 while (tmp && tmp!=linel) {
1333                         strncpy(buf+length, tmp->line, tmp->len);
1334                         length+= tmp->len;
1335                         
1336                         buf[length++]='\n';                     
1337                         
1338                         tmp= tmp->next;
1339                 }
1340                 strncpy(buf+length, linel->line, charl);
1341                 length+= charl;
1342                 
1343                 buf[length]=0;
1344         }       
1345
1346         return buf;
1347 }
1348
1349 void txt_insert_buf(Text *text, const char *in_buffer)
1350 {
1351         int i=0, l=0, j, u, len;
1352         TextLine *add;
1353
1354         if (!text) return;
1355         if (!in_buffer) return;
1356
1357         txt_delete_sel(text);
1358         
1359         if(!undoing) txt_undo_add_block (text, UNDO_IBLOCK, in_buffer);         
1360
1361         u= undoing;
1362         undoing= 1;
1363
1364         /* Read the first line (or as close as possible */
1365         while (in_buffer[i] && in_buffer[i]!='\n') {
1366                 txt_add_char(text, in_buffer[i]);
1367                 i++;
1368         }
1369         
1370         if (in_buffer[i]=='\n') txt_split_curline(text);
1371         else { undoing = u; return; }
1372         i++;
1373
1374         /* Read as many full lines as we can */
1375         len= strlen(in_buffer);
1376
1377         while (i<len) {
1378                 l=0;
1379
1380                 while (in_buffer[i] && in_buffer[i]!='\n') {
1381                         i++; l++;
1382                 }
1383         
1384                 if(in_buffer[i]=='\n') {
1385                         add= txt_new_linen(in_buffer +(i-l), l);
1386                         BLI_insertlinkbefore(&text->lines, text->curl, add);
1387                         i++;
1388                 } else {
1389                         for (j= i-l; j<i && j<(int)strlen(in_buffer); j++) {
1390                                 txt_add_char(text, in_buffer[j]);
1391                         }
1392                         break;
1393                 }
1394         }
1395         
1396         undoing= u;
1397 }
1398
1399 /******************/
1400 /* Undo functions */
1401 /******************/
1402
1403 static int max_undo_test(Text *text, int x)
1404 {
1405         while (text->undo_pos+x >= text->undo_len) {
1406                 if(text->undo_len*2 > TXT_MAX_UNDO) {
1407                         /* XXX error("Undo limit reached, buffer cleared\n"); */
1408                         MEM_freeN(text->undo_buf);
1409                         init_undo_text(text);
1410                         return 0;
1411                 } else {
1412                         void *tmp= text->undo_buf;
1413                         text->undo_buf= MEM_callocN(text->undo_len*2, "undo buf");
1414                         memcpy(text->undo_buf, tmp, text->undo_len);
1415                         text->undo_len*=2;
1416                         MEM_freeN(tmp);
1417                 }
1418         }
1419
1420         return 1;
1421 }
1422
1423 static void dump_buffer(Text *text) 
1424 {
1425         int i= 0;
1426         
1427         while (i++<text->undo_pos) printf("%d: %d %c\n", i, text->undo_buf[i], text->undo_buf[i]);
1428 }
1429
1430 void txt_print_undo(Text *text)
1431 {
1432         int i= 0;
1433         int op;
1434         char *ops;
1435         int linep, charp;
1436         
1437         dump_buffer(text);
1438         
1439         printf ("---< Undo Buffer >---\n");
1440         
1441         printf ("UndoPosition is %d\n", text->undo_pos);
1442         
1443         while (i<=text->undo_pos) {
1444                 op= text->undo_buf[i];
1445                 
1446                 if (op==UNDO_CLEFT) {
1447                         ops= "Cursor left";
1448                 } else if (op==UNDO_CRIGHT) {
1449                         ops= "Cursor right";
1450                 } else if (op==UNDO_CUP) {
1451                         ops= "Cursor up";
1452                 } else if (op==UNDO_CDOWN) {
1453                         ops= "Cursor down";
1454                 } else if (op==UNDO_SLEFT) {
1455                         ops= "Selection left";
1456                 } else if (op==UNDO_SRIGHT) {
1457                         ops= "Selection right";
1458                 } else if (op==UNDO_SUP) {
1459                         ops= "Selection up";
1460                 } else if (op==UNDO_SDOWN) {
1461                         ops= "Selection down";
1462                 } else if (op==UNDO_STO) {
1463                         ops= "Selection ";
1464                 } else if (op==UNDO_CTO) {
1465                         ops= "Cursor ";
1466                 } else if (op==UNDO_INSERT) {
1467                         ops= "Insert";
1468                 } else if (op==UNDO_BS) {
1469                         ops= "Backspace";
1470                 } else if (op==UNDO_DEL) {
1471                         ops= "Delete";
1472                 } else if (op==UNDO_SWAP) {
1473                         ops= "Cursor swap";
1474                 } else if (op==UNDO_DBLOCK) {
1475                         ops= "Delete text block";
1476                 } else if (op==UNDO_IBLOCK) {
1477                         ops= "Insert text block";
1478                 } else if (op==UNDO_INDENT) {
1479                         ops= "Indent ";
1480                 } else if (op==UNDO_UNINDENT) {
1481                         ops= "Unindent ";
1482                 } else if (op==UNDO_COMMENT) {
1483                         ops= "Comment ";
1484                 } else if (op==UNDO_UNCOMMENT) {
1485                         ops= "Uncomment ";
1486                 } else {
1487                         ops= "Unknown";
1488                 }
1489                 
1490                 printf ("Op (%o) at %d = %s", op, i, ops);
1491                 if (op==UNDO_INSERT || op==UNDO_BS || op==UNDO_DEL) {
1492                         i++;
1493                         printf (" - Char is %c", text->undo_buf[i]);  
1494                         i++;
1495                 } else if (op==UNDO_STO || op==UNDO_CTO) {
1496                         i++;
1497
1498                         charp= text->undo_buf[i]; i++;
1499                         charp= charp+(text->undo_buf[i]<<8); i++;
1500
1501                         linep= text->undo_buf[i]; i++;
1502                         linep= linep+(text->undo_buf[i]<<8); i++;
1503                         linep= linep+(text->undo_buf[i]<<16); i++;
1504                         linep= linep+(text->undo_buf[i]<<24); i++;
1505                         
1506                         printf ("to <%d, %d> ", linep, charp);
1507
1508                         charp= text->undo_buf[i]; i++;
1509                         charp= charp+(text->undo_buf[i]<<8); i++;
1510
1511                         linep= text->undo_buf[i]; i++;
1512                         linep= linep+(text->undo_buf[i]<<8); i++;
1513                         linep= linep+(text->undo_buf[i]<<16); i++;
1514                         linep= linep+(text->undo_buf[i]<<24); i++;
1515                         
1516                         printf ("from <%d, %d>", linep, charp);
1517                 } else if (op==UNDO_DBLOCK || op==UNDO_IBLOCK) {
1518                         i++;
1519
1520                         linep= text->undo_buf[i]; i++;
1521                         linep= linep+(text->undo_buf[i]<<8); i++;
1522                         linep= linep+(text->undo_buf[i]<<16); i++;
1523                         linep= linep+(text->undo_buf[i]<<24); i++;
1524                         
1525                         printf (" (length %d) <", linep);
1526                         
1527                         while (linep>0) {
1528                                 putchar(text->undo_buf[i]);
1529                                 linep--; i++;
1530                         }
1531                         
1532                         linep= text->undo_buf[i]; i++;
1533                         linep= linep+(text->undo_buf[i]<<8); i++;
1534                         linep= linep+(text->undo_buf[i]<<16); i++;
1535                         linep= linep+(text->undo_buf[i]<<24); i++;
1536                         printf ("> (%d)", linep);
1537                 } else if (op==UNDO_INDENT || op==UNDO_UNINDENT) {
1538                         i++;
1539
1540                         charp= text->undo_buf[i]; i++;
1541                         charp= charp+(text->undo_buf[i]<<8); i++;
1542
1543                         linep= text->undo_buf[i]; i++;
1544                         linep= linep+(text->undo_buf[i]<<8); i++;
1545                         linep= linep+(text->undo_buf[i]<<16); i++;
1546                         linep= linep+(text->undo_buf[i]<<24); i++;
1547                         
1548                         printf ("to <%d, %d> ", linep, charp);
1549
1550                         charp= text->undo_buf[i]; i++;
1551                         charp= charp+(text->undo_buf[i]<<8); i++;
1552
1553                         linep= text->undo_buf[i]; i++;
1554                         linep= linep+(text->undo_buf[i]<<8); i++;
1555                         linep= linep+(text->undo_buf[i]<<16); i++;
1556                         linep= linep+(text->undo_buf[i]<<24); i++;
1557                         
1558                         printf ("from <%d, %d>", linep, charp);
1559                 }
1560                 
1561                 printf (" %d\n",  i);
1562                 i++;
1563         }
1564 }
1565
1566 static void txt_undo_add_op(Text *text, int op)
1567 {
1568         if(!max_undo_test(text, 2))
1569                 return;
1570         
1571         text->undo_pos++;
1572         text->undo_buf[text->undo_pos]= op;
1573         text->undo_buf[text->undo_pos+1]= 0;
1574 }
1575
1576 static void txt_undo_add_block(Text *text, int op, const char *buf)
1577 {
1578         int length;
1579         
1580         length= strlen(buf);
1581         
1582         if(!max_undo_test(text, length+11))
1583                 return;
1584
1585         text->undo_pos++;
1586         text->undo_buf[text->undo_pos]= op;
1587         
1588         text->undo_pos++;
1589         text->undo_buf[text->undo_pos]= (length)&0xff;
1590         text->undo_pos++;
1591         text->undo_buf[text->undo_pos]= (length>>8)&0xff;
1592         text->undo_pos++;
1593         text->undo_buf[text->undo_pos]= (length>>16)&0xff;
1594         text->undo_pos++;
1595         text->undo_buf[text->undo_pos]= (length>>24)&0xff;
1596
1597         text->undo_pos++;
1598         strncpy(text->undo_buf+text->undo_pos, buf, length);
1599         text->undo_pos+=length;
1600
1601         text->undo_buf[text->undo_pos]= (length)&0xff;
1602         text->undo_pos++;
1603         text->undo_buf[text->undo_pos]= (length>>8)&0xff;
1604         text->undo_pos++;
1605         text->undo_buf[text->undo_pos]= (length>>16)&0xff;
1606         text->undo_pos++;
1607         text->undo_buf[text->undo_pos]= (length>>24)&0xff;
1608
1609         text->undo_pos++;
1610         text->undo_buf[text->undo_pos]= op;
1611         
1612         text->undo_buf[text->undo_pos+1]= 0;
1613 }
1614
1615 void txt_undo_add_toop(Text *text, int op, unsigned int froml, unsigned short fromc, unsigned int tol, unsigned short toc)
1616 {
1617         if(!max_undo_test(text, 15))
1618                 return;
1619
1620         if (froml==tol && fromc==toc) return;
1621
1622         text->undo_pos++;
1623         text->undo_buf[text->undo_pos]= op;
1624
1625         text->undo_pos++;
1626         text->undo_buf[text->undo_pos]= (fromc)&0xff;
1627         text->undo_pos++;
1628         text->undo_buf[text->undo_pos]= (fromc>>8)&0xff;
1629
1630         text->undo_pos++;
1631         text->undo_buf[text->undo_pos]= (froml)&0xff;
1632         text->undo_pos++;
1633         text->undo_buf[text->undo_pos]= (froml>>8)&0xff;
1634         text->undo_pos++;
1635         text->undo_buf[text->undo_pos]= (froml>>16)&0xff;
1636         text->undo_pos++;
1637         text->undo_buf[text->undo_pos]= (froml>>24)&0xff;
1638
1639         text->undo_pos++;
1640         text->undo_buf[text->undo_pos]= (toc)&0xff;
1641         text->undo_pos++;
1642         text->undo_buf[text->undo_pos]= (toc>>8)&0xff;
1643
1644         text->undo_pos++;
1645         text->undo_buf[text->undo_pos]= (tol)&0xff;
1646         text->undo_pos++;
1647         text->undo_buf[text->undo_pos]= (tol>>8)&0xff;
1648         text->undo_pos++;
1649         text->undo_buf[text->undo_pos]= (tol>>16)&0xff;
1650         text->undo_pos++;
1651         text->undo_buf[text->undo_pos]= (tol>>24)&0xff;
1652
1653         text->undo_pos++;
1654         text->undo_buf[text->undo_pos]= op;
1655
1656         text->undo_buf[text->undo_pos+1]= 0;
1657 }
1658
1659 static void txt_undo_add_charop(Text *text, int op, char c)
1660 {
1661         if(!max_undo_test(text, 4))
1662                 return;
1663
1664         text->undo_pos++;
1665         text->undo_buf[text->undo_pos]= op;
1666         text->undo_pos++;
1667         text->undo_buf[text->undo_pos]= c;
1668         text->undo_pos++;
1669         text->undo_buf[text->undo_pos]= op;
1670         text->undo_buf[text->undo_pos+1]= 0;
1671 }
1672
1673 void txt_do_undo(Text *text)
1674 {
1675         int op= text->undo_buf[text->undo_pos];
1676         unsigned int linep, i;
1677         unsigned short charp;
1678         TextLine *holdl;
1679         int holdc, holdln;
1680         char *buf;
1681         
1682         if (text->undo_pos<0) {
1683                 return;
1684         }
1685
1686         text->undo_pos--;
1687
1688         undoing= 1;
1689         
1690         switch(op) {
1691                 case UNDO_CLEFT:
1692                         txt_move_right(text, 0);
1693                         break;
1694                         
1695                 case UNDO_CRIGHT:
1696                         txt_move_left(text, 0);
1697                         break;
1698                         
1699                 case UNDO_CUP:
1700                         txt_move_down(text, 0);
1701                         break;
1702                         
1703                 case UNDO_CDOWN:
1704                         txt_move_up(text, 0);
1705                         break;
1706
1707                 case UNDO_SLEFT:
1708                         txt_move_right(text, 1);
1709                         break;
1710
1711                 case UNDO_SRIGHT:
1712                         txt_move_left(text, 1);
1713                         break;
1714
1715                 case UNDO_SUP:
1716                         txt_move_down(text, 1);
1717                         break;
1718
1719                 case UNDO_SDOWN:
1720                         txt_move_up(text, 1);
1721                         break;
1722                 
1723                 case UNDO_CTO:
1724                 case UNDO_STO:
1725                         text->undo_pos--;
1726                         text->undo_pos--;
1727                         text->undo_pos--;
1728                         text->undo_pos--;
1729                 
1730                         text->undo_pos--;
1731                         text->undo_pos--;
1732                 
1733                         linep= text->undo_buf[text->undo_pos]; text->undo_pos--;
1734                         linep= (linep<<8)+text->undo_buf[text->undo_pos]; text->undo_pos--;
1735                         linep= (linep<<8)+text->undo_buf[text->undo_pos]; text->undo_pos--;
1736                         linep= (linep<<8)+text->undo_buf[text->undo_pos]; text->undo_pos--;
1737
1738                         charp= text->undo_buf[text->undo_pos]; text->undo_pos--;
1739                         charp= (charp<<8)+text->undo_buf[text->undo_pos]; text->undo_pos--;
1740                         
1741                         if (op==UNDO_CTO) {
1742                                 txt_move_toline(text, linep, 0);
1743                                 text->curc= charp;
1744                                 txt_pop_sel(text);
1745                         } else {
1746                                 txt_move_toline(text, linep, 1);
1747                                 text->selc= charp;
1748                         }
1749                         
1750                         text->undo_pos--;
1751                         break;
1752                         
1753                 case UNDO_INSERT:
1754                         txt_backspace_char(text);
1755                         text->undo_pos--;
1756                         text->undo_pos--;
1757                         break;
1758
1759                 case UNDO_BS:
1760                         txt_add_char(text, text->undo_buf[text->undo_pos]);
1761                         text->undo_pos--;
1762                         text->undo_pos--;
1763                         break;
1764
1765                 case UNDO_DEL:
1766                         txt_add_char(text, text->undo_buf[text->undo_pos]);
1767                         txt_move_left(text, 0);
1768                         text->undo_pos--;
1769                         text->undo_pos--;
1770                         break;
1771
1772                 case UNDO_SWAP:
1773                         txt_curs_swap(text);
1774                         break;
1775
1776                 case UNDO_DBLOCK:
1777                         linep= text->undo_buf[text->undo_pos]; text->undo_pos--;
1778                         linep= (linep<<8)+text->undo_buf[text->undo_pos]; text->undo_pos--;
1779                         linep= (linep<<8)+text->undo_buf[text->undo_pos]; text->undo_pos--;
1780                         linep= (linep<<8)+text->undo_buf[text->undo_pos]; text->undo_pos--;
1781
1782                         buf= MEM_mallocN(linep+1, "dblock buffer");
1783                         for (i=0; i < linep; i++){
1784                                 buf[(linep-1)-i]= text->undo_buf[text->undo_pos]; 
1785                                 text->undo_pos--;
1786                         }
1787                         buf[i]= 0;
1788                         
1789                         txt_curs_first(text, &holdl, &holdc);
1790                         holdln= txt_get_span(text->lines.first, holdl);
1791                         
1792                         txt_insert_buf(text, buf);                      
1793                         MEM_freeN(buf);
1794
1795                         text->curl= text->lines.first;
1796                         while (holdln>0) {
1797                                 if(text->curl->next)
1798                                         text->curl= text->curl->next;
1799                                         
1800                                 holdln--;
1801                         }
1802                         text->curc= holdc;
1803
1804                         linep= text->undo_buf[text->undo_pos]; text->undo_pos--;
1805                         linep= (linep<<8)+text->undo_buf[text->undo_pos]; text->undo_pos--;
1806                         linep= (linep<<8)+text->undo_buf[text->undo_pos]; text->undo_pos--;
1807                         linep= (linep<<8)+text->undo_buf[text->undo_pos]; text->undo_pos--;
1808
1809                         text->undo_pos--;
1810                         
1811                         break;
1812
1813                 case UNDO_IBLOCK:
1814                         linep= text->undo_buf[text->undo_pos]; text->undo_pos--;
1815                         linep= (linep<<8)+text->undo_buf[text->undo_pos]; text->undo_pos--;
1816                         linep= (linep<<8)+text->undo_buf[text->undo_pos]; text->undo_pos--;
1817                         linep= (linep<<8)+text->undo_buf[text->undo_pos]; text->undo_pos--;
1818
1819                         txt_delete_sel(text);
1820                         while (linep>0) {
1821                                 txt_backspace_char(text);
1822                                 text->undo_pos--;
1823                                 linep--;
1824                         }
1825
1826                         text->undo_pos--;
1827                         text->undo_pos--;
1828                         text->undo_pos--; 
1829                         text->undo_pos--;
1830                         
1831                         text->undo_pos--;
1832
1833                         break;
1834                 case UNDO_INDENT:
1835                 case UNDO_UNINDENT:
1836                 case UNDO_COMMENT:
1837                 case UNDO_UNCOMMENT:
1838                         linep= text->undo_buf[text->undo_pos]; text->undo_pos--;
1839                         linep = (linep<<8)+text->undo_buf[text->undo_pos]; text->undo_pos--;
1840                         linep = (linep<<8)+text->undo_buf[text->undo_pos]; text->undo_pos--;
1841                         linep = (linep<<8)+text->undo_buf[text->undo_pos]; text->undo_pos--;
1842                         //linep is now the end line of the selection
1843                         
1844                         charp = text->undo_buf[text->undo_pos]; text->undo_pos--;
1845                         charp = (charp<<8)+text->undo_buf[text->undo_pos]; text->undo_pos--;
1846                         //charp is the last char selected or text->line->len
1847                         //set the selcetion for this now
1848                         text->selc = charp;
1849                         text->sell = text->lines.first;
1850                         for (i= 0; i < linep; i++) {
1851                                 text->sell = text->sell->next;
1852                         }
1853
1854                         linep= text->undo_buf[text->undo_pos]; text->undo_pos--;
1855                         linep = (linep<<8)+text->undo_buf[text->undo_pos]; text->undo_pos--;
1856                         linep = (linep<<8)+text->undo_buf[text->undo_pos]; text->undo_pos--;
1857                         linep = (linep<<8)+text->undo_buf[text->undo_pos]; text->undo_pos--;
1858                         //first line to be selected
1859                         
1860                         charp = text->undo_buf[text->undo_pos]; text->undo_pos--;
1861                         charp = (charp<<8)+text->undo_buf[text->undo_pos]; text->undo_pos--;
1862                         //first postion to be selected
1863                         text->curc = charp;
1864                         text->curl = text->lines.first;
1865                         for (i = 0; i < linep; i++) {
1866                                 text->curl = text->curl->next;
1867                         }
1868
1869                         
1870                         if (op==UNDO_INDENT) {
1871                                 unindent(text);
1872                         } else if (op== UNDO_UNINDENT) {
1873                                 indent(text);
1874                         } else if (op == UNDO_COMMENT) {
1875                                 uncomment(text);
1876                         } else if (op == UNDO_UNCOMMENT) {
1877                                 comment(text);
1878                         }
1879
1880                         text->undo_pos--;
1881                         break;
1882                 default:
1883                         //XXX error("Undo buffer error - resetting");
1884                         text->undo_pos= -1;
1885                         
1886                         break;
1887         }
1888
1889         /* next undo step may need evaluating */
1890         if (text->undo_pos>=0) {
1891                 switch (text->undo_buf[text->undo_pos]) {
1892                         case UNDO_STO:
1893                                 txt_do_undo(text);
1894                                 txt_do_redo(text); /* selections need restoring */
1895                                 break;
1896                         case UNDO_SWAP:
1897                                 txt_do_undo(text); /* swaps should appear transparent */
1898                                 break;
1899                 }
1900         }
1901         
1902         undoing= 0;     
1903 }
1904
1905 void txt_do_redo(Text *text)
1906 {
1907         char op;
1908         unsigned int linep, i;
1909         unsigned short charp;
1910         char *buf;
1911         
1912         text->undo_pos++;       
1913         op= text->undo_buf[text->undo_pos];
1914         
1915         if (!op) {
1916                 text->undo_pos--;
1917                 return;
1918         }
1919         
1920         undoing= 1;
1921
1922         switch(op) {
1923                 case UNDO_CLEFT:
1924                         txt_move_left(text, 0);
1925                         break;
1926                         
1927                 case UNDO_CRIGHT:
1928                         txt_move_right(text, 0);
1929                         break;
1930                         
1931                 case UNDO_CUP:
1932                         txt_move_up(text, 0);
1933                         break;
1934                         
1935                 case UNDO_CDOWN:
1936                         txt_move_down(text, 0);
1937                         break;
1938
1939                 case UNDO_SLEFT:
1940                         txt_move_left(text, 1);
1941                         break;
1942
1943                 case UNDO_SRIGHT:
1944                         txt_move_right(text, 1);
1945                         break;
1946
1947                 case UNDO_SUP:
1948                         txt_move_up(text, 1);
1949                         break;
1950
1951                 case UNDO_SDOWN:
1952                         txt_move_down(text, 1);
1953                         break;
1954                 
1955                 case UNDO_INSERT:
1956                         text->undo_pos++;
1957                         txt_add_char(text, text->undo_buf[text->undo_pos]);
1958                         text->undo_pos++;
1959                         break;
1960
1961                 case UNDO_BS:
1962                         text->undo_pos++;
1963                         txt_backspace_char(text);
1964                         text->undo_pos++;
1965                         break;
1966
1967                 case UNDO_DEL:
1968                         text->undo_pos++;
1969                         txt_delete_char(text);
1970                         text->undo_pos++;
1971                         break;
1972
1973                 case UNDO_SWAP:
1974                         txt_curs_swap(text);
1975                         txt_do_redo(text); /* swaps should appear transparent a*/
1976                         break;
1977                         
1978                 case UNDO_CTO:
1979                 case UNDO_STO:
1980                         text->undo_pos++;
1981                         text->undo_pos++;
1982
1983                         text->undo_pos++;
1984                         text->undo_pos++;
1985                         text->undo_pos++;
1986                         text->undo_pos++;
1987
1988                         text->undo_pos++;
1989
1990                         charp= text->undo_buf[text->undo_pos];
1991                         text->undo_pos++;
1992                         charp= charp+(text->undo_buf[text->undo_pos]<<8);
1993
1994                         text->undo_pos++;
1995                         linep= text->undo_buf[text->undo_pos]; text->undo_pos++;
1996                         linep= linep+(text->undo_buf[text->undo_pos]<<8); text->undo_pos++;
1997                         linep= linep+(text->undo_buf[text->undo_pos]<<16); text->undo_pos++;
1998                         linep= linep+(text->undo_buf[text->undo_pos]<<24); text->undo_pos++;
1999                         
2000                         if (op==UNDO_CTO) {
2001                                 txt_move_toline(text, linep, 0);
2002                                 text->curc= charp;
2003                                 txt_pop_sel(text);
2004                         } else {
2005                                 txt_move_toline(text, linep, 1);
2006                                 text->selc= charp;
2007                         }
2008
2009                         break;
2010
2011                 case UNDO_DBLOCK:
2012                         text->undo_pos++;
2013                         linep= text->undo_buf[text->undo_pos]; text->undo_pos++;
2014                         linep= linep+(text->undo_buf[text->undo_pos]<<8); text->undo_pos++;
2015                         linep= linep+(text->undo_buf[text->undo_pos]<<16); text->undo_pos++;
2016                         linep= linep+(text->undo_buf[text->undo_pos]<<24); text->undo_pos++;
2017
2018                         txt_delete_sel(text);
2019                         text->undo_pos+=linep;
2020
2021                         text->undo_pos++;
2022                         text->undo_pos++;
2023                         text->undo_pos++; 
2024                         text->undo_pos++;
2025                         
2026                         break;
2027
2028                 case UNDO_IBLOCK:
2029                         text->undo_pos++;
2030                         linep= text->undo_buf[text->undo_pos]; text->undo_pos++;
2031                         linep= linep+(text->undo_buf[text->undo_pos]<<8); text->undo_pos++;
2032                         linep= linep+(text->undo_buf[text->undo_pos]<<16); text->undo_pos++;
2033                         linep= linep+(text->undo_buf[text->undo_pos]<<24); text->undo_pos++;
2034
2035                         buf= MEM_mallocN(linep+1, "iblock buffer");
2036                         memcpy (buf, &text->undo_buf[text->undo_pos], linep);
2037                         text->undo_pos+= linep;
2038                         buf[linep]= 0;
2039                         
2040                         txt_insert_buf(text, buf);                      
2041                         MEM_freeN(buf);
2042
2043                         linep= text->undo_buf[text->undo_pos]; text->undo_pos++;
2044                         linep= linep+(text->undo_buf[text->undo_pos]<<8); text->undo_pos++;
2045                         linep= linep+(text->undo_buf[text->undo_pos]<<16); text->undo_pos++;
2046                         linep= linep+(text->undo_buf[text->undo_pos]<<24); text->undo_pos++;
2047
2048                         break;
2049                 case UNDO_INDENT:
2050                 case UNDO_UNINDENT:
2051                 case UNDO_COMMENT:
2052                 case UNDO_UNCOMMENT:
2053                         text->undo_pos++;
2054                         charp = text->undo_buf[text->undo_pos]; text->undo_pos++;
2055                         charp = charp+(text->undo_buf[text->undo_pos]<<8); text->undo_pos++;
2056                         //charp is the first char selected or 0
2057                         
2058                         linep= text->undo_buf[text->undo_pos]; text->undo_pos++;
2059                         linep = linep+(text->undo_buf[text->undo_pos]<<8); text->undo_pos++;
2060                         linep = linep+(text->undo_buf[text->undo_pos]<<16); text->undo_pos++;
2061                         linep = linep+(text->undo_buf[text->undo_pos]<<24); text->undo_pos++;
2062                         //linep is now the first line of the selection                  
2063                         //set the selcetion for this now
2064                         text->curc = charp;
2065                         text->curl = text->lines.first;
2066                         for (i= 0; i < linep; i++) {
2067                                 text->curl = text->curl->next;
2068                         }
2069                         
2070                         charp = text->undo_buf[text->undo_pos]; text->undo_pos++;
2071                         charp = charp+(text->undo_buf[text->undo_pos]<<8); text->undo_pos++;
2072                         //last postion to be selected
2073                         linep= text->undo_buf[text->undo_pos]; text->undo_pos++;
2074                         linep = linep+(text->undo_buf[text->undo_pos]<<8); text->undo_pos++;
2075                         linep = linep+(text->undo_buf[text->undo_pos]<<16); text->undo_pos++;
2076                         linep = linep+(text->undo_buf[text->undo_pos]<<24); text->undo_pos++;
2077                         //Last line to be selected
2078                         
2079                         text->selc = charp;
2080                         text->sell = text->lines.first;
2081                         for (i = 0; i < linep; i++) {
2082                                 text->sell = text->sell->next;
2083                         }
2084
2085                         if (op==UNDO_INDENT) {
2086                                 indent(text);
2087                         } else if (op== UNDO_UNINDENT) {
2088                                 unindent(text);
2089                         } else if (op == UNDO_COMMENT) {
2090                                 comment(text);
2091                         } else if (op == UNDO_UNCOMMENT) {
2092                                 uncomment(text);
2093                         }
2094                         break;
2095                 default:
2096                         //XXX error("Undo buffer error - resetting");
2097                         text->undo_pos= -1;
2098
2099                         break;
2100         }
2101         
2102         undoing= 0;     
2103 }
2104
2105 /**************************/
2106 /* Line editing functions */ 
2107 /**************************/
2108
2109 void txt_split_curline (Text *text) 
2110 {
2111         TextLine *ins;
2112         TextMarker *mrk;
2113         char *left, *right;
2114         int lineno= -1;
2115         
2116         if (!text) return;
2117         if (!text->curl) return;
2118
2119         txt_delete_sel(text);
2120
2121         /* Move markers */
2122
2123         lineno= txt_get_span(text->lines.first, text->curl);
2124         mrk= text->markers.first;
2125         while (mrk) {
2126                 if (mrk->lineno==lineno && mrk->start>text->curc) {
2127                         mrk->lineno++;
2128                         mrk->start -= text->curc;
2129                         mrk->end -= text->curc;
2130                 } else if (mrk->lineno > lineno) {
2131                         mrk->lineno++;
2132                 }
2133                 mrk= mrk->next;
2134         }
2135
2136         /* Make the two half strings */
2137
2138         left= MEM_mallocN(text->curc+1, "textline_string");
2139         if (text->curc) memcpy(left, text->curl->line, text->curc);
2140         left[text->curc]=0;
2141         
2142         right= MEM_mallocN(text->curl->len - text->curc+1, "textline_string");
2143         if (text->curl->len - text->curc) memcpy(right, text->curl->line+text->curc, text->curl->len-text->curc);
2144         right[text->curl->len - text->curc]=0;
2145
2146         MEM_freeN(text->curl->line);
2147         if (text->curl->format) MEM_freeN(text->curl->format);
2148
2149         /* Make the new TextLine */
2150         
2151         ins= MEM_mallocN(sizeof(TextLine), "textline");
2152         ins->line= left;
2153         ins->format= NULL;
2154         ins->len= text->curc;
2155         
2156         text->curl->line= right;
2157         text->curl->format= NULL;
2158         text->curl->len= text->curl->len - text->curc;
2159         
2160         BLI_insertlinkbefore(&text->lines, text->curl, ins);    
2161         
2162         text->curc=0;
2163         
2164         txt_make_dirty(text);
2165         txt_clean_text(text);
2166         
2167         txt_pop_sel(text);
2168         if(!undoing) txt_undo_add_charop(text, UNDO_INSERT, '\n');
2169 }
2170
2171 static void txt_delete_line (Text *text, TextLine *line) 
2172 {
2173         TextMarker *mrk=NULL, *nxt;
2174         int lineno= -1;
2175
2176         if (!text) return;
2177         if (!text->curl) return;
2178
2179         lineno= txt_get_span(text->lines.first, line);
2180         mrk= text->markers.first;
2181         while (mrk) {
2182                 nxt= mrk->next;
2183                 if (mrk->lineno==lineno)
2184                         BLI_freelinkN(&text->markers, mrk);
2185                 else if (mrk->lineno > lineno)
2186                         mrk->lineno--;
2187                 mrk= nxt;
2188         }
2189
2190         BLI_remlink (&text->lines, line);
2191         
2192         if (line->line) MEM_freeN(line->line);
2193         if (line->format) MEM_freeN(line->format);
2194
2195         MEM_freeN(line);
2196
2197         txt_make_dirty(text);
2198         txt_clean_text(text);
2199 }
2200
2201 static void txt_combine_lines (Text *text, TextLine *linea, TextLine *lineb)
2202 {
2203         char *tmp;
2204         TextMarker *mrk= NULL;
2205         int lineno=-1;
2206         
2207         if (!text) return;
2208         
2209         if(!linea || !lineb) return;
2210
2211         mrk= txt_find_marker_region(text, lineb, 0, lineb->len, 0, 0);
2212         if (mrk) {
2213                 lineno= mrk->lineno;
2214                 do {
2215                         mrk->lineno--;
2216                         mrk->start += linea->len;
2217                         mrk->end += linea->len;
2218                         mrk= mrk->next;
2219                 } while (mrk && mrk->lineno==lineno);
2220         }
2221         if (lineno==-1) lineno= txt_get_span(text->lines.first, lineb);
2222         if (!mrk) mrk= text->markers.first;
2223         
2224         tmp= MEM_mallocN(linea->len+lineb->len+1, "textline_string");
2225         
2226         strcpy(tmp, linea->line);
2227         strcat(tmp, lineb->line);
2228
2229         make_new_line(linea, tmp);
2230         
2231         txt_delete_line(text, lineb);
2232         
2233         txt_make_dirty(text);
2234         txt_clean_text(text);
2235 }
2236
2237 void txt_delete_char (Text *text) 
2238 {
2239         char c='\n';
2240         
2241         if (!text) return;
2242         if (!text->curl) return;
2243
2244         if (txt_has_sel(text)) { /* deleting a selection */
2245                 txt_delete_sel(text);
2246                 txt_make_dirty(text);
2247                 return;
2248         }
2249         else if (text->curc== text->curl->len) { /* Appending two lines */
2250                 if (text->curl->next) {
2251                         txt_combine_lines(text, text->curl, text->curl->next);
2252                         txt_pop_sel(text);
2253                 }
2254         } else { /* Just deleting a char */
2255                 int i= text->curc;
2256
2257                 TextMarker *mrk= txt_find_marker_region(text, text->curl, i-1, text->curl->len, 0, 0);
2258                 if (mrk) {
2259                         int lineno= mrk->lineno;
2260                         if (mrk->end==i) {
2261                                 if ((mrk->flags & TMARK_TEMP) && !(mrk->flags & TMARK_EDITALL)) {
2262                                         txt_clear_markers(text, mrk->group, TMARK_TEMP);
2263                                 } else {
2264                                         BLI_freelinkN(&text->markers, mrk);
2265                                 }
2266                                 return;
2267                         }
2268                         do {
2269                                 if (mrk->start>i) mrk->start--;
2270                                 mrk->end--;
2271                                 mrk= mrk->next;
2272                         } while (mrk && mrk->lineno==lineno);
2273                 }
2274                 
2275                 c= text->curl->line[i];
2276                 while(i< text->curl->len) {
2277                         text->curl->line[i]= text->curl->line[i+1];
2278                         i++;
2279                 }
2280                 text->curl->len--;
2281
2282                 txt_pop_sel(text);
2283         }
2284
2285         txt_make_dirty(text);
2286         txt_clean_text(text);
2287         
2288         if(!undoing) txt_undo_add_charop(text, UNDO_DEL, c);
2289 }
2290
2291 void txt_delete_word (Text *text) 
2292 {
2293         txt_jump_right(text, 1);
2294         txt_delete_sel(text);
2295 }
2296
2297 void txt_backspace_char (Text *text) 
2298 {
2299         char c='\n';
2300         
2301         if (!text) return;
2302         if (!text->curl) return;
2303         
2304         if (txt_has_sel(text)) { /* deleting a selection */
2305                 txt_delete_sel(text);
2306                 txt_make_dirty(text);
2307                 return;
2308         }
2309         else if (text->curc==0) { /* Appending two lines */
2310                 if (!text->curl->prev) return;
2311                 
2312                 text->curl= text->curl->prev;
2313                 text->curc= text->curl->len;
2314                 
2315                 txt_combine_lines(text, text->curl, text->curl->next);
2316                 txt_pop_sel(text);
2317         }
2318         else { /* Just backspacing a char */
2319                 int i= text->curc-1;
2320
2321                 TextMarker *mrk= txt_find_marker_region(text, text->curl, i, text->curl->len, 0, 0);
2322                 if (mrk) {
2323                         int lineno= mrk->lineno;
2324                         if (mrk->start==i+1) {
2325                                 if ((mrk->flags & TMARK_TEMP) && !(mrk->flags & TMARK_EDITALL)) {
2326                                         txt_clear_markers(text, mrk->group, TMARK_TEMP);
2327                                 } else {
2328                                         BLI_freelinkN(&text->markers, mrk);
2329                                 }
2330                                 return;
2331                         }
2332                         do {
2333                                 if (mrk->start>i) mrk->start--;
2334                                 mrk->end--;
2335                                 mrk= mrk->next;
2336                         } while (mrk && mrk->lineno==lineno);
2337                 }
2338                 
2339                 c= text->curl->line[i];
2340                 while(i< text->curl->len) {
2341                         text->curl->line[i]= text->curl->line[i+1];
2342                         i++;
2343                 }
2344                 text->curl->len--;
2345                 text->curc--;
2346
2347                 txt_pop_sel(text);
2348         }
2349
2350         txt_make_dirty(text);
2351         txt_clean_text(text);
2352         
2353         if(!undoing) txt_undo_add_charop(text, UNDO_BS, c);
2354 }
2355
2356 void txt_backspace_word (Text *text) 
2357 {
2358         txt_jump_left(text, 1);
2359         txt_delete_sel(text);
2360 }
2361
2362 /* Max spaces to replace a tab with, currently hardcoded to TXT_TABSIZE = 4.
2363  * Used by txt_convert_tab_to_spaces, indent and unintent.
2364  * Remember to change this string according to max tab size */
2365 static char tab_to_spaces[] = "    ";
2366
2367 static void txt_convert_tab_to_spaces (Text *text)
2368 {
2369         /* sb aims to pad adjust the tab-width needed so that the right number of spaces
2370          * is added so that the indention of the line is the right width (i.e. aligned
2371          * to multiples of TXT_TABSIZE)
2372          */
2373         char *sb = &tab_to_spaces[text->curc % TXT_TABSIZE];
2374         txt_insert_buf(text, sb);
2375 }
2376
2377 int txt_add_char (Text *text, char add) 
2378 {
2379         int len, lineno;
2380         char *tmp;
2381         TextMarker *mrk;
2382         
2383         if (!text) return 0;
2384         if (!text->curl) return 0;
2385
2386         if (add=='\n') {
2387                 txt_split_curline(text);
2388                 return 1;
2389         }
2390         
2391         /* insert spaces rather then tabs */
2392         if (add == '\t' && text->flags & TXT_TABSTOSPACES) {
2393                 txt_convert_tab_to_spaces(text);
2394                 return 1;
2395         }
2396
2397         txt_delete_sel(text);
2398         
2399         mrk= txt_find_marker_region(text, text->curl, text->curc-1, text->curl->len, 0, 0);
2400         if (mrk) {
2401                 lineno= mrk->lineno;
2402                 do {
2403                         if (mrk->start>text->curc) mrk->start++;
2404                         mrk->end++;
2405                         mrk= mrk->next;
2406                 } while (mrk && mrk->lineno==lineno);
2407         }
2408         
2409         tmp= MEM_mallocN(text->curl->len+2, "textline_string");
2410         
2411         if(text->curc) memcpy(tmp, text->curl->line, text->curc);
2412         tmp[text->curc]= add;
2413         
2414         len= text->curl->len - text->curc;
2415         if(len>0) memcpy(tmp+text->curc+1, text->curl->line+text->curc, len);
2416         tmp[text->curl->len+1]=0;
2417         make_new_line(text->curl, tmp);
2418                 
2419         text->curc++;
2420
2421         txt_pop_sel(text);
2422         
2423         txt_make_dirty(text);
2424         txt_clean_text(text);
2425
2426         if(!undoing) txt_undo_add_charop(text, UNDO_INSERT, add);
2427         return 1;
2428 }
2429
2430 void txt_delete_selected(Text *text)
2431 {
2432         txt_delete_sel(text);
2433         txt_make_dirty(text);
2434 }
2435
2436 int txt_replace_char (Text *text, char add)
2437 {
2438         char del;
2439         
2440         if (!text) return 0;
2441         if (!text->curl) return 0;
2442
2443         /* If text is selected or we're at the end of the line just use txt_add_char */
2444         if (text->curc==text->curl->len || txt_has_sel(text) || add=='\n') {
2445                 TextMarker *mrk;
2446                 int i= txt_add_char(text, add);
2447                 mrk= txt_find_marker(text, text->curl, text->curc, 0, 0);
2448                 if (mrk && mrk->end==text->curc) mrk->end--;
2449                 return i;
2450         }
2451         
2452         del= text->curl->line[text->curc];
2453         text->curl->line[text->curc]= (unsigned char) add;
2454         text->curc++;
2455         txt_pop_sel(text);
2456         
2457         txt_make_dirty(text);
2458         txt_clean_text(text);
2459
2460         /* Should probably create a new op for this */
2461         if(!undoing) {
2462                 txt_undo_add_charop(text, UNDO_DEL, del);
2463                 txt_undo_add_charop(text, UNDO_INSERT, add);
2464         }
2465         return 1;
2466 }
2467
2468 void indent(Text *text)
2469 {
2470         int len, num;
2471         char *tmp;
2472
2473         char *add = "\t";
2474         int indentlen = 1;
2475         
2476         /* hardcoded: TXT_TABSIZE = 4 spaces: */
2477         int spaceslen = TXT_TABSIZE;
2478
2479         /* insert spaces rather then tabs */
2480         if (text->flags & TXT_TABSTOSPACES){
2481                 add = tab_to_spaces;
2482                 indentlen = spaceslen;
2483         }
2484         
2485         if (!text) return;
2486         if (!text->curl) return;
2487         if (!text->sell) return;
2488
2489         num = 0;
2490         while (TRUE)
2491         {
2492                 tmp= MEM_mallocN(text->curl->len+indentlen+1, "textline_string");
2493                 
2494                 text->curc = 0; 
2495                 if(text->curc) memcpy(tmp, text->curl->line, text->curc); /* XXX never true, check prev line */
2496                 memcpy(tmp+text->curc, add, indentlen);
2497                 
2498                 len= text->curl->len - text->curc;
2499                 if(len>0) memcpy(tmp+text->curc+indentlen, text->curl->line+text->curc, len);
2500                 tmp[text->curl->len+indentlen]= 0;
2501
2502                 make_new_line(text->curl, tmp);
2503                         
2504                 text->curc+= indentlen;
2505                 
2506                 txt_make_dirty(text);
2507                 txt_clean_text(text);
2508                 
2509                 if(text->curl == text->sell) 
2510                 {
2511                         text->selc = text->sell->len;
2512                         break;
2513                 } else {
2514                         text->curl = text->curl->next;
2515                         num++;
2516                 }
2517         }
2518         text->curc = 0;
2519         while( num > 0 )
2520         {
2521                 text->curl = text->curl->prev;
2522                 num--;
2523         }
2524         
2525         if(!undoing) 
2526         {
2527                 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);
2528         }
2529 }
2530
2531 void unindent(Text *text)
2532 {
2533         int num = 0;
2534         char *remove = "\t";
2535         int indent = 1;
2536         
2537         /* hardcoded: TXT_TABSIZE = 4 spaces: */
2538         int spaceslen = TXT_TABSIZE;
2539
2540         /* insert spaces rather then tabs */
2541         if (text->flags & TXT_TABSTOSPACES){
2542                 remove = tab_to_spaces;
2543                 indent = spaceslen;
2544         }
2545
2546         if (!text) return;
2547         if (!text->curl) return;
2548         if (!text->sell) return;
2549
2550         while(TRUE)
2551         {
2552                 int i = 0;
2553                 
2554                 if (BLI_strncasecmp(text->curl->line, remove, indent) == 0)
2555                 {
2556                         while(i< text->curl->len) {
2557                                 text->curl->line[i]= text->curl->line[i+indent];
2558                                 i++;
2559                         }
2560                         text->curl->len-= indent;
2561                 }
2562         
2563                 txt_make_dirty(text);
2564                 txt_clean_text(text);
2565                 
2566                 if(text->curl == text->sell) 
2567                 {
2568                         text->selc = text->sell->len;
2569                         break;
2570                 } else {
2571                         text->curl = text->curl->next;
2572                         num++;
2573                 }
2574                 
2575         }
2576         text->curc = 0;
2577         while( num > 0 )
2578         {
2579                 text->curl = text->curl->prev;
2580                 num--;
2581         }
2582         
2583         if(!undoing) 
2584         {
2585                 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);
2586         }
2587 }
2588
2589 void comment(Text *text)
2590 {
2591         int len, num;
2592         char *tmp;
2593         char add = '#';
2594         
2595         if (!text) return;
2596         if (!text->curl) return;
2597         if (!text->sell) return;// Need to change this need to check if only one line is selected ot more then one
2598
2599         num = 0;
2600         while (TRUE)
2601         {
2602                 tmp= MEM_mallocN(text->curl->len+2, "textline_string");
2603                 
2604                 text->curc = 0; 
2605                 if(text->curc) memcpy(tmp, text->curl->line, text->curc);
2606                 tmp[text->curc]= add;
2607                 
2608                 len= text->curl->len - text->curc;
2609                 if(len>0) memcpy(tmp+text->curc+1, text->curl->line+text->curc, len);
2610                 tmp[text->curl->len+1]=0;
2611
2612                 make_new_line(text->curl, tmp);
2613                         
2614                 text->curc++;
2615                 
2616                 txt_make_dirty(text);
2617                 txt_clean_text(text);
2618                 
2619                 if(text->curl == text->sell) 
2620                 {
2621                         text->selc = text->sell->len;
2622                         break;
2623                 } else {
2624                         text->curl = text->curl->next;
2625                         num++;
2626                 }
2627         }
2628         text->curc = 0;
2629         while( num > 0 )
2630         {
2631                 text->curl = text->curl->prev;
2632                 num--;
2633         }
2634         
2635         if(!undoing) 
2636         {
2637                 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);
2638         }
2639 }
2640
2641 void uncomment(Text *text)
2642 {
2643         int num = 0;
2644         char remove = '#';
2645         
2646         if (!text) return;
2647         if (!text->curl) return;
2648         if (!text->sell) return;
2649
2650         while(TRUE)
2651         {
2652                 int i = 0;
2653                 
2654                 if (text->curl->line[i] == remove)
2655                 {
2656                         while(i< text->curl->len) {
2657                                 text->curl->line[i]= text->curl->line[i+1];
2658                                 i++;
2659                         }
2660                         text->curl->len--;
2661                 }
2662                          
2663         
2664                 txt_make_dirty(text);
2665                 txt_clean_text(text);
2666                 
2667                 if(text->curl == text->sell) 
2668                 {
2669                         text->selc = text->sell->len;
2670                         break;
2671                 } else {
2672                         text->curl = text->curl->next;
2673                         num++;
2674                 }
2675                 
2676         }
2677         text->curc = 0;
2678         while( num > 0 )
2679         {
2680                 text->curl = text->curl->prev;
2681                 num--;
2682         }
2683         
2684         if(!undoing) 
2685         {
2686                 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);
2687         }
2688 }
2689
2690 int setcurr_tab_spaces (Text *text, int space)
2691 {
2692         int i = 0;
2693         int test = 0;
2694         const char *word = ":";
2695         const char *comm = "#";
2696         const char indent= (text->flags & TXT_TABSTOSPACES) ? ' ' : '\t';
2697         static char *back_words[]= {"return", "break", "continue", "pass", "yield", NULL};
2698         if (!text) return 0;
2699         if (!text->curl) return 0;
2700
2701         while (text->curl->line[i] == indent)
2702         {
2703                 //we only count those tabs/spaces that are before any text or before the curs;
2704                 if (i == text->curc)
2705                 {
2706                         return i;
2707                 } else {
2708                         i++;
2709                 }
2710         }
2711         if(strstr(text->curl->line, word))
2712         {
2713                 //if we find a : then add a tab but not if it is in a comment
2714                 int a, indent = 0;
2715                 for(a=0; text->curl->line[a] != '\0'; a++)
2716                 {
2717                         if (text->curl->line[a]=='#') {
2718                                 break;
2719                         } else if (text->curl->line[a]==':') {
2720                                 indent = 1;
2721                         } else if (text->curl->line[a]==']') {
2722                                 indent = 0;
2723                         }
2724                 }
2725                 if (indent) {
2726                         i += space;
2727                 }
2728         }
2729
2730         for(test=0; back_words[test]; test++)
2731         {
2732                 /* if there are these key words then remove a tab because we are done with the block */
2733                 if(strstr(text->curl->line, back_words[test]) && i > 0)
2734                 {
2735                         if(strcspn(text->curl->line, back_words[test]) < strcspn(text->curl->line, comm))
2736                         {
2737                                 i -= space;
2738                         }
2739                 }
2740         }
2741         return i;
2742 }
2743
2744 /*********************************/
2745 /* Text marker utility functions */
2746 /*********************************/
2747
2748 /* Creates and adds a marker to the list maintaining sorted order */
2749 void txt_add_marker(Text *text, TextLine *line, int start, int end, char color[4], int group, int flags) {
2750         TextMarker *tmp, *marker;
2751
2752         marker= MEM_mallocN(sizeof(TextMarker), "text_marker");
2753         
2754         marker->lineno= txt_get_span(text->lines.first, line);
2755         marker->start= MIN2(start, end);
2756         marker->end= MAX2(start, end);
2757         marker->group= group;
2758         marker->flags= flags;
2759
2760         marker->color[0]= color[0];
2761         marker->color[1]= color[1];
2762         marker->color[2]= color[2];
2763         marker->color[3]= color[3];
2764
2765         for (tmp=text->markers.last; tmp; tmp=tmp->prev)
2766                 if (tmp->lineno < marker->lineno || (tmp->lineno==marker->lineno && tmp->start < marker->start))
2767                         break;
2768
2769         if (tmp) BLI_insertlinkafter(&text->markers, tmp, marker);
2770         else BLI_addhead(&text->markers, marker);
2771 }
2772
2773 /* Returns the first matching marker on the specified line between two points.
2774    If the group or flags fields are non-zero the returned flag must be in the
2775    specified group and have at least the specified flags set. */
2776 TextMarker *txt_find_marker_region(Text *text, TextLine *line, int start, int end, int group, int flags) {
2777         TextMarker *marker, *next;
2778         int lineno= txt_get_span(text->lines.first, line);
2779         
2780         for (marker=text->markers.first; marker; marker=next) {
2781                 next= marker->next;
2782
2783                 if (group && marker->group != group) continue;
2784                 else if ((marker->flags & flags) != flags) continue;
2785                 else if (marker->lineno < lineno) continue;
2786                 else if (marker->lineno > lineno) break;
2787
2788                 if ((marker->start==marker->end && start<=marker->start && marker->start<=end) ||
2789                                 (marker->start<end && marker->end>start))
2790                         return marker;
2791         }
2792         return NULL;
2793 }
2794
2795 /* Clears all markers on the specified line between two points. If the group or
2796    flags fields are non-zero the returned flag must be in the specified group
2797    and have at least the specified flags set. */
2798 short txt_clear_marker_region(Text *text, TextLine *line, int start, int end, int group, int flags) {
2799         TextMarker *marker, *next;
2800         int lineno= txt_get_span(text->lines.first, line);
2801         short cleared= 0;
2802         
2803         for (marker=text->markers.first; marker; marker=next) {
2804                 next= marker->next;
2805
2806                 if (group && marker->group != group) continue;
2807                 else if ((marker->flags & flags) != flags) continue;
2808                 else if (marker->lineno < lineno) continue;
2809                 else if (marker->lineno > lineno) break;
2810
2811                 if ((marker->start==marker->end && start<=marker->start && marker->start<=end) ||
2812                         (marker->start<end && marker->end>start)) {
2813                         BLI_freelinkN(&text->markers, marker);
2814                         cleared= 1;
2815                 }
2816         }
2817         return cleared;
2818 }
2819
2820 /* Clears all markers in the specified group (if given) with at least the
2821    specified flags set. Useful for clearing temporary markers (group=0,
2822    flags=TMARK_TEMP) */
2823 short txt_clear_markers(Text *text, int group, int flags) {
2824         TextMarker *marker, *next;
2825         short cleared= 0;
2826         
2827         for (marker=text->markers.first; marker; marker=next) {
2828                 next= marker->next;
2829
2830                 if ((!group || marker->group==group) &&
2831                                 (marker->flags & flags) == flags) {
2832                         BLI_freelinkN(&text->markers, marker);
2833                         cleared= 1;
2834                 }
2835         }
2836         return cleared;
2837 }
2838
2839 /* Finds the marker at the specified line and cursor position with at least the
2840    specified flags set in the given group (if non-zero). */
2841 TextMarker *txt_find_marker(Text *text, TextLine *line, int curs, int group, int flags) {
2842         TextMarker *marker;
2843         int lineno= txt_get_span(text->lines.first, line);
2844         
2845         for (marker=text->markers.first; marker; marker=marker->next) {
2846                 if (group && marker->group != group) continue;
2847                 else if ((marker->flags & flags) != flags) continue;
2848                 else if (marker->lineno < lineno) continue;
2849                 else if (marker->lineno > lineno) break;
2850
2851                 if (marker->start <= curs && curs <= marker->end)
2852                         return marker;
2853         }
2854         return NULL;
2855 }
2856
2857 /* Finds the previous marker in the same group. If no other is found, the same
2858    marker will be returned */
2859 TextMarker *txt_prev_marker(Text *text, TextMarker *marker) {
2860         TextMarker *tmp= marker;
2861         while (tmp) {
2862                 if (tmp->prev) tmp= tmp->prev;
2863                 else tmp= text->markers.last;
2864                 if (tmp->group == marker->group)
2865                         return tmp;
2866         }
2867         return NULL; /* Only if marker==NULL */
2868 }
2869
2870 /* Finds the next marker in the same group. If no other is found, the same
2871    marker will be returned */
2872 TextMarker *txt_next_marker(Text *text, TextMarker *marker) {
2873         TextMarker *tmp= marker;
2874         while (tmp) {
2875                 if (tmp->next) tmp= tmp->next;
2876                 else tmp= text->markers.first;
2877                 if (tmp->group == marker->group)
2878                         return tmp;
2879         }
2880         return NULL; /* Only if marker==NULL */
2881 }
2882
2883
2884 /*******************************/
2885 /* Character utility functions */
2886 /*******************************/
2887
2888 int text_check_bracket(char ch)
2889 {
2890         int a;
2891         char opens[] = "([{";
2892         char close[] = ")]}";
2893
2894         for(a=0; a<(sizeof(opens)-1); a++) {
2895                 if(ch==opens[a])
2896                         return a+1;
2897                 else if(ch==close[a])
2898                         return -(a+1);
2899         }
2900         return 0;
2901 }
2902
2903 int text_check_delim(char ch)
2904 {
2905         int a;
2906         char delims[] = "():\"\' ~!%^&*-+=[]{};/<>|.#\t,";
2907
2908         for(a=0; a<(sizeof(delims)-1); a++) {
2909                 if(ch==delims[a])
2910                         return 1;
2911         }
2912         return 0;
2913 }
2914
2915 int text_check_digit(char ch)
2916 {
2917         if(ch < '0') return 0;
2918         if(ch <= '9') return 1;
2919         return 0;
2920 }
2921
2922 int text_check_identifier(char ch)
2923 {
2924         if(ch < '0') return 0;
2925         if(ch <= '9') return 1;
2926         if(ch < 'A') return 0;
2927         if(ch <= 'Z' || ch == '_') return 1;
2928         if(ch < 'a') return 0;
2929         if(ch <= 'z') return 1;
2930         return 0;
2931 }
2932
2933 int text_check_whitespace(char ch)
2934 {
2935         if(ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n')
2936                 return 1;
2937         return 0;
2938 }