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