Fix #28087: Opening files in the text editor ignores the last newline '\n'
[blender.git] / source / blender / blenkernel / intern / text.c
1 /* text.c
2  *
3  *
4  * $Id$
5  *
6  * ***** BEGIN GPL LICENSE BLOCK *****
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software Foundation,
20  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21  *
22  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
23  * All rights reserved.
24  *
25  * The Original Code is: all of this file.
26  *
27  * Contributor(s): none yet.
28  *
29  * ***** END GPL LICENSE BLOCK *****
30  */
31
32 /** \file blender/blenkernel/intern/text.c
33  *  \ingroup bke
34  */
35
36
37 #include <string.h> /* strstr */
38 #include <sys/types.h>
39 #include <sys/stat.h>
40
41 #include "MEM_guardedalloc.h"
42
43 #include "BLI_blenlib.h"
44 #include "BLI_utildefines.h"
45
46 #include "DNA_constraint_types.h"
47 #include "DNA_controller_types.h"
48 #include "DNA_scene_types.h"
49 #include "DNA_screen_types.h"
50 #include "DNA_space_types.h"
51 #include "DNA_text_types.h"
52 #include "DNA_userdef_types.h"
53 #include "DNA_object_types.h"
54
55 #include "BKE_depsgraph.h"
56 #include "BKE_global.h"
57 #include "BKE_library.h"
58 #include "BKE_main.h"
59 #include "BKE_text.h"
60
61
62 #ifdef WITH_PYTHON
63 #include "BPY_extern.h"
64 #endif
65
66 /***************/ /*
67
68 How Texts should work
69 --
70 A text should relate to a file as follows -
71 (Text *)->name should be the place where the 
72         file will or has been saved.
73         
74 (Text *)->flags has the following bits
75         TXT_ISDIRTY - should always be set if the file in mem. differs from
76                                         the file on disk, or if there is no file on disk.
77         TXT_ISMEM - should always be set if the Text has not been mapped to
78                                         a file, in which case (Text *)->name may be NULL or garbage.                    
79         TXT_ISEXT - should always be set if the Text is not to be written into
80                                         the .blend
81         TXT_ISSCRIPT - should be set if the user has designated the text
82                                         as a script. (NEW: this was unused, but now it is needed by
83                                         space handler script links (see header_view3d.c, for example)
84
85 ->>> see also: /makesdna/DNA_text_types.h
86
87 Display
88 --
89 The st->top determines at what line the top of the text is displayed.
90 If the user moves the cursor the st containing that cursor should
91 be popped ... other st's retain their own top location.
92
93 Markers
94 --
95 The mrk->flags define the behaviour and relationships between markers. The
96 upper two bytes are used to hold a group ID, the lower two are normal flags. If
97 TMARK_EDITALL is set the group ID defines which other markers should be edited.
98
99 The mrk->clr field is used to visually group markers where the flags may not
100 match. A template system, for example, may allow editing of repeating tokens
101 (in one group) but include other marked positions (in another group) all in the
102 same template with the same color.
103
104 Undo
105 --
106 Undo/Redo works by storing
107 events in a queue, and a pointer
108 to the current position in the
109 queue...
110
111 Events are stored using an
112 arbitrary op-code system
113 to keep track of
114 a) the two cursors (normal and selected)
115 b) input (visible and control (ie backspace))
116
117 input data is stored as its
118 ASCII value, the opcodes are
119 then selected to not conflict.
120
121 opcodes with data in between are
122 written at the beginning and end
123 of the data to allow undo and redo
124 to simply check the code at the current
125 undo position
126
127 */ /***************/
128
129 /***/
130
131 static void txt_pop_first(Text *text);
132 static void txt_pop_last(Text *text);
133 static void txt_undo_add_op(Text *text, int op);
134 static void txt_undo_add_block(Text *text, int op, const char *buf);
135 static void txt_delete_line(Text *text, TextLine *line);
136 static void txt_delete_sel (Text *text);
137 static void txt_make_dirty (Text *text);
138
139 /***/
140
141 static unsigned char undoing;
142
143 /* allow to switch off undoing externally */
144 void txt_set_undostate(int u)
145 {
146         undoing = u;
147 }
148
149 int txt_get_undostate(void)
150 {
151         return undoing;
152 }
153
154 static void init_undo_text(Text *text)
155 {
156         text->undo_pos= -1;
157         text->undo_len= TXT_INIT_UNDO;
158         text->undo_buf= MEM_mallocN(text->undo_len, "undo buf");
159 }
160
161 void free_text(Text *text)
162 {
163         TextLine *tmp;
164
165         for (tmp= text->lines.first; tmp; tmp= tmp->next) {
166                 MEM_freeN(tmp->line);
167                 if (tmp->format)
168                   MEM_freeN(tmp->format);
169         }
170         
171         BLI_freelistN(&text->lines);
172         BLI_freelistN(&text->markers);
173
174         if(text->name) MEM_freeN(text->name);
175         MEM_freeN(text->undo_buf);
176 #ifdef WITH_PYTHON
177         if (text->compiled) BPY_text_free_code(text);
178 #endif
179 }
180
181 Text *add_empty_text(const char *name) 
182 {
183         Main *bmain= G.main;
184         Text *ta;
185         TextLine *tmp;
186         
187         ta= alloc_libblock(&bmain->text, ID_TXT, name);
188         ta->id.us= 1;
189         
190         ta->name= NULL;
191
192         init_undo_text(ta);
193
194         ta->nlines=1;
195         ta->flags= TXT_ISDIRTY | TXT_ISMEM;
196         if((U.flag & USER_TXT_TABSTOSPACES_DISABLE)==0)
197                 ta->flags |= TXT_TABSTOSPACES;
198
199         ta->lines.first= ta->lines.last= NULL;
200         ta->markers.first= ta->markers.last= NULL;
201
202         tmp= (TextLine*) MEM_mallocN(sizeof(TextLine), "textline");
203         tmp->line= (char*) MEM_mallocN(1, "textline_string");
204         tmp->format= NULL;
205         
206         tmp->line[0]=0;
207         tmp->len= 0;
208                                 
209         tmp->next= NULL;
210         tmp->prev= NULL;
211                                 
212         BLI_addhead(&ta->lines, tmp);
213         
214         ta->curl= ta->lines.first;
215         ta->curc= 0;
216         ta->sell= ta->lines.first;
217         ta->selc= 0;
218
219         return ta;
220 }
221
222 // this function removes any control characters from
223 // a textline
224
225 static void cleanup_textline(TextLine * tl)
226 {
227         int i;
228
229         for (i = 0; i < tl->len; i++ ) {
230                 if (tl->line[i] < ' ' && tl->line[i] != '\t') {
231                         memmove(tl->line + i, tl->line + i + 1, tl->len - i);
232                         tl->len--;
233                         i--;
234                 }
235         }
236 }
237
238 int reopen_text(Text *text)
239 {
240         FILE *fp;
241         int i, llen, len;
242         unsigned char *buffer;
243         TextLine *tmp;
244         char str[FILE_MAXDIR+FILE_MAXFILE];
245         struct stat st;
246
247         if (!text || !text->name) return 0;
248         
249         BLI_strncpy(str, text->name, FILE_MAXDIR+FILE_MAXFILE);
250         BLI_path_abs(str, G.main->name);
251         
252         fp= fopen(str, "r");
253         if(fp==NULL) return 0;
254
255         /* free memory: */
256
257         for (tmp= text->lines.first; tmp; tmp= tmp->next) {
258                 MEM_freeN(tmp->line);
259                 if (tmp->format) MEM_freeN(tmp->format);
260         }
261         
262         BLI_freelistN(&text->lines);
263
264         text->lines.first= text->lines.last= NULL;
265         text->curl= text->sell= NULL;
266
267         /* clear undo buffer */
268         MEM_freeN(text->undo_buf);
269         init_undo_text(text);
270         
271         fseek(fp, 0L, SEEK_END);
272         len= ftell(fp);
273         fseek(fp, 0L, SEEK_SET);        
274
275         text->undo_pos= -1;
276         
277         buffer= MEM_mallocN(len, "text_buffer");
278         // under windows fread can return less then len bytes because
279         // of CR stripping
280         len = fread(buffer, 1, len, fp);
281
282         fclose(fp);
283
284         stat(str, &st);
285         text->mtime= st.st_mtime;
286         
287         text->nlines=0;
288         llen=0;
289         for(i=0; i<len; i++) {
290                 if (buffer[i]=='\n') {
291                         tmp= (TextLine*) MEM_mallocN(sizeof(TextLine), "textline");
292                         tmp->line= (char*) MEM_mallocN(llen+1, "textline_string");
293                         tmp->format= NULL;
294                         
295                         if(llen) memcpy(tmp->line, &buffer[i-llen], llen);
296                         tmp->line[llen]=0;
297                         tmp->len= llen;
298                                 
299                         cleanup_textline(tmp);
300
301                         BLI_addtail(&text->lines, tmp);
302                         text->nlines++;
303                                 
304                         llen=0;
305                         continue;
306                 }
307                 llen++;
308         }
309
310         if (llen!=0 || text->nlines==0) {
311                 tmp= (TextLine*) MEM_mallocN(sizeof(TextLine), "textline");
312                 tmp->line= (char*) MEM_mallocN(llen+1, "textline_string");
313                 tmp->format= NULL;
314                 
315                 if(llen) memcpy(tmp->line, &buffer[i-llen], llen);
316
317                 tmp->line[llen]=0;
318                 tmp->len= llen;
319                 
320                 cleanup_textline(tmp);
321
322                 BLI_addtail(&text->lines, tmp);
323                 text->nlines++;
324         }
325         
326         text->curl= text->sell= text->lines.first;
327         text->curc= text->selc= 0;
328         
329         MEM_freeN(buffer);      
330         return 1;
331 }
332
333 Text *add_text(const char *file, const char *relpath) 
334 {
335         Main *bmain= G.main;
336         FILE *fp;
337         int i, llen, len;
338         unsigned char *buffer;
339         TextLine *tmp;
340         Text *ta;
341         char str[FILE_MAXDIR+FILE_MAXFILE];
342         struct stat st;
343
344         BLI_strncpy(str, file, FILE_MAXDIR+FILE_MAXFILE);
345         if (relpath) /* can be NULL (bg mode) */
346                 BLI_path_abs(str, relpath);
347         
348         fp= fopen(str, "r");
349         if(fp==NULL) return NULL;
350         
351         ta= alloc_libblock(&bmain->text, ID_TXT, BLI_path_basename(str));
352         ta->id.us= 1;
353
354         ta->lines.first= ta->lines.last= NULL;
355         ta->markers.first= ta->markers.last= NULL;
356         ta->curl= ta->sell= NULL;
357
358         if((U.flag & USER_TXT_TABSTOSPACES_DISABLE)==0)
359                 ta->flags= TXT_TABSTOSPACES;
360
361         fseek(fp, 0L, SEEK_END);
362         len= ftell(fp);
363         fseek(fp, 0L, SEEK_SET);        
364
365         ta->name= MEM_mallocN(strlen(file)+1, "text_name");
366         strcpy(ta->name, file);
367
368         init_undo_text(ta);
369         
370         buffer= MEM_mallocN(len, "text_buffer");
371         // under windows fread can return less then len bytes because
372         // of CR stripping
373         len = fread(buffer, 1, len, fp);
374
375         fclose(fp);
376
377         stat(str, &st);
378         ta->mtime= st.st_mtime;
379         
380         ta->nlines=0;
381         llen=0;
382         for(i=0; i<len; i++) {
383                 if (buffer[i]=='\n') {
384                         tmp= (TextLine*) MEM_mallocN(sizeof(TextLine), "textline");
385                         tmp->line= (char*) MEM_mallocN(llen+1, "textline_string");
386                         tmp->format= NULL;
387                         
388                         if(llen) memcpy(tmp->line, &buffer[i-llen], llen);
389                         tmp->line[llen]=0;
390                         tmp->len= llen;
391                         
392                         cleanup_textline(tmp);
393
394                         BLI_addtail(&ta->lines, tmp);
395                         ta->nlines++;
396                                 
397                         llen=0;
398                         continue;
399                 }
400                 llen++;
401         }
402
403         /* create new line in cases:
404            - rest of line (if last line in file hasn't got \n terminator).
405              in this case content of such line would be used to fill text line buffer
406            - file is empty. in this case new line is needed to start editing from.
407            - last characted in buffer is \n. in this case new line is needed to
408              deal with newline at end of file. (see [#28087]) (sergey) */
409         if (llen!=0 || ta->nlines==0 || buffer[len-1]=='\n') {
410                 tmp= (TextLine*) MEM_mallocN(sizeof(TextLine), "textline");
411                 tmp->line= (char*) MEM_mallocN(llen+1, "textline_string");
412                 tmp->format= NULL;
413                 
414                 if(llen) memcpy(tmp->line, &buffer[i-llen], llen);
415
416                 tmp->line[llen]=0;
417                 tmp->len= llen;
418                 
419                 cleanup_textline(tmp);
420
421                 BLI_addtail(&ta->lines, tmp);
422                 ta->nlines++;
423         }
424         
425         ta->curl= ta->sell= ta->lines.first;
426         ta->curc= ta->selc= 0;
427         
428         MEM_freeN(buffer);      
429
430         return ta;
431 }
432
433 Text *copy_text(Text *ta)
434 {
435         Text *tan;
436         TextLine *line, *tmp;
437         
438         tan= copy_libblock(ta);
439         
440         /* file name can be NULL */
441         if(ta->name) {
442                 tan->name= MEM_mallocN(strlen(ta->name)+1, "text_name");
443                 strcpy(tan->name, ta->name);
444         }
445         else {
446                 tan->name= NULL;
447         }
448
449         tan->flags = ta->flags | TXT_ISDIRTY;
450         
451         tan->lines.first= tan->lines.last= NULL;
452         tan->markers.first= tan->markers.last= NULL;
453         tan->curl= tan->sell= NULL;
454         
455         tan->nlines= ta->nlines;
456
457         line= ta->lines.first;  
458         /* Walk down, reconstructing */
459         while (line) {
460                 tmp= (TextLine*) MEM_mallocN(sizeof(TextLine), "textline");
461                 tmp->line= MEM_mallocN(line->len+1, "textline_string");
462                 tmp->format= NULL;
463                 
464                 strcpy(tmp->line, line->line);
465
466                 tmp->len= line->len;
467                 
468                 BLI_addtail(&tan->lines, tmp);
469                 
470                 line= line->next;
471         }
472
473         tan->curl= tan->sell= tan->lines.first;
474         tan->curc= tan->selc= 0;
475
476         init_undo_text(tan);
477
478         return tan;
479 }
480
481 void unlink_text(Main *bmain, Text *text)
482 {
483         bScreen *scr;
484         ScrArea *area;
485         SpaceLink *sl;
486         Scene *scene;
487         Object *ob;
488         bController *cont;
489         bConstraint *con;
490         short update;
491
492         /* dome */
493         for(scene=bmain->scene.first; scene; scene=scene->id.next)
494                 if(scene->r.dometext == text)
495                         scene->r.dometext = NULL;
496
497         for(ob=bmain->object.first; ob; ob=ob->id.next) {
498                 /* game controllers */
499                 for(cont=ob->controllers.first; cont; cont=cont->next) {
500                         if(cont->type==CONT_PYTHON) {
501                                 bPythonCont *pc;
502                                 
503                                 pc= cont->data;
504                                 if(pc->text==text) pc->text= NULL;
505                         }
506                 }
507
508                 /* pyconstraints */
509                 update = 0;
510
511                 if(ob->type==OB_ARMATURE && ob->pose) {
512                         bPoseChannel *pchan;
513                         for(pchan= ob->pose->chanbase.first; pchan; pchan= pchan->next) {
514                                 for(con = pchan->constraints.first; con; con=con->next) {
515                                         if(con->type==CONSTRAINT_TYPE_PYTHON) {
516                                                 bPythonConstraint *data = con->data;
517                                                 if (data->text==text) data->text = NULL;
518                                                 update = 1;
519                                                 
520                                         }
521                                 }
522                         }
523                 }
524
525                 for(con = ob->constraints.first; con; con=con->next) {
526                         if(con->type==CONSTRAINT_TYPE_PYTHON) {
527                                 bPythonConstraint *data = con->data;
528                                 if (data->text==text) data->text = NULL;
529                                 update = 1;
530                         }
531                 }
532                 
533                 if(update)
534                         DAG_id_tag_update(&ob->id, OB_RECALC_DATA);
535         }
536
537         /* pynodes */
538         // XXX nodeDynamicUnlinkText(&text->id);
539         
540         /* text space */
541         for(scr= bmain->screen.first; scr; scr= scr->id.next) {
542                 for(area= scr->areabase.first; area; area= area->next) {
543                         for(sl= area->spacedata.first; sl; sl= sl->next) {
544                                 if(sl->spacetype==SPACE_TEXT) {
545                                         SpaceText *st= (SpaceText*) sl;
546                                         
547                                         if(st->text==text) {
548                                                 st->text= NULL;
549                                                 st->top= 0;
550                                         }
551                                 }
552                         }
553                 }
554         }
555
556         text->id.us= 0;
557 }
558
559 void clear_text(Text *text) /* called directly from rna */
560 {
561         int oldstate;
562
563         oldstate = txt_get_undostate(  );
564         txt_set_undostate( 1 );
565         txt_sel_all( text );
566         txt_delete_sel(text);
567         txt_set_undostate( oldstate );
568
569         txt_make_dirty(text);
570 }
571
572 void write_text(Text *text, const char *str) /* called directly from rna */
573 {
574         int oldstate;
575
576         oldstate = txt_get_undostate(  );
577         txt_insert_buf( text, str );
578         txt_move_eof( text, 0 );
579         txt_set_undostate( oldstate );
580
581         txt_make_dirty(text);
582 }
583
584 /*****************************/
585 /* Editing utility functions */
586 /*****************************/
587
588 static void make_new_line (TextLine *line, char *newline) 
589 {
590         if (line->line) MEM_freeN(line->line);
591         if (line->format) MEM_freeN(line->format);
592         
593         line->line= newline;
594         line->len= strlen(newline);
595         line->format= NULL;
596 }
597
598 static TextLine *txt_new_line(const char *str)
599 {
600         TextLine *tmp;
601
602         if(!str) str= "";
603         
604         tmp= (TextLine *) MEM_mallocN(sizeof(TextLine), "textline");
605         tmp->line= MEM_mallocN(strlen(str)+1, "textline_string");
606         tmp->format= NULL;
607         
608         strcpy(tmp->line, str);
609         
610         tmp->len= strlen(str);
611         tmp->next= tmp->prev= NULL;
612         
613         return tmp;
614 }
615
616 static TextLine *txt_new_linen(const char *str, int n)
617 {
618         TextLine *tmp;
619
620         tmp= (TextLine *) MEM_mallocN(sizeof(TextLine), "textline");
621         tmp->line= MEM_mallocN(n+1, "textline_string");
622         tmp->format= NULL;
623         
624         BLI_strncpy(tmp->line, (str)? str: "", n+1);
625         
626         tmp->len= strlen(tmp->line);
627         tmp->next= tmp->prev= NULL;
628         
629         return tmp;
630 }
631
632 void txt_clean_text (Text *text) 
633 {       
634         TextLine **top, **bot;
635         
636         if (!text) return;
637         
638         if (!text->lines.first) {
639                 if (text->lines.last) text->lines.first= text->lines.last;
640                 else text->lines.first= text->lines.last= txt_new_line(NULL);
641         } 
642         
643         if (!text->lines.last) text->lines.last= text->lines.first;
644
645         top= (TextLine **) &text->lines.first;
646         bot= (TextLine **) &text->lines.last;
647         
648         while ((*top)->prev) *top= (*top)->prev;
649         while ((*bot)->next) *bot= (*bot)->next;
650
651         if(!text->curl) {
652                 if(text->sell) text->curl= text->sell;
653                 else text->curl= text->lines.first;
654                 text->curc= 0;
655         }
656
657         if(!text->sell) {
658                 text->sell= text->curl;
659                 text->selc= 0;
660         }
661 }
662
663 int txt_get_span (TextLine *from, TextLine *to)
664 {
665         int ret=0;
666         TextLine *tmp= from;
667
668         if (!to || !from) return 0;
669         if (from==to) return 0;
670
671         /* Look forwards */
672         while (tmp) {
673                 if (tmp == to) return ret;
674                 ret++;
675                 tmp= tmp->next;
676         }
677
678         /* Look backwards */
679         if (!tmp) {
680                 tmp= from;
681                 ret=0;
682                 while(tmp) {
683                         if (tmp == to) break;
684                         ret--;
685                         tmp= tmp->prev;
686                 }
687                 if(!tmp) ret=0;
688         }
689
690         return ret;     
691 }
692
693 static void txt_make_dirty (Text *text)
694 {
695         text->flags |= TXT_ISDIRTY;
696 #ifdef WITH_PYTHON
697         if (text->compiled) BPY_text_free_code(text);
698 #endif
699 }
700
701 /* 0:whitespace, 1:punct, 2:alphanumeric */
702 static short txt_char_type (char ch)
703 {
704         if (ch <= ' ') return 0; /* 32 */
705         if (ch <= '/') return 1; /* 47 */
706         if (ch <= '9') return 2; /* 57 */
707         if (ch <= '@') return 1; /* 64 */
708         if (ch <= 'Z') return 2; /* 90 */
709         if (ch == '_') return 2; /* 95, dont delimit '_' */
710         if (ch <= '`') return 1; /* 96 */
711         if (ch <= 'z') return 2; /* 122 */
712         return 1;
713 }
714
715 /****************************/
716 /* Cursor utility functions */
717 /****************************/
718
719 static void txt_curs_cur (Text *text, TextLine ***linep, int **charp)
720 {
721         *linep= &text->curl; *charp= &text->curc;
722 }
723
724 static void txt_curs_sel (Text *text, TextLine ***linep, int **charp)
725 {
726         *linep= &text->sell; *charp= &text->selc;
727 }
728
729 static void txt_curs_first (Text *text, TextLine **linep, int *charp)
730 {
731         if (text->curl==text->sell) {
732                 *linep= text->curl;
733                 if (text->curc<text->selc) *charp= text->curc;
734                 else *charp= text->selc;
735         } else if (txt_get_span(text->lines.first, text->curl)<txt_get_span(text->lines.first, text->sell)) {
736                 *linep= text->curl;
737                 *charp= text->curc;
738         } else {
739                 *linep= text->sell;
740                 *charp= text->selc;             
741         }
742 }
743
744 /****************************/
745 /* Cursor movement functions */
746 /****************************/
747
748 void txt_move_up(Text *text, short sel)
749 {
750         TextLine **linep;
751         int *charp, old;
752         
753         if (!text) return;
754         if(sel) txt_curs_sel(text, &linep, &charp);
755         else { txt_pop_first(text); txt_curs_cur(text, &linep, &charp); }
756         if (!*linep) return;
757         old= *charp;
758
759         if((*linep)->prev) {
760                 *linep= (*linep)->prev;
761                 if (*charp > (*linep)->len) {
762                         *charp= (*linep)->len;
763                         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);
764                 } else {
765                         if(!undoing) txt_undo_add_op(text, sel?UNDO_SUP:UNDO_CUP);
766                 }
767         } else {
768                 txt_move_bol(text, sel);
769         }
770
771         if(!sel) txt_pop_sel(text);
772 }
773
774 void txt_move_down(Text *text, short sel) 
775 {
776         TextLine **linep;
777         int *charp, old;
778         
779         if (!text) return;
780         if(sel) txt_curs_sel(text, &linep, &charp);
781         else { txt_pop_last(text); txt_curs_cur(text, &linep, &charp); }
782         if (!*linep) return;
783         old= *charp;
784
785         if((*linep)->next) {
786                 *linep= (*linep)->next;
787                 if (*charp > (*linep)->len) {
788                         *charp= (*linep)->len;
789                         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);
790                 } else
791                         if(!undoing) txt_undo_add_op(text, sel?UNDO_SDOWN:UNDO_CDOWN);  
792         } else {
793                 txt_move_eol(text, sel);
794         }
795
796         if(!sel) txt_pop_sel(text);
797 }
798
799 void txt_move_left(Text *text, short sel) 
800 {
801         TextLine **linep;
802         int *charp, oundoing= undoing;
803         
804         if (!text) return;
805         if(sel) txt_curs_sel(text, &linep, &charp);
806         else { txt_pop_first(text); txt_curs_cur(text, &linep, &charp); }
807         if (!*linep) return;
808
809         undoing= 1;
810         if (*charp== 0) {
811                 if ((*linep)->prev) {
812                         txt_move_up(text, sel);
813                         *charp= (*linep)->len;
814                 }
815         } else {
816                 (*charp)--;
817         }
818         undoing= oundoing;
819         if(!undoing) txt_undo_add_op(text, sel?UNDO_SLEFT:UNDO_CLEFT);
820         
821         if(!sel) txt_pop_sel(text);
822 }
823
824 void txt_move_right(Text *text, short sel) 
825 {
826         TextLine **linep;
827         int *charp, oundoing= undoing;
828         
829         if (!text) return;
830         if(sel) txt_curs_sel(text, &linep, &charp);
831         else { txt_pop_last(text); txt_curs_cur(text, &linep, &charp); }
832         if (!*linep) return;
833
834         undoing= 1;
835         if (*charp== (*linep)->len) {
836                 if ((*linep)->next) {
837                         txt_move_down(text, sel);
838                         *charp= 0;
839                 }
840         } else {
841                 (*charp)++;
842         }
843         undoing= oundoing;
844         if(!undoing) txt_undo_add_op(text, sel?UNDO_SRIGHT:UNDO_CRIGHT);
845
846         if(!sel) txt_pop_sel(text);
847 }
848
849 void txt_jump_left(Text *text, short sel)
850 {
851         TextLine **linep, *oldl;
852         int *charp, oldc, count, i;
853         unsigned char oldu;
854
855         if (!text) return;
856         if(sel) txt_curs_sel(text, &linep, &charp);
857         else { txt_pop_first(text); txt_curs_cur(text, &linep, &charp); }
858         if (!*linep) return;
859
860         oldl= *linep;
861         oldc= *charp;
862         oldu= undoing;
863         undoing= 1; /* Don't push individual moves to undo stack */
864
865         count= 0;
866         for (i=0; i<3; i++) {
867                 if (count < 2) {
868                         while (*charp>0 && txt_char_type((*linep)->line[*charp-1])==i) {
869                                 txt_move_left(text, sel);
870                                 count++;
871                         }
872                 }
873         }
874         if (count==0) txt_move_left(text, sel);
875
876         undoing= oldu;
877         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);
878 }
879
880 void txt_jump_right(Text *text, short sel)
881 {
882         TextLine **linep, *oldl;
883         int *charp, oldc, count, i;
884         unsigned char oldu;
885
886         if (!text) return;
887         if(sel) txt_curs_sel(text, &linep, &charp);
888         else { txt_pop_last(text); txt_curs_cur(text, &linep, &charp); }
889         if (!*linep) return;
890
891         oldl= *linep;
892         oldc= *charp;
893         oldu= undoing;
894         undoing= 1; /* Don't push individual moves to undo stack */
895
896         count= 0;
897         for (i=0; i<3; i++) {
898                 if (count < 2) {
899                         while (*charp<(*linep)->len && txt_char_type((*linep)->line[*charp])==i) {
900                                 txt_move_right(text, sel);
901                                 count++;
902                         }
903                 }
904         }
905         if (count==0) txt_move_right(text, sel);
906
907         undoing= oldu;
908         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);
909 }
910
911 void txt_move_bol (Text *text, short sel) 
912 {
913         TextLine **linep;
914         int *charp, old;
915         
916         if (!text) return;
917         if(sel) txt_curs_sel(text, &linep, &charp);
918         else txt_curs_cur(text, &linep, &charp);
919         if (!*linep) return;
920         old= *charp;
921         
922         *charp= 0;
923
924         if(!sel) txt_pop_sel(text);
925         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);
926 }
927
928 void txt_move_eol (Text *text, short sel) 
929 {
930         TextLine **linep;
931         int *charp, old;
932         
933         if (!text) return;
934         if(sel) txt_curs_sel(text, &linep, &charp);
935         else txt_curs_cur(text, &linep, &charp);
936         if (!*linep) return;
937         old= *charp;
938                 
939         *charp= (*linep)->len;
940
941         if(!sel) txt_pop_sel(text);
942         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);
943 }
944
945 void txt_move_bof (Text *text, short sel)
946 {
947         TextLine **linep;
948         int *charp, old;
949         
950         if (!text) return;
951         if(sel) txt_curs_sel(text, &linep, &charp);
952         else txt_curs_cur(text, &linep, &charp);
953         if (!*linep) return;
954         old= *charp;
955
956         *linep= text->lines.first;
957         *charp= 0;
958
959         if(!sel) txt_pop_sel(text);
960         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);
961 }
962
963 void txt_move_eof (Text *text, short sel)
964 {
965         TextLine **linep;
966         int *charp, old;
967         
968         if (!text) return;
969         if(sel) txt_curs_sel(text, &linep, &charp);
970         else txt_curs_cur(text, &linep, &charp);
971         if (!*linep) return;
972         old= *charp;
973
974         *linep= text->lines.last;
975         *charp= (*linep)->len;
976
977         if(!sel) txt_pop_sel(text);
978         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);     
979 }
980
981 void txt_move_toline (Text *text, unsigned int line, short sel)
982 {
983         txt_move_to(text, line, 0, sel);
984 }
985
986 void txt_move_to (Text *text, unsigned int line, unsigned int ch, short sel)
987 {
988         TextLine **linep, *oldl;
989         int *charp, oldc;
990         unsigned int i;
991         
992         if (!text) return;
993         if(sel) txt_curs_sel(text, &linep, &charp);
994         else txt_curs_cur(text, &linep, &charp);
995         if (!*linep) return;
996         oldc= *charp;
997         oldl= *linep;
998         
999         *linep= text->lines.first;
1000         for (i=0; i<line; i++) {
1001                 if ((*linep)->next) *linep= (*linep)->next;
1002                 else break;
1003         }
1004         if (ch>(unsigned int)((*linep)->len))
1005                 ch= (unsigned int)((*linep)->len);
1006         *charp= ch;
1007         
1008         if(!sel) txt_pop_sel(text);
1009         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);
1010 }
1011
1012 /****************************/
1013 /* Text selection functions */
1014 /****************************/
1015
1016 static void txt_curs_swap (Text *text)
1017 {
1018         TextLine *tmpl;
1019         int tmpc;
1020                 
1021         tmpl= text->curl;
1022         text->curl= text->sell;
1023         text->sell= tmpl;
1024         
1025         tmpc= text->curc;
1026         text->curc= text->selc;
1027         text->selc= tmpc;
1028         
1029         if(!undoing) txt_undo_add_op(text, UNDO_SWAP);
1030 }
1031
1032 static void txt_pop_first (Text *text)
1033 {
1034                         
1035         if (txt_get_span(text->curl, text->sell)<0 ||
1036                 (text->curl==text->sell && text->curc>text->selc)) {    
1037                 txt_curs_swap(text);
1038         }
1039
1040         if(!undoing) txt_undo_add_toop(text, UNDO_STO,
1041                 txt_get_span(text->lines.first, text->sell), 
1042                 text->selc, 
1043                 txt_get_span(text->lines.first, text->curl), 
1044                 text->curc);            
1045         
1046         txt_pop_sel(text);
1047 }
1048
1049 static void txt_pop_last (Text *text)
1050 {
1051         if (txt_get_span(text->curl, text->sell)>0 ||
1052                 (text->curl==text->sell && text->curc<text->selc)) {
1053                 txt_curs_swap(text);
1054         }
1055
1056         if(!undoing) txt_undo_add_toop(text, UNDO_STO,
1057                 txt_get_span(text->lines.first, text->sell), 
1058                 text->selc, 
1059                 txt_get_span(text->lines.first, text->curl), 
1060                 text->curc);            
1061         
1062         txt_pop_sel(text);
1063 }
1064
1065 /* never used: CVS 1.19 */
1066 /*  static void txt_pop_selr (Text *text) */
1067
1068 void txt_pop_sel (Text *text)
1069 {
1070         text->sell= text->curl;
1071         text->selc= text->curc; 
1072 }
1073
1074 void txt_order_cursors(Text *text)
1075 {
1076         if (!text) return;
1077         if (!text->curl) return;
1078         if (!text->sell) return;
1079         
1080                 /* Flip so text->curl is before text->sell */
1081         if (txt_get_span(text->curl, text->sell)<0 ||
1082                         (text->curl==text->sell && text->curc>text->selc))
1083                 txt_curs_swap(text);
1084 }
1085
1086 int txt_has_sel(Text *text)
1087 {
1088         return ((text->curl!=text->sell) || (text->curc!=text->selc));
1089 }
1090
1091 static void txt_delete_sel (Text *text)
1092 {
1093         TextLine *tmpl;
1094         TextMarker *mrk;
1095         char *buf;
1096         int move, lineno;
1097         
1098         if (!text) return;
1099         if (!text->curl) return;
1100         if (!text->sell) return;
1101
1102         if (!txt_has_sel(text)) return;
1103         
1104         txt_order_cursors(text);
1105
1106         if(!undoing) {
1107                 buf= txt_sel_to_buf(text);
1108                 txt_undo_add_block(text, UNDO_DBLOCK, buf);
1109                 MEM_freeN(buf);
1110         }
1111
1112         buf= MEM_mallocN(text->curc+(text->sell->len - text->selc)+1, "textline_string");
1113         
1114         if (text->curl != text->sell) {
1115                 txt_clear_marker_region(text, text->curl, text->curc, text->curl->len, 0, 0);
1116                 move= txt_get_span(text->curl, text->sell);
1117         } else {
1118                 mrk= txt_find_marker_region(text, text->curl, text->curc, text->selc, 0, 0);
1119                 if (mrk && (mrk->start > text->curc || mrk->end < text->selc))
1120                         txt_clear_marker_region(text, text->curl, text->curc, text->selc, 0, 0);
1121                 move= 0;
1122         }
1123
1124         mrk= txt_find_marker_region(text, text->sell, text->selc-1, text->sell->len, 0, 0);
1125         if (mrk) {
1126                 lineno= mrk->lineno;
1127                 do {
1128                         mrk->lineno -= move;
1129                         if (mrk->start > text->curc) mrk->start -= text->selc - text->curc;
1130                         mrk->end -= text->selc - text->curc;
1131                         mrk= mrk->next;
1132                 } while (mrk && mrk->lineno==lineno);
1133         }
1134
1135         strncpy(buf, text->curl->line, text->curc);
1136         strcpy(buf+text->curc, text->sell->line + text->selc);
1137         buf[text->curc+(text->sell->len - text->selc)]=0;
1138
1139         make_new_line(text->curl, buf);
1140         
1141         tmpl= text->sell;
1142         while (tmpl != text->curl) {
1143                 tmpl= tmpl->prev;
1144                 if (!tmpl) break;
1145                 
1146                 txt_delete_line(text, tmpl->next);
1147         }
1148         
1149         text->sell= text->curl;
1150         text->selc= text->curc;
1151 }
1152
1153 void txt_sel_all (Text *text)
1154 {
1155         if (!text) return;
1156
1157         text->curl= text->lines.first;
1158         text->curc= 0;
1159         
1160         text->sell= text->lines.last;
1161         text->selc= text->sell->len;
1162 }
1163
1164 void txt_sel_line (Text *text)
1165 {
1166         if (!text) return;
1167         if (!text->curl) return;
1168         
1169         text->curc= 0;
1170         text->sell= text->curl;
1171         text->selc= text->sell->len;
1172 }
1173
1174 /***************************/
1175 /* Cut and paste functions */
1176 /***************************/
1177
1178 char *txt_to_buf (Text *text)
1179 {
1180         int length;
1181         TextLine *tmp, *linef, *linel;
1182         int charf, charl;
1183         char *buf;
1184         
1185         if (!text) return NULL;
1186         if (!text->curl) return NULL;
1187         if (!text->sell) return NULL;
1188         if (!text->lines.first) return NULL;
1189
1190         linef= text->lines.first;
1191         charf= 0;
1192                 
1193         linel= text->lines.last;
1194         charl= linel->len;
1195
1196         if (linef == text->lines.last) {
1197                 length= charl-charf;
1198
1199                 buf= MEM_mallocN(length+2, "text buffer");
1200                 
1201                 BLI_strncpy(buf, linef->line + charf, length+1);
1202                 buf[length]=0;
1203         } else {
1204                 length= linef->len - charf;
1205                 length+= charl;
1206                 length+= 2; /* For the 2 '\n' */
1207                 
1208                 tmp= linef->next;
1209                 while (tmp && tmp!= linel) {
1210                         length+= tmp->len+1;
1211                         tmp= tmp->next;
1212                 }
1213                 
1214                 buf= MEM_mallocN(length+1, "cut buffer");
1215
1216                 strncpy(buf, linef->line + charf, linef->len-charf);
1217                 length= linef->len - charf;
1218                 
1219                 buf[length++]='\n';
1220                 
1221                 tmp= linef->next;
1222                 while (tmp && tmp!=linel) {
1223                         strncpy(buf+length, tmp->line, tmp->len);
1224                         length+= tmp->len;
1225                         
1226                         buf[length++]='\n';                     
1227                         
1228                         tmp= tmp->next;
1229                 }
1230                 strncpy(buf+length, linel->line, charl);
1231                 length+= charl;
1232                 
1233                 /* python compiler wants an empty end line */
1234                 buf[length++]='\n';                     
1235                 buf[length]=0;
1236         }
1237         
1238         return buf;
1239 }
1240
1241 int txt_find_string(Text *text, char *findstr, int wrap, int match_case)
1242 {
1243         TextLine *tl, *startl;
1244         char *s= NULL;
1245
1246         if (!text || !text->curl || !text->sell) return 0;
1247         
1248         txt_order_cursors(text);
1249
1250         tl= startl= text->sell;
1251         
1252         if(match_case) s= strstr(&tl->line[text->selc], findstr);
1253         else s= BLI_strcasestr(&tl->line[text->selc], findstr);
1254         while (!s) {
1255                 tl= tl->next;
1256                 if (!tl) {
1257                         if (wrap)
1258                                 tl= text->lines.first;
1259                         else
1260                                 break;
1261                 }
1262
1263                 if(match_case) s= strstr(tl->line, findstr);
1264                 else s= BLI_strcasestr(tl->line, findstr);
1265                 if (tl==startl)
1266                         break;
1267         }
1268         
1269         if (s) {
1270                 int newl= txt_get_span(text->lines.first, tl);
1271                 int newc= (int)(s-tl->line);
1272                 txt_move_to(text, newl, newc, 0);
1273                 txt_move_to(text, newl, newc + strlen(findstr), 1);
1274                 return 1;                               
1275         } else
1276                 return 0;
1277 }
1278
1279 char *txt_sel_to_buf (Text *text)
1280 {
1281         char *buf;
1282         int length=0;
1283         TextLine *tmp, *linef, *linel;
1284         int charf, charl;
1285         
1286         if (!text) return NULL;
1287         if (!text->curl) return NULL;
1288         if (!text->sell) return NULL;
1289         
1290         if (text->curl==text->sell) {
1291                 linef= linel= text->curl;
1292                 
1293                 if (text->curc < text->selc) {
1294                         charf= text->curc;
1295                         charl= text->selc;
1296                 } else{
1297                         charf= text->selc;
1298                         charl= text->curc;
1299                 }
1300         } else if (txt_get_span(text->curl, text->sell)<0) {
1301                 linef= text->sell;
1302                 linel= text->curl;
1303
1304                 charf= text->selc;              
1305                 charl= text->curc;
1306         } else {
1307                 linef= text->curl;
1308                 linel= text->sell;
1309                 
1310                 charf= text->curc;
1311                 charl= text->selc;
1312         }
1313
1314         if (linef == linel) {
1315                 length= charl-charf;
1316
1317                 buf= MEM_mallocN(length+1, "sel buffer");
1318                 
1319                 BLI_strncpy(buf, linef->line + charf, length+1);
1320         } else {
1321                 length+= linef->len - charf;
1322                 length+= charl;
1323                 length++; /* For the '\n' */
1324                 
1325                 tmp= linef->next;
1326                 while (tmp && tmp!= linel) {
1327                         length+= tmp->len+1;
1328                         tmp= tmp->next;
1329                 }
1330                 
1331                 buf= MEM_mallocN(length+1, "sel buffer");
1332                 
1333                 strncpy(buf, linef->line+ charf, linef->len-charf);
1334                 length= linef->len-charf;
1335                 
1336                 buf[length++]='\n';
1337                 
1338                 tmp= linef->next;
1339                 while (tmp && tmp!=linel) {
1340                         strncpy(buf+length, tmp->line, tmp->len);
1341                         length+= tmp->len;
1342                         
1343                         buf[length++]='\n';                     
1344                         
1345                         tmp= tmp->next;
1346                 }
1347                 strncpy(buf+length, linel->line, charl);
1348                 length+= charl;
1349                 
1350                 buf[length]=0;
1351         }       
1352
1353         return buf;
1354 }
1355
1356 static void txt_shift_markers(Text *text, int lineno, int count)
1357 {
1358         TextMarker *marker;
1359
1360         for (marker=text->markers.first; marker; marker= marker->next)
1361                 if (marker->lineno>=lineno) {
1362                         marker->lineno+= count;
1363                 }
1364 }
1365
1366 void txt_insert_buf(Text *text, const char *in_buffer)
1367 {
1368         int i=0, l=0, j, u, len, lineno= -1, count= 0;
1369         TextLine *add;
1370
1371         if (!text) return;
1372         if (!in_buffer) return;
1373
1374         txt_delete_sel(text);
1375         
1376         if(!undoing) txt_undo_add_block (text, UNDO_IBLOCK, in_buffer);         
1377
1378         u= undoing;
1379         undoing= 1;
1380
1381         /* Read the first line (or as close as possible */
1382         while (in_buffer[i] && in_buffer[i]!='\n') {
1383                 txt_add_raw_char(text, in_buffer[i]);
1384                 i++;
1385         }
1386         
1387         if (in_buffer[i]=='\n') txt_split_curline(text);
1388         else { undoing = u; return; }
1389         i++;
1390
1391         /* Read as many full lines as we can */
1392         len= strlen(in_buffer);
1393         lineno= txt_get_span(text->lines.first, text->curl);
1394
1395         while (i<len) {
1396                 l=0;
1397
1398                 while (in_buffer[i] && in_buffer[i]!='\n') {
1399                         i++; l++;
1400                 }
1401         
1402                 if(in_buffer[i]=='\n') {
1403                         add= txt_new_linen(in_buffer +(i-l), l);
1404                         BLI_insertlinkbefore(&text->lines, text->curl, add);
1405                         i++;
1406                         count++;
1407                 } else {
1408                         if(count) {
1409                                 txt_shift_markers(text, lineno, count);
1410                                 count= 0;
1411                         }
1412
1413                         for (j= i-l; j<i && j<(int)strlen(in_buffer); j++) {
1414                                 txt_add_raw_char(text, in_buffer[j]);
1415                         }
1416                         break;
1417                 }
1418         }
1419
1420         if(count) {
1421                 txt_shift_markers(text, lineno, count);
1422                 count= 0;
1423         }
1424
1425         undoing= u;
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         TextMarker *tmp, *marker;
2795
2796         marker= MEM_mallocN(sizeof(TextMarker), "text_marker");
2797         
2798         marker->lineno= txt_get_span(text->lines.first, line);
2799         marker->start= MIN2(start, end);
2800         marker->end= MAX2(start, end);
2801         marker->group= group;
2802         marker->flags= flags;
2803
2804         marker->color[0]= color[0];
2805         marker->color[1]= color[1];
2806         marker->color[2]= color[2];
2807         marker->color[3]= color[3];
2808
2809         for (tmp=text->markers.last; tmp; tmp=tmp->prev)
2810                 if (tmp->lineno < marker->lineno || (tmp->lineno==marker->lineno && tmp->start < marker->start))
2811                         break;
2812
2813         if (tmp) BLI_insertlinkafter(&text->markers, tmp, marker);
2814         else BLI_addhead(&text->markers, marker);
2815 }
2816
2817 /* Returns the first matching marker on the specified line between two points.
2818    If the group or flags fields are non-zero the returned flag must be in the
2819    specified group and have at least the specified flags set. */
2820 TextMarker *txt_find_marker_region(Text *text, TextLine *line, int start, int end, int group, int flags) {
2821         TextMarker *marker, *next;
2822         int lineno= txt_get_span(text->lines.first, line);
2823         
2824         for (marker=text->markers.first; marker; marker=next) {
2825                 next= marker->next;
2826
2827                 if (group && marker->group != group) continue;
2828                 else if ((marker->flags & flags) != flags) continue;
2829                 else if (marker->lineno < lineno) continue;
2830                 else if (marker->lineno > lineno) break;
2831
2832                 if ((marker->start==marker->end && start<=marker->start && marker->start<=end) ||
2833                                 (marker->start<end && marker->end>start))
2834                         return marker;
2835         }
2836         return NULL;
2837 }
2838
2839 /* Clears all markers on the specified line between two points. If the group or
2840    flags fields are non-zero the returned flag must be in the specified group
2841    and have at least the specified flags set. */
2842 short txt_clear_marker_region(Text *text, TextLine *line, int start, int end, int group, int flags) {
2843         TextMarker *marker, *next;
2844         int lineno= txt_get_span(text->lines.first, line);
2845         short cleared= 0;
2846         
2847         for (marker=text->markers.first; marker; marker=next) {
2848                 next= marker->next;
2849
2850                 if (group && marker->group != group) continue;
2851                 else if ((marker->flags & flags) != flags) continue;
2852                 else if (marker->lineno < lineno) continue;
2853                 else if (marker->lineno > lineno) break;
2854
2855                 if ((marker->start==marker->end && start<=marker->start && marker->start<=end) ||
2856                         (marker->start<end && marker->end>start)) {
2857                         BLI_freelinkN(&text->markers, marker);
2858                         cleared= 1;
2859                 }
2860         }
2861         return cleared;
2862 }
2863
2864 /* Clears all markers in the specified group (if given) with at least the
2865    specified flags set. Useful for clearing temporary markers (group=0,
2866    flags=TMARK_TEMP) */
2867 short txt_clear_markers(Text *text, int group, int flags) {
2868         TextMarker *marker, *next;
2869         short cleared= 0;
2870         
2871         for (marker=text->markers.first; marker; marker=next) {
2872                 next= marker->next;
2873
2874                 if ((!group || marker->group==group) &&
2875                                 (marker->flags & flags) == flags) {
2876                         BLI_freelinkN(&text->markers, marker);
2877                         cleared= 1;
2878                 }
2879         }
2880         return cleared;
2881 }
2882
2883 /* Finds the marker at the specified line and cursor position with at least the
2884    specified flags set in the given group (if non-zero). */
2885 TextMarker *txt_find_marker(Text *text, TextLine *line, int curs, int group, int flags) {
2886         TextMarker *marker;
2887         int lineno= txt_get_span(text->lines.first, line);
2888         
2889         for (marker=text->markers.first; marker; marker=marker->next) {
2890                 if (group && marker->group != group) continue;
2891                 else if ((marker->flags & flags) != flags) continue;
2892                 else if (marker->lineno < lineno) continue;
2893                 else if (marker->lineno > lineno) break;
2894
2895                 if (marker->start <= curs && curs <= marker->end)
2896                         return marker;
2897         }
2898         return NULL;
2899 }
2900
2901 /* Finds the previous marker in the same group. If no other is found, the same
2902    marker will be returned */
2903 TextMarker *txt_prev_marker(Text *text, TextMarker *marker) {
2904         TextMarker *tmp= marker;
2905         while (tmp) {
2906                 if (tmp->prev) tmp= tmp->prev;
2907                 else tmp= text->markers.last;
2908                 if (tmp->group == marker->group)
2909                         return tmp;
2910         }
2911         return NULL; /* Only if marker==NULL */
2912 }
2913
2914 /* Finds the next marker in the same group. If no other is found, the same
2915    marker will be returned */
2916 TextMarker *txt_next_marker(Text *text, TextMarker *marker) {
2917         TextMarker *tmp= marker;
2918         while (tmp) {
2919                 if (tmp->next) tmp= tmp->next;
2920                 else tmp= text->markers.first;
2921                 if (tmp->group == marker->group)
2922                         return tmp;
2923         }
2924         return NULL; /* Only if marker==NULL */
2925 }
2926
2927
2928 /*******************************/
2929 /* Character utility functions */
2930 /*******************************/
2931
2932 int text_check_bracket(char ch)
2933 {
2934         int a;
2935         char opens[] = "([{";
2936         char close[] = ")]}";
2937
2938         for(a=0; a<(sizeof(opens)-1); a++) {
2939                 if(ch==opens[a])
2940                         return a+1;
2941                 else if(ch==close[a])
2942                         return -(a+1);
2943         }
2944         return 0;
2945 }
2946
2947 int text_check_delim(char ch)
2948 {
2949         int a;
2950         char delims[] = "():\"\' ~!%^&*-+=[]{};/<>|.#\t,";
2951
2952         for(a=0; a<(sizeof(delims)-1); a++) {
2953                 if(ch==delims[a])
2954                         return 1;
2955         }
2956         return 0;
2957 }
2958
2959 int text_check_digit(char ch)
2960 {
2961         if(ch < '0') return 0;
2962         if(ch <= '9') return 1;
2963         return 0;
2964 }
2965
2966 int text_check_identifier(char ch)
2967 {
2968         if(ch < '0') return 0;
2969         if(ch <= '9') return 1;
2970         if(ch < 'A') return 0;
2971         if(ch <= 'Z' || ch == '_') return 1;
2972         if(ch < 'a') return 0;
2973         if(ch <= 'z') return 1;
2974         return 0;
2975 }
2976
2977 int text_check_whitespace(char ch)
2978 {
2979         if(ch == ' ' || ch == '\t' || ch == '\r' || ch == '\n')
2980                 return 1;
2981         return 0;
2982 }