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