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