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