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