doxygen: blender/blenkernel tagged.
[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         i=0;
382         llen=0;
383         for(i=0; i<len; i++) {
384                 if (buffer[i]=='\n') {
385                         tmp= (TextLine*) MEM_mallocN(sizeof(TextLine), "textline");
386                         tmp->line= (char*) MEM_mallocN(llen+1, "textline_string");
387                         tmp->format= NULL;
388                         
389                         if(llen) memcpy(tmp->line, &buffer[i-llen], llen);
390                         tmp->line[llen]=0;
391                         tmp->len= llen;
392                         
393                         cleanup_textline(tmp);
394
395                         BLI_addtail(&ta->lines, tmp);
396                         ta->nlines++;
397                                 
398                         llen=0;
399                         continue;
400                 }
401                 llen++;
402         }
403
404         if (llen!=0 || ta->nlines==0) {
405                 tmp= (TextLine*) MEM_mallocN(sizeof(TextLine), "textline");
406                 tmp->line= (char*) MEM_mallocN(llen+1, "textline_string");
407                 tmp->format= NULL;
408                 
409                 if(llen) memcpy(tmp->line, &buffer[i-llen], llen);
410
411                 tmp->line[llen]=0;
412                 tmp->len= llen;
413                 
414                 cleanup_textline(tmp);
415
416                 BLI_addtail(&ta->lines, tmp);
417                 ta->nlines++;
418         }
419         
420         ta->curl= ta->sell= ta->lines.first;
421         ta->curc= ta->selc= 0;
422         
423         MEM_freeN(buffer);      
424
425         return ta;
426 }
427
428 Text *copy_text(Text *ta)
429 {
430         Text *tan;
431         TextLine *line, *tmp;
432         
433         tan= copy_libblock(ta);
434         
435         /* file name can be NULL */
436         if(ta->name) {
437                 tan->name= MEM_mallocN(strlen(ta->name)+1, "text_name");
438                 strcpy(tan->name, ta->name);
439         }
440         else {
441                 tan->name= NULL;
442         }
443
444         tan->flags = ta->flags | TXT_ISDIRTY;
445         
446         tan->lines.first= tan->lines.last= NULL;
447         tan->markers.first= tan->markers.last= NULL;
448         tan->curl= tan->sell= NULL;
449         
450         tan->nlines= ta->nlines;
451
452         line= ta->lines.first;  
453         /* Walk down, reconstructing */
454         while (line) {
455                 tmp= (TextLine*) MEM_mallocN(sizeof(TextLine), "textline");
456                 tmp->line= MEM_mallocN(line->len+1, "textline_string");
457                 tmp->format= NULL;
458                 
459                 strcpy(tmp->line, line->line);
460
461                 tmp->len= line->len;
462                 
463                 BLI_addtail(&tan->lines, tmp);
464                 
465                 line= line->next;
466         }
467
468         tan->curl= tan->sell= tan->lines.first;
469         tan->curc= tan->selc= 0;
470
471         init_undo_text(tan);
472
473         return tan;
474 }
475
476 void unlink_text(Main *bmain, Text *text)
477 {
478         bScreen *scr;
479         ScrArea *area;
480         SpaceLink *sl;
481         Scene *scene;
482         Object *ob;
483         bController *cont;
484         bConstraint *con;
485         short update;
486
487         /* dome */
488         for(scene=bmain->scene.first; scene; scene=scene->id.next)
489                 if(scene->r.dometext == text)
490                         scene->r.dometext = NULL;
491
492         for(ob=bmain->object.first; ob; ob=ob->id.next) {
493                 /* game controllers */
494                 for(cont=ob->controllers.first; cont; cont=cont->next) {
495                         if(cont->type==CONT_PYTHON) {
496                                 bPythonCont *pc;
497                                 
498                                 pc= cont->data;
499                                 if(pc->text==text) pc->text= NULL;
500                         }
501                 }
502
503                 /* pyconstraints */
504                 update = 0;
505
506                 if(ob->type==OB_ARMATURE && ob->pose) {
507                         bPoseChannel *pchan;
508                         for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
509                                 for(con = pchan->constraints.first; con; con=con->next) {
510                                         if(con->type==CONSTRAINT_TYPE_PYTHON) {
511                                                 bPythonConstraint *data = con->data;
512                                                 if (data->text==text) data->text = NULL;
513                                                 update = 1;
514                                                 
515                                         }
516                                 }
517                         }
518                 }
519
520                 for(con = ob->constraints.first; con; con=con->next) {
521                         if(con->type==CONSTRAINT_TYPE_PYTHON) {
522                                 bPythonConstraint *data = con->data;
523                                 if (data->text==text) data->text = NULL;
524                                 update = 1;
525                         }
526                 }
527                 
528                 if(update)
529                         DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
530         }
531
532         /* pynodes */
533         // XXX nodeDynamicUnlinkText(&text->id);
534         
535         /* text space */
536         for(scr= bmain->screen.first; scr; scr= scr->id.next) {
537                 for(area= scr->areabase.first; area; area= area->next) {
538                         for(sl= area->spacedata.first; sl; sl= sl->next) {
539                                 if(sl->spacetype==SPACE_TEXT) {
540                                         SpaceText *st= (SpaceText*) sl;
541                                         
542                                         if(st->text==text) {
543                                                 st->text= NULL;
544                                                 st->top= 0;
545                                         }
546                                 }
547                         }
548                 }
549         }
550
551         text->id.us= 0;
552 }
553
554 void clear_text(Text *text) /* called directly from rna */
555 {
556         int oldstate;
557
558         oldstate = txt_get_undostate(  );
559         txt_set_undostate( 1 );
560         txt_sel_all( text );
561         txt_delete_sel(text);
562         txt_set_undostate( oldstate );
563
564         txt_make_dirty(text);
565 }
566
567 void write_text(Text *text, const char *str) /* called directly from rna */
568 {
569         int oldstate;
570
571         oldstate = txt_get_undostate(  );
572         txt_insert_buf( text, str );
573         txt_move_eof( text, 0 );
574         txt_set_undostate( oldstate );
575
576         txt_make_dirty(text);
577 }
578
579 /*****************************/
580 /* Editing utility functions */
581 /*****************************/
582
583 static void make_new_line (TextLine *line, char *newline) 
584 {
585         if (line->line) MEM_freeN(line->line);
586         if (line->format) MEM_freeN(line->format);
587         
588         line->line= newline;
589         line->len= strlen(newline);
590         line->format= NULL;
591 }
592
593 static TextLine *txt_new_line(const char *str)
594 {
595         TextLine *tmp;
596
597         if(!str) str= "";
598         
599         tmp= (TextLine *) MEM_mallocN(sizeof(TextLine), "textline");
600         tmp->line= MEM_mallocN(strlen(str)+1, "textline_string");
601         tmp->format= NULL;
602         
603         strcpy(tmp->line, str);
604         
605         tmp->len= strlen(str);
606         tmp->next= tmp->prev= NULL;
607         
608         return tmp;
609 }
610
611 static TextLine *txt_new_linen(const char *str, int n)
612 {
613         TextLine *tmp;
614
615         tmp= (TextLine *) MEM_mallocN(sizeof(TextLine), "textline");
616         tmp->line= MEM_mallocN(n+1, "textline_string");
617         tmp->format= NULL;
618         
619         BLI_strncpy(tmp->line, (str)? str: "", n+1);
620         
621         tmp->len= strlen(tmp->line);
622         tmp->next= tmp->prev= NULL;
623         
624         return tmp;
625 }
626
627 void txt_clean_text (Text *text) 
628 {       
629         TextLine **top, **bot;
630         
631         if (!text) return;
632         
633         if (!text->lines.first) {
634                 if (text->lines.last) text->lines.first= text->lines.last;
635                 else text->lines.first= text->lines.last= txt_new_line(NULL);
636         } 
637         
638         if (!text->lines.last) text->lines.last= text->lines.first;
639
640         top= (TextLine **) &text->lines.first;
641         bot= (TextLine **) &text->lines.last;
642         
643         while ((*top)->prev) *top= (*top)->prev;
644         while ((*bot)->next) *bot= (*bot)->next;
645
646         if(!text->curl) {
647                 if(text->sell) text->curl= text->sell;
648                 else text->curl= text->lines.first;
649                 text->curc= 0;
650         }
651
652         if(!text->sell) {
653                 text->sell= text->curl;
654                 text->selc= 0;
655         }
656 }
657
658 int txt_get_span (TextLine *from, TextLine *to)
659 {
660         int ret=0;
661         TextLine *tmp= from;
662
663         if (!to || !from) return 0;
664         if (from==to) return 0;
665
666         /* Look forwards */
667         while (tmp) {
668                 if (tmp == to) return ret;
669                 ret++;
670                 tmp= tmp->next;
671         }
672
673         /* Look backwards */
674         if (!tmp) {
675                 tmp= from;
676                 ret=0;
677                 while(tmp) {
678                         if (tmp == to) break;
679                         ret--;
680                         tmp= tmp->prev;
681                 }
682                 if(!tmp) ret=0;
683         }
684
685         return ret;     
686 }
687
688 static void txt_make_dirty (Text *text)
689 {
690         text->flags |= TXT_ISDIRTY;
691 #ifdef WITH_PYTHON
692         if (text->compiled) BPY_text_free_code(text);
693 #endif
694 }
695
696 /* 0:whitespace, 1:punct, 2:alphanumeric */
697 static short txt_char_type (char ch)
698 {
699         if (ch <= ' ') return 0; /* 32 */
700         if (ch <= '/') return 1; /* 47 */
701         if (ch <= '9') return 2; /* 57 */
702         if (ch <= '@') return 1; /* 64 */
703         if (ch <= 'Z') return 2; /* 90 */
704         if (ch == '_') return 2; /* 95, dont delimit '_' */
705         if (ch <= '`') return 1; /* 96 */
706         if (ch <= 'z') return 2; /* 122 */
707         return 1;
708 }
709
710 /****************************/
711 /* Cursor utility functions */
712 /****************************/
713
714 static void txt_curs_cur (Text *text, TextLine ***linep, int **charp)
715 {
716         *linep= &text->curl; *charp= &text->curc;
717 }
718
719 static void txt_curs_sel (Text *text, TextLine ***linep, int **charp)
720 {
721         *linep= &text->sell; *charp= &text->selc;
722 }
723
724 static void txt_curs_first (Text *text, TextLine **linep, int *charp)
725 {
726         if (text->curl==text->sell) {
727                 *linep= text->curl;
728                 if (text->curc<text->selc) *charp= text->curc;
729                 else *charp= text->selc;
730         } else if (txt_get_span(text->lines.first, text->curl)<txt_get_span(text->lines.first, text->sell)) {
731                 *linep= text->curl;
732                 *charp= text->curc;
733         } else {
734                 *linep= text->sell;
735                 *charp= text->selc;             
736         }
737 }
738
739 /****************************/
740 /* Cursor movement functions */
741 /****************************/
742
743 void txt_move_up(Text *text, short sel)
744 {
745         TextLine **linep;
746         int *charp, old;
747         
748         if (!text) return;
749         if(sel) txt_curs_sel(text, &linep, &charp);
750         else { txt_pop_first(text); txt_curs_cur(text, &linep, &charp); }
751         if (!*linep) return;
752         old= *charp;
753
754         if((*linep)->prev) {
755                 *linep= (*linep)->prev;
756                 if (*charp > (*linep)->len) {
757                         *charp= (*linep)->len;
758                         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);
759                 } else {
760                         if(!undoing) txt_undo_add_op(text, sel?UNDO_SUP:UNDO_CUP);
761                 }
762         } else {
763                 txt_move_bol(text, sel);
764         }
765
766         if(!sel) txt_pop_sel(text);
767 }
768
769 void txt_move_down(Text *text, short sel) 
770 {
771         TextLine **linep;
772         int *charp, old;
773         
774         if (!text) return;
775         if(sel) txt_curs_sel(text, &linep, &charp);
776         else { txt_pop_last(text); txt_curs_cur(text, &linep, &charp); }
777         if (!*linep) return;
778         old= *charp;
779
780         if((*linep)->next) {
781                 *linep= (*linep)->next;
782                 if (*charp > (*linep)->len) {
783                         *charp= (*linep)->len;
784                         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);
785                 } else
786                         if(!undoing) txt_undo_add_op(text, sel?UNDO_SDOWN:UNDO_CDOWN);  
787         } else {
788                 txt_move_eol(text, sel);
789         }
790
791         if(!sel) txt_pop_sel(text);
792 }
793
794 void txt_move_left(Text *text, short sel) 
795 {
796         TextLine **linep;
797         int *charp, oundoing= undoing;
798         
799         if (!text) return;
800         if(sel) txt_curs_sel(text, &linep, &charp);
801         else { txt_pop_first(text); txt_curs_cur(text, &linep, &charp); }
802         if (!*linep) return;
803
804         undoing= 1;
805         if (*charp== 0) {
806                 if ((*linep)->prev) {
807                         txt_move_up(text, sel);
808                         *charp= (*linep)->len;
809                 }
810         } else {
811                 (*charp)--;
812         }
813         undoing= oundoing;
814         if(!undoing) txt_undo_add_op(text, sel?UNDO_SLEFT:UNDO_CLEFT);
815         
816         if(!sel) txt_pop_sel(text);
817 }
818
819 void txt_move_right(Text *text, short sel) 
820 {
821         TextLine **linep;
822         int *charp, oundoing= undoing;
823         
824         if (!text) return;
825         if(sel) txt_curs_sel(text, &linep, &charp);
826         else { txt_pop_last(text); txt_curs_cur(text, &linep, &charp); }
827         if (!*linep) return;
828
829         undoing= 1;
830         if (*charp== (*linep)->len) {
831                 if ((*linep)->next) {
832                         txt_move_down(text, sel);
833                         *charp= 0;
834                 }
835         } else {
836                 (*charp)++;
837         }
838         undoing= oundoing;
839         if(!undoing) txt_undo_add_op(text, sel?UNDO_SRIGHT:UNDO_CRIGHT);
840
841         if(!sel) txt_pop_sel(text);
842 }
843
844 void txt_jump_left(Text *text, short sel)
845 {
846         TextLine **linep, *oldl;
847         int *charp, oldc, count, i;
848         unsigned char oldu;
849
850         if (!text) return;
851         if(sel) txt_curs_sel(text, &linep, &charp);
852         else { txt_pop_first(text); txt_curs_cur(text, &linep, &charp); }
853         if (!*linep) return;
854
855         oldl= *linep;
856         oldc= *charp;
857         oldu= undoing;
858         undoing= 1; /* Don't push individual moves to undo stack */
859
860         count= 0;
861         for (i=0; i<3; i++) {
862                 if (count < 2) {
863                         while (*charp>0 && txt_char_type((*linep)->line[*charp-1])==i) {
864                                 txt_move_left(text, sel);
865                                 count++;
866                         }
867                 }
868         }
869         if (count==0) txt_move_left(text, sel);
870
871         undoing= oldu;
872         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);
873 }
874
875 void txt_jump_right(Text *text, short sel)
876 {
877         TextLine **linep, *oldl;
878         int *charp, oldc, count, i;
879         unsigned char oldu;
880
881         if (!text) return;
882         if(sel) txt_curs_sel(text, &linep, &charp);
883         else { txt_pop_last(text); txt_curs_cur(text, &linep, &charp); }
884         if (!*linep) return;
885
886         oldl= *linep;
887         oldc= *charp;
888         oldu= undoing;
889         undoing= 1; /* Don't push individual moves to undo stack */
890
891         count= 0;
892         for (i=0; i<3; i++) {
893                 if (count < 2) {
894                         while (*charp<(*linep)->len && txt_char_type((*linep)->line[*charp])==i) {
895                                 txt_move_right(text, sel);
896                                 count++;
897                         }
898                 }
899         }
900         if (count==0) txt_move_right(text, sel);
901
902         undoing= oldu;
903         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);
904 }
905
906 void txt_move_bol (Text *text, short sel) 
907 {
908         TextLine **linep;
909         int *charp, old;
910         
911         if (!text) return;
912         if(sel) txt_curs_sel(text, &linep, &charp);
913         else txt_curs_cur(text, &linep, &charp);
914         if (!*linep) return;
915         old= *charp;
916         
917         *charp= 0;
918
919         if(!sel) txt_pop_sel(text);
920         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);
921 }
922
923 void txt_move_eol (Text *text, short sel) 
924 {
925         TextLine **linep;
926         int *charp, old;
927         
928         if (!text) return;
929         if(sel) txt_curs_sel(text, &linep, &charp);
930         else txt_curs_cur(text, &linep, &charp);
931         if (!*linep) return;
932         old= *charp;
933                 
934         *charp= (*linep)->len;
935
936         if(!sel) txt_pop_sel(text);
937         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);
938 }
939
940 void txt_move_bof (Text *text, short sel)
941 {
942         TextLine **linep;
943         int *charp, old;
944         
945         if (!text) return;
946         if(sel) txt_curs_sel(text, &linep, &charp);
947         else txt_curs_cur(text, &linep, &charp);
948         if (!*linep) return;
949         old= *charp;
950
951         *linep= text->lines.first;
952         *charp= 0;
953
954         if(!sel) txt_pop_sel(text);
955         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);
956 }
957
958 void txt_move_eof (Text *text, short sel)
959 {
960         TextLine **linep;
961         int *charp, old;
962         
963         if (!text) return;
964         if(sel) txt_curs_sel(text, &linep, &charp);
965         else txt_curs_cur(text, &linep, &charp);
966         if (!*linep) return;
967         old= *charp;
968
969         *linep= text->lines.last;
970         *charp= (*linep)->len;
971
972         if(!sel) txt_pop_sel(text);
973         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);     
974 }
975
976 void txt_move_toline (Text *text, unsigned int line, short sel)
977 {
978         txt_move_to(text, line, 0, sel);
979 }
980
981 void txt_move_to (Text *text, unsigned int line, unsigned int ch, short sel)
982 {
983         TextLine **linep, *oldl;
984         int *charp, oldc;
985         unsigned int i;
986         
987         if (!text) return;
988         if(sel) txt_curs_sel(text, &linep, &charp);
989         else txt_curs_cur(text, &linep, &charp);
990         if (!*linep) return;
991         oldc= *charp;
992         oldl= *linep;
993         
994         *linep= text->lines.first;
995         for (i=0; i<line; i++) {
996                 if ((*linep)->next) *linep= (*linep)->next;
997                 else break;
998         }
999         if (ch>(unsigned int)((*linep)->len))
1000                 ch= (unsigned int)((*linep)->len);
1001         *charp= ch;
1002         
1003         if(!sel) txt_pop_sel(text);
1004         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);
1005 }
1006
1007 /****************************/
1008 /* Text selection functions */
1009 /****************************/
1010
1011 static void txt_curs_swap (Text *text)
1012 {
1013         TextLine *tmpl;
1014         int tmpc;
1015                 
1016         tmpl= text->curl;
1017         text->curl= text->sell;
1018         text->sell= tmpl;
1019         
1020         tmpc= text->curc;
1021         text->curc= text->selc;
1022         text->selc= tmpc;
1023         
1024         if(!undoing) txt_undo_add_op(text, UNDO_SWAP);
1025 }
1026
1027 static void txt_pop_first (Text *text)
1028 {
1029                         
1030         if (txt_get_span(text->curl, text->sell)<0 ||
1031                 (text->curl==text->sell && text->curc>text->selc)) {    
1032                 txt_curs_swap(text);
1033         }
1034
1035         if(!undoing) txt_undo_add_toop(text, UNDO_STO,
1036                 txt_get_span(text->lines.first, text->sell), 
1037                 text->selc, 
1038                 txt_get_span(text->lines.first, text->curl), 
1039                 text->curc);            
1040         
1041         txt_pop_sel(text);
1042 }
1043
1044 static void txt_pop_last (Text *text)
1045 {
1046         if (txt_get_span(text->curl, text->sell)>0 ||
1047                 (text->curl==text->sell && text->curc<text->selc)) {
1048                 txt_curs_swap(text);
1049         }
1050
1051         if(!undoing) txt_undo_add_toop(text, UNDO_STO,
1052                 txt_get_span(text->lines.first, text->sell), 
1053                 text->selc, 
1054                 txt_get_span(text->lines.first, text->curl), 
1055                 text->curc);            
1056         
1057         txt_pop_sel(text);
1058 }
1059
1060 /* never used: CVS 1.19 */
1061 /*  static void txt_pop_selr (Text *text) */
1062
1063 void txt_pop_sel (Text *text)
1064 {
1065         text->sell= text->curl;
1066         text->selc= text->curc; 
1067 }
1068
1069 void txt_order_cursors(Text *text)
1070 {
1071         if (!text) return;
1072         if (!text->curl) return;
1073         if (!text->sell) return;
1074         
1075                 /* Flip so text->curl is before text->sell */
1076         if (txt_get_span(text->curl, text->sell)<0 ||
1077                         (text->curl==text->sell && text->curc>text->selc))
1078                 txt_curs_swap(text);
1079 }
1080
1081 int txt_has_sel(Text *text)
1082 {
1083         return ((text->curl!=text->sell) || (text->curc!=text->selc));
1084 }
1085
1086 static void txt_delete_sel (Text *text)
1087 {
1088         TextLine *tmpl;
1089         TextMarker *mrk;
1090         char *buf;
1091         int move, lineno;
1092         
1093         if (!text) return;
1094         if (!text->curl) return;
1095         if (!text->sell) return;
1096
1097         if (!txt_has_sel(text)) return;
1098         
1099         txt_order_cursors(text);
1100
1101         if(!undoing) {
1102                 buf= txt_sel_to_buf(text);
1103                 txt_undo_add_block(text, UNDO_DBLOCK, buf);
1104                 MEM_freeN(buf);
1105         }
1106
1107         buf= MEM_mallocN(text->curc+(text->sell->len - text->selc)+1, "textline_string");
1108         
1109         if (text->curl != text->sell) {
1110                 txt_clear_marker_region(text, text->curl, text->curc, text->curl->len, 0, 0);
1111                 move= txt_get_span(text->curl, text->sell);
1112         } else {
1113                 mrk= txt_find_marker_region(text, text->curl, text->curc, text->selc, 0, 0);
1114                 if (mrk && (mrk->start > text->curc || mrk->end < text->selc))
1115                         txt_clear_marker_region(text, text->curl, text->curc, text->selc, 0, 0);
1116                 move= 0;
1117         }
1118
1119         mrk= txt_find_marker_region(text, text->sell, text->selc-1, text->sell->len, 0, 0);
1120         if (mrk) {
1121                 lineno= mrk->lineno;
1122                 do {
1123                         mrk->lineno -= move;
1124                         if (mrk->start > text->curc) mrk->start -= text->selc - text->curc;
1125                         mrk->end -= text->selc - text->curc;
1126                         mrk= mrk->next;
1127                 } while (mrk && mrk->lineno==lineno);
1128         }
1129
1130         strncpy(buf, text->curl->line, text->curc);
1131         strcpy(buf+text->curc, text->sell->line + text->selc);
1132         buf[text->curc+(text->sell->len - text->selc)]=0;
1133
1134         make_new_line(text->curl, buf);
1135         
1136         tmpl= text->sell;
1137         while (tmpl != text->curl) {
1138                 tmpl= tmpl->prev;
1139                 if (!tmpl) break;
1140                 
1141                 txt_delete_line(text, tmpl->next);
1142         }
1143         
1144         text->sell= text->curl;
1145         text->selc= text->curc;
1146 }
1147
1148 void txt_sel_all (Text *text)
1149 {
1150         if (!text) return;
1151
1152         text->curl= text->lines.first;
1153         text->curc= 0;
1154         
1155         text->sell= text->lines.last;
1156         text->selc= text->sell->len;
1157 }
1158
1159 void txt_sel_line (Text *text)
1160 {
1161         if (!text) return;
1162         if (!text->curl) return;
1163         
1164         text->curc= 0;
1165         text->sell= text->curl;
1166         text->selc= text->sell->len;
1167 }
1168
1169 /***************************/
1170 /* Cut and paste functions */
1171 /***************************/
1172
1173 char *txt_to_buf (Text *text)
1174 {
1175         int length;
1176         TextLine *tmp, *linef, *linel;
1177         int charf, charl;
1178         char *buf;
1179         
1180         if (!text) return NULL;
1181         if (!text->curl) return NULL;
1182         if (!text->sell) return NULL;
1183         if (!text->lines.first) return NULL;
1184
1185         linef= text->lines.first;
1186         charf= 0;
1187                 
1188         linel= text->lines.last;
1189         charl= linel->len;
1190
1191         if (linef == text->lines.last) {
1192                 length= charl-charf;
1193
1194                 buf= MEM_mallocN(length+2, "text buffer");
1195                 
1196                 BLI_strncpy(buf, linef->line + charf, length+1);
1197                 buf[length]=0;
1198         } else {
1199                 length= linef->len - charf;
1200                 length+= charl;
1201                 length+= 2; /* For the 2 '\n' */
1202                 
1203                 tmp= linef->next;
1204                 while (tmp && tmp!= linel) {
1205                         length+= tmp->len+1;
1206                         tmp= tmp->next;
1207                 }
1208                 
1209                 buf= MEM_mallocN(length+1, "cut buffer");
1210
1211                 strncpy(buf, linef->line + charf, linef->len-charf);
1212                 length= linef->len - charf;
1213                 
1214                 buf[length++]='\n';
1215                 
1216                 tmp= linef->next;
1217                 while (tmp && tmp!=linel) {
1218                         strncpy(buf+length, tmp->line, tmp->len);
1219                         length+= tmp->len;
1220                         
1221                         buf[length++]='\n';                     
1222                         
1223                         tmp= tmp->next;
1224                 }
1225                 strncpy(buf+length, linel->line, charl);
1226                 length+= charl;
1227                 
1228                 /* python compiler wants an empty end line */
1229                 buf[length++]='\n';                     
1230                 buf[length]=0;
1231         }
1232         
1233         return buf;
1234 }
1235
1236 int txt_find_string(Text *text, char *findstr, int wrap)
1237 {
1238         TextLine *tl, *startl;
1239         char *s= NULL;
1240         int oldcl, oldsl;
1241
1242         if (!text || !text->curl || !text->sell) return 0;
1243         
1244         txt_order_cursors(text);
1245
1246         oldcl= txt_get_span(text->lines.first, text->curl);
1247         oldsl= txt_get_span(text->lines.first, text->sell);
1248         tl= startl= text->sell;
1249         
1250         s= strstr(&tl->line[text->selc], findstr);
1251         while (!s) {
1252                 tl= tl->next;
1253                 if (!tl) {
1254                         if (wrap)
1255                                 tl= text->lines.first;
1256                         else
1257                                 break;
1258                 }
1259
1260                 s= strstr(tl->line, findstr);
1261                 if (tl==startl)
1262                         break;
1263         }
1264         
1265         if (s) {
1266                 int newl= txt_get_span(text->lines.first, tl);
1267                 int newc= (int)(s-tl->line);
1268                 txt_move_to(text, newl, newc, 0);
1269                 txt_move_to(text, newl, newc + strlen(findstr), 1);
1270                 return 1;                               
1271         } else
1272                 return 0;
1273 }
1274
1275 char *txt_sel_to_buf (Text *text)
1276 {
1277         char *buf;
1278         int length=0;
1279         TextLine *tmp, *linef, *linel;
1280         int charf, charl;
1281         
1282         if (!text) return NULL;
1283         if (!text->curl) return NULL;
1284         if (!text->sell) return NULL;
1285         
1286         if (text->curl==text->sell) {
1287                 linef= linel= text->curl;
1288                 
1289                 if (text->curc < text->selc) {
1290                         charf= text->curc;
1291                         charl= text->selc;
1292                 } else{
1293                         charf= text->selc;
1294                         charl= text->curc;
1295                 }
1296         } else if (txt_get_span(text->curl, text->sell)<0) {
1297                 linef= text->sell;
1298                 linel= text->curl;
1299
1300                 charf= text->selc;              
1301                 charl= text->curc;
1302         } else {
1303                 linef= text->curl;
1304                 linel= text->sell;
1305                 
1306                 charf= text->curc;
1307                 charl= text->selc;
1308         }
1309
1310         if (linef == linel) {
1311                 length= charl-charf;
1312
1313                 buf= MEM_mallocN(length+1, "sel buffer");
1314                 
1315                 BLI_strncpy(buf, linef->line + charf, length+1);
1316         } else {
1317                 length+= linef->len - charf;
1318                 length+= charl;
1319                 length++; /* For the '\n' */
1320                 
1321                 tmp= linef->next;
1322                 while (tmp && tmp!= linel) {
1323                         length+= tmp->len+1;
1324                         tmp= tmp->next;
1325                 }
1326                 
1327                 buf= MEM_mallocN(length+1, "sel buffer");
1328                 
1329                 strncpy(buf, linef->line+ charf, linef->len-charf);
1330                 length= linef->len-charf;
1331                 
1332                 buf[length++]='\n';
1333                 
1334                 tmp= linef->next;
1335                 while (tmp && tmp!=linel) {
1336                         strncpy(buf+length, tmp->line, tmp->len);
1337                         length+= tmp->len;
1338                         
1339                         buf[length++]='\n';                     
1340                         
1341                         tmp= tmp->next;
1342                 }
1343                 strncpy(buf+length, linel->line, charl);
1344                 length+= charl;
1345                 
1346                 buf[length]=0;
1347         }       
1348
1349         return buf;
1350 }
1351
1352 void txt_insert_buf(Text *text, const char *in_buffer)
1353 {
1354         int i=0, l=0, j, u, len;
1355         TextLine *add;
1356
1357         if (!text) return;
1358         if (!in_buffer) return;
1359
1360         txt_delete_sel(text);
1361         
1362         if(!undoing) txt_undo_add_block (text, UNDO_IBLOCK, in_buffer);         
1363
1364         u= undoing;
1365         undoing= 1;
1366
1367         /* Read the first line (or as close as possible */
1368         while (in_buffer[i] && in_buffer[i]!='\n') {
1369                 txt_add_char(text, in_buffer[i]);
1370                 i++;
1371         }
1372         
1373         if (in_buffer[i]=='\n') txt_split_curline(text);
1374         else { undoing = u; return; }
1375         i++;
1376
1377         /* Read as many full lines as we can */
1378         len= strlen(in_buffer);
1379
1380         while (i<len) {
1381                 l=0;
1382
1383                 while (in_buffer[i] && in_buffer[i]!='\n') {
1384                         i++; l++;
1385                 }
1386         
1387                 if(in_buffer[i]=='\n') {
1388                         add= txt_new_linen(in_buffer +(i-l), l);
1389                         BLI_insertlinkbefore(&text->lines, text->curl, add);
1390                         i++;
1391                 } else {
1392                         for (j= i-l; j<i && j<(int)strlen(in_buffer); j++) {
1393                                 txt_add_char(text, in_buffer[j]);
1394                         }
1395                         break;
1396                 }
1397         }
1398         
1399         undoing= u;
1400 }
1401
1402 /******************/
1403 /* Undo functions */
1404 /******************/
1405
1406 static int max_undo_test(Text *text, int x)
1407 {
1408         while (text->undo_pos+x >= text->undo_len) {
1409                 if(text->undo_len*2 > TXT_MAX_UNDO) {
1410                         /* XXX error("Undo limit reached, buffer cleared\n"); */
1411                         MEM_freeN(text->undo_buf);
1412                         init_undo_text(text);
1413                         return 0;
1414                 } else {
1415                         void *tmp= text->undo_buf;
1416                         text->undo_buf= MEM_callocN(text->undo_len*2, "undo buf");
1417                         memcpy(text->undo_buf, tmp, text->undo_len);
1418                         text->undo_len*=2;
1419                         MEM_freeN(tmp);
1420                 }
1421         }
1422
1423         return 1;
1424 }
1425
1426 static void dump_buffer(Text *text) 
1427 {
1428         int i= 0;
1429         
1430         while (i++<text->undo_pos) printf("%d: %d %c\n", i, text->undo_buf[i], text->undo_buf[i]);
1431 }
1432
1433 void txt_print_undo(Text *text)
1434 {
1435         int i= 0;
1436         int op;
1437         const char *ops;
1438         int linep, charp;
1439         
1440         dump_buffer(text);
1441         
1442         printf ("---< Undo Buffer >---\n");
1443         
1444         printf ("UndoPosition is %d\n", text->undo_pos);
1445         
1446         while (i<=text->undo_pos) {
1447                 op= text->undo_buf[i];
1448                 
1449                 if (op==UNDO_CLEFT) {
1450                         ops= "Cursor left";
1451                 } else if (op==UNDO_CRIGHT) {
1452                         ops= "Cursor right";
1453                 } else if (op==UNDO_CUP) {
1454                         ops= "Cursor up";
1455                 } else if (op==UNDO_CDOWN) {
1456                         ops= "Cursor down";
1457                 } else if (op==UNDO_SLEFT) {
1458                         ops= "Selection left";
1459                 } else if (op==UNDO_SRIGHT) {
1460                         ops= "Selection right";
1461                 } else if (op==UNDO_SUP) {
1462                         ops= "Selection up";
1463                 } else if (op==UNDO_SDOWN) {
1464                         ops= "Selection down";
1465                 } else if (op==UNDO_STO) {
1466                         ops= "Selection ";
1467                 } else if (op==UNDO_CTO) {
1468                         ops= "Cursor ";
1469                 } else if (op==UNDO_INSERT) {
1470                         ops= "Insert";
1471                 } else if (op==UNDO_BS) {
1472                         ops= "Backspace";
1473                 } else if (op==UNDO_DEL) {
1474                         ops= "Delete";
1475                 } else if (op==UNDO_SWAP) {
1476                         ops= "Cursor swap";
1477                 } else if (op==UNDO_DBLOCK) {
1478                         ops= "Delete text block";
1479                 } else if (op==UNDO_IBLOCK) {
1480                         ops= "Insert text block";
1481                 } else if (op==UNDO_INDENT) {
1482                         ops= "Indent ";
1483                 } else if (op==UNDO_UNINDENT) {
1484                         ops= "Unindent ";
1485                 } else if (op==UNDO_COMMENT) {
1486                         ops= "Comment ";
1487                 } else if (op==UNDO_UNCOMMENT) {
1488                         ops= "Uncomment ";
1489                 } else {
1490                         ops= "Unknown";
1491                 }
1492                 
1493                 printf ("Op (%o) at %d = %s", op, i, ops);
1494                 if (op==UNDO_INSERT || op==UNDO_BS || op==UNDO_DEL) {
1495                         i++;
1496                         printf (" - Char is %c", text->undo_buf[i]);  
1497                         i++;
1498                 } else if (op==UNDO_STO || op==UNDO_CTO) {
1499                         i++;
1500
1501                         charp= text->undo_buf[i]; i++;
1502                         charp= charp+(text->undo_buf[i]<<8); i++;
1503
1504                         linep= text->undo_buf[i]; i++;
1505                         linep= linep+(text->undo_buf[i]<<8); i++;
1506                         linep= linep+(text->undo_buf[i]<<16); i++;
1507                         linep= linep+(text->undo_buf[i]<<24); i++;
1508                         
1509                         printf ("to <%d, %d> ", linep, charp);
1510
1511                         charp= text->undo_buf[i]; i++;
1512                         charp= charp+(text->undo_buf[i]<<8); i++;
1513
1514                         linep= text->undo_buf[i]; i++;
1515                         linep= linep+(text->undo_buf[i]<<8); i++;
1516                         linep= linep+(text->undo_buf[i]<<16); i++;
1517                         linep= linep+(text->undo_buf[i]<<24); i++;
1518                         
1519                         printf ("from <%d, %d>", linep, charp);
1520                 } else if (op==UNDO_DBLOCK || op==UNDO_IBLOCK) {
1521                         i++;
1522
1523                         linep= text->undo_buf[i]; i++;
1524                         linep= linep+(text->undo_buf[i]<<8); i++;
1525                         linep= linep+(text->undo_buf[i]<<16); i++;
1526                         linep= linep+(text->undo_buf[i]<<24); i++;
1527                         
1528                         printf (" (length %d) <", linep);
1529                         
1530                         while (linep>0) {
1531                                 putchar(text->undo_buf[i]);
1532                                 linep--; i++;
1533                         }
1534                         
1535                         linep= text->undo_buf[i]; i++;
1536                         linep= linep+(text->undo_buf[i]<<8); i++;
1537                         linep= linep+(text->undo_buf[i]<<16); i++;
1538                         linep= linep+(text->undo_buf[i]<<24); i++;
1539                         printf ("> (%d)", linep);
1540                 } else if (op==UNDO_INDENT || op==UNDO_UNINDENT) {
1541                         i++;
1542
1543                         charp= text->undo_buf[i]; i++;
1544                         charp= charp+(text->undo_buf[i]<<8); i++;
1545
1546                         linep= text->undo_buf[i]; i++;
1547                         linep= linep+(text->undo_buf[i]<<8); i++;
1548                         linep= linep+(text->undo_buf[i]<<16); i++;
1549                         linep= linep+(text->undo_buf[i]<<24); i++;
1550                         
1551                         printf ("to <%d, %d> ", linep, charp);
1552
1553                         charp= text->undo_buf[i]; i++;
1554                         charp= charp+(text->undo_buf[i]<<8); i++;
1555
1556                         linep= text->undo_buf[i]; i++;
1557                         linep= linep+(text->undo_buf[i]<<8); i++;
1558                         linep= linep+(text->undo_buf[i]<<16); i++;
1559                         linep= linep+(text->undo_buf[i]<<24); i++;
1560                         
1561                         printf ("from <%d, %d>", linep, charp);
1562                 }
1563                 
1564                 printf (" %d\n",  i);
1565                 i++;
1566         }
1567 }
1568
1569 static void txt_undo_add_op(Text *text, int op)
1570 {
1571         if(!max_undo_test(text, 2))
1572                 return;
1573         
1574         text->undo_pos++;
1575         text->undo_buf[text->undo_pos]= op;
1576         text->undo_buf[text->undo_pos+1]= 0;
1577 }
1578
1579 static void txt_undo_add_block(Text *text, int op, const char *buf)
1580 {
1581         int length;
1582         
1583         length= strlen(buf);
1584         
1585         if(!max_undo_test(text, length+11))
1586                 return;
1587
1588         text->undo_pos++;
1589         text->undo_buf[text->undo_pos]= op;
1590         
1591         text->undo_pos++;
1592         text->undo_buf[text->undo_pos]= (length)&0xff;
1593         text->undo_pos++;
1594         text->undo_buf[text->undo_pos]= (length>>8)&0xff;
1595         text->undo_pos++;
1596         text->undo_buf[text->undo_pos]= (length>>16)&0xff;
1597         text->undo_pos++;
1598         text->undo_buf[text->undo_pos]= (length>>24)&0xff;
1599
1600         text->undo_pos++;
1601         strncpy(text->undo_buf+text->undo_pos, buf, length);
1602         text->undo_pos+=length;
1603
1604         text->undo_buf[text->undo_pos]= (length)&0xff;
1605         text->undo_pos++;
1606         text->undo_buf[text->undo_pos]= (length>>8)&0xff;
1607         text->undo_pos++;
1608         text->undo_buf[text->undo_pos]= (length>>16)&0xff;
1609         text->undo_pos++;
1610         text->undo_buf[text->undo_pos]= (length>>24)&0xff;
1611
1612         text->undo_pos++;
1613         text->undo_buf[text->undo_pos]= op;
1614         
1615         text->undo_buf[text->undo_pos+1]= 0;
1616 }
1617
1618 void txt_undo_add_toop(Text *text, int op, unsigned int froml, unsigned short fromc, unsigned int tol, unsigned short toc)
1619 {
1620         if(!max_undo_test(text, 15))
1621                 return;
1622
1623         if (froml==tol && fromc==toc) return;
1624
1625         text->undo_pos++;
1626         text->undo_buf[text->undo_pos]= op;
1627
1628         text->undo_pos++;
1629         text->undo_buf[text->undo_pos]= (fromc)&0xff;
1630         text->undo_pos++;
1631         text->undo_buf[text->undo_pos]= (fromc>>8)&0xff;
1632
1633         text->undo_pos++;
1634         text->undo_buf[text->undo_pos]= (froml)&0xff;
1635         text->undo_pos++;
1636         text->undo_buf[text->undo_pos]= (froml>>8)&0xff;
1637         text->undo_pos++;
1638         text->undo_buf[text->undo_pos]= (froml>>16)&0xff;
1639         text->undo_pos++;
1640         text->undo_buf[text->undo_pos]= (froml>>24)&0xff;
1641
1642         text->undo_pos++;
1643         text->undo_buf[text->undo_pos]= (toc)&0xff;
1644         text->undo_pos++;
1645         text->undo_buf[text->undo_pos]= (toc>>8)&0xff;
1646
1647         text->undo_pos++;
1648         text->undo_buf[text->undo_pos]= (tol)&0xff;
1649         text->undo_pos++;
1650         text->undo_buf[text->undo_pos]= (tol>>8)&0xff;
1651         text->undo_pos++;
1652         text->undo_buf[text->undo_pos]= (tol>>16)&0xff;
1653         text->undo_pos++;
1654         text->undo_buf[text->undo_pos]= (tol>>24)&0xff;
1655
1656         text->undo_pos++;
1657         text->undo_buf[text->undo_pos]= op;
1658
1659         text->undo_buf[text->undo_pos+1]= 0;
1660 }
1661
1662 static void txt_undo_add_charop(Text *text, int op, char c)
1663 {
1664         if(!max_undo_test(text, 4))
1665                 return;
1666
1667         text->undo_pos++;
1668         text->undo_buf[text->undo_pos]= op;
1669         text->undo_pos++;
1670         text->undo_buf[text->undo_pos]= c;
1671         text->undo_pos++;
1672         text->undo_buf[text->undo_pos]= op;
1673         text->undo_buf[text->undo_pos+1]= 0;
1674 }
1675
1676 void txt_do_undo(Text *text)
1677 {
1678         int op= text->undo_buf[text->undo_pos];
1679         unsigned int linep, i;
1680         unsigned short charp;
1681         TextLine *holdl;
1682         int holdc, holdln;
1683         char *buf;
1684         
1685         if (text->undo_pos<0) {
1686                 return;
1687         }
1688
1689         text->undo_pos--;
1690
1691         undoing= 1;
1692         
1693         switch(op) {
1694                 case UNDO_CLEFT:
1695                         txt_move_right(text, 0);
1696                         break;
1697                         
1698                 case UNDO_CRIGHT:
1699                         txt_move_left(text, 0);
1700                         break;
1701                         
1702                 case UNDO_CUP:
1703                         txt_move_down(text, 0);
1704                         break;
1705                         
1706                 case UNDO_CDOWN:
1707                         txt_move_up(text, 0);
1708                         break;
1709
1710                 case UNDO_SLEFT:
1711                         txt_move_right(text, 1);
1712                         break;
1713
1714                 case UNDO_SRIGHT:
1715                         txt_move_left(text, 1);
1716                         break;
1717
1718                 case UNDO_SUP:
1719                         txt_move_down(text, 1);
1720                         break;
1721
1722                 case UNDO_SDOWN:
1723                         txt_move_up(text, 1);
1724                         break;
1725                 
1726                 case UNDO_CTO:
1727                 case UNDO_STO:
1728                         text->undo_pos--;
1729                         text->undo_pos--;
1730                         text->undo_pos--;
1731                         text->undo_pos--;
1732                 
1733                         text->undo_pos--;
1734                         text->undo_pos--;
1735                 
1736                         linep= 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                         linep= (linep<<8)+text->undo_buf[text->undo_pos]; text->undo_pos--;
1740
1741                         charp= text->undo_buf[text->undo_pos]; text->undo_pos--;
1742                         charp= (charp<<8)+text->undo_buf[text->undo_pos]; text->undo_pos--;
1743                         
1744                         if (op==UNDO_CTO) {
1745                                 txt_move_toline(text, linep, 0);
1746                                 text->curc= charp;
1747                                 txt_pop_sel(text);
1748                         } else {
1749                                 txt_move_toline(text, linep, 1);
1750                                 text->selc= charp;
1751                         }
1752                         
1753                         text->undo_pos--;
1754                         break;
1755                         
1756                 case UNDO_INSERT:
1757                         txt_backspace_char(text);
1758                         text->undo_pos--;
1759                         text->undo_pos--;
1760                         break;
1761
1762                 case UNDO_BS:
1763                         txt_add_char(text, text->undo_buf[text->undo_pos]);
1764                         text->undo_pos--;
1765                         text->undo_pos--;
1766                         break;
1767
1768                 case UNDO_DEL:
1769                         txt_add_char(text, text->undo_buf[text->undo_pos]);
1770                         txt_move_left(text, 0);
1771                         text->undo_pos--;
1772                         text->undo_pos--;
1773                         break;
1774
1775                 case UNDO_SWAP:
1776                         txt_curs_swap(text);
1777                         break;
1778
1779                 case UNDO_DBLOCK:
1780                         linep= 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                         linep= (linep<<8)+text->undo_buf[text->undo_pos]; text->undo_pos--;
1784
1785                         buf= MEM_mallocN(linep+1, "dblock buffer");
1786                         for (i=0; i < linep; i++){
1787                                 buf[(linep-1)-i]= text->undo_buf[text->undo_pos]; 
1788                                 text->undo_pos--;
1789                         }
1790                         buf[i]= 0;
1791                         
1792                         txt_curs_first(text, &holdl, &holdc);
1793                         holdln= txt_get_span(text->lines.first, holdl);
1794                         
1795                         txt_insert_buf(text, buf);                      
1796                         MEM_freeN(buf);
1797
1798                         text->curl= text->lines.first;
1799                         while (holdln>0) {
1800                                 if(text->curl->next)
1801                                         text->curl= text->curl->next;
1802                                         
1803                                 holdln--;
1804                         }
1805                         text->curc= holdc;
1806
1807                         linep= 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                         linep= (linep<<8)+text->undo_buf[text->undo_pos]; text->undo_pos--;
1811
1812                         text->undo_pos--;
1813                         
1814                         break;
1815
1816                 case UNDO_IBLOCK:
1817                         linep= 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                         linep= (linep<<8)+text->undo_buf[text->undo_pos]; text->undo_pos--;
1821
1822                         txt_delete_sel(text);
1823                         while (linep>0) {
1824                                 txt_backspace_char(text);
1825                                 text->undo_pos--;
1826                                 linep--;
1827                         }
1828
1829                         text->undo_pos--;
1830                         text->undo_pos--;
1831                         text->undo_pos--; 
1832                         text->undo_pos--;
1833                         
1834                         text->undo_pos--;
1835
1836                         break;
1837                 case UNDO_INDENT:
1838                 case UNDO_UNINDENT:
1839                 case UNDO_COMMENT:
1840                 case UNDO_UNCOMMENT:
1841                         linep= 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 = (linep<<8)+text->undo_buf[text->undo_pos]; text->undo_pos--;
1845                         //linep is now the end line of the selection
1846                         
1847                         charp = text->undo_buf[text->undo_pos]; text->undo_pos--;
1848                         charp = (charp<<8)+text->undo_buf[text->undo_pos]; text->undo_pos--;
1849                         //charp is the last char selected or text->line->len
1850                         //set the selcetion for this now
1851                         text->selc = charp;
1852                         text->sell = text->lines.first;
1853                         for (i= 0; i < linep; i++) {
1854                                 text->sell = text->sell->next;
1855                         }
1856
1857                         linep= 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                         linep = (linep<<8)+text->undo_buf[text->undo_pos]; text->undo_pos--;
1861                         //first line to be selected
1862                         
1863                         charp = text->undo_buf[text->undo_pos]; text->undo_pos--;
1864                         charp = (charp<<8)+text->undo_buf[text->undo_pos]; text->undo_pos--;
1865                         //first postion to be selected
1866                         text->curc = charp;
1867                         text->curl = text->lines.first;
1868                         for (i = 0; i < linep; i++) {
1869                                 text->curl = text->curl->next;
1870                         }
1871
1872                         
1873                         if (op==UNDO_INDENT) {
1874                                 unindent(text);
1875                         } else if (op== UNDO_UNINDENT) {
1876                                 indent(text);
1877                         } else if (op == UNDO_COMMENT) {
1878                                 uncomment(text);
1879                         } else if (op == UNDO_UNCOMMENT) {
1880                                 comment(text);
1881                         }
1882
1883                         text->undo_pos--;
1884                         break;
1885                 default:
1886                         //XXX error("Undo buffer error - resetting");
1887                         text->undo_pos= -1;
1888                         
1889                         break;
1890         }
1891
1892         /* next undo step may need evaluating */
1893         if (text->undo_pos>=0) {
1894                 switch (text->undo_buf[text->undo_pos]) {
1895                         case UNDO_STO:
1896                                 txt_do_undo(text);
1897                                 txt_do_redo(text); /* selections need restoring */
1898                                 break;
1899                         case UNDO_SWAP:
1900                                 txt_do_undo(text); /* swaps should appear transparent */
1901                                 break;
1902                 }
1903         }
1904         
1905         undoing= 0;     
1906 }
1907
1908 void txt_do_redo(Text *text)
1909 {
1910         char op;
1911         unsigned int linep, i;
1912         unsigned short charp;
1913         char *buf;
1914         
1915         text->undo_pos++;       
1916         op= text->undo_buf[text->undo_pos];
1917         
1918         if (!op) {
1919                 text->undo_pos--;
1920                 return;
1921         }
1922         
1923         undoing= 1;
1924
1925         switch(op) {
1926                 case UNDO_CLEFT:
1927                         txt_move_left(text, 0);
1928                         break;
1929                         
1930                 case UNDO_CRIGHT:
1931                         txt_move_right(text, 0);
1932                         break;
1933                         
1934                 case UNDO_CUP:
1935                         txt_move_up(text, 0);
1936                         break;
1937                         
1938                 case UNDO_CDOWN:
1939                         txt_move_down(text, 0);
1940                         break;
1941
1942                 case UNDO_SLEFT:
1943                         txt_move_left(text, 1);
1944                         break;
1945
1946                 case UNDO_SRIGHT:
1947                         txt_move_right(text, 1);
1948                         break;
1949
1950                 case UNDO_SUP:
1951                         txt_move_up(text, 1);
1952                         break;
1953
1954                 case UNDO_SDOWN:
1955                         txt_move_down(text, 1);
1956                         break;
1957                 
1958                 case UNDO_INSERT:
1959                         text->undo_pos++;
1960                         txt_add_char(text, text->undo_buf[text->undo_pos]);
1961                         text->undo_pos++;
1962                         break;
1963
1964                 case UNDO_BS:
1965                         text->undo_pos++;
1966                         txt_backspace_char(text);
1967                         text->undo_pos++;
1968                         break;
1969
1970                 case UNDO_DEL:
1971                         text->undo_pos++;
1972                         txt_delete_char(text);
1973                         text->undo_pos++;
1974                         break;
1975
1976                 case UNDO_SWAP:
1977                         txt_curs_swap(text);
1978                         txt_do_redo(text); /* swaps should appear transparent a*/
1979                         break;
1980                         
1981                 case UNDO_CTO:
1982                 case UNDO_STO:
1983                         text->undo_pos++;
1984                         text->undo_pos++;
1985
1986                         text->undo_pos++;
1987                         text->undo_pos++;
1988                         text->undo_pos++;
1989                         text->undo_pos++;
1990
1991                         text->undo_pos++;
1992
1993                         charp= text->undo_buf[text->undo_pos];
1994                         text->undo_pos++;
1995                         charp= charp+(text->undo_buf[text->undo_pos]<<8);
1996
1997                         text->undo_pos++;
1998                         linep= text->undo_buf[text->undo_pos]; text->undo_pos++;
1999                         linep= linep+(text->undo_buf[text->undo_pos]<<8); text->undo_pos++;
2000                         linep= linep+(text->undo_buf[text->undo_pos]<<16); text->undo_pos++;
2001                         linep= linep+(text->undo_buf[text->undo_pos]<<24); text->undo_pos++;
2002                         
2003                         if (op==UNDO_CTO) {
2004                                 txt_move_toline(text, linep, 0);
2005                                 text->curc= charp;
2006                                 txt_pop_sel(text);
2007                         } else {
2008                                 txt_move_toline(text, linep, 1);
2009                                 text->selc= charp;
2010                         }
2011
2012                         break;
2013
2014                 case UNDO_DBLOCK:
2015                         text->undo_pos++;
2016                         linep= text->undo_buf[text->undo_pos]; text->undo_pos++;
2017                         linep= linep+(text->undo_buf[text->undo_pos]<<8); text->undo_pos++;
2018                         linep= linep+(text->undo_buf[text->undo_pos]<<16); text->undo_pos++;
2019                         linep= linep+(text->undo_buf[text->undo_pos]<<24); text->undo_pos++;
2020
2021                         txt_delete_sel(text);
2022                         text->undo_pos+=linep;
2023
2024                         text->undo_pos++;
2025                         text->undo_pos++;
2026                         text->undo_pos++; 
2027                         text->undo_pos++;
2028                         
2029                         break;
2030
2031                 case UNDO_IBLOCK:
2032                         text->undo_pos++;
2033                         linep= text->undo_buf[text->undo_pos]; text->undo_pos++;
2034                         linep= linep+(text->undo_buf[text->undo_pos]<<8); text->undo_pos++;
2035                         linep= linep+(text->undo_buf[text->undo_pos]<<16); text->undo_pos++;
2036                         linep= linep+(text->undo_buf[text->undo_pos]<<24); text->undo_pos++;
2037
2038                         buf= MEM_mallocN(linep+1, "iblock buffer");
2039                         memcpy (buf, &text->undo_buf[text->undo_pos], linep);
2040                         text->undo_pos+= linep;
2041                         buf[linep]= 0;
2042                         
2043                         txt_insert_buf(text, buf);                      
2044                         MEM_freeN(buf);
2045
2046                         linep= text->undo_buf[text->undo_pos]; text->undo_pos++;
2047                         linep= linep+(text->undo_buf[text->undo_pos]<<8); text->undo_pos++;
2048                         linep= linep+(text->undo_buf[text->undo_pos]<<16); text->undo_pos++;
2049                         linep= linep+(text->undo_buf[text->undo_pos]<<24); text->undo_pos++;
2050
2051                         break;
2052                 case UNDO_INDENT:
2053                 case UNDO_UNINDENT:
2054                 case UNDO_COMMENT:
2055                 case UNDO_UNCOMMENT:
2056                         text->undo_pos++;
2057                         charp = text->undo_buf[text->undo_pos]; text->undo_pos++;
2058                         charp = charp+(text->undo_buf[text->undo_pos]<<8); text->undo_pos++;
2059                         //charp is the first char selected or 0
2060                         
2061                         linep= text->undo_buf[text->undo_pos]; text->undo_pos++;
2062                         linep = linep+(text->undo_buf[text->undo_pos]<<8); text->undo_pos++;
2063                         linep = linep+(text->undo_buf[text->undo_pos]<<16); text->undo_pos++;
2064                         linep = linep+(text->undo_buf[text->undo_pos]<<24); text->undo_pos++;
2065                         //linep is now the first line of the selection                  
2066                         //set the selcetion for this now
2067                         text->curc = charp;
2068                         text->curl = text->lines.first;
2069                         for (i= 0; i < linep; i++) {
2070                                 text->curl = text->curl->next;
2071                         }
2072                         
2073                         charp = text->undo_buf[text->undo_pos]; text->undo_pos++;
2074                         charp = charp+(text->undo_buf[text->undo_pos]<<8); text->undo_pos++;
2075                         //last postion to be selected
2076                         linep= text->undo_buf[text->undo_pos]; text->undo_pos++;
2077                         linep = linep+(text->undo_buf[text->undo_pos]<<8); text->undo_pos++;
2078                         linep = linep+(text->undo_buf[text->undo_pos]<<16); text->undo_pos++;
2079                         linep = linep+(text->undo_buf[text->undo_pos]<<24); text->undo_pos++;
2080                         //Last line to be selected
2081                         
2082                         text->selc = charp;
2083                         text->sell = text->lines.first;
2084                         for (i = 0; i < linep; i++) {
2085                                 text->sell = text->sell->next;
2086                         }
2087
2088                         if (op==UNDO_INDENT) {
2089                                 indent(text);
2090                         } else if (op== UNDO_UNINDENT) {
2091                                 unindent(text);
2092                         } else if (op == UNDO_COMMENT) {
2093                                 comment(text);
2094                         } else if (op == UNDO_UNCOMMENT) {
2095                                 uncomment(text);
2096                         }
2097                         break;
2098                 default:
2099                         //XXX error("Undo buffer error - resetting");
2100                         text->undo_pos= -1;
2101
2102                         break;
2103         }
2104         
2105         undoing= 0;     
2106 }
2107
2108 /**************************/
2109 /* Line editing functions */ 
2110 /**************************/
2111
2112 void txt_split_curline (Text *text) 
2113 {
2114         TextLine *ins;
2115         TextMarker *mrk;
2116         char *left, *right;
2117         int lineno= -1;
2118         
2119         if (!text) return;
2120         if (!text->curl) return;
2121
2122         txt_delete_sel(text);
2123
2124         /* Move markers */
2125
2126         lineno= txt_get_span(text->lines.first, text->curl);
2127         mrk= text->markers.first;
2128         while (mrk) {
2129                 if (mrk->lineno==lineno && mrk->start>text->curc) {
2130                         mrk->lineno++;
2131                         mrk->start -= text->curc;
2132                         mrk->end -= text->curc;
2133                 } else if (mrk->lineno > lineno) {
2134                         mrk->lineno++;
2135                 }
2136                 mrk= mrk->next;
2137         }
2138
2139         /* Make the two half strings */
2140
2141         left= MEM_mallocN(text->curc+1, "textline_string");
2142         if (text->curc) memcpy(left, text->curl->line, text->curc);
2143         left[text->curc]=0;
2144         
2145         right= MEM_mallocN(text->curl->len - text->curc+1, "textline_string");
2146         if (text->curl->len - text->curc) memcpy(right, text->curl->line+text->curc, text->curl->len-text->curc);
2147         right[text->curl->len - text->curc]=0;
2148
2149         MEM_freeN(text->curl->line);
2150         if (text->curl->format) MEM_freeN(text->curl->format);
2151
2152         /* Make the new TextLine */
2153         
2154         ins= MEM_mallocN(sizeof(TextLine), "textline");
2155         ins->line= left;
2156         ins->format= NULL;
2157         ins->len= text->curc;
2158         
2159         text->curl->line= right;
2160         text->curl->format= NULL;
2161         text->curl->len= text->curl->len - text->curc;
2162         
2163         BLI_insertlinkbefore(&text->lines, text->curl, ins);    
2164         
2165         text->curc=0;
2166         
2167         txt_make_dirty(text);
2168         txt_clean_text(text);
2169         
2170         txt_pop_sel(text);
2171         if(!undoing) txt_undo_add_charop(text, UNDO_INSERT, '\n');
2172 }
2173
2174 static void txt_delete_line (Text *text, TextLine *line) 
2175 {
2176         TextMarker *mrk=NULL, *nxt;
2177         int lineno= -1;
2178
2179         if (!text) return;
2180         if (!text->curl) return;
2181
2182         lineno= txt_get_span(text->lines.first, line);
2183         mrk= text->markers.first;
2184         while (mrk) {
2185                 nxt= mrk->next;
2186                 if (mrk->lineno==lineno)
2187                         BLI_freelinkN(&text->markers, mrk);
2188                 else if (mrk->lineno > lineno)
2189                         mrk->lineno--;
2190                 mrk= nxt;
2191         }
2192
2193         BLI_remlink (&text->lines, line);
2194         
2195         if (line->line) MEM_freeN(line->line);
2196         if (line->format) MEM_freeN(line->format);
2197
2198         MEM_freeN(line);
2199
2200         txt_make_dirty(text);
2201         txt_clean_text(text);
2202 }
2203
2204 static void txt_combine_lines (Text *text, TextLine *linea, TextLine *lineb)
2205 {
2206         char *tmp;
2207         TextMarker *mrk= NULL;
2208         int lineno=-1;
2209         
2210         if (!text) return;
2211         
2212         if(!linea || !lineb) return;
2213
2214         mrk= txt_find_marker_region(text, lineb, 0, lineb->len, 0, 0);
2215         if (mrk) {
2216                 lineno= mrk->lineno;
2217                 do {
2218                         mrk->lineno--;
2219                         mrk->start += linea->len;
2220                         mrk->end += linea->len;
2221                         mrk= mrk->next;
2222                 } while (mrk && mrk->lineno==lineno);
2223         }
2224         if (lineno==-1) lineno= txt_get_span(text->lines.first, lineb);
2225         
2226         tmp= MEM_mallocN(linea->len+lineb->len+1, "textline_string");
2227         
2228         strcpy(tmp, linea->line);
2229         strcat(tmp, lineb->line);
2230
2231         make_new_line(linea, tmp);
2232         
2233         txt_delete_line(text, lineb);
2234         
2235         txt_make_dirty(text);
2236         txt_clean_text(text);
2237 }
2238
2239 void txt_delete_char (Text *text) 
2240 {
2241         char c='\n';
2242         
2243         if (!text) return;
2244         if (!text->curl) return;
2245
2246         if (txt_has_sel(text)) { /* deleting a selection */
2247                 txt_delete_sel(text);
2248                 txt_make_dirty(text);
2249                 return;
2250         }
2251         else if (text->curc== text->curl->len) { /* Appending two lines */
2252                 if (text->curl->next) {
2253                         txt_combine_lines(text, text->curl, text->curl->next);
2254                         txt_pop_sel(text);
2255                 }
2256         } else { /* Just deleting a char */
2257                 int i= text->curc;
2258
2259                 TextMarker *mrk= txt_find_marker_region(text, text->curl, i-1, text->curl->len, 0, 0);
2260                 if (mrk) {
2261                         int lineno= mrk->lineno;
2262                         if (mrk->end==i) {
2263                                 if ((mrk->flags & TMARK_TEMP) && !(mrk->flags & TMARK_EDITALL)) {
2264                                         txt_clear_markers(text, mrk->group, TMARK_TEMP);
2265                                 } else {
2266                                         BLI_freelinkN(&text->markers, mrk);
2267                                 }
2268                                 return;
2269                         }
2270                         do {
2271                                 if (mrk->start>i) mrk->start--;
2272                                 mrk->end--;
2273                                 mrk= mrk->next;
2274                         } while (mrk && mrk->lineno==lineno);
2275                 }
2276                 
2277                 c= text->curl->line[i];
2278                 while(i< text->curl->len) {
2279                         text->curl->line[i]= text->curl->line[i+1];
2280                         i++;
2281                 }
2282                 text->curl->len--;
2283
2284                 txt_pop_sel(text);
2285         }
2286
2287         txt_make_dirty(text);
2288         txt_clean_text(text);
2289         
2290         if(!undoing) txt_undo_add_charop(text, UNDO_DEL, c);
2291 }
2292
2293 void txt_delete_word (Text *text) 
2294 {
2295         txt_jump_right(text, 1);
2296         txt_delete_sel(text);
2297 }
2298
2299 void txt_backspace_char (Text *text) 
2300 {
2301         char c='\n';
2302         
2303         if (!text) return;
2304         if (!text->curl) return;
2305         
2306         if (txt_has_sel(text)) { /* deleting a selection */
2307                 txt_delete_sel(text);
2308                 txt_make_dirty(text);
2309                 return;
2310         }
2311         else if (text->curc==0) { /* Appending two lines */
2312                 if (!text->curl->prev) return;
2313                 
2314                 text->curl= text->curl->prev;
2315                 text->curc= text->curl->len;
2316                 
2317                 txt_combine_lines(text, text->curl, text->curl->next);
2318                 txt_pop_sel(text);
2319         }
2320         else { /* Just backspacing a char */
2321                 int i= text->curc-1;
2322
2323                 TextMarker *mrk= txt_find_marker_region(text, text->curl, i, text->curl->len, 0, 0);
2324                 if (mrk) {
2325                         int lineno= mrk->lineno;
2326                         if (mrk->start==i+1) {
2327                                 if ((mrk->flags & TMARK_TEMP) && !(mrk->flags & TMARK_EDITALL)) {
2328                                         txt_clear_markers(text, mrk->group, TMARK_TEMP);
2329                                 } else {
2330                                         BLI_freelinkN(&text->markers, mrk);
2331                                 }
2332                                 return;
2333                         }
2334                         do {
2335                                 if (mrk->start>i) mrk->start--;
2336                                 mrk->end--;
2337                                 mrk= mrk->next;
2338                         } while (mrk && mrk->lineno==lineno);
2339                 }
2340                 
2341                 c= text->curl->line[i];
2342                 while(i< text->curl->len) {
2343                         text->curl->line[i]= text->curl->line[i+1];
2344                         i++;
2345                 }
2346                 text->curl->len--;
2347                 text->curc--;
2348
2349                 txt_pop_sel(text);
2350         }
2351
2352         txt_make_dirty(text);
2353         txt_clean_text(text);
2354         
2355         if(!undoing) txt_undo_add_charop(text, UNDO_BS, c);
2356 }
2357
2358 void txt_backspace_word (Text *text) 
2359 {
2360         txt_jump_left(text, 1);
2361         txt_delete_sel(text);
2362 }
2363
2364 /* Max spaces to replace a tab with, currently hardcoded to TXT_TABSIZE = 4.
2365  * Used by txt_convert_tab_to_spaces, indent and unintent.
2366  * Remember to change this string according to max tab size */
2367 static char tab_to_spaces[] = "    ";
2368
2369 static void txt_convert_tab_to_spaces (Text *text)
2370 {
2371         /* sb aims to pad adjust the tab-width needed so that the right number of spaces
2372          * is added so that the indention of the line is the right width (i.e. aligned
2373          * to multiples of TXT_TABSIZE)
2374          */
2375         char *sb = &tab_to_spaces[text->curc % TXT_TABSIZE];
2376         txt_insert_buf(text, sb);
2377 }
2378
2379 int txt_add_char (Text *text, char add) 
2380 {
2381         int len, lineno;
2382         char *tmp;
2383         TextMarker *mrk;
2384         
2385         if (!text) return 0;
2386         if (!text->curl) return 0;
2387
2388         if (add=='\n') {
2389                 txt_split_curline(text);
2390                 return 1;
2391         }
2392         
2393         /* insert spaces rather then tabs */
2394         if (add == '\t' && text->flags & TXT_TABSTOSPACES) {
2395                 txt_convert_tab_to_spaces(text);
2396                 return 1;
2397         }
2398
2399         txt_delete_sel(text);
2400         
2401         mrk= txt_find_marker_region(text, text->curl, text->curc-1, text->curl->len, 0, 0);
2402         if (mrk) {
2403                 lineno= mrk->lineno;
2404                 do {
2405                         if (mrk->start>text->curc) mrk->start++;
2406                         mrk->end++;
2407                         mrk= mrk->next;
2408                 } while (mrk && mrk->lineno==lineno);
2409         }
2410         
2411         tmp= MEM_mallocN(text->curl->len+2, "textline_string");
2412         
2413         if(text->curc) memcpy(tmp, text->curl->line, text->curc);
2414         tmp[text->curc]= add;
2415         
2416         len= text->curl->len - text->curc;
2417         if(len>0) memcpy(tmp+text->curc+1, text->curl->line+text->curc, len);
2418         tmp[text->curl->len+1]=0;
2419         make_new_line(text->curl, tmp);
2420                 
2421         text->curc++;
2422
2423         txt_pop_sel(text);
2424         
2425         txt_make_dirty(text);
2426         txt_clean_text(text);
2427
2428         if(!undoing) txt_undo_add_charop(text, UNDO_INSERT, add);
2429         return 1;
2430 }
2431
2432 void txt_delete_selected(Text *text)
2433 {
2434         txt_delete_sel(text);
2435         txt_make_dirty(text);
2436 }
2437
2438 int txt_replace_char (Text *text, char add)
2439 {
2440         char del;
2441         
2442         if (!text) return 0;
2443         if (!text->curl) return 0;
2444
2445         /* If text is selected or we're at the end of the line just use txt_add_char */
2446         if (text->curc==text->curl->len || txt_has_sel(text) || add=='\n') {
2447                 TextMarker *mrk;
2448                 int i= txt_add_char(text, add);
2449                 mrk= txt_find_marker(text, text->curl, text->curc, 0, 0);
2450                 if (mrk && mrk->end==text->curc) mrk->end--;
2451                 return i;
2452         }
2453         
2454         del= text->curl->line[text->curc];
2455         text->curl->line[text->curc]= (unsigned char) add;
2456         text->curc++;
2457         txt_pop_sel(text);
2458         
2459         txt_make_dirty(text);
2460         txt_clean_text(text);
2461
2462         /* Should probably create a new op for this */
2463         if(!undoing) {
2464                 txt_undo_add_charop(text, UNDO_DEL, del);
2465                 txt_undo_add_charop(text, UNDO_INSERT, add);
2466         }
2467         return 1;
2468 }
2469
2470 void indent(Text *text)
2471 {
2472         int len, num;
2473         char *tmp;
2474
2475         const char *add = "\t";
2476         int indentlen = 1;
2477         
2478         /* hardcoded: TXT_TABSIZE = 4 spaces: */
2479         int spaceslen = TXT_TABSIZE;
2480
2481         /* insert spaces rather then tabs */
2482         if (text->flags & TXT_TABSTOSPACES){
2483                 add = tab_to_spaces;
2484                 indentlen = spaceslen;
2485         }
2486         
2487         if (!text) return;
2488         if (!text->curl) return;
2489         if (!text->sell) return;
2490
2491         num = 0;
2492         while (TRUE)
2493         {
2494                 tmp= MEM_mallocN(text->curl->len+indentlen+1, "textline_string");
2495                 
2496                 text->curc = 0; 
2497                 if(text->curc) memcpy(tmp, text->curl->line, text->curc); /* XXX never true, check prev line */
2498                 memcpy(tmp+text->curc, add, indentlen);
2499                 
2500                 len= text->curl->len - text->curc;
2501                 if(len>0) memcpy(tmp+text->curc+indentlen, text->curl->line+text->curc, len);
2502                 tmp[text->curl->len+indentlen]= 0;
2503
2504                 make_new_line(text->curl, tmp);
2505                         
2506                 text->curc+= indentlen;
2507                 
2508                 txt_make_dirty(text);
2509                 txt_clean_text(text);
2510                 
2511                 if(text->curl == text->sell) 
2512                 {
2513                         text->selc = text->sell->len;
2514                         break;
2515                 } else {
2516                         text->curl = text->curl->next;
2517                         num++;
2518                 }
2519         }
2520         text->curc = 0;
2521         while( num > 0 )
2522         {
2523                 text->curl = text->curl->prev;
2524                 num--;
2525         }
2526         
2527         if(!undoing) 
2528         {
2529                 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);
2530         }
2531 }
2532
2533 void unindent(Text *text)
2534 {
2535         int num = 0;
2536         const char *remove = "\t";
2537         int indent = 1;
2538         
2539         /* hardcoded: TXT_TABSIZE = 4 spaces: */
2540         int spaceslen = TXT_TABSIZE;
2541
2542         /* insert spaces rather then tabs */
2543         if (text->flags & TXT_TABSTOSPACES){
2544                 remove = tab_to_spaces;
2545                 indent = spaceslen;
2546         }
2547
2548         if (!text) return;
2549         if (!text->curl) return;
2550         if (!text->sell) return;
2551
2552         while(TRUE)
2553         {
2554                 int i = 0;
2555                 
2556                 if (BLI_strncasecmp(text->curl->line, remove, indent) == 0)
2557                 {
2558                         while(i< text->curl->len) {
2559                                 text->curl->line[i]= text->curl->line[i+indent];
2560                                 i++;
2561                         }
2562                         text->curl->len-= indent;
2563                 }
2564         
2565                 txt_make_dirty(text);
2566                 txt_clean_text(text);
2567                 
2568                 if(text->curl == text->sell) 
2569                 {
2570                         text->selc = text->sell->len;
2571                         break;
2572                 } else {
2573                         text->curl = text->curl->next;
2574                         num++;
2575                 }
2576                 
2577         }
2578         text->curc = 0;
2579         while( num > 0 )
2580         {
2581                 text->curl = text->curl->prev;
2582                 num--;
2583         }
2584         
2585         if(!undoing) 
2586         {
2587                 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);
2588         }
2589 }
2590
2591 void comment(Text *text)
2592 {
2593         int len, num;
2594         char *tmp;
2595         char add = '#';
2596         
2597         if (!text) return;
2598         if (!text->curl) return;
2599         if (!text->sell) return;// Need to change this need to check if only one line is selected ot more then one
2600
2601         num = 0;
2602         while (TRUE)
2603         {
2604                 tmp= MEM_mallocN(text->curl->len+2, "textline_string");
2605                 
2606                 text->curc = 0; 
2607                 if(text->curc) memcpy(tmp, text->curl->line, text->curc);
2608                 tmp[text->curc]= add;
2609                 
2610                 len= text->curl->len - text->curc;
2611                 if(len>0) memcpy(tmp+text->curc+1, text->curl->line+text->curc, len);
2612                 tmp[text->curl->len+1]=0;
2613
2614                 make_new_line(text->curl, tmp);
2615                         
2616                 text->curc++;
2617                 
2618                 txt_make_dirty(text);
2619                 txt_clean_text(text);
2620                 
2621                 if(text->curl == text->sell) 
2622                 {
2623                         text->selc = text->sell->len;
2624                         break;
2625                 } else {
2626                         text->curl = text->curl->next;
2627                         num++;
2628                 }
2629         }
2630         text->curc = 0;
2631         while( num > 0 )
2632         {
2633                 text->curl = text->curl->prev;
2634                 num--;
2635         }
2636         
2637         if(!undoing) 
2638         {
2639                 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);
2640         }
2641 }
2642
2643 void uncomment(Text *text)
2644 {
2645         int num = 0;
2646         char remove = '#';
2647         
2648         if (!text) return;
2649         if (!text->curl) return;
2650         if (!text->sell) return;
2651
2652         while(TRUE)
2653         {
2654                 int i = 0;
2655                 
2656                 if (text->curl->line[i] == remove)
2657                 {
2658                         while(i< text->curl->len) {
2659                                 text->curl->line[i]= text->curl->line[i+1];
2660                                 i++;
2661                         }
2662                         text->curl->len--;
2663                 }
2664                          
2665         
2666                 txt_make_dirty(text);
2667                 txt_clean_text(text);
2668                 
2669                 if(text->curl == text->sell) 
2670                 {
2671                         text->selc = text->sell->len;
2672                         break;
2673                 } else {
2674                         text->curl = text->curl->next;
2675                         num++;
2676                 }
2677                 
2678         }
2679         text->curc = 0;
2680         while( num > 0 )
2681         {
2682                 text->curl = text->curl->prev;
2683                 num--;
2684         }
2685         
2686         if(!undoing) 
2687         {
2688                 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);
2689         }
2690 }
2691
2692 int setcurr_tab_spaces (Text *text, int space)
2693 {
2694         int i = 0;
2695         int test = 0;
2696         const char *word = ":";
2697         const char *comm = "#";
2698         const char indent= (text->flags & TXT_TABSTOSPACES) ? ' ' : '\t';
2699         static const char *back_words[]= {"return", "break", "continue", "pass", "yield", NULL};
2700         if (!text) return 0;
2701         if (!text->curl) return 0;
2702
2703         while (text->curl->line[i] == indent)
2704         {
2705                 //we only count those tabs/spaces that are before any text or before the curs;
2706                 if (i == text->curc)
2707                 {
2708                         return i;
2709                 } else {
2710                         i++;
2711                 }
2712         }
2713         if(strstr(text->curl->line, word))
2714         {
2715                 /* if we find a ':' on this line, then add a tab but not if it is:
2716                  *      1) in a comment
2717                  *      2) within an identifier
2718                  *      3) after the cursor (text->curc), i.e. when creating space before a function def [#25414] 
2719                  */
2720                 int a, indent = 0;
2721                 for(a=0; (a < text->curc) && (text->curl->line[a] != '\0'); a++)
2722                 {
2723                         if (text->curl->line[a]=='#') {
2724                                 break;
2725                         } else if (text->curl->line[a]==':') {
2726                                 indent = 1;
2727                         } else if (text->curl->line[a]==']') {
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 }