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