0b5347ca304efab04b4e6d1159a391d517f4e68f
[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         if (!mrk) mrk= text->markers.first;
2222         
2223         tmp= MEM_mallocN(linea->len+lineb->len+1, "textline_string");
2224         
2225         strcpy(tmp, linea->line);
2226         strcat(tmp, lineb->line);
2227
2228         make_new_line(linea, tmp);
2229         
2230         txt_delete_line(text, lineb);
2231         
2232         txt_make_dirty(text);
2233         txt_clean_text(text);
2234 }
2235
2236 void txt_delete_char (Text *text) 
2237 {
2238         char c='\n';
2239         
2240         if (!text) return;
2241         if (!text->curl) return;
2242
2243         if (txt_has_sel(text)) { /* deleting a selection */
2244                 txt_delete_sel(text);
2245                 txt_make_dirty(text);
2246                 return;
2247         }
2248         else if (text->curc== text->curl->len) { /* Appending two lines */
2249                 if (text->curl->next) {
2250                         txt_combine_lines(text, text->curl, text->curl->next);
2251                         txt_pop_sel(text);
2252                 }
2253         } else { /* Just deleting a char */
2254                 int i= text->curc;
2255
2256                 TextMarker *mrk= txt_find_marker_region(text, text->curl, i-1, text->curl->len, 0, 0);
2257                 if (mrk) {
2258                         int lineno= mrk->lineno;
2259                         if (mrk->end==i) {
2260                                 if ((mrk->flags & TMARK_TEMP) && !(mrk->flags & TMARK_EDITALL)) {
2261                                         txt_clear_markers(text, mrk->group, TMARK_TEMP);
2262                                 } else {
2263                                         BLI_freelinkN(&text->markers, mrk);
2264                                 }
2265                                 return;
2266                         }
2267                         do {
2268                                 if (mrk->start>i) mrk->start--;
2269                                 mrk->end--;
2270                                 mrk= mrk->next;
2271                         } while (mrk && mrk->lineno==lineno);
2272                 }
2273                 
2274                 c= text->curl->line[i];
2275                 while(i< text->curl->len) {
2276                         text->curl->line[i]= text->curl->line[i+1];
2277                         i++;
2278                 }
2279                 text->curl->len--;
2280
2281                 txt_pop_sel(text);
2282         }
2283
2284         txt_make_dirty(text);
2285         txt_clean_text(text);
2286         
2287         if(!undoing) txt_undo_add_charop(text, UNDO_DEL, c);
2288 }
2289
2290 void txt_delete_word (Text *text) 
2291 {
2292         txt_jump_right(text, 1);
2293         txt_delete_sel(text);
2294 }
2295
2296 void txt_backspace_char (Text *text) 
2297 {
2298         char c='\n';
2299         
2300         if (!text) return;
2301         if (!text->curl) return;
2302         
2303         if (txt_has_sel(text)) { /* deleting a selection */
2304                 txt_delete_sel(text);
2305                 txt_make_dirty(text);
2306                 return;
2307         }
2308         else if (text->curc==0) { /* Appending two lines */
2309                 if (!text->curl->prev) return;
2310                 
2311                 text->curl= text->curl->prev;
2312                 text->curc= text->curl->len;
2313                 
2314                 txt_combine_lines(text, text->curl, text->curl->next);
2315                 txt_pop_sel(text);
2316         }
2317         else { /* Just backspacing a char */
2318                 int i= text->curc-1;
2319
2320                 TextMarker *mrk= txt_find_marker_region(text, text->curl, i, text->curl->len, 0, 0);
2321                 if (mrk) {
2322                         int lineno= mrk->lineno;
2323                         if (mrk->start==i+1) {
2324                                 if ((mrk->flags & TMARK_TEMP) && !(mrk->flags & TMARK_EDITALL)) {
2325                                         txt_clear_markers(text, mrk->group, TMARK_TEMP);
2326                                 } else {
2327                                         BLI_freelinkN(&text->markers, mrk);
2328                                 }
2329                                 return;
2330                         }
2331                         do {
2332                                 if (mrk->start>i) mrk->start--;
2333                                 mrk->end--;
2334                                 mrk= mrk->next;
2335                         } while (mrk && mrk->lineno==lineno);
2336                 }
2337                 
2338                 c= text->curl->line[i];
2339                 while(i< text->curl->len) {
2340                         text->curl->line[i]= text->curl->line[i+1];
2341                         i++;
2342                 }
2343                 text->curl->len--;
2344                 text->curc--;
2345
2346                 txt_pop_sel(text);
2347         }
2348
2349         txt_make_dirty(text);
2350         txt_clean_text(text);
2351         
2352         if(!undoing) txt_undo_add_charop(text, UNDO_BS, c);
2353 }
2354
2355 void txt_backspace_word (Text *text) 
2356 {
2357         txt_jump_left(text, 1);
2358         txt_delete_sel(text);
2359 }
2360
2361 /* Max spaces to replace a tab with, currently hardcoded to TXT_TABSIZE = 4.
2362  * Used by txt_convert_tab_to_spaces, indent and unintent.
2363  * Remember to change this string according to max tab size */
2364 static char tab_to_spaces[] = "    ";
2365
2366 static void txt_convert_tab_to_spaces (Text *text)
2367 {
2368         /* sb aims to pad adjust the tab-width needed so that the right number of spaces
2369          * is added so that the indention of the line is the right width (i.e. aligned
2370          * to multiples of TXT_TABSIZE)
2371          */
2372         char *sb = &tab_to_spaces[text->curc % TXT_TABSIZE];
2373         txt_insert_buf(text, sb);
2374 }
2375
2376 int txt_add_char (Text *text, char add) 
2377 {
2378         int len, lineno;
2379         char *tmp;
2380         TextMarker *mrk;
2381         
2382         if (!text) return 0;
2383         if (!text->curl) return 0;
2384
2385         if (add=='\n') {
2386                 txt_split_curline(text);
2387                 return 1;
2388         }
2389         
2390         /* insert spaces rather then tabs */
2391         if (add == '\t' && text->flags & TXT_TABSTOSPACES) {
2392                 txt_convert_tab_to_spaces(text);
2393                 return 1;
2394         }
2395
2396         txt_delete_sel(text);
2397         
2398         mrk= txt_find_marker_region(text, text->curl, text->curc-1, text->curl->len, 0, 0);
2399         if (mrk) {
2400                 lineno= mrk->lineno;
2401                 do {
2402                         if (mrk->start>text->curc) mrk->start++;
2403                         mrk->end++;
2404                         mrk= mrk->next;
2405                 } while (mrk && mrk->lineno==lineno);
2406         }
2407         
2408         tmp= MEM_mallocN(text->curl->len+2, "textline_string");
2409         
2410         if(text->curc) memcpy(tmp, text->curl->line, text->curc);
2411         tmp[text->curc]= add;
2412         
2413         len= text->curl->len - text->curc;
2414         if(len>0) memcpy(tmp+text->curc+1, text->curl->line+text->curc, len);
2415         tmp[text->curl->len+1]=0;
2416         make_new_line(text->curl, tmp);
2417                 
2418         text->curc++;
2419
2420         txt_pop_sel(text);
2421         
2422         txt_make_dirty(text);
2423         txt_clean_text(text);
2424
2425         if(!undoing) txt_undo_add_charop(text, UNDO_INSERT, add);
2426         return 1;
2427 }
2428
2429 void txt_delete_selected(Text *text)
2430 {
2431         txt_delete_sel(text);
2432         txt_make_dirty(text);
2433 }
2434
2435 int txt_replace_char (Text *text, char add)
2436 {
2437         char del;
2438         
2439         if (!text) return 0;
2440         if (!text->curl) return 0;
2441
2442         /* If text is selected or we're at the end of the line just use txt_add_char */
2443         if (text->curc==text->curl->len || txt_has_sel(text) || add=='\n') {
2444                 TextMarker *mrk;
2445                 int i= txt_add_char(text, add);
2446                 mrk= txt_find_marker(text, text->curl, text->curc, 0, 0);
2447                 if (mrk && mrk->end==text->curc) mrk->end--;
2448                 return i;
2449         }
2450         
2451         del= text->curl->line[text->curc];
2452         text->curl->line[text->curc]= (unsigned char) add;
2453         text->curc++;
2454         txt_pop_sel(text);
2455         
2456         txt_make_dirty(text);
2457         txt_clean_text(text);
2458
2459         /* Should probably create a new op for this */
2460         if(!undoing) {
2461                 txt_undo_add_charop(text, UNDO_DEL, del);
2462                 txt_undo_add_charop(text, UNDO_INSERT, add);
2463         }
2464         return 1;
2465 }
2466
2467 void indent(Text *text)
2468 {
2469         int len, num;
2470         char *tmp;
2471
2472         const char *add = "\t";
2473         int indentlen = 1;
2474         
2475         /* hardcoded: TXT_TABSIZE = 4 spaces: */
2476         int spaceslen = TXT_TABSIZE;
2477
2478         /* insert spaces rather then tabs */
2479         if (text->flags & TXT_TABSTOSPACES){
2480                 add = tab_to_spaces;
2481                 indentlen = spaceslen;
2482         }
2483         
2484         if (!text) return;
2485         if (!text->curl) return;
2486         if (!text->sell) return;
2487
2488         num = 0;
2489         while (TRUE)
2490         {
2491                 tmp= MEM_mallocN(text->curl->len+indentlen+1, "textline_string");
2492                 
2493                 text->curc = 0; 
2494                 if(text->curc) memcpy(tmp, text->curl->line, text->curc); /* XXX never true, check prev line */
2495                 memcpy(tmp+text->curc, add, indentlen);
2496                 
2497                 len= text->curl->len - text->curc;
2498                 if(len>0) memcpy(tmp+text->curc+indentlen, text->curl->line+text->curc, len);
2499                 tmp[text->curl->len+indentlen]= 0;
2500
2501                 make_new_line(text->curl, tmp);
2502                         
2503                 text->curc+= indentlen;
2504                 
2505                 txt_make_dirty(text);
2506                 txt_clean_text(text);
2507                 
2508                 if(text->curl == text->sell) 
2509                 {
2510                         text->selc = text->sell->len;
2511                         break;
2512                 } else {
2513                         text->curl = text->curl->next;
2514                         num++;
2515                 }
2516         }
2517         text->curc = 0;
2518         while( num > 0 )
2519         {
2520                 text->curl = text->curl->prev;
2521                 num--;
2522         }
2523         
2524         if(!undoing) 
2525         {
2526                 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);
2527         }
2528 }
2529
2530 void unindent(Text *text)
2531 {
2532         int num = 0;
2533         const char *remove = "\t";
2534         int indent = 1;
2535         
2536         /* hardcoded: TXT_TABSIZE = 4 spaces: */
2537         int spaceslen = TXT_TABSIZE;
2538
2539         /* insert spaces rather then tabs */
2540         if (text->flags & TXT_TABSTOSPACES){
2541                 remove = tab_to_spaces;
2542                 indent = spaceslen;
2543         }
2544
2545         if (!text) return;
2546         if (!text->curl) return;
2547         if (!text->sell) return;
2548
2549         while(TRUE)
2550         {
2551                 int i = 0;
2552                 
2553                 if (BLI_strncasecmp(text->curl->line, remove, indent) == 0)
2554                 {
2555                         while(i< text->curl->len) {
2556                                 text->curl->line[i]= text->curl->line[i+indent];
2557                                 i++;
2558                         }
2559                         text->curl->len-= indent;
2560                 }
2561         
2562                 txt_make_dirty(text);
2563                 txt_clean_text(text);
2564                 
2565                 if(text->curl == text->sell) 
2566                 {
2567                         text->selc = text->sell->len;
2568                         break;
2569                 } else {
2570                         text->curl = text->curl->next;
2571                         num++;
2572                 }
2573                 
2574         }
2575         text->curc = 0;
2576         while( num > 0 )
2577         {
2578                 text->curl = text->curl->prev;
2579                 num--;
2580         }
2581         
2582         if(!undoing) 
2583         {
2584                 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);
2585         }
2586 }
2587
2588 void comment(Text *text)
2589 {
2590         int len, num;
2591         char *tmp;
2592         char add = '#';
2593         
2594         if (!text) return;
2595         if (!text->curl) return;
2596         if (!text->sell) return;// Need to change this need to check if only one line is selected ot more then one
2597
2598         num = 0;
2599         while (TRUE)
2600         {
2601                 tmp= MEM_mallocN(text->curl->len+2, "textline_string");
2602                 
2603                 text->curc = 0; 
2604                 if(text->curc) memcpy(tmp, text->curl->line, text->curc);
2605                 tmp[text->curc]= add;
2606                 
2607                 len= text->curl->len - text->curc;
2608                 if(len>0) memcpy(tmp+text->curc+1, text->curl->line+text->curc, len);
2609                 tmp[text->curl->len+1]=0;
2610
2611                 make_new_line(text->curl, tmp);
2612                         
2613                 text->curc++;
2614                 
2615                 txt_make_dirty(text);
2616                 txt_clean_text(text);
2617                 
2618                 if(text->curl == text->sell) 
2619                 {
2620                         text->selc = text->sell->len;
2621                         break;
2622                 } else {
2623                         text->curl = text->curl->next;
2624                         num++;
2625                 }
2626         }
2627         text->curc = 0;
2628         while( num > 0 )
2629         {
2630                 text->curl = text->curl->prev;
2631                 num--;
2632         }
2633         
2634         if(!undoing) 
2635         {
2636                 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);
2637         }
2638 }
2639
2640 void uncomment(Text *text)
2641 {
2642         int num = 0;
2643         char remove = '#';
2644         
2645         if (!text) return;
2646         if (!text->curl) return;
2647         if (!text->sell) return;
2648
2649         while(TRUE)
2650         {
2651                 int i = 0;
2652                 
2653                 if (text->curl->line[i] == remove)
2654                 {
2655                         while(i< text->curl->len) {
2656                                 text->curl->line[i]= text->curl->line[i+1];
2657                                 i++;
2658                         }
2659                         text->curl->len--;
2660                 }
2661                          
2662         
2663                 txt_make_dirty(text);
2664                 txt_clean_text(text);
2665                 
2666                 if(text->curl == text->sell) 
2667                 {
2668                         text->selc = text->sell->len;
2669                         break;
2670                 } else {
2671                         text->curl = text->curl->next;
2672                         num++;
2673                 }
2674                 
2675         }
2676         text->curc = 0;
2677         while( num > 0 )
2678         {
2679                 text->curl = text->curl->prev;
2680                 num--;
2681         }
2682         
2683         if(!undoing) 
2684         {
2685                 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);
2686         }
2687 }
2688
2689 int setcurr_tab_spaces (Text *text, int space)
2690 {
2691         int i = 0;
2692         int test = 0;
2693         const char *word = ":";
2694         const char *comm = "#";
2695         const char indent= (text->flags & TXT_TABSTOSPACES) ? ' ' : '\t';
2696         static const char *back_words[]= {"return", "break", "continue", "pass", "yield", NULL};
2697         if (!text) return 0;
2698         if (!text->curl) return 0;
2699
2700         while (text->curl->line[i] == indent)
2701         {
2702                 //we only count those tabs/spaces that are before any text or before the curs;
2703                 if (i == text->curc)
2704                 {
2705                         return i;
2706                 } else {
2707                         i++;
2708                 }
2709         }
2710         if(strstr(text->curl->line, word))
2711         {
2712                 /* if we find a ':' on this line, then add a tab but not if it is:
2713                  *      1) in a comment
2714                  *      2) within an identifier
2715                  *      3) after the cursor (text->curc), i.e. when creating space before a function def [#25414] 
2716                  */
2717                 int a, indent = 0;
2718                 for(a=0; (a < text->curc) && (text->curl->line[a] != '\0'); a++)
2719                 {
2720                         if (text->curl->line[a]=='#') {
2721                                 break;
2722                         } else if (text->curl->line[a]==':') {
2723                                 indent = 1;
2724                         } else if (text->curl->line[a]==']') {
2725                                 indent = 0;
2726                         }
2727                 }
2728                 if (indent) {
2729                         i += space;
2730                 }
2731         }
2732
2733         for(test=0; back_words[test]; test++)
2734         {
2735                 /* if there are these key words then remove a tab because we are done with the block */
2736                 if(strstr(text->curl->line, back_words[test]) && i > 0)
2737                 {
2738                         if(strcspn(text->curl->line, back_words[test]) < strcspn(text->curl->line, comm))
2739                         {
2740                                 i -= space;
2741                         }
2742                 }
2743         }
2744         return i;
2745 }
2746
2747 /*********************************/
2748 /* Text marker utility functions */
2749 /*********************************/
2750
2751 /* Creates and adds a marker to the list maintaining sorted order */
2752 void txt_add_marker(Text *text, TextLine *line, int start, int end, const unsigned char color[4], int group, int flags) {
2753         TextMarker *tmp, *marker;
2754
2755         marker= MEM_mallocN(sizeof(TextMarker), "text_marker");
2756         
2757         marker->lineno= txt_get_span(text->lines.first, line);
2758         marker->start= MIN2(start, end);
2759         marker->end= MAX2(start, end);
2760         marker->group= group;
2761         marker->flags= flags;
2762
2763         marker->color[0]= color[0];
2764         marker->color[1]= color[1];
2765         marker->color[2]= color[2];
2766         marker->color[3]= color[3];
2767
2768         for (tmp=text->markers.last; tmp; tmp=tmp->prev)
2769                 if (tmp->lineno < marker->lineno || (tmp->lineno==marker->lineno && tmp->start < marker->start))
2770                         break;
2771
2772         if (tmp) BLI_insertlinkafter(&text->markers, tmp, marker);
2773         else BLI_addhead(&text->markers, marker);
2774 }
2775
2776 /* Returns the first matching marker on the specified line between two points.
2777    If the group or flags fields are non-zero the returned flag must be in the
2778    specified group and have at least the specified flags set. */
2779 TextMarker *txt_find_marker_region(Text *text, TextLine *line, int start, int end, int group, int flags) {
2780         TextMarker *marker, *next;
2781         int lineno= txt_get_span(text->lines.first, line);
2782         
2783         for (marker=text->markers.first; marker; marker=next) {
2784                 next= marker->next;
2785
2786                 if (group && marker->group != group) continue;
2787                 else if ((marker->flags & flags) != flags) continue;
2788                 else if (marker->lineno < lineno) continue;
2789                 else if (marker->lineno > lineno) break;
2790
2791                 if ((marker->start==marker->end && start<=marker->start && marker->start<=end) ||
2792                                 (marker->start<end && marker->end>start))
2793                         return marker;
2794         }
2795         return NULL;
2796 }
2797
2798 /* Clears all markers on the specified line between two points. If the group or
2799    flags fields are non-zero the returned flag must be in the specified group
2800    and have at least the specified flags set. */
2801 short txt_clear_marker_region(Text *text, TextLine *line, int start, int end, int group, int flags) {
2802         TextMarker *marker, *next;
2803         int lineno= txt_get_span(text->lines.first, line);
2804         short cleared= 0;
2805         
2806         for (marker=text->markers.first; marker; marker=next) {
2807                 next= marker->next;
2808
2809                 if (group && marker->group != group) continue;
2810                 else if ((marker->flags & flags) != flags) continue;
2811                 else if (marker->lineno < lineno) continue;
2812                 else if (marker->lineno > lineno) break;
2813
2814                 if ((marker->start==marker->end && start<=marker->start && marker->start<=end) ||
2815                         (marker->start<end && marker->end>start)) {
2816                         BLI_freelinkN(&text->markers, marker);
2817                         cleared= 1;
2818                 }
2819         }
2820         return cleared;
2821 }
2822
2823 /* Clears all markers in the specified group (if given) with at least the
2824    specified flags set. Useful for clearing temporary markers (group=0,
2825    flags=TMARK_TEMP) */
2826 short txt_clear_markers(Text *text, int group, int flags) {
2827         TextMarker *marker, *next;
2828         short cleared= 0;
2829         
2830         for (marker=text->markers.first; marker; marker=next) {
2831                 next= marker->next;
2832
2833                 if ((!group || marker->group==group) &&
2834                                 (marker->flags & flags) == flags) {
2835                         BLI_freelinkN(&text->markers, marker);
2836                         cleared= 1;
2837                 }
2838         }
2839         return cleared;
2840 }
2841
2842 /* Finds the marker at the specified line and cursor position with at least the
2843    specified flags set in the given group (if non-zero). */
2844 TextMarker *txt_find_marker(Text *text, TextLine *line, int curs, int group, int flags) {
2845         TextMarker *marker;
2846         int lineno= txt_get_span(text->lines.first, line);
2847         
2848         for (marker=text->markers.first; marker; marker=marker->next) {
2849                 if (group && marker->group != group) continue;
2850                 else if ((marker->flags & flags) != flags) continue;
2851                 else if (marker->lineno < lineno) continue;
2852                 else if (marker->lineno > lineno) break;
2853
2854                 if (marker->start <= curs && curs <= marker->end)
2855                         return marker;
2856         }
2857         return NULL;
2858 }
2859
2860 /* Finds the previous marker in the same group. If no other is found, the same
2861    marker will be returned */
2862 TextMarker *txt_prev_marker(Text *text, TextMarker *marker) {
2863         TextMarker *tmp= marker;
2864         while (tmp) {
2865                 if (tmp->prev) tmp= tmp->prev;
2866                 else tmp= text->markers.last;
2867                 if (tmp->group == marker->group)
2868                         return tmp;
2869         }
2870         return NULL; /* Only if marker==NULL */
2871 }
2872
2873 /* Finds the next marker in the same group. If no other is found, the same
2874    marker will be returned */
2875 TextMarker *txt_next_marker(Text *text, TextMarker *marker) {
2876         TextMarker *tmp= marker;
2877         while (tmp) {
2878                 if (tmp->next) tmp= tmp->next;
2879                 else tmp= text->markers.first;
2880                 if (tmp->group == marker->group)
2881                         return tmp;
2882         }
2883         return NULL; /* Only if marker==NULL */
2884 }
2885
2886
2887 /*******************************/
2888 /* Character utility functions */
2889 /*******************************/
2890
2891 int text_check_bracket(char ch)
2892 {
2893         int a;
2894         char opens[] = "([{";
2895         char close[] = ")]}";
2896
2897         for(a=0; a<(sizeof(opens)-1); a++) {
2898                 if(ch==opens[a])
2899                         return a+1;
2900                 else if(ch==close[a])
2901                         return -(a+1);
2902         }
2903         return 0;
2904 }
2905
2906 int text_check_delim(char ch)
2907 {
2908         int a;
2909         char delims[] = "():\"\' ~!%^&*-+=[]{};/<>|.#\t,";
2910
2911         for(a=0; a<(sizeof(delims)-1); a++) {
2912                 if(ch==delims[a])
2913                         return 1;
2914         }
2915         return 0;
2916 }
2917
2918 int text_check_digit(char ch)
2919 {
2920         if(ch < '0') return 0;
2921         if(ch <= '9') return 1;
2922         return 0;
2923 }
2924
2925 int text_check_identifier(char ch)
2926 {
2927         if(ch < '0') return 0;
2928         if(ch <= '9') return 1;
2929         if(ch < 'A') return 0;
2930         if(ch <= 'Z' || ch == '_') return 1;
2931         if(ch < 'a') return 0;
2932         if(ch <= 'z') return 1;
2933         return 0;
2934 }
2935
2936 int text_check_whitespace(char ch)
2937 {
2938         if(ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n')
2939                 return 1;
2940         return 0;
2941 }