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