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