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