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