Silence some unused-but-set-variable warnings.
[blender.git] / source / blender / blenkernel / intern / text.c
1 /* text.c
2  *
3  *
4  * $Id$
5  *
6  * ***** BEGIN GPL LICENSE BLOCK *****
7  *
8  * This program is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU General Public License
10  * as published by the Free Software Foundation; either version 2
11  * of the License, or (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU General Public License
19  * along with this program; if not, write to the Free Software Foundation,
20  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21  *
22  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
23  * All rights reserved.
24  *
25  * The Original Code is: all of this file.
26  *
27  * Contributor(s): none yet.
28  *
29  * ***** END GPL LICENSE BLOCK *****
30  */
31
32 /** \file blender/blenkernel/intern/text.c
33  *  \ingroup bke
34  */
35
36
37 #include <string.h> /* strstr */
38 #include <sys/types.h>
39 #include <sys/stat.h>
40
41 #include "MEM_guardedalloc.h"
42
43 #include "BLI_blenlib.h"
44 #include "BLI_utildefines.h"
45
46 #include "DNA_constraint_types.h"
47 #include "DNA_controller_types.h"
48 #include "DNA_scene_types.h"
49 #include "DNA_screen_types.h"
50 #include "DNA_space_types.h"
51 #include "DNA_text_types.h"
52 #include "DNA_userdef_types.h"
53 #include "DNA_object_types.h"
54
55 #include "BKE_depsgraph.h"
56 #include "BKE_global.h"
57 #include "BKE_library.h"
58 #include "BKE_main.h"
59 #include "BKE_text.h"
60
61
62 #ifdef WITH_PYTHON
63 #include "BPY_extern.h"
64 #endif
65
66 /***************/ /*
67
68 How Texts should work
69 --
70 A text should relate to a file as follows -
71 (Text *)->name should be the place where the 
72         file will or has been saved.
73         
74 (Text *)->flags has the following bits
75         TXT_ISDIRTY - should always be set if the file in mem. differs from
76                                         the file on disk, or if there is no file on disk.
77         TXT_ISMEM - should always be set if the Text has not been mapped to
78                                         a file, in which case (Text *)->name may be NULL or garbage.                    
79         TXT_ISEXT - should always be set if the Text is not to be written into
80                                         the .blend
81         TXT_ISSCRIPT - should be set if the user has designated the text
82                                         as a script. (NEW: this was unused, but now it is needed by
83                                         space handler script links (see header_view3d.c, for example)
84
85 ->>> see also: /makesdna/DNA_text_types.h
86
87 Display
88 --
89 The st->top determines at what line the top of the text is displayed.
90 If the user moves the cursor the st containing that cursor should
91 be popped ... other st's retain their own top location.
92
93 Markers
94 --
95 The mrk->flags define the behaviour and relationships between markers. The
96 upper two bytes are used to hold a group ID, the lower two are normal flags. If
97 TMARK_EDITALL is set the group ID defines which other markers should be edited.
98
99 The mrk->clr field is used to visually group markers where the flags may not
100 match. A template system, for example, may allow editing of repeating tokens
101 (in one group) but include other marked positions (in another group) all in the
102 same template with the same color.
103
104 Undo
105 --
106 Undo/Redo works by storing
107 events in a queue, and a pointer
108 to the current position in the
109 queue...
110
111 Events are stored using an
112 arbitrary op-code system
113 to keep track of
114 a) the two cursors (normal and selected)
115 b) input (visible and control (ie backspace))
116
117 input data is stored as its
118 ASCII value, the opcodes are
119 then selected to not conflict.
120
121 opcodes with data in between are
122 written at the beginning and end
123 of the data to allow undo and redo
124 to simply check the code at the current
125 undo position
126
127 */ /***************/
128
129 /***/
130
131 static void txt_pop_first(Text *text);
132 static void txt_pop_last(Text *text);
133 static void txt_undo_add_op(Text *text, int op);
134 static void txt_undo_add_block(Text *text, int op, const char *buf);
135 static void txt_delete_line(Text *text, TextLine *line);
136 static void txt_delete_sel (Text *text);
137 static void txt_make_dirty (Text *text);
138
139 /***/
140
141 static unsigned char undoing;
142
143 /* allow to switch off undoing externally */
144 void txt_set_undostate(int u)
145 {
146         undoing = u;
147 }
148
149 int txt_get_undostate(void)
150 {
151         return undoing;
152 }
153
154 static void init_undo_text(Text *text)
155 {
156         text->undo_pos= -1;
157         text->undo_len= TXT_INIT_UNDO;
158         text->undo_buf= MEM_mallocN(text->undo_len, "undo buf");
159 }
160
161 void free_text(Text *text)
162 {
163         TextLine *tmp;
164
165         for (tmp= text->lines.first; tmp; tmp= tmp->next) {
166                 MEM_freeN(tmp->line);
167                 if (tmp->format)
168                   MEM_freeN(tmp->format);
169         }
170         
171         BLI_freelistN(&text->lines);
172         BLI_freelistN(&text->markers);
173
174         if(text->name) MEM_freeN(text->name);
175         MEM_freeN(text->undo_buf);
176 #ifdef WITH_PYTHON
177         if (text->compiled) BPY_text_free_code(text);
178 #endif
179 }
180
181 Text *add_empty_text(const char *name) 
182 {
183         Main *bmain= G.main;
184         Text *ta;
185         TextLine *tmp;
186         
187         ta= alloc_libblock(&bmain->text, ID_TXT, name);
188         ta->id.us= 1;
189         
190         ta->name= NULL;
191
192         init_undo_text(ta);
193
194         ta->nlines=1;
195         ta->flags= TXT_ISDIRTY | TXT_ISMEM;
196         if((U.flag & USER_TXT_TABSTOSPACES_DISABLE)==0)
197                 ta->flags |= TXT_TABSTOSPACES;
198
199         ta->lines.first= ta->lines.last= NULL;
200         ta->markers.first= ta->markers.last= NULL;
201
202         tmp= (TextLine*) MEM_mallocN(sizeof(TextLine), "textline");
203         tmp->line= (char*) MEM_mallocN(1, "textline_string");
204         tmp->format= NULL;
205         
206         tmp->line[0]=0;
207         tmp->len= 0;
208                                 
209         tmp->next= NULL;
210         tmp->prev= NULL;
211                                 
212         BLI_addhead(&ta->lines, tmp);
213         
214         ta->curl= ta->lines.first;
215         ta->curc= 0;
216         ta->sell= ta->lines.first;
217         ta->selc= 0;
218
219         return ta;
220 }
221
222 // this function removes any control characters from
223 // a textline
224
225 static void cleanup_textline(TextLine * tl)
226 {
227         int i;
228
229         for (i = 0; i < tl->len; i++ ) {
230                 if (tl->line[i] < ' ' && tl->line[i] != '\t') {
231                         memmove(tl->line + i, tl->line + i + 1, tl->len - i);
232                         tl->len--;
233                         i--;
234                 }
235         }
236 }
237
238 int reopen_text(Text *text)
239 {
240         FILE *fp;
241         int i, llen, len;
242         unsigned char *buffer;
243         TextLine *tmp;
244         char str[FILE_MAXDIR+FILE_MAXFILE];
245         struct stat st;
246
247         if (!text || !text->name) return 0;
248         
249         BLI_strncpy(str, text->name, FILE_MAXDIR+FILE_MAXFILE);
250         BLI_path_abs(str, G.main->name);
251         
252         fp= fopen(str, "r");
253         if(fp==NULL) return 0;
254
255         /* free memory: */
256
257         for (tmp= text->lines.first; tmp; tmp= tmp->next) {
258                 MEM_freeN(tmp->line);
259                 if (tmp->format) MEM_freeN(tmp->format);
260         }
261         
262         BLI_freelistN(&text->lines);
263
264         text->lines.first= text->lines.last= NULL;
265         text->curl= text->sell= NULL;
266
267         /* clear undo buffer */
268         MEM_freeN(text->undo_buf);
269         init_undo_text(text);
270         
271         fseek(fp, 0L, SEEK_END);
272         len= ftell(fp);
273         fseek(fp, 0L, SEEK_SET);        
274
275         text->undo_pos= -1;
276         
277         buffer= MEM_mallocN(len, "text_buffer");
278         // under windows fread can return less then len bytes because
279         // of CR stripping
280         len = fread(buffer, 1, len, fp);
281
282         fclose(fp);
283
284         stat(str, &st);
285         text->mtime= st.st_mtime;
286         
287         text->nlines=0;
288         llen=0;
289         for(i=0; i<len; i++) {
290                 if (buffer[i]=='\n') {
291                         tmp= (TextLine*) MEM_mallocN(sizeof(TextLine), "textline");
292                         tmp->line= (char*) MEM_mallocN(llen+1, "textline_string");
293                         tmp->format= NULL;
294                         
295                         if(llen) memcpy(tmp->line, &buffer[i-llen], llen);
296                         tmp->line[llen]=0;
297                         tmp->len= llen;
298                                 
299                         cleanup_textline(tmp);
300
301                         BLI_addtail(&text->lines, tmp);
302                         text->nlines++;
303                                 
304                         llen=0;
305                         continue;
306                 }
307                 llen++;
308         }
309
310         if (llen!=0 || text->nlines==0) {
311                 tmp= (TextLine*) MEM_mallocN(sizeof(TextLine), "textline");
312                 tmp->line= (char*) MEM_mallocN(llen+1, "textline_string");
313                 tmp->format= NULL;
314                 
315                 if(llen) memcpy(tmp->line, &buffer[i-llen], llen);
316
317                 tmp->line[llen]=0;
318                 tmp->len= llen;
319                 
320                 cleanup_textline(tmp);
321
322                 BLI_addtail(&text->lines, tmp);
323                 text->nlines++;
324         }
325         
326         text->curl= text->sell= text->lines.first;
327         text->curc= text->selc= 0;
328         
329         MEM_freeN(buffer);      
330         return 1;
331 }
332
333 Text *add_text(const char *file, const char *relpath) 
334 {
335         Main *bmain= G.main;
336         FILE *fp;
337         int i, llen, len;
338         unsigned char *buffer;
339         TextLine *tmp;
340         Text *ta;
341         char str[FILE_MAXDIR+FILE_MAXFILE];
342         struct stat st;
343
344         BLI_strncpy(str, file, FILE_MAXDIR+FILE_MAXFILE);
345         if (relpath) /* can be NULL (bg mode) */
346                 BLI_path_abs(str, relpath);
347         
348         fp= fopen(str, "r");
349         if(fp==NULL) return NULL;
350         
351         ta= alloc_libblock(&bmain->text, ID_TXT, BLI_path_basename(str));
352         ta->id.us= 1;
353
354         ta->lines.first= ta->lines.last= NULL;
355         ta->markers.first= ta->markers.last= NULL;
356         ta->curl= ta->sell= NULL;
357
358         if((U.flag & USER_TXT_TABSTOSPACES_DISABLE)==0)
359                 ta->flags= TXT_TABSTOSPACES;
360
361         fseek(fp, 0L, SEEK_END);
362         len= ftell(fp);
363         fseek(fp, 0L, SEEK_SET);        
364
365         ta->name= MEM_mallocN(strlen(file)+1, "text_name");
366         strcpy(ta->name, file);
367
368         init_undo_text(ta);
369         
370         buffer= MEM_mallocN(len, "text_buffer");
371         // under windows fread can return less then len bytes because
372         // of CR stripping
373         len = fread(buffer, 1, len, fp);
374
375         fclose(fp);
376
377         stat(str, &st);
378         ta->mtime= st.st_mtime;
379         
380         ta->nlines=0;
381         llen=0;
382         for(i=0; i<len; i++) {
383                 if (buffer[i]=='\n') {
384                         tmp= (TextLine*) MEM_mallocN(sizeof(TextLine), "textline");
385                         tmp->line= (char*) MEM_mallocN(llen+1, "textline_string");
386                         tmp->format= NULL;
387                         
388                         if(llen) memcpy(tmp->line, &buffer[i-llen], llen);
389                         tmp->line[llen]=0;
390                         tmp->len= llen;
391                         
392                         cleanup_textline(tmp);
393
394                         BLI_addtail(&ta->lines, tmp);
395                         ta->nlines++;
396                                 
397                         llen=0;
398                         continue;
399                 }
400                 llen++;
401         }
402
403         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, int match_case)
1236 {
1237         TextLine *tl, *startl;
1238         char *s= NULL;
1239
1240         if (!text || !text->curl || !text->sell) return 0;
1241         
1242         txt_order_cursors(text);
1243
1244         tl= startl= text->sell;
1245         
1246         if(match_case) s= strstr(&tl->line[text->selc], findstr);
1247         else s= BLI_strcasestr(&tl->line[text->selc], findstr);
1248         while (!s) {
1249                 tl= tl->next;
1250                 if (!tl) {
1251                         if (wrap)
1252                                 tl= text->lines.first;
1253                         else
1254                                 break;
1255                 }
1256
1257                 if(match_case) s= strstr(tl->line, findstr);
1258                 else s= BLI_strcasestr(tl->line, findstr);
1259                 if (tl==startl)
1260                         break;
1261         }
1262         
1263         if (s) {
1264                 int newl= txt_get_span(text->lines.first, tl);
1265                 int newc= (int)(s-tl->line);
1266                 txt_move_to(text, newl, newc, 0);
1267                 txt_move_to(text, newl, newc + strlen(findstr), 1);
1268                 return 1;                               
1269         } else
1270                 return 0;
1271 }
1272
1273 char *txt_sel_to_buf (Text *text)
1274 {
1275         char *buf;
1276         int length=0;
1277         TextLine *tmp, *linef, *linel;
1278         int charf, charl;
1279         
1280         if (!text) return NULL;
1281         if (!text->curl) return NULL;
1282         if (!text->sell) return NULL;
1283         
1284         if (text->curl==text->sell) {
1285                 linef= linel= text->curl;
1286                 
1287                 if (text->curc < text->selc) {
1288                         charf= text->curc;
1289                         charl= text->selc;
1290                 } else{
1291                         charf= text->selc;
1292                         charl= text->curc;
1293                 }
1294         } else if (txt_get_span(text->curl, text->sell)<0) {
1295                 linef= text->sell;
1296                 linel= text->curl;
1297
1298                 charf= text->selc;              
1299                 charl= text->curc;
1300         } else {
1301                 linef= text->curl;
1302                 linel= text->sell;
1303                 
1304                 charf= text->curc;
1305                 charl= text->selc;
1306         }
1307
1308         if (linef == linel) {
1309                 length= charl-charf;
1310
1311                 buf= MEM_mallocN(length+1, "sel buffer");
1312                 
1313                 BLI_strncpy(buf, linef->line + charf, length+1);
1314         } else {
1315                 length+= linef->len - charf;
1316                 length+= charl;
1317                 length++; /* For the '\n' */
1318                 
1319                 tmp= linef->next;
1320                 while (tmp && tmp!= linel) {
1321                         length+= tmp->len+1;
1322                         tmp= tmp->next;
1323                 }
1324                 
1325                 buf= MEM_mallocN(length+1, "sel buffer");
1326                 
1327                 strncpy(buf, linef->line+ charf, linef->len-charf);
1328                 length= linef->len-charf;
1329                 
1330                 buf[length++]='\n';
1331                 
1332                 tmp= linef->next;
1333                 while (tmp && tmp!=linel) {
1334                         strncpy(buf+length, tmp->line, tmp->len);
1335                         length+= tmp->len;
1336                         
1337                         buf[length++]='\n';                     
1338                         
1339                         tmp= tmp->next;
1340                 }
1341                 strncpy(buf+length, linel->line, charl);
1342                 length+= charl;
1343                 
1344                 buf[length]=0;
1345         }       
1346
1347         return buf;
1348 }
1349
1350 static void txt_shift_markers(Text *text, int lineno, int count)
1351 {
1352         TextMarker *marker;
1353
1354         for (marker=text->markers.first; marker; marker= marker->next)
1355                 if (marker->lineno>=lineno) {
1356                         marker->lineno+= count;
1357                 }
1358 }
1359
1360 void txt_insert_buf(Text *text, const char *in_buffer)
1361 {
1362         int i=0, l=0, j, u, len, lineno= -1, count= 0;
1363         TextLine *add;
1364
1365         if (!text) return;
1366         if (!in_buffer) return;
1367
1368         txt_delete_sel(text);
1369         
1370         if(!undoing) txt_undo_add_block (text, UNDO_IBLOCK, in_buffer);         
1371
1372         u= undoing;
1373         undoing= 1;
1374
1375         /* Read the first line (or as close as possible */
1376         while (in_buffer[i] && in_buffer[i]!='\n') {
1377                 txt_add_raw_char(text, in_buffer[i]);
1378                 i++;
1379         }
1380         
1381         if (in_buffer[i]=='\n') txt_split_curline(text);
1382         else { undoing = u; return; }
1383         i++;
1384
1385         /* Read as many full lines as we can */
1386         len= strlen(in_buffer);
1387         lineno= txt_get_span(text->lines.first, text->curl);
1388
1389         while (i<len) {
1390                 l=0;
1391
1392                 while (in_buffer[i] && in_buffer[i]!='\n') {
1393                         i++; l++;
1394                 }
1395         
1396                 if(in_buffer[i]=='\n') {
1397                         add= txt_new_linen(in_buffer +(i-l), l);
1398                         BLI_insertlinkbefore(&text->lines, text->curl, add);
1399                         i++;
1400                         count++;
1401                 } else {
1402                         if(count) {
1403                                 txt_shift_markers(text, lineno, count);
1404                                 count= 0;
1405                         }
1406
1407                         for (j= i-l; j<i && j<(int)strlen(in_buffer); j++) {
1408                                 txt_add_raw_char(text, in_buffer[j]);
1409                         }
1410                         break;
1411                 }
1412         }
1413
1414         if(count) {
1415                 txt_shift_markers(text, lineno, count);
1416                 count= 0;
1417         }
1418
1419         undoing= u;
1420 }
1421
1422 /******************/
1423 /* Undo functions */
1424 /******************/
1425
1426 static int max_undo_test(Text *text, int x)
1427 {
1428         while (text->undo_pos+x >= text->undo_len) {
1429                 if(text->undo_len*2 > TXT_MAX_UNDO) {
1430                         /* XXX error("Undo limit reached, buffer cleared\n"); */
1431                         MEM_freeN(text->undo_buf);
1432                         init_undo_text(text);
1433                         return 0;
1434                 } else {
1435                         void *tmp= text->undo_buf;
1436                         text->undo_buf= MEM_callocN(text->undo_len*2, "undo buf");
1437                         memcpy(text->undo_buf, tmp, text->undo_len);
1438                         text->undo_len*=2;
1439                         MEM_freeN(tmp);
1440                 }
1441         }
1442
1443         return 1;
1444 }
1445
1446 static void dump_buffer(Text *text) 
1447 {
1448         int i= 0;
1449         
1450         while (i++<text->undo_pos) printf("%d: %d %c\n", i, text->undo_buf[i], text->undo_buf[i]);
1451 }
1452
1453 void txt_print_undo(Text *text)
1454 {
1455         int i= 0;
1456         int op;
1457         const char *ops;
1458         int linep, charp;
1459         
1460         dump_buffer(text);
1461         
1462         printf ("---< Undo Buffer >---\n");
1463         
1464         printf ("UndoPosition is %d\n", text->undo_pos);
1465         
1466         while (i<=text->undo_pos) {
1467                 op= text->undo_buf[i];
1468                 
1469                 if (op==UNDO_CLEFT) {
1470                         ops= "Cursor left";
1471                 } else if (op==UNDO_CRIGHT) {
1472                         ops= "Cursor right";
1473                 } else if (op==UNDO_CUP) {
1474                         ops= "Cursor up";
1475                 } else if (op==UNDO_CDOWN) {
1476                         ops= "Cursor down";
1477                 } else if (op==UNDO_SLEFT) {
1478                         ops= "Selection left";
1479                 } else if (op==UNDO_SRIGHT) {
1480                         ops= "Selection right";
1481                 } else if (op==UNDO_SUP) {
1482                         ops= "Selection up";
1483                 } else if (op==UNDO_SDOWN) {
1484                         ops= "Selection down";
1485                 } else if (op==UNDO_STO) {
1486                         ops= "Selection ";
1487                 } else if (op==UNDO_CTO) {
1488                         ops= "Cursor ";
1489                 } else if (op==UNDO_INSERT) {
1490                         ops= "Insert";
1491                 } else if (op==UNDO_BS) {
1492                         ops= "Backspace";
1493                 } else if (op==UNDO_DEL) {
1494                         ops= "Delete";
1495                 } else if (op==UNDO_SWAP) {
1496                         ops= "Cursor swap";
1497                 } else if (op==UNDO_DBLOCK) {
1498                         ops= "Delete text block";
1499                 } else if (op==UNDO_IBLOCK) {
1500                         ops= "Insert text block";
1501                 } else if (op==UNDO_INDENT) {
1502                         ops= "Indent ";
1503                 } else if (op==UNDO_UNINDENT) {
1504                         ops= "Unindent ";
1505                 } else if (op==UNDO_COMMENT) {
1506                         ops= "Comment ";
1507                 } else if (op==UNDO_UNCOMMENT) {
1508                         ops= "Uncomment ";
1509                 } else {
1510                         ops= "Unknown";
1511                 }
1512                 
1513                 printf ("Op (%o) at %d = %s", op, i, ops);
1514                 if (op==UNDO_INSERT || op==UNDO_BS || op==UNDO_DEL) {
1515                         i++;
1516                         printf (" - Char is %c", text->undo_buf[i]);  
1517                         i++;
1518                 } else if (op==UNDO_STO || op==UNDO_CTO) {
1519                         i++;
1520
1521                         charp= text->undo_buf[i]; i++;
1522                         charp= charp+(text->undo_buf[i]<<8); i++;
1523
1524                         linep= text->undo_buf[i]; i++;
1525                         linep= linep+(text->undo_buf[i]<<8); i++;
1526                         linep= linep+(text->undo_buf[i]<<16); i++;
1527                         linep= linep+(text->undo_buf[i]<<24); i++;
1528                         
1529                         printf ("to <%d, %d> ", linep, charp);
1530
1531                         charp= text->undo_buf[i]; i++;
1532                         charp= charp+(text->undo_buf[i]<<8); i++;
1533
1534                         linep= text->undo_buf[i]; i++;
1535                         linep= linep+(text->undo_buf[i]<<8); i++;
1536                         linep= linep+(text->undo_buf[i]<<16); i++;
1537                         linep= linep+(text->undo_buf[i]<<24); i++;
1538                         
1539                         printf ("from <%d, %d>", linep, charp);
1540                 } else if (op==UNDO_DBLOCK || op==UNDO_IBLOCK) {
1541                         i++;
1542
1543                         linep= text->undo_buf[i]; i++;
1544                         linep= linep+(text->undo_buf[i]<<8); i++;
1545                         linep= linep+(text->undo_buf[i]<<16); i++;
1546                         linep= linep+(text->undo_buf[i]<<24); i++;
1547                         
1548                         printf (" (length %d) <", linep);
1549                         
1550                         while (linep>0) {
1551                                 putchar(text->undo_buf[i]);
1552                                 linep--; i++;
1553                         }
1554                         
1555                         linep= text->undo_buf[i]; i++;
1556                         linep= linep+(text->undo_buf[i]<<8); i++;
1557                         linep= linep+(text->undo_buf[i]<<16); i++;
1558                         linep= linep+(text->undo_buf[i]<<24); i++;
1559                         printf ("> (%d)", linep);
1560                 } else if (op==UNDO_INDENT || op==UNDO_UNINDENT) {
1561                         i++;
1562
1563                         charp= text->undo_buf[i]; i++;
1564                         charp= charp+(text->undo_buf[i]<<8); i++;
1565
1566                         linep= text->undo_buf[i]; i++;
1567                         linep= linep+(text->undo_buf[i]<<8); i++;
1568                         linep= linep+(text->undo_buf[i]<<16); i++;
1569                         linep= linep+(text->undo_buf[i]<<24); i++;
1570                         
1571                         printf ("to <%d, %d> ", linep, charp);
1572
1573                         charp= text->undo_buf[i]; i++;
1574                         charp= charp+(text->undo_buf[i]<<8); i++;
1575
1576                         linep= text->undo_buf[i]; i++;
1577                         linep= linep+(text->undo_buf[i]<<8); i++;
1578                         linep= linep+(text->undo_buf[i]<<16); i++;
1579                         linep= linep+(text->undo_buf[i]<<24); i++;
1580                         
1581                         printf ("from <%d, %d>", linep, charp);
1582                 }
1583                 
1584                 printf (" %d\n",  i);
1585                 i++;
1586         }
1587 }
1588
1589 static void txt_undo_add_op(Text *text, int op)
1590 {
1591         if(!max_undo_test(text, 2))
1592                 return;
1593         
1594         text->undo_pos++;
1595         text->undo_buf[text->undo_pos]= op;
1596         text->undo_buf[text->undo_pos+1]= 0;
1597 }
1598
1599 static void txt_undo_add_block(Text *text, int op, const char *buf)
1600 {
1601         int length;
1602         
1603         length= strlen(buf);
1604         
1605         if(!max_undo_test(text, length+11))
1606                 return;
1607
1608         text->undo_pos++;
1609         text->undo_buf[text->undo_pos]= op;
1610         
1611         text->undo_pos++;
1612         text->undo_buf[text->undo_pos]= (length)&0xff;
1613         text->undo_pos++;
1614         text->undo_buf[text->undo_pos]= (length>>8)&0xff;
1615         text->undo_pos++;
1616         text->undo_buf[text->undo_pos]= (length>>16)&0xff;
1617         text->undo_pos++;
1618         text->undo_buf[text->undo_pos]= (length>>24)&0xff;
1619
1620         text->undo_pos++;
1621         strncpy(text->undo_buf+text->undo_pos, buf, length);
1622         text->undo_pos+=length;
1623
1624         text->undo_buf[text->undo_pos]= (length)&0xff;
1625         text->undo_pos++;
1626         text->undo_buf[text->undo_pos]= (length>>8)&0xff;
1627         text->undo_pos++;
1628         text->undo_buf[text->undo_pos]= (length>>16)&0xff;
1629         text->undo_pos++;
1630         text->undo_buf[text->undo_pos]= (length>>24)&0xff;
1631
1632         text->undo_pos++;
1633         text->undo_buf[text->undo_pos]= op;
1634         
1635         text->undo_buf[text->undo_pos+1]= 0;
1636 }
1637
1638 void txt_undo_add_toop(Text *text, int op, unsigned int froml, unsigned short fromc, unsigned int tol, unsigned short toc)
1639 {
1640         if(!max_undo_test(text, 15))
1641                 return;
1642
1643         if (froml==tol && fromc==toc) return;
1644
1645         text->undo_pos++;
1646         text->undo_buf[text->undo_pos]= op;
1647
1648         text->undo_pos++;
1649         text->undo_buf[text->undo_pos]= (fromc)&0xff;
1650         text->undo_pos++;
1651         text->undo_buf[text->undo_pos]= (fromc>>8)&0xff;
1652
1653         text->undo_pos++;
1654         text->undo_buf[text->undo_pos]= (froml)&0xff;
1655         text->undo_pos++;
1656         text->undo_buf[text->undo_pos]= (froml>>8)&0xff;
1657         text->undo_pos++;
1658         text->undo_buf[text->undo_pos]= (froml>>16)&0xff;
1659         text->undo_pos++;
1660         text->undo_buf[text->undo_pos]= (froml>>24)&0xff;
1661
1662         text->undo_pos++;
1663         text->undo_buf[text->undo_pos]= (toc)&0xff;
1664         text->undo_pos++;
1665         text->undo_buf[text->undo_pos]= (toc>>8)&0xff;
1666
1667         text->undo_pos++;
1668         text->undo_buf[text->undo_pos]= (tol)&0xff;
1669         text->undo_pos++;
1670         text->undo_buf[text->undo_pos]= (tol>>8)&0xff;
1671         text->undo_pos++;
1672         text->undo_buf[text->undo_pos]= (tol>>16)&0xff;
1673         text->undo_pos++;
1674         text->undo_buf[text->undo_pos]= (tol>>24)&0xff;
1675
1676         text->undo_pos++;
1677         text->undo_buf[text->undo_pos]= op;
1678
1679         text->undo_buf[text->undo_pos+1]= 0;
1680 }
1681
1682 static void txt_undo_add_charop(Text *text, int op, char c)
1683 {
1684         if(!max_undo_test(text, 4))
1685                 return;
1686
1687         text->undo_pos++;
1688         text->undo_buf[text->undo_pos]= op;
1689         text->undo_pos++;
1690         text->undo_buf[text->undo_pos]= c;
1691         text->undo_pos++;
1692         text->undo_buf[text->undo_pos]= op;
1693         text->undo_buf[text->undo_pos+1]= 0;
1694 }
1695
1696 void txt_do_undo(Text *text)
1697 {
1698         int op= text->undo_buf[text->undo_pos];
1699         unsigned int linep, i;
1700         unsigned short charp;
1701         TextLine *holdl;
1702         int holdc, holdln;
1703         char *buf;
1704         
1705         if (text->undo_pos<0) {
1706                 return;
1707         }
1708
1709         text->undo_pos--;
1710
1711         undoing= 1;
1712         
1713         switch(op) {
1714                 case UNDO_CLEFT:
1715                         txt_move_right(text, 0);
1716                         break;
1717                         
1718                 case UNDO_CRIGHT:
1719                         txt_move_left(text, 0);
1720                         break;
1721                         
1722                 case UNDO_CUP:
1723                         txt_move_down(text, 0);
1724                         break;
1725                         
1726                 case UNDO_CDOWN:
1727                         txt_move_up(text, 0);
1728                         break;
1729
1730                 case UNDO_SLEFT:
1731                         txt_move_right(text, 1);
1732                         break;
1733
1734                 case UNDO_SRIGHT:
1735                         txt_move_left(text, 1);
1736                         break;
1737
1738                 case UNDO_SUP:
1739                         txt_move_down(text, 1);
1740                         break;
1741
1742                 case UNDO_SDOWN:
1743                         txt_move_up(text, 1);
1744                         break;
1745                 
1746                 case UNDO_CTO:
1747                 case UNDO_STO:
1748                         text->undo_pos--;
1749                         text->undo_pos--;
1750                         text->undo_pos--;
1751                         text->undo_pos--;
1752                 
1753                         text->undo_pos--;
1754                         text->undo_pos--;
1755                 
1756                         linep= text->undo_buf[text->undo_pos]; text->undo_pos--;
1757                         linep= (linep<<8)+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
1761                         charp= text->undo_buf[text->undo_pos]; text->undo_pos--;
1762                         charp= (charp<<8)+text->undo_buf[text->undo_pos]; text->undo_pos--;
1763                         
1764                         if (op==UNDO_CTO) {
1765                                 txt_move_toline(text, linep, 0);
1766                                 text->curc= charp;
1767                                 txt_pop_sel(text);
1768                         } else {
1769                                 txt_move_toline(text, linep, 1);
1770                                 text->selc= charp;
1771                         }
1772                         
1773                         text->undo_pos--;
1774                         break;
1775                         
1776                 case UNDO_INSERT:
1777                         txt_backspace_char(text);
1778                         text->undo_pos--;
1779                         text->undo_pos--;
1780                         break;
1781
1782                 case UNDO_BS:
1783                         txt_add_char(text, text->undo_buf[text->undo_pos]);
1784                         text->undo_pos--;
1785                         text->undo_pos--;
1786                         break;
1787
1788                 case UNDO_DEL:
1789                         txt_add_char(text, text->undo_buf[text->undo_pos]);
1790                         txt_move_left(text, 0);
1791                         text->undo_pos--;
1792                         text->undo_pos--;
1793                         break;
1794
1795                 case UNDO_SWAP:
1796                         txt_curs_swap(text);
1797                         break;
1798
1799                 case UNDO_DBLOCK:
1800                         linep= text->undo_buf[text->undo_pos]; text->undo_pos--;
1801                         linep= (linep<<8)+text->undo_buf[text->undo_pos]; text->undo_pos--;
1802                         linep= (linep<<8)+text->undo_buf[text->undo_pos]; text->undo_pos--;
1803                         linep= (linep<<8)+text->undo_buf[text->undo_pos]; text->undo_pos--;
1804
1805                         buf= MEM_mallocN(linep+1, "dblock buffer");
1806                         for (i=0; i < linep; i++){
1807                                 buf[(linep-1)-i]= text->undo_buf[text->undo_pos]; 
1808                                 text->undo_pos--;
1809                         }
1810                         buf[i]= 0;
1811                         
1812                         txt_curs_first(text, &holdl, &holdc);
1813                         holdln= txt_get_span(text->lines.first, holdl);
1814                         
1815                         txt_insert_buf(text, buf);                      
1816                         MEM_freeN(buf);
1817
1818                         text->curl= text->lines.first;
1819                         while (holdln>0) {
1820                                 if(text->curl->next)
1821                                         text->curl= text->curl->next;
1822                                         
1823                                 holdln--;
1824                         }
1825                         text->curc= holdc;
1826
1827                         linep= text->undo_buf[text->undo_pos]; text->undo_pos--;
1828                         linep= (linep<<8)+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
1832                         text->undo_pos--;
1833                         
1834                         break;
1835
1836                 case UNDO_IBLOCK:
1837                         linep= text->undo_buf[text->undo_pos]; text->undo_pos--;
1838                         linep= (linep<<8)+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
1842                         txt_delete_sel(text);
1843                         while (linep>0) {
1844                                 txt_backspace_char(text);
1845                                 text->undo_pos--;
1846                                 linep--;
1847                         }
1848
1849                         text->undo_pos--;
1850                         text->undo_pos--;
1851                         text->undo_pos--; 
1852                         text->undo_pos--;
1853                         
1854                         text->undo_pos--;
1855
1856                         break;
1857                 case UNDO_INDENT:
1858                 case UNDO_UNINDENT:
1859                 case UNDO_COMMENT:
1860                 case UNDO_UNCOMMENT:
1861                         linep= text->undo_buf[text->undo_pos]; text->undo_pos--;
1862                         linep = (linep<<8)+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 is now the end line of the selection
1866                         
1867                         charp = text->undo_buf[text->undo_pos]; text->undo_pos--;
1868                         charp = (charp<<8)+text->undo_buf[text->undo_pos]; text->undo_pos--;
1869                         //charp is the last char selected or text->line->len
1870                         //set the selcetion for this now
1871                         text->selc = charp;
1872                         text->sell = text->lines.first;
1873                         for (i= 0; i < linep; i++) {
1874                                 text->sell = text->sell->next;
1875                         }
1876
1877                         linep= text->undo_buf[text->undo_pos]; text->undo_pos--;
1878                         linep = (linep<<8)+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                         //first line to be selected
1882                         
1883                         charp = text->undo_buf[text->undo_pos]; text->undo_pos--;
1884                         charp = (charp<<8)+text->undo_buf[text->undo_pos]; text->undo_pos--;
1885                         //first postion to be selected
1886                         text->curc = charp;
1887                         text->curl = text->lines.first;
1888                         for (i = 0; i < linep; i++) {
1889                                 text->curl = text->curl->next;
1890                         }
1891
1892                         
1893                         if (op==UNDO_INDENT) {
1894                                 txt_unindent(text);
1895                         } else if (op== UNDO_UNINDENT) {
1896                                 txt_indent(text);
1897                         } else if (op == UNDO_COMMENT) {
1898                                 txt_uncomment(text);
1899                         } else if (op == UNDO_UNCOMMENT) {
1900                                 txt_comment(text);
1901                         }
1902
1903                         text->undo_pos--;
1904                         break;
1905                 default:
1906                         //XXX error("Undo buffer error - resetting");
1907                         text->undo_pos= -1;
1908                         
1909                         break;
1910         }
1911
1912         /* next undo step may need evaluating */
1913         if (text->undo_pos>=0) {
1914                 switch (text->undo_buf[text->undo_pos]) {
1915                         case UNDO_STO:
1916                                 txt_do_undo(text);
1917                                 txt_do_redo(text); /* selections need restoring */
1918                                 break;
1919                         case UNDO_SWAP:
1920                                 txt_do_undo(text); /* swaps should appear transparent */
1921                                 break;
1922                 }
1923         }
1924         
1925         undoing= 0;     
1926 }
1927
1928 void txt_do_redo(Text *text)
1929 {
1930         char op;
1931         unsigned int linep, i;
1932         unsigned short charp;
1933         char *buf;
1934         
1935         text->undo_pos++;       
1936         op= text->undo_buf[text->undo_pos];
1937         
1938         if (!op) {
1939                 text->undo_pos--;
1940                 return;
1941         }
1942         
1943         undoing= 1;
1944
1945         switch(op) {
1946                 case UNDO_CLEFT:
1947                         txt_move_left(text, 0);
1948                         break;
1949                         
1950                 case UNDO_CRIGHT:
1951                         txt_move_right(text, 0);
1952                         break;
1953                         
1954                 case UNDO_CUP:
1955                         txt_move_up(text, 0);
1956                         break;
1957                         
1958                 case UNDO_CDOWN:
1959                         txt_move_down(text, 0);
1960                         break;
1961
1962                 case UNDO_SLEFT:
1963                         txt_move_left(text, 1);
1964                         break;
1965
1966                 case UNDO_SRIGHT:
1967                         txt_move_right(text, 1);
1968                         break;
1969
1970                 case UNDO_SUP:
1971                         txt_move_up(text, 1);
1972                         break;
1973
1974                 case UNDO_SDOWN:
1975                         txt_move_down(text, 1);
1976                         break;
1977                 
1978                 case UNDO_INSERT:
1979                         text->undo_pos++;
1980                         txt_add_char(text, text->undo_buf[text->undo_pos]);
1981                         text->undo_pos++;
1982                         break;
1983
1984                 case UNDO_BS:
1985                         text->undo_pos++;
1986                         txt_backspace_char(text);
1987                         text->undo_pos++;
1988                         break;
1989
1990                 case UNDO_DEL:
1991                         text->undo_pos++;
1992                         txt_delete_char(text);
1993                         text->undo_pos++;
1994                         break;
1995
1996                 case UNDO_SWAP:
1997                         txt_curs_swap(text);
1998                         txt_do_redo(text); /* swaps should appear transparent a*/
1999                         break;
2000                         
2001                 case UNDO_CTO:
2002                 case UNDO_STO:
2003                         text->undo_pos++;
2004                         text->undo_pos++;
2005
2006                         text->undo_pos++;
2007                         text->undo_pos++;
2008                         text->undo_pos++;
2009                         text->undo_pos++;
2010
2011                         text->undo_pos++;
2012
2013                         charp= text->undo_buf[text->undo_pos];
2014                         text->undo_pos++;
2015                         charp= charp+(text->undo_buf[text->undo_pos]<<8);
2016
2017                         text->undo_pos++;
2018                         linep= text->undo_buf[text->undo_pos]; text->undo_pos++;
2019                         linep= linep+(text->undo_buf[text->undo_pos]<<8); text->undo_pos++;
2020                         linep= linep+(text->undo_buf[text->undo_pos]<<16); text->undo_pos++;
2021                         linep= linep+(text->undo_buf[text->undo_pos]<<24); text->undo_pos++;
2022                         
2023                         if (op==UNDO_CTO) {
2024                                 txt_move_toline(text, linep, 0);
2025                                 text->curc= charp;
2026                                 txt_pop_sel(text);
2027                         } else {
2028                                 txt_move_toline(text, linep, 1);
2029                                 text->selc= charp;
2030                         }
2031
2032                         break;
2033
2034                 case UNDO_DBLOCK:
2035                         text->undo_pos++;
2036                         linep= text->undo_buf[text->undo_pos]; text->undo_pos++;
2037                         linep= linep+(text->undo_buf[text->undo_pos]<<8); text->undo_pos++;
2038                         linep= linep+(text->undo_buf[text->undo_pos]<<16); text->undo_pos++;
2039                         linep= linep+(text->undo_buf[text->undo_pos]<<24); text->undo_pos++;
2040
2041                         txt_delete_sel(text);
2042                         text->undo_pos+=linep;
2043
2044                         text->undo_pos++;
2045                         text->undo_pos++;
2046                         text->undo_pos++; 
2047                         text->undo_pos++;
2048                         
2049                         break;
2050
2051                 case UNDO_IBLOCK:
2052                         text->undo_pos++;
2053                         linep= text->undo_buf[text->undo_pos]; text->undo_pos++;
2054                         linep= linep+(text->undo_buf[text->undo_pos]<<8); text->undo_pos++;
2055                         linep= linep+(text->undo_buf[text->undo_pos]<<16); text->undo_pos++;
2056                         linep= linep+(text->undo_buf[text->undo_pos]<<24); text->undo_pos++;
2057
2058                         buf= MEM_mallocN(linep+1, "iblock buffer");
2059                         memcpy (buf, &text->undo_buf[text->undo_pos], linep);
2060                         text->undo_pos+= linep;
2061                         buf[linep]= 0;
2062                         
2063                         txt_insert_buf(text, buf);                      
2064                         MEM_freeN(buf);
2065
2066                         linep= text->undo_buf[text->undo_pos]; text->undo_pos++;
2067                         linep= linep+(text->undo_buf[text->undo_pos]<<8); text->undo_pos++;
2068                         linep= linep+(text->undo_buf[text->undo_pos]<<16); text->undo_pos++;
2069                         linep= linep+(text->undo_buf[text->undo_pos]<<24); text->undo_pos++;
2070                         (void)linep;
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                                 txt_indent(text);
2111                         } else if (op== UNDO_UNINDENT) {
2112                                 txt_unindent(text);
2113                         } else if (op == UNDO_COMMENT) {
2114                                 txt_comment(text);
2115                         } else if (op == UNDO_UNCOMMENT) {
2116                                 txt_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 than 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 txt_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 than 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 txt_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 than 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 txt_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 to 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 txt_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, is_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                                 is_indent = 1;
2759                         } else if (ch!=' ' && ch!='\t') {
2760                                 is_indent = 0;
2761                         }
2762                 }
2763                 if (is_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 }