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