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